跳到主要内容

它内部长什么样 — 一次一个词元,其余全是在省时间

这一章讲三件事: 你按下回车之后,机器内部到底在重复什么动作; 为什么这个动作决定了它「写长文必然慢、而且有硬性长度上限」; 以及为什么近几年那一长串听起来很唬人的改进,几乎全都在优化同一个步骤。 在全书链条里,这一章是「往下写」那一支的解剖图—— 第 04 章往后所有「怎么用它」的讨论,都建立在这张图上。 不需要额外基础,遇到的生词都在当场解释。

1. 先看现象:字是一个一个蹦出来的,这不是动画效果

你用这类工具时,回答不是「唰」一下整段出现,而是一小段一小段往外冒

那就是它真实的工作节奏。 书说得很直接:模型并不是一次性生成全部文本的, 它一次只生成一个词元1

而且每生成一个词元,它都要做同一件事:

你打的整段字

├─ 完整跑一遍机器 → 得到「下一个词元该是什么」的可能性清单 → 挑一个


把挑中的那一小段接到文末,得到一段更长的字

├─ 完整再跑一遍 →(从头来过)

……一直到它算出「该停了」,或者撞到你设的长度上限为止

图说:每冒一小段字,整台机器就完整过一遍。这一条解释了这一章后面的全部内容。

书给这一次完整的过程起了个名字,后面反复出现,现在就讲掉:

前向传播:输入进入网络,一层一层往前流过所有该做的计算,最后在另一端出结果。 「前向」是相对「反向」说的——训练时要倒着回去调整参数,那叫反向; 平时用它的时候只有前向,没有反向。1

书还给这种「拿自己刚写的东西当输入,接着写下一个」的模型起了个正式名字: 自回归模型——它使用自己早期的预测结果来做出后续的预测2。 第 01 章已经见过这个词,这里是它的精确定义。

注意一个容易被忽略的事实:那个「一直循环」的动作,不在神经网络里面。 书的措辞很准:神经网络周围的软件基本上在一个循环中运行它3—— 循环在外层的普通程序里,网络本身一次只往前走一趟,从不回头。

2. 顶层全景:三段构造,一个外层循环

┌─────────────────────────── 外层循环(普通程序,不是神经网络)───────────────────┐
│ │
▼ │
你的整段文字 │
│ │
│ ① 词元化器:切成一小段一小段,查出每段的编号 │
▼ │
[编号, 编号, 编号, …] │
│ │
│ ② 一大摞「处理块」:原始论文里六个左右,今天的大模型超过一百个 │
│ 每一块里只有两样东西——回头看前文的那一层 + 存放知识的那一层 │
▼ │
每个位置出来一串数 │
│ │
│ ③ 最后一层:只拿最末一个位置的那串数,把它摊成「词表里每个词元的可能性」 │
▼ │
一张几万行的可能性清单 ──▶ 按某种规矩挑一个 ──▶ 接到文末 ───────────────────────────┘

图说:三段是固定的,循环是外挂的。 书给这三段的正式叫法是: 词元化器、一堆 Transformer 块、以及一个「语言模型头部(就是接在最后面、 负责把内部结果翻译成你要的形式的那一小层)」4

「头部」这个词需要就地讲清楚:

头部就是接在那一大摞处理块最后面的一小层,负责把内部结果翻译成你要的形式。 装哪种头部,这台机器就干哪一行。书点名了另外两种: 序列分类头部(整段文字给一个类别)和词元分类头部(每一小段各给一个类别)5

这句话很重要:同一摞处理块,换个头部就换了工种。 第 08 章讲「逐词分类」时用的就是它。

3. 核心原理

3.1 打开一个真实模型,看看这三段是不是真的只有三段

这一节回答:上面那张图是简化的示意,还是它真的长这样。

书做了一件很实在的事——把模型内部的零件清单直接打印出来,让你看它的层次结构。 拿书全程用的那个 38 亿参数的小模型来看,结果是这样6:

打印出来的东西它是什么
一张 32,064 × 3,072 的大表词表里 32,064 个词元,每个词元对应一串 3,072 个数
32 个同样结构的「解码器层」就是上面说的「一大摞处理块」
每一层里:一个注意力部件 + 一个前馈部件书特意注明前馈部件也叫 MLP 或多层感知机——三个名字同一样东西7
最后一层:吃 3,072 个数,吐 32,064 个数就是「头部」。吐出来的个数正好等于词表大小

最后一行是理解全局的关键。 头部输出的那 32,064 个数,一个数对应词表里的一个词元, 数值大小代表「它当下一个词元的可能性有多大」。

别把三个数字弄混了,它们各管各的:

数字是什么例子里的值
词表大小模型认识多少种词元32,064
模型维度(就是「每个位置内部用多长的一串数来表示」)见下面那段说明3,072
层数那摞处理块有几块32

注意这里的「3,072」不是 3,072 个词。 它是同一个位置内部的表示长度—— 书管这个数叫模型维度8。你可以把它理解成「机器描述一个位置时用多少条属性」, 但这些属性单独拿出来通常没法解释(第 01 章讲过,书自己也承认了)。

3.2 挑哪一个:一张清单,和一条「不总挑最高分」的规矩

这一节回答:为什么同一句话问两遍,答案不一样。

概率就是「可能性有多大」,用 0 到 1 之间的一个数表示——0 是绝不会,1 是一定会。 本章说的「分数」「可能性」指的都是它;3.1 节那个头部吐出来的 32,064 个数, 就是词表里每个词元各拿到的一个概率。

温度就是「一个你自己可以调的设定,它决定这台机器多敢挑冷门」—— 数值越大越敢,调到 0 就是完全不敢、每次都挑最高分。 它改的不是上面那些概率本身,而是「拿到这些概率之后怎么挑」;完整讲法在第 05 章。

有了那张几万行的可能性清单,还要从中挑一个。挑的规矩,书叫「解码策略」9

最简单的规矩是每次都挑分数最高的那个。 但书的评价很直白: 在实践中,对于大多数用例来说,这往往不会产生最佳输出; 更好的方法是加入一些随机性,有时选择概率第二高或第三高的词元10

具体做法是「按分数抽签」:书举的例子是——如果某个词元的可能性是 40%, 那它就有 40% 的机会被选中,而不是「因为它最高所以一定被选中」10

规矩怎么挑你会看到什么
贪婪解码(书里的叫法)每次都挑最高分,一点随机都没有稳定、可复现,但常常平淡
按分数抽签分数高的更容易中,低的也有机会有变化,同一问题问两遍答案不同

「贪婪」这个词在计算机领域是个术语,意思是「每一步只挑当下看起来最好的,不管全局」。 它不含褒贬。

书还顺手把一个你可能调过的设定和这里对上了: 把温度设成零,发生的就是贪婪解码11

书给了一个可以自己验的最小例子:输入「法国的首都是」,取分数最高的那个词元, 解出来是**「巴黎」**12这就是整台机器最基本的一次动作。

3.3 一个位置一条流水线:上下文长度到底限制的是什么

这一节回答:「上下文长度 4K」这句话,限制的到底是什么东西。

书说 Transformer 相较之前的架构,一个最引人注目的特性是更适合并行计算13。 具体到文本生成上,这句话的意思是:

每个输入词元都流经自己的一条计算路径。 书把这些路径叫「流」或「轨道」13

上下文长度,限制的就是「同时能有多少条流」:

一个上下文长度 4K 的模型只能处理 4K 个词元,因此也就只有 4K 条这样的流14

这里有一件事很容易被问到,书主动回答了:

既然最后只用最末一条流的结果,为什么还要费劲算前面所有流?15

因为前面那些流的中间结果,是算最后一条流所必需的。 书的原话是:我们没有使用它们的最终输出向量,但在每一块的注意力步骤里, 用了它们较早的输出

这句话是下一节讲「缓存(就是把算过的结果先存着,下次直接取现成的,不重算)」的全部前提。

每条流的开头,除了那串代表词元本身的数,还要掺进「你是第几个」这条信息—— 书叫位置嵌入,本章 3.10 节会讲它今天怎么做16

为什么必须掺位置? 因为注意力这个机制天生不分先后,它同时看所有位置。 不告诉它谁在前谁在后,「狗咬人」和「人咬狗」在它眼里就是同一堆词。

3.4 为什么写长文必然慢,以及唯一的解法

这一节回答:为什么第一个字出来得快,长文却越写越久;以及为什么各家接口都要「流式」吐字。

慢的原因在第 1 节就已经说完了:每吐一个词元,整台机器完整跑一遍。

但这里面有大量重复劳动:第二次跑的时候,前面那些位置的中间结果和第一次一模一样—— 因为它们看到的前文没变。

所以解法很朴素:把算过的存下来,别重算。 书给这个做法的正式名字是键值缓存 (常写作 KV 缓存,「键」和「值」是注意力机制里的两个部件,3.6 节会讲)17

存下来之后,每一轮只剩最后一条流是活的,前面全部直接取现成的18

书给了实测,这是全书最有说服力的一组数字之一——同一台免费显卡、同一个提示、生成 100 个词元19:

设置耗时
开启缓存(各家默认都是开的)约 4.5 秒
关闭缓存21.8 秒

差了将近五倍。

书紧接着说了一句更实在的话:即使 4 秒,对于盯着屏幕等的人来说也太长了—— 这正是各家接口都要「边生成边往外吐」而不是等全部生成完再一次性返回的原因之一20

判断(我们的,不是书里的): 这条实测数字今天仍然成立,但它的含义变了。 2024 年它讲的是「记得开缓存」;今天缓存早就是默认,这条数字真正的用处是让你理解 为什么「长上下文」很贵——缓存要占显存,而且占的量跟已经生成的长度成正比: 长度翻一倍,占用也翻一倍(这种「成正比」的关系,这一行叫线性)。 你把十万字塞进去再让它写,慢的不是「读那十万字」,是「每写一个字都要背着那十万字的缓存」。 如果错,会错在: 如果某种新结构让「回头看前文」的开销不再随长度增长 (确实有人在做这个方向),那这条推论就只对今天这一代结构成立。

3.5 处理块里只有两样东西

这一节回答:那一百多块里到底装着什么。

书说得很干脆:一个 Transformer 块由两个连续的组件组成21:

组件干什么书的原话
注意力层回头看前文,把有关的信息拿过来「主要负责从其他输入词元和位置中整合相关信息21
前馈层存放它记住的东西,以及在记住的东西之间做推断承载了模型的大部分处理能力22

块与块之间只是简单串联:每个块处理它的输入,然后把结果传给下一个块23。 原始论文里大概六块,今天的大模型超过一百块23

前馈层:知识存在这儿

书给了一个很好懂的例子。你输入「The Shawshank」,指望它接上「Redemption」 (那是 1994 年一部著名电影的英文名)。这份知识存在哪儿?存在前馈层里24

书的解释是:模型在通读海量文本时(其中出现了大量「The Shawshank Redemption」), 学习并存储了让它能完成这件事的信息和行为24

但它不只是个大数据库。

先讲一个词:插值就是「在两个已知的点之间推出中间那一段」—— 你测过 3 楼和 5 楼扔球各要多久,4 楼不必再测,按那两个数推一下就够了。 书说前馈层干的正是这件事:它存下来的不只是一个个孤立的例子,还有例子与例子之间的过渡, 所以你换一个它没原样见过的说法,它照样接得上。

由此就到了这一行衡量「学没学会」的那个标准。 书的原话是:

模型能够用同一套机制在数据点和更复杂的模式之间进行插值,从而进行泛化—— 也就是在之前没见过的、不在训练数据里的输入上也能表现良好25

引文里那个词就叫泛化:在没见过的情况上也做得对。 训练数据上答得再漂亮,换个新情况就答错,这一行不算它学会了。

「记住」和「推出没见过的」都在这一层,这就是它被叫做「承载大部分处理能力」的原因。

一个必须澄清的地方:你日常用的东西,已经不是这一节说的模型了

书在这里插了一条很诚实的注,值得单独摘出来:

把「The Shawshank」发给一个聊天式的模型,它不会只接「Redemption」, 而是会告诉你这是 1994 年的电影、谁导演的、改编自谁的小说。 原因是:原始的语言模型对普通用户来说难以正确使用, 所以它们又经过了「教它听指令」和「按人的偏好调」这两道工序26

换句话说:这一章解剖的是「毛坯」,你日常接触的是「装修完的」。 那两道工序是第 09 章的内容。

注意力层:为什么非要有它

书的论证很短但很有力:光靠记忆——在这一行就是「把训练数据里见过的搭配存下来」—— 再加上插值,只能走这么远。 它给的证据不是推理,而是历史——在神经网络出现之前,这就是构建语言模型的主要方法之一, 所以我们知道那条路的天花板在哪27

补充(不在书里,来自通用知识): 书这里指的老办法是「数 N 个词一组的出现频次」。 它的问题在第 01 章讲过的组合爆炸:词一多,能数的组合就少得可怜。

注意力解决的是「代词指谁」这类问题。 书给的例句:

「狗追了松鼠,因为它……」

要预测「它」后面是什么,模型得先知道**「它」指的是狗还是松鼠**28

注意力做的事,就是把上下文里的相关信息,添加进「它」这个词元的表示里29。 书还提醒:模型是根据从训练数据中看到和学到的模式来做这个判断的, 而且前文的其他线索也会参与——比如前面用「她」指代过狗,那就能确定「它」指的是松鼠30

3.6 注意力怎么算:只有两步

这一节是这一章的技术核心,而它比想象中简单——只有两步。

书的原话31:

第一步:相关性评分
给每一个先前的词元打一个分,表示「它对我现在要处理的这个位置有多相关」


第二步:信息整合
按这些分数,把各个位置的信息加权合成一个结果

图说:先打分,再按分数把信息拌在一起。整个注意力机制没有第三步。

三个投影矩阵:查询、键、值

要打分和整合,得先把每个位置那串数变成三份不同用途的版本。 训练过程会产生三张固定的变换表,书叫查询投影矩阵、键投影矩阵、值投影矩阵32

「投影」在这里是数学词,意思就是「用一张固定的数字表格把一串数变换成另一串数」。 不用管它怎么算,只要知道:同一串数经过三张不同的表,得到三份不同用途的版本。

三份的分工,可以用图书馆借书打个比方:

名字相当于用在哪一步
查询你手里那张「我要找什么」的条子第一步
每本书书脊上的标签第一步
书里真正的内容第二步

比方到此为止,后文一律用查询、键、值这三个名字。

两步的具体动作

第一步:当前位置的查询去和所有位置的键相乘,得到一排分数—— 分数表明每个先前词元的相关程度;然后把这排分数按比例摊成占比,使它们之和为 133

「按比例摊成占比」就是: 一堆大小不一的数,按各自的大小等比例缩放, 让它们加起来正好等于 1。这样每个数就能直接当成「它占多大比重」来用。 干这件事最常用的那个具体做法叫 Softmax,记不住这个名字不影响理解。

第二步:每个词元的值,乘上它刚才拿到的分数,然后全部加起来—— 加出来的那一串数,就是这一步注意力的输出34

一句话总结:相关的位置在结果里占的比重大,不相关的占的比重小。

一条走查:处理到「它」这一步,机器手里的数长什么样

书讲这一步时用的例句是「Sarah 喂了猫,因为它……」, 目标是让「它」这个位置的新表示里,融进来自「猫」那个位置的信息35但书只给了起点和目标,没给中间结果。下面把这一步摊开算一遍。

这一段里的分数和占比都是为演示编的,不是真实数值。 只有三个形状数(5、3,072、32,064)是真的——它们来自 3.1 节那台真实模型。

这句话在之前有 5 个词元:Sarah 因为。现在轮到第 6 个位置

第一步,打分。那一份查询,和这 5 个位置的键各乘一次,得到 5 个原始分; 再把这 5 个数按比例摊成占比:

先前位置Sarah因为
原始分1.20.30.12.40.2
摊成占比后0.090.040.030.810.03

下面那一行加起来正好是 1.00,所以每个数可以直接读成「这一份占多大比重」。 拿到 0.81,只有 0.03,相差二十七倍—— 「它指的是猫、不是别的什么」这件事,在机器内部就长这个样子。

第二步,按占比取值。 这 5 个位置各有一份值,每一份都是 3,072 个数长 (3.1 节那台模型的模型维度就是 3,072)。做的事是把它们按上面那一行的占比加起来:

0.09 × `Sarah` 那份值(3,072 个数)
+ 0.04 × `喂` 那份值(3,072 个数)
+ 0.03 × `了` 那份值(3,072 个数)
+ 0.81 × `猫` 那份值(3,072 个数) ← 大头在这儿
+ 0.03 × `因为` 那份值(3,072 个数)
─────────────────────────────────────
= `它` 这一步的新表示,还是 3,072 个数

图说:**加权和不改变长度。** 进去 5 份各 3,072 个数,出来一份 3,072 个数;
而这出来的一份里,八成来自`猫`那一份。

这一份 3,072 个数接着往下走完剩余的处理块,最后交给头部,被摊成 32,064 个可能性 ——词表里每个词元各分到一个数,所以头部吐出来的个数正好等于 3.1 节那张大表的行数 32,064那 0.81 的影响,最终就体现在这 32,064 个数上: 和猫有关的那些词元(饿)分数被抬高,和 Sarah 有关的被压低。

这条走查上,这一章的三个承重零件各占一步: 查询和键决定了那一排原始分(第一步);值和占比决定了新表示里各家占多少(第二步); 而头部把这份新表示翻译回「下一个词元该是什么」(3.1 节那一行)。

为什么要有很多个「头」

一次注意力只能按一种标准去看相关性。所以做法是同时做很多次,每次一套自己的查询/键/值表。 书管每一次并行的注意力叫一个注意力头,理由是: 这提高了模型对输入序列中复杂模式进行建模的能力,这些模式需要同时关注不同的模式36

用大白话说:一个头盯语法——就是「词和词之间怎么搭」;另一个头盯「这个『它』指的是谁」; 再一个头盯「这段话在讲什么主题」。它们各看各的,最后把结果拼在一起。

注意:书没有说「每个头各管一件事」,这是我们为了好懂打的比方。 实际上每个头学到什么,是训练自己跑出来的,并没有人分配。

3.7 这些年的改进:几乎全在优化同一步

这一节回答:那一长串听起来很唬人的名词,到底在干什么。

书给了一句话就把它们全串起来了:

研究社区最关注的领域是注意力层。这是因为注意力计算是过程中计算成本最高的部分。37

补充(不在书里,来自通用知识): 贵在哪儿——要给每一对位置都打一次分。 位置数翻一倍,要打的分就是四倍。所以序列一长,这一步的开销涨得比别的都快。

下面这张表是这一节的全部内容。 读它的正确方式:不是记名字,是看「省的是哪一种资源」——这里的资源只有三样: 算的次数、占的显存、搬数据的时间。

稀疏就是「不是每个都看,只看一部分」,它的反面是「每个都看」。 下面第一张表省的正是这个:本来要和前面所有位置各打一次分,改成只和其中一部分打。

分组查询就是「把那些注意力头分成若干组」,同一组共用一份东西。 分几组是可调的:组越少越省,但共用得越狠,质量掉得也越多。

先看省「算的次数」的那一种:

改进它省什么具体怎么省
稀疏注意力(不是每个位置都看,只看一部分)与滑动窗口注意力计算次数限制模型可以关注的先前词元的范围,不再每个位置都看38

再看省显存的那两种,它们是同一条路上的两步:

改进它省什么具体怎么省
多查询注意力显存和推理开销所有头共享同一份键和值,每个头只保留自己的查询39
分组查询注意力(名字来自它的做法:把那些头分成若干组)同上,但换回一点质量键值不砍到只剩一份,而是分成若干组,每组的头共享一份40

还有两项改进的性质和上面三项不一样:一项省的是搬数据的时间,一项改的是位置信息。 它们各占下面一节。

稀疏注意力那一行有个重要的补充,别漏了: 书说 GPT-3 用了这种机制, 但并非在所有块里都用——因为如果模型只能看到少量先前的词元,生成质量会大幅下降; 所以它交织使用了全注意力和高效注意力的块,一层全看、一层只看附近,交替进行41

这就是这一整节的通用套路:效率和质量是明码标价的,做法是「哪儿便宜省哪儿,不够就掺着来」。

3.8 FlashAttention:唯一一种不拿质量换速度的改进

它值得单独说清楚,因为它的性质和别的不一样:它不改数学结果, 只是重新安排数据在显卡两种内存之间怎么搬42

先讲一个词:带宽就是「单位时间内能搬多少数据」,像水管的粗细。 书说它通过优化在显卡的共享内存和高带宽内存之间加载和移动的值来加速43。 大白话:显卡上有一小块极快的内存和一大块较慢的内存, 老做法在两者之间来回搬数据,搬的时间比算的时间还长;它重排了顺序,少搬几趟。

关键性质:它算出来的结果和原来一模一样,不是近似。

补充(不在书里): 书没有给任何数字。原论文摘要给的是—— BERT-large(序列长 512)端到端(就是「从最开头量到最末尾」,不只看中间某一步)快 15%, GPT-2(序列长 1K)快 3 倍,长序列基准(1K–4K)快 2.4 倍。 标题里那个词是「Exact」,即精确——算出来的结果精度一点都不打折。 来源:《FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness》 (Dao、Fu、Ermon、Rudra、Ré,2022-05-27)https://arxiv.org/abs/2205.14135(查阅于 2026-08-25)

3.9 块内部的三处小改动

书还列了三处不在注意力上、但今天普遍采用的改动44。它们的共同点是都为了训练更稳、更快

归一化:把一层算出来的那些数按比例拉回到一个规整的范围, 免得它们一层层放大或缩小到失控。它不改变信息,只改变尺度。

这个「归一化」和 3.6 节那个「按比例摊成占比」不是同一件事—— 那一个要求结果加起来正好等于 1(所以上面那五个占比才凑成 1.00), 这一个只要求数值别跑飞,不管加起来等于几。两者名字容易混,记住这句区分就够。

激活函数:每层算完之后套的一个小函数,作用是让整个网络不至于退化成一条直线—— 没有它,叠一百层和叠一层没区别。下表最后一行换掉的就是它。

改动老做法新做法
归一化放在哪一步放在注意力和前馈之后放在之前,据报告可以减少所需的训练时间
用哪一种归一化LayerNormRMSNorm,书的评价是「更简单、更高效」
用哪一种激活函数ReLUSwiGLU 这类更新的变体

书还提到了一种叫残差连接的做法45它就是「把这一层的输入,原样加到这一层的输出上」—— 好处是信息和训练信号能直接穿过整摞层,不会被中间某一层吃掉。

这个名字的来历: 有了这条直通的通路之后,这一层实际要学的东西, 就从「输出该是什么」变成了「输出和输入之间的那一点差」——那一点差,就叫残差。

3.10 位置信息:一个由「省算力」逼出来的改动

这一小节值得单独看,因为它是「工程约束反过来改变设计」的一个漂亮例子。

老做法叫绝对位置嵌入:第一个词元标位置 1,第二个标位置 2,依此类推46

问题出在训练时的一个省钱习惯上。 书讲得很清楚47:

训练集里大量文档比上下文短得多
│ 把 4K 的位置全分配给一个十来个词的短句,太浪费

于是「打包」:把好几篇不相干的短文档塞进同一个上下文里,尽量不留空


麻烦来了:第 50 篇文档从位置 50 开始
│ 你告诉模型「你是第 50 个词元」,它会以为前面 49 个是自己的上文

可前面那 49 个属于另一篇完全无关的文档,它本该忽略

图说:「打包」省下了大量算力,却把「绝对位置」这个概念弄脏了。

旋转位置嵌入(常写作 RoPE)就是对这件事的回应。 它的两个特点48:

  1. 它同时编码绝对位置和相对位置,不是只标「你是第几个」;
  2. 它不在流水线开头一次性加进去,而是在注意力那一步、 把查询和键相乘打分之前,才把位置信息混进这两者里49

第二条是关键:位置信息直接参与「打分」,而不是被当成词元本身的一部分。

补充(不在书里): 书只说它「基于在嵌入空间中旋转向量的思想」,没有展开。 原论文摘要的说法是:用一个旋转变换来编码绝对位置,同时在注意力式子里带进显式的相对位置依赖; 它宣称的三个好处是序列长度灵活、相对距离越远相互影响越弱、能给线性注意力也装上相对位置。 来源:《RoFormer: Enhanced Transformer with Rotary Position Embedding》 (Su、Lu、Pan、Murtadha、Wen、Liu,2021-04-20)https://arxiv.org/abs/2104.09864(查阅于 2026-08-25)

4. 作者的判断与证据

这一节把「书里给了证据的」和「书里只是断言的」分开。

说法属于哪一类说明
开缓存比不开快约五倍有实测4.5 秒 vs 21.8 秒,同一台免费显卡、同一段任务、生成 100 个词元19
「注意力计算是过程中计算成本最高的部分」断言,但被整节内容间接佐证书没给分解数据,但它接着列的一整排改进全冲着这一步去,本身就是旁证37
前馈层承载模型的大部分处理能力断言,措辞已经打了折原文用的是「可能承担了模型大部分的记忆和插值工作」,书自己留了余地22
归一化前置能减少训练时间转述,不是自证原文措辞是「据报告」,并给了论文名44
「每个数字单独打分再加权」这套机制为什么有效书没有回答书讲了怎么算,没有讲为什么这样算就能捕捉语言结构——这是全领域都还答不好的问题
稀疏注意力必须和全注意力交替有机制解释 + 实例理由是只看局部会让生成质量大幅下降;实例是 GPT-3 的交替排布41

书在这一章还有一处很坦白的自我限制,出现在讲「流」的时候: 它说每个词元流经自己的计算路径——紧接着加了一句「至少这是一个很好的初步直觉」13

这半句是诚实的:流之间在注意力那一步是有交互的,「各走各的」只是入门时的方便说法。

5. 边界与局限

第一,书完全没有讲注意力为什么有效。 它把「怎么算」讲得很清楚:打分、加权、相加。 但**「为什么这个式子能学出语言结构」一个字没有**——这不是这本书的问题, 这是整个领域都还没有好答案的问题。 读到这里不必怀疑自己没看懂。

第二,那些效率改进书只给了名字和一句话。 FlashAttention 具体重排了什么、 分组查询到底分几组合适、稀疏注意力的窗口该开多大——都要去看论文,书给不了。 它的定位是「让你听到这些名字时知道它们在解决什么问题」,这个定位它完成得很好。

第三,上下文长度这条的数量级已经过时了。 书里的示例设成 2048,讨论 4K 的模型; 今天的模型动辄几十万到上百万。但书里那条推理没有过时:每多一个词元,缓存就多一份、 每一对位置都要打一次分。长上下文没有变便宜,只是变得可用了。

而且「能塞进去」不等于「找得到」。 有一项工作专门测了这件事:

补充(不在书里): 把同一条关键信息放在长输入的不同位置,模型的表现差别很大—— 放在开头或结尾时表现最好,放在中段时明显下降。 来源:《Lost in the Middle: How Language Models Use Long Contexts》 (Liu、Lin、Hewitt、Paranjape、Bevilacqua、Petroni、Liang,2023-07-06) https://arxiv.org/abs/2307.03172(查阅于 2026-08-25)

判断(我们的,不是书里的): 这条实测把第 06 章那套「先查再答」从「省钱手段」 变成了「质量手段」。 就算上下文长得能把整本手册塞进去,把有关的那几段挑出来放在显眼位置,仍然更准。 如果错,会错在: 如果新一代模型在长输入上不再有位置偏好 (这确实是各家在改的方向),那「挑出来」的收益就只剩省钱和省时间了。 判据很好测:把同一条信息分别放在开头、中间、结尾,各问十遍,看答对率差多少。

第四,书没有讲「一次一个词元」这件事本身的代价。 它讲了慢,但没有讲这条结构带来的能力边界—— 比如为什么它不擅长需要通盘规划的任务。这一点第 05 章会从另一个角度碰到: 让它把中间步骤写出来,等于用「多写几个字」换「多想几步」。

6. 可带走的

  1. 生成 = 反复跑同一条流水线:切字 → 一百多块处理 → 头部出可能性清单 → 挑一个 → 接到文末 → 从头再来;
  2. 循环在网络外面,网络本身一次只往前走一趟,从不回头;
  3. 三段构造:词元化器 + 一摞处理块 + 一个头部;换个头部就换个工种,第 08、09 章各用到一次;
  4. 别把三个数字弄混:词表大小(认识多少种词元)、模型维度(每个位置用多长的一串数)、层数;
  5. 它不总挑最高分——每次都挑最高叫贪婪解码,输出稳定但平淡;按分数抽签才有变化;
  6. 上下文长度限制的是「同时能有多少条流」,而你的提问和它的回答共用这份额度;
  7. 写长文必然慢,唯一解法是把算过的中间结果存下来;书的实测是 4.5 秒 vs 21.8 秒;
  8. 长上下文的真实代价在缓存——每多写一个字,都要背着前面所有字的缓存;
  9. 处理块里只有两样东西:注意力层(回头看前文、拿相关信息)+ 前馈层(存知识、做推断);
  10. 注意力只有两步:给每个先前位置打分 → 按分数把它们的信息加权合起来。没有第三步。 「Sarah 喂了猫,因为它」走到时,占 0.81、占 0.03,新表示里八成来自;
  11. 加权合并不改变长度:进去 5 份各 3,072 个数,出来仍是一份 3,072 个数;
  12. 查询、键、值是同一串数的三个不同版本,分别用于「我要找什么」「你是什么」「我拿走什么」;
  13. 多个头 = 同时按多种标准看相关性,但每个头学到什么是训练跑出来的,没人分配;
  14. 近几年的改进几乎全在省注意力的开销——省计算次数、省显存、省搬运时间,各省各的;
  15. 效率和质量明码标价:只看局部会掉质量,所以实际做法是全看和局部看交替;
  16. 位置信息今天在注意力打分那一步才混进去,这个改动是被「把多篇短文档打包进同一个上下文」逼出来的;
  17. 「能塞进去」不等于「找得到」:同一条信息放在长输入中段,模型明显更容易漏掉。

7. 原文地图

主题原书章原文位置
一次只生成一个词元、前向传播深入大语言模型内部text/05-fm.txt:57(搜「前向传播」)
外层循环在网络之外深入大语言模型内部text/05-fm.txt:65(搜「在一个循环中运行它」)
自回归模型的定义深入大语言模型内部text/05-fm.txt:67(搜「自回归模型」)
三段构造深入大语言模型内部text/05-fm.txt:91(搜「这堆Transformer块之后是语言模型头部」) · text/05-fm.txt:93(搜「词元化器、一堆Transformer块和一个语言模型头部」)
头部可以换、另外两种头部深入大语言模型内部text/05-fm.txt:103(搜「序列分类头部和词元分类头部」)
打印出来的真实结构深入大语言模型内部text/05-fm.txt:162(搜「32,064个词元」) · text/05-fm.txt:164(搜「32个Phi3DecoderLayer」) · text/05-fm.txt:166(搜「多层感知机」)
模型维度深入大语言模型内部text/05-fm.txt:220(搜「模型维度」)
解码策略、加入随机性、40%深入大语言模型内部text/05-fm.txt:172(搜「解码策略」) · text/05-fm.txt:174(搜「加入一些随机性」) · text/05-fm.txt:176(搜「40%」)
贪婪解码与温度为零深入大语言模型内部text/05-fm.txt:180(搜「贪婪解码」)
法国的首都是 → 巴黎深入大语言模型内部text/05-fm.txt:208(搜「巴黎」)
并行、流、上下文长度深入大语言模型内部text/05-fm.txt:212(搜「更适合并行计算」) · text/05-fm.txt:216(搜「上下文长度」)
只用最后一条流、但前面的中间结果是必需的深入大语言模型内部text/05-fm.txt:222(搜「只有最后一个流的输出结果」) · text/05-fm.txt:224(搜「较早的输出」)
位置嵌入(第一次出现)深入大语言模型内部text/05-fm.txt:218(搜「位置嵌入」)
键值缓存与实测数字深入大语言模型内部text/05-fm.txt:243(搜「键值(KV)缓存」) · text/05-fm.txt:273(搜「4.5秒」) · text/05-fm.txt:288(搜「21.8秒」)
为什么要流式输出深入大语言模型内部text/05-fm.txt:288(搜「流式输出词元」)
块数:六个 vs 超过一百个深入大语言模型内部text/05-fm.txt:292(搜「超过100个」)
块里的两个组件深入大语言模型内部text/05-fm.txt:298(搜「整合相关信息」) · text/05-fm.txt:300(搜「承载了模型的大部分处理能力」)
前馈层存知识、插值与泛化深入大语言模型内部text/05-fm.txt:306(搜「The Shawshank」) · text/05-fm.txt:312(搜「模型能够使用相同的机制在数据点和更复杂的模式之间进行插值」)
商用模型已经不是「毛坯」了深入大语言模型内部text/05-fm.txt:319(搜「指令调优」)
为什么需要注意力、老办法的天花板深入大语言模型内部text/05-fm.txt:323(搜「N-gram语言模型」) · text/05-fm.txt:327(搜「狗追了松鼠」) · text/05-fm.txt:331(搜「注意力将上下文中的信息添加到」)
注意力的两步深入大语言模型内部text/05-fm.txt:343(搜「两个主要步骤」) · text/05-fm.txt:345(搜「相关程度」)
查询、键、值三个投影矩阵深入大语言模型内部text/05-fm.txt:369(搜「查询投影矩阵」) · text/05-fm.txt:367(搜「Sarah fed the cat because it」)
相关性评分与信息整合的具体动作深入大语言模型内部text/05-fm.txt:389(搜「Softmax操作归一化」) · text/05-fm.txt:395(搜「值向量乘以该词元的分数」)
注意力头深入大语言模型内部text/05-fm.txt:353(搜「注意力头」)
注意力是最贵的一步深入大语言模型内部text/05-fm.txt:405(搜「计算成本最高的部分」)
稀疏与滑动窗口注意力、GPT-3 交替使用深入大语言模型内部text/05-fm.txt:409(搜「诸如稀疏注意力」) · text/05-fm.txt:413(搜「交织使用了全注意力」)
多查询与分组查询注意力深入大语言模型内部text/05-fm.txt:427(搜「对Transformer的一个更近期的高效注意力改进是分组查询注意力」) · text/05-fm.txt:437(搜「所有头共享键和值矩阵」) · text/05-fm.txt:443(搜「过于苛刻」)
FlashAttention 与两种显存深入大语言模型内部text/05-fm.txt:449(搜「共享内存(SRAM)」)
残差连接与层归一化深入大语言模型内部text/05-fm.txt:453(搜「残差连接和层归一化」)
预归一化、RMSNorm、SwiGLU深入大语言模型内部text/05-fm.txt:459(搜「归一化发生在注意力和前馈层之前」)
绝对位置嵌入与打包问题深入大语言模型内部text/05-fm.txt:467(搜「绝对位置嵌入」) · text/05-fm.txt:469(搜「文档被打包到训练批次」) · text/05-fm.txt:475(搜「文档50从位置50开始」)
旋转位置嵌入及其位置深入大语言模型内部text/05-fm.txt:465(搜「旋转位置嵌入(或」) · text/05-fm.txt:481(搜「混入查询和键矩阵」)
Transformer 走出语言的其他领域深入大语言模型内部text/05-fm.txt:487(搜「A Survey of Transformers」)
BERT 的 B 代表双向深入大语言模型内部text/05-fm.txt:423(搜「B代表双向」)

Footnotes

  1. 出处:「深入大语言模型内部」第 57 段(text/05-fm.txt:57,搜「前向传播」)。原文对前向传播的解释是:「这是机器学习术语,指输入进入神经网络,流经计算图中所需的计算,最终在另一端产生输出」。 2

  2. 出处:「深入大语言模型内部」第 67 段(text/05-fm.txt:67,搜「自回归模型」)。书还指出这个词通常用来把生成模型和 BERT 这类表示模型区分开——后者不是自回归的。

  3. 出处:「深入大语言模型内部」第 65 段(text/05-fm.txt:65,搜「在一个循环中运行它」)。

  4. 出处:「深入大语言模型内部」第 91 段(text/05-fm.txt:91,搜「这堆Transformer块之后是语言模型头部」)与第 93 段(text/05-fm.txt:93,搜「词元化器、一堆Transformer块和一个语言模型头部」)。

  5. 出处:「深入大语言模型内部」第 103 段(text/05-fm.txt:103,搜「序列分类头部和词元分类头部」)。原文:语言模型头部「本身也是一个简单的神经网络层」,是「可以附加到Transformer块堆上以构建不同类型系统的多种可能『头部』之一」。

  6. 出处:「深入大语言模型内部」第 162 段(text/05-fm.txt:162,搜「32,064个词元」)与第 164 段(text/05-fm.txt:164,搜「32个Phi3DecoderLayer」)。这些数字来自书里直接打印模型变量的输出,不是书的转述。

  7. 出处:「深入大语言模型内部」第 166 段(text/05-fm.txt:166,搜「多层感知机」)。

  8. 出处:「深入大语言模型内部」第 220 段(text/05-fm.txt:220,搜「模型维度」)。原文:每条处理流「接收一个向量作为输入,并产生一个相同大小的最终结果向量(通常称为模型维度)」。

  9. 出处:「深入大语言模型内部」第 172 段(text/05-fm.txt:172,搜「解码策略」)。

  10. 出处:「深入大语言模型内部」第 174 段(text/05-fm.txt:174,搜「加入一些随机性」)与第 176 段(text/05-fm.txt:176,搜「40%」)。原文的正式说法是「按照概率分数从概率分布中进行采样」。 2

  11. 出处:「深入大语言模型内部」第 180 段(text/05-fm.txt:180,搜「贪婪解码」)。书把温度的完整讲解留给了原书第 6 章,即我们的第 05 章。

  12. 出处:「深入大语言模型内部」第 208 段(text/05-fm.txt:208,搜「巴黎」)。书的做法是把输入送进模型、取头部输出里分数最高的那个编号,再解码回文字。

  13. 出处:「深入大语言模型内部」第 212 段(text/05-fm.txt:212,搜「更适合并行计算」)。原文在说「每个输入词元都流经自己的计算路径」之后,紧跟了一句「至少这是一个很好的初步直觉」——因为在注意力那一步,各条流之间是有交互的。 2 3

  14. 出处:「深入大语言模型内部」第 216 段(text/05-fm.txt:216,搜「上下文长度」)。

  15. 出处:「深入大语言模型内部」第 224 段(text/05-fm.txt:224,搜「较早的输出」)与第 222 段(text/05-fm.txt:222,搜「只有最后一个流的输出结果」)。

  16. 出处:「深入大语言模型内部」第 218 段(text/05-fm.txt:218,搜「位置嵌入」)。

  17. 出处:「深入大语言模型内部」第 243 段(text/05-fm.txt:243,搜「键值(KV)缓存」)。原文:如果让模型能缓存先前计算的结果(特别是注意力机制中的某些特定向量),就「不再需要重复前面的流的计算了」。

  18. 出处:「深入大语言模型内部」第 247 段(text/05-fm.txt:247,搜「一遍又一遍地重复相同的计算」)。

  19. 出处:「深入大语言模型内部」第 273 段(text/05-fm.txt:273,搜「4.5秒」)与第 288 段(text/05-fm.txt:288,搜「21.8秒」)。测试条件:一台配 T4 显卡的免费 Colab 实例,同一段提示,生成 100 个词元。 2

  20. 出处:「深入大语言模型内部」第 288 段(text/05-fm.txt:288,搜「流式输出词元」)。

  21. 出处:「深入大语言模型内部」第 296 段(text/05-fm.txt:296,搜「两个连续的组件」)与第 298 段(text/05-fm.txt:298,搜「整合相关信息」)。 2

  22. 出处:「深入大语言模型内部」第 300 段(text/05-fm.txt:300,搜「承载了模型的大部分处理能力」)。注意书在图注里用的措辞更保守:前馈组件「可能承担了模型大部分的记忆和插值工作」(第 310 段,text/05-fm.txt:310,搜「可能承担了模型大部分的记忆」)。 2

  23. 出处:「深入大语言模型内部」第 292 段(text/05-fm.txt:292,搜「超过100个」)。 2

  24. 出处:「深入大语言模型内部」第 306 段(text/05-fm.txt:306,搜「The Shawshank」)与第 308 段(text/05-fm.txt:308,搜「学习并存储了」)。 2

  25. 出处:「深入大语言模型内部」第 312 段(text/05-fm.txt:312,搜「模型能够使用相同的机制在数据点和更复杂的模式之间进行插值」)。原文:模型「不仅仅是一个大型数据库」,记忆「只是令人印象深刻的文本生成配方中的一个成分」。

  26. 出处:「深入大语言模型内部」第 316 段(text/05-fm.txt:316,搜「并非严格意义上的」)与第 319 段(text/05-fm.txt:319,搜「指令调优」)。这段在原书里是一条「注」。

  27. 出处:「深入大语言模型内部」第 323 段(text/05-fm.txt:323,搜「N-gram语言模型」)。书指的参考是 Jurafsky 与 Martin 的《语音与语言处理》第 3 章。

  28. 出处:「深入大语言模型内部」第 327 段(text/05-fm.txt:327,搜「狗追了松鼠」)。

  29. 出处:「深入大语言模型内部」第 331 段(text/05-fm.txt:331,搜「注意力将上下文中的信息添加到」)。

  30. 出处:「深入大语言模型内部」第 335 段(text/05-fm.txt:335,搜「指代狗」)。

  31. 出处:「深入大语言模型内部」第 343 段(text/05-fm.txt:343,搜「两个主要步骤」)与第 345 段(text/05-fm.txt:345,搜「相关程度」)。

  32. 出处:「深入大语言模型内部」第 369 段(text/05-fm.txt:369,搜「查询投影矩阵」)。原文:训练过程产生三个投影矩阵,注意力先把输入与它们相乘,得到查询、键、值三个矩阵。

  33. 出处:「深入大语言模型内部」第 389 段(text/05-fm.txt:389,搜「Softmax操作归一化」)。

  34. 出处:「深入大语言模型内部」第 395 段(text/05-fm.txt:395,搜「值向量乘以该词元的分数」)。

  35. 出处:「深入大语言模型内部」第 367 段(text/05-fm.txt:367,搜「Sarah fed the cat because it」)。

  36. 出处:「深入大语言模型内部」第 353 段(text/05-fm.txt:353,搜「注意力头」)。「一个头盯语法、一个头盯指代」是我们为了好懂打的比方,书没有这么说。

  37. 出处:「深入大语言模型内部」第 405 段(text/05-fm.txt:405,搜「计算成本最高的部分」)。 2

  38. 出处:「深入大语言模型内部」第 409 段(text/05-fm.txt:409,搜「诸如稀疏注意力」)。书点名的两篇工作是《Generating long sequences with sparse transformers》和《Longformer: The long-document transformer》。

  39. 出处:「深入大语言模型内部」第 437 段(text/05-fm.txt:437,搜「所有头共享键和值矩阵」)。

  40. 出处:「深入大语言模型内部」第 427 段(text/05-fm.txt:427,搜「对Transformer的一个更近期的高效注意力改进是分组查询注意力」)与第 443 段(text/05-fm.txt:443,搜「过于苛刻」)。书说 Llama 2 和 3 用的就是分组查询注意力;而这些方法的目的是「通过减小相关矩阵的规模来提升更大模型的推理可扩展性」。

  41. 出处:「深入大语言模型内部」第 413 段(text/05-fm.txt:413,搜「交织使用了全注意力」)。原文的理由:「如果模型只能看到少量先前的词元,生成质量会大幅下降」。同一段还指出解码器块只能关注先前的词元,而 BERT 可以关注两侧——「因此BERT中的B代表双向」(第 423 段,text/05-fm.txt:423,搜「B代表双向」)。 2

  42. 出处:「深入大语言模型内部」第 449 段(text/05-fm.txt:449,搜「共享内存(SRAM)」)。

  43. 出处:「深入大语言模型内部」第 449 段(text/05-fm.txt:449,搜「共享内存(SRAM)」)。书只给了论文名,没有给任何加速数字。

  44. 出处:「深入大语言模型内部」第 459 段(text/05-fm.txt:459,搜「归一化发生在注意力和前馈层之前」)。三处改动的出处论文书都给了名字:《On layer normalization in the Transformer architecture》《Root mean square layer normalization》《GLU Variants Improve Transformer》。 2

  45. 出处:「深入大语言模型内部」第 453 段(text/05-fm.txt:453,搜「残差连接和层归一化」)。「残差连接就是把输入原样加到输出上」是我们补的解释,书只提了名字。

  46. 出处:「深入大语言模型内部」第 467 段(text/05-fm.txt:467,搜「绝对位置嵌入」)。书区分了两种老做法:静态的(用几何函数生成位置向量)和学习式的(模型训练时自己给位置赋值)。

  47. 出处:「深入大语言模型内部」第 469 段(text/05-fm.txt:469,搜「文档被打包到训练批次」)与第 475 段(text/05-fm.txt:475,搜「文档50从位置50开始」)。

  48. 出处:「深入大语言模型内部」第 465 段(text/05-fm.txt:465,搜「旋转位置嵌入(或」)与第 477 段(text/05-fm.txt:477,搜「捕获绝对和相对词元位置信息」)。

  49. 出处:「深入大语言模型内部」第 481 段(text/05-fm.txt:481,搜「混入查询和键矩阵」)。