Requirements
- A cookbook checkout.
- A model with generator support. Models marked trainer-only in the catalog can’t run GRPO.
- Your own dataset, unless you train on a built-in one. Each row needs a prompt and a target to score completions against.
- Weights & Biases (W&B) logging set up or turned off.
GRPOConfiglogs to W&B by default, so without a W&B setup, a run fails after it provisions the model resource. ExportWANDB_API_KEY, setWANDB_MODE=offlineto log locally, or passwandb_project=Noneto turn logging off.
Run a GRPO recipe
Pass aGRPOConfig to the cookbook’s train_single_turn function. The config’s dataset field selects a registered dataset, which supplies the prompt format and the reward. To train on your own data, register your own dataset and reward.
This example trains Qwen3.6 35B A3B on GSM8K, the dataset the GRPO quickstart runs end to end:
train_single_turn provisions a model resource with num_generator_replicas generators (one by default) and one session, then stops both when it returns. To train on a model resource that’s already running, set model_resources_id instead of model.
Settings worth adjusting first, one at a time and in small increments:
learning_rate: The default is1e-5. Try5e-6for steadier training or2e-5for faster convergence. To compare several rates at once, train multiple adapters on one resource.group_sizeandtemperature: The defaults are8and0.7. Rewards and advantages explains when to change them.
GRPOConfig field.
Built-in datasets
The cookbook registers four datasets, each paired with a reward. To train on one, setdataset on GRPOConfig to its name. Their entries in post_training_cookbook/datasets/registry.py are also working examples of the fields in Register a dataset and reward.
Register a dataset and reward
To train on your own data, add an entry to the cookbook’s dataset registry. Each entry maps a dataset name to how its rows become prompts, what each row’s target is, and how to score completions against that target. These steps register a dataset of Python coding problems, where each row has aprompt and an expected_output.
1
Write the reward function
Add your reward function to The cookbook scores every completion twice: once with
post_training_cookbook/datasets/rewards.py. The recipe calls it with the model’s decoded completion and the row’s target, and expects a float back. This example runs the generated code and compares its standard output with the expected output:compute_reward, and once with compute_metric, which falls back to the reward when you don’t set one. This example sets no metric, so the lru_cache decorator returns the cached score on the second call, and each completion’s code runs only once.Single-turn recipes score completions one at a time, so a reward that waits on an API call, a model, or a subprocess repeats that wait for every completion, including the extra ones that dynamic sampling and validation score. To score with something slow, such as a judge model, write a one-turn environment instead. The cookbook runs every episode’s async step concurrently, so the waits overlap instead of adding up.2
Register the dataset
Add a
DatasetConfig entry to post_training_cookbook/datasets/registry.py:To train on data that isn’t on Hugging Face, load it from a local folder:
- Save the rows as a JSON Lines file named
train.jsonl, one row per line, in a folder of its own. - Set
hf_nameto the folder’s path,hf_configtoNone, andhf_splitto"train". The cookbook loads data with the Hugging Facedatasetslibrary, which readstrain.jsonlinto thetrainsplit. A relative path, such as"./my_problems", resolves from the directory you run the recipe in.
3
Test the reward
Run the reward on hand-written completions before you train on it:Add the same cases to
tests/datasets/test_rewards.py, then run uv run pytest tests/datasets. Include one edge case per failure mode: an empty completion, a very long completion, output in an unexpected format, and, for this reward, a timeout.4
Create and run the recipe
Create A code solution takes more tokens than a math answer, so this recipe sets
recipes/grpo/code_output.py:max_sample_tokens higher than the GSM8K recipe does. It also raises temperature from the default 0.7 to 0.8, so the completions in each group vary more.Run the recipe from your cookbook checkout:Report a separate metric
To train on one reward and report a different metric, set bothcompute_reward and compute_metric on the dataset’s registry entry. For example, train on a smooth, tiered reward that gives GRPO a gradient to follow, and report a strict binary metric that tells you how often the model is actually right:
compute_reward drives the advantages, and validation reports compute_metric as val/accuracy. The built-in game24 dataset works this way, pairing a partial-credit reward with a strict 0-or-1 metric.
Tune a GRPO recipe
TheseGRPOConfig settings change how the recipe samples, scores, and trains. Multi-turn training uses the same settings, except where noted, and adds async training.
Length budgets, overlong handling, and dynamic sampling run in the cookbook’s loop, not on the post-training service, so a loop you write with the SDK has them only if you implement them. Loss variants select one of the service’s built-in loss functions, so your own loop can use the same objectives.
Length budgets
max_model_length caps the prompt plus the completion, in tokens, and defaults to 2048. The cookbook derives two budgets from it:
max_sample_tokens: The maximum number of tokens generated per completion. It defaults tomax_model_length, so set it lower to leave room for the prompt.max_prompt_length: The longest prompt that single-turn training keeps. Longer prompts are dropped before training. It defaults tomax_model_length - max_sample_tokens, so if you set neither budget, every prompt is dropped.
thinking_effort on models that support it. Supported models lists each model’s thinking default and effort levels.
A truncated_ratio above about 30% indicates a tight budget or runaway-length output.
Overlong responses
The cookbook has two independent controls for completions that approach the token budget:- Overlong penalty (on by default): A reward penalty from the decoupled clip and dynamic sampling policy optimization (DAPO) paper. It ramps up linearly over the last
overlong_buffer_lentokens of the budget. The buffer defaults tomax_sample_tokens // 8.overlong_penalty_factorscales the penalty, and0turns it off. - Overlong masking (off by default): Set
overlong_masking=Trueto exclude truncated completions from training. Their rewards still count toward the group baseline. Single-turn training skips any step that masking leaves with fewer thangroup_sizesamples.
overlong_masking=True and overlong_penalty_factor=0.
Dynamic sampling
Dynamic sampling, also from the DAPO paper, keeps drawing fresh batches until it collectsbatch_size groups with varied rewards, or until it has drawn max_sampling_attempts batches. To turn it on, set max_sampling_attempts above its default of 1. The cookbook’s GSM8K recipe uses 3.
Each attempt generates a full batch, so a step can cost up to max_sampling_attempts batches of generation. If no attempt produces a group with varied rewards, the cookbook skips the step. Single-turn training logs skipped: 0/<groups> groups had non-zero reward variance, and multi-turn training logs samples=0.
Loss variants
The cookbook samples groups and computes advantages the same way for every objective, so switching objectives changes only the loss. TheseGRPOConfig fields select and configure the objective:
- Policy loss:
lossdefaults to"grpo". The other built-in loss functions are"ppo","cispo","dppo","dro", and"importance_sampling". They sum token losses instead of averaging them, so their loss grows with the number of trained tokens. When you switch to one, you may need to retune the learning rate. - Clip band:
clip_low_thresholdandclip_high_thresholdapply only to"grpo","ppo", and"cispo". Leave themNoneto use the service defaults, which are[0.8, 1.2]for GRPO and PPO, and[0, 4]for CISPO. To set your own bounds, see Clipping. CISPO’s clipping scales a token’s gradient instead of zeroing it. - DRO penalty:
"dro"requiresdro_beta, the coefficient on its quadratic log-ratio penalty. - Loss aggregation:
loss_agg_typeapplies only to"grpo"and sets how its per-token losses reduce to a single value. Aggregation changes the effective learning rate, so keep it fixed when comparing runs.GRPO_LOSS_AGGREGATION_TYPE_FIXED_HORIZON, the default, divides the masked sum by all batch tokens, including padding.TOKEN_MEANdivides by trained tokens.SEQUENCE_MEANaverages within each sequence, then across sequences, so each sequence carries equal weight.
- GSPO: To train with group sequence policy optimization (GSPO), keep the default
"grpo"loss and setratio_type="GRPO_LOSS_RATIO_TYPE_SEQUENCE". GSPO computes the importance ratio and clips over the whole completion instead of per token.
fb/loss/clip/* and fb/loss/ratio/mean, described in Service-emitted metrics. The other objectives use keys such as fb/ratio_mean, and CISPO reports fb/clip_fraction.
The cookbook config has no Kullback-Leibler (KL) reference-policy penalty. To add one, add a KL penalty in your own loop. For an objective the built-in loss functions don’t cover, write a custom loss function.
Continue a run
The recipe saves training checkpoints only when you settraining_checkpoint_interval, so set it on any run you might want to continue. To continue a run, rerun its recipe with the original settings, plus the ID of a training checkpoint the run saved and the original run’s directory:
checkpoints.jsonl, which holds the step the checkpoint was saved at and the run’s position in the dataset. The server’s step counter can’t replace the record, because a step that collects no usable groups advances the schedule without an optimizer update.
- Set
num_stepsto the total, not the remainder: Continuing from a checkpoint saved at step 40 withnum_steps=200trains 160 more steps. If the checkpoint is at or pastnum_steps, the run trains nothing and provisions nothing. - Keep the data and batch shape the same: The dataset contents and order,
batch_size, and single-turn or multi-turn mode must match the original run. The cookbook checks them against the record and fails before provisioning if they differ. - Keep the learning-rate and checkpoint settings the same: If they differ, the continued run’s schedule won’t match the original’s.
- Keep the run directory: Continuing requires the checkpoint’s record. If the record is missing, or the checkpoint was saved before the cookbook started recording data positions, start a new run from the weights with
load_optimizer=Falseinstead. - Use JSON-serializable training inputs: Any run that saves training checkpoints fingerprints its training data, so multi-turn task targets and metadata must be JSON-serializable.
Troubleshooting
zero_variance_skip_ratiostays above 50%: Checkidentical_nonzero_reward_skip_ratio(single-turn only) to tell identical nonzero rewards apart from all-zero rewards. To recover more groups, turn on dynamic sampling by raisingmax_sampling_attemptsabove1, at the cost of extra sampling. To adjust task difficulty, temperature, or group size, see GRPO troubleshooting.- Checkpoint creation fails with
You need to complete at least one training step before you can create an inference checkpoint: Every step was skipped. Check the log forskipped:(single-turn) orsamples=0(multi-turn). If groups had identical rewards, see the previous item. If overlong masking left too few samples, adjust the length budget. - Out of memory or timeouts: Reduce
batch_size,max_model_length, orgroup_size. For timeouts, you can instead raisetimeouton the config.
Next steps
Loss functions
Compare the built-in loss functions behind each loss variant.
Multi-turn recipes
Train tool-use agents on an environment you write.
Monitor training runs
Read the metrics a GRPO recipe logs and act on them.
Cookbook config reference
Look up every
GRPOConfig field and its default.