The problem
GRPO (Group Relative Policy Optimization) trains a model with reinforcement learning without a second "value" model. For each prompt it samples a group of answers, scores each with a reward (for example 1 if a math answer is correct, 0 if not), and uses each answer's reward relative to its group as the advantage of all its tokens. OLMo 3's OlmoRL keeps that idea with a few changes (report, section 4.4.1). You will implement them.
Rewards come as an array of shape : prompts, each with consecutive answers.
Change 1: advantages without dividing by the spread
The advantage of answer is its reward minus the mean of its group: . Original GRPO also divides by the group's standard deviation (plus 1e-6, to avoid dividing by 0). OlmoRL does not: dividing would overweight prompts that are almost always solved or almost never.
Example. .
- — advantages (OlmoRL): ; advantages (divided by the std):
- — advantages (OlmoRL): ; advantages (divided by the std):
- — advantages (OlmoRL): ; advantages (divided by the std):
A prompt with a small spread (almost all answers the same) gets inflated advantages when divided.
Change 2: drop groups with no signal
If all rewards in a group are identical, every advantage is 0: the group carries no gradient, and costs compute. nonzero_signal_groups marks the groups to keep: those whose rewards are not all identical.
Example. Rewards : all correct, dropped. : all wrong, dropped. : kept.
Changes 3 and 4: the objective
The per-token term is the one of PPO (Proximal Policy Optimization) (a clipped ratio), but the clip is asymmetric: is limited to with and ("clip-higher"), which lets good tokens move up a little more. With and advantage :
And the objective is the sum over all real tokens of all answers, divided by the total number of real tokens: token-level normalization.
Example. Token-level against per-answer. Two answers: the first has 1 token and the second has 3, and say the per-token terms are for the first and for each of the second.
- Token-level: . Every token counts the same.
- Per-answer (mean of each answer, then mean over answers): . The token of the short answer counts three times as much as one of the long answer: a length bias.
Clip-higher: a token with and . With the symmetric limit its term is , constant: no gradient. With the limit the term is : not clipped yet.
What to implement
group_advantages(rewards, G, normalize_std): the advantages; divide by the standard deviation plus1e-6only ifnormalize_stdis true (original GRPO). OlmoRL usesnormalize_std=False.nonzero_signal_groups(rewards, G): a boolean array of shape , true for the groups whose rewards are not all identical.olmorl_objective(logp_new, logp_old, adv, mask, eps_low=0.2, eps_high=0.272): token-level arrays of shape ( answers of up to tokens) andadvof shape , the same for every token of an answer.maskmarks the real tokens (not padding). Return the token-level objective.
Requirements
- Token-level normalization weighs every real token equally, as in the example.
- No loops.
Hint
adv[:, None] has shape and broadcasts over the tokens of each answer. For the groups, rewards.reshape(-1, G) puts one group per row.