Seq2Seq(Sequence to Sequence) 是一个处理序列问题的模型,传统的 RNN 或 LSTM 只能处理 输入及输出是定长即 一对一或多对多的问题,而 Seq2Seq 则能处理一对多的问题,它也是 RNN 最重要的一个变种:N vs M(输入与输出序列长度不同),
如:输入(你是谁),输出(我是某某某)。因此 Seq2Seq 在很多方面也得到应用:机器翻译、QA 系统、文档摘要、图片描述问题
基本结构
上图为最常见的 Seq2Seq 模型,又叫做 编码-解码 模型,主要的思想就是二个 RNN,一个 RNN 作为 Encoder,另一个 RNN 作为 Decoder
Encoder 主要负责将输入序列压缩成指定长度向量,这个向量可以看成是这个序列的语义,这个过程叫做编码
获取词向量的方式分为多种:
- 一种是直接将最后一个输出的隐状态作为语义词向量 C,假设网络单元为 f , 那么 hidden state为 h t = f ( x t , h t − 1 ) h_t = f(x_t,h_{t-1}) ht=f(xt,ht−1)
- 第二种是对最后一个隐状态作一次变换得到语义词向量,如取平均等方法
Decoder 主要负责将语义词向量分成指定的序列,这个过程叫解码
-
最简单的方式是将 语义词向量作为初始状态输入到 RNN 中,得到输出序列,语义词向量 C 只作为初始状态参与运算,与后面的运算无关
-
另一种将语义词向量参与所有时刻的运算,上一时刻的输出仍然作为当前时刻的输入参与运算
Attention 机制
在上面的 decoder 步骤中我们可以看到,各个时刻使用的都是相同的 content vector(词语义向量),而 Attention 是一种让模型在解码过程中学习集中关注输入序列特定部分的模型
以 英-中 翻译为例,给定 "Cat chase mouse ", 输出 “猫捉老鼠”,在翻译 “mouse” 时,我们发现 “cat”、“chase”、“mouse” 对 “老鼠” 的关注度都是一样的,但实际上 “mouse” 才是对 “猫” 影响最大的,因此我们需要在解码时,对输入序列在不同时刻分配不同的注意力,这就是注意力机制的由来
在 encoder 中我们使用的都是同一个语义向量 C,我们想使用不同的向量来表示不同时刻的 C,那么可以写成 C 1 C_1 C1、 C 2 C_2 C2、 C 3 C_3 C3 … C i C_i Ci
用 C i C_i Ci 来表示 i 时刻的语义向量,我们之前用过最后一个值或者平均值,很容易的想到可以用加权求和来表示 c i = ∑ j = 1 T ∂ i j h j c_i = \sum_{j=1}^{T}\partial_{ij}h_j ci=∑j=1T∂ijhj ,其中 h j h_j hj 表示 encoder 中的隐层状态, ∂ i j \partial_{ij} ∂ij 表示的是一个分布概率,即 softmax 值
∂ i j \partial_{ij} ∂ij 既然是一个 softmax 值,那么可以写成 ∂ i j = e x p ( e t j ) ∑ k = 1 T e x p ( e t j ) \partial_{ij} = \frac{exp(e_tj)}{\sum_{k=1}^{T}exp(e_tj)} ∂ij=∑k=1Texp(etj)exp(etj) ,其中 e t j e_tj etj 表示的是当前时刻 encoder 的隐层状态 h t h_t ht 与 上一时刻 decoder 的隐层状态 s t − 1 s_{t-1} st−1 的相似程度
e t j e_{tj} etj 既然表示的是 h1~ht 之间的相似程度,那么我们可以用一个 score 函数来表示 e i j = s c o r e ( s t − 1 , h j ) e_{ij} =score(s_{t-1},h_j) eij=score(st−1,hj),score 函数可以是多样的,后面会介绍
为了方便理解,我们可以结合下面的示意图来更清楚的了解其中的步骤:
Encoder 输出状态 h t h_t ht 表示,Decoder 输出状态 S t S_t St 表示
- 首先,我们用一个 score 函数来表示 h t h_t ht 与 S t S_t St 之间的相似度,用 e t j e_{tj} etj 来表示:
e t j = s c o r e ( S t − 1 , h j ) e_{tj} = score(S_{t-1}, h_j) etj=score(St−1,hj)
- 通过 softmax 作归一化操作方便计算:
∂ t j = e x p ( e i j ) ∑ k = 1 T e x p ( e t k ) \partial_{tj} = \frac{exp(e_{ij})}{\sum_{k=1}^{T}exp(e_{tk})} ∂tj=∑k=1Texp(etk)exp(eij)
- 再对归一化的值进行一个加权求和即可得到每个时刻的 content vector:
C t = ∑ j = 1 T ∂ t j h j C_t = \sum_{j=1}^{T}\partial_{tj}h_j Ct=j=1∑T∂tjhj
- 最终我们根据每个时刻的 C t C_t Ct 可以计算 Decoder 的输出 S t S_t St:
S t = f ( S t − 1 , y t − 1 , C t ) S_t = f(S_{t-1}, y_{t-1}, C_t) St=f(St−1,yt−1,Ct)
至此,attention 机制的结构与运行方式就很清晰了,在上面我们使用了一个 score 函数来表示
h
t
h_t
ht 与
S
t
S_t
St 之间的相似度,一般来说常用的也就下面的几种:
s
c
o
r
e
(
s
t
−
1
,
h
j
)
=
{
s
t
−
1
T
h
j
s
t
−
1
T
W
a
h
j
v
a
T
t
a
n
h
(
W
a
[
s
t
−
1
;
h
j
]
)
score(s_{t-1},h_j) = \begin{cases}s_{t-1}^{T}h_j\\s_{t-1}^{T}W_ah_j\\v_a^{T}tanh(W_a[s_{t-1};h_j])\end{cases}
score(st−1,hj)=⎩⎪⎨⎪⎧st−1Thjst−1TWahjvaTtanh(Wa[st−1;hj])
总结
此处介绍的是最基础的 attention 结构,具体细节仍然需要在代码中仔细理解,还有 self-attention 机制及针对不同 score 函数的选取而得到的不同的注意力机制,留在以后的文章中再学习