Appearance
第9章 · 现代推理、KV Cache、RMSNorm/SwiGLU、量化与 PagedKV
性能结论必须绑定实际设备、重复次数和权重格式。
先修:第7章的 attention shape 和第8章的生成路径。
内容节奏:本章内容量约等于两个标准章节(10 个概念、6 个实现任务),一口气硬塞的结果是每个概念都停在“看过”而不是“做过”。建议分两部分推进:
- 第一部分 · 现代架构组件:§1.1–1.4 的 RoPE、RMSNorm、SwiGLU、Tied Embedding,对应 §4.1 的任务 1、4、5、6。
- 第二部分 · 推理系统:MQA/GQA、KV Cache 等价对拍、PagedKV、量化与 benchmark,对应 §4.1 的任务 2、3 和 §4.2/4.3 的验证与写作。
两部分之间建议留出消化间隔;只完成第一部分不算完成本章,本章离开
gate仍需两部分的证据齐备。
本章目标
- 推导 RoPE、RMSNorm、SwiGLU、MQA/GQA、prefill/decode 和单 batch KV Cache。
- 证明增量 decode 与完整 causal forward 的 token/logit 等价。
- 理解 PagedKV 块表如何用"操作系统的虚拟内存"思路降低显存碎片。
- 理解 Tied Embedding 如何在 vocab 巨大时节省参数。
- 在同一设备、同一 prompt、warm-up 和重复次数下比较 cache、吞吐、首 token 延迟、权重大小和量化误差。
1. 公式与 shape
1.1 RoPE(旋转位置编码)
RoPE 对每个二维通道对应用位置相关旋转:
其中
关键性质:
前端类比:RoPE 就像 Vue Router 的路由参数,position 是 URL 参数,旋转矩阵是"参数化变换";位置变化 = 同一查询在路由表里被映射到不同 view。
1.2 RMSNorm(均方根归一化)
与 LayerNorm 区别:RMSNorm 没有
| 维度 | LayerNorm | RMSNorm |
|---|---|---|
| 中心化(去均值) | ✅ | ❌ |
| 缩放 | ✅ | ✅ |
| 可学习参数 | ||
| 计算复杂度 | 高(2 次 reduce) | 低(1 次 reduce) |
| 工程使用 | 原始 Transformer | LLaMA / PaLM / Mistral |
前端类比:LayerNorm = transform: translate(-50%, -50%) scale(...),RMSNorm = transform: scale(...) 只缩放不偏移。RMSNorm 速度约快 40%,性能几乎无差。
1.3 SwiGLU(前沿 FFN 激活)
LLaMA/PaLM/Mistral 全部使用 SwiGLU 替代 ReLU FFN:
其中
为什么需要三个矩阵:
工程细节:hidden_dim 通常设为 multiple_of=64(GPU 矩阵分块友好)。
前端类比:SwiGLU = React 的 useMemo + useState;gate(W1)决定要不要算,value(W2)算的是什么,最终输出(W3)是把门控后的结果应用。
1.4 Tied Embedding(权重共享)
input = token_ids [B, T]
embed = wte[input] # vocab_size × dim → [B, T, dim]
hidden = transformer(embed) # [B, T, dim]
logits = hidden @ wte.T # 共享 wte 权重 → [B, T, vocab_size]参数节省:GPT-2 small 是 50257 × 768 = 3860 万参数。lm_head 是 768 × 50257 = 又 3860 万。tied 之后省一半(约 3800 万参数,约占总参数 30%)。
为什么能行:语义上"把 token 映射成向量"和"把向量映射回 token 概率"是同一个映射——同一空间。
前端类比:Tied embedding = Redux store 的 state 和 selector 共享同一个 reducer;输入和输出经过同一份数据。
1.5 PagedKV / PagedAttention(vLLM 标准)
传统 KV Cache:
连续内存分配 seq_len × n_layers × n_heads × head_dim × 2(K+V)
→ 显存碎片,长短不一 batch 浪费
PagedKV(分页):
把 cache 切成固定大小的 block(如 block_size=16 tokens)
每个 request 用 block_table 维护逻辑→物理映射
→ 类似操作系统虚拟内存,按需分页,无碎片| 对象 | 传统连续 | PagedKV |
|---|---|---|
| 内存分配 | 一次性 | 按 block_size 增长 |
| 碎片 | 长短不一浪费 | 几乎为零 |
| 共享 prefix | 难 | 容易(共享 block table) |
| 复杂度 | O(seq_len) | O(n_blocks × block_size) |
前端类比:PagedKV = IndexedDB 的分页存储;逻辑块号映射到物理页帧。
1.6 推理阶段 shape
| 对象 | shape | 说明 |
|---|---|---|
| Q | (B, H_q, T_new, d) | decode 时 T_new 常为 1 |
| K/V cache | (B, H_kv, T_cache, d) | MQA/GQA 中 H_kv ≤ H_q |
| attention weights | (B, H_q, T_new, T_cache) | position 必须单调递增 |
| quantized weight | 与权重同 shape + scale | 记录 bits、scale、bytes 和误差 |
2. 关键概念与论文绑定
| 概念 | 阅读绑定 | 动手输出 / 口述题 | 明确边界 |
|---|---|---|---|
| RoPE 与位置连续性 | RoFormer | position 0 不变、二维范数保持;解释 cache append 为什么不能重置 position | 只验证教学实现的数值性质,不宣称覆盖所有 RoPE 变体 |
| RMSNorm | Root Mean Square Layer Normalization | 写出最后一维 RMS 归一化的 shape,解释它和 LayerNorm 是否去均值 | 这是原理/shape 练习,不等于生产 kernel 或训练稳定性结论 |
| SwiGLU | GLU Variants Improve Transformer | 写出三矩阵 FFN 的前向 shape;解释为什么 LLaMA/PaLM 全部采用 | 教学实现非生产 kernel;不替代 fused kernel 性能 |
| Tied Embedding | GPT-2 / LLaMA 架构 | 数参数:tied vs untied;解释 lm_head 复用 wte 的语义 | 不涵盖完整 lm_head bias 策略 |
| MQA / GQA | GQA | 用 (H_q, H_kv, D) 画 head 分组,验证 H_q % H_kv == 0 与重复顺序 | 小 fixture 的 head 对拍不代表大模型吞吐收益 |
| FlashAttention / PagedAttention | FlashAttention、vLLM/PagedAttention | 只画 IO/显存访问与分页 cache 的差异,说明为何不能用本地 NumPy 计时替代 kernel 对比 | 本章不写 CUDA/kernel,也不安装或验收 vLLM |
| GPTQ / AWQ | GPTQ、AWQ | 比较量化 bits、scale、bytes、误差字段;解释校准数据和权重误差的关系 | 当前对称量化 fixture 不是 GPTQ/AWQ 的复现 |
阅读后每个概念至少留下一个 shape 草图、一个故障预期和一个局限说明;上游代码只作为接口/论文索引,不能复制到 clean-room 提交。
数学桥接:第2章 → 第9章
第9章的六组扩展(RoPE、RMSNorm、SwiGLU、Tied Embedding、MQA/GQA、量化)全部建立在第2章的基础算子之上。每个桥接点对应一个你已经写过的函数:
RoPE 与 dot_product:第2章 §4 的 dot_product(a, b) = Σ a[i]·b[i] 衡量两个向量的相似度。RoPE 在二维平面上旋转 query 和 key:旋转保持范数不变,因此 ||q_rot|| = ||k_rot|| = 1(单位向量)。两个位置差为 Δ 的 token 经过相同旋转后,dot_product(q_rot, k_rot) = cos(Δθ)——点积天然编码了相对位置,而不是绝对位置。这就是为什么 RoPE 外推时,频率 ω_i 的选择比绝对位置编码更关键。
SwiGLU 与 relu / mlp_layer_forward:第2章 §8 的 relu(x) = max(0, x) 是最简门控——负值直接归零,正值无损通过。SwiGLU 将 ReLU 替换为 SiLU(x · sigmoid(βx)),形成软门控:负值不是完全阻断,而是按 sigmoid 概率衰减。从 shape 角度看,第2章 mlp_layer_forward(x, W, b, activation) 的一次调用对应 SwiGLU 的 W_gate 或 W_up 分支;两次独立前向后逐元素相乘,再经过 W_down 投影回 D 维。
RMSNorm 与 partial_derivative:第2章 §3 的 partial_derivative 是链式法则的标量版本。LayerNorm 反向需要三条路径(分子直达 / 均值路径 / 方差路径),RMSNorm 去掉均值路径后只剩两条——这就是它比 LayerNorm 更快的根本原因:少算一个均值,反向少一路梯度。第2章 §3 的 partial_derivative 对每个输入维度独立求值,正是 LayerNorm/RMSNorm 沿 feature 维归一化的核心思想。
量化与 entropy:第2章 §6 的 entropy(probs) 衡量一个分布的平均不确定性。INT8 量化把 16-bit 浮点权重压缩到 8-bit,每个权重的自信息从 16 位降到 8 位;INT4 进一步压缩。信息损失 = 量化前后的熵差。calibration(校准)就是用一小段代表数据估计每个 channel/block 的最优 scale/zero_point,让重建分布尽量接近原始分布——这与 mle_bernoulli 的"用样本估计参数"思想同构。
Tied Embedding 与 gradient_descent_step:第2章 §7 的 gradient_descent_step(param, grad, lr) 每次更新消耗 lr * grad 的步长。当 wte(token embedding)和 lm_head 共享同一权重矩阵时,梯度不需要在两处独立更新——参数量从 2 × V × D 降到 V × D,每次 backward 的梯度计算量和 optimizer state 都减半。Tie 的语义是"同一个 token 的输入表示和输出预测应该用同一个向量空间"。
MQA/GQA 与 dot_product:标准 MHA 每个 query head 独立计算 q_i · k_j,共 H_q × H_kv 组点积。GQA 让多个 query head 共享同一组 KV,点积计算量从 O(H_q × T × T) 降到 O(H_kv × T × T)。第2章 §4 的 dot_product 是 attention score 的核心——减少 score 计算就是减少 dot_product 调用次数,而不是改变点积本身的公式。
前端类比:RoPE = CSS transform: rotate(θ) + transform-origin: center——每个元素绕自己的中心旋转,旋转后的相对位置由角度差决定。SwiGLU = Vue 的 v-model 带 computed 中间层——不是直接把 input 映射到 output,而是经过一个可学习的"门控信号"决定多少信息通过。量化 = CSS image-rendering: pixelated——减少颜色深度,保留结构但损失细节。
3. 交互观察
交互:因果 Attention 权重
用玩具向量演示「当前 token 该看谁」。真实模型里 Q/K/V 是学出来的。
完整权重矩阵(行=query,列=key)
the
cat
sat
the
1.0
cat
0.5
0.5
sat
0.3
0.3
0.3
交互:KV Cache 增量解码
自回归生成时,历史 token 的 K/V 永远不变。缓存它们,把每步的 O(n) 重算降成 O(1)。
KV Cache(每步只追加一行)
| # | token | K | V | attn |
|---|---|---|---|---|
| 0 | <s> | [-0.56, -0.94, -0.14, -0.89] | [-0.10, -0.63, -0.05, -0.85] | |
| 1 | 我 | [0.78, -0.18, 0.69, 0.08] | [-0.26, -0.70, 0.67, 0.42] |
无 Cache(全量重算)Q,K,V 形状 [2, 4]累计行数 3 → O(n²)
有 Cache(增量)Q 形状 [1, 4],KV 读缓存 [2, 4]累计行数 2 → O(n)
输出(两条路径完全相同)[-0.2185, -0.6789, 0.4682, 0.0666]
$$\text{Attn}(q_n, K_{1:n}, V_{1:n}) = \text{softmax}\!\left(\frac{q_n K_{1:n}^\top}{\sqrt{d}}\right) V_{1:n}$$
教学要点:加速的代价是显存 —— cache 大小 = 2 × layers × heads × n × d_head。 这就是长上下文推理「显存爆炸」的根源,也是 MQA / GQA / PagedAttention 要解决的问题。
交互:RoPE 旋转位置编码
每一对维度 (d2k, d2k+1) 被当成复平面上的一个矢量,位置 pos 让它旋转 θ = pos × ωk。
| k | ωk | θ = pos·ω | 圈数 |
|---|---|---|---|
| 0 | 1.00e+0 | 3.000 | 0.48 |
| 1 | 7.50e-1 | 2.250 | 0.36 |
| 2 | 5.62e-1 | 1.687 | 0.27 |
| 3 | 4.22e-1 | 1.265 | 0.20 |
$$\omega_k = \text{base}^{-2k/d},\quad \begin{pmatrix} x'_{2k} \\ x'_{2k+1} \end{pmatrix} = \begin{pmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{pmatrix} \begin{pmatrix} x_{2k} \\ x_{2k+1} \end{pmatrix},\ \theta = \text{pos}\cdot\omega_k$$
教学要点:低维(k 小)旋转快 → 编码「近距离」;高维(k 大)旋转慢 → 编码「远距离」。 调大 base 会让所有频率变慢,这正是「NTK 插值」外推长上下文的原理。
交互:量化的显存 / 精度权衡
量化就是把 float 权重塞进更少的比特里。省下来的显存,代价是舍入误差。
显存占用(GB)
FP16
14.0
INT8
7.0
INT4 (朴素)
3.5
INT4 (GPTQ)
3.7
权重重建误差 RMSE(越低越好)
FP16
0.0000
INT8
0.0130
INT4 (朴素)
0.2224
INT4 (GPTQ)
0.0926
$$\text{scale} = \frac{\max|W_{\text{group}}|}{2^{b-1}-1},\qquad \hat{W} = \text{scale}\cdot\text{round}\!\left(\frac{W}{\text{scale}}\right)$$
教学要点(试试关掉分组量化 + 拉高离群值):单个离群值会撑大整个 tensor 的 scale, 让其余权重全被压成 0 —— 这就是朴素 INT4 崩掉的原因。GPTQ/AWQ 靠缩小分组把离群值隔离, 用 5% 的额外存储换回几乎无损的精度。
交互:LayerNorm vs RMSNorm
LLaMA 之后几乎所有模型都换成了 RMSNorm。差别只有一步:要不要减均值。
LayerNorm
- μ = 1.7000
- σ² = 3.1600
- σ = 1.7776
- y = γ·(x−μ)/σ + β
x0
0.169
x1
-1.519
x2
1.294
x3
-0.675
x4
0.731
参数量:10(γ + β),归约次数 2
RMSNorm
μ = 0(跳过)- mean(x²) = 6.0500
- RMS = 2.4597
- y = γ·x / RMS
x0
0.813
x1
-0.407
x2
1.626
x3
0.203
x4
1.220
参数量:5(只有 γ),归约次数 1
$$\text{LN}(x)=\gamma\odot\frac{x-\mu}{\sqrt{\sigma^2+\epsilon}}+\beta,\qquad \text{RMSNorm}(x)=\gamma\odot\frac{x}{\sqrt{\frac{1}{d}\sum_i x_i^2+\epsilon}}$$
教学要点:拖动「整体平移 c」。LayerNorm 输出完全不动(平移不变), RMSNorm 输出会跟着变 —— 它牺牲了平移不变性。 实践证明这个性质没什么用,换来的是省掉一次全量归约(GPU 上约 10~15% 的 norm 层加速)和一半参数。
交互:SwiGLU 门控前馈层
普通 FFN 是「过一次激活」。SwiGLU 是「一路算内容,另一路算开关,然后逐元素相乘」。
Swish(xW) — 门(虚线)
xV — 内容(点线)
SwiGLU = 门 ⊙ 内容
ReLU(xV) — 老方案
ReLU FFN(d=4096):hidden = 16384,2 个矩阵,参数 134M
SwiGLU FFN:hidden = 10923(缩到 8/3 d),3 个矩阵,参数 134M
$$\text{Swish}_\beta(x)=x\cdot\sigma(\beta x),\qquad \text{SwiGLU}(x)=\text{Swish}(xW)\odot(xV),\qquad \text{FFN}(x)=\text{SwiGLU}(x)\,W_2$$
教学要点:把 β 拉到 8,Swish 几乎就是 ReLU —— 但 SwiGLU 曲线依然是平滑的、可正可负的, 因为它是两条曲线相乘。这种「乘法交互」让 FFN 能表达更复杂的特征选择, 代价是多一个矩阵(所以 LLaMA 把 hidden 从 4d 缩到 8/3·d 来保持参数量持平)。
这些组件覆盖:causal visibility(温习第7章)、KV cache 增量解码、RoPE 频率对比、量化误差、归一化方式差异、SwiGLU 门控信号。
4. 动手任务与验收(从零实践)
4.1 必做(核心)
- RoPE:实现
apply_rope,验证 position 0 不改变输入且每个旋转保持二维范数。 - GQA:实现 KV head 分组、单调 cache append、bounded context 和逐 token decode。
- PagedKV:实现 block table append、读取和清空;验证跨请求 prefix 共享。
- SwiGLU:实现
swiglu_forward与swiglu_backward;对拍 torch 参考实现。 - RMSNorm:实现前向 + 反向;与 LayerNorm 做数值与参数数对比。
- Tied Embedding:实现
TiedEmbedding;对比参数数。
4.2 必做(验证)
对同一随机权重和 token 序列分别运行 full forward 与 incremental path,比较每一步 logits。
运行当前入口:
bashPYTHONPATH=python python -m pytest \ python/tests/test_transformer_contract.py \ python/tests/test_align_kv.py \ python/tests/test_quantization.py \ python/tests/test_diagnostics.py \ python/tests/test_w07_algorithms.py -q
当前 transformer.py、kv_cache.py 和 post_training.py 已有本地实现和测试;swiglu.py、rmsnorm.py、tied_embedding.py、paged_kv.py 是本章新增的扩展模块。diagnostics.py 的 benchmark_callable 补齐了 warm-up/repeat 计时。对 T=32, head_dim=16 的本地实测使用 warm-up 5、重复 20,设备标识为 Darwin-arm64-python3.14.6-cpu:full attention 的 p50/p95 为 0.020354/0.021179 ms,incremental attention 的 p50/p95 为 0.367562/0.386408 ms。这些数字只描述本次设备和实现,不能外推跨硬件性能或吞吐收益;真实权重格式和 context overflow 行为仍需单独 evidence。
4.3 必做(写作)
- 为上述七组概念各写一条"论文主张 → 本地可观察量 → 不可推出的结论"记录;至少包含 RoPE position、GQA head grouping、cache/full 等价、量化误差、SwiGLU gate、RMSNorm 中心化、PagedKV 碎片率。缺少实际运行值时填写
<未测量>,不要从论文或别人的 benchmark 补数。
5. 故障注入与预期信号
| 注入 | 预期失败信号 | 修复后证据 |
|---|---|---|
| K/V head repeat 错位 | GQA 输出与 MHA 对照不一致 | head grouping fixture allclose |
| cache 超过 context 未定义策略 | 形状增长无界或读取旧位置 | bounded-context 失败策略可解释 |
| 量化 scale 为零未处理 | 全零权重产生 NaN/Inf | zero-weight test finite |
SwiGLU 把 * 写成 + | 反向传播梯度符号错误 | torch 参考实现对拍通过 |
RMSNorm eps=0 | 除零 → NaN | eps=1e-5 后 finite |
Tied embedding 漏掉 detach | 反向时梯度回流到 wte 两次 | 梯度累加正确 |
| PagedKV block_id 越界 | 写入越界或读取到旧 block | 边界测试 fail-closed |
6. 论文与延伸
- RoFormer: Enhanced Transformer with Rotary Position Embedding(Su 等,2021)
- GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints(Ainslie 等,2023)
- FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness(Dao 等,2022)
- PagedAttention: Virtual Memory for LLM Serving(Kwon 等,2023,vLLM)
- Root Mean Square Layer Normalization(Zhang 等,2019)
- GLU Variants Improve Transformer(Shazeer,2020)
完整索引见 参考 / 必读论文。
7. 参考实践绑定
- Karpathy 的 nanochat / llama2.c:只观察现代推理、cache 和端到端约束;把一个观察问题改写成自己的 shape 草图或失败题。
- Raschka 的 LLMs-from-scratch Chapter 4 的 KV cache/attention 选读:只对照接口和张量流,运行本章自己的 full/cache 对拍。
- 中文复习入口:LLMs-from-scratch-CN 与 dive-into-llms;只用于术语和问题导航,不把其中的 benchmark、代码或正文复制成 evidence。
动手实验
证明两件事:增量 KV Cache 解码的输出与一次性全量前向在数值容差内一致;RoPE 旋转在 position 0 处保持恒等、向量二维范数不变。把这两点用作后续量化与吞吐实验的对照基准。
环境准备
bash
cd <仓库根>
export PYTHONPATH="$PWD/python"命令与预期输出
上方 4.2 节已经跑过包含 test_align_kv.py、test_quantization.py 在内的完整入口;这里单独给出这两个文件的独立判定信号:
bash
python -m pytest python/tests/test_align_kv.py \
python/tests/test_quantization.py -qtext
............................. [ 100% ]
2 passed in 1.86s
判定信号:
逐 token 增量解码 logits 与全量前向差距 < atol=1e-5
KVCache 每 append 一次长度严格加 1、shape 与 head 对齐
RoPE position=0 等价于恒等变换;任意位置的二维范数与旋转前相等
量化只在显式反量化后才参与损失计算概念图
图:第9章推理优化 + 模型结构优化双路径 — 第9章的两条优化线并行作用于 TinyGPT:推理线(KV Cache 缓存历史 K/V 避免重算 + RoPE 把位置编码内嵌到 Q/K 旋转中,使 attention 天然感知相对位置)和结构线(RMSNorm 去掉 LayerNorm 的偏置项、SwiGLU 用 gate × up 门控替代 ReLU)。两条线汇聚为优化版 TinyGPT Block,输出给后续第10章微调、第13章 Agent 推理和第19章 Capstone checkpoint 恢复。
故障注入清单
| 故障 | 表现 | 修复 |
|---|---|---|
| append 时重置 position 指针 | RoPE 相位错乱、输出分布漂移 | 维护单调递增的绝对位置计数器 |
| KV 缓存未按 head 维对齐 | 矩阵乘 shape 报错或静默错位 | 缓存维度顺序固定为 批 头 序 维 |
| 量化后忘记反量化 scale | 输出尺度整体偏移、loss 假低 | 算前反量化、算后再量化落盘 |
| 用单一小 fixture 计时宣称吞吐提升 | 结论不成立、无法泛化 | 至少三个长度 + 三种 batch 重复测量 |
8. 前端/Agent 迁移
- KV Cache ≈ 增量渲染缓存:命中旧状态可减少重复计算,但 cache key、position、失效和容量边界必须是显式契约。
- PagedKV ≈ IndexedDB 分页存储:逻辑地址映射物理页帧;前端做无限滚动列表时可借鉴这种"虚拟化"思路。
- Tied Embedding ≈ Redux store 与 selector 共享 reducer:输入和输出经过同一份语义空间。
- SwiGLU ≈ React useMemo 门控:gate 决定要不要计算,value 是计算内容。
- Agent 上下文压缩 / 记忆:必须区分"可复用事实"与"当前会话状态",不能无条件复用旧结果。
9. 口述与自测(不看资料,5–10 分钟)
- 解释 RoPE 如何在二维对上旋转、为何 position 0 不变;再说明 prefill 与逐 token decode 的 position/cache 更新如何保持等价。
- 以同一模型和环境为前提,比较 MQA/GQA、FlashAttention/PagedAttention 与量化分别优化的瓶颈;指出本地 NumPy 计时或体积数字各不能证明什么。
- 用一句中文解释 SwiGLU 为什么需要三个矩阵,不是两个。
- 写出 PagedKV 的 block_table 在跨请求 prefix 共享场景下如何工作。
- 解释为什么 RMSNorm 比 LayerNorm 更快,且参数更少。
- 用第2章的
dot_product解释 RoPE 旋转后的二维点积为什么只依赖位置差 Δ;两个位置差为 π/2 的向量点积为 0,说明它们在注意力空间中正交。 - 用第2章的
relu和mlp_layer_forward对比 SwiGLU 与 ReLU 的门控机制:ReLU 是硬门控(负值归零),SwiGLU 的 SiLU 是软门控(负值仍有小信号);SwiGLU 的三矩阵对应两次mlp_layer_forward(gate + up)加一次逐元素相乘,再经W_down投影。 - 用第2章的
entropy解释 INT8/INT4 量化的信息损失:量化后每个权重的自信息从 16-bit 降到 8-bit 或 4-bit,熵减少意味着信息损失;校准(calibration)就是用 MLE 估计最优 scale/zero_point。
10. 资源 / 成本 / 隐私
RoPE、cache 等价性、SwiGLU 对拍、RMSNorm 和 int8 误差可在本地 CPU/MPS 完成;性能数字只对记录的设备有效,预计 gross cost 为 0。不要下载未核验权重或把真实 prompt 写进 benchmark。
Evidence
仓库当前机器证据(只读快照)
evidence/module-manifest-v1.json 中 09.evidence 指向当前文件:evidence/09-runtime-v1.json。这是当前 checkout 的脱敏机器运行记录,只覆盖该 JSON 记录的命令、指标、产物和已知失败;它不是学习者提交,也不能推出学习者已完成本章。
学习者提交模板(待填写,不是当前机器证据)
复制下面模板并填写自己的真实运行结果。所有 <...> 都是未填写状态;actual 和 artifacts 尤其不能被当作已运行或已通过。artifacts 必须替换为本次提交中真实存在的仓库相对路径。
yaml
schema: learn-llm.evidence.v1
module: 09-inference
commit: <learner-commit-sha>
verified_at: <iso-date>
environment: <sanitized-python-device>
seed: 7
commands:
- PYTHONPATH=python python -m pytest python/tests/test_transformer_contract.py python/tests/test_align_kv.py python/tests/test_quantization.py -q
- PYTHONPATH=python python -m pytest python/tests/test_diagnostics.py python/tests/test_w07_algorithms.py -q
metrics:
- name: cache_full_max_logit_error
expected: <versioned-tolerance>
actual: <recorded-value>
- name: quantized_weight_bytes
expected: <versioned-bound>
actual: <recorded-value>
- name: generation_fixture_match
expected: <versioned-threshold>
actual: <recorded-value>
- name: benchmark_warmup_repeats
expected: "5/20"
actual: <recorded-warmup-and-repeat-count>
- name: full_attention_p50_ms
expected: measured-on-device
actual: <recorded-value>
- name: incremental_attention_p95_ms
expected: measured-on-device
actual: <recorded-value>
- name: swiglu_forward_max_error_vs_torch
expected: <versioned-tolerance>
actual: <recorded-value>
- name: rmsnorm_no_mean_centered
expected: "mean(out) !=_0"
actual: <recorded-value>
- name: tied_param_count_savings
expected: "≈30%"
actual: <recorded-value>
- name: pagedkv_block_table_fragmentation_rate
expected: <versioned-bound>
actual: <recorded-value>
artifacts:
- <learner-repo-relative-artifact-path>
cost:
gross_usd: 0
credit_usd: 0
licenses:
- source: <source>
version: <version>
license: <license>
attribution: <attribution>
redistribution: <redistribution>
known_failures:
- <sanitized-failure-or-none>若没有同环境 baseline、warm-up/重复次数、cache/full 对拍、量化误差和新增算法对拍,本章只能标记为 gate;本地 benchmark 不替代这些 learner-side gates。