文章目录
前言
研究生开学一个月啦,暑假学习的东西太不深入,也不系统。九月阅读的论文有时间也需要找个时间整理。
我看了吴恩达老师的序列模型课程和台大李宏毅老师的课程,他们的课程都提到了Atention的内容,吴恩达老师的课程讲述得比较浅,只是大概给出一个概念,李宏毅老师讲述得比较细致,同时Transformer的部分讲得也很好,如果想全面地学习这一部分,更推荐李宏毅老师的课程。
国庆节前学习了Attention、Transformer,想要具体地整理一遍,如果能梳理清晰就应该是真正理解了吧
首先想说明:Encoder-Decoder是一种架构。Seq2seq的结构与En-Dn基本相同,只是可以理解为seq2seq是具体的模型而en-dn是一种抽象概念。seq2seq有多种类型:N vs 1,N vs N,基于RNN,基于LSTM…Transformer模型是seq2seq结构的一种具体的模型,而且是完全基于Attention机制的seq2seq结构
一、Attention
encode过程会生成一个中间向量C,用于保存序列的语义信息,但这个向量的长度是固定的,当输入的原序列的长度太长时,向量C无法保存全部的语义信息,context信息受到了限制,也限制的模型的理解能力。
LSTM只能在一定程度上缓解了长距离依赖问题。
Attention就如同字面意思,把注意力放在重要的内容上面,而不是对重要的内容和不重要的内容“一视同仁”,重要的多关注,不重要的少关注,自然提升了理解能力和效果。
1、attention
attention 的基本原理可以理解为公式:
A
t
t
e
n
t
i
o
n
(
Q
u
e
r
y
,
S
o
u
r
c
e
)
=
∑
m
a
t
c
h
(
Q
,
K
)
∗
V
Attention(Query,Source)=\sum match(Q,K)*V
Attention(Query,Source)=∑match(Q,K)∗V
以下图为例:
在这张图中,h是输入的语义编码,h就是key,z是我们要去匹配的内容,也就是query,match是一个函数,可以自行选择,match可以是相似度函数,比如余弦相似度,也可以是一个简单的神经网络。
match后计算出的
α
\alpha
α 就可以理解为在
t
t
t时,花在
h
t
h_{t}
ht上的注意力的数量。
对每个
h
t
h_{t}
ht进行计算得出
α
\alpha
α后,在这里进行了一个
s
o
f
t
m
a
x
softmax
softmax计算,如图中:
c
=
∑
α
∗
h
c=\sum\alpha*h
c=∑α∗h
在这个机器翻译的例子中,
h
h
h在作为key的同时,又作为value参与到最终
c
c
c的计算中,在最初的学习中我还弄不明白这个q,k,v所指的内容,后来有人说明在此 key 和 value 都是指每个单字所对应的语义编码
h
h
h,我才将最开始提到的公式与例子对上。
这里的
∗
*
∗符号不一定是简单的相乘,李宏毅老师的课件里提到了两种计算方法:
-
一种是点乘(Dot-product):
-
另一种是Additive:
但是我自己还没有具体实现过代码,所以代码的实现还不是很清楚。查阅了一下pyTorch的官网,torch好像是已经有封装好的multi-head attention了,有时间还要再看看。
另外, z z z也需要说明, z 0 z_{0} z0可以认为是<SOS_token>的嵌入向量,如果嵌入是随机初始化,那么 z 0 z_{0} z0就是随机的, z 0 z_{0} z0与 c 0 c_{0} c0一起计算得出 z 1 z_{1} z1,后面的 z t z_{t} zt都是对应的decoder的状态。
之前一直想不通“明明还没翻译/生成出内容,为什么就可以作为q来计算attention了”,实际上参与计算的是decoder的状态,是可以计算出来的。
在计算过程中,
h
t
h_{t}
ht与
z
i
z_{i}
zi分别计算,也就是说,decoder每生成一个词都要遍历一遍input的所有内容,但是因为Transformer中,encoder是一次性获得整个句子的,所以它可以做到并行计算每个
h
t
h_{t}
ht的注意力(当然,在Transformer中用的不是简单的Attention)
Attention计算得到的
α
t
\alpha^t
αt是只限于当前sequence的,,而例如CNN,求得的weight是整个网络的,所有输入进去的内容都使用这个weight。
2、self-attention
在了解了上面所述的attention的原理后,接下来是另一种:self-attention。
简单来说,attention的query和source是不同的,而self-attention中,query和source都是它自身。
self_attention将整个sequence的内容通过加权操作融合到每个词的向量表示中,如下图所示:
上面这张图描述的是整个self-attention layer工作的状态,
a
t
a_{t}
at输入进self-attention层,经过计算,获取了整个sequence的信息后,叠加上这个信息生成
b
t
b_{t}
bt。
在这张图中,首先由于self-attention层的输入还可能是某一层的输出,是一中间向量,所以记
x
x
x为它的输入,经过变换,比如嵌入等操作,得到
a
t
a^t
at,在这里,
a
t
a^t
at既是source,又是query。
每个
a
t
a^t
at经过计算得到相应的
q
t
q^t
qt,
k
t
k^t
kt,
v
t
v^t
vt,计算公式如下:
q
t
=
W
q
∗
a
t
q^t=W_{q}*a^t
qt=Wq∗at
k
t
=
W
k
∗
a
t
k^t=W_{k}*a^t
kt=Wk∗at
v
t
=
W
v
∗
a
t
v^t=W_{v}*a^t
vt=Wv∗at
这3个矩阵是待学习的参数。
在学习到这里的时候我进入了一个误区,我把参数矩阵 W q W_{q} Wq等和 α \alpha α弄混了,Attention计算得到的注意力数量(或者说匹配度权重) α \alpha α是仅针对当前sequence的,而这个参数矩阵是用于计算 q t q^t qt等等的,并不是针对某一sequence,通过学习是可以学习到的。
并且如下图所示,计算
q
t
q^t
qt、
k
t
k^t
kt、
v
t
v^t
vt的过程是可以并行的。
得到q,k,v后,就要计算注意力的数量
α
\alpha
α。
α
t
,
i
=
q
t
∗
k
i
\alpha_{t,i}=q^t*k^i
αt,i=qt∗ki
由此,得到四个
α
\alpha
α,很多例子中都用的
s
o
f
t
m
a
x
softmax
softmax,但其实不一定必须使用这个,还可以使用其他的方法,比如
R
e
L
u
ReLu
ReLu,可以尝试使用不同的方法。再看下图:
在此,得到
α
^
t
,
i
\hat{\alpha}_{t,i}
α^t,i后,计算得到
b
t
b^t
bt。这里的
b
t
b^t
bt就是前面说的经过s-a层后带有整个sequence信息的向量。
b
t
=
∑
α
^
t
,
i
∗
v
i
b^t=\sum\hat{\alpha}_{t,i}*v^i
bt=∑α^t,i∗vi
这一步就是所谓的融合(Extract information based on attention scores)。
而且,生成
b
t
b^t
bt的操作也是可以并行的。
总结起来,self-attention的过程就是对一个seq里的多个input/hidden,分贝计算它们之间互相的相关性的过程。
3、multi-head attention
multi-head attention简单来说就是在self-attention的基础上,将
q
t
q^t
qt等等通过计算得到更多个
q
t
,
i
q^{t,i}
qt,i。多头的注意力可以使网络获取到更丰富的信息。
如下图:
和self-attention一样,通过参数矩阵获取到
q
t
q^t
qt等等,但是变化在于,
q
t
q^t
qt又会再次经过计算得到两个
q
t
,
i
q^{t,i}
qt,i
q
t
,
i
=
W
q
,
i
∗
q
t
q^{t,i}=W^{q,i}*q^t
qt,i=Wq,i∗qt
k
t
k^t
kt、
v
t
v^t
vt也是相同操作。
接下来,每个
q
t
,
i
q^{t,i}
qt,i与每个
k
t
,
i
k^{t,i}
kt,i进行运算,以上图中的例子解释,
q
1
,
1
q^{1,1}
q1,1只和
k
1
,
1
k^{1,1}
k1,1、
k
2
,
1
k^{2,1}
k2,1、
k
3
,
1
k^{3,1}
k3,1相乘,而不会与
k
1
,
2
k^{1,2}
k1,2、
k
2
,
2
k^{2,2}
k2,2相乘。
q
q
q与
k
k
k相乘后,与self-attention一样,再与
v
v
v进行运算得到最终的
b
b
b,与
v
v
v相乘也遵守上面的规则,
q
1
,
1
q^{1,1}
q1,1与
k
1
,
1
k^{1,1}
k1,1的结果只和
v
1
,
1
v^{1,1}
v1,1进行运算,而不和
v
1
,
2
v^{1,2}
v1,2运算。
这也可以理解为,每个head是单独进行运算的,不同的head之间不会进行运算。 每个head就是不同类型的相关性。
计算完成后,如图,
a
i
a^i
ai得到2个结果:
b
i
,
1
b^{i,1}
bi,1、
b
i
,
2
b^{i,2}
bi,2(因为这个例子里有2个head)
将
b
i
,
1
b^{i,1}
bi,1、
b
i
,
2
b^{i,2}
bi,2连接起来,乘上一个output参数矩阵:
W
o
W^{o}
Wo,得到最终的
b
i
b^{i}
bi:
b
i
=
W
o
∗
[
b
i
,
1
,
b
i
,
2
]
b^{i}=W^o*[b^{i,1},b^{i,2}]
bi=Wo∗[bi,1,bi,2]
4、Masked self-attention
masked self-attention是在Transformer的decoder中提到的,这里也是由于Transformer自身的结构所引出的一种结构,因为encoder中获得
x
t
x^t
xt是一次性全部获得的,而decoder中的self-attention层,是一个一个依次获得
x
t
x^t
xt的,所以,在得到
x
t
x^t
xt时,还不能得知
x
t
+
1
x^{t+1}
xt+1,因此,生成
b
1
b^1
b1时只考虑
a
1
a^1
a1,生成
b
2
b^2
b2时,考虑
a
1
a^1
a1、
a
2
a^2
a2……即不考虑右边的内容。如下图:
二、Transformer
我在学习Transformer时最初是阅读了论文Attention is all you need,但在阅读过程中发现对Attention mechanism不甚明了,所以又转回头学习attention。
学习完attention后我觉得理解transformer也不那么吃力了。
我个人觉得不太明白的地方是residual:
residual即为残差连接,这里我看了一个知乎的专栏:残差连接,这部分内容应该放在神经网络的笔记中整理,总结起来就是residual能够防止梯度消失,缓解神经网络的退化,但这部分内容我不是很清楚,之后要再找时间学习。
如上图,左侧为encoder结构,右侧为decoder结构。两边的multi-head attention都有加上位置信息的嵌入向量。
这里解释一下,我们在学习attention的时候就能看出来,attention生成新内容时并没有考虑sequence的位置信息,而在序列中,位置信息是很重要的,所以我们可以对输入向量进行一个运算,使它与位置信息结合起来,使得attention层除了输入内容以外还可以接收到位置信息。
图中所示的Add&Norm是指residual和Layer Norm。
右侧decoder中使用的是masked multi-head attention正如上面所解释的,是由于decoder自身的逻辑才使用的。
上图所示,是decoder中第二层multi-head attention,从图中我们可以看到,这一层的输入既有来自于第一层masked multi-head attention的内容,又有来自encoder的内容。这个操作是,由masked m-h a 层输出的向量,乘上一个矩阵后,视为q,与它一起运算的k,v是来自于encoder的输出,这一操作是cross attention。
字典中每个字都有一个one-hot向量,decoder生成的向量经过
s
o
f
t
m
a
x
softmax
softmax之后得到的最终结果,我们需要做的就是使最终结果和正确答案之间的交叉熵最小。
如上图,在训练过程中,会给decoder输入正确答案,我们的训练目标就是使它们的交叉熵在一起最小(teacher forcing)。
这里我们使用的是交叉熵来进行优化,也可以使用BLEU分数,但是两个句子之间的BLEU分数很难微分,我们可以使用强化学习。(这里李宏毅老师说的特别可爱,不好去optimize的就把它当作强化学习的award)
另外还有一些在训练transformer时可以使用的方法,我就不一一记录了,这个在实际应用时再具体学习就好,根据不同的应用环境选择更合适的方法。
在这里我记录一下老师提到的:copy mechanism,适用于对话生成方面。
总结
以上是我整理的attention方面的知识,以及一部分transformer的原理,但还需要真正把transformer应用到项目里才能彻底学会,接下来有时间想应用transformer做一个简单的项目。
引用:
台湾大学李宏毅教授课件
论文Attention is all you need