680 lines
15 KiB
Markdown
680 lines
15 KiB
Markdown
# Model Training Guide
|
||
|
||
Learn how to train MiniMind language models from scratch using pure PyTorch.
|
||
|
||
## 📊 Training Overview
|
||
|
||
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)
|
||
```
|
||
|
||
## 💰 Training Costs (Single NVIDIA 3090)
|
||
|
||
| 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
|
||
|
||
## 📋 Data Preparation
|
||
|
||
### 1. Download Datasets
|
||
|
||
Download from [ModelScope](https://www.modelscope.cn/datasets/gongjy/minimind_dataset) or [HuggingFace](https://huggingface.co/datasets/jingyaogong/minimind_dataset):
|
||
|
||
```bash
|
||
mkdir -p dataset
|
||
cd dataset
|
||
# Download required files
|
||
```
|
||
|
||
### 2. Dataset Directory Structure
|
||
|
||
```
|
||
./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)
|
||
```
|
||
|
||
### 3. Data Formats
|
||
|
||
**Pretraining Data** (`pretrain_hq.jsonl`):
|
||
```json
|
||
{"text": "How to overcome procrastination? Overcoming procrastination is not easy, but these suggestions may help..."}
|
||
```
|
||
|
||
**SFT Data** (`sft_*.jsonl`):
|
||
```json
|
||
{
|
||
"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`):
|
||
```json
|
||
{
|
||
"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`):
|
||
```json
|
||
{
|
||
"conversations": [
|
||
{"role": "user", "content": "What's the treatment for cervical spondylosis?"},
|
||
{"role": "assistant", "content": "Cervical spondylosis treatment typically includes..."}
|
||
]
|
||
}
|
||
```
|
||
|
||
## 🎯 Complete Training Pipeline
|
||
|
||
All training scripts are in the `./trainer` directory.
|
||
|
||
```bash
|
||
cd trainer
|
||
```
|
||
|
||
### Stage 1: Pretraining
|
||
|
||
**Purpose**: Learn foundational knowledge (word continuation)
|
||
|
||
```bash
|
||
# 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.py
|
||
```
|
||
|
||
**Key Parameters**:
|
||
- `max_seq_len`: 512 (adjust based on GPU memory)
|
||
- `learning_rate`: 1e-4
|
||
- `epochs`: 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
|
||
|
||
### Stage 2: Supervised Fine-Tuning (SFT)
|
||
|
||
**Purpose**: Teach conversation patterns and chat templates
|
||
|
||
```bash
|
||
# Single GPU
|
||
python train_full_sft.py
|
||
|
||
# Multi-GPU
|
||
torchrun --nproc_per_node 2 train_full_sft.py
|
||
```
|
||
|
||
**Configuration**:
|
||
- Load pretrained model from Stage 1
|
||
- Use SFT dataset (`sft_mini_512.jsonl` or `sft_512.jsonl`)
|
||
- Adjust `max_seq_len` to 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
|
||
|
||
### Stage 3: LoRA Fine-Tuning (Optional)
|
||
|
||
**Purpose**: Parameter-efficient domain adaptation
|
||
|
||
**Use Cases**:
|
||
- Medical Q&A knowledge
|
||
- Personal identity/self-awareness
|
||
- Proprietary domain knowledge
|
||
|
||
```bash
|
||
# Edit train_lora.py to set correct dataset and base model
|
||
python train_lora.py
|
||
|
||
# Multi-GPU
|
||
torchrun --nproc_per_node 2 train_lora.py
|
||
```
|
||
|
||
**Output**: `./out/lora/lora_*.pth`
|
||
|
||
**Example 1: Medical Domain**
|
||
|
||
Prepare `dataset/lora_medical.jsonl`:
|
||
```json
|
||
{
|
||
"conversations": [
|
||
{"role": "user", "content": "What's the correct pillow height for cervical spondylosis?"},
|
||
{"role": "assistant", "content": "For cervical spondylosis, pillow height should be..."}
|
||
]
|
||
}
|
||
```
|
||
|
||
Train:
|
||
```bash
|
||
# Modify train_lora.py: lora_name = 'medical'
|
||
python train_lora.py
|
||
```
|
||
|
||
**Example 2: Identity/Self-Awareness**
|
||
|
||
Prepare `dataset/lora_identity.jsonl`:
|
||
```json
|
||
{
|
||
"conversations": [
|
||
{"role": "user", "content": "Who are you?"},
|
||
{"role": "assistant", "content": "I am MiniMind..."}
|
||
]
|
||
}
|
||
```
|
||
|
||
### Stage 4: Direct Preference Optimization (DPO)
|
||
|
||
**Purpose**: Align model responses with human preferences
|
||
|
||
DPO eliminates the need for separate reward models by directly optimizing preference pairs.
|
||
|
||
```bash
|
||
python train_dpo.py
|
||
|
||
# Multi-GPU
|
||
torchrun --nproc_per_node 2 train_dpo.py
|
||
```
|
||
|
||
**Output**: `./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
|
||
|
||
### Stage 5: Reinforcement Learning from AI Feedback (RLAIF)
|
||
|
||
RLAIF is an advanced training approach using AI-generated rewards instead of human annotations. MiniMind implements three modern algorithms:
|
||
|
||
#### 5.1 PPO (Proximal Policy Optimization)
|
||
|
||
Classical on-policy RL algorithm with proven stability.
|
||
|
||
```bash
|
||
python train_ppo.py
|
||
|
||
# Multi-GPU
|
||
torchrun --nproc_per_node 2 train_ppo.py
|
||
```
|
||
|
||
**Algorithm**:
|
||
$$\mathcal{L}_{PPO} = -\mathbb{E}\left[\min(r_t \cdot A_t, \text{clip}(r_t, 1-\varepsilon, 1+\varepsilon) \cdot A_t)\right] + \beta \cdot \mathbb{E}[\text{KL}]$$
|
||
|
||
**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
|
||
|
||
#### 5.2 GRPO (Group Relative Policy Optimization)
|
||
|
||
Modern algorithm used in DeepSeek-R1, with faster convergence.
|
||
|
||
```bash
|
||
python train_grpo.py
|
||
|
||
# Multi-GPU
|
||
torchrun --nproc_per_node 2 train_grpo.py
|
||
```
|
||
|
||
**Algorithm**:
|
||
$$\mathcal{L}_{GRPO} = -\mathbb{E}\left[r_t \cdot A_t - \beta \cdot \text{KL}_t\right]$$
|
||
|
||
Where advantage is computed as:
|
||
$$A_t = \frac{R - \mu_{group}}{\sigma_{group}}$$
|
||
|
||
**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
|
||
|
||
#### 5.3 SPO (Single-stream Policy Optimization)
|
||
|
||
Newest algorithm (2025) addressing GRPO's degenerate group problem.
|
||
|
||
```bash
|
||
python train_spo.py
|
||
|
||
# Multi-GPU
|
||
torchrun --nproc_per_node 2 train_spo.py
|
||
```
|
||
|
||
**Algorithm**:
|
||
$$\mathcal{L}_{SPO} = -\mathbb{E}\left[\log \pi_\theta(a_t|s) \cdot A_t - \beta \cdot \text{KL}_t\right]$$
|
||
|
||
With adaptive baseline: $B_t^{adaptive}$
|
||
|
||
**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
|
||
|
||
#### RLAIF Dataset Preparation
|
||
|
||
All RLAIF algorithms use `rlaif-mini.jsonl` (1MB, 10k examples):
|
||
|
||
```bash
|
||
# 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**:
|
||
|
||
```bash
|
||
# 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/
|
||
```
|
||
|
||
#### RLAIF vs DPO Comparison
|
||
|
||
| 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 |
|
||
|
||
### Stage 6: Reasoning Model Distillation
|
||
|
||
**Purpose**: Distill DeepSeek-R1-style reasoning into MiniMind
|
||
|
||
```bash
|
||
python train_distill_reason.py
|
||
|
||
# Multi-GPU
|
||
torchrun --nproc_per_node 2 train_distill_reason.py
|
||
```
|
||
|
||
**Data Format** (`r1_mix_1024.jsonl`):
|
||
```json
|
||
{
|
||
"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)
|
||
|
||
## 🔧 Multi-GPU Training
|
||
|
||
### DDP (Distributed Data Parallel)
|
||
|
||
Best for single-machine multi-GPU:
|
||
|
||
```bash
|
||
torchrun --nproc_per_node N train_xxx.py
|
||
# N = number of GPUs
|
||
```
|
||
|
||
### DeepSpeed
|
||
|
||
For advanced optimization:
|
||
|
||
```bash
|
||
deepspeed --master_port 29500 --num_gpus=N train_xxx.py
|
||
```
|
||
|
||
### Wandb Monitoring
|
||
|
||
Track training progress:
|
||
|
||
```bash
|
||
# 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 available
|
||
```
|
||
|
||
## 🧪 Model Testing
|
||
|
||
### Evaluate Pretrain Model
|
||
|
||
```bash
|
||
python eval_model.py --model_mode 0
|
||
```
|
||
|
||
### Evaluate Chat Model
|
||
|
||
```bash
|
||
python eval_model.py --model_mode 1
|
||
```
|
||
|
||
### Evaluate with LoRA
|
||
|
||
```bash
|
||
python eval_model.py --lora_name 'lora_medical' --model_mode 1
|
||
```
|
||
|
||
### Evaluate Reasoning Model
|
||
|
||
```bash
|
||
python eval_model.py --model_mode 3
|
||
```
|
||
|
||
### Evaluate RLAIF Models
|
||
|
||
```bash
|
||
# PPO model
|
||
python eval_model.py --model_mode 4
|
||
|
||
# GRPO model
|
||
python eval_model.py --model_mode 4
|
||
```
|
||
|
||
### RoPE Length Extrapolation
|
||
|
||
Test with extended context:
|
||
|
||
```bash
|
||
python eval_model.py --model_mode 1 --inference_rope_scaling True
|
||
```
|
||
|
||
## 📐 Model Architecture
|
||
|
||
### MiniMind Structure
|
||
|
||
**Decoder-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
|
||
```
|
||
|
||
### Model Configurations
|
||
|
||
| 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 |
|
||
|
||
### Modify Architecture
|
||
|
||
Edit `./model/LMConfig.py`:
|
||
|
||
```python
|
||
class LMConfig:
|
||
hidden_size: int = 768
|
||
num_layers: int = 16
|
||
num_heads: int = 8
|
||
num_kv_heads: int = 2
|
||
# ... other configs
|
||
```
|
||
|
||
## 🔍 Training Tips & Best Practices
|
||
|
||
### Data Quality > Quantity
|
||
|
||
- High-quality pretraining data accelerates convergence
|
||
- `pretrain_hq.jsonl` is carefully curated for quality
|
||
- Consider data deduplication and cleaning
|
||
|
||
### Learning Rate Scheduling
|
||
|
||
```python
|
||
# Recommended schedules
|
||
- Linear warmup then decay
|
||
- Initial: 1e-4 to 5e-4
|
||
- Warmup steps: 10% of total
|
||
- Final: 10% of initial LR
|
||
```
|
||
|
||
### Batch Size & Sequence Length
|
||
|
||
```python
|
||
# 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
|
||
```
|
||
|
||
### Memory Optimization
|
||
|
||
```bash
|
||
# Reduce batch size if OOM
|
||
python train_xxx.py --batch_size 8
|
||
|
||
# Or use gradient accumulation
|
||
python train_xxx.py --gradient_accumulation_steps 4
|
||
```
|
||
|
||
### Checkpoint Management
|
||
|
||
- Saves every 100 steps by default
|
||
- Each new save overwrites the old one
|
||
- Automatic backup before training
|
||
|
||
## 🚨 Common Issues & Solutions
|
||
|
||
### Issue: CUDA Out of Memory
|
||
|
||
```bash
|
||
# 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
|
||
```
|
||
|
||
### Issue: Training Not Converging
|
||
|
||
```python
|
||
# 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
|
||
```
|
||
|
||
### Issue: Multi-GPU Sync Errors
|
||
|
||
```bash
|
||
# 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
|
||
```
|
||
|
||
### Issue: Different Results Than Expected
|
||
|
||
```python
|
||
# Check:
|
||
1. Random seed set (reproducibility)
|
||
2. Correct model checkpoint loaded
|
||
3. Correct dataset being used
|
||
4. Same hyperparameters as reference
|
||
```
|
||
|
||
## 📈 Training Progression
|
||
|
||
Typical training curves:
|
||
|
||
```
|
||
Pretraining Loss: ↘↘↘ (steep decline, then plateau)
|
||
SFT Loss: ↘ (steady decline)
|
||
PPO Reward: ↗ (rising, may plateau)
|
||
GRPO Reward: ↗↗ (faster rise, more stable)
|
||
```
|
||
|
||
## 🎓 Advanced Topics
|
||
|
||
### Custom Datasets
|
||
|
||
Create your own dataset:
|
||
|
||
```python
|
||
# Format: JSONL with conversations list
|
||
# Each line is one training example
|
||
# Ensure consistent quality and format
|
||
```
|
||
|
||
### Model Quantization (Post-training)
|
||
|
||
```bash
|
||
# 4-bit quantization for inference
|
||
# Use tools like:
|
||
# - llama.cpp (gguf format)
|
||
# - bitsandbytes (dynamic quantization)
|
||
# - AutoGPTQ (static quantization)
|
||
```
|
||
|
||
### Model Merging
|
||
|
||
```python
|
||
# Merge base model + LoRA weights
|
||
# Use tools like: peft, llama.cpp
|
||
```
|
||
|
||
## 📚 References
|
||
|
||
- [Scaling Laws](https://arxiv.org/pdf/2001.08361.pdf)
|
||
- [RoPE Position Embeddings](https://arxiv.org/abs/2104.09864)
|
||
- [YaRN Length Extrapolation](https://arxiv.org/abs/2309.00071)
|
||
- [PPO Algorithm](https://arxiv.org/abs/1707.06347)
|
||
- [GRPO (DeepSeek)](https://arxiv.org/pdf/2402.03300)
|
||
- [SPO Algorithm](https://arxiv.org/abs/2509.13232)
|
||
- [DPO](https://arxiv.org/abs/2305.18290)
|
||
|
||
---
|
||
|
||
**Next**: Deploy your trained model or explore [advanced inference options](quickstart.md#third-party-inference-frameworks)
|
||
|