在之前的SGD的实现中,我们只考虑了通过前向传播设计的计算。在计算梯度是,我们只调用了pytorch框架提供的函数,而不知其所以然。
能够自动计算梯度大大简化了深度学习算法的实现。
前向传播
前向传播指的是:按顺序计算和存储神经网络中每层的结果
为了简单起见,我们假设输入样本是 x∈Rd, 并且我们的隐藏层不包括偏置项。中间变量z
z=W(1)x,
其中W(1)∈Rh×d 是隐藏层的权重参数。 将中间变量z∈Rh通过激活函数ϕ后, 我们得到长度为h的隐藏激活向量:
h=ϕ(z).
隐藏变量h也是一个中间变量。 假设输出层的参数只有权重W(2)∈Rq×h, 我们可以得到输出层变量,它是一个长度为q的向量:
o=W(2)h.
假设损失函数为l,样本标签为y,我们可以计算单个数据样本的损失项,
L=l(o,y).
根据L2正则化的定义,给定超参数λ,正则化项为
s=2λ(∥W(1)∥F2+∥W(2)∥F2),
其中矩阵的Frobenius范数是将矩阵展平为向量后应用的L2范数。 最后,模型在给定数据样本上的正则化损失为:
J=L+s.

反向传播
应用链式法则,假设我们有函数Y=f(X)和Z=g(Y), 其中输入和输出X,Y,Z是任意形状的张量。 利用链式法则,我们可以计算Z关于X的导数
∂X∂Z=prod(∂Y∂Z,∂X∂Y).
对于我们上面提到的计算图 我们接着使用反向传播计算∂J/∂W(1)和 ∂J/∂W(2)。为此我们应用链式法则,依次计算每个中间变量和参数的梯度。计算的顺序与前向传播执行的顺序相反。
∂L∂J=1and∂s∂J=1.
∂o∂J=prod(∂L∂J,∂o∂L)=∂o∂L∈Rq.
∂W(1)∂s=λW(1)and∂W(2)∂s=λW(2).
∂W(2)∂J=prod(∂o∂J,∂W(2)∂o)+prod(∂s∂J,∂W(2)∂s)=∂o∂Jh⊤+λW(2).
∂h∂J=prod(∂o∂J,∂h∂o)=W(2)⊤∂o∂J.
∂z∂J=prod(∂h∂J,∂z∂h)=∂h∂J⊙ϕ′(z).
∂W(1)∂J=prod(∂z∂J,∂W(1)∂z)+prod(∂s∂J,∂W(1)∂s)=∂z∂Jx⊤+λW(1).
nn的训练
训练神经网路时,前向传播和反向传播相互依赖。 对于前向传播,我们沿着依赖的方向遍历计算图并计算其路径上的所有变量。 然后将这些用于反向传播,其中计算顺序与计算图的相反。
因此,在训练神经网络时,在初始化模型参数后, 我们交替使用前向传播和反向传播,利用反向传播给出的梯度来更新模型参数。 注意,反向传播重复利用前向传播中存储的中间值,以避免重复计算。 带来的影响之一是我们需要保留中间值,直到反向传播完成。 这也是训练比单纯的预测需要更多的内存(显存)的原因之一。
此外,这些中间值的大小与网络层的数量和批量的大小大致成正比。 因此,使用更大的批量来训练更深层次的网络更容易导致内存不足错误。