Sam Babichenko

Reading notes / Running RL at scale

Diagnosing the training-inference mismatch

Zhong et al., 2026

Policies and responses
x;x; y;y; yt;y_t; ∣y∣|y|prompt; response; its tt-th token; its length in tokens
sts_tprefix (x,y1,…,yt−1)(x,y_1,\dots,y_{t-1})
θ;\theta; θ′\theta'weights being updated; weights that sampled the batch
μθ′\mu_{\theta'}sampler's policy at θ′\theta' (the paper's πoldrollout\pi^{\rm rollout}_{\rm old})
πθ′;\pi_{\theta'}; πθ\pi_\thetatrainer's recomputation at θ′\theta' (the paper's πoldtrain\pi^{\rm train}_{\rm old}); trainer's policy at θ\theta
A^\hat Aadvantage, shared by all tokens of a response
Mismatch and ratios
δt\delta_tper-token gap log⁡πθ′(yt∣st)−log⁡μθ′(yt∣st)\log\pi_{\theta'}(y_t\mid s_t)-\log\mu_{\theta'}(y_t\mid s_t), in nats
rtr_tbypass ratio πθ/μθ′\pi_\theta/\mu_{\theta'} at token yty_t
rt′r'_trecomputation ratio πθ/πθ′\pi_\theta/\pi_{\theta'} at token yty_t
wtw_tcorrection weight πθ′/μθ′=eδt\pi_{\theta'}/\mu_{\theta'}=e^{\delta_t}; rt=rt′wtr_t=r'_tw_t
ℓ(r,A^)\ell(r,\hat A)PPO's clipped per-token loss with ratio rr
K1;K_1; K3K_3KL estimators −log⁡r-\log r and (r−1)−log⁡r(r-1)-\log r
ℓ0(r)\ell_0(r)zero-centered loss contribution −(r−1)A^-(r-1)\hat A
qtq_tper-token signal used for rejection: wtw_t or rtr_t
Constants
ε\varepsilonclip range of the PPO loss
CCtruncation cap on wtw_t in TIS (2)
τseq\tau_{\rm seq}threshold on a response's summed mismatch ∑tK(qt)\sum_tK(q_t) (0.001)

Context. In reinforcement-learning (RL) fine-tuning of a language model, an software built for fast generation, such as vLLM or SGLang; this note calls it the sampler generates the responses and a software that computes losses and gradients, such as PyTorch FSDP or Megatron; this note calls it the trainer computes the gradient, and the two give slightly different probabilities to the same tokens under the same weights because their kernels round differently. This the difference between the token probabilities that the sampler and the trainer assign to the same response under the same weights has been linked to collapsing runs, and earlier work reduced it by moving both engines from bfloat16: a 16-bit floating-point format with 7 mantissa bits, the default for large-model training to IEEE half precision: a 16-bit floating-point format with 10 mantissa bits, so its rounding is 8 times finer than BF16's (Qi et al., 2025) or corrected for it with importance weights. What it does by itself has been hard to say, because proximal policy optimization: a policy-gradient method that takes several gradient steps on each sampled batch and clips each token's probability ratio so the policy moves only a little per batch-style training is off-policy by design, with several gradient steps on each batch, and because clipping and the corrections change how the mismatch acts. Zhong et al. (2026) build the authors' sampler for the VeRL framework whose token log-probabilities equal those of the FSDP trainer bit for bit, a sampler whose token log-probabilities equal the trainer's bit for bit, and use it as a baseline with no mismatch at all.

Against this baseline, the small disagreements of a standard sampler are enough on their own to collapse a the policy gradient ∇θlog⁡πθ(y∣x) A\nabla_\theta\log\pi_\theta(y\mid x)\,A applied once per batch, with no ratio and no clipping run, which has no other source of off-policy error. In PPO and group relative policy optimization: PPO with each response's advantage computed from the rewards of the other responses sampled for the same prompt the mismatch enters through the old policy's probability in the denominator of the ratio. If that probability is recomputed by the trainer, clipping measures each update from a distribution that never generated the data, and the advantage-weighted loss terms become skewed long before a a running estimate of the KL divergence between the current and the old policy, computed from the ratios of sampled tokens and watched for signs of instability moves. If it is taken from the sampler, the reference is right, but the gradient still flows through the trainer's log-probabilities, and reward still falls. Of the four corrections tested that reweight or discard samples after generation, truncating the per-token weight between the two engines together with rejecting responses whose summed mismatch is large comes closest to the exact baseline.

Setting. A prompt xx gets a response y=(y1,…,y∣y∣)y=(y_1,\dots,y_{|y|}), and the prefix (x,y1,…,yt−1)(x,y_1,\dots,y_{t-1}) before token tt is the prefix before token tt. Write the sampler's policy at the weights θ′\theta' that generated the batch; the paper writes πoldrollout\pi^{\rm rollout}_{\rm old} for the sampler's policy at the weights that generated the batch, the trainer's recomputation of the same policy at the same weights θ′\theta'; the paper writes πoldtrain\pi^{\rm train}_{\rm old} for the trainer's recomputation of it, the trainer's policy at the weights θ\theta being updated; θ=θ′\theta=\theta' before the first gradient step on a batch for the policy being trained, and the per-token gap log⁡πθ′(yt∣st)−log⁡μθ′(yt∣st)\log\pi_{\theta'}(y_t\mid s_t)-\log\mu_{\theta'}(y_t\mid s_t), in nats; 0 for every token with an exact sampler for the per-token gap between trainer and sampler, δt=log⁡πθ′(yt∣st)−log⁡μθ′(yt∣st)\delta_t=\log\pi_{\theta'}(y_t\mid s_t)-\log\mu_{\theta'}(y_t\mid s_t). Three ratios follow: the ratio against the sampler, πθ(yt∣st)/μθ′(yt∣st)\pi_\theta(y_t\mid s_t)/\mu_{\theta'}(y_t\mid s_t), the PPO ratio with the sampler's probability in the denominator (the paper's bypass ratio), the ratio against the recomputation, πθ(yt∣st)/πθ′(yt∣st)\pi_\theta(y_t\mid s_t)/\pi_{\theta'}(y_t\mid s_t), the PPO ratio with the trainer's recomputed probability in the denominator, and the correction weight πθ′(yt∣st)/μθ′(yt∣st)=eδt\pi_{\theta'}(y_t\mid s_t)/\mu_{\theta'}(y_t\mid s_t)=e^{\delta_t}, the ratio between the two engines at the same weights, related by rt=rt′ wtr_t=r'_t\,w_t, with rt=πθ/μθ′r_t=\pi_\theta/\mu_{\theta'}, rt′=πθ/πθ′r'_t=\pi_\theta/\pi_{\theta'} and wt=πθ′/μθ′w_t=\pi_{\theta'}/\mu_{\theta'} at yt∣sty_t\mid s_t. With the advantage, shared by every token of a response; in GRPO it is the response's reward minus the mean reward of the responses sampled for the same prompt the advantage and the clip range of PPO and GRPO; the ratio is clipped to [1−ε,1+ε][1-\varepsilon,1+\varepsilon] the clip range, each token's PPO loss isℓ(r,A^)=−min⁡(r A^,clip⁡(r,1−ε,1+ε) A^),\begin{aligned}&\ell(r,\hat A)=-\min\big(r\,\hat A,\\&\qquad\operatorname{clip}(r,1-\varepsilon,1+\varepsilon)\,\hat A\big),\end{aligned}summed over the response and averaged over the batch. computing the old policy's probabilities with a trainer forward pass at θ′\theta' before updating, and using the loss ℓ(rt′,A^)\ell(r'_t,\hat A) uses ℓ(rt′,A^)\ell(r'_t,\hat A), and using the log-probabilities the sampler recorded while generating as the old policy's, giving the loss ℓ(rt,A^)\ell(r_t,\hat A) uses ℓ(rt,A^)\ell(r_t,\hat A). With an exact sampler wt=1w_t=1 and the two are the same.

Main findings (all empirical). (a) On Qwen3-8B in BF16, one sampled token, “that” in “The problem states that”, has log-probability −0.694-0.694 in the sampler and −0.827-0.827 in the trainer, and the trainer's most likely token there is a different one, a colon followed by two line breaks; the other tokens of the sentence agree to within 0.01. In a GRPO run of Qwen3-1.7B the batch mean of ∣δt∣|\delta_t| stays small while the batch maximum reaches about 1. (b) With REINFORCE on Qwen3-30B-A3B, the run with a standard sampler improves and then, after step 280, falls from 0.574 to 0.255 training reward and from 0.293 to 0.067 validation reward on AIME 2024, while the VeXact run keeps improving, to 0.753 and 0.534. (c) With GRPO on Qwen3-1.7B, VeXact holds training reward near 0.93; recomputation falls from about 0.87 to 0.40, partly recovers, and collapses to near zero after step 1,665; bypass falls to about 0.4 and stays there. (d) Under recomputation, KL monitors computed from the ratio stay at the exact baseline's level for 700 steps of falling reward, while the loss contributions of positive- and negative-advantage tokens are already skewed. (e) Truncating wtw_t and rejecting responses with a large summed mismatch nearly reproduces VeXact's curves.

Consequences. The mismatch is a cause of instability in its own right, worth checking alongside the hyperparameters and the reward when a run collapses. Where possible make the sampler exact, which takes the same model code in both engines and kernels whose results do not depend on batch composition, and the paper releases such a sampler. Otherwise use the sampler's log-probabilities as the old policy, truncate the correction weight wtw_t, and drop responses whose summed mismatch is large; the same conclusion about the reference distribution comes from a trust-region analysis that measures each update in total variation from the sampler's probabilities (Qi et al., 2026). A flat KL monitor computed from the ratio is no evidence of a healthy run under recomputation, since the recomputation ratio rt′r'_t is 1 before the first update whatever the mismatch is; monitor δt\delta_t itself. An exact sampler is also a reference for tuning the thresholds of the corrections, which otherwise have to be set by trial and error.

Step 1

The mismatch has two sources, and VeXact removes both. The first is different code: inference engines use kernel libraries built for generation, such as FlashInfer, that the trainer does not use, and make their own choices in implementing the same model. The second is the order of floating-point operations. Floating-point addition is not associative, so a kernel that sums in a different order gives a different result, and the order changes with GPU additions into the same memory location in whatever order threads arrive, which makes the order of summation, and so the rounding, vary from run to run and with the tiling of the computation, which a kernel's autotuning may change with the batch size. Since the sampler batches many requests together and the trainer batches responses differently, the same token can be computed in different orders. VeXact runs the same HuggingFace model code in both engines, with its own kernels registered in the FSDP trainer, and uses GPU kernels with fixed tiling and reduction order, so the result for each row of the input does not depend on how many other rows are in the batch (He, 2025), adding its own for RMSNorm, batched matrix multiplication and mixture-of-experts layers, with key-value splitting in attention turned off. Fixed tiling costs speed, which VeXact recovers in part with chunked prefill, CUDA graphs, pipeline parallelism and optimistic memory allocation for the attention cache; the paper reports no throughput figures against vLLM. With these changes every token's log-probability equals the trainer's, so δt=0\delta_t=0.

Step 2

With nothing else changed, the mismatch alone collapses REINFORCE. REINFORCE takes one gradient step per batch with no ratio and no clipping, so the only off-policy error is the mismatch and nothing hides it. With advantages standardized over the batch, the authors train a dense Qwen3-1.7B on the filtered MATH set from the FP16 study (problems R1-Distill-Qwen-1.5B solves sometimes but not always) and a Qwen3-30B-A3B mixture-of-experts model on DAPO-Math-17k, each once with vLLM as the sampler and once with VeXact. In the mixture-of-experts runs the vLLM run improves at first and then degrades after step 280, from 0.574 to 0.255 training reward and from 0.293 to 0.067 on AIME 2024 (mean over 32 samples per problem), with spikes in the gradient norm; the VeXact run keeps improving, to 0.753 and 0.534. The dense runs show the same pattern. Since the two runs differ only in the sampler, the authors conclude that the mismatch by itself destabilizes training.

Step 3

Recomputation measures each update from the wrong distribution and skews the loss before KL monitors react. In GRPO on Qwen3-1.7B (64 prompts per step with 8 responses each, 16 per minibatch, responses up to 8,192 tokens), VeXact holds training reward near 0.93, while recomputation degrades from about 0.87 to 0.40 within 650 steps, partly recovers, and collapses to near zero after step 1,665. KL monitors miss the onset. The estimators K1(r)=−log⁡rK_1(r)=-\log r, a per-token estimate of the KL divergence from the ratio rr; unbiased on average but of either sign, so terms can cancel and K3(r)=(r−1)−log⁡rK_3(r)=(r-1)-\log r, a per-token estimate of the same KL divergence that is never negative, computed from rt′r'_t, stay at VeXact's level for the first 700 steps, which follows from the definition: rt′r'_t compares the trainer with itself and equals 1 before the first update whatever δt\delta_t is. To see what the optimizer receives, the authors track the ℓ0(r)=−(r−1)A^\ell_0(r)=-(r-1)\hat A: the part of a token's loss that depends on the ratio; it has the same gradient as −rA^-r\hat A and is 0 at r=1r=1 of each token, split by the sign of A^\hat A. Under recomputation it is distorted unevenly: some contributions that push in a harmful direction are amplified, and the ones that would offset them are not amplified as much, so the update gets a net push in one direction before the mismatch is large enough to move a KL estimate. The authors suggest, without testing it directly, that the asymmetry comes from clipping, which removes the gradient above 1+ε1+\varepsilon only for positive advantages and below 1−ε1-\varepsilon only for negative ones, so noise in the denominator changes which tokens keep their gradient differently for the two signs.

Step 4

Bypass gives the right reference, but the gradient is still the trainer's, and reward degrades without loss spikes. With bypass the ratio rtr_t divides by the probability of the distribution that actually sampled the token, and the KL monitors do rise, since rt=wt≠1r_t=\htmlData{gr=g18}{w_t}\ne1 before the first update. Reward still falls to about 0.4 and stays there, with no loss anomaly of the size seen under recomputation. The numerator πθ\pi_\theta and its gradient ∇θlog⁡πθ\nabla_\theta\log\pi_\theta are computed by the trainer, so each update raises or lowers probabilities as the trainer computes them. Where the engines disagree, part of that change does not show up in the sampler's probabilities, so the sampler's policy, which generates the next batch, improves less than the trainer's numbers say.

Step 5

Truncating the correction weight and rejecting high-mismatch responses nearly matches the exact sampler. Two corrections from earlier work are tested as probes. multiplying each token's loss by min⁡(wt,C)\min(w_t,C), so tokens where the engines disagree strongly get bounded weight multiplies each token's recomputation loss ℓ(rt′,A^)\ell(\htmlData{gr=g17}{r'_t},\hat A) by min⁡(wt,C)\min(w_t,C), with the truncation cap on the correction weight, 2 in these runs the cap. Since wt rt′=rtw_t\,r'_t=\htmlData{gr=g16}{r_t}, for tokens below the cap this turns the unclipped term of the recomputation loss into that of bypass. Sequence-level rejection drops a response from the loss when its summed mismatch ∑tK(qt)\sum_tK(q_t) exceeds the threshold on a response's summed per-token mismatch above which it is dropped; 0.001 in these runs, where KK is K1K_1 or K3K_3 and the signal qtq_t is either wtw_t or rtr_t; used alone it is applied to the bypass loss, and combined with TIS to the truncated recomputation loss. Rejecting on wtw_t gives higher reward than rejecting on rtr_t; the authors' reason is that wtw_t measures only how far the sampler is from the trainer, while rtr_t also contains the policy's movement over the gradient steps, which clipping already controls. Combining truncation with rejection on wtw_t tracks VeXact most closely, because some single tokens have large mismatches inside responses whose total is moderate, and some responses accumulate a large total from many small ones; K1K_1 against K3K_3 makes little difference. These corrections only reweight or discard responses already generated, which loses some learning signal, and their thresholds have to be calibrated, here against VeXact.

Limits. The models are a 1.7B dense model and a 30B mixture-of-experts model, the GRPO experiments use only the 1.7B model, the corrections are compared on one filtered math set, each configuration is run once, and the curves comparing corrections are smoothed over 25 steps. The account of how recomputation skews the loss is a hypothesis supported by one diagnostic plot. That the corrections track the exact baseline is shown for this setting and these thresholds; the authors do not claim it generalizes, and they note the corrections may have side effects their experiments do not show. VeXact's speed relative to vLLM, which decides whether an exact sampler is practical at scale, is not reported.

Terms

VeXact
sampler whose log-probabilities equal the trainer's bit for bit: same model code, batch-invariant kernels
Batch-invariant kernel
fixed tiling and reduction order, so a row's result does not depend on the batch
Recomputation
old-policy probability from a trainer forward pass at θ′\theta'; ratio rt′=πθ/πθ′r'_t=\pi_\theta/\pi_{\theta'}
Bypass
old-policy probability recorded by the sampler; ratio rt=πθ/μθ′r_t=\pi_\theta/\mu_{\theta'}
Correction weight
wt=πθ′/μθ′=eδtw_t=\pi_{\theta'}/\mu_{\theta'}=e^{\delta_t}, so rt=rt′wtr_t=r'_tw_t
Zero-centered contribution
ℓ0(r)=−(r−1)A^\ell_0(r)=-(r-1)\hat A: the ratio-dependent part of a token's loss, 0 at r=1r=1
KL monitors under recomputation
rt′=1r'_t=1 before the first update whatever δt\delta_t is, so they miss the mismatch
TIS + sequence rejection
weight min⁡(wt,C)\min(w_t,C) and drop responses with ∑tK(wt)>τseq\sum_tK(w_t)\gt\tau_{\rm seq}: closest to VeXact
REINFORCE result
standard sampler: 0.574 to 0.255 training reward after step 280; VeXact: 0.753

Quiz

  1. Truncated importance sampling multiplies the recomputation loss ℓ(rt′,A^)\ell(r'_t,\hat A) by min⁡(wt,C)\min(w_t,C). Why does this help?
  2. Why does combining token-level truncation with sequence-level rejection track the exact sampler more closely than sequence-level rejection alone?

Sources