苦猿的大模型日记 · Day32 · 推理参数调参心法-帮普通人把AI学进简历系列
前言:我见过最多的推理参数错误,不是调太猛——是调太保守
我见过最多的推理参数错误,不是 temperature 调到 1.5 让模型开始说胡话。
是把 temperature 一路往 0 方向拧,觉得越低越"稳"——然后发现模型的输出越来越像背书:第一段讲清楚了,第二段开始重复,第三段绕圈,第四段你会看到它把第一段的话用不同的方式又说了一遍。
这是 greedy decoding 的经典陷阱。很多人以为调低 temperature 是在"让模型更严谨",实际上是在让它越来越贪——每一步只走局部概率最高的那条路,结果走进了死角。
temperature / top_p / repetition_penalty / max_new_tokens——这几个参数背后各有一套逻辑。盲调只是在瞎撞。理解每个参数真正控制的是什么、调错了会出现什么症状,才能从症状倒推旋钮方向,快速找到问题。
这篇就讲这个。
Qwen3-8B 的官方推荐 temperature 是 0.7,很多人第一次用时直接设 0.1——这不是小事,在 reasoning 任务上这个差距会直接影响模型能不能绕出来。
PART 01:temperature——不是越低越准,是越低越贪
先把底层机制一句话说清楚:temperature 是 softmax 的缩放系数,决定概率分布的"尖锐/平坦"程度。
temperature=1.0:原始概率分布,不做任何缩放。
temperature<1.0:分布被压尖,高概率词的优势被放大,低概率词几乎没机会被选到。
temperature>1.0:分布被拉平,低概率词混进候选的机会变大。
听起来 temperature 低 = 模型更"确定",是好事。问题就出在这里。
temperature=0(或接近 0)是 greedy decoding。每一步选当前概率最高的词,听起来最"理性"。但这条路很容易走进本地最优:前几步走得很好,到了某个节点,所有"好"的下文概率都差不多,greedy 就会选那个最高概率的,然后下一步发现自己被带进了一个重复的圈子——因为"重复上一句"往往是语言模型的高概率选择。
症状你应该见过:模型输出的后半段开始重复句式,或者把结论翻来覆去说,或者两段话之间的意思完全一样只是换了个措辞。
temperature 太高也有问题:概率分布被拉平之后,低质量词(甚至乱码词)混进候选。症状是中英文随机切换、名词突然拼错、逻辑在某句话之后断裂。
那经验值是多少?按任务类型来,不能一刀切:
- 知识问答 / RAG 检索增强:0.3-0.5(需要事实准确,但不能 greedy)
- 代码生成:0.1-0.3(逻辑严格,低随机)
- 创意写作 / 头脑风暴:0.7-1.0(需要多样性)
- Reasoning CoT(思维链):0.6-0.8
最后这条是反直觉的。很多人觉得 reasoning 任务要"严谨",应该把 temperature 调低。但思维链推理需要"探索空间"——模型要在脑子里走几条不同的路,排除错误,收敛到正确答案。temperature 太低,推理路径变成一条直线,遇到稍微复杂一点的题就绕不出来。
看个代码:
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
model_name = "Qwen/Qwen3-8B"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto"
)
prompt = "请解释什么是注意力机制,并举一个直觉类比。"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
for temp in [0.1, 0.7, 1.2]:
output = model.generate(
**inputs,
max_new_tokens=256,
temperature=temp,
do_sample=True, # 关键:不开这个,temperature 完全失效,下一节细讲
top_p=0.9,
)
decoded = tokenizer.decode(
output[0][inputs["input_ids"].shape[1]:],
skip_special_tokens=True
)
print(f"\\n=== temperature={temp} ===")
print(decoded)
temperature=0.1 的输出通常简洁工整,但偶尔会在末尾重复一两句;temperature=0.7 输出最自然;temperature=1.2 有时能给出有趣的类比,但也容易在细节上飘。
监控信号:temperature 调太低的时候,输出文本里的 unique token 比例会下降,重复 ngram 数量会上升。如果你有评测脚本,可以顺手把这两个指标加进去。

PART 02:top_p 和 top_k——两把筛子,别同时开到最大
top_p 和 top_k 都是采样时的过滤器,但工作方式不一样。
top_p(nucleus sampling):先把所有词按概率从高到低排序,然后只保留"概率累积到 p 的那些词"作为候选。
top_p=0.9:候选集涵盖了 90% 的概率质量,低概率的长尾词被排除。
这个设计的好处是自适应:如果模型对下一个词非常确定(最高概率词的概率是 0.95),top_p=0.9 的候选集可能只有 1 个词;如果模型比较"迷茫"(概率分散),候选集就会更大,给多样性留空间。
top_k:直接截断,只保留概率最高的 k 个词。
top_k=50 就是每次从概率排名前 50 的词里采样。逻辑更直接,但有个问题——它不管概率分布的形状。词表大的模型在某些位置概率高度集中,top_k=50 里其实只有前 5 个词有意义,后 45 个概率接近 0 却还在候选里,浪费了筛选空间。
两者叠加的陷阱:top_p=0.9 + top_k=50 不是"双重保险"。
两个过滤器是串联的,不是并联。先用 top_k 截断,再用 top_p 过滤——如果 top_k 先把候选词集缩到 20 个,top_p 在 20 个里再过滤,最终候选可能只剩个位数。多样性直接崩了,输出会变得跟纯 greedy 差不多。
经验:通常只开一个。推荐 top_p=0.9, top_k=0(top_k=0 代表关掉 top_k 过滤)。词表大的模型 top_k 的合理值不好判断,top_p 更鲁棒。
do_sample——最容易被漏掉的开关
这个参数值得单独强调。
do_sample=False:greedy decoding,temperature / top_p / top_k 全部失效。
不是"效果变弱",是完全失效。你把 temperature 调到 0.9,top_p 调到 0.95,如果 do_sample 没开,模型完全不看这两个参数,直接 greedy 选概率最高的词。
我见过不止一次有人调了半天参数,输出一点变化没有,最后发现 do_sample 忘了开。症状非常明显:多次采样输出完全一样,改了 temperature 之后结果丝毫不变。
# ❌ 错误用法:do_sample=False 时,temperature 和 top_p 完全不生效
output = model.generate(
**inputs,
max_new_tokens=256,
temperature=0.8, # 设了也没用
top_p=0.9, # 设了也没用
do_sample=False, # 默认值,greedy decoding
)
# ✅ 正确用法
output = model.generate(
**inputs,
max_new_tokens=256,
temperature=0.7,
top_p=0.9,
top_k=0, # 关掉 top_k,只用 top_p
do_sample=True, # 必须显式开启
)

PART 03:repetition_penalty——调猛了比不调更坏
repetition_penalty 的机制是:对已经出现过的 token,把它的 logit 除以 penalty 值。
penalty > 1.0:压制重复,值越大抑制越强。
penalty < 1.0:鼓励重复(少见场景)。
推荐范围 1.05-1.2。1.1 是个不错的起点,大部分情况够用。
penalty > 1.3 开始变危险。
原理是:penalty 是全局的,不区分"这个词是废话重复"还是"这个词是必须重复的专业术语"。调猛了,模型会主动回避刚出现过的词——包括那些本该重复的词。
症状很微妙:技术文档里同一个变量名在第二次出现时被替换成了不同的词("模型"变成了"神经网络","系统","算法"……)。代码生成里,同一个函数名在调用的时候被"优化"成了别的名字。输出语法上通顺,语义上已经漂移。
这才是最难排查的问题——不是明显的乱码,而是悄悄偷换了概念。
prompt = "请详细介绍 Transformer 中的自注意力机制,包括 Query、Key、Value 的计算过程。"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
for penalty in [1.0, 1.2, 1.5]:
output = model.generate(
**inputs,
max_new_tokens=300,
do_sample=False, # 用 greedy,单独观察 penalty 的效果,排除 temperature 干扰
repetition_penalty=penalty,
)
decoded = tokenizer.decode(
output[0][inputs["input_ids"].shape[1]:],
skip_special_tokens=True
)
print(f"\\n=== repetition_penalty={penalty} ===")
print(decoded)
跑这段代码,penalty=1.5 的输出里,"Query"/"Key"/"Value" 这几个词在第二次出现时大概率被换成了别的表达。
如果主要问题是句尾循环复读,更精准的方案是 no_repeat_ngram_size=3:禁止连续 3 个 token 的 ngram 重复。比全局 penalty 精准,不会误伤必要的术语重复。
output = model.generate(
**inputs,
max_new_tokens=300,
do_sample=True,
temperature=0.7,
no_repeat_ngram_size=3, # 只禁止 3-gram 重复,不影响单词级别的重复
repetition_penalty=1.0, # 关掉全局 penalty
)
监控信号:penalty 调过头的输出里,同一概念会出现多个不同的指代词,用 set(output_tokens) 数一下 unique 词的数量反而会升高——听起来多样了,实际上是语义在漂移。

PART 04:max_new_tokens——给模型一个收束点,不是保险箱
常见做法:设一个大数(4096 甚至 8192),心想"反正能输出就让它输出,截断总比不够好"。
这个逻辑不完全对。
对话 / 问答类任务,max_new_tokens 太大,模型会填充废话直到达到上限。
不是"模型不知道停下来"——语言模型是会生成 <|endoftext|> 的。问题是在 stop token 出现之前,模型会根据 context 判断"我是不是应该再补充一点"。如果 max 设得很大,模型的"补充意愿"会相对强,正文说完之后继续重申、补充、反复强调,直到它觉得"说得够了"或碰到了上限。
按任务类型给经验值:
- 简单问答 / 意图分类:64-256
- 摘要 / 分析 / 翻译:512-1024
- 代码生成(单函数):512-1024
- 代码生成(完整模块):1024-2048
- Reasoning CoT(思维链):2048-4096
最后一条又是反直觉的:reasoning 模型越想越长,这是特性不是 bug。模型在推理过程中会自我检查、修正、重算,这些都会显著增加 token 数。如果 max_new_tokens 设太小,thinking 过程被截断,最终答案质量直接下降。
还有一个配套参数 min_new_tokens:防止模型输出一两个词就停。对于强制要求完整回答的场景(比如摘要任务,你不希望模型只吐一句话),把 min_new_tokens 设到预期最短输出长度,可以避免意外截断。
# 问答场景:限制输出,避免废话填充
output_qa = model.generate(
**inputs,
max_new_tokens=256, # 问答不需要很长
min_new_tokens=10, # 至少说 10 个 token,防止空回答
do_sample=True,
temperature=0.4,
top_p=0.9,
)
# Reasoning 场景:给足空间,不要截断思维链
output_cot = model.generate(
**inputs,
max_new_tokens=3072, # CoT 需要足够空间
do_sample=True,
temperature=0.7, # reasoning 需要一定探索空间
top_p=0.9,
)
PART 05:一张表,帮你从症状倒推旋钮
调了这么多参数,怎么在实际问题里快速定位?
答案是先看症状,再选旋钮,不要上来就调 temperature。
| 输出后半段重复、绕圈 | temperature 太低(greedy 陷阱) | temperature ↑ 到 0.5-0.7 |
| 输出语言混乱、逻辑断裂 | temperature 太高 | temperature ↓ 到 0.5 以下 |
| 改了 temperature 输出没变化 | do_sample=False | 先开 do_sample=True |
| 专业词汇被替换成同义词 | repetition_penalty 太高 | penalty ↓ 到 1.1-1.15 |
| 回答没说完就停了 | max_new_tokens 太小 | 翻倍再试 |
| 回答说完了还在废话 | max_new_tokens 太大 | 按任务类型收紧 |
| 输出每次都一样,多样性差 | top_p 太低或 top_k 太小 | top_p ↑ 到 0.9,top_k 设 0 |
| Reasoning 推理答案质量差 | temperature 太低 + CoT 被截断 | temperature ↑ 到 0.7,max_new_tokens ↑ |
最后一行是个组合症状,也是最容易被漏掉的——你以为是模型推理能力不行,实际上只是参数限制了它的思考空间。
心法:这 5 个参数背后控制的,说到底是"置信度"和"探索度"的平衡。
置信度太高(temperature 低、top_p 低):模型走最安全的路,走进死角。
探索度太高(temperature 高、penalty 低):模型走太远,走进混乱。
不同任务对这个平衡的要求不一样——把任务类型想清楚,参数就基本对了。

结尾:参数只是表象,任务类型才是锚
这 5 个参数各有脾气,但规律都是一样的:每个参数背后都有一个对应的症状,症状反向告诉你旋钮该往哪转。
能在面试里讲清楚"为什么 reasoning 模型的 temperature 要比代码生成模型高"——这一题在大模型工程岗位面试里是真实考点,问的就是你有没有跑过、有没有踩过坑。
苦猿的观察:大多数工程翻车在 do_sample 和 repetition_penalty 这两个地方。temperature 其实是最容易调的,因为症状最直接,一眼能看出来哪边出了问题。真正坑人的是 do_sample 忘了开——调了半天参数,模型输出纹丝不动,最后发现一行代码的事。
推理参数调的不是随机性,调的是"你允许模型有多大的犯错空间"——太严格,它走进死角;太宽松,它走进混乱。
找到那个平衡点,就是这件事的全部。
互动时间:
你跑推理时最常在哪个参数上翻车?do_sample 没开还是 repetition_penalty 调过头?评论区聊聊,说不定能避一个坑。
下一篇预告:推理参数调好了,下一步是怎么把微调好的模型高效部署起来——vLLM 本地部署实战,我们接着聊。
觉得有用的话,点个在看,帮更多想学 AI 的人看到这篇。
— END —
苦猿 · 帮普通人把 AI 学进简历




