В последние 2 недели я пытался реализовать классификатор рукописных цифр с нейронной сетью с прямой связью, используя базу данных MNIST.Нейронная сеть использует Cross-Entropy loss и Softmax для выходного слоя;Остальные узлы активируются с помощью функции Sigmoid.
Основная проблема, с которой я столкнулся при обучении сети, заключалась в том, что потеря никогда не сходилась и была очень спастической.
Я уже пытался переписать свой сценарий, проверить, правильно ли я реализовал Math, и мне не повезло с ним.Я также прочитал другие сообщения с той же проблемой, но ни один из них не дал мне ответ, который я искал.Кроме того, я попытался проверить с помощью более простой нейронной сети, но тоже не повезло.Источники, которые я использовал в своем коде: Обратное распространение Кен-Чен Производное Softmax с перекрестной энтропией Пример прямой передачи и обратного распространения
Примечание Я почти уверен, что проблема возникает при обновлении весов, но я не могу сказать.
Примечание Приведенный ниже код переписан.
import numpy as np
import matplotlib.pyplot as plt
import pickle
from mnist import MNIST
class Neural_Re:
def __init__(self, layers):
self.layers = layers
self.weights = []
self.bias_weights = [0 for i in range(len(layers) - 1)]
self.activated_sums = []
self.derivative_sums = []
self.learning_rate = 0.1
self.error = []
# Initialize Random Weights
for i in range(len(layers) - 1):
weights_matrix = np.random.rand(self.layers[i + 1], self.layers[i]).dot(
np.sqrt(2 / (self.layers[i] + self.layers[i + 1])))
self.weights.append(weights_matrix)
def add_bias(self, layer):
# e.g: if layer == 0, layer in bias_weights is 0
layer -= 1
weights_matrix = np.random.rand(self.layers[layer + 1]).dot(
np.sqrt(2 / (self.layers[layer] + self.layers[layer + 1])))
self.bias_weights.insert(layer, weights_matrix)
def set_input(self, inputs):
self.activated_sums.append(inputs)
@staticmethod
def activation_sigmoid(sums, derivative=False):
for i in range(len(sums)):
if sums[i] > 37:
sums[i] = 37
elif sums[i] < -37:
sums[i] = -37
s = 1 / (1 + np.exp(-sums))
if derivative:
return s * (1 - s)
else:
return s
@staticmethod
def activation_softmax(sums):
exp = np.exp(sums)
return exp / exp.sum()
def propagate(self):
for layer in range(len(self.weights)):
# Calculate Sum w*x + b
zl = self.weights[layer].dot(self.activated_sums[layer])
np.add(zl, self.bias_weights[layer], out=zl)
# Saving Sums of (w*x + b) for use in calculating the error of
each node in backprop
self.derivative_sums.append(zl)
if layer == len(self.weights) - 1:
al = self.activation_softmax(zl)
else:
al = self.activation_sigmoid(zl)
self.activated_sums.append(al)
def backprop(self, target_vector):
for layer in range(len(self.weights) - 1, -1, -1):
if layer == len(self.weights) - 1: # If layer is output layer:
# calculate derivative w.r.t Output sum vector (∂J/∂z)
# [J = Loss function , z = Sum vector before activation]
self.derivative_sums[layer] = np.subtract(self.activated_sums[len(self.activated_sums)-1], target_vector)
else:
# Calculate Error of each Node in layer
derivative_sigmoid = self.activation_sigmoid(self.derivative_sums[layer], derivative=True)
sum_errors_in_next_layer = np.sum(self.derivative_sums[layer + 1].dot(self.weights[layer + 1]))
self.derivative_sums[layer] = np.multiply(sum_errors_in_next_layer, derivative_sigmoid)
for layer in range(0, len(self.weights) - 1):
# Stochastic Gradient Descent, Update weights.
self.SGD(layer)
# Calculate Error of model in iteration n
self.calc_J(target_vector)
# Reset Activated_sums, derivative_sums for next iteration.
self.activated_sums = []
self.derivative_sums = []
def SGD(self, layer):
gradient_error = np.multiply(np.outer(self.derivative_sums[layer], self.activated_sums[layer].T), self.learning_rate)
self.weights[layer] = np.subtract(self.weights[layer], gradient_error)
if self.bias_weights[layer] is not 0:
self.bias_weights[layer] = np.subtract(self.bias_weights[layer], np.multiply(self.derivative_sums[layer], self.learning_rate))
def calc_J(self, hot_vector):
x = -np.sum(hot_vector * np.log(self.activated_sums[len(self.activated_sums) - 1]))
print(x)
self.error.append(x)
def graph_loss(self):
x = self.error
y = [i for i in range(len(self.error))]
plt.plot(y, x) # x axis -> Loss, y axis -> epochs
plt.show()
if __name__ == "__main__":
mndata = MNIST('samples', gz=True)
images, labels = mndata.load_training()
NN = Neural_Re([784, 200, 200, 10])
NN.learning_rate = 0.001
iter = 2000
for i in range(iter):
image = np.multiply(mndata.process_images_to_numpy(images[i]), 1 / 256)
# label vector
label = [0 for j in range(10)]
label[labels[i]] = 1
print(i)
NN.set_input(image)
NN.propagate()
NN.backprop(label)
NN.graph_loss()
Изначально я ожидал получить что-то вроде этого (информация о графике не имеет значения, только для контекста): Ожидается: http://cs231n.github.io/assets/nn3/loss.jpeg
Но когда я разрешу свою нейронную сетьrun (2000 учебных примеров): результат: http://prntscr.com/o4346c
Почему нейронная сеть не сходится ни к чему?Любые ответы будут оценены!
Изменить 1: Изменена строка в Backprop "Рассчитать производную по ___"
Редактировать 2: Добавлена скорость обучения [Была не полная проблема, все еще не работает]
Редактировать 3: добавлены операторы Reset для activ_sums и Derivative_Sumмассивы [Вызванная проблема стекирования входов, которая не устранила ее]