Temperature 与 Top-p 原理解析
-
调用 LLM 时,你是否好奇为何同一提示词每次生成的回答不尽相同?决定模型是严谨还是发散的,正是 Temperature(温度) 和 Top-p(核采样) 。
-
这两个参数直接干预生成时的概率分布,是控制模型输出风格的核心。本文将深入底层原理,拆解它们如何重塑概率分布。掌握这两者,不仅是精准控制模型输出的关键,更是理解 LLM 推理机制的最佳切入点。
-
附代码库:jiangya-66/python-learn
参考:py-day8.py 采样参数对比:测试temperature和top_p对LLM输出的影响
1. Temperature(温度)
作用:控制概率分布的平滑程度,影响随机性大小。
- 低温度(<1.0,如 0.1~0.5):概率分布更陡峭,模型倾向选择高概率词 → 输出更确定、保守、连贯。适合事实问答、代码生成。
- 高温度(>1.0,如 1.2~2.0):概率分布更平坦,低概率词被更多选中 → 输出更有创造性、意外性,但可能逻辑变差或胡言乱语。
- temperature=1:保持原始概率分布。
- temperature→0(实际常用 0.1):近似贪婪解码,几乎总是选最高概率词。
简单理解:高温加大“冒险”,低温让模型“保守”。
2. Top-p(核采样,nucleus sampling)
作用:从累积概率达到 p 的最小词汇集合中采样,动态调整候选词数量。
- 低 p(如 0.1~0.3):只考虑概率最高的少数词 → 输出更集中、可控。
- 高 p(如 0.9~0.95):考虑更多可能词,保留占概率质量 90% 的词汇集合 → 输出更丰富、多样。
- p=1:考虑所有词,等同于不限制(但仍受概率分布影响)。
简单理解:p 越小,可选词越少,输出越确定。
3. 两者对比与配合
| 参数 | 控制方式 | 候选集变化 | 常用范围 |
|---|---|---|---|
| temperature | 重塑概率分布 | 所有词,但概率权重改变 | 0.1~1.5 |
| top_p | 动态裁剪词汇表大小 | 只保留累积概率前 p 的词 | 0.7~0.95 |
常见组合策略:
- 确定性任务(代码、公式、事实回答):
temperature=0.1~0.3,top_p=0.1~0.3 - 创意任务(故事、诗歌、头脑风暴):
temperature=0.8~1.2,top_p=0.9~0.95 - 平衡模式:
temperature=0.7,top_p=0.9
注意:
- 一般不要同时极端调低两者(比如 temp=0.1 且 p=0.1),否则输出会极其重复甚至死循环。
- 有些 API 要求
temperature和top_p不能同时修改,只能调其一(如 OpenAI 旧版建议)。 - 实际使用建议:先固定
top_p=0.9,然后单独调整temperature观察效果;或者反过来。
上面的内容太抽象,让人云里雾里? 接下来将拨云见日...
LLM概率以及概率分布
理解概率和概率分布是真正掌握 temperature 和 top_p 的基础。下面我从头拆解。
一、模型输出的原始东西:Logits
大模型最后一层输出的不是概率,而是一个分数向量,每个分数对应一个词(token)。这些分数叫 logits。
Logits 的特点:可以是任意实数(正、负、零),没有上下限,也不满足概率公理(不非负、不加起来为1)。
举例(假设词汇表只有5个词):
词: "猫" "狗" "鸟" "鱼" "虫"
logits: [5.2, 3.1, 2.4, 1.0, -0.5]
- 数值越大,表示模型认为该词越可能
- 但这里 5.2 并不是概率(因为 5.2 > 1)
二、Softmax:从 Logits 到概率分布
Softmax 函数将 logits 转换为概率分布(所有值在 0~1 之间,且和为 1)。
公式(简化理解):
P(词_i) = e^(logit_i) / Σ e^(logit_j)
对上面例子计算(示意值,非精确计算):
词: "猫" "狗" "鸟" "鱼" "虫"
logits: 5.2 3.1 2.4 1.0 -0.5
概率: 0.72 0.18 0.07 0.02 0.01
验证:0.72 + 0.18 + 0.07 + 0.02 + 0.01 = 1.00 ✅
关键性质:
- 较大的 logit → 更大的概率(指数放大差距)
- 较小的 logit → 极小的概率(负数 logit 会变成很小的正数)
- 输出是一个概率分布:描述了模型对“下一个词应该是什么”的信念
三、概率分布的形状:陡峭 vs 平坦
概率分布的形状决定了采样的行为。
1. 陡峭分布(高确定性)
词: "猫" "狗" "鸟" "鱼" "虫"
概率: 0.85 0.10 0.03 0.01 0.01
- 一个词(猫)占绝对主导
- 采样几乎总是选“猫”
- 出现在:模型非常确定时
2. 平坦分布(高不确定性)
词: "猫" "狗" "鸟" "鱼" "虫"
概率: 0.25 0.23 0.20 0.18 0.14
- 没有明显主导词
- 多个词都有机会被选中
- 出现在:模型犹豫不决时(如开放性问题)
3. 中间分布
词: "猫" "狗" "鸟" "鱼" "虫"
概率: 0.50 0.25 0.15 0.06 0.04
- 有主导词,但也有合理的备选
四、Temperature 如何改变概率分布
Temperature 操作是在 softmax 之前:将 logits 除以 T。
新 logits = 原 logits / T
效果对比(用上面的 logits 例子)
原始 logits: [5.2, 3.1, 2.4, 1.0, -0.5]
| Temperature | 操作 | 新 logits 近似 | 概率分布形状 | 效果 |
|---|---|---|---|---|
| 0.5 (低温) | 除以 0.5 = 乘 2 | [10.4, 6.2, 4.8, 2.0, -1.0] | 极陡峭 | "猫"概率→0.97,其他极低 |
| 1.0 | 不变 | [5.2, 3.1, 2.4, 1.0, -0.5] | 原始分布 | 0.72, 0.18, 0.07... |
| 1.5 (高温) | 除以 1.5 | [3.47, 2.07, 1.60, 0.67, -0.33] | 变平坦 | "猫"概率↓0.60,其他↑ |
可视化理解:
低温 (T=0.5): ████ █ ▏ ▏ ▏
原始 (T=1.0): ███ ██ ▏ ▏ ▏
高温 (T=1.5): ██ █ █ ▏ ▏
核心原理:
- T < 1:放大 logits 之间的差距(强者更强)
- T = 1:不变
- T > 1:缩小 logits 之间的差距(趋向均匀)
极端情况:
- T → 0:只有最大 logit 对应的词概率 → 1(贪婪解码)
- T → ∞:所有词概率相等(均匀分布,完全随机)
五、Top-p 如何基于概率分布裁剪
Top-p 在 softmax 之后操作,直接作用在概率分布上。
步骤:
- 将词按概率从大到小排序
- 从最大的开始累加概率
- 当累积概率 ≥ p 时停止
- 保留这些词,丢弃其他词
- 在保留的词中重新归一化概率(和为1)
详细例子(p=0.9)
原始分布:
词: "猫" "狗" "鸟" "鱼" "虫"
概率: 0.50 0.25 0.15 0.06 0.04
累积: 0.50 0.75 0.90 0.96 1.00
↑保留 ↑保留 ↑保留 └ 丢弃 ┘
候选集 = {猫, 狗, 鸟},累积概率 = 0.90
重新归一化:
猫的新概率 = 0.50 / 0.90 ≈ 0.556
狗的新概率 = 0.25 / 0.90 ≈ 0.278
鸟的新概率 = 0.15 / 0.90 ≈ 0.166
(总和=1.00)
关键:丢弃“鱼”和“虫”后,它们的概率被按比例重新分配给保留的词。
六、两个例子看懂 Temperature + Top-p 的协同
场景 A:陡峭分布 + 低 p
原始分布: [0.85, 0.10, 0.03, 0.01, 0.01]
p=0.5: 只保留 "猫" (0.85 ≥ 0.5)
结果: 100% 选 "猫"
- top_p 的“裁剪”在陡峭分布下几乎没影响(反正只留一个词)
场景 B:平坦分布 + 低 p
原始分布: [0.25, 0.23, 0.20, 0.18, 0.14]
p=0.5: 保留 "猫"+"狗" (0.25+0.23=0.48 < 0.5? 需要加下一个)
保留 "猫"+"狗"+"鸟" (0.25+0.23+0.20=0.68 ≥ 0.5)
结果: 从 {猫,狗,鸟} 中按 [0.37, 0.34, 0.29] 采样
- 平坦分布 + 低 p 会强制截断很多合理的候选词
场景 C:高温 + 高 p
高温使分布变平坦 → 原始分布 [0.25,0.23,0.20,0.18,0.14]
p=0.95: 保留所有5个词
结果: 多样性最高
七、总结:概率分布视角
| 参数 | 作用时机 | 对概率分布的操作 |
|---|---|---|
| Temperature | Softmax 之前 | 缩放 logits,改变分布的陡峭/平坦程度 |
| Top-p | Softmax 之后 | 裁剪低概率词,改变分布的有效支撑集大小 |
一句话理解:
Temperature 决定了概率分布是“尖峰”还是“丘陵”,Top-p 决定了你是从整个山脉中采样,还是只从主峰群采样。
可视化类比:
- 原始分布 = 一座山,主峰高,周围有小丘
- 低温 = 山变得更尖,只有主峰突出
- 高温 = 山变平缓,多个山峰高度接近
- 低 p = 只画主峰群,砍掉远处的孤立小山包
- 高 p = 画整个山脉
模型最终返回所有的“保留的词”吗?
不是的。
模型最终只返回 一个词(或者几个词,如果设置了 n > 1),而不是返回所有“保留的词”。
让我把这个过程讲清楚:
完整流程
1. 模型计算 logits(所有词的分数)
↓
2. 应用 temperature(缩放 logits)
↓
3. Softmax 得到概率分布(所有词都有概率)
↓
4. Top-p 裁剪(保留部分词,丢弃其余)
↓
5. 【关键】从保留的词中【随机采样】一个词
↓
6. 返回这个被选中的词
Top-p 的裁剪只是中间步骤,目的是限定“采样池”,而不是最终输出所有池中的词。
详细例子
假设经过 softmax 后得到分布:
词: A B C D E
概率: 0.40 0.30 0.20 0.07 0.03
设置 p=0.8:
- 累积:A(0.40) → B(0.70) → C(0.90) 已经超过 0.8
- 保留池 = {A, B, C}
- 重新归一化:A=0.40/0.90≈0.444, B≈0.333, C≈0.223
然后采样:
- 随机抽一个,概率按 44.4%、33.3%、22.2%
- 假设抽到 B
最终返回:
B
(只有 B,不是 {A, B, C})
特殊情况:n > 1
如果你设置 n=3(生成 3 个候选结果),模型会独立重复这个过程 3 次,返回 3 个词(或 3 段文本)。
返回:
[
"B", # 第一次采样
"A", # 第二次采样
"C" # 第三次采样
]
但每次采样仍然只返回一个词,不是返回整个池。
为什么会有这个误解?
可能把 Top-p 和 Top-k 展示(如 logprobs 参数)混淆了。
| 参数 | 作用 | 返回内容 |
|---|---|---|
top_p | 控制采样池的大小 | 最终只返回 1 个词 |
logprobs | 要求返回前 k 个词的概率 | 返回前 k 个词 + 它们的概率(仅用于查看,不作为输出) |
示例(OpenAI API):
{
"logprobs": True,
"top_logprobs": 3 # 返回概率最高的 3 个词供查看
}
这会额外返回 {"token": "A", "logprob": -0.91} 等信息,但模型实际输出仍然是采样得到的那一个词。
一句话总结
Top-p 决定“从哪些词里抽”,但不改变“只抽一个词”的结果。
保留池是内部的候选集,不是输出内容。最终输出只有一个词(除非明确设置n>1生成多个独立结果)。
LLM中最重要的参数有哪些
在 LLM 推理(生成)时,最重要的参数可以分为控制输出形态和控制生成行为两类。
一、最核心的 4 个参数(几乎每次调用都会用到)
| 参数 | 作用 | 常见范围 | 备注 |
|---|---|---|---|
| temperature | 控制随机性/创造性 | 0 | 越低越确定,越高越随机 |
| top_p | 动态控制候选词池大小 | 0.8~0.95 | 与 temperature 配合使用 |
| max_tokens / max_new_tokens | 限制生成长度 | 视任务而定 | 防止无限生成或超预算 |
| stop | 停止符 | 字符串或列表 | 如 ["\n", "。", "Q:"] |
这四个参数几乎每个生产级 LLM API 都支持。
二、次重要但常用的参数
| 参数 | 作用 | 何时使用 |
|---|---|---|
| top_k | 固定候选词数量 | 需要严格限制候选集大小时 |
| frequency_penalty | 降低已出现词的重复概率 | 减少重复循环、增加多样性 |
| presence_penalty | 惩罚已出现过的主题/词 | 鼓励引入新话题(不关注具体频次) |
| n | 生成几个独立候选结果 | 需要多个答案备选时 |
| seed | 固定随机种子 | 需要结果可复现时 |
三、参数的作用层次(理解优先级)
第一层:基础结构
├── max_tokens(必须设,否则可能无限生成)
└── stop(强烈建议设,控制结束边界)
第二层:随机性控制(核心取舍)
├── temperature(主要控制)
└── top_p(辅助控制,通常固定 0.9 左右)
第三层:多样性增强(按需)
├── frequency_penalty
├── presence_penalty
└── top_k
第四层:调试/复现
└── seed
四、最常见的最佳实践组合
1. 确定性任务(代码、公式、事实问答)
temperature = 0.1
top_p = 0.3
max_tokens = 适当
stop = ["\n\n"] 或合适的边界
2. 创意任务(故事、诗歌、头脑风暴)
temperature = 0.8~1.2
top_p = 0.9
frequency_penalty = 0.3~0.5
presence_penalty = 0.3~0.5
3. 平衡对话/通用场景
temperature = 0.7
top_p = 0.9
frequency_penalty = 0.1~0.2
(其他默认)
4. 可复现的调试场景
temperature = 0.7
top_p = 0.9
seed = 42 # 固定种子
五、容易被忽略但重要的参数
| 参数 | 作用 | 为什么重要 |
|---|---|---|
| logprobs | 返回每个生成词的对数概率 | 调试、置信度评估、质量监控 |
| echo | 是否在输出中包含输入 prompt | 调试 prompt 效果 |
| best_of(部分 API) | 生成 n 个选最好的 | 提高质量但增加成本 |
| logit_bias | 手动调整特定词的 logits | 禁止某些词或偏向某些词 |
六、一个实际建议的调试顺序
如果你第一次用某个模型:
- 固定
max_tokens和stop(防止跑飞) - 固定
top_p = 0.9(比较安全的默认值) - 调整
temperature:- 太重复/无聊 → 升高 temperature
- 太乱/逻辑错 → 降低 temperature
- 按需调整
frequency_penalty(如果仍重复) - 最后调
top_k或presence_penalty(精细优化)
一句话总结
temperature 和 top_p 控制“怎么选词”,max_tokens 和 stop 控制“什么时候停”,这 4 个是最不可缺的参数。
频率惩罚类参数在需要多样性时再加入,seed 只在需要可复现时用。