Self-Distilled Reasoner: On-Policy Self-Distillation for Large Language Models

August 30, 2026 · View on GitHub


Overview

On-Policy Self-Distillation (OPSD) trains a single model to act as both student and teacher by conditioning on different contexts — the student sees only the problem, while the teacher additionally sees the ground-truth solution — and performs token-level distribution matching along the student's own on-policy trajectories.

Updates

  • Mar 18, 2026: Released updated code.

    (1) Fixed chat template and zero2 bugs (see template issue), we re-ran experiments with updated results (detailed results & ablations updated on arxiv/blog). The fixes yield improved OPSD performance, most notably on Qwen3-1.7B.

    (2) Added a new training stabilization strategy 🚀: per-token point-wise KL clipping. We find style tokens (such as 'wait', 'think') can exhibit 6–15× higher KL divergence than math-related tokens, and dominates the training signal. Clipping stablizes training and improves performance.

  • Mar 3, 2026: Initial code release.

Installation

conda env create -f environment.yml
conda activate opsd
pip install flash-attn==2.8.3 --no-build-isolation

If you encounter difficulties installing flash-attn, you can check the version matching your CUDA and PyTorch versions from the flash-attention releases page.

The code uses trl's experimental GOLD trainer as a base.

Repository Structure

├── opsd_trainer.py          # OPSDTrainer: core self-distillation trainer
├── data_collator.py         # Data collator for self-distillation
├── opsd_train.py            # OPSD training entry point
├── sft_train.py             # SFT baseline training entry point
├── grpo_train.py            # GRPO baseline training entry point
├── accelerate.yaml          # Accelerate config (multi-GPU)
├── scripts/
│   ├── run_opsd.sh          # Example launch script for OPSD
│   ├── run_sft.sh           # Example launch script for SFT
│   └── run_grpo.sh          # Example launch script for GRPO
└── eval/
    ├── evaluate_math.py     # Evaluation script (vLLM)
    └── run_eval.sh          # Example evaluation script

Quick Start

Reproduce results on Qwen3-1.7B (🚀 training only takes ~15 minutes on 4×H100 and peaks within 100 steps):

bash scripts/run_opsd_1b.sh

Evaluation: (evaluation takes ~ 30-50 minutes on 4xh100 for each checkpoint)

cd eval
bash run_eval.sh

Evaluation Results across Tasks on Qwen3-1.7B

Thinking Mode Eval:

AIME24 AIME25 HMMT25
StepAvg@12
Base51.5%
2551.4%
5052.8%
7554.4%
10057.2%
StepAvg@12
Base36.7%
2542.5%
5043.9%
7540.6%
10041.1%
StepAvg@12
Base23.1%
2524.7%
5027.8%
7526.9%
10029.2%

Evaluation settings: temperature=1.0, thinking mode enabled, max new tokens=38912, top-p=none, top-k disabled, min-p=0, presence penalty=0, num samples=12

Reproducibility note: The results above report Avg@12 using a single seed run, so some variation across runs is expected. We acknowledge that multi-seed evaluation should be adopted and more reliable. For reference, the authors of OP²SD have independently evaluated OPSD across 4 decoding seeds; their results may serve as a helpful reference.

Non-Thinking Mode

OPSD can also run in non-thinking setting where both the Qwen student and teacher are enabled_thinking=False during training (--student_thinking False --teacher_thinking False) and evaluated with non-thinking inference (--no_thinking), with faster evaluation time than thinking mode.

Training:

bash scripts/run_opsd_4b_nonthink.sh
bash scripts/run_opsd_8b_nonthink.sh

Evaluation:

cd eval
bash run_eval_nonthink.sh

Evaluation Results with Non-Thinking Mode across Models

Qwen3-8B (--jsd_token_clip 1e-7)

AIME24 AIME25 HMMT25
StepAvg@12
Base26.4%
5049.7%
7545.3%
10038.3%
StepAvg@12
Base19.7%
5035.0%
7526.9%
10027.5%
StepAvg@12
Base10.8%
5018.3%
7517.5%
10015.3%

Qwen3-4B (--jsd_token_clip 1e-6)

AIME24 AIME25 HMMT25
StepAvg@12
Base23.1%
5020.3%
7527.5%
10031.1%
15032.8%
StepAvg@12
Base21.4%
5021.4%
7520.8%
10021.1%
15021.9%
StepAvg@12
Base10.8%
5011.1%
7513.1%
10016.4%
15014.4%

Qwen3-1.7B (--jsd_token_clip 1e-6)

AIME24 AIME25 HMMT25
StepAvg@12
Base11.9%
5015.0%
7513.9%
10012.5%
StepAvg@12
Base9.2%
506.2%
758.3%
1008.1%
StepAvg@12
Base5.0%
257.2%
505.8%
755.0%

Evaluation settings: temperature=1.0, non-thinking mode, num samples=12.

Key OPSD arguments

ArgumentDefaultDescription
--fixed_teacherFalseFix the teacher to the initial policy (step 0). Requires --use_peft. Note ❗ If you disable PEFT, the teacher will keep updating at every training step, which may make training unstable. Our main results use the fixed teacher, which is currently implemented with LoRA adapter weights.
--use_tinker_lossFalseUse sampled-token policy-gradient objective instead of full-vocabulary JSD. More memory efficient. Currently no clipped implemented for this variant, could be unstable.
--max_completion_lengthStudent generation length for distillation. We use 1024 in our main experiments.
--betaInterpolation weight for the JSD mixture distribution. Beta=0 means forward KL and 1 means reverse KL.
--jsd_token_clip0.05Clip the point-wise JSD loss contributions to a maximum value before summing over vocabulary (i.e., clipping is applied point-wise, not to the vocabulary-summed loss). This can improve stability by preventing stylistic tokens from dominating the training signal. Note when clipping is applied, the loss can be negative due to positive KL summand being capped.
--reason_firstFalsePrepend an explicit rationalization to the teacher context before distillation.
--run_configNoneCustom name suffix for the output directory and WandB run.

SFT Baseline

See scripts/run_sft.sh.

GRPO Baseline

See scripts/run_grpo.sh.

Acknowledgements

Our implementation builds on TRL GOLD Trainer. We sincerely thank @simran135 and @beanie00 for identifying the prompt template bugs and the zero-2 issue, respectively!

Citation

If you find this useful, please consider citing:

@article{zhao2026self,
  title={Self-Distilled Reasoner: On-Policy Self-Distillation for Large Language Models},
  author={Zhao, Siyan and Xie, Zhihui and Liu, Mengchen and Huang, Jing and Pang, Guan and Chen, Feiyu and Grover, Aditya},
  journal={arXiv preprint arXiv:2601.18734},
  year={2026}
}