Функция потери увеличивается вместо уменьшения - PullRequest
3 голосов
/ 05 марта 2020

Я пытался создать свои собственные нейронные сети с нуля. Через некоторое время я сделал это, но столкнулся с проблемой, которую не могу решить. Я следую учебнику , который показывает, как это сделать. Проблема, с которой я столкнулся, заключалась в том, как моя сеть обновляет весы и смещения. Ну, я знаю, что градиентный спуск не всегда будет уменьшать потери, и в течение нескольких эпох он может даже немного увеличиться, но все равно он должен уменьшиться и работать намного лучше, чем у меня. Иногда весь процесс застревает в потерях 9 и 13 и не может выйти из него. Я проверил много учебников, видео и веб-сайтов, но я не нашел ничего плохого в своем коде. self.activate, self.dactivate, self.loss и self.dloss:

# sigmoid
self.activate = lambda x: np.divide(1, 1 + np.exp(-x))
self.dactivate = lambda x: np.multiply(self.activate(x), (1 - self.activate(x)))

# relu
self.activate = lambda x: np.where(x > 0, x, 0)
self.dactivate = lambda x: np.where(x > 0, 1, 0)

# loss I use (cross-entropy)
clip = lambda x: np.clip(x, 1e-10, 1 - 1e-10) # it's used to squeeze x into a probability between 0 and 1 (which I think is required)
self.loss = lambda x, y: -(np.sum(np.multiply(y, np.log(clip(x))) + np.multiply(1 - y, np.log(1 - clip(x))))/y.shape[0])
self.dloss = lambda x, y: -(np.divide(y, clip(x)) - np.divide(1 - y, 1 - clip(x)))

Код, который я использую для прямого распространения:

self.activate(np.dot(X, self.weights) + self.biases) # it's an example for first hidden layer

И это код для обратного распространения:

Первая часть, в DenseNeuralNetwork классе:

last_derivative = self.dloss(output, y)

for layer in reversed(self.layers):
    last_derivative = layer.backward(last_derivative, self.lr)

И вторая часть, в Dense классе:

def backward(self, last_derivative, lr):
    w = self.weights

    dfunction = self.dactivate(last_derivative)
    d_w = np.dot(self.layer_input.T, dfunction) * (1./self.layer_input.shape[1])
    d_b = (1./self.layer_input.shape[1]) * np.dot(np.ones((self.biases.shape[0], last_derivative.shape[0])), last_derivative)

    self.weights -= np.multiply(lr, d_w)
    self.biases -= np.multiply(lr, d_b)

    return np.dot(dfunction, w.T)

Я также сделал repl , чтобы вы могли проверить весь код и запустить его без проблем.

Ответы [ 2 ]

1 голос
/ 05 марта 2020

1.

строка 12

self.dloss = lambda x, y: -(np.divide(y, clip(x)) - np.divide(1 - y, 1 - clip(x)))

если вы собираетесь обрезать x, вы тоже должны обрезать y.
Я имею в виду, что есть несколько способов реализовать это, но если вы собираетесь использовать этот способ.
изменить на

self.dloss = lambda x, y: -(np.divide(clip(y), clip(x)) - np.divide(1 - clip(y), 1 - clip(x)))

2.

строка 75

dfunction = self.dactivate(last_derivative)

эта часть обратного распространения просто неверна .
изменить на

dfunction = last_derivative*self.dactivate(np.dot(self.layer_input, self.weights) + self.biases)

3.

строка 77

d_b = (1./self.layer_input.shape[1]) * np.dot(np.ones((self.biases.shape[0], last_derivative.shape[0])), last_derivative)

last_derivative должен быть dfunction. Я думаю, что это просто ошибка.
изменить на

d_b = (1./self.layer_input.shape[1]) * np.dot(np.ones((self.biases.shape[0], last_derivative.shape[0])), dfunction)

4.

строка 85

self.weights = np.random.randn(neurons, self.neurons) * np.divide(6, np.sqrt(self.neurons * neurons))
self.biases = np.random.randn(1, self.neurons) * np.divide(6, np.sqrt(self.neurons * neurons))

Не уверен, куда вы идете с этим, но я думаю, что инициализированные значения слишком велики. Мы не выполняем точную гипертюнинг, поэтому я просто уменьшил его.

self.weights = np.random.randn(neurons, self.neurons) * np.divide(6, np.sqrt(self.neurons * neurons)) / 100
self.biases = np.random.randn(1, self.neurons) * np.divide(6, np.sqrt(self.neurons * neurons)) / 100

Все хорошо сейчас

После этого я изменил скорость обучения на 0,01, потому что она была медленной, и это работал нормально.
Я думаю, что вы неправильно понимаете обратное распространение. Вы, вероятно, должны дважды проверить, как это работает. Другие части в порядке, я думаю.

0 голосов
/ 05 марта 2020

Это может быть вызвано данными вашей тренировки. Либо это слишком мало, либо слишком много разных ярлыков (что я получаю из вашего кода по ссылке, которой вы делитесь).

Я перезапускаю ваш код несколько раз, и он дает разные результаты обучения. Иногда потеря продолжает уменьшаться до последней эпохи, иногда она продолжает увеличиваться, в одно время она уменьшалась до некоторой точки и увеличивалась. (С минимальными потерями, достигнутыми 0,5)

Я думаю, что в этот раз важны данные о вашей тренировке. Тем не менее, скорость обучения достаточно хорошая (при условии, что вы выполнили расчет для линейной комбинации, обратного распространения и т. Д. c справа).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...