Project Info
This project did not submit a demo video on Devpost.
Inspiration
LLMs can't learn from corrections, which is kind of absurd when you think about it. You tell it it's wrong, it apologizes, and then next conversation it makes the exact same mistake because nothing about the model actually changed. The SDFT paper (Shenfeld et al., ICLR 2025) showed that self-distillation lets you fine-tune without catastrophic forgetting, but it was all benchmark experiments on curated datasets. We wanted to see what happens when you put that in a real chat loop where a user just talks to the model and corrects it naturally.
What it does
You talk to a local model, and if you correct it, the system picks up on that, generates training data around the correction, and fine-tunes in the background while you keep chatting. The header shows a progress bar during training and flips green when it's done. Multiple chat sessions are stored in SQLite but the corrections carry across all of them because it's the model weights themselves that changed, not some per-session prompt injection.
How we built it
FastAPI backend running on a single GPU, vanilla HTML/CSS/JS frontend with a sidebar for sessions and a live training indicator. No React, no framework, nothing fancy. The training pipeline is where the work went. We adapted HuggingFace TRL's DistilTrainer to do SDFT: when a correction fires, GPT-4o-mini handles the structured parts (detecting what went wrong, generating prompt variations, writing expert demos), and then the local Qwen2.5-7B trains against a demo-conditioned copy of itself using reverse KL divergence. Teacher weights are an EMA of the student so they stay current, full fine-tuning in bfloat16, no LoRA. We serve chat during training by hooking into the trainer callback and running inference between optimizer steps, which is hacky but means the user never has to sit there waiting for training to finish.
Challenges we ran into
The model kept getting worse at unrelated tasks after corrections, which is exactly what SDFT is supposed to prevent, so we had to figure out what was going wrong with our implementation versus the paper. The biggest issue was that the KL divergence was pointing the wrong direction. Forward KL computes the loss weighted by the teacher's probabilities across the entire vocabulary at every token position, so even positions that have nothing to do with your correction get pushed around, and over multiple corrections those small shifts compound into real degradation. Reverse KL weights by the student's own probabilities instead, which means if the student and teacher already agree on a token then basically nothing happens and the update stays focused on what actually matters. There were a couple other things too: the teacher model was frozen at init instead of being an EMA that tracks the student, and the teacher prompt said "short, direct response" which made it generate in a totally different style from the student, widening the gap between their distributions in ways that had nothing to do with the actual correction.
Accomplishments we're proud of
The model genuinely accumulates corrections. You can correct it on three different topics and it retains all three while still being normal on everything else, which is the core promise of the paper and we got it working in a live interactive setting. The background training UX is also something we're happy with since gradient descent is literally running behind your conversation and the only indication is a progress bar filling up.
What we learned
The direction of KL divergence matters way more than we expected. The paper writes reverse KL in its equations but the reference code actually ships forward KL, and that works fine when you train on hundreds of diverse examples because the noise at irrelevant token positions cancels out across the dataset. With 10 examples about one fact it doesn't cancel, everything drifts, and you get the forgetting you were trying to avoid. The other thing is that on-policy learning only prevents forgetting when the teacher distribution stays close to the student's, and there are a surprising number of ways to accidentally violate that (bad prompt template, frozen teacher weights, wrong KL direction) where each one independently brings the forgetting back.
What's next
Adding a replay buffer so each training run mixes in some data from past corrections, since reverse KL handles most of the forgetting but an explicit anchor would help as you get into dozens of updates. After that we want to extend beyond factual corrections to style and reasoning, and let the model flag low-confidence answers proactively so it can ask for help before getting something wrong.
Life-Learn: SDFT Chat Correction Pipeline
A backend that demonstrates Self-Distillation Fine-Tuning (SDFT) triggered by user corrections in chat. When a user corrects the model, the system detects the correction, generates expert demonstrations, runs on-policy SDFT, and the model permanently learns from the correction.
Based on the paper "Self-Distillation Enables Continual Learning" by Shenfeld et al.
How It Works
User corrects the model in chat
-> Correction Detector (local LLM classifier)
-> Prompt Augmenter (local LLM generates diverse question variations)
-> Expert Demo Generator (GPT-4o-mini produces correct demonstrations)
-> Data Formatter (HuggingFace Dataset with prompt + teacher_prompt)
-> SDFT Trainer (on-policy distillation via DistilTrainer)
-> Verification (fresh context, analogous question -- model should get it right)
During SDFT training (handled internally by DistilTrainer):
- Student generates on-policy rollouts from the prompt alone
- Teacher (same model, EMA weights) is conditioned on prompt + expert demo
- Loss: KL divergence between student and teacher distributions
- Teacher weights track student via exponential moving average
Setup
1. Clone
git clone https://github.com/Honyant/life-learn.git
cd life-learn
2. Create conda environment
conda env create -f environment.yml
conda activate sdft
Or manually:
conda create -n sdft python=3.10
conda activate sdft
pip install -r requirements.txt
pip install pytest openai
3. Set OpenAI API key
export OPENAI_API_KEY="sk-..."
Or place it in ~/Documents/temp_dir/.env:
export OPENAI_API_KEY="sk-..."
Usage
Multi-tenant Web Workspace (new)
This repo now includes a full-stack workspace with:
- secure authentication (session cookies + CSRF)
- multi-organization tenancy with role-based access
- invite-based member management per org
- multi-chat threads per organization
- per-organization model versioning and rollback
- correction-triggered continual learning jobs (SDFT)
Run it with:
uvicorn sdft_platform.main:app --host 0.0.0.0 --port 8080 --reload
Then open http://localhost:8080.
Environment variables (optional):
| Variable | Default | Description |
|---|---|---|
LL_DATABASE_URL | sqlite:///life_learn.db | Database connection string |
LL_SECRET_KEY | dev-secret-change-me | App secret (set a strong value in production) |
LL_INFERENCE_BACKEND | mock | mock, local, or openai |
LL_BASE_MODEL_NAME | Qwen/Qwen2.5-7B-Instruct | Base model ID/path for new orgs |
LL_OPENAI_MODEL | gpt-4o-mini | OpenAI model for API-based inference/structured tasks |
LL_USE_LOCAL_STRUCTURED | false | Use local model for correction detection/augmentation |
LL_MODEL_STORAGE_DIR | sdft_correction/output/org_models | Per-org trained model storage root |
Run unit tests (no GPU needed)
python -m pytest sdft_correction/test_pipeline.py -v -k 'not gpu'
Run full integration test (GPU + API key)
python -m pytest sdft_correction/test_pipeline.py -v -m gpu -s
Interactive demo
python -m sdft_correction.chat
Chat with the model. When you correct it, the pipeline triggers automatically:
- Detects the correction
- Generates 8 prompt variations via the local model
- Gets 4 expert demos per prompt from GPT-4o-mini (~36 training pairs)
- Runs SDFT training
- Loads the trained model and verifies on an analogous question
Project Structure
.
├── distil_config.py # SDFT config (patched: added full_logit_distillation field)
├── distil_trainer.py # DistilTrainer from the Self-Distillation paper
├── main.py # Original paper's training script (reference)
├── sdft_platform/ # New multi-tenant web workspace (FastAPI + UI)
├── environment.yml # Conda environment file
├── requirements.txt # Pip dependencies
└── sdft_correction/ # Chat correction pipeline
├── config.py # PipelineConfig (model, paths, hyperparams)
├── inference.py # Local model wrapper for generation
├── correction_detector.py # LLM-based correction classifier
├── augmenter.py # Generates diverse prompt variations
├── expert_demos.py # GPT-4o-mini expert demonstration generator
├── data_formatter.py # Formats data for DistilTrainer
├── trainer.py # SDFT training wrapper (full fine-tuning)
├── chat.py # Interactive chat loop
├── conftest.py # Pytest config
└── test_pipeline.py # 22 unit tests + 1 GPU integration test
Configuration
Edit sdft_correction/config.py to change defaults:
| Parameter | Default | Description |
|---|---|---|
model_name | Qwen/Qwen2.5-0.5B-Instruct | Base model (use 7B+ for real results) |
openai_model | gpt-4o-mini | Expert demo generator |
learning_rate | 5e-5 | SDFT learning rate |
num_train_epochs | 2 | Training epochs |
gradient_accumulation_steps | 8 | Effective batch size |
num_prompt_variations | 8 | Augmented prompts per correction |
num_expert_demos_per_prompt | 4 | Expert demos per prompt |
Key Design Decisions
- On-policy student rollouts:
generate_from_teacher=False-- the student generates its own completions during training, matching the paper's Algorithm 1 - External expert demos: GPT-4o-mini provides the demonstration context
cfor the teacher, analogous to how the paper uses GPT-4o for SciKnowEval - Full fine-tuning: No LoRA, matching the paper's experimental setup. Use larger GPUs for 7B+ models.
- Forward KL (
alpha=0.0): Matches the referencemain.pyimplementation - EMA teacher (
sync_ref_model=True,ref_model_mixup_alpha=0.01): Teacher tracks student progress while smoothing updates
Original Paper
This project builds on the Self-Distillation repo by Shenfeld et al.:
Self-Distillation Enables Continual Learning (ICLR 2025) Idan Shenfeld, Mehul Damani, Jonas Hubotter, Pulkit Agrawal https://arxiv.org/abs/2601.19897
Analysis
View
Metric
- 28
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- CSSIn code
- FastAPIIn code
- HTMLIn code
- Hugging FaceIn code
- JavaScriptIn code
- OpenAIIn code
- PythonIn code
- PyTorchIn code
8 of 8 appear in the indexed code.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
289 KB
Source files
35
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
Honyant/life-learn
39 files · 22.4 MB · @ 9fa3a0a
Structure
Interface
1 file · 3%Screens, components and styles rendered to the user.
Application logic
32 files · 82%Domain rules, services and shared utilities.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- Python88%
- JavaScript5%
- CSS3%
- Markdown2%
- HTML2%
- YAML0%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
requirements.txt
pypi · 22- accelerate
- argon2-cffi
- datasets
- deepspeed
- fastapi
- flashinfer-python
- jinja2
- matplotlib
- numpy
- openai
- pandas
- peft
- rich
- scipy
- sqlalchemy
- torch
- tqdm
- transformers
- +4 more
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
This project’s features have not been analysed yet.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.