Смог решить проблему. Ошибка оказалась в двух местах:
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)}")