本文详细解析了Transformer模型的核心原理,从编码器、解码器的结构到注意力机制、位置编码等关键组件,通过图解和公式展示了向量如何在模型中流动,并解释了多头注意力、残差连接等技术的应用。文章适合零基础读者入门,帮助读者深入理解Transformer的工作机制。
网上讲 Transformer 的文章很多,但要说最通俗易懂、看完真能懂的,Jay Alammar 这篇《Illustrated Transformer》绝对是排得上号的——作者用一步步的图解把复杂的注意力机制、编解码结构拆解得明明白白,零基础也能跟上,想入门 Transformer 别错过这篇。原文链接:https://jalammar.github.io/illustrated-transformer/
正文开始
这篇文章主角是Transformer——它靠注意力机制加速模型训练,在特定任务上性能比当年谷歌的神经机器翻译模型还更好。
但 Transformer 最大的好处,其实是天生适合并行计算。谷歌云甚至官方推荐把它作为 Cloud TPU 的参考模型。那咱们就拆开来,看看它到底怎么工作。
Transformer 出自论文 Attention is All You Need。TensorFlow 实现已经在 Tensor2Tensor 包里了。哈佛 NLP 团队还做了带注释的指南,附了 PyTorch 实现。
这篇文章里我尽量把概念拆碎,一步一步讲,希望没那么多背景知识也能看懂。
2025 更新:作者做了一个免费短课程,带动画,把本文内容更新到了现在。
整体概览
我们先把整个模型看成一个黑箱。做机器翻译的话,输入一种语言的句子,输出另一种语言的翻译。
打开黑箱,里面有三部分:**编码器(encoder)**堆、**解码器(decoder)**堆,还有它们之间的连接。
编码器堆就是好几个编码器摞起来——论文里摞了六个,数字六没什么神奇的,你想换别的数量也可以。解码器堆也是同样数量的解码器摞起来。
所有编码器结构一样,但不共享权重。每个编码器拆成两个子层:
编码器输入先过自注意力层——这一层在编码某个单词的时候,能让它同时看到输入句子里其他单词。这个我们后面细说。
自注意力层的输出再过一个前馈神经网络。每个位置的向量都独立地过同一个前馈网络。
解码器也有这两层,但中间多了一个注意力层,用来帮解码器聚焦输入句子里相关的部分——和 seq2seq 模型里注意力干的活差不多。
张量视角
现在我们知道了大体结构,看看各种向量/张量是怎么在组件之间流动,最终把输入变成输出的。
和所有 NLP 任务一样,第一步我们先用词嵌入把每个单词转成向量。每个单词嵌入完是一个 512 维向量。我们就用小方框来表示它们。
嵌入只在最底下那个编码器做。所有编码器都接收一个 512 维向量列表——最底层是词嵌入,上面的编码器接收底下编码器的输出。列表长度是超参数,就是你训练集里最长句子的长度。
输入序列嵌入完,每个单词依次过编码器的两个子层。
这里能看到 Transformer 一个关键特点:每个位置的单词在编码器里走自己的路径。自注意力层里这些路径之间有依赖,但前馈层没有依赖,所以过前馈层的时候可以并行算。
下面我换个短句子做例子,一步步看编码器每个子层到底干嘛。
开始编码!
编码器接收向量列表,先过自注意力,再过前馈神经网络,然后输出给上一层编码器。
每个单词都先做自注意力,再过前馈——每个向量独立走同一个网络。
自注意力是什么?
别被这个名词唬住,我一开始读论文的时候也没听说过。一句话说清楚它干了啥。
假设我们要翻译这句话:
“The animal didn’t cross the street because it was too tired”
(动物没过马路,因为它太累了)
这句话里的it指的是谁?是 street 还是 animal?对人来说太简单了,但对算法来说可不简单。
模型处理it的时候,自注意力让它把it和animal关联起来。
模型处理每个单词(输入序列每个位置)的时候,自注意力允许它看输入序列其他位置,找线索,帮自己更好编码当前词。
如果你熟悉 RNN,想想 RNN 是怎么把之前处理过的词的信息揉进当前隐藏状态的。自注意力就是 Transformer 用来把其他相关词的理解揉进当前词的方法。
比如我们在第五个编码器(堆顶那个)编码it的时候,一部分注意力就会跑到The Animal那里,把它的表示揉进it的编码里。
你可以去这个 Tensor2Tensor notebook 自己玩,加载个 Transformer 模型,用交互式可视化看看。
自注意力细节
我们先看怎么用向量算自注意力,再看实际怎么用矩阵实现。
第一步,从编码器输入向量(也就是每个词的嵌入)变出三个向量:Query、Key、Value。每个词都有这三个向量,就是用嵌入乘以三个训练出来的权重矩阵得到的。
注意这三个向量比原来嵌入向量维度小。嵌入和编码器输入输出是 512 维,这三个是 64 维。不一定非要更小,这是架构选择,让多头注意力计算量基本保持不变。
输入 x1 乘以 WQ 得到 q1,就是这个词的 Query 向量。最后我们给输入句子每个词都变出 Query、Key、Value 三个投影向量。
这三个向量到底是什么?
它们就是帮我们计算注意力的抽象概念。看完计算过程你就懂它们各自的作用了。
第二步算分数。假设我们现在算第一个词 “Thinking” 的自注意力,我们要给输入里每个单词打个分。分数代表编码当前词的时候,要给其他位置放多少注意力。
分数就是 Query 向量点乘你要打分那个词的 Key 向量。所以处理位置 1 的词,第一个分数就是 q1 ⋅ k1,第二个分数是 q1 ⋅ k2,以此类推。
第三步,分数除以 8(论文里 Key 向量是 64 维,√64 = 8。这么做为了梯度更稳定,其他值也可以,8 是默认),然后过一遍 softmax。Softmax 把分数归一化,让所有分数都是正数,加起来等于 1。
Softmax 分数就是当前位置对每个词的权重,代表这个词占多少比例。显然当前位置得分最高,但有时候关注相关的其他词也很有用。
第五步,每个 Value 向量乘以 softmax 分数。思路就是,留住你想关注的词的值,把不相关的淹没掉(比如乘个 0.001 就没了)。
第六步,加权后的 Value 向量加起来,就是当前位置自注意力层的输出(第一个词)。
到这自注意力计算就完了,得到的向量送给前馈神经网络。实际实现里为了更快,用矩阵一次性算完。我们看过单词级别直觉后,现在看看矩阵版本。
自注意力的矩阵计算
第一步还是算 Query、Key、Value 矩阵。我们把所有词嵌入打包成矩阵 X,X 乘以三个权重矩阵 WQ、WK、WV 就得到了 Q、K、V 三个矩阵。
X 每一行对应输入一个词。这里你能看到嵌入(512 维,图里画四个框)和 q/k/v(64 维,图里三个框)尺寸的区别。
最后,因为是矩阵运算,我们可以把二到六步压缩成一个公式,直接算出自注意力层输出:
多头注意力
论文又进一步改进了自注意力,加了多头注意力机制。它从两个方面提升性能:
- 扩展了模型关注不同位置的能力。比如上面那个例子,it 的编码里多少带了其他词,但主要还是它自己。如果是 “The animal didn’t cross the street because it was too tired”,我们需要知道 it 具体指谁,多头就有用了。
- 给注意力层多个表示****子空间。多头注意力不只有一组 Q/K/V 权重,Transformer 用了八个头,所以就是八组。每组训练完,都会把输入(或者下层来的向量)投影到不同的表示子空间。
所以每个头都有自己独立的 Q/K/V 权重,得到不同的 Q/K/V 矩阵。和之前一样,X 乘以权重得到三个矩阵。
然后我们重复八次不同权重矩阵的自注意力计算,最后得到八个不同的 Z 矩阵。
但这里有个问题:前馈层不想要八个矩阵,它只想要一个矩阵——每个词对应一个向量。所以我们需要把八个矩阵压缩回一个矩阵。
做法就是把八个矩阵拼起来,再乘以一个额外的权重矩阵 WO 就好了。
多头自注意力基本就是这些了。我知道矩阵有点多,我尽力把它放一张图里方便看。
说完注意力头,我们回到之前那个例子,看看编码 “it” 的时候,不同注意力头都关注哪:
编码 “it” 的时候,一个头主要关注 “the animal”,另一个关注 “tired”——所以模型对 “it” 的表示,已经融合了 “animal” 和 “tired” 的信息,这正是我们想要的。
要是把所有头都画出来,反而就不太好读了。
位置编码:怎么表示序列顺序
现在我们讲的模型里,还差一样东西:怎么记录输入序列单词的顺序?
Transformer 解决方法是,给每个输入嵌入加一个位置编码****向量。这些向量遵循模型能学会的特定模式,帮助模型确定每个词的位置,还有不同词之间的距离。直觉是,把这些值加到嵌入上,当投影到 Q/K/V 做点积注意力的时候,嵌入向量之间会有有意义的距离区分。
简单说,就是加位置编码,让模型知道单词顺序。位置编码长什么样呢?
如果嵌入维度是 4,实际位置编码大概长这样,每行一个位置:
实际例子是 20 个单词(行),嵌入维度 512(列),每行 512 个值在 -1 到 1 之间。我们上色之后,就能看到 pattern:从中间对半分,左半部分用正弦生成,右半部分用余弦生成,拼起来就是每个位置编码向量。
位置编码公式论文在 3.5 节写了,生成代码看 get_timing_signal_1d()。这不是唯一的位置编码方法,但好处是可以处理比训练集更长的句子——见过的长度你会算,没见过的你也会算。
2020 年 7 月更新:上面展示的是 Tensor2Tensor 实现里的位置编码。论文里方法有点不一样,不是直接拼接,而是把两个信号交织起来。下图就是那种样子,这里是生成代码。
残差连接
讲完了,讲编码器架构里一个细节:每个子层(自注意力、前馈网络)外面都有一个残差连接,然后接层****归一化。
要是把向量和层归一化画出来就是这样。
解码器子层也一样。如果想象一个两层编码器两层解码器的 Transformer,大概长这样。
解码器
编码器我们讲完了,解码器组件其实我们差不多都知道了,现在看看它们怎么配合工作。
编码器先处理输入序列,顶层编码器输出变成一组 K(Key)和 V(Value)注意力向量,给每个解码器的“编码器-解码器注意力”层用,帮助解码器聚焦输入序列合适的位置。
编码完开始解码,解码每一步输出输出序列的一个元素(就是我们例子里的英文翻译)。
重复这个过程,直到输出特殊的句子结束符号,就完成了。每一步输出送给下一个时间步的最底层解码器,然后结果一路往上,和编码器一样。而且和编码器输入一样,我们也要对解码器输入做嵌入,加上位置编码告诉模型每个词的位置。
解码器里自注意力层工作方式和编码器有点不一样:
- 在解码器里,自注意力只允许关注当前位置之前的位置。方法就是在 softmax 之前把未来位置都 mask 掉(设成负无穷),这样它们就不会被算进来。
编码器-解码器注意力层和多头自注意力工作原理差不多,区别就是 Query 矩阵是下层来的,Key 和 Value 矩阵用编码器栈的输出。
最后一步:线性层 + Softmax
解码器栈输出一个浮点数向量,怎么把它变成一个单词?这就是最后线性层加Softmax 层干的活。
线性层就是一个简单的全连接神经网络,把解码器输出投影到一个非常宽的向量,叫做 logits 向量。
假设我们模型训练的时候学了 10000 个唯一的英语单词(这就是输出词汇表),那 logits 向量就有 10000 格,每一格对应一个单词的分数。这就是线性层输出怎么解读。
Softmax 把分数转成概率(都正,加起来是 1),概率最高那格对应的单词就是当前时间步的输出。
该图从底部开始,初始是解码器栈输出的向量,然后再将其转换为输出词。
训练过程回顾
现在训练好的模型前向传播我们走完一遍,我们简单回顾一下训练是咋回事。
训练的时候,没训练的模型也走一模一样的前向传播。但我们是在有标签的数据集上训练,所以可以把输出和正确答案比一比。
举个例子,假设输出词汇表只有六个词:“a”, “am”, “i”, “thanks”, “student”, “”(句子结束)。
输出词汇表是训练前预处理就做好的。定义好之后,每个单词用一个独热向量表示——比如单词 “am” 就是对应位置是 1,其他都是 0。
看完这个我们说说损失函数——训练阶段我们优化的目标,最后靠它得到训练好准确的模型。
损失函数
假设我们现在训练,第一步,训练一个简单例子:把 “merci” 翻译成 “thanks”。意思就是我们希望模型输出单词 “thanks” 的概率高。但模型还没训练,不可能一下就对。
因为参数都是随机初始化,没训练的模型每个单词都给个随机概率。我们把预测概率和真实概率比一比,然后反向传播调所有参数,让输出慢慢接近期望输出。
怎么比两个概率分布?直接减了算交叉熵或者 KL 散度就行,具体可以看交叉熵(cross-entropy)和 KL 散度(Kullback–Leibler divergence)的介绍。
我这是过度简化了,实际句子都比一个词长。比如输入 “je suis étudiant”,期望输出 “i am a student”。实际意思是,我们希望模型连续输出概率分布,满足:
- 每个概率分布向量宽度是词汇表大小(我们例子是 6,实际一般 30000 或 50000)
- 第一个概率分布单词 “i” 概率最高
- 第二个概率分布单词 “am” 概率最高
- …直到第五个输出是 “” 符号,10000 词词汇表里也有它位置。
训练例子里,我们就是对着这些目标概率分布训模型。
数据集够大,训练够久之后,我们希望出来的概率分布长这样:
训练完我们就希望输出正确翻译。当然如果这个句子在训练集里也不能说明啥,交叉验证了解一下。注意即使某个位置不太可能输出,softmax 也会给一点概率,这个性质对训练很有帮助。
现在因为模型一次输出一个词,我们可以选概率最高那个词,扔掉剩下的,这叫贪心解码。另一种方法是保留比如前两个词(比如 “I” 和 “a”),下一步跑两次模型:一次假设第一个位置输出是 “I”,一次假设输出是 “a”,哪个版本在考虑两个位置后误差更小就留哪个。这个过程对位置 2、3…一直重复,这叫集束搜索(beam search),我们例子里 beam_size 是 2(就是任何时候内存里留两个未完成翻译假设),top_beams 也是 2(最后返回两个翻译结果)。这两个都是超参数,你可以自己调。
最近两年大模型发展很迅速,在理论研究方面得到很大的拓展,基础模型的能力也取得重大突破,大模型现在正在积极探索落地的方向,如果与各行各业结合起来是未来落地的一个重大研究方向
大模型应用工程师年包50w+属于中等水平,如果想要入门大模型,那现在正是最佳时机
2025年Agent的元年,2026年将会百花齐放,相应的应用将覆盖文本,视频,语音,图像等全模态
如果你对AI大模型入门感兴趣,那么你需要的话可以点击这里大模型重磅福利:入门进阶全套104G学习资源包免费分享!
扫描下方csdn官方合作二维码获取哦!
给大家推荐一个大模型应用学习路线
这个学习路线的具体内容如下:
第一节:提示词工程
提示词是用于与AI模型沟通交流的,这一部分主要介绍基本概念和相应的实践,高级的提示词工程来实现模型最佳效果,以现实案例为基础进行案例讲解,在企业中除了微调之外,最喜欢的就是用提示词工程技术来实现模型性能的提升
第二节:检索增强生成(RAG)
可能大家经常会看见RAG这个名词,这个就是将向量数据库与大模型结合的技术,通过外部知识来增强改进提升大模型的回答结果,这一部分主要介绍RAG架构与组件,从零开始搭建RAG系统,生成部署RAG,性能优化等
第三节:微调
预训练之后的模型想要在具体任务上进行适配,那就需要通过微调来提升模型的性能,能满足定制化的需求,这一部分主要介绍微调的基础,模型适配技术,最佳实践的案例,以及资源优化等内容
第四节:模型部署
想要把预训练或者微调之后的模型应用于生产实践,那就需要部署,模型部署分为云端部署和本地部署,部署的过程中需要考虑硬件支持,服务器性能,以及对性能进行优化,使用过程中的监控维护等
第五节:人工智能系统和项目
这一部分主要介绍自主人工智能系统,包括代理框架,决策框架,多智能体系统,以及实际应用,然后通过实践项目应用前面学习到的知识,包括端到端的实现,行业相关情景等
学完上面的大模型应用技术,就可以去做一些开源的项目,大模型领域现在非常注重项目的落地,后续可以学习一些Agent框架等内容
上面的资料做了一些整理,有需要的同学可以下方添加二维码获取(仅供学习使用)