Train-infer mismatch for Open-weight MoE RL in Open-source code

kiddyboots2161 pts0 comments

0 train-infer mismatch for Open-weight MoE RL in Open-source code

Skip to the article

Contents

Where does mismatch come from, and where does it go?

In RL we sample rollouts with an inference engine and update our model by backpropagating gradients<br>through log probabilities computed by prefilling those sampled rollouts through a training engine.<br>These engines can compute different logprobs at many points in the forward pass, and there are multiple<br>ways to correct for the induced off-policyness of those rollouts.

Trainer scoring, inference prefill and decode

Inference processes the prompt in parallel during prefill, writes the model's cached state and<br>generates subsequent tokens with one-token decode calls. The trainer scores the completed sequence in<br>parallel. The<br>kernels in these paths can round or add the same values differently. For recurrent and compressed<br>attention, prefill also creates state that every subsequent decode step reads and updates.

++<br>++

2−8<br>2−8<br>2−8<br>2−8

1 + 2−7<br>(a + b) + c<br>a + (b + c)

Rounding depends on the order of additions

The smallest representable number in BF16 that is larger than 1 is 1 + 2−7. If we<br>compute (1 + 2−8), this rounds to 1. But if we first add (2−8 +<br>2−8), we get 2−7. Any time we do a reduction, we need to enforce the same<br>order of additions across engines.

KERNEL A<br>KERNEL B

OP 1<br>BF16<br>OP 2<br>OUTPUT A<br>OP 1<br>FP32<br>OP 2<br>OUTPUT B<br>same operations · different intermediate precision

Precision

When a kernel evaluates a sequence of operations, the precision of every intermediate value is part<br>of the calculation. If one engine rounds an intermediate to BF16 while the other keeps it in FP32,<br>later operations receive different values and can produce different output bits.

XORL TRAINING<br>SGLANG PREFILL<br>SGLANG DECODE

COMPLETE SEQUENCE · PARALLEL<br>PROMPT<br>STATE<br>HANDOFF<br>STATE<br>+t<br>STATE<br>+t

Cached state must match across prefill and decode

Decode reads the cache and updates it on each decoded token. In order to achieve agreement for<br>stateful layers like Gated DeltaNet, we need the prefill cache and every decode update to match<br>what the trainer computes. This means we need to eliminate prefill-decode mismatch.

How mismatch changes the GRPO update

Let qθ denote the policy evaluated by the trainer and<br>Ât the detached group-relative advantage in GRPOShao et al., “DeepSeekMath” (2024). Introduces GRPO and its importance ratio..<br>For a token at sampled after prefix ht, the on-policy gradient is:

∇J(θ) = Σₜ Eₕₜ∼dqθ, ₐₜ∼qθ(·|hₜ)[Âₜ ∇ log qθ(aₜ | hₜ)]

Each token gets scored multiple times.<br>Inference records a probability for the token while sampling.<br>Inference will record a slightly different probability if you ask it to prefill the finished sequence, and the trainer will compute a different one when it scores the rollout.<br>Write qt for the trainer's selected-token probability,<br>sPt for an inference<br>prefill replay at the current weights,<br>sDt for cached decode<br>at those weights and μt for the probability recorded<br>during sampling. We can define the importance sampling ratio in terms of these different forward<br>paths.

ρₜ = qₜ / μₜ = [qₜ / sᴾₜ] trainer scoring vs. serving prefill × [sᴾₜ / sᴰₜ] serving prefill vs. serving decode × [sᴰₜ / μₜ] stale state

The objectives we optimize in RL weight the gradient by the importance sampling ratio to correct for<br>these sources of mismatch.

The importance ratio in detail

At a fixed prefix ht, weighting samples by<br>qt /<br>μt rewrites an expectation over<br>μ(· | ht) as one over<br>qθ(· | ht). The prefix itself<br>still comes from μ:

At fixed hₜ, Eμ[ρₜ f] = Eqθ[f]. Across rollouts, Eμ[ρₜ f] keeps hₜ ∼ dμ, not hₜ ∼ dqθ.

Stale weights and cached state

Let s̃Dt denote decode<br>at the current weights with its cache rebuilt at those weights. The two terms are the weight change,<br>and the stale cache:

sᴰₜ / μₜ = [s̃ᴰₜ / μₜ] stale weights × [sᴰₜ / s̃ᴰₜ] stale cache

We always flush the cache after updating the weights of the inference engine, so the second term<br>is one.

Unclipped importance sampling

Unclipped importance sampling multiplies the token gradient by ρt. We detach the<br>selected-token logprobs used to form the ratio and differentiate log<br>qt.Tinker’s importance-sampling loss uses this formulation.

∇Lᴵˢₜ = −ρₜ Âₜ ∇ log qₜ; Eμ[‖gₜ‖²] = Eμ[ρₜ² Âₜ² ‖∇ log qₜ‖²]

The second moment of the gradient scales with the square of the importance ratio. We're training in<br>finite-sized batches, so there may be some high-variance steps where the gradient is dominated by a few<br>tokens with large ratios.

Clipped importance ratios

GRPO commonly clips this ratio using the PPOSchulman et al., &ldquo;Proximal Policy Optimization Algorithms&rdquo; (2017). Introduces the clipped surrogate objective. objective:

Jᴾᴾᴼₜ = min(ρₜ Âₜ, clip(ρₜ, 1 − εlow, 1 + εhigh) Âₜ); ∇Lᴾᴾᴼₜ = −1[not clipped] ρₜ Âₜ ∇ log qₜ

For a positive-advantage token, PPO drops the policy gradient once ρt exceeds<br>1 + εhigh. For a negative-advantage token, it drops the gradient once<br>ρt falls below 1 −...

prefill decode token importance state inference

Related Articles