Skip to content

feat(rl): multimodal context parallelism#2948

Merged
hubert-marek merged 2 commits into
feat/rl-multimodal-packingfrom
feat/rl-multimodal-cp
Jul 9, 2026
Merged

feat(rl): multimodal context parallelism#2948
hubert-marek merged 2 commits into
feat/rl-multimodal-packingfrom
feat/rl-multimodal-cp

Conversation

@hubert-marek

@hubert-marek hubert-marek commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Part 7/7 — the only new code in the stack (in neither #2485 nor #2889; both deferred RL MM CP). Based on #2947 (feat/rl-multimodal-packing) — needs MM packing from #2947, the VLM CP machinery from #2946 (which is trainer-agnostic, in the modeling files), and the seq_lens contract from #2944. Rebased onto the current #2947 after its packing review fixes and the finalized #2946 CP cleanup.

Mirrors the SFT VLM CP path in the RL trainer, replacing the blanket NotImplementedError:

  • MRoPE multimodal batches under ulysses defer sharding to the model: input_ids/position_ids stay global so the vision encoder and image-embed merge see the full sequence; the model root forward shards embeds/positions after merge (and routed_experts with them). Text batches keep the existing pre-shard path.
  • seq_lens passes with seq_lens_are_pre_shard=True under CP — micro-batch boundaries span the pre-shard sequence, and pad_to_multiple_of=cp already guarantees shard divisibility (padding is its own seq_lens segment since feat(trainer): seq_lens as the packed-sample boundary contract (SFT + RL) #2944).
  • The LoRA token-count CP adjustment derives chunk_size from the sharded length even when input_ids stays global.
  • Gates relaxed: VLM + CP now requires cp_style='ulysses' and a custom model implementation instead of blanket rejection (impl="auto" passes the validator; get_model rejects it at load time if it resolves to hf).
  • Docs: guide on adding a custom VLM implementation (VLM training is custom-only as of feat(sft): multimodal (VLM) SFT #2945; this documents the full contract — registration, seq_lens consumption, MRoPE, dummy-vision FSDP symmetry, packing capability flag, CP hooks, KL validation).

Proposed follow-up (not in this stack): the deferral condition and sharding choreography here deliberately mirror sft/train.py; extracting the shared logic into one helper would keep the two trainer loops from drifting.

E2E

  • RL MM cp=2 ulysses run (multimodal_color_codeword shape) — W&B link TBD

  • Integration suite on 8×H100 at this tip: 27 passed, 6 failed, 1 error — every failure also fails (or is family-flaky) on unmodified main on the same node: 4× test_benchmark_regression (tolerances calibrated for the CI A6000 runners), test_reverse_text_moe (FileNotFoundError, env), test_reverse_text_multi_run (flaky family — main fails a sibling test). The one stack-correlated flip, test_reverse_text_sft::test_loss_goes_down, is a marginal assertion: 5 steps at batch 4, last-vs-first loss delta +0.0018, and this branch's loss/grad values match the feat(sft): Enable VLM SFT  #2485-head reference bit-for-bit (5.5987/73.2068). Suggest lengthening that CI run or comparing first-k/last-k means — flagged for review.

🤖 Generated with Claude Code


Note

Medium Risk
Touches distributed RL forward/CP sharding for multimodal batches; incorrect boundary or shard handling could skew logprobs/gradients, but scope is aligned with existing SFT behavior and gated to ulysses + custom models.

Overview
RL multimodal training can use context parallelism when cp_style='ulysses', instead of being rejected outright.

The RL trainer mirrors the SFT VLM CP path: for MRoPE batches (multimodal kwargs with image_grid_thw), it skips pre-forward sharding of input_ids/position_ids so vision merge and image embeds see the full sequence; the custom model shards after merge. Text-only CP batches still use the existing setup_cp_params path. seq_lens stays on the wire under CP with seq_lens_are_pre_shard=True so packed document boundaries refer to the pre-shard sequence. LoRA CP token counting uses sharded labels length for chunk_size when inputs stay global.

Config validation replaces blanket “VLM + CP forbidden” with VLM + CP requires cp_style='ulysses' (ring CP with VLM still errors).

Docs add a development guide for custom VLM implementations (registration, packed seq_lens, dummy vision for FSDP, optional CP hooks).

Tests swap the old rejection test for ring-vs-ulysses coverage and assert ulysses + VLM + cp=2 validates.

Reviewed by Cursor Bugbot for commit bd09c63. Bugbot is set up for automated code reviews on this repo. Configure here.

@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from 61959a4 to b0e3d09 Compare July 3, 2026 08:02
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-packing branch from 8b1fc96 to 027edc2 Compare July 3, 2026 08:02
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from b0e3d09 to f1920ce Compare July 3, 2026 09:43
@hubert-marek hubert-marek requested a review from samsja July 3, 2026 20:11
@hubert-marek hubert-marek marked this pull request as ready for review July 3, 2026 20:11
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from f1920ce to f34c10d Compare July 6, 2026 19:38
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-packing branch from 496a1b5 to 9c6eb77 Compare July 6, 2026 19:38
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from f34c10d to ce84dbf Compare July 6, 2026 19:42
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-packing branch 2 times, most recently from b99a705 to d8ce43b Compare July 6, 2026 19:51
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from ce84dbf to 1c096cf Compare July 6, 2026 19:51
Comment thread src/prime_rl/trainer/rl/train.py Outdated
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from 1c096cf to 7b18d58 Compare July 6, 2026 19:55
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-packing branch from d8ce43b to 1b600e5 Compare July 6, 2026 19:55
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from 7b18d58 to 7c38ffa Compare July 6, 2026 19:59
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-packing branch from 1b600e5 to 0221756 Compare July 6, 2026 19:59

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7c38ffa. Configure here.

Comment thread src/prime_rl/trainer/rl/train.py Outdated
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from 7c38ffa to dd21849 Compare July 6, 2026 20:30
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-packing branch from 0221756 to ab8ac2d Compare July 6, 2026 20:30
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from dd21849 to 3e99cac Compare July 7, 2026 21:32
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-packing branch 2 times, most recently from 3527041 to 7edc462 Compare July 9, 2026 23:37
Mirror the SFT VLM CP path in the RL trainer, replacing the blanket
NotImplementedError:

- MRoPE multimodal batches under ulysses defer sharding to the model:
  input_ids/position_ids stay global so the vision encoder and
  image-embed merge see the full sequence; the model root forward
  shards embeds/positions after merge (and routed_experts with them).
  Text batches keep the existing pre-shard path.
- seq_lens passes with seq_lens_are_global=True under CP — micro-batch
  boundaries span the pre-shard sequence and pad_to_multiple_of=cp
  already guarantees shard divisibility (padding is its own seq_lens
  segment).
- The LoRA token-count CP adjustment derives chunk_size from the
  sharded length even when input_ids stays global.
- Config gate relaxed: VLM + CP now requires cp_style='ulysses' and a
  custom model implementation instead of being rejected outright;
  resolve_pack_multimodal drops its CP rejection (packed MM rows meet
  the same divisibility guarantee).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Use the pre-shard boundary API, rely on shared CP implementation validation, and derive local LoRA chunk sizes from already-sharded labels.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hubert-marek hubert-marek force-pushed the feat/rl-multimodal-cp branch from 3e99cac to bd09c63 Compare July 9, 2026 23:44
@hubert-marek hubert-marek merged commit bd09c63 into feat/rl-multimodal-packing Jul 9, 2026
4 of 15 checks passed
@hubert-marek

Copy link
Copy Markdown
Contributor Author

Folded into #2947 now that the RL multimodal CP delta is small and directly coupled to multimodal packing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant