Top-k 采样
将选择范围限制为概率最高的 k 个词元,以及这种做法对输出多样性的影响。
Top-k 采样 是 CoddyKit 上的免费 AI Prompt Engineering 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Prompt Engineering 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Prompt Engineering 课程共包含 4 节课。
什么是 Top-k 采样?
Top-k 采样会将模型限制在每一步概率最高的 k 个词元中进行采样。Top-k 之外的所有词元都会被赋予零概率,无法被选中。
k=1 是贪心解码(只有概率最高的单个词元)。k=50 是典型的创意范围。k=词汇表大小等同于不受限制的采样。
Top-k 算法
该算法比 Top-p 更简单:
- 计算完整词汇表上的 softmax 概率
- 按概率降序排列词元
- 只保留前 k 个词元;将其他所有词元的概率设为 0
- 重新归一化 Top-k 的概率,使总和为 1
- 从重新归一化后的分布中采样
import numpy as np
def softmax(logits, temperature=1.0):
scaled = logits / temperature
e = np.exp(scaled - np.max(scaled))
return e / e.sum()
def top_k_sample(logits, k=50, temperature=1.0):
probs = softmax(logits, temperature)
# Find top-k indices
top_k_indices = np.argsort(probs)[::-1][:k]
top_k_probs = probs[top_k_indices]
# Renormalize
top_k_probs = top_k_probs / top_k_probs.sum()
# Sample
chosen = np.random.choice(top_k_indices, p=top_k_probs)
return chosen
# With a 10-token vocabulary:
logits = np.random.randn(10)
print('Chosen token:', top_k_sample(logits, k=3))k=1:贪心解码
当 k=1 时,候选集中只有概率最高的单个词元。从大小为 1 的集合中采样是确定性的——模型始终选择该词元。这与贪心解码(温度=0)完全相同。
logits = np.array([3.0, 2.0, 1.0, 0.5, -1.0])
# k=1: greedy
top_1 = top_k_sample(logits, k=1)
print(f'k=1 always picks: {np.argmax(logits)}') # index 0, the highest logit
print(f'top_k_sample result: {top_1}') # always 0
# Multiple runs
for _ in range(5):
print(top_k_sample(logits, k=1), end=' ')
# Output: 0 0 0 0 0 — perfectly deterministic典型创意范围:k=50
k=50 是创意文本生成中常见的默认值。它允许模型在每一步从 50 个词元中进行探索,同时防止模型选择其置信度极低的词元。
当词汇表包含 50,000 个词元时,k=50 意味着模型在每一步只考虑排名前 0.1% 的词元。这是一个相当大的限制——词汇表中的大多数词元都会被排除。
import openai
client = openai.OpenAI(api_key='sk-...')
# Note: OpenAI API does not expose top_k directly in chat completions.
# Top-k is primarily a parameter in Hugging Face Transformers and Anthropic's API.
# Hugging Face example:
from transformers import pipeline
generator = pipeline('text-generation', model='gpt2')
output = generator(
'Once upon a time',
max_new_tokens=100,
do_sample=True,
top_k=50,
temperature=1.0
)
print(output[0]['generated_text'])Anthropic 的 Claude API 中的 Top-k
Anthropic 的 Claude API 将 top_k 作为直接参数公开。这使得尝试固定词汇表截断对 Claude 输出的影响变得十分容易。
import anthropic
claude = anthropic.Anthropic(api_key='sk-ant-...')
# top_k limits the number of tokens considered
message = claude.messages.create(
model='claude-opus-4-5',
max_tokens=256,
temperature=1.0,
top_k=50, # sample from top 50 most probable tokens
messages=[{
'role': 'user',
'content': 'Write a short poem about debugging code.'
}]
)
print(message.content[0].text)固定 k 的问题
Top-k 的根本限制是:无论模型在该步骤的置信度如何,k 都是固定的。
当模型非常确定时(某个词元的概率为 95%),k=50 仍会包含 49 个基本无关的词元。当模型非常不确定时(50 个词元的概率各约为 2%),k=50 可能确实很合适。
问题在于:根据上下文不同,k=50 可能同时过于严格或过于宽松。Top-p 通过动态调整核大小解决了这一问题。
# Illustrating the fixed-k problem
logits_confident = np.array([5.0] + [0.1] * 9) # model is very sure
logits_uncertain = np.array([1.0] * 10) # model has no idea
probs_conf = softmax(logits_confident)
probs_unc = softmax(logits_uncertain)
print('Confident — top 3 tokens cover:', np.sort(probs_conf)[::-1][:3].sum().round(3))
# ~0.998 — k=50 is extremely wasteful, includes near-zero probability tokens
print('Uncertain — top 3 tokens cover:', np.sort(probs_unc)[::-1][:3].sum().round(3))
# ~0.30 — k=50 may actually be needed to cover a reasonable nucleus分布尾部的 Top-k
Top-k 和 Top-p 在分布尾部的差异最为明显:
- 使用 top-k=50 时,第 50 个词元的概率可能只有 0.001%(极不可能,但仍在候选集中)
- 使用 top-p=0.9 时,任何位于 90% 核之外的词元都会被排除,包括原本会进入 Top-k 的词元
Top-p 对尾部行为的处理更合理:它根据概率而不是排名位置来排除低概率词元。
# Tail behavior comparison
import numpy as np
# Highly skewed distribution (one dominant token)
skewed_logits = np.array([4.0, 2.0, 1.5, 1.0, 0.5,
0.1, 0.0, -0.1, -0.5, -1.0])
probs = softmax(skewed_logits)
print('Probability of tokens 6-9 (tail):')
for i in range(6, 10):
print(f' Token {i}: {probs[i]:.4%}')
# These tokens are very unlikely but are included in top-k=10
# Top-p=0.9 would exclude them entirely结合 Top-k 与 Top-p
一些实现会同时应用 Top-k 和 Top-p:先截断到 Top-k,再在该集合中应用 Top-p 核采样。这样既能通过 Top-k 对词汇表大小设置硬上限,又能通过 Top-p 应用基于概率的筛选。
def top_k_top_p_sample(logits, k=50, p=0.9, temperature=1.0):
probs = softmax(logits, temperature)
# First apply top-k
top_k_indices = np.argsort(probs)[::-1][:k]
top_k_probs = probs[top_k_indices]
# Then apply top-p within top-k
sorted_k = np.sort(top_k_probs)[::-1]
cumulative = np.cumsum(sorted_k)
nucleus_size = np.searchsorted(cumulative, p) + 1
final_indices = top_k_indices[:nucleus_size]
final_probs = top_k_probs[:nucleus_size]
final_probs = final_probs / final_probs.sum()
return np.random.choice(final_indices, p=final_probs)何时优先使用 Top-k 而不是 Top-p
尽管 Top-p 在理论上具有优势,但在某些场景中更适合使用 Top-k:
- 词汇表受限的任务:当模型只能从固定集合中输出时(例如多项选择 A/B/C/D),较小的 Top-k(4)可以直接强制实现这一限制
- 可复现性:Top-k 的行为更容易推断——“始终恰好考虑 50 个词元”
- 经过硬件优化的实现:某些推理引擎实现 Top-k 的效率高于 Top-p
# Constrained output with top-k=4
# For a multiple choice task (A, B, C, D)
# If A/B/C/D tokens have indices 32, 33, 34, 35
# top-k=4 with those as the top-4 logits forces selection from those 4 only
multiple_choice_prompt = (
'Answer with only A, B, C, or D.\n'
'What is the capital of France?\n'
'A) Berlin\n'
'B) Paris\n'
'C) Rome\n'
'D) Madrid\n'
'Answer:'
)
# With temperature=0, top_k=1: always picks the highest logit tokenTop-k 实用指南
何时使用 Top-k,以及如何选择其值:
- k=1:贪心 / 事实型任务
- k=5–20:侧重集中的创意任务,变化最小
- k=40–100:大多数语言模型中的标准创意范围
- k=500+:非常开放的探索(很少需要;请改用 Top-p)
在大多数现代 LLM API 中,Top-p 是首选参数。当您需要对词汇表设置硬上限,或 API 提供 Top-k 但不提供 Top-p 时,请使用 Top-k。
TOP_K_GUIDELINES = {
'factual_qa': 1, # greedy
'code_generation': 10, # near-greedy, correct syntax
'summarization': 20, # slightly varied but focused
'chat': 50, # natural variation
'creative_writing': 100, # wider vocabulary exploration
'poetry': 200, # unusual word choices encouraged
}
def call_with_top_k(task, prompt, model='claude-opus-4-5'):
k = TOP_K_GUIDELINES.get(task, 50)
claude = anthropic.Anthropic(api_key='sk-ant-...')
return claude.messages.create(
model=model,
max_tokens=512,
top_k=k,
messages=[{'role': 'user', 'content': prompt}]
)Top-k 与温度结合
Top-k 和温度会按顺序应用:先由温度重新塑造对数几率分布,再由 Top-k 将其截断为概率最高的 k 个词元。在 Hugging Face Transformers 管道中,同时使用二者很常见。
典型组合:温度=0.9(适度多样性)+ Top-k=50(严格限制词汇表)。这样既能避免单独使用高温度时分布过于平坦,也能避免从尾部采样的问题。
from transformers import pipeline
generator = pipeline('text-generation', model='gpt2')
# Combined top-k + temperature
output = generator(
'The future of AI is',
max_new_tokens=80,
do_sample=True,
top_k=50,
temperature=0.9
)
print(output[0]['generated_text'])
# Compare: top-k=1 (greedy)
greedy_output = generator(
'The future of AI is',
max_new_tokens=80,
do_sample=False # greedy, equivalent to top_k=1
)
print(greedy_output[0]['generated_text'])知识检查
在 Top-k 采样中,如果设置 k=1,模型会表现出哪种行为?
回顾:Top-k 采样
Top-k 采样会在采样前将词汇表截断为概率最高的 k 个词元:
- k=1:贪心解码——具有确定性,总是选择概率最高的词元
- k=50:典型的创作范围——在多样性和连贯性之间取得平衡
- 主要限制:无论模型的置信度如何,k 都是固定的——可能过于宽松,也可能过于严格
- 与 top-p 相比:top-p 的动态核心集合会适应置信度,而 top-k 不会
当您需要严格限制词汇表时,请使用 Top-k。对于大多数生产应用,优先选择 top-p。下一课:为您的具体使用场景选择参数。
常见问题解答
「Top-k 采样」课时是免费的吗?
是的 — 「Top-k 采样」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Prompt Engineering 课程的其余内容,请升级到 CoddyKit PRO。 AI Prompt Engineering 课程共包含 4 节课。
「Top-k 采样」这节课中我会学到什么?
将选择范围限制为概率最高的 k 个词元,以及这种做法对输出多样性的影响。 你通过在浏览器中直接运行的动手代码来练习 AI Prompt Engineering,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 AI Prompt Engineering 需要有经验吗?
无需任何先前经验。CoddyKit 上的 AI Prompt Engineering 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「Top-k 采样」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 AI Prompt Engineering 课中编写并运行代码吗?
能。每节 AI Prompt Engineering 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- LLM 中的温度是什么?
- Top-p 核采样
- Top-k 采样
- 为您的使用场景选择参数