LLM 基础:跟着一个 token 走完全程
这份文档不按字母排序,按数据流排序。 每个名词只出现在它真正被用到的位置上——如果一个概念放不进下面这条链,它就不属于本文。
每个条目固定三行:是什么 / 一个可验证的数字 / 没有它会坏在哪。第三行才是理解的地方,前两行只是记忆。
🔗 相关:ReAct 论文笔记
0. 骨架
先看全貌。下面 12 行是一个现代 decoder-only LLM 的全部推理逻辑,剩下的都是细节:
# 输入:"猫吃鱼"
ids = tokenizer.encode(text) # → [1234, 5678, 91011] §1
x = embedding[ids] # → [T, d_model] 残差流起点 §1 §2
for layer in range(L): # L 个结构完全相同的 block §2
x = x + attn(rmsnorm(x)) # token 之间交换信息 §3
x = x + ffn (rmsnorm(x)) # 80% 的参数在这一行 §4
logits = lm_head(rmsnorm(x)) # → [T, vocab] §5
next = sample(logits[-1], temp, top_p) # 只取最后一个位置 §5
# 把这一步的 K,V 存进 cache,把 next 接到序列末尾,回到第 3 行 §6三件事看完这段就该记住:
- 模型是
L个相同 block 的堆叠,不是一个巨大的黑箱。看懂一层就看懂了全部。 - 每层只做两件事:attention 混 token,FFN 存知识。
- 生成是一个循环,每次只产出一个 token,然后把自己的输出接回输入。
速查表(当字典用)
| 名词 | 在哪 | 名词 | 在哪 |
|---|---|---|---|
| Token / 分词 / Tokenizer | §1 | FFN / MLP / SwiGLU | §4 |
| 词表 Vocabulary | §1 | MoE / 激活参数 | §4 |
| Embedding / input_ids | §1 | logits / lm_head | §5 |
| 残差流 / RMSNorm | §2 | 温度 / Top-p / 采样 | §5 |
| 参数 / 权重 / 模型尺寸 | §2 | 自回归生成 | §6 |
| Attention / QKV / 多头 | §3 | KV Cache | §6 |
| GQA / MQA / MLA | §3 | 上下文窗口 / RoPE 外推 | §6 |
| 位置编码 / RoPE | §3 | Prefill / Decode / TTFT | §6 |
| Causal Mask | §3 | 基座 / Instruct 模型 | §7 |
| 预训练 / SFT / RLHF / RLVR | §7 | Loss / 梯度 / 反向传播 | §7 |
| 蒸馏 Distillation | §7 | GPU / HBM / 量化 | §8 |
1. 入口:从文本到向量
Token / 分词(Tokenization)
- 是什么:模型不吃字符,吃 token——通常是子词(subword)。英文里
tokenization可能切成token|ization;中文一个常用字往往就是一个 token,生僻字会被拆成多个字节 token。 - 数字:英文约 1 token ≈ 0.75 词,中文约 1 token ≈ 1~1.5 字。算 API 成本和上下文占用都靠这个换算。
- 没有它会坏在哪:退化到字符级建模,同一段文本的序列长度涨 4~5 倍——KV Cache(§6)和注意力算力同比膨胀,而 attention 是
O(T²)。分词首先是一个压缩手段。
分词器(Tokenizer:BPE / SentencePiece)
- 是什么:执行分词的算法。主流是 BPE(byte-level,从字节开始反复合并高频对);SentencePiece 是它的一种工程封装。关键性质:byte-level 意味着任何字节串都能被编码,永远不会 OOV。
- 数字:Tokenizer 是独立训练的,且训完就冻结。换 tokenizer 等于换词表,等于整个 embedding 矩阵作废、必须重训。
- 没有它会坏在哪:这一层的怪癖会一路渗透到模型行为。经典案例:数字被切得乱七八糟(
1234可能是12|34),所以 LLM 做算术天生吃亏;SolidGoldMagikarp这类词表里存在但训练时几乎没出现的 token,会触发彻底失控的输出。很多看起来像"模型笨"的现象,根因在 tokenizer。
词表(Vocabulary)
是什么:模型认识的全部 token 的集合。生成时"下一个词"只能从这张表里选。
数字:现在是 10 万~26 万,不是"几万"。
模型 vocab_size Llama 2 / Mistral(旧世代) 32,000 GPT-4(cl100k) 100,256 Llama 3 / 3.1 128,256 DeepSeek V3 / R1 129,280 Qwen3 151,936 GPT-4o / o1 / o3(o200k) ~200,000 Llama 4 Scout 202,048 Gemma 3 262,208 没有它会坏在哪:词表大小是一个真实的成本项,不是无关参数。它同时决定 embedding 矩阵和 lm_head 的宽度:Gemma 3 27B 光 embedding 就 417M 参数。词表变大 → 序列变短(省算力)但矩阵变宽(费显存),是一个权衡而非免费改进。
Embedding(词嵌入)/ input_ids
- 是什么:
input_ids是 token 在词表里的整数下标;embedding 是一张[vocab, d_model]的查找表,把下标换成向量。注意这是查表,不是矩阵乘。 - 数字:
d_model(隐藏维度)现在典型 2048~8192。Llama 3 70B 是 8192。 - 没有它会坏在哪:不查表就得直接拿整数进网络,那样 id=1234 和 id=1235 在数值上相邻——而词表里相邻的两个 token 毫无语义关系。embedding 的作用是把"任意编号"变成"可学习的几何位置",语义近的词向量才可能近。
2. 主干:残差流
这一节是原文档完全缺失的部分,也是最该先补的部分。不理解残差流,Transformer 就只是一堆零件。
残差流(Residual Stream)
- 是什么:贯穿全模型的一条
[T, d_model]的"主干道"。每个子层都不是替换它,而是往上加一笔:x = x + sublayer(norm(x))。可以把它想成一块共享白板,每一层读一遍、往上写一点。 - 数字:Llama 3 70B 有 80 层 × 2 个子层 = 160 次读写,全都作用在同一条 8192 维的流上。
- 没有它会坏在哪:梯度传不回去,深网络根本训不动。 加法让反向传播有一条恒等短路(
∂x/∂x = 1),梯度可以直达第一层。ResNet 解决的就是这个问题,Transformer 继承了它。这也是为什么"深"是免费的——每层只需要学一个小增量。
RMSNorm / Pre-Norm
- 是什么:进子层之前先把向量归一化。现代模型用 RMSNorm(只除以均方根,不减均值),比 LayerNorm 少一半操作。位置在子层之前(pre-norm),不是之后。
- 数字:
x / sqrt(mean(x²) + eps) * weight——每层只有d_model个可学习参数,可以忽略不计。 - 没有它会坏在哪:残差流是一路相加的,激活值会逐层放大直到溢出,或坍缩到 0。归一化把每次进子层的输入拉回可控尺度。Pre-norm 而非 post-norm 是让大模型训练稳定的关键工程细节之一——原始 2017 Transformer 用的是 post-norm,深层时不加 warmup 就发散。
参数 / 权重 / 模型尺寸
- 是什么:权重是学出来的数字(矩阵乘的系数),偏置是加性项,参数 = 两者统称。现代 LLM 里偏置基本被去掉了(Llama 系全无 bias),所以"权重"和"参数"在实践中同义。模型 = 固定架构(代码)+ 参数(数字)。
- 数字:一个 block 的参数分布(Llama 3 70B):attention 17.6%,FFN 82.4%。跨模型都在 16~20% / 80~84%。
- 没有它会坏在哪:把参数当成一坨无结构的数字,就会漏掉最重要的事实——知识主要不在注意力里,在 FFN 里(§4)。MoE、剪枝、LoRA 该往哪下手,全由这个分布决定。
- ⚠️ 参数量已经不再直接代表能力:"7B / 70B" 这套刻度在 MoE 时代断了。DeepSeek V3 是 671B 总参 / 37B 激活。规则:激活参数预测算力和延迟,总参数预测显存。
3. Attention 子层
一层里的第一个子层。它是模型中唯一让不同位置的 token 互相看见的地方。
注意力(Attention)/ QKV
- 是什么:每个位置从残差流里投影出三个向量——Q(我在找什么)、K(我能提供什么)、V(我实际携带的内容)。用
Q·Kᵀ算出每个位置对其他位置的关注权重,softmax 归一化,再对 V 加权求和写回残差流。四个矩阵:W_q, W_k, W_v, W_o。 - 数字:
softmax(Q Kᵀ / sqrt(head_dim)) V。那个sqrt(head_dim)不是装饰——不除,点积方差随维度线性增长,softmax 会饱和成 one-hot,梯度消失。 - 没有它会坏在哪:没有 attention,每个位置就是独立的,模型退化成对每个 token 单独做变换的 MLP,无法利用上下文。这是"上下文"这个词的物理来源。
Causal Mask(因果掩码)
- 是什么:训练时把注意力矩阵的上三角设为
-inf,让位置t只能看见≤ t的位置。 - 数字:一行代码,
scores.masked_fill_(mask, -inf)。 - 没有它会坏在哪:训练和推理会不一致。 训练时模型能看见后面的答案,学会了"抄";推理时后面还不存在,直接崩。有了 mask,一次前向对
T个位置同时提供T个训练信号——这是"预测下一个词"能高效并行训练的全部原因。
多头注意力(Multi-Head Attention)
- 是什么:把 attention 拆成
n_heads份并行做,每份在head_dim维的子空间里,结果拼回再过W_o。 - 数字:Llama 3 70B:64 头 × 128 维。注意
head_dim现在常和d_model / n_heads解耦——Gemma 3 27B 是 hidden 5376、32 头、head_dim 128(32×128 = 4096 ≠ 5376)。 - 没有它会坏在哪:单个 softmax 只能表达一种关注模式。要同时追踪句法依赖、指代、和远距离主题,需要多个独立的注意力模式并行。
GQA / MQA / MLA(KV 压缩)
是什么:Q 用满头,但 K/V 共享更少的头。MHA(每头独立)→ GQA(几个 Q 头共享一组 KV)→ MQA(全部共享一组)→ MLA(DeepSeek:把 K/V 压成一个共享的低秩 latent 向量 + 一个单独的 RoPE key)。
数字:Llama 3 全系
num_key_value_heads = 8。模型 Q/KV 头 每 token KV @128k 若用 MHA 压缩 Llama 3 8B 32 / 8 128 KiB 17.2 GB 68.7 GB 4× Llama 3 70B 64 / 8 320 KiB 42.9 GB 343.6 GB 8× Llama 3 405B 128 / 8 504 KiB 67.6 GB — 16× DeepSeek V3(MLA) 128 / latent 68.6 KiB 9.2 GB — ~工程等价 GQA-2.25 DeepSeek V3 是 671B 的模型,KV Cache 却比 70B 的 Llama 3 还小 4.7 倍。MLA 论文自己的说法是"KV cache 等于 2.25 组的 GQA,但效果强于 MHA";V2 相比 MHA 版本减少 93.3% KV,生成吞吐 5.76×。
没有它会坏在哪:Llama 3 70B 权重 140 GB + MHA 的 343.6 GB KV,128k 上下文在任何单机上都放不下。GQA 不是微优化,它是长上下文能被商业化服务的前提。
同类杠杆:滑动窗口 / 局部-全局交替。Gemma 3 用 5:1 的局部:全局比例、局部窗口 1024,把 128k 的 KV 从 66.6 GB 压到 11.2 GB(~6×)。
位置编码 / RoPE(旋转位置编码)
是什么:RoPE 现在是事实标准。 它不往 embedding 上加任何东西——而是在每一层 attention 内部,对 Q 和 K 按位置做旋转。旋转的相对角度天然编码了相对距离。
数字:唯一的关键超参是
rope_theta(base frequency),它直接决定能撑多长上下文:模型 rope_theta 原始 Llama 2 / DeepSeek V3 10,000 Kimi K2 50,000 Llama 3(从 10k 提到 500k) 500,000 Qwen3 / Gemma 3 1,000,000 Qwen3-Next 10,000,000 没有它会坏在哪:不给位置信息,attention 对输入顺序是置换等变的——「猫吃鱼」和「鱼吃猫」会得到完全相同的 logits。位置编码是唯一打破这个对称性的东西。
⚠️ 别用旧框架:「给每个 token 注入位置信息」描述的是 2017 年的 sinusoidal 绝对位置编码和 BERT/GPT-2 的可学习绝对位置表。两者都已淘汰,致命缺陷是没法外推超过训练时那张表的长度。RoPE 因为是相对的、且由一个 base 参数控制,才有了 YaRN 这类外推手段(§6)。
2026 趋势(值得知道名字):部分 RoPE——只旋转一部分维度(GLM-4.6
partial_rotary_factor: 0.5,Qwen3-Next0.25),或只在一部分层用(Llama 4 的 iRoPE:每 4 层有 1 层完全不加位置编码,48 层里 12 层是 NoPE)。DeepSeek 的 MLA 本身就是部分 RoPE 设计(qk_nope_head_dim=128+qk_rope_head_dim=64)。
4. FFN 子层:80% 的参数在这里
原文档从头到尾没提过 FFN。这是最大的漏洞——它是模型里参数量最大的模块,也是知识的存放处。
FFN / MLP / SwiGLU
是什么:一个逐位置(position-wise)的两层网络:升维 → 非线性 → 降维。它不跨 token,每个位置独立计算。现代模型用 SwiGLU,需要三个矩阵:
gate、up(都是d_model → d_ff)、down(d_ff → d_model),计算是down(silu(gate(x)) * up(x))——那个逐元素乘就是"门控"。数字:
d_ff ≈ 3.5 × d_model(Llama 3 8B:4096 → 14336)。参数占比:模型 Attention FFN Llama 3 8B 19.2% 80.8% Llama 3 70B 17.6% 82.4% Qwen3-32B 19.4% 80.6% Gemma 3 27B 16.0% 84.0% Llama 2 7B(MHA,无 GQA) 33.2% 66.8% 没有它会坏在哪:只剩 attention 的话,模型基本是一串加权平均——线性混合加一个 softmax,几乎没有存储和加工知识的容量。"巴黎是法国首都"这条事实存在 FFN 的权重里,不在注意力里。 常用的心智模型:attention 负责路由信息,FFN 负责存储和计算。
为什么占比比教科书还高:经典说法是 FFN 约 2/3(
4h的 FFN + MHA)。SwiGLU 用 3 个矩阵而非 2 个,GQA 又砍小了 K/V 投影,两个因素一起把 FFN 推到了 80%+。
MoE(Mixture of Experts)/ 激活参数
是什么:把 FFN 换成
N个并行的 FFN(专家),一个路由器给每个 token 只挑 top-k 个执行。MoE 只作用在 FFN 层——而 FFN 正好是 80% 的参数所在,这不是巧合。数字:前沿开源模型现在基本全是 MoE。
模型 总参 激活 路由专家 top-k 共享专家 Mixtral 8x7B(老世代) 46.7B 12.9B 8 2 0 DeepSeek V3 / R1 671B 37B 256 8 1 Qwen3-235B-A22B 235B 22B 128 8 0 Qwen3-Next-80B-A3B 80B 3B 512 10 1 Kimi K2 1.04T 32B 384 8 1 Llama 4 Maverick 400B 17B 128 1 1 gpt-oss-120b 117B 5.1B 128 4 0 没有它会坏在哪:想要 671B 的容量就得付 671B 的算力。MoE 把容量和算力解耦——V3 每 token 只算 37B(≈ 5.5%),但保有 671B 的知识量。
⚠️ 但显存不省:671B 全部都得驻留在 HBM 里,即使每步只用 37B。这是 MoE 部署最反直觉的一点。
趋势方向:Llama 3(2024)是最后一个大型密集前沿模型,论文明确说选密集是"为了最大化训练稳定性";一年后 Llama 4 全线转 MoE。粒度也在变细:Mixtral 的 8 专家 top-2 → 现在 256~512 专家 top-8/10 + 1 个常开的共享专家。约 35B 以下仍以密集为主(Qwen3-32B、Gemma 3 全系),因为部署简单。
5. 出口:从向量到 token
lm_head / logits
- 是什么:最后一层 norm 之后,用一个
[d_model, vocab]的矩阵把隐状态投影成词表上的原始分数(logits),未归一化。softmax 之后才是概率分布。 - 数字:输出形状
[T, vocab],但生成时只用最后一行——前面T-1行在推理时是浪费的(训练时它们全都是有效信号,见 causal mask)。小模型常把 lm_head 和 embedding 共享同一份权重(tied embeddings)省显存。 - 没有它会坏在哪:残差流是
d_model维的连续向量,跟词表没有对应关系。lm_head 是唯一把它翻译回"哪个 token"的那一步。
温度 / Top-p / Top-k / 采样
- 是什么:
logits / temperature→ softmax → 截断 → 抽样。温度缩放分布(低 → 尖锐确定,高 → 平坦随机,temp → 0等于贪心);top-k 只留概率最高的 k 个;top-p(nucleus) 留累计概率到 p 的最小集合。do_sample=False就是贪心。 - 数字:常见默认
temp=0.7, top_p=0.9。注意顺序:先温度缩放,后截断。 - 没有它会坏在哪:全贪心解码输出确定,但会陷入重复循环、文风乏味。反过来温度太高就开始胡言乱语。这一层是纯推理期的旋钮,不动一个权重——同一个模型换采样参数,表现可以差很远,很多"模型不行"其实是采样参数不对。
6. 循环:KV Cache 与上下文
自回归生成(Autoregressive Generation)
- 是什么:一次一个 token,每一步都把自己刚生成的接回输入,重跑一遍前向。
- 数字:生成 500 token = 500 次完整前向,串行,无法并行。
- 没有它会坏在哪:这是 LLM 延迟的根本来源。也是幻觉会自我放大的机制来源——一旦第 10 个 token 错了,后面 490 个都在这个错误的条件下生成,模型会努力让错误显得自洽。
KV Cache
是什么:注意力只需要历史 token 的 K 和 V,而它们不随后续 token 改变。所以算一次就缓存,新 token 只算自己的 QKV 再去查缓存。
数字:公式必须用
num_kv_heads,不是hidden_dim:KV bytes = 2(K,V) × num_layers × num_kv_heads × head_dim × bytes_per_elem × seq_len × batch用
hidden_size的话会高估整整一个 GQA 倍数(Llama 3 70B 是 8 倍)。核对一遍 Llama 3 70B:2 × 80 × 8 × 128 × 2 = 327,680 B = 320 KiB/token。没有它会坏在哪:每步都重算全部历史的注意力,单步
O(T²),生成整段变成O(T³)。KV Cache 把单步降到O(T)。它是用显存换算力——这一笔交易直接把上下文长度变成了显存问题(§8)。
Prefill vs Decode
- 是什么:Prefill 一次性并行处理整段输入 prompt(算力受限 / compute-bound),对应首 token 延迟 TTFT。Decode 之后逐 token 生成,每步只算一个(带宽受限 / memory-bound),对应每 token 延迟 TPOT。
- 数字:这解释了"长 prompt 开头卡一下、之后稳定吐字"。也解释了为什么两个阶段的优化手段完全不同——prefill 拼 FLOPS,decode 拼带宽和 batch size。
- 没有它会坏在哪:不区分这两个阶段,就无法理解任何推理性能数据。"这个模型多快"这个问题本身是没定义的,得问 TTFT 还是 TPOT。
上下文窗口(Context Window)
- 是什么:模型能处理的最大 token 数(输入 + 已生成)。
- ⚠️ 这是主要由训练决定的属性,不是由显存决定的。 原文档把它写成"本质受 HBM 容量约束",这个因果颠倒了。真正决定它的是:
- 训练序列长度 + 长上下文续训。Llama 3 405B 从 8K 起步,分六个阶段升到 128K,烧掉约 800B token 的长上下文数据,每一阶段都要求短上下文评测不掉、needle-in-a-haystack 全对才推进。
rope_theta(§3)。- 外推方法:YaRN、NTK-aware scaling、位置插值。DeepSeek V3 用 YaRN
factor: 40,把 4096 扩到 163,840;Kimi K2-Thinking 用factor: 64到 262,144。
- HBM 决定的是"这个窗口里你能经济地服务多长",两件事分开看。两个干净的证据:(a) 同样的权重换更大显存的机器,不用重训就能吃更长上下文;(b) Gemma 3 论文明说模型能泛化到 128K,但再往上"迅速退化"——那是固定硬件下的质量悬崖,是训练边界不是内存边界。
- 第三个常被混同的概念:有效上下文。声称 128k、实际检索质量在多少 token 之后开始掉,通常远低于标称值。标称长度、可服务长度、有效长度,是三个不同的数。
7. 权重从哪来:训练流水线
前面六节讲的是权重已经存在时会发生什么。这一节讲那些数字怎么来的。
推理 vs 训练
- 推理(Inference):拿固定权重跑前向,产出结果。就是 §0 那 12 行。
- 训练(Training):跑前向 → 算 loss → 反向传播 → 更新权重。显存开销约是推理的 3~4 倍(要额外存激活值、梯度、优化器状态)。
训练机制
- 损失函数 / 交叉熵(Cross-entropy):衡量预测分布离真实下一个 token 有多远。
loss = -log P(正确token)。数字直觉:随机猜的 loss ≈ln(vocab)≈ 11.7(词表 128k);训好的模型在 2 以下。 - 梯度 / 反向传播(Backpropagation):对 loss 求每个权重的偏导,得到"往哪调、调多少",从输出层逐层回传。没有残差流(§2)的恒等短路,这个回传在几十层深度上就会消失。
① 预训练(Pretraining)——最贵的一步
- 数据:万亿级 token 的无标注文本(网页 / 书 / 代码)。数据质量是这一步的全部——去重、过滤、配比比架构调参重要得多。
- 任务:自监督"预测下一个词"。没有人工标注,文本自己当标签。
- 产出:基座模型(Base / Pretrained Model)——只会续写。你问它"法国首都是哪",它可能续一句"?德国首都是哪?意大利首都是哪?",因为在互联网语料里这才是常见的接下去写法。它不是不知道答案,是不知道你在提问。
② 监督微调(SFT)
- 数据:人工写的(指令,理想回答)对。量少质精——通常几万到几十万条,不是几亿。
- 产出:指令微调模型(Instruct Model)。base 和 instruct 的分水岭就在这。它主要学的是"格式和角色",不是新知识——知识在预训练里就装完了。
- 附带效应:这一步引入了 chat template(
<|im_start|>user之类的特殊 token)。用错模板,instruct 模型表现会断崖下跌,这是本地部署最常见的坑。
③ 偏好对齐(RLHF / DPO)
- 数据:人类对"哪个回答更好"的成对比较。
- 做法:RLHF 先训一个奖励模型打分,再用 PPO 让策略往高分走;DPO 跳过奖励模型和 RL 循环,直接在偏好对上做优化。
- 目标:有用、无害、诚实。
- ⚠️ 别再写"DPO 现在更主流":2024 年是对的(Llama 3 就是 DPO 的高点,论文明确说 DPO 比 PPO 省算力且效果更好)。R1 之后翻过去了——DeepSeek V3 论文里 "DPO" 出现 0 次,用 GRPO;Qwen3 也是 0 次,用 GRPO 后来换 GSPO;Llama 4 把 DPO 降级成最后一道轻量抛光,并明确说"SFT 和 DPO 会过度约束模型,限制 online RL 阶段的探索"。DPO 在 2026 年的准确定位:小规模社区微调的便宜稳定选项 + 最后的轻量打磨,不是前沿主线。轨迹是
PPO/RLHF (2022) → DPO (2023-24) → GRPO/RLVR (2025-26)。
④ 推理训练(RLVR:RL with Verifiable Rewards)
- 数据:能自动判对错的题——数学、代码、单元测试。"可验证"是关键词:奖励来自编译器和测试,不来自人类打分,所以能无限扩展。
- 做法:用 GRPO 及其后继(GSPO、DAPO)奖励正确答案,长链思考是被逼出来的副产品,不是被教的。
- 产出:推理模型(o1/o3、DeepSeek-R1、Qwen3 thinking)。R1-Zero 最惊人的地方是纯 RL、不做 SFT 就涌现出了反思和自我纠错。
- 和 CoT 的关系:CoT(§9)原本是提示技巧——你要求模型一步步想。RLVR 之后它变成了训练进权重的行为——模型自己就会想,不需要你提示。
附:蒸馏(Distillation)
- 是什么:两种不同的东西,别混:
- 经典 logit 蒸馏:学生模型拟合教师的完整输出分布(软标签),比拟合硬标签信息量大。
distilgpt2、DistilBERT是这一类。 - "R1-distill" 式:拿大模型生成的推理轨迹去做学生的 SFT。本质是 SFT,只是数据由模型产出。今天说"蒸馏"多半指这个。
- 经典 logit 蒸馏:学生模型拟合教师的完整输出分布(软标签),比拟合硬标签信息量大。
- 没有它会坏在哪:小模型直接从原始数据训,效果远不如从大模型的输出学。这也是 2025 年后小模型能力猛涨的主因——它们不是学得更好,是有了更好的老师。
各类模型走到了哪一步
| 模型 | 走过的阶段 | 行为特征 |
|---|---|---|
distilgpt2 | ① 预训练 + 蒸馏 | 只会续写,不听指令 |
Llama-3-8B(base) | ① | 同上 |
Qwen3-0.5B-Instruct | ① → ② → ③ | 能听懂并回答问题 |
DeepSeek-R1 | ① → ② → ③ → ④ | 先输出长思考链再答 |
8. 硬件账本
顺序很重要:硬件放在软件链之后。它是前面那条链的后果,不是原因。
GPU
- 是什么:海量核心的并行处理器。LLM 的前向几乎全是大矩阵乘。CPU 核少但单核快、擅长串行分支;GPU 核多且规整、擅长并行数值计算。
- 数字:H100 的 bf16 算力约 1000 TFLOPS(稀疏),HBM 带宽约 3.35 TB/s。记住这两个数的比例。
HBM(高带宽显存)
- 是什么:堆叠在 GPU 芯片旁的高速显存。带宽极高,容量有限(H100 是 80 GB)。权重、激活、KV Cache 全住在这里。
- 数字:一张显存预算表(70B,bf16):
权重 70B × 2 B = 140 GB ← 已经超过单张 H100 的 80 GB KV Cache @128k = 42.9 GB ← 见 §6 激活值 + 碎片 ≈ 几 GB ──────────────────────────────────── 合计 ≈ 185 GB → 至少 3~4 张卡 - 为什么"带宽"比"算力"更常成为瓶颈:decode 阶段每生成 1 个 token,要把全部权重从 HBM 读进计算单元一遍。70B bf16 = 140 GB,除以 3.35 TB/s → 理论上限约 24 token/s(batch=1),跟算力毫无关系。这就是 decode 是 memory-bound 的意思,也是为什么提高 batch size 几乎免费提升总吞吐:读一次权重,服务多个请求。
量化(Quantization)
- 是什么:用更少的位表示权重(和可选的激活/KV Cache)。⚠️ 起点是 bf16,不是 fp32——早就没人用 fp32 做推理了,DeepSeek V3 甚至原生 FP8 训练。
- 数字:70B 的体积:常见方案名:GPTQ、AWQ、GGUF(llama.cpp)。
bf16 → 140 GB (2 张 80G 卡) fp8 → 70 GB (1 张,刚好) int4 → 35 GB (1 张,宽裕;常见质量损失 1~3%) - 没有它会坏在哪:70B 在单卡上根本跑不起来。而且量化不只省容量——它同时减少要搬的字节数,所以直接提升 decode 速度(回到上面那个带宽账)。这是"省显存"之外更容易被忽略的收益。
三者关系链
算力(FLOPS) ← prefill 卡在这里
▲
┌───────────────────┴───────────────────┐
│ GPU 计算单元:矩阵乘(权重 × 激活) │
└───────────────────┬───────────────────┘
▲
每步都要把权重整个读一遍
带宽 3.35 TB/s ← decode 卡在这里
▲
┌───────────────────┴───────────────────┐
│ HBM 80 GB:权重 + 激活 + KV Cache │
└───────────────────┬───────────────────┘
▲
KV Cache 随上下文线性膨胀,挤占容量
(GQA / MLA / 滑动窗口 就是压这一项,见 §3)一句话:prefill 拼算力,decode 拼带宽;容量决定"装不装得下",带宽决定"跑多快"。上下文长度由训练决定,能不能负担得起由 HBM 决定。
9. 不在本文范围
下面这些不属于"一次前向传播"这条链,硬塞进来只会让文档退回成词汇表。它们各自应该是独立的一篇:
已经写了的
- 应用层 → Agent:跟着一次循环走完全程 Agent / Function Calling / RAG / MCP / 上下文工程。主轴是那个只增不减的
ctx数组。思想原型见 ReAct 论文笔记。 - 多模态 → 多模态:一张图怎么变成 token ViT / CLIP / SigLIP / 投影层 / 图像计费 / 原生多模态。主轴是
N——一张图变成多少个 token。 - 训练系统 → 一张卡装不下之后:把模型切开 DP / TP / PP / CP / EP、ZeRO、FlashAttention、MFU。从 §8 那张 185 GB 的账单往下接。
还没写的
- 提示技巧:Prompt Engineering、CoT、few-shot、ReAct ⚠️ 但注意 §7④——CoT 已经从提示技巧变成了训进权重的行为。这一篇真要写,主轴应该是时间线(《哪些提示技巧被训进了权重》),而不是技巧清单。
- 安全:提示注入、越狱、护栏、对齐税 最好的切入点是把攻击点标在 §0 那张骨架图上。提示注入之所以无法用 prompt 修复,是因为"指令"和"数据"进的是同一张 embedding 表——模型在架构层面就分不开。
- 评测:MMLU / GPQA / SWE-bench、污染问题、LLM-as-judge 固定格式该是「测什么 / 满分多少、蒙分基线多少 / 怎么被刷」。MMLU 四选一的蒙分基线是 25%,这个数一放,一半的榜单叙事就自己塌了。
- 模型选型:开源 vs 闭源、许可证、API 定价 这个不该写成文章——价格三个月变一次,写死就是负债。它该是一张定期更新的表加一个成本函数,而成本函数的料在 §1(token 换算)、§8(显存账)和 agent-loop §1(上下文预算)里已经齐了。