本节摘要:模板决定模型听到什么,采样参数决定它怎么说话——同一个模型、同一份提示,只动三个旋钮就能从"严谨助教"变成"酒后诗人"。本节先讲三旋钮各自在概率分布上做了什么(温度:整体拉平/削尖;top-p:截掉长尾;重复惩罚:给已出现的 token 降权),再给一张"症状 → 旋钮"的手感影响矩阵——发散、复读、死板、胡编四种手感病各对应哪组药方;最后给出对话场景的默认参数组合(temperature 0.7 / top-p 0.9 起步)与调参次序。参数没有"最优值",只有"症状匹配"。
阅读完本节,你应当能够:
模型每步输出的是全词表的概率分布,采样参数是分布的三种改法:
原始分布 p(token) ──► 采样出下一个 token │ ├─ temperature = T:logits 除以 T 再归一化 │ T < 1:削尖(高概率更高、低概率更低)→ 保守、确定 │ T > 1:拉平(差距缩小)→ 发散、野 │ T = 0:argmax,贪心解码 → 完全确定,易复读 ├─ top-p(nucleus):只在累计概率 ≥ p 的最小 token 集合里采样 │ p = 0.9:砍掉长尾 10% 的"胡话候选" └─ repeat_penalty = r:已出现过的 token,logits 除以 r(r>1 则降权) 对抗"同一段话无限循环"的老毛病
三者关系:温度和 top-p 都在改"每一步选谁",功能有重叠——通常只主调其一,另一个锁定;重复惩罚作用在"选过的别再选",与前两者正交、可叠加。
💡 与预训练的关系:预训练评测常用 T=0 的贪心解码(要可比性),对话恰恰相反——T=0 的模型像复读机,每个问题都用最"安全"的句式开头。**对话场景天生需要一点随机性**,这是 4.2 存在的根本原因。
四诊法:先看症状,再开药方。
| 症状(手感病) | 典型表现 | 首选处方 | 次选 |
|---|---|---|---|
| 发散 | 东拉西扯、话题漂移、答非所问 | temperature 降档(0.9→0.7→0.5) | top-p 收紧(0.95→0.85) |
| 复读 | 同一句循环、口头禅高频复现 | repeat_penalty 升(1.0→1.1→1.2) | temperature 微升,跳出吸引子 |
| 死板 | 每个回答同款开头、干瘪无细节 | temperature 升(0.5→0.7→0.9) | 检查是否 T=0 贪心解码 |
| 胡编 | 一本正经编造事实、编引用 | top-p 收紧 + temperature 降 | 严重时是数据病,回 2.2 查质量 |
两条边界提醒:
| 场景 | temperature | top-p | repeat_penalty | 说明 |
|---|---|---|---|---|
| 通用对话(默认) | 0.7 | 0.9 | 1.1 | 本书冒烟测试(3.3)用的量级 |
| 事实问答/客服 | 0.3~0.5 | 0.85 | 1.1 | 要稳:低温窄核 |
| 创意写作/头脑风暴 | 0.9~1.0 | 0.95 | 1.05 | 要野:放宽两档,重复惩罚少加 |
| 评测回归(第 5 章用) | 0(贪心)或固定种子 | — | — | 可比性优先,牺牲一点手感 |
调参次序(十分钟纪律):
# sample_playground.py —— 采样参数对照实验(写法示意) import itertools import torch from transformers import AutoModelForCausalLM, AutoTokenizer tok = AutoTokenizer.from_pretrained("runs/sft_124m/final") model = AutoModelForCausalLM.from_pretrained("runs/sft_124m/final", dtype=torch.bfloat16) ids = tok.apply_chat_template( [{"role": "user", "content": "用两句话介绍一下长城。"}], tokenize=True, add_generation_prompt=True, return_tensors="pt") for t, p in itertools.product([0.3, 0.7, 1.0], [0.85, 0.95]): torch.manual_seed(7) # 固定种子:参数是唯一变量 out = model.generate(ids, max_new_tokens=80, do_sample=True, temperature=t, top_p=p, repetition_penalty=1.1) print(f"--- T={t} p={p} ---") print(tok.decode(out[0][ids.shape[1]:], skip_special_tokens=True))
跑一遍这个双因子循环,四组输出的差异比任何文字描述都直观——发散与死板的边界在眼前成形。
⚠️ 一次只动一个旋钮是铁律。同时调温度和 top-p 再看效果,你永远不知道是哪个起的作用——单变量原则在调参与在实验科学里同样不可妥协。
本节的完整定位——接到手感投诉时的分流决策树:
手感差 ├─ 换默认参数组 10 分钟内好转?──► 是:采样问题,本节解决,收工 └─ 否 ──► 看三征兆(3.2:复读/变短/逐字复述) ├─ 有 ──► 过拟合:回退 checkpoint / 减 epoch └─ 无 ──► 0.1 的三种行为病(不停/身份错/不听指令) └─ 有 ──► 模板错位(4.3)或数据形态缺陷(2.1)
第 5 章会把"5 个测试问题 + 人眼判断"升级为正式的评测集与打分流程,但分流的骨架就是这棵树。
采样只在"拼对了序列"的前提下有意义——4.3 节清算拼错序列的 N 种死法:模板不一致,全流水线最贵的 bug。