视频链接:李宏毅 self-attention讲解上
参考文章:RNN详解
Attention详解
彻底搞懂Attention机制
知乎Transformer详解
传统的编码器解码器架构
一般最简单的编码器-解码器架构都是基于RNN模型的,编码器将输入的序列转换为语义编码C,语义编码C中包含了整个输入序列的信息。
解码器也是采用RNN模型,计算每一时刻的输出
y
t
y_t
yt时,除了上一时刻的输出
y
t
−
1
y_{t-1}
yt−1和上一时刻隐藏层的值
h
t
−
1
h_{t-1}
ht−1还需要语义编码C。
h
t
=
f
(
h
t
−
1
,
y
t
−
1
,
C
)
h_t = f(h_{t-1},y_{t-1},C)
ht=f(ht−1,yt−1,C)
y
t
=
f
(
h
t
,
y
t
−
1
,
C
)
y_t = f(h_t,y_{t-1},C)
yt=f(ht,yt−1,C)
h
t
h_t
ht表示当前t时刻的隐藏层的数值,
y
t
−
1
y_{t-1}
yt−1为上一时刻预测的输出,每个时刻语义编码C都是相同的。
这种编码方法,无法体现对一个句子序列中不同语素的关注程度。在自然语言中,一个句子中的不同部分是有不同含义和重要性的。比如:I hate this movie。如果是做情感分析的应用场景,训练的时候明显应该对hate这个词语做更多的关注。如果采用上述的方法则达不到这种效果。
注意力机制
人类在感知环境的时候(比如看一张图片或者一个句子),大脑让我们能够分清哪部分是重要的,哪部分是次要的,从而聚焦更重要的方面以获得对应的信息。我们在设计神经网络模型的时候,希望模型也具有这样的能力。这就引入了注意力机制。
在计算机算力资源有限的情况下,注意力机制可以将注意力集中在有用的信息上,从而减少了在噪声中花费的时间。
上图是引入了Attention机制的编码器-解码器架构。此时编码器的输出不再是只有有一个单一的语义编码C。而是具有多个C1,C2,C3这样的编码。在预测Y1的时候,使用C1作为语义编码,在预测Y2的时候,使用C2作为语义编码,这样就模拟了人类的注意力机制。其中
C
i
C_i
Ci就是对序列中的不同语素,赋予不同的权重吗,最后进行加权平均得到的结果,我们称为中间语义表示。Attention机制的关键是将固定的中间语义表示C换成了根据当前输出单词而变换的中间语义表示
C
i
C_i
Ci
中间语义表示
下面我们来介绍一下中间语义表示是如何进行计算的。
注意力机制描述了(序列)元素的加权平均值,其权重是根据输入的query和元素的键值进行动态计算的。具体地,在注意力机制中,有4个概念需要明确。
- Query:Query(查询)是一个特征向量,描述我们在序列中寻找什么,即我们可能想要注意什么。
- Keys:每个输入元素有一个键,它也是一个特征向量。该特征向量粗略地描述了该元素“提供”什么,或者它何时可能很重要。键的设计应该使得我们可以根据Query来识别我们想要关注的元素。
- Values:每个输入元素,我们还有一个值向量。这个向量就是我们想要平均的向量。
- Score function:评分函数,为了对想要关注的元素进行评分,我们需要指定一个评分函数f。该函数将查询和键作为输入,并输出查询-键对的得分(注意力权重)。它通常通过简单的相似性度量来实现,例如点积。
a
i
=
exp
(
f
a
t
t
n
(
k
e
y
i
,
q
u
e
r
y
)
Σ
j
exp
(
f
a
t
t
n
k
e
y
j
,
q
u
e
r
y
)
)
out
=
∑
i
a
i
⋅
v
a
l
u
e
i
\begin{aligned}a_i&=\frac{\exp(f_{attn}(key_i,query)}{\Sigma_j\exp(f_{attn}key_j,query))} \\\text{out}&=\sum_ia_i\cdot value_i\end{aligned}
aiout=Σjexp(fattnkeyj,query))exp(fattn(keyi,query)=i∑ai⋅valuei
注意力机制的目的是解码器在进行解码的时候,对输入数据有着不同权重的参考。
如图所示,来自于Decoder模块中的查询首先和每个encoder中的键进行点集,得到一个评分函数,该评分函数通过softmax函数得到评分权重,将评分权重和encoder中的值进行相乘得到最终的输出,这个输出也就是我们所说的中间语义表示。
公式化结果
- 首先利用RNN模型获得序列的隐层状态 h 1 , h 2 , . . . , h n h_1,h_2,...,h_n h1,h2,...,hn
- 如果当前Decoder阶段已经到达 S i − 1 S_{i-1} Si−1需要进行下一个 S i S_i Si的预测了,现在计算每一个输入位置 h j h_j hj对当前位置的影响 e i , j = a ( s i − 1 , h j ) e_{i,j}=a(s_{i-1},h_j) ei,j=a(si−1,hj)
- 对评分函数进行归一化处理,得到评分权重分布 α i j = exp ( e i j ) ∑ k = 1 T x exp ( e i k ) \alpha_{ij}=\frac{\exp\left(e_{ij}\right)}{\sum_{k=1}^{T_x}\exp\left(e_{ik}\right)} αij=∑k=1Txexp(eik)exp(eij)
- 利用评分权重加权求和,得到相应的中间语义表示 c i = ∑ j = 1 T x α i j h j c_i=\sum_{j=1}^{T_x}\alpha_{ij}h_j ci=∑j=1Txαijhj
- 计算Decoder中第i个隐藏层的输出 s i = f ( s t − 1 , y t − 1 , C ) s_i =f(s_{t-1},y_{t-1},C) si=f(st−1,yt−1,C)
编码器中的隐层状态又可以看作是Attention机制的keys和values
Attention模型优缺点
优点
- 速度快(据说可以在Decoder阶段实现并行化,但是目前没弄懂,t时刻的隐状态需要t-1时刻的隐状态,这样如何并行呢)
- 效果好:注意力机制能够获取到局部的重要信息,能够抓住重点
缺点
- Encoder 部分仍然依赖于RNN结构,对于中长距离之间两个词之间的关系没有办法很好的获取
自注意力模块
自注意力机制和传统的注意力机制不同,传统的注意力机制是根据源端和目标端的隐变量计算注意力的,结果是源端的每个词和目标端的每个词之间的依赖关系。
自注意力机制,首先分别在源端和目标端进行自身的注意力,捕捉到其自身的词与词之间的依赖关系,之后将源端得到的自注意力加到目标端中的attention机制中,称为Cross-Attention,以捕获源端和目标端词与词之间的依赖关系。
按照Transformer论文计算自注意力机制的流程
- 将输入单词转换为嵌入向量 a i a^i ai
- 根据嵌入向量与权重参数 W q , W k , W v W^q,W^k,W^v Wq,Wk,Wv 相乘得到𝑞,𝑘,𝑣三个向量
- 为每个向量计算一个分数: a i , j = q i ⋅ k j a_{i,j}=q_i\cdot k_j ai,j=qi⋅kj
- 归一化处理 a i , j ′ = s o f t m a x ( q i ⋅ k j d ) a_{i,j}^\prime=softmax(\frac{q_i\cdot k_j}{\sqrt d}) ai,j′=softmax(dqi⋅kj)
- 与 v a l u e value value进行点乘,得到加权的每个输入向量的评分 v v v
- 相加后得到最终的输出结果𝑧=∑𝑣
self-attention 通过
Q
∗
K
Q*K
Q∗K得到词与词之间的相关性矩阵,然后根据此相关性与V进行加权求和,这样最终输出的向量主要就是与之相似的词的向量构成的,与之不相似的向量对其的贡献度可以忽略不计。(数值很小,几乎可以忽略不计)
最终计算出来的向量就会变成,如果两个词很相似,那么向量的相似度也很近,就起到了语义层面的意义,这样词与词之间就因语义的不同而自动区分开来了。
多头自注意力机制
由于每个向量可能会有不同的相关性,可以利用
q
q
q去寻找不同的相关性。得到不同相关性的输出。