5.1 先搞清楚:LLM 的输出不是"查答案"

很多人的误解:LLM 像数据库,查一下就知道该输出什么。

实际上:模型每生成一个 Token,都是在做"选择题"。

模型会为词汇表里的每个词计算一个概率,然后从这个概率分布里挑一个词作为输出。

输入:"中国的首都是____"

模型内部(简化):
  北京      → 概率 0.85
  上海      → 概率 0.07
  南京      → 概率 0.03
  东京      → 概率 0.01
  其他词    → 概率 0.04

看到没有?模型眼里没有"唯一正确答案",只有一个概率分布。而 temperaturetop-ptop-k 这些参数,控制的就是"怎么从分布里选词"。

在这里插入图片描述

图 1:LLM 不是查答案,而是面对概率分布做选择题。


5.2 temperature(温度):调节"胆子"

原理

在选词之前,模型会先把原始概率经过一个"软化"步骤(softmax),temperature 就是这个过程的缩放因子

概率'ᵢ = exp(得分ᵢ / temperature) / Σ exp(得分ⱼ / temperature)

看不懂公式没关系,记住直觉就行:

temperature效果类比
低(0~0.3)几乎总是选最高概率的词刻板的优等生,每次都写标准答案
中(0.7~1.0)偶尔选概率稍低的词正常人,回答自然有变化
高(1.2~2.0)经常选冷门词,输出天马行空喝醉的诗人,啥都敢说

实际效果演示(同一问题"讲个笑话")

temperature = 0.1  → 每次回答都差不多,风格固定
temperature = 0.7  → 每次略有不同,但都在正常范围
temperature = 1.5  → 可能冒出很怪的梗,甚至逻辑混乱

怎么选

任务类型推荐 temperature
代码生成0 ~ 0.2
数学/逻辑推理0 ~ 0.3
翻译0.3
通用对话0.7
文案创意/头脑风暴0.9 ~ 1.2
诗歌/脑洞1.2+

核心心法:任务越"需要确定性",温度越低;任务越"需要多样性",温度越高。

在这里插入图片描述

图 2:temperature 就像旋钮,低则刻板,高则"喝醉"。


5.3 top-k:只给前几名机会

top-k 的意思是:只允许模型从概率最高的 k 个词里选,其余全部清零。

k = 5 时:

排序后的概率:
  北京   0.85 ✅
  上海   0.07 ✅
  南京   0.03 ✅
  天津   0.02 ✅
  广州   0.01 ✅
  东京   0.01 ❌ 被排除
  纽约   0.008 ❌ 被排除
  • k = 1 ≈ 每次选概率最高的(等价于贪心)
  • k = 10 左右比较常用
  • 作用:剔除概率太低、明显不合理的词,避免突然蹦出奇怪的词

5.4 top-p(核采样):按累计概率截断

top-p 不看排名,看累计概率:从概率最高的词开始累加,直到累计概率超过 p 就截断。

p = 0.9 时:

累加过程:
  北京   0.85   → 累计 0.85
  上海   0.07   → 累计 0.92 ≥ 0.9 停止 ✅
  南京   0.03   → 被排除 ❌

对比 top-k 和 top-p:

参数截断依据特点
top-k固定数量简单,但 k 设多少不看分布情况
top-p动态比例分布集中时保留少,分布分散时保留多,更智能

OpenAI 官方建议:temperature 和 top-p 通常只调一个,不要同时猛调,否则输出可能不稳定。

在这里插入图片描述

图 3:top-k 按"前 k 名"截断,top-p 按"累计概率"截断。


5.5 惩罚参数:frequency_penalty 和 presence_penalty

这两个参数解决的是同一个痛点:模型爱重复自己

presence_penalty(存在惩罚)

这个词出现过就扣分。防止模型反复提同一个词/话题。

presence_penalty = 0   → 模型可能反复说"我觉得"
presence_penalty = 0.6 → 说过"我觉得"后,再说的概率降低

frequency_penalty(频率惩罚)

出现次数越多扣分越多。专门治"一句话说三遍"的毛病。

参数范围典型用途
presence_penalty-2 ~ 2让回答不跑题、不唠叨
frequency_penalty-2 ~ 2治复读机、治列表刷屏

一般设 0.3 ~ 0.8 就够用,设太大模型会说话不自然(故意避开常用词)。


5.6 动手实验:手写一个 mini 采样器

不用调 API,纯 Python 就能模拟整个采样过程:

import numpy as np

vocab = ["北京", "上海", "南京", "东京", "纽约", "巴黎"]
logits = np.array([4.0, 2.0, 1.5, 0.5, 0.3, 0.2])  # 模型原始的"得分"

def softmax_with_temperature(logits, temperature):
    """temperature 越大,分布越平(更随机)"""
    scaled = logits / temperature
    exp = np.exp(scaled - np.max(scaled))  # 数值稳定处理
    return exp / exp.sum()

def top_k_filter(probs, k):
    """只保留概率最高的 k 个词"""
    threshold = np.sort(probs)[-k]          # 第 k 大的概率
    filtered = np.where(probs >= threshold, probs, 0)
    return filtered / filtered.sum()

def top_p_filter(probs, p):
    """按累计概率截断"""
    sorted_idx = np.argsort(probs)[::-1]
    cumsum = np.cumsum(probs[sorted_idx])
    keep = sorted_idx[cumsum <= p]
    filtered = np.zeros_like(probs)
    filtered[keep] = probs[keep]
    return filtered / filtered.sum()

def sample(probs):
    """按概率分布随机抽一个"""
    return np.random.choice(len(probs), p=probs)

# 实验:不同 temperature 下的分布变化
for temp in [0.2, 0.7, 1.5]:
    probs = softmax_with_temperature(logits, temp)
    print(f"temperature={temp}: 北京={probs[0]:.3f} 上海={probs[1]:.3f} "
          f"东京={probs[3]:.3f} 巴黎={probs[5]:.3f}")

运行结果(示意):

temperature=0.2: 北京=0.987 上海=0.012 东京=0.000 巴黎=0.000
temperature=0.7: 北京=0.728 上海=0.164 东京=0.014 巴黎=0.009
temperature=1.5: 北京=0.433 上海=0.196 东京=0.054 巴黎=0.040

看到没有?温度一高,"北京"从 98.7% 掉到 43.3%,冷门词的机会上来了。 这就是 temperature 的全部秘密。


5.7 组合建议速查表

场景temperaturetop_p惩罚
代码生成0.11(关闭)0
结构化数据抽取010
翻译0.310
智能客服0.50.90.3
文案创作0.90.90.6
头脑风暴1.20.950.6

最后记住一条铁律:先调 temperature 一个参数,不够再加 top-p,最后才考虑惩罚参数。别一上来全调满,你根本不知道是谁的锅。


更多推荐