Вопрос или проблема
Я пытаюсь реализовать нейронную сеть для бинарной классификации, используя только 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) и обновление весов. Подробно рассмотрим этапы обратного распространения:
-
Вычисление производных активации: Поскольку вы используете сигмоидную функцию активации, её производная такова: sigmoid'(x) = sigmoid(x) * (1 – sigmoid(x)). Это особенно важно для вычисления градиентов.
-
Вычисление ошибочного сигнала: Ошибочный сигнал на каждом слое вычисляется как производная функции потерь по активации, умноженная на производную функции активации. Для последнего слоя это выглядит как:
[
\text{delta_layer3} = \text{(a3 – Y)} * \text{derivative_sigmoid}(a3)
]
Тут важно понимать, что delta_layerN покажет, насколько сильно активация самого слоя повлияла на ошибку. -
Распространение ошибок назад: Основной шаг алгоритма — это "проталкивание" ошибки назад через все слои сети. Вы используете формулу:
[
\text{delta_layer(N-1)} = (\text{weights_layerN}^T \cdot \text{delta_layerN}) * \text{derivative_sigmoid}(a(N-1))
] -
Обновление весов и смещений: Изменение весов осуществляется путем вычитания произведения градиента и скорости обучения из текущих весов:
[
\text{weights} -= \text{learning_rate} \times \Delta w
]
Вы используете обратный порядок эпис (1/epoc) в обновлениях, что может быть причиной того, что модель не обучается. Скорость обучения должна быть фиксированной или динамически адаптированной, но не функцией числа эпох.
Некоторые возможные источники проблем в вашей реализации, которые стоит перепроверить:
- Скорость обучения: Попробуйте установить фиксированную скорость обучения, например 0.01.
- Инициализация весов: Использование np.random.rand может не обеспечить оптимальных условий для быстрого обучения, попробуйте использовать np.random.randn для нормального распределения.
- Проверка градиентов: Убедитесь, что вычисления градиентов производятся корректно. Используйте численное приближение градиента для отладки.
Оптимизация этих аспектов и корректная проверка всех шагов алгоритма помогут улучшить производительность вашей сети. Успехов в работе над проектом!