Для кросс-энтропийной ошибки необходим корректный выходной слой в сети. Сигмоид обычно не работает и также не должен использоваться.
Ваши формулы выглядят немного не так. Для текущей конфигурации сети вы определили: 3 слоя (2, 2, 1) у вас есть w0 (2x2) и w1 (1x2). Не забудьте найти dw1 у вас есть следующее:
d1 = (guess - target) * sigmoid_prime(net_inputs[1]) <- when you differentiated da2/dz1 you ended up f'(z1) and not f'(a2)!
dw1 = d1 * activations[1]
db1 = np.sum(d1, axis=1)
d0 = d1 * w1 * sigmoid_prime(net_inputs[0])
dw0 = d0 * activations[0]
db0 = np.sum(d0, axis=1)
Следует помнить, что каждый слой имеет net_inputs как
z: = w @ x + b
и активации
a: = f (z)
. Во время обратного распространения, когда вы вычисляете da [i] / dz [i-1], вам нужно применить производную функции активации к z [i-1], а не к [i].
z = w @ x + b
a = f (z)
da / dz = f '(z) !!!
И это для всех слоев. Некоторые незначительные моменты, на которые следует обратить внимание:
Переключите расчет ошибки на: np.mean (.5 * (активации [-1] - y) ** 2), если вы не используете функции активаций soft / hardmax для выходного слоя (для один выходной нейрон с чего бы вам).
Использование z-s в производных функции активации при вычислении дельты
Не используйте сигмоид (это проблематично с точки зрения исчезающих градиентов), попробуйте ReLu: np.where (x <= 0, 0, x) /np.where (x <= 0, 0, 1) или некоторые его варианты. </p>
Для скорости обучения по XOR выбор между [.0001, .1] должен быть более чем достаточным с использованием любого вида оптимизации.
Если вы инициализируете свои весовые матрицы как: [number_of_input_units x number_of_output_units] вместо [number_of_output_units x number_of_input_units], что у вас есть сейчас, вы можете изменить z = w @ x + b на z = x @ w + b и Вам не нужно будет транспонировать свои входы и выходы.
Вот пример реализации вышеуказанного:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
def cost(guess, target):
return np.mean(np.sum(.5 * (guess - target)**2, axis=1), axis=0)
datasetX = np.array([[0., 0.], [0., 1.], [1., 0.], [1., 1.]])
datasetY = np.array([[0.], [1.], [1.], [0.]])
w0 = np.random.normal(0., 1., size=(2, 4))
w1 = np.random.normal(0., 1., size=(4, 1))
b0 = np.zeros(4)
b1 = np.zeros(1)
f1 = lambda x: np.where(x <= 0, 0, x)
df1 = lambda d: np.where(d <= 0, 0, 1)
f2 = lambda x: np.where(x <= 0, .1*x, x)
df2 = lambda d: np.where(d <= 0, .1, 1)
costs = []
for i in range(250):
a0 = datasetX
z0 = a0 @ w0 + b0
a1 = f1(z0)
z1 = a1 @ w1 + b1
a2 = f2(z1)
costs.append(cost(a2, datasetY))
d1 = (a2 - datasetY) * df2(z1)
d0 = d1 @ w1.T * df1(z0)
dw1 = a1.T @ d1
db1 = np.sum(d1, axis=0)
dw0 = a0.T @ d0
db0 = np.sum(d0, axis=0)
w0 = w0 - .1 * dw0
b0 = b0 - .1 * db0
w1 = w1 - .1 * dw1
b1 = b1 - .1 * db1
print(f2(f1(datasetX @ w0 + b0) @ w1 + b1))
plt.plot(costs)
plt.show()
Результат, который он дает:
[[0.00342399]
[0.99856158]
[0.99983358]
[0.00156524]]