Обратное распространение на матрице весов

Вопрос или проблема

Я пытаюсь реализовать нейронную сеть для бинарной классификации, используя только Python и numpy.

Структура моей сети следующая:
входные признаки: матрица 2 [1X2]
Скрытый слой1: 5 нейронов [2X5] матрица
Скрытый слой2: 5 нейронов [5X5] матрица
Выходной слой: 1 нейрон [5X1] матрица
Я использую сигмоидную функцию активации во всех слоях.

Теперь допустим, я использую бинарную кросс-энтропию в качестве функции потерь. Как мне выполнить обратное распространение на этих матрицах, чтобы обновить веса?

class Layer():
    def __init__(self,number_of_neurons,number_of_inputs):
        self.weights=np.random.rand(number_of_neurons, number_of_inputs)
        self.bias=np.random.rand(number_of_neurons,1)     

class NeralNetwork():
        def __init__(self, layer1, layer2,layer3):
            self.layer1 = layer1
            self.layer2 = layer2
            self.layer3 = layer3

    def sigmoid(self,x):
        return 1 / (1 + np.exp(-x))

    def derivative_sigmoid(self,x):
        return x*(1-x)

    def get_cost_value(self,Y_hat, Y):
        m = Y_hat.shape[1]
        cost = -1 / m * (np.dot(Y, np.log(Y_hat).T) + np.dot(1 - Y, np.log(1 - Y_hat).T))
        return np.squeeze(cost)

    def get_cost_derivative(self,Y_hat,Y):
        return  - (np.divide(Y, Y_hat) - np.divide(1 - Y, 1 - Y_hat))

    def train(self,inputs,labels,epocs):
        for epoc in range(1,epocs+1):
            z1=np.dot(self.layer1.weights,inputs)+self.layer1.bias
            a1=self.sigmoid(z1)
            z2=np.dot(self.layer2.weights,a1)+self.layer2.bias
            a2=self.sigmoid(z2)
            #print(a2.shape)
            z3=np.dot(self.layer3.weights,a2)+self.layer3.bias
            a3=self.sigmoid(z3)
            #print(a3.shape)
            if epoc%100 == 0:
                print(a3)

            cost=self.get_cost_value(a3,labels)
            #print(cost)



            layer3_delta=self.derivative_sigmoid(a3)*self.get_cost_derivative(a3,labels)
            print(layer3_delta.shape)
            Dw_layer3=np.dot(layer3_delta,a2.T)
            Db_layer3=layer3_delta
            #print(Dw_layer3.shape)
            layer2_delta=np.dot(self.layer3.weights.T,layer3_delta)*self.derivative_sigmoid(a2)
            #print(layer2_delta.shape)
            Dw_layer2=np.dot(layer2_delta,a1.T)
            Db_layer2=layer2_delta

            layer1_delta=np.dot(self.layer2.weights.T,layer2_delta)*self.derivative_sigmoid(a1)
            Dw_layer1=np.dot(layer1_delta,inputs.T)
            Db_layer1=layer1_delta
            #print(Dw_layer1)

            self.layer1.weights-=((1/epoc)*Dw_layer1)
            self.layer2.weights-=((1/epoc)*Dw_layer2)
            self.layer3.weights-=((1/epoc)*Dw_layer3)

            self.layer1.bias-=((1/epoc)*Db_layer1)
            self.layer2.bias-=((1/epoc)*Db_layer2)
            self.layer3.bias-=((1/epoc)*Db_layer3)

На данный момент я пытался реализовать это, как показано выше. Но, по-видимому, есть ошибка, потому что после обучения сеть не научилась. Пожалуйста, дайте знать, если у вас есть какие-либо идеи.

Ваш вопрос требует широкого объяснения.

Я предлагаю посмотреть это видео на YouTube DNN Backpropagation
Оно объяснит, как работает обратное распространение в случае DNN.

Ответ или решение

Ваша задача по реализации нейронной сети на Python с использованием библиотеки Numpy для бинарной классификации представляет интерес как с точки зрения практического применения машинного обучения, так и с точки зрения глубинного изучения алгоритмов. В данной структуре сети вы выбрали трехслойную архитектуру: входной слой, два скрытых слоя и выходной слой. У вас также используется сигмоидная функция активации и функция потерь — бинарная кросс-энтропия.

Для успешного обучения сети снижением ошибки (потери) необходимо корректно реализовать метод обратного распространения ошибки (backpropagation) и обновление весов. Подробно рассмотрим этапы обратного распространения:

  1. Вычисление производных активации: Поскольку вы используете сигмоидную функцию активации, её производная такова: sigmoid'(x) = sigmoid(x) * (1 – sigmoid(x)). Это особенно важно для вычисления градиентов.

  2. Вычисление ошибочного сигнала: Ошибочный сигнал на каждом слое вычисляется как производная функции потерь по активации, умноженная на производную функции активации. Для последнего слоя это выглядит как:
    [
    \text{delta_layer3} = \text{(a3 – Y)} * \text{derivative_sigmoid}(a3)
    ]
    Тут важно понимать, что delta_layerN покажет, насколько сильно активация самого слоя повлияла на ошибку.

  3. Распространение ошибок назад: Основной шаг алгоритма — это "проталкивание" ошибки назад через все слои сети. Вы используете формулу:
    [
    \text{delta_layer(N-1)} = (\text{weights_layerN}^T \cdot \text{delta_layerN}) * \text{derivative_sigmoid}(a(N-1))
    ]

  4. Обновление весов и смещений: Изменение весов осуществляется путем вычитания произведения градиента и скорости обучения из текущих весов:
    [
    \text{weights} -= \text{learning_rate} \times \Delta w
    ]
    Вы используете обратный порядок эпис (1/epoc) в обновлениях, что может быть причиной того, что модель не обучается. Скорость обучения должна быть фиксированной или динамически адаптированной, но не функцией числа эпох.

Некоторые возможные источники проблем в вашей реализации, которые стоит перепроверить:

  • Скорость обучения: Попробуйте установить фиксированную скорость обучения, например 0.01.
  • Инициализация весов: Использование np.random.rand может не обеспечить оптимальных условий для быстрого обучения, попробуйте использовать np.random.randn для нормального распределения.
  • Проверка градиентов: Убедитесь, что вычисления градиентов производятся корректно. Используйте численное приближение градиента для отладки.

Оптимизация этих аспектов и корректная проверка всех шагов алгоритма помогут улучшить производительность вашей сети. Успехов в работе над проектом!

Оцените материал
Добавить комментарий

Капча загружается...