English interface selected · this article is currently written in Chinese.
训练块怎样组成一个 batch:打包、边界与有效 token
English overview →具备清晰骨架、初步数学推导或工程验证,持续补充论据与实测。
数字花园
🌿 什么是数字花园与思想演进?
遵循 Maggie Appleton 与 Andy Matuschak 的常青笔记理念:这里不是按时间沉底的流水账,而是持续修剪打磨的知识生态。文章按成熟度经历:
- 🌱 萌芽 (Seedling): 发散灵感、读书摘录、开放猜想
- 🌿 演进 (Budding): 结构渐明、初步代码推导、积极修正
- 🌳 常青 (Evergreen): 逻辑闭环、持久经得起检验的基石
- 🔬 实验待证 (Speculative): 结合沙盒与仿真探针的理论探索
模型每次需要接收一批形状合适的输入,可原始文章长短不一:一条只有十几个 token,另一条有几千个。怎么把它们放到一起,往往被当作训练前的杂务。
但这个步骤会改变模型看见的上下文,也会改变哪些目标参与更新。一个打包程序可以运行得很快,同时悄悄丢掉文档末尾,或让下一篇文章的开头依赖上一篇的结尾。
batch 的构造既是效率问题,也是训练目标的一部分。 本篇沿用前面的自回归预测任务,把几个容易混淆的决定拆开。
先固定一组小到能逐项核对的数据
假设两篇短文已经完成分词,各自加上开始和结束标记:
文档 A:[BOS] 甲 乙 [EOS]文档 B:[BOS] 丙 [EOS]A 占四个输入位置,B 占三个。它们合计七个非填充位置,但独立训练时,A 有三个下一个 token 目标,B 有两个,合计五个有效目标。
这个区别值得先停一下:输入 token 数与有效预测目标数并不总相等。 开始标记、末尾位置、padding 和忽略标签都会影响计数。
本文使用“完整序列输入,内部错位一次”的约定。标签表中列的是与输入同位置保存的标签;真正计分时,再由模型内部把前一个输出与后一个标签对齐。若你的实现已经在外部错位,需要转换成相应形式,不能照抄两遍。
第一种办法:独立放置,补齐长度
把 B 补到四个位置,两行就能组成矩形张量:
| 项目 | A | B |
|---|---|---|
| 输入 | BOS / 甲 / 乙 / EOS | BOS / 丙 / EOS / PAD |
| 非填充标记 | 1 / 1 / 1 / 1 | 1 / 1 / 1 / 0 |
| 标签 | 忽略 / 甲 / 乙 / EOS | 忽略 / 丙 / EOS / 忽略 |
每行的第一个标签不会被本例的内部错位取到,这里仍显式写成忽略,便于后面保持边界约定一致。模型在 EOS 位置不需要学习预测 PAD。
输入的非填充占比是 ,而有效目标数是五。不能把八个格子全部记成有效训练 token。
实际工具的 data collator 可以根据 batch 中最长序列动态补齐。比如 Transformers 4.57.1 的相关文档区分了动态 padding 与语言建模标签处理;因果语言建模配置中会忽略 padding 标签。Data Collator 文档
这里只补一格很省,但若一条长两千、另一条长二十,浪费就明显。按近似长度分组可以减少填充。不过分组会改变样本顺序,若长文与某个领域高度相关,就需要检查训练顺序是否形成了额外偏向。
第二种办法:把多篇文档装进同一个块
假设允许块长为七,把两篇连起来:
位置: 0 1 2 3 4 5 6输入: BOS 甲 乙 EOS BOS 丙 EOS文档: A A A A B B B没有 padding 了,但现在必须回答两个新问题:
- B 中的位置是否允许读取 A?
- A 的 EOS 位置是否要预测 B 的 BOS?
如果只使用普通下三角因果遮罩,B 可以读取整个 A。如果标签没有额外处理,还会把 A 的 EOS 到 B 的 BOS 当作一个预测目标。
这可以是一种有意设计的连续文本流目标,但它与独立文档目标不同。不能只说“packing 不改变训练内容”,然后忽略上下文和目标已经改变。
EOS 是一个 token,不是一堵墙
模型可以学会 EOS 的统计意义,例如一段文本结束后常出现什么。但普通注意力计算不会因为见到这个编号,就自动把更早的内容清除。
若希望两个文档相互独立,注意力的允许条件应该同时满足:目标位置不晚于当前查询位置,而且它们属于同一篇文档。
设查询位置为 、被读取位置为 ,文档编号为 ,允许读取可写成:
有效位置之外还要排除 padding。这样形成的是每篇文档内部各自为下三角的遮罩,而不是一整个大下三角。
对上面的例子,B 的“丙”只读取 B 的 BOS 和“丙”本身,不能读取 A。实现可以用显式遮罩,也可以用支持分段序列的高效接口;不要求为了教学公式真的在所有场景里分配一张巨大矩阵。
同时,把标签中 B 的 BOS 位置设为忽略,避免 A 的 EOS 被要求预测下一篇的开始。这样两个文档的有效目标仍是三个加两个,共五个。
注意:屏蔽跨文档注意力和屏蔽跨文档目标,是不同操作。只做其中一个,未必得到预期的独立文档训练。
位置编号也要与目标一致
如果模型采用绝对位置嵌入,而独立训练 B 时编号从零开始,那么为了复现同样的输入语义,打包后的 B 也应使用自己的位置编号,例如 0,1,2。
若直接使用整个块的编号 4,5,6,即使跨文档注意力已屏蔽,B 的位置嵌入也与独立训练不同。
纯粹依靠相对位移的某些注意力设计,对整段共同平移可能具有相应不变性,但不能据此假设所有模型、所有位置缩放或缓存实现都一样。要比较“独立处理”和“独立打包”是否等价,应明确 position IDs、边界、随机性和算子,而不是只看 token 序列。
这也是为什么最初调通流程时,独立补齐通常容易解释:它未必最快,却少一些边界变量。等目标正确后,再测优化后的打包是否保留了预期行为。
长文切块:末尾和跨块目标去哪了
短文可以拼接,长文则要切开。假设一篇文档有十个 token,块长为四。
一种简单处理是得到四、四、二三个片段,然后丢掉最后两个。此时模型每轮都少见两成内容。如果文档都按某种格式组织,结尾经常包含结论、答案或异常处理,丢失就不只是随机少读一点。
保留最后一个片段并补齐可以避免这部分删除,但还要检查块间预测。若把每块当独立序列,前一块最后一个输入与后一块第一个目标之间的关系可能不再训练。
一种替代设计是每次取 个连续 token,前 个做输入,后 个做目标;相邻窗口让边界 token 作为下一窗口的首个输入延续。这样可以覆盖相邻预测关系,但后一个窗口仍看不到更早的完整前缀。
无论采用哪种设计,都要分别记录:哪些 token 作为输入出现了,哪些作为目标计分了,每个目标出现几次。重叠窗口若重复计分同一目标,会增加它的权重;只说“覆盖率更高”掩盖不了这个变化。
训练集和验证集也不能先混成文本流、打包之后才随机拆块。第一卷已经说明,相关文档应该先按来源或分组划分,再在各自集合里独立处理,否则边界附近容易泄漏。
padding 与 EOS 共用编号时,小心误删监督
有些配置把 padding token 与 EOS 设置成同一个编号。输入层面这样做是否可行,取决于模型与遮罩约定;但构造标签时不能再想当然地把所有这个编号都忽略。
否则原本应该学习的真实 EOS 也可能被删除,模型就少了“什么时候结束”的监督。问题不在共用编号这个事实本身,而在标签代码用什么信息区分真实内容和补齐位置。
应该利用长度、位置或明确的非填充标记识别 padding,并检查处理后的标签里,真实 EOS 是否仍然作为目标保留。实际 collator 的行为要按版本核对,不能只根据类名猜测。
同样,二维的非填充标记和完整注意力关系不是同一张表。一个位置不是 padding,不表示它就可以读取所有其他非 padding 位置;因果限制和文档限制仍然需要叠加。
梯度累积为什么也需要数有效目标
假设一次参数更新分两次小批次计算。第一个小批次有两个有效目标,平均损失为 1;第二个有八个,平均损失为 3。
如果每次都把平均损失除以二再反向传播,得到的是两个小批次等权的目标:。
如果希望十个 token 等权,目标应该是:
一般地,已知每个小批次的有效数 、平均损失 ,应该累积:
在这次更新期间参数不变,求导的线性性质才允许我们把各部分梯度相加。各小批次大小相同、有效数相等时,简单除以累积次数才对应 token 等权平均。
分布式训练还多了跨进程的归约规则。如果框架已经平均梯度,又在外部错误地重复除以进程数,梯度尺度就变了。这里不提供一个声称适用于所有训练器的通用乘除公式;应从最终希望得到的全局损失反推实现,并用小例子对照。
“每步多少 token”至少有三种口径
假设四个设备,每个设备一次放两条长度 512 的序列,累积八次更新。名义输入位置数为:
如果平均只有四分之三的位置是有效目标,那么一次更新约有 24,576 个有效目标。若后续 SFT 只监督回答,比例还可能更低。
| 口径 | 回答的问题 |
|---|---|
| 输入位置数 | 实际组织了多少张量位置 |
| 非填充 token 数 | 读取了多少非 padding 内容 |
| 有效目标数 | 有多少位置直接参与损失 |
吞吐也一样。按输入位置算的速度提升,可能只是补齐更多;按有效目标算才更接近训练信号的处理速度。不过不同目标的学习价值也不能仅由数量决定。
一个能发现边界错误的对照设计
先不比较训练质量,而是检查打包是否符合定义:
- 把 A、B 分别送入模型,记录各有效目标的 logits 和损失。
- 再按独立文档方式打包,保持文档内位置编号、目标和权重一致。
- 关闭 dropout 等随机性,比较对应位置的结果,允许合理的数值误差。
- 只替换 A,检查 B 的输出是否保持不变。
若目标本来就是连续文本流,第 4 项不变性就不是要求。测试必须服务于预先定义的语义,不能拿独立打包的验收去否定有意允许跨文档上下文的方案。
等边界检查通过,再在固定原始数据和目标口径下测吞吐、峰值内存和验证表现。速度更快而验证变差时,要回头检查是否连有效样本分布也改变了。
两个检查理解的问题
第一,A 的 EOS 后面接 B 的 BOS。只把 B 的 BOS 标签忽略,为什么 B 仍可能利用 A 的内容?反过来,只屏蔽跨文档注意力,为什么训练目标也未必等于独立处理?
第二,两个小批次分别有十个和九十个有效目标,你希望每个 token 等权。两个平均损失分别应该乘多大的系数,再参与这一次更新?
下一篇讨论训练为什么会不稳定。有效目标和归一化约定固定以后,才有条件判断学习率、精度或优化器的变化究竟造成了什么。
文章概念网络
从章节进入正文概念。实线表示所属层级,虚线连接跨章节重复出现的概念,不代表因果关系。
54 / 54 个节点 · 正文标记术语与关键词概念
训练块怎样组成一个 batch:打包、边界与有效 token
回到原文 ↗最后更新于 2026-10-20
引用本文
@article{2026-10-20-llm-training-pretrain-04-batching,
author = {枫语},
title = {训练块怎样组成一个 batch:打包、边界与有效 token},
journal = {枫语},
year = {2026},
month = {oct},
url = {https://meiluosi.github.io/posts/2026-10-20-llm-training-pretrain-04-batching/}
}