AIMO Progress Prize Winning Solution

repository·main·Indexed 19 days ago

https://github.com/project-numina/aimo-progress-prize

Complete training and inference pipeline to replicate the winning solution for the 1st AIMO Progress Prize. Features a two-stage fine-tuning methodology (Chain of Thought and Tool Integrated Reasoning) to transform DeepSeekMath-Base 7B into a reasoning agent. Includes the SC-TIR (Self-Consistency with Tool-Integrated Reasoning) algorithm, PythonREPL for sandboxed code execution, quantization scripts for 8-bit precision, and specialized math datasets.

Tokens
3.6K
Snippets
10
Records
13
Agent score
17%

What's inside aimo-progress-prize

  1. Project structure overview

    main

    The repository is organized as follows:

    • training/: Contains all training logic.
      • configs/: Hyperparameter configuration files for SFT and DeepSpeed.
      • numina/: Core source code.
      • sft.py: Main script for fine-tuning.
      • quantization.py: Script for model quantization.
    • kaggle-solution.ipynb: Notebook containing the Self-Consistency Tool-Integrated Reasoning (SC-TIR) inference code used for Kaggle submissions.
    • requirements.txt: List of Python dependencies.
  2. How the Numina training methodology works

    main

    The training follows the MuMath-Code recipe in two distinct stages to transform a base model into a reasoning agent:

    1. Stage 1: Chain of Thought (CoT) training: Fine-tuning on math problems and text-based solutions using the NuminaMath-CoT dataset.
    2. Stage 2: Tool Integrated Reasoning (TIR) training: Fine-tuning the Stage 1 model on math problems and code-based solutions using the NuminaMath-TIR dataset. This teaches the model to use tools (code execution) to solve problems.

    This process transforms DeepSeekMath-Base 7B into a CoT model (e.g., NuminaMath-7B-CoT), and finally into a reasoning agent (e.g., NuminaMath-7B-TIR).

  3. Install the AIMO project environment

    main

    To replicate the Numina solution, set up a Python 3.10 environment, install PyTorch v2.1.2 (specific version required for reproducibility), and install dependencies via requirements.txt. You must also install Flash Attention 2 and Git LFS.

    Note on Flash Attention 2: If your machine has less than 96GB of RAM and many CPU cores, reduce the MAX_JOBS argument during installation.

    Note on Hugging Face: You must log in via huggingface-cli login to access datasets and models.

    ```shell
    # Create and activate environment
    conda create -n aimo python=3.10 && conda activate aimo
    
    # Install dependencies (ensure PyTorch v2.1.2 is installed first)
    pip install -r requirements.txt
    
    # Install Flash Attention 2
    python -m pip install flash-attn --no-build-isolation
    
    # If low RAM/high CPU cores, use:
    # MAX_JOBS=4 pip install flash-attn --no-build-isolation
    
    # Login to Hugging Face
    huggingface-cli login
    
    # Install Git LFS
    sudo apt-get install git-lfs
    ```埋
  4. Execute Python code via PythonREPL

    main

    The PythonREPL class provides a sandboxed environment for executing generated Python code.

    Key Features:

    • Automatic Imports: Automatically prepends import math, import numpy as np, and import sympy as sp to every query.
    • Print Wrapping: Ensures the last line of the query is wrapped in a print() call if it isn't already, to capture output.
    • Timeout Protection: Uses a signal-based time_limit to prevent infinite loops or long-running code from hanging the process.
    • Error Handling: Captures tracebacks and cleans up error messages to make them usable for the model's next reasoning step.
    • Security: Explicitly blocks the use of subprocess and venv within the generated code.
    class PythonREPL:
        def __init__(self, timeout=5):
            self.timeout = timeout
    
        @contextmanager
        def time_limit(self, seconds):
            # ... implementation details ...
    
        def __call__(self, query):
            # ... executes code via subprocess.run ...
            return success, output
  5. How Self-Consistency with Tool-Integrated Reasoning (SC-TIR) works

    main

    The Numina solution uses an algorithm called SC-TIR (Self-Consistency with Tool-Integrated Reasoning) to solve math problems. It combines majority voting with Python code execution to improve reasoning accuracy.

    The SC-TIR Workflow:

    1. Prompt Expansion: For a given problem, create $M$ copies of the input to define the initial batch (the 'width' of the search).
    2. Reasoning & Code Generation: Sample completions until the model produces a complete block of Python code (using the ToRA format).
    3. Tool Execution: Execute the generated Python blocks in a REPL and append the output (including tracebacks) to the reasoning trace.
    4. Iterative Generation: Repeat the generation process $N$ times (the 'depth') to create a set of reasoning traces. Traces that fail to produce sensible outputs (e.g., incomplete code or no \boxed{} result) are pruned.
    5. Majority Voting: Post-process the valid solution candidates and apply majority voting to select the final answer.
  6. Install dependencies for Numina Solution

    main

    To run the Numina solution, you need to install several Python packages. The installation method depends on whether you are using a standard environment or a Kaggle environment.

    Standard pip installation

    pip install vllm==0.4.2
    pip install grpcio==1.62.2
    pip install antlr4-python3-runtime==4.11.0
    pip install networkx shapely sage matplotlib gmpy2 scipy numpy sympy mpmath

    Kaggle installation

    On Kaggle, you must uninstall torch first and use specific local wheels for vllm, grpcio, and ray to ensure compatibility with the T4 environment.

    pip uninstall -y torch
    pip install -U --no-index --find-links=/kaggle/input/vllm-whl -U vllm
    pip install -U --upgrade /kaggle/input/vllm-t4-fix/grpcio-1.62.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
    pip install -U --upgrade /kaggle/input/vllm-t4-fix/ray-2.11.0-cp310-cp310-manylinux2014_x86_64.whl
    pip install -U --upgrade /kaggle/input/antlr4-python3-runtime-package-4-11/antlr4_python3_runtime-4.11.0-py3-none-any.whl
    # If using pip
    # !pip install vllm==0.4.2
    # !pip install grpcio==1.62.2
    # !pip install antlr4-python3-runtime==4.11.0
    # !pip install networkx shapely sage matplotlib gmpy2 scipy numpy sympy mpmath
    
    # If on Kaggle
    # !pip uninstall -y torch
    # !pip install -U --no-index --find-links=/kaggle/input/vllm-whl -U vllm
    # !pip install -U --upgrade /kaggle/input/vllm-t4-fix/grpcio-1.62.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
    # !pip install -U --upgrade /kaggle/input/vllm-t4-fix/ray-2.11.0-cp310-cp310-manylinux2014_x86_64.whl
    # !pip install -U --upgrade /kaggle/input/antlr4-python3-runtime-package-4-11/antlr4_python3_runtime-4.11.0-py3-none-any.whl
  7. Train the model using Stage 1 (CoT)

    main

    Run Stage 1 SFT (Supervised Fine-Tuning) to train the model on Chain of Thought math problems. This uses DeepSpeed ZeRO-3 configuration.

    Requirements: 8 GPUs with 80GB VRAM are recommended for full model training.

    accelerate launch --config_file=training/configs/deepspeed_zero3.yaml training/sft.py training/configs/stage-1-cot.yaml
  8. Quantize a trained model to 8-bit precision

    main

    Use training/quantization.py with AutoGPTQ to quantize a trained model to 8-bit precision. This is useful for improving performance on hardware like Kaggle's T4 GPUs which do not support bfloat16 types.

    Note: The model may perform better in 16-bit precision if the hardware supports it.

    python training/quantization.py --model_id AI-MO/NuminaMath-7B-TIR --calibration_dataset data/NuminaMath-TIR
  9. Train the model using Stage 2 (TIR)

    main

    Run Stage 2 SFT to fine-tune the Stage 1 model on Tool Integrated Reasoning (TIR) data. This enables the model to generate code solutions.

    Requirements: 8 GPUs with 80GB VRAM are recommended for full model training.

    accelerate launch --config_file=training/configs/deepspeed_zero3.yaml training/sft.py training/configs/stage-2-tir.yaml
  10. Configure the Numina solution via Config class

    main

    The solution uses a Config dataclass to manage all hyperparameters and runtime settings.

    KeyTypeDescription
    model_idstrThe ID of the model to use (e.g., AI-MO/NuminaMath-7B-TIR-GPTQ).
    num_samplesintNumber of candidates to generate (width).
    num_generationsintNumber of steps to generate per candidate (depth).
    restart_on_failboolIf True, regenerate a step if it fails to generate Python codeblocks.
    temperaturefloatSampling temperature.
    max_new_tokensintMaximum number of tokens to generate.
    validation_setstrDataset to use (e.g., AI-MO/aimo-validation-amc).
    is_submissionboolSet to True if running in a Kaggle competition rerun environment.
    @dataclass
    class Config:
        model_id: str
    
        # Decoding Parameters
        num_samples: int        # Number of candidates to generate (width)
        num_generations: int    # Number of steps to generate per candidate (depth)
        restart_on_fail: bool   # Regenerate a step if it fails to generate Python codeblocks
    
        # Sampling Parameters
        temperature: float
        max_new_tokens: int
    
        # Runtime Parameters
        validation_set: str     # One of AI-MO/aimo-validation-amc, AI-MO/aimo-validation-aime, AI-MO/aimo-validation-math-level-4, AI-MO/aimo-validation-math-level-5
        is_submission: bool = bool(os.getenv("KAGGLE_IS_COMPETITION_RERUN"))
  11. Build a vLLM instance with quantization support

    main

    The build_vllm function initializes a vLLM engine. It automatically detects the number of available GPUs and configures quantization based on the model_id string.

    • If awq is in the model_id, it uses AWQ quantization.
    • If gptq is in the model_id, it uses gptq quantization.
    • Otherwise, it uses no quantization (standard 16-bit precision).
    def build_vllm(config):
        num_gpus = torch.cuda.device_count()
        if "awq" in config.model_id.lower():
            quantization = "AWQ"
        elif "gptq" in config.model_id.lower():
            quantization = "gptq"
        else:
            quantization = None
        vllm = LLM(
            model=config.model_id,
            tensor_parallel_size=num_gpus,
            quantization=quantization,
            swap_space=0,
        )
        return vllm
  12. Filter and vote on candidate answers

    main

    After generating multiple reasoning traces, the solution uses a two-step process to select the final answer:

    1. filter_answers(answers): Validates that answers are numeric. It rounds the value and checks if it stays within a tolerance (0.2). It then applies a modulo 1000 operation (as required by the competition format).
    2. get_majority_vote(answers): Uses collections.Counter to find the most common answer among the filtered candidates.
    def filter_answers(answers):
        def validate_answer_is_numeric(x, tolerance=0.2):
            # ... logic ...
        formatted = [validate_answer_is_numeric(a) for a in answers]
        filtered = [a % 1000 for a in formatted if a >= 0]
        return filtered
    
    def get_majority_vote(answers):
        if not len(answers):
            return 0
        c = Counter(answers)
        value, _ = c.most_common()[0]
        return value