一、前言
平平无奇的计算机大学生,本科上完你是否想了解一下最近现代、最AI的Transformer。我作为一个科研小白,研0无聊,故计划去学习什么是Transformer,怎么拼凑的Transformer,因此,本文浅入浅出,从最初到当今技术发展,以及Transformer的真实面貌。
二、入门
1. 函数
传统函数是x到y的映射关系,遇到不能轻易看出来的,或者难以找到完全拟合的函数,用猜或者近似解来解决,即"符号主义->联结主义"。
后遇到复杂的数学模型,产生了线性函数->非线性函数 的衍化,其间产生了激活函数 概念,形如(常写作):
即可看成 X(输入层)->Y(输出层),X可以为多个 ,上述激活函数的所参与的函数变换,可以看成 X(输入层)->a(隐藏层)->Y(输出层) 。上述内容即为前向传播:
实则就是努力去构建一个函数用未知数X猜出Y的值,目标是去算出w和b两个变量。
2. 如何求解w和b
什么样的参数是好的: 总的真实值与预测值的差距尽量小:
为解决绝对值计算困难,产生了均方误差(MSE) 的概念来解释**损失函数(Loss,一般用L表示)**的好坏:
其中的预测值就是根据函数模型来的,即想得到最好的函数模型使损失函数最小,就是求解让L最小的w和b的值。
求w和b,式子展开,代入:
化简后,求偏导等于0求解,找到损失函数中w和b在这个函数中所应取到的最小值(二元函数):
上述求解方法即为传统的线性回归。
此外,现实计算中,不能简单求偏导求到最优,这种情况即可用w,b值去试,试是有参考性的,试值过程中,w和b的改变也会引起损失函数值的改变,这种改变就是其偏导数的值 。这种方法即为梯度下降。
第t次迭代和第t-1次迭代的公式如下,这里引入了学习率的概念:
其中η为学习率 ,就转换为了求两个偏导数的值,由最初的X(输入层)->a(隐藏层)->Y(输出层),推导出:
可见由x到L的链式关系,故可用链式求导法则 ,求到w和b的偏导(是我们要求的那个目标激活函数的未知变量,以
为例):
可见,求L对的偏导可以看作从右到左式子依次求偏导,也就是从后往前依次求偏导,此后以求导为依据,更新参数。
神经网络的一次训练:前向传播一次,从x计算到y,之后后向传播求偏导,更新参数。
3. 前述方法问题与解决方法
过拟合:训练数据上表现很好,但是在新数据上很糟糕的现象
泛化能力:在没见过的数据上的表现能力。
如何解决过拟合:
简化模型,防止学会其中的噪声和正常的随机波动;
增加数据量 ,如果无法收集,尝试数据增强(从原数据创造一些新数据,以增强模型的鲁棒性);
训练时在损失函数中进行修改,使其为
为正则化系数,M为层数深度,j为第j层。此方法为正则化 方法,其一为L1正则化 ,其二为L2正则化 ,因绝对值之和为L1范数 ,平方和的平方根为L2范数,其中L2正则化用的为L2范数的平方;
Dropout(丢弃),随机丢弃一些参数,在某些训练时不会过度依赖某个参数,以增强所有参数的鲁棒性。
此外训练时会出现的问题还有:梯度消失(激活函数导数 < 1,连乘衰减)、梯度爆炸(导数 > 1 或初始化过大,连乘增长)、收敛速度过慢(病态曲率、鞍点、学习率难调)、计算开销过大(参数/数据规模庞大)等。
4. CNN
参考矩阵运算:
抽象为:
即有,在神经网络中,根据前后关系,也就有了:
L为层数,每一层的输入是上一层的输出。方便GPU进行并行运算。
对于图片由于全连接 (FC,所有神经元都连接起来,例如输入有10个,输出有20个,则参数量为200)的参数量过大 ,且要将二维图片展为一维向量,容易丢失空间结构 ,因此提出了CNN(卷积神经网络):引入卷积核:
实则可以看成是9个参数,由此构成了卷积层(Conv):
又有池化层(Pooling,压缩特征图尺寸,下采样,保留最重要信息),则CNN的神经网络可以看为:
可以有多个池化层和卷积层以及全连接层。适合静态数据,图片等。
5. RNN
目的是区分一个句子中不同词的词性,最初是如何在计算机中表示一个词,产生了词嵌入概念,即通过深度学习的方法,获得可以表达一个词的向量。
把这些向量送入神经网络依旧出现了类似于CNN的问题,并且一个词的词义无法和这个句子中前一个或几个词的词义联系起来,故产生了**RNN(循环神经网络)**在隐藏层,将结果传入H,作为后续词义解析参考的信息:
其中右上尖括号值为词语在句中位置,h为其中RNN的特色内容,向下一位置的解析传播,同一套 应用于序列的所有时间步(权值共享)。
RNN公式总结来说为:
多了前一时期的隐藏状态传入。
但是,RNN也存在一些问题,即无法捕捉长期依赖(长句子) ,无法并行计算(GRU和LSTM改进,缓解无法根治,因此诞生Transformer)。
三、Transformer
1. 自注意力机制
给句子中每个词加一个位置编码,把这个位置编码加入上述的词向量,即拥有了在句子中的位置信息,那么如何拥有上下文信息:
设置新的三个矩阵,可以理解成上面的参数,但是是以矩阵的形式,训练的时候主要是训练这三个矩阵:
将每个词的词向量与相同的上述三个矩阵分别相乘,得到词向量的:,n为第几个词。
在实际计算中,多列词向量构成了一个大的词矩阵,因此,相乘出的结果就是q矩阵,k矩阵,v矩阵,上述三者中每一列代表了每一个词所对应的内容。其中:
-
q: Query(查询):"我在找什么信息?"
-
k: Key(键):"我能提供什么信息的关键字?"
-
v: Value(值):"我实际能提供的信息内容"
其后,把第一个词的q与包含自己的其他词的k做点积,就能得到这个词和其他的相似度,后,分别拿这个相似度与各个对应的v值相乘,各个结果相加,得到一个加和后的值,即得到了对于第一个词的上下文有关的信息,扩展到每个词,简言之:
就是在第
个词的视角下 ,把每个词的权重都有所顾及,把全部上下文信息都包含了进来。此外,为了防止点积过大导致梯度消失,将上述式子变形为:
特别除了一个,即K向量的维度的开根号,又有softmax归一化保证权重不会很大,方便后续训练。
得到上述公式后,就得到了注意力矩阵中位置的值,推广至矩阵运算,就能得到Transformer中自注意力机制:
输入(最基本的词向量):
对(《Attention Is All You Need》中是以上标标注q、k、v的,实际意义和上文中的符号意义相同),分别投影:
计算得分(即上文提及的,把第一个词的q与包含自己的其他词的k做点积,得到这个词和其他的相似度),这里是矩阵形式,上文展示了向量中的一个值的求法:
这里是得分矩阵,
由上文最初的
构成。
之后进行Softmax归一化:
这里的矩阵,注意力权重矩阵,就是由后面softmax后的
构成的。
最后,根据上文的一个值的求法,对应的,得到最后上下文矩阵 ,注意力函数
(其输出就是上下文矩阵):
这就是自注意力机制。
2. 多头注意力机制
多头注意力就是把单头注意力的整个流程,用多组不同的 并行跑多次,得到多个输出,再把它们拼接起来。
每个头按照上述的自注意力机制去得到自己的上下文矩阵,h为头的序号,每个头都会产生一个。之后将这些矩阵进行拼接,得到多头注意力输出:
其中是输出投影矩阵,作用在"拼接后的多头输出"上,把多头的特征融合成最终的表示。这里的拼接指的是类如词一在第一个头对于其他词产生了1,2,3,第二个头则4,5,6,拼接之后为1,2,3,4,5,6也就是维度改变,不会新增行数。
3. 残差连接与层归一化(Add &Norm)
残差连接(Add)就是在子层输入和输出之间加一条直连运算,把最初的输入和多头注意力的输出相加,目的是保证梯度,无论如何梯度都能保持大于X,从根本上缓解了梯度越来越小的梯度消失。
层归一化(LayerNorm,LN)则是用类似正态分布的方式,强制把输出分布近似于正态分布,防止残差相加后的数值爆炸。
下述式子分别是Transformer原始论文中的(Post-LN),以及现代大模型主流的(Pre-LN)残差连接与归一化:
在现代主流中,Sublayer表示Transformer架构中的各种层,包含多头自注意力、FFN、交叉注意力。现代的大模型算法先把输入层归一化后进行sublayer处理,然后再残差连接(Pre-LN)。
4. 前馈网络(Position-wise Feed-Forward Networks,FFN)
由上述注意力机制的原理可见,Transformer做的都是线性运算,例如加权求和操作,只是线性变换的组合,因此,为使模型可以更好的表达需要表达的事务,需要引入非线性变换内容,故提出前馈神经网络(FFN)来进行表达。
FFN由三部分构成:升维、激活函数、降维。
升维就是将得到的矩阵升维展开,细分其中内容,以便寻求更好的表达:
激活函数,通过一个激活函数,对进行处理,达到非线性的需求,Transformer原文是采用ReLU激活函数:
降维,将细节处理完毕之后的矩阵缩放回原来的维度:
上述的W和b,伙同最初的QKV三个W矩阵相同,都是最初随机值,通过学习得到。
5. 交叉注意力机制
Transformer分为Encoder和Decoder。
Decoder中独有的、连接 Encoder 与 Decoder 的"信息桥梁", "用一方去查另一方"------Q 来自 Decoder,K 和 V 来自 Encoder 。即Decoder去Encoder查字典,类似于翻译中,用英文单词找到词典中与其最相似的汉语词汇,通过计算 Q 与所有 K 的相似度并归一化,得到注意力权重矩阵,再以该权重对 V 进行加权求和,从而将源序列中与当前生成任务最相关的信息提取并融合为一个新的上下文矩阵输出给 Decoder 的后续子层。
Cross-Attention的注意力权重矩阵 Aij,就是Decoder的第i个词对于Encoder的第j个词的相似度得分。
6. Liner层与最后的Softmax
Decoder中最后处理部分的层,经过交叉注意力以及一系列残差连接、归一化和前馈网络,最后会输出一个m个位置的上下文向量,也就是输入的每个词Q对于不同位置K的权重经过一系列优化后的结果。把这个上下文信息的矩阵进行与一个叫做输出投影矩阵W(可理解为用于给上下文信息矩阵打分,存有词语的隐含意义)进行点乘,加入适当的偏置b,就能得出每个位置对于每个词的分数logits:
例如,解码策略是贪心策略时,就是当生成一句话时,这个位置大概率最可能生成哪个词,就是那个权重最大的那个词。
上述Liner层输出了各个位置对于各个词的原始匹配分,最后的Softmax层就负责将这个匹配分分布成符合符合概率分布的结果(所有值介于0到1之间,且和为1),拉大高分数和低分数的差距,让模型的判断更明确。
7. Encoder-Decoder 整体架构

图 Attention Is All Your Need中的整体架构示意图
**Encoder(编码器):**负责源序列语义理解的编码模块。在上图的左半部分,经过嵌入和位置编码输入到多次进行的多头注意力模块,以及残差连接、归一化,随后进入前馈网络增加非线性的能力,之后同样残差连接归一化......最后,编码器输出K,V矩阵,传入解码器。
Decoder(解码器): 依旧是经过嵌入和位置编码输入,但是其后注意力模块包含掩码 ,即使句子从左到右词语依次输入,遮盖后续内容,防止当前位置看到未来词。经过多头注意力后,残差连接、归一化,随后,进入到一个交叉注意力的部分,它的输入是编码器传来的K、V,和解码器前述内容的Q,输出一个一个新的上下文矩阵,随后依旧是通过残差、归一化、前馈、残差、归一化,执行N次,最后经过Liner层和Softmax得到可能性输出。
四、Transformer其他知识点
1. 嵌入矩阵
嵌入矩阵是Transformer输入时候的第一个要进入的模块,是指按照输入,通过**分词器(Tokenizer)**分为几个词(Token),然后根据这几个token的数字表示,在嵌入矩阵找出对应的位置上的向量,类似于数组下标的样子。
初始的嵌入矩阵是随机的,在训练过程中,会随着其他矩阵一起在反向传播过程中更新,最后会变得含有有效信息。现代的算法中,Liner层的常常等同于嵌入矩阵的转置(被称为参数共享)。
2. Transformer的训练
总体的训练过程和"入门"部分算w和b的方法相似,无非是更新的不只是一个参数,而是一个矩阵,同样满足"前向传播算损失 → 反向传播算梯度 → 梯度下降更新参数"的过程。
五、后记
转眼间就差一个月,也该读研一了,到现在为止只是扩展了一下之前学的Golang,然后学了一下LLM的基础,也就是这篇文章。回看本科时的几篇文章,确实也挺慨叹的,不过考研也是顺利落地了,唯恐浪费时间,所以就每天稍微学一点,保持一下状态,后面没想好要做什么,其实golang还有一部分内容没有学,到时候时机成熟再学吧。
才疏学浅,上面的内容都是自己看了一些视频,外加询问AI自己总结的,可能也有表达有误的地方,尽请拨冗指正。