Нейронная сеть не сходится "где угодно" - PullRequest
1 голос
/ 20 июня 2019

В последние 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массивы [Вызванная проблема стекирования входов, которая не устранила ее]

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