Prevent policy collapse onto common solutions during reinforcement learning by applying difficulty-aware focal weighting to gradient contributions, maintaining diversity across solution modes while preserving performance.
Scanned 9/9/2026
Install to Claude Code
npx -y skills add ADu2021/skillXiv --skill f-grpo-focal-policy-optimization --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of F Grpo Focal Policy Optimization?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/adu2021-f-grpo-focal-policy-optimization)More formats (shields.io, HTML) on the badges page.
---
name: f-grpo-focal-policy-optimization
title: "F-GRPO: Don't Let Your Policy Learn the Obvious and Forget the Rare"
version: 0.0.2
engine: skillxiv-v0.0.2-claude-opus-4.6
license: MIT
url: "https://arxiv.org/abs/2602.06717"
keywords: [Policy Optimization, Diversity, Rare Solution Mining, RL Verifiable Rewards, Focal Weighting]
description: "Prevent policy collapse onto common solutions during reinforcement learning by applying difficulty-aware focal weighting to gradient contributions, maintaining diversity across solution modes while preserving performance."
---
# F-GRPO: Don't Let Your Policy Learn the Obvious and Forget the Rare
## Problem Context
Reinforcement learning with verifiable rewards (RLVR) for language models exhibits distribution sharpening at intermediate group sizes. Policies improve pass@1 (best single solution) but degrade pass@256 (solution diversity), indicating concentration onto a narrow set of solutions. This erases rare but valuable solutions, reducing the value of the learned model for exploring multiple reasoning paths.
## Core Concept
F-GRPO applies [focal weighting, difficulty-aware scaling, group-relative advantage] to address this phenomenon. The insight is that distribution collapse peaks at intermediate group sizes due to how probability mass redistributes. A single scalar multiplier per prompt down-weights high-success cases where concentration pressure peaks, preserving rare solutions without additional compute.
## Architecture Overview
- **Theoretical foundation**: Closed-form tail-miss probability showing non-monotonic dependence on group size
- **Focal weight function**: g(x) = (1 − μ̂_pos(x))^γ scaling advantage contributions
- **Group-relative optimization**: Asymmetric pressure preserving diversity at specific group sizes
- **Hyperparameter**: Single γ (focal strength, typically 0.5-1.0) with intuitive effects
- **Integration**: Drop-in modification to GRPO, DAPO, CISPO without new networks
## Implementation
### Step 1: Estimate success probability per prompt
Compute empirical pass rate (probability of getting any correct solution) for each prompt during training.
```python
# Estimate success probability
def estimate_success_probability(
prompt_ids, group_results, smoothing_alpha=0.5
):
"""
Estimate μ_pos(x): probability of success for each prompt.
Smooth with alpha for stability on small groups.
"""
success_probs = {}
for prompt_id in set(prompt_ids):
# Get all results for this prompt
prompt_results = [
r for r, p in zip(group_results, prompt_ids)
if p == prompt_id
]
# Count correct solutions (non-zero reward)
num_correct = sum(1 for r in prompt_results if r > 0)
total = len(prompt_results)
# Estimate with smoothing
raw_prob = num_correct / max(total, 1)
smoothed_prob = (num_correct + smoothing_alpha) / (total + 2 * smoothing_alpha)
success_probs[prompt_id] = smoothed_prob
return success_probs
```
### Step 2: Compute focal weights for each prompt
Calculate difficulty-aware weights based on estimated success probability. Down-weight high-success prompts where concentration risk is highest.
```python
# Focal weighting
def compute_focal_weights(success_probs, gamma=1.0):
"""
Compute focal weights: g(x) = (1 - μ_pos(x))^gamma
Higher gamma → stronger down-weighting of easy prompts
"""
focal_weights = {}
for prompt_id, prob in success_probs.items():
# Focal weight: prioritizes hard prompts
weight = (1.0 - prob) ** gamma
focal_weights[prompt_id] = weight
return focal_weights
```
### Step 3: Apply focal weighting to advantage calculation
Modify the advantage computation in GRPO to include focal weights. This directly affects gradient magnitudes.
```python
# Modified advantage with focal weighting
def compute_focal_advantages(
log_probs, rewards, prompt_ids, focal_weights,
group_size=8
):
"""
Compute GRPO advantages with focal weighting.
F-GRPO advantage: Â^F-GRPO = g(x) * Â^GRPO
"""
# First compute standard GRPO advantages
batch_size = len(log_probs)
num_groups = batch_size // group_size
standard_advantages = []
for group_idx in range(num_groups):
group_start = group_idx * group_size
group_end = (group_idx + 1) * group_size
group_rewards = rewards[group_start:group_end]
group_probs = log_probs[group_start:group_end]
# Compute per-token advantage within group
mean_reward = group_rewards.mean()
for i in range(group_size):
advantage = group_rewards[i] - mean_reward
standard_advantages.append(advantage)
# Apply focal weights
focal_advantages = []
for adv, prompt_id in zip(standard_advantages, prompt_ids):
weight = focal_weights.get(prompt_id, 1.0)
focal_adv = weight * adv
focal_advantages.append(focal_adv)
return torch.tensor(focal_advantages)
```
### Step 4: Integrate into GRPO training step
Replace standard advantage computation with focal advantages in the main training loop.
```python
# GRPO training with focal weighting
def grpo_step_with_focal(
model, batch_prompts, group_size=8, gamma=1.0,
optimizer=None, clip_ratio=0.2
):
"""
Single GRPO training step with focal weighting.
"""
# Generate responses
responses = []
log_probs_list = []
for prompt in batch_prompts:
response, log_prob = model.generate_with_logprobs(
prompt, max_tokens=200
)
responses.append(response)
log_probs_list.append(log_prob)
# Compute rewards (assuming verifier available)
rewards = []
for response in responses:
reward = verifier(response)
rewards.append(reward)
rewards = torch.tensor(rewards)
# Estimate success probability per prompt
prompt_ids = list(range(len(batch_prompts))) # Simplification
success_probs = estimate_success_probability(
prompt_ids, rewards.tolist()
)
# Compute focal weights
focal_weights = compute_focal_weights(success_probs, gamma=gamma)
# Compute advantages with focal weighting
log_probs = torch.stack(log_probs_list)
focal_advantages = compute_focal_advantages(
log_probs, rewards, prompt_ids, focal_weights,
group_size=group_size
)
# Standard GRPO clipping objective
log_prob_ratio = log_probs - log_probs.detach()
clipped_ratio = torch.clamp(
torch.exp(log_prob_ratio), 1 - clip_ratio, 1 + clip_ratio
)
loss = -torch.min(
log_prob_ratio * focal_advantages,
clipped_ratio * focal_advantages
).mean()
if optimizer is not None:
optimizer.zero_grad()
loss.backward()
optimizer.step()
return loss.item()
```
### Step 5: Monitor diversity metrics during training
Track pass@k metrics to verify that focal weighting preserves solution diversity.
```python
# Diversity monitoring
def compute_pass_at_k_metrics(
model, test_prompts, verifier, k_values=[1, 8, 256],
num_samples_per_prompt=256
):
"""
Compute pass@k: probability of having at least one correct solution
in k samples per prompt.
"""
results = {f'pass@{k}': [] for k in k_values}
for prompt in test_prompts:
samples = []
for _ in range(num_samples_per_prompt):
response = model.generate(prompt, max_tokens=200)
is_correct = verifier(response)
samples.append(is_correct)
for k in k_values:
# Pass@k: has at least one correct in first k
has_correct = any(samples[:k])
results[f'pass@{k}'].append(float(has_correct))
# Average over prompts
final_results = {
k: sum(v) / len(v) for k, v in results.items()
}
return final_results
```
## Practical Guidance
**When to use**: Reasoning tasks (math, code, logic) where solution diversity matters for downstream use (ensemble, reranking, exploration). Less critical for single-solution tasks (generation, summarization).
**Hyperparameters**:
- **γ (focal strength)**: 0.5 (mild) to 2.0 (strong)
- γ=0.5: gentle diversity preservation
- γ=1.0: balanced (recommended baseline)
- γ>1.5: aggressive diversity, may hurt pass@1
- **Group size**: Typically 4-8; focal weighting effects stronger at 8-16
- **Smoothing alpha**: 0.5-1.0 for probability estimation stability
**Tuning guidance**:
1. Measure baseline pass@1 and pass@256 without focal weighting
2. Apply γ=1.0 and evaluate; if pass@1 drops >1%, reduce γ
3. If pass@256 still dropping, increase γ slightly
4. Use pass@256 / pass@1 ratio to track diversity relative to quality
**Common pitfalls**:
- Focal weights computed with insufficient samples; use averaging over recent batches
- Over-aggressive γ (>1.5) hurts quality; start conservative
- Forgetting to smooth success probabilities; sparse data leads to unstable weights
- Not evaluating on test set; training pass@256 may not match test
**Scaling**: Negligible computational overhead; single scalar multiplication per prompt. Effective across model sizes (1B-70B parameters) and group sizes (4-256).
## Reference
Paper: https://arxiv.org/abs/2602.06717
Code: Available at author's repository
Related work: GRPO, policy divergence, solution diversity in RL
Metrics: pass@k evaluation, diversity-quality tradeoffs
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!