SamplingBatchInfo、vocab mask 与采样元数据流#
这章解决什么问题#
执行模型前面的章节已经解释了 logits、sampler、grammar sync 和 output processing,但还有一层真正把“请求侧采样参数”压成“执行侧采样元数据”的桥没有单独展开:SamplingBatchInfo。如果不把这层讲出来,读者会知道 SamplingParams 很重要,也会知道 sampler 最后拿到了若干 tensor,却不清楚中间到底发生了哪一次批量化压缩。
这一章专门解释这条元数据流。
为什么 SamplingBatchInfo 值得单独讲#
因为它是执行循环里非常典型的“中间抽象”:
- 向前,它承接每个
Req上零散的SamplingParams - 向后,它把这些参数压成 sampler 真正消费的 batched tensor 与 mask
也就是说,它不是又一个普通数据类,而是 execution model 在采样边界上的正式收敛层。
一张图:采样元数据是怎样被压缩成 batch 级对象的#
这张图解决的理解障碍是:很多读者会觉得“每个请求有一组采样参数,sampler 再逐条取出来用”,而源码其实早就把它们收敛成批量对象了。
flowchart LR
Req["per-request SamplingParams"] --> SBI["SamplingBatchInfo.from_schedule_batch()"]
SBI --> Tensors["temperatures / top_ps / top_ks / min_ps"]
SBI --> Penalty["penalizer / logit_bias / custom processor"]
SBI --> Grammar["grammars / vocab_mask"]
Tensors --> Sampler["Sampler.forward()"]
Penalty --> Sampler
Grammar --> Sampler图比纯文字多解释的一点是:采样不是把一堆 request 参数逐条搬进 sampler,而是先做一次批量语义归并。
from_schedule_batch(...) 真正做了什么#
sampling_batch_info.py 的 from_schedule_batch(...) 是这条桥的核心入口。它至少会做这些事:
- 从每个 request 收集
temperature、top_p、top_k、min_p - 生成 batched tensor,例如
temperatures、top_ps、top_ks、min_ps - 计算
is_all_greedy、need_top_p_sampling、need_top_k_sampling、need_min_p_sampling - 生成
sampling_seed - 处理
logit_bias - 构造 custom logit processor 的 batched 视图
- 初始化 penalty orchestrator
这说明 SamplingBatchInfo 真正承担的是“把每请求参数空间降维成批量采样执行计划”。
为什么 is_all_greedy、need_top_p_sampling 这些布尔值很重要#
它们看起来只是小优化字段,但实际上直接决定 sampler 会走哪条路径。例如:
- 全 greedy 时可以直接
argmax - 没有 top-p/top-k/min-p 时可以走 simple sampling case
也就是说,SamplingBatchInfo 不只在搬运数据,它还在提前做一次执行路径分类。
vocab_mask 在这一层怎样进入采样#
前面结构化生成章节已经解释了 grammar backend 如何生成 allocate_vocab_mask(...) / fill_vocab_mask(...) / apply_vocab_mask(...)。在执行模型里,真正把它接进采样的是:
self.grammarsupdate_regex_vocab_mask()self.vocab_maskself.apply_mask_func
这里的关键在于:
- 先从 grammar 列表里找一个可用 grammar 作为 mask 分配器
- 为整个 batch 分配 mask tensor
- 再逐 request 调
fill_vocab_mask(...) - 最后把 mask 移到执行设备上
这意味着 grammar 对 execution 的影响,不是抽象意义上的“限制输出”,而是非常具体的“修改 batch 级采样可见词表”。
apply_logits_bias(...) 为什么是 execution model 的一个重要关口#
这段函数把多条采样侧信号串在了一起:
- additive penalties
- scaling penalties
- penalizer orchestrator
- vocab mask
- logit bias
这说明 sampler 真正拿到的 logits,并不是模型前向直接吐出来那一版,而是已经经过一轮“批量采样语义变换”的 logits。书里把这层讲出来,能明显降低读者对“为什么最终采样结果和裸 logits 不一样”的困惑。
custom logit processor 为什么也在这里汇总#
SamplingBatchInfo 不只是处理内建 top-p/top-k/min-p。它还会把 custom logit processor 按字符串归类,再构造对应 mask tensor。也就是说,系统已经把“用户自定义 logits 变换”也纳入了这套 batched metadata 模型,而不是把它留在每请求的孤立逻辑里。
这进一步说明:SamplingBatchInfo 是采样语义的总汇点,而不是某个单独功能的容器。
SamplingBatchInfo 为什么属于执行模型,而不是 sampling 参数文档附录#
因为这里讨论的不是“字段列表有什么”,而是:
- 这些字段如何被批量化
- 如何改变 execution path
- 如何转成 sampler 真正消费的 tensor 和 mask
这是很典型的 execution model 关注点,而不是 API 文档关注点。
这一层最容易出现的误判#
1. 把 SamplingParams 直接等同于 sampler 输入#
中间还隔着一层 SamplingBatchInfo 的批量化压缩。
2. 把 grammar mask 看成 sampler 额外外挂#
它在这里已经成为 batched sampling metadata 的正式一部分。
3. 以为 top-p/top-k/min-p 的存在只影响数学公式#
它们还会改变 sampler 走哪条执行路径。
如果你要顺着采样边界读源码,先怎么走#
建议按这个顺序:
- 先看
SamplingParams - 再看
SamplingBatchInfo.from_schedule_batch(...) - 再看
update_regex_vocab_mask()与apply_logits_bias() - 最后再看
Sampler.forward()如何消费这些 batched 元数据
小结#
这一章真正要补齐的,是 execution model 里非常关键的“采样收敛层”:
SamplingBatchInfo把每请求采样语义收敛成 batch 级执行元数据- grammar mask、penalty、custom processor 都在这一层汇合
- 只有把这层讲透,sampler 才不会显得像一个突然拿到很多 tensor 的黑箱
到这里,执行模型对“采样元数据怎样流动”才真正完整。
叶王 © 2013-2026 版权所有。如果本文档对你有所帮助,可以请作者喝饮料。