主题
第 11 章 生成接口:温度、top-k、提示和随机性
11.1 采样从一个非空提示开始
sample.py 默认从 out_dir/ckpt.pt 恢复,按 model_args 建模、处理 _orig_mod. 前缀、载入状态,然后 eval() 并移到设备。也可从四种 GPT-2 型号初始化,但这会走预训练权重路径,本书不执行。脚本随后查 checkpoint 中的 dataset 名称,寻找对应 meta.pkl;找到就使用字符映射,否则假定 GPT-2 BPE。采样脚本
这段默认逻辑有两个容易遗漏的条件。第一,自定义 BPE 不能只凭 meta.pkl 文件存在就自动工作,因为代码明确期待 stoi 和 itos 字符表。第二,即使用字符表,脚本顶层仍导入 tiktoken,所以运行原版 sample.py 需要安装该包,但字符路径不会调用 BPE 编码或下载词表。
start 若以 FILE: 开头,就把后面的路径作为 UTF-8 文本文件读入;否则直接编码字符串。得到的一维编号加一个批次维,成为 <|endoftext|> 这个特殊 token,不等于允许任何任意特殊标记。
11.2 一次循环只增加一个 token
GPT.generate 循环 max_new_tokens 次。先取最近不超过 block_size 的输入窗口,前向得到最后位置 logits,再按温度和 top-k 调整,softmax 后用 torch.multinomial 抽一项,拼回完整 idx。因此返回长度为
窗口裁短只影响“下一步拿什么作条件”,不截断已经累积的输出。原版没有遇到 EOT 就提前停止的判断,没有批内不同长度终止,也没有 KV cache。对每个新 token,它重新计算当前窗口的所有层。生成速度与训练吞吐因此不能用同一个 tokens/s 简单比较。
11.3 温度改变概率比,不改变正温度下的排序
令原 logits 为
两项概率比为
原接口没有检查温度为正。零会产生除零、无穷或 NaN,负温度甚至反转排序,都不是通常所说的温度采样。若想确定性贪心,应显式用 argmax,而不是给一个未经验证的零温度。有限精度下非常低的温度也可能造成数值极端。
11.4 top-k 的阈值实现有平分边界
源码先取 min(top_k,V) 个最大分数,拿第 k 个分数当阈值,把严格小于它的项设负无穷。再 softmax,相当于在保留集合上重新归一化。若
k 超过词表被截为 V,等于不排除任何项。k=0 或负数无效,源码没有提供友好的参数范围错误。正温度缩放保持排序,所以在数学上先缩放再取 top-k 与先按原分数选集合一致;浮点溢出或极端数值不在这个理想前提里。
| 设置 | 主要作用 | 常见代价 |
|---|---|---|
| 较低正温度 | 更偏向高分项 | 重复与僵化可能增加 |
| 较高温度 | 更多低分项被抽到 | 语法或内容错误可能增加 |
| 较小 top-k | 限制候选范围 | 正确但暂时低分的项可能被排除 |
| 无 top-k | 保留全词表概率 | 长尾噪声也参与抽样 |
这些是采样机制的方向性影响,不是某个温度对所有任务更好的经验定律。比较生成应使用同一模型、提示和明确 seed,并看多个样本。
11.5 eval、no_grad 和 seed 各做一件事
eval() 关闭 dropout,no_grad() 不建立反向图,seed 控制随机抽样流。只设 seed 不会关闭 dropout;只设 eval 仍会从概率分布随机抽样。generate 方法有 no_grad 装饰器,但不会替调用方自动设 eval,因此直接调用模型方法时要自己确认状态。
本书两次单独启动 sample.py,使用 seed=17、温度 0.8、top-k=3、提示 ab、生成 32 token,完整 stdout 一致。生成片段为:
text
ab
aab
cab
cabcab caab
aab
cab ca它能看出合成训练数据的局部片段,却仍有重复和拼接错误。这是 25 步小实验的真实输出,不用“像语言”包装成自然语言能力。若同一次进程里连续抽多个样本,RNG 状态会前进,通常得到不同结果;并不是每个样本前都会重新 seed。
补充练习。 提示长度已超过 block_size,max_new_tokens=2,返回值会只有 block_size+2 吗?
答案。 不会。计算时仅裁条件窗口,完整 idx 保留原提示,因此返回原提示长度加 2。每次条件窗口内位置重新从 0 开始,老 token 离开窗口后不能再直接参与注意力。
11.6 采样入口的全部默认开关
| 字段 | 固定源码默认值 | 含义及边界 |
|---|---|---|
init_from | 'resume' | resume 或四种 gpt2* 型号;采样只加载模型,不创建或恢复优化器,不支持 scratch |
out_dir | 'out' | checkpoint 目录;复用旧目录前先确认 init_from |
start | '\n' | 字面提示或 FILE:路径;必须编码成非空序列 |
num_samples | 10 | 连续生成次数;循环内不会每次重新设 seed |
max_new_tokens | 500 | 每个样本新增 token 数;不含提示长度 |
temperature | 0.8 | 正温度缩放 logits;原接口不验证范围 |
top_k | 200 | 按第 k 大阈值排除候选;并列可能多于 k |
seed | 1337 | 初始化抽样 RNG;跨环境不保证逐位一致 |
device | 'cuda' | 采样模型和输入所在设备;此入口没有 DDP 分支 |
dtype | 支持 CUDA BF16 则 bfloat16,否则 float16 | 自动混合精度选择;CPU 不使用 autocast |
compile | False | 是否 torch.compile;本书关闭,不测编译收益 |
这些是原版默认,完整 CPU 实验显式覆盖设备、dtype、长度、样本数、温度、top-k 和 seed。
本章来源
- sample.py:加载、编码、文件提示和输出
- model.py:generate
- 本书样本及重复性:
qa/author/tiny_sample.log、tiny_sample_repeat.log;并列阈值反例:qa/author/mechanics.json。