目录
1、从高处俯瞰整个模型
2、把张量画进流程图
3、编码器里的数据流
4、自注意力(高层直觉)
5、自注意力(向量级细节)
6、自注意力的矩阵实现
7、多头注意力
8、位置编码:让模型感知顺序
9、残差连接与层归一化
10、解码器一侧
11、最后的 Linear 与 Softmax 层
12、训练过程回顾
13、损失函数
14、延伸阅读
开篇
在上一篇关于 Attention 的文章里,我们看到注意力机制如何帮助神经机器翻译模型获得突破。本文要拆解的Transformer,则把注意力机制推到了前台——它完全抛弃了 RNN,仅靠注意力就完成序列建模,因此天然适合并行计算,训练速度大幅提升,在特定任务上还超过了当时的 Google NMT。Google Cloud 也推荐把它作为 TPU 的参考模型。
Transformer 来自论文 Attention is All You Need,官方 TensorFlow 实现见 Tensor2Tensor;Harvard NLP 组提供了 PyTorch 注释版。下面我们逐步拆开它的内部结构。
1. 从高处俯瞰整个模型
先把 Transformer 当成一个黑盒:以机器翻译为例,喂它一句法语,它吐出一句英语。
把外壳掀开,里面其实分成两部分:一组编码器(Encoders)和一组解码器(Decoders),两者通过中间通道相连。
编码端是若干层编码器堆叠而成,原论文堆了 6 层(数字 6 没有特别含义,可以换);解码端同样堆相同数量的解码器。
每个编码器的结构都一样(但权重不共享),内部由两个子层组成:
自注意力层(Self-Attention):
编码某个词时,让模型同时去看输入句中其他词,从中捕捉依赖关系。
前馈神经网络(Feed-Forward):
对每个位置独立施加同一份前馈网络。
解码器除了拥有这两层,在它们中间还多了一层编码器-解码器注意力(Encoder-Decoder Attention),作用类似 seq2seq 模型里的 attention,让解码器去关注输入序列中相关的位置。
2. 把张量画进流程图
下面我们看数据本身是怎样在这些组件之间流动的。和大多数 NLP 任务一样,第一步是把每个词通过词嵌入变成向量。
每个词被嵌入为长度 512 的向量,图中用方框表示。
词嵌入只在最底层编码器输入端做一次。所有编码器对外的契约其实是一致的:接收一组长度为 512 的向量列表。最底层接收的是词向量,而上面每一层接收的,是它正下方那一层的输出。这个列表的长度是个超参数,通常取训练集中最长句子的长度。
嵌入完成后,每个词向量都会先后经过编码器的两个子层:
**关键性质:**每个位置的词在编码器内部走自己独立的一条路径。自注意力子层里这些路径之间存在依赖(每个位置都要看别的位置);但到了前馈子层,各位置之间彼此独立,因此可以完全并行计算。这是 Transformer 训练比 RNN 快得多的根本原因之一。
3. 编码器里的数据流
下面用更短的句子做例子,看看一个编码器内部到底发生了什么。
每个位置的词向量都先穿过自注意力,再穿过前馈网络(每个位置独立通过同一份前馈网络)。
4. 自注意力(高层直觉)
"自注意力"这个词第一次听可能很玄乎,作者本人也是读了Attention is All You Need论文才第一次接触。下面用一个例子建立直觉。
假设我们要翻译这句话:
“The animal didn’t cross the street because it was too tired”
句中的it指代什么?是street还是animal?人一眼就看出来是animal,但让算法自己判断并不容易。
当模型处理it这个词时,自注意力机制能帮它把it和animal在表示上关联起来。模型每处理一个位置,自注意力都会让它"环顾"输入序列中的其他位置,从中找出对当前位置有帮助的信息。
如果你熟悉 RNN,可以这样类比:RNN 用隐状态把"已经处理过的词"的信息传给当前词;而 Transformer 通过自注意力把"序列中所有相关词"的理解一次性"烤"进当前词的表示里。
当第 5 层(最顶层)编码器处理 “it” 时,注意力机制把一部分焦点放在 “The Animal” 上,并将其表示融入到 “it” 的编码中。
感兴趣可以去 Tensor2Tensor 提供的 Notebook 加载 Transformer 模型并交互式查看注意力可视化。
5. 自注意力(向量级细节)
先用向量讲清楚自注意力怎么算,再扩展到矩阵实现。
第一步:构造 Query / Key / Value 三个向量
对每个输入向量(词嵌入),分别乘上三个训练得到的权重矩阵W^Q、W^K、W^V,得到对应的Query 向量 q、Key 向量 k、Value 向量 v。
注意:这三个新向量的维度比词嵌入要小——论文中嵌入是 512 维,q/k/v 是 64 维。这并不是必须的,而是为了让多头注意力的总计算量大致保持不变。
把 x1 乘上 W^Q 矩阵就得到对应的 q1(Query 向量)。每个词都得到一组 query/key/value 投影。
**怎么直观理解 query / key / value?**它们是三种"角色"。可以想象在做信息检索:query 是当前词发出的"查询",每个 key 像是其他词挂在外面的"标签",把 query 和各个 key 做点积就得到匹配分数;分数越高,意味着对应那个词的 value(实际内容)应该被更多地拿回来。继续往下看具体计算就更清楚了。
第二步:计算分数
假设我们正在为第一个词Thinking计算自注意力。我们要给输入句中每个词打一个"分数",决定编码Thinking时该有多关注其他位置。
分数 =当前词的 query 向量与被打分词的 key 向量的点积。处理位置 1 时:
score₁ = q1 · k1,score₂ = q1 · k2,依此类推。
第三、四步:缩放 + Softmax
把每个分数除以√d_k = √64 = 8(key 向量维度的平方根),目的是让梯度更稳定。然后再过一层 Softmax,把所有分数归一化成正数且加起来等于 1。
这个 softmax 分数就是"在当前位置上,每个词应该被表达多少"。当前位置自己的分数通常最高,但有时把注意力分给一些相关的其他词更有用。
第五、六步:加权求和得到输出
第五步:
把每个 value 向量乘上对应的 softmax 分数。直觉是——保留我们想关注词的内容(乘以接近 1 的数),淡化无关词(乘以例如 0.001 这样很小的数)。
第六步:
把这些加权后的 value 向量求和,得到当前位置(这里是第一个词)的自注意力输出向量 z1。
这就完成了一个位置上的自注意力计算。这个输出向量接下来会被送进前馈网络。但实际工程实现里,我们不会逐位置算,而是一口气用矩阵把所有位置一起算了,下面就是矩阵版本。
6. 自注意力的矩阵实现
第一步:算出 Q、K、V 三个矩阵
把所有词的嵌入按行拼成矩阵 X,然后分别乘上训练好的W^Q、W^K、W^V,得到Q、K、V三个矩阵。
X 矩阵每行是一个词。注意嵌入维度(512,图中 4 格)和 q/k/v 维度(64,图中 3 格)的差别。
最后一步:一个公式搞定
因为我们直接用矩阵,第二到第六步可以浓缩成一个公式:
矩阵形式的自注意力计算公式:Z = softmax( Q·Kᵀ / √d_k ) · V
7. 多头注意力(Multi-Head Attention)
论文进一步对自注意力做了改进,引入了"多头"机制。它带来两个好处:
扩展模型关注不同位置的能力。
前面的例子中,z1 虽然包含了一点其他词的信息,但很可能被自己这个词主导。多头让模型有更多"机会"去关注不同的位置,比如句子 “The animal didn’t cross the street because it was too tired” 中,知道
it指代谁就很关键。提供多个"表示子空间"。
一组 Q/K/V 权重矩阵是一个"头",论文里用了8 个头,所以每个编码器/解码器都有 8 套 Q/K/V 权重。每套都随机初始化,训练完后会把输入投影到 8 个不同的表示子空间里。
多头注意力为每个头维护一组独立的 Q/K/V 权重矩阵,把 X 分别投影出多组 Q/K/V。
把前面那一套自注意力流程跑 8 遍(每遍用不同的权重),就会得到 8 个不同的输出矩阵 Z:
但下游的前馈层只接受单个矩阵,怎么办?做法是:把 8 个 Z 在维度上拼接起来,再乘上一个额外的权重矩阵W^O,把它压回到合适的维度。
多头自注意力大体就这些步骤,矩阵确实很多。把所有步骤画在一张图里复盘一下:
讲完了多头机制,再回到it的例子,看不同的注意力头各自在关注什么:
编码 “it” 时,一个头主要关注 “the animal”,而另一个头主要关注 “tired”——某种意义上,模型对 “it” 的表示同时融入了 “animal” 和 “tired” 的信息。
但如果把所有 8 个头一起画出来,可视化就会变得很难解读:
8. 位置编码:让模型感知顺序
到目前为止,我们描述的模型还差一个东西:它对词的先后顺序毫无感知。RNN 天然按顺序处理,所以位置信息隐含在递归过程中;但 Transformer 是并行处理所有位置的,必须显式地告诉它"谁在前、谁在后"。
解决方法是——给每个输入嵌入向量再加上一个位置编码向量(Positional Encoding)。这些向量遵循一种特定的模式,模型在训练后就能据此推断每个词的位置或词与词之间的距离。直觉是:把这些位置信息加进嵌入后,再投影成 Q/K/V 做点积时,向量之间就能体现"有意义的距离"。
为了让模型感知词序,我们额外加上位置编码向量——它的取值遵循特定的数学模式。
假设嵌入维度只有 4,真实的位置编码可能长这样:
嵌入维度为 4 时位置编码的真实例子。
那位置编码的"模式"长什么样?下图中每一行就是一个位置上对应的位置编码向量——第 1 行加给序列中第 1 个词,第 2 行加给第 2 个词,依此类推。每行有 512 个值,每个值都在 -1 到 1 之间,颜色编码后能看出明显的图案。
20 个词(行)× 512 维(列)的真实位置编码。可以看到从中间一分两半:左半边由 sine 函数生成,右半边由 cosine 函数生成,再拼接起来构成完整的位置编码向量。
具体公式见原论文 3.5 节,参考实现可看 Tensor2Tensor 仓库里的get_timing_signal_1d()。这并不是唯一的位置编码方法,但它有一个好处——能外推到训练时未见过的更长序列。
**2020 年 7 月勘误(作者补充):**上面这张图展示的是 Tensor2Tensor 实现里的位置编码方式(左右拼接 sin/cos)。论文里的写法略有不同,是把 sin 和 cos 两路信号交错排列而不是直接拼接。下图展示了论文版本的样子(生成代码在此):
9. 残差连接与层归一化
编码器内部还有一个细节值得说:每个子层(自注意力 / 前馈网络)外面都包了一层残差连接(residual connection),并在残差相加后再做一次层归一化(Layer Normalization)。
如果把自注意力的向量流和层归一化操作画出来,看起来是这样:
解码器的子层也是同样套路。如果想象一个由 2 层编码器 + 2 层解码器组成的精简版 Transformer,整体结构大致是这样:
10. 解码器一侧
编码端的概念都讲清楚了,解码器的组件其实和编码器几乎一样——下面看它们如何协同工作。
编码器先处理整个输入序列,最顶层编码器输出会被转换成两个矩阵K和V,作为"编码端记忆"提供给每一层解码器中的encoder-decoder attention子层使用。借助这两个矩阵,解码器在生成时能持续关注输入序列里的相关位置:
编码阶段结束后进入解码阶段,每一步解码器会输出目标序列里的一个词(这里就是英文翻译里的下一个词)。
之后这个过程不断重复,直到生成一个特殊的"句末"标记 ``,表示翻译结束。每一步的输出会作为下一时间步最底层解码器的输入。和编码器一侧一样,对解码器输入也要做嵌入并加上位置编码,以指示词的位置。
解码器在生成第 t 个位置时只能看到自己已经生成的位置 1…t-1,不能看未来。实现方式是在 Softmax 之前把"未来位置"对应的分数设成
-inf,softmax 之后这些位置的权重就趋近于 0。Encoder-Decoder Attention:
结构和多头自注意力完全一样,区别在于——它的 Query 来自下一层(即解码器自身的输出),而 Key、Value 来自编码器栈最顶层的输出。这就是解码器"看输入"的通道。
11. 最后的 Linear 与 Softmax 层
解码器栈输出的还是一组浮点向量,怎么把它变成一个真实的词?这就是最后的 Linear 层 + Softmax 层负责做的事情。
Linear 层是一个简单的全连接网络,把解码器输出的向量投影成一个非常长的向量,称为logits 向量。
假设我们的模型从训练集中学到了 10,000 个不同的英文单词(“输出词表”),那 logits 向量就有 10,000 个元素,每个元素对应一个词的"得分"。
Softmax 层把这些得分变成概率(全部为正且加起来等于 1),概率最大的那个位置对应的词,就是这一时间步的输出。
从下往上看:解码器栈输出向量 → 通过 Linear 投影到 logits 向量 → Softmax 转成概率 → 取最大概率对应的词。
12. 训练过程回顾
前向传播(推理)讲完了,再回头看一眼训练是怎么进行的。
未训练的模型走的就是同样一套前向流程,只不过我们手上有带标注的训练数据,可以拿模型的输出和"正确答案"做对比,再用反向传播把权重朝正确方向调整。
为了画图简单,假设我们的输出词表只有 6 个词:a、am、i、thanks、student、``(句末符)。
词表是在训练之前的预处理阶段就确定好的。
定义好词表后,可以用一个长度等于词表大小的向量来表示每个词,也就是one-hot 编码。例如表示词am:
玩具词表的 one-hot 编码示例。
13. 损失函数
假设这是训练的第一步,我们用一个最简单的样本:把法语merci翻译成英语thanks。
我们希望模型输出的概率分布在thanks这一格上概率最高。但因为模型权重都是随机初始化的,目前它给出的分布几乎是任意的:
初始权重随机,未训练模型给出的分布是任意的;我们把它和期望的分布做差异度量,再用反向传播去调整所有权重,让输出逐步贴近期望。
怎么衡量两个概率分布的差异?最直接的就是相减;更严谨的指标是交叉熵(cross-entropy)和 KL 散度。
真实场景里我们当然不会只翻译一个词。比如输入"je suis étudiant",期望输出"i am a student"。这意味着我们希望模型连续输出 5 个概率分布,满足:
- 每个分布是长度为词表大小的向量(玩具例子里是 6,真实场景往往是 30,000 或 50,000);
- 第 1 个分布在
i上概率最高; - 第 2 个分布在
am上概率最高; - ……依此类推;
- 第 5 个分布在 ``(句末符)上概率最高。
训练样本对应的目标概率分布——我们就是让模型去逼近这组分布。
充分训练之后,我们希望模型能输出像下图那样的分布:
训练完成后,模型应当能输出我们期望的翻译。注意每个位置上其它词也会分到一点点概率——这是 softmax 的一个有用性质,对训练过程很有帮助。当然,如果这句话本来就在训练集里,泛化能力还要靠交叉验证来真正考察。
解码策略:贪婪 vs Beam Search
由于模型一次输出一个词,最简单的做法是:每一步直接选概率最大的那个词,丢掉其他。这就是贪婪解码(greedy decoding)。
更聪明一点的做法是Beam Search——每一步保留概率最高的若干个候选(比如 2 个,记beam_size = 2):在第 1 步保留I和a两个候选;第 2 步则分别假设第 1 个词是I跑一次模型,再假设是a跑一次,比较哪条路径整体误差更小,就把好的那条留下;不断重复。beam_size(每一步保留的候选数)和最终返回的翻译数量都是可调超参数。
最后
选择AI大模型就是选择未来!最近两年,大家都可以看到AI的发展有多快,时代在瞬息万变,我们又为何不给自己多一个选择,多一个出路,多一个可能呢?
与其在传统行业里停滞不前,不如尝试一下新兴行业,而AI大模型恰恰是这两年的大风口,人才需求急为紧迫!
人工智能时代最缺的是什么?就是能动手解决问题还会动脑创新的技术牛人!智泊AI为了让学员毕业后快速成为抢手的AI人才,直接把课程升级到了V6.0版本。
这个课程就像搭积木一样,既有机器学习、深度学习这些基本功教学,又教大家玩转大模型开发、处理图片语音等多种数据的新潮技能,把AI技术从基础到前沿全部都包圆了!
课堂上不光教理论,还带着学员做了十多个真实项目。学员要亲自上手搞数据清洗、模型调优这些硬核操作,把课本知识变成真本事!
课程还教大家怎么和AI搭档一起工作,就像程序员带着智能助手写代码、优化方案,效率直接翻倍!
这么练出来的学员确实吃香,83%的应届生都进了大厂搞研发,平均工资比同行高出四成多。
智泊AI还特别注重培养"人无我有"的能力,比如需求分析、创新设计这些AI暂时替代不了的核心竞争力,让学员在AI时代站稳脚跟。
课程优势一:人才库优秀学员参与真实商业项目实训
课程优势二:与大厂深入合作,共建大模型课程
课程优势三:海外高校学历提升
课程优势四:热门岗位全覆盖,匹配企业岗位需求
如果说你是以下人群中的其中一类,都可以来智泊AI学习人工智能,找到高薪工作,一次小小的“投资”换来的是终身受益!
·应届毕业生:无工作经验但想要系统学习AI大模型技术,期待通过实战项目掌握核心技术。
·零基础转型:非技术背景但关注AI应用场景,计划通过低代码工具实现“AI+行业”跨界。
·业务赋能 突破瓶颈:传统开发者(Java/前端等)学习Transformer架构与LangChain框架,向AI全栈工程师转型。
智泊AI始终秉持着“让每个人平等享受到优质教育资源”的育人理念,通过动态追踪大模型开发、数据标注伦理等前沿技术趋势,构建起"前沿课程+智能实训+精准就业"的高效培养体系。
重磅消息
人工智能V6.0升级两大班型:AI大模型全栈班、AI大模型算法班,为学生提供更多选择。
由于文章篇幅有限,在这里我就不一一向大家展示了,学习AI大模型是一项系统工程,需要时间和持续的努力。但随着技术的发展和在线资源的丰富,零基础的小白也有很好的机会逐步学习和掌握。
【最新最全版】AI大模型全套学习籽料(可无偿送):LLM面试题+AI大模型学习路线+大模型PDF书籍+640套AI大模型报告等等,从入门到进阶再到精通,超全面存下吧!
获取方式:有需要的小伙伴,可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】
来智泊AI,高起点就业
培养企业刚需人才
扫码咨询 抢免费试学
⬇⬇⬇
AI大模型学习之路,道阻且长,但只要你坚持下去,就一定会有收获。