Prhub

#1891 Only allow --allgather-cp for DSA model

原始 PR 作者 zhuzilin 合并时间 2026-05-06 14:36 文件变更 4 提交数 1 评论 0 代码增减 +175 / -1

执行摘要

限制 --allgather-cp 仅用于 DSA 模型,防止 token 错乱

Issue #1871 详细描述了当启用 --allgather-cp 且 context_parallel_size > 1 时,非 DSA 模型的 token 顺序会被静默打乱。这是因为 data.py 的 DSA 分区与 hf_attention.py 的 zigzag 重排不匹配,而形状仍然正确,导致静默数值错误。该 PR 旨在通过前置验证阻止这种错误配置,从根本上消除静默数据损坏的风险。

值得精读。展示了从静默 bug 报告到前置验证修复的完整过程。重点学习 _validate_allgather_cp_supported 函数的防御性设计(快速返回、明确异常、可扩展白名单),以及测试模块如何通过 monkeypatch 模拟重型依赖进行纯单元测试。

讨论亮点

该 PR 无直接 review 评论,但关联 Issue #1871 包含详尽的技术分析,指出 data.py 的 DSA 分区逻辑与 hf_attention.py 的 zigzag 重排逻辑不匹配是 bug 根源。作者在 Issue 中给出了最小化复现脚本,并提出了解决方案思路。

实现拆解

  1. 定义 DSA 模型白名单:在 slime/backends/megatron_utils/arguments.py 中新增集合 _ALLGATHER_CP_DSA_ARCHITECTURES,包含 DeepseekV32ForCausalLMGlmMoeDsaForCausalLM
  2. 实现判断与验证函数:_is_allgather_cp_dsa_model 读取 HF config 的 architectures 字段;_validate_allgather_cp_supported 在 allgather-cp 启用且 cp_size > 1 时调用前者,不匹配则抛出 ValueError。
  3. 集成到参数解析入口:修改 megatron_parse_args,在解析 HF config 后(skip_hf_validate=False 时)调用验证函数。
  4. 单元测试:新建 tests/test_megatron_argument_validation.py,通过 monkeypatch 模拟依赖,覆盖允许、拒绝、忽略等 6 个场景。
  5. CI 集成:在 .github/workflows/pr-test.yml 及其模板中添加了该测试作为 CPU 任务。
文件 模块 状态 重要度
slime/backends/megatron_utils/arguments.py 参数校验 modified 7.51
tests/test_megatron_argument_validation.py 测试 added 7.57
.github/workflows/pr-test.yml CI 配置 modified 2.95
.github/workflows/pr-test.yml.j2 CI 模板 modified 2.24

关键符号

_is_allgather_cp_dsa_model _validate_allgather_cp_supported megatron_parse_args load_arguments_module make_allgather_cp_args

关键源码片段

slime/backends/megatron_utils/arguments.py core-logic

添加了核心验证函数 _is_allgather_cp_dsa_model 和 _validate_allgather_cp_supported,并在参数解析入口 megatron_parse_args 中调用。

# slime/backends/megatron_utils/arguments.py ( 新增部分 )# 所有支持 allgather-cp 的 DSA 模型架构名称集合
_ALLGATHER_CP_DSA_ARCHITECTURES = {
    'DeepseekV32ForCausalLM',
    'GlmMoeDsaForCausalLM',
}
​
​
def _is_allgather_cp_dsa_model(hf_config):
    '判断 Hugging Face 配置是否为支持的 DSA 模型'
    if hf_config is None:
        return False
    # 从 hf_config 中提取 architectures 列表(可能为 None 或空)
    architecture_names = getattr(hf_config, 'architectures', None) or []
    # 检查是否与白名单中的任何一个匹配
    return any(name in _ALLGATHER_CP_DSA_ARCHITECTURES for name in architecture_names)
​
​
def _validate_allgather_cp_supported(args, hf_config=None):
    '验证 --allgather-cp 是否在当前模型架构下合法'
    # 如果未启用 allgather-cp 或 context_parallel_size <= 1,直接通过
    if not getattr(args, 'allgather_cp', False) or getattr(args, 'context_parallel_size', 1) <= 1:
        return
    # 如果模型属于白名单,通过
    if _is_allgather_cp_dsa_model(hf_config):
        return
    # 否则抛出 ValueError,明确告知用户问题及解决方案
    raise ValueError(
        '--allgather-cp with --context-parallel-size > 1 is currently only supported for '
        'DSA attention models (DeepSeek-V3.2 and GLM-5.1). Non-DSA models still use the '
        'zigzag CP layout and would silently scramble token order under allgather CP. '
        'Please remove --allgather-cp, set --context-parallel-size 1, or use a supported DSA model.'
    )
​
​
# 在 megatron_parse_args 函数中集成验证(修改部分)
def megatron_parse_args(extra_args_provider, skip_hf_validate=False):
    args = _megatron_parse_args(extra_args_provider=extra_args_provider, ignore_unknown_args=True)
​
    hf_config = None # 确保 hf_config 变量总是被定义
    if args.hf_checkpoint and not skip_hf_validate:
        hf_config = AutoConfig.from_pretrained(args.hf_checkpoint, trust_remote_code=True)
        _hf_validate_args(args, hf_config)
​
    # 新加入的验证:在所有情况下(包括 hf_checkpoint 为空或 skip_hf_validate 时)都检查
    # 但如果 skip_hf_validate 为 True,hf_config 可能为 None,验证函数会安全地返回 False
    if not skip_hf_validate:
        _validate_allgather_cp_supported(args, hf_config)
​
    args.rank = 0
    args.world_size = args.actor_num_nodes * args.actor_num_gpus_per_node
    args = _set_default_megatron_args(args)
    return args
tests/test_megatron_argument_validation.py test-coverage

新增的全面单元测试文件,覆盖了验证函数的各种场景,包括正常通过、拒绝非 DSA 模型、允许 DSA 模型、忽略 cp_size=1 等。

# tests/test_megatron_argument_validation.py ( 新增文件摘要 )import importlib.util
import sys
import types
from pathlib import Pathimport pytest# ... 辅助函数:动态加载 arguments.py 模块(避免依赖实际 megatron 包)
def load_arguments_module(monkeypatch):
    '使用空桩模块替换真实的 megatron 和 transformers,加载目标模块'
    # 创建桩模块
    megatron_mod = types.ModuleType('megatron')
    training_mod = types.ModuleType('megatron.training')
    arguments_mod = types.ModuleType('megatron.training.arguments')
    # ... 其他桩
    arguments_mod.parse_args = lambda *args, **kwargs: None
    arguments_mod.validate_args = lambda args: args
​
    monkeypatch.setitem(sys.modules, 'megatron', megatron_mod)
    # ... 更多桩注册
​
    # 从文件加载目标模块
    module_path = Path(__file__).resolve().parents[1] / 'slime' / 'backends' / 'megatron_utils' / 'arguments.py'
    module_name = 'test_megatron_argument_validation_module'
    spec = importlib.util.spec_from_file_location(module_name, module_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module
​
​
def make_allgather_cp_args(**overrides):
    '构造简化版 args 对象,包含 allgather_cp 和 context_parallel_size'
    values = dict(allgather_cp=True, context_parallel_size=2)
    values.update(overrides)
    return types.SimpleNamespace(**values)
​
​
@pytest.mark.unit
def test_allgather_cp_rejects_non_dsa_cp_models(monkeypatch):
    '验证非 DSA 模型使用 allgather-cp 时抛出 ValueError'
    module = load_arguments_module(monkeypatch)
    args = make_allgather_cp_args()
    hf_config = types.SimpleNamespace(architectures=['Qwen3ForCausalLM'], model_type='qwen3')
    with pytest.raises(ValueError, match='only supported for DSA attention models'):
        module._validate_allgather_cp_supported(args, hf_config)
​
​
@pytest.mark.unit
@pytest.mark.parametrize(
    'hf_config',
    [
        types.SimpleNamespace(architectures=['DeepseekV32ForCausalLM'], model_type='deepseek_v3'),
        types.SimpleNamespace(architectures=['GlmMoeDsaForCausalLM'], model_type='glm'),
    ],
)
def test_allgather_cp_allows_dsa_architectures(monkeypatch, hf_config):
    '验证 DSA 模型使用 allgather-cp 时不抛出异常'
    module = load_arguments_module(monkeypatch)
    module._validate_allgather_cp_supported(make_allgather_cp_args(), hf_config) # 应正常返回
​
​
@pytest.mark.unit
def test_allgather_cp_ignores_cp_size_one(monkeypatch):
    '验证 context_parallel_size=1 时即便是非 DSA 模型也通过'
    module = load_arguments_module(monkeypatch)
    args = make_allgather_cp_args(context_parallel_size=1)
    hf_config = types.SimpleNamespace(architectures=['Qwen3ForCausalLM'])
    module._validate_allgather_cp_supported(args, hf_config) # 应正常返回

评论区精华

Issue #1871 bug 分析 正确性

该 PR 无直接 review 评论,但关联 Issue #1871 详细分析了 allgather-cp 与 zigzag 布局不匹配导致的静默 token 顺序错乱问题,并给出了复现步骤。

结论:问题确认,决定通过前置验证禁止非 DSA 模型使用 allgather-cp。 · 已解决

风险与影响

  1. 架构白名单维护成本:未来新增 DSA 模型需要同步更新集合,否则会错误阻止新模型使用 allgather-cp。
  2. 依赖 hf_config.architectures 字段:若该字段缺失或为空,验证函数会保守拒绝,可能误伤未正确设置 architectures 的本地 checkpoint。
  3. skip_hf_validate 绕过验证:当此标志为 True 时不会调用验证,主要影响离线工具,但训练中通常为 False。
  4. 测试为纯单元级别,未在真实 GPU 环境验证。

用户影响:之前错误启用 allgather-cp 的非 DSA 模型任务将立即得到明确的 ValueError,而非默默产生错误结果。DSA 模型用户无影响。系统影响:参数解析阶段增加一次轻量函数调用,性能开销可忽略。团队影响:需要维护白名单,并确保新增 DSA 模型时及时更新。

架构白名单需手动维护 依赖 hf_config.architectures 字段 skip_hf_validate 会绕过检查 测试覆盖限于单元级别

关联 Issue

#1871 [Bug] # [Bug] `--allgather-cp` silently scrambles token order in `hf_attention.py` CP reshuffle

完整报告

参与讨论