GRPO Plus (v0.50.0)

22 features. Unsloth + axolotl GRPO parity.

7 GRPO variants

yaml
task: grpo
training:
  grpo_variant: gspo   # standard | gspo | dapo | dr_grpo | bnpo | two_sided | rft
  grpo_delta: 0.5      # required for two_sided, optional for gspo (clip radius,
                       # default 0.2). (0, 1]. Rejected on every other variant
  grpo_fp16: true      # explicit FP16 mixed precision

The KL penalty: every variant applies grpo_beta (v0.76.0)

gspo, dapo, dr_grpo, bnpo, two_sided and rft train with a KL penalty of training.grpo_beta (default 0.1) against the reference policy (#1232). Each variant adds trl 0.29's per-token estimator exp(ref - logp) - (ref - logp) - 1, weighted by grpo_beta, to its per-token loss before its own reduction; rft applies it only to the completions it accepts and trains on. On a real trainer, the dapo loss and gradient equal trl's stock dapo ones at beta 0.1, 1 and 10 on the same generated batch. No speed or quality effect of the penalty is claimed.

(Since closed in v0.76.0: through v0.75.2 these variants validated grpo_beta and dropped it. The variant loss replaces trl's _compute_loss, the one place trl adds the KL term on Soup's path, so neither grpo_beta nor the beta the reward-hack controller sets at runtime changed the loss or the gradient, while trl still ran the reference forward on every step.) A variant config from v0.75.x trained with no KL term, and a v0.76.0 run of the same config will not reproduce it. If a recipe relied on the old behaviour (a dapo or dr_grpo recipe that expects to be KL-free), set grpo_beta: 0 explicitly.

  • grpo_beta: 0 is valid (#1247). It turns the KL penalty off, as the KL-free DAPO and Dr. GRPO recipes do, and it skips the reference-model forward pass. It is refused together with reward_hack_mitigation: kl_control or pid_lagrangian, because the controller needs a positive beta to steer: grpo_beta: 0 is mutually exclusive with reward_hack_mitigation='kl_control' (the mitigation controller requires a positive beta to steer); use grpo_beta > 0 or disable mitigation. A boolean or NaN grpo_beta is refused.
  • The kl metric is logged again for a variant with a non-zero grpo_beta, as trl's stock loss does: the masked batch mean of the per-token KL, averaged over the accepted tokens for rft (#1263).
  • Ratios are clamped before exponentiation (#1609). The kernels bound the importance ratio and the sequence ratio from above with a bound that adapts to the advantage's magnitude, and mask padding with a boolean mask, so a diverging policy no longer turns infinite terms into NaN parameter gradients. A run that went to NaN under a non-standard grpo_variant before v0.76.0 may have hit this.
  • Non-finite steps are skipped, for every task: grpo run (#342). A gradient watchdog zeroes non-finite gradients before the optimizer step, so that step updates nothing and the adapter is preserved. The step still counts toward the step total and the learning-rate schedule. The skipped-step count and fraction are saved in trainer_state.json, a run that skipped 5% or more of its steps prints a Gradient watchdog: warning at the end, and soup adapters audit reports the figure.
  • Validation. task: grpo does not evaluate by default and does not hold data.val_split out of training: the split is ignored, every row trains, and the run prints a one-line note saying so. Set training.eval_steps to hold the split out and evaluate it; the validation number is then the held-out reward, eval_reward (#1223, #1389). See Training methods.
  • Gold answers. reward_fn: accuracy and the verifiable math domain now read the gold the way they read the completion, and a gold that states no single final answer is refused before generation. See RLVR.

Re-run this

If you trainedWhat was wrongWhat to do
task: grpo with grpo_variant other than standardThe variant dropped grpo_beta, so it trained with no KL term while trl still ran the reference forward each step (#1232). With reward_hack_mitigation, the controller moved a beta the loss ignoredRe-run if you need the KL-regularised behaviour. Set grpo_beta: 0 to keep the KL-free behaviour on purpose
task: grpo with grpo_variant other than standard, and a run that went to NaNImportance ratios were exponentiated without a bound, so a diverging policy could produce infinite terms and NaN gradients (#1609)Re-run. v0.76.0 clamps the ratio
task: grpo with reward_fn: accuracy, or reward_fn: verifiable with verifiable_domain: mathSee RLVR: the gold was compared raw, and accuracy paid 0.5 for a gold that merely appeared in the completionRe-run if the logged reward sat near 0.0

Long-context & vision GRPO

yaml
task: grpo
training:
  vllm_sleep_mode: true     # free vLLM memory between rollouts

long_context_grpo and vision_grpo are validation gates, not features: they check the task, the backend and, for vision, a known VLM base (and the vision modality), and nothing reads them afterwards. long_context_grpo does not extend rope scaling into rollouts, and no Tiled MLP code stands behind it. vision_grpo does not make GRPO multimodal: the GRPO trainer loads the base with AutoModelForCausalLM and a tokenizer, not a vision processor. Both are on the list of 29 settings that are accepted and read by nothing: set to a non-default value they print Warning: training.long_context_grpo is accepted but read by nothing; v0.77 will refuse it when the config loads, and the run proceeds. "Refused from v0.77" is upstream's stated deadline, printed by the CLI. vllm_sleep_mode is read by the GRPO trainer.

Async rollout backends

yaml
task: grpo
training:
  rollout_backend: openenv   # art | ruler | nemo_gym | openenv
  rollout_func: soup_cli.envs.calculator:rollout   # required by openenv

openenv is the only backend that runs at v0.76.0. rollout_func is a module:function that receives the dataset prompts as seeds and returns the rows GRPO then trains on, which replace the dataset's prompts. It is not given the model and runs no interactive multi-turn episodes; the bundled soup_cli.envs.calculator:rollout is a single-shot seeder (see PRM-guided GRPO). art (OpenPipe ART), ruler and nemo_gym are accepted names whose adapters are not wired: they raise an ImportError naming the missing package, and a "not yet validated" error when it is installed.

Stability knobs (accepted; mostly record-only)

yaml
task: grpo
training:
  ref_model_ema_alpha: 0.99   # EMA-style ref refresh
  replay_buffer_size: 50000
  tis_threshold: 1.5          # truncated importance sampling
  mask_truncated_completions: true
  defer_rerolling: true
  skip_zero_advantage: true
  off_policy_mask_threshold: 0.7
  async_grpo_prefetch: true

At v0.76.0 these eight keys are accepted for task: grpo and attach a callback that writes counters (replay_size, tis_alerts, ema_alpha) into the training log. The one thing that callback does to training is the gradient watchdog described above, which skips non-finite optimizer steps and is attached to every task: grpo run, with or without these keys. async_grpo_prefetch, mask_truncated_completions, defer_rerolling, skip_zero_advantage and off_policy_mask_threshold do not change what the trainer does: the callback's own notes call them advisory, a hint or record-only. The replay buffer is never fed and the TIS counter is never called. ref_model_ema_alpha can only act if the callback is handed a separate reference model, and the Hugging Face callback handler does not hand it one, so there is nothing for it to update; a LoRA run has no separate reference model anyway (read from the code, not run on a GPU). These eight keys are not on the list of fields that warn at load, so nothing prints and no refusal date is attached to them. What does work on this page: the seven grpo_variant objectives (with the grpo_beta KL penalty on the six non-standard ones), grpo_delta, grpo_fp16, vllm_sleep_mode and the openenv rollout.

New task: prm

task: prm (Process Reward Model) wired through the standard reward-model trainer. Gates: vision + GRPO compat checked.

v0.50.0 shipped the schema. v0.53.3 wired grpo_fp16 routing + the known-VLM-base gate for vision_grpo; v0.53.11 lit up real loss kernels for every non-standard variant (gspo / dapo / dr_grpo / bnpo / two_sided / rft) via make_grpo_trainer_variant, made task: prm live with per-step MSE + reward head, and attached GRPOStabilityCallback, which logs counters for the stability knobs above (most of them are record-only, as that section says). See v0.53.11 — GRPO Plus finish.

Soup is free and Apache-2.0. If it saved you a training run, starring the repo costs nothing and helps most. You can also fund the GPU time behind the work a 4 GB laptop cannot reach.