整体总结
- 样本 packing 训练可以大幅提升模型训练速度
- 由此引发的训练样本的组织方式和 loss 权重有多种组合形式
- 本文对 样本 packing 和 packing 后的样本权重(packing weight)相关的各种情况进行总结
packing 模式的一些结论
- 下面两个数据处理方式在算法理论层面不等价(特别是在 tool 等 prompt 很长,response 很短的场景):
- 数据处理方案一:将多轮拆成多个单轮样本训练
- 数据处理方案二:保留多轮,即多轮本身是一个样本,但训练计算损失时每个轮次视作一个样本(即按照轮次处理 Loss 归一化)
- 上述两种训练方式不等价的原因包括:
- 样本训练时的分布不等价
- 样本训练时的权重不等价
- 多轮拆单轮/保留多轮 的数据处理方式暂无法简单从算法层面实现等价
- 除非 GBS 无穷大,达到所有样本一次过完
非 packing 下的训练模式(与 packing 无关的朴素模式)
数据集内所有 token 权重相等训练模式
- 具体实现方式为:
$$
\begin{align}
\text{Loss}(\text{batch}) &= \sum_{\text{token}_i \in \text{batch}} \text{loss}_{\text{token}_i} \\
\text{Loss}(\text{batch}) &= \frac{1}{\text{GBS}} \sum_{\text{token}_i \in \text{batch}} \text{loss}_{\text{token}_i}
\end{align}
$$- 每个 token 的 loss 直接累加,不做任何归一化 或 仅除以 Global Batch 内样本数 GBS(一般对同一次训练来说是固定值)
- 这种方式可以确保训练的整个过程中,所有 token 的权重都是相同的
- 可能会导致部分短序列得到的关注度不够高
- 预训练中就应该使用这种方式,每个 Token 是等权的
Global Batch 内部每个 token 权重相等训练模式(Global Batch 间的 token 权重可能不相等)
- 具体实现方式:
$$
\text{Loss}(\text{batch}) = \frac{1}{\text{GBS_Token_Num}} \sum_{\text{token}_i\in \text{batch}} \text{loss}_{\text{token}_i}
$$- 每个 token 的 loss 累加,除以 Global Batch 内的 token 总数
- 这种方式可以确保同一个 Global Batch 内部的 token 权重相等
- 不同 Global Batch 内部的权重可能是不相同的,跟 Global Batch 内部的 token 总数有关
- 学习率相同的情况下,不同 Global Batch 对模型训练的贡献相等
- 当 Global Batch Size 非常大时,这个实现方式几乎等价于 数据集所有 token 权重相等训练模式
样本内部每个 token 权重相等训练模式(样本间的 token 权重可能不相等)
- 具体实现方式:
$$
\begin{align}
\text{Loss}(\text{batch}) &= \sum_{\text{sample} \in \text{batch}} \frac{1}{|\text{sample}|} \sum_{\text{token}_i \in \text{sample}}\text{loss}_{\text{token}_i} \\
\text{Loss}(\text{batch}) &= \frac{1}{\text{GBS}} \sum_{\text{sample} \in \text{batch}} \frac{1}{|\text{sample}|} \sum_{\text{token}_i \in \text{sample}} \text{loss}_{\text{token}_i}
\end{align}
$$- 每个 token 的 loss 累加,除以 样本 内的 token 总数,同时样本间 不做任何归一化 或 除以 Global Batch 内样本数 GBS
- 这种情况下可以防止长序列样本权重过高,让不同样本对训练的影响权重相同
- 长序列样本中的 Token 可能因为平均后权重过低而学不好
- 很多框架实现中的目标就是实现这样的方式,想要保证样本粒度的 Loss 贡献相同
样本 packing 下的训练模式
数据集内所有 token 权重相等训练模式
- 具体实现方式:
$$
\begin{align}
\text{Loss}(\text{batch}) &= \sum_{\text{token}_i \in \text{batch}} \text{loss}_{\text{token}_i} \\
\text{Loss}(\text{batch}) &= \frac{1}{\text{GBS}} \sum_{\text{token}_i \in \text{batch}} \text{loss}_{\text{token}_i}
\end{align}
$$- 每个 token 的 loss 直接累加,不做任何归一化 或 除以 Global Batch 内样本数 GBS(一般对同一次训练来说是固定值)
- 这种方式下,与非 packing 的训练方式相同
- 注:如果想要与非 packing 情况下的 数据集内所有 token 权重相等训练模式 完全等价,自回归下 Attention 时需要对前面的样本做 mask 才可以
- 预训练中就应该使用这种方式
Global Batch 内部每个 token 权重相等训练模式(Global Batch 间的 token 权重可能不相等)
- 具体实现方式:
$$\text{Loss}(\text{batch}) = \frac{1}{\text{GBS_Token_Num}} \sum_{\text{token}_i\in \text{batch}} \text{loss}_{\text{token}_i}$$- 每个 token 的 loss 累加,除以 Global Batch 内的 token 总数
- 此时已经是 packing 以后的样本,但理论上仍然与 非 packing 下的训练方式相同
- 注:如果想要完全等价于 非 packing 模式下的 Global Batch 内部每个 token 权重相等训练模式 ,自回归下 Attention 时需要对前面的样本做 mask 才可以
packing 样本内部每个 token 权重相等训练模式(样本间的 token 权重可能不相等)
- 具体实现方式:
$$
\begin{align}
\text{Loss}(\text{batch}) &= \sum_{\text{sample} \in \text{batch}} \frac{1}{|\text{sample}|} \sum_{\text{token}_i \in \text{sample}}\text{loss}_{\text{token}_i} \\
\text{Loss}(\text{batch}) &= \frac{1}{\text{GBS}} \sum_{\text{sample} \in \text{batch}} \frac{1}{|\text{sample}|} \sum_{\text{token}_i \in \text{sample}} \text{loss}_{\text{token}_i}
\end{align}
$$- 每个 token 的 loss 累加,除以 样本 内的 token 总数,同时样本间 不做任何归一化 或 除以 Global Batch 内样本数 GBS
- 这种情况下由于短序列样本被 packing 为固定长度的样本,所以与 非 packing 下的 样本内部平均完全不同 ,无法做到每个样本贡献度一致了(长样本占优)
真实样本内部每个 token 权重相等训练模式
- 每个 token 的 loss 累加,除以 真实样本(需要特殊手段去识别) 内的 token 总数,同时有归一化有三种实现方式:
- 方式一:样本间 不做任何归一化
$$
\text{Loss}(\text{batch}) = \sum_{\color{red}{\text{sample}_{true}} \in \text{batch}} \frac{1}{|\color{red}{\text{sample}_{true}}|} \sum_{\text{token}_i \in \color{red}{\text{sample}_{true}}}\text{loss}_{\text{token}_i}
$$ - 方式二:除以 Global Batch 内样本数 GBS
$$
\text{Loss}(\text{batch}) = \frac{1}{\text{GBS}} \sum_{\color{red}{\text{sample}_{true}} \in \text{batch}} \frac{1}{|\color{red}{\text{sample}_{true}}|} \sum_{\text{token}_i \in \color{red}{\text{sample}_{true}}} \text{loss}_{\text{token}_i}
$$ - 方式三:除以 Global Batch 内真实样本数
GBS_true
$$
\text{Loss}(\text{batch}) = \frac{1}{\color{red}{\text{GBS_true}}} \sum_{\color{red}{\text{sample}_{true}} \in \text{batch}} \frac{1}{|\color{red}{\text{sample}_{true}}|} \sum_{\text{token}_i \in \color{red}{\text{sample}_{true}}} \text{loss}_{\text{token}_i}
$$ - 特别注意,此时除以
GBS和 除以GBS_true是不同的,为了保证真实样本间的权重一致,应该除以GBS_true
多轮场景的训练思考
- 有时候想要将多轮中的每一轮单独看做一个样本(即一次回复一个样本)
- 这种思路的基础逻辑是认为一次回复就应该是一个样本
- 实际上,评估指标中,可能涉及多轮回复只对应一个分数,即多轮回复对整体评估的贡献跟单轮回复权重一样,这时候要注意训练指标和评估指标的一致性问题
- 多轮场景下,即使不做样本 packing,也相当于隐含的存在 样本 packing 了,如果想要做到轮次间权重一致,可以参考 真实样本内部每个 token 权重相等训练模式 的实现,这种实现方式可以分为两种:
- 方式一:将多轮拆成多个样本,labels 仅保留最后一个回复,再使用用样本平均的方式实现
- 注:这种拆开的方式会引入一些特别的变化,且是不可恢复的
- 这种拆开会导致重复计算一些 prompt ,且会将同一个多轮样本分散到不同的 Batch 上学习,是不可恢复的
- 这种拆开还会导致 拆单轮 情况 vs 不拆单轮 情况的单个 Batch 内数量和分布不一致,导致一些差异,也是不可恢复的
- 这里的影响应该还好,主要强调的是不可恢复性
- 总结:拆开的方式虽然方便,但与合并训练的方式相比,无论如何也无法恢复到真实水平
- 注:这种拆开的方式会引入一些特别的变化,且是不可恢复的
- 方式二:不管是否 packing,都将单个轮次识别为当个真实样本做归一化和学习
- 此时不管是否 packing,都可以做到 轮次即样本,两者基本等价
在 DP 不为 1 的情况思考
- 在 DP 不为 1 的情况下,每个 DP 内部是独立计算梯度的,所以需要先做归一化












































> Baby, there ain't no mountain high enough.
Ain't no context long enough.
— Inspired by Marvin Gaye & Tammi Terrell














