RubenUrr09/Ollama_Training_model

0

stars

17

commits

Python

primary language

Mar 29, 2026

updated

README

Physics Tutor — SLM Fine-Tuning Workflow

A complete, production-grade pipeline for fine-tuning a small language model to be an expert introductory physics tutor. The model learns to show full chain-of-thought derivations, label every step with the governing physical principle, and verify units at each stage.


Quick Start

Venv:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

# 1. Fetch real textbook examples (OpenStax, LibreTexts, MIT OCW)
python scripts/fetch_textbook_data.py --gh-token YOUR_GITHUB_TOKEN

# 2. Generate AI parametric examples for numeric variety
python generate_physics_data.py --n 500 --seed 42

# 3. Add adversarial misconception examples
python scripts/generate_adversarial.py

# 4. Re-split with stratification (85% train / 15% val)
python scripts/stratified_split.py

# 5. Audit data quality before training
python scripts/audit_dataset.py

# 6. Smoke test — verify full pipeline in ~2 min (GPU required)
python validate_fixes.py --smoke-test

# 7. Train
python fine_tune_model.py

# 6. Evaluate on held-out benchmark
python evaluation/evaluate_model.py

# 7. Analyse failures and decide next action
python evaluation/failure_report.py

Repository Structure

├── generate_physics_data.py     # Multi-agent graph pipeline — generates training data
├── fine_tune_model.py           # QLoRA fine-tuning with early stopping
├── nodes.py                     # Graph nodes: problem generator, deriver, QC judge
├── graph_engine.py              # LangGraph-style state machine
├── validate_fixes.py            # Pre-training smoke test + config validation
│
├── scripts/
│   ├── fetch_textbook_data.py   # Fetch real examples from OpenStax / LibreTexts / MIT OCW
│   ├── audit_dataset.py         # Pre-training quality gate (8 checks)
│   ├── stratified_split.py      # Stratified train/val split by topic
│   └── generate_adversarial.py  # 10 misconception-targeting examples
│
├── evaluation/
│   ├── evaluate_model.py        # Runs 50-problem benchmark, scores responses
│   ├── score_rubric.py          # Two-dimensional scoring logic
│   └── failure_report.py        # Prioritised failure analysis + remediation plan
│
├── data/
│   └── benchmark.json           # 50 held-out problems (never used in training)
│
├── training_data.json           # Generated training examples
├── val.json                     # Generated validation examples
├── fine_tune_config.json        # Training hyperparameters
└── Modelfile                    # Ollama model definition

AWS EC2 Setup

1. SSH & Setup

ssh -i "key.pem" ubuntu@YOUR-EC2-IP
lsblk # To check the lv ephemerals you can mount
df -h | grep nvme  # See if it's already mounted

# If mounting for the first time
sudo mkdir -p /opt/dlami/nvme
sudo mount /dev/mapper/vg.01-lv_ephemeral /opt/dlami/nvme
sudo chown -R ubuntu:ubuntu /opt/dlami/nvme # Gives permissions to the ubuntu user

# REQUIRED

cd /opt/dlami/nvme
git clone https://github.com/RubenUrr09/Ollama_Training_model.git
cd Ollama_Training_model

2. Install

sudo apt update && sudo apt upgrade -y
python3 --version
sudo apt install python3.12-venv -y
python3 -m venv /opt/dlami/nvme/venv
source /opt/dlami/nvme/venv/bin/activate
pip install --upgrade pip setuptools wheel
# PyTorch (adjust CUDA version!)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu130

# Core HF stack
pip install -U transformers datasets huggingface-hub accelerate

# Training tools
pip install -U peft bitsandbytes

# Extra tools
pip install -U unsloth langgraph
sudo apt install python3.12-dev -y

3. Set Env Vars

export HF_HOME=/opt/dlami/nvme/.cache/huggingface
export TRANSFORMERS_CACHE=/opt/dlami/nvme/.cache/huggingface
export PIP_CACHE_DIR=/opt/dlami/nvme/.cache/pip

Pure AI-generated data produces models that pattern-match rather than reason. Combine all three sources for the best generalisation:

SourceShareWhy
Textbook examples (OpenStax / LibreTexts / MIT OCW)60–70%Human-authored, numerically verified, academic rigour
AI-generated parametric (generate_physics_data.py)20–30%Wide numeric variety, covers unusual values
Adversarial misconception (generate_adversarial.py)5–10%Forces reasoning, prevents pattern-matching

Textbook sources (all CC BY 4.0)

SourceContentModule count
OpenStax University Physics (GitHub CNXML)Worked examples + end-of-chapter exercises with solutions20 modules
LibreTexts Physics (HTML)OpenStax chapters + extra professor examples10 pages
MIT OCW 8.01 (HTML)Classical mechanics problem sets with solutions5 pages
# Install extra deps first
pip install requests beautifulsoup4 lxml

# Fetch textbook examples and append to training_data.json
python scripts/fetch_textbook_data.py

# Use a GitHub token to avoid the 60 req/hr rate limit
python scripts/fetch_textbook_data.py --gh-token YOUR_GITHUB_TOKEN

# Preview without writing
python scripts/fetch_textbook_data.py --dry-run

# Single source
python scripts/fetch_textbook_data.py --source openstax
python scripts/fetch_textbook_data.py --source libretexts
python scripts/fetch_textbook_data.py --source mit_ocw

Phase 1 — Data Preparation

What the pipeline generates

Every training example follows the exact same structure:

{
  "messages": [
    {
      "role": "system",
      "content": "You are an expert introductory physics tutor. Always show full derivations, label each step with the governing principle, verify units at every stage, and state assumptions explicitly..."
    },
    {
      "role": "user",
      "content": "**Problem:** A car starts from rest and accelerates at 4 m/s² for 8 s...\n**Knowns:** ...\n**Find:** ..."
    },
    {
      "role": "assistant",
      "content": "**Governing Principles:**\n  • v = v₀ + at\n  • x = v₀t + ½at²\n\n**Step 1** — ...\n**Final Answer:** v = 32 m/s, x = 128 m\n**Units Verified:** ✓"
    }
  ]
}

Topics covered (8 areas)

TopicProblem Types
1-D Kinematicsuniform acceleration, free fall, deceleration, drop
2-D Kinematics & Projectile Motionprojectile, horizontal launch
Newton's Laws of Motionapplied force, Atwood machine, friction
Work, Energy & Powerconservation of energy, spring energy, power
Momentum, Impulse & Collisionsinelastic, elastic
Circular Motion & Gravitationcircular motion, orbital mechanics
Rotational Motion & Torquerotational dynamics
Simple Harmonic Motionspring-mass, pendulum

Stratified split

The save() function in generate_physics_data.py uses stratified sampling by topic — every topic is proportionally represented in both train and val. This prevents all examples of a rare topic landing in one split.

# Re-split existing data without regenerating
python scripts/stratified_split.py --train-ratio 0.85 --dry-run

Adversarial examples

10 hand-crafted examples targeting the most common student misconceptions:

MisconceptionTopic
Speed vs velocityKinematics
Weight vs massNewton's Laws
Forgetting initial velocityKinematics
Average vs final velocityKinematics
Sign errors in decelerationKinematics
At max height, only vᵧ = 0Projectile Motion
Elastic vs inelastic KECollisions
Normal force ≠ weight on inclineNewton's Laws
Pendulum period independent of massSHM
Centripetal force is not a separate forceCircular Motion
python scripts/generate_adversarial.py --dry-run   # preview
python scripts/generate_adversarial.py             # append to training_data.json

Pre-training quality audit

Run this before every training run:

python scripts/audit_dataset.py

Checks performed:

  1. Schema validation — every example has system / user / assistant roles
  2. Topic distribution — flags any topic below 10% threshold
  3. Step count — every assistant turn must have ≥ 3 labelled steps
  4. Units in final answer — final answer must contain a unit string
  5. Answer without derivation — catches examples that state answers without steps
  6. Near-duplicate detection — same template + same params
  7. Adversarial coverage — warns if no misconception examples present
  8. Cross-split leakage — verifies no example appears in both train and val

Phase 2 — Training

Model selection by VRAM

VRAMRecommended ModelNotes
8–16 GBPhi-3-mini-4k (3.8B)Best for introductory physics
24 GBQwen2-7B or Mistral-7BGood balance
40 GB+Qwen3.5-27B (Jackrong)Only if reasoning depth justifies cost

Note: A 3.8B model fine-tuned well on domain-specific data will outperform a 40B model on introductory physics. Use the smallest model that fits your VRAM.

QLoRA configuration

r=16,           # LoRA rank
lora_alpha=32,  # scale = alpha/r = 2 (standard)
lora_dropout=0.05,
use_rslora=True,  # rank-stabilised LoRA
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                 "gate_proj", "up_proj", "down_proj"],
load_in_4bit=True,

Training hyperparameters

learning_rate=2e-4,
lr_scheduler_type="cosine",
warmup_steps=50,
num_train_epochs=3,
per_device_train_batch_size=2,
gradient_accumulation_steps=8,   # effective batch = 16
eval_steps=50,
save_steps=50,
save_total_limit=2,
load_best_model_at_end=True,     # keeps lowest val loss checkpoint

Early stopping

Training stops automatically if validation loss has not improved for 3 consecutive evaluations (150 steps). The best checkpoint (lowest val loss) is kept automatically.

EarlyStoppingCallback(early_stopping_patience=3)

What to watch during training

SignalMeaningAction
Both losses decreaseNormal trainingContinue
Train loss falls, val loss risesOverfittingStop — use last good checkpoint
Both losses plateau after epoch 1LR too low or data quality issueCheck data, try LR 1e-4
Val loss within 10–15% of train lossGood convergenceContinue to completion

# BEFORE TRAINING MODEL
python validate_fixes.py --smoke-test # with GPU: confirms full pipeline in ~2 min

python fine_tune_model.py
# Checkpoints saved to: results/checkpoint-{step}/
# Best checkpoint: results/checkpoint-{best_step}/

Phase 3 — Evaluation

Held-out benchmark

50 problems in data/benchmark.json that never appear in training or validation:

  • Spans all 8 topic areas proportionally
  • 20 multi-step problems requiring 2+ physical principles
  • Covers introductory through intermediate difficulty

Two-dimensional scoring

Every response is scored on both dimensions — a correct answer with wrong reasoning is a failure:

DimensionPass Criteria
Final AnswerCorrect numerical value AND correct units
Reasoning Chain≥ 3 labelled steps + principles named + units in derivation
python evaluation/evaluate_model.py
# Output: evaluation/results/eval_results_latest.json

Failure categories

CategoryDescription
unit_errorWrong or missing units
wrong_formulaRight principle, wrong equation
sign_errorDirection or vector component wrong
missing_stepsJumped to answer without derivation
wrong_principleFundamentally wrong physics
incompleteResponse cut off or refused

Failure analysis

python evaluation/failure_report.py

Produces:

  • Failure breakdown by category (ranked by frequency)
  • Failure rate per topic (flags topics > 30% failure rate)
  • Prioritised remediation plan with specific commands
  • Random sample of 5 failed responses for manual review
  • Go/no-go decision for next iteration

Production threshold

AccuracyDecision
≥ 85% across all topicsProduction ready
Any topic < 70%Add 50+ targeted examples for that topic
Chain-of-thought poor across the boardRevise system prompt + data format
Val loss diverged during trainingReduce LR to 1e-4 and retrain

Iteration Loop

Plan for 2–3 full iterations before the model is reliable:

Phase 1: Data
    ↓
Phase 2: Train
    ↓
Phase 3: Evaluate
    ↓
failure_report.py → fix highest-frequency failure category
    ↓
Back to Phase 1 (targeted data addition)

Iteration rule: Fix the highest-frequency failure category first. Do not add data indiscriminately — targeted, high-quality additions outperform bulk data every time.


Converting to Ollama (after training)

# Convert best checkpoint to GGUF
python llama.cpp/convert_hf_to_gguf.py results/checkpoint-{BEST}/

# Quantize
./llama.cpp/llama-quantize model.gguf model-q4_k_m.gguf Q4_K_M

# Create Ollama model
ollama create physics-tutor -f Modelfile

# Test
ollama run physics-tutor "A ball is dropped from 45 m. Find the time to hit the ground."

Key Design Decisions

Why stratified split?

A random shuffle can accidentally put all thermodynamics examples in val. Stratification guarantees every topic is proportionally represented in both splits.

Why adversarial examples?

The model learns the pattern of your training data. If every example follows the same template, it will pattern-match rather than reason. Adversarial examples force careful reasoning by presenting problems where the intuitive approach is wrong.

Why two-dimensional scoring?

A model can get the right answer with wrong reasoning by coincidence. This will fail on novel problems. Scoring both the answer AND the reasoning chain ensures the model actually learned physics, not just memorised answers.

Why early stopping?

With ~1000 examples, the model can memorise the training set in 2–3 epochs. Early stopping prevents this by stopping when val loss stops improving, keeping the checkpoint that generalises best.

Why not a 40B model?

A 40B model fine-tuned on 1000 physics problems is massive overkill for introductory-level content. A 3.8B model fine-tuned well will outperform it on this specific domain at a fraction of the inference cost and VRAM.


Dependencies

pip install unsloth transformers trl peft datasets torch
pip install huggingface_hub packaging
# For textbook data fetching
pip install requests beautifulsoup4 lxml

For Ollama conversion:

git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp && make
pip install -r requirements.txt

Citations:

@misc{jackrong_qwen35_opus_distilled, title = {Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled}, author = {Jackrong}, year = {2026}, publisher = {Hugging Face}, howpublished = {\url{https://huggingface.co/Jackrong/Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled}} }

Contributors

RubenUrr09

17 commits

RubenUrr09/Ollama_Training_model

0

stars

17

commits

Python

primary language

Mar 29, 2026

updated

README

Physics Tutor — SLM Fine-Tuning Workflow

A complete, production-grade pipeline for fine-tuning a small language model to be an expert introductory physics tutor. The model learns to show full chain-of-thought derivations, label every step with the governing physical principle, and verify units at each stage.


Quick Start

Venv:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

# 1. Fetch real textbook examples (OpenStax, LibreTexts, MIT OCW)
python scripts/fetch_textbook_data.py --gh-token YOUR_GITHUB_TOKEN

# 2. Generate AI parametric examples for numeric variety
python generate_physics_data.py --n 500 --seed 42

# 3. Add adversarial misconception examples
python scripts/generate_adversarial.py

# 4. Re-split with stratification (85% train / 15% val)
python scripts/stratified_split.py

# 5. Audit data quality before training
python scripts/audit_dataset.py

# 6. Smoke test — verify full pipeline in ~2 min (GPU required)
python validate_fixes.py --smoke-test

# 7. Train
python fine_tune_model.py

# 6. Evaluate on held-out benchmark
python evaluation/evaluate_model.py

# 7. Analyse failures and decide next action
python evaluation/failure_report.py

Repository Structure

├── generate_physics_data.py     # Multi-agent graph pipeline — generates training data
├── fine_tune_model.py           # QLoRA fine-tuning with early stopping
├── nodes.py                     # Graph nodes: problem generator, deriver, QC judge
├── graph_engine.py              # LangGraph-style state machine
├── validate_fixes.py            # Pre-training smoke test + config validation
│
├── scripts/
│   ├── fetch_textbook_data.py   # Fetch real examples from OpenStax / LibreTexts / MIT OCW
│   ├── audit_dataset.py         # Pre-training quality gate (8 checks)
│   ├── stratified_split.py      # Stratified train/val split by topic
│   └── generate_adversarial.py  # 10 misconception-targeting examples
│
├── evaluation/
│   ├── evaluate_model.py        # Runs 50-problem benchmark, scores responses
│   ├── score_rubric.py          # Two-dimensional scoring logic
│   └── failure_report.py        # Prioritised failure analysis + remediation plan
│
├── data/
│   └── benchmark.json           # 50 held-out problems (never used in training)
│
├── training_data.json           # Generated training examples
├── val.json                     # Generated validation examples
├── fine_tune_config.json        # Training hyperparameters
└── Modelfile                    # Ollama model definition

AWS EC2 Setup

1. SSH & Setup

ssh -i "key.pem" ubuntu@YOUR-EC2-IP
lsblk # To check the lv ephemerals you can mount
df -h | grep nvme  # See if it's already mounted

# If mounting for the first time
sudo mkdir -p /opt/dlami/nvme
sudo mount /dev/mapper/vg.01-lv_ephemeral /opt/dlami/nvme
sudo chown -R ubuntu:ubuntu /opt/dlami/nvme # Gives permissions to the ubuntu user

# REQUIRED

cd /opt/dlami/nvme
git clone https://github.com/RubenUrr09/Ollama_Training_model.git
cd Ollama_Training_model

2. Install

sudo apt update && sudo apt upgrade -y
python3 --version
sudo apt install python3.12-venv -y
python3 -m venv /opt/dlami/nvme/venv
source /opt/dlami/nvme/venv/bin/activate
pip install --upgrade pip setuptools wheel
# PyTorch (adjust CUDA version!)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu130

# Core HF stack
pip install -U transformers datasets huggingface-hub accelerate

# Training tools
pip install -U peft bitsandbytes

# Extra tools
pip install -U unsloth langgraph
sudo apt install python3.12-dev -y

3. Set Env Vars

export HF_HOME=/opt/dlami/nvme/.cache/huggingface
export TRANSFORMERS_CACHE=/opt/dlami/nvme/.cache/huggingface
export PIP_CACHE_DIR=/opt/dlami/nvme/.cache/pip

Pure AI-generated data produces models that pattern-match rather than reason. Combine all three sources for the best generalisation:

SourceShareWhy
Textbook examples (OpenStax / LibreTexts / MIT OCW)60–70%Human-authored, numerically verified, academic rigour
AI-generated parametric (generate_physics_data.py)20–30%Wide numeric variety, covers unusual values
Adversarial misconception (generate_adversarial.py)5–10%Forces reasoning, prevents pattern-matching

Textbook sources (all CC BY 4.0)

SourceContentModule count
OpenStax University Physics (GitHub CNXML)Worked examples + end-of-chapter exercises with solutions20 modules
LibreTexts Physics (HTML)OpenStax chapters + extra professor examples10 pages
MIT OCW 8.01 (HTML)Classical mechanics problem sets with solutions5 pages
# Install extra deps first
pip install requests beautifulsoup4 lxml

# Fetch textbook examples and append to training_data.json
python scripts/fetch_textbook_data.py

# Use a GitHub token to avoid the 60 req/hr rate limit
python scripts/fetch_textbook_data.py --gh-token YOUR_GITHUB_TOKEN

# Preview without writing
python scripts/fetch_textbook_data.py --dry-run

# Single source
python scripts/fetch_textbook_data.py --source openstax
python scripts/fetch_textbook_data.py --source libretexts
python scripts/fetch_textbook_data.py --source mit_ocw

Phase 1 — Data Preparation

What the pipeline generates

Every training example follows the exact same structure:

{
  "messages": [
    {
      "role": "system",
      "content": "You are an expert introductory physics tutor. Always show full derivations, label each step with the governing principle, verify units at every stage, and state assumptions explicitly..."
    },
    {
      "role": "user",
      "content": "**Problem:** A car starts from rest and accelerates at 4 m/s² for 8 s...\n**Knowns:** ...\n**Find:** ..."
    },
    {
      "role": "assistant",
      "content": "**Governing Principles:**\n  • v = v₀ + at\n  • x = v₀t + ½at²\n\n**Step 1** — ...\n**Final Answer:** v = 32 m/s, x = 128 m\n**Units Verified:** ✓"
    }
  ]
}

Topics covered (8 areas)

TopicProblem Types
1-D Kinematicsuniform acceleration, free fall, deceleration, drop
2-D Kinematics & Projectile Motionprojectile, horizontal launch
Newton's Laws of Motionapplied force, Atwood machine, friction
Work, Energy & Powerconservation of energy, spring energy, power
Momentum, Impulse & Collisionsinelastic, elastic
Circular Motion & Gravitationcircular motion, orbital mechanics
Rotational Motion & Torquerotational dynamics
Simple Harmonic Motionspring-mass, pendulum

Stratified split

The save() function in generate_physics_data.py uses stratified sampling by topic — every topic is proportionally represented in both train and val. This prevents all examples of a rare topic landing in one split.

# Re-split existing data without regenerating
python scripts/stratified_split.py --train-ratio 0.85 --dry-run

Adversarial examples

10 hand-crafted examples targeting the most common student misconceptions:

MisconceptionTopic
Speed vs velocityKinematics
Weight vs massNewton's Laws
Forgetting initial velocityKinematics
Average vs final velocityKinematics
Sign errors in decelerationKinematics
At max height, only vᵧ = 0Projectile Motion
Elastic vs inelastic KECollisions
Normal force ≠ weight on inclineNewton's Laws
Pendulum period independent of massSHM
Centripetal force is not a separate forceCircular Motion
python scripts/generate_adversarial.py --dry-run   # preview
python scripts/generate_adversarial.py             # append to training_data.json

Pre-training quality audit

Run this before every training run:

python scripts/audit_dataset.py

Checks performed:

  1. Schema validation — every example has system / user / assistant roles
  2. Topic distribution — flags any topic below 10% threshold
  3. Step count — every assistant turn must have ≥ 3 labelled steps
  4. Units in final answer — final answer must contain a unit string
  5. Answer without derivation — catches examples that state answers without steps
  6. Near-duplicate detection — same template + same params
  7. Adversarial coverage — warns if no misconception examples present
  8. Cross-split leakage — verifies no example appears in both train and val

Phase 2 — Training

Model selection by VRAM

VRAMRecommended ModelNotes
8–16 GBPhi-3-mini-4k (3.8B)Best for introductory physics
24 GBQwen2-7B or Mistral-7BGood balance
40 GB+Qwen3.5-27B (Jackrong)Only if reasoning depth justifies cost

Note: A 3.8B model fine-tuned well on domain-specific data will outperform a 40B model on introductory physics. Use the smallest model that fits your VRAM.

QLoRA configuration

r=16,           # LoRA rank
lora_alpha=32,  # scale = alpha/r = 2 (standard)
lora_dropout=0.05,
use_rslora=True,  # rank-stabilised LoRA
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                 "gate_proj", "up_proj", "down_proj"],
load_in_4bit=True,

Training hyperparameters

learning_rate=2e-4,
lr_scheduler_type="cosine",
warmup_steps=50,
num_train_epochs=3,
per_device_train_batch_size=2,
gradient_accumulation_steps=8,   # effective batch = 16
eval_steps=50,
save_steps=50,
save_total_limit=2,
load_best_model_at_end=True,     # keeps lowest val loss checkpoint

Early stopping

Training stops automatically if validation loss has not improved for 3 consecutive evaluations (150 steps). The best checkpoint (lowest val loss) is kept automatically.

EarlyStoppingCallback(early_stopping_patience=3)

What to watch during training

SignalMeaningAction
Both losses decreaseNormal trainingContinue
Train loss falls, val loss risesOverfittingStop — use last good checkpoint
Both losses plateau after epoch 1LR too low or data quality issueCheck data, try LR 1e-4
Val loss within 10–15% of train lossGood convergenceContinue to completion

# BEFORE TRAINING MODEL
python validate_fixes.py --smoke-test # with GPU: confirms full pipeline in ~2 min

python fine_tune_model.py
# Checkpoints saved to: results/checkpoint-{step}/
# Best checkpoint: results/checkpoint-{best_step}/

Phase 3 — Evaluation

Held-out benchmark

50 problems in data/benchmark.json that never appear in training or validation:

  • Spans all 8 topic areas proportionally
  • 20 multi-step problems requiring 2+ physical principles
  • Covers introductory through intermediate difficulty

Two-dimensional scoring

Every response is scored on both dimensions — a correct answer with wrong reasoning is a failure:

DimensionPass Criteria
Final AnswerCorrect numerical value AND correct units
Reasoning Chain≥ 3 labelled steps + principles named + units in derivation
python evaluation/evaluate_model.py
# Output: evaluation/results/eval_results_latest.json

Failure categories

CategoryDescription
unit_errorWrong or missing units
wrong_formulaRight principle, wrong equation
sign_errorDirection or vector component wrong
missing_stepsJumped to answer without derivation
wrong_principleFundamentally wrong physics
incompleteResponse cut off or refused

Failure analysis

python evaluation/failure_report.py

Produces:

  • Failure breakdown by category (ranked by frequency)
  • Failure rate per topic (flags topics > 30% failure rate)
  • Prioritised remediation plan with specific commands
  • Random sample of 5 failed responses for manual review
  • Go/no-go decision for next iteration

Production threshold

AccuracyDecision
≥ 85% across all topicsProduction ready
Any topic < 70%Add 50+ targeted examples for that topic
Chain-of-thought poor across the boardRevise system prompt + data format
Val loss diverged during trainingReduce LR to 1e-4 and retrain

Iteration Loop

Plan for 2–3 full iterations before the model is reliable:

Phase 1: Data
    ↓
Phase 2: Train
    ↓
Phase 3: Evaluate
    ↓
failure_report.py → fix highest-frequency failure category
    ↓
Back to Phase 1 (targeted data addition)

Iteration rule: Fix the highest-frequency failure category first. Do not add data indiscriminately — targeted, high-quality additions outperform bulk data every time.


Converting to Ollama (after training)

# Convert best checkpoint to GGUF
python llama.cpp/convert_hf_to_gguf.py results/checkpoint-{BEST}/

# Quantize
./llama.cpp/llama-quantize model.gguf model-q4_k_m.gguf Q4_K_M

# Create Ollama model
ollama create physics-tutor -f Modelfile

# Test
ollama run physics-tutor "A ball is dropped from 45 m. Find the time to hit the ground."

Key Design Decisions

Why stratified split?

A random shuffle can accidentally put all thermodynamics examples in val. Stratification guarantees every topic is proportionally represented in both splits.

Why adversarial examples?

The model learns the pattern of your training data. If every example follows the same template, it will pattern-match rather than reason. Adversarial examples force careful reasoning by presenting problems where the intuitive approach is wrong.

Why two-dimensional scoring?

A model can get the right answer with wrong reasoning by coincidence. This will fail on novel problems. Scoring both the answer AND the reasoning chain ensures the model actually learned physics, not just memorised answers.

Why early stopping?

With ~1000 examples, the model can memorise the training set in 2–3 epochs. Early stopping prevents this by stopping when val loss stops improving, keeping the checkpoint that generalises best.

Why not a 40B model?

A 40B model fine-tuned on 1000 physics problems is massive overkill for introductory-level content. A 3.8B model fine-tuned well will outperform it on this specific domain at a fraction of the inference cost and VRAM.


Dependencies

pip install unsloth transformers trl peft datasets torch
pip install huggingface_hub packaging
# For textbook data fetching
pip install requests beautifulsoup4 lxml

For Ollama conversion:

git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp && make
pip install -r requirements.txt

Citations:

@misc{jackrong_qwen35_opus_distilled, title = {Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled}, author = {Jackrong}, year = {2026}, publisher = {Hugging Face}, howpublished = {\url{https://huggingface.co/Jackrong/Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled}} }

Contributors

RubenUrr09

17 commits

Languages

Python

100.0%