时长 8 小时:§1 搜索式解码(1h)· §2 采样式解码(1h)· §3 上下文长度(0.5h)· §4 提示与上下文学习(1.5h)· §5 思维链与自洽性(1.5h)· §A–§C(0.5h)· 实验(2h)
对应正课 Lecture 3 的后半:上下文长度、温度、采样策略、提示与上下文学习、思维链、自洽性。Week 0 Day 5 §5 已经讲过 temperature、top-k、top-p 和 nucleus 的 off-by-one,今天在它上面继续。
配套代码
w2day3_checks.py(纯 numpy):贪心 vs beam、长度偏置与归一化、搜索的诅咒、三种截断策略(top-k、top-p、min-p)在不同分布与温度下的表现、T→0 与 top-p→0、重复惩罚的写法、线性注意力 = 一步梯度下降、上下文校准、自洽性投票的成立条件§B 参考实现(
code/目录):w2d3_decoding.py(在一个小型开放模型上:手写各种解码并与 HFgenerate对拍、温度 × top-p 网格实验、少样本的顺序敏感性与校准、GSM8K 上直接回答 vs 思维链 vs 自洽性)。今天的目标:能说清每个解码旋钮调大调小会怎样;能用三种机制解释上下文学习,并说出”顺序敏感”揭示了什么;能说清思维链为什么有效、为什么小模型上失效;能算出自洽性投票什么时候有用。
§0 今天的主线
预训练给了我们一个条件分布 。今天讨论的全部内容,都是在不改权重的前提下,怎样从这个分布里拿到更好的输出:
怎么从分布里取 token 解码策略 §1–§2
给模型看多少上下文 上下文长度 §3
在上下文里放什么 提示与上下文学习(ICL) §4
让模型先写中间步骤 思维链(CoT) §5.1–5.2
采样多次再汇总 自洽性(self-consistency) §5.3
后两项是 Lecture 6”推理时计算扩展”的起点:用推理时多花的算力换准确率。
今天要用到的旧结论:temperature、top-k、top-p 与 nucleus 的右移(Week 0 Day 5 §5.2–§5.3);beam search 为什么在开放生成上失效(Week 0 Day 5 §5.4);KV Cache 与长上下文的代价(Week 1 Day 4 §1.4);softmax 的温度(Week 0 Day 4 §5.3);线性注意力(Week 1 Day 6 §2)。
§1 搜索式解码:找概率最大的序列
1.1 贪心与 beam search
贪心:每一步选概率最大的 token。简单、确定,但局部最优不等于全局最优。w2day3_checks.py 第 1 组构造了一个两步的玩具模型:第一步 A(0.6)比 B(0.4)更可能,但 A 之后的分布很平,B 之后几乎确定是 x。
| 方法 | 结果 | 整句概率 |
|---|---|---|
| 贪心 | (A, x) | 0.240 |
| beam = 2 | (B, x) | 0.360 |
| 穷举 | (B, x) | 0.360 |
beam search 每一步保留累积 log 概率最高的 个前缀,给后面”反悔”的机会。代价是 倍的计算和 KV Cache。
1.2 长度偏置与长度归一化
整句概率是每步概率的连乘,每一项都小于 1,越长越吃亏。第 2 组:
| 候选 | GNMT | GNMT | ||
|---|---|---|---|---|
| 短(3 个 token) | −1.001 | −0.334 | −0.842 | −0.751 |
| 长(8 个 token) | −1.368 | −0.171 | −0.860 | −0.631 |
按总 log 概率,短句胜;按每 token 平均,长句胜(它每一步都更自信)。GNMT 的长度惩罚 在两者之间插值, 是一个要调的旋钮。翻译里 beam search 必须配长度归一化,否则系统性地偏爱过短的译文。HF 的 length_penalty 就是这个指数。
1.3 搜索的诅咒
如果每一步都有一定概率直接结束,空输出可能是整个空间里概率最大的序列。第 3 组:每步有 25% 概率结束时,空输出的概率 0.25,比任何一个具体的长句都大(长度 5 的最可能句子只有 0.035)。
Stahlberg & Byrne(2019)在神经机器翻译上做了精确搜索,发现对相当比例的句子,最优译文就是空串。beam search 效果好,部分恰恰因为它搜不准。这和 Week 0 Day 5 §5.4 的结论互相印证:最大化概率不等于得到最好的文本。
1.4 什么时候用搜索
封闭式任务(翻译、摘要、代码补全里”唯一正确答案”的部分、结构化抽取):好答案的分布尖锐,概率最大和质量最好高度重合,用贪心或 beam。开放式任务(对话、写作):用采样(§2)。推理任务:通常用采样 + 汇总(§5.3)。
§2 采样式解码
2.1 三种截断策略的对比
截断策略的共同目的:砍掉分布的长尾(Week 0 Day 5 §5.3:长尾里每个 token 概率很低,但加起来不低,采到一个就可能跑偏)。区别在于”砍多少”怎么定:
- top-k:固定保留 个。
- top-p:保留累积概率刚到 的最小集合。
- min-p(Nguyen et al. 2024):保留概率 的 token,阈值跟着最大概率走。
第 4 组(词表 1000):
| 分布 | 最大概率 | top-k=50 | top-p=0.9 | min-p=0.1 |
|---|---|---|---|---|
| 平坦 | 0.004 | 50 | 790 | 977 |
| 尖锐 | 0.667 | 50 | 3 | 3 |
| 尖锐, | 0.075 | 50 | 831 | 3 |
| 尖锐, | 0.019 | 50 | 865 | 4 |
读这张表:top-k 不看分布形状。top-p 会自适应,但高温时会失控:温度把分布拉平后,前几个 token 的累积概率不到 0.9,top-p 一下子放进 800 多个长尾 token。min-p 的阈值随 一起下降,仍然只保留少数几个相对最可能的 token( 时 4 个)。这是 min-p 被提出的动机:想用高温增加多样性,又不想引入垃圾 token。
顺序很重要:温度是在截断之前还是之后应用,结果不同。HF 的 generate 按固定顺序依次应用各个 logits 处理器,两个库的默认顺序不一定相同,对拍时要注意。
2.2 “temperature = 0 和 top-p = 0 是一回事吗?”
学习计划里的自测题。数学上两者的极限都是贪心:第 5 组, 从 1 降到 0.01,最大概率从 0.459 升到 1.000;top-p = 0 时”累积和刚跨过 0 的最小集合”只有第一个 token。
但工程上不完全一样:
- 会除零,各实现都要特判(通常直接走 argmax)。
- top-p = 0 依赖”至少保留一个”的右移(Week 0 Day 5 §5.3);HF 用
min_tokens_to_keep=1保证这一点。 - 两个 logit 恰好相等时选哪个,取决于实现。
- GPU 上的浮点运算本身有非确定性(并行归约的顺序),批处理的组成也会影响数值。“T=0 就完全可复现”在大规模推理服务里并不总成立。
2.3 重复惩罚
第 6 组对比了两种写法(token 0 出现过 3 次,token 2 出现过 1 次):
| 写法 | 公式 | 结果 |
|---|---|---|
乘法式(CTRL,HF 的 repetition_penalty) | 出现过的 token:正 logit ,负 logit | |
| 加法式(OpenAI 的 frequency / presence) |
乘法式必须区分正负号:负 logit 如果也除以 ,会变大,反而鼓励重复。加法式按出现次数累积,出现越多扣得越多。
两者都是补丁:模型并没有学会”不该重复”,只是被从外面推开。惩罚太重,模型会回避必要的重复(人名、代码里的变量名)。根本原因是训练目标与生成目标的错位(Week 0 Day 5 §5.5),Lecture 5 的偏好对齐从训练侧处理它。
2.4 质量与多样性的权衡
所有采样旋钮本质上都在质量(贴近模型最自信的输出)与多样性(覆盖更多可能)之间移动。学习计划的动手任务之一就是把它画出来:同一个 prompt,扫温度 × top-p 的网格,每个格子采样多次,横轴多样性(不同 n-gram 的比例)、纵轴质量(参考模型下的平均 log 概率或人工打分)。参考代码 --grid 做了这件事。你应该看到一条从”单调但稳定”到”多样但混乱”的曲线,而不是某个格子全面胜出。
§3 上下文长度
3.1 代价
长上下文的两笔账在 Week 1 已经算过:注意力的 计算与显存(Week 1 Day 1 §5),以及随长度线性增长的 KV Cache(Week 1 Day 4 §1.4:Llama 2 7B 每 token 512 KiB)。位置编码的外推问题见 Week 1 Day 5 §3。
3.2 迷失在中间
Liu et al.(2023)做了一个简单的实验:多文档问答,把包含答案的那篇文档放在上下文的不同位置。结果是 U 形:答案在开头或结尾时准确率最高,放在中间时明显下降,有时甚至低于完全不给文档。上下文越长,这个效应越明显。
意义:
- “能装下”不等于”能用好”(Week 1 Day 5 §3.7 提过:名义长度、困惑度不崩的长度、真正能利用信息的长度是三个不同的数)。
- 实践上:关键信息放在开头或结尾;RAG 时少而精的检索结果往往比塞满上下文更好(第 4 周,Lecture 7)。
§4 提示与上下文学习
4.1 零样本、少样本
GPT-3 论文的核心发现:足够大的模型,不更新任何权重,只在提示里放几个输入-输出示例,就能完成新任务。
零样本: 把下面的句子翻译成法语:cheese =>
少样本: sea otter => loutre de mer
peppermint => menthe poivrée
cheese =>
论文报告少样本表现随模型规模显著提升,小模型几乎不能从示例中受益。
4.2 这是”学习”吗?
权重没变,所以不是梯度意义上的学习。那模型从示例里得到了什么?Min et al.(2022)做了一个惊人的实验:把少样本示例的标签随机打乱,性能只下降一点点。真正重要的是:输入的分布(示例长什么样)、标签的集合(有哪些可能的答案)、以及格式。
一种解读:ICL 更多是在定位任务(“哦,这是情感分类,答案是 positive / negative”),而不是从零学一个新映射。模型在预训练中已经见过海量的类似任务,示例的作用是把它引到正确的那个。
4.3 三种机制解释
一、隐式贝叶斯推断(Xie et al. 2021):预训练文本由许多潜在”概念”生成,模型学会了从上下文推断当前是哪个概念。示例越多,后验越集中。这解释了”定位任务”的观点。
二、归纳头(Olsson et al. 2022):一种两个注意力头配合的电路,实现”之前见过 [A][B],现在又看到 [A],就预测 [B]“的复制模式。它在训练中的某个阶段突然形成,同一时刻 ICL 能力显著提升。这是 Week 1 Day 1 §4.5 的 QK / OV 电路视角的一个具体应用。
三、隐式梯度下降(von Oswald et al. 2023):对”在上下文里学线性回归”这个任务,一层线性注意力的输出恰好等于在上下文示例上做一步梯度下降的预测。
推导很短。上下文有 个示例 ,查询 。从 出发,对 做一步学习率为 的梯度下降:,预测 。再看线性注意力(Week 1 Day 6 §2,去掉 softmax):query 取 ,key 取 ,value 取 ,输出 。两者逐项相同。 w2day3_checks.py 第 7 组:差 ;多做几步(对应更多层)误差从 0.500 降到 0.087、0.000。
三种解释不矛盾:它们在不同的抽象层次上描述同一个现象。任何一个都不是完整的答案。
4.4 敏感性与校准
ICL 出了名的不稳定:
- 顺序:Lu et al.(2022)发现,同样的几个示例换一种排列,准确率可以从接近最好到接近随机猜测。
- 格式:标签词、分隔符、换行都会影响结果。
- 多数标签偏置、近因偏置(Zhao et al. 2021):模型倾向于预测示例里出现最多的标签,以及离结尾最近的那个示例的标签。
上下文校准(Zhao et al. 2021):用一个无内容的输入(比如”N/A”)替换查询,模型给出的分布就是它的先验偏置 ;实际预测时除以它:。第 8 组模拟了一个偏爱第 0 类的模型:
| 准确率 | 预测为第 0 类的比例(真实约 0.333) | |
|---|---|---|
| 校准前 | 0.621 | 0.612 |
| 校准后 | 0.720 | 0.335 |
回答学习计划的自测题:“为什么 few-shot 示例的顺序会显著影响结果?这说明了 ICL 的什么本质?” 说明 ICL 不是一个稳健的”从示例中学习”过程,而是对上下文表面特征高度敏感的条件生成:模型在用示例定位任务、猜测答案格式,同时带着来自预训练的各种偏置。这也是为什么 Lecture 4 的指令微调(Day 6)能大幅提高零样本的稳定性。
§5 思维链与自洽性
5.1 思维链
Wei et al.(2022):在少样本示例里写出推理过程,模型就会模仿着先写推理、再给答案。论文报告在 GSM8K 数学题上,PaLM 540B 的准确率从直接回答的约 18% 提升到约 57%。Kojima et al.(2022)发现,连示例都不用,只加一句”Let’s think step by step”也有显著效果(零样本 CoT)。
5.2 为什么有效,为什么小模型上失效
一、更多的串行计算。 一个 层的 Transformer 生成一个 token 时,只做 步串行计算。有些问题(多步算术、状态追踪)本质上需要比 更多的串行步骤。思维链的每个中间 token 都会作为输入再经过一遍全部 层,于是有效的串行深度随推理长度增长。理论工作(如 Feng et al. 2023、Merrill & Sabharwal 2023)证明,允许生成中间步骤会严格扩大 Transformer 能解决的问题类别。
二、把中间结果放进上下文。 模型不必在隐状态里”记住” 37×24 的中间积,写出来之后后续步骤可以直接注意到它。
三、为什么小模型上失效。 Wei et al. 报告思维链的收益在约 100B 参数以上才明显。小模型能写出流畅但错误的推理链:它学会了”推理的样子”,但每一步的正确率不够高。由 Day 1 §5 的 ,每步 90% 正确,10 步全对只剩 35%。推理链越长,对单步准确率的要求越高。
一个警告:忠实性。 Turpin et al.(2023)发现,模型写出的推理过程并不总是它得出答案的真实原因:在提示里加入偏向某个答案的暗示后,模型会改变答案,却在推理中只字不提那个暗示。思维链是有用的计算,但不一定是可信的解释。
5.3 自洽性:投票的数学
Wang et al.(2022):对同一个问题,用 采样 条推理链,提取每条的最终答案,取出现最多的那个。论文报告在 PaLM 540B 上把 GSM8K 从约 57% 提升到约 74%。
投票什么时候有用? 第 9 组模拟了每条推理以概率 正确、否则给出某个错误答案的情形:
| 单次正确率 | 错误答案 | ||||
|---|---|---|---|---|---|
| 0.4 | 分散在 8 个值上 | 0.404 | 0.628 | 0.831 | 0.954 |
| 0.4 | 集中在同一个常见错误上 | 0.406 | 0.369 | 0.324 | 0.256 |
| 0.2 | 分散 | 0.196 | 0.256 | 0.354 | 0.453 |
| 0.4 | 分散,样本间相关 | 0.405 | 0.505 | 0.621 | 0.669 |
三条结论:
- 成立条件不是 ,而是”正确答案比任何单个错误答案都更常见”(相对多数)。推理题的错误答案往往五花八门,所以 也能被投票放大。
- 错误集中时投票有害:第二行里最常见的错误答案概率 ,投票把这个错误锁定成最终答案, 越大越糟。
- 样本相关性削弱收益:温度太低、提示太强时,多条推理高度相似, 再大也接近单次采样。
代价:token 成本随 线性增长。这就是”用推理算力换准确率”的最简单形式,Lecture 6 会把它推广到 best-of-n + 验证器、过程奖励模型引导的搜索。
§A 解码与提示:API 用法与边界
A.1 HuggingFace 的 generate
out = model.generate(**inputs,
max_new_tokens=256, # 用它,而不是 max_length(后者包含 prompt)
do_sample=True, # False 时 temperature / top_p 等全部被忽略
temperature=0.7, top_p=0.9, top_k=50, min_p=None,
num_beams=1, length_penalty=1.0,
repetition_penalty=1.0, no_repeat_ngram_size=0,
num_return_sequences=5, # 自洽性:一次采样 5 条
pad_token_id=tok.eos_token_id)
top_k默认是 50。 只设top_p=0.9时,top-k=50 也同时生效。要纯 top-p,得显式top_k=0(或None)。- 模型自带的
generation_config.json会覆盖库的默认值。 很多对话模型默认do_sample=True、temperature=0.6之类,你不传参数时用的是它们。复现实验前先打印model.generation_config。 do_sample=False时传temperature会被忽略并警告。- 没有 pad token 的模型(Week 0 Day 3 §A.5)批量生成时要传
pad_token_id,并左补齐(Week 1 Day 1 §3.4)。 min_p在较新的版本里才有。
A.2 对话模板
msgs = [{"role": "system", "content": "..."}, {"role": "user", "content": "..."}]
text = tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True)
- 指令模型必须用它自己的对话模板,自己拼字符串很容易少一个特殊 token,效果莫名变差。
add_generation_prompt=True在末尾加上”助手开始说话”的标记;不加,模型可能去续写用户的话。- 基座模型(非指令版)没有对话模板,少样本实验用纯文本提示。
A.3 可复现性
- 采样实验要固定种子(
torch.manual_seed),并且多种子取平均:解码实验的方差很大,单个种子的结论不可靠(Week 0 的教训)。 - 比较提示或解码策略时,同一批问题、同一批种子,只改一个变量。
A.4 答案提取
GSM8K 这类任务的准确率很大程度上取决于怎么从输出里抽出答案。常用做法:要求模型以固定格式结尾(如”答案是 X”),用正则抓最后一个数字,统一去掉逗号和单位。抽取规则要在看结果之前定好,否则很容易不自觉地为某个方法调规则。
§B 实操
- 跑通
w2day3_checks.py,9 组结论都看懂。第 9 组的三种情形各想一个现实中的例子。 - 推导(0.5h):线性注意力 = 一步梯度下降(§4.3);GNMT 长度惩罚在 和 时分别退化成什么。
- 手写解码(1h):在一个小模型上实现贪心、beam(带长度惩罚)、temperature + top-k + top-p + min-p、重复惩罚。贪心和 beam 与 HF
generate对拍。 - 解码网格(1h,学习计划的动手任务 1):同一个 prompt,温度 ∈ {0.3, 0.7, 1.0, 1.3} × top-p ∈ {0.5, 0.9, 1.0},每格采样 10 次,画出多样性 vs 质量。
- 少样本的敏感性与校准(1h):在 SST-2 上用基座模型做 4-shot,枚举示例的不同顺序,记录准确率的范围;再加上下文校准,看范围是否收窄。
- CoT 与自洽性(2h,学习计划的动手任务 2):GSM8K 前 50 题,比较直接回答、CoT、自洽性(),统计准确率和 token 成本。再看自洽性的错误案例:错误答案是分散的还是集中的?
参考实现:code/w2d3_decoding.py(默认用 Qwen/Qwen2.5-0.5B-Instruct,CPU 上可以跑;换 1.5B 效果更明显)。
§C 自测
动机与直觉层
- 贪心为什么会错过最优序列?beam search 用什么代价换来了什么?
- 为什么”概率最大的序列”可能是空的?这对”解码就是找最可能的输出”这种想法意味着什么?
- top-k、top-p、min-p 各自在什么情况下表现不好?为什么 min-p 在高温下更稳?
- 为什么说重复惩罚是补丁?
- “迷失在中间”说明了什么?RAG 时怎么应对?
- ICL 是在”学习”吗?随机打乱标签几乎不影响性能,说明了什么?
- 思维链为什么有效?为什么小模型上失效?
- 思维链的”忠实性”问题是什么?
推导与计算层
- 推出线性注意力的输出等于在上下文示例上做一步梯度下降的预测。
- 写出 GNMT 的长度惩罚,说明 的作用。
- 写出乘法式重复惩罚,解释为什么要区分正负 logit。
- 自洽性投票成立的条件是什么?构造一个投票反而有害的例子。
- 每步 90% 正确,10 步推理全对的概率是多少?这对思维链意味着什么?
工程层
- HF
generate里只设top_p=0.9,实际生效的截断有哪些? - 为什么复现实验前要打印
generation_config? - temperature = 0 和 top-p = 0 在工程上有什么区别?
- 为什么答案抽取规则要在看结果之前定好?
出关标准
- 能用例子说明贪心的局限、beam 的作用、长度偏置与归一化、搜索的诅咒
- 能比较三种截断策略(top-k、top-p、min-p),说清 min-p 的动机
- 能回答”temperature = 0 和 top-p = 0 是一回事吗”
- 能说出 lost in the middle 的现象和实践启示
- 能用三种机制解释 ICL,推出线性注意力 = 一步梯度下降
- 能解释少样本的顺序敏感与上下文校准
- 能说清思维链为什么有效、为什么小模型上失效、忠实性问题
- 能算出自洽性投票何时有用、何时有害
- 完成解码网格与 GSM8K 的 CoT / 自洽性实验