arganzheng · 2026-04-09
从熟悉的 Transformer 主干出发,读配置、数参数,再用算量、访存和 Roofline 理解现代 LLM 的结构改动与代价。
llm_cost.py,把模型配置和硬件参数换算成成本表结论:沿着 GPT-2 的槽位比较现代模型,配置中的五处关键改动解释了 Llama 的结构选择;逐项计数得到 Llama-3-8B 的 8,030,261,248 个参数。
\[N = L\big[d(2d + 2d_{kv}) + 3d \cdot d_{ff} + 2d\big] + 2Vd + d\]| Llama-3-8B | 每层 | × 32 层 | 占比 |
|---|---|---|---|
| attention(Q、O 各 d²,K、V 各 d·d_kv) | 41.94M | 1.34B | 17% |
| SwiGLU 三矩阵 3 × 4096 × 14336 | 176.16M | 5.64B | 70% |
| embedding + lm_head(不共享) | — | 2 × 525.3M | 13% |
结论:每参数每 token 2 FLOPs;decode 每步把 16 GB 权重读一遍、算术强度在数值上就等于 B,ridge 是 989 / 3.35 ≈ 295——这就是「decode 是 memory-bound」的全部含义。
| 量 | 数 |
|---|---|
| 每 token 权重 FLOPs | 2 × 7.5B = 15.0 G;attention 8K 4.3 G、128K 68.7 G |
| decode B = 1 | 读 16.06 GB / 3.35 TB/s = 4.8 ms、208 token/s;算力时间 0.02 ms |
| 要 compute-bound | B ≈ 295,但 8K 下这些请求的 KV 要 295 GiB,剩 64 GB 放不下 |
| KV 读取强度 | 常数 g = 4,不随 B 摊薄;总强度趋于 18 → 单卡任何可行 batch 都 memory-bound |
| 真正的约束 | B × s ≤ 52 万 token(64 GB / 128 KiB) |
| prefill 8K | 因果三角约 140 TFLOP;峰值 0.14–0.16 s 是物理下限,60% MFU 约 0.24–0.27 s |
结论:RoPE 把 \(d_{head}\) 维向量看成 64 对复数,每对以自己的波长旋转;8K 训练时 14 对低频维度没转完一圈——推到 32K 出现从未见过的相位。不是装不下,是没见过。
结论:KV cache 每 token = \(2\,L\,n_{kv}\,d_{head} \cdot\) bytes/elem,公式里没有 \(n_h\)——V3 128 头 61 层的 KV(68.6 KiB)比 8B 32 头(128 KiB)还小。
| 模型 | 结构 | KV / token | 128K 上下文 | decode attention 强度 |
|---|---|---|---|---|
| 8B 若用 MHA | 32 KV 头 | 512 KiB | 64 GiB | 1 |
| Llama-3-8B | GQA,8 KV 头(g = 4) | 128 KiB | 16 GiB | 4 |
| Llama-3-70B | GQA,8 KV 头(g = 8) | 320 KiB | 40 GiB | 8 |
| DeepSeek-V3 | MLA,512 + 64 维 latent | 68.6 KiB | 8.6 GiB | 242 |
| V3 若用 MHA | 128 头 × 128 | 3.81 MiB | 477 GiB | 1 |
%%{init: {"flowchart": {"wrappingWidth": 260}}}%%
flowchart LR
H["h_t (7168)"] --> DKV["W_DKV + W_KR<br/>7168 → 576"]
DKV --> CKV["c^KV_t (512)<br/>进 cache"]
DKV --> KR["k^R_t (64),RoPE<br/>128 个 head 共享,进 cache"]
CKV --> UKV["W_UK / W_UV 升维<br/>512 → 128 head × 128"]
UKV --> ATT["attention<br/>q·[k^C ; k^R] / √192"]
KR --> ATT
H --> Q["W_DQ → W_UQ + W_QR<br/>q^C 128 维 + q^R 64 维"] --> ATT
ATT --> WO["W_O 16384 → 7168"]
classDef cache fill:#fde68a,stroke:#b45309,stroke-width:2px,stroke-dasharray:5 3
class CKV,KR cache
| Llama-3-8B | Llama-3-70B | |
|---|---|---|
| attention = 权重 FLOPs 的交叉点 | 约 28.6K | 约 53.8K |
| 128K 时 attention / 权重 FLOPs | 68.7 G / 15.0 G = 4.6 倍 | 344 G / 141 G |
| 128K prefill(60% MFU) | 6.5 PFLOP,约 11 s | 41 PFLOP,约 69 s |
| 128K 一条请求的 KV | 16 GiB | 40 GiB |
结论:总参数定显存(671B,FP8 也放不进 8 卡 640 GB)、激活参数定 FLOPs(37B,是 70B 的一半)、每步实际读取的参数定 decode 带宽——中等 batch 下几乎读全部专家,稀疏省了算量没省访存。
| DeepSeek-V3,256 专家取 top-8 | B = 1 | B = 32 | B = 128 |
|---|---|---|---|
| 期望激活专家数 \(E[1 - (1 - k/E)^B]\) | 8 | 163 | 252 |
| 每步读取的专家参数(FP8) | 37 GB | 434 GB | 660 GB |
| 对比 dense 70B(BF16) | 141 GB | 141 GB | 141 GB |
%%{init: {"flowchart": {"wrappingWidth": 240}}}%%
flowchart LR
TOK["token x(d 维)"] --> RT["router W_r x<br/>E 个分数 → top-k(k = 8)"]
RT -->|"g_1"| E1["FFN_1 选中"]
RT -.->|"g_2 = 0"| E2["FFN_2 不计算"]
RT -->|"g_E"| EE["… FFN_E 选中"]
TOK --> SH["共享专家<br/>不经 router"]
E1 & EE & SH --> SUM["y = Σ g_i · FFN_i(x) + FFN_shared(x)<br/>算了 k + 1 个,持有 E + 1 个"]
classDef active fill:#dff0d8,stroke:#3c763d
classDef idle fill:#f5f5f5,stroke:#aaa,color:#888
classDef sh fill:#fcf8e3,stroke:#8a6d3b
class E1,EE active
class E2 idle
class SH sh
结论:每个位置额外预测 \(t_{i+2}\),信号密度 ×(1 + D),主干被逼编码更远的未来;模块顺序喂真实 \(t_{i+1}\) 保持因果链(teacher forcing 的延伸);推理时丢弃或当投机 draft。
%%{init: {"flowchart": {"wrappingWidth": 220}}}%%
flowchart LR
T["主干 Transformer<br/>h_i"] --> H0["lm_head → 预测 t_{i+1}<br/>L_main"]
T --> C["RMSNorm(h_i) ‖ RMSNorm(emb(t_{i+1}))<br/>投影 2d → d"]
E["真实 t_{i+1}"] --> C
C --> BLK["一个 Transformer block"] --> H1["共享 lm_head → 预测 t_{i+2}<br/>λ · L_MTP"]
classDef mtp fill:#e0e7ff,stroke:#4338ca
class C,BLK,H1 mtp
| DeepSeek-V3(D = 1) | nanoGPT 0.8M 实验 | |
|---|---|---|
| 额外参数 | 一个 block + 2d²,< 2% | +29% |
| 每步耗时 | — | +38% |
| 主任务 | 大规模上有收益(13B 以上明显) | 1.9375 对 1.9364,不变 |
| MTP 头 | 当投机 draft:接受率 85–90%、TPS 约 1.8 倍 | t+2 命中 45%(知道 t+1 且多一层) |
结论:先猜 γ 个 token、再用一次前向验证,接受 / 拒绝重采样让输出分布严格等于目标模型;验证 γ+1 个 token 只在 memory-bound 区间几乎免费。
| 量 | 8B 上的数字 | 边界 |
|---|---|---|
| 期望产出 | \((1-\alpha^{\gamma+1})/(1-\alpha)\):α=0.8、γ=4 → 3.36 token | γ 越大增量越小,草稿成本线性涨 |
| 加速比 | \(\mathbb{E}[\text{tokens}]/(\gamma c + 1)\):c=0.1 → 2.4×,c=0.02 → 3.1× | 前提是验证 γ+1 个与验证 1 个同样贵 |
| 转折 batch | ridge/(γ+1) ≈ 60;60% MFU 下 B=64 掉到 1.6、B=128 低于 1 | 完全 compute-bound 时 3.36/5.4 ≈ 0.62 |
| 草稿来源 | 独立小模型、Medusa、EAGLE、n-gram、MTP 模块(α≈0.85–0.9,1.8×) | 改 α 与 c,改不了收益区间 |
Table: 投机解码的正确性、收益与边界
结论:图片贵的不是 encoder(一次性、compute-bound、12 ms),是它变成的 token 在 decoder 里占的 KV——与同长文本同价、活到请求结束;1024² 在 Qwen2-VL 里 = 1369 个 token,一段无法被 tokenizer 压短的 system prompt。
| 段 | 账 | 数 |
|---|---|---|
| vision encoder(0.63B ViT,5476 patch) | \(2N_{vit}n_p + 4L_{vit}n_p^2 d_{vit}\) | 11.8 TFLOP,attention 二次项占 42%;一次性 |
| connector | 决定 token 数 | MLP 不压缩 576 · 2×2 merge 1369 · resampler 定长;同一张图 576–6404 差 11 倍 |
| decoder | prefill + KV | 193 TFLOP;KV 428 MiB,是 encoder 输出 21 MiB 的 20 倍 |
结论:前向反向要范围、权重更新要精度——所以 BF16 计算 + FP32 master weights;\(\Delta w / w \sim 10^{-4}\) 低于 BF16 的单位舍入 \(2^{-8} = 0.004\),1.0 + 0.001 会被舍回 1.0。
| 位置 | 例子 | 对策 |
|---|---|---|
| 大数吃小数 | 1.0 + 0.001 in BF16 | FP32 master weights |
| 长求和 | k = 4096 的点积在 BF16 累加噪声 ε√k ≈ 25% | FP32 累加器 |
| 指数溢出 | softmax 的 e^x,x > 11 在 FP16 溢出 | 减最大值 |
| 相消 | 方差 = E[x²] − E[x]² | Welford |
%%{init: {"flowchart": {"wrappingWidth": 260}}}%%
flowchart TB
S["结构:L、d、d_ff、n_kv、E、k<br/>(01、06)"] --> N["参数量 N、激活参数量(01、06)"]
S --> K["KV / token = 2 L n_kv d_head · bytes/elem(04)"]
D["数值:bytes/param、bytes/elem(10、量化专题)"] --> W["权重字节 = N · bytes/param"]
N --> W
D --> K
N --> F["FLOPs / token ≈ 2N + 4dLs(02、05)"]
R["运行点:B、s、prefill / decode(02、05、06、08、09)"] --> F
R --> H["每步字节 = 权重字节 + B · s · KV/token"]
K --> H
W --> H
F --> I["算术强度 I = FLOPs / 字节"]
H --> I
I --> T["与 ridge ≈ 295 比较 → memory / compute-bound → 时间下界"]
| 线 | 落点 |
|---|---|
| 一份代码 | 原理系列 01 六步手算 → 02 极小 GPT → 03 nanoGPT → 04 训起来;现代系列 01/02/04/06/07/08 在这些基线上逐项展开 |
| 一条 Roofline | \(I_{weight} = B\)、\(I_{KV} = g\);MLA 把 g 抬到 242;MoE 专家强度 Tk/E;投机转折 ridge/(γ+1)(量化专题的 W4A16 在 ridge/4) |
| KV 与上下文 | 128 KiB/token 打开成四个乘子;B × s ≤ 显存 / KV;长上下文贵在 HBM 字节数不在调度 |
| 「参数量」拆成几个数 | N → N_gemm 7.5B → 总 / 激活 / 每步读取 → bytes/param 推理 2、训练 16(4.25 bit 与可训练 0.52% 在两个专题) |
| 字节里存了什么 | E4M3 / E5M2 分工、128 分块 scale、FP8 KV、FP8 dispatch;数值决定结构细节 |
| 篇 | 一个公式 / 一个数 |
|---|---|
| 01 | \(N = L[d(2d + 2d_{kv}) + 3d\,d_{ff} + 2d] + 2Vd + d\);8,030,261,248 |
| 02 | ridge = 989 / 3.35 ≈ 295;\(I_{weight} = B\)、\(I_{KV} = g\);训练 \(6ND\) |
| 03 | \(\lambda_i = 2\pi\cdot\text{base}^{2i/d_{head}}\);位置外推不等于长文理解 |
| 04 | KV/token = \(2Ln_{kv}d_{head}\cdot\)bytes;MLA 68.6 KiB、强度 242 |
| 05 | 交叉点 8B 28.6K;128K prefill 约 11 s,KV 16 GiB |
| 06 | \(E[1-(1-k/E)^B]\):B = 32 → 163 个专家、434 GB |
| 07 | \(\mathcal L_{main} + \lambda\bar{\mathcal L}_{MTP}\);接受率 85–90%、1.8 倍 |
| 08 | \((1-\alpha^{\gamma+1})/(1-\alpha)\) = 3.36;转折 ridge/(γ+1) ≈ 60 |
| 09 | \(n_{img} = \lceil H/28\rceil\lceil W/28\rceil\);KV / encoder 输出 = 20 |
| 10 | BF16 单位舍入 2⁻⁸;16 B / 参数;ε√k |
/llm-architecture-evolution-roadmap-from-gpt2.html;通关自测在系列总结