一句话定义:长度分桶(Length Bucketing)是一种训练/推理时的样本组织策略,先把长度相近的样本分到同一组(桶),再从同一个桶里凑出一个 batch,从而减少 padding 带来的无效计算。
白算的那部分算力从哪来
Transformer 处理的是一个 batch 的张量,形状必须规整,所以同一个 batch 里的序列要补齐(padding)到等长,通常补到 batch 内最长那条的长度。问题在于,很多任务的样本长度差异极大:短的可能几十个 token,长的可能上千。
举个可推演的例子:batch size 是 4,四条样本长度分别是 8、12、500、510。补齐后每条都要按 510 个 token 占位,前两条里 98% 以上的位置都是填充符;注意力机制和矩阵乘也不会因为「这里是 padding」就自动跳过,算力照付。更糟的是,如果靠随机采样组 batch,一条超长样本就会把整个 batch 拉长,而这种浪费在长尾分布的数据里非常常见。
做法与一个比方
做法很朴素:先统计所有样本的长度,按长度排序或按区间切成若干桶,然后只在桶内采样组 batch,每个 batch 再按批内最长的那条做 padding。
好比搬家装车:车厢高度固定,每个纸箱都按「本车最高那只箱子」来占位。如果一车里混进一只特别高的柜子,旁边一堆矮箱子头顶全是空气。把高度接近的箱子装同一车,空间利用率立刻就上去了。
和序列打包不是一回事
这是最容易混淆的地方。序列打包(Sequence Packing)是把多条短样本首尾相接拼进一条长序列,通常要配合特殊的 attention mask(块对角掩码)和位置编码处理,让不同样本互不「看见」,几乎做到零 padding,但需要改数据管线和模型实现。长度分桶不拼接、不改模型,只是重新安排「谁和谁同批」,桶内仍然存在 padding,只是浪费小得多。
| 手段 | 做什么 | 是否改模型 | padding 残留 |
|---|---|---|---|
| 长度分桶 | 长度相近的样本同批 | 否 | 少量 |
| 序列打包 | 多条拼成一条长序列 | 通常需要 | 接近零 |
| 动态 padding | 单批内按实际最长补齐 | 否 | 单批内为零,跨批仍有 |
三者可以叠加:分桶决定「谁同批」,动态 padding 决定「这批补到多长」,打包则进一步把批内空隙填满。
从业者要注意的几点
- 分桶会改变样本的出现顺序和组合,通常要在桶之间打乱、桶内加随机缓冲,避免模型长期看到固定搭配而影响收敛。
- 按长度分桶会让同一 batch 内的长度方差变小,梯度噪声的结构随之改变,学习率和 batch size 的关系可能需要重新调。
- 推理时同样可用:把长度相近的请求凑批能明显提升吞吐(throughput),但要记录原始顺序的索引,输出后按索引还原,别把结果派发给错误的人。
- 数据长度分布很不均、或者长样本占比很低时,收益最明显;如果本来就都是等长样本(比如定长切片),那这项优化意义不大。
一句话总结:长度分桶是性价比极高的一档优化——不动模型、不动算法,只是把「谁和谁一批」这件事排得更聪明一点。具体实现细节各家框架不同,以官方文档为准。
