Temperature 与 Top P:大模型输出的"调音台"

前言:你有没有遇到过这种情况------同一个问题问 ChatGPT 三次,得到三个完全不同的回答?或者想让模型"严谨一点"却不知道怎么调?其实背后就是 TemperatureTop P 在捣鬼。今天咱们就把这两个参数拆得透透的,顺便把 Top K、Repetition Penalty 这些"配角"也一起讲了,保证你调参不再靠玄学


先搞懂一个前提:模型是怎么"选词"的?

在聊参数之前,必须先理解一件事:模型每一步输出,本质上都是在做选择

当你输入"今天天气真",模型会对词表里的每个词打一个分数(logits),然后转成概率:

erlang 复制代码
词表概率分布(示例):
  "好"    → 0.35  (35%)
  "不错"  → 0.20  (20%)
  "热"    → 0.15  (15%)
  "冷"    → 0.10  (10%)
  "糟糕"  → 0.05  (5%)
  ...其他几千个词 → 0.15  (15%)

采样(Sampling) 就是根据这个概率分布,选出一个词作为输出。

问题来了:怎么选?

  • 永远选概率最高的?→ 太死板
  • 完全按概率随机选?→ 太混乱
  • 这就是 Temperature 和 Top P 要解决的问题

一、Temperature:控制输出的"创意程度"

1.1 一句话解释

Temperature(温度) 控制的是概率分布的"尖锐程度":

  • Temperature 高 → 分布变平 → 输出更随机、更有创意
  • Temperature 低 → 分布变尖 → 输出更确定、更保守
  • Temperature = 0 → 完全取最高概率 → 贪心解码(Greedy)

1.2 数学原理(很简单)

Temperature 的计算公式:

ini 复制代码
logits_after = logits / temperature
probs = softmax(logits_after)

就这么简单------把 logits 除以 temperature,再做 softmax

python 复制代码
import torch
import torch.nn.functional as F

def apply_temperature(logits, temperature):
    """
    logits: 模型原始输出 (vocab_size,)
    temperature: 温度参数
    """
    # 核心:logits 除以 temperature
    scaled_logits = logits / temperature

    # softmax 转成概率
    probs = F.softmax(scaled_logits, dim=-1)

    return probs

# ============ 直观感受 ============
logits = torch.tensor([2.0, 1.0, 0.5, 0.1, -1.0])  # 5个候选词

# 不同温度下的概率分布
for t in [0.1, 0.5, 1.0, 2.0, 5.0]:
    probs = apply_temperature(logits, t)
    print(f"T={t}: {probs.numpy().round(3)}")

输出结果:

ini 复制代码
T=0.1: [0.881 0.107 0.006 0.004 0.001]   ← 几乎只选第一个
T=0.5: [0.576 0.241 0.105 0.051 0.026]   ← 偏向第一个,但其他也有机会
T=1.0: [0.351 0.192 0.117 0.071 0.031]   ← 原始分布(默认值)
T=2.0: [0.227 0.185 0.149 0.114 0.077]   ← 越来越均匀
T=5.0: [0.167 0.154 0.141 0.124 0.104]   ← 接近均匀分布

看到规律了吗? Temperature 越小,概率越集中在高分的词上;Temperature 越大,概率分布越"平",低分词也有机会被选中。

1.3 用一个比喻理解

把模型选词想象成选秀节目评委打分

Temperature 比喻
T → 0 评委只看最高分,其他一律无视(独裁)
T = 0.3 评委偏心高分选手,低分基本没戏
T = 1.0 评委公正打分,按分数概率选人
T = 3.0 评委佛系,谁都有机会出道
T → ∞ 完全随机抽签,分数没意义了

1.4 不同场景的推荐值

场景 推荐 Temperature 原因
代码生成 0.0 ~ 0.2 代码需要精确,不能瞎编
翻译 0.1 ~ 0.3 需要忠实原文
问答/摘要 0.3 ~ 0.7 需要准确但允许一定灵活性
创意写作 0.7 ~ 1.0 需要多样性和创意
头脑风暴 1.0 ~ 1.5 越发散越好
角色扮演 0.8 ~ 1.2 需要自然但有个性

踩坑经验 :Temperature 绝对不能设成负数!我见过有人把 temperature 设成 -1,然后模型直接报错。另外,temperature 太高(比如 > 2)会导致输出完全不可控,出现大量乱码或无意义内容。


二、Top P(核采样):精准控制随机范围

2.1 为什么需要 Top P?

Temperature 虽然能控制整体随机性,但有个问题:它对概率分布"一刀切"

假设模型输出"好"的概率是 0.9,其他所有词加起来才 0.1。这时候即使 Temperature 设到 1.0,模型也几乎只会选"好"------因为概率差距太大了。

但有些场景下,概率分布比较均匀:

arduino 复制代码
"继续往下说" 的续写概率:
  "因为"  → 0.25
  "所以"  → 0.20
  "而且"  → 0.18
  "但是"  → 0.15
  "不过"  → 0.10
  ...其他 → 0.12

这时候你希望模型在前几个高概率词里选,而不是偶尔蹦出一个概率只有 0.01 的词。

Top P(也叫核采样 Nucleus Sampling) 就是干这个的。

2.2 核心思想

把概率从高到低排序,累加到超过阈值 P 就停下来,只在这个范围内采样。

css 复制代码
Top P = 0.9 的示例:

排序后的概率:
  "好"    → 0.35  累计: 0.35  ✅
  "不错"  → 0.20  累计: 0.55  ✅
  "热"    → 0.15  累计: 0.70  ✅
  "冷"    → 0.10  累计: 0.80  ✅
  "糟糕"  → 0.05  累计: 0.85  ✅
  "还行"  → 0.06  累计: 0.91  ❌ 超过 0.9,截断!
  ...其他全部丢弃

最终采样范围: ["好", "不错", "热", "冷", "糟糕"]

2.3 代码实现

python 复制代码
import torch
import torch.nn.functional as F

def top_p_sampling(logits, top_p=0.9):
    """
    logits: (vocab_size,) 模型原始输出
    top_p: 核采样的概率阈值
    """
    # 1. softmax 得到概率
    probs = F.softmax(logits, dim=-1)

    # 2. 按概率从高到低排序
    sorted_probs, sorted_indices = torch.sort(probs, descending=True)

    # 3. 计算累计概率
    cumulative_probs = torch.cumsum(sorted_probs, dim=-1)

    # 4. 找到累计概率超过 top_p 的位置
    # 移除累计概率超过 top_p 的词
    sorted_indices_to_remove = cumulative_probs - sorted_probs > top_p

    # 5. 把被移除的词的概率设为 0
    sorted_probs[sorted_indices_to_remove] = 0.0

    # 6. 重新归一化
    sorted_probs = sorted_probs / sorted_probs.sum()

    # 7. 从过滤后的分布中采样
    next_token = torch.multinomial(sorted_probs, num_samples=1)

    # 8. 映射回原始词表索引
    actual_token = sorted_indices[next_token]

    return actual_token.item()

# ============ 使用示例 ============
logits = torch.randn(50000)  # 假设词表大小 50000

# Top P = 0.9:在前 90% 概率的词中采样
token = top_p_sampling(logits, top_p=0.9)
print(f"采样结果: {token}")

2.4 Top P 的推荐值

Top P 值 效果 适用场景
0.1 极度保守,几乎只选最高概率词 代码生成、数学计算
0.5 偏保守,在少数候选中选择 翻译、事实性问答
0.9 最常用的默认值 通用对话、写作
0.95 较开放,允许更多选择 创意写作、头脑风暴
1.0 不做任何过滤 等于没用 Top P

三、Top K:Top P 的"老前辈"

3.1 Top K 是什么?

Top K 比 Top P 更简单粗暴:只保留概率最高的 K 个词,其余全部丢弃。

makefile 复制代码
Top K = 3 的示例:

原始概率:
  "好"    → 0.35  ✅ 保留
  "不错"  → 0.20  ✅ 保留
  "热"    → 0.15  ✅ 保留
  "冷"    → 0.10  ❌ 丢弃
  "糟糕"  → 0.05  ❌ 丢弃
  ...其他全部丢弃

重新归一化:
  "好"    → 0.35/0.70 = 0.50
  "不错"  → 0.20/0.70 = 0.29
  "热"    → 0.15/0.70 = 0.21

3.2 Top K vs Top P

维度 Top K Top P
过滤方式 固定保留 K 个词 动态保留累计概率达 P 的词
适应性 ❌ 不适应概率分布变化 ✅ 自动适应
极端情况 K=50,但可能前 2 个词就占了 99% 不会浪费采样空间
实现难度 简单 稍复杂

为什么 Top P 更好? 举个极端例子:

模型非常确定下一个词是"好"(概率 0.99),其他词概率都很小。

  • Top K=50:会保留 49 个几乎不可能的词,浪费计算
  • Top P=0.9:只保留"好"(0.99 > 0.9),干净利落

反过来,如果概率分布很均匀:

  • Top K=5:可能只覆盖了 40% 的概率空间,丢掉了大量合理选项
  • Top P=0.9:自动保留足够多的候选词

所以现在主流做法是 Top P 为主,Top K 为辅(或者干脆不用 Top K)。

3.3 代码实现

python 复制代码
def top_k_sampling(logits, top_k=50):
    """Top K 采样"""
    # 1. 找到概率最高的 K 个
    top_k_values, top_k_indices = torch.topk(logits, top_k)

    # 2. 过滤:把不在 Top K 中的 logits 设为 -inf
    filtered_logits = torch.full_like(logits, float('-inf'))
    filtered_logits.scatter_(0, top_k_indices, top_k_values)

    # 3. softmax 采样
    probs = F.softmax(filtered_logits, dim=-1)
    next_token = torch.multinomial(probs, num_samples=1)

    return next_token.item()

四、Repetition Penalty:防止复读机

4.1 问题来了

大模型有个臭毛病:喜欢复读

makefile 复制代码
用户: 讲个笑话
模型: 从前有个人,从前有个人,从前有个人...

这是因为模型在生成过程中,已经生成的词会影响后续的概率分布,导致模型倾向于"重复自己说过的话"。

4.2 Repetition Penalty 怎么解决?

核心思想:如果某个词已经出现过了,就人为降低它的概率。

python 复制代码
def apply_repetition_penalty(logits, token_ids, penalty=1.2):
    """
    logits: (vocab_size,) 当前步的 logits
    token_ids: 已经生成过的 token 列表
    penalty: 惩罚系数 (> 1.0)
    """
    for token_id in set(token_ids):
        # 如果 logits > 0,除以 penalty(降低概率)
        # 如果 logits < 0,乘以 penalty(也降低概率)
        if logits[token_id] > 0:
            logits[token_id] = logits[token_id] / penalty
        else:
            logits[token_id] = logits[token_id] * penalty

    return logits

# 示例
logits = torch.tensor([2.0, 1.5, 0.8, -0.5, -1.0])
already_generated = [0, 2]  # 词 0 和词 2 已经生成过了

penalized = apply_repetition_penalty(logits.clone(), already_generated, penalty=1.2)
print(f"惩罚前: {logits}")
print(f"惩罚后: {penalized}")
# 惩罚前: tensor([ 2.0,  1.5,  0.8, -0.5, -1.0])
# 惩罚后: tensor([ 1.67,  1.5,  0.67, -0.6, -1.0])
#                         ↑词0降了    ↑词2降了

penalty 推荐值 :通常设为 1.1 ~ 1.3。太小没效果,太大会让模型说话不自然(刻意避开常用词)。


五、参数组合:实战调参指南

5.1 常见组合推荐

python 复制代码
# ============ 不同场景的参数配置 ============

configs = {
    "代码生成": {
        "temperature": 0.1,
        "top_p": 0.1,         # 几乎只选最高概率
        "top_k": 1,           # 等于贪心解码
        "repetition_penalty": 1.0,  # 代码不怕重复
        "max_tokens": 2048,
    },
    "中文翻译": {
        "temperature": 0.3,
        "top_p": 0.85,
        "top_k": 40,
        "repetition_penalty": 1.1,
        "max_tokens": 1024,
    },
    "通用对话": {
        "temperature": 0.7,
        "top_p": 0.9,         # 最经典的组合
        "top_k": 50,
        "repetition_penalty": 1.1,
        "max_tokens": 2048,
    },
    "创意写作": {
        "temperature": 1.0,
        "top_p": 0.95,
        "top_k": 100,
        "repetition_penalty": 1.15,
        "max_tokens": 4096,
    },
    "头脑风暴": {
        "temperature": 1.2,
        "top_p": 0.95,
        "top_k": 100,
        "repetition_penalty": 1.2,   # 创意场景更怕复读
        "max_tokens": 4096,
    },
}

5.2 调参的黄金法则

scss 复制代码
┌──────────────────────────────────────────────────┐
│                 调参决策树                        │
├──────────────────────────────────────────────────┤
│                                                  │
│  输出太无聊/太重复?                              │
│    ├── 是 → 提高 temperature (0.7 → 1.0)         │
│    │        提高 top_p (0.9 → 0.95)              │
│    │                                              │
│  输出太随机/胡说八道?                            │
│    ├── 是 → 降低 temperature (1.0 → 0.5)         │
│    │        降低 top_p (0.95 → 0.85)             │
│    │                                              │
│  输出一直复读?                                   │
│    ├── 是 → 提高 repetition_penalty (1.1 → 1.2)   │
│    │        降低 temperature                     │
│    │                                              │
│  输出太短就停了?                                  │
│    ├── 是 → 检查 max_tokens 是否太小              │
│    │        检查 stop_sequences 设置              │
│    │                                              │
│  想要每次回答一致?                                │
│    └── 是 → temperature = 0(贪心解码)            │
│                                                  │
└──────────────────────────────────────────────────┘

5.3 Temperature 和 Top P 能同时用吗?

可以,而且推荐同时使用。 它们的作用层面不同:

  • Temperature:调整整体概率分布的"尖锐程度"
  • Top P:在调整后的分布上,截断低概率的尾部

两者是正交的,可以独立调节:

python 复制代码
# 完整的采样流程
def sample_with_params(logits, temperature=0.7, top_p=0.9, top_k=50):
    """
    完整采样流程:Temperature → Top K → Top P → 采样
    """
    # Step 1: 应用 Temperature
    logits = logits / temperature

    # Step 2: 应用 Top K 过滤
    if top_k > 0:
        top_k_values, top_k_indices = torch.topk(logits, min(top_k, logits.size(-1)))
        filtered_logits = torch.full_like(logits, float('-inf'))
        filtered_logits.scatter_(-1, top_k_indices, top_k_values)
        logits = filtered_logits

    # Step 3: 应用 Top P(核采样)
    if top_p < 1.0:
        sorted_logits, sorted_indices = torch.sort(logits, descending=True)
        cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)
        sorted_indices_to_remove = cumulative_probs - F.softmax(sorted_logits, dim=-1) > top_p
        sorted_logits[sorted_indices_to_remove] = float('-inf')
        logits = torch.scatter(logits, -1, sorted_indices, sorted_logits)

    # Step 4: 采样
    probs = F.softmax(logits, dim=-1)
    next_token = torch.multinomial(probs, num_samples=1)

    return next_token

六、采样策略全景图

除了上面讲的,还有一些进阶采样策略,简单提一下:

策略 原理 优缺点
Greedy 每步选最高概率词 ✅ 确定性强 ❌ 容易复读、不自然
Beam Search 每步保留 N 条最优路径 ✅ 全局最优 ❌ 生成文本不自然、多样性差
Top K 保留前 K 个候选词 ✅ 简单 ❌ 不自适应
Top P (Nucleus) 保留累计概率达 P 的词 ✅ 自适应、效果好 ❌ 实现稍复杂
Min P 过滤概率低于最高概率×P 的词 ✅ 更精细的自适应过滤
Typical Sampling 过滤概率远离"典型"值的词 ✅ 平衡随机性和质量

实际工作中 :对话场景几乎都用 Temperature + Top P 的组合。Beam Search 主要用于翻译和摘要任务。Top K 一般作为 Top P 的补充(先 Top K 粗筛,再 Top P 精筛)。


写在最后

Temperature 和 Top P 看起来只是两个数字,但它们直接影响模型的"性格":

  • Temperature 低 + Top P 低 → 严谨的工程师
  • Temperature 中 + Top P 中 → 正常的对话伙伴
  • Temperature 高 + Top P 高 → 天马行空的艺术家

理解了这些参数,你就能根据不同场景精准调参,让模型输出最符合你期望的内容。

调参的本质不是"试数字",而是理解"每个参数在控制什么"。 希望这篇文章帮你建立起了这个直觉

**觉得有帮助?点赞收藏,评论区聊聊你平时常用的参数配置 **

相关推荐
段一凡-华北理工大学1 小时前
工业领域的Hadoop架构学习~系列文章04:YARN资源调度架构
人工智能·hadoop·学习·架构·系统架构·高炉炼铁·高炉炼铁智能化
weixin_429630261 小时前
3.47 室内环境下全向成像孪生神经网络机器人定位的实验评价
人工智能·神经网络·机器人
山居秋暝LS1 小时前
paddlelabe标注注意事项
人工智能·opencv·计算机视觉
li-xun1 小时前
职场AI困境、技术故障排查与情感生活百态
人工智能
Web极客码1 小时前
AI的下一个风口:智能助力超越ChatGPT
服务器·人工智能·ai编程
szxinmai主板定制专家1 小时前
基于 ARM+FPGA 数据机床实时工业控制设计--以雕刻机为例
arm开发·人工智能·嵌入式硬件·fpga开发
微效电子1 小时前
辉芒微代理商-FMD辉芒微MCU-8位、32位微控制器芯片代理商-深圳市微效电子有限公司
人工智能
梦想的颜色2 小时前
Claude Code 桌面端 vs CLI 全面安装指南与对比:2026 最新版,选哪个?
人工智能·架构·claude code
Omics Pro2 小时前
基因泰克:检测级虚拟细胞基准!大语言模型+智能体
大数据·数据库·人工智能·机器学习·语言模型·自然语言处理·r语言