9.4 神经网络的参数训练 311
9.4 神经网络的参数训练
简单来说,神经网络可以被看作是由变量和函数组成的表达式,例如:y = x +b、
y = ReLU(xW + b)、y = Sigmoid(ReLU(xW
[1]
+ b
[1]
)W
[2]
+ b
[2]
) 等等,其中的 x 和 y 作
为输入和输出向量,W、b 等其他变量作为模型参数(Model Parameters)。确定了函
数表达式和模型参数,也就确定了神经网络模型。通常,表达式的形式需要系统开
发者设计,而模型参数的数量有时会非常巨大,因此需要自动学习,这个过程也被
称为模型学习或训练。为了实现这个目标,通常会准备一定量的带有标准答案的数
据,称之为有标注数据。这些数据会用于对模型参数的学习,这也对应了统计模型
中的参数估计过程。在机器学习中,一般把这种使用有标注数据进行统计模型参数
训练的过程称为有指导的训练或有监督的训练(Supervised Training)。在本章中,如
果没有特殊说明,模型训练都是指有监督的训练。那么神经网络内部是怎样利用有
标注数据对参数进行训练的呢?
为了回答这个问题,可以把模型参数的学习过程看作是一个优化问题,即找到
一组参数,使得模型达到某种最优的状态。这个问题又可以被转化为两个新的问题:
• 优化的目标是什么?
• 如何调整参数以达到优化目标?
下面会围绕这两个问题对神经网络的参数学习方法展开介绍。
9.4.1 损失函数
在神经网络的有监督学习中,训练模型的数据是由输入和正确答案所组成的样
本构成的。假设有多个输入样本 {x
[1]
..., x
[n]
},每一个 x
[i]
都对应一个正确答案 y
[i]
,
{x
[i]
,y
[i]
} 就构成一个优化神经网络的训练数据集(Training Data Set)。对于一个神经
网络模型 y = f(x), 每个 x
[i]
也会有一个输出
ˆ
y
[i]
。如果可以度量正确答案 y
[i]
和神经
网络输出
ˆ
y
[i]
之间的偏差,进而通过调整网络参数减小这种偏差,就可以得到更好的
模型。
答案 y
[i]
预测
ˆ
y
[i]
偏差 |y
[i]
−
ˆ
y
[i]
|
图 9.29 正确答案与神经网络输出之间的偏差
通常,可以通过设计损失函数(Loss Function)来度量正确答案 y
[
i
]
和神经网络输
出
ˆ
y
[
i
]
之间的偏差。而这个损失函数往往充当训练的目标函数(Objective Function),
神经网络训练就是通过不断调整神经网络内部的参数而使损失函数最小化。图9.29展
示了一个绝对值损失函数的实例。
312 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
这里用 Loss(y
[i]
,
ˆ
y
[i]
) 表示网络输出
ˆ
y
[i]
相对于答案 y
[i]
的损失,简记为 L。表9.3是
几种常见损失函数的定义。需要注意的是,没有一种损失函数可以适用于所有的问
题。损失函数的选择取决于许多因素,包括:数据中是否有离群点、模型结构的选
择、是否易于找到函数的导数以及预测结果的置信度等。对于相同的神经网络,不
同的损失函数会对训练得到的模型产生不同的影响。对于新的问题,如果无法找到
已有的、适合于该问题的损失函数,研究人员也可以自定义损失函数。因此设计新
的损失函数也是神经网络中有趣的研究方向。
表 9.3 常见的损失函数
名称 定义 应用
0-1 损失 L =
(
0 y
[i]
=
ˆ
y
[i]
1 y
[i]
̸=
ˆ
y
[i]
感知机
Hinge 损失 L = max(0,1 −y
[i]
·
ˆ
y
[i]
) SVM
绝对值损失 L = |y
[i]
−
ˆ
y
[i]
| 回归
Logistic 损失 L = log(1 + y
[i]
·
ˆ
y
[i]
) 回归
平方损失 L = (y
[i]
−
ˆ
y
[i]
)
2
回归
指数损失 L = exp(−y
[i]
·
ˆ
y
[i]
) AdaBoost
交叉熵损失 L = −
P
k
y
[i]
k
logˆy
[i]
k
多分类
其中,y
[i]
k
表示 y
[i]
的第 k 维
在实际系统开发中,损失函数中除了损失项(即用来度量正确答案 y
[i]
和神经网
络输出
ˆ
y
[i]
之间的偏差的部分)之外,还可以包括正则项,比如 L1 正则和 L2 正则。
设置正则项本质上是要加入一些偏置,使模型在优化的过程中偏向某个方向多一些。
关于正则项的内容将在9.4.5节介绍。
9.4.2 基于梯度的参数优化
对于第 i 个样本 (x
[i]
,y
[i]
),把损失函数 L(y
[i]
,
ˆ
y
[i]
) 看作是参数 θ
θ
θ 的函数
5
,因为
模型输出
ˆ
y
[i]
是由输入 x
[i]
和模型参数 θ
θ
θ 决定,因此也把损失函数写为 L(x
[i]
,y
[i]
;θ
θ
θ)。
下式描述了参数学习的过程:
b
θ
θ
θ = argmin
θ
θ
θ
1
n
n
X
i=1
L(x
[i]
,y
[i]
;θ
θ
θ) (9.37)
其中,
b
θ
θ
θ 表示在训练数据上使损失的平均值达到最小的参数,n 为训练数据总量。
1
n
n
P
i=1
L(x
[i]
,y
[i]
;θ
θ
θ) 也被称作代价函数(Cost Function),它是损失函数均值期望的估
5
为了简化描述,可以用 θ
θ
θ 表示神经网络中的所有参数,包括各层的权重矩阵 W
[1]
...W
[n]
和偏置向
量 b
[1]
...b
[n]
等。
9.4 神经网络的参数训练 313
计,记为 J(θ
θ
θ)。
参数优化的核心问题是:找到使代价函数 J(θ
θ
θ) 达到最小的 θ
θ
θ。然而 J(θ
θ
θ) 可能会
包含大量的参数,比如,基于神经网络的机器翻译模型的参数量可能会超过一亿个。
这时不可能用手动方法进行调参。为了实现高效的参数优化,比较常用的手段是使
用梯度下降法(The Gradient Descent Method)。
1. 梯度下降
梯度下降法是一种常用的优化方法,非常适用于目标函数可微分的问题。它的
基本思想是:给定函数上的第一个点,找到使函数值变化最大的方向,然后前进一
“步”,这样模型就可以朝着更大(或更小)的函数值以最快的速度移动
6
。具体来说,
梯度下降通过迭代更新参数 θ
θ
θ,不断沿着梯度的反方向让参数 θ
θ
θ 朝着损失函数更小
的方向移动:如果 J(θ
θ
θ) 对 θ
θ
θ 可微分,则
∂J(θ
θ
θ)
∂θ
θ
θ
将指向 J(θ
θ
θ) 在 θ
θ
θ 处变化最大的方向,
这里将其称之为梯度方向。θ
θ
θ 沿着梯度方向更新,新的 θ
θ
θ 可以使函数更接近极值,其
过程如图9.30所示
7
。
−1
0
1
2
3
0
2
4
−0.1
-
∂J (θ
θ
θ)
∂θ
θ
θ
θ
θ
θ
[1]
θ
θ
θ
[2]
J(θ
θ
θ )
0
−0.1
图 9.30 函数上一个点沿着不同方向移动的示例
应用梯度下降算法时,首先需要初始化参数 θ
θ
θ。一般情况下深度学习中的参数应
该初始化为一个不太大的随机数。一旦初始化 θ
θ
θ 后,就开始对模型进行不断的更新,
参数更新的规则(Parameter Update Rule)如下:
θ
θ
θ
t+1
= θ
θ
θ
t
−α ·
∂J(θ
θ
θ)
∂θ
θ
θ
(9.38)
其中 t 表示更新的步数,α 是一个超参数,被称作学习率(Learning Rate),表示更
新步幅的大小。α 的设置需要根据任务进行调整。
从优化的角度看,梯度下降是一种典型的基于梯度的方法(The Gradient-based
Method),属于基于一阶导数的方法。其他类似的方法还有牛顿法、共轭方向法、拟
牛顿法等。在具体实现时,公式(9.38)可以有以下不同的形式。
6
梯度下降的一种实现是最速下降(Steepest Descent)。该方法的每一步移动都选取合适的步长,进而
使目标函数能得到最大程度的增长(或下降)。
7
图中的 θ
θ
θ
[1]
和 θ
θ
θ
[2]
分别是参数 θ
θ
θ 的不同变化方向
314 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
1)批量梯度下降(Batch Gradient Descent)
批量梯度下降是梯度下降法中最原始的形式,这种梯度下降法在每一次迭代时
使用所有的样本进行参数更新。参数优化的目标函数如下:
J(θ
θ
θ) =
1
n
n
X
i=1
L(x
[i]
,y
[i]
;θ
θ
θ) (9.39)
公式(9.39)是公式(9.38)的严格实现,也就是将全部训练样本的平均损失作为目
标函数。由全数据集确定的方向能够更好地代表样本总体,从而朝着模型在数据上
整体优化所在的方向更新参数。
不过,这种方法的缺点也十分明显,因为要在全部训练数据上最小化损失,每
一次参数更新都需要计算在所有样本上的损失。在使用海量数据进行训练的情况下,
这种计算是非常消耗时间的。当训练数据规模很大时,很少使用这种方法。
2)随机梯度下降(Stochastic Gradient Descent)
随机梯度下降(简称 SGD)不同于批量梯度下降,每次迭代只使用一个样本对
参数进行更新。SGD 的目标函数如下:
J(θ
θ
θ) = L(x
[i]
,y
[i]
;θ
θ
θ) (9.40)
由于每次只随机选取一个样本 (x
[i]
,y
[i]
) 进行优化,这样更新的计算代价低,参数更
新的速度大大加快,而且也适用于利用少量样本进行在线学习的情况
8
。
因为随机梯度下降算法每次优化的只是某一个样本上的损失,所以它的问题也
非常明显:单个样本上的损失无法代表在全部样本上的损失,因此参数更新的效率
低,方法收敛速度极慢。即使在目标函数为强凸函数的情况下,SGD 仍旧无法做到
线性收敛。
3)小批量梯度下降(Mini-batch Gradient Descent)
为了综合批量梯度下降和随机梯度下降的优缺点,在实际应用中一般采用这两
个算法的折中
——
小批量梯度下降。其思想是:每次迭代计算一小部分训练数据
的损失函数,并对参数进行更新。这一小部分数据被称为一个批次(mini-batch 或者
batch
)。小批量梯度下降的参数优化的目标函数如下:
J(θ
θ
θ) =
1
m
j+m−1
X
i=j
L(x
[i]
,y
[i]
;θ
θ
θ) (9.41)
8
比如,训练数据不是一次给定的,而是随着模型的使用不断追加的。这时,需要不断地用新的训练
样本更新模型,这种模式也被称作在线学习(Online Learning)。
9.4 神经网络的参数训练 315
其中,m 表示一个批次中的样本的数量,j 表示这个批次在全体训练数据的起始位
置。这种方法可以更充分的利用 GPU 设备,因为批次中的样本可以一起计算。而且
每次使用多个样本可以大大减小使模型收敛所需要的参数更新次数。但是需要注意
的是批次大小的选择对模型的最终性能是存在一定影响的。
2. 梯度获取
梯度下降算法的一个核心是要得到目标函数相对于参数的梯度。下面将介绍三
种常见的求梯度方法:数值微分、符号微分和自动微分,深度学习实现过程中多是
采用自动微分方法计算梯度
[412]
。
1)数值微分(Numerical Dierentiation)
数学上,梯度的求解其实就是求函数偏导的问题。导数是用极限来定义的,如
公式(9.42)所示:
∂L(θ
θ
θ)
∂θ
θ
θ
= lim
∆θ
θ
θ→0
L(θ
θ
θ + ∆θ
θ
θ) −L(θ
θ
θ −∆θ
θ
θ)
2∆θ
θ
θ
(9.42)
其中,θ
θ
θ 表示参数的一个很小的变化值。公式(9.42)也被称作导数的双边定义。如果
一个函数是初等函数,可以用求导法则来求得其导函数。如果不知道函数导数的解
析式,则必须利用数值方法来求解该函数在某个点上的导数,这种方法就是数值微
分。
数值微分根据导数的原始定义完成,根据公式可知,要得到损失函数在某个参
数状态 θ
θ
θ 下的梯度,可以将 θ
θ
θ 增大或减小一点(∆θ
θ
θ),例如,取 |∆θ
θ
θ| = 0.0001,之
后观测损失函数的变化与 ∆θ
θ
θ 的比值。∆θ
θ
θ 的取值越小计算的结果越接近导数的真实
值,但是对计算的精度要求越高。
这种求梯度的方法很简单,但是计算量很大,求解速度非常慢,而且这种方法
会造成截断误差(Truncation Error)和舍入误差(Round-off Error)。在网络比较复杂、
参数量稍微有点大的模型上一般不会使用这种方法。
截断误差和舍入误差是如何造成的呢?数值微分方法求梯度时,需用极限或无
穷过程来求得。然而计算机需要将求解过程化为一系列有限的算术运算和逻辑运算。
这样就要对某种无穷过程进行“截断”,即仅保留无穷过程的前段有限序列而舍弃它
的后段。这就带来截断误差;舍入误差,是指运算得到的近似值和精确值之间的差
异。由于数值微分方法计算复杂函数的梯度问题时,经过无数次的近似,每一次近
似都产生了舍入误差,在这样的情况下,误差会随着运算次数增加而积累得很大,最
终得出没有意义的运算结果。实际上,截断误差和舍入误差在训练复杂神经网络中,
特别是使用低精度计算时,也会出现,因此是实际系统研发中需要注意的问题。
尽管数值微分不适用于大模型中的梯度求解,但是由于其非常简单,因此经常
被用于检验其他梯度计算方法的正确性。比如在实现反向传播的时候(详见9.4.6节),
可以检验求导是否正确(Gradient Check),这个过程就是利用数值微分实现的。
316 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
2)符号微分(Symbolic Dierentiation)
顾名思义,符号微分就是通过建立符号表达式求解微分的方法:借助符号表达式
和求导公式,推导出目标函数关于自变量的微分表达式,最后再带入具体数值得到
微分结果。例如,对于表达式 L( θ
θ
θ) = xθ
θ
θ +2θ
θ
θ
2
,可以手动推导出微分表达式
∂L(θ
θ
θ)
∂θ
θ
θ
=
x + 4θ
θ
θ,最后将具体数值 x = (
2 −3
) 和 θ
θ
θ = (
−1 1
) 带入后,得到微分结果
∂L(θ
θ
θ)
∂θ
θ
θ
= (
2 −3
) + 4(
−1 1
) = (
−2 1
)。
使用这种求梯度的方法,要求必须将目标函数转化成一种完整的数学表达式,这
个过程中存在表达式膨胀(Expression Swell)的问题,很容易导致符号微分求解的表
达式急速“膨胀”,大大增加系统存储和处理表达式的负担。关于这个问题的一个实
例请看表9.4。在深层的神经网络中,神经元数量和参数量极大,损失函数的表达式
会非常冗长,不易存储和管理,而且,仅仅写出损失函数的微分表达式就是一个很
庞大的工作量。从另一方面来说,这里真正需要的是微分的结果值,而不是微分表
达式,推导微分表达式仅仅是求解过程中的中间产物。
表 9.4 符号微分的表达式随函数的规模增加而膨胀
函数 微分表达式 化简的微分表达式
x 1 1
x ·(x + 1) (x + 1) + x 2x + 1
x ·(x + 1)· (x + 1) ·(x
2
+ x + 1) 4x
3
+ 6x
2
(x
2
+ x + 1) +x ·(x
2
+ x + 1) +4x + 1
+x ·(x + 1) ·(2 x + 1)
(x
2
+ x)· (2x+ 1) ·(x
2
+ x + 1)· 8x
7
+ 28x
6
(x
2
+ x + 1)· (x
4
+ 2x
3
+ 2x
2
+ x + 1) +48x
5
+ 50x
4
(x
4
+ 2x
3
+(2x + 1) ·(x
2
+ x)· +36x
3
+ 18x
2
+2x
2
+ x + 1) (x
4
+ 2x
3
+ 2x
2
+ x + 1) +6x + 1
+(x
2
+ x) ·(x
2
+ x + 1)·
(4x
3
+ 6x
2
+ 4x + 1)
3)自动微分(Automatic Dierentiation)
自动微分是一种介于数值微分和符号微分的方法:将符号微分应用于最基本的
算子,如常数、幂函数、指数函数、对数函数、三角函数等,然后代入数值,保留中
间结果,最后再应用于整个函数。通过这种方式,将复杂的微分变成了简单的步骤,
这些步骤完全自动化,而且容易进行存储和计算。
由于它只对基本函数或常数运用符号微分法则,所以它非常适合嵌入编程语言
的循环条件等结构中,形成一种程序化的微分过程。在具体实现时,自动微分往往
被当做是一种基于图的计算,相关的理论和技术方法相对成熟,因此是深度学习中
使用最广泛的一种方法。不同于一般的编程模式,图计算先生成计算图,然后按照
9.4 神经网络的参数训练 317
计算图执行计算过程。
自动微分可以用一种反向模式(Reverse Mode/Backward Mode)即反向传播思想
进行描述
[412]
。令 h
i
是神经网络的计算图中第 i 个节点的输出。反向模式的自动微分
是要计算:
¯
h
i
=
∂L
∂h
i
(9.43)
这里,
¯
h
i
表示损失函数 L 相对于 h
i
的梯度信息,它会被保存在节点 i 处。为了计算
¯
h
i
,需要从网络的输出反向计算每一个节点处的梯度。具体实现时,这个过程由一个
包括前向计算和反向计算的两阶段方法实现。
首先,从神经网络的输入,逐层计算每层网络的输出值。如图9.31所示,第 i 层
的输出 h
i
作为第 i + 1 层的输入,数据流在神经网络内部逐层传递。
输入 输出
¬
®
¯
°
± ²
前向:层 i 的输出 h
i
图 9.31 前向计算示意图
前向计算实际上就是网络构建的过程,所有的计算都会被转化为计算图上的节
点,前向计算和反向计算都依赖计算图来完成。构建计算图有以下两种实现方式:
• 动态图:前向计算与计算图的搭建同时进行,函数表达式写完即能得到前向计
算的结果,有着灵活、易于调试的优点。
• 静态图:先搭建计算图,后执行运算,函数表达式完成后,并不能得到前向计
算结果,需要显性调用一个 Forward 函数。但是计算图可以进行深度优化,执
行效率较高。
对于反向计算的实现,一般从神经网络的输出开始,逆向逐层计算每层网络输
入所对应的微分结果。如图9.32所示,在第 i 层计算此处的梯度
∂L
∂h
i
,并将微分值向
前一层传递,根据链式法则继续计算梯度。
输入 输出
²
±
°
¯
®
¬
反向:h
i
处的梯度
∂L
∂h
i
图 9.32 反向计算示意图
318 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
反向计算也是深度学习中反向传播方法的基础。其实现的内部细节将在9.4.6节
详细阐述,所以在这里不再赘述。
3. 基于梯度的方法的变种和改进
参数优化通常基于梯度下降算法,即在每个更新步骤 t,沿梯度反方向更新参数,
该过程如下:
θ
θ
θ
t+1
= θ
θ
θ
t
−α ·
∂J(θ
θ
θ
t
)
∂θ
θ
θ
t
(9.44)
其中,α 是一个超参数,表示更新步幅的大小,称作学习率。当然,这是一种最基本
的梯度下降法。如果函数的形状非均向,比如呈延伸状,搜索最优点的路径就会非
常低效,因为这时梯度的方向并没有指向最小值的方向,并且随着参数的更新,梯
度方向往往呈锯齿状,这将是一条相当低效的路径;此外这种梯度下降算法并不是
总能到达最优点,而是在其附近徘徊;还有一个最令人苦恼的问题
——
设置学习率,
如果学习率设置的比较小,会导致训练收敛速度慢,如果学习率设置的比较大,会
导致训练过程中因为优化幅度过大而频频跳过最优点。我们希望网络在优化的时候
损失函数有一个很好的收敛速度同时又不至于摆动幅度太大。
针对以上问题,很多学者尝试对梯度下降法做出改进,如 Momentum
[413]
, Ada-
Grad
[414]
, Adadelta
[415]
, RMSProp
[416]
, Adam
[417]
, AdaMax
[417]
, Nadam
[418]
, AMSGrad
[419]
等
等,在这里将介绍 Momentum、AdaGrad、RMSProp、Adam 这 4 种方法。
1)Momentum
Momentum 梯度下降算法的参数更新方式如公式(9.45)和(9.46)所示
9
:
v
t
= βv
t−1
+ (1 −β)
∂J
∂θ
t
(9.45)
θ
t+1
= θ
t
−αv
t
(9.46)
该算法引入了一个“动量”的理念
[413]
,它是基于梯度的移动指数加权平均。公
式中的 v
t
是损失函数在前 t −1 次更新中累积的梯度动量,β 是梯度累积的一个指
数,这里一般设置值为 0.9。所以 Momentum 梯度下降算法的主要思想就是对网络的
参数进行平滑处理,让梯度的摆动幅度变得更小。
这里的“梯度”不再只是现在的损失函数的梯度,而是之前的梯度的加权和。在
原始的梯度下降算法中,如果在某个参数状态下,梯度方向变化特别大,甚至与上一
次参数更新中梯度方向成 90 度夹角,下一次参数更新中梯度方向可能又是一次 90
度的改变,这时参数优化路径将会成“锯齿”状(如图9.33所示),优化效率极慢。而
Momentum 梯度下降算法不会让梯度发生 90 度的变化,而是让梯度慢慢发生改变:
9
在梯度下降算法的几种改进方法的公式中,其更新对象是某个具体参数而非参数矩阵,因此不再
使用加粗样式。
9.4 神经网络的参数训练 319
如果当前的梯度方向与之前的梯度方向相同,在原梯度方向上加速更新参数;如果
当前的梯度方向与之前的梯度方向相反,并不会产生一个急转弯,而是尽量把优化
路径平滑地进行改变。这样做的优点也非常明显,一方面杜绝了“锯齿”状优化路
径的出现,另一方面将优化幅度变得更加平滑,不会导致频频跳过最优点。
•
•
•
•
•
•
•
(a) 梯度下降算法中的 “锯齿” 现象
•
•
•
•
(b)Momentum 梯度下降算法更加 “平滑” 地更新
图 9.33 Momentum 梯度下降 vs 普通梯度下降
2)AdaGrad
在神经网络的学习中,学习率的设置很重要。学习率过小,会导致学习花费过
多时间;反过来,学习率过大,则会导致学习发散,甚至造成模型的“跑偏”。在深
度学习实现过程中,有一种被称为学习率衰减(Decay)的方法,即最初设置较大的
学习率,随着学习的进行,使学习率逐渐减小,这种方法相当于将“全体”参数的学
习率值一起降低。AdaGrad 梯度下降算法进一步发展了这个思想
[414]
。
AdaGrad 会为参数的每个元素适当地调整学习率,与此同时进行学习。其参数更
新方式如公式(9.47)和(9.48)所示:
z
t
= z
t−1
+
∂J
∂θ
t
·
∂J
∂θ
t
(9.47)
θ
t+1
= θ
t
−η
1
√
z
t
·
∂J
∂θ
t
(9.48)
这里新出现了变量 z,它保存了以前的所有梯度值的平方和。如公式(9.48)所示,
320 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
在更新参数时,通过除以
√
z
t
,就可以调整学习的尺度。这意味着,变动较大(被大
幅度更新)的参数的学习率将变小。也就是说,可以按参数的元素进行学习率衰减,
使变动大的参数的学习率逐渐减小。
3)RMSProp
RMSProp 算法是一种自适应学习率的方法
[416]
,它是对 AdaGrad 算法的一种改
进,可以避免 AdaGrad 算法中学习率不断单调下降以至于过早衰减的缺点。
RMSProp 算法沿袭了 Momentum 梯度下降算法中指数加权平均的思路,不过
Momentum 算法中加权平均的对象是梯度(即
∂J
∂θ
),而 RMSProp 算法加权平均的对
象是梯度的平方(即
∂J
∂θ
·
∂J
∂θ
)。RMSProp 算法的参数更新方式如公式(9.49)和(9.50)所
示:
z
t
= γz
t−1
+ (1 −γ)
∂J
∂θ
t
·
∂J
∂θ
t
(9.49)
θ
t+1
= θ
t
−
η
√
z
t
+ ϵ
·
∂J
∂θ
t
(9.50)
公式中的 ϵ 是为了维持数值稳定性而添加的常数,一般可设为 10
−8
。和 AdaGrad
的想法类似,模型参数中每个元素都拥有各自的学习率。
RMSProp 与 AdaGrad 相比,学习率的分母部分(即两种梯度下降算法迭代公式
中的 z)的计算由累积方式变成了指数衰减移动平均。于是,每个参数的学习率并不
是呈衰减趋势,而是既可以变小也可以变大,从而避免 AdaGrad 算法中学习率不断
单调下降以至于过早衰减的缺点。
4)Adam
Adam 梯度下降算法是在 RMSProp 算法的基础上进行改进的,可以将其看成是
带有动量项的 RMSProp 算法
[417]
。该算法在自然语言处理领域非常流行。Adam 算法
的参数更新方式如公式(9.51)(9.52)(9.53)所示:
v
t
= βv
t−1
+ (1 −β)
∂J
∂θ
t
(9.51)
z
t
= γz
t−1
+ (1 −γ)
∂J
∂θ
t
·
∂J
∂θ
t
(9.52)
θ
t+1
= θ
t
−
η
√
z
t
+ ϵ
v
t
(9.53)
可以看到 Adam 算法相当于在 RMSProp 算法中引入了 Momentum 算法中的动量项,
这样做使得 Adam 算法兼具了 Momentum 算法和 RMSProp 算法的优点:既能使梯度
更为“平滑”地更新,同时可以为神经网络中的每个参数设置不同的学习率。
需要注意的是包括 Adam 在内的很多参数更新算法中的学习率都需要人为设置。
而且模型学习的效果与学习率的设置关系极大,甚至在研发实际系统时工程师需要
9.4 神经网络的参数训练 321
进行大量的实验,才能得到最佳的模型。
9.4.3 参数更新的并行化策略
当神经网络较为复杂时,模型训练还是需要几天甚至几周的时间。如果希望尽可
能缩短一次学习所需的时间,最直接的想法就是把不同的训练样本分配给多个 GPU
或 CPU,然后在这些设备上同时进行训练,即实现并行化训练。这种方法也被称作
数据并行。具体实现时,有两种常用的并行化策略:(参数)同步更新和(参数)异
步更新。
θ
θ
θ
o
θ
θ
θ
h
参数服务器: θ
θ
θ
new
= θ
θ
θ −α ·
∂J
∂θ
θ
θ
处理器 2
(G2)
(a) 同步更新
处理器 1
(G1)
处理器 3
(G3)
∂J
∂θ
θ
θ
θ
θ
θ
new
Fetch(·)
Push(·)
F
minibatch3
P
F
minibatch2
P
F
minibatch1
P
更新
G3 G2 G1
同步更新
时间轴
θ
θ
θ
o
θ
θ
θ
h
参数服务器: θ
θ
θ
new
= θ
θ
θ −α ·
∂J
∂θ
θ
θ
处理器 2
(G2)
(b) 异步更新
处理器 1
(G1)
处理器 3
(G3)
∂J
∂θ
θ
θ
θ
θ
θ
new
Fetch(·)
Push(·)
F
minibatch3
P
F
minibatch2
P
F
minibatch1
P
更新
更新
G3 G2 G1
异步更新
时间轴
图 9.34 同步更新与异步更新对比
• 同步更新(Synchronous Update)是指所有计算设备完成计算后,统一汇总并更
新参数。当所有设备的反向传播算法完成之后同步更新参数,不会出现单个设
备单独对参数进行更新的情况。这种方法效果稳定,但是效率比较低,在同步
更新时,每一次参数更新都需要所有设备统一开始、统一结束,如果设备的运
行速度不一致,那么每一次参数更新都需要等待最慢的设备结束才能开始。
322 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
• 异步更新(Asynchronous Update)是指每个计算设备可以随时更新参数。不同
设备可以随时读取参数的最新值,然后根据当前参数值和分配的训练样本,各
自执行反向传播过程并独立更新参数。由于设备间不需要相互等待,这种方法
并行度高。但是不同设备读取参数的时间可能不同,会造成不同设备上的参数
不同步,导致这种方法不十分稳定,有可能无法达到较好的训练结果。
图9.34对比了同步更新和异步更新的区别,在这个例子中,使用 4 台设备对一个
两层神经网络中的参数进行更新,其中使用了一个参数服务器(Parameter Server)来
保存最新的参数,不同设备(图中的 G1、G2、G3)可以通过同步或者异步的方式访
问参数服务器。图中的 θ
θ
θ
o
和 θ
θ
θ
h
分别代表输出层和隐藏层的全部参数,操作 Push(·)
表示设备向参数服务器传送梯度,操作 Fetch(·) 表示参数服务器向设备传送更新后的
参数。
此外,在使用多个设备进行并行训练的时候,由于设备间带宽的限制,大量的
数据传输会有较高的延时。对于复杂神经网络来说,设备间参数和梯度传递的时间
消耗也会成为一个不得不考虑的因素。有时候,设备间数据传输的时间甚至比模型
计算的时间都长,大大降低了并行度
[420]
。对于这种问题,可以考虑对数据进行压缩
或者减少传输的次数来缓解问题。
9.4.4 梯度消失、梯度爆炸和稳定性训练
深度学习中随着神经网络层数的增加,导数可能会出现指数级的下降或者指数
级的增加,这种现象分别称为梯度消失(Gradient Vanishing)和梯度爆炸(Gradient
Explosion)。出现这两种现象的本质原因是反向传播过程中链式法则导致梯度矩阵
的多次相乘。这类问题很容易导致训练的不稳定。
1. 易于优化的激活函数
网络训练过程中,如果每层网络的梯度都小于 1,各层梯度的偏导数会与后面层
传递而来的梯度相乘得到本层的梯度,并向前一层传递。该过程循环进行,最后导
致梯度指数级地减小,这就产生了梯度消失现象。这种情况会导致神经网络层数较
浅的部分梯度接近 0。一般来说,产生很小梯度的原因是使用了类似于 Sigmoid 这样
的激活函数,当输入的值过大或者过小的时候这类函数曲线会趋于直线,梯度近似
为零。针对这个问题,主要的解决办法是使用更加易于优化的激活函数,比如,使用
ReLU 代替 Sigmoid 和 Tanh 作为激活函数。
2. 梯度裁剪
网络训练过程中,如果参数的初始值过大,而且每层网络的梯度都大于 1,反向
传播过程中,各层梯度的偏导数都会比较大,会导致梯度指数级地增长直至超出浮
点数表示的范围,这就产生了梯度爆炸现象。如果发生这种情况,模型中离输入近
的部分比离输入远的部分参数更新得更快,使网络变得非常不稳定。在极端情况下,
9.4 神经网络的参数训练 323
模型的参数值变得非常大,甚至于溢出。针对梯度爆炸的问题,常用的解决办法为
梯度裁剪(Gradient Clipping)。
梯度裁剪的思想是设置一个梯度剪切阈值。在更新梯度的时候,如果梯度超过
这个阈值,就将其强制限制在这个范围之内。假设梯度为 g,梯度剪切阈值为 σ,梯
度裁剪过程可描述为下式:
g
′
= min(
σ
∥g∥
,1)g (9.54)
其中,∥·∥ 表示 l
2
范数。梯度裁剪经常被使用在层数较多的模型中,如循环神经网
络。
3. 稳定性训练
为了使神经网络模型训练更加稳定,通常还会考虑其他策略。
• 批量标准化(Batch Normalization)。批量标准化,顾名思义,是以进行学习时
的小批量样本为单位进行标准化
[421]
。具体而言,就是对神经网络隐藏层输出的
每一个维度,沿着批次的方向进行均值为 0、方差为 1 的标准化。在深层神经
网络中,每一层网络都可以使用批量标准化操作。这样使神经网络任意一层的
输入不至于过大或过小,从而防止隐藏层中异常值导致模型状态的巨大改变。
• 层标准化(Layer Normalization)。类似的,层标准化更多是针对自然语言处理
这种序列处理任务
[422]
,它和批量标准化的原理是一样的,只是标准化操作是
在序列上同一层网络的输出结果上进行的,也就是标准化操作沿着序列方向进
行。这种方法可以很好的避免序列上不同位置神经网络输出结果的不可比性。
同时由于标准化后所有的结果都转化到一个可比的范围,使得隐藏层状态可以
在不同层之间进行自由组合。
• 残差网络(Residual Networks)。最初,残差网络是为了解决神经网络持续加深
时的模型退化问题
[423]
,但是残差结构对解决梯度消失和梯度爆炸问题也有所
帮助。有了残差结构,可以很轻松的构建几十甚至上百层的神经网络,而不用
担心层数过深造成的梯度消失问题。残差网络的结构如图9.35所示。图9.35中
右侧的曲线叫做跳接(Skip Connection),通过跳接在激活函数前,将上一层
(或几层)之前的输出与本层计算的输出相加,将求和的结果输入到激活函数
中作为本层的输出。假设残差结构的输入为 x
l
,输出为 x
l+1
,则有
x
l+1
= F (x
l
) + x
l
(9.55)
相比较于简单的多层堆叠的结构,残差网络提供了跨层连接结构。这种结构在
反向传播中有很大的好处,比如,对于一个训练样本,损失函数为 L,x
l
处的
梯度的计算方式如公式(9.56)所示。残差网络可以将后一层的梯度
∂L
∂x
l+1
不经过
任何乘法项直接传递到
∂L
∂x
l
,从而缓解了梯度经过每一层后多次累乘造成的梯
324 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
度消失问题。在第十二章中还会看到,在机器翻译中残差结构可以和层标准化
一起使用,而且这种组合可以取得很好的效果。
∂L
∂x
l
=
∂L
∂x
l+1
·
∂x
l+1
∂x
l
=
∂L
∂x
l+1
·
1 +
∂F (x
l
)
∂x
l
=
∂L
∂x
l+1
+
∂L
∂x
l+1
·
∂F (x
l
)
∂x
l
(9.56)
ReLU
L
函数变换
x
等值传递
F (x)
F (x) + x
x
图 9.35 残差网络的结构
9.4.5 过拟合
理想中,我们总是希望尽可能地拟合输入和输出之间的函数关系,即让模型尽
量模拟训练数据中由输入预测答案的行为。然而,在实际应用中,模型在训练数据
上的表现不一定代表了其在未见数据上的表现。如果模型训练过程中过度拟合训练
数据,最终可能无法对未见数据做出准确的判断,这种现象叫做过拟合(Overfitting)
。随着模型复杂度增加,特别在神经网络变得更深、更宽时,过拟合问题会表现得
更为突出。如果训练数据量较小,而模型又很复杂,可以“完美”地拟合这些数据,
这时过拟合也很容易发生。所以在模型训练时,往往不希望去完美拟合训练数据中
的每一个样本。
正则化(Regularization)是常见的缓解过拟合问题的手段,通过在损失函数中加
上用来刻画模型复杂程度的正则项来惩罚过度复杂的模型,从而避免神经网络过度
学习造成过拟合。引入正则化处理之后目标函数变为 J(θ
θ
θ) + λR(θ
θ
θ),其中 J(θ
θ
θ) 是原
来的代价函数,R(θ
θ
θ) 即为正则项,λ 用来调节正则项对结果影响的程度。
过拟合的模型通常会表现为部分非零参数过多或者参数的值过大。这种参数产
生的原因在于模型需要复杂的参数才能匹配样本中的个别现象甚至噪声。基于此,常
见的正则化方法有 L1 正则化和 L2 正则化,其命名方式是由 R(θ
θ
θ) 的计算形式来决
定的。在
L1
正则化中,
R
(
θ
θ
θ
)
即为参数
θ
θ
θ
的
l
1
范数,即
R
(
θ
θ
θ
) =
∥
θ
θ
θ
∥
1
=
n
P
i=1
|θ
i
|;在 L2
正则化中,R(θ
θ
θ) 即为参数 θ
θ
θ 的 l
2
范数的平方,即 R(θ
θ
θ) = (∥θ
θ
θ∥
2
)
2
=
n
P
i=1
θ
2
i
。L1 正则
9.4 神经网络的参数训练 325
化中的正则项衡量了模型中参数的绝对值大小,倾向于生成值为 0 的参数,从而让
参数变得更加稀疏;而 L2 正则化由于平方的加入,当参数中的某一项小到一定程度,
比如 0.001 的时候,参数的平方结果已经可以忽略不计了,因此 L2 正则化会倾向生
成很小的参数,在这种情况下,即便训练数据中含有少量随机噪音,模型也不太容
易通过增加个别参数的值来对噪声进行过度拟合,即提高了模型的抗扰动能力。
此外,在第十二章即将介绍的 Dropout 和标签平滑方法也可以被看作是一种正
则化操作。它们都可以提高模型在未见数据上的泛化能力。
9.4.6 反向传播
为了获取梯度,最常用的做法是使用自动微分技术,通常通过反向传播来实现。
该方法分为两个计算过程:前向计算和反向计算。前向计算的目的是从输入开始,逐
层计算,得到网络的输出,并记录计算图中每个节点的局部输出。反向计算过程从
输出端反向计算梯度,这个过程可以被看作是一种梯度的“传播”,最终计算图中所
有节点都会得到相应的梯度结果。
这里,首先对反向传播算法中涉及到的符号进行统一说明。图9.36是一个多层神
经网络,其中层 k −1、层 k、层 k + 1 均为神经网络中的隐藏层,层 K 为神经网络
中的输出层。为了化简问题,这里每层网络没有使用偏置项。
层 k − 1
层 k
层 k + 1
层 K(输出)
... ... ... ...
... ... ... ...
输出 输出 输出 输出
第 k 层, 第 i 个神经元
w
k
4,4
图 9.36 多层神经网络实例
下面是一些符号的定义:
• h
k
i
:第 k 层第 i 个神经元的输出;
• h
k
:第 k 层的输出。若第 k 层有 n 个神经元,则:
h
k
= (h
k
1
,h
k
2
,...,h
k
n
) (9.57)
326 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
• w
k
j,i
:第 k −1 层神经元 j 与第 k 层神经元 i 的连接权重;
• W
k
:第 k −1 层与第 k 层的连接权重。若第 k −1 层有 m 个神经元,第 k 层有
n 个神经元,则:
W
k
=
w
k
1,1
w
k
1,2
... w
k
1,n
w
k
2,1
... ... ...
... ... ... ...
w
k
m,1
... ... w
k
m,n
(9.58)
• h
K
:整个网络的输出;
• s
k
:第 k 层的线性变换结果,其计算方式如下:
s
k
= h
k−1
W
k
=
X
h
k−1
j
w
k
j,i
(9.59)
• f
k
:第 k 层的激活函数,h
k
= f
k
(s
k
)。
于是,在神经网络的第 k 层,前向计算过程可以描述为:
h
k
= f
k
(s
k
)
= f
k
(h
k−1
W
k
) (9.60)
1. 输出层的反向传播
反向传播是由输出层开始计算梯度,之后逆向传播到每一层网络,直至到达输
入层。这里首先讨论输出层的反向传播机制。输出层(即第 K 层)可以被描述为公
式(9.61)(9.62):
h
K
= f
K
(s
K
) (9.61)
s
K
= h
K−1
W
K
(9.62)
也就是,输出层(第 K 层)的输入 h
K−1
先经过线性变换右乘 W
K
转换为中间状态
s
K
,之后 s
K
再经过激活函数 f
K
(·) 变为 h
K
,h
K
即为第 K 层(输出层)的输出。最
后,h
K
和标准答案一起计算得到损失函数的值
10
,记为 L。以上过程如图9.37所示。
10
在反向传播算法部分我们以某一个训练样本为例进行讲解,因而这里不再计算代价函数 J,而是损
失函数 L。
9.4 神经网络的参数训练 327
这里将输出层的前向计算过程细化为两个阶段:线性变换阶段和激活函数 + 损失函
数阶段。
h
K−1
s
K
h
K
L
损失
s
K
= h
K−1
W
K
线性变换
h
K
= f
K
(s
K
)
激活函数
输出层
第一阶段:线性变换
第二阶段:激活函数 + 损失函数
图 9.37 输出层的前向计算过程
在前向过程中,计算次序为 h
K−1
→ s
K
→ h
K
→ L。而反向计算中节点访问的
次序与之相反:
• 第一步,获取
∂L
∂h
K
,即计算损失函数 L 关于网络输出结果 h
K
的梯度,并将梯
度向前传递;
• 第二步,获取
∂L
∂s
K
,即计算损失函数 L 关于中间状态 s
K
的梯度,并将梯度向
前传递;
• 第三步,获取
∂L
∂h
K−1
和
∂L
∂W
K
,即计算损失函数 L 关于第 K −1 层输出结果 h
K−1
的梯度,并将梯度向前传递;同时计算损失函数 L 关于第 K 层参数 W
K
的梯
度,并用于参数更新。
对于前两个步骤,如图9.38所示:
s
K
h
K
L
求梯度
∂L
∂h
K
=?
求梯度
∂f
K
(s
K
)
∂s
K
=?
图 9.38 从损失到中间状态的反向传播(输出层)
在第一阶段,计算的目标是得到损失函数 L 关于第 K 层中间状态 s
K
的梯度,
这里令 π
π
π
K
=
∂L
∂s
K
,利用链式法则有:
π
π
π
K
=
∂L
∂s
K
=
∂L
∂h
K
·
∂h
K
∂s
K
=
∂L
∂h
K
·
∂f
K
(s
K
)
∂s
K
(9.63)
其中:
•
∂L
∂h
K
表示损失函数
L
相对网络输出
h
K
的梯度。比如,对于平方损失
L
=
1
2
∥y −h
K
∥
2
,有
∂L
∂h
K
= y −h
K
。计算结束后,将
∂L
∂h
K
向前传递。
328 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
•
∂f
K
(s
K
)
∂s
K
表示激活函数相对于其输入 s
K
的梯度。比如,对于 Sigmoid 函数 f(s) =
1
1+e
−s
,有
∂f (s)
∂s
= f (s)(1 −f(s))
这个过程可以得到 s
K
节点处的梯度 π
π
π
K
=
∂L
∂s
K
,在后续的过程中可以直接使用
其作为前一层提供的梯度计算结果,而不需要从 h
K
节点处重新计算。这也体现了自
动微分与符号微分的差别,对于计算图的每一个阶段,并不需要得到完整的微分表
达式,而是通过前一层提供的梯度,直接计算当前的梯度即可,这样避免了大量的
重复计算。
在得到 π
π
π
K
=
∂L
∂s
K
之后,下一步的目标是:1)计算损失函数 L 相对于第 K −1
层与输出层之间连接权重 W
K
的梯度;2)计算损失函数 L 相对于神经网络第 K −1
层输出结果 h
K−1
的梯度。这部分内容如图9.39所示。
h
K−1
s
K
s
K
= h
K−1
W
K
已经得到:π
π
π
K
=
∂L
∂s
K
∂L
∂W
K
=?,
∂L
∂h
K−1
=?
图 9.39 从中间状态到输入的反向传播(输出层)
具体来说:
• 计算
∂L
∂W
K
:由于 s
K
= h
K−1
W
K
,且损失函数 L 关于 s
K
的梯度 π
π
π
K
=
∂L
∂s
K
已
经得到,于是有
∂L
∂W
K
=
h
K−1
T
π
π
π
K
(9.64)
其中 [·]
T
表示转置操作
11
。
• 计算
∂L
∂h
K−1
:与求解
∂L
∂W
K
类似,可以得到
∂L
∂h
K−1
= π
π
π
K
W
K
T
(9.65)
梯度
∂L
∂h
K−1
需要继续向前一层传递,用于计算网络中间层的梯度。
∂L
∂W
K
会作为
参数 W
K
的梯度计算结果,用于模型参数的更新
12
。
11
如果 h
K− 1
是一个向量,
h
h
K− 1
i
T
表示向量的转置,比如,行向量变成列向量;如果 h
K−1
是一
个高阶张量,
h
h
K−1
i
T
表示沿着张量最后两个方向的转置。
12
W
K
可能会在同一个网络中被多次使用(类似于网络不同部分共享同一个参数),这时需要累加相
关计算节点处得到的
∂L
∂W
K
。
9.4 神经网络的参数训练 329
2. 隐藏层的反向传播
对于第 k 个隐藏层,有:
h
k
= f
k
(s
k
) (9.66)
s
k
= h
k−1
W
k
(9.67)
其中,h
k
、s
k
、h
k−1
、W
k
和分别表示隐藏层的输出、中间状态、隐藏层的输入和参
数矩阵。隐藏层的前向计算过程如图9.40所示,第 k −1 层神经元的输出 h
k−1
经过线
性变换和激活函数后,将计算结果 h
k
向后一层传递。
h
k−1
s
k
h
k
... ...
s
k
= h
k−1
W
k
h
k
= f
k
(s
k
)
图 9.40 隐藏层的前向计算过程
与输出层类似,隐藏层的反向传播也是逐层逆向计算。
• 第一步,获取
∂L
∂s
k
,即计算损失函数 L 关于第 k 层中间状态 s
k
的梯度,并将梯
度向前传递;
• 第二步,获取
∂L
∂h
k−1
和
∂L
∂W
k
,即计算损失函数
L
关于第
k
−
1
层输出结果
h
k−1
的梯度,并将梯度向前传递。同时计算损失函数 L 关于参数 W
k
的梯度,并用
于参数更新。
这两步和输出层的反向传播十分类似。可以利用链式法则得到:
∂L
∂s
k
=
∂L
∂h
k
·
∂h
k
∂s
k
=
∂L
∂h
k
·
∂f
k
(s
k
)
∂s
k
(9.68)
其中
∂L
∂
h
k
表示损失函数 L 相对该隐藏层输出 h
k
的梯度。进一步,由于 s
k
= h
k−1
W
k
,
可以得到
∂L
∂W
k
=
h
h
k−1
i
T
·
∂L
∂s
k
(9.69)
∂L
∂h
k−1
=
∂L
∂s
k
·
h
W
k
i
T
(9.70)
∂L
∂h
k−1
需要继续向第 k −1 隐藏层传递。
∂L
∂W
k
会作为参数的梯度用于参数更新。
图9.41展示了隐藏层反向传播的计算过程。
综合输出层和隐藏层的反向传播方法,可以得到神经网络中任意位置和任意参
数的梯度信息。只需要根据网络的拓扑结构,逆向访问每一个节点,并执行上述反
330 Chapter 9. 人工神经网络和神经语言建模 肖桐 朱靖波
向计算过程。
h
k−1
s
k
h
k
... ...
s
k
= h
k−1
W
k
h
k
= f
k
(s
k
)
反向传播反向传播反向传播
k = k −1 重复上述过程
∂L
∂h
k
∂L
∂s
k
∂L
∂h
k−1
,
∂L
∂W
k
图 9.41 隐藏层的反向传播