执行摘要
- 一句话:VeOmni EP感知分片delta导出,大幅加速MoE权重同步
- 推荐动作:值得仔细阅读。特别关注:(1) 如何通过handler自身探测slot表(
enumerate_hf_slots)来避免手动维护slot表;(2) NaN sentinel的设计使得每个rank可以独立完成delta转换,无需rank 0全量重建;(3) BlockPlacement如何统一表达EP手动分割和FSDP DTensor分割。
功能与动机
VeOmni后端使用融合专家堆栈(如gate_up_proj)并结合EP+FSDP2混合并行,原有的分片delta引擎无法正确处理其shard geometry,导致无法使用delta_sharded模式进行高效权重同步。本PR填补了这一空白,使得VeOmni用户也能享受分片delta带来的通信节省。PR body明确指出这是review-requested split的第二部分(引擎核心+FSDP已移至#7144),在本PR中“wires veomni FSDP2+EP into the sharded delta sync end to end”。
实现拆解
- 重构MoE参数转换器:将
_map_moe_params_common和default_moe_param_handler的ep_rank参数改为expert_id_base,使其支持任意起点索引,从而兼容EP局部快照和全局全量。
- 新增VeOmni专用converter machinery:在
verl/workers/engine/veomni/utils.py中实现slot表自动枚举(enumerate_hf_slots)、单行转换(convert_row_to_hf)、delta entry构建器(hf_entry_converter)以及顶层导出生成器(veomni_shard_export),支持融合专家堆栈到HF独立命名的映射。
- 扩展spec层:在
ShardSpec中新增contributes字段,用于处理HSDP副本维度;将derive_placement重命名为derive_dtensor_placement并明确其职责;同时修改hf_delta_export,使其在spec.place已显式设置时直接使用exporter提供的placement,而不经过DTensor推导。
- 在VeOmni引擎中接入:替换
transformer_impl.py中get_per_tensor_param_shard的NotImplementedError为实际实现,通过调用veomni_shard_export获取带Spec的shard,并实现_hf_delta_entry方法,对含converter的spec分派到hf_entry_converter,否则回退到父类的DTensor逻辑。
- 配套测试与文档:在
test_sharded_delta.py中新增test_hf_delta_export_converter_param等测试,覆盖converter参数的delta导出、非平凡block placement、以及HSDP replica rank锁步行为;更新delta_weight_sync.md文档。
关键文件:
verl/workers/engine/veomni/utils.py(模块 VeOmni引擎;类别 source;类型 core-logic;符号 _map_moe_params_common, default_moe_param_handler, enumerate_hf_slots, convert_row_to_hf): 核心逻辑所在,新增所有converter machinery:slot表枚举、NaN探针、delta entry构建、顶层导出生成器。变更量最大(+256/-7)。
verl/workers/engine/veomni/transformer_impl.py(模块 VeOmni引擎;类别 source;类型 dependency-wiring;符号 _with_offload_back, _hf_delta_entry): 引擎入口集成,将get_per_tensor_param_shard从NotImplementedError改为实际实现,新增_hf_delta_entry进行converter dispatch。
tests/checkpoint_engine/test_sharded_delta.py(模块 分片delta;类别 test;类型 test-coverage;符号 test_hf_delta_export_converter_param, test_hf_delta_export_converter_nontrivial_block, test_hf_delta_export_replica_rank_stays_lockstep, test_derive_dtensor_placement_unsharded): 新增测试覆盖converter参数delta导出、非平凡block placement、HSDP replica锁步等场景。
verl/workers/engine/spec.py(模块 规格层;类别 source;类型 core-logic;符号 derive_dtensor_placement, ShardSpec.contributes): 基础spec扩展:新增contributes字段,重命名derive_placement为derive_dtensor_placement,调整docstring。
verl/workers/engine/utils.py(模块 引擎工具;类别 source;类型 dependency-wiring): hf_delta_export dispatch修改:当spec.place已设置时直接使用exporter提供的placement,而非通过derive_dtensor_placement推导。
docs/advance/delta_weight_sync.md(模块 文档;类别 docs;类型 documentation): 更新文档以反映VeOmni支持。
关键符号:_map_moe_params_common, default_moe_param_handler, enumerate_hf_slots, convert_row_to_hf, hf_entry_converter, veomni_shard_export, _is_ep_param, get_per_tensor_param_shard, _hf_delta_entry, derive_dtensor_placement, hf_delta_export
关键源码片段
verl/workers/engine/veomni/transformer_impl.py
引擎入口集成,将get_per_tensor_param_shard从NotImplementedError改为实际实现,新增_hf_delta_entry进行converter dispatch。
def get_per_tensor_param_shard(self, **kwargs):
"""Yield each rank's *local* shard with its ShardSpec."""
from .utils import veomni_shard_export
# 判断是否需要手动 offload(CPUOffloadPolicy 下不需要)
manual_offload = not getattr(self, "_uses_fsdp2_cpu_offload_policy", False)
if manual_offload:
load_veomni_model_to_gpu(self.module)
# 调用专用导出函数获取生成器和元数据
gen, meta = veomni_shard_export(self.module)
def _with_offload_back():
"""包装生成器,在迭代结束后执行 offload(如果适用)"""
yield from gen
if manual_offload and self._is_offload_param:
offload_veomni_model_to_cpu(self.module)
return _with_offload_back(), meta
def _hf_delta_entry(self, name, spec, place, lidx, lval):
"""veomni 的每参数 delta entry 构建:对含 converter 的 fused expert 参数使用
hf_entry_converter,否则回退到父类的 DTensor 处理。"""
from ..spec import BlockPlacement
from .utils import NO_SLOTS_MSG, hf_entry_converter
# 如果 spec 携带了完整的 converter 信息(to_hf_chunk + hf_slots),
# 并且 place 是 BlockPlacement,则使用 hf_entry_converter 构建 entry
if spec.to_hf_chunk is not None and isinstance(place, BlockPlacement) and spec.hf_slots is not None:
return hf_entry_converter(name, spec, place, lidx, lval)
# 如果只有 to_hf_chunk 但没有 hf_slots,则无法在发送端完成转换,报错
if spec.to_hf_chunk is not None:
raise NotImplementedError(f"{name}: {NO_SLOTS_MSG}")
# 其余参数走父类的 DTensor identity 处理
return super()._hf_delta_entry(name, spec, place, lidx, lval)
评论区精华
风险与影响
-
风险:依赖风险:本PR基于#7144的引擎核心重构,若#7144有回归会影响本PR。
正确性风险:converter machinery涉及EP+FSDP2混合并行placement推导,若BlockPlacement计算错误可能导致权重损坏或训练发散。
性能风险:每次sync中增加的converter调用(每参数NaN probe和slot lookup)可能引入额外开销,但从benchmark看收益远超成本。
兼容性风险:hf_delta_export的dispatch修改可能影响其他后端(如纯FSDP),需确保回归测试覆盖。
内存风险:虽然避免全量materialize,但NaN probe需要构造临时tensor,对于超大MoE仍应注意上限。
-
影响:用户影响:VeOmni后端用户现在可使用delta_sharded模式,获得4-21倍的权重同步加速。
系统影响:分片delta引擎的能力从仅支持FSDP扩展到支持EP+FSDP混合并行,为后续支持其他后端(如mcore)奠定基础。
团队影响:完成了重构路线图中的重要一步,使VeOmni能充分利用delta增益。
-
风险标记:依赖上游重构(#7144), EP+FSDP混合并行placement复杂, 新增converter可能引入回归, NaN Probe临时内存开销
关联脉络
- PR #7144 [ckpt, fsdp] feat: sharded delta block placements + backend-owned HF export (engine core + FSDP): 本PR基于 #7144 的引擎核心+FSDP重构,是本PR的前置依赖。
- PR #7080 [veomni] EP-aware sharded delta export (fused expert stacks): 被本PR取代的更早实现(已关闭)。
- PR #6612 [veomni] full-weight ep export: 讨论中提及的全量权重EP导出方案,本PR通过delta方法彻底优化了该路径。
- PR #7060 issue/PR about slot table mechanism: 在 NO_SLOTS_MSG 中引用 (#7060),可能是 slot 表机制的原始 issue 或 PR。
参与讨论