前面,我们从零训练了一个小语言模型,也看到了它怎么一步步生成文字。
接着就遇到了一个很容易混淆的参数:temperature,温度。
有人把它叫作"创意参数":温度低,回答稳定;温度高,回答发散。于是,一个自然的问题出现了:
温度高了,回答更容易出错,是不是和量化一样,把模型的精度降低了?
不是。这里混在一起的,其实是两件不同的事:
- 温度:调整选取下一个 token 的概率。
- 量化:用更低位数表示模型中的数字。
一个影响"怎么选",一个影响"数字怎么表示"。下面用一个例子把它们拆开。
1. 温度在生成过程的哪个位置?
先看常见的自回归语言模型生成一个 token 的过程:

文字先转换成 token 编号,经过词嵌入、多层 Transformer 和输出层 LM Head,得到一组分数。这些还没有归一化成概率的分数,叫作 logits。
假设词表有 10,000 个 token,那么在当前生成位置,输出层会给这 10,000 个候选各算一个分数。它不是另外找一个模型评分,而是当前模型的一部分。
接下来,生成程序根据分数选择一个 token。温度通常就在这个阶段起作用:先缩放分数,再转换成概率,最后按概率抽取。Transformers 将温度归入输出 logits 的处理参数,具体说明见文本生成文档。
这意味着:调温度不需要重训模型,也不会因此改变层数、参数量或权重的存储位数。它是生成时的配置,不是训练出来的那几亿个权重之一。
图中只画温度采样的主线,省略了候选过滤等细节;量化标注以权重量化为例,也不代表每次生成都重新量化一次模型。
2. 同一组分数,温度怎样改变概率?
假设模型正在续写:
text
小白在......
为了方便计算,我们构造一个只有三个候选的教学例子:
| 候选 token | 原始分数 |
|---|---|
| 餐 | 2 |
| 公 | 1 |
| 家 | 0 |
这里按字符演示,"餐""公""家"各是一个 token。真实分词器未必一个汉字对应一个 token;这些分数也不是某个真实模型的测量结果。
当温度 T > 0 时,常见计算方式是:
text
概率 = softmax(原始分数 ÷ 温度)
pᵢ = exp(zᵢ / T) / Σⱼ exp(zⱼ / T)
其中,zᵢ 是第 i 个候选的分数,exp(x) 就是 e 的 x 次方;分母把所有候选的结果加起来,让概率总和等于 1。
不记公式也没关系,只看这三个步骤:所有分数除以温度 → 取指数 → 除以总和。
用同一组 [2, 1, 0] 计算,可以得到:
| 温度 | 除以温度后的分数 | 餐 | 公 | 家 |
|---|---|---|---|---|
| 0.5 | 4, 2, 0 | 86.7% | 11.7% | 1.6% |
| 1.0 | 2, 1, 0 | 66.5% | 24.5% | 9.0% |
| 2.0 | 1, 0.5, 0 | 50.6% | 30.7% | 18.6% |
百分比保留一位小数,因此显示值相加可能不是恰好 100%。

可以看到:
- 温度降低,分数差距被放大,概率更集中在第一名。
- 温度升高,分数差距被缩小,其他候选更容易被抽中。
T = 1不进行温度缩放,但后面的采样仍然可以是随机的。
所以,温度高不是"凭空创造一个词",而是让原本概率较小的候选获得更多机会。TemperatureLogitsWarper 文档也把它定义为对分布随机性的调节。
这里比较的是相同上下文、同一步生成。如果这一步抽中了不同 token,下一步的上下文就变了,模型后续算出的原始分数也可能不同。
3. 一个关键细节:调温度不等于改选第一名
在上面的三个温度下,排名始终是:
text
餐 > 公 > 家
正温度会改变分数差距,但在理想数学计算中,不改变候选排名。
因此,如果生成代码一直是"取最高分",而不是"按概率抽取",那么只除以温度,通常看不出效果。
前面那个 MLX 教学模型,当前恰好使用这样的代码:
python
# 排除 PAD 补齐标记,在其余 token 中选最高分的编号。
next_id = 1 + int(mx.argmax(logits[1:]).item())
这叫贪心生成。它没有启用随机采样,也没有通过温度改变抽取概率。
理解两种方式的区别,可以想象抽奖:
- 贪心生成:谁的概率最高,就直接选谁。
- 随机采样:按概率分配抽签机会,第一名最容易中,但其他候选也可能中。
这不是"平均随机选一个"。86.7% 的候选,比 1.6% 的候选有大得多的机会。
对于 Transformers 的常见单束生成,使用温度采样还需要开启 do_sample=True;如果关闭采样、使用贪心解码,调温度并不能获得预期的随机效果。见生成参数说明。
那 temperature = 0 呢?
公式不能直接除以 0。
一些实现约定:T = 0 时走单独的贪心分支,直接选最高分。另一些实现要求温度严格大于 0,需要通过其他配置关闭采样。
因此,"0 表示贪心"是实现约定,不是把 0 代入公式。不同框架和模型 API 的支持范围、默认值也不能一概而论。
4. 不下载模型,也能运行这个实验
下面只用 Python 标准库,模拟"模型已经给出分数以后,如何选 token"。不需要 GPU、不需要 API Key,也没有训练过程。
把代码保存为你自己的 temperature_demo.py,执行 python3 temperature_demo.py 即可。
python
import math
import random
def temperature_probs(logits, temperature):
# 教学例子只接受非空、有限的分数;禁止生成的 token 应先过滤。
if not logits or not all(math.isfinite(x) for x in logits):
raise ValueError("logits 必须非空,且全部是有限数字")
# 本例允许 0,并单独实现贪心;负数、NaN 和无穷大都不接受。
if not math.isfinite(temperature) or temperature < 0:
raise ValueError("temperature 必须是有限的非负数")
# 0 不进入除法;并列最高分时,本例选靠前的候选。
if temperature == 0:
winner = max(range(len(logits)), key=lambda i: logits[i])
return [1.0 if i == winner else 0.0 for i in range(len(logits))]
# 先减去最大分数,避免指数过大;数学上不改变 softmax 的结果。
maximum = max(logits)
# 温度越大,候选间的分数差距越小。
weights = [math.exp((x - maximum) / temperature) for x in logits]
# 归一化以后,所有候选的概率总和为 1。
total = sum(weights)
return [weight / total for weight in weights]
# 固定候选及分数,只比较温度的作用;这不是模型实测 logits。
tokens = ["餐", "公", "家"]
logits = [2.0, 1.0, 0.0]
# 固定本例的随机种子,方便在相同环境里复现。
rng = random.Random(7)
for temperature in (0.5, 1.0, 2.0):
# 同一组分数,分别转成三种温度下的概率。
probabilities = temperature_probs(logits, temperature)
# 按概率抽一个 token,而不是直接取最大值。
selected = rng.choices(tokens, weights=probabilities, k=1)[0]
# 同时打印概率和本次抽取结果,观察两者的区别。
percentages = [f"{p:.1%}" for p in probabilities]
print(f"T={temperature}: 概率={percentages}, 抽中={selected}")
这份代码在本地运行得到:
text
T=0.5: 概率=['86.7%', '11.7%', '1.6%'], 抽中=餐
T=1.0: 概率=['66.5%', '24.5%', '9.0%'], 抽中=餐
T=2.0: 概率=['50.6%', '30.7%', '18.6%'], 抽中=公
三组概率与前面的表格一致。单次抽取可能在不同温度下选到同一个字,这完全正常:概率改变,不代表每次结果一定改变。
注意,这段代码只反复观察一个固定生成位置。如果想生成完整句子,还需要把选中的 token 追加到上下文,再让模型计算下一步的 logits,而不是拿这一组分数一直抽。
示例中的 T = 0 分支把全部概率给一个最高分候选,只是为了统一返回格式;没有执行除以 0,也没有修改模型权重。
5. 量化改变的又是什么?
量化关注的是数字的表示方式。以权重量化为例:原来用 16 位浮点数保存的权重,经过量化,可能用 8 位或 4 位编码来近似表示,从而减少权重占用的空间。具体方法及其适用条件可参考量化概览。
它不是简单地"保留几位小数"。一种常见的均匀量化思路,是将数值映射到有限的整数档位,并配合缩放系数等信息还原近似值。不同量化方案的编码方式并不相同。
为了直观理解,可以看一个人为指定步长的例子:
text
假设只能用步长为 0.1 的档位表示:
原始数值 0.1234 → 最近档位 0.1
原始数值 0.2761 → 最近档位 0.3
这只是"用有限档位近似数字"的示意,不代表真实 4-bit 模型都按 0.1 四舍五入。
这里还要分清三个容易混用的概念:
| 概念 | 关注的问题 |
|---|---|
| 数值精度 | 数字可以表示得多细,有多大的近似误差? |
| 回答正确率 | 模型在具体任务上,答对了多少? |
| 采样随机性 | 生成时,非最高分候选有多少机会被选中? |
量化涉及第一项,温度主要影响第三项,两者都可能影响最终回答,但不能直接画等号。
再补一个部署中常见的误区:4-bit 权重不代表所有计算都是 4-bit。 例如,bitsandbytes 支持保存 4-bit 权重,同时选择 BF16 计算类型,见其计算数据类型说明。
量化也不一定只针对权重,还有针对激活值、KV Cache 的方案。因此,看见"量化"时,要进一步问:量化的是哪部分,用什么格式,运行时又怎么计算?
把两者放在一起就很清楚了:
| 对比项 | 温度 Temperature | 权重量化 |
|---|---|---|
| 主要改变什么 | 选 token 时的概率分布 | 权重数值的表示方式 |
| 是否改权重存储位数 | 不改 | 通常降低 |
| 是否减少模型参数个数 | 不会 | 单纯量化通常也不会 |
| 是否因此缩小权重文件 | 不会 | 通常会,有额外编码信息开销 |
| 对回答的可能影响 | 多样性、稳定性、错误概率变化 | 数值近似可能导致输出变化 |
| 能否一起使用 | 可以 | 可以 |
所以,一个量化模型完全可以低温生成,也可以高温生成。温度不是量化等级,也不能把量化损失的数值信息恢复回来。
6. 温度高,就更有创意、更不准确吗?
"创意参数"是方便理解的比喻,但不是能力保证。
提高温度,让不那么常见的候选更有机会出现,可能带来不同表达,也可能带来不相关的词、前后矛盾或错误细节。它不会自动补上模型没学会的知识。
同样,降低温度只是让选择更集中。如果模型本来就把错误答案排在第一,低温可能让它更稳定地答错。
这也解释了为什么前面的教学小模型,不能靠调温度就变成通用问答助手:数据、训练效果和模型能力仍然是基础。
实际比较时,可以固定模型版本、提示词和其他生成设置,只改变温度,多生成几次,分别观察正确率和表达差异。不要只拿一条"刚好写得好"的输出当结论。
还要注意,top_k、top_p 等候选过滤设置也会影响结果:前者限制保留多少个高概率候选,后者按累计概率截取候选集合。它们不是温度的别名,具体组合顺序要看实现。定义见生成配置文档。
7. 最后记住这三句话
- 模型先根据上下文给候选 token 打分,温度再调整用于采样的概率分布。
- 温度高不等于数字精度低;量化才是在改变模型数字的表示方式。
- 温度调的是选择方式,不是知识量,更不是"正确率旋钮"。
如果只用一句话总结:
温度决定"按什么概率选下一个 token",量化决定"用什么精度表示模型中的数字"。