Repository navigation
Generic RemoteBackend abstraction + Arctic adapter (parallel to PR #1) - #2
Conversation
1becf47 to
d71d8a6
Compare
| from ..utils import enable_full_determinism, postprocess_batch_func, prepare_micro_batches | ||
| from .utils import create_device_mesh, get_sharding_strategy | ||
|
|
||
| from arctic_training.debug import pr, pr0 |
There was a problem hiding this comment.
Changes in this file were for debugging. File should be unchanged.
| else: | ||
| total_data_size = len(data) | ||
| micro_batch_size_per_gpu = data["micro_batch_size_per_gpu"] | ||
| # assert total_data_size % (force_group_size * micro_batch_size_per_gpu) == 0, ( |
There was a problem hiding this comment.
File should be unchanged. Revert changes.
| Returns: | ||
| tensor: sliced response tensor of shape [bsz, max_response_len] | ||
| """ | ||
| # print(f"{tensor.is_nested=}") |
There was a problem hiding this comment.
File should be unchanged.
| logger = logging.getLogger(__file__) | ||
| logger.setLevel(os.getenv("VERL_LOGGING_LEVEL", "WARN")) | ||
|
|
||
| ENABLE_TIMERS = False |
There was a problem hiding this comment.
File should be unchanged.
| @@ -0,0 +1,272 @@ | |||
| """ | |||
There was a problem hiding this comment.
Remove this recipe file. Let's use gsm8k example instead.
| @@ -0,0 +1,164 @@ | |||
| #!/bin/bash | |||
There was a problem hiding this comment.
Replace with gsm8k equivalent.
| ENABLE_TIMERS = False | ||
| if ENABLE_TIMERS: | ||
| from arctic_training.debug import SynchronizedWallClockTimerSimple | ||
| timers = SynchronizedWallClockTimerSimple(wall_clock_breakdown=True) | ||
| else: | ||
| from arctic_training.debug import SynchronizedWallClockTimerSimpleDummy | ||
| timers = SynchronizedWallClockTimerSimpleDummy(wall_clock_breakdown=True) | ||
|
|
There was a problem hiding this comment.
Remove debugging and timer codes
| dataloader_kwargs={"shuffle": shuffle}, | ||
| ) | ||
|
|
||
| tname_update_actor = timers.start("xyz ray_trainer._update_actor actor_rollout_wg.update_actor") |
There was a problem hiding this comment.
Remove timer codes.
|
|
||
| tname_update_actor = timers.start("xyz ray_trainer._update_actor actor_rollout_wg.update_actor") | ||
| actor_output = self.actor_rollout_wg.update_actor(batch_td) | ||
| timers.stop_and_print_elapsed(tname_update_actor) |
There was a problem hiding this comment.
Remove timer code.
| else: | ||
| actor_output = self.actor_rollout_wg.update_actor(batch) | ||
|
|
||
| timers.stop_and_print_elapsed(tname_e2e) |
There was a problem hiding this comment.
Remove timer codes.
0da961c to
34d4c31
Compare
| trainer.experiment_name=$experiment_name \ | ||
| trainer.project_name=arctic_rl_gsm8k_public \ | ||
| trainer.val_before_train=False \ | ||
| trainer.n_gpus_per_node=$NGPU_PER_NODE \ |
There was a problem hiding this comment.
| trainer.n_gpus_per_node=$NGPU_PER_NODE \ | |
| trainer.n_gpus_per_node=1 \ |
| algorithm.adv_estimator=grpo \ | ||
| data.train_files=/code/shared/gsm8k/train.parquet \ | ||
| data.val_files=/code/shared/gsm8k/test.parquet \ | ||
| data.train_batch_size=$BSZ \ |
There was a problem hiding this comment.
Simplify for readability by using consts instead of bash vars.
| data.train_batch_size=$BSZ \ | |
| data.train_batch_size=16 \ |
Mirrors snowflake-eng/arctic-verl#22, rebased onto upstream verl v0.7.1 (bec9ef7) so the verl team can review the generic remote-backend abstraction in isolation. Adds: * `verl/remote_backend/{base,trainer,worker,worker_utils}.py` — a 9- method `RemoteBackend` ABC, `RemoteBackendRegistry` (lazy-imports adapters via a `name -> dotted-path` map), `RemoteBackendTrainer` (RayPPOTrainer subclass that drives a backend via `from_config` and threads a `reconnect_handle()` into every forwarder), and a CPU-only forwarder worker that delegates compute_log_prob / update_actor / update_weights / save_checkpoint to the backend and rebuilds NJTs + FlopsCounter MFU on the way back. * `verl/trainer/ppo/arctic_rl_client.py` — the Arctic adapter, registered as `"arctic"` via `@RemoteBackendRegistry.register(...)`, and `verl/workers/rollout/arctic_rollout/` — the Arctic vLLM rollout replica that re-attaches via `from_config(handle=...)`. * `trainer.remote_backend` config field in `ppo_trainer.yaml` and the one-line conditional in `main_ppo.py` that selects `RemoteBackendTrainer` + the generic forwarder when the field is set. The standard in-process verl path is untouched when it isn't. * One canonical example (`examples/arctic_rl/run_bird_grpo_arl_zorro_yes.sh`) plus baseline scripts and a reward helper for parity with the Arctic public PR. Reviewer notes: * Backend payload schemas, wire formats, and loss-function plumbing stay inside each backend; the ABC says nothing about them. * `RemoteBackend.requires_single_forwarder() -> True` for Arctic; the trainer asserts `n_gpus_per_node * nnodes == 1` and a single rollout replica because the backend owns its own training / sampling parallelism downstream. * Adapters live in `RemoteBackendRegistry.MODULES`; adding a new backend (e.g. `"tinker"`) is one map entry + one decorated class. This branch is the parallel of snowflake-eng/arctic-verl#22 rebased onto upstream v0.7.1. Co-authored-by: Cursor <cursoragent@cursor.com>
34d4c31 to
d55fe02
Compare
61ed850
into
Snowflake-AI-Research:arctic_rl_share_v0.7.1
|
cc @sfc-gh-truwase @zw0610 — opened the upstream RFC for this abstraction at verl-project#6537 (modeled after the |
Addresses @zw0610's structural review on verl-project#6422: - #2: verl/trainer/ppo/arctic_rl_client.py -> verl/workers/remote_client/arctic_rl_client.py - #3: verl/workers/rollout/arctic_rollout/ -> verl/workers/rollout/remote_rollout/arctic_rollout/ - #4: TODO(@zw0610) on RolloutReplicaRegistry registrations in verl/workers/rollout/replica.py:360 about lazy-init each option to avoid pulling in vLLM + SGLang + TRT-LLM transitive deps eagerly. Pure code-motion + a TODO; no behavior change. Imports inside the moved files and in workers/rollout/replica.py updated. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
This PR introduces a generic
RemoteBackendabstraction inverl/remote_backend/plus the Arctic adapter as a reference implementation, rebased onto upstream verlv0.7.1(bec9ef74).It has been iterated on with @sfc-gh-truwase's review and now lands here as a single, review-friendly diff.
Why
Today verl's training loop talks directly to in-process workers (FSDP/Megatron). For RL backends that already own their own training engine, sampling engine, log-prob engine, and weight-sync plumbing (Arctic RL, Tinker, etc.) the integration cost is large because there is no documented contract for "run this op out-of-process and give me back tensors / metrics".
RemoteBackendis that contract.What
verl/remote_backend/base.py— 9-method ABC:from_config(sole public constructor; takes an optionalhandle=to re-attach),reconnect_handle,destroy,compute_log_prob,update_actor,generate,update_weights,save_checkpoint,requires_single_forwarder. All abstract — backends declare every behaviour explicitly, no silent defaults.RemoteBackendRegistry—name -> dotted pathmap; backends register themselves at import time.main_ppo.pynever imports a concrete backend.verl/remote_backend/trainer.py—RayPPOTrainersubclass that creates the backend on the driver, threads thereconnect_handle()intowg_kwargs, and enforces single-forwarder constraints when the backend opts in.verl/remote_backend/worker.py— CPU-only forwarder worker group. Owns dispatch annotations, FlopsCounter MFU, NJT reconstruction, and metric normalisation. Forwards every op to the backend.verl/trainer/ppo/arctic_rl_client.py— Arctic adapter, registered as"arctic". Implements all 9 ABC methods on top ofArcticTraining.ArcticRLClient.verl/workers/rollout/arctic_rollout/— Arctic vLLM rollout replica that re-attaches viafrom_config(handle=...).trainer.remote_backend: nullfield inppo_trainer.yamland a one-line conditional inmain_ppo.pythat selects theRemoteBackendTrainerwhen the field is set. The standard in-process verl path is untouched when it isn't.Plug-in shape
Adding a new backend (e.g.
tinker) is two edits:"tinker": "verl.trainer.ppo.tinker_backend"toRemoteBackendRegistry.MODULES.@RemoteBackendRegistry.register("tinker")and implement the 9 ABC methods.Test plan
Notes
Made with Cursor