Почему нейросеть на Python не меняет веса: решение проблемы
При обучении простой нейросети на Python разработчики часто сталкиваются с ситуацией, когда после цикла обучения веса и смещение возвращаются к исходным значениям, хотя внутри цикла они корректно изменяются. Рассмотрим причины этой проблемы на примере кода с однослойным перцептроном и функцией активации hardlim. В статье объясним, как работают списки и массивы numpy, и предложим правильное решение.
Как работает код обучения нейросети
В представленном коде используется список w из трёх случайных весов и смещение bias. Функция train на каждой эпохе проходит по всем примерам, вычисляет ошибку и обновляет веса и смещение. Внутри цикла print показывает, что значения меняются: w[0], w[1], w[2] и bias обновляются. Однако после завершения функции возвращаются исходные значения, созданные в начале train.
Почему возвращаются начальные веса
Ключевая причина - область видимости переменных в Python. Внутри функции train создаются новые локальные переменные w и bias. Когда вы присваиваете w = [uniform(0, 1) for _ in range(3)], вы создаёте новый объект списка, который существует только внутри функции. После выхода из функции этот локальный список уничтожается, и возвращается то, что было объявлено вне цикла - в данном случае глобальный W, который не изменялся, потому что функция работала со своей копией.
Как исправить: передача весов по ссылке
Чтобы изменения весов сохранялись, нужно передавать в функцию уже существующий список и изменять его элементы, не создавая новый. Например, можно объявить w и bias вне функции и передавать их как аргументы. Тогда изменения внутри функции будут влиять на оригинальный объект. Альтернатива - использовать numpy.array, который поддерживает мутабельные операции и часто ведёт себя предсказуемее.
Пример корректного кода с numpy
import numpy as np
from random import uniform
data = np.array([...]) # ваши данные
response = np.array([...])
def train(data, current, w, bias, epoch=10):
for _ in range(epoch):
for i in range(len(data)):
res = net(data[i], w, bias)
err = current[i] - res
w += err * data[i]
bias += err
return w, bias
w = np.array([uniform(0,1) for _ in range(3)])
bias = 0
W, bias = train(data, response, w, bias, 3)
print(W, bias)Теперь w изменяется напрямую, и после обучения вы получите обновлённые веса.
Распространённые ошибки при обучении нейросетей
- Создание новых списков внутри цикла: каждый вызов
[w[j] + ... for j in range(len(w))]создаёт новый список, а не изменяет существующий. - Путаница с областью видимости: локальные переменные в функции не влияют на глобальные, если не использовать
globalили не передавать объекты по ссылке. - Игнорирование мутабельности: списки Python мутабельны, но присваивание (
w = ...) заменяет ссылку, а не изменяет содержимое.
Заключение
Чтобы нейросеть на Python правильно обновляла веса, избегайте создания новых списков внутри функции. Передавайте существующий список или используйте numpy.array для наглядности. Помните, что оператор = меняет ссылку, а методы вроде w[0] = ... изменяют объект. Следуя этим правилам, вы быстро исправите баг и получите работающий алгоритм обучения.