本页目录
Token 与采样
模型不读字符,而是一个 token 一个 token 地解码。输入端子词切分决定了成本基线,输出端采样策略决定了创造性和确定性——而 2026 年这两端在 Claude 和本地模型之间已经分叉。
概述
模型不读字符,也不直接吐句子。进的一端,文本被切成 token;出的一端,模型一步只产出"下一个 token 在整个词表上的概率分布",再由一套采样策略挑一个,拼回输入,自回归地一个个生成。这两端平时看不见,却决定了三件实际开发中天天撞上的事:算不算得准成本(计费单位是 token)、调不调得对"创造性"(采样策略)、以及为什么同样的 prompt 两次结果不一样(非确定性)。
更要紧的是:这两端的"常识"在 2026 年分叉了。开源/本地模型仍然靠 temperature/top_p/top_k 调采样(本机跑的 Qwen 就是);而 Claude 的托管 API 从 Opus 4.7 起干脆移除了这些参数——传了直接 400。同一个概念,两个世界,这篇把两端的机制和这道分叉讲透。
输入端:文本怎么变成 token
子词切分与 BPE
现代 LLM 不按字符也不按词切,而是按子词(subword)。最常用的算法是 BPE(Byte Pair Encoding):训练时从字符开始,反复把"出现最频繁的相邻 token 对"合并成一个新 token,直到词表达到设定大小(通常几万到十几万)。结果是:高频词成一个 token,低频词被拆成几个子词。
现代实现多用 byte-level BPE:在字节而非 Unicode 字符上做 BPE。好处是没有 OOV(未登录词)——任何输入(生僻字、emoji、二进制串)都能用字节兜底,绝不会切不出来。代价是非 ASCII(中文、日文)一个字往往占多个字节、多个 token。
几个反直觉但要命的点
- token ≠ word ≠ 字符。 英文约 1 token ≈ 4 字符;中文一个字常 1–2+ token,代码里的标点/缩进也吃 token。按字数估长度会系统性偏低。
- chat template 也吃 token。 多轮对话里,
role标记、消息分隔符、工具定义的 schema 全都被算进 input token——不是只有正文在花钱。 - 不同模型的 token 不通用,且会随版本漂移。 Claude 的分词器 ≠ OpenAI 的;别用
tiktoken估 Claude(少算 15–20%,代码/中文更离谱)。即便同厂,换代也会变:Opus 4.7 的分词器可能把同样文本切成上一代的 1.0–1.35 倍的 token 数,换模型要重新基线,别套旧倍率。 - 要准就调 API,别用第三方近似器:
# 单段计数
= .
# 估成本(以官方定价为准,会变):input_tokens × 单价
# 比较两个版本的 diff:分别 count,相减——端点无状态,自己做差
count_tokens 是无状态的;要算"改了多少 token"就分别计数再相减。
输出端:一个 token 一个 token 地采样
模型每一步输出的不是词,而是整个词表上的分数向量(logits)。经 softmax 变成概率分布,再按策略采样,拼回输入重走——自回归(autoregressive)。
flowchart LR
T["文本"] --> TK["分词器 → tokens"]
TK --> M["模型 (Transformer)"]
M --> L["logits<br/>(词表上每个 token 一个分数)"]
L --> SM["softmax(logits / T)<br/>→ 概率分布"]
SM --> S["采样策略<br/>top_p / top_k / greedy"]
S --> N["下一个 token"]
N -->|拼回输入, 自回归| M
采样旋钮(在仍支持它们的模型上)
| 策略 | 机制 | 效果 |
|---|---|---|
| greedy (argmax) | 每步取概率最高 token | 最确定,但易陷入重复/退化 |
temperature T | softmax 前对 logits 除以 T | T>1 拉平分布→更随机;T<1 变尖→更聚焦;T=0 退化为 greedy |
| top_p (nucleus) | 只在"累计概率达到 p 的最小 token 集"里采样 | 动态截断长尾,随分布形状自适应 |
| top_k | 只在概率最高的 k 个里采样 | 固定截断 |
| repetition / presence penalty | 对已出现 token 降权 | 抑制复读 |
| stop sequence | 命中指定串就停 | 控制输出边界 |
实务上 temperature + top_p 组合最常用:温度控随机度,nucleus 砍掉荒诞长尾。top_k 较粗。
为什么聊天 LLM 不用 beam search
经典机器翻译用 beam search(同时维护多条候选路径取整体最优)。聊天/开放生成几乎不用:它倾向产出高概率但乏味、重复的文本("neural text degeneration"),牺牲多样性和自然度。采样(top_p/温度)反而生成更像人、更有信息量的文本——这是 2020 年 nucleus sampling 论文的核心结论。
为什么 T=0 也不保证逐字节复现
很多人以为"温度 0 = 可复现"。做不到。 原因在硬件与调度,不在采样:
- 浮点不可结合:GPU 上大量并行加法的累加顺序不固定,
(a+b)+c ≠ a+(b+c)在浮点下成立,极小差异会在 argmax 临界处翻转选择。 - 批处理组成:请求和谁拼在一个 batch 里、kernel 怎么调度,都影响数值。
- MoE 路由:MoE 模型 的专家选择对数值更敏感。
"确定性"是工程目标(固定 prompt、低随机、接受微小漂移),不是某个采样参数的保证。
分叉:Claude 删了采样,本地模型还在用
到 Claude Opus 4.7 / 4.8 / Fable 5 这代,temperature、top_p、top_k 被移除——传了直接 400。这不是退化,是设计取向变了:
- 在强推理模型上,用采样温度去"调创造性/确定性"收益有限且不可控。
- Anthropic 转而提供两件更高层的工具:
- prompting——要确定就把指令写死;要发散就在 prompt 里明确要求多样性(例:前端设计场景让模型"先给 4 个不同方向再实现",见迁移文档的 design 部分);
- effort(
output_config.effort)——控制想多深、花多少 token(见 推理与 thinking)。
迁移要点:旧代码里的 temperature=0.7 在新模型上要删掉,把意图转译成 prompt + effort——temperature=0(求确定)→ effort:"low" + 写死指令;temperature=高(求发散)→ prompt 里明确"给出多个不同方向"。这套迁移配方在 提示工程与结构化输出 展开。
但在另一端,采样依然活着。 本地/开源模型(llama.cpp、vLLM)照常吃这些参数——本地 LLM 部署 里就在设。而且模型卡通常给推荐值:Qwen-AgentWorld 官方推荐 temperature 0.6 / top_p 0.95 / top_k 20。所以"删采样"是 Claude 托管 API 的取向,不是行业通则——在哪个世界,就用哪套规则。
最佳实践
- 要准就调
count_tokens,别用第三方近似器。tiktoken估 Claude 少算 15–20%,中文/代码更离谱;chat template 和工具 schema 也吃 token。 - 换模型必重新基线 token。 分词器随版本漂移,别套旧倍率;比较两版差异就分别 count 再相减。
- 在哪个世界,用哪套规则。 本机/开源模型照常调
temperature/top_p/top_k(按模型卡推荐值);Claude 托管 API 已删采样,改用 prompt + effort。 - 本地模型照模型卡设采样值。 如 AgentWorld 推荐 0.6 / 0.95 / 20,别用默认值赌发散度。
- 别指望
T=0逐字节复现。 浮点累加、批处理、MoE 路由都在抖;要严格复现就缓存结果。
权衡与失败模式
- 按字数估 token:中文/代码严重低估 →
count_tokens,并把 chat template/工具 schema 算进去。 - 跨模型套 token 数或旧温度:分词器和采样面都会变 → 换模型重新基线,迁移时删掉采样参数。
- 指望
T=0复现:做不到逐字节一致 → 固定 prompt + 低 effort,接受微小漂移;要严格复现就缓存结果。 - 本地模型不设推荐采样值:用默认温度可能太发散或太呆 → 照模型卡(如 AgentWorld 的 0.6/0.95/20)。
参考
- Anthropic 官方文档: Token Counting、Models API、Migration Guide(采样参数移除)(platform.claude.com)
- 论文: "Neural Machine Translation of Rare Words with Subword Units"(BPE, Sennrich 2016)、"The Curious Case of Neural Text Degeneration"(nucleus sampling, Holtzman 2020)
- 模型卡: Qwen-AgentWorld-35B-A3B(推荐采样值)
Keywords: tokenization, subword, BPE, byte-level BPE, vocabulary, OOV, chat template, token ≠ word, count_tokens, tiktoken, tokenizer drift, logits, softmax, autoregressive, greedy, temperature, top_p, nucleus sampling, top_k, repetition penalty, stop sequence, beam search, text degeneration, 确定性, floating-point non-associativity, 采样参数移除, effort