— order: 4 —1 Attention is All You Need时间:2017.61.1 创新点seq2seq 模型一般使用 encoder-decoder 结构,过去的一些工作使用 CNN RNN 辅以 seq2seq 模型,而 Transformer 模型则完全基于 attention 机制,并且效果很好。另外,自注意力机制很重要,但并不是本文第一次提出卷积好的地方在于可以做多个输出通道,每个输出通道认为是识别一种特定的模式。Transformer 吸收这一思想而提出 multi-head概念可以看到,Tranformer 的特点在于并行度高但计算消耗大1.2 模型结构总体模型架构左边是编码器右边是解码器,解码器之前的输出作为当前的输入(所以这里最下面写的是outputNx 表示由𝑁 Block 构成,字面意思上的堆叠,最后一层 encoder 的输出将会作为每一层 decoder 的输入一共有三种 Attention但区别之在于输入𝑄𝐾𝑉来源以及 Attention score 是否采用掩码具体注意力的计算,一般有两种做法,一种是如果𝑄𝐾𝑉长度不同可以用 addictive attention可学参数较多);另一种是如果长度相同可以用 scaled dot-product attention1.2.1 形状解释与步骤细分首先是 Q K V 的形状𝑄:𝑛×𝑑𝑘,𝐾:𝑚×𝑑𝑘,𝑉:𝑚×𝑑𝑣𝐾,𝑉个数成配对,𝑄,𝐾长度都为𝑑𝑘允许 scaled dot-product attention对单个注意力头而言,经过Attention(𝑄,𝐾,𝑉)=softmax(𝑄𝐾𝑇𝑑𝑘)𝑉中间𝑄,𝐾得到形状为𝑛×𝑚的矩阵,表示𝑛 query 𝑚 key 的相似度经过 (masked) softmax 后与𝑉相乘,得到了𝑛×𝑑𝑣的输出,也即对每个 query我们都得到了𝑉的某种加权平均这里除以𝑑𝑘的原因是:算出来的权重差距较大,经过 softmax 1,0差距悬殊,采用这个数字从实践上刚好适合对多头注意力而言MultiHead(𝑄,𝐾,𝑉)=Concat(head1,head2,,head)𝑊𝑂wherehead𝑖=Attention(𝑄𝑊𝑄𝑖,𝐾𝑊𝐾𝑖,𝑉𝑊𝑉𝑖)=8也就是有 8 个头,论文中𝑑model=512指的是多头拼接后的向量长度,于是𝑑𝑘=𝑑𝑣=𝑑model/=64,每次我们把𝑄𝐾𝑉经过 Linear 512变为64,然后再在最后一维 concat 起来,最后再经过一个 Linear 512512实际上 dot-product attention 的注意力层可学的参数就在 Linear 中,我们希望将它分出个通道让它在不同的语义空间上学习不同的模式然后是 Position-wise Feed-Forward NetworksFFN(𝑥)=max(0,𝑥𝑊1+𝑏1)𝑊2+𝑏2经过(自或交叉)注意力后,再经过 concat Linear 后将通道融合得到𝑛×𝑑model的输出随后过两个全连接层,从𝑑model(512)𝑑𝑓𝑓(2048)𝑑model(512)这里的 Position-wise 的是说,每个样本用的是同一个 MLP不是真的全连接)可以这么想:通过注意力层学到了不同 query 的语义特征(汇聚所有我感兴趣的信息),然后用同一个 MLP 将它们做变换(但 query 之间不能融合)来减少参数量,并且一定程度上有助于泛化当然上图是训练的时候(𝑛 query 并行),测试的时候则是一个个来,但依旧是同一个 MLP有点像 RNN1.2.2 位置编码参考1.详解自注意力机制中的位置编码(第一部分)2.详解自注意力机制中的位置编码(第二部分)3.Transformer 学习笔记一:Positional Encoding位置编码)4.让研究人员绞尽脑汁的 Transformer 位置编码Transformer 中的位置编码PE𝑡,2𝑖=sin(𝑡100002𝑖/𝑑),PE𝑡,2𝑖+1=cos(𝑡100002𝑖/𝑑)即采用sin,cos交替的方式做固定位置编码,在波长上形成了从2𝜋100002𝜋几何级数它有两个性质1.两个位置编码的点积可以反应出两个位置编码间的距离(因此虽然是绝对位置编码,但多少也携带了一些相对位置信息);用一个不依赖于𝑡而是依赖于Δ𝑡的线性变换矩阵可以把𝑡位置变换到𝑡+Δ𝑡位置2.位置编码的点积是无向的采用外置位置编码的形式,可以推出标准 Transformer 的自注意力机制如下𝑞𝑖=(𝑥𝑖+𝑝𝑖)𝑊𝑄𝑘𝑗=(𝑥𝑗+𝑝𝑗)𝑊𝐾𝑣𝑗=(𝑥𝑗+𝑝𝑗)𝑊𝑉𝑎𝑖,𝑗=softmax(𝑞𝑖𝑘𝑇𝑗𝑑𝑘)𝑜𝑖=𝑗𝑎𝑖,𝑗𝑣𝑗其它位置编码,可以大体这样分类(个人理解)根据位置编码的可学习与否1.固定位置编码:直接使用固定方式产生的位置编码,不参与训练2.可学习位置编码:位置编码作为模型的一部分,参与训练根据位置编码加入的形式1.外置位置编码:位置编码直接加到模型输入上2.内置位置编码:位置编码加到模型的某一层中,比如作为模型权重的 bias可学习)根据位置关系1.绝对位置编码:考虑绝对位置2.相对位置编码:仅靠虑相对位置1.2.3 Batch Norm & Layer NormBatchNormtrain的时候,一般是取小批量里的均值和方差,在预测的时候用的是全局的均值和方差。什么是批标准化 (BatchNormalization) - 知乎在输入为变长的情况下我们不使用 BatchNorm而是使用 LayerNorm一个简单的记法:xxx-norm 就是按 xxx 方向进行归一化,或者说按 xxx 方向切,还可以说是不分解 xxx对于二维和三维的 xxx-norm都是适用的 batch-norm 为例,二维就是顺着 batch 方向切,即纵切;三维需要注意,一定保留了序列方向不被分解,再结合按照 batch 方向切,就得出了蓝色框切法 Layer-norm 顺着 Layer 的方向,在这里就是 seq 方向切,即横切但事实上好像这种理解还是有问题,似乎文本的 LN 是一行而不是一个面关于 BN LN 以及它们的代码可以参考 对比pytorch中的BatchNormLayerNorm文本中的 LayerNorm 本质上是一种 InstanceNorm Group Norm实际上还有一个更新、更强大的 Group Norm是恺明大佬提出的,可以参考 这篇文章1.3 (李沐版)代码实现的一些细节论文里没有代码,不过有开源,但不看那个,看得是李沐的版本之前以为多头注意力就是一个 attention 里有多个 attention但其实多头还是一个整体大的 attention然后在里面把𝑑拆分成多个。之所以可以这样写是因为分数计算使用的是 DotProductAttention这个是𝑄𝐾直接做内积,不需要学习参数。所以代码本质还是使用一 attention只不过因为每个 attention 本质都一样,所以可以并行计算。本质上就是重用batch这一维的并行性来做多头的同时计算具体来说:输入是𝑏,𝑛,𝑑,把它拆分成𝑏,𝑛,,𝑑𝑖permute 𝑏,,𝑛,𝑑𝑖,再融合前两维成𝑏,𝑛,𝑑𝑖。这样送进 attention 模块使得它并没感知到输入被分头了。最后将输出做逆操作回去就行了Encoder 按下不表,而 Decoder 部分相对复杂训练阶段,输出序列的所有 token 在同一时间处理,但在算 softmax 的时候使用 mask因此 key_values 等于完整的 X,然后需要 dec_valid_lens预测阶段,输出序列是一句话内一个一个 token 处理的,输入 X shape (𝑏,𝑛𝑡,𝑑),其𝑛是变化的,key_values 通过 concat包含直到当前时间步𝑡为止第𝑖 decoder 块的输出表示这里多多少少有点体现出 Transformer 对于变长数据的灵活性我们利用 state 三元组来传输块与块之间 enc dec 之间的数据,分别保存:encoder 输出,encoder 输出的有效长度以及每个decoder 块的输出表示python123456789101112131415161718class DecoderBlock(nn.Module): # ... def forward(self, X, state): enc_outputs, enc_valid_lens = state[0], state[1] if state[2][self.i] is None: key_values = X else: key_values = torch.cat((state[2][self.i], X), axis=1) state[2][self.i] = key_values if self.training: batch_size, num_steps, _ = X.shape # dec_valid_lens的形状:(batch_size,num_steps), # 其中每一行是[1,2,...,num_steps] dec_valid_lens = torch.arange( 1, num_steps + 1, device=X.device).repeat(batch_size, 1) else: dec_valid_lens = None