附录 C — 交互实验索引

全书 15 个交互实验,都在 experiments/ 下,双击就能打开。

不联网、不需要服务器、不需要装任何东西——每个页面是一个自包含的 HTML, 数据已经嵌在页面里。

C.1 怎么用

三种打开方式:

  1. 直接双击 experiments/exp-1-1.html
  2. 从正文点链接(HTML 版每一节都有入口)
  3. 在 PDF 里看静态快照(assets/exp-static/,每个实验一张)

每个实验页都有三个部分:

  • 控件(滑块、按钮)——改参数
  • 画布——实时重算
  • 结论框——告诉你”该看什么”

页面里的数字全是现算的,不是预先录好的动画。 所以你可以把参数拖到书里没写过的位置,看它怎么变。

注记为什么 PDF 里是静态图

PDF 装不下交互。

每个实验在 PDF 里配一张快照,选的是”最能说明问题的那一帧”, 不是初始帧。比如实验 6-2 的快照是 b=0.10、输出已经塌缩到 54% 的那一帧。

想看完整的,回到 HTML 版。

C.2 总表

编号 章 一句话 主要控件 数据来源
1-1 1 缩放因子与 softmax 饱和 d_k、位置数、种子 现算
1-2 1 真实模型学到的注意力 层、头、是否缩放 预计算(42 万参数模型)
1-3 1 改 Q,K,V,看注意力怎么变 九个数字、五个预设 现算(3 token)
3-1 3 温度、top-k、top-p 步数、\tau、k、p、顺序 真实模型分布
3-2 3 先截断还是先调温 \tau、p、步数 真实模型分布
3-3 3 KV 缓存省下了什么 生成长度、提示词长度 现算
4-1 4 逐层逐头看注意力图 层、头 真实权重 + 随机对照
4-2 4 消融实验室 点头屏蔽 预计算消融
5-1 5 前向过程 t 现算(700 点)
5-2 5 反向过程:加不加噪声 t、并排开关 预计算轨迹
5-3 5 噪声调度 调度、T、\beta 端点 现算
6-1 6 采样器对比 采样器、步数 现算(1D 高斯)
6-2 6 偏差诊断 偏差 b、步数、\eta 现算
7-1 7 CFG 引导强度 类别、w 预计算(每 w 600 点)
8-1 8 绑定实验室 编码器、类别 预计算(每类 300 点)

15 个实验里,7 个是”现算”、8 个是”预计算”。

区别在数据量:现算的页面只需要几十个参数; 预计算的需要真实模型的采样结果(几十万个浮点数),只能离线算好再嵌进去。

C.3 逐个说明

C.3.1 实验 1-1 缩放因子与 softmax 饱和

拖 d_k 从 4 到 512。

看三件事同时发生:logits 的分布变宽、softmax 变尖、梯度(最大概率处的斜率)塌向 0。

结论:\sqrt{d_k} 不是”让数值好看”,是”让 softmax 留在还能学的位置”。 d_k 小时看不出差别——这就是正文案例里”小模型上没事”的原因。

C.3.2 实验 1-2 真实模型学到的注意力

一个真实的 2 层 4 头模型(425,984 参数),在真实唐诗上训练后的注意力。

可以切层、切头,也可以切到”不缩放”的那个模型做对照。

看什么:正常的模型有清晰的局部模式(盯前一位、盯句首); 不缩放的模型注意力摊平,几乎没有结构。

数据来源:教师机离线算好,页面只做回放。

C.3.3 实验 1-3 改 Q,K,V,看注意力怎么变

3 个 token、2 维,九个数字全部可改。

五个预设:正文那个例子、只让自己可见、所有位置一样、制造一个尖锐分布、随机。

结论:注意力前向传播没有黑箱——改一个数,打分、softmax、输出立刻跟着变。 你可以亲手制造”注意力退化成平均”的那个 bug。

C.3.4 实验 3-1 采样实验室

四个滑块:生成到第几步、温度 \tau、top-k、top-p。

还有一个关键的开关:先截断后调温 / 先调温后截断。

结论:同样一组 (\tau,p),只换顺序,候选集能差几十倍。 这不是理论上的差别,是你会不会采到那个词。

C.3.5 实验 3-2 顺序陷阱

3-1 的”顺序”那一个维度,单独放大。

固定 \tau 和 p,只换先后,看候选集怎么变。

结论:top_p 和 temperature 不是可交换的操作。 很多实现出错就出在这个顺序上。

C.3.6 实验 3-3 KV 缓存省下了什么

每一格代表”某一步里某个位置被重新算了一次”。

拖生成长度 N 和提示词长度 P,看格子怎么被涂满。

结论:没有缓存时,第 i 步要重算前 i 个位置,总量是 O(N^2); 有缓存时是 O(N)。 提示词越长,省得越多。

C.3.7 实验 4-1 逐层逐头看注意力图

左边是训练好的模型,右边是随机初始化的同结构模型。

切层(0/1)、切头(H0–H3)。

结论:随机模型也会画出”看起来有结构”的图。 没有对照,你分不清”学到了”和”初始化碰巧”。

C.3.8 实验 4-2 消融实验室

点一个头把它屏蔽掉,看验证集 loss 和生成文本怎么变。

结论:有些头一屏蔽 loss 就涨,有些头屏蔽了几乎没变化。 “这个头很重要”必须用消融证明,不能看注意力图猜。

C.3.9 实验 5-1 前向过程实验室

700 个点,同一个 x_0、同一串噪声,拖 t 从 0 到 1000。

结论:双月分布逐渐溶成标准正态。 注意 t=300 时形状已经看不出来,但 \bar\alpha_t 还有 0.40—— “看起来是噪声”和”数学上是噪声”不是一回事。

C.3.10 实验 5-2 反向过程实验室

同一个模型、同一批初始噪声,只改”反向每一步加不加 \sigma_t z“。

并排回放两条轨迹。

结论:加噪声的轨迹每一步都在抖,不加的是一条平滑曲线。 两条轨迹的终点分布相同,路径完全不同—— 这就是 DDPM 和 DDIM 的关系。

C.3.11 实验 5-3 噪声调度实验室

改调度类型(线性/余弦)、步数 T、\beta 的起点和终点。

看 \bar\alpha_t 和 log-SNR 的形状。

红色区域是”信号已被破坏殆尽”的区间。

结论:线性调度在高噪声端浪费了很多步; 余弦调度把步子更多分给”信号还在”的区间。

C.3.12 实验 6-1 采样器对比实验室

同一个解析问题(1D 高斯 + 完美去噪器),比较 DDPM、DDIM、Heun。

拖步数,看精度随 NFE 怎么变。

结论:同样的 NFE 下,二阶的 Heun 明显好于一阶的 DDIM, DDIM 好于 DDPM。 “步数少”不等于”便宜”——要统一到 NFE。

C.3.13 实验 6-2 偏差诊断实验室

给完美去噪器注入可控偏差 b,看 DDIM 会不会发散。

结论和直觉相反:均匀偏差不会让 DDIM 发散,只会让输出塌缩到目标的 1-b 倍。 真正让 DDIM 崩的,是”误差随 t 变化”或”模型把误差放大”。

这一条推翻了正文初稿的一个假设——实验做出来才发现。

C.3.14 实验 7-1 CFG 引导强度实验室

选一个类(外月/内月),拖 w 从 0 到 40。

看两件事同时发生:类内越来越纯,样本越来越少、越来越散。

结论:准确率在 w=3 就饱和,之后它不再告诉你任何事; 标准差和越界率继续恶化。 “准确率”是一个会骗人的指标。

C.3.15 实验 8-1 绑定实验室

选编码器(均值池化/交叉注意力)、选类别(RR/RB/BR/BB)。

每个点是一个生成样本,画在”左点 y × 右点 y“平面上。

结论:均值池化在 RB 和 BR 上给出镜像对称的两团,各占一半—— 因为它拿到的条件向量完全相同。 交叉注意力只落在目标象限。

重要第 3 步是实验 8-1 的全部意义

同一个词袋、两种顺序、同一个分布。

这不是”生成质量差”,是”条件里没有信息”。 看到镜像对称的两团,就是绑定失败的指纹。

C.4 三条推荐路线

C.4.1 六十分钟:只看”会骗人的指标”

时间 实验 得到什么
0–15 7-1 准确率饱和,标准差爆炸
15–30 8-1 0.55 不是”一半对”,是零信息
30–45 6-2 均匀偏差不会发散,只会塌缩
45–60 4-1 随机模型也有”结构”

四个实验的共同点:都推翻了”看一个数就够”的直觉。

C.4.2 半天:按章走一遍

上午:1-1 → 1-3 → 3-1 → 3-2 → 3-3(上篇的机制)

下午:5-1 → 5-2 → 5-3 → 6-1 → 7-1(下篇的机制)

每个 20–30 分钟,够你把参数拖到书里没写过的位置。

C.4.3 一学期:把实验当作业的起点

每个实验页都可以改。 把”只读”变成”改写”,是这份材料真正的用法—— 附录 D 的作业就是按这个思路设计的。

C.5 自己改一个实验

每个实验页的结构都一样:

<h1>实验 X-Y 标题</h1>
<p class="sub">一句话说明</p>
<div class="row">
  <div class="ctl">  ← 控件
    <label>参数</label>
    <input type="range" ...>
  </div>
  <canvas id="cv"></canvas>  ← 画布
</div>
<div class="task">动手:...</div>
<script>
const D = {...};       ← 数据
function draw() {...}  ← 重绘
</script>

改三处就能做出一个新实验:

  1. 换数据:改 const D = {...}
  2. 换算公式:改 draw() 里的计算
  3. 换结论:改 .task 里那段话
警告改完必须重新截图

PDF 里的快照不会自动更新。

改完 HTML,要跑:

python3 qa/snap_experiments.py exp-8-1

它用无头浏览器截图,按 qa/snap_experiments.py 里 SPECS 的尺寸和 URL 参数。 如果新实验的”最能说明问题的那一帧”不是默认帧, 在 SPECS 里给它加一个 URL 参数。

还有一个隐藏要求:页面加载完成后要设 document.body.dataset.qaReady = "1"。 这是给自动检查用的——没有它,截图脚本不知道什么时候算”画完了”。