从 Logits 到 Token:解码采样策略

886 字
4 分钟
从 Logits 到 Token:解码采样策略

Datawhale
Datawhale

大家好,我是芯缘,是 Datawhale 社区发起的 2026 年 8 月“llm-algo-leetcode 推理优化方向”组队学习活动的运营助教。本文记录了我学习Task 3:解码算法 Decoding Strategies的笔记。

基本原理#

大模型最后一步输出的不是文字,而是对词表中每个 token 的未归一化打分,称为 logits。它的形状通常是 [vocab_size],比如 32000 个 token 就对应 32000 个分数。

解码采样的目标,就是把这组 logits 转成下一个 token。常见流程是:

logits截断或平滑softmax采样 / 选择 token\text{logits} \rightarrow \text{截断或平滑} \rightarrow \text{softmax} \rightarrow \text{采样 / 选择 token}

常见策略#

策略核心作用直观影响
Temperature调整 logits 的分布尖锐程度控制生成结果更确定(T<1T<1)还是更随机(T>1T>1)
Top-K只保留概率最高的 KK 个 token删除长尾低概率 token
Top-P / Nucleus保留累计概率达到 pp 的最小 token 集合根据分布形状动态决定候选数量

Temperature#

Temperature 在 softmax 之前对 logits 做缩放:

pi=exp(zi/T)jexp(zj/T)p_i=\frac{\exp(z_i/T)}{\sum_j \exp(z_j/T)}

其中 ziz_i 是第 ii 个 token 的 logit,TT 是温度系数。

  • T<1T<1:分布更尖锐,高分 token 更容易被选中,结果更稳定。
  • T>1T>1:分布更平滑,低分 token 也有更多机会,结果更随机。

Top-K#

Top-K 采样只保留 logits 中分数最高的 KK 个 token,其余 token 的概率置为 0:

zi={zi,iTopK(z,K),otherwisez_i'= \begin{cases} z_i, & i\in \operatorname{TopK}(z, K) \\ -\infty, & \text{otherwise} \end{cases}

然后再对保留下来的 token 做 softmax 和采样。

Top-P#

Top-P 也叫 Nucleus Sampling。它先按概率从大到小排序,然后保留累计概率刚好超过阈值 pp 的最小 token 集合:

S=min{S:iSpip}S=\min \left\{S': \sum_{i\in S'} p_i \ge p \right\}

和固定候选数量的 Top-K 不同,Top-P 会根据当前概率分布自动调整候选 token 数量:

  • 分布越集中,候选越少;
  • 分布越平缓,候选越多。

Claude Code的文字水印技术#

Claude Code 的文字水印,本质上也是发生在“从 logits 到 token”的采样阶段。

普通采样时,模型先把 logits 变成概率分布,然后用随机数从这个分布里抽一个 token:

logitssoftmaxrandom sampletoken\text{logits} \rightarrow \text{softmax} \rightarrow \text{random sample} \rightarrow \text{token}

文字水印的核心想法是: 不往文本里塞隐藏字符,而是把采样时的随机数,换成由 secret key 和上下文共同决定的伪随机数。 这样每一步看起来仍然是在随机采样,但长文本累积下来,会出现只有知道 key 才能检测出来的统计模式。

示意代码:

def keyed_uniform(secret_key, context_tokens):
"""用 secret key + 上下文生成一个稳定的伪随机数。"""
message = f"{secret_key}|{context_tokens}".encode()
digest = hashlib.sha256(message).digest()
value = int.from_bytes(digest[:8], "big")
return value / 2**64
def sample_with_watermark(logits, secret_key, context_tokens):
"""水印采样示意:采样仍按概率分布走,但随机数由 key 和上下文决定。"""
probs = softmax(logits)
u = keyed_uniform(secret_key, context_tokens)
cumulative = np.cumsum(probs)
return int(np.searchsorted(cumulative, u))

只要 secret_keycontext_tokens 不变,伪随机数 u 就是稳定的。检测时,如果一段文本里 token 的选择反复符合这套带 key 的伪随机规则,就可以判断它很可能来自对应模型。

真实的 SynthID-Text 会更复杂,不只是简单固定一个随机数。它会在候选 token 的采样过程中引入带密钥的打分或排序规则,让水印信号分布在很多 token 选择里。但直觉上可以先抓住这一点:水印不是文本层面的标记,而是采样随机性的标记

参考资料#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

从 Logits 到 Token:解码采样策略
https://github.com/datawhalechina/llm-algo-leetcode/blob/main/02_PyTorch_Algorithms/21_Decoding_Strategies.ipynb
作者
平昊阳
发布于
2026-09-07
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
平昊阳
乘长风,破巨浪, 展鸿图于未央!
--
总访问量
--
访客数
公告
欢迎来到我的个人博客!欢迎关注交流吖!
更多相关公告,见
社交-留言」。
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
站点统计
文章
123
分类
20
标签
174
总字数
1,103,045
运行时长
0
最后活动
0 天前

文章目录