Transformer 全面拆解:从「预测下一个词」到改变世界的技术

Posted on Apr 20, 2026 · 9 min read

写给那些不满足于"注意力机制很强大"这种解释的人。

本文目标:把一个"黑箱",拆成你能 mentally simulate 的系统。

引言:一个看似简单的问题

你有没有想过——ChatGPT 为什么"看起来懂你"?

当你问它"帮我写一封拒绝朋友请求的礼貌邮件",它不仅能写出来,还能感知到"拒绝"和"礼貌"之间的张力,给出一封语气恰当、逻辑自洽的信。这不是简单的模板填充,也不是数据库检索。

它真的理解语言吗?

答案比你想象的既更简单,也更深刻。

Transformer 本质上只做一件事:预测下一个 Token

没有世界模型,没有常识库,没有专门的语法规则。就是反复地、大规模地、在海量文本上——预测下一个词。

然而就是这个看似朴素的任务,催生出了 GPT-4、Claude、Gemini 这些震动世界的系统。

为什么?

因为"完美预测下一个词",在信息论上,等价于"完全理解语言背后的结构"。而 Transformer 架构,提供了一种前所未有的、可大规模扩展的方式去逼近这个目标。

这篇文章,我们就来把这个"黑箱",一层一层地拆开。

读完之后,你不一定能写出 GPT-4,但你应该能:

  • 在脑子里模拟 token 从输入到输出的完整流程
  • 理解为什么 Transformer 打败了 RNN
  • 知道为什么模型越大越聪明,以及这条路的边界在哪里
  • 对"模型到底理解了什么"有自己批判性的判断

不需要记公式,但需要你认真思考。

让我们开始。

第一部分:核心直觉

1. Transformer 的本质:信息如何在序列中流动?

在理解任何细节之前,先建立最重要的直觉:

Transformer 是一台"信息路由机器"。

给定一个 token 序列(比如一句话),Transformer 的核心任务是:让每个 token 能够"看到"并"收集"序列中其他位置的相关信息,然后据此更新自己的表示。

这句话有三个关键词:

  1. 看到:不是所有 token 都同等重要,需要有选择地关注
  2. 收集:把分散在序列中的信息汇聚到当前位置
  3. 更新:根据收集到的信息,修正自己对这个 token 的"理解"

这个过程,在每一层 Transformer Block 里都会发生一次。一个现代 LLM 有 96 层甚至更多,也就是说,每个 token 会经历 96 次这样的"信息更新"。

最终输出的向量,已经不再是孤立的词义,而是包含了整个上下文信息的、高度压缩的语义表示

2. 从 RNN 到 Attention:为什么旧时代崩塌?

要真正理解 Attention 的价值,必须先理解它在解决什么问题。

RNN/LSTM 的三个致命问题

问题一:长距离依赖(Long-range dependency)

RNN 的工作方式像一个人在读书,每次只看一个词,然后把"记忆"(hidden state)传递给下一个时刻。问题是:当序列很长时,早期的信息经过多次传递,会越来越稀薄。

想象一个极端例子:

“我在法国长大,父母都是巴黎人,从小说法语,后来去了美国读书,毕业后在硅谷工作,现在已经十年没回去了,但我的母语仍然是______”

答案是"法语",但关键信息在序列最开始。RNN 在处理到最后时,早已"忘记"了开头的上下文。LSTM 通过门控机制有所改善,但本质问题没有解决——信息必须通过一个窄瓶颈(hidden state)逐步传递

问题二:串行计算(Sequential computation)

RNN 必须按顺序处理每个 token:先处理第 1 个,再处理第 2 个,依此类推。这意味着:

  • 无法并行化
  • 在现代 GPU(擅长大规模并行计算)上效率极低
  • 训练长序列时,时间成本线性增长

这是工程上的死穴。

问题三:表达瓶颈(Representation bottleneck)

整个序列的语义,必须被压缩进一个固定大小的 hidden state 向量里。无论句子多长、多复杂,这个向量的维度是固定的。

这就像要你用一张明信片,描述清楚一本书的全部内容。

Attention 如何一刀解决

Self-Attention 的核心思想极其优雅:

与其让信息"穿越时间隧道"逐步传递,不如让每个 token 直接"看到"序列中所有其他 token,按需提取信息。

  • 长距离依赖?不存在。任意两个 token 之间的距离,都是 1(直接注意力连接)。
  • 串行计算?不存在。所有 token 的 Attention 计算可以完全并行。
  • 表达瓶颈?不存在。每个 token 都可以直接访问完整的序列信息,不需要挤过一个固定大小的瓶颈。

三个问题,一个机制,全部解决。

这就是为什么 2017 年那篇论文用了一个如此自信的标题——Attention Is All You Need

第二部分:一张图看懂 Transformer

3. 一张总图:LLM 在干什么?

让我们把整个流程用最直观的方式串联起来:

输入文本: "The cat sat on the"
        ↓
[Tokenization]
"The" / "cat" / "sat" / "on" / "the"
        ↓
[Embedding]
每个 token → 一个高维向量(比如 4096 维)
        ↓
[Position Encoding]
向量 + 位置信息(第1个词、第2个词...)
        ↓
[Transformer Block × N]
  ┌──────────────────────┐
  │  Multi-Head Attention │  ← 信息路由(token 之间通信)
  │  Add & LayerNorm      │
  │  Feed-Forward Network │  ← 信息处理(每个 token 独立计算)
  │  Add & LayerNorm      │
  └──────────────────────┘
        ↓ (重复 N 次,N 可能是 96 或更多)
[最后一层的输出向量]
        ↓
[Language Model Head]
输出向量 → 词汇表上的概率分布(50000 个词,每个有一个概率)
        ↓
[采样]
选取下一个 token(比如 "mat")

这就是完整的数据流。没有魔法,只是矩阵乘法和非线性激活函数,在极大规模下重复执行。

4. 为什么现代 LLM 都是 Decoder-only?

原始的 Transformer(2017 年)有两部分:EncoderDecoder,专门用于机器翻译——Encoder 读源语言,Decoder 写目标语言。

但现代 LLM——GPT、Claude、LLaMA——都是 Decoder-only 架构。Encoder 去哪儿了?

Encoder 被"淘汰"的本质原因:

Encoder 的设计是双向的,也就是说,处理某个 token 时,它可以同时看到左边和右边的上下文。这对理解任务(分类、抽取)很好,但对生成任务来说,这在逻辑上是矛盾的——你在生成第 k 个词时,右边的词根本还不存在。

Decoder-only 架构使用因果掩码(Causal Mask),强制每个 token 只能看到自己左边(之前)的内容。这使得它天然适合自回归生成:一个 token 一个 token 地往右生成。

更深层的原因是:研究者发现,Decoder-only 架构 + 足够大的规模 + 足够多的数据,在几乎所有任务上都能超越 Encoder-Decoder 架构。包括原本被认为需要 Encoder 的理解类任务。

这是 Scaling Law 带来的实证结论,不是先验设计。

第三部分:语言是怎么变成向量的?

5. Tokenization:语言不是词,而是压缩编码

大多数人以为 LLM 以"词"为单位处理文本。这是错的。

LLM 处理的是 Token——一种介于字符和单词之间的语言单位。

"unbelievable" 可能被切分成 ["un", "believ", "able"] 三个 token。 "ChatGPT" 可能是一个 token,也可能是两个,取决于训练数据。 中文 "我爱北京天安门" 在某些 tokenizer 里每个字是一个 token。

为什么要这样做?

核心原因是信息压缩

BPE(Byte Pair Encoding)和 SentencePiece 的算法逻辑是:找出训练语料中出现频率最高的字符组合,将其合并成一个新的 token。反复执行这个过程,直到词表达到目标大小(通常 32000 到 200000 个 token)。

效果是:常见词(“the”、“is”)被编码为单个 token,罕见词被拆成子词片段,极罕见字符退化为字节。

关键认知点:Token ≠ 单词。

这个认知会帮你理解很多 LLM 的"奇怪行为":

  • 为什么 LLM 数数字母有时会数错?(因为"strawberry"可能是一个 token,模型没有"看到"里面有几个 r)
  • 为什么某些拼写任务对 LLM 困难?(操作 token 不等于操作字符)
  • 为什么 token 长度影响模型成本?(按 token 计费,不按词计费)

6. Embedding:语义空间是如何形成的?

Token 经过 Tokenization 变成一个整数 ID(比如 4821)。但整数 ID 本身没有任何语义结构——4821 和 4822 不应该比 4821 和 9000 更"相似"。

Embedding 把这个 ID 映射成一个高维向量(比如 4096 维的浮点数向量)。

这个映射本身就是一个巨大的查找表,在训练过程中不断被优化。

“词向量"其实是统计结构

Embedding 的神奇之处在于:训练结束后,语义相似的词,在向量空间中的距离也相近。

这不是人为设计的,而是从大量文本的共现统计中涌现出来的。

直觉解释:如果"猫"和"狗"总是出现在相似的上下文里(“我养了一只___"、“宠物___"),那么预测任务会倾向于给它们分配相似的向量——因为相似的向量在后续计算中会产生相似的行为,这对减小预测误差是有利的。

为什么相似词会靠近?

因为 Transformer 的目标是最小化预测下一个 token 的误差。在这个目标下,语义相近的词可以"共享"更多的表示结构,这比让它们的向量天各一方更高效。优化压力,自然地将语言的语义结构编码进了几何空间。

7. 位置编码:Transformer 如何"记住顺序”?

这是一个非常微妙但重要的问题。

为什么 Attention 本身是"无序的”?

Self-Attention 机制在计算时,把所有 token 的向量打包成一个矩阵,然后做矩阵运算。在这个过程中,每个 token 对其他 token 的关注度,取决于它们向量之间的相似性——但完全不考虑它们的位置顺序。

换句话说,如果你把输入 token 的顺序打乱,Self-Attention 的计算结果(排列意义上)是一样的。

这意味着:如果不额外加入位置信息,"猫 吃 鱼""鱼 吃 猫" 对模型来说完全一样——这显然是灾难性的。

解决方案:位置编码(Positional Encoding)

最简单的方式:给每个位置生成一个固定的向量,加到 Embedding 上。模型就能感知到"这是序列里第 3 个 token”。

原始 Transformer 用的是正弦/余弦函数生成的固定位置向量。这有效,但有局限:预设了最大序列长度,外推能力差。

RoPE 为什么成为主流?

RoPE(Rotary Position Embedding,旋转位置编码)是现代 LLM 的主流选择(LLaMA、Mistral、GPT-NeoX 等都用它)。

直觉解释:与其把位置信息"加"到向量上,RoPE 将其旋转向量。具体来说,两个 token 在 Attention 计算中的相对关系,通过它们向量之间的旋转角度来体现。

这样设计的好处:

  1. 相对位置自然涌现:模型关注的是两个 token 的相对距离,而非绝对位置
  2. 外推能力强:通过频率调整(YaRN、LongRoPE 等方法),可以将上下文窗口从 4K 扩展到 128K 甚至更长
  3. 与 Attention 计算无缝结合:不需要修改架构,只需在 Q、K 向量上施加旋转变换

你不需要理解旋转的具体数学,只需记住直觉:RoPE 让模型在做 Attention 时,天然感知到两个 token 之间的相对距离

第四部分:注意力机制

8. Self-Attention 的直觉版本(不写公式)

先忘掉公式,从最直观的角度理解。

Query / Key / Value = 信息检索系统

想象你在用搜索引擎:

  • 你输入一个搜索词(Query)
  • 数据库里每条记录有一个索引标签(Key)
  • 实际内容是Value

搜索的过程:将你的 Query 和每条记录的 Key 做匹配,相关的 Key 得到高分,最终返回高分记录的 Value 的加权组合。

Self-Attention 做的就是这件事,只不过:

  • Query、Key、Value 都来自同一个序列
  • 每个 token 同时扮演三个角色:它既是查询者(Query),也是被查询者(Key + Value)
  • 每个 token 通过自己的 Query,在序列中查找和自己相关的信息(Key 高度匹配的位置),然后提取那些位置的 Value

类比

想象一个圆桌会议,每个人都在讲话。你(当前 token)需要决定:此刻,谁说的话和我最相关?你用自己的"问题"(Query)去匹配每个人的"话题标签"(Key),然后重点听那些标签最符合你问题的人说的话(Value)。

这就是 Self-Attention。

9. 从直觉到数学:Attention 到底在算什么?

Attention(Q, K, V) = softmax(QK^T / √dk) · V

看起来复杂,但拆开来非常清晰:

第一步:QK^T(计算相关性分数)

Q 是一个矩阵(每行是一个 token 的 Query 向量),K 也是一个矩阵(每行是一个 token 的 Key 向量)。

QK^T 做的是:让序列中每对 token 之间计算一个点积分数,得到一个 n×n 的分数矩阵。

点积越大 → 两个向量越相似 → 这两个 token 越"相关"。

第二步:除以 √dk(缩放)

为什么要除以一个数?

因为 dk(Key 向量的维度)通常很高(比如 128)。在高维空间里,点积的数值会随维度增大而增大,导致 softmax 的输入数值过大,使得 softmax 的输出变得极度尖锐(几乎把所有注意力集中在一个 token 上)。

除以 √dk 是一个归一化操作,把数值压回一个合理的范围,确保梯度不会消失,训练稳定进行。这是一个非常实用的工程技巧,背后的直觉就是:高维点积需要缩放才能被 softmax 正常处理

第三步:softmax(转换为概率)

Softmax 把分数矩阵的每一行转换成概率分布——所有值加起来等于 1,且都是正数。

这就是"注意力权重":token i 对 token j 的注意力权重,表示 i 在更新自己时应该从 j 那里提取多少信息。

第四步:乘以 V(加权聚合)

用注意力权重对 Value 矩阵做加权求和,得到每个 token 的新表示。

整个过程:用相似度加权,从序列中提取最相关的信息,融合成当前 token 的新表示

10. Multi-Head Attention:为什么要"多视角看世界"?

单头 Attention 有一个问题:在同一次计算中,每个 token 只能用一种"注意力模式"去看其他 token。但语言的关系是多维的——语法关系、语义关系、指代关系、位置关系……

Multi-Head Attention 的解法极其优雅:

把 Q、K、V 各自线性投影到 h 个更小的子空间(每个子空间叫一个 “head”),在每个子空间里独立做 Attention,然后把所有 head 的输出拼接起来,再投影回原始维度。

每个 head 学不同模式

大量的可视化研究表明,不同 head 确实专注于不同的语言关系:

  • 某些 head 关注直接相邻的 token(局部语法结构)
  • 某些 head 追踪代词和其指代对象之间的关系(“他”→“约翰”)
  • 某些 head 捕捉动词和其宾语之间的依存关系
  • 某些 head 关注对称性结构(括号匹配、列举关系)

没有人告诉模型要学这些,这些模式是在预测任务的压力下自动涌现的。

这是 Transformer 最令人惊叹的特性之一:它在没有语言学监督的情况下,自己发现了语言学规律

11. Causal Mask:LLM 为什么不会"偷看答案"?

回到前面提到的一个关键问题:LLM 是自回归的——它在生成第 k 个 token 时,右边的 token 还不存在。

但 Transformer 的 Attention 机制在没有限制的情况下,会让每个 token 都能看到序列中所有其他 token(包括右边的)。

这在生成时是不允许的:如果训练时 token k 能看到 token k+1、k+2,它就"作弊"了,学到的不是真正的语言分布,而是一种取巧的映射。

Causal Mask 的解法

在 Attention 分数矩阵上,把所有"向右看"的位置(上三角)设为负无穷(-∞)。经过 softmax 后,-∞ 变成 0——也就是说,这些位置的注意力权重为 0,完全被忽略。

这样,每个 token 只能从左边(之前生成的内容)获取信息,自然地实现了时间因果性

训练时和推理时,这个 mask 的逻辑完全一致:模型从来不"看未来"。

第五部分:Transformer Block 内部到底发生了什么?

12. 一个 Block 的完整拆解(最重要小节)

这是理解 Transformer 最关键的部分。让我们把一个 Block 拆成四个步骤:

输入 x
  ↓
[Multi-Head Self-Attention(x)]
  ↓ 得到注意力输出 attn
[x + attn]  ← 残差连接(Residual Connection)
  ↓ 得到 x₁
[LayerNorm(x₁)]
  ↓ 得到 x₂
[Feed-Forward Network(x₂)]
  ↓ 得到 ffn
[x₂ + ffn]  ← 残差连接
  ↓ 得到 x₃
[LayerNorm(x₃)]
  ↓
输出(作为下一个 Block 的输入)

每个 Block 的作用:让 token 的表示向量,经过一次"信息交换 + 信息处理"后,变得更丰富、更具上下文感知能力。

96 个 Block 串联下来,最后一层的向量,已经包含了整个上下文的高度抽象语义信息。

13. FFN:为什么 Attention 之后还要 MLP?

这是一个好问题:既然 Attention 已经做了信息聚合,为什么还需要一个 Feed-Forward Network(本质是两层 MLP)?

Attention 是"通信",FFN 是"计算"。

  • Attention 的作用:让 token 之间相互交换信息(跨位置的信息路由)
  • FFN 的作用:对每个 token 独立地进行非线性变换(位置无关的特征提取)

一个流行的解释来自 Geva 等人 2021 年的研究:FFN 层像是一个巨大的键值存储(key-value memory store)

直觉上,Attention 层负责确定"现在这个位置应该关注什么类型的信息",而 FFN 层负责"根据这个类型,检索和处理相应的知识"。

这就是为什么 FFN 的中间维度通常是 Attention 维度的 4 倍甚至更多——它需要足够的容量来存储和处理大量的"隐性知识"。

有研究表明,一些具体的事实性知识(比如"巴黎是法国的首都")可以被定位到特定 FFN 层的特定神经元。这说明 FFN 不只是在做特征变换,还在进行某种形式的"知识存储"。

14. LayerNorm + Residual:训练为什么不会崩?

残差连接(Residual Connection)

公式:output = x + f(x)

为什么这个简单的加法操作,对深度网络至关重要?

想象没有残差连接的 96 层网络。梯度在反向传播时,每经过一层都要乘以一个雅可比矩阵。96 次相乘之后,梯度要么变成 0(梯度消失),要么爆炸到无穷大——两种情况都会让训练完全失败。

残差连接提供了一条"梯度高速公路":梯度可以直接通过跳跃连接流回早期层,不需要经过 f(x) 的变换。这让梯度传播变得稳定,使得 100+ 层的网络可以被正常训练。

另一个直觉:残差连接让网络学的是"残差"(需要修正的部分),而不是"全量变换"。如果某一层的变换不必要,它可以学成恒等映射(f(x) → 0),让输入直接通过。这降低了每层的学习难度。

Layer Normalization

LayerNorm 对每个 token 的向量做归一化(减去均值,除以标准差),使得数值保持在合理范围内。

为什么需要归一化?因为随着网络加深,向量的数值分布会漂移,导致后续层的 Attention 计算出现数值不稳定问题。LayerNorm 把分布"拉回"标准化状态,让每一层都能在稳定的数值范围内工作。

现代 LLM(如 LLaMA)通常把 LayerNorm 放在 Attention 和 FFN 之前(Pre-Norm),而不是之后(Post-Norm),这进一步改善了训练稳定性。

第六部分:模型是怎么学会语言的?

15. 训练目标:为什么"预测下一个词"就够了?

这是整个 LLM 领域最深刻的洞见之一,值得认真思考。

信息论视角:

Shannon 的信息论告诉我们,一个信息源的"熵",等于从该信息源完美预测下一个符号所需要的平均比特数。

换句话说:如果你能完美预测下一个词,你就完全理解了这个信息源的统计结构

语言是一个极其复杂的信息源。为了完美预测下一个词,模型需要学会:

  • 语法规则(“is” 后面跟动名词还是原型?)
  • 语义关系(“法国的首都"后面最可能是"巴黎”)
  • 世界知识(“爱因斯坦发现了___“后面是相对论相关的内容)
  • 文体风格(学术论文的下一句和微博的下一句截然不同)
  • 逻辑推理(前提铺垫之后,合理的结论是什么?)

所有这些,都被这一个训练目标隐含地涵盖了。这就是为什么"预测下一个词"看似简单,实则是一个极其通用和强大的学习信号。

16. Loss 函数到底在优化什么?

LLM 的训练损失是交叉熵(Cross-Entropy)

不用记公式,理解直觉:

模型对每个位置输出一个概率分布,表示"下一个 token 是词表中每个词的概率”。如果词表有 50000 个词,那就是一个 50000 维的概率向量。

训练的目标是:让真实 token(ground truth)对应的概率尽可能高,其他 token 的概率尽可能低。

交叉熵的计算方式是:取真实 token 的概率的负对数。如果真实 token 概率很高(接近 1),负对数接近 0(损失小);如果概率很低(接近 0),负对数趋向无穷(损失大)。

整个训练就是在最小化这个损失:让模型在每个位置,对真实下一个 token 的预测概率越来越高

17. Scaling Law:为什么模型越大越聪明?

2020 年,OpenAI 发表了一篇奠基性论文,揭示了 LLM 的幂律缩放定律(Scaling Laws)

模型在语言建模任务上的性能(用 loss 衡量),与参数量 N训练数据量 D计算量 C 之间,存在可预测的幂律关系。

直觉解释:

  • 更多参数:更大的 FFN 层 = 更多可以存储的"隐性知识”;更宽的 Attention = 更高维的语义表示空间
  • 更多数据:更丰富的语言分布统计 = 更少的泛化误差
  • 更多计算:参数被更充分地更新,收敛到更好的局部最优

关键洞见:这三者之间存在最优比例关系(Chinchilla 定律)。粗略来说:参数量翻倍时,训练数据量也应该翻倍,才能充分利用模型容量。早期很多模型(包括 GPT-3)其实是"过大欠训"(参数很多但训练数据相对不足)的。

为什么这改变了世界?

因为这意味着模型性能是可预测的可工程化的。你不需要靠运气找到好的架构——只要把三个旋钮(参数、数据、计算)按比例调大,性能就会按规律提升。这让 LLM 的研发从"科学探索"变成了"工程建设"。

第七部分:推理时到底发生了什么?

18. 生成过程逐步拆解(token by token)

假设输入是:"The capital of France is"

模型怎么生成下一个词?

步骤一:前向传播

把输入 tokenize,每个 token 经过 Embedding + Position Encoding,然后通过所有 Transformer Block,最终得到最后一个 token("is")的输出向量。

步骤二:Language Model Head

把输出向量乘以词表矩阵(本质上是 Embedding 层的转置),得到词表上的 logits 向量(50000 维)。

步骤三:Softmax

把 logits 转换为概率分布。

步骤四:采样

从概率分布中选取下一个 token(比如 "Paris")。

步骤五:自回归循环

"Paris" 拼接到输入序列末尾,重复上述过程,生成下一个 token(比如 ".")。

如此往复,直到生成终止 token 或达到最大长度。

关键点:每次生成一个 token,都需要对整个(到目前为止的)序列做一次完整的前向传播。如果没有优化,这会极其低效。

19. KV Cache:为什么推理可以加速 10x?

上面提到,每次生成新 token 时,需要对整个序列做前向传播。但仔细想想——对于已经处理过的 token,它们的 Key 和 Value 向量(Attention 计算中用到的)其实不会改变!

(因为 Causal Mask,每个已有 token 的 Attention 结果不依赖于未来的 token)

KV Cache 的原理:把每一层 Attention 计算出的 Key 和 Value 向量缓存起来,下次生成新 token 时,只需要:

  1. 对新 token 计算它的 Q、K、V
  2. 用新 token 的 Q,与缓存的所有历史 K 做 Attention
  3. 用 Attention 权重,对缓存的所有历史 V 做加权求和

不需要重新计算历史 token 的 K、V——这些计算量占了推理的绝大部分。

效果:理论上,推理速度从 O(n²) 降到了近似 O(n)(n 是序列长度),实际加速可以达到 10x 甚至更多。

代价:KV Cache 需要存储每一层、每个 token 的 K 和 V 向量,内存占用巨大。对于长上下文(128K token)的大模型,KV Cache 本身就可能占用数十 GB 内存。这是现代 LLM 推理的主要内存瓶颈。

20. 采样策略:模型"性格"的来源

为什么同一个模型,有时输出非常保守,有时输出天马行空?

这取决于采样策略

Greedy Decoding(贪心解码)

每次选择概率最高的 token。

特点:确定性、可复现,但输出往往单调、重复。极端情况下会陷入循环(“这是最好的,这是最好的,这是最好的……")。

Temperature(温度采样)

在 softmax 之前,把 logits 除以一个"温度"参数 T。

  • T < 1(低温):概率分布变得更尖锐,高概率 token 更容易被选中,输出更保守、更可预测
  • T > 1(高温):概率分布变得更平坦,低概率 token 也有机会被选中,输出更随机、更多样
  • T = 1:原始分布

Temperature 就是调节"模型性格"的旋钮:低温=谨慎,高温=放飞自我。

Top-p(Nucleus Sampling)

不是限制候选数量,而是限制候选集合的累积概率。

具体做法:按概率从高到低排列所有 token,找到使累积概率刚好超过 p(比如 0.9)的最小 token 集合,从这个集合中采样。

好处:动态调整候选集大小——当模型非常确定时(一个词的概率就超过了 0.9),只在极小的候选集里选;当模型不确定时(需要很多词才能累积到 0.9),有更广泛的选择空间。

为什么模型有时"发散”?

高 Temperature + 不当的 top-p 设置 + 某些触发条件,可能让模型进入"高熵状态"——每步都选择了低概率 token,导致上下文越来越偏离,模型试图"圆场"时反而越跑越远。这就是"模型发散"的本质。

第八部分:现代 LLM 的进化方向

21. 长上下文问题:为什么 Transformer 很贵?

Self-Attention 的计算量和内存占用,与序列长度 n 的平方成正比——这就是著名的 O(n²) 问题。

为什么是 n²?

因为 Attention 要计算序列中每对 token 之间的相关性。n 个 token 就有 n² 个 token 对,每个 token 对需要一次向量点积计算。

对于短序列(n=2048),这还可以接受。但当 n=128000(现代长上下文模型的目标),计算量是 (128000)² = 1.6 × 10¹⁰,内存占用是 128K × 128K 的矩阵——在所有层上叠加,这是无法承受的数字。

这就是 Transformer 扩展到超长上下文的核心瓶颈。

22. 各种优化思路

Sparse Attention(Longformer / BigBird)

核心思想:不是让每个 token 关注所有其他 token,而是只关注:局部窗口内的 token + 少量全局 token + 随机采样的 token。

效果:把计算复杂度从 O(n²) 降到 O(n),代价是可能错过一些远距离的重要联系。

Linear Attention

将 Attention 的计算重新组合,利用矩阵运算的结合律,把 O(n²) 降到 O(n)。

技术上是可行的,但现实中 Linear Attention 的表现通常不如标准 Attention——它牺牲了一定的表达能力来换取效率。这是一个活跃的研究领域。

MoE(Mixture of Experts,混合专家)

这是一个完全不同的思路:与其增大模型密度(让所有参数对每个 token 都激活),不如增大模型总参数量,但每次只激活其中一小部分(“专家”)。

比如 GPT-4 据传是 MoE 架构,总参数量约 1.8T,但每次推理只激活约 220B 的参数。

好处:用更少的计算量(FLOPs),获得更大模型的性能。

代价:路由机制复杂,训练稳定性要求高,推理时仍需加载全部参数(内存需求未减少)。

23. 新架构挑战者

Mamba(SSM 系列)

Mamba 基于状态空间模型(State Space Model),用一个"选择性"的循环机制代替 Self-Attention。

核心优势:推理时的计算复杂度是 O(n)(每步只需更新固定大小的状态),内存占用不随序列增长而增长。

对于超长上下文(百万 token 级别),Mamba 理论上有显著优势。

现实挑战:目前,Mamba 在大规模训练和性能上仍落后于同量级的 Transformer,尚未证明能够完全替代。

RWKV

RWKV 尝试结合 RNN 的推理效率和 Transformer 的训练并行性——训练时像 Transformer(并行),推理时像 RNN(线性)。

挑战与 Mamba 类似:在最强性能上,目前仍难以匹敌顶级 Transformer。

结论:Transformer 的地位目前非常稳固。挑战者存在,但尚未证明自己。这个领域在快速演化,未来三年可能会有实质性的架构变化。

第九部分:模型内部到底学了什么?

24. Attention 可视化:模型在"看哪里"?

通过提取各层 Attention 权重矩阵并可视化,研究者发现了很多有趣的模式:

  • 浅层:倾向于关注相邻 token(局部依存关系,类似 n-gram)
  • 中间层:出现语法相关的模式(动词关注其论元,代词关注指代对象)
  • 深层:更抽象的语义关系,以及与任务目标相关的模式

一个经典发现:在处理"The animal didn’t cross the street because it was too tired"时,对应"it"的 Attention head 会将注意力集中在"animal"上,正确解析了指代关系。这种模式在不同模型、不同规模中都能重现,说明 Transformer 确实学到了一些语言学意义上的结构。

但要小心过度解读:Attention 权重并不直接等于"重要性",它是信息路由的权重,不是因果解释。

25. Logit Lens:中间层在说什么?

Logit Lens 是一个优雅的分析工具。

想法:把每一个中间层的输出向量,直接通过 Language Model Head 解码成 token 概率分布,看看"模型在这一层的输出,如果直接生成,会说什么"。

发现:

  • 最初几层:输出的 token 几乎是随机的,没有语义
  • 中间层:开始出现合理但粗糙的预测(比如正确的语义类别)
  • 后面几层:预测越来越精准,最终与实际输出一致

这说明模型是在逐层精化语义表示——不是在某一层突然"顿悟",而是每一层都在对表示做一次小幅度的修正和改进。

这个视角支持了一个重要观点:Transformer 的深度不只是为了"更多计算",而是为了"多轮语义精化"

26. 模型有没有"理解"?(批判性讨论)

这是 AI 领域最有争议的问题之一。让我们用更精确的方式来讨论它。

支持"有理解"的证据:

  • 模型能够完成需要多步推理的任务(数学证明、代码调试)
  • 在没有见过的领域组合上能够泛化(将医学知识和法律知识结合应用)
  • 语义表示空间具有组合性(King - Man + Woman ≈ Queen 之类的关系)
  • Attention 模式对应语言学结构(见上)

反对"真正理解"的证据:

  • 分布外脆弱性:对输入做微小的语义无关修改(改变词序、添加无关短语),模型有时会给出完全不同的答案
  • 逻辑一致性问题:模型对同一问题的不同表述,有时给出矛盾的答案
  • 无世界模型:模型没有独立于语言的感知和行动能力,所有"理解"都是语言内的统计关系
  • 幻觉问题:模型会自信地生成错误信息,这表明它并不"知道自己知道什么"

更准确的描述:

LLM 学到的是一种语言的统计结构映射——它能非常精准地模拟"一个博学之人在各种情境下会说什么"。这种模拟在很多情况下和"理解"在功能上等价,但在机制上有本质不同。

用 John Searle 的"中文房间"思想实验来说:LLM 可能是一个极其精密的"中文房间"。但这个"房间"足够大时,“理解"和"完美的功能性模拟"之间的实践区别,也许比我们以为的要小。

这个问题没有确定答案,而且可能无法用现有的哲学框架解答。

第十部分:实践

27. 用 PyTorch 写一个最小 Transformer

只保留核心逻辑,去掉所有工程噪音:

import torch
import torch.nn as nn
import torch.nn.functional as F
import math

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_heads):
        super().__init__()
        assert d_model % n_heads == 0
        self.d_k = d_model // n_heads
        self.n_heads = n_heads
        
        # Q, K, V 投影矩阵
        self.W_q = nn.Linear(d_model, d_model, bias=False)
        self.W_k = nn.Linear(d_model, d_model, bias=False)
        self.W_v = nn.Linear(d_model, d_model, bias=False)
        self.W_o = nn.Linear(d_model, d_model, bias=False)
    
    def forward(self, x, mask=None):
        B, T, C = x.shape  # batch, seq_len, d_model
        
        # 投影并分成多个头
        Q = self.W_q(x).view(B, T, self.n_heads, self.d_k).transpose(1, 2)
        K = self.W_k(x).view(B, T, self.n_heads, self.d_k).transpose(1, 2)
        V = self.W_v(x).view(B, T, self.n_heads, self.d_k).transpose(1, 2)
        # 现在 Q/K/V 的形状: (B, n_heads, T, d_k)
        
        # Attention 计算
        scores = Q @ K.transpose(-2, -1) / math.sqrt(self.d_k)  # (B, n_heads, T, T)
        
        # Causal mask(只看左边)
        if mask is not None:
            scores = scores.masked_fill(mask == 0, float('-inf'))
        
        attn = F.softmax(scores, dim=-1)
        
        # 加权聚合
        out = attn @ V  # (B, n_heads, T, d_k)
        out = out.transpose(1, 2).contiguous().view(B, T, C)  # 拼接所有头
        
        return self.W_o(out)


class TransformerBlock(nn.Module):
    def __init__(self, d_model, n_heads, d_ff):
        super().__init__()
        self.attn = MultiHeadAttention(d_model, n_heads)
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.GELU(),
            nn.Linear(d_ff, d_model)
        )
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
    
    def forward(self, x, mask=None):
        # Pre-Norm + 残差连接
        x = x + self.attn(self.norm1(x), mask)
        x = x + self.ffn(self.norm2(x))
        return x


class MiniGPT(nn.Module):
    def __init__(self, vocab_size, d_model=256, n_heads=8, n_layers=6, 
                 d_ff=1024, max_seq_len=512):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, d_model)
        self.pos_embedding = nn.Embedding(max_seq_len, d_model)
        self.blocks = nn.ModuleList([
            TransformerBlock(d_model, n_heads, d_ff) for _ in range(n_layers)
        ])
        self.norm = nn.LayerNorm(d_model)
        self.lm_head = nn.Linear(d_model, vocab_size, bias=False)
        
        # 构建 Causal Mask
        self.register_buffer(
            'mask', 
            torch.tril(torch.ones(max_seq_len, max_seq_len)).view(1, 1, max_seq_len, max_seq_len)
        )
    
    def forward(self, idx):
        B, T = idx.shape
        
        # Token + 位置 Embedding
        x = self.embedding(idx) + self.pos_embedding(torch.arange(T, device=idx.device))
        
        # 通过所有 Transformer Block
        mask = self.mask[:, :, :T, :T]
        for block in self.blocks:
            x = block(x, mask)
        
        x = self.norm(x)
        
        # 输出 logits
        return self.lm_head(x)  # (B, T, vocab_size)


# 使用示例
model = MiniGPT(vocab_size=50257)  # GPT-2 词表大小
x = torch.randint(0, 50257, (2, 128))  # batch=2, seq_len=128
logits = model(x)
print(f"输出形状: {logits.shape}")  # torch.Size([2, 128, 50257])

# 计算语言建模 Loss
targets = torch.randint(0, 50257, (2, 128))
loss = F.cross_entropy(logits.view(-1, 50257), targets.view(-1))
print(f"Loss: {loss.item():.4f}")

代码量不到 80 行,但包含了 Transformer 的所有核心组件:Multi-Head Attention、Causal Mask、Feed-Forward、LayerNorm、Residual Connection、自回归语言模型头。

28. 用 Hugging Face 拆一个 GPT

from transformers import GPT2Model, GPT2Tokenizer
import torch

# 加载模型和 tokenizer
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
model = GPT2Model.from_pretrained('gpt2', output_attentions=True, output_hidden_states=True)
model.eval()

# 准备输入
text = "The capital of France is"
inputs = tokenizer(text, return_tensors='pt')

# 前向传播
with torch.no_grad():
    outputs = model(**inputs)

# 分析输出结构
hidden_states = outputs.hidden_states  # 所有层的隐状态
attentions = outputs.attentions        # 所有层的注意力权重

print(f"层数: {len(hidden_states)}")           # 13 (embedding + 12 layers)
print(f"每层形状: {hidden_states[0].shape}")   # (1, seq_len, 768)
print(f"Attention 头数: {attentions[0].shape[1]}")  # 12

# 可视化最后一层的 Attention
import matplotlib.pyplot as plt
import seaborn as sns

tokens = tokenizer.convert_ids_to_tokens(inputs['input_ids'][0])
last_layer_attn = attentions[-1][0].mean(dim=0)  # 对所有 head 取平均

plt.figure(figsize=(8, 6))
sns.heatmap(
    last_layer_attn.numpy(),
    xticklabels=tokens,
    yticklabels=tokens,
    cmap='Blues'
)
plt.title("Last Layer Attention (Head Average)")
plt.tight_layout()
plt.show()

# 用 Logit Lens 分析中间层
from transformers import GPT2LMHeadModel

lm_model = GPT2LMHeadModel.from_pretrained('gpt2', output_hidden_states=True)
lm_model.eval()

with torch.no_grad():
    lm_outputs = lm_model(**inputs)

# 把每个中间层的 hidden state 解码成 token
print("\n=== Logit Lens 分析 ===")
for layer_idx, hidden in enumerate(lm_outputs.hidden_states):
    # 直接用 LM head 解码中间层
    logits = lm_model.lm_head(lm_model.transformer.ln_f(hidden))
    pred_token = tokenizer.decode(logits[0, -1].argmax())
    print(f"Layer {layer_idx:2d}: 预测下一词 = '{pred_token}'")

运行这段代码,你会看到:

  • Layer 0-2:预测几乎是随机的(比如 “Paris” 可能是 “London” 或 " a”)
  • Layer 5-8:开始出现语义上合理的预测(地名、国家相关词)
  • Layer 10-12:越来越接近正确答案 “Paris”

这就是"逐层语义精化"的直观证明。

结语:Transformer 为什么改变世界?

29. 它真正的突破是什么?

Transformer 的诞生,表面上是一个更好的 NLP 模型。但它真正的突破,在于两件事:

第一:可扩展性(Scalability)

Transformer 的设计,在工程上极其适合扩展:

  • Self-Attention 和 FFN 可以完全并行化,完美匹配 GPU 的计算范式
  • 层数、宽度、头数可以独立增加,性能预测性好(Scaling Law)
  • 训练目标(预测下一个 token)可以从互联网上无限获取训练数据

这意味着:只要投入更多的计算资源,性能就会可预测地提升。这是一种"工业化"的 AI 研发范式,让大公司的工程能力成为了核心竞争力。

第二:通用表示学习(Universal Representation Learning)

Transformer 在预测下一个 token 的过程中,被迫学习了语言中几乎所有的结构——语法、语义、逻辑、世界知识、推理模式……

这些表示不是针对某个特定任务设计的,而是通用的。同一个预训练模型,通过 fine-tuning(甚至仅仅通过 prompting),可以应用到翻译、摘要、代码生成、数学推理、对话、创作……

这就是"基础模型"(Foundation Model)概念的核心:一个大规模预训练模型,是整个 AI 应用栈的基础设施,而不是一个特定任务的解决方案。

30. 它的局限 & 下一步

诚实地说,Transformer 并非没有根本性的局限。

长上下文的代价

O(n²) 的 Attention 计算,使得超长上下文(百万 token 级别)在计算上极其昂贵。目前的各种优化(Sparse Attention、FlashAttention、RoPE 外推等)只是在工程上缓解,没有从根本上解决问题。当上下文窗口扩展到极限时,模型的"注意力稀释"问题(无法同时关注超长上下文中所有重要信息)也是一个悬而未决的挑战。

推理成本

运行一个顶级 LLM 的边际成本,远超一次搜索引擎查询。当 AI 被嵌入到日常工作流的每一个环节,推理成本将成为一个严肃的经济和能源问题。

真正的推理能力

目前的 LLM 在复杂多步推理上仍不稳定。CoT(Chain of Thought)提示大幅改善了表现,但这更像是让模型"工作步骤可见从而减少错误",而非真正实现了可靠的符号推理。o1/o3 等模型的出现(通过在推理时花更多计算)是一个有趣的方向,但可能也只是更复杂的 token 预测,而非架构上的突破。

是否会被替代?

Mamba、RWKV、线性 Attention 等架构在特定场景下展示了潜力,但到目前为止,没有一个能在相同规模下全面超越 Transformer。更可能的未来是混合架构——将 Transformer 的表达能力与循环模型的推理效率结合起来。

最后的思考

我们开头问了一个问题:ChatGPT 真的理解语言吗?

读到这里,你应该有了自己的判断。

它学到的不是人类意义上的"理解",而是一种深度的统计结构映射——从人类书写的海量文本中,提炼出语言的形式规律、知识模式和推理框架,压缩进数百亿个参数里。

这种压缩,在功能上极其强大,以至于在很多任务上可以模拟出"理解"的效果。但这与人类的认知有根本的不同——人类的理解是具身的、因果的、有意图的;LLM 的理解是统计的、关联的、无意图的。

Transformer 改变世界,不是因为它创造了人工智能,而是因为它创造了一种前所未有的、能够大规模编码和利用人类知识的技术路径。

这条路通向哪里,仍然是一个开放的问题。

但理解这条路是如何走出来的,是我们每个技术人的必修课。