Communication-Efficient Pipeline-Parallel GRPO trained at 20/20 without collapse

LA-WARMUP-V2 · issue #59 · Qwen2.5-1.5B-Instruct + GSM8K · 1×H200 · vanilla GRPO (no-KL, no-entropy), flat LR 1e-6, 100 steps, val every 25 · locked substrate: PowerSGD rank-77, anchor cadence = delay_K = 20, signed_ema (α=0.25, β=0.50), resp=1024 · final deliverable, 2026-07-04

Contents: 1 Result · 2 What each run is · 3 Mechanism · 4 The four exercises E1–E4 · 5 E4 & softening · 6 Q-health · 7 Anchor-owned Q (C8a) · 8 Comm efficiency · 9 Findings · 10 Theory · 11 Implementation · 12 Caveats · 13 Links

C2 final (winner)
0.738
vs dense 0.776 — within 0.04
boundary comm
≈5%
bytes_ratio 0.050, ≈20× reduction
C7 collapse floor
0.064
same code · A0's exact level
Q reconstruction err
0.029
fast-owned Q · cold Q was 0.975

1 · The result — validation accuracy per arm

Held-out GSM8K accuracy (val-core/openai/gsm8k/acc/mean@1, 1319 prompts) at the four validation checkpoints. DENSE is the dashed ceiling; the collapse arms (C7 on current code, A0 historical) both dive to 0.064. C2 holds a flat near-dense plateau; C1 overshoots at step 75 then softens; C8a rises steadily but pays a warmup tax.

0.80.60.4 0.20.0 GSM8K val accuracy 02550 75100 training step epoch 2 (data repeats) → dense ceiling ~0.78 A0 collapse 0.064 C7 collapse 0.064 @75 C1 peak 0.762 C2 0.738
DENSE — ceiling (comm_eff OFF), no collapse (−0.009) C2 — fresh-sign-only (E4 winner) C1 — correct every tick (stale signs) C8a — anchor-owned Q + projection C7 — pure stale-anchor floor → collapses @75 A0 — historical control (older code) → collapsed

Final ranking by val@100: DENSE 0.776 > C2 0.738 > C8a 0.686 > C1 0.582 > C7 0.390 (collapsed).

Results table

armideav@0v@25v@50v@75v@100collapse?
DENSEdense GRPO ceiling, comm_eff OFF (byte-identical upstream) 0.7510.7730.7850.776no (−0.009)
C2proj + correct fires-only (fresh signs, cadence=20) (0.083)0.5100.7320.7300.738no (stable)
C8aanchor-owned Q kept fresh via projection (stale-Q warmup) 0.6310.6720.6790.686no (stable)
C1proj + correct every tick (stale signs, cadence=1) 0.0830.4770.6360.7620.582no (softens −0.180)
C7pure stale-anchor floor: owns_q, no projection, stale θ + stale rollouts 0.6260.6050.0640.390YES @75
C0 / A0historical control on older code (owns_q, stale-M from fire 1) 0.0800.3880.064YES @50

val@0 is identical across the config-only comm-eff arms (0.083); DENSE has no anchor so its val@0 is not run. Every number is drawn from the per-run train.log / wandb (see §13).

2 · What each run actually is

Shared substrate (identical across every arm): Qwen2.5-1.5B-Instruct + GSM8K, 1×H200, vanilla GRPO (no KL, no entropy bonus), constant LR 1e-6 (no decay), 100 training steps, val every 25 (the epoch-1→2 data boundary is at step 58 = 7473/128). The comm-eff arms add PowerSGD rank-77 activation compression, anchor cadence = delay_K = 20, and the signed_ema merger (α=0.25, β=0.50), resp=1024. The arms differ only in the four knobs below — everything else is byte-identical. ("step=" in the comm_eff logs is an optimizer tick = 2× the training step.)

armQ ownerprojection (lookahead)warmup M (steps 1–29)sign cadencerolloutswhat it isolates
DENSE— (OFF)— (no anchor)full grads Dense GRPO, no compression, no anchor — the quality ceiling.
C1fast (owns_q=false, Q←orth(V) every step)learned-linear, engages step 30 (min_snaps=2)no_correct (M cold → zero correction)every tick (cadence=1)current-step (matched) The full redesign; correction folded continuously (stale signs between fires).
C2fast (owns_q=false)learned-linear, engages step 30no_correctfires only (cadence=20, fresh signs)current-step (matched) Same as C1 but correct only when sign(M) is fresh — the E4 test. Winner.
C7anchor (owns_q=true)OFF (no projection ever)stale_correct (M from raw stale θ[t−20], from fire 1)every tickstale paired Pure pre-lookahead comm-eff GRPO = the naive method. The collapse floor.
C8aanchor (owns_q=true, Q from θ̂ once engaged)learned-linear, engages step 30stale_correct (forced — no_correct illegal with owns_q=true)every tickcurrent-step when projecting Anchor owns Q, kept fresh via projection (fully anchor-centric architecture).
C0 / A0anchor (owns_q=true)learned, late (fire 4, step ~40)stale_correct (from fire 1)every tickstale paired Historical control on older code; collapsed. C7 is its fresh same-code equivalent.

The four knobs that vary: anchor.owns_q (who rebuilds the PowerSGD basis Q), anchor.lookahead_* (whether/when the projector engages), anchor.warmup_mode (no_correct = skip the anchor pass while M would be stale vs stale_correct = today's raw stale correction), and spectral.cadence (fold the held M every tick vs only on fresh-M fires).

3 · The mechanism — project → measure → correct → feedback

The fast net trains on PowerSGD-compressed (rank-77) boundary activations: its per-target gradient keeps the right magnitude but has corrupted signs on the coordinates the codec dropped. A clean "anchor" gradient could repair those signs — but in pipeline-parallel the anchor lags by K=20 ticks, where the stale weights have rotated ~orthogonal to the live ones (k-collapse, the mechanism that killed A0). The method makes the anchor gradient both fresh (via linear projection) and clean (measured at the projected point on the current batch), then folds only its signs into the trusted fast-net magnitude.

STAGE 1 · PROJECT from ≥K-stale θ[τ0],θ[τ1]: θ̂=(1+α·h/g)θ[τ0] −(α·h/g)θ[τ1]+r h=g ⇒ 2θ[τ0]−θ[τ1] (frozen AsyncPP seed) STAGE 2 · MEASURE load θ̂ into isolated clone → one clean GRPO fwd/bwd on current batch G_anchor → EMA M M←βM+(1−β)G_anchor STAGE 3 · CORRECT G_corr = α·G + (1−α)·|G|·sign(M) magnitude ← fast net sign ← clean anchor cold-M guard: pass-through STAGE 4 · FEEDBACK per-block scalar residual r←clip(r+η·e, ±1e-3) from retrospective prediction error e fine trim, ≈0 at 20/20 r feeds back into next projection (no-peek, retrospective only) The 4-stage anchor loop — per fire at tick t no-peek throughout · anchor takes no optimizer step, generates no rollouts, recomputes no rewards

The 5 SOLID invariants (all verified live at 0 / dev=0 in the logs): no-peek staleness cross-rank determinism (dev=0) magnitude/sign separation cold-M guard anchor isolation

4 · The four exercises E1–E4

The redesign is defined by four decisions, each posed as an exercise and verified against a specific run.

exquestionchoice madehow it was verified
E1Who owns / refreshes the PowerSGD basis Q? Fast net owns Q, rebuilt from orth(V) every step (owns_q=false). C1 recon 0.975→0.029 in ~7 steps, then flat; grad_norm settles ~1.2 by step 7. No cold-Q blowup. Contrast: C7/C8a (owns_q) hold recon 0.975 with grad_norm in the 100s–1200s through step 9.
E2What does warmup do before projection is legal? no_correct — skip the anchor pass entirely, zero correction, until projection is possible. Merger counters: coldM_fallbacks == corrected (196==196, every pass a no-op) for steps 1–29, flips to coldM_fallbacks=0 the instant M is set at step 30. warmup_no_correct_skips locks at 2.
E3When is projection legal, and then correct + tune? Earliest-legal projection (min_snapshots=2), then correct + learn the residual every fire. First projection at fire 60 = step 30: src_ticks=[39,19], h=20, g=20 → coeffs=(2.0000,−1.0000) (exact seed, r=0); 196 matrices projected / 142 excluded; batch=current_step. All isolation guards = 0; cross_rank_max_rel_dev = 0.
E4Fold the held M every tick, or only on a fresh-sign fire? Fresh-sign-only (cadence=20) — the winner: C2 > C1. C2 converges faster (0.732@50 vs 0.636) and ends higher & stable (0.738 vs 0.582); C1 overshoots to 0.762@75 then softens. The correction is large either way (rel_change ≈ 1.0, ~57% of signs flipped) — the win is about when to apply it.

5 · E4 head-to-head + softening decomposition

C1 folds the held M every tick (signs up to 19 ticks stale between fires); C2 folds it only on the fire tick when sign(M) is fresh. Side-by-side, the two share the same early trajectory but diverge in the epoch-2 window: C2 plateaus, C1 gives back its overshoot.

0.80.60.40.0 02550 75100 step C1 0.762 C2 0.738 (a) val — C1 vs C2
(b) peak → final softening Δval from each arm's peak to its val@100 0 DENSE −0.009 C2 ≈0 (stable) C1 −0.180 longer bar = larger late drop
Softening decomposition (needs the dense control). DENSE — same no-KL / flat-LR RL config, no comm-eff correction — peaks 0.785@75 and softens only −0.009 to 0.776. So generic RL overfitting accounts for ~0.01 of the late drop. C1's −0.180 is therefore dominated by the every-tick stale-sign correction, and C2 (fresh signs) shows ≈0 softening. This upgrades E4: stale-sign folding doesn't merely lower the level, it actively destabilizes the epoch-2 tail. C1's 0.582 is still 9× above A0's 0.064 and retains most of the peak's learning — over-sharpened (entropy still falling), not collapsed.

6 · Q-health — the cold-Q blowup and its cure (E1)

Two per-step signals from the CSVs. (a) reconstruction error: fast-owned Q (C1) drops from 0.975 to ~0.03 within ~7 steps and stays flat. The owns_q arms (C7, C8a) hold Q frozen at 0.975 through step 9, then snap to ~0.04 at the first fire (step 10) when the anchor rebuilds Q. (b) gradient norm (log scale): C1/C2 settle near ~1–2; C7 blows to the hundreds–thousands during the cold-Q window and later spikes into the ignition that precedes its collapse.

(a) reconstruction_rel_error 1.00.670.330.0 150100 step C7/C8a: cold Q 0.975 through step 9 C1 → 0.029
(b) grad_norm (log scale) 1000502.50.3 150100 step C7 cold-Q blowup 122–1201 C1 ~1.2

Panel (b) is drawn on a log axis so the fast-owned arms' calm ~1–2 range and C7's early hundreds/thousands and its late ignition spike are visible on one plot. C7's grad_norm hit 1201 at step 5 and spikes again near the collapse ignition; the ignition precedes the val@75 dive to 0.064.

7 · Can the anchor own Q if projection keeps it fresh? (C8a vs C7)

C7 and C8a start identically — both owns_q=true, both begin with a cold/stale Q rebuilt at stale weights. The single difference is projection: C7 never projects (raw stale θ + stale rollouts); C8a projects from step 30, so the anchor rebuilds Q from the projected θ̂ (≈θ_t, fresh) instead of θ[t−K]. The outcome is the whole story of k-collapse.

armprojectionQ source after warmupv@50v@75v@100grad_norm @75resp_len finaloutcome
C7nonestale θ[t−20]0.6050.0640.390ignition (30 @50)739COLLAPSE
C8afrom step 30projected θ̂ (≈θ_t)0.6720.6790.6861.42351HELD
Projection rescues anchor-owned Q — a fully anchor-centric architecture is viable. Where C7 collapses at v@75 (grad_norm spiked to 30 at step 50), C8a holds a calm, monotone rise (0.631→0.672→0.679→0.686, grad_norm 2.07@50 / 1.42@75, no softening). Once projection engages, the anchor rebuilds Q from the projected θ̂ instead of stale θ[t−K], averting k-collapse for Q the same way it does for M. So the anchor can own both Q and M — the architecture faithful to real pipeline-parallel — provided projection keeps them fresh. Caveat: C8a (0.686) sits below fast-owned C2 (0.738); it pays the cold/stale-Q warmup tax over steps 1–29 that fast-owned Q avoids. Net: fast-owned Q is the better default; anchor-owned + projection is a viable, non-collapsing alternative.

Collapse signature — the degenerate long-rambling mode (C7)

C7 doesn't just lose accuracy; it drifts into a degenerate policy. Its response length balloons from ~250 to ~1000 tokens and its entropy caves toward ~0.4 — the classic long-rambling collapse — while healthy arms hold resp_len ~200–250.

1050750450150 response length (tok) 150100 training step C7 resp_len → ~1000, degenerate healthy arms ~200–250

C7 response length climbs across steps ~55–75 to ~1000 tokens as the policy degenerates, then partially unwinds to ~730 during the erratic partial recovery; entropy over the same window falls to ~0.35–0.42. The healthy arms (C1 dashed shown) stay in a tight ~200–250 band.

8 · Communication efficiency — near-dense at 5% of the traffic

The training-path boundary traffic (activation + gradient across pipeline-stage boundaries) is bytes_ratio = 0.050 of the dense-equivalent — a ≈20× reduction. The whole point of the study is that this buys near-dense quality: C2's 0.738 sits within 0.038 of the dense 0.776.

Boundary communication (relative to dense) DENSE = 100% comm-eff ≈ 5% (≈20× less) Final GSM8K accuracy 0 0.40.8 DENSE 0.776 (ceiling) C2 0.738 gap 0.038

Headline: ~20× less boundary communication for a final-accuracy gap of just 0.038 against the dense ceiling — the project's north star. The method pays an early-learning tax (val@25: dense 0.751 vs C2 0.510) that the correction largely closes by step 50 (C2 0.732 vs dense 0.773).

9 · Empirical findings (all 9, with evidence)

1 · Goal met — no collapse.

The method (C1/C2) trains comm-eff GRPO at the locked cadence = delay_K = 20 / signed_ema substrate without collapsing. C2 final 0.738 vs dense 0.776 — within 0.04 of dense at ~5% of boundary communication.
Evidence: C2 val 0.510→0.732→0.730→0.738; bytes_ratio ≈ 0.05 (wandb t4qymvvu).

2 · E4 answer — fresh-sign-only (C2) > stale-every-tick (C1).

C2: fast, stable ~0.73 plateau, 0.738 final. C1: overshoots 0.762@75 then softens to 0.582. Folding stale signs between fires is a background stressor — it doesn't stop learning, but makes the trajectory overshoot-prone and noisier.
Evidence: the two val curves (§1, §5a); identical config apart from spectral.cadence.

3 · Collapse is REAL on current code.

C7 (pure stale-anchor, the same code as the method) collapses to 0.064 at val@75 — A0's exact level — then erratically partially recovers to 0.390. grad_norm spiked to 30 at step 50 (the ignition). The method prevents this. A clean same-code control, stronger than the older-code A0.
Evidence: C7 val 0.626→0.605→0.064→0.390; grad_norm ignition at step 50; resp_len → ~1000 (wandb imdo7bsb).

4 · Softening decomposition (needs the dense control).

DENSE softens only −0.009 (0.785→0.776) under the identical no-KL / flat-LR RL config. C1 softens −0.180. ⇒ C1's late drop is dominated by the every-tick stale-sign correction, not generic RL overfitting. This upgrades E4: stale-every-tick is actively harmful late.
Evidence: DENSE peak 0.785→0.776; C1 peak 0.762→0.582; C2 ≈0 softening (§5b).

5 · E1 (fast-owned Q) works.

recon_rel_error 0.975→0.029 in ~7 steps, then flat ~0.027–0.030; grad_norm settles to ~1.2 by step 7. No cold-Q blowup. Contrast: A0 and C7 (owns_q, cold Q) blew grad_norm to 122–1201 (C7) / 150–1760 (A0) for steps 1–9 at recon 0.975 (frozen random Q).
Evidence: C1.csv recon & grad_norm columns (§6); C7 recon held 0.975 through step 9.

6 · E2 (no_correct warmup) works — exactly.

Merger coldM_fallbacks == corrected (every pass a no-op → zero correction) on every tick before the first projection; flips to coldM_fallbacks=0 the instant M is set at step 30. warmup_no_correct_skips locks at 2 (double-skip is a delay_K=20 replay-granularity artifact; both fires 20 & 40 resolve to the same snapshot_tick=19).
Evidence: merger per-step counters in the C1 train.log.

7 · E3 (projection) works.

First projection at fire 60 = step 30: src_ticks=[39,19], h=20, g=20 → coeffs=(2.0000,−1.0000) = exact frozen AsyncPP seed (r=0), 196 decoder matrices projected / 142 excluded, batch=current_step (matched rollouts). Correction magnitude rel_change ≈ 1.0 across matrices (anchor flips sign on ~half the coords). All isolation guards = 0 (anchor_optimizer_steps / rollouts_generated / rewards_recomputed / grad_corrected). cross_rank_max_rel_dev = 0 (Q and residual DP-identical).
Evidence: projection log line + isolation-guard counters (C1 train.log).

8 · Early compression tax.

dense v@25 = 0.751 vs C1/C2 0.477/0.510 — compressed grads + no-correct warmup learn slower early; the correction (from step 30) closes most of the gap by step 50 (C2 0.732 vs dense 0.773).
Evidence: val@25 vs val@50 across arms (§1 table).

9 · C8a — projection rescues anchor-owned Q.

C8a = owns_q=true + stale_correct warmup + projection. Same cold-Q warmup start as C7, but where C7 collapsed at v@75 (0.064, grad_norm spiked to 30), C8a HELD: 0.631→0.672→0.679→0.686, grad_norm calm (2.07@50, 1.42@75), no collapse, no softening. Once projection engages the anchor rebuilds Q from the projected θ̂ (fresh, ≈θ_t) instead of stale θ[t−K], averting k-collapse for Q the same way it does for M. The anchor CAN own both Q and M if projection keeps them fresh — the architecture faithful to real pipeline-parallel. Caveat: C8a (0.686) sits below fast-owned C2 (0.738) — it pays the cold/stale-Q warmup tax (steps 1–29). Net: fast-owned Q is the better default; anchor-owned + projection is a viable, non-collapsing alternative.
Evidence: C8a.csv (val, grad_norm, resp_len); direct C7↔C8a contrast (§7); wandb c3eie0ls.

10 · Theoretical claims — why the mechanism holds

The 4-stage loop (§3) rests on four design choices, each with a stated reason:

The 5 SOLID invariants, all verified live in the logs:

  1. No-peek staleness — every anchor input (both snapshots, the residual) is ≥K-stale; nothing reads the live weights.
  2. Cross-rank determinismcross_rank_max_rel_dev = 0 for both Q and the learned residual (DP-identical across ranks).
  3. Magnitude/sign separation — magnitude strictly from G_noisy, sign strictly from M.
  4. Cold-M guard — ‖M‖ ≤ eps ⇒ gradient passes through unchanged (no correction from an unset M).
  5. Anchor isolation — anchor takes no optimizer step, generates no rollouts, recomputes no rewards, and corrects no gradient outside the merger (all four counters = 0).

11 · Implementation

All changes are additive; defaults are byte-identical to upstream verl (with the method's knobs at their defaults, training is unchanged). Commit 7a9653a0 on vast-ai-workload.

7 files touched:

filerole
verl/workers/config/comm_eff.pythe two new knobs + 4 validation rules
verl/trainer/config/actor/actor.yamlknob defaults (byte-identical behavior)
verl/workers/comm_eff/lookahead.pyearliest-legal projection + min_snapshots gate
verl/workers/engine/fsdp/transformer_impl.pyFSDP integration of the anchor loop
verl/workers/comm_eff/state.pyring min_points readiness gate + skip counter
examples/grpo_trainer/vast_comm_eff_baseline_...shlauncher wiring for the new knobs
tests/…15 new unit tests

12 · Honest caveats

wandb project: verl_compression_research (entity shamanework-pl).

armwandb runfinal val
DENSEytthgqdg0.776 (ceiling)
C1aevasghy0.582
C2t4qymvvu0.738 (winner)
C7imdo7bsb0.390 (collapsed @75)
C8ac3eie0ls0.686
C0 / A0a6d174tp0.064 (historical collapse)

Final deliverable for issue #59 (LA-WARMUP-V2). Self-contained (inline CSS + inline SVG, no external assets); renders offline. All headline numbers from research/runs/LA-WARMUP-V2/FINDINGS.md; all full-resolution curves computed from the per-step CSVs in research/runs/LA-WARMUP-V2/data/.