riZe_0
@riZe_0

Как исправить неправильное обновление весов в реализации back propagation?

Есть реализация логической операции XOR с помощью нейронной сети, обучение - back propagation, язык - python:
import numpy as np

class NeuralNetwork:
    
    def __init__(self, input_size, hidden_size, output_size):
        
        self.weights_input_hidden = np.random.rand(input_size + 1, hidden_size).T
        self.weights_hidden_output = np.random.rand(hidden_size + 1, output_size).T
        #print('Веса входного слоя (weights_input)=\n', self.weights_input_hidden) # w_jh
        #print('Веса выходного слоя (weights_hidden)=\n', self.weights_hidden_output, '\n') # w_hm

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))
    
    def sigmoid_derivative(self, x):
        return x * (1 - x)
    
    # Прямой ход
    def feedforward(self, X):
        self.hidden_layer_activation = np.dot(self.weights_input_hidden, X)
        self.hidden_layer_output = self.sigmoid(self.hidden_layer_activation) # u_h / sigma_h
        self.hidden_layer_output_bias = np.concatenate((self.hidden_layer_output, [-1]))
        
        self.output_layer_activation = np.dot(self.weights_hidden_output, self.hidden_layer_output_bias)
        output = self.sigmoid(self.output_layer_activation) # a_m / sigma_m
        
        return output
        
    # Обратный ход
    def back_prop(self, X, y, output, theta):
        output_error = output - y
        output_delta = output_error * self.sigmoid_derivative(output)
        
        hidden_error = output_delta.dot(self.weights_hidden_output)
        hidden_delta = hidden_error * self.sigmoid_derivative(self.hidden_layer_output_bias)
        
        self.weights_hidden_output -= theta * output_delta * self.hidden_layer_output_bias
        self.weights_input_hidden -= theta * hidden_delta * X
        
    def train(self, X, y, epochs, theta=0.01):
        X = np.concatenate((X, [-1]))
        for epoch in range(epochs):
            output = self.feedforward(X)
            self.back_prop(X, y, output, theta)
        
X = np.array([[0, 0],
              [0, 1],
              [1, 0],
              [1, 1]])

y = np.array([[0], [1], [1], [0]]) 

xor = NeuralNetwork(2, 2, 1)

for i in range(len(y)):
    xor.train(X[i], y[i], 10000)

for x in X:
    x = np.concatenate((x, [-1]))
    print(f"{x} -> {xor.feedforward(x)}")


Проблема:
После обучения сети получается неправильный ответ (если значение xor.feedforward > 0.5, 1, 0). Пробовал вариант с добавлением смещения в виде отдельной переменной, пробовал даже без самого смещения - ответ неверный.
Подозреваю, что проблема в обновлении весов в функции back_prop, но где именно и почему я понять не в состоянии. Чувствую, что ответ лежит на поверхности.
Теорию брал из лекций К.В.Воронцова 2014 года на youtube.
Сам алгоритм из лекции:
671d0cb72094e823554228.png
  • Вопрос задан
  • 51 просмотр
Решения вопроса 1
riZe_0
@riZe_0 Автор вопроса
Смог решить проблему. Ошибка оказалась в двух местах:

1. Неправильно обновлял входные веса:
self.weights_input_hidden -= theta * hidden_delta * X

В переменной hidden_delta хранился вектор, который получался при перемножении вектора ошибок с учетом bias на выходные значения скрытого слоя.
По идее получалось так, что когда я обновлял входные веса, я еще распространял ошибку смещения скрытого слоя, когда его учитывать не надо было.

2. После обучения нейронной сети:
for i in range(len(y)):
    xor.train(X[i], y[i], 10000)

Здесь я брал один объект и передавал в функцию train, где один объект проходил по циклу:
def train(self, X, y, epochs, theta=0.01):
        X = np.concatenate((X, [-1]))
        for epoch in range(epochs):
            output = self.feedforward(X)
            self.back_prop(X, y, output, theta)

Получилось так, что один объект мог обучаться 10 тысяч раз, и каждый раз веса всей сети менялись для одного объекта.

В итоге исправленный код будет выглядеть так:
import numpy as np

class NeuralNetwork:
    
    def __init__(self, input_size, hidden_size, output_size):
        
        self.weights_input_hidden = np.random.rand(input_size + 1, hidden_size).T
        self.weights_hidden_output = np.random.rand(hidden_size + 1, output_size).T

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))
    
    def sigmoid_derivative(self, x):
        return x * (1 - x)
    
    # Прямой ход
    def feedforward(self, X):
        self.hidden_layer_activation = np.dot(self.weights_input_hidden, X)
        self.hidden_layer_output = self.sigmoid(self.hidden_layer_activation)
        self.hidden_layer_output_bias = np.concatenate((self.hidden_layer_output, [-1])) # Добавил bias для скр.слоя
        
        self.output_layer_activation = np.dot(self.weights_hidden_output, self.hidden_layer_output_bias)
        output = self.sigmoid(self.output_layer_activation)
        
        return output
        
    # Обратынй ход
    def back_prop(self, X, y, output, theta):
        output_error = output - y
        output_delta = output_error * self.sigmoid_derivative(output)
        
        hidden_error = output_delta @ self.weights_hidden_output
        hidden_delta = hidden_error * self.sigmoid_derivative(self.hidden_layer_output_bias)

        self.weights_hidden_output -= (output_delta * self.hidden_layer_output_bias) * theta
        self.weights_input_hidden -= (X.reshape(3, 1) * hidden_delta[:-1]).T * theta
        
    def train(self, X, y, theta=0.1):
        output = self.feedforward(X)
        self.back_prop(X, y, output, theta)
                
        
X = np.array([[0, 0, -1],
              [0, 1, -1],
              [1, 0, -1],
              [1, 1, -1]])

y = np.array([[0], [1], [1], [0]]) 

xor = NeuralNetwork(2, 2, 1)
epochs = 10000

for epoch in range(epochs):
    for i in range(len(X)):
        xor.train(X[i], y[i])

for x in X:
    print(f"{x} -> {xor.feedforward(x)}")
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

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

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