| name | octopus-self-correction |
| title | Learning Self-Correction in VLMs via Rollout Augmentation |
| version | 0.0.2 |
| engine | skillxiv-v0.0.2-claude-opus-4.6 |
| license | MIT |
| url | https://arxiv.org/abs/2602.08503 |
| keywords | ["Vision-Language Models","Self-Correction","RL Training","Rollout Augmentation","Dense Learning Signals"] |
| description | Enable vision-language models to self-correct by synthesizing dense training examples from existing rollouts, creating n² correction pairs from n original trajectories. |
Learning Self-Correction in VLMs via Rollout Augmentation
Problem Context
Vision-language models rarely generate effective self-corrections naturally. While effective self-correction examples are extremely sparse in standard RL training, the necessary learning signals already exist within standard RL rollouts—correct and incorrect reasoning trajectories coexist for given inputs.
Core Concept
Correction-Specific Rollout Augmentation (Octopus) is an RL framework that synthesizes dense self-correction training examples by recombining existing rollouts. Rather than waiting for the model to spontaneously generate corrections, the method pairs responses generated before and after a special correction token to create explicit learning signals.
Architecture Overview
- Format Learning (Cold-Start): Finetune on self-correction format using mixed sampling from policy and teacher models
- Rollout Augmentation: Create n² paired combinations from n original rollouts, categorizing into positive/negative samples
- Two-Stage RL: Stage I masks pre-correction response (learn from target); Stage II selectively unmasks (learn from both)
- Balanced Sample Selection: Prefer wrong→correct examples while maintaining positive/negative balance
Implementation
Phase 1: Format Learning
def format_learning(model, examples, teacher_model):
"""Cold-start: finetune on self-correction format"""
for epoch in range(num_epochs):
for example in examples:
o1_policy = model.generate(example['input'])
o1_teacher = teacher_model.generate(example['input'])
if random.random() < 0.5:
o1 = o1_policy
else:
o1 = o1_teacher
o2 = teacher_model.generate_correction(example['input'], o1)
formatted = format_as_correction(o1, o2)
logits = model.forward(formatted)
loss = cross_entropy_loss(logits, formatted)
loss.backward()
optimizer.step()
Phase 2: Rollout Augmentation
def augment_rollouts(rollouts):
"""Create n² paired combinations from n rollouts"""
augmented = []
pair_types = {
'wrong_to_correct': [],
'correct_to_correct': [],
'correct_to_wrong': [],
'wrong_to_wrong': []
}
for i, rollout_a in enumerate(rollouts):
for j, rollout_b in enumerate(rollouts):
if i == j:
continue
label_a = rollout_a['label']
label_b = rollout_b['label']
pair = {
'input': rollout_a['input'],
'o1': rollout_a['output'],
'o2': rollout_b['output'],
'label_a': label_a,
'label_b': label_b
}
if label_a == 'wrong' and label_b == 'correct':
pair_types['wrong_to_correct'].append(pair)
elif label_a == 'correct' and label_b == :
pair_types[].append(pair)
label_a == label_b == :
pair_types[].append(pair)
:
pair_types[].append(pair)
n_positive = (pair_types[])
n_negative = (pair_types[])
weak_pos = pair_types[]
weak_neg = pair_types[]
balanced_positive = (pair_types[] +
sample(weak_pos, ((weak_pos), n_positive)))
balanced_negative = (pair_types[] +
sample(weak_neg, ((weak_neg), n_negative)))
augmented = balanced_positive + balanced_negative
augmented
Phase 3: Two-Stage RL Training
def two_stage_rl(model, augmented_rollouts):
"""Stage I: Learn from target; Stage II: Learn from both"""
print("Stage I: Masking o1...")
for epoch in range(num_epochs_stage1):
for pair in augmented_rollouts:
input_text = pair['input']
o1_masked = "<masked>"
o2 = pair['output']
context = f"{input_text}\n<sc>\n{o2}"
logprob = model.log_probability(context, o2)
is_positive = (pair['label_a'] == 'wrong' and
pair['label_b'] == 'correct')
advantage = 1.0 if is_positive else -1.0
loss = -advantage * logprob
loss.backward()
optimizer.step()
print("Stage II: Unmasking o1 for non-conflicting signals...")
for epoch in range(num_epochs_stage2):
for pair in augmented_rollouts:
input_text = pair['input']
o1 = pair[]
o2 = pair[]
label_a = pair[]
label_b = pair[]
label_a == label_b:
context =
:
context =
logprob = model.log_probability(context, o2)
is_positive = (label_a == label_b == )
advantage = is_positive -
loss = -advantage * logprob
loss.backward()
optimizer.step()
Practical Guidance
When to use: Deploy for VLM tasks where self-correction is valuable (visual question answering, image captioning with refinement). Highly effective when base model generates some correct outputs alongside incorrect ones.
Format choice: Use a special token (e.g., , , ) to mark correction boundaries. Token choice affects downstream performance marginally; consistency matters more.
Pair type balancing: Prefer wrong→correct pairs (strongest signal). Maintain positive:negative ratio around 1:1 to avoid bias. Weak pairs (correct→correct, wrong→wrong) provide regularization.
Stage transition: Move from Stage I to Stage II after policy converges on strong signals. Stage I typically runs for 1–2 epochs; Stage II for 2–3 epochs. Monitor loss to detect convergence.
Data efficiency: From n = 100 rollouts, generate up to 10,000 pairs. This 100× amplification enables RL training on limited seed data.
Reference
Octopus achieves state-of-the-art self-correction performance while requiring only 0.72× training time compared to baseline methods. The key insight is that standard RL rollouts already contain sufficient signal for self-correction training if properly organized. Rollout augmentation extracts this signal without additional data collection, enabling efficient training of correction behaviors.