10.5 训练及推断 375
10.5 训练及推断
神经机器翻译模型的训练大多使用基于梯度的方法(见第九章),本节将介绍这
种方法训练循环神经网络的应用细节。进一步,会介绍神经机器翻译模型的推断方
法。
10.5.1 训练
在基于梯度的方法中,模型参数可以通过损失函数 L 对参数的梯度进行不断更
新。对于第 step 步参数更新,首先进行神经网络的前向计算,之后进行反向计算,并
得到所有参数的梯度信息,再使用下面的规则进行参数更新:
w
step+1
= w
step
−α ·
∂L (w
step
)
∂w
step
(10.24)
其中,w
step
表示更新前的模型参数,w
step+1
表示更新后的模型参数,L(w
step
) 表示模
型相对于 w
step
的损失,
∂L(w
step
)
∂w
step
表示损失函数的梯度,α 是更新的步长。也就是说,
给定一定量的训练数据,不断执行公式(10.24)的过程。反复使用训练数据,直至模
型参数达到收敛或者损失函数不再变化。通常,把公式的一次执行称为“一步”更
新/训练,把访问完所有样本的训练称为“一轮”训练。
将公式(10.24)应用于神经机器翻译有几个基本问题需要考虑:1)损失函数的选
择;2)参数初始化的策略,也就是如何设置 w
0
;3)优化策略和学习率调整策略;4)
训练加速。下面对这些问题进行讨论。
1. 损失函数
神经机器翻译在目标端的每个位置都会输出一个概率分布,表示这个位置上不
同单词出现的可能性。设计损失函数时,需要知道当前位置输出的分布相比于标准
答案的“差异”。在神经机器翻译中,常用的损失函数是交叉熵损失函数。令
ˆ
y 表示
机器翻译模型输出的分布,y 表示标准答案,则交叉熵损失可以被定义为:
L
ce
(
ˆ
y,y) = −
|V |
X
k=1
ˆ
y[k]log(y[k]) (10.25)
其中 y[k] 和
ˆ
y[k] 分别表示向量 y 和
ˆ
y 的第 k 维,|V | 表示输出向量的维度(等于词表
大小)。假设有 n 个训练样本,模型输出的概率分布为
b
Y = {
ˆ
y
1
,...,
ˆ
y
n
},标准答案的
分布 Y = {y
1
,...,y
n
}。这个训练样本集合上的损失函数可以被定义为:
L(
b
Y,Y) =
n
X
j=1
L
ce
(
ˆ
y
j
,y
j
) (10.26)
公式(10.26)是一种非常通用的损失函数形式,除了交叉熵,也可以使用其他的
376 Chapter 10. 基于循环神经网络的模型 肖桐 朱靖波
损失函数,这时只需要替换 L
ce
(·) 即可。这里使用交叉熵损失函数的好处在于,它非
常容易优化,特别是与 Softmax 组合,其反向传播的实现非常高效。此外,交叉熵损
失(在一定条件下)也对应了极大似然的思想,这种方法在自然语言处理中已经被
证明是非常有效的。
除了交叉熵,很多系统也使用了面向评价的损失函数,比如,直接利用评价指
标 BLEU 定义损失函数
[235]
。不过这类损失函数往往不可微分,因此无法直接获取梯
度。这时可以引入强化学习技术,通过策略梯度等方法进行优化。不过这类方法需
要采样等手段,这里不做重点讨论,相关内容会在第十三章进行介绍。
2. 参数初始化
神经网络的参数主要是各层中的线性变换矩阵和偏置。在训练开始时,需要对
参数进行初始化。但是,由于神经机器翻译的网络结构复杂,因此损失函数往往不
是凸函数,不同初始化会导致不同的优化结果。而且在大量实践中已经发现,神经
机器翻译模型对初始化方式非常敏感,性能优异的系统往往需要特定的初始化方式。
因为 LSTM 是神经机器翻译中常用的一种模型,下面以 LSTM 模型为例(见10.3.2
节),介绍机器翻译模型的初始化方法,这些方法也可以推广到 GRU 等结构。具体
内容如下:
• LSTM 遗忘门偏置初始化为 1,也就是始终选择遗忘记忆 c,这样可以有效防止
初始化时 c 里包含的错误信号传播到后面的时刻。
• 网络中的其他偏置一般都初始化为 0,可以有效防止加入过大或过小的偏置后
使得激活函数的输出跑到“饱和区”,也就是梯度接近 0 的区域,防止训练一
开始就无法跳出局部极小的区域。
• 网络的权重矩阵 w 一般使用 Xavier 参数初始化方法
[472]
,可以有效稳定训练过
程,特别是对于比较“深”的网络。令 d
in
和 d
out
分别表示 w 的输入和输出的
维度大小
5
,则该方法的具体实现如下:
w ∼ U(−
r
6
d
in
+ d
out
,
r
6
d
in
+ d
out
) (10.27)
其中 U(a,b) 表示以 [a,b] 为范围的均匀分布。
3. 优化策略
公式(10.24)展示了最基本的优化策略,也被称为标准的 SGD 优化器。实际上,
训练神经机器翻译模型时,还有非常多的优化器可以选择,在第九章也有详细介绍,
本章介绍的循环神经网络考虑使用 Adam 优化器
[417]
。Adam 通过对梯度的一阶矩估计
(First Moment Estimation)和二阶矩估计(Second Moment Estimation)进行综合考虑,
计算出更新步长。
5
对于变换 y = xw,w 的列数为 d
in
,行数为 d
out
。
10.5 训练及推断 377
通常,Adam 收敛地比较快,不同任务基本上可以使用同一套配置进行优化,虽
性能不算差,但很难达到最优效果。相反,SGD 虽能通过在不同的数据集上进行调
整,来达到最优的结果,但是收敛速度慢。因此需要根据不同的需求来选择合适的
优化器。若需要快速得到模型的初步结果,选择 Adam 较为合适,若是需要在一个任
务上得到最优的结果,选择 SGD 更为合适。
4. 梯度裁剪
需要注意的是,训练循环神经网络时,反向传播使得网络层之间的梯度相乘。在
网络层数过深时,如果连乘因子小于 1 可能造成梯度指数级的减少,甚至趋近于 0,
导致网络无法优化,也就是梯度消失问题。当连乘因子大于 1 时,可能会导致梯度
的乘积变得异常大,造成梯度爆炸的问题。在这种情况下需要使用“梯度裁剪”来
防止梯度超过阈值。梯度裁剪在第九章已经介绍过,这里简单回顾一下。梯度裁剪
的具体公式如下:
w
′
= w ·
γ
max(γ, ∥w∥
2
)
(10.28)
其中,γ 是手工设定的梯度大小阈值,∥·∥
2
是 l
2
范数,w
′
表示梯度裁剪后的参数。
这个公式的含义在于只要梯度大小超过阈值,就按照阈值与当前梯度大小的比例进
行放缩。
5. 学习率策略
在公式(10.24)中,α 决定了每次参数更新时更新的步幅大小,称之为学习率。学
习率作为基于梯度方法中的重要超参数,它决定目标函数能否收敛到较好的局部最
优点以及收敛的速度。合理的学习率能够使模型快速、稳定地达到较好的状态。但
是,如果学习率太小,收敛过程会很慢;而学习率太大,则模型的状态可能会出现震
荡,很难达到稳定,甚至使模型无法收敛。图10.25 对比了不同学习率对优化过程的
影响。
−4 −3 −2 −1 0 1 2 3 4
0
1
2
3
4
w
L(w)
−4 −3 −2 −1 0 1 2 3 4
0
1
2
3
4
w
L(w)
图 10.25 学习率过小(左)vs 学习率过大(右)
不同优化器需要的学习率不同,比如 Adam 一般使用 0.001 或 0.0001,而 SGD
378 Chapter 10. 基于循环神经网络的模型 肖桐 朱靖波
则在 0.1∼1 之间进行挑选。在梯度下降法中,都是给定的统一的学习率,整个优化
过程中都以确定的步长进行更新。因此无论使用哪个优化器,为了保证训练又快又
好,通常都需要根据当前的更新次数来动态调整学习率的大小。
图10.26展示了一种常用的学习率调整策略。它分为两个阶段:预热阶段和衰减
阶段。模型训练初期梯度通常很大,如果直接使用较大的学习率很容易让模型陷入
局部最优。学习率的预热阶段是指在训练初期使学习率从小到大逐渐增加的阶段,目
的是缓解在初始阶段模型“跑偏”的现象。一般来说,初始学习率太高会使得模型
进入一种损失函数曲面非常不平滑的区域,进而使得模型进入一种混乱状态,后续
的优化过程很难取得很好的效果。一个常用的学习率预热方法是逐渐预热(Gradual
Warmup)。假设预热的更新次数为 N,初始学习率为 α
0
,则预热阶段第 step 次更新
的学习率计算为:
α
t
=
step
N
α
0
, 1 ≤ t ≤ T
′
(10.29)
另一方面,当模型训练逐渐接近收敛的时候,使用太大学习率会很容易让模型在局
部最优解附近震荡,从而错过局部极小,因此需要通过减小学习率来调整更新的步
长,以此来不断地逼近局部最优,这一阶段也称为学习率的衰减阶段。学习率衰减
的方法有很多,比如指数衰减以及余弦衰减等,图10.26右侧下降部分的曲线展示了
分段常数衰减(Piecewise Constant Decay),即每经过 m 次更新,学习率衰减为原来
的 β
m
(β
m
< 1)倍,其中 m 和 β
m
为根据经验设置的超参。
更新次数
学习率
图 10.26 学习率与更新次数的变化关系
6. 并行训练
机器翻译是自然语言处理中很“重”的任务。因为数据量巨大而且模型较为复
杂,模型训练的时间往往很长。比如,使用一千万句的训练数据,性能优异的系统
往往需要几天甚至一周的时间。更大规模的数据会导致训练时间更长。特别是使用
多层网络同时增加模型容量时(比如增加隐藏层宽度时),神经机器翻译的训练会更
加缓慢。对于这个问题,一个思路是从模型训练算法上进行改进。比如前面提到的
10.5 训练及推断 379
Adam 就是一种高效的训练策略。另一种思路是利用多设备进行加速,也称作分布式
训练。
常用的多设备并行化加速方法有数据并行和模型并行,其优缺点的简单对比如
表10.9所示。数据并行是指把同一个批次的不同样本分到不同设备上进行并行计算。
其优点是并行度高,理论上有多大的批次就可以有多少个设备并行计算,但模型体
积不能大于单个设备容量的极限。而模型并行是指把“模型”切分成若干模块后分
配到不同设备上并行计算。其优点是可以对很大的模型进行运算,但只能有限并行,
比如,如果按层对模型进行分割,那么有多少层就需要多少个设备,同时这两种方
法可以一起使用进一步提高神经网络的训练速度。具体来说:
表 10.9 数据并行与模型并行优缺点对比
并行方法 优点 缺点
数据并行 并行度高,理论上有多大的
批次(Batch)就可以有多少
个设备并行计算
模型不能大于单个设备的极
限
模型并行 可以对很大的模型进行运算 只能有限并行,比如有多少
层就有多少个设备
• 数据并行。如果一台设备能完整放下一个神经机器翻译模型,那么数据并行可以
把一个大批次均匀切分成 n 个小批次,然后分发到 n 个设备上并行计算,最后
把结果汇总,相当于把运算时间变为原来的 1/n,数据并行的过程如图10.27所
示。不过,需要注意的是,多设备并行需要在不同设备间传输数据,特别是在
多个 GPU 的情况下,设备间传输的带宽十分有限,设备间传输数据往往会造
成额外的时间消耗
[420]
。通常,数据并行的训练速度无法随着设备数量增加呈线
性增长。不过这个问题也有很多优秀的解决方案,比如采用多个设备的异步训
练,但是这些内容已经超出本章的内容,因此这里不做过多讨论。
3
2
句子 1
batch 大小
句子 2
句子 3
句子 1
0
···
设备 1
0
·· ·
设备 2
0
·· ·
设备 3
图 10.27 数据并行过程
380 Chapter 10. 基于循环神经网络的模型 肖桐 朱靖波
• 模型并行。另一种思路是,把较大的模型分成若干小模型,之后在不同设备上
训练小模型。对于循环神经网络,不同层的网络天然就是一个相对独立的模型,
因此非常适合使用这种方法。比如,对于 l 层的循环神经网络,把每层都看做
一个小模型,然后分发到 l 个设备上并行计算。在序列较长的时候,该方法使
其运算时间变为原来的 1/l。图10.28以三层循环网络为例展示了对句子“你 很
不错 。”进行模型并行的过程。其中,每一层网络都被放到了一个设备上。当
模型根据已经生成的第一个词“你”,并预测下一个词时(图10.28(a)),同层
的下一个时刻的计算和对“你”的第二层的计算就可以同时开展(图10.28(b))。
以此类推,就完成了模型的并行计算。
0
0
0
正在运算的
循环单元
正在使用的
设备 1
空闲的
设备 2
空闲的
设备 3
第 1 层
第 2 层
第 3 层
你
很
不错
。
(a) 第一步
0
0
0
正在运算的
循环单元
正在使用的
设备 1
正在使用的
设备 2
空闲的
设备 3
第 1 层
第 2 层
第 3 层
你
很
不错
。
(b) 第二步
0
0
0
很
正在运算的
循环单元
正在使用的
设备 1
正在使用的
设备 2
正在使用的
设备 3
第 1 层
第 2 层
第 3 层
你
很
不错
。
(c) 第三步
0
0
0
很
不错
正在运算的
循环单元
正在使用的
设备 1
正在使用的
设备 2
正在使用的
设备 3
第 1 层
第 2 层
第 3 层
你
很
不错
。
(d) 第四步
0
0
0
很
不错
。
正在运算的
循环单元
空闲的
设备 1
正在使用的
设备 2
正在使用的
设备 3
第
1
层
第
2
层
第
3
层
你
很
不错
。
(e) 第五步
0
0
0
很
不错
。
⟨
eos
⟩
正在运算的
循环单元
空闲的
设备 1
空闲的
设备 2
正在使用的
设备 3
第
1
层
第
2
层
第
3
层
你
很
不错
。
(f) 第六步
图 10.28 一个三层循环神经网络的模型并行过程
10.5.2 推断
神经机器翻译的推断是一个典型的搜索问题(见第二章)。这个过程是指:利用
已经训练好的模型对新的源语言句子进行翻译的过程。具体来说,首先利用编码器
生成源语言句子的表示,之后利用解码器预测目标语言译文。也就是,对于源语言
10.5 训练及推断 381
句子 x,生成一个使翻译概率 P (y|x) 最大的目标语言译文 ˆy,具体计算如下(详细
过程见10.3.1 节):
ˆy = argmax
y
P (y|x)
= argmax
y
n
Y
j=1
P (y
j
|y
<j
,x) (10.30)
在具体实现时,由于当前目标语言单词的生成需要依赖前面单词的生成,因此
无法同时生成所有的目标语言单词。理论上,可以枚举所有的 y,之后利用 P (y|x)
的定义对每个 y 进行评价,然后找出最好的 y。这也被称作全搜索(Full Search)。但
是,枚举所有的译文单词序列显然是不现实的。因此,在具体实现时,并不会访问所
有可能的译文单词序列,而是用某种策略进行有效的搜索。常用的做法是自左向右
逐词生成。比如,对于每一个目标语言位置 j,可以执行:
ˆy
j
= argmax
y
j
P (y
j
|ˆy
<j
,x) (10.31)
其中,ˆy
j
表示位置 j 概率最高的单词,ˆy
<j
= {ˆy
1
,..., ˆy
j−1
} 表示已经生成的最优译文
单词序列。也就是,把最优的译文看作是所有位置上最优单词的组合。显然,这是一
种贪婪搜索,因为无法保证 {ˆy
1
,..., ˆy
n
} 是全局最优解。一种缓解这个问题的方法是,
在每步中引入更多的候选。这里定义 ˆy
jk
表示在目标语言第 j 个位置排名在第 k 位
的单词。在每一个位置 j,可以生成 k 个最可能的单词,而不是 1 个,这个过程可以
被描述为:
{ˆy
j1
,..., ˆy
jk
} = argmax
{ˆy
j1
,...,ˆy
jk
}
P (y
j
|{ˆy
<j∗
},x) (10.32)
这里,{ˆy
j1
,..., ˆy
jk
} 表示对于位置 j 翻译概率最大的前 k 个单词,{ˆy
<j∗
} 表示前 j −
1 步 top-k 单词组成的所有历史。ˆy
<j∗
可以被看作是一个集合,里面每一个元素都
是一个目标语言单词序列,这个序列是前面生成的一系列 top-k 单词的某种组成。
P (y
j
|{ˆy
<j∗
},x) 表示基于 {ˆy
<j∗
} 的某一条路径生成 y
j
的概率
6
。这种方法也被称为
束搜索,意思是搜索时始终考虑一个集束内的候选。
不论是贪婪搜索还是束搜索都是一个自左向右的过程,也就是每个位置的处理
需要等前面位置处理完才能执行。这是一种典型的自回归模型(Autoregressive Model)
,它通常用来描述时序上的随机过程,其中每一个时刻的结果对时序上其他部分的
结果有依赖
[473]
。相对应的,也有非自回归模型(Non-autoregressive Model),它消除
了不同时刻结果之间的直接依赖
[273]
。由于自回归模型是当今神经机器翻译主流的推
6
严格来说,P (y
j
|ˆy
<j∗
) 不是一个准确的数学表达,公式(10.32)通过这种写法强调 y
j
是由 {ˆy
<j∗
} 中
的某个译文单词序列作为条件生成的。
382 Chapter 10. 基于循环神经网络的模型 肖桐 朱靖波
断方法,这里仍以自回归的贪婪搜索和束搜索为基础进行讨论。
1. 贪婪搜索
图10.29展示了一个基于贪婪方法的神经机器翻译解码过程。每一个时间步的单
词预测都依赖于其前一步单词的生成。在解码第一个单词时,由于没有之前的单词
信息,会用 <sos> 进行填充作为起始的单词,且会用一个零向量(可以理解为没有之
前时间步的信息)表示第 0 步的中间层状态。
h
1
...
h
m
e
x
()
...
e
x
()
你
...
⟨eos⟩
0
编码器
e
y
() e
y
() e
y
() e
y
()
...
s
1
s
2
s
3
s
4
...
Softmax
解码器
Softmax Softmax Softmax
...
⟨sos⟩
Have
you
learned
Have
[step 1]
you
[step 2]
learned
[step 3]
nothing
[step 4]
top1 top1 top1 top1
0
C
2
注意力机制:上下文
...
C
3
C
4
图 10.29 基于贪婪方法的解码过程
解码器的每一步 Softmax 层会输出所有单词的概率,由于是基于贪心的方法,这
里会选择概率最大(top-1)的单词作为输出。这个过程可以参考图10.30的内容。选
择分布中概率最大的单词“Have”作为得到的第一个单词,并再次送入解码器,作
为第二步的输入同时预测下一个单词。以此类推,直到生成句子的终止符为止,就
得到了完整的译文。
e
y
()
s
1
⟨sos⟩
Softmax
0
Have 0.50
I 0.02
it 0.03
has 0.30
you 0.01
the 0.01
a 0.01
an 0.02
he 0.03
she 0.01
are 0.00
am 0.01
... ...
单词的概率分布
Have
top-1
图 10.30 解码第一个位置输出的单词概率分布(“Have”的概率最高)
贪婪搜索的优点在于速度快。在对翻译速度有较高要求的场景中,贪婪搜索是
一种十分有效的系统加速方法。而且贪婪搜索的原理非常简单,易于快速实现。不
过,由于每一步只保留一个最好的局部结果,贪婪搜索往往会带来翻译品质上的损
失。
10.5 训练及推断 383
2. 束搜索
束搜索是一种启发式图搜索算法。相比于全搜索,它可以减少搜索所占用的空
间和时间,在每一步扩展的时候,剪掉一些质量比较差的结点,保留下一些质量较
高的结点。具体到机器翻译任务,对于每一个目标语言位置,束搜索选择了概率最
大的前 k 个单词进行扩展(其中 k 叫做束宽度,或简称为束宽)。如图10.31所示,假
设 {y
1
,...,y
n
} 表示生成的目标语言序列,且 k = 3,则束搜索的具体过程为:在预测
第一个位置时,可以通过模型得到 y
1
的概率分布,选取概率最大的前 3 个单词作为
候选结果(假设分别为“have”, “has”, “it”)。在预测第二个位置的单词时,模
型针对已经得到的三个候选结果(“have”, “has”, “it”)计算第二个单词的概率
分布。因为 y
2
对应 |V | 种可能,总共可以得到 3 ×|V | 种结果。然后从中选取使序
列概率 P (y
2
,y
1
|x) 最大的前三个 y
2
作为新的输出结果,这样便得到了前两个位置的
top-3 译文。在预测其他位置时也是如此,不断重复此过程直到推断结束。可以看到,
束搜索的搜索空间大小与束宽度有关,也就是:束宽度越大,搜索空间越大,更有可
能搜索到质量更高的译文,但同时搜索会更慢。束宽度等于 3,意味着每次只考虑三
个最有可能的结果,贪婪搜索实际上便是束宽度为 1 的情况。在神经机器翻译系统
实现中,一般束宽度设置在 4~8 之间。
e
y
() e
y
() (×3) e
y
() (×3)
...
s
1
s
2
(×3) s
3
(×3)
...
Softmax
Softmax (×3) Softmax (×3)
...
⟨sos⟩
Have
you
Have
you
learned
top-3 top-3 top-3
0
C
2
C
2
C
2
C
3
C
3
C
3
Have 0.50
I 0.02
it 0.03
has 0.30
you 0.01
the 0.01
a 0.01
an 0.02
he 0.03
she 0.01
are 0.00
am 0.01
... ...
单词的概率分布
Have
has
it
top-3
束搜索 (k = 3)
图 10.31 束搜索过程
3. 长度惩罚
这里用 P (y|x) =
Q
n
j=1
P (y
j
|y
<j
,x) 作为翻译模型。直接实现这个公式有一个明
显的缺点:当句子过长时乘法运算容易产生溢出,也就是多个数相乘可能会产生浮
点数无法表示的运算结果。为了解决这个问题,可以利用对数操作将乘法转换为加
法,得到新的计算方式:log P (y|x) =
P
n
j=1
log P (y
j
|y
<j
,x)。对数函数不会改变函数
的单调性,因此在具体实现时,通常用 log P (y| x) 表示句子的得分,而不用 P (y|x)。
不管是使用 P (y|x) 还是 log P (y|x) 计算句子得分,还面临两个问题:
• P (y|x) 的范围是 [0,1],如果句子过长,那么句子的得分就是很多个小于 1 的数
384 Chapter 10. 基于循环神经网络的模型 肖桐 朱靖波
相乘,或者说取 log 之后很多个小于 0 的数相加。这也就是说,句子的得分会
随着长度的增加而变小,即模型倾向于生成短句子。
• 模型本身并没有考虑每个源语言单词被使用的程度,比如一个单词可能会被翻
译很多“次”。这个问题在统计机器翻译中并不存在,因为所有词在翻译中必
须被“覆盖”到。但是早期的神经机器翻译模型没有所谓覆盖度的概念,因此
也无法保证每个单词被翻译的“程度”是合理的
[474, 475]
。
为了解决上面提到的问题,可以使用其他特征与 log P (y|x) 一起组成新的模型
得分 score(y,x)。针对模型倾向于生成短句子的问题,常用的做法是引入惩罚机制。
比如,可以定义一个惩罚因子,形式如下:
lp(y) =
(5 + |y|)
α
(5 + 1)
α
(10.33)
其中,|y| 代表已经得到的译文长度,α 是一个固定的常数,用于控制惩罚的强度。同
时在计算句子得分时,额外引入表示覆盖度的因子,如下:
cp(y,x) = β ·
|x|
X
i=1
log
min(
|y|
X
j
α
ij
,1)
(10.34)
cp(·) 会惩罚把某些源语言单词对应到很多目标语言单词的情况(覆盖度),被覆盖的
程度用
P
|y|
j
α
ij
度量。β 是需要根据经验设置的超参数,用于对覆盖度惩罚的强度进
行控制。
最终,模型得分定义如下:
score(y,x) =
logP (y|x)
lp(y)
+ cp(y,x) (10.35)
显然,当目标语言 y 越短时,lp(y) 的值越小,因为 log P (y|x) 是负数,所以句子得
分 score(y,x) 越小。也就是说,模型会惩罚译文过短的结果。当覆盖度较高时,同样
会使得分变低。通过这样的惩罚机制,使模型得分更为合理,从而帮助模型选择出
质量更高的译文。