上一节介绍了 Seq2seq \text{Seq2seq} Seq2seq中注意力机制 ( Attention ) (\text{Attention}) (Attention)的动机,并介绍了权重系数、 Score \text{Score} Score函数。本节将完整介绍注意力机制在 Seq2seq \text{Seq2seq} Seq2seq中的执行过程。
在经典的 Seq2seq \text{Seq2seq} Seq2seq模型结构中,关于解码器在各时刻的输出 y ( i ) ( i = 1 , 2 , ⋯ , T ′ ) y^{(i)}(i=1,2,\cdots,\mathcal T') y(i)(i=1,2,⋯,T′)均使用共同的 Context \text{Context} Context向量 C \mathcal C C生成出来的结果:
其中
⟨ Start ⟩ \left\langle\text{Start}\right\rangle ⟨Start⟩无语义信息,后续省略。
{ y ( 1 ) = f ( C , ⟨ Start ⟩ ) y ( 2 ) = f ( y ( 1 ) , C ) y ( 3 ) = f ( y ( 1 ) , y ( 2 ) , C ) ⋮ \begin{cases} y^{(1)} &= f(\mathcal C,\left\langle\text{Start}\right\rangle) \\ y^{(2)} & = f(y^{(1)},\mathcal C) \\ y^{(3)} & = f(y^{(1)},y^{(2)},\mathcal C) \\ & \vdots \end{cases} ⎩ ⎨ ⎧y(1)y(2)y(3)=f(C,⟨Start⟩)=f(y(1),C)=f(y(1),y(2),C)⋮
但从 Encoder \text{Encoder} Encoder中生成的 Context \text{Context} Context向量 C \mathcal C C虽然保留了输入序列数据 X \mathcal X X的完整序列信息,但因梯度消失问题,导致: C \mathcal C C对序列数据 X \mathcal X X的初始时刻信息存在遗忘现象。
从而基于 C \mathcal C C在解码器中生成出的序列信息 Y = ( y ( 1 ) , y ( 2 ) , ⋯ , y ( T ′ ) ) T \mathcal Y = (y^{(1)},y^{(2)},\cdots,y^{(\mathcal T')})^T Y=(y(1),y(2),⋯,y(T′))T,其初始时刻的序列信息并不准确。也就是说:生成出的初始时刻信息如 y ( 1 ) , y ( 2 ) y^{(1)},y^{(2)} y(1),y(2)等与输入序列数据 X \mathcal X X中的初始时刻信息如 x ( 1 ) , x ( 2 ) x^{(1)},x^{(2)} x(1),x(2)等关联性不强。这违背了翻译过程中的对齐逻辑 ⇒ \Rightarrow ⇒ 对齐问题。
这里的
x ( i ) , y ( i ) ( i = 1 , 2 , ⋯ ) x^{(i)},y^{(i)}(i=1,2,\cdots) x(i),y(i)(i=1,2,⋯)仅仅是举一个例子,它们仅描述‘初始时刻位置的信息’,但并不是说它们‘整整齐齐地对齐在一起’。因为
X , Y \mathcal X,\mathcal Y X,Y的序列长度可能存在差异。
针对对齐问题,存在一个朴素想法:在解码器 i i i时刻生成 y ( i ) y^{(i)} y(i)时,我们更希望在编码器中找到与 y ( i ) y^{(i)} y(i)关联程度更高的若干个序列信息作为输入,而不是仅仅描述完整序列信息的 Context \text{Context} Context。
基于该想法,具体动机是:将编码器中所有时刻的序列信息 H = ( h ( 1 ) , h ( 2 ) , ⋯ , h ( T ) ) \mathcal H = (h^{(1)},h^{(2)},\cdots,h^{(\mathcal T)}) H=(h(1),h(2),⋯,h(T))都输出出来,并对每一个序列信息 h ( j ) ( j = 1 , 2 , ⋯ , T ) h^{(j)}(j=1,2,\cdots,\mathcal T) h(j)(j=1,2,⋯,T)与 y ( i ) y^{(i)} y(i)的相关性进行打分,分值越高,相关性越强;最终将 H \mathcal H H与相关性结果 S \mathcal S S做线性运算:
其中
h D ( i ) h_{\mathcal D}^{(i)} hD(i)表示
i i i时刻解码器的序列信息:而
C i \mathcal C_i Ci表示替代原始
Context \text{Context} Context向量作为解码器
i i i时刻的序列信息。
表示’解码器‘第
i i i时刻的序列信息
h D ( i ) h_{\mathcal D}^{(i)} hD(i)与’编码器‘中第
j j j时刻序列信息
h ( j ) h^{(j)} h(j)之间的评分结果。
在编码器部分,使用双向 GRU \text{GRU} GRU结构 ( Bidirectional GRU,BiGRU ) (\text{Bidirectional GRU,BiGRU}) (Bidirectional GRU,BiGRU):
正常的 GRU \text{GRU} GRU结构仅捕捉到了正向个时刻的序列信息;而双向结构是在正向的基础上,增加了反向的序列信息:
其中
H S i n g l e \mathcal H_{Single} HSingle表示单向的序列信息;对应地,
H B i \mathcal H_{Bi} HBi表示双向的序列信息。
H S i n g l e = { h L ; 1 , h L ; 2 , ⋯ , h L ; T } H B i = { h L R ; 1 , h L R ; 2 , ⋯ , h L R ; T } \mathcal H_{Single} = \{h_{\mathcal L;1},h_{\mathcal L;2},\cdots,h_{\mathcal L;\mathcal T}\} \\ \mathcal H_{Bi} = \{h_{\mathcal L\mathcal R;1},h_{\mathcal L\mathcal R;2},\cdots,h_{\mathcal L\mathcal R;\mathcal T}\} HSingle={hL;1,hL;2,⋯,hL;T}HBi={hLR;1,hLR;2,⋯,hLR;T}
其中 h L R ; i h_{\mathcal L\mathcal R;i} hLR;i表示第 i i i时刻正、反方向序列信息的拼接 ( Concatenate ) (\text{Concatenate}) (Concatenate)结果,以此类推。
h L R ; i = [ h L : i ; h R ; ( T + 1 − i ) ] i = 1 , 2 ⋯ , T h_{\mathcal L\mathcal R;i} = \left[h_{\mathcal L:i};h_{\mathcal R;(\mathcal T +1 -i)}\right] \quad i=1,2\cdots,\mathcal T hLR;i=[hL:i;hR;(T+1−i)]i=1,2⋯,T
在代码中序列信息的描述表示如下:
import torch
from torch import nn as nn
BatchSize = 100
SeqLength = 10
EmbedSize = 8
NumHiddens = 16
NumLayers = 1
x = torch.randn(BatchSize,SeqLength,EmbedSize).permute(1,0,2)
SingleRNN = nn.GRU(EmbedSize,NumHiddens,NumLayers)
BiRNN = nn.GRU(EmbedSize,NumHiddens,NumLayers,bidirectional=True)
Output,State = SingleRNN(x)
print(x.shape)
print(Output.shape,State.shape)
BiOutput,BiState = BiRNN(x)
print(BiOutput.shape,BiState.shape)
序列信息的张量格式 ( Shape ) (\text{Shape}) (Shape)结果表示如下:
# Embedding Shape
torch.Size([10, 100, 8])
# SingleGRU.Output shape;FinalState shape
torch.Size([10, 100, 16]) torch.Size([1, 100, 16])
# BiGRU.Output shape;FinalState shape
torch.Size([10, 100, 32]) torch.Size([2, 100, 16])
可以观察一下,随意选取一个时刻。例如 T = 2 \mathcal T = 2 T=2时刻。它对应的序列信息可表示为:
h L R ; 2 = [ h L ; 2 ; h R ; T − 1 ] h_{\mathcal L\mathcal R;2} = \left[h_{\mathcal L;2};h_{\mathcal R;\mathcal T-1}\right] hLR;2=[hL;2;hR;T−1]
观察:
因而: h L R ; 2 h_{\mathcal L\mathcal R;2} hLR;2相比单向结构的 h L ; 2 h_{\mathcal L;2} hL;2包含更加丰富的序列信息。
解码过程这里同样以第 2 2 2时刻的解码为例:
这里'查询向量'使用
h D ( 1 ) , h D ( 2 ) h_{\mathcal D}^{(1)},h_{\mathcal D}^{(2)} hD(1),hD(2)都是有道理的。详见
上一节——注意力机制的动机
y ( 2 ) = G ( y ( 1 ) , C 2 , h D ( 2 ) ) y^{(2)} = \mathcal G(y^{(1)},\mathcal C_2,h_{\mathcal D}^{(2)}) y(2)=G(y(1),C2,hD(2))
描述生成 y ( 2 ) y^{(2)} y(2)信息的复杂函数 G ( ⋅ ) \mathcal G(\cdot) G(⋅)中,一共包含 3 3 3类信息:
类似于上面单向网络,
( Bi ) s 2 j (\text{Bi})s_{2j} (Bi)s2j描述’解码器‘第
2 2 2时刻的序列信息
h D ( 2 ) h_{\mathcal D}^{(2)} hD(2)与‘编码器’第
j j j时刻的双向序列信息
H B i ( j ) = [ h L ; j ; h R ; ( T + 1 − j ) ] \mathcal H_{Bi}^{(j)} = \left[h_{\mathcal L;j};h_{\mathcal R;(\mathcal T+1 - j)}\right] HBi(j)=[hL;j;hR;(T+1−j)]之间的评分结果。
这种将 H B i \mathcal H_{Bi} HBi中的所有时刻的序列信息均做加权求和求解 C 2 \mathcal C_2 C2的方式称作软注意力机制 ( Soft-Attention ) (\text{Soft-Attention}) (Soft-Attention);
相反,与软注意力机制对应的是硬注意力机制 ( Hard-Attention ) (\text{Hard-Attention}) (Hard-Attention)。这种注意力机制将 Score \text{Score} Score评分结果仅仅集中在若干个离散的序列信息中。也就是说,仅有 1 1 1个/若干个结果有 Score \text{Score} Score值,其余值均无影响。
但硬注意力机制比较困难,因为它在函数空间中并不处处可导。相反,软注意力机制在函数空间中处处可导,从而可以在反向传播过程中梯度进行传播。
这里有一点啰嗦,不是一天写的,担待一下~
回顾机器翻译任务,最终目标是求解:给定输入序列数据 X \mathcal X X以及解码器前 t − 1 t-1 t−1个时刻的输出信息 { y ( 1 ) , y ( 2 ) , ⋯ , y ( t − 1 ) } \{y^{(1)},y^{(2)},\cdots,y^{(t-1)}\} {y(1),y(2),⋯,y(t−1)}条件下,求解 t t t时刻输出信息 y ( t ) y^{(t)} y(t)的条件概率:
P ( y ( t ) ∣ X , y ( 1 ) , y ( 2 ) , ⋯ , y ( t − 1 ) ) \mathcal P(y^{(t)} \mid \mathcal X,y^{(1)},y^{(2)},\cdots,y^{(t-1)}) P(y(t)∣X,y(1),y(2),⋯,y(t−1))
从注意力机制的角度,将这个概率描述成函数的形式:
P ( y ( t ) ∣ X , y ( 1 ) , y ( 2 ) , ⋯ , y ( t − 1 ) ) = G ( y ( t − 1 ) , h D ( t ) , C t ) \mathcal P(y^{(t)} \mid \mathcal X,y^{(1)},y^{(2)},\cdots,y^{(t-1)}) = \mathcal G(y^{(t-1)},h_{\mathcal D}^{(t)},\mathcal C_t) P(y(t)∣X,y(1),y(2),⋯,y(t−1))=G(y(t−1),hD(t),Ct)
其中 y ( t − 1 ) y^{(t-1)} y(t−1)表示解码器 t − 1 t-1 t−1时刻的输出信息,作为 t t t时刻输入的一部分; h D ( t ) h_{\mathcal D}^{(t)} hD(t)作为解码器当前时刻的序列信息,它表示为如下形式:
这里的‘复杂函数’
f ( ⋅ ) f(\cdot) f(⋅)就是指循环神经网络系列的模型:
LSTM,GRU,RNN \text{LSTM,GRU,RNN} LSTM,GRU,RNN
h D ( t ) = f ( y ( t − 1 ) , h D ( t − 1 ) , C t ) h_{\mathcal D}^{(t)} = f(y^{(t-1)},h_{\mathcal D}^{(t-1)},\mathcal C_t) hD(t)=f(y(t−1),hD(t−1),Ct)
关于 C t \mathcal C_t Ct就是编码器各时刻的输出与相应 Score \text{Score} Score的加权求和结果:
这里仍然用‘双向循环网络’结构示例。
C t = ∑ j = 1 T s t j ⋅ [ h L ; j ; h R ; ( T + 1 − j ) ] \mathcal C_t = \sum_{j=1}^{\mathcal T} s_{tj} \cdot \left[h_{\mathcal L;j};h_{\mathcal R;(\mathcal T+1 - j)}\right] Ct=j=1∑Tstj⋅[hL;j;hR;(T+1−j)]
关于编码器第 j j j个时刻的输出 [ h L ; j ; h R ; ( T + 1 − j ) ] \left[h_{\mathcal L;j};h_{\mathcal R;(\mathcal T+1 - j)}\right] [hL;j;hR;(T+1−j)]与解码器 t t t时刻的序列信息 h D ( t ) h_{\mathcal D}^{(t)} hD(t)之间 Score \text{Score} Score结果 s t j s_{tj} stj的计算共分两个步骤:
用内积、或者构建神经网络的方式求解 Score \text{Score} Score结果;
关于两种方法的描述详见上一节:
注意力机制的动机
e t j = Score ( h D ( t ) ; [ h L ; j ; h R ; ( T + 1 − j ) ] ) j = 1 , 2 , ⋯ , T e_{tj} = \text{Score}(h_{\mathcal D}^{(t)};\left[h_{\mathcal L;j};h_{\mathcal R;(\mathcal T+1 - j)}\right]) \quad j=1,2,\cdots,\mathcal T etj=Score(hD(t);[hL;j;hR;(T+1−j)])j=1,2,⋯,T
这里以构建神经网络为例,描述 Score \text{Score} Score输出 E t ( e t 1 , e t 2 , ⋯ , e t T ) T \mathcal E_t (e_{t1},e_{t2},\cdots,e_{t\mathcal T})^T Et(et1,et2,⋯,etT)T的执行过程:
个人理解:在
数值稳定性、模型初始化与激活函数中介绍了激活函数的本质。激活函数的目的是:
维持低次项数值稳定的基础上(激活函数的线性近似区逼近 y = x y=x y=x,即恒等映射),去学习高次项特征。关于激活函数作用的输出分布
O ~ ( t ) \widetilde{\mathcal O}^{(t)} O (t),从物理意义的角度,它仅仅是
h D ( t ) h_{\mathcal D}^{(t)} hD(t)与
[ h L ; j ; h R ; ( T + 1 − j ) ] \left[h_{\mathcal L;j};h_{\mathcal R;(\mathcal T+1 - j)}\right] [hL;j;hR;(T+1−j)]之间关系的一个‘抽象’描述。但不可否认的是:
O ~ ( t ) \widetilde{\mathcal O}^{(t)} O (t)中的分量对表示两者之间关系存在实际价值。如果使用
ReLU \text{ReLU} ReLU激活函数去稀疏这个信息(使一部分分量置
0 0 0),个人认为不太可取。
其次,从泰勒公式的角度,明显能够看出
Tanh \text{Tanh} Tanh激活函数在
低次项数值的映射结果中,它比
Sigmoid \text{Sigmoid} Sigmoid函数更接近‘恒等映射’:
并且
Tanh \text{Tanh} Tanh激活函数的映射范围是
( − 1 , 1 ) (-1,1) (−1,1),因此关于一些信息的非线性映射,
Tanh \text{Tanh} Tanh激活函数效果更优。
计算出的关于 Score \text{Score} Score的结果向量 E t = ( e t 1 , e t 2 , ⋯ , e t T ) T \mathcal E_t = (e_{t1},e_{t2},\cdots,e_{t\mathcal T})^T Et=(et1,et2,⋯,etT)T不能直接使用,需要将其映射成概率形式—— Softmax \text{Softmax} Softmax函数:
{ s t j = exp ( e t j ) ∑ k = 1 T exp ( e t k ) j = 1 , 2 , ⋯ , T S t = ( s t 1 , s t 2 , ⋯ , s t T ) T \begin{cases} \begin{aligned} s_{tj} & = \frac{\exp(e_{tj})}{\begin{aligned}\sum_{k=1}^{\mathcal T} \exp(e_{tk})\end{aligned}} \quad j = 1,2,\cdots,\mathcal T \\ \mathcal S_t & = (s_{t1},s_{t2},\cdots,s_{t\mathcal T})^T \end{aligned} \end{cases} ⎩ ⎨ ⎧stjSt=k=1∑Texp(etk)exp(etj)j=1,2,⋯,T=(st1,st2,⋯,stT)T
最终通过线性计算,求出 C t \mathcal C_t Ct。
至此,关于 3 3 3个信息: y ( t − 1 ) , h D ( t ) , C t y^{(t-1)},h_{\mathcal D}^{(t)},\mathcal C_t y(t−1),hD(t),Ct都已求出,针对 3 3 3个信息构建神经网络,对 y ( t ) y^{(t)} y(t)的后验概率 G ( y ( t − 1 ) , h D ( t ) , C t ) \mathcal G(y^{(t-1)},h_{\mathcal D}^{(t)},\mathcal C_t) G(y(t−1),hD(t),Ct)进行预测:
对应函数的执行过程表示如下:
需要注意的是:这里的
y ( t − 1 ) y^{(t-1)} y(t−1)是上一时刻的输出特征,在作为下一时刻输入时,需将其重新转化为
Embedding \text{Embedding} Embedding向量信息。
关于
MaxOut \text{MaxOut} MaxOut激活函数,该函数一次比对若干个连续结果的大小,并取出其中最大的元素进行输出;移动窗口,执行下一次比较。其效果类似于
卷积神经网络中的最大池化层,用于“保留信息的基础上,降低特征维数。”这里使用窗口大小为
2 2 2进行示例。
{ h ~ t = U o ⋅ h D ( t ) + V o ⋅ C t + C o ⋅ Embedding ( y ( t − 1 ) ) h t = max { h ~ t ; 2 i − 1 , h ~ t ; 2 i } V t = W o ⋅ h t P t = Softmax ( V t ) \begin{cases} \begin{aligned} \widetilde{h}_t & = \mathcal U_o \cdot h_{\mathcal D}^{(t)} + \mathcal V_o \cdot \mathcal C_t + \mathcal C_o \cdot \text{Embedding}(y^{(t-1)}) \\ h_t & = \max\{\widetilde{h}_{t;2i-1},\widetilde{h}_{t;2i}\} \\ \mathcal V_t & = \mathcal W_o \cdot h_t \\ \mathcal P_t & = \text{Softmax}(\mathcal V_t) \end{aligned} \end{cases} ⎩ ⎨ ⎧h thtVtPt=Uo⋅hD(t)+Vo⋅Ct+Co⋅Embedding(y(t−1))=max{h t;2i−1,h t;2i}=Wo⋅ht=Softmax(Vt)
最终使用 Argmax \text{Argmax} Argmax选择出对应位置的词语结果即可。
相关参考:
seq2seq与attention机制
激活函数( ReLU,Swish,Maxout \text{ReLU,Swish,Maxout} ReLU,Swish,Maxout)
Seq2seq进阶,双向GRU