From b1be57803cf46b51eff2a4748c7f9a1b59159fd9 Mon Sep 17 00:00:00 2001 From: Yan Bai Date: Mon, 15 Dec 2025 06:22:31 -0800 Subject: [PATCH 1/4] megatron engine assert mbridge=True --- .../open_math_reasoning/run_sft_qwen3_8b.sh | 2 +- verl/models/mcore/registry.py | 77 ++++++---- verl/utils/megatron_utils.py | 8 + .../engine/megatron/transformer_impl.py | 143 +++++++----------- 4 files changed, 112 insertions(+), 118 deletions(-) diff --git a/recipe/open_math_reasoning/run_sft_qwen3_8b.sh b/recipe/open_math_reasoning/run_sft_qwen3_8b.sh index 3b7e9bb5c6c..ec564a1d602 100644 --- a/recipe/open_math_reasoning/run_sft_qwen3_8b.sh +++ b/recipe/open_math_reasoning/run_sft_qwen3_8b.sh @@ -55,7 +55,7 @@ MEGATRON_ENGINE_CONFIG="\ engine.pipeline_model_parallel_size=${PP_SIZE} \ engine.virtual_pipeline_model_parallel_size=${VPP_SIZE} \ engine.context_parallel_size=${CP_SIZE} \ - engine.use_mbridge=False" + engine.use_mbridge=True" if [ "$backend" = "fsdp" ]; then ENGINE_CONFIG="$FSDP_ENGINE_CONFIG" diff --git a/verl/models/mcore/registry.py b/verl/models/mcore/registry.py index 88d90be3713..fd0f0ce1a5f 100644 --- a/verl/models/mcore/registry.py +++ b/verl/models/mcore/registry.py @@ -22,6 +22,54 @@ import torch import torch.nn as nn +from .model_forward import gptmodel_forward_no_padding, model_forward_gen +from .model_forward_fused import fused_forward_model_gen + + +class SupportedVLM(Enum): + QWEN2_5_VL = "Qwen2_5_VLForConditionalGeneration" + QWEN3_MOE_VL = "Qwen3VLMoeForConditionalGeneration" + QWEN3_VL = "Qwen3VLForConditionalGeneration" + + +def get_mcore_forward_fn(hf_config) -> Callable: + """ + Get the forward function for given model architecture. + """ + assert len(hf_config.architectures) == 1, "Only one architecture is supported for now" + if hf_config.architectures[0] in SupportedVLM: + return model_forward_gen(True) + else: + # default to language model + return model_forward_gen(False) + + +def get_mcore_forward_no_padding_fn(hf_config) -> Callable: + """ + Get the forward function for given model architecture. + """ + assert len(hf_config.architectures) == 1, "Only one architecture is supported for now" + return gptmodel_forward_no_padding + + +def get_mcore_forward_fused_fn(hf_config) -> Callable: + """ + Get the forward function for given model architecture. + """ + assert len(hf_config.architectures) == 1, "Only one architecture is supported for now" + if hf_config.architectures[0] in SupportedVLM: + return fused_forward_model_gen(True) + else: + # default to language model + return fused_forward_model_gen(False) + + +# ruff: noqa + +######################################################## +# below is the deprecated code +######################################################## + from .config_converter import ( PretrainedConfig, TransformerConfig, @@ -33,8 +81,6 @@ hf_to_mcore_config_qwen2moe, hf_to_mcore_config_qwen3moe, ) -from .model_forward import gptmodel_forward_no_padding, model_forward_gen -from .model_forward_fused import fused_forward_model_gen from .model_initializer import ( BaseModelInitializer, DeepseekV3Model, @@ -239,33 +285,6 @@ def init_mcore_model( ) -def get_mcore_forward_fn(hf_config: PretrainedConfig) -> Callable: - """ - Get the forward function for given model architecture. - """ - assert len(hf_config.architectures) == 1, "Only one architecture is supported for now" - model = get_supported_model(hf_config.architectures[0]) - return MODEL_FORWARD_REGISTRY[model] - - -def get_mcore_forward_no_padding_fn(hf_config: PretrainedConfig) -> Callable: - """ - Get the forward function for given model architecture. - """ - assert len(hf_config.architectures) == 1, "Only one architecture is supported for now" - model = get_supported_model(hf_config.architectures[0]) - return MODEL_FORWARD_NOPAD_REGISTRY[model] - - -def get_mcore_forward_fused_fn(hf_config: PretrainedConfig) -> Callable: - """ - Get the forward function for given model architecture. - """ - assert len(hf_config.architectures) == 1, "Only one architecture is supported for now" - model = get_supported_model(hf_config.architectures[0]) - return MODEL_FORWARD_FUSED_REGISTRY[model] - - def get_mcore_weight_converter(hf_config: PretrainedConfig, dtype: torch.dtype) -> Callable: """ Get the weight converter for given model architecture. diff --git a/verl/utils/megatron_utils.py b/verl/utils/megatron_utils.py index c319910d855..13c63ebdce0 100644 --- a/verl/utils/megatron_utils.py +++ b/verl/utils/megatron_utils.py @@ -1220,3 +1220,11 @@ def register_megatron_training_hooks(model: list[torch.nn.Module], optimizer): config.param_sync_func = [model_chunk.start_param_sync for model_chunk in model] if len(model) == 1: config.param_sync_func = config.param_sync_func[0] + + +def mapping_string_to_attn_backend(args: dict) -> dict: + if "attention_backend" in args and isinstance(args["attention_backend"], str): + from megatron.core.transformer.enums import AttnBackend + + args["attention_backend"] = AttnBackend[args["attention_backend"]] + return args diff --git a/verl/workers/engine/megatron/transformer_impl.py b/verl/workers/engine/megatron/transformer_impl.py index 00292a73c2e..1a1756012d8 100644 --- a/verl/workers/engine/megatron/transformer_impl.py +++ b/verl/workers/engine/megatron/transformer_impl.py @@ -41,13 +41,11 @@ load_megatron_optimizer, offload_megatron_model_to_cpu, offload_megatron_optimizer, - per_tensor_generator, register_megatron_training_hooks, ) from verl.utils.model import ( extract_multi_modal_inputs_tensordict, load_mcore_dist_weights, - load_megatron_gptmodel_weights, ) from verl.workers.config import HFModelConfig, McoreEngineConfig, McoreOptimizerConfig @@ -76,7 +74,7 @@ def __init__( self.engine_config = engine_config self.optimizer_config = optimizer_config self.checkpoint_config = checkpoint_config - + assert self.engine_config.use_mbridge, "use_mbridge must be True" self._init_device_mesh() set_random_seed(seed=self.engine_config.seed) @@ -110,70 +108,62 @@ def _init_device_mesh(self): ) def _build_tf_config(self): - from verl.models.mcore import hf_to_mcore_config - from verl.models.mcore.config_converter import mapping_string_to_attn_backend + from verl.utils.megatron_utils import mapping_string_to_attn_backend from verl.utils.torch_dtypes import PrecisionType self.param_dtype = PrecisionType.to_dtype(self.engine_config.dtype) - if self.param_dtype == torch.float16: - assert self.engine_config.use_mbridge, "fp16 mode requires use_mbridge to be True" self.dtype = PrecisionType.to_dtype(self.param_dtype) override_transformer_config = mapping_string_to_attn_backend({**self.engine_config.override_transformer_config}) - use_mbridge = self.engine_config.use_mbridge self.provider = None self.vanilla_bridge = self.engine_config.vanilla_mbridge - if use_mbridge: - if self.vanilla_bridge: - from verl.models.mcore.mbridge import AutoBridge - - bridge = AutoBridge.from_config(self.model_config.hf_config, dtype=self.param_dtype) - bridge.set_extra_args(**override_transformer_config) - tf_config = bridge.config - tf_config.fp16 = self.param_dtype == torch.float16 - tf_config.bf16 = self.param_dtype == torch.bfloat16 - else: - from verl.models.mcore.bridge import AutoBridge - - # Use Megatron-Bridge to convert HF config to Megatron config - bridge = AutoBridge.from_hf_pretrained( - self.model_config.local_path, trust_remote_code=self.model_config.trust_remote_code - ) - # Get Megatron provider and configure it - provider = bridge.to_megatron_provider(load_weights=False) - - # In case of invalid overrides, we need to make sure some critical params are set correctly - provider.params_dtype = self.param_dtype - - # Pass distributed info - provider.tensor_model_parallel_size = self.engine_config.tensor_model_parallel_size - provider.pipeline_model_parallel_size = self.engine_config.pipeline_model_parallel_size - provider.expert_model_parallel_size = self.engine_config.expert_model_parallel_size - provider.expert_tensor_parallel_size = self.engine_config.expert_tensor_parallel_size - provider.virtual_pipeline_model_parallel_size = self.engine_config.virtual_pipeline_model_parallel_size - provider.context_parallel_size = self.engine_config.context_parallel_size - provider.sequence_parallel = self.engine_config.sequence_parallel - - # Match verl implementation (need variable_seq_lengths) - from megatron.core.transformer.enums import AttnBackend - - provider.attention_backend = AttnBackend.flash - provider.variable_seq_lengths = True - provider.moe_token_dispatcher_type = "alltoall" - provider.moe_router_load_balancing_type = "none" - - # Apply transformer config overrides - for key, value in override_transformer_config.items(): - setattr(provider, key, value) - - provider.finalize() - self.provider = provider - tf_config = None # Will be set after model creation - self.bridge = bridge + if self.vanilla_bridge: + from verl.models.mcore.mbridge import AutoBridge + + bridge = AutoBridge.from_config(self.model_config.hf_config, dtype=self.param_dtype) + bridge.set_extra_args(**override_transformer_config) + tf_config = bridge.config + tf_config.fp16 = self.param_dtype == torch.float16 + tf_config.bf16 = self.param_dtype == torch.bfloat16 else: - self.bridge = None - tf_config = hf_to_mcore_config(self.model_config.hf_config, self.dtype, **override_transformer_config) + from verl.models.mcore.bridge import AutoBridge + + # Use Megatron-Bridge to convert HF config to Megatron config + bridge = AutoBridge.from_hf_pretrained( + self.model_config.local_path, trust_remote_code=self.model_config.trust_remote_code + ) + # Get Megatron provider and configure it + provider = bridge.to_megatron_provider(load_weights=False) + + # In case of invalid overrides, we need to make sure some critical params are set correctly + provider.params_dtype = self.param_dtype + + # Pass distributed info + provider.tensor_model_parallel_size = self.engine_config.tensor_model_parallel_size + provider.pipeline_model_parallel_size = self.engine_config.pipeline_model_parallel_size + provider.expert_model_parallel_size = self.engine_config.expert_model_parallel_size + provider.expert_tensor_parallel_size = self.engine_config.expert_tensor_parallel_size + provider.virtual_pipeline_model_parallel_size = self.engine_config.virtual_pipeline_model_parallel_size + provider.context_parallel_size = self.engine_config.context_parallel_size + provider.sequence_parallel = self.engine_config.sequence_parallel + + # Match verl implementation (need variable_seq_lengths) + from megatron.core.transformer.enums import AttnBackend + + provider.attention_backend = AttnBackend.flash + provider.variable_seq_lengths = True + provider.moe_token_dispatcher_type = "alltoall" + provider.moe_router_load_balancing_type = "none" + + # Apply transformer config overrides + for key, value in override_transformer_config.items(): + setattr(provider, key, value) + + provider.finalize() + self.provider = provider + tf_config = None # Will be set after model creation + self.bridge = bridge if not self.bridge: self.weight_converter = get_mcore_weight_converter(self.model_config.hf_config, self.dtype) @@ -232,28 +222,14 @@ def _build_megatron_module(self): if self.engine_config.use_dist_checkpointing: load_mcore_dist_weights(module, self.engine_config.dist_checkpointing_path, is_value_model=is_value_model) else: - if self.bridge is not None: - if self.vanilla_bridge: - self.bridge.load_weights(module, self.model_config.local_path) - else: - allowed_mismatched_params = [] - if self.is_value_model: - allowed_mismatched_params = ["output_layer.weight"] - self.bridge.load_hf_weights( - module, self.model_config.local_path, allowed_mismatched_params=allowed_mismatched_params - ) + if self.vanilla_bridge: + self.bridge.load_weights(module, self.model_config.local_path) else: - # (vermouth1992) this is a workaround to be compatible with the old API - tmp_config = OmegaConf.create( - {"model": {"path": self.model_config.local_path, "use_shm": self.model_config.use_shm}} - ) - - load_megatron_gptmodel_weights( - tmp_config, - self.model_config.hf_config, - module, - params_dtype=self.dtype, - is_value_model=is_value_model, + allowed_mismatched_params = [] + if self.is_value_model: + allowed_mismatched_params = ["output_layer.weight"] + self.bridge.load_hf_weights( + module, self.model_config.local_path, allowed_mismatched_params=allowed_mismatched_params ) if torch.distributed.get_rank() == 0: @@ -562,16 +538,7 @@ def forward_backward_batch(self, data: TensorDict, loss_function: Callable, forw def get_per_tensor_param(self): if self._is_offload_param: load_megatron_model_to_gpu(self.module, load_grad=False) - if self.bridge is not None: - per_tensor_param = self.bridge.export_weights(self.module) - else: - per_tensor_param = per_tensor_generator( - self.module, - self.model_config.hf_config, - self.weight_converter, - self.tf_config, - self.layer_name_mapping, - ) + per_tensor_param = self.bridge.export_weights(self.module) # TODO: support megatron LoRA return per_tensor_param, None From da5ce3db524db5a773dff94ee5d5461e586adfe9 Mon Sep 17 00:00:00 2001 From: Yan Bai Date: Mon, 15 Dec 2025 06:40:03 -0800 Subject: [PATCH 2/4] remove legacy CI --- .github/workflows/checkpoint_converter.yml | 175 ------------------ .../e2e_ppo_trainer_megatron_sglang.yml | 10 - .../e2e_ppo_trainer_megatron_sglang_2.yml | 32 ---- .../e2e_ppo_trainer_megatron_vllm.yml | 5 - .../e2e_ppo_trainer_megatron_vllm_2.yml | 37 ---- .github/workflows/model.yml | 30 --- docs/advance/checkpoint.rst | 28 +-- docs/perf/best_practices.rst | 4 + 8 files changed, 6 insertions(+), 315 deletions(-) delete mode 100644 .github/workflows/checkpoint_converter.yml diff --git a/.github/workflows/checkpoint_converter.yml b/.github/workflows/checkpoint_converter.yml deleted file mode 100644 index 4820497f79c..00000000000 --- a/.github/workflows/checkpoint_converter.yml +++ /dev/null @@ -1,175 +0,0 @@ -# # Tests layout - -# Each folder under tests/ corresponds to a test category for a sub-namespace in verl. For instance: -# - `tests/trainer` for testing functionality related to `verl/trainer` -# - `tests/models` for testing functionality related to `verl/models` -# - ... - -# There are a few folders with `special_` prefix, created for special purposes: -# - `special_distributed`: unit tests that must run with multiple GPUs -# - `special_e2e`: end-to-end tests with training/generation scripts -# - `special_npu`: tests for NPUs -# - `special_sanity`: a suite of quick sanity tests -# - `special_standalone`: a set of test that are designed to run in dedicated environments - -# Accelerators for tests -# - By default tests are run with GPU available, except for the ones under `special_npu`, and any test script whose name ends with `on_cpu.py`. -# - For test scripts with `on_cpu.py` name suffix would be tested on CPU resources in linux environment. - -# # Workflow layout - -# All CI tests are configured by yaml files in `.github/workflows/`. Here's an overview of all test configs: -# 1. A list of always triggered CPU sanity tests: `check-pr-title.yml`, `secrets_scan.yml`, `check-pr-title,yml`, `pre-commit.yml`, `doc.yml` -# 2. Some heavy multi-GPU unit tests, such as `model.yml`, `vllm.yml`, `sgl.yml` -# 3. End-to-end tests: `e2e_*.yml` -# 4. Unit tests -# - `cpu_unit_tests.yml`, run pytest on all scripts with file name pattern `tests/**/test_*_on_cpu.py` -# - `gpu_unit_tests.yml`, run pytest on all scripts with file without the `on_cpu.py` suffix. -# - Since cpu/gpu unit tests by default runs all tests under `tests`, please make sure tests are manually excluded in them when -# - new workflow yaml is added to `.github/workflows` -# - new tests are added to workflow mentioned in 2. - -name: checkpoint_converter -# latest version: Megatron-LM core_v0.14.0 https://github.com/NVIDIA/Megatron-LM/tree/core_v0.14.0 - -on: - # Trigger the workflow on push or pull request, - # but only for the main branch - push: - branches: - - main - - v0.* - pull_request: - branches: - - main - - v0.* - paths: - - "**/*.py" - # Other entrypoints - - "!examples/**" - - "!tests/**" - - "!verl/trainer/main_*.py" - - "!verl/trainer/fsdp_sft_trainer.py" - # Recipes - - "!recipe/**" - # FSDP - - "!verl/workers/**/*dp_*.py" - # Entrypoints - - ".github/workflows/checkpoint_converter.yml" - - ".github/workflows/e2e_ppo_trainer_megatron.yml" - - "examples/data_preprocess/gsm8k.py" - - "tests/special_e2e/run_ppo_trainer_megatron.sh" - - "verl/trainer/main_ppo.py" - - "verl/trainer/config/ppo_megatron_trainer.yaml" - -# Cancel jobs on the same ref if a new one is triggered -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} - -# Declare permissions just read content. -permissions: - contents: read - -env: - IMAGE: "verl-ci-cn-beijing.cr.volces.com/verlai/verl:sgl055.dev2" - DYNAMIC_RUNNER_ENDPOINT: "https://sd10g3clalm04ug7alq90.apigateway-cn-beijing.volceapi.com/runner" - -jobs: - setup: - if: github.repository_owner == 'volcengine' - runs-on: ubuntu-latest - outputs: - runner-label: ${{ steps.create-runner.outputs.runner-label }} - mlp-task-id: ${{ steps.create-runner.outputs.mlp-task-id }} - steps: - - uses: actions/checkout@v4 - - id: create-runner - uses: volcengine/vemlp-github-runner@v1 - with: - mode: "create" - faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}" - mlp-image: "${{ env.IMAGE }}" - - checkpoint_converter: - needs: setup - runs-on: [ "${{ needs.setup.outputs.runner-label || 'L20x8' }}" ] - timeout-minutes: 20 # Increase this timeout value as needed - env: - HTTP_PROXY: ${{ secrets.PROXY_HTTP }} - HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }} - NO_PROXY: "localhost,127.0.0.1" - HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - name: Install the current repository - run: | - pip3 install -e .[test] -# - name: Download Model to Use -# run: | -# huggingface-cli download Qwen/Qwen2.5-0.5B --local-dir ${HOME}/models/Qwen/Qwen2.5-0.5B -# huggingface-cli download deepseek-ai/deepseek-coder-1.3b-instruct --local-dir ${HOME}/models/deepseek-ai/deepseek-coder-1.3b-instruct -# export HF_HUB_OFFLINE=1 - - name: Running Huggingface to Megatron dist_ckpt converter (Qwen/Qwen2.5-0.5B) - run: | - ray stop --force - python scripts/converter_hf_to_mcore.py --hf_model_path=${HOME}/models/Qwen/Qwen2.5-0.5B --output_path checkpoints/Qwen/Qwen2.5-0.5B --test - - name: Running Huggingface to Megatron dist_ckpt converter (deepseek-ai/deepseek-coder-1.3b-instruct) - run: | - ray stop --force - python scripts/converter_hf_to_mcore.py --hf_model_path=${HOME}/models/deepseek-ai/deepseek-coder-1.3b-instruct --output_path checkpoints/deepseek-ai/deepseek-coder-1.3b-instruct --test - - name: Clean up - run: | - rm -rf checkpoints - - checkpoint_converter_large_moe_models: - needs: setup - runs-on: [ "${{ needs.setup.outputs.runner-label || 'L20x8' }}" ] - timeout-minutes: 30 # Increase this timeout value as needed - env: - HTTP_PROXY: ${{ secrets.PROXY_HTTP }} - HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }} - NO_PROXY: "localhost,127.0.0.1" - HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable - HF_ENDPOINT: "https://hf-mirror.com" - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - name: Install the current repository - run: | - pip3 install -e .[test] -# - name: Download Model to Use -# run: | -# huggingface-cli download Qwen/Qwen1.5-MoE-A2.7B-Chat --local-dir ${HOME}/models/Qwen/Qwen1.5-MoE-A2.7B-Chat -# export HF_HUB_OFFLINE=1 - - name: Running Huggingface to Megatron dist_ckpt CPU converter (Qwen/Qwen1.5-MoE-A2.7B-Chat) - run: | - ray stop --force - python scripts/converter_hf_to_mcore.py --hf_model_path=${HOME}/models/Qwen/Qwen1.5-MoE-A2.7B-Chat --output_path checkpoints/Qwen/Qwen1.5-MoE-A2.7B-Chat --use_cpu_initialization - - name: Running distributed Huggingface to Megatron dist_ckpt CPU converter (Qwen/Qwen1.5-MoE-A2.7B-Chat) - run: | - ray stop --force - torchrun --nproc_per_node 8 --nnodes 1 scripts/converter_hf_to_mcore.py --hf_model_path=${HOME}/models/Qwen/Qwen1.5-MoE-A2.7B-Chat --output_path checkpoints/Qwen/Qwen1.5-MoE-A2.7B-Chat_dist --use_cpu_initialization - - name: clean up - run: | - rm -rf checkpoints - - cleanup: - runs-on: ubuntu-latest - needs: - [ - setup, - checkpoint_converter, - checkpoint_converter_large_moe_models - ] - if: always() - steps: - - id: destroy-runner - uses: volcengine/vemlp-github-runner@v1 - with: - mode: "destroy" - faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}" - mlp-task-id: "${{ needs.setup.outputs.mlp-task-id }}" \ No newline at end of file diff --git a/.github/workflows/e2e_ppo_trainer_megatron_sglang.yml b/.github/workflows/e2e_ppo_trainer_megatron_sglang.yml index ccdc7c9c15d..df049bb0871 100644 --- a/.github/workflows/e2e_ppo_trainer_megatron_sglang.yml +++ b/.github/workflows/e2e_ppo_trainer_megatron_sglang.yml @@ -136,11 +136,6 @@ jobs: export VLLM_USE_V1=1 ray start --head ENGINE=sglang MODE=async RESUME_MODE=auto MODEL_ID=deepseek-ai/deepseek-coder-1.3b-instruct TOTAL_TRAIN_STEPS=2 bash tests/special_e2e/run_ppo_trainer_megatron.sh - - name: Test Megatron checkpoints merging function (DeepSeek Actor and Critic) - run: | - exp_name="deepseek-coder-1.3b-instruct-megatron-gsm8k-minimal" - python -m verl.model_merger test --backend megatron --local_dir checkpoints/verl-test/${exp_name}/global_step_1/actor --test_hf_dir checkpoints/verl-test/${exp_name}/global_step_1/actor/huggingface - python -m verl.model_merger test --backend megatron --is-value-model --local_dir checkpoints/verl-test/${exp_name}/global_step_1/critic --test_hf_dir checkpoints/verl-test/${exp_name}/global_step_1/critic/huggingface - name: Profiling GRPO GSM8K E2E training tests with 3D parallelism on 8 L20 GPUs with Megatron (Deepseek) run: | ray stop --force @@ -181,11 +176,6 @@ jobs: run: | ray stop --force ALL_OFFLOAD=True VAL_BEFORE_TRAIN=True TEST_FREQ=1 SAVE_FREQ=1 LR_WARMUP_STEPS=1 TOTAL_TRAIN_STEPS=2 MODEL_ID=Qwen/Qwen3-0.6B bash tests/special_e2e/run_ppo_trainer_megatron.sh - - name: Test Megatron checkpoints merging function (Qwen3 Actor and Critic) - run: | - exp_name="qwen3-0.6b-megatron-gsm8k-minimal" - python -m verl.model_merger test --backend megatron --tie-word-embedding --local_dir checkpoints/verl-test/${exp_name}/global_step_1/actor --test_hf_dir checkpoints/verl-test/${exp_name}/global_step_1/actor/huggingface - python -m verl.model_merger test --backend megatron --is-value-model --local_dir checkpoints/verl-test/${exp_name}/global_step_1/critic --test_hf_dir checkpoints/verl-test/${exp_name}/global_step_1/critic/huggingface - name: Running GSM8K E2E training tests with 3D parallelism on 8 L20 GPUs with FP8 rollout run: | ray stop --force diff --git a/.github/workflows/e2e_ppo_trainer_megatron_sglang_2.yml b/.github/workflows/e2e_ppo_trainer_megatron_sglang_2.yml index ccc503b0d58..e738fde2f8b 100644 --- a/.github/workflows/e2e_ppo_trainer_megatron_sglang_2.yml +++ b/.github/workflows/e2e_ppo_trainer_megatron_sglang_2.yml @@ -105,37 +105,6 @@ jobs: faas-url: "${{ env.DYNAMIC_RUNNER_ENDPOINT }}" mlp-image: "${{ env.IMAGE }}" - e2e_ppo_trainer_megatron-qwen2_5vl-3b: - needs: setup - runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"] - timeout-minutes: 60 # Increase this timeout value as needed - env: - HTTP_PROXY: ${{ secrets.PROXY_HTTP }} - HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }} - NO_PROXY: "localhost,127.0.0.1,hf-mirror.com" - HF_ENDPOINT: "https://hf-mirror.com" - HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - name: Install the current repository - run: | - pip3 install --no-deps -e .[test] - - name: Prepare Geo3k dataset - run: | - python3 examples/data_preprocess/geo3k.py --local_dataset_path ${HOME}/models/hf_data/hiyouga/geometry3k/ - - name: Prepare dist_ckpt of Qwen2.5-VL-3B, only supports dist_ckpt - run: | - python3 scripts/converter_hf_to_mcore.py --hf_model_path ${HOME}/models/Qwen/Qwen2.5-VL-3B-Instruct --output_path checkpoints/verl-test/qwen2.5-vl-3b-megatron - - name: Running Geo3k E2E training tests with 3D parallelism on 8 L20 GPUs with Megatron (Qwen) - run: | - ray stop --force - ENGINE=sglang ROLLOUT_MODE=async TRAIN_FILES=${HOME}/data/geo3k/train.parquet VAL_FILES=${HOME}/data/geo3k/test.parquet MAX_PROMPT_LENGTH=1024 MAX_RESPONSE_LENGTH=2048 MODEL_ID=Qwen/Qwen2.5-VL-3B-Instruct ADV_ESTIMATOR=grpo USE_DYNAMIC_BSZ=False SKIP_SAVE_HF_MODEL=1 COMMON_PP=4 COMMON_VPP=null COMMON_CP=1 COMMON_TP=2 USE_DIST_CKPT=true DIST_CKPT_PATH=checkpoints/verl-test/qwen2.5-vl-3b-megatron bash tests/special_e2e/run_ppo_trainer_megatron.sh - - name: clean up - run: | - rm -rf checkpoints - e2e_ppo_trainer_fsdp_sglang: needs: setup runs-on: [ "${{ needs.setup.outputs.runner-label || 'L20x8' }}" ] @@ -221,7 +190,6 @@ jobs: needs: [ setup, - e2e_ppo_trainer_megatron-qwen2_5vl-3b, e2e_ppo_trainer_fsdp-qwen2_5vl-3b, e2e_ppo_trainer_fsdp_sglang, ] diff --git a/.github/workflows/e2e_ppo_trainer_megatron_vllm.yml b/.github/workflows/e2e_ppo_trainer_megatron_vllm.yml index 35a50aae0ea..0fef97c8d1c 100644 --- a/.github/workflows/e2e_ppo_trainer_megatron_vllm.yml +++ b/.github/workflows/e2e_ppo_trainer_megatron_vllm.yml @@ -186,11 +186,6 @@ jobs: run: | ray stop --force ALL_OFFLOAD=True VAL_BEFORE_TRAIN=True TEST_FREQ=1 SAVE_FREQ=1 LR_WARMUP_STEPS=1 TOTAL_TRAIN_STEPS=2 MODEL_ID=Qwen/Qwen3-0.6B bash tests/special_e2e/run_ppo_trainer_megatron.sh - - name: Test Megatron checkpoints merging function (Qwen3 Actor and Critic) - run: | - exp_name="qwen3-0.6b-megatron-gsm8k-minimal" - python -m verl.model_merger test --backend megatron --tie-word-embedding --local_dir checkpoints/verl-test/${exp_name}/global_step_1/actor --test_hf_dir checkpoints/verl-test/${exp_name}/global_step_1/actor/huggingface - python -m verl.model_merger test --backend megatron --is-value-model --local_dir checkpoints/verl-test/${exp_name}/global_step_1/critic --test_hf_dir checkpoints/verl-test/${exp_name}/global_step_1/critic/huggingface - name: Running GSM8K E2E training tests with 3D parallelism on 8 L20 GPUs with FP8 rollout run: | ray stop --force diff --git a/.github/workflows/e2e_ppo_trainer_megatron_vllm_2.yml b/.github/workflows/e2e_ppo_trainer_megatron_vllm_2.yml index fb3e73ed02d..df99cc54e41 100644 --- a/.github/workflows/e2e_ppo_trainer_megatron_vllm_2.yml +++ b/.github/workflows/e2e_ppo_trainer_megatron_vllm_2.yml @@ -153,42 +153,6 @@ jobs: run: | rm -rf checkpoints - e2e_ppo_trainer_megatron-qwen2_5vl-3b: - needs: setup - runs-on: ["${{ needs.setup.outputs.runner-label || 'L20x8' }}"] - timeout-minutes: 60 # Increase this timeout value as needed - env: - HTTP_PROXY: ${{ secrets.PROXY_HTTP }} - HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }} - NO_PROXY: "localhost,127.0.0.1,hf-mirror.com" - HF_ENDPOINT: "https://hf-mirror.com" - HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - name: Install the current repository - run: | - pip3 install --no-deps -e .[test] - pip3 install transformers==$TRANSFORMERS_VERSION - - name: Prepare Geo3k dataset - run: | - python3 examples/data_preprocess/geo3k.py --local_dataset_path ${HOME}/models/hf_data/hiyouga/geometry3k/ - - name: Prepare dist_ckpt of Qwen2.5-VL-3B, only supports dist_ckpt - run: | - python3 scripts/converter_hf_to_mcore.py --hf_model_path ${HOME}/models/Qwen/Qwen2.5-VL-3B-Instruct --output_path checkpoints/verl-test/qwen2.5-vl-3b-megatron - - name: Running Geo3k E2E training tests with 3D parallelism on 8 L20 GPUs with Megatron (Qwen) - run: | - ray stop --force - TRAIN_FILES=${HOME}/data/geo3k/train.parquet VAL_FILES=${HOME}/data/geo3k/test.parquet \ - MAX_PROMPT_LENGTH=1024 MAX_RESPONSE_LENGTH=2048 MODEL_ID=Qwen/Qwen2.5-VL-3B-Instruct ADV_ESTIMATOR=grpo \ - USE_DYNAMIC_BSZ=False USE_FUSED_KERNELS=True SKIP_SAVE_HF_MODEL=1 \ - COMMON_PP=4 COMMON_VPP=null COMMON_CP=1 COMMON_TP=2 USE_DIST_CKPT=true \ - DIST_CKPT_PATH=checkpoints/verl-test/qwen2.5-vl-3b-megatron bash tests/special_e2e/run_ppo_trainer_megatron.sh - - name: clean up - run: | - rm -rf checkpoints - e2e_ppo_trainer_fsdp_vllm: needs: setup runs-on: [ "${{ needs.setup.outputs.runner-label || 'L20x8' }}" ] @@ -330,7 +294,6 @@ jobs: [ setup, e2e_ppo_trainer_megatron-moe-expert-parallel, - e2e_ppo_trainer_megatron-qwen2_5vl-3b, e2e_ppo_trainer_fsdp-qwen2_5vl-3b, e2e_ppo_trainer_fsdp_vllm, ] diff --git a/.github/workflows/model.yml b/.github/workflows/model.yml index cab35a68d96..c9f1f2deac2 100644 --- a/.github/workflows/model.yml +++ b/.github/workflows/model.yml @@ -48,7 +48,6 @@ on: # Entrypoints - ".github/workflows/model.yml" - "tests/special_distributed/test_fsdp_ckpt.py" - - "tests/special_distributed/test_mcore_config_converter.py" - "tests/special_distributed/test_tensor_dict.py" - "tests/models/**" - "tests/special_distributed/run_all.sh" @@ -144,34 +143,6 @@ jobs: run: | STRATEGY=fsdp2 torchrun --nproc_per_node=8 tests/special_distributed/test_fsdp_ckpt.py - mcore_config_converter: - needs: setup - runs-on: [ "${{ needs.setup.outputs.runner-label || 'L20x8' }}" ] - timeout-minutes: 20 # Increase this timeout value as needed - env: - HTTP_PROXY: ${{ secrets.PROXY_HTTP }} - HTTPS_PROXY: ${{ secrets.PROXY_HTTPS }} - NO_PROXY: "localhost,127.0.0.1,hf-mirror.com" - HF_ENDPOINT: "https://hf-mirror.com" - HF_HUB_ENABLE_HF_TRANSFER: "0" # This is more stable - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - fetch-depth: 0 - - name: Install the current repository - run: | - pip3 install -e .[test] -# - name: Download model config files -# run: | -# hf download Qwen/Qwen2.5-7B config.json --local-dir $HOME/configs/Qwen/Qwen2.5-7B -# hf download Qwen/Qwen3-8B config.json --local-dir $HOME/configs/Qwen/Qwen3-8B -# hf download deepseek-ai/deepseek-coder-1.3b-instruct config.json --local-dir $HOME/configs/deepseek-ai/deepseek-coder-1.3b-instruct -# hf download Qwen/Qwen2-57B-A14B config.json --local-dir $HOME/configs/Qwen/Qwen2-57B-A14B -# hf download Qwen/Qwen3-30B-A3B config.json --local-dir $HOME/configs/Qwen/Qwen3-30B-A3B -# hf download deepseek-ai/DeepSeek-V3-Base config.json --local-dir $HOME/configs/deepseek-ai/DeepSeek-V3-Base - - name: Running mcore config converter tests on 8 L20 GPUs - run: | - torchrun --nproc_per_node=8 tests/special_distributed/test_mcore_config_converter.py model_engine: needs: setup @@ -206,7 +177,6 @@ jobs: setup, model_rmpad, model_rmpad_fsdp2_unstable, - mcore_config_converter, model_engine ] if: always() diff --git a/docs/advance/checkpoint.rst b/docs/advance/checkpoint.rst index 56bec4a75c3..9782af951d9 100644 --- a/docs/advance/checkpoint.rst +++ b/docs/advance/checkpoint.rst @@ -137,32 +137,8 @@ Current implementation use solution 2. HuggingFace to Megatron DistCheckpoint details ---------------------------------------------- -If your model is quite huge, we recommend you to use Megatron dist-checkpoint to load the model. -Megatron dist-checkpoint supports loading with different kinds of model parallelism, -and it is much faster than the original checkpoint loading. - -To convert original HuggingFace model to Megatron dist-checkpoint, -you can use the ``scripts/converter_hf_to_mcore.py`` script. Large MoE models are temporarily supported with CPU initialization, -which is a little slower. While we are working on a better solution to support large models. - -Example command to convert the model is as follows: - -.. code:: bash - - python scripts/converter_hf_to_mcore.py \ - --hf_model_path Qwen/Qwen1.5-MoE-A2.7B-Chat \ - --output_path /mnt/disk/Qwen/Qwen1.5-MoE-A2.7B-Chat \ - --use_cpu_initialization # Only work for MoE models - - -Example command to distributed convert the huge model like deepseekv3 671B is as follows: - -.. code:: bash - - torchrun --nproc_per_node 1 --nnodes 8 --node_rank ${RANK} scripts/converter_hf_to_mcore.py \ - --hf_model_path deepseek-ai/DeepSeek-V3 \ - --output_path /mnt/disk/deepseek-ai/DeepSeek-V3 \ - --use_cpu_initialization # Only work for MoE models +Through ``mbridge``, we can directly save the mcore model to huggingface format during training. +No need to convert the model to Megatron dist-checkpoint format. Original Checkpoint Utils ------------------------- diff --git a/docs/perf/best_practices.rst b/docs/perf/best_practices.rst index d7ff382c250..69d8286710a 100644 --- a/docs/perf/best_practices.rst +++ b/docs/perf/best_practices.rst @@ -110,6 +110,10 @@ Parameter Reference Path to the actor checkpoint in HuggingFace-compatible format. - ``actor_rollout_ref.actor.megatron.use_mbridge``: Enable mbridge format conversion when the model was trained with Megatron. Use the latest mbridge release: https://github.com/ISEEKYAN/mbridge. + Now it must be True. + - ``actor_rollout_ref.actor.megatron.vanilla_mbridge``: + If set to True, use mbridge, else use Megatron-Bridge https://github.com/NVIDIA-NeMo/Megatron-Bridge. + Now it is True by default. and it will defaultly be set to False in the future(v0.8). :math:`\pi` - ``actor_rollout_ref.rollout.name``: From 9cab72bccc27d7d31ea4343ff584a37e9f137d8e Mon Sep 17 00:00:00 2001 From: Yan Bai Date: Mon, 15 Dec 2025 21:33:06 -0800 Subject: [PATCH 3/4] fix CI --- verl/trainer/config/_generated_ppo_megatron_trainer.yaml | 4 ++-- verl/trainer/config/engine/megatron.yaml | 2 +- verl/workers/config/engine.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/verl/trainer/config/_generated_ppo_megatron_trainer.yaml b/verl/trainer/config/_generated_ppo_megatron_trainer.yaml index 9e8e0e813bd..a117c0f332f 100644 --- a/verl/trainer/config/_generated_ppo_megatron_trainer.yaml +++ b/verl/trainer/config/_generated_ppo_megatron_trainer.yaml @@ -52,7 +52,7 @@ actor_rollout_ref: recompute_num_layers: null attention_backend: flash override_mcore_model_config: {} - use_mbridge: false + use_mbridge: true vanilla_mbridge: true use_remove_padding: true forward_only: false @@ -433,7 +433,7 @@ critic: recompute_num_layers: null attention_backend: flash override_mcore_model_config: {} - use_mbridge: false + use_mbridge: true vanilla_mbridge: true use_remove_padding: true forward_only: false diff --git a/verl/trainer/config/engine/megatron.yaml b/verl/trainer/config/engine/megatron.yaml index 84601f5a3f5..b588a96c1b3 100644 --- a/verl/trainer/config/engine/megatron.yaml +++ b/verl/trainer/config/engine/megatron.yaml @@ -75,7 +75,7 @@ override_transformer_config: override_mcore_model_config: {} # oc.select: default val for ref.megatron.use_mbridge -use_mbridge: False +use_mbridge: True # oc.select: default val for ref.megatron.vanilla_mbridge vanilla_mbridge: True diff --git a/verl/workers/config/engine.py b/verl/workers/config/engine.py index a8799c35691..b645715489e 100644 --- a/verl/workers/config/engine.py +++ b/verl/workers/config/engine.py @@ -120,7 +120,7 @@ class McoreEngineConfig(EngineConfig): override_ddp_config: dict[str, Any] = field(default_factory=dict) override_transformer_config: dict[str, Any] = field(default_factory=dict) override_mcore_model_config: dict[str, Any] = field(default_factory=dict) - use_mbridge: bool = False + use_mbridge: bool = True vanilla_mbridge: bool = True strategy: str = "megatron" From d9a02cc011e6dcf1bba360ea1154f4c00c25b7c7 Mon Sep 17 00:00:00 2001 From: Yan Bai Date: Mon, 15 Dec 2025 22:31:10 -0800 Subject: [PATCH 4/4] fix CI, defaultl use_mbridge --- tests/trainer/config/legacy_ppo_megatron_trainer.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/trainer/config/legacy_ppo_megatron_trainer.yaml b/tests/trainer/config/legacy_ppo_megatron_trainer.yaml index ea2a15d685e..06e2e94a662 100644 --- a/tests/trainer/config/legacy_ppo_megatron_trainer.yaml +++ b/tests/trainer/config/legacy_ppo_megatron_trainer.yaml @@ -111,7 +111,7 @@ actor_rollout_ref: dist_checkpointing_path: null seed: 42 override_transformer_config: {} # additional transformer config like: num_layers_in_first(/last)_pipeline_stage - use_mbridge: False + use_mbridge: True vanilla_mbridge: True profile: # profile the actor model in `update_policy` use_profile: False # open it when you want to profile the actor model