我正在尝试理解和运行迈克尔·尼尔森的《神经网络与深度学习》第二章中关于反向传播的代码:http://neuralnetworksanddeeplearning.com/chap2.html#the_code_for_backpropagation。
在反向传播的开始部分,代码是这样的:
delta = self.cost_derivative(activations[-1], y) * \
sigmoid_prime(zs[-1])
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
前向传播创建了activations
列表,其中activations[i]
包含第i
层的激活值向量。因此,activations[-1]
是最后一层。y
是期望的输出。
cost_derivative
定义如下:
def cost_derivative(self, output_activations, y):
"""Return the vector of partial derivatives \partial C_x /
\partial a for the output activations."""
return (output_activations-y)
所以第一行输出一个与输出层形状相同的向量。因此,我的疑问是第四行的np.dot
如何工作?我理解activations[-2]
是倒数第二层神经元的激活值向量,它可以有任意数量的神经元,所以我不确定如何对它(或它的转置)进行点积运算(与delta
进行点积运算),而delta
具有输出层的形状。
我运行了代码(https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/src/network.py),并添加了一些调试行来尝试理解这一点,但似乎不起作用:
>>> from network import *; net = Network([2,1,2])
>>> net.backprop([1,2], [3,4])
Activations[0] [1, 2]
Activations[1] [[ 0.33579893]]
Activations[2] [[ 0.37944698] [ 0.45005939]]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<snip>/neural-networks-and-deep-learning/src/network.py", line 117, in backprop
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
ValueError: shapes (2,2) and (1,1) not aligned: 2 (dim 1) != 1 (dim 0)
activations
看起来完全符合我的预期 – 2个激活值,然后是1个,然后是2个。失败发生在我不清楚的那一行上,并且失败的方式也符合我的预期。但是,显然这本书中的代码是经过测试的(这本书非常优秀),所以我一定是做错了什么。我正在编写一个独立的实现,并遇到了同样的问题,所以我期望能够拆解这段代码来解决这个问题 – 但我无法理解这应该如何工作,或者为什么对作者来说它能工作。
我很感激任何关于我遗漏了什么的见解。谢谢!:)
回答:
为什么反向传播中的形状匹配
假设网络架构是[...,N,M]
,即最后一层输出大小为M
的向量,前一层输出大小为N
的向量(让我们关注最后两层,忽略其余部分)。N
和M
可以是任意值。另外,让我们忽略批处理,就像你的问题中一样:我们只输入一个输入和一个标签。
在这种情况下,最后的权重矩阵,即self.weights[-1]
,将具有[M,N]
形状,因此nabla_w[-1]
也必须是这个形状才能正确执行更新。现在:
delta
将具有[M,1]
形状(对应于输出)。activations[-2]
将具有[N,1]
形状,因此转置后是[1,N]
。- 它们的点积产生
[M,1]*[1,N] -> [M,N]
形状,这正是我们需要的形状。
为什么你的测试运行失败了
因为在numpy中,形状(2,)
与[1,2]
或[2,1]
不同:
>>> np.array([1, 2]).shape
(2,)
网络架构区分了x
和y
的行和列,你必须为它们提供正确的形状才能使其工作。否则,你会得到意外的广播和形状不匹配。尝试这个例子来看看实际情况:
net = Network([2,1,2])
x = np.array([1, 2]).reshape([2, 1]) # 一个大小为2的示例
y = np.array([3, 4]).reshape([2, 1]) # 一个大小为2的示例
net.backprop(x, y)