| name | high-entropy-minority-tokens-rl |
| title | Beyond the 80/20 Rule: High-Entropy Minority Tokens Drive Effective RL for LLM Reasoning |
| version | 0.0.2 |
| engine | skillxiv-v0.0.2-claude-opus-4.6 |
| license | MIT |
| url | https://arxiv.org/abs/2506.01939 |
| keywords | ["Reinforcement Learning","Token Entropy","Selective Gradient Updates","Reasoning"] |
| description | Optimize only high-entropy tokens during RL training to achieve better reasoning performance with 80% fewer gradient updates. |
Focus RL Training on Decision Points, Not Repetition
During chain-of-thought reasoning, most tokens are predictable continuations of established patterns. A small fraction—high-entropy tokens at logical branching points—actually determine which reasoning path the model takes. Standard RL training updates all tokens equally, wasting compute on tokens that barely vary across samples. This skill teaches selective gradient updates: identify and optimize only the high-entropy tokens that drive diverse reasoning outcomes, achieving superior performance with 5x fewer parameter updates.
The insight is that token entropy reveals decision points. When entropy is high, the model genuinely considers multiple paths; when low, the token is predetermined. By concentrating learning signals on these critical decision points, you maximize the impact of each gradient update and prevent the model from overfitting to low-entropy repetitive patterns.
Core Concept
In typical token generation, entropy varies dramatically: deterministic tokens (articles, common words) have near-zero entropy, while decision-bearing tokens (logical operators, structure choices) have high entropy. Standard reinforcement learning treats all tokens equally, computing gradients for both the predictable and the consequential. Selective gradient updates invert this: compute gradients only for the minority of high-entropy tokens that actually influence which reasoning path is taken. This concentrates learning signal and reduces gradient noise from tokens that aren't truly "choosing" anything.
Architecture Overview
- Entropy Analysis Module: Computes token-level entropy across rollouts to identify decision points
- Token Filtering: Masks low-entropy tokens and focuses optimization on high-entropy subset (typically 15-25% of tokens)
- Selective PPO/REINFORCE: Standard RL algorithm but with gradients computed only for high-entropy positions
- Adaptive Threshold: Entropy threshold adjusts based on task; math reasoning needs higher threshold than code
- Verification Integration: Works with verifiable reward signals from solvers or evaluators
Implementation
This implementation demonstrates entropy-based token filtering and selective gradient updates for LLM RL training.
First, analyze entropy patterns to identify high-entropy tokens:
import torch
import torch.nn.functional as F
import numpy as np
from typing import List, Tuple
from transformers import AutoTokenizer, AutoModelForCausalLM
class TokenEntropyAnalyzer:
"""Identify high-entropy decision tokens in LLM outputs."""
def __init__(self, model_name: str = "gpt2-medium"):
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
self.model = AutoModelForCausalLM.from_pretrained(model_name)
def compute_token_entropy(self, prompt: str, generated_ids: List[int]) -> np.ndarray:
"""
Compute entropy for each token in a generation.
High entropy = LLM was uncertain; low entropy = LLM was confident.
"""
input_ids = self.tokenizer.encode(prompt, return_tensors="pt")
with torch.no_grad():
outputs = self.model(input_ids, output_hidden_states=True)
logits = outputs.logits[0]
probs = F.softmax(logits, dim=-1)
entropy = -(probs * torch.log(probs + 1e-10)).sum(dim=-1)
return entropy.cpu().numpy()
def () -> np.ndarray:
entropy = .compute_token_entropy(prompt, generated_ids)
threshold = np.percentile(entropy, percentile)
high_entropy_mask = (entropy > threshold).astype()
high_entropy_mask, entropy
() -> :
all_entropy = []
all_masks = []
prompt, gen_ids (prompts, batch_generations):
entropy = .compute_token_entropy(prompt, gen_ids)
all_entropy.append(entropy)
entropy_concat = np.concatenate(all_entropy)
{
: (entropy_concat.mean()),
: (entropy_concat.std()),
: (np.percentile(entropy_concat, )),
: (np.percentile(entropy_concat, )),
: (np.percentile(entropy_concat, )),
: ((entropy_concat > np.percentile(entropy_concat, )).mean())
}
analyzer = TokenEntropyAnalyzer()
prompt =
generated_text =
generated_ids = analyzer.tokenizer.encode(generated_text)
high_entropy_mask, entropy = analyzer.identify_high_entropy_tokens(
prompt, generated_ids, percentile=
)
()
()
Implement selective gradient updates during RL training:
import torch.optim as optim
class SelectiveGradientRLTrainer:
"""RL trainer that optimizes only high-entropy tokens."""
def __init__(self, model, analyzer: TokenEntropyAnalyzer,
learning_rate: float = 1e-5, entropy_percentile: float = 75):
self.model = model
self.analyzer = analyzer
self.optimizer = optim.AdamW(model.parameters(), lr=learning_rate)
self.entropy_percentile = entropy_percentile
def compute_masked_loss(self, logits: torch.Tensor, target_ids: torch.Tensor,
entropy_mask: torch.Tensor, rewards: torch.Tensor) -> torch.Tensor:
"""
Compute RL loss only for high-entropy positions.
Standard REINFORCE: log_prob * reward, but only for masked positions.
"""
log_probs = F.log_softmax(logits, dim=-1)
selected_log_probs = log_probs.gather(-1, target_ids.unsqueeze(-1)).squeeze(-1)
masked_log_probs = selected_log_probs * entropy_mask
loss = -(masked_log_probs * rewards.unsqueeze(-1)).mean()
return loss
def train_step(self, prompts: List[str], generated_ids_list: List[List[]],
rewards: []) -> :
batch_size = (prompts)
total_loss =
total_high_entropy =
total_tokens =
prompt, gen_ids, reward (prompts, generated_ids_list, rewards):
high_entropy_mask, entropy = .analyzer.identify_high_entropy_tokens(
prompt, gen_ids, percentile=.entropy_percentile
)
total_high_entropy += high_entropy_mask.()
total_tokens += (high_entropy_mask)
full_ids = .analyzer.tokenizer.encode(prompt + +
.analyzer.tokenizer.decode(gen_ids), return_tensors=)
outputs = .model(full_ids, output_hidden_states=)
logits = outputs.logits[, :-]
target_ids = full_ids[, :]
mask_tensor = torch.tensor(high_entropy_mask, device=logits.device)
reward_tensor = torch.tensor([reward] * (high_entropy_mask),
device=logits.device)
loss = .compute_masked_loss(logits, target_ids,
mask_tensor, reward_tensor)
total_loss += loss.item()
loss.backward()
.optimizer.step()
.optimizer.zero_grad()
{
: total_loss / batch_size,
: total_high_entropy / total_tokens,
: (total_high_entropy)
}
model = AutoModelForCausalLM.from_pretrained()
trainer = SelectiveGradientRLTrainer(model, analyzer, entropy_percentile=)
prompts = [] *
generations = [
[, , , , , ],
[, , , , , ],
[, , , , , ],
[, , , , , ],
]
rewards = [, , , ]
epoch ():
stats = trainer.train_step(prompts, generations, rewards)
(
)
Compare selective updates to baseline full-parameter updates:
class ComparisonBenchmark:
"""Compare selective vs. standard RL training."""
def __init__(self, model_name: str = "gpt2-medium"):
self.model_selective = AutoModelForCausalLM.from_pretrained(model_name)
self.model_standard = AutoModelForCausalLM.from_pretrained(model_name)
self.analyzer = TokenEntropyAnalyzer(model_name)
self.trainer_selective = SelectiveGradientRLTrainer(
self.model_selective, self.analyzer, entropy_percentile=75
)
self.trainer_standard = StandardRLTrainer(self.model_standard)
def run_comparison(self, test_prompts: List[str],
test_labels: List[int], num_epochs: int = 10):
"""Train both models and compare convergence."""
selective_losses = []
standard_losses = []
selective_accuracies = []
standard_accuracies = []
for epoch in range(num_epochs):
sel_stats = self.trainer_selective.train_step(
test_prompts,
[self.analyzer.tokenizer.encode(p) for p in test_prompts],
[float(l) for l in test_labels]
)
selective_losses.append(sel_stats[])
std_stats = .trainer_standard.train_step(
test_prompts, test_labels
)
standard_losses.append(std_stats[])
epoch % == :
sel_acc = .evaluate_accuracy(.model_selective, test_prompts)
std_acc = .evaluate_accuracy(.model_standard, test_prompts)
selective_accuracies.append(sel_acc)
standard_accuracies.append(std_acc)
{
: selective_losses,
: standard_losses,
: selective_accuracies,
: standard_accuracies
}
() -> :
correct =
prompt prompts:
output = model.generate(
.analyzer.tokenizer.encode(prompt, return_tensors=),
max_length=
)
correct +=
correct / (prompts)
benchmark = ComparisonBenchmark()
test_prompts = [] *
test_labels = [, , , , , , , ]
results = benchmark.run_comparison(test_prompts, test_labels, num_epochs=)
(
)
Practical Guidance
| Aspect | Details |
|---|
| Entropy Percentile | 75th percentile captures ~25% of tokens; adjust 70-85 based on task complexity |
| Task-Specific Tuning | Math reasoning: 75-80, Code: 70-75, Language: 85+ (fewer decision points) |
| Compute Savings | Selective updates reduce gradient compute by ~70-80% per step |
| Convergence Speed | Typically 2-4x faster convergence vs. standard RL on same dataset |
| Entropy Stability | Entropy patterns stable across model sizes and checkpoints in practice |
When to Use:
- RL on reasoning tasks with limited compute budget (fewer GPUs, tighter deadline)
- Large models where gradient computation dominates training cost
- Tasks with verifiable rewards (MATH, code, logic) enabling strong reward signals
- Need faster experimentation iteration with RL
- Scaling RL to larger batch sizes on fixed hardware
When NOT to Use:
- Tasks where all tokens contribute equally to output quality (poetry, creative writing)
- Entropy analysis doesn't align with task structure (domain-specific reasoning)
- Extremely low-entropy outputs where selective updates activate too few tokens
- Fine-grained stylistic control where repetition patterns matter
- Real-time systems with latency constraints (entropy analysis adds overhead)
Common Pitfalls:
- Entropy percentile too aggressive (>85): too few tokens updated, underfitting
- Percentile too conservative (<65): minimal compute savings, defeats purpose
- Task entropy distribution differs from analysis set: recalibrate on target domain
- Reward signal noise: entropy filtering helps but doesn't fix poor reward model
- Ignoring task-specific entropy patterns: logic and code have different decision densities
Reference
Beyond the 80/20 Rule: High-Entropy Minority Tokens Drive Effective Reinforcement Learning for LLM Reasoning
https://arxiv.org/abs/2506.01939