Token、概率与采样

本页目录

同一段文字如何变成模型输入,下一段文字又如何从分数中产生?先沿“文本 → token ID → logits → 概率 → 选取”走一遍,再比较温度与候选截断。这里的数值例子只解释解码机制;参数好坏要由具体任务验证。

从文本到 token ID

token 是词表单位,不等于字符或单词

文本模型通常把字符串编码成整数序列,每个整数对应词表中的一个 token。一个 token 可以表示完整词、词的一部分、标点、空格组合或字节片段。一个 Unicode 字符也可能跨多个 token;反过来,多个字符可能合成一个 token。因此不能靠“汉字数乘二”计算所有模型的输入。

BPE 是常见的子词构造方法。训练时从较小单位开始,根据语料统计反复合并相邻单位;编码时使用已经训练好的词表与合并规则,而不是对每次输入重新训练。例如,⁠假设已学到按序合并 (l,o)→lo、(lo,w)→low,l o w e r 就可得到 low e r。这只是机制示例,不代表任何现成模型对 lower 的实际切分。

byte-level BPE 使用可覆盖字节的基础表示,可以避免纯字符词表缺少某个字符的问题,但“BPE”这个名字本身不保证字节回退。分词还可能使用 Unigram、WordPiece 等方法,配置中的规范化、预切分、特殊 token 也会影响最终结果。Tokenizers 模型类型

为什么只计算正文还不够

聊天系统需要把角色和消息边界编码进模型输入。工具定义、结构化内容、图像等也可能产生额外计数。用户看到的字符串长度与模型输入长度属于不同表示层:先确定完整请求如何构造,再计算其 token 数。

例如,两条用户消息与把两段文本合成一条消息,正文字符可以完全相同,但消息边界可能不同。若 tokenizer 含规范化处理,外观相同或近似的 Unicode 字符序列也可能产生不同结果;不能假设所有 tokenizer 都保留或都消除这种差异。分词流水线

服务端计数应使用目标模型对应的接口,并用真实响应 usage 校准。Claude 的计数文档也说明计数结果是估计,模型换代后应重新计数;不存在对所有中文、代码和工具 schema 都成立的跨模型换算倍率。Claude Token counting

从 logits 到概率

标准自回归文本生成在当前前缀条件下预测下一个 token。模型输出词表各位置的分数 logits;解码器再依据配置选一个 ID,将它接到前缀后继续。实际推理引擎可以使用批处理或推测解码优化执行,但条件生成的语义仍可用这个逐步过程理解。

当前 token 序列提示 + 已生成内容模型前向计算输出下一位置 logits处理与选择温度、过滤、采样选中的 token ID解码后逐步形成可见文本一次自回归生成:分数、候选集合与选择是不同步骤停止条件由结束标记、长度限制或接口规则决定;可见文本块不一定等于一个 token。

对正温度 T,softmax 的一个数值稳定写法是:

pᵢ = exp((zᵢ − max(z))/T) / Σⱼ exp((zⱼ − max(z))/T)

减去最大值不会改变归一化后的概率,却能避免直接计算巨大指数。设只有 A、B、C 三个候选,logits 为 [2, 1, 0]:

温度ABC
T=166.52%24.47%9.00%
T=0.586.68%11.73%1.59%
T = 1A 66.52% B 24.47% C 9.00%T = 0.5A 86.68% B 11.73% C 1.59%降温没有新增证据,也没有提高模型知识;它使已有高分候选更容易被选中。相同 logits = [2, 1, 0],温度改变概率集中程度

降温放大相对分数差异,升温使分布更平坦。这改变选择的随机性,不是“正确率旋钮”:若错误答案本来分数最高,降温会更稳定地选择它。整个回答也不是抽样一次后写出,早期 token 的变化会改变后续条件分布。

候选截断:top-k 与 top-p

top-k 保留分数最高的 k 个候选;top-p 先按概率排序,保留累计概率首次达到阈值 p 的最小前缀,然后在保留集合内重新归一化。后者的集合大小会随分布改变。

对上面的 T=1 分布,top-p=0.8 时 A 单独只有 0.6652,不够;加入 B 后达到约 0.9100,因此保留 A、B,丢弃 C。再归一化得到 A≈0.7311、B≈0.2689。top-p=0.8 不是保留 80% 的词表,也不是让最高候选拥有 80% 概率。

在 T=0.5 时,A 一项已超过 0.8,同样阈值就只保留 A。因此温度与过滤顺序会共同影响结果。不同引擎还可能组合惩罚、最小保留数量和其他过滤器;要核对具体实现的顺序,不能把不同服务上的同名参数视为完全相同。Transformers 生成配置

下面的独立例子复现表中的数值,并明确采用“温度后再做 top-p”的顺序;无需下载模型。

import math

def softmax(logits, temperature=1.0):
    if not logits or temperature <= 0:
        raise ValueError("需要非空 logits 和正温度")
    peak = max(logits)
    weights = [math.exp((z - peak) / temperature) for z in logits]
    total = sum(weights)
    return [w / total for w in weights]

def nucleus(probs, threshold):
    if not 0 < threshold <= 1:
        raise ValueError("阈值应在 (0, 1] 内")
    kept, mass = [], 0.0
    for i in sorted(range(len(probs)), key=lambda i: (-probs[i], i)):
        kept.append(i)
        mass += probs[i]
        if mass >= threshold:
            break
    return {i: probs[i] / mass for i in kept}

p = softmax([2, 1, 0])
q = nucleus(p, 0.8)
assert list(q) == [0, 1]
assert math.isclose(q[0], 1 / (1 + math.exp(-1)))
assert nucleus(softmax([2, 1, 0], 0.5), 0.8) == {0: 1.0}
assert math.isclose(sum(p), 1.0)
assert len(nucleus(p, 1.0)) == 3
print([round(x, 4) for x in p])
print({i: round(v, 4) for i, v in q.items()})
# [0.6652, 0.2447, 0.09]
# {0: 0.7311, 1: 0.2689}

这个实验验证概率变换,不证明某个阈值在实际任务上更好。nucleus sampling 的原始研究讨论了开放文本生成中概率尾部与退化问题;其结果不能直接推出翻译、代码、分类等所有任务都该采用同一解码方式。The Curious Case of Neural Text Degeneration

候选概率怎样变成一次选择?

把概率看成 [0,1) 上的区间,抽样就是定位 u 落在哪段。过滤后必须重新分配这些区间;删去 C 却不归一化,会留下没有候选的空隙。变化的是给定分数下的选择机制,图中没有“正确答案概率”的含义。

正在呈现知识画面
候选概率怎样变成一次选择?

top-p 保留累计质量达到阈值的最小前缀,再归一化;抽样位置决定本次落入哪一项。

贪心选择与确定性

greedy 每一步取最高分候选。T=0 时上面的除法公式无定义;一些接口把零温度约定为贪心解码,另一些要求通过单独参数关闭采样。应区分数学极限与接口约定。

贪心只保证局部选择。假设第一步 A=0.6、B=0.4,选 A 后最佳下一步概率是 0.5,选 B 后最佳下一步是 0.9,那么两条候选路径概率分别为 0.30 与 0.36。逐步贪心会先选 A,却没有得到这两条路径中概率更大的那条。beam search 会保留多条候选来缓解这类问题,但仍受搜索宽度、长度偏置和评分目标限制;序列概率最高也不等于事实最正确。

对完全相同的 logits,固定平局处理的 argmax 是确定的;真实服务是否能再次产生完全相同的 logits,则取决于模型版本、精度、内核、批处理和确定性设置。固定 seed 也只控制相应随机数过程,不能锁住全部执行条件。低温度不能单独承诺逐字节复现,低 effort 更不是确定性的替代设置。

对审计或回归测试,保存完整输入、模型与生成配置、服务版本信息以及原始输出。若必须重现之前展示的内容,应读取那次的保存结果;这与重新运行模型得到同一答案是两种保证。

每一步最大,整条路径也最大吗?

局部选择只比较当前前缀下的候选。把 B 的后续概率移过 0.75,观察完整路径的排序反转,而贪心首步仍为 A。这里只比较概率目标,不能进一步推断哪条回答更真实。

正在呈现知识画面
每一步最大,整条路径也最大吗?

首步 A=0.6 大于 B=0.4;当 B 的后续概率超过 0.75,B 的最佳两步路径反而更高。

输出停止与格式约束

控制它约束什么没有保证什么
EOS 或结束标记模型选择结束任务已经正确完成
最大生成长度输出规模上限JSON、代码块一定完整
stop sequence指定边界处停止不会误截断正文中的同样字符串
格式或 schema 约束允许的语法、字段或类型范围字段值在业务上真实、合法
重复惩罚调整已出现 token 的分数所有重复都不必要

例如,生成订单 JSON 时,schema 可以要求 quantity 是整数,却未必知道仓库实际只剩 2 件。输出 {"quantity": 5} 可以语法有效而业务无效。客户端仍要检查停止原因、完整解析、执行字段校验,并在业务层验证库存和权限。

重复惩罚也有场景代价:代码中的变量名、JSON 字段或固定术语本就需要反复出现,过强惩罚可能破坏一致性。选择采样参数前,先确认输出协议与任务约束,结构化输出的完整流程见 提示工程与结构化输出。

迁移时重新建立基线

换模型时分别确认 tokenizer、聊天模板、支持的生成参数及其默认值,再在相同任务集上比较长度、格式通过率、任务正确率和结果波动。不要根据“本地”或“托管”就推断它一定支持或不支持某个参数;以具体模型和端点为准。Claude 迁移指南

如果新接口不再提供 temperature,先移除不支持的字段,再用清晰任务约束和评估确认输出行为。effort 调整推理投入,temperature 调整采样分布,两者不能数值映射。 要求“给四个不同方案”能表达输出目标,但不等价于升温;要求“只输出一个标签”也不等价于降低 effort。推理投入的独立权衡见 推理与 thinking。

继续阅读:注意力、前馈网络与 MoE。