多种多样的self-attention
local/truncated attention 只看自己和前后一个向量之间的attention.
stride attention 自己选择看自己和之外的距离多远的向量的attention.
global attention, 在原来的sequence里面加入一个特殊的token(令牌), 表示这个位置要做global attention.
global attention中的token会做两件事:
- 当前的token他会attend到sequence里的每一个token, 来获得整个sequence的信息.
- 被所有的包括自己在内的其他token attend.
global attention的两种做法:
- 在原来的sequence中直接assign一些vector作为special token. eg将每个句子的开头或者句号作为special token.
- 外加额外的token. 不管input是什么sequence, 都硬加进去几个token, 这几个token会attend所有其他的token(包括自己). 所有其他的token也会attend到这几个token
在global attention中, 除了特殊的global token之外, 其他的token都不计算任何attention. 所有信息透过special token传递.
在实际应用中, 不是单独选一个attention手段, 而是全部都用. 在self-attention中是有multi-head的, 可以每个head用不同的手段. 比如: longformer: local+stride+global; big bird(self-attention的变形): random attention+local+global = longformer+random attention(随机选一些token,让他们彼此之间计算attention).
但是上述都是认为设定的减轻attention matrix计算量的手段. 有没有一种方法可以数据驱动(data driven)呢?
比如计算完了整个attention matrix, 有的地方大有的小.小的地方说明attention小, 说明对应的两个向量之间关系不大.
有没有可以直接估计哪里的大, 哪里的小, 把小的直接设置为零来减少计算量呢?
reformer和routing former如此做到.
他们用了clustering(聚类)的手段.
先把sequence的原始向量们计算出的query和key拿出来, 进行聚类, 把比较近的分类在一起, 比较远的则分别属于不同的clustering.
然后在计算attention matrix的时候, 只有在query和key被归类到同一个clustering种类里才会去计算他们的attention, 否则直接设置为0.
这样就可以加快attention matrix的计算.
但是即使是clustering也是基于人类的理解来判断的.
但是有一种方法可以纯粹通过learn的手段来决定选择计算哪些:
Sinkhorn sorting network
通过一个和attention matrix同样大的matrix来决定哪个位置需要计算, 1代表需要计算, 0代表不需要.
这个矩阵是如何得到的呢? 另外做一个network.
把最初input 的sequence的每一个位置的vector通过一个network产生一个vector. 其长度与sequence一样. 把这些产生的vector全部拼起来, 其大小和attention matrix相同.
得到了上述矩阵后, 还需要使其数值变为只有0, 1. 用一种方法把本来不是binary的matrix变成binary的. 而且这个过程可以微分. 所以最后这个矩阵是和整个network一起训练出来的.back propagation是可以走过这个从continuous到binary的计算过程的. 实际的计算方法请参考文献.
但是这样的话, 还是要对每一个input vector在另一个网路中进行计算, 这样会比原来的要快吗.
事实上, 在用来训练决定矩阵的神经网络中, 好几个input的vector会共用同一个vector. 如此来减少vector的计算量.
但是, 我们是否真的需要一个完整的attention matrix呢?
informer表示也许不需要. 该论文中进行了一个实验, 发现在计算的attention matrix中, 很多columns都是重复的redundant(冗余的).
论文中发现的过程是:计算了attention matrix的rank(秩), 发现他们都是low rank 的矩阵,发现很多column都是其他column一样的, 或者是线性组合.
那么如何把重复的拿掉, 计算小的column呢?
实际做法是:
本来有N个key, 从N个中只调K个key作为代表. 这样只需要算N*K的matrix即可.
接下来如何通过这个attention matrix来产生self-attention的output呢?
首先由N个value vector.
然后把K个key对第一个query计算出的attention weight, 对K个value vector做数乘然后相加. 第二个同样. 以此类推得到整个module的output.
但是为什么只选有代表性的key, 而不是query呢?
可以 但是这样会缩短output的sequence的length. 队友有的任务可以 有的任务不行.
如何选出有代表性的key:
在compressed attention中用CNN扫过sequence, 这样使sequence变短, 这个短的sequence就是有代表性的Key.
informer的做法:
对于原始的key vectors在一起可以看作一个d * N的矩阵. 将这个d * N的矩阵乘一个N * K的矩阵就会得到一个d * K的矩阵. 这个d * K的矩阵的每一个column就是key个有代表性的key. 实际上这k个新得到的vector就是原来的N个vector的linear combination。
这里引入一个概念:对于矩阵乘法A * B * C,设A为1 * 1000,B为1000 * 1,C为1 * 1000.
当先计算A * B,需计算1000次乘法并求和。时间复杂度为 1 0 3 10^3 103。得到一个常数。
再与C相乘时,将常数与C的每个元素相乘,时间复杂度为 1 0 3 10^3 103。
总体计算流程时间复杂度为 1 0 3 10^3 103。
但是如果先计算B * C。B的每个元素都要和C的所有元素相乘一次,时间复杂度为 1 0 6 10^6 106。得到1000 * 1000的矩阵。
再与A相乘时,为1 * 1000 * 1000 * 1000,此时A向量与后者矩阵的每个列向量进行乘法,每个列向量为 1 0 3 10^3 103,又与 1 0 3 10^3 103个向量相乘,总计 1 0 6 10^6 106的时间复杂度。
综上可以从直观角度得出,矩阵的计算顺序与计算量有很大影响。
下面用未知数来取代数字,进行更广泛的推导:
当A(a * b) * B(b * d)的两个尺寸的矩阵相乘的时候,所需要进行的乘法次数为a * b * d,即第一个矩阵的row乘第一个矩阵的column乘第二个矩阵的column。(括号内为矩阵的尺寸)
再将C乘在前面的话为C(c * a) * (a * d),本次乘法所需要的乘法次数为c * a * d。
总的次数为:a * b * d + c * a * d = a * d * (b + c)。
对于self-attention来说,
O
=
V
∗
K
T
∗
Q
O = V * K^T * Q
O=V∗KT∗Q。其中Q为输出的一组向量
b
\bm b
b组成的矩阵,V尺寸:
d
′
∗
N
d' * N
d′∗N;K尺寸:
d
∗
N
d * N
d∗N, Q尺寸:
N
∗
d
N * d
N∗d。带入上面的先让后两个相乘再乘V的话总次数公式则次数为:
(
d
+
d
′
)
∗
N
2
(d + d') * N^2
(d+d′)∗N2。其中V中的向量长度d’可以是和d相同的,也可以是不同的,这个问题在上面已经讨论过。
同理计算先前两个后与第三个相乘的话总乘法次数为: 2 d ′ d N 2d'dN 2d′dN。
综上,在self-attention中,可以看出前者需要的计算量大得多。
按照上述结论,可以得知如果可以先计算 V ∗ K T V*K^T V∗KT的话便可以极大减小计算量。但是attention matrix是通过 K T ∗ Q K^T*Q KT∗Q计算得来,而且按照self-attention的流程还需要将该矩阵进行sotf-max操作再与V相乘,这样的话如何先计算前两个向量的乘法呢?
先乘前两个再与后一个相乘,如此便是先撤去了soft-max,那如何再将其还原回来呢?
记住其中的dot-product计算得到scalar。
上图的原始计算过程是可以简化的。
首先存在函数 Φ \Phi Φ(函数的输出仍然是一个向量,与输入的 q \bm q q维度 不 一 定 相 同 \bm{不一定相同} 不一定相同)可以使得 e x p ( q k ) ≈ Φ ( q ) ∗ Φ ( k ) exp(\bm q \bm k) \approx \Phi(\bm q) * \Phi(\bm k) exp(qk)≈Φ(q)∗Φ(k)
对分子简化,其中的点乘也是元素相乘再相加得到scalar,因为 Φ ( q ) Φ ( k ) \Phi(\bm q) \Phi(\bm k) Φ(q)Φ(k)所得仍然都是向量,所以可以将其按照向量模式书写,如下图第二行右边。
相乘展开后,按照向量 q \bm q q的元素提取公因式,括号内的求和部分可以看作向量 v \bm v v的线性组合。
将括号中整体视作一个向量,则总共有向量 Φ ( q ) \Phi (\bm q) Φ(q)的维度的个数的向量。则整个式子可以视为一组向量乘以 Φ ( q ) \Phi (\bm q) Φ(q)。
上图可以注意到,除了 Φ ( q ) \Phi (\bm q) Φ(q)与输出向量 b \bm b b的计算过程中与位置有关之外,分子分母的另外两部分(图中蓝色和黄色部分)都和当前是第几个向量无关,所以可以计算一次然后应用到所有的向量计算中。
上图为将计算过程在神经网络中展示的状态。
上图是对此前的简化计算的一种理解。
个人理解: 每一个向量v利用同一维度的所有k的元素来线性组合得到的向量是计算出输入的sequence的那些向量的某一个重要的template(模板),在针对这些template每个位置选择不同的query向量的每个元素进行线性组合来作为输出,提取该位置认为重要的template。 |
最后的问题:如何将 e x p ( q ∗ k ) exp(\bm q * \bm k) exp(q∗k)拆开呢?一下每个论文都有各自的方法。
该方法将整个attention matrix视为参数,甚至不需要计算,直接参与神经网络的参数迭代。但是这与我们此前对self-attention的认识不同,本来是认为对于不同的sequence,需要计算出不同的attention matrix,但是在这个方法中,所有人都用同一个训练好的矩阵。
这让我们不禁思考到底attention matrix的价值是什么。处理sequence一定需要attention吗?
上图进行了讨论