Часть 1

Вступление:

Я думаю, что слишком многие инженеры по машинному обучению сразу начинают применять модели машинного обучения с такими библиотеками, как Keras и Tensorflow, не понимая, что такое машинное обучение. Хорошее упражнение для этого - вернуться и попытаться создать алгоритм машинного обучения с нуля. Я решил пойти на один уровень глубже, пытаясь воссоздать функции, которые предоставляет Keras, с нуля.

Задача:

Целью этой программы было создание гибкого пользовательского интерфейса для создания нейронных сетей. Для простоты программа будет включать только самый простой тип слоя.

Концепция:

Структура моей программы была очень похожа на сборочную линию. Каждая часть программы действовала как рабочий на сборочной линии. Продукт будет передаваться из каждой части кода в центральный процессор, который выполняет все вычисления и отправляет обратно соответствующую информацию. Наилучший способ дать каждой программе эти атрибуты «сборочной линии» - это представить всю модель как класс, а каждый тип слоя - как подкласс.

Проходим код:

Шаг 1 | Предпосылки:

import numpy
from matplotlib import pyplot as plt
def sigmoid(x):
    return 1/(1+np.exp(-x))
def sigmoid_p(x):
    return sigmoid(x)*(1 -sigmoid(x))
def relu(x):
    return np.maximum(x, 0)
def relu_p(x):
    return np.heaviside(x, 0)
def tanh(x):
    return np.tanh(x)
def tanh_p(x):
    return 1.0 - np.tanh(x)**2
def deriv_func(z,function):
    if function == sigmoid:
        return sigmoid_p(z)
    elif function == relu:
        return relu_p(z)
    elif function == tanh:
        return tanh_p(z)

Я импортировал Numpy для управления матрицей и Matplotlib для построения графика потерь с течением времени. Функции активации описаны ниже. Существует также другая функция, которая принимает значение и функцию активации в качестве входных данных и возвращает производное значение.

Шаг 2 | Создайте класс основной нейронной сети:

class NeuralNetwork:
    def __init__(self):
        self.layers = []
        self.weights = []
        self.loss = []
    def add(self,layer_function):
        self.layers.append(layer_function)
        
    def initialize_weights(self):
        for layer in self.layers:
            index = self.layers.index(layer)
            weights = layer.initialize_weights(self.layers,index)
            self.weights.append(weights)
            
    def propagate(self,X):
        As,Zs = [],[]
        input_data = X
        for layer in self.layers:
            a,z = layer.propagate(input_data)
            As.append(a)
            Zs.append(z)
            input_data = a
        return As,Zs

Это класс, в котором определены все остальные классы и функции. Это работник, который работает со всеми продуктами другого работника и возвращает соответствующую информацию. Следовательно, он имеет доступ ко всем переменным, содержащимся во вложенных классах.

Шаг 3 | Создайте класс Perceptron:

class Perceptron:
        def __init__(self,nodes,input_shape= None,activation = None):
            self.nodes = nodes
            self.input_shape = input_shape
            self.activation = activation
        def initialize_weights(self,layers,index):
            if self.input_shape:
                self.weights = np.random.randn(self.input_shape[-1],self.nodes)
            else:
                self.weights = np.random.randn(layers[index-1].weights.shape[-1],self.nodes)
            return self.weights
        def propagate(self,input_data):
            z = np.dot(input_data,self.weights)
            if self.activation:
                a = self.activation(z)
            else:
                a = z
            return a,z
        def network_train(self,gradient):
            self.weights += gradient

Персептрон действует как мини-нейронная сеть в более крупной сети. Функция поезда очень проста, так как не требуется никаких внешних производных.

Шаг 4 | Определить обучение:

def train(self,X,y,iterations):
        loss = []
        for i in range(iterations):
            As,Zs = self.propagate(X)
            loss.append(np.square(sum(y - As[-1])))
            As.insert(0,X)
            g_wm = [0] * len(self.layers)
            for i in range(len(g_wm)):
                pre_req = (y-As[-1])*2
                a_1 = As[-(i+2)]
                z_index = -1
                w_index = -1
                if i == 0:
                    range_value = 1
                else:
                    range_value = 2*i
                for j in range(range_value):
                    if j% 2 == 0:
                        pre_req = pre_req * sigmoid_p(Zs[z_index])
                        z_index -= 1
                    else:
                        pre_req = np.dot(pre_req,self.weights[w_index].T)
                        w_index -= 1
                gradient = np.dot(a_1.T,pre_req)
                g_wm[-(i+1)] = gradient
                for i in range(len(self.layers)):
                    self.layers[i].network_train(g_wm[i])
        return loss

Созданная мною обучающая функция может быть не самой лаконичной, но она надежна, так что сеть может сохранять свою гибкость.

Полный исходный код:

import numpy
from matplotlib import pyplot as plt
def sigmoid(x):
    return 1/(1+np.exp(-x))
def sigmoid_p(x):
    return sigmoid(x)*(1 -sigmoid(x))
def relu(x):
    return np.maximum(x, 0)
def relu_p(x):
    return np.heaviside(x, 0)
def tanh(x):
    return np.tanh(x)
def tanh_p(x):
    return 1.0 - np.tanh(x)**2
def deriv_func(z,function):
    if function == sigmoid:
        return sigmoid_p(z)
    elif function == relu:
        return relu_p(z)
    elif function == tanh:
        return tanh_p(z)
class NeuralNetwork:
    def __init__(self):
        self.layers = []
        self.weights = []
        self.loss = []
    def add(self,layer_function):
        self.layers.append(layer_function)
        
    def initialize_weights(self):
        for layer in self.layers:
            index = self.layers.index(layer)
            weights = layer.initialize_weights(self.layers,index)
            self.weights.append(weights)
            
    def propagate(self,X):
        As,Zs = [],[]
        input_data = X
        for layer in self.layers:
            a,z = layer.propagate(input_data)
            As.append(a)
            Zs.append(z)
            input_data = a
        return As,Zs
    
    def train(self,X,y,iterations):
        loss = []
        for i in range(iterations):
            As,Zs = self.propagate(X)
            loss.append(np.square(sum(y - As[-1])))
            As.insert(0,X)
            g_wm = [0] * len(self.layers)
            for i in range(len(g_wm)):
                pre_req = (y-As[-1])*2
                a_1 = As[-(i+2)]
                z_index = -1
                w_index = -1
                if i == 0:
                    range_value = 1
                else:
                    range_value = 2*i
                for j in range(range_value):
                    if j% 2 == 0:
                        pre_req = pre_req * sigmoid_p(Zs[z_index])
                        z_index -= 1
                    else:
                        pre_req = np.dot(pre_req,self.weights[w_index].T)
                        w_index -= 1
                gradient = np.dot(a_1.T,pre_req)
                g_wm[-(i+1)] = gradient
                for i in range(len(self.layers)):
                    self.layers[i].network_train(g_wm[i])
        return loss
        
    class Perceptron:
        def __init__(self,nodes,input_shape= None,activation = None):
            self.nodes = nodes
            self.input_shape = input_shape
            self.activation = activation
        def initialize_weights(self,layers,index):
            if self.input_shape:
                self.weights = np.random.randn(self.input_shape[-1],self.nodes)
            else:
                self.weights = np.random.randn(layers[index-1].weights.shape[-1],self.nodes)
            return self.weights
        def propagate(self,input_data):
            z = np.dot(input_data,self.weights)
            if self.activation:
                a = self.activation(z)
            else:
                a = z
            return a,z
        def network_train(self,gradient):
            self.weights += gradient
                
model = NeuralNetwork()
Perceptron = model.Perceptron
X = np.array([[0,1,1],[1,1,0],[1,0,1]])
y = np.array([[0],[1],[1]])
model.add(Perceptron(5,input_shape = (None,3),activation = sigmoid))
model.add(Perceptron(10,activation = sigmoid))
model.add(Perceptron(10,activation = sigmoid))
model.add(Perceptron(1,activation = sigmoid))
model.initialize_weights()
loss = model.train(X,y,1000)
plt.plot(loss)

Надеюсь, вы кое-что узнали из этой статьи! Не стесняйтесь использовать этот код, чтобы углубить свое понимание машинного обучения!