Learn how to train MiniMind language models from scratch using pure PyTorch.
MiniMind implements a complete training pipeline:
Tokenizer Training
โ
Pretraining (Learn knowledge)
โ
SFT (Learn conversation)
โ
โโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ
โ โ โ โ
LoRA DPO/RLHF RLAIF (PPO/GRPO/SPO) Distillation
(Domain adapt) (Preference) (Reinforcement Learn) (Reasoning)
| Model | Dataset | Duration | Cost (RMB) | Quality |
|---|---|---|---|---|
| MiniMind2-Small | pretrain_hq + sft_mini_512 | 2.1h | โ3 | ๐๐ |
| MiniMind2-Small | Full dataset | 38h | โ50 | ๐๐๐๐๐๐ |
| MiniMind2 | pretrain_hq + sft_mini_512 | 3.3h | โ5 | ๐๐ |
| MiniMind2 | Full dataset | 122h | โ160 | ๐๐๐๐๐๐๐ |
!!! success "Ultra-Fast Training" Just 2.1 hours + $3 = Functional ChatBot!
Use `pretrain_hq.jsonl` + `sft_mini_512.jsonl` for fastest reproduction
Download from ModelScope or HuggingFace:
mkdir -p dataset
cd dataset
# Download required files./dataset/
โโโ pretrain_hq.jsonl โจ (1.6GB, required for pretraining)
โโโ sft_mini_512.jsonl โจ (1.2GB, fastest SFT)
โโโ sft_512.jsonl (7.5GB, standard SFT)
โโโ sft_1024.jsonl (5.6GB, longer SFT)
โโโ sft_2048.jsonl (9GB, very long SFT)
โโโ dpo.jsonl (909MB, DPO training)
โโโ r1_mix_1024.jsonl (340MB, reasoning distillation)
โโโ rlaif-mini.jsonl (1MB, RLAIF algorithms)
โโโ lora_identity.jsonl (22.8KB, identity LoRA)
โโโ lora_medical.jsonl (34MB, medical domain LoRA)
Pretraining Data (pretrain_hq.jsonl):
{"text": "How to overcome procrastination? Overcoming procrastination is not easy, but these suggestions may help..."}SFT Data (sft_*.jsonl):
{
"conversations": [
{"role": "user", "content": "Hello!"},
{"role": "assistant", "content": "Hello! How can I help?"},
{"role": "user", "content": "Tell me a joke."},
{"role": "assistant", "content": "Why did the scarecrow win an award? Because he was outstanding in his field!"}
]
}DPO Data (dpo.jsonl):
{
"chosen": [
{"role": "user", "content": "What is 2+2?"},
{"role": "assistant", "content": "2+2 equals 4."}
],
"rejected": [
{"role": "user", "content": "What is 2+2?"},
{"role": "assistant", "content": "2+2 equals 5."}
]
}LoRA Domain Data (lora_*.jsonl):
{
"conversations": [
{"role": "user", "content": "What's the treatment for cervical spondylosis?"},
{"role": "assistant", "content": "Cervical spondylosis treatment typically includes..."}
]
}All training scripts are in the ./trainer directory.
cd trainerPurpose: Learn foundational knowledge (word continuation)
# Single GPU
python train_pretrain.py
# Multi-GPU (DDP)
torchrun --nproc_per_node 2 train_pretrain.py
# Multi-GPU (DeepSpeed)
deepspeed --master_port 29500 --num_gpus=2 train_pretrain.pyKey Parameters:
max_seq_len: 512 (adjust based on GPU memory)learning_rate: 1e-4epochs: Adjust based on dataset size
Output: ./out/pretrain_*.pth
Training Duration:
- MiniMind2-Small (26M): ~1.1h
- MiniMind2 (104M): ~3.9h
!!! tip "Pretraining Tips"
- Start with pretrain_hq.jsonl for best results
- Quality > Quantity for pretraining data
- Monitor loss curve to detect overfitting
Purpose: Teach conversation patterns and chat templates
# Single GPU
python train_full_sft.py
# Multi-GPU
torchrun --nproc_per_node 2 train_full_sft.pyConfiguration:
- Load pretrained model from Stage 1
- Use SFT dataset (
sft_mini_512.jsonlorsft_512.jsonl) - Adjust
max_seq_lento match training data
Output: ./out/full_sft_*.pth
Training Duration:
- With sft_mini_512: 1-3 hours
- With full sft_512: 20-25 hours
!!! warning "SFT Data Selection"
- sft_mini_512.jsonl: Fastest, ~1.2GB, 512 tokens max
- sft_512.jsonl: Standard, ~7.5GB, 512 tokens max
- sft_1024.jsonl: Longer, ~5.6GB, 1024 tokens max
- sft_2048.jsonl: Extended, ~9GB, 2048 tokens max
Purpose: Parameter-efficient domain adaptation
Use Cases:
- Medical Q&A knowledge
- Personal identity/self-awareness
- Proprietary domain knowledge
# Edit train_lora.py to set correct dataset and base model
python train_lora.py
# Multi-GPU
torchrun --nproc_per_node 2 train_lora.pyOutput: ./out/lora/lora_*.pth
Example 1: Medical Domain
Prepare dataset/lora_medical.jsonl:
{
"conversations": [
{"role": "user", "content": "What's the correct pillow height for cervical spondylosis?"},
{"role": "assistant", "content": "For cervical spondylosis, pillow height should be..."}
]
}Train:
# Modify train_lora.py: lora_name = 'medical'
python train_lora.pyExample 2: Identity/Self-Awareness
Prepare dataset/lora_identity.jsonl:
{
"conversations": [
{"role": "user", "content": "Who are you?"},
{"role": "assistant", "content": "I am MiniMind..."}
]
}Purpose: Align model responses with human preferences
DPO eliminates the need for separate reward models by directly optimizing preference pairs.
python train_dpo.py
# Multi-GPU
torchrun --nproc_per_node 2 train_dpo.pyOutput: ./out/rlhf_*.pth
Key Features:
- Off-policy training (reuse data across epochs)
- No separate reward model needed
- Better sample efficiency than PPO
- Stable training convergence
Training Duration: ~1-3 hours
RLAIF is an advanced training approach using AI-generated rewards instead of human annotations. MiniMind implements three modern algorithms:
Classical on-policy RL algorithm with proven stability.
python train_ppo.py
# Multi-GPU
torchrun --nproc_per_node 2 train_ppo.pyAlgorithm:
Characteristics:
- Stable but slower reward improvement
- Requires both Actor and Critic networks
- High memory usage (1.5-2ร single network)
- Good for exploration
Output: ./out/ppo_actor_*.pth
Training Duration: ~1-3 hours
Modern algorithm used in DeepSeek-R1, with faster convergence.
python train_grpo.py
# Multi-GPU
torchrun --nproc_per_node 2 train_grpo.pyAlgorithm:
Where advantage is computed as:
Characteristics:
- Single-network design (memory efficient)
- Faster reward improvement
- Group normalization removes bias
- Better convergence stability
Output: ./out/grpo_*.pth
Training Duration: ~1-3 hours
Newest algorithm (2025) addressing GRPO's degenerate group problem.
python train_spo.py
# Multi-GPU
torchrun --nproc_per_node 2 train_spo.pyAlgorithm: $$\mathcal{L}{SPO} = -\mathbb{E}\left[\log \pi\theta(a_t|s) \cdot A_t - \beta \cdot \text{KL}_t\right]$$
With adaptive baseline:
Characteristics:
- No group dependency (1 input โ 1 training sample)
- Adaptive value tracking
- Better handling of difficult examples
- Experimental on small models
Output: ./out/spo_*.pth
Training Duration: ~1-3 hours
All RLAIF algorithms use rlaif-mini.jsonl (1MB, 10k examples):
# Download dataset
# Format: Same as SFT, but assistant content is "ๆ " (none)
{
"conversations": [
{"role": "user", "content": "Explain photosynthesis briefly."},
{"role": "assistant", "content": "ๆ "}
]
}The model generates completions during training, which are scored by a Reward Model (e.g., InternLM2-1.8B-Reward).
Reward Model Setup:
# Download reward model to parent directory
cd ../
git clone https://huggingface.co/internlm/internlm2-1_8b-reward
# Directory structure should be:
# project/
# โโโ minimind/
# โโโ internlm2-1_8b-reward/| Aspect | DPO | RLAIF (PPO/GRPO/SPO) |
|---|---|---|
| Training Type | Off-policy | On-policy |
| Data Freshness | Static pairs | Dynamic (generated) |
| Reward Source | Implicit | Explicit model |
| Convergence | Fast | Slower |
| Memory Usage | Lower | Higher |
| Best For | Preference refinement | Capability improvement |
Purpose: Distill DeepSeek-R1-style reasoning into MiniMind
python train_distill_reason.py
# Multi-GPU
torchrun --nproc_per_node 2 train_distill_reason.pyData Format (r1_mix_1024.jsonl):
{
"conversations": [
{
"role": "user",
"content": "Solve: 5 + 3 = ?"
},
{
"role": "assistant",
"content": "<think>\nI need to add 5 and 3.\n5 + 3 = 8\n</think>\n<answer>\n5 + 3 = 8\n</answer>"
}
]
}Output: ./out/reason_*.pth
Training Features:
- Enforces
<think>and<answer>tags - Penalty loss for format violations
- Mixed data (reasoning + multi-turn + English)
Best for single-machine multi-GPU:
torchrun --nproc_per_node N train_xxx.py
# N = number of GPUsFor advanced optimization:
deepspeed --master_port 29500 --num_gpus=N train_xxx.pyTrack training progress:
# Login first
wandb login
# Enable wandb logging
torchrun --nproc_per_node N train_xxx.py --use_wandb
# Or SwanLab (China-friendly alternative)
python train_xxx.py --use_wandb # Automatically uses SwanLab if availablepython eval_model.py --model_mode 0python eval_model.py --model_mode 1python eval_model.py --lora_name 'lora_medical' --model_mode 1python eval_model.py --model_mode 3# PPO model
python eval_model.py --model_mode 4
# GRPO model
python eval_model.py --model_mode 4Test with extended context:
python eval_model.py --model_mode 1 --inference_rope_scaling TrueDecoder-Only Transformer (similar to Llama3):
Input Tokens
โ
Token Embedding (6400 vocab)
โ
Rotary Embeddings (RoPE) [with YaRN for length extrapolation]
โ
[Transformer Blocks] รN
โโ Attention (Multi-Head)
โโ RMSNorm
โโ SwiGLU FFN [or MoE for MoE variant]
โโ Residual Connections
โ
RMSNorm
โ
LM Head (โ 6400 vocab logits)
โ
Output Probabilities
| Config | MiniMind2-Small | MiniMind2 | MiniMind2-MoE |
|---|---|---|---|
| Parameters | 26M | 104M | 145M |
| Hidden Dim | 512 | 768 | 640 |
| Layers | 8 | 16 | 8 |
| KV Heads | 2 | 2 | 2 |
| Q Heads | 8 | 8 | 8 |
| Vocab Size | 6,400 | 6,400 | 6,400 |
| Context Length | 2,048 | 2,048 | 2,048 |
Edit ./model/LMConfig.py:
class LMConfig:
hidden_size: int = 768
num_layers: int = 16
num_heads: int = 8
num_kv_heads: int = 2
# ... other configs- High-quality pretraining data accelerates convergence
pretrain_hq.jsonlis carefully curated for quality- Consider data deduplication and cleaning
# Recommended schedules
- Linear warmup then decay
- Initial: 1e-4 to 5e-4
- Warmup steps: 10% of total
- Final: 10% of initial LR# Balance between GPU memory and convergence
- Pretraining: max_seq_len=512, batch_size=32
- SFT: max_seq_len=512, batch_size=16
- LoRA: max_seq_len=512, batch_size=16# Reduce batch size if OOM
python train_xxx.py --batch_size 8
# Or use gradient accumulation
python train_xxx.py --gradient_accumulation_steps 4- Saves every 100 steps by default
- Each new save overwrites the old one
- Automatic backup before training
# Solution 1: Reduce batch size
python train_xxx.py --batch_size 4
# Solution 2: Use gradient accumulation
python train_xxx.py --batch_size 16 --gradient_accumulation_steps 2
# Solution 3: Use smaller model
# Edit trainer script to use MiniMind2-Small instead# Possible causes:
1. Learning rate too high/low
2. Data quality issues
3. Model capacity mismatch
# Solutions:
- Reduce learning rate: --learning_rate 1e-5
- Check data format and quality
- Try smaller model first# Ensure:
1. All GPUs visible: nvidia-smi
2. Same CUDA version across all GPUs
3. Network connectivity for distributed training
# Debug:
torchrun --nproc_per_node 2 train_xxx.py --debug# Check:
1. Random seed set (reproducibility)
2. Correct model checkpoint loaded
3. Correct dataset being used
4. Same hyperparameters as referenceTypical training curves:
Pretraining Loss: โโโ (steep decline, then plateau)
SFT Loss: โ (steady decline)
PPO Reward: โ (rising, may plateau)
GRPO Reward: โโ (faster rise, more stable)
Create your own dataset:
# Format: JSONL with conversations list
# Each line is one training example
# Ensure consistent quality and format# 4-bit quantization for inference
# Use tools like:
# - llama.cpp (gguf format)
# - bitsandbytes (dynamic quantization)
# - AutoGPTQ (static quantization)# Merge base model + LoRA weights
# Use tools like: peft, llama.cpp- Scaling Laws
- RoPE Position Embeddings
- YaRN Length Extrapolation
- PPO Algorithm
- GRPO (DeepSeek)
- SPO Algorithm
- DPO
Next: Deploy your trained model or explore advanced inference options