Один пример построения нейронной сети с нуля
Первоначальная цель этого поста заключалась в том, чтобы я просто коснулся математики в нейронных сетях, так как мне нравится хорошо разбираться во внутренней работе алгоритмов и разбираться в сути вещей. Затем я думаю, что с таким же успехом мог бы составить историю, а не просто пересматривать формулы в своем блокноте снова и снова. Хотя вы можете найти ряд руководств по созданию простой нейронной сети с нуля. У разных людей разные углы зрения, а также акцент в учебе. Другой способ мышления может в некотором смысле улучшить понимание. Итак, приступим.
Коротко о нейронной сети
Ядро нейронной сети - это большая функция, которая сопоставляет некоторые входные данные с желаемым целевым значением, на промежуточном этапе выполняет операцию по созданию сети, которая заключается в умножении весов и добавлении смещения в сценарии конвейера, который делает это снова и снова. . Процесс обучения нейронной сети заключается в определении набора параметров, которые минимизируют разницу между ожидаемым значением и результатом модели. Это выполняется с помощью градиентного спуска (также известного как обратное распространение), который по определению включает два шага: вычисление градиентов функции потерь / ошибок, затем обновление существующих параметров в ответ на градиенты, что и происходит при спуске. Этот цикл повторяется до достижения минимумов функции потерь. Этот процесс обучения можно описать простым уравнением: W (t + 1) = W (t) - dJ (W) / dW (t).
Математическая интуиция
Для моей практической цели мне нравится использовать небольшую сеть с одним скрытым слоем, как показано на диаграмме. В этом макете X представляет вход, индексы i, j, k обозначают количество единиц во входном, скрытом и выходном слоях соответственно; w_ij представляет собой веса, соединяющие входной слой со скрытым слоем, а w_jk - веса, соединяющие скрытый с выходным слоем.
Расчет выходных данных модели в этом случае будет следующим:
Часто выбор функции потерь представляет собой сумму квадратов ошибок. Здесь я использую сигмовидную функцию активации и для простоты предполагаю, что смещение b равно 0, что означает, что веса - единственные переменные, которые влияют на вывод модели. Выведем формулу для расчета градиентов от скрытых до выходных весов w_jk.
Сложность определения входных данных для скрытых весов заключается в том, что они косвенно влияют на ошибку выходных данных. Выход каждого скрытого модуля влияет на выход модели, таким образом, ввод для скрытых весов w_ij зависит от ошибок на всех модулях, к которым он подключен. Вывод начинается так же, просто чтобы расширить цепное правило в z_k до подфункции.
Еще мысли:
Обратите внимание, что градиенты двух весов имеют похожую форму. Ошибка передается обратно через производную функции активации, а затем взвешивается по входу (выходу активации) из предыдущего слоя. Во второй формуле ошибка обратного распространения из выходного слоя далее проецируется на w_jk, затем повторяется тот же способ обратного распространения и взвешивается входными данными. Этот процесс обратного распространения повторяется вплоть до самого первого уровня нейронной сети произвольного уровня. «Таким образом, градиенты по каждому параметру считаются вкладом параметра в ошибку и следует отрицать во время обучения ».
Помещение вышеуказанного процесса в код:
Ниже приведен полный пример:
import numpy as np class NeuralNetwork: def __init__(self): np.random.seed(10) # for generating the same results self.wij = np.random.rand(3,4) # input to hidden layer weights self.wjk = np.random.rand(4,1) # hidden layer to output weights def sigmoid(self, x, w): z = np.dot(x, w) return 1/(1 + np.exp(-z)) def sigmoid_derivative(self, x, w): return self.sigmoid(x, w) * (1 - self.sigmoid(x, w)) def gradient_descent(self, x, y, iterations): for i in range(iterations): Xi = x Xj = self.sigmoid(Xi, self.wij) yhat = self.sigmoid(Xj, self.wjk) # gradients for hidden to output weights g_wjk = np.dot(Xj.T, (y - yhat) * self.sigmoid_derivative(Xj, self.wjk)) # gradients for input to hidden weights g_wij = np.dot(Xi.T, np.dot((y - yhat) * self.sigmoid_derivative(Xj, self.wjk), self.wjk.T) * self.sigmoid_derivative(Xi, self.wij)) # update weights self.wij += g_wij self.wjk += g_wjk print('The final prediction from neural network are: ') print(yhat) if __name__ == '__main__': neural_network = NeuralNetwork() print('Random starting input to hidden weights: ') print(neural_network.wij) print('Random starting hidden to output weights: ') print(neural_network.wjk) X = np.array([[0, 0, 1], [1, 1, 1], [1, 0, 1], [0, 1, 1]]) y = np.array([[0, 1, 1, 0]]).T neural_network.gradient_descent(X, y, 10000)
Использованная литература: