6.6 训练神经网络
前馈神经网络属于监督机器学习:每个观测 x \mathbf x x 的正确输出 y \mathbf y y 都已知。系统通过式 6.13 产生 y ^ \hat{\mathbf y} y ^ ,即对真实 y \mathbf y y 的估计。训练目标是为每层 i i i 学习参数 W [ i ] \mathbf W^{[i]} W [ i ] 和 b [ i ] \mathbf b^{[i]} b [ i ] ,使每个训练观测的 y ^ \hat{\mathbf y} y ^ 尽可能接近真实 y \mathbf y y 。
总体上,我们沿用第 4 章逻辑回归的方法,本节用通用简单网络讲解,而非专门为情感分析或语言建模设计的网络。
第一,需要一个表示系统输出与金标准输出距离的损失函数,通常使用逻辑回归中的交叉熵损失。
第二,使用第 4 章的梯度下降优化算法,寻找使损失最小的参数。
第三,梯度下降需要损失函数的梯度,即包含损失对每个参数偏导数的向量。逻辑回归可以为每个观测直接计算损失对单个 w w w 或 b b b 的导数;神经网络却可能有许多层、数百万个参数,而损失只连接在很后面的层上,因此很难直接看出如何计算损失对第 1 层某个权重的偏导数。解决方法是误差反向传播 (error backpropagation),也称反向微分 (backward differentiation)。
6.6.1 损失函数 ¶ 神经网络使用的交叉熵损失与逻辑回归相同。若网络用于二元分类,最终层使用 sigmoid,损失就是式 4.19:
L C E ( y ^ , y ) = − log p ( y ∣ x ) = − [ y log y ^ + ( 1 − y ) log ( 1 − y ^ ) ] (6.25) L_{CE}(\hat y,y)=-\log p(y\mid x)=-[y\log\hat y+(1-y)\log(1-\hat y)]\tag{6.25} L CE ( y ^ , y ) = − log p ( y ∣ x ) = − [ y log y ^ + ( 1 − y ) log ( 1 − y ^ )] ( 6.25 ) 若网络分类为三类或更多,损失与第 4 章第 112 页的多项回归相同。此时 y \mathbf y y 和 y ^ \hat{\mathbf y} y ^ 都是向量。对只有一个正确类别的硬分类,真实标签 y \mathbf y y 含 K K K 个元素;正确类别为 c c c 时 y c = 1 y_c=1 y c = 1 ,其余均为 0,即独热向量。分类器输出估计向量 y ^ \hat{\mathbf y} y ^ ,其中 y ^ k \hat y_k y ^ k 表示估计概率 p ( y k = 1 ∣ x ) p(y_k=1\mid\mathbf x) p ( y k = 1 ∣ x ) 。
单个样本的损失,是 K K K 个输出类别对数以 y k y_k y k 加权后求和并取负:
L C E ( y ^ , y ) = − ∑ k = 1 K y k log y ^ k (6.26) L_{CE}(\hat{\mathbf y},\mathbf y)=-\sum_{k=1}^Ky_k\log\hat y_k\tag{6.26} L CE ( y ^ , y ) = − k = 1 ∑ K y k log y ^ k ( 6.26 ) 用条件成立时为 1、否则为 0 的指示函数 1 { } \mathbb1\{\} 1 { } 可写为:
L C E ( y ^ , y ) = − ∑ k = 1 K 1 { y k = 1 } log y ^ k L_{CE}(\hat{\mathbf y},\mathbf y)=-\sum_{k=1}^K\mathbb1\{y_k=1\}\log\hat y_k L CE ( y ^ , y ) = − k = 1 ∑ K 1 { y k = 1 } log y ^ k 除真实类别对应项外,其余各项均为 0。因此交叉熵损失就是正确类别输出概率的负对数,也称负对数似然损失:
L C E ( y ^ , y ) = − log y ^ c ( c 为正确类别 ) (6.27) L_{CE}(\hat{\mathbf y},\mathbf y)=-\log\hat y_c\qquad(c\text{ 为正确类别})\tag{6.27} L CE ( y ^ , y ) = − log y ^ c ( c 为正确类别 ) ( 6.27 ) 代入式 6.9 的 softmax:
L C E ( y ^ , y ) = − log exp ( z c ) ∑ j = 1 K exp ( z j ) ( c 为正确类别 ) (6.28) L_{CE}(\hat{\mathbf y},\mathbf y)=-\log\frac{\exp(z_c)}{\sum_{j=1}^K\exp(z_j)}\qquad(c\text{ 为正确类别})\tag{6.28} L CE ( y ^ , y ) = − log ∑ j = 1 K exp ( z j ) exp ( z c ) ( c 为正确类别 ) ( 6.28 ) 完美分类器给正确类别概率 1、所有错误类别概率 0。正确类别概率越接近 1,分类器越好;越接近 0,分类器越差。负对数概率是很合适的损失:从 − log 1 = 0 -\log1=0 − log 1 = 0 (无损失)到 − log 0 = ∞ -\log0=\infty − log 0 = ∞ (无限损失)。它还确保正确答案概率最大化时,错误答案概率都被最小化,因为全部概率之和为 1。
输出向量的类别数 K K K 可大可小。三分类情感分析中类别为正面、负面、中性;词性判断中,K K K 是标注集中的词性数(第 18 章将定义的标注集有 17 类);语言建模中类别是全部词元,可能有 50,000 或 100,000 个。
6.6.2 计算梯度 ¶ 计算损失梯度,需要求损失函数对每个参数的偏导数。只有一个权重层和 sigmoid 输出的网络就是逻辑回归,可直接使用式 6.29(推导见第 4.15 节):
∂ L C E ( y ^ , y ) ∂ w j = ( y ^ − y ) x j = ( σ ( w ⋅ x + b ) − y ) x j (6.29) \begin{array}{rcl}\frac{\partial L_{CE}(\hat y,y)}{\partial w_j}&=&(\hat y-y)x_j\\&=&(\sigma(\mathbf w\cdot\mathbf x+b)-y)x_j\end{array}\tag{6.29} ∂ w j ∂ L CE ( y ^ , y ) = = ( y ^ − y ) x j ( σ ( w ⋅ x + b ) − y ) x j ( 6.29 ) 只有一个权重层和 softmax 输出的网络就是多项逻辑回归,可使用式 4.41。对权重 w k , i w_{k,i} w k , i :
∂ L C E ( y ^ , y ) ∂ w k , i = − ( y k − y ^ k ) x i = − ( y k − p ( y k = 1 ∣ x ) ) x i = − ( y k − exp ( w k ⋅ x + b k ) ∑ j = 1 K exp ( w j ⋅ x + b j ) ) x i (6.30) \begin{array}{ll}\frac{\partial L_{CE}(\hat{\mathbf y},\mathbf y)}{\partial w_{k,i}}&=-(y_k-\hat y_k)x_i\\&=-(y_k-p(y_k=1\mid\mathbf x))x_i\\&=-\left(y_k-\frac{\exp(\mathbf w_k\cdot\mathbf x+b_k)}{\sum_{j=1}^K\exp(\mathbf w_j\cdot\mathbf x+b_j)}\right)x_i\end{array}\tag{6.30} ∂ w k , i ∂ L CE ( y ^ , y ) = − ( y k − y ^ k ) x i = − ( y k − p ( y k = 1 ∣ x )) x i = − ( y k − ∑ j = 1 K e x p ( w j ⋅ x + b j ) e x p ( w k ⋅ x + b k ) ) x i ( 6.30 ) 但这些导数只适用于一个权重层,也就是最后一层。深层网络的损失在末端计算,而要对前面各层的权重参数求导,情况复杂得多。解决这一问题的算法称为误差反向传播 或 backprop (Rumelhart et al., 1986)。它其实与更一般的反向微分过程相同,而反向微分依赖计算图 (computation graph)。
6.6.3 计算图 ¶ 计算图把数学表达式的计算过程拆成独立操作,每个操作表示为图中的一个节点。
以函数 L ( a , b , c ) = c ( a + 2 b ) L(a,b,c)=c(a+2b) L ( a , b , c ) = c ( a + 2 b ) 为例,显式写出加法、乘法并为中间输出命名:
d = 2 b e = a + d L = c e \begin{array}{rl}d&=2b\\e&=a+d\\L&=ce\end{array} d e L = 2 b = a + d = ce 可把它画成图:每个操作一个节点,有向边将某操作的输出作为下一操作的输入。图 6.15 设 a = 3 , b = 1 , c = − 2 a=3,b=1,c=-2 a = 3 , b = 1 , c = − 2 ,展示前向传递得到 L ( 3 , 1 , − 2 ) = − 10 L(3,1,-2)=-10 L ( 3 , 1 , − 2 ) = − 10 。计算图的前向传递 (forward pass)从左到右应用各操作,把每步输出传给下一节点。
图 6.15 函数 L ( a , b , c ) = c ( a + 2 b ) L(a,b,c)=c(a+2b) L ( a , b , c ) = c ( a + 2 b ) 的计算图。输入 a = 3 , b = 1 , c = − 2 a=3,b=1,c=-2 a = 3 , b = 1 , c = − 2 ,展示计算 L L L 的前向传递。
6.6.4 计算图上的反向微分 ¶ 计算图的重要性来自反向传递 (backward pass),它用于计算权重更新所需的导数。这个例子要计算输出 L L L 对每个输入变量的导数 ∂ L ∂ a , ∂ L ∂ b , ∂ L ∂ c \frac{\partial L}{\partial a},\frac{\partial L}{\partial b},\frac{\partial L}{\partial c} ∂ a ∂ L , ∂ b ∂ L , ∂ c ∂ L ;例如 ∂ L ∂ a \frac{\partial L}{\partial a} ∂ a ∂ L 表示 a a a 的微小变化会使 L L L 改变多少。
反向微分使用微积分的链式法则。若复合函数 f ( x ) = u ( v ( x ) ) f(x)=u(v(x)) f ( x ) = u ( v ( x )) :
d f d x = d u d v ⋅ d v d x (6.31) \frac{df}{dx}=\frac{du}{dv}\cdot\frac{dv}{dx}\tag{6.31} d x df = d v d u ⋅ d x d v ( 6.31 ) 对 f ( x ) = u ( v ( w ( x ) ) ) f(x)=u(v(w(x))) f ( x ) = u ( v ( w ( x ))) :
d f d x = d u d v ⋅ d v d w ⋅ d w d x (6.32) \frac{df}{dx}=\frac{du}{dv}\cdot\frac{dv}{dw}\cdot\frac{dw}{dx}\tag{6.32} d x df = d v d u ⋅ d w d v ⋅ d x d w ( 6.32 ) 反向微分把梯度从最终节点传回图中所有节点。图 6.16 展示节点 e e e 的一部分计算:节点接收右侧父节点传来的上游梯度 (upstream gradient),为每个输入计算局部梯度 (local gradient,即输出对该输入的梯度),再依据链式法则将二者相乘,得到传给更早节点的下游梯度 (downstream gradient)。
由于计算图中 L = c e L=ce L = ce ,可以直接得到:
∂ L ∂ c = e (6.33) \frac{\partial L}{\partial c}=e\tag{6.33} ∂ c ∂ L = e ( 6.33 ) 另外两个导数使用链式法则:
∂ L ∂ a = ∂ L ∂ e ∂ e ∂ a ∂ L ∂ b = ∂ L ∂ e ∂ e ∂ d ∂ d ∂ b (6.34) \begin{array}{l}\frac{\partial L}{\partial a}=\frac{\partial L}{\partial e}\frac{\partial e}{\partial a}\\\frac{\partial L}{\partial b}=\frac{\partial L}{\partial e}\frac{\partial e}{\partial d}\frac{\partial d}{\partial b}\end{array}\tag{6.34} ∂ a ∂ L = ∂ e ∂ L ∂ a ∂ e ∂ b ∂ L = ∂ e ∂ L ∂ d ∂ e ∂ b ∂ d ( 6.34 ) 图 6.16 每个节点(如 e e e )接收上游梯度,与局部梯度相乘,利用链式法则计算传给前一节点的下游梯度。多输入节点会有多个局部梯度。
所需中间导数为:
L = c e : ∂ L ∂ e = c , ∂ L ∂ c = e e = a + d : ∂ e ∂ a = 1 , ∂ e ∂ d = 1 d = 2 b : ∂ d ∂ b = 2 \begin{array}{cc}L=ce:&\frac{\partial L}{\partial e}=c,\quad\frac{\partial L}{\partial c}=e\\e=a+d:&\frac{\partial e}{\partial a}=1,\quad\frac{\partial e}{\partial d}=1\\d=2b:&\frac{\partial d}{\partial b}=2\end{array} L = ce : e = a + d : d = 2 b : ∂ e ∂ L = c , ∂ c ∂ L = e ∂ a ∂ e = 1 , ∂ d ∂ e = 1 ∂ b ∂ d = 2 反向传递从右向左沿每条边计算偏导。先从 L L L 节点计算 ∂ L ∂ e \frac{\partial L}{\partial e} ∂ e ∂ L 和 ∂ L ∂ c \frac{\partial L}{\partial c} ∂ c ∂ L ;到节点 e e e 后,把上游梯度 ∂ L ∂ e \frac{\partial L}{\partial e} ∂ e ∂ L 与局部梯度 ∂ e ∂ d \frac{\partial e}{\partial d} ∂ d ∂ e 相乘,得到传回 d d d 的 ∂ L ∂ d \frac{\partial L}{\partial d} ∂ d ∂ L ,依此类推,直到所有输入变量都标注完毕。前向传递已计算并保存 d , e d,e d , e 等中间变量的值,可以直接用于求这些导数。
图 6.17 函数 L ( a , b , c ) = c ( a + 2 b ) L(a,b,c)=c(a+2b) L ( a , b , c ) = c ( a + 2 b ) 的计算图,展示反向传递计算 ∂ L ∂ a , ∂ L ∂ b , ∂ L ∂ c \frac{\partial L}{\partial a},\frac{\partial L}{\partial b},\frac{\partial L}{\partial c} ∂ a ∂ L , ∂ b ∂ L , ∂ c ∂ L 。
神经网络的反向微分 ¶ 真实神经网络的计算图复杂得多。图 6.18 展示一个两层网络的样例计算图,其中 n 0 = 2 , n 1 = 2 , n 2 = 1 n_0=2,n_1=2,n_2=1 n 0 = 2 , n 1 = 2 , n 2 = 1 。为简单起见,假设进行二元分类并使用 sigmoid 输出。计算函数为:
z [ 1 ] = W [ 1 ] x + b [ 1 ] a [ 1 ] = ReLU ( z [ 1 ] ) z [ 2 ] = W [ 2 ] a [ 1 ] + b [ 2 ] a [ 2 ] = σ ( z [ 2 ] ) y ^ = a [ 2 ] (6.35) \begin{array}{rcl}\mathbf z^{[1]}&=&\mathbf W^{[1]}\mathbf x+\mathbf b^{[1]}\\\mathbf a^{[1]}&=&\operatorname{ReLU}(\mathbf z^{[1]})\\z^{[2]}&=&\mathbf W^{[2]}\mathbf a^{[1]}+b^{[2]}\\a^{[2]}&=&\sigma(z^{[2]})\\\hat y&=&a^{[2]}\end{array}\tag{6.35} z [ 1 ] a [ 1 ] z [ 2 ] a [ 2 ] y ^ = = = = = W [ 1 ] x + b [ 1 ] ReLU ( z [ 1 ] ) W [ 2 ] a [ 1 ] + b [ 2 ] σ ( z [ 2 ] ) a [ 2 ] ( 6.35 ) 反向传递还需计算损失。二元 sigmoid 输出的损失为:
L C E ( y ^ , y ) = − [ y log y ^ + ( 1 − y ) log ( 1 − y ^ ) ] (6.36) L_{CE}(\hat y,y)=-[y\log\hat y+(1-y)\log(1-\hat y)]\tag{6.36} L CE ( y ^ , y ) = − [ y log y ^ + ( 1 − y ) log ( 1 − y ^ )] ( 6.36 ) 由于 y ^ = a [ 2 ] \hat y=a^{[2]} y ^ = a [ 2 ] :
L C E ( a [ 2 ] , y ) = − [ y log a [ 2 ] + ( 1 − y ) log ( 1 − a [ 2 ] ) ] (6.37) L_{CE}(a^{[2]},y)=-[y\log a^{[2]}+(1-y)\log(1-a^{[2]})]\tag{6.37} L CE ( a [ 2 ] , y ) = − [ y log a [ 2 ] + ( 1 − y ) log ( 1 − a [ 2 ] )] ( 6.37 ) 图 6.18 一个简单两层神经网络的样例计算图,包含两个输入单元和两个隐藏单元。为避免节点中的方程过长,图中只标出所计算的函数和结果变量。例如,w 11 [ 1 ] w_{11}^{[1]} w 11 [ 1 ] 右侧的乘号表示它要与 x 1 x_1 x 1 相乘,z [ 1 ] = + z^{[1]}=+ z [ 1 ] = + 节点表示对流入的两个乘积和偏置项求和。青色表示需要更新、因而需要损失偏导数的权重。
进行反向传递需要图中所有函数的导数。sigmoid 的导数为:
d σ ( z ) d z = σ ( z ) ( 1 − σ ( z ) ) (6.38) \frac{d\sigma(z)}{dz}=\sigma(z)(1-\sigma(z))\tag{6.38} d z d σ ( z ) = σ ( z ) ( 1 − σ ( z )) ( 6.38 ) tanh 的导数为:
d tanh ( z ) d z = 1 − tanh 2 ( z ) (6.39) \frac{d\tanh(z)}{dz}=1-\tanh^2(z)\tag{6.39} d z d tanh ( z ) = 1 − tanh 2 ( z ) ( 6.39 ) ReLU 的导数为:
d ReLU ( z ) d z = { 0 z < 0 1 z ≥ 0 (6.40) \frac{d\operatorname{ReLU}(z)}{dz}=\left\{\begin{array}{ll}0&z<0\\1&z\ge0\end{array}\right.\tag{6.40} d z d ReLU ( z ) = { 0 1 z < 0 z ≥ 0 ( 6.40 ) 下面开始计算损失 L L L 对 z = z [ 2 ] z=z^{[2]} z = z [ 2 ] 的导数,其余留作练习。由链式法则:
∂ L ∂ z = ∂ L ∂ a [ 2 ] ∂ a [ 2 ] ∂ z (6.41) \frac{\partial L}{\partial z}=\frac{\partial L}{\partial a^{[2]}}\frac{\partial a^{[2]}}{\partial z}\tag{6.41} ∂ z ∂ L = ∂ a [ 2 ] ∂ L ∂ z ∂ a [ 2 ] ( 6.41 ) 先对式 6.37 关于 a [ 2 ] a^{[2]} a [ 2 ] 求导:
∂ L ∂ a [ 2 ] = − [ y 1 a [ 2 ] + ( 1 − y ) 1 1 − a [ 2 ] ( − 1 ) ] = − ( y a [ 2 ] + y − 1 1 − a [ 2 ] ) (6.42) \begin{array}{rcl}\frac{\partial L}{\partial a^{[2]}}&=&-\left[y\frac1{a^{[2]}}+(1-y)\frac1{1-a^{[2]}}(-1)\right]\\&=&-\left(\frac y{a^{[2]}}+\frac{y-1}{1-a^{[2]}}\right)\end{array}\tag{6.42} ∂ a [ 2 ] ∂ L = = − [ y a [ 2 ] 1 + ( 1 − y ) 1 − a [ 2 ] 1 ( − 1 ) ] − ( a [ 2 ] y + 1 − a [ 2 ] y − 1 ) ( 6.42 ) 再由 sigmoid 的导数:
∂ a [ 2 ] ∂ z = a [ 2 ] ( 1 − a [ 2 ] ) \frac{\partial a^{[2]}}{\partial z}=a^{[2]}(1-a^{[2]}) ∂ z ∂ a [ 2 ] = a [ 2 ] ( 1 − a [ 2 ] ) 最后应用链式法则:
∂ L ∂ z = ∂ L ∂ a [ 2 ] ∂ a [ 2 ] ∂ z = − ( y a [ 2 ] + y − 1 1 − a [ 2 ] ) a [ 2 ] ( 1 − a [ 2 ] ) = a [ 2 ] − y (6.43) \begin{array}{rcl}\frac{\partial L}{\partial z}&=&\frac{\partial L}{\partial a^{[2]}}\frac{\partial a^{[2]}}{\partial z}\\&=&-\left(\frac y{a^{[2]}}+\frac{y-1}{1-a^{[2]}}\right)a^{[2]}(1-a^{[2]})\\&=&a^{[2]}-y\end{array}\tag{6.43} ∂ z ∂ L = = = ∂ a [ 2 ] ∂ L ∂ z ∂ a [ 2 ] − ( a [ 2 ] y + 1 − a [ 2 ] y − 1 ) a [ 2 ] ( 1 − a [ 2 ] ) a [ 2 ] − y ( 6.43 ) 继续把梯度依次传过 b 1 [ 2 ] b_1^{[2]} b 1 [ 2 ] 、两个乘积节点以及更早节点,求得所有青色节点的梯度,留作读者练习。
6.6.5 学习的更多细节 ¶ 神经网络优化是非凸优化问题,比逻辑回归更复杂,因此成功训练涉及许多最佳实践。
逻辑回归可以把所有权重和偏置初始化为 0;神经网络则必须用较小随机数初始化权重。将输入值归一化为均值 0、方差 1 也有帮助。
防止过拟合会采用各种正则化,其中最重要的方法之一是 dropout :训练期间随机丢弃网络中的一些单元及其连接(Hinton et al., 2012;Srivastava et al., 2014)。每次参数更新时——使用小批量梯度下降时就是每个小批量——选择概率 p p p ,以概率 p p p 把每个单元的输出替换为 0,并重新归一化该层其余输出。因此 p p p 是算法的超参数。
超参数调节也很重要。神经网络参数是权重 W \mathbf W W 和偏置 b \mathbf b b ,由梯度下降学习;超参数由算法设计者选择,在开发集上调优,而非通过训练集上的梯度下降学得。超参数包括学习率 η \eta η 、小批量大小、模型架构(层数、每层隐藏节点数、激活函数)、正则化方式等。梯度下降本身也有许多变体,如 Adam(Kingma and Ba, 2015)。
最后,现代神经网络大都基于计算图框架构建,以便自然地计算梯度,并在基于向量的 GPU(图形处理器)上并行化。PyTorch(Paszke et al., 2017)和 TensorFlow(Abadi et al., 2015)是最流行的两个框架。更多细节可参见神经网络教材,本章末尾列出了一些建议。