Skip to content

Commit aea5c53

Browse files
Bsgg1cuichenx
andauthored
feat: support pipeline-stage-local HF export (#6291)
Signed-off-by: yangbosong <2112536022@qq.com> Signed-off-by: Chen Cui <chcui@nvidia.com> Co-authored-by: Chen Cui <chcui@nvidia.com>
1 parent 2161fe9 commit aea5c53

17 files changed

Lines changed: 955 additions & 48 deletions

File tree

‎docs/bridge-guide.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ AutoBridge.to_megatron_model(load_weights: bool = True, hf_path: str | Path | No
346346
AutoBridge.load_hf_weights(model: list[MegatronModule], hf_path: str | Path | None = None) -> None
347347

348348
# Megatron → HF conversion
349-
AutoBridge.export_hf_weights(model: list[MegatronModule], cpu: bool = False, show_progress: bool = True, conversion_tasks: Optional[list[WeightConversionTask]] = None) -> Iterable[HFWeightTuple]
349+
AutoBridge.export_hf_weights(model: list[MegatronModule], cpu: bool = False, show_progress: bool = True, conversion_tasks: Optional[list[WeightConversionTask]] = None, current_pp_stage_only: bool = False) -> Iterable[HFWeightTuple]
350350
AutoBridge.save_hf_pretrained(model: list[MegatronModule], path: str | Path, show_progress: bool = True) -> None
351351
AutoBridge.save_hf_weights(model: list[MegatronModule], path: str | Path, show_progress: bool = True) -> None
352352

‎docs/bridge-rl-integration.md‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,28 @@ for name, weight in bridge.export_hf_weights(megatron_model, cpu=True, show_prog
352352

353353
Point your inference engine (e.g., vLLM) to `"/path/to/hf_export"`.
354354

355-
### B) Zero-copy streaming via ZMQ (fast refit, colocated)
355+
### B) Pipeline-stage-local streaming for RL weight synchronization
356+
357+
When inference workers collectively receive shards from every training pipeline stage, avoid replicating the full
358+
HF stream across PP ranks:
359+
360+
```python
361+
for name, weight in bridge.export_hf_weights(
362+
megatron_model,
363+
show_progress=False,
364+
current_pp_stage_only=True,
365+
):
366+
send_to_inference_workers(name, weight)
367+
```
368+
369+
- `current_pp_stage_only` changes ownership only across the PP axis.
370+
- TP/EP ranks in the owning stage still gather shards and perform normal HF layout conversion.
371+
- Combining one representative stream from every PP stage reconstructs the normal complete export.
372+
- Source-only HF passthrough tensors are assigned to PP stage 0; duplicate Megatron parameters use the lowest PP owner.
373+
374+
This mode is opt-in. Omitting the flag preserves the complete stream on every rank.
375+
376+
### C) Zero-copy streaming via ZMQ (fast refit, colocated)
356377

357378
Stream tensors from the training side to your inference runtime without writing to disk. The transport is ZMQ peer-to-peer with async send/recv and ping‑pong buffers for overlap; Ray is used only for lightweight coordination. This replaces the earlier ad‑hoc per‑tensor IPC handle passing and aligns with the refactor in [NVIDIA-NeMo/RL#1267](https://github.com/NVIDIA-NeMo/RL/pull/1267).
358379

‎docs/fern/versions/nightly/pages/bridge-guide.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ AutoBridge.to_megatron_model(load_weights: bool = True, hf_path: str | Path | No
309309
AutoBridge.load_hf_weights(model: list[MegatronModule], hf_path: str | Path | None = None) -> None
310310

311311
# Megatron → HF conversion
312-
AutoBridge.export_hf_weights(model: list[MegatronModule], cpu: bool = False, show_progress: bool = True, conversion_tasks: Optional[list[WeightConversionTask]] = None) -> Iterable[HFWeightTuple]
312+
AutoBridge.export_hf_weights(model: list[MegatronModule], cpu: bool = False, show_progress: bool = True, conversion_tasks: Optional[list[WeightConversionTask]] = None, current_pp_stage_only: bool = False) -> Iterable[HFWeightTuple]
313313
AutoBridge.save_hf_pretrained(model: list[MegatronModule], path: str | Path, show_progress: bool = True) -> None
314314
AutoBridge.save_hf_weights(model: list[MegatronModule], path: str | Path, show_progress: bool = True) -> None
315315

‎src/megatron/bridge/models/conversion/auto_bridge.py‎

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -294,29 +294,34 @@ def _drop_readonly_config_properties(
294294
return {key: value for key, value in config_dict.items() if key not in readonly_properties}
295295

296296

297-
def _check_with_megatron_names_support(stream_fn: Callable[..., Any], owner: object) -> None:
298-
"""Fail up front when a bridge's streaming override cannot accept ``with_megatron_names``.
299-
300-
Bridges that override the streaming export with an explicit signature (rather than
301-
``*args, **kwargs``) have to forward the keyword themselves; without this check the
302-
request would only surface as an opaque ``TypeError`` once the generator is consumed.
303-
"""
297+
def _check_streaming_argument_support(stream_fn: Callable[..., Any], owner: object, argument: str) -> None:
298+
"""Fail early when a streaming override cannot accept an export argument."""
304299
try:
305300
parameters = inspect.signature(stream_fn).parameters
306301
except (TypeError, ValueError):
307302
# Not introspectable (C callables); let the call itself decide.
308303
return
309-
if "with_megatron_names" in parameters or any(
304+
if argument in parameters or any(
310305
parameter.kind is inspect.Parameter.VAR_KEYWORD for parameter in parameters.values()
311306
):
312307
return
313308
raise TypeError(
314309
f"{type(owner).__name__}.{getattr(stream_fn, '__name__', 'stream_weights')} does not accept "
315-
"'with_megatron_names'; a bridge that overrides the streaming export must forward this flag "
316-
"before its exported weights can carry source Megatron parameter names."
310+
f"'{argument}'; a bridge that overrides the streaming export must forward this flag "
311+
"before the requested export mode can be used."
317312
)
318313

319314

315+
def _check_with_megatron_names_support(stream_fn: Callable[..., Any], owner: object) -> None:
316+
"""Fail up front when a streamer cannot accept ``with_megatron_names``."""
317+
_check_streaming_argument_support(stream_fn, owner, "with_megatron_names")
318+
319+
320+
def _check_current_pp_stage_only_support(stream_fn: Callable[..., Any], owner: object) -> None:
321+
"""Fail early when a streamer cannot accept PP-local export."""
322+
_check_streaming_argument_support(stream_fn, owner, "current_pp_stage_only")
323+
324+
320325
class AutoBridge(Generic[MegatronModelT]):
321326
"""
322327
Automatically select and instantiate the appropriate bridge for a model.
@@ -801,13 +806,15 @@ def export_hf_weights(
801806
merge_adapter_weights: bool = True,
802807
weight_dtype: Optional[torch.dtype] = None,
803808
with_megatron_names: bool = False,
809+
current_pp_stage_only: bool = False,
804810
) -> Iterable["HFWeightTuple"]:
805811
"""
806812
Export Megatron model weights to HuggingFace format.
807813
808814
This method yields weight tensors in HuggingFace format, handling the
809815
gathering of distributed tensors and format conversion. It's useful for
810-
streaming weight export or custom processing. All ranks get full tensors.
816+
streaming weight export or custom processing. By default all ranks get full tensors;
817+
set ``current_pp_stage_only=True`` to yield only the current pipeline stage's tensors.
811818
812819
If the model contains LoRA adapters, they will be automatically merged
813820
into the base weights before export. This ensures the exported model
@@ -832,6 +839,10 @@ def export_hf_weights(
832839
a grouped-expert export, and none for HF-only passthrough tensors. Bridges that
833840
override ``stream_weights_megatron_to_hf`` must accept the flag; otherwise a
834841
``TypeError`` is raised before any weight is streamed.
842+
current_pp_stage_only: Export only parameters owned by the current pipeline stage and
843+
suppress cross-stage PP broadcasts. TP/EP gathering and HF layout conversion remain
844+
enabled. Defaults to False. Bridges that override the streamer must accept this
845+
flag when it is enabled.
835846
836847
Yields:
837848
HFWeightTuple: Named tuples of (param_name, weight_tensor), or HFSourcedWeightTuple
@@ -859,6 +870,8 @@ def export_hf_weights(
859870
bridge = self._model_bridge
860871
if with_megatron_names:
861872
_check_with_megatron_names_support(bridge.stream_weights_megatron_to_hf, bridge)
873+
if current_pp_stage_only:
874+
_check_current_pp_stage_only_support(bridge.stream_weights_megatron_to_hf, bridge)
862875
return bridge.stream_weights_megatron_to_hf(
863876
model,
864877
self.hf_pretrained,
@@ -869,6 +882,7 @@ def export_hf_weights(
869882
weight_dtype=weight_dtype,
870883
# Only forward the flag when set so bridges with a custom streamer keep working by default.
871884
**({"with_megatron_names": True} if with_megatron_names else {}),
885+
**({"current_pp_stage_only": True} if current_pp_stage_only else {}),
872886
)
873887

874888
def export_hf_weights_modelopt(

0 commit comments

Comments
 (0)