01 · 为什么空位也可能耗费设备时间
一批文本长度不同,但普通稠密Tensor需要整齐的行列。例如长度2与8的两条样本,补齐后形状是[2,8]。补出的空位称为Padding,方便批量执行,却可能仍经过投影、归一化或稠密Attention等计算。
Attention mask可以禁止读取无效位置,Loss mask可以不统计空位目标,但“结果被屏蔽”不意味着内核没有处理这些坐标。是否真正省计算,要看数据表示与配套内核。
Tokenizer将文本变成token id是正常输入流程,不是本篇的加速方法;这里优化的是多个已分词样本怎样组成批次和进入内核。
依据:Transformers · Padding与截断(main) ↗ · PyTorch · Variable Length Attention官方教程 ↗
02 · 长度分组:先把相近的样本放一起
自设四条样本长度为[2,8,3,7],每批2条,总计20个有效token。按原顺序,两批分别补到8与7,分配16+14=30个槽位;按相近长度分组为[2,3]与[7,8],只需6+16=22个槽位。
| 组织方式 | 批次 | 总槽位 | 有效token占比 |
|---|---|---|---|
| 原顺序 | [2,8]、[3,7] | 30 | 20/30≈66.7% |
| 长度分组 | [2,3]、[7,8] | 22 | 20/22≈90.9% |
| 无Padding的变长表示 | 保存四段实际内容及边界 | 20(未计元数据/对齐) | 元素内容中100%有效 |
长度分组不要求把不同样本拼成同一序列,通常仍各占一行。训练应保留合适的随机性与样本分布,不宜整轮永远从短到长;服务也不能为了凑长度接近的请求,让某些请求无限等待。
03 · 去Padding:让内核知道每段实际在哪里
可以将有效内容紧凑存放,再用长度、累计偏移等元数据标出各条序列。例如长度[3,2]的两段内容合为5个位置,累计长度cu_seqlens=[0,3,5],表示第一段在[0,3),第二段在[3,5)。
分开的样本:A=[11,12,13],B=[21,22]
紧凑内容:[11,12,13,21,22]
累计边界:[0,3,5]
原样本位置:A的0/1/2,B的0/1
# 元数据告诉支持变长的内核:哪里是一段的起止。变长Attention内核可以按段处理,减少对补齐区域的工作。普通稠密内核若不理解这些边界,仅把有效token排成一条长向量并不够。框架还需支持合适的布局、dtype、最大长度和后续操作。
对上面的[2,8,3,7],每段长度平方之和为4+64+9+49=126;原两批稠密打分矩阵元素数为2×8²+2×7²=226。这只是稠密候选坐标计数;因果三角、分块实现和其他层成本要分别考虑。
04 · Packing:多个样本装入一个容量更大的容器
训练Packing常把多条短样本放进一个pack。容量10时,长度8+2和7+3各组成一包,20个有效token恰好装入两包。这与“每条样本各一行、按长度分组”不同。
容器合并不等于语义合并。本篇目标是保留各个独立样本的计算:每个样本仍只看自己此前的位置。若任务本来要求文档连续流式拼接,那是另一种数据目标,应单独说明。
如果只在长度10的稠密Attention上添加块状mask,两个包仍可能各生成10×10候选区域;只有支持相应段边界的内核才可能避免跨样本区域工作。Pack槽位填满,不保证Attention比其他组织方式更快。
05 · 最容易出错的地方:B不能看见A
用A=[11,12,13]、B=[21,22]构成五个位置。正确的独立样本因果mask允许A3看A1/A2/A3,允许B1只看B1。普通整包下三角mask却允许B1看A1/A2/A3,已经改变模型输入的含义。
选择一个查询,看它能读取哪些位置
行是查询,列是可读Key;●允许,·屏蔽,选中查询行高亮。A有3个位置、B有2个位置;保持独立样本时必须禁止跨样本读取。表格可横向滚动。06 · mask对了,还要检查位置与标签
| 对象 | 本例正确处理 | 错误会怎样 |
|---|---|---|
| 位置编号 | A用0/1/2;B用0/1,以复现各自独立输入 | 若无依据让B从3开始,可能改变位置相关计算。 |
| 下一token目标 | 对齐输入位置的目标:[12,13,忽略,22,忽略] | A末尾不应预测B首token。 |
| Loss分母 | 本例有效目标3项;按既定目标归一化 | 按5个槽位或按包数平均可能改变权重。 |
| 还原结果 | 用边界恢复A、B各自输出 | 不能把输出位置直接当作原批次行号。 |
表中目标已经与产生logits的输入位置对齐;有些框架会在模型内部做label shift,接入时不能再次移位。若样本原本包含EOS或只监督回答区,保留原EOS及Loss mask,而不是照搬这里的简化三项目标。
位置重置是为了复现本例独立样本的编号,不是所有训练配方的通用要求。应与原数据目标、RoPE/其他位置机制和内核所需position_ids保持一致。
07 · 训练和推理分别怎么用
| 场景 | 适合关注的方式 | 另外的约束 |
|---|---|---|
| 训练短样本 | 随机分桶、有效token预算、独立样本Packing | mask、标签、位置、Loss归一化与DP负载。 |
| 推理Prefill | 长度分组或变长内核组织独立请求 | TTFT、等待时间与各请求独立KV。 |
| 推理Decode | 按活跃请求与KV长度选择执行布局 | 每请求当前新增token少,历史读取长度不相同。 |
Packing与连续批处理不是同一件事:前者改变输入容器,后者随生成迭代更新活跃请求集合。长度分组也可能改变编译形状与图重放档位,需要一起观察额外padding和缓存命中。
08 · 用一个两层教学Attention检查边界
下方CPU脚本比较逐条执行与带段边界的打包执行,验证两层输出一致;再故意去掉样本隔离或错误延续位置,展示B输出改变。同时检查next-token目标、有效目标数和上述槽位算例。
pack_valid_tokens(samples)
build_segment_boundaries_and_original_positions()
keep_original_loss_mask_and_align_next_token_targets()
outputs = supported_varlen_attention(tokens, boundaries, positions)
unpack_outputs_by_original_sample()
# 对比逐样本执行的有效位置输出和同目标Loss;训练再检查梯度。NVIDIA可从NeMo/Megatron打包文档和PyTorch变长Attention教程进入实现。昇腾需要按实际框架/内核支持核对变长布局和段边界参数,不能因也支持Attention就默认上述接口全部可用。
依据:PyTorch · Variable Length Attention官方教程 ↗ · Megatron Bridge · Packed Sequences(main) ↗
09 · 正确性与性能要分别验收
- 先固定样本、dtype与随机状态,对比有效位置输出、同目标Loss和梯度。
- 检查每段开始、结束、EOS、空监督区,以及padding尾部。
- 记录每秒有效token,不把padding token算作训练吞吐。
- 测量分组/打包CPU成本、内核时间、峰值内存和端到端步骤。
- 服务同时观察首token时间与长请求/短请求尾延迟;训练观察样本分布和收敛。
自查:mask禁止读空位为什么仍可能算空位?cu_seqlens=[0,3,5]表示什么?EOS为什么不能代替样本隔离?打包后有效Loss分母为什么不能直接等于容器容量?
流程位置与关联方法
这个方法可能出现在多个位置;计算线包含模型计算与算子实现,执行线包含运行时、内存和通信。
资料与核查范围
原理依据以原始论文和官方文档为主;算例与时间线是教学推导。文档核查日期:2026-10-10。引用 main / latest 文档时,实际接口以安装版本为准。
- Transformers · Padding与截断(main) ↗
- PyTorch · Variable Length Attention官方教程 ↗
- NeMo 25.04 · Sequence Packing与样本边界 ↗
- Megatron Bridge · Packed Sequences(main) ↗
代码为接入片段或教学伪代码;本页没有声称完成 NVIDIA / 昇腾硬件实测。