主要参考学习资料:
《动手学深度学习》阿斯顿·张 等 著
【动手学深度学习 PyTorch版】哔哩哔哩@跟李牧学AI
概述
- 门控循环单元和长短期记忆网络利用门控机制实现对序列输入的选择性记忆。
- 深度循环神经网络堆叠多个循环神经网络层以实现更强的表达能力和特征提取能力。
- 双向循环神经网络同时捕捉过去和未来两个方向的依赖关系。
- 序列到序列类任务的输入和输出均为可变长度序列,主要使用编码器-解码器架构。
- 束搜索是一种在解码器选择输出序列时兼顾精确度和计算量的搜索方法。
目录
门控循环单元(GRU)
在序列数据中,通常不是所有的信息都同等重要,我们需要提炼出关键信息,过滤掉无关的内容,门控机制则受此启发而来。当判断出重要信息时,信息从开启的门流出,当信息可以被遗忘时,则被关闭的门隔断。最早的相关方法为长短期记忆网络(LSTM),而门控循环单元(GRU)作为其简化变体,先行学习更易于理解。
重置门和更新门
GRU包含重置门和更新门两个门控,它们都以当前时间步的输入和前一个时间步的隐状态作为输入,并通过sigmoid激活函数将各自的输出压缩到区间 ( 0 , 1 ) (0,1) (0,1)中,而输出将作为比例系数应用到后续计算中。换言之,重置门和更新门先根据输入给出筛选信息的方法,而筛选的实际操作是在之后进行的。
对于在时间步 t t t的小批量输入 X t ∈ R n × d \mathbf{X}_t \in \mathbb{R}^{n \times d} Xt∈Rn×d( n n n个样本,每个样本 d d d个输入),前一个时间步的隐状态 H t − 1 ∈ R n × h \mathbf H_{t-1}\in\mathbb R^{n\times h} Ht−1∈Rn×h( h h h个隐藏单元),重置门 R t ∈ R n × h \mathbf R_t\in\mathbb R^{n\times h} Rt∈Rn×h和更新门 Z t ∈ R n × h \mathbf Z_t\in\mathbb R^{n\times h} Zt∈Rn×h的计算如下:
R t = σ ( X t W x r + H t − 1 W h r + b r ) \mathbf{R}_t=\sigma(\mathbf{X}_t\mathbf{W}_{xr}+\mathbf{H}_{t-1}\mathbf{W}_{hr}+\mathbf{b}_r) Rt=σ(XtWxr+Ht−1Whr+br)
Z t = σ ( X t W x z + H t − 1 W h z + b z ) \mathbf{Z}_t=\sigma(\mathbf{X}_t\mathbf{W}_{xz}+\mathbf{H}_{t-1}\mathbf{W}_{hz}+\mathbf{b}_z) Zt=σ(XtWxz+Ht−1Whz+bz)
其中 W x r , W x z ∈ R d × h \mathbf{W}_{xr}, \mathbf{W}_{xz} \in \mathbb{R}^{d \times h} Wxr,Wxz∈Rd×h和 W h r , W h z ∈ R h × h \mathbf{W}_{hr}, \mathbf{W}_{hz} \in \mathbb{R}^{h \times h} Whr,Whz∈Rh×h为权重参数, b r , b z ∈ R 1 × h \mathbf{b}_r, \mathbf{b}_z \in \mathbb{R}^{1 \times h} br,bz∈R1×h为偏置参数。
候选隐状态
接下来我们将重置门 R t \mathbf R_t Rt应用到常规RNN的隐状态更新公式中,得到的隐状态被称为候选隐状态:
H ~ t = tanh ( X t W x h + ( R t ⊙ H t − 1 ) W h h + b h ) \widetilde{\mathbf{H}}_t = \tanh(\mathbf{X}_t \mathbf{W}_{xh} + \left(\mathbf{R}_t \odot \mathbf{H}_{t-1}\right) \mathbf{W}_{hh} + \mathbf{b}_h) H t=tanh(XtWxh+(Rt⊙Ht−1)Whh+bh)
其中 W x h ∈ R d × h \mathbf{W}_{xh} \in \mathbb{R}^{d \times h} Wxh∈Rd×h和 W h h ∈ R h × h \mathbf{W}_{hh} \in \mathbb{R}^{h \times h} Whh∈Rh×h是权重参数, b h ∈ R 1 × h \mathbf{b}_h \in \mathbb{R}^{1 \times h} bh∈R1×h是偏置参数(关于参数及其维度均可由字母及输入输出维度关系推得,此后不再赘述),符号 ⊙ \odot ⊙是按元素乘法。激活函数 tanh \tanh tanh将输出压缩到区间 ( − 1 , 1 ) (-1,1) (−1,1)中,既能避免数值爆炸,又能通过正负号增强信息表达。
R t \mathbf R_t Rt中的每个元素都在区间 ( 0 , 1 ) (0,1) (0,1)中,当其与 H t − 1 \mathbf{H}_{t-1} Ht−1中对应的元素相乘时,可以决定保留该元素的比例。它筛选过去的经验来作为理解新信息的参考。
隐状态
最后我们使用更新门 Z t \mathbf Z_t Zt将旧的隐状态 H t − 1 \mathbf H_{t-1} Ht−1(对过去信息的记忆)和候选隐状态 H ~ t \widetilde{\mathbf{H}}_t H t(对新信息的理解)加权求和,得到最终的隐状态(同时也是输出):
H t = Z t ⊙ H t − 1 + ( 1 − Z t ) ⊙ H ~ t \mathbf{H}_t = \mathbf{Z}_t \odot \mathbf{H}_{t-1} + (1 - \mathbf{Z}_t) \odot \widetilde{\mathbf{H}}_t Ht=Zt⊙Ht−1+(1−Zt)⊙H t
当 Z t \mathbf Z_t Zt接近 1 1 1时,模型倾向于忽略新的信息,保留旧的记忆,从而跳过依赖链中的当前时间步;当 Z t \mathbf Z_t Zt接近 0 0 0时,模型倾向于让新的信息覆盖旧的记忆。该方法与ResNet的残差连接有异曲同工之处。
总而言之,重置门有助于捕获序列中的短期依赖关系,更新门有助于捕获序列中的长期依赖关系。这使得模型能更灵活地处理依赖关系的同时,还通过对信息的筛选降低了梯度传播路径的复杂性,避免其被不必要的分支稀释,缓解了梯度消失和梯度爆炸的问题。
长短期记忆网络(LSTM)
长短期记忆网络(LSTM)比GRU出现得更早,但设计更为复杂。
LSTM在隐状态之外引入了另一条在神经元之间传递的信息流,称为记忆元。可以将隐状态(同时也是输出)理解为短期记忆或对当前预测有用的信息,而记忆元则代表长期记忆。除此之外,LSTM包含遗忘门、输入门和输出门三个门控和候选记忆元计算,其中遗忘门、输入门和候选记忆元负责从短期记忆提炼信息以更新长期记忆,而输出门负责捕获长期依赖应用于当前预测。
遗忘门
对于在时间步 t t t的小批量输入 X t ∈ R n × d \mathbf{X}_t \in \mathbb{R}^{n \times d} Xt∈Rn×d( n n n个样本,每个样本 d d d个输入),前一个时间步的隐状态 H t − 1 ∈ R n × h \mathbf H_{t-1}\in\mathbb R^{n\times h} Ht−1∈Rn×h( h h h个隐藏单元),遗忘门 F t ∈ R n × h \mathbf{F}_t \in \mathbb{R}^{n \times h} Ft∈Rn×h的计算如下:
F t = σ ( X t W x f + H t − 1 W h f + b f ) \mathbf{F}_t = \sigma(\mathbf{X}_t \mathbf{W}_{xf} + \mathbf{H}_{t-1} \mathbf{W}_{hf} + \mathbf{b}_f) Ft=σ(XtWxf+Ht−1Whf+bf)
和GRU同样地,sigmoid激活函数将其压缩到区间 ( 0 , 1 ) (0,1) (0,1)得到一个比例系数,它通过乘法作用于长期记忆,决定应该遗忘哪些信息( C t 0 \mathbf{C}_{t0} Ct0为博主自行用于表示记忆元计算的中间状态的变量名):
C t 0 = F t ⊙ C t − 1 \mathbf{C}_{t0} = \mathbf{F}_t \odot \mathbf{C}_{t-1} Ct0=Ft⊙Ct−1
输入门和候选记忆
候选记忆元 C ~ t ∈ R n × h \widetilde{\mathbf{C}}_t \in \mathbb{R}^{n \times h} C t∈Rn×h和GRU的候选隐状态类似,表征一种在短期记忆基础上对新信息的理解,其计算如下:
C ~ t = tanh ( X t W x c + H t − 1 W h c + b c ) \widetilde{\mathbf{C}}_t = \text{tanh}(\mathbf{X}_t \mathbf{W}_{xc} + \mathbf{H}_{t-1} \mathbf{W}_{hc} + \mathbf{b}_c) C t=tanh(XtWxc+Ht−1Whc+bc)
输入门 I t ∈ R n × h \mathbf{I}_t \in \mathbb{R}^{n \times h} It∈Rn×h则作为候选记忆元的比例系数,决定了对新信息的哪些理解可以进入长期记忆,其计算如下:
I t = σ ( X t W x i + H t − 1 W h i + b i ) \mathbf{I}_t = \sigma(\mathbf{X}_t \mathbf{W}_{xi} + \mathbf{H}_{t-1} \mathbf{W}_{hi} + \mathbf{b}_i) It=σ(XtWxi+Ht−1Whi+bi)
将输入门作用于候选记忆元,得到的信息流入长期记忆,我们有这一阶段记忆元的更新公式如下:
C t = C t 0 + I t ⊙ C ~ t = F t ⊙ C t − 1 + I t ⊙ C ~ t \mathbf{C}_t = \mathbf{C}_{t0} + \mathbf{I}_t \odot \tilde{\mathbf{C}}_t= \mathbf{F}_t \odot \mathbf{C}_{t-1} + \mathbf{I}_t \odot \tilde{\mathbf{C}}_t Ct=Ct0+It⊙C~t=Ft⊙Ct−1+It⊙C~t
输出门
输出门用于捕获长期记忆中对当前预测较为关键的依赖信息并更新短期记忆,其计算如下:
O t = σ ( X t W x o + H t − 1 W h o + b o ) \mathbf{O}_t = \sigma(\mathbf{X}_t \mathbf{W}_{xo} + \mathbf{H}_{t-1} \mathbf{W}_{ho} + \mathbf{b}_o) Ot=σ(XtWxo+Ht−1Who+bo)
记忆元在被输出门筛选得到下一个隐状态之前,还会经过tanh激活函数以防数值爆炸:
H t = O t ⊙ tanh ( C t ) \mathbf{H}_t = \mathbf{O}_t \odot \tanh(\mathbf{C}_t) Ht=Ot⊙tanh(Ct)
现在,我们将LSTM的计算过程总结如下:
F t = σ ( X t W x f + H t − 1 W h f + b f ) \mathbf{F}_t = \sigma(\mathbf{X}_t \mathbf{W}_{xf} + \mathbf{H}_{t-1} \mathbf{W}_{hf} + \mathbf{b}_f) Ft=σ(XtWxf+Ht−1Whf+bf)
I t = σ ( X t W x i + H t − 1 W h i + b i ) \mathbf{I}_t = \sigma(\mathbf{X}_t \mathbf{W}_{xi} + \mathbf{H}_{t-1} \mathbf{W}_{hi} + \mathbf{b}_i) It=σ(XtWxi+Ht−1Whi+bi)
O t = σ ( X t W x o + H t − 1 W h o + b o ) \mathbf{O}_t = \sigma(\mathbf{X}_t \mathbf{W}_{xo} + \mathbf{H}_{t-1} \mathbf{W}_{ho} + \mathbf{b}_o) Ot=σ(XtWxo+Ht−1Who+bo)
C ~ t = tanh ( X t W x c + H t − 1 W h c + b c ) \widetilde{\mathbf{C}}_t = \text{tanh}(\mathbf{X}_t \mathbf{W}_{xc} + \mathbf{H}_{t-1} \mathbf{W}_{hc} + \mathbf{b}_c) C t=tanh(XtWxc+Ht−1Whc+bc)
C t = F t ⊙ C t − 1 + I t ⊙ C ~ t \mathbf{C}_t = \mathbf{F}_t \odot \mathbf{C}_{t-1} + \mathbf{I}_t \odot \tilde{\mathbf{C}}_t Ct=Ft⊙Ct−1+It⊙C~t
H t = O t ⊙ tanh ( C t ) \mathbf{H}_t = \mathbf{O}_t \odot \tanh(\mathbf{C}_t) Ht=Ot⊙tanh(Ct)
总而言之,LSTM将长短期记忆分流处理、相互作用,短期记忆帮助长期记忆选择性遗忘和记忆,而长期记忆帮助短期记忆重新捕获和当下有关的长期依赖关系。短期记忆涉及了更多的参数,但数值范围被压缩以防数值爆炸;长期记忆的数值没有限制,但涉及的运算更为简洁。LSTM虽然比GRU复杂,但二者在大多数任务上的表现差不多。
对于序列中过长距离的依赖,LSTM和GRU的训练成本都是相当高的,在下一章将介绍更高级的替代模型Transformer。
深度循环神经网络(DRNN)
上一章只讨论了单隐藏层的RNN。通过将多层RNN堆叠在一起,我们可以得到具有更强表达能力和特征抽象能力的深度RNN(DRNN)。
在深度RNN的隐藏层中,每一层接收来自上一层的隐状态和自己上一个时间步的隐状态来更新自己当前时间步的隐状态(除了第一层),又将自己更新后的隐状态作为输入传给下一层和下一个时间步的自己。
对于在时间步 t t t的小批量输入 H t ( 0 ) = X t ∈ R n × d \mathbf{H}_t^{(0)} = \mathbf{X}_t \in \mathbb{R}^{n \times d} Ht(0)=Xt∈Rn×d( n n n个样本,每个样本 d d d个输入),设第 l l l个隐藏层( l = 1 , ⋯ , L l=1,\cdots,L l=1,⋯,L)的隐状态为 H t ( l ) ∈ R n × h \mathbf{H}_t^{(l)} \in \mathbb{R}^{n \times h} Ht(l)∈Rn×h( h h h个隐藏单元),使用的激活函数为 ϕ l \phi_l ϕl,且设输出层变量为 O t ∈ R n × q \mathbf{O}_t \in \mathbb{R}^{n \times q} Ot∈Rn×q( q q q个输出单元),则每个隐藏层的隐状态计算如下:
H t ( l ) = ϕ l ( H t ( l − 1 ) W x h ( l ) + H t − 1 ( l ) W h h ( l ) + b h ( l ) ) \mathbf{H}_t^{(l)} = \phi_l(\mathbf{H}_t^{(l-1)} \mathbf{W}_{xh}^{(l)} + \mathbf{H}_{t-1}^{(l)} \mathbf{W}_{hh}^{(l)} + \mathbf{b}_h^{(l)}) Ht(l)=ϕl(Ht(l−1)Wxh(l)+Ht−1(l)Whh(l)+bh(l))
最终输出层的计算为:
O t = H t ( L ) W h q + b q \mathbf{O}_t = \mathbf{H}_t^{(L)} \mathbf{W}_{hq} + \mathbf{b}_q Ot=Ht(L)Whq+bq
其中隐藏层数 L L L和每层的隐藏单元数 h h h都是超参数。此外,将LSTM和GRU计算隐状态的公式(输入替换为上一层的输出)即可实现深层LSTM和深层GRU,其中深层LSTM的记忆单元仍在层内传递。
双向循环神经网络(BiRNN)
在序列学习中,我们以往假定的目标是在给定观测的情况下对下一个输出进行预测。但除此之外,还有一种任务是根据上下文预测序列中间空缺的信息,这使得模型需要捕捉过去和未来两个方向上的依赖关系,而目前为止,RNN只会观测过去的信息。
双向RNN(BiRNN)在传统RNN的基础上添加了反向传递信息的隐藏层。输入会分别进入前向隐藏层和反向隐藏层进行处理,而输出层则将前向隐状态和反向隐状态连接起来进行计算。
对于在时间步 t t t的小批量输入 X t ∈ R n × d \mathbf{X}_t \in \mathbb{R}^{n \times d} Xt∈Rn×d( n n n个样本,每个样本 d d d个输入),设该时间步的前向隐状态和反向隐状态分别为 H → t ∈ R n × h \overrightarrow{\mathbf{H}}_t \in \mathbb{R}^{n \times h} Ht∈Rn×h和 H ← t ∈ R n × h \overleftarrow{\mathbf{H}}_t \in \mathbb{R}^{n \times h} Ht∈Rn×h( h h h个隐藏单元,两个方向上的数目可以是不同的),则其计算如下:
H → t = ϕ ( X t W x h ( f ) + H → t − 1 W h h ( f ) + b h ( f ) ) \overrightarrow{\mathbf{H}}_t = \phi(\mathbf{X}_t \mathbf{W}_{xh}^{(f)} + \overrightarrow{\mathbf{H}}_{t-1} \mathbf{W}_{hh}^{(f)} + \mathbf{b}_h^{(f)}) Ht=ϕ(XtWxh(f)+Ht−1Whh(f)+bh(f))
H ← t = ϕ ( X t W x h ( b ) + H ← t + 1 W h h ( b ) + b h ( b ) ) \overleftarrow{\mathbf{H}}_t = \phi(\mathbf{X}_t \mathbf{W}_{xh}^{(b)} + \overleftarrow{\mathbf{H}}_{t+1} \mathbf{W}_{hh}^{(b)} + \mathbf{b}_h^{(b)}) Ht=ϕ(XtWxh(b)+Ht+1Whh(b)+bh(b))
参数的上标 ( f ) (f) (f)和 ( b ) (b) (b)分别表示前向和反向。
将前向隐状态 H → t \overrightarrow{\mathbf{H}}_t Ht和反向隐状态 H ← t \overleftarrow{\mathbf{H}}_t Ht连接得到进入输出层(在深度BiRNN中进入下一个双向隐藏层)的隐状态 H t ∈ R n × 2 h \mathbf{H}_t \in \mathbb{R}^{n \times 2h} Ht∈Rn×2h,最终输出层的输出 O t ∈ R n × q \mathbf{O}_t \in \mathbb{R}^{n \times q} Ot∈Rn×q( q q q个输出单元)计算如下:
O t = H t W h q + b q \mathbf{O}_t = \mathbf{H}_t \mathbf{W}_{hq} + \mathbf{b}_q Ot=HtWhq+bq
由于BiRNN需要同时进行前向和反向的隐状态计算,因此需要完整的序列才能计算每一个时间步的输出。在填充序列空缺任务中,其预测值为对应时间步的输出,而在序列标注任务中,最终的输出会结合所有时间步的输出给出。
BiRNN不适用于单向预测,因为这样的测试缺少在训练中能够利用的未来的信息,大大降低了模型的精确度。还有一个严重的问题是,双向递归使得梯度求解的链条变得非常长,导致模型的计算速度非常慢。
序列到序列(Seq2Seq)
在一类重要的自然语言处理任务中,输入和输出都是不定长的序列,例如机器翻译和对话,它们被称为序列到序列(Seq2Seq)类学习任务,这些是序列转换模型的核心问题。
编码器-解码器架构
为了处理上述类型的输入和输出,深度学习引入了通信领域中编码与解码的概念。为了避免信息在传输过程中被干扰失真,在传输前需要先将信号编码为易于传输的形式,然后在接收时将信号解码还原。
Seq2Seq使用包含两个主要组件的架构。第一个组件是编码器,它接收一个长度可变的序列作为输入,并将其转换为具有固定形状的编码状态;第二个组件是解码器,它将固定形状的编码状态映射到长度可变的序列。这被称为编码器-解码器架构。
编码器
编码器将长度可变的输入序列转换成形状固定的上下文变量 c \mathbf c c,并将输入序列的信息在该上下文变量中进行编码。以用RNN设计的编码器为例,考虑单个样本,在编码之前,RNN先遍历序列 { x t } \{\mathbf x_t\} {xt}计算出每一个时间步 t t t的隐状态 h t \mathbf h_t ht:
h t = f ( x t , h t − 1 ) \mathbf{h}_t = f(\mathbf{x}_t, \mathbf{h}_{t-1}) ht=f(xt,ht−1)
编码过程则以所有时间步的隐状态作为输入,通过选定的函数 q q q将其转换为上下文变量:
c = q ( h 1 , ⋯ , h T ) \mathbf{c} = q(\mathbf{h}_1, \cdots, \mathbf{h}_T) c=q(h1,⋯,hT)
上下文变量是从输入序列中提取出的特征表示,一种简单的选择是令 c = h T \mathbf{c} = \mathbf{h}_T c=hT,直接使用最后时间步的隐状态作为上下文变量。
解码器
解码器使用来自编码器的上下文变量初始化自己的隐状态,并作为之后输出的参考。假设使用另一个RNN作为解码器,对于解码过程,在输出序列的时间步 t ′ t' t′,解码器将连接自己上一个时间步的输出 y t ′ − 1 \mathbf y_{t'-1} yt′−1和上下文变量 c \mathbf c c作为输入来更新自己上一个时间步的隐状态 s t ′ − 1 \mathbf s_{t'-1} st′−1(使用 s \mathbf s s与解码器的隐状态区分开)为 s t ′ \mathbf s_{t'} st′:
s t ′ = g ( y t ′ − 1 , c , s t ′ − 1 ) \mathbf{s}_{t^\prime} = g(y_{t^\prime-1}, \mathbf{c}, \mathbf{s}_{t^\prime-1}) st′=g(yt′−1,c,st′−1)
随后解码器通过输出层计算在时间步 t ′ t' t′的输出 y t ′ \mathbf y_{t'} yt′,这个过程也是将隐状态映射为词表大小的矩阵,以便对其进行softmax操作转化成各个词元作为预测结果的条件概率,最终给出条件概率最大的词元。只要给解码器一个初始的输入 y 1 \mathbf y_1 y1,解码器就能通过对后续时间步的预测输出序列,直到其判断序列可以终止。
上图给出了在机器翻译中编码器-解码器架构是如何工作的,其中 <bos> \texttt{<bos>} <bos>和 <eos> \texttt{<eos>} <eos>分别为序列开始词元和序列结束词元。编码器提炼待翻译序列的信息送往解码器,解码器接收 <bos> \texttt{<bos>} <bos>作为起点,根据编码信息和已翻译出的内容继续预测翻译内容的下一个词元,直到预测出 <eos> \texttt{<eos>} <eos>作为翻译内容的结束。而在训练过程中,解码器会直接接收标签序列作为输入以从中进行学习。
预测序列的评估
对于以可变长度序列为输出的预测,我们可以通过与真实的标签序列进行比较来评估。BLEU(Bilingual Evaluation Understudy)最先被用于评估机器翻译的结果,但现在已经被广泛用于度量输出序列的质量。其定义如下:
B L E U = exp ( min ( 0 , 1 − l e n label l e n pred ) ) ∏ n = 1 k p n 1 / 2 n \mathrm{BLEU}=\exp\left(\min\left(0, 1 - \frac{\mathrm{len}_{\text{label}}}{\mathrm{len}_{\text{pred}}}\right)\right) \displaystyle\prod_{n=1}^k p_n^{1/2^n} BLEU=exp(min(0,1−lenpredlenlabel))n=1∏kpn1/2n
从右往左看, p n p_n pn表示 n n n元语法(连续 n n n个词元组成的序列)的精确率,其计算步骤如下:
- 分别统计标签序列和预测序列中的所有 n n n元语法(包括重复出现的项);
- 将标签序列的 n n n元语法与预测序列的 n n n元语法一一匹配,每项只能被匹配一次,统计匹配次数;
- 将匹配次数除以预测序列中 n n n元语法的总数(包括重复出现的项)。
例如对于标签序列 { A , B , C , C , D } \{A,B,C,C,D\} {A,B,C,C,D}和预测序列 { A , B , B , C } \{A,B,B,C\} {A,B,B,C}, p 1 = 3 4 p_1=\displaystyle\frac34 p1=43, p 2 = 2 3 \displaystyle p_2=\frac23 p2=32, p 3 = p 4 = 0 p_3=p_4=0 p3=p4=0。
由于 n n n越大, n n n元语法的匹配难度也越大, B L E U \mathrm{BLEU} BLEU通过 p n 1 / 2 n p_n^{1/2^n} pn1/2n为更长的 n n n元语法的精确率赋予了更大的权重(底数小于 1 1 1时,指数越小幂越大),再将所有的精确率累乘。
最后,越短的预测序列,其 n n n元语法的总数越小,获得的 p n p_n pn越大,因此 B L E U \mathrm{BLEU} BLEU通过系数 exp ( min ( 0 , 1 − l e n label l e n pred ) ) \exp\left(\min\left(0, 1 - \frac{\mathrm{len}_{\text{label}}}{\mathrm{len}_{\text{pred}}}\right)\right) exp(min(0,1−lenpredlenlabel))对其进行惩罚。当预测序列长度小于标签序列时, e e e的负数次幂会降低评估结果。而当预测序列长度大于标签序列时,精确率则会下降。只有当预测序列与标签序列完全相同时, B L E U = 1 \mathrm{BLEU}=1 BLEU=1。
束搜索
本节讨论解码器在选择词元作为输出时的策略问题。
贪心搜索
在上一节中,解码器直接在所有词元中选择条件概率最大的作为输出,即采取贪心搜索:
y t ′ = argmax y ∈ Y P ( y ∣ y 1 , … , y t ′ − 1 , c ) y_{t'} = \operatorname*{argmax}_{y \in \mathcal{Y}} P(y \mid y_1, \ldots, y_{t'-1}, \mathbf{c}) yt′=argmaxy∈YP(y∣y1,…,yt′−1,c)
一旦输出序列包含了 <eos> \texttt{<eos>} <eos>或到达其最大长度 T ′ T' T′,则输出完成。
这种策略带来的问题是,在当前时间步条件概率最大的词元,长远来看,在其后的几个时间步中,以该词元为起点的预测序列并不一定是所有预测序列中条件概率最高的,即局部最优不一定带来总体最优。
在上图所示的例子中,横向为不同时间步,纵向为不同词元。左侧使用贪心算法得到的预测序列为ABC,其概率为 0.5 × 0.4 × 0.4 × 0.6 = 0.048 0.5\times0.4\times0.4\times0.6=0.048 0.5×0.4×0.4×0.6=0.048,而右侧在第 2 2 2个时间步未使用贪心算法,得到的预测序列为ACB ,其概率为 0.5 × 0.3 × 0.6 × 0.6 = 0.054 0.5\times0.3\times0.6\times0.6=0.054 0.5×0.3×0.6×0.6=0.054(第 3 3 3、 4 4 4个时间步的概率分布不一样是因为第 2 2 2个时间步选择的概率条件不一样),优于贪心算法。
穷举搜索
如果想获得最优序列,我们可以考虑穷举搜索,即穷举所有可能的输出序列及其条件概率,计算输出条件概率最大的那一个。
穷举搜索虽然能保证选择的最优性,但其计算量 O ( ∣ Y ∣ T ′ ) O(|Y|^{T'}) O(∣Y∣T′)大得惊人( ∣ Y ∣ |Y| ∣Y∣为词表大小),在计算机上运行是几乎不可能的。
束搜索
束搜索介于贪心搜索和穷举搜索之间,兼顾精确度和计算成本。
束搜索在每个时间步 t t t做选择时,会同时选中条件概率最大的 k k k个词元作为候选输出,超参数 k k k是束搜索的束宽。在下一个时间步,从这 k k k个词元衍生出来的所有分支总共有 k ∣ Y ∣ k|Y| k∣Y∣个选择( ∣ Y ∣ |Y| ∣Y∣为词表大小),而束搜索会继续同时选中这所有 k ∣ Y ∣ k|Y| k∣Y∣个选择中条件概率最大的 k k k个词元,以此类推。在预测的过程中,束搜索将始终保持有 k k k个候选输出序列,如果一条候选序列遇到 <eos> \texttt{<eos>} <eos>则停止延伸,剩余可扩展的候选序列数量会相应地减少。所有候选序列均完成后则终止束搜索。
最后我们将从最终候选输出序列集合中选择分数最高的序列作为输出序列,每个候选的分数计算如下:
1 L α log P ( y 1 , … , y L ∣ c ) = 1 L α ∑ t ′ = 1 L log P ( y t ′ ∣ y 1 , … , y t ′ − 1 , c ) \displaystyle\frac{1}{L^\alpha} \log P(y_1, \ldots, y_{L}\mid \mathbf{c}) = \frac{1}{L^\alpha} \sum_{t'=1}^L \log P(y_{t'} \mid y_1, \ldots, y_{t'-1}, \mathbf{c}) Lα1logP(y1,…,yL∣c)=Lα1t′=1∑LlogP(yt′∣y1,…,yt′−1,c)
由于条件概率由概率累乘而来,容易产生数值下溢,因此在计算时取对数处理。除此之外,在候选序列中,长序列的条件概率会显著小于短序列,为了给予补偿,我们使用系数 1 L α \displaystyle\frac1{L^\alpha} Lα1,其中 L L L为序列长度, α \alpha α一般取 0.75 0.75 0.75。对数运算结果为负数,对于更大的 L L L,该系数使最终分数更大。
束搜索的计算量为 O ( k ∣ Y ∣ T ′ ) O(k|Y|T') O(k∣Y∣T′),即每个时间步需要遍历 k ∣ Y ∣ k|Y| k∣Y∣项,其计算成本介于贪心搜索和穷举搜索之间。通过灵活地选择束宽,束搜索可以在精确度和计算成本之间权衡。