← 返回题目列表

SFT 训练中的样本 Packing 有什么收益和风险?

中等 第 26 / 26 题 更新于 2026/09/17

简化版

Packing 把多个短样本拼进同一固定长度序列,减少 Padding,让 GPU 计算更多有效 Token。例如最大长度 4096,而平均样本只有 400 Token,不 Packing 会浪费大量计算。

风险是不同样本可能互相 Attention、位置编号和 Loss Mask 错位,模型学到跨样本关联;还可能漏掉 EOS、切断长样本或改变样本权重。正确实现要插入边界 Token,使用 Block-Diagonal Attention 或明确接受跨样本可见,并逐 Token 验证标签。

Packing 优化的是计算利用率,不应改变每个训练样本的语义边界和监督目标。

详细版

有效 Token 利用率:

utilization = non_padding_tokens / total_tokens_processed

若一个 Batch 有 8 条,统一 Padding 到 4096,实际长度平均 512,则利用率约 512/4096=12.5%。将短样本装箱到 4096 序列,理论上可接近 80%~95%,但实际受长度分布和并行策略影响。

策略利用率边界隔离实现复杂度
普通 Padding天然隔离
简单 Concatenate可能跨样本可见
Block-Diagonal Mask严格隔离高,需算子支持
Flash Varlen/Unpadding按序列隔离高,需长度元数据

完整版教学

1. 为什么短样本会浪费计算

标准 Dense Batch 要对齐长度,短样本补 PAD 后,Attention 和 FFN 仍可能对矩形张量执行大量计算。长度方差越大,浪费越严重。

Packing 将空白空间填入其他样本,提高每步有效监督 Token。

2. Packing 与动态 Batch 有何区别

动态 Batch 让同批样本长度接近,减少 Padding,但每个样本仍独立;Packing 会把多个样本放入同一序列槽。

两者可组合:先按长度分桶,再把剩余短样本做装箱。

动态 Batch 的收益主要来自减少序列尾部空算,改动较小;Packing 还能利用同一上下文窗口里的空位,但必须正确处理样本边界、位置编码和 Loss Mask。前者适合作为低风险基线,后者适合短样本占比很高且吞吐成为瓶颈的场景。

3. 如何做装箱

可把样本长度视为 Bin Packing,容器容量为最大序列长度。First-Fit Decreasing 按长度降序,放入第一个能容纳的容器,简单且效果不错。

capacity=16
lengths=[9,7,6,5,4,1]
packs=[9+7], [6+5+4+1]

实际还要为 EOS、BOS 和模板 Token 留空间。

4. 样本边界如何表示

每个会话末尾加入正确 EOS/End-of-Turn,下一样本按模板重新开始。不能直接把 Assistant 结尾和下一个 User 文本无分隔拼接。

Tokenizer 是否自动添加 BOS/EOS 必须显式控制,避免重复或缺失。

5. 跨样本 Attention 有什么问题

简单拼接使用普通因果 Mask,后一个样本可以看到前一个样本内容。模型可能利用不应存在的上下文,训练/服务分布不一致。

EOS 只是一个 Token,不会自动阻止 Attention;真正隔离需要 Block-Diagonal Mask 或支持独立序列的 Varlen 算子。

某些项目接受跨样本可见以换取简单实现,但必须通过评测证明无害并记录选择。

6. Block-Diagonal Causal Mask 如何工作

每个样本内部使用因果下三角,不同样本间 Attention 为禁止:

sample A: positions 0..4   attends only A past
sample B: positions 5..9   attends only B past

矩阵呈多个对角块。直接构造完整 Mask 可能占内存,应使用支持 varlen/segment IDs 的高效 Kernel。

7. Position IDs 应该连续还是重置

严格隔离时常为每个样本从 0 重置位置,更接近独立推理;简单拼接也可连续,但后面样本总处于高位置,会改变位置分布。

RoPE 与高效 Attention 实现可能对重置有约束,必须做数值测试而不是只修改 position_ids。

8. Loss Mask 最容易错在哪里

System/User/PAD 通常设 -100,Assistant 正文与结束 Token计 Loss。Packing 后索引偏移,容易把前一条标签复制到下一条。

区域Input 可见Loss
System/UserMask
Assistant计算
Tool Result通常 Mask
Padding否/MaskMask

随机抽样打印 Token、角色和 label 对齐是上线前必做。

9. 长样本怎样处理

超过最大长度可截断、分块或单独长序列 Batch。不能无意识切掉 System Prompt、Assistant 答案或工具结果。

按会话轮次裁剪并保留协议合法性;对必须完整的代码和文档任务,使用专门长上下文策略。

10. Packing 会改变样本权重吗

若 Loss 对所有有效 Token 平均,长答案贡献更多梯度;若按每样本归一再平均,短长样本权重更均衡。不同训练框架默认可能不同。

token_mean:  L = Σ token_loss / N_tokens
sample_mean: L = mean_i(Σ loss_i / N_i)

需根据目标选择,并在分布式梯度累积下保持一致。

11. 分布式训练有什么坑

不同 Rank 的 Pack 数和有效 Token 不同,会造成负载不均;按样本数同步梯度也可能让每步 Token 数波动。

按有效 Token 预算组 Batch,记录每卡 Tokens/s,并确保 Loss 归一化在 Data Parallel 间正确。

如果各 Rank 直接对本地有效 Token 的平均 Loss 做等权聚合,短 Pack 较多的卡会被赋予不成比例的权重。更稳妥的做法是先累计各卡损失总和与有效 Token 数,再用全局有效 Token 数归一化,并让采样器尽量平衡每卡的 Token 负载。

12. 如何评估 Packing 是否值得

比较有效 Token 利用率、Tokens/s、峰值显存和相同有效 Token 训练后的质量。不要按 Step 数比较,因为 Packing 后每步看到的监督 Token 更多。

同时跑独立样本与多轮边界测试,检查跨样本泄漏和输出停止行为。

13. 与 Sequence Packing 专用实现的区别

有些框架做真正的无 Padding Varlen Attention,有些只是把文本 concatenate 再切固定块。配置都叫 Packing,但语义隔离不同。

阅读实现并用合成样本测试:改变 Pack 中前一条内容,后一条 Logits 在严格隔离时不应变化。

14. 常见误区与追问

  • 误区:加入 EOS 就能阻止跨样本 Attention。 EOS 没有硬隔离作用,需要 Mask/Varlen 边界。
  • 误区:Packing 只影响速度,不影响训练语义。 边界、位置和 Loss 归一化都可能改变。
  • 误区:按相同 Step 比较开关 Packing 公平。 每步有效 Token 不同,应固定总 Token 预算。
  • 误区:所有长样本都可以直接截断。 可能截掉规则或监督答案,制造错误样本。
  • 误区:框架叫 Packing 就一定做 Block Mask。 有些只做简单拼接,必须查实现。
  • 追问:如何证明没有跨样本泄漏? 改变前一段内容,检查后一段 Logits 是否保持不变。
  • 追问:位置是否应重置? 独立样本通常重置更贴近推理,但需与 RoPE/Kernel 兼容验证。

15. 加强记忆

  1. 收益公式:有效 Token / 总处理 Token,提高利用率和吞吐。
  2. 四个边界:EOS、Attention Mask、Position IDs、Loss Mask。
  3. EOS 不等于隔离,Block-Diagonal 或 Varlen 才是硬边界。
  4. 公平对比按总 Token,不是按 Step。
  5. 最强测试:改变前样本,后样本 Logits 不应变化。