Как можно исправить ошибки в обновлении весов при реализации метода обратного распространения ошибки (back propagation)?

Можете ли вы предоставить реализацию логической операции XOR с использованием нейронной сети, обученной методом обратного распространения ошибки на языке Python?
  • 26 октября 2024 г. 15:52
Ответы на вопрос 2
Обновление весов в методе обратного распространения ошибки может быть подвержено ряду ошибок. Вот некоторые распространенные подходы для исправления этих ошибок:

1. **Правильный расчет градиентов**: Убедитесь, что вы правильно вычисляете градиенты функции потерь по отношению к весам. Используйте правила дифференцирования корректно.

2. **Скорость обучения (learning rate)**: Подберите подходящее значение скорости обучения. Слишком большое значение может привести к расходимости, а слишком малое - к очень медліному обучению.

3. **Инициализация весов**: Не забывайте правильно инициализировать веса. Часто используют случайные значения или методы, такие как Xavier или He инициализация.

4. **Регуляризация**: Избегайте переобучения, используя регуляризацию, такую как L1 или L2, чтобы контролировать величину весов.

5. **Улучшение алгоритма оптимизации**: Вместо стандартного градиентного спуска используйте продвинутые оптимизаторы, такие как Adam или RMSProp, которые могут адаптивно изменять скорость обучения.

6. **Нормализация данных**: Убедитесь, что входные данные нормализованы, чтобы улучшить сходимость.

Теперь давайте реализуем логическую операцию XOR с помощью нейронной сети, обученной методом обратного распространения ошибки на Python. Мы будем использовать библиотеку NumPy.

```python
import numpy as np

# sigmoid функция и её производная
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def sigmoid_derivative(x):
    return x * (1 - x)

# Входные данные для XOR
X = np.array([[0, 0],
              [0, 1],
              [1, 0],
              [1, 1]])

# Выходные данные для XOR
y = np.array([[0], [1], [1], [0]])

# Установка случайного генератора
np.random.seed(42)

# Инициализация весов
input_layer_size = 2
hidden_layer_size = 2
output_layer_size = 1

weights_input_hidden = np.random.uniform(size=(input_layer_size, hidden_layer_size))
weights_hidden_output = np.random.uniform(size=(hidden_layer_size, output_layer_size))

# Обучение сети
learning_rate = 0.5
epochs = 10000

for epoch in range(epochs):
    # Прямое распространение
    hidden_layer_input = np.dot(X, weights_input_hidden)
    hidden_layer_output = sigmoid(hidden_layer_input)

    output_layer_input = np.dot(hidden_layer_output, weights_hidden_output)
    predicted_output = sigmoid(output_layer_input)

    # Обратное распространение ошибки
    error = y - predicted_output
    d_predicted_output = error * sigmoid_derivative(predicted_output)

    error_hidden_layer = d_predicted_output.dot(weights_hidden_output.T)
    d_hidden_layer = error_hidden_layer * sigmoid_derivative(hidden_layer_output)

    # Обновление весов
    weights_hidden_output += hidden_layer_output.T.dot(d_predicted_output) * learning_rate
    weights_input_hidden += X.T.dot(d_hidden_layer) * learning_rate

# Тестирование сети
print("Выходные данные после обучения:")
print(predicted_output)
```

В этом коде мы создаем простую нейронную сеть с одним скрытым слоем и обучаем ее на входах для операции XOR. Мы используем сигмоидную функцию активации и обновляем веса с использованием метода обратного распространения ошибки. После обучения выполняется вывод предсказанных значений. 

Если вы запустите этот код, вы увидите выходные данные, которые должны быть достаточно близки к ожидаемым результатам логической операции XOR (0, 1, 1, 0) для входных данных (0,0), (0,1), (1,0) и (1,1).
Смог решить проблему. Ошибка оказалась в двух местах: 

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)}")
Похожие вопросы