NLP-Word Embedding-Attention机制

attention机制是个坑。
要明白attention机制,首先要明白seq2seq
要明白seq2sql,首先要明白RNN
要明白RNN,首先要明白MLP和BP算法
这是attention机制的前坑

明白attention机制后,要明白self-attention,以及attention在诸多领域的应用
明白self-attention后,要明白Transformer
明白transformer后,要明白Bert
这是attention机制的后坑

1,seq2seq (Sequence-to-sequence)

即一个序列生成另一个序列。最直观的就是机器翻译,一句英文翻译成一句中文。或者应答,问一句答一句。
模型结构:输入数据连接一个encoder(编码器)得到编码输出又称中间状态C然后连接到一个decoder(解码器)最后解码器的输出作为模型输出

seq2seq的一种表示

这里的encoder,decoder都是RNN网络。可以是LSTM,或者是GRU。总之这样的网络可以循环接收输入的每一个词w,然后为每一个词都计算得到一个输出h(w)。通常,这个h(w)可以连同下一个w一起做输入,最终的语义编码c可以是最后一个词的h(w)。
理解循环神经网络RNN以及seq2seq,可以这样抽象理解:

RNN是某种外星生物,like eating itself`s shit。
现在RNN小E要吃一筐水果,吃第一个水果a1,拉出来h(a1)(其实是RNN的隐层状态h(x))
然后连同h(a1)和下一个水果a2有一起吃进去,拉出h(a2)(其实是RNN的隐层状态h(x))
然后连同h(a2)和下一个水果a3有一起吃进去,拉出h(a3)(其实是RNN的隐层状态h(x))
按照这样的方法一直吃到最后一个an,拉出h(an)(其实是RNN的隐层状态h(x))
这里啰嗦几句细节,
1,小E觉得吃a1的时候仿佛少了点什么,于是利用外星高科技——Random-Shit-Initializer获取到h(a0)(其实是随机初始化一个隐层状态)连同a1一起吃下。
2,小E利用外星高科技——Shit-Copier 保留了每个h(ai),这样别人如果要中间产物,他也可以骄傲地拿出来
此时,小E的朋友小D走过来,发现小E把所有的水果都吃完了,不开心。二话不说,把小E刚拉的h(an)吃了下去,妄图知道小E到底吃了什么。
小D重复了小E的动作,只不过略有差别:
小D吃下h(an),拉出来h(h(an)),送入外星高科技Shit-Identifier 获得b1(其实是RNN的每轮输出y(x))
然后连同b1和h(h(an))一起吃下,拉出来h(b1),送入Shit-Identifier 获得b2(其实是RNN的每轮输出y(x))
然后连同b2和h(b1)一起吃下,拉出来h(b2),送入Shit-Identifier 获得b3(其实是RNN的每轮输出y(x))
直到小D实在吃不动了,最终拉出h(bm-1),送入Shit-Identifier 获得bm(其实是RNN的每轮输出y(x))
小D骄傲地说,你分别吃了b1,b2,...bm。小E露出了赞许的目光。
这里啰嗦几句细节,
1,小D觉得吃h(an)的时候仿佛少了点什么,于是利用外星高科技——Random-Shit-Initializer获取到b0(其实是随机初始化一个输入)连同h(an)一起吃下。
这里最重要的是连接小E和小D的h(an)。我们可以认为它就是上图中的C。

seq2seq问答模型

也可以发现,RNN星球的外星人,执着于吃自己的隐层状态h(x),同时,其隐层状态可以继续计算得到输出y。当然,他们也会去吃y,当手头没有输入时他们会把前一个y当输入吃掉,就像小D。当手头有输入时,他们可以不吃y而吃输入。就像小E。至于为什么这样,可能是他们当地人的习惯吧,总得有点下shit菜不是。

2,Attention机制

小E和小D是RNN星球的外星小伙伴,这个大家都知道了
小E吃了一筐水果小D来了发现没了很不开心,这个大家都知道了
当小D骄傲的说出小E具体吃了什么时,他们发现,小D说的没有那么准。
两人对视一会,发现问题:小D只吃了小E最后的h(an),那最后的h(an)携带的前面的信息就很少了。要改变!要把小E所有的h(ai)都用上!而且,要尽量找到对应关系,例如预测第一个时,要尽量用到第一个h(a)的信息。


attention

具体是怎么做的呢?
小D是这么想的,首先,让小E把他拉的所有h(a)都给他。即[h(a1),h(a2),h(a3)...h(an)],每个h(ai)都是一个向量,所以这些组成了一个矩阵K
然后,小D每拉出一个h(b)就去这个矩阵分别计算该h(b)和每个h(a)的相关度,得到一系列分数,按照分数大小来组合这些h(a)从而得到一个专属于该h(b)的C。下图中的a即是表示对应的分数


image.png

现在小D手上有一个h(b1)还有一个C,其实还有上一个输出b1呀。小D只习惯一次吃俩,这3个怎么办?
他选择坚守自己的习惯。还是吃b1和h(b1)。但是C怎么办?
他把该h(b)和C合起来(向量拼接)送入外星高科技Shit-Identifier,计算得到b2这样,相当于虽然吃了b和h(b)但b中包含着C的信息,没有浪费。
image.png

还有一个问题,这里的分数,通过softmax做了一个归一化变成了加和为1的概率值

还有一个问题,怎么计算相关度?cos相关度,点乘,皮尔森相关等都可以
最后一个问题,初始的隐藏状态是什么,因为没有用到非attention机制的h(h(an))。老规矩,随机初始化。

所以,attention机制,像是一种配方,根据当前不同位置来调配encoder一系列隐藏状态h(ai)的组合比重,这显然是很有道理的,因为就拿翻译来说,词的顺序往往是对应的。

简言之,encoder获取状态矩阵K,decoder根据每一轮隐藏状态h计算注意力分数获得注意力向量C,拼接h和C获得输出y,y和h进入下一轮decode获得新的h。。。

3,self-Attention

上一步有个矩阵K,来自encoder。而来自decoder的h类似于一个查询(query)。这个查询分别与K中的不同向量h(a)(key)计算相关度得到分数(score),然后利用分数做权重与对应的h(a)(value)加权求和得到自己想要的结果C。
这里我为什么要写query,key,value呢?是为了讲解self-attention。注意这里key和value都是指h(a)即encoder的状态矩阵。
那如果这个query不来自decoder而来自encoder本身呢?即自己查自己?
有了query,key,value概念之后,就比较好理解self-attention了。
首先,输入的词汇(小E吃的水果也好,翻译中一句话分成的一组词也好)都是要embedding成一个固定长度的向量x才输入模型的。即对于一句话的所有词,组成了一个输入矩阵X。
然后,我们随机生成3个矩阵Q,K,V对应query,key,value
对于一个输入x,
用x点乘Q得到query
用x点乘K得到key
用x点乘V得到value
这样,对于一句话中的所有x,都可以得到对应的query,key,value


image.png

有了这些就好办了
每个x,都可以用自己的query去和其他key计算score,然后用该score和对应的其他value来计算自己的注意力向量C。经过这样的计算,x变成了C。


image.png

上图中的z即为C。而score到softmax之间的步骤是一些tricks,不用管。

同样,可以多叠加几层self-attention,用同样的操作不同的QKV矩阵由c变成cc,变成ccc
这就是self-attention。

self-attention像是一种向量转换。x变为c,维度没变,值变了。而同时,这种转变又蕴含了x与上下文x之间的关系。rnn也可以实现由x变为另一个向量,同时也考虑了上下文关系,但是,他存在循环神经网络的弊端,无法并行。而self-attention组成的transformer则可以实现并行运算。即,他不需要等待下一个状态h计算出来再计算C,而是直接通过QKV矩阵和当前x计算所得。
那QKV怎么得到?随机初始,训练所得。

4,参考

https://caicai.science/2018/10/06/attention%E6%80%BB%E8%A7%88/
https://jalammar.github.io/visualizing-neural-machine-translation-mechanics-of-seq2seq-models-with-attention/
https://zhuanlan.zhihu.com/p/37601161
https://jalammar.github.io/illustrated-transformer/

推荐阅读更多精彩内容