@Ratyk_ss

В чем проблема моего алгоритма обратного распространения?

Написал небольшую нейронную сеть, которая должна распознавать цифры. Задача стоит в том чтобы это было на numpy.
Так вот, обучение происходит, веса меняются, но на выходе всегда преобладает один нейрон. Я думаю что ошибка в алгоритме обратного распространения. Делал все по формулам, но ошибка как таковой остается. Любые замечания приветствуются.
Код программы
import numpy as np
from keras.datasets import mnist
import matplotlib.pyplot as plt

(train_X, train_y), (test_X, test_y) = mnist.load_data()

SIZE = train_X.shape[1]     # высота картинки

# линеаризируем входные данные
train_X = train_X.reshape(-1, SIZE*SIZE)
test_X = test_X.reshape(-1, SIZE*SIZE)

# масштабируем данные, чтобы значения лежали между 0 и 1:
train_X = train_X / 255
test_X = test_X / 255

class Layer:
    def __init__(self, neuron_number, weight_number):
        self.neuronList = np.random.sample((neuron_number, weight_number))
        self.b = np.random.sample(neuron_number)
    
    
    def sigmoid(self, s, alpha=0.1):
        return 1 / (1 + np.exp(-alpha*s))
    
    def change_weight(self, subtracted_weight, neuron_number):
        self.neuronList[neuron_number] -= subtracted_weight
    
    def change_b(self, subtracted_b, b_number):
        self.b[b_number] -= subtracted_b
    
    def feedForward(self, input_data, neuron_number):
        s = np.dot(self.neuronList[neuron_number], input_data) + self.b[neuron_number]
        return self.sigmoid(s)
    
    
    def layerOutPuts(self, data): 
        length = self.neuronList.shape[0]
        self.outputs = np.zeros(length)
        for i in range(length):
            self.outputs[i] = self.feedForward(data, i)
        

class Network:
    def __init__(self, network_data):
        self.layerList = []
        for i in range(1, len(network_data)):
            neuron_number = network_data[i]
            weight_number = network_data[i - 1]
            self.layerList.append(Layer(neuron_number, weight_number))
            
    
    def deriv_sigmoid(self, y):
        return y * (1 - y)
    
    
    def trueDataConvert(self, a):
        y_true_convert = [
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
        ]
        return y_true_convert[a]
    
    
    def answer(self, input_datas, y_trues):
        
        for input_data, y_true in zip(input_datas, y_trues):
                
            
            for i in range(len(self.layerList)):
                
                if i == 0:
                    self.layerList[i].layerOutPuts(input_data)
                
                else:
                    self.layerList[i].layerOutPuts(self.layerList[i-1].outputs)
                    
                        
                        
            y_pred = self.layerList[len(self.layerList) - 1].outputs
                  
            print('Ответ: ', np.argmax(y_pred))
            
            print('Правильный ответ: ',y_true)
        
            
            
    
    #обучение
    def fit(self, input_datas, y_trues):
        
        learn_rate = 0.1
        epochs = 3 
        
        
        for epoch in range(epochs):
            loses = []
            
            print()
            
            for input_data, y_true in zip(input_datas, y_trues):
                
                
                
                y_true = np.array(self.trueDataConvert(y_true))
                
                #данные идут на вход
                for i in range(len(self.layerList)):
                    #первый слой
                    if i == 0:
                        self.layerList[i].layerOutPuts(input_data)
                    
                    #оcтальные слой
                    else:
                        self.layerList[i].layerOutPuts(self.layerList[i-1].outputs)
                    
                
                #выходы нейросети
                
                y_pred = self.layerList[len(self.layerList) - 1].outputs
                
                               
                d_L_d_ypred = 2/10 * (y_pred - y_true)
                
                               
                #потери
                loses = np.append(loses, ((y_true - y_pred) ** 2).mean())
                
                
               
                
                d_th_list= [] #tetha скрытого слоя
                
                
                #обратный проход
                for i in range(len(self.layerList) - 1, -1, -1):
                        
                        
                        #последний слой
                        if i == len(self.layerList) - 1:
                            
                            
                            #проход по нейронам
                            for j in range(self.layerList[i].neuronList.shape[0]):
                                
                                subtracted_weight = learn_rate * self.layerList[i - 1].outputs * d_L_d_ypred[j] * self.deriv_sigmoid(y_pred[j])
                                self.layerList[i].change_weight(subtracted_weight, j)
                                
                                subtracted_b = learn_rate * d_L_d_ypred[j] * self.deriv_sigmoid(y_pred[j])
                                self.layerList[i].change_b(subtracted_b, j)
                                
                        
                        #скрытые слои
                        elif i != 0:
                            
                            
                            #проход по нейронам
                            for j in range(self.layerList[i].neuronList.shape[0]):
                                                                
                                #веса следующего слоя   
                                next_layer_weights = np.array(self.layerList[i+1].neuronList[:, j])
                                
                                
                                
                                d_th_list.append(np.dot(d_L_d_ypred * self.deriv_sigmoid(y_pred), next_layer_weights) * self.deriv_sigmoid(self.layerList[i].outputs[j]))
                                
                                                              
                                subtracted_weight = learn_rate * self.layerList[i - 1].outputs * d_th_list[j]
                                self.layerList[i].change_weight(subtracted_weight, j)
                                
                                subtracted_b = learn_rate * d_th_list[j]
                                self.layerList[i].change_b(subtracted_b, j)
                        
                        #входной слой
                        else:
                            
                            
                            for j in range(self.layerList[i].neuronList.shape[0]):
                                
                                #веса следующего слоя
                                next_layer_weights = np.array(self.layerList[i+1].neuronList[:, j])
                                
                                
                                subtracted_weight = learn_rate * input_data * np.dot(next_layer_weights, d_th_list) * self.deriv_sigmoid(self.layerList[i].outputs[j])
                                self.layerList[i].change_weight(subtracted_weight, j)
                                
                                subtracted_b = learn_rate * np.dot(next_layer_weights, d_th_list) * self.deriv_sigmoid(self.layerList[i].outputs[j])
                                self.layerList[i].change_b(subtracted_b, j)
            
            
            
            
            #построение графика потерь
            x = range(100)
            plt.grid(True)
            plt.plot(x, 
                    loses, 
                    'bo-', 
                    label='Train losses')

            plt.xlabel('Epoch')
            plt.ylabel('Loss')

            plt.legend(loc='upper right')                   

#задаем нейросеть в 3 слоя

network_data = [784, 32, 16, 10]               

nt = Network(network_data)

new_train_x = train_X[:100]
new_train_y = train_y[:100]


nt.fit(new_train_x, new_train_y)

#nt.answer(new_train_x, new_train_y)
  • Вопрос задан
  • 109 просмотров
Решения вопроса 1
@Ratyk_ss Автор вопроса
Изменил способ инициализации параметров так, чтобы изначально были как положительные, так и отрицательные веса. В моем методе все параметры были положительными. В итоге это привело к тому, что теперь сеть обучается исправно.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 4
@qoqo
Есть простая реализация на numpy от Тарика Рашида ( https://github.com/makeyourownneuralnetwork/makeyo... ). Код очень простой, я без знаний phyton легко реализовал его на Rust .
Ответ написан
Комментировать
mayton2019
@mayton2019
Bigdata Engineer
Дружище @Ratyk_ss

Какая у тебя стоит главная задача? Я вижу разные направления.
Если ты хочешь просто изучить алгоритм обратного распространения ошибки (ОРО)
то тебе нужно взять просто Python и желательно готовую реализацию и просто разобраться
как она работает. И сделать график схождения этой ошибки. График и матрица классификации
будет подтверждением того что код написан правильно.

Ты затащил к себе в код библиотеку numpy и keras. Достаточно сложные. И для себя нужно
проверить что каждое действие например с numpy правильно тобой понято и применено и
выдает правильный результат. Я-бы покрыл модульным тестом все взаимодействия. Вот что такое np.dot?
Произведение. Какое? Скалярное? Векторное? ДА их до чорта много. Проверяй что за произведение.

Твоя ошибка - сложная. Нечеткая. Это не то что NPE, или index out of range. Ты говоришь - один нейрон активен.
Почему он один? Почему другие не корректируют свои веса? Вот этот случай ты должен проверить
и доказать в обратную сторону. Чтоб была коррекция цикл должен их покрывать. Они должны РЕАГИРОВАТЬ
на прямой ход и обратный код.

Логгируй. Вставляй asserts. Ничего другого я пока советовать не могу. Сомнительно что code-review здесь поможет.

Если у тебя стоит другая задача. А именно - просто применение нейросетей - то тебе ничего програмиировать вообще не надо. В наше время все НС - решены в виде коробочных продуктов.
И их надо сконфигурировать и просто подать на вход данные.
Ответ написан
Комментировать
@AlexSku
не буду отвечать из-за модератора
А почему нельзя было воспользоваться нормальной библиотекой? Вместо сотни строк получили бы 10. Если же вы делаете на низком уровне, то не делайте трёх слоёв, хватило бы и одного.
Ответ написан
ML_Karasik
@ML_Karasik
Люблю нейросети и что с ними связанно
Пробовал lr поставить ниже? 0.1 это прям много
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы