Vaibhav Mishra
vaibhavm209625@gmail.com
Self-evolving reasoning frameworks let LLMs improve by iteratively generating and solving problems β but they silently collapse into semantic repetition after a few iterations. Prism fixes this.
Self-evolving reasoning systems train a Questioner to generate problems and a Solver to learn from them in a closed loop. Without explicit diversity pressure, the Questioner exploits familiar problem types and the curriculum gradually collapses β even when surface-level variation is preserved.
Prism introduces two targeted interventions:
| Component | What it does |
|---|---|
| π― Semantic Coverage Reward | Maintains an embedding-based partition of mathematical problems and rewards questions from underexplored semantic regions using cross-iteration EMA memory |
| β‘ Solver-Initialised Questioner | At each iteration, re-derives the Questioner from the latest Solver β eliminating capability lag and resetting distributional bias |
Together, these components resolve the two root causes of curriculum collapse: semantic mode-seeking and capability-lag-induced narrowing.
Evaluated on 7 mathematical reasoning benchmarks against 5 self-evolving baselines (all using Qwen3-4B-Base):
| Model | GSM8K | MATH-500 | AMC | Minerva | OlyBench | AIME 2024 | AIME 2025 |
|---|---|---|---|---|---|---|---|
| Qwen3-4B-Base | 72.60 | 68.20 | 47.50 | 42.30 | 34.80 | 6.70 | 10.30 |
| R-Zero | 92.12 | 79.60 | 57.27 | 52.94 | 44.59 | 13.40 | 9.60 |
| R-Few (5%) | 92.60 | 78.00 | 52.40 | 53.20 | 42.80 | 14.50 | 9.90 |
| R-Few (1%) | 92.30 | 77.80 | 52.70 | 52.10 | 42.40 | 13.60 | 9.10 |
| Absolute Zero | 89.30 | 76.20 | 52.50 | 38.20 | 38.50 | 13.30 | 13.30 |
| SPICE | 92.70 | 78.00 | 57.50 | 51.90 | 42.70 | 12.20 | 19.10 |
| Prism (ours) | 93.45 | 81.02 | 61.25 | 56.62 | 45.58 | 16.77 | 12.92 |
Prism achieves the highest accuracy on 6 out of 7 benchmarks, with gains of +3.37 on AIME 2024, +3.68 on Minerva Math, and +3.75 on AMC over R-Zero.
We measured per-cluster question frequencies over a fixed K=128 semantic partition:
| Questioner | Active Clusters | Norm. Entropy | Gini | Top-10% Share |
|---|---|---|---|---|
| Base model | 89 / 128 | 0.71 | 0.81 | 61.2% |
| R-Zero | 65 / 128 | 0.53 | 0.90 | 79.5% |
| Prism | 107 / 128 | 0.83 | 0.66 | 42.4% |
R-Zero worsens coverage vs. the base model β a single cluster accumulates ~50Γ the mean. Prism reverses this, covering 107/128 clusters with no dominant mode.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Prism Self-Evolution Loop β
β β
β Iteration t: β
β β
β 1. Q_t β S_{t-1} (Solver-initialised Questioner) β
β β
β 2. For each GRPO step: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Generate batch {q_g} ~ Q_t β β
β β Score via vLLM: p(q) = majority-vote solvability β β
β β For q with p(q) β [0.5, 0.9]: β β
β β c(q) = nearest centroid to embed(q) β cluster assign β β
β β d(q) = exp(-n_c / nΜ) β rarity bonus β β
β β r(q) = zpd(p(q)) Β· (1 + λ·d(q)) β final reward β β
β β Update Q_t via GRPO β β
β β n_k β Ξ³Β·n_k + (1-Ξ³)Β·π[k visited] β EMA update β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β 3. Generate question pool π«_t from Q_t β
β 4. Train S_t via GRPO on π«_t with majority-vote rewards β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The reward formula:
r(q) = zpd(p(q)) Γ (1 + Ξ» Β· exp(-n_{c(q)} / nΜ))
βββ quality βββ βββββββ diversity bonus βββββββ
zpd(p) = max(0, 1 - |p - 0.75| / 0.4), p β [0.5, 0.9]
Ξ» = 5.0, Ξ³ (EMA decay) = 0.99, K = 128 clusters
Prism/
βββ rentropy_config.yaml # Central config: centroids, embedding model, Ξ», Ξ³
β
βββ cluster_space/ # Offline cluster construction (one-time)
β βββ download_corpus.py # Download MATH + GSM8K datasets
β βββ build_clusters.py # Embed β K-Means β save centroids
β βββ cluster_from_embeddings.py # Cluster from pre-computed embeddings
β βββ cluster_assigner.py # Runtime cluster assignment + EMA tracking
β
βββ question_generation_clustering/ # Question generation pipeline
β βββ balanced_cluster_generation.py # Generate questions balanced across clusters
β βββ evaluator.py # vLLM-based majority-vote scoring
β βββ create_cluster_frequency.py # Compute cluster frequency arrays
β βββ create_hf_dataset.py # Upload evaluated questions to HuggingFace
β βββ create_hf_dataset_filtered.py # Upload with score filtering (0.5β0.9)
β
βββ train/
βββ scripts/
β βββ main_rentropy.sh # π Main entry point: full pipeline
β βββ questioner_train_rentropy.sh # Questioner GRPO training
β βββ solver_train.sh # Solver GRPO training
β
βββ examples/
β βββ config.yaml # verl trainer config
β βββ reward_function/
β β βββ caller_rentropy.py # Prism diversity reward function
β β βββ math.py # Baseline math reward
β βββ format_prompt/ # Jinja2 prompt templates
β
βββ question_generate/
β βββ question_generate.py # Parallel question generation via vLLM
β βββ compute_diversity_scores.py # Add diversity scores to generated questions
β βββ curate_balanced_dataset.py # Pool + balance questions for solver training
β
βββ question_evaluate/
β βββ evaluate.py # Score questions via majority voting
β βββ upload.py # Upload final dataset to HuggingFace
β
βββ vllm_service_init/
β βββ start.sh # Launch vLLM solver servers
β
βββ evaluation/
β βββ evaluate.bash # Run all math benchmarks
β
βββ benchmark_grpo/ # GRPO fine-tuning on all benchmarks
βββ prepare_data.py
βββ config.yaml
βββ train.sh
git clone https://github.com/vaibhavmishra1/Prism.git
cd prism
pip install -r train/requirements.txt
pip install flash_attn==2.7.4.post1 --no-build-isolationKey dependencies: torch==2.7, vllm==0.9.1, transformers==4.52.4, ray==2.46.0, verl (included in train/verl/)
Set your credentials:
export HUGGINGFACENAME="your_hf_username"
export STORAGE_PATH="/path/to/storage" # defaults to train/storage/
export HF_TOKEN="your_hf_token"
export WANDB_API_KEY="your_wandb_key"# Download the MATH + GSM8K corpus
python cluster_space/download_corpus.py
# Embed questions and run K-Means to produce 128 cluster centroids
python cluster_space/build_clusters.py \
--num_clusters 128 \
--embedding_model Qwen/Qwen3-Embedding-0.6B \
--output_dir cluster_space/cluster_data/This produces cluster_space/cluster_data/centroids.npy β a fixed semantic coordinate system used as the diversity target throughout training.
cd train
export HUGGINGFACENAME="your_hf_username"
export STORAGE_PATH="/path/to/storage" # defaults to train/storage/
bash scripts/main_rentropy.sh \
Qwen/Qwen3-4B-Base \ # Questioner base model (initialised from Solver)
Qwen/Qwen3-4B-Base \ # Solver base model (used for vLLM majority voting)
qwen3-4b # Experiment name prefixInternally this:
- Downloads both models locally
- Starts vLLM solver servers (GPUs 4β6) for majority-vote scoring
- Trains the Questioner via GRPO with Prism's coverage-aware reward (GPUs 0β3, 6 steps)
- Merges the final checkpoint β
$STORAGE_PATH/models/qwen3-4b_questioner_v1/.../huggingface/
Using the trained Questioner, generate questions and balance them across the 128 semantic clusters:
cd question_generation_clustering
python balanced_cluster_generation.py \
--models $STORAGE_PATH/models/qwen3-4b_questioner_v1/.../huggingface \
--centroids_file ../cluster_space/cluster_data/centroids.npy \
--embedding_model Qwen/Qwen3-Embedding-0.6B \
--output_file balanced_questions_v1.json \
--max_per_cluster 100 \
--max_iterations 50Then evaluate question quality via majority voting:
CUDA_VISIBLE_DEVICES=0 python evaluator.py \
--input_file balanced_questions_v1.json \
--model Qwen/Qwen3-4B-Base \
--num_samples 8
# outputs β balanced_questions_v1_results.jsonUpload the scored dataset to HuggingFace:
python create_hf_dataset_filtered.py # uploads questions with score β [0.5, 0.9]
# or
python create_hf_dataset.py # uploads all scored questionsTrain the Solver on the generated question dataset (reads from the HuggingFace repo uploaded in Step 3):
cd train
QUESTIONER_CKPT="$STORAGE_PATH/models/qwen3-4b_questioner_v1/.../actor"
bash scripts/solver_train.sh \
Qwen/Qwen3-4B-Base \ # Solver base model
$QUESTIONER_CKPT \ # Questioner checkpoint (used to set dataset name)
qwen3-4b_solver_v1 # Experiment nameThis:
- Trains the Solver via GRPO on
$HUGGINGFACENAME/qwen3-4b_solver_v1@train(50 steps, 8 roll-outs) - Merges the final checkpoint
- Runs the full evaluation suite across all 7 benchmarks
Repeat Steps 2β4, initialising each new Questioner from the latest Solver checkpoint:
# Iteration 2
SOLVER_V1_CKPT="$STORAGE_PATH/models/qwen3-4b_solver_v1/.../actor"
bash scripts/questioner_train_rentropy.sh \
$SOLVER_V1_CKPT \ # β Solver becomes new Questioner init
$SOLVER_V1_CKPT \
qwen3-4b_questioner_v2
# Then repeat Steps 3 & 4 for v2 ...All Prism-specific parameters are in rentropy_config.yaml:
# Path to cluster centroids (built by cluster_space/build_clusters.py)
centroids_path: "cluster_space/cluster_data/centroids.npy"
# Embedding model for cluster assignment
embedding_model: "Qwen/Qwen3-Embedding-0.6B"
# Majority voting threshold β diversity reward applied only if p(q) >= this
majority_vote_threshold: 0.5
# Diversity weight Ξ»
lambda_weight: 5.0
# EMA decay Ξ³ for cluster count tracking
ema_decay: 0.99
# Smoothing constant Ξ± (initial cluster count value)
smoothing_alpha: 1.0
# Optional: warm-start cluster counts from a previous iteration
# Set to null for uniform initialisation
init_cluster_counts_path: nullAs a byproduct of training, we release Prism-Math β ~100K semantically diverse, difficulty-calibrated synthetic math questions generated by the Prism questioner. Unlike existing synthetic datasets, Prism-Math is explicitly optimised for semantic breadth and edge-of-solvability difficulty across all 128 semantic clusters.
π₯ Download Prism-Math on HuggingFace
If you use Prism or Prism-Math in your work, please cite:
@article{mishra2025prism,
title = {Preventing Curriculum Collapse in Self-Evolving Reasoning Systems},
author = {Mishra, Vaibhav},
journal = {arXiv preprint},
year = {2025},
url = {https://github.com/PLACEHOLDER/prism}
}Prism builds on and extends R-Zero and uses the verl framework for GRPO training. We thank the authors of R-Zero, R-Few, Absolute Zero, and SPICE for their work and open-source contributions.
