反向传播算法实现中的矩阵维度与权重更新困惑
我希望能深入理解反向传播算法,以达到可以从零编写神经网络及其学习流程的程度。目前主要困惑点在于计算中的矩阵维度,尤其是矩阵转置操作,以及部分计算逻辑。我自学了矩阵相关数学知识,但仍有疑惑。
以下是我调整后的代码(功能与原代码一致):
def fit_partial(self, x, y): A = [np.atleast_2d(x)] for layer in np.arange(0, len(self.W)): net = A[-1].dot(self.W[layer]) out = self.sigmoid(net) A.append(out) error = A[-1] - y D = [error * self.sigmoid_derivative(A[-1])] for layer in np.arange(len(A) - 2, 0, -1): delta = np.matmul(D[-1], self.W[layer].T) * self.sigmoid_derivative(A[layer]) D.append(delta) D = D[::-1] for layer in np.arange(0, len(self.W)): self.W[layer] -= self.alpha * np.matmul(A[layer].T, D[layer])
权重更新公式为:$W_{ij}' = W_{ij} - \alpha \cdot (\partial C / \partial W_{ij})$,我无法理解该公式与代码的对应关系,具体疑问如下,以下是对应的解答:
疑问解答
A[-1].dot(self.W[layer])的结果是否始终为单行矩阵?
不一定,取决于输入x的维度和权重矩阵的维度。如果输入x是单个样本(形状为(1, n_features)),结果是单行矩阵;但如果是批量输入(比如(m, n_features),m为样本数),结果就是(m, n_next_layer_units)的矩阵,每行对应一个样本的加权和输出。代码里np.atleast_2d(x)保证输入是二维,但样本数可多可少,所以结果不一定是单行。使用均方误差时,为何代码中
error = A[-1] - y而非error = 2*(A[-1]-y)?
均方误差损失函数通常写成$C = \frac{1}{2}\sum(\hat{y} - y)^2$,求导后$\partial C/\partial \hat{y} = \hat{y} - y$——这里的1/2就是为了抵消求导后出现的系数2,简化计算。代码里直接用A[-1]-y已经是损失对输出层激活值的导数,不需要再乘2:损失函数的系数已经消去了2,且不影响权重更新方向(等效缩放可通过学习率alpha调整)。反向遍历层时,注释中“最后两层是特殊情况”是什么含义?
这里的“最后两层”指输出层和它的前一层。输出层的误差(D的第一个元素)是直接用error * sigmoid_derivative(A[-1])计算的,不需要依赖后续层(因为没有后续层);而隐藏层的delta需要用下一层的delta和对应权重矩阵推导。所以输出层的delta计算逻辑和所有隐藏层不同,属于特殊情况,反向遍历从倒数第二层开始,就跳过了输出层的特殊计算步骤。核心困惑:
delta = np.matmul(D[-1], self.W[layer].T) * self.sigmoid_derivative(A[layer])中,权重矩阵转置的作用是什么?为何要引入权重矩阵?
delta的本质是损失对当前层输入(加权和net)的导数。对于隐藏层,这个导数需要通过下一层的delta传递:下一层的delta是损失对下一层输入的导数,要得到当前层的delta,需用下一层delta和当前层到下一层的权重做矩阵乘法——这是链式法则的体现,用来把误差从后往前传递。
转置权重矩阵是为了匹配矩阵乘法的维度:假设当前层有n个单元,下一层有m个单元,权重矩阵W[layer]形状为(n, m),下一层deltaD[-1]形状为(样本数, m),转置后W[layer].T为(m, n),此时D[-1].dot(W[layer].T)结果形状为(样本数, n),和当前层激活值导数形状匹配,才能做逐元素乘法。
引入权重矩阵是因为每个当前层单元对下一层单元的贡献由权重决定:权重越大,当前层单元对下一层误差的影响越大,对应的delta也越大。权重更新代码
self.W[layer] -= self.alpha * np.matmul(A[layer].T, D[layer])中,激活矩阵转置的原因是什么?
先看维度:A[layer]是当前层激活值,形状为(样本数, n_units_current);D[layer]是当前层delta,形状为(样本数, n_units_next)。我们需要计算的$\partial C/\partial W_{ij}$是损失对当前层第i个单元到下一层第j个单元权重的导数,根据链式法则,该导数等于A[layer][:,i] * D[layer][:,j]的求和(或平均,批量场景下)。
把A[layer]转置为(n_units_current, 样本数),和D[layer]做矩阵乘法,得到的结果形状为(n_units_current, n_units_next),正好和权重矩阵W[layer]的形状一致,每个元素对应权重的导数之和。转置就是为了让矩阵乘法结果维度与权重矩阵匹配,直接对应每个权重的更新量。
内容的提问来源于stack exchange,提问作者Anthony

