NLP基础(注意力机制,多头注意力,层归一化,位置编码,掩码注意力) RNN中的注意力机制和很多人想的不一样注意力机制不是一诞生就取代了RNN相反开始RNN和注意力机制是同时存在的。前面说过RNN的问题是循环中会把信息反复压缩这样距离远的token的占比会越来越低表现为模型输出时忘记前面的内容为了解决这个问题提出过LSTM引入长期记忆但是这个做法效果提升有限太长了该忘还是忘而且计算更复杂。如下图正常的encoder-decoder模型生成一个token比如说y1时输入只有上一个隐状态s0以及上一个token模型对编码阶段的记忆完全由s0提供但是s0是反复压缩后的结果记不住前面的x0,x1信息。于是一个自然的想法是既然我们想让模型记住前面token的信息为什么不直接把前面token对应的隐状态传递给当前层前面每个token的因转台都需要被传递过来肯定不能拼接这样太长了考虑做一个加权平均这个加权平均就是注意力信息也就是attni∑αihiattn_i\sum \alpha_ih_iattni​∑αi​hi​这样就能考虑到前面所有token并且哪个token比较重要我们可以通过权重αi\alpha_iαi​来调整。最后把这个attniattn_iattni​取代sis_isi​输入和yiy_iyi​拼接起来作为生成yi1y_{i1}yi1​时的输入Attention is all you need更进一步谷歌论文的指出我们不需要RNN了只需要注意力机制就能解决NLP问题效果比RNN更好计算还更快。注意力机制的优点是从根本上解决了长距离依赖问题并行度远高于RNN推理训练大幅加速最终的benchmark结果表明纯注意力机制比RNN得分更高计算还更快完爆RNN。这里的注意力机制整体仍然和之前RNN的注意力类似一个token的注意力是其他所有token的状态的加权平均需要注意另一个token的话就把需要注意的这个token的权重调大。不同点在于这里没有RNN所以隐状态不是RNN循环层算出来的而是词向量直接乘上一个矩阵WvW_vWv​算出来的。另外注意力权重的具体计算方式是每个token乘上Wq,WkW_q,W_kWq​,Wk​生成q,k向量一个token计算和其他token的注意力权重就让这个token的q和其他token的k做点乘得到的值做一个softmax归一化就是权重然后根据这个权重做一个v的加权平均就是这个token的隐状态attni∑αjvj∑(qikj)vjattn_i\sum \alpha_jv_j\sum (q_ik_j)v_jattni​∑αj​vj​∑(qi​kj​)vj​这样有两个好处一是可以直接捕捉长距离依赖这里的Wq,Wk,WvW_q,W_k,W_vWq​,Wk​,Wv​矩阵都是可训练的二是注意到我们token的注意力计算不存在依赖关系可以同时计算而RNN需要逐个计算出隐状态才能计算注意力具体来说就是开始我们可以把全部token拼在一起形成一个矩阵然后去乘Wq,Wk,WvW_q,W_k,W_vWq​,Wk​,Wv​就能得到Q,K,VQ,K,VQ,K,V矩阵。然后让QKTQK^TQKT相乘就能得到注意力矩阵然后让QKTQK^TQKT相乘再softmax就能得到注意力矩阵最后让注意力矩阵和V矩阵相乘就能得到每个token的隐状态然后就能去推理了。整个过程除了要遵循先算QKTQK^TQKT再算隐状态的这个依赖之外token之间都是可以贵遇到矩阵乘法计算然后并行的而矩阵乘法是一个并行优化非常充分的算子因此整个注意力计算流程可以实现很高的并行度这让纯注意力机制比传统RNN有很大的性能优势最后来看看形式化公式。转置是因为Q,K,VQ,K,VQ,K,V的形状都是[seqlen,hiddendim][seqlen,hiddendim][seqlen,hiddendim]想要每一行分别做点乘需要转置。最后QKTQK^TQKT的形状就是[seqlen,seqlen][seqlen,seqlen][seqlen,seqlen]了可以直接乘上VVV得到张量形状为[seqlen,hiddendim][seqlen,hiddendim][seqlen,hiddendim]每一行表示一个token的隐状态这个隐状态就相当于RNN里的hih_ihi​了可以接上普通网络实现下游任务。Attention(Q,K,V)Softmax(QKTdk)VAttention(Q,K,V)Softmax(\frac{QK^T}{\sqrt {d_k}})VAttention(Q,K,V)Softmax(dk​​QKT​)V这里softmax之前除以dk\sqrt{d_k}dk​​是因为如果词向量长度dkd_kdk​太大点乘出来的logits也会太大经过softmax进一步放到会导致注意力集中于某几个token其余token几乎没有权重。所以除以一个正比于词向量长度的量让logits分布更均匀多头注意力MHA有点类似于MoE的专家思路只能训练一组Wq,Wk,WvW_q,W_k,W_vWq​,Wk​,Wv​矩阵的话能学习到的特征不够灵活相同参数量的前提下不如把大的WqW_qWq​拆成多个小的矩阵WqiW_{qi}Wqi​这样得到多组注意力结果再拼接起来隐状态长度还和以前一样但每组注意力头是分开反向传播的能学到不同特征组合起来学习到的特征更全面LayerNorm之前的层归一化是用BatchNorm实现的简单来说就是每一个batch内部平均值和方差然后进行归一化但这样在NLP里的问题是输入shape是[batch,seqlen,hiddendim][batch,seqlen,hiddendim][batch,seqlen,hiddendim]在一个batch内归一化的话由于seqlen是变长的我们如果想统一处理一般会在后面补0到统一长度但这样再计算均值和方差可能会把很多占位符都算进去导致结果失真。所以对于序列长度可变的问题改为在每个序列内部归一化也就是计算每个序列内所有token的词向量的均值标准差然后在序列内部归一化这样计算更简单结果还不容易失真。另一个好处是BatchNorm需要区分训练和推理推理时用训练学会的均值和标准差归一化训练时计算输入的均值和标准差但LayerNorm没这个问题训练和推理都临时计算当前序列的token均值标准差。如下图图中箭头就是归一化的维度位置编码仔细分析前面的QKV计算过程会发现一个问题我们是批量计算每个token对其他token的注意力的这里面并不包含token在序列中的位置信息也就是我们把QKV矩阵里随机交换两行也不改变注意力的计算结果但是文本是序列信息即使组成的token完全相同顺序不同也会导致语义不同比如你欠我钱和我欠你钱显然不一样。所以我们还要想办法把位置信息加入进去这就是位置编码。具体来说就是每个token生成一个和词向量长度相同的向量表示这个token在序列中的位置然后把这个位置向量和词向量相加。绝对位置编码比较朴素的方法把位置编码看成一个二进制数第i个token的编码就是i的二进制表示。这样的问题是能处理的最大位置就是2dk2^{d_k}2dk​再长的序列无法编码吗但是语言模型输入序列长度可能是变化的如果推理超出训练时的最长序列就不知道如何编码三角函数编码每个token用一组不同相位的三角函数编码不同位置的token用波长来区分第i个token波长取i。相位的变化规律如下这里的i不是第i个token而是词向量里的第i个位置。这个表示看起来有点奇怪但是机器学习的一个设计思路是不要尝试人脑理解模型参数只要给这个模型足够的表达空间训练时能自动学会规律就行。事实证明这个编码方式比绝对位置编码要好首先就解决序列长度有上限这个问题。三角函数是个周期函数没有值域超过上限的问题然后区分位置是通过波长进行的这可以类比为一个时钟时针分针秒针都是周期函数值域还一样我们区分它们的方式是转速或者说波长。实际transformer里还更复杂一点奇数位用sin偶数位用cos相位规律如下也就是词向量里相邻两个位置的sin和cos相位一样cos的好处是对于两个不同位置的token计算注意力时点乘根据和差化积能得到结果只和两个token的位置之差也就是相对位置有关和两个token的绝对位置无关。这是符合语言规律的。整体架构基于注意力机制的模型整体架构如下可以看到仍然是一个encoder-decoder架构处理任务是序列到序列也就是一般所说的Seq2Seq编码阶段接受输入是输入文本先把token加上位置编码然后经过多头注意力层然后addnorm意思是一条通路是残差连接直接接到下一层同时另一条通路是LayerNorm归一化再连接到下一层最后经过一个FFN再经过一层addnorm。上面这被称为一个EncoderBlock这样的Block循环N次完成编码阶段结果传给解码器解码阶段自回归输出把生成的输出序列作为输入先位置编码经过一个多头注意力和addnorm这里对自己做注意力也叫自注意力同时接受编码器的结果也做一次多头注意力和addnorm这里注意力计算的不是自己的序列而是另一个序列称为交叉注意力。自注意力和交叉注意力结果叠加传给FFN层上面整体被称为DecoderBlock也是重复N次注意是每生成一个Token重复N次不是一共N次。重复完之后再经过一个线性分类层和softmax映射到每一个token的概率决定下一个token是什么。这里有了概率决定下一个token选什么是我们第一节里讲过的推理采样有贪心采样Beam Searchtopk/topp随机采样多种方式。解码阶段什么时候结束取决于什么时候输出表示结束的特殊tokenAttention的并行训练能力训练时采用自回归训练模式也就是给一个无标注文本截出他的多个前缀第iii个token的预测训练就是输入[1,i−1][1,i-1][1,i−1]的前缀前向传播然后和实际答案[1,i][1,i][1,i]的前缀对比计算Loss进行反向传播。注意到由于整个文本我们都知道了这个训练方式可以并行我们可以同时进行i∈[i,1]i∈[i,1]i∈[i,1]的所有token的前向传播然后和对应答案对比计算Loss。当然推理阶段肯定还是要逐个token生成这相比RNN是个巨大进步RNN训练时是必须串行的Attention可以并行大大降低了训练开销。Masked Attention掩码注意力注意到前面的架构图里自注意力不是一般的MHA而是Masked MHA。这是因为如上节所述训练时我们已经知道每一步应该预测的下一个token是什么可以对每一步并行训练。但为了防止模型作弊直接去看下一个token是什么下一个是什么在编码器阶段已经见过了我们需要给注意力做一个掩码已经预测了前i个token下一步预测i1则只能看到前i个token的注意力结果。具体来说我们构造一个掩码矩阵第i行表示第i步推理。计算注意力结果时1的位置正常计算0的位置设为负无穷。这样和下一步的交叉注意力相加后0的位置仍然是一个接近于负无穷的量然后经过注意力的softmax时负无穷会被计算为接近于0的值相当于注意力得分为0了忽略后面的token。推理时由于不知道后面的token是什么没这个作弊问题不需要掩码。