深度学习中的注意力机制
前言
注意力机制(Attention Mechanism)并非是在最近才被提出的,但它确实在最近几年才变得火热起来,尤其是谷歌2017年发表的那篇《Attention is all you need》,为人们提供了一种用Attention结构完全替代传统CNN和RNN结构的新思路,在那之后似乎有着层出不穷的Attention结构应用。Attention,顾名思义,是由人类观察环境的习惯规律总结而来的,人类在观察环境时,大脑往往只关注某几个特别重要的局部,获取需要的信息,构建出关于环境的某种描述,而Attention Mechanism正是如此,去学习不同局部的重要性,再结合起来。
听起来简单,实现起来其实也简单,我认为对于Attention可以有三种理解。
- 首先,从数学公式上和代码实现上Attention可以理解为加权求和。
- 其次,从形式上Attention可以理解为键值查询。
- 最后,从物理意义上Attention可以理解为相似性度量。
Sequence to Sequence
Attention Mechanism的大量使用源于机器翻译,机器翻译本质上是解决一个Sequence-to-Sequence问题,所以这里从Sequence-to-Sequence讲起,将要说明为什么需要Attention,哪里使用Attention,以及如何使用Attention。
Sequence to Sequence 的各种形式

如上图所示,Sequence-to-Sequence一般有5种形式,区别无非在于输入和输出序列的长度,以及是否同步产出,具体到每一个基本的模块,又能用不同的网络结构实现,包括CNN、RNN,但是万变不离其宗,即它们的实现基本上离不开一个固定的结构:Encoder-Decoder
结构。
Encoder-Decoder结构作为Sequence-to-Sequence任务的最佳拍档,常见于各种深度学习任务,从简单的时序预测分类,到GAN里都有它的影子。之所以强调这个结构,就是因为Attention解决了它的“分心问题”。下图给出了一个通用的Encoder-Decoder结构,它可能会给你某种强烈的既视感。Encoder负责从输入序列 X X X中学习某种表达 C C C,然后Decoder参考该表达 C C C生成每一个输出 Y i Y_i Yi。

Encoder-Decoder这种原始的做法是不太合理的,因为在生成每一个 Y i Y_i Yi时,Decoder参照的是同一个表征 C C C,它没有抓住重点。这有点像老师让一个小学生写作文,结果他写了一篇流水账一样。在机器翻译中,输出序列的每一个局部,往往只与输入序列的某个或几个局部有关。这里举一个例子,英译中任务:
显然答案是:
在Sequence-to-Sequence里我们要解决的是这样一个问题:
按照Encoder-Decoder的原始做法,我们为三个中文单词分别计算 C C C:
C 比 利 = h ( B i l l y ) + h ( e a t s ) + h ( a ) + h ( b a n a n a ) C 吃 = h ( B i l l y ) + h ( e a t s ) + h ( a ) + h ( b a n a n a ) C 香 蕉 = h ( B i l l y ) + h ( e a t s ) + h ( a ) + h ( b a n a n a ) C_{比利}=h(Billy)+h(eats)+h(a)+h(banana)\\ C_{吃}=h(Billy)+h(eats)+h(a)+h(banana)\\ C_{香蕉}=h(Billy)+h(eats)+h(a)+h(banana) C比利=h(Billy)+h(eats)+h(a)+h(banana)C吃=h(Billy)+h(eats)+h(a)+h(banana)C香蕉=h(Billy)+h(eats)+h(a)+h(banana)
这并不合理,为什么翻译输出“比利”这个词要关注整个原句子呢?只关注或者主要关注“Billy”这个词不是更好吗?同理,输出“吃”这个词时应该更多地关注“eat”,所以下面这种计算 C C C的方式要更加合理一些。
C 比 利 = 0.6 ⋅ h ( B i l l y ) + 0.2 ⋅ h ( e a t s ) + 0.1 ⋅ h ( a n ) + 0.1 ⋅ h ( b a n a n a ) C 吃 = 0.2 ⋅ h ( B i l l y ) + 0.6 ⋅ h ( e a t s ) + 0.1 ⋅ h ( a n ) + 0.1 ⋅ h ( b a n a n a ) C 香 蕉 = 0.1 ⋅ h ( B i l l y ) + 0.1 ⋅ h ( e a t s ) + 0.2 ⋅ h ( a n ) + 0.6 ⋅ h ( b a n a n a ) C_{比利}=0.6\cdot h(Billy)+0.2\cdot h(eats)+0.1\cdot h(an)+0.1\cdot h(banana)\\ C_{吃}=0.2\cdot h(Billy)+0.6\cdot h(eats)+0.1\cdot h(an)+0.1\cdot h(banana)\\ C_{香蕉}=0.1\cdot h(Billy)+0.1\cdot h(eats)+0.2\cdot h(an)+0.6\cdot h(banana) C比利=0.6⋅h(Billy)+0.2⋅h(eats)+0.1⋅h(an)+0.1⋅h(banana)C吃</