第 16 章 · 序列与 Transformer
为什么需要注意力
从这一章起,我们正式向大模型进发。但在拆零件之前,先解决一件更要紧的事:别让知识点散成一地。 本章分两段走:先说明序列为何成为新难题、旧办法为何不够,由此引出注意力(attention); 再拿一句诗和一张贯穿第 16~22 章的总路线图,把“喂给它文本 → 它怎么训练 → 它怎么推理”从头到尾走一遍。
这 7 章不是七个独立话题,而是一条流水线上的七段。本章先说明为何需要注意力,再给全景和一个完整例子。
读完这一章,你会明白
- “序列”这种数据为什么对前面的网络是个难题,两种朴素做法各自的硬伤(信息瓶颈 / 传话衰减);
- 注意力的一句话核心:让每个词直接看全局,自己决定关注谁;
- 一眼看懂从文本到大模型的完整流水线,以及每一段分别在后面哪一章展开;
- 跟着一句诗的例子,看模型怎么被喂进去、怎么训练、又怎么自己接下去(推理);
- 理清一个到处出现、却各有所指的词——“上下文”到底在说什么。
1. 新挑战:句子是一串会互相影响的词
一句话由许多 token(可以先理解成“词”或“字”)按顺序组成。 它有两个让旧网络头疼的特点:长度不固定(有的句子三个词,有的三十个), 以及词之间有关系(谁修饰谁、谁指代谁)。固定大小的 MLP 一次只吃固定长度的向量, 天然不适合这种“弹性又互相纠缠”的输入。
2. 两种老办法,各有硬伤
老办法一:把整句话压成一个向量。 先把所有词的信息硬塞进一个固定大小的向量,再交给前面学的 MLP。问题是:句子越长,这个向量就越“挤”,细节被严重压缩——这叫信息瓶颈。
老办法二:按顺序一个一个传,也就是上一部分的 RNN。 它用隐藏状态把“记忆”一路往后递(第 14 章)。 思路很自然,但我们已经看到它的两个硬伤:
- 远距离会“失忆”:信息像传话游戏,每传一步就被改写一点,隔得远的词之间依赖几乎学不到(梯度消失,第 14 章)。
- 快不起来:严格串行,第 5 个词的隐藏状态得等第 4 个算完,没法拆给一堆 GPU 同时算——训大模型时是致命瓶颈(第 21 章)。
LSTM(第 14 章)用门控缓解了“失忆”,但“串行慢”始终没解决。我们真正想要的,是一个 既能一步连接远处、又能并行的新思路。
3. 真正的难点:相关的词,可能离得很远
看这句话:
小明把书放在桌子上,因为它太重了。
“它”指的是“书”还是“桌子”?要判断,模型必须把“它”和前面的“书”联系起来—— 可它们之间隔了好几个词。难点不在于词多,而在于“该关联的词常常离得很远”。 按顺序传递的办法,在这种长距离依赖上特别吃力。
4. 注意力的核心想法
注意力(attention)换了个思路,简单得近乎暴力:不要再一站站传话了, 让每个词直接去看序列里所有的词,然后自己决定该重点关注谁。 “它”可以一眼扫过全句,发现自己和“书”最相关,于是把注意力大部分放在“书”上。
由此自然得到这样三步走:
- 都看一眼:让“它”和句子里每一个词都照个面;
- 打个相关分:给每次照面打个分,“它”和“书”最搭 → 高分,和“桌子”一般 → 低分;
- 按分数融合:分数高的词多吸收一点内容,分数低的少吸收一点,把大家的信息加权平均成“它”的新含义。
这三步就是注意力的全部骨架。此处不必追究细节——只要记住这个“先打分、再按分数加权平均”的味道, 下一章我们会把每一步都换成具体的算法和数字。
上:信息沿链条逐跳衰减。下:“它”一步直达全句,粗线表示它把更多注意力分给了“书”。
开会:一个人发言前,先扫一眼全场,决定主要听谁的、参考谁的,而不是只听旁边一个人耳语转述。
查资料:带着一个查询(query),扫过所有资料的标题(key),挑出最相关的几份,重点读它们的内容(value)。
注意力的本质,不是“更复杂的矩阵运算”,而是赋予每个词一种能力:主动地、按相关性,把注意力分配到序列里所有其他词上。
上面四节回答的是“为什么需要注意力”。接下来用一句诗,把 tokenizer → embedding → Transformer(含注意力) → 输出层 这条完整流水线走一遍——后面几章会沿这条路逐站拆开。
5. 先把整条流水线走一遍(喂它一句诗)
很多人读大模型这几章会“越读越散”:注意力、Transformer、训练、采样、并行……像一地零件,不知道怎么拼成一台机器。 前面四节已经说明为什么需要注意力;这一节把整机跑一遍:拿一个最小的例子,把“喂文本 → 训练 → 推理”从头到尾走通。 此处不必追究每一步的内部细节——只要记住顺序和每一步在哪一章展开。这就是那张能把后面几章串起来的“地图”。
一次“前向”:一段文本进去,出来的是“下一个字该是谁”的概率。训练和推理,都建立在这条前向之上——下面逐段看。
例子:我们只教它背一句诗
把喂给模型的全部“课本”设成一句诗:「床前明月光」(《静夜思》第一句)。 模型的任务朴素到极点:看着前面几个字,猜下一个字。只要它学会了,就能自己把这句诗背出来、接下去。 真实大模型的“课本”是整个互联网,任务却一模一样,只是规模天差地别。
① 喂进去:先把文本切成字、编成号(tokenizer)
模型不认字,只认数字。第一步就是把这句诗切成一个个字,再给每个字发一个固定的号码(id):
| 字 | 床 | 前 | 明 | 月 | 光 |
|---|---|---|---|---|---|
| id 号码 | 0 | 1 | 2 | 3 | 4 |
这张“字 ↔ 号码”的对照表叫词表。于是「床前明月」就变成了一串数:[0, 1, 2, 3]。
这一步 = 第 19 章的 tokenizer;真实大模型切的是“子词”而非单字,原理一样(第 20 章 BPE)。
② 每个号码查成一串数,并标上“第几位”(embedding + 位置编码)
号码本身没有含义(2 不比 1“大”)。所以再拿号码去查一张大表,把每个字换成一串数(词向量)—— 这串数就是这个字的“意思坐标”,意思相近的字,数也相近。同时再给它标上“这是第几个字”(位置编码), 否则“明月”和“月明”在模型眼里会一模一样。
这一步 = 第 19 章的 embedding 查表 + 第 18 章的位置编码;这些向量为什么能带“语义”,见第 15 章、第 20 章。
③ 让每个字互相打量、反复加工(注意力 + Transformer)
这是整机里最核心的一步,也是第 4 节注意力思路的具体实现。每个字都回头看一眼前面所有的字, 按“和我有多相关”给它们打分,再把大家的信息加权融合进自己——这就是注意力。 比如“月”会更多地参考“明”。这样融合一轮叫一层,反复堆很多层(每层还夹着一些常规加工),字的表示就越来越“懂上下文”。
当模型处理“月”时,它能看到的前文——“床、前、明”——就是“月”的上下文。 一个字能不能看到、能看到多远,全在这一步决定。(“上下文”这个词后面还会以别的意思出现,本章末尾专门列一张表理清。)
这一步 = 第 17 章的注意力 + 第 18 章的 Transformer Block(堆叠、残差、归一化、前馈)。
④ 吐出“下一个字”的概率(LM Head)
第 ③ 步加工完,每个位置都得到一串新的数(向量)——这串“带着上下文的数”, 既是注意力那几层算出来的输出,又正好当作这一步输出层的输入 (就像 MLP 里,隐藏层算出的那排数,就是下一层的输入)。最上面再接一个输出层, 把最后位置那串数投影到整张词表,给每个候选字打分,过 softmax 变成概率。 假设现在喂进「床前明月」,一个刚初始化、还没训练的模型给出的概率大概是这样(乱猜,谁也不突出):
| 下一个字候选 | 床 | 前 | 明 | 月 | 光(正确答案) |
|---|---|---|---|---|---|
| 模型给的概率 | 0.24 | 0.19 | 0.22 | 0.23 | 0.12 |
正确答案“光”只拿到 0.12——模型此刻还什么都不会。输入一串字,输出下一个字的概率:这就是“输入输出”的全部。
这一步 = 第 19 章的 LM Head(输出层 + softmax)。到这里,一次“前向”就走完了。
输入是一串字([床,前,明,月]),输出是“下一个字是谁”的一排概率。 模型自始至终只会这一件事。接下来的“训练”和“推理”,都只是反复使用这条前向——一个用来改参数,一个用来写下文。
⑤ 怎么训练:对答案、算错、往回改一点点
模型一开始把“光”只猜到 0.12,错得离谱。怎么让它变准?关键是:正确答案根本不用人标—— 原文里“床前明月”后面本来就跟着“光”。这就是天然的答案(叫自监督)。有了答案,训练就是一个循环:
- 前向:走一遍上面 ①~④,拿到概率(“光”=0.12)。
- 算错得多离谱(损失):只看它给正确答案的概率,用交叉熵
−log(0.12) ≈ 2.1。给对的概率越低,这个数越大。 - 反向传播:把这个“错”从输出层一路反着传回去(第 6 章),给模型里每一个“可以被调的数”都算出一个梯度——也就是“往哪边挪、挪多少”的建议。
- 更新:每个数都按同一条规则挪一小步:
新值 = 旧值 − 学习率 × 梯度(第 5 章)。挪完,“光”的概率就大了一点点。
把这个循环在这句诗上重复很多遍,概率就会肉眼可见地往正确答案集中、损失一路下滑:
| 「床前明月」→ ? | 给“光”的概率 | 损失 −log(概率) |
|---|---|---|
| 刚开始(乱猜) | 0.12 | ≈ 2.1 |
| 练了一会儿 | 0.55 | ≈ 0.6 |
| 练久了 | 0.90 | ≈ 0.1 |
“练一次挪一点”,重复亿万次(真实大模型),那些查表向量和注意力矩阵就从随机数,长成了“懂这门语言”的样子。
这一步 = 第 4 章损失 + 第 5 章梯度下降 + 第 6 章反向传播;“答案自带”的自监督见第 19 章、第 20 章。
“参数”就是模型里所有可以被训练调整的数。 语言模型里主要有这么几类(先有个印象即可,后面每一类都有专章):
- 查表向量(嵌入表):每个字对应的那串数(第 ② 步)——第 19 章;
- 注意力里的几个矩阵:决定“每个字该拿什么去问别人、给自己挂什么标签、被选中后交出什么内容”(第 ③ 步)——第 17 章;
- 每层的小网络(前馈)和归一化的缩放系数(第 ③ 步)——第 18 章;
- 最后的输出层:把向量投影成“下一个字的概率”(第 ④ 步)——第 19 章。
关键:不管哪一类,更新方式一字不差,全是那一条 新值 = 旧值 − 学习率 × 梯度。
它和 MNIST 那个手写数字网络本质完全一样——唯一的区别是:MNIST 那张网只有“权重 + 偏置”两种参数,
而语言模型多了“嵌入表、注意力矩阵、归一化系数”这些新种类;但每一种怎么算梯度、怎么挪,和 MNIST 完全相同。
(真实大模型爱用 Adam 这种更聪明的优化器,第 8 章,但那只是“挪得更讲究”,并没改变“算梯度 → 挪一步”这个套路。)
一张完整的参数清单,见第 19 章。
⑥ 怎么推理:自己一个字一个字接下去(自回归)
训练好之后,怎么让它“写字”?给它一个开头(叫 prompt),让它反复走前向,每次挑出概率最高的字,接到末尾,再喂回去。 拿开头“床前”举例:
| 第几步 | 喂进去的上文 | 模型最看好的下一个字 | 接完变成 |
|---|---|---|---|
| 1 | 床前 | 明 (0.88) | 床前明 |
| 2 | 床前明 | 月 (0.86) | 床前明月 |
| 3 | 床前明月 | 光 (0.90) | 床前明月光 |
把“猜出来的字”接回输入、再猜下一个——这个循环叫自回归。你看到 ChatGPT “一个字一个字往外蹦”,就是它。
这一步 = 第 19 章的自回归生成;至于是“死板挑最高”还是“带点随机”(temperature/top-k/top-p),也在第 19 章。
⑦ 再往上:放大成大模型、以及怎么用它
到第 ⑥ 步,一个“会背诗、会接话”的迷你语言模型就齐活了。剩下的几章,是把这台小机器往上推:
- 放大:同一套东西,把参数、数据、算力一起放大上亿倍,就“涌现”出聊天、写代码的能力(第 20 章);
- 跑得起来:大到单卡装不下,要靠 GPU、显存、多卡并行、KV cache、MoE 等工程手段(第 21 章);
- 用得好:不必训练它,只用它——提示词、RAG 外挂知识、工具调用、Agent(第 22 章)。
一张总表:每个环节分别在哪一章
这就是开头那句“流程里每个地方分别在哪”的答案。读后面任何一章卡住时,回到这张表对一下坐标:
| 流程环节 | 在这个例子里干了啥 | 哪一章细讲 |
|---|---|---|
| ① 切字·编号(tokenizer) | 「床前明月光」→ [0,1,2,3,4] | 19 · BPE 20 |
| ② 查表成向量 + 位置 | 每个 id → 一串数,并标上第几位 | 嵌入 19 · 位置 18 |
| ③ 互相打量(注意力) | 每个字按相关性看前文、融合 | 17 |
| ③ 反复加工堆叠(Block) | 残差 / 归一化 / 前馈,堆很多层 | 18 |
| ④ 出下一个字概率(LM Head) | 给词表每个字打分 → softmax | 19 |
| ⑤ 对答案算错(损失) | 和真下一个字“光”比,交叉熵 | 4 · 19 |
| ⑤ 往回改参数(反向传播) | 一路更新到注意力矩阵、嵌入、输出层 | 5 · 6 |
| ⑥ 自己接龙(自回归推理) | 把猜出的字接回去,再猜下一个 | 19 |
| ⑦ 放大成大模型 | 参数 / 数据 / 算力一起放大 | 20 · 21 |
| ⑦ 用好它 | 提示 / RAG / 工具 / Agent | 22 |
顺便说清:“上下文(context)”到底指什么
“上下文”这个词在后面会反复出现,但它在不同地方指的东西并不一样。下表一次性理清:
| 说法 | 到底指什么 | 在哪一章 |
|---|---|---|
| 上文 / 上下文(注意力里) | 当前这个字能回头看到的前面那些字(“月”的上下文 = 床、前、明) | 17 |
上下文窗口 context window / context_size | 一次最多能塞进去多少字/token(窗口多大) | 19 |
| 长上下文 long context | 把窗口做得很大(几十万 token),能一次读一本书 | 20 · 21 |
| 上下文学习 in-context learning | 不改任何参数,只靠你在输入里给的例子/说明,让它临场学会做题(few-shot) | 22 |
| KV cache 缓存的“上文” | 生成时把已经算过的前文的中间结果存起来复用,省得每步重算 | 21 |
同一个词,五种意思。记住:前三种都在说“能看到多少前文”,第四种是“靠前文临场学”,第五种是“把前文的计算缓存起来”。
流水线地图到此为止。前面四节说明了为什么需要注意力;这里用一句诗把整机跑通。后面各章会沿这条路逐站展开——下一章从 Q、K、V 算起。每章开头那条路线图,会一直提醒你走到哪一站。
小结
- 语言是序列:长度可变、词与词互相关联,固定大小的 MLP 天然不适合。
- 把整句压成一个向量 → 信息瓶颈;按顺序逐个传递 → 传话式的远距离衰减。
- 真正的难点是“相关的词可能离得很远”(如代词指代)。
- 注意力让每个词直接看全局、按相关性自己分配关注,从根上解决了长距离依赖。
动手与思考
问题 1:为什么说“按顺序逐个传递”处理长句子有先天劣势?
因为信息像传话游戏一样要经过很多中间步骤,每一步都可能被改写或稀释,等传到很远的位置,最初的信息早已模糊。长距离依赖最容易在这种方式里丢失。
问题 2:用一句话概括注意力解决了什么核心问题?
让每个词都能直接“看到”序列里所有其他词,并按相关性自主决定关注谁,从而绕开了长距离信息传递的衰减问题。
如果整章你只带走一句话,请带走这句:注意力的输出,就是对所有词做一次“加权平均”。 权重代表“相关性”——越相关的词,在这次平均里占比越大。下一章那些 Q、K、V、softmax, 全都只是在回答一个问题:这组加权平均的权重,到底该怎么算? 带着这个念头去看,会顺很多。
想法很美,但“直接看全局、决定关注谁”到底怎么用数字算出来? 下一章就把这台机器拆开:Q、K、V、打分、softmax、加权求和,一个都不少。