How LLMs Actually Work(翻译)
本文基于 0xkato 的英文文章 "How LLMs Actually Work" 改写整理,用于中文读者学习参考。
原文:https://www.0xkato.xyz/how-llms-actually-work
标签:Machine Learning · Transformers · LLM · Neural Networks · AI
写在前面
现代主流大语言模型(LLM)几乎都是把同一种结构——Transformer block——一层一层堆起来的。所以只要把一个 Transformer 内部的几个零件理解透,就能看懂绝大部分主流 LLM 的论文和 model card。
模型与模型之间的区别,主要在于三件事:
- 用什么数据训练;
- 模型规模与超参(层数、宽度、注意力头数等);
- 后训练阶段做了什么(SFT、RLHF、DPO……)。
下面按 9 个主题,把 LLM 的"内部机器"从输入到输出走一遍。
1. Tokenization:把文字变成整数
模型并不直接读文字,它读的是整数 ID。把字符串切成整数序列的过程叫 tokenization(分词 / 切分 token)。
- Token ID:词表里每一项对应一个整数。模型只认这个整数,不认字符本身。
- Vocabulary(词表):分词器固定的"小积木清单"。现代 LLM 词表通常在几万到几十万条之间。
token 通常不是完整单词,而是子词(subword)片段:
tokenization可能会切成["token", "ization"]running可能会切成["run", "ning"]
为什么用子词?
- 整词词表太大、对新词不泛化;
- 字符级太小,模型连最简单的拼写规律都得从头学;
- 子词介于两者之间:常见片段直接是一个 token,罕见词由小片段拼出来。
经典坑:问 LLM "strawberry 里有几个 r",早期模型常答错。不是模型不会数数,而是它根本看不到字母——它看到的是几个 token ID,"strawberry" 也许只是 1~2 个 token,里面的字母被打包了。
不同家族用不同分词器:GPT 系列用 BPE(Byte Pair Encoding) 的变体,LLaMA 系常用 SentencePiece。算力消耗、多语种覆盖会受影响,但本质都是:文字进,整数出。
2. Embeddings:让整数有了"意思"
1024 这样一个 token ID 本身没有任何含义,它只是一个行号。让它有意义的,是一张巨大的查找表——embedding matrix(词嵌入矩阵)。
- 每一行对应词表里的一个 token;
- 每一行是一个长向量,长度等于模型的隐藏维度(hidden size)。
- 7B 量级的模型常见隐藏维度是 4096,更大的模型用更宽的向量。
Vector(向量):就是一串数字。Transformer 内部一切运算都是向量与矩阵的乘法。
模型拿到 token ID 后,会去 embedding 表里查出对应的那一行,后面的所有计算都用这个向量进行。
有趣的性质:训练完成后,语义相似的 token 在向量空间里也接近。
- "king" 和 "queen" 的向量距离很近;
- "Paris" 和 "France" 的向量距离也很近;
- 著名的近似等式:
king − man + woman ≈ queen。
这种几何结构没人手写过,是模型为了把"下一个 token 预测"做好,自己把它学出来的。
⚠️ 注意:到这一步,每个 token 已经变成向量了,但向量本身不带"位置信息"。"dog"无论排在第 1 个还是第 5 个,查表得到的是同一个向量。这就需要下一步的位置编码来补上。
3. Positional Encoding:告诉模型"谁先谁后"
纯粹的 self-attention 天生不区分顺序,需要额外注入"位置信号",否则模型分不清是 "dog bites man" 还是 "man bites dog"。
3.1 经典做法:正弦余弦位置编码(Vaswani et al., 2017)
给每个位置一组固定的数字(不同频率的 sin/cos 组合),直接加到 token 的 embedding 上。位置 1 加一个模式,位置 5 加另一个模式。
优点:可以外推到训练时没见过的更长序列。
但随着模型变大,加性方案有两个问题:
- embedding 既要装"语义"又要装"位置",容量受限;
- 学习版的绝对位置编码在长上下文上外推不稳——训练只见过 2048 长度,到 5000 时就不行了。
3.2 现代主流:RoPE(Rotary Position Embeddings)
Su et al. 2021 提出,目前 LLaMA、Mistral、Gemma、Qwen 等几乎所有开源模型都用它。
直觉:不再"加"位置向量,而是按位置旋转 Query 和 Key 向量。
- 位置 1 的 token 转一个小角度;
- 位置 100 的 token 转一个大角度;
- 注意力打分时,真正起作用的是两个 token 旋转角度的差——也就是它们的相对距离。
优点:
- 天然编码相对位置(更贴合 attention 的需求);
- 长上下文外推更平滑;
- 不引入新参数。
3.3 "Lost in the Middle" 现象
Liu et al. 2023 报告:即使位置编码足够好,LLM 在长 prompt 中对开头和结尾的信息利用得更好,对中间的反而会"忘"。这也是"重要内容放开头/结尾"这条 prompt 工程经验的科学依据——模型并不是均匀使用你的 prompt 的每一部分。
4. Attention:token 之间互相"看"
Attention(注意力) 是 Transformer 名字的来源,也是它最核心的机制。每一层 Transformer 里,attention 做一件事:让每个 token 看一眼它被允许看到的其他 token,决定哪些对自己接下来的表示更重要。
为此,每个 token 同时扮演三个角色,被映射成三个新向量:
| 角色 | 含义 |
|---|---|
| Query (Q) | 我想从别的 token 那里找什么? |
| Key (K) | 我能给别的 token 提供什么"标签"用来匹配? |
| Value (V) | 一旦匹配上,我会传出去什么内容? |
Q、K、V 是同一个 token 经过三个不同的、可学习的线性变换得到的。
4.1 匹配过程
- 每个 token 的 Query 与所有可见 token 的 Key 做点积(dot product),点积越大表示越"对得上"。
- 点积分数除以 √d_k 做缩放(防止 softmax 饱和)。
- 用 softmax 把分数变成"和为 1 的权重"。
- 用这些权重对 Value 做加权求和——就是这个 token 在本层的新表示。
Dot product:衡量两个向量"对齐程度"的最简单方式。
Softmax:把任意一组分数变成像概率一样、加起来等于 1 的权重。
4.2 一个例子
句子:"The cat that I saw yesterday was sleeping."
模型处理 was 时,要搞清楚"是谁在 sleeping"。was 的 Query 与所有可见 token 的 Key 做点积:
- 与
cat的点积很高(动词需要主语,cat这种主语恰好提供匹配的 Key); - 与
yesterday的点积很低; - softmax 后,
cat拿到大权重,yesterday拿到小权重; was的新向量主要由cat的 Value 决定——这就是"指代"在数学上是怎么实现的。
4.3 Causal Masking(因果遮罩)
GPT 类模型是从左到右生成的,所以位置 5 的 token 只能看到位置 1~5,不能看到 6、7、8(那些还没生成出来)。
做法很简单:把"未来"位置的匹配分数设成极小的负数,softmax 之后权重几乎为 0。
4.4 Induction Heads(归纳头)
Anthropic 在 2022 年发现的一类专门化注意力头:会识别 prompt 里 A B … A 这种模式,遇到第二个 A 时,回头看上一次 A 后面跟的是 B,于是预测下一个就是 B。
这是目前已知最清晰的 in-context learning(上下文学习) 机制之一——为什么你在 prompt 里给几个示例,模型就能照着续写。
4.5 Attention 的代价
完整 attention 中,每个 token 要和所有可见 token 比较,因此 prompt 长度翻倍,计算量大约翻 4 倍(O(n²))。这就是长 prompt 又慢又贵的根因,也是 FlashAttention、稀疏注意力、线性注意力等优化方向的存在意义。
5. Multi-Head Attention:多视角看同一段话
一次 attention 只能给模型一种"谁该关注谁"的判断。但语言里同时存在很多种关系:主谓一致、代词指代、跨句长程引用、局部短语……一种视角不够。
多头注意力:并行跑多次 attention,每次走自己一小块空间,叫一个 head(头)。
一个常见误解
很多教程说"把 token 向量切成几段,每段一个头"——错的。
正确做法是:每个头有自己一套可学习的投影矩阵,把完整的 token 向量投影到自己的小空间里得到 Q、K、V。
例如:模型 hidden=4096,32 个头,每个头的工作空间是 128 维。但这 128 维是从完整 4096 维投影出来的,不是固定的某 128 个分量。所以每个头是同一个 token 的"不同视角",不是它的"碎片"。
流程
- 每个头独立做自己的 attention;
- 所有头的输出拼接起来;
- 经过一个最终的线性层(
W_o)混合回完整的 hidden 维度。
混合矩阵也是学出来的——模型自己决定该怎么把多个视角融合起来。
6. Feed-Forward Network(FFN):模型存"知识"的主要地方
Transformer block 里除了 attention,还有一个逐 token 的前馈网络(FFN,也叫 MLP)。流程通常是:
hidden → 线性升维(4×)→ 非线性激活(GELU / SwiGLU)→ 线性降维 → hidden
要点:
- 逐 token 独立处理:FFN 不混合不同位置(混合是 attention 干的),它只是对每个 token 的表示做一次"深度加工"。
- 参数量大头:在大多数 LLM 中,FFN 占总参数的 60–70%,是模型存储事实、风格、模式等"知识"的主要载体。可解释性研究中常发现某些 FFN 神经元会专门激活于特定主题、词族或概念。
- 门控变体:现代模型(LLaMA、Qwen 等)大量使用 SwiGLU——比传统 ReLU/GELU FFN 在同等算力下表现更好。
直觉:Attention 负责"调动信息",FFN 负责"加工信息"。
7. Residual Stream + Layer Norm:为什么深层堆叠能训得动
Transformer block 不是"输入 → attention → FFN → 输出"这种串行的线,而是:
x ──► LayerNorm ──► Attention ──► + ──► LayerNorm ──► FFN ──► + ──► 下一层
└───────────────────────────────────┘ └─────────────────────────┘
residual(残差) residual
7.1 残差连接(Residual Connection)
每个子模块输出加回它的输入。好处:
- 梯度高速公路:梯度可以沿残差直通底层,避免深层堆叠时的梯度消失;
- 信息保持:可解释性社区把这条主干叫做 residual stream(残差流)——所有层都在往这条流上"读 / 写"信息,每一层都是对它的微调。
7.2 Layer Normalization(层归一化)
把每个 token 向量按通道归一化(减均值除标准差,再用一对可学习参数缩放/偏移),让数值分布稳定。
现代模型几乎都用 Pre-LN(先归一化再进子模块)而非原始 Transformer 的 Post-LN——更利于深层稳定训练。
许多新模型(LLaMA 系)用 RMSNorm——比 LayerNorm 更省、效果相当。
记住一个口诀:残差让深度可训,归一化让数值不爆。
8. Predicting the Next Token:模型实际输出什么
走完所有 Transformer 层之后,每个位置都会得到一个最终的 hidden 向量。模型最关心的是最后一个位置的那个向量——它要根据这个向量预测"下一个 token 是什么"。
8.1 LM Head(语言模型头)
最后一层 hidden 向量乘上一个矩阵(通常和输入 embedding 矩阵共享权重,叫 weight tying),得到一个长度等于词表大小的向量,叫 logits。
- logits 里的每个数值,对应词表中一个 token 的"原始打分";
- 经过 softmax 后变成"下一个 token 的概率分布"。
8.2 采样策略(Sampling)
拿到概率分布后怎么选?常见几种:
| 策略 | 含义 |
|---|---|
| Greedy(贪心) | 永远选概率最高的那个。确定性高,但容易死板、循环。 |
| Temperature | 把 logits 除以 T 再 softmax。T<1 更"自信",T>1 更"发散"。 |
| Top-k | 只在概率最高的 k 个 token 里抽样。 |
| Top-p / Nucleus | 选累计概率 ≥ p 的最小集合后抽样,更自适应。 |
| Min-p | 按相对最大概率的比例阈值过滤,抗噪声。 |
8.3 自回归生成循环
1. 把 prompt token 序列喂进模型
2. 取最后一个位置的 logits → softmax → 采样得到下一个 token
3. 把这个 token 拼到序列尾部
4. 回到第 1 步,直到遇到 EOS 或达到最大长度
这就是"AI 在打字"背后的全部秘密——它一次只生成一个 token,每个 token 都是基于前面所有 token 重新算一遍。
KV Cache:实际部署时,前面 token 的 Key/Value 会缓存下来,每生成新 token 只算它自己那一份注意力,避免 O(n²) 重复计算。这是推理加速的关键技巧。
9. 架构 vs 训练权重:什么是共享的,什么是不同的
读完前 8 节,你会发现 GPT、Claude、Gemini、LLaMA、Mistral、Qwen、DeepSeek……架构上几乎完全是同一套东西:
- 一样的 token → embedding → 多层 Transformer → LM head 流程;
- 都是 decoder-only、因果遮罩;
- 注意力大多是多头(或 GQA / MQA 变体);
- 大多用 RoPE、RMSNorm、SwiGLU、Pre-LN 残差结构。
那为什么模型之间能力差这么多?区别主要在四件事上:
9.1 训练数据
什么语种、什么领域、什么质量、是否经过严格清洗、是否包含代码、是否包含数学推理过程——这是"模型最终知道什么、擅长什么"的根源。
9.2 规模与配置
层数、隐藏维度、头数、上下文窗口长度、词表大小、稀疏 vs 稠密(MoE)……这些都是"骨架的尺寸"。
9.3 后训练(Post-Training)
- SFT(监督微调):用人工写的高质量回答教模型学会"该怎么答";
- RLHF / DPO / GRPO:用人类偏好或奖励模型,把它从"会说"调成"愿意答得让人满意";
- 工具使用、Agent 能力训练:教模型调用 API、写代码、规划多步任务;
- 安全对齐:拒答有害请求、避免越狱。
9.4 工程细节
量化(FP16 / BF16 / FP8 / INT4)、KV cache、推理引擎(vLLM、TensorRT-LLM、SGLang)、并行策略(TP / PP / EP)等——这些不改架构,但决定速度和成本。
一句话总结:架构决定能力上限的形状,数据和后训练决定模型实际把这块上限"刻"成什么样子。
一张速记图
文字 prompt
│
▼ Tokenizer
整数 ID 序列
│
▼ Embedding 矩阵(查表)
每个 token 一个向量
│
▼ + Positional Encoding(RoPE 是旋转 Q/K)
带位置的向量
│
▼ ┌─ LayerNorm ─► Multi-Head Attention ─► + (残差)
│ │ │
│ └─ LayerNorm ─► Feed-Forward (FFN) ──────► +
│ │
│ ←─── 同样结构再堆 N 层(Llama-7B = 32 层) ──┘
│
▼ 最后一层 LayerNorm + LM Head
词表上的 logits
│
▼ softmax + 采样(greedy / top-p / temperature)
下一个 token
│
└──► 拼回序列,循环生成
浙公网安备 33010602011771号