Почему нейросеть на 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] = ... изменяют объект. Следуя этим правилам, вы быстро исправите баг и получите работающий алгоритм обучения.

    Часто задаваемые вопросы