正向传播与反向传播
1. 正向传播
正向传播是指对神经网络沿着从输入层到输出层的顺序,依次计算并存储模型的中间变量(包括输出)。
假设输入是一个特征为\(\boldsymbol{x} \in \mathbb{R}^d\)的样本,且不考虑偏差项,那么中间变量
\[\boldsymbol{z} = \boldsymbol{W}^{(1)} \boldsymbol{x},
\]
(矩阵相乘)
其中\(\boldsymbol{W}^{(1)} \in \mathbb{R}^{h \times d}\)是隐藏层的权重参数。把中间变量\(\boldsymbol{z} \in \mathbb{R}^h\)输入按元素运算的激活函数\(\phi\)后,将得到向量长度为\(h\)的隐藏层变量
\[\boldsymbol{h} = \phi (\boldsymbol{z}).
\]
隐藏层变量\(\boldsymbol{h}\)也是一个中间变量。假设输出层参数只有权重\(\boldsymbol{W}^{(2)} \in \mathbb{R}^{q \times h}\),可以得到向量长度为\(q\)的输出层变量
\[\boldsymbol{o} = \boldsymbol{W}^{(2)} \boldsymbol{h}.
\]
假设损失函数为\(\ell\),且样本标签为\(y\),可以计算出单个数据样本的损失项
\[L = \ell(\boldsymbol{o}, y).
\]
根据\(L_2\)范数正则化的定义,给定超参数\(\lambda\),正则化项即(超参数\(\lambda\)即表示惩罚的力度)
\[s = \frac{\lambda}{2} \left(|\boldsymbol{W}^{(1)}|_F^2 + |\boldsymbol{W}^{(2)}|_F^2\right),
\]
其中矩阵的Frobenius范数等价于将矩阵变平为向量后计算\(L_2\)范数。最终,模型在给定的数据样本上带正则化的损失为
\[J = L + s.
\]
我们将\(J\)称为有关给定数据样本的目标函数。
2. 反向传播
反向传播用于计算神经网络中的参数梯度。反向传播利用微积分中的链式法则,沿着从输出层到输入层的顺序进行依次计算目标函数有关神经网络各层的中间变量以及参数的梯度。
依据链式法则,我们可以知道:
\[\frac{\partial J}{\partial \boldsymbol{o}} = \text{prod}\left(\frac{\partial J}{\partial L}, \frac{\partial L}{\partial \boldsymbol{o}}\right) = \frac{\partial L}{\partial \boldsymbol{o}}.
\]
\[\left( \frac{\partial J}{\partial L} = 1, \quad \frac{\partial J}{\partial s} = 1\right)
\]
其中\(\text{prod}\)运算符将根据两个输入的形状,在必要的操作(如转置和互换输入位置)后对两个输入做乘法。
\[\frac{\partial J}{\partial \boldsymbol{W}^{(2)}} = \text{prod}\left(\frac{\partial J}{\partial \boldsymbol{o}}, \frac{\partial \boldsymbol{o}}{\partial \boldsymbol{W}^{(2)}}\right) + \text{prod}\left(\frac{\partial J}{\partial s}, \frac{\partial s}{\partial \boldsymbol{W}^{(2)}}\right) = \frac{\partial J}{\partial \boldsymbol{o}} \boldsymbol{h}^\top + \lambda \boldsymbol{W}^{(2)}
\]
其中:
\[\left(\frac{\partial s}{\partial \boldsymbol{W}^{(1)}} = \lambda \boldsymbol{W}^{(1)},\quad\frac{\partial s}{\partial \boldsymbol{W}^{(2)}} = \lambda \boldsymbol{W}^{(2)}\right)
\]
还有:
\[\frac{\partial J}{\partial \boldsymbol{W}^{(2)}} = \text{prod}\left(\frac{\partial J}{\partial \boldsymbol{o}}, \frac{\partial \boldsymbol{o}}{\partial \boldsymbol{W}^{(2)}}\right) + \text{prod}\left(\frac{\partial J}{\partial s}, \frac{\partial s}{\partial \boldsymbol{W}^{(2)}}\right) = \frac{\partial J}{\partial \boldsymbol{o}} \boldsymbol{h}^\top + \lambda \boldsymbol{W}^{(2)}
\]
\[\frac{\partial J}{\partial \boldsymbol{h}} = \text{prod}\left(\frac{\partial J}{\partial \boldsymbol{o}}, \frac{\partial \boldsymbol{o}}{\partial \boldsymbol{h}}\right) = {\boldsymbol{W}^{(2)}}^\top \frac{\partial J}{\partial \boldsymbol{o}}
\]
\[\frac{\partial J}{\partial \boldsymbol{z}} = \text{prod}\left(\frac{\partial J}{\partial \boldsymbol{h}}, \frac{\partial \boldsymbol{h}}{\partial \boldsymbol{z}}\right) = \frac{\partial J}{\partial \boldsymbol{h}} \odot \phi'\left(\boldsymbol{z}\right)
\]
所以,可以得到:
\[\frac{\partial J}{\partial \boldsymbol{W}^{(1)}} = \text{prod}\left(\frac{\partial J}{\partial \boldsymbol{z}}, \frac{\partial \boldsymbol{z}}{\partial \boldsymbol{W}^{(1)}}\right) + \text{prod}\left(\frac{\partial J}{\partial s}, \frac{\partial s}{\partial \boldsymbol{W}^{(1)}}\right) = \frac{\partial J}{\partial \boldsymbol{z}} \boldsymbol{x}^\top + \lambda \boldsymbol{W}^{(1)}
\]
在模型参数初始化完成后,需要交替地进行正向传播和反向传播,并根据反向传播计算的梯度迭代模型参数。在反向传播中使用了正向传播中计算得到的中间变量来避免重复计算,同时这个复用也导致正向传播结束后不能立即释放中间变量内存。这也是训练要比预测占用更多内存的一个重要原因。这些中间变量的个数大体上与网络层数线性相关,每个变量的大小跟批量大小和输入个数也是线性相关的,这是导致较深的神经网络使用较大批量训练时更容易超内存的主要原因。