mcpField
N
nemo-mbridge-perf-sequence-packing

nemo-mbridge-perf-sequence-packing

github.com/nvidia开发3.8k1.8k 次安装今天更新
代码仓库

关于 nemo-mbridge-perf-sequence-packing

用于在 Megatron Bridge 中配置和验证 LLM 及受支持 VLM 微调序列打包的技能。它区分离线、批内和 Energon 打包路径,并记录配置限制与验证检查。

主要功能

  1. 01配置离线打包 SFT
  2. 02设置 GPT-SFT 批内打包
  3. 03配置 Qwen-VL 的 Energon 在线打包
  4. 04对齐序列长度与并行设置
  5. 05通过测试验证配置约束

适用场景

  1. 01为训练吞吐量调整打包长度
  2. 02为 GPT-SFT 数据集启用打包
  3. 03使用 Qwen-VL 数据进行在线打包
  4. 04训练前检查打包配置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nvidia/skills --skill nemo-mbridge-perf-sequence-packing

安装后在 Claude Code 中输入/nemo-mbridge-perf-sequence-packing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

BENCHMARK.md
card.yaml
skill-card.md
skill.oms.sig
evals/
evals.json

详细介绍

序列打包技能

如需了解稳定背景与推荐级别,请参阅:

  • @docs/training/packed-sequences.md
  • @skills/nemo-mbridge-perf-sequence-packing/card.yaml

启用

用于 LLM 微调的离线打包 SFT:

import math

from megatron.bridge.data.datasets.packed_sequence import PackedSequenceSpecs

cfg.train.micro_batch_size = 1
cfg.train.global_batch_size = 8
cfg.dataset.seq_length = 8192
cfg.model.seq_length = 8192
cfg.dataset.enable_offline_packing = True

cp_size = cfg.model.context_parallel_size
tp_size = cfg.model.tensor_model_parallel_size
cp_multiple = 2 * cp_size if cp_size > 1 else 1
sp_multiple = cp_size * tp_size if cfg.model.sequence_parallel and tp_size > 1 else 1
cfg.dataset.offline_packing_specs = PackedSequenceSpecs(
    packed_sequence_size=8192,
    pad_seq_to_mult=math.lcm(cp_multiple, sp_multiple),
)
选择离线打包长度

对于纯文本 LLM SFT 和 PEFT 验证,在模型上下文限制、内存和模型系列支持的条件允许时,从 8192 个 token 的离线打包开始。 在每个优化器步骤的 token 槽位数相同的情况下,对打包长度进行基准测试:

token_slots_per_step = packed_sequence_size * global_batch_size

例如,2K/GBS32、4K/GBS16 和 8K/GBS8 每步均提供 65,536 个 token 槽位。较长的打包会将更多源样本聚合到每个物理 MBS1 行中,并可减少梯度累积和每步开销。它们也会增加激活内存,并可能暴露内核宽度限制,因此应选择实测后可用的最大配置,而不要假设越长就一定越快。

离线打包要求使用 MBS1。要求 global_batch_size % data_parallel_size == 0 且 global_batch_size >= data_parallel_size;因此,8K/GBS8 工作负载要求 DP 不大于 8。保持 model.seq_length、 dataset.seq_length 和 packed_sequence_size 相等;更改其中任一项后,都要使用新的打包数据输出根目录,并检查解析后的 setup 配置。

相同的 token 槽位数并不意味着不同打包长度在数值上完全一致:较长的目标长度会改变截断方式和打包成员。替换已验证的证据前,请重新运行有限损失、无跳过/NaN 和收敛哨兵测试。

启用 CP 进行微调时:

cfg.model.context_parallel_size = 2
cfg.model.calculate_per_token_loss = True
cfg.ddp.average_in_collective = False

SFT 和 PEFT 使用相同的对齐公式。TP1/CP1 且禁用 SP 时,该公式的结果为 1;TP4/CP1 且启用 SP 时,结果为 4。离线打包不会自动推导此值,因此请显式固定该值,并在拓扑更改后重新构建打包数据。

如果分发器或内核要求最终 token 宽度固定:

cfg.dataset.dataset_kwargs = {
    **(cfg.dataset.dataset_kwargs or {}),
    "pad_to_max_length": True,
}

选择满足内核倍数要求的 packed_sequence_size。例如,HybridEP 使用 128-token 合并块,因此宽度必须能被 128 整除。这与 pad_cu_seqlens 不同,后者用于对齐每个组成序列,以满足 CP/SP 的要求。

如果为此打包路径启用了 CUDA 图,则必须固定 token 宽度,且打包元数据也必须具有静态形状:

cfg.dataset.offline_packing_specs.pad_cu_seqlens = True
cfg.dataset.dataset_kwargs["pad_to_max_length"] = True

注意: pad_cu_seqlens = True 还要求在打包数据集旁提供 JSON 元数据文件(在 src/megatron/bridge/data/datasets/sft.py 中断言)。省略元数据文件的自定义打包数据集会在数据集初始化时触发断言。

用于 GPT SFT 和支持的 VLM 微调的批内打包:

cfg.dataset.enable_in_batch_packing = True
cfg.dataset.dataloader_type = "single"
cfg.train.micro_batch_size = 4

对于本地或已物化的 GPT-SFT JSONL,这会保留现有的 mmap 支持型数据集,并延迟执行分词。提示词/补全形式(GPTSFTDataset)和聊天形式(GPTSFTChatDataset)都会保留其损失掩码语义。使用 dataloader_type="single" 或 "cyclic",确保每次 DataLoader 产出一个逻辑微批次;GPT-SFT 批内打包不支持全局批次 "batch" 数据加载器。

Qwen-VL 的 Energon 在线打包使用 Energon 的每工作进程候选缓冲区,而不是将选择范围限制在单个整理器微批次内:

cfg.dataset.packing_buffer_size = 16
cfg.dataset.micro_batch_size = 1
cfg.train.micro_batch_size = 1
cfg.model.calculate_per_token_loss = True
cfg.ddp.average_in_collective = False

packing_buffer_size 是唯一的原生打包选择器;将旧版 collator 和 step 所拥有的打包标志保留为默认值。请使用 vlm_step。缓冲区大小统计的是每个 worker 已准备好的候选样本数,而不是字节数或打包后的 token 数。由于准备好的图像/视频 patch 张量在选择之前一直保留在主机内存中,对于高分辨率或视频数据,请从 8-16 开始,并在增大之前测量 worker RSS、首批次延迟和 bin 填充情况。此路径不会写入离线数据包;源 WebDataset 分片保持不变。它支持带有 MBS1 的 eager Qwen-VL,并拒绝 MTP、CUDA graphs、Qwen3-VL DistTrain 和 PP。请求的 MoE 专家并行通信重叠会被禁用并发出警告。标准 eager alltoall EP 在 TP1/PP1/EP8 且重叠禁用的情况下对 Qwen3.6-35B-A3B 具有功能覆盖;这不构成性能证据。其他 EP 分发器在固定宽度原生打包下被接受,但尚无同等的运行时证据。Qwen-VL 模型根据逻辑和物理 THD 边界推导出 MoE padding mask,因此固定宽度间隙不会进入辅助损失、z-loss 或专家偏置统计。当前 MCore 仍可能分发填充位置;专家容量/令牌丢弃配置缺少原生打包的运行时覆盖。

长上下文基线:

cfg.model.seq_length = 16384
cfg.dataset.seq_length = 16384
cfg.model.context_parallel_size = 2

代码锚点

LLM packed SFT 配置接口:

dataset_kwargs = {}
offline_packing_specs = None
if enable_offline_packing:
    dataset_kwargs["pad_to_max_length"] = True
    offline_packing_specs = PackedSequenceSpecs(packed_sequence_size=seq_length, pad_seq_to_mult=pad_seq_to_mult)

return _text_hf_dataset_config(
    source=HFDatasetSourceConfig(dataset_name="squad"),
    preprocessing=PromptCompletionSFTPreprocessingConfig(separator=" "),
    seq_length=seq_length,
    enable_offline_packing=enable_offline_packing,
    offline_packing_specs=offline_packing_specs,
    dataset_kwargs=dataset_kwargs,
    val_proportion=0.1,
    num_workers=1,
)

共享文本数据集助手当前选择使用固定宽度打包。请将其视为助手默认值,而不是通用的离线打包运行时要求;当所选分发器、内核或 CUDA-graph 路径需要静态宽度时,请保留它。

桥接验证:

enable_in_batch_packing = getattr(self.dataset, "enable_in_batch_packing", False)
enable_offline_packing = getattr(self.dataset, "enable_offline_packing", False)
offline_packing_specs = getattr(self.dataset, "offline_packing_specs", None)

if enable_offline_packing and enable_in_batch_packing:
    raise ValueError("enable_offline_packing and enable_in_batch_packing are mutually exclusive.")
if enable_offline_packing and offline_packing_specs is None:
    raise ValueError("offline_packing_specs must be set when enable_offline_packing=True.")
...
if enable_in_batch_packing:
    ...
    cp_multiple = 2 * cp_size if cp_size > 1 else 1
    sp_multiple = cp_size * tp_size if has_sp and tp_size > 1 else 1
    self.dataset.in_batch_packing_pad_to_multiple_of = math.lcm(cp_multiple, sp_multiple)
if self.model.context_parallel_size > 1:
    assert self.model.seq_length % (self.model.context_parallel_size * 2) == 0, ...
    if isinstance(self.dataset, FinetuningDatasetConfig):
        assert self.model.calculate_per_token_loss, ...
        assert not self.ddp.average_in_collective, ...
...
if enable_offline_packing and self.train.micro_batch_size > 1:
    raise ValueError(...)
...
if enable_in_batch_packing and self.train.micro_batch_size == 1:
    raise ValueError(...)

VLM 提供程序所使用的 collate 时批次内运行时:

def prepare_padded_or_packed_sequence_batch(
    batch,
    *,
    sequence_length,
    ...
    enable_in_batch_packing=False,
    in_batch_packing_pad_to_multiple_of=1,
    ...
):
    ...
    if enable_in_batch_packing:
        pack_right_padded_sequence_batch_to_mcore_thd(
            batch,
            sequence_length=sequence_length,
            pad_to_multiple_of=in_batch_packing_pad_to_multiple_of,
            ...
        )
        return

GPT-SFT 直接行打包:

def _collate_in_batch(self, batch):
    ...
    return build_mcore_thd_sequence_batch_from_rows(...)

Packed THD 运行时约束:

if batch.get("cu_seqlens_q") is not None:
    cu_seqlens = batch.get("cu_seqlens_q_padded")
    if cu_seqlens is None:
        cu_seqlens = batch["cu_seqlens_q"]
    if cu_seqlens.dim() > 1 and cu_seqlens.size(0) != 1:
        raise ValueError("Packed THD batches expect micro-batch size 1 for context-parallel slicing (THD layout)")
    return cu_seqlens.squeeze()

cu_seqlens = batch["cu_seqlens"]
if cu_seqlens.dim() > 1 and cu_seqlens.size(0) != 1:
    raise ValueError("Packed THD batches expect micro-batch size 1 for context-parallel slicing (THD layout)")

陷阱

  1. 离线 packed SFT、运行时批次内打包和 Energon 在线打包是不同的功能。离线和 Energon 打包使用物理 MBS1;运行时批次内打包使用大于 1 的 MBS。
  2. GPT-SFT 批次内打包需要 dataloader_type="single" 或 "cyclic";它不支持 "batch"。
  3. 启用 CP 时,打包序列长度必须满足可被 2 * context_parallel_size 整除。
  4. 使用 CP 进行微调时,需要 calculate_per_token_loss=True 和 ddp.average_in_collective=False。
  5. pad_cu_seqlens=True 还需要 pad_to_max_length=True。
  6. 打包支持因模型系列而异。Qwen3-Next、GLM-4.5 和 Qwen3.5-VL 在不同路径中包含显式退出。
  7. MTP 微调在文档中记录为与打包序列不兼容。
  8. 合成填充行,包括通过 samples_mapping 重新映射的负索引,必须保留全零损失掩码。
  9. 当离线打包使用 MBS1 时,global_batch_size 必须可被数据并行大小整除且不小于数据并行大小。
  10. 对于 SFT 和 PEFT,都应从 CP/TP/SP 推导 pad_seq_to_mult;不要按工作负载类型硬编码不同的值。
  11. pad_to_max_length 控制最终打包宽度,并且以固定形状执行要求为条件。
  12. Energon packing_buffer_size 是按 worker 计算的,并且也会影响验证;全局/评估批次计数指的是物理数据包,而不是源对话。
  13. 精确的 Energon 加载器恢复要求分片/拆分、DP world size、worker 数量、shuffle 设置/种子、处理器、序列长度、拓扑和打包缓冲区大小保持不变。

验证

使用已检入的单元覆盖:

uv run python -m pytest tests/unit_tests/training/utils/test_packed_seq_utils.py -v && \
uv run python -m pytest tests/unit_tests/training/test_config.py -k "packed_sequence or enable_in_batch_packing or offline_and_in_batch_packing_are_mutually_exclusive or context_parallel_seq_length_divisibility or context_parallel_finetuning_validations" -v && \
uv run python -m pytest tests/unit_tests/data/packing/test_in_batch.py -v && \
uv run python -m pytest tests/unit_tests/data/datasets/test_gpt_sft.py -k "in_batch_packing" -v && \
uv run python -m pytest tests/unit_tests/data/builders/test_gpt_sft_config.py -v && \
uv run python -m pytest tests/unit_tests/training/test_vlm_step.py -k "deferred_in_batch_packing or packed_metadata" -v && \
uv run python -m pytest tests/unit_tests/models/qwen_vl/data/test_energon.py tests/unit_tests/data/builders/test_energon_builder.py -v && \
uv run python -m pytest tests/unit_tests/tutorials/test_multimodal_data_tutorials.py -k "native_packing_loader" -v && \
uv run python -m pytest tests/unit_tests/data/datasets/test_packed_parquet.py -k "negative_index_zeroes_loss_mask" -v && \
uv run python -m pytest tests/unit_tests/data/datasets/test_sft.py -k "mapped_padding_rows_do_not_contribute_to_loss" -v

成功标准:

  • 所有选定的测试通过
  • 离线和批次内配置验证保持互斥
  • 打包元数据以 MCore THD 形式到达训练步骤
  • GPT-SFT 批次内打包拒绝全局批次 "batch" dataloader
  • 原生 Energon 打包精确恢复待处理组,并在不丢弃样本的情况下刷新有限的部分缓冲区
  • 映射的填充行不贡献损失

评论

支持 Markdown