day05-llm-sampling-params

LLM 输出不稳定的根因分析:温度 / Top-P / Top-K 调参指南

你有没有遇到这种情况:同样的 Prompt,第一次给出了一段精彩的代码,第二次却输出了完全不同的废话?这不是玄学,是采样参数在作怪。


前言

在实际 LLM 应用开发中,我们最常听到的抱怨之一是:

"模型输出太不稳定了,同一个问题问了三次,三次结果都不一样!"

其实,这种"不稳定"本质上是可控的 。控制它的核心旋钮,就是三个采样参数:Temperature(温度)、Top-P 和 Top-K

本文将从原理出发,结合实测数据和工程经验,帮你彻底搞清楚这三个参数该怎么调。


一、LLM 生成 Token 的底层机制

在理解采样参数之前,先搞清楚 LLM 是如何生成文字的。

1.1 Logits → Softmax → 概率分布

每次 LLM 生成下一个 token,模型会对词表中的每一个 token 计算一个 logit 分数 (未归一化的原始分数),然后通过 Softmax 函数转换为概率分布:

复制代码
P(token_i) = exp(logit_i) / Σ exp(logit_j)

举个例子,假设下一个词的候选分布如下:

Token Logit Softmax 概率
"快速" 3.2 34.2%
"高效" 2.8 23.1%
"迅速" 2.5 17.1%
"优秀" 1.9 9.4%
其他... ... 16.2%

1.2 贪婪解码 vs 随机采样

  • 贪婪解码(Greedy Decoding):每次选概率最高的 token,输出完全确定,但容易陷入重复和平庸。
  • 随机采样(Sampling):按照概率分布随机抽取 token,引入多样性,但会带来不确定性。

采样参数的本质:控制概率分布的形状,从而控制多样性与确定性之间的权衡。


二、Temperature(温度)深度解析

2.1 数学原理

Temperature 通过对 logit 做缩放来改变 Softmax 的"尖锐程度":

复制代码
P(token_i) = exp(logit_i / T) / Σ exp(logit_j / T)

其中 T 就是 Temperature 参数。

2.2 三种典型状态

T = 1.0(默认)

原始概率分布不变,保持模型训练时的自然分布。

T < 1.0(低温)

logit 被放大,Softmax 输出更"尖锐",高概率 token 被强化,低概率 token 被压制。结果更确定,更保守,更少创意。

T > 1.0(高温)

logit 被压缩,Softmax 输出更"平坦",各 token 概率趋于均等。结果更随机,更多样,但也更容易出现无意义输出。

T → 0(极限情况)

等价于贪婪解码,每次选最高概率 token,输出完全可复现。

T → ∞(极限情况)

所有 token 概率相等,纯随机选择,输出完全混乱。

2.3 实测对比

用同一 Prompt 测试不同 Temperature,问题:"用一句话描述 RAG"

Temperature 输出示例
T=0.0 "RAG 是一种将检索与生成相结合的 AI 技术架构。"
T=0.3 "RAG 是在 LLM 生成时动态注入外部知识的技术,有效减少幻觉。"
T=0.7 "RAG 就像给 LLM 配了一个实时百科全书,想要啥知识随时查随时用。"
T=1.0 "RAG 将检索与生成解耦,是当前企业知识库落地的核心路径之一。"
T=1.5 "RAG 是让模型在知识的海洋中冲浪,每次都能带着新鲜的浪花生成答案。"
T=2.0 "检索生成的聚合体,一种特殊的神经符号融合算法接口。"(开始出现语义漂移)

2.4 调参建议

场景 推荐 Temperature
代码生成、SQL 查询、数据提取 0.0 ~ 0.2
事实问答、信息检索 0.1 ~ 0.3
通用对话、解释说明 0.5 ~ 0.7
创意写作、头脑风暴 0.8 ~ 1.2
诗歌、小说、极度创意 1.2 ~ 1.8

三、Top-K 采样

3.1 原理

Top-K 在采样前,只保留概率最高的 K 个 token,其余全部置为 0,然后对这 K 个 token 重新归一化再采样。

python 复制代码
# 伪代码
logits_sorted = sort(logits, descending=True)
# 只保留前 K 个
logits_filtered = logits_sorted[:K]
# 重新归一化
probs = softmax(logits_filtered)
# 从这 K 个 token 中采样
next_token = sample(probs)

3.2 优缺点

优点

  • 避免了极低概率 token 被意外选中,防止"灾难性输出"
  • 控制候选集大小,计算相对稳定

缺点

  • K 值是固定的,不考虑实际的概率分布形态
  • 当模型高度确定(某个 token 概率 99%)时,K=50 仍强行保留 49 个低质量候选
  • 当概率分布较为均匀时,K=10 又会过早截断合理候选

3.3 调参建议

场景 推荐 Top-K
高确定性任务(代码、SQL) 1 ~ 10
通用对话 40 ~ 60
创意写作 80 ~ 100
关闭 Top-K(配合 Top-P 使用) 0(禁用)

四、Top-P(Nucleus Sampling)

4.1 原理

Top-P 是 Top-K 的"自适应版本",由 Holtzman et al. 2019 提出。它不是固定保留 K 个 token,而是保留累积概率恰好超过 P 阈值的最少 token 集合

python 复制代码
# 伪代码
probs_sorted = sort(softmax(logits), descending=True)
cumsum_probs = cumulative_sum(probs_sorted)
# 找到累积概率超过 P 的截断点
cutoff_idx = first_index_where(cumsum_probs >= P)
# 只保留前 cutoff_idx+1 个 token
probs_filtered = probs_sorted[:cutoff_idx+1]
probs_filtered = renormalize(probs_filtered)
next_token = sample(probs_filtered)

4.2 为什么 Top-P 优于 Top-K?

用一个例子说明:

高确定性情况(模型很确定下一个词):

Token 概率
"是" 82%
"为" 10%
"在" 5%
其他 3%
  • Top-K=50:强行保留 50 个候选,大量低质量 token 混入
  • Top-P=0.9:只保留前两个 token(92% > 90%),候选集精准

低确定性情况(模型不确定,分布平坦):

Token 概率
"构建" 8%
"设计" 7%
"实现" 7%
"开发" 6%
其他... 72%
  • Top-K=5:只保留 5 个,过早截断很多合理候选
  • Top-P=0.9:自动扩展到包含足够多的候选,保留多样性

Top-P 的核心优势:根据模型的确定性程度,自适应调整候选集大小。

4.3 调参建议

场景 推荐 Top-P
高确定性任务(代码、SQL、事实问答) 0.1 ~ 0.5
通用对话 0.7 ~ 0.9
创意写作 0.9 ~ 0.95
极限多样性 0.99 ~ 1.0

五、三个参数的组合策略

实际工程中,Temperature、Top-K、Top-P 通常组合使用,顺序如下:

复制代码
原始 Logits
    ↓
[Temperature 缩放]  → 调整分布尖锐度
    ↓
[Top-K 过滤]       → 截断极低概率候选(可选)
    ↓
[Top-P 过滤]       → 自适应截断(主要控制)
    ↓
[重新归一化 + 采样]  → 最终选取 token

5.1 主流模型默认配置

模型 Temperature Top-P Top-K 备注
GPT-4o 1.0 1.0 - 推荐用户自行调整
Claude 3.5 1.0 - - 通常配合 Temperature 使用
Gemini 1.5 1.0 - 40 默认 Top-K=40
Llama 3.1 0.6 0.9 - Meta 推荐配置
Qwen2.5 0.7 0.8 - 阿里推荐配置
DeepSeek-V3 1.0 - - 基础温度默认

5.2 推荐最佳实践组合

python 复制代码
# 代码生成 / SQL / 结构化输出
config_precise = {
    "temperature": 0.1,
    "top_p": 0.3,
    "top_k": 10,
}

# 通用 RAG 问答
config_rag = {
    "temperature": 0.3,
    "top_p": 0.7,
    "top_k": 40,
}

# 普通对话
config_chat = {
    "temperature": 0.7,
    "top_p": 0.9,
    "top_k": 0,  # 禁用 Top-K,仅用 Top-P
}

# 创意写作
config_creative = {
    "temperature": 1.1,
    "top_p": 0.95,
    "top_k": 0,  # 禁用 Top-K
}

六、常见陷阱与反直觉结论

陷阱 1:Temperature=0 并不意味着"最准确"

温度为 0 只是确定性最高,但"最可能的 token" ≠ "最正确的答案"。模型如果在 Pretraining 时学到了错误知识,Temperature=0 会更坚定地输出错误答案。

陷阱 2:Top-P=1.0 不等于"关闭 Top-P"

Top-P=1.0 表示保留 100% 累积概率,即保留所有 token,相当于不过滤。这与"关闭 Top-P"的效果一致,但并不等于关闭了采样机制。

陷阱 3:同时设置高 Temperature 和低 Top-P 效果混乱

python 复制代码
# 反例:不要这样做
bad_config = {
    "temperature": 1.8,  # 高温让分布非常平坦
    "top_p": 0.1,        # 低 Top-P 只保留前 10% 概率的 token
}
# 结果:高温使所有 token 概率接近均匀,Top-P=0.1 截断后可能只剩 1-2 个 token
# 随机性极低,但质量也极低

原则:Temperature 和 Top-P 应该同向调整------低温配低 Top-P,高温配高 Top-P。

陷阱 4:Top-K 和 Top-P 不要同时很小

python 复制代码
# 反例
bad_config_2 = {
    "temperature": 0.7,
    "top_p": 0.5,   # 已经很保守
    "top_k": 5,     # 又加了 Top-K=5
}
# 结果:双重截断,候选集可能只剩 2-3 个 token,输出非常单调

七、实战调参 SOP

7.1 确定任务类型

复制代码
任务需要确定性输出?(代码/SQL/数据提取)
    → 是:Temperature=0~0.2, Top-P=0.1~0.5
    → 否:继续

需要遵循格式(JSON/Markdown/表格)?
    → 是:Temperature=0.1~0.3, Top-P=0.5~0.7
    → 否:继续

普通对话/问答?
    → 是:Temperature=0.5~0.7, Top-P=0.8~0.9
    → 否:创意任务:Temperature=0.9~1.2, Top-P=0.95

7.2 A/B 测试方法

python 复制代码
import statistics

def evaluate_stability(prompt, config, n=20):
    """测试给定配置的输出稳定性"""
    outputs = [call_llm(prompt, **config) for _ in range(n)]
    
    # 计算语义相似度(用 embedding cosine similarity)
    similarities = [cosine_sim(outputs[0], o) for o in outputs[1:]]
    
    return {
        "mean_similarity": statistics.mean(similarities),
        "std_similarity": statistics.stdev(similarities),
        "min_similarity": min(similarities),
    }

# 测试两种配置
config_a = {"temperature": 0.7, "top_p": 0.9}
config_b = {"temperature": 0.3, "top_p": 0.7}

result_a = evaluate_stability("解释一下 Transformer 的注意力机制", config_a)
result_b = evaluate_stability("解释一下 Transformer 的注意力机制", config_b)

print(f"Config A - 平均相似度: {result_a['mean_similarity']:.3f}, 标准差: {result_a['std_similarity']:.3f}")
print(f"Config B - 平均相似度: {result_b['mean_similarity']:.3f}, 标准差: {result_b['std_similarity']:.3f}")

7.3 生产环境建议

python 复制代码
# 推荐做法:不同场景维护独立的配置文件
SAMPLING_CONFIGS = {
    "code_gen": {"temperature": 0.1, "top_p": 0.3},
    "qa_factual": {"temperature": 0.2, "top_p": 0.5},
    "qa_general": {"temperature": 0.6, "top_p": 0.85},
    "chat": {"temperature": 0.8, "top_p": 0.9},
    "creative": {"temperature": 1.1, "top_p": 0.95},
    "brainstorm": {"temperature": 1.3, "top_p": 0.98},
}

def get_llm_config(task_type: str) -> dict:
    return SAMPLING_CONFIGS.get(task_type, SAMPLING_CONFIGS["chat"])

八、补充:Repetition Penalty 和 Frequency Penalty

除了三个核心参数,还有两个与输出质量密切相关的参数:

8.1 Repetition Penalty

来自 Hugging Face transformers,取值范围 [1.0, 无穷):

  • = 1.0:无惩罚(默认)
  • 1.0:对已生成的 token 降低概率,避免重复

  • 推荐:1.1 ~ 1.3(过高会导致模型绕开常见词,输出奇怪)

8.2 Frequency Penalty / Presence Penalty(OpenAI)

OpenAI API 特有:

  • Frequency Penalty :按 token 已出现频率降低概率,值越高越避免高频重复
    • 范围:-2.0 ~ 2.0,推荐 0.1 ~ 0.4
  • Presence Penalty :按 token 是否出现过降低概率(二值惩罚),鼓励引入新话题
    • 范围:-2.0 ~ 2.0,推荐 0.1 ~ 0.5

总结

参数 作用 推荐默认值
Temperature 控制整体随机性/多样性 0.7(通用对话)
Top-K 固定候选集大小 40(通用)/ 0(禁用,配合 Top-P)
Top-P 自适应候选集大小 0.9(通用对话)
Repetition Penalty 防止重复输出 1.1 ~ 1.2

三条核心原则:

  1. 确定性任务低温低 P:代码、SQL、提取用 T=0.1, P=0.3
  2. Top-P 优于 Top-K:自适应候选集比固定 K 更合理
  3. 同向调整:Temperature 和 Top-P 要保持一致方向,不要互相对抗

掌握这三个旋钮,你就掌握了 LLM 输出质量的主动权。下次再遇到"输出不稳定"的问题,先查采样参数,90% 的情况都能找到根因。


关注专栏,持续更新 LLM 工程实战干货。下篇预告:本地跑 LLM 哪家强?Llama / Qwen / DeepSeek 全方位对比

相关推荐
火山引擎开发者社区19 分钟前
龙虾突然“罢工”?别慌,我们派出了“AI 医生”
人工智能
NQBJT23 分钟前
青鸾云步:基于 Cordova 的 AI 导盲机器人 APP 全栈开发实战
人工智能·app·导盲·轮足机器人·青鸾云步
深兰科技1 小时前
韩国KAIST AI半导体高管项目代表团到访深兰科技,聚焦AI算力与智能产业合作机会
人工智能·机器人·symfony·ai算力·深兰科技·韩国科学技术院·kaist
快乐on9仔1 小时前
NLP学习(一)transformers之pipeline体验
人工智能·深度学习
冬奇Lab1 小时前
Agent系列(六):记忆管理——让 Agent 记住重要的事
人工智能·agent
冬奇Lab1 小时前
一天一个开源项目(第113篇):notebooklm-py - 把 Google NotebookLM 变成可编程 API,还能接入 Claude Code
人工智能·google·开源
字节跳动开源2 小时前
Viking AI 搜索 CLI 正式发布:会说话,就能做搜索推荐
数据库·人工智能·开源
阿杰技术2 小时前
AI 编程助手落地实战:从提效到重构的全场景指南
人工智能·重构
Agent手记2 小时前
制造业生产流程自动化,Agent需要具备哪些能力?深度拆解2026工业级智能体落地范式与核心架构
大数据·人工智能·ai·架构·自动化