TheIndependentCode / Neural-Network

Machine Learning library for educational purpose.
290 stars 80 forks source link

Numpy convolutional #2

Open King-Oni opened 2 years ago

King-Oni commented 2 years ago

First I have to say thanks for your tutorials on neural networks, now I understand the topic better

But there was a thing that I couldn't let pass through

In the conv layer you used a functionality from scipy, But scipy doesn't work in my device for some reasons And I need a way to implement these functions using numpy, but I don't understand the math behind that Nor do I find some mathematical formula that tells me how to 2d convolve or correlate and I can't come up with working solution that's why I'm here I need a way to implement conv layer in numpy only

King-Oni commented 2 years ago

I came up with a solution

correlate

import numpy as np
from numpy.fft import fft2, ifft2

def _correlate2D(array, kernel):
    a = array
    f = kernel
    s = f.shape + tuple(np.subtract(a.shape, f.shape) + 1)
    strd = np.lib.stride_tricks.as_strided
    subM = strd(a, shape=s, strides=a.strides * 2)
    return np.einsum("ij,ijkl->kl", f, subM)

convolve

def _convolve2D(array, kernel):
    a = array
    f = np.flip(kernel)
    s = f.shape + tuple(np.subtract(a.shape, f.shape) + 1)
    strd = np.lib.stride_tricks.as_strided
    subM = strd(a, shape=s, strides=a.strides * 2)
    return np.einsum("ij,ijkl->kl", f, subM)

active use

def correlate2d(array: np.array, kernel: np.array, mode="full"):
    modes = ["full", "valid"]
    keyFlag = mode in modes
    if not keyFlag:
        raise KeyError("mode is either full or valid!.\n other than that can't do.")

    if mode == "valid":
        return _correlate2D(array, kernel)

    return _correlate2D(np.pad(array, 1), kernel)

def convolve2d(array: np.array, kernel: np.array, mode="full"):
    modes = ["full", "valid"]
    keyFlag = mode in modes
    if not keyFlag:
        raise KeyError("mode is either full or valid!.\n other than that can't do.")

    if mode == "valid":
        return _convolve2D(array, kernel)

    return _convolve2D(np.pad(array, 1), kernel)

then it can be used like this:

class Convolutional(Layer):
    def __init__(self, input_shape, kernel_size, depth):
        input_depth, input_height, input_width = input_shape
        self.depth = depth
        self.input_shape = input_shape
        self.input_depth = input_depth
        self.output_shape = (depth, input_height - kernel_size + 1, input_width - kernel_size + 1)
        self.kernels_shape = (depth, input_depth, kernel_size, kernel_size)
        self.kernels = np.random.randn(*self.kernels_shape)
        self.biases = np.random.randn(*self.output_shape)

    def forward(self, input):
        self.input = input
        self.output = np.copy(self.biases)
        for i in range(self.depth):
            for j in range(self.input_depth):
                self.output[i] += correlate2d(self.input[j], self.kernels[i, j], "valid")
        return self.output

    def backward(self, output_gradient, learning_rate):
        kernels_gradient = np.zeros(self.kernels_shape)
        input_gradient = np.zeros(self.input_shape)

        for i in range(self.depth):
            for j in range(self.input_depth):
                kernels_gradient[i, j] = correlate2d(self.input[j], output_gradient[i], "valid")
                input_gradient[j] += convolve2d(output_gradient[i], self.kernels[i, j], "full")

        self.kernels -= learning_rate * kernels_gradient
        self.biases -= learning_rate * output_gradient
        return input_gradient

I'm not sure if it really works well I only tested it on one matrix which is the one Used in the video to explain how it works