Skip to content

第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 对每个二维通道对应用位置相关旋转:

[x2ix2i+1]=[cosθp,isinθp,isinθp,icosθp,i][x2ix2i+1]

其中 θp,i=pωiωi=100002i/d

关键性质RoPE(q,m),RoPE(k,n) 只依赖 (mn)。这意味着相对位置编码是"免费送"的——LLaMA/Qwen/ChatGLM 全部依赖这一性质。

前端类比:RoPE 就像 Vue Router 的路由参数,position 是 URL 参数,旋转矩阵是"参数化变换";位置变化 = 同一查询在路由表里被映射到不同 view。

1.2 RMSNorm(均方根归一化)

RMSNorm(x)i=γiximean(x2)+ϵ

与 LayerNorm 区别:RMSNorm 没有 β 平移项,不做 mean centering(μ=0),只缩放。

维度LayerNormRMSNorm
中心化(去均值)
缩放
可学习参数γ,βγ
计算复杂度高(2 次 reduce)低(1 次 reduce)
工程使用原始 TransformerLLaMA / PaLM / Mistral

前端类比:LayerNorm = transform: translate(-50%, -50%) scale(...),RMSNorm = transform: scale(...) 只缩放不偏移。RMSNorm 速度约快 40%,性能几乎无差。

1.3 SwiGLU(前沿 FFN 激活)

LLaMA/PaLM/Mistral 全部使用 SwiGLU 替代 ReLU FFN:

SwiGLU(x,W1,W2,W3)=(SiLU(xW1)(xW2))W3

其中 SiLU(z)=zσ(z)(Sigmoid Linear Unit)。

为什么需要三个矩阵W1 产生"门控信号"(gate),W2 产生"value",W3 重新压回原维度。中间的逐元素乘 让网络学习"什么信息该保留"。

工程细节:hidden_dim 通常设为 234d,再向上对齐到 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 与位置连续性RoFormerposition 0 不变、二维范数保持;解释 cache append 为什么不能重置 position只验证教学实现的数值性质,不宣称覆盖所有 RoPE 变体
RMSNormRoot Mean Square Layer Normalization写出最后一维 RMS 归一化的 shape,解释它和 LayerNorm 是否去均值这是原理/shape 练习,不等于生产 kernel 或训练稳定性结论
SwiGLUGLU Variants Improve Transformer写出三矩阵 FFN 的前向 shape;解释为什么 LLaMA/PaLM 全部采用教学实现非生产 kernel;不替代 fused kernel 性能
Tied EmbeddingGPT-2 / LLaMA 架构数参数:tied vs untied;解释 lm_head 复用 wte 的语义不涵盖完整 lm_head bias 策略
MQA / GQAGQA(H_q, H_kv, D) 画 head 分组,验证 H_q % H_kv == 0 与重复顺序小 fixture 的 head 对拍不代表大模型吞吐收益
FlashAttention / PagedAttentionFlashAttentionvLLM/PagedAttention只画 IO/显存访问与分页 cache 的差异,说明为何不能用本地 NumPy 计时替代 kernel 对比本章不写 CUDA/kernel,也不安装或验收 vLLM
GPTQ / AWQGPTQAWQ比较量化 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 的选择比绝对位置编码更关键。

scoreij=qikjdkRoPE(RΔqi)(RΔkj)dk=cos(Δθ)dk

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_gateW_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-modelcomputed 中间层——不是直接把 input 映射到 output,而是经过一个可学习的"门控信号"决定多少信息通过。量化 = CSS image-rendering: pixelated——减少颜色深度,保留结构但损失细节。

3. 交互观察

交互:因果 Attention 权重

用玩具向量演示「当前 token 该看谁」。真实模型里 Q/K/V 是学出来的。

the
32.6%
cat
33.6%
sat
33.8%

完整权重矩阵(行=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(每步只追加一行)

#tokenKVattn
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=0 k=1 k=2 k=3
kωkθ = pos·ω圈数
01.00e+03.0000.48
17.50e-12.2500.36
25.62e-11.6870.27
34.22e-11.2650.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. μ = 1.7000
  2. σ² = 3.1600
  3. σ = 1.7776
  4. y = γ·(x−μ)/σ + β
x0
0.169
x1
-1.519
x2
1.294
x3
-0.675
x4
0.731

参数量:10(γ + β),归约次数 2

RMSNorm

  1. μ = 0(跳过)
  2. mean(x²) = 6.0500
  3. RMS = 2.4597
  4. 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 必做(核心)

  1. RoPE:实现 apply_rope,验证 position 0 不改变输入且每个旋转保持二维范数。
  2. GQA:实现 KV head 分组、单调 cache append、bounded context 和逐 token decode。
  3. PagedKV:实现 block table append、读取和清空;验证跨请求 prefix 共享。
  4. SwiGLU:实现 swiglu_forwardswiglu_backward;对拍 torch 参考实现。
  5. RMSNorm:实现前向 + 反向;与 LayerNorm 做数值与参数数对比。
  6. Tied Embedding:实现 TiedEmbedding;对比参数数。

4.2 必做(验证)

  1. 对同一随机权重和 token 序列分别运行 full forward 与 incremental path,比较每一步 logits。

  2. 运行当前入口:

    bash
    PYTHONPATH=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.pykv_cache.pypost_training.py 已有本地实现和测试;swiglu.pyrmsnorm.pytied_embedding.pypaged_kv.py 是本章新增的扩展模块。diagnostics.pybenchmark_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 必做(写作)

  1. 为上述七组概念各写一条"论文主张 → 本地可观察量 → 不可推出的结论"记录;至少包含 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/Infzero-weight test finite
SwiGLU 把 * 写成 +反向传播梯度符号错误torch 参考实现对拍通过
RMSNorm eps=0除零 → NaNeps=1e-5 后 finite
Tied embedding 漏掉 detach反向时梯度回流到 wte 两次梯度累加正确
PagedKV block_id 越界写入越界或读取到旧 block边界测试 fail-closed

6. 论文与延伸

完整索引见 参考 / 必读论文

7. 参考实践绑定

  • Karpathy 的 nanochat / llama2.c:只观察现代推理、cache 和端到端约束;把一个观察问题改写成自己的 shape 草图或失败题。
  • Raschka 的 LLMs-from-scratch Chapter 4 的 KV cache/attention 选读:只对照接口和张量流,运行本章自己的 full/cache 对拍。
  • 中文复习入口:LLMs-from-scratch-CNdive-into-llms;只用于术语和问题导航,不把其中的 benchmark、代码或正文复制成 evidence。

动手实验

证明两件事:增量 KV Cache 解码的输出与一次性全量前向在数值容差内一致;RoPE 旋转在 position 0 处保持恒等、向量二维范数不变。把这两点用作后续量化与吞吐实验的对照基准。

环境准备

bash
cd <仓库>
export PYTHONPATH="$PWD/python"

命令与预期输出

上方 4.2 节已经跑过包含 test_align_kv.pytest_quantization.py 在内的完整入口;这里单独给出这两个文件的独立判定信号:

bash
python -m pytest python/tests/test_align_kv.py \
  python/tests/test_quantization.py -q
text
.............................                                     [ 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章的 relumlp_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.json09.evidence 指向当前文件:evidence/09-runtime-v1.json。这是当前 checkout 的脱敏机器运行记录,只覆盖该 JSON 记录的命令、指标、产物和已知失败;它不是学习者提交,也不能推出学习者已完成本章。

学习者提交模板(待填写,不是当前机器证据)

复制下面模板并填写自己的真实运行结果。所有 <...> 都是未填写状态;actualartifacts 尤其不能被当作已运行或已通过。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。

私有学习站 · 原理从零构建 · 勿提交个人隐私或密钥