暑假开摆了许久,最近有了一些学习的动力,终于终于终于刷完了Andrej的课,学习到了当下大语言模型最主流的架构——Transformer。这篇博客主要则是记录我在学习Transformer模型以及通读“Attention is all you need”论文后的一些个人思考和总结。你应该发现了标题是“Autobots, assemble”,如果你和我一样是变形金刚的狂热粉丝,你应该能get到这个小彩蛋。

自注意力机制 Self-attention

在Transformer架构推出之前,人们主要采用的是CNN和RNN架构,在论文前言部分,就提到了RNN架构的问题。首先他是一个时序模型,当前词语的隐藏状态是由前一个词语的隐藏状态和当前词语所决定的。换句话说,如果前面词语的隐藏状态没有计算出来,我们就无法预测下一个词。这种时序模型的计算性能很差,因为他没有办法并行处理。其次这些随着时序一步步记录的每个词语的隐藏状态,随着模型的训练可能会失真,而想要避免失真只能讲隐藏矩阵进一步扩大,但与此同时也会带来巨大的内存开销。

Transformer中的自注意力机制,则很好的解决了RNN架构中出现的这种问题。它选择使用缩放点积注意力。我们首先接受一个输入,得到他的词嵌入矩阵和位置嵌入矩阵的和,然后我们利用三个可训练的权重矩阵WQ,WK,WV生成查询向量,键向量和值向量。Query则表示“提问者”,即当前所需要关注的问题,用于与其他位置的Key匹配相似度。Key表示其他位置的标识,用于与Query计算关联度。Value则表示实际存储的信息,根据Query和Key的相似度加权求和后输出。自注意力机制的公式如下:

我们知道,两个向量越相似,在对方的投影这越大。因此,使用向量的点积可以很好的表达相似度这一概念,当前词块可以从中知道需要融合更多哪个词块的信息。同时我们可以看到,softmax函数中还除以了$\sqrt{d_k}$,即键向量的维度,这么做是为了防止矩阵的方差过大,和归一化中的操作是一样的。

多头注意力机制 Multi-head Self-attention

Multihead
开头我们提到,Transformer架构相比于RNN,它的优点在于并行计算。多头注意力机制的核心就在于,将关联度矩阵投影到多个低维度的向量中,让这些向量并行计算,以扩展模型专注于不同位置的能力。相当于给了模型多次机会学习不同的投影方式,然后将每次学习到的关联度矩阵连接在一起,再做一次投影整合全部的信息,可以类比于CNN中的多个输出通道。

交叉注意力机制 Cross-attention

Transformer
论文中提到,Transformer架构其实是为了机器翻译而设计的,这也是Transformer架构图是由编码器和解码器共同组成的原因,当然这两者并不是一定要绑定在一起,Andrej的Zero to Hero课中实现的gpt2只需要学习文本生成文本,不需要编码器,在未来也会学习到很多编码器和解码器单独应用的模型架构。每一个编码器和解码器模块都由多头注意力和前馈层组成。要注意的是,在解码器中,多头注意力是Masked的,因为在解码的过程中,每一个词块只能注意到历史的词块,通过注意力机制得到与历史词块之间的关联度矩阵。模型是为了学习训练集,预测生成下一个词块,所以在解码器中是不能注意到未来词块的信息的。因此,我们要利用一个下三角的矩阵,将关联度矩阵每个词块与未来词块的关联度设置为0。相反,在编码器中,需要全知性,需要全局捕捉输入序列的双向依赖关系,因此允许每个位置关注序列中的所有其他位置。

回到正题,交叉注意力机制大体上和多头注意力机制一样,区别在于注意力机制中Q,K,V的来源。Query的来源是解码器,是一个需要补充信息的目标序列,而Key和Value的来源则是编码器,是一个提供信息的参考序列。这样的设计使得模型可以让两个不同的序列建立联系,从而实现信息的融合。

前馈层 Feed Forward Pass

在编码器和解码器中,均存在一个前馈层。前馈层实际上就是一个MLP的架构,他的主要作用就是对关联度矩阵中的特征信息进行非线性的变化和增强(论文中提到将神经元的数量放大到原来的四倍,经过ReLU/GeLU函数进行非线性变换,最后变换为输入时的shape),让模型具有更强大的表达能力。