前言: 隨著 Qwen、LLaMA、Mistral 等高品質開源模型的普及,越來越多工程師開始思考:「如何讓這些模型更符合我的業務需求?」本文將系統性地介紹各種 Post-Training 方法,讓你在選擇技術路線前有完整的全局觀。
什麼是 Post-Training?
Post-Training(後訓練)是指在基礎模型(Base Model)預訓練完成後,透過額外的訓練步驟,使模型具備特定能力或行為的過程。
┌─────────────────────────────────────────────────────────────────────┐
│ LLM 訓練生命週期 │
└─────────────────────────────────────────────────────────────────────┘
Pre-training Post-Training
───────────── ───────────────────────────────────────
海量語料 ┌─ SFT(監督式微調)
自回歸語言建模 → ├─ RLHF(人類回饋強化學習)
學習語言結構 ├─ DPO(直接偏好優化)
├─ ORPO(比率偏好優化)
├─ 持續預訓練(Continued Pretraining)
└─ 合併/蒸餾(Merge / Distillation)
方法一:監督式微調(SFT — Supervised Fine-Tuning)
是什麼?
SFT 是最直觀的 Post-Training 方法:準備一批「輸入 → 理想輸出」的配對資料,讓模型學習模仿這些示範。
1# SFT 資料格式範例(指令跟隨)
2{
3 "messages": [
4 {"role": "system", "content": "你是一位專業的台灣稅務顧問。"},
5 {"role": "user", "content": "個人綜合所得稅要怎麼申報?"},
6 {"role": "assistant", "content": "台灣個人綜合所得稅申報步驟如下:\n\n1. **確認申報期間**:每年 5 月 1 日至 5 月 31 日..."}
7 ]
8}
使用 Qwen + HuggingFace TRL 進行 SFT
1from transformers import AutoModelForCausalLM, AutoTokenizer
2from trl import SFTTrainer, SFTConfig
3from datasets import load_dataset
4import torch
5
6# 1. 載入模型(以 Qwen2.5-7B-Instruct 為例)
7model_name = "Qwen/Qwen2.5-7B-Instruct"
8tokenizer = AutoTokenizer.from_pretrained(model_name)
9model = AutoModelForCausalLM.from_pretrained(
10 model_name,
11 torch_dtype=torch.bfloat16,
12 device_map="auto"
13)
14
15# 2. 準備資料集
16dataset = load_dataset("json", data_files="my_sft_data.jsonl")
17
18# 3. 設定訓練參數
19config = SFTConfig(
20 output_dir="./qwen-sft-output",
21 num_train_epochs=3,
22 per_device_train_batch_size=2,
23 gradient_accumulation_steps=8,
24 learning_rate=2e-5,
25 warmup_ratio=0.1,
26 lr_scheduler_type="cosine",
27 bf16=True,
28 logging_steps=10,
29 save_steps=500,
30 max_seq_length=2048,
31)
32
33# 4. 啟動訓練
34trainer = SFTTrainer(
35 model=model,
36 train_dataset=dataset["train"],
37 args=config,
38)
39trainer.train()
搭配 LoRA 節省記憶體
全參數 SFT 對 GPU 記憶體要求極高,LoRA(Low-Rank Adaptation)是最常見的解法:
1from peft import LoraConfig, get_peft_model, TaskType
2
3lora_config = LoraConfig(
4 task_type=TaskType.CAUSAL_LM,
5 r=16, # Rank:越高能力越強,但記憶體也越多
6 lora_alpha=32, # 縮放係數
7 target_modules=[ # 對哪些層做 LoRA
8 "q_proj", "k_proj", "v_proj",
9 "o_proj", "gate_proj", "up_proj", "down_proj"
10 ],
11 lora_dropout=0.05,
12 bias="none",
13)
14
15model = get_peft_model(model, lora_config)
16model.print_trainable_parameters()
17# trainable params: 83,886,080 || all params: 7,615,832,064
18# trainable%: 1.10% ← 只需訓練 1% 的參數!
優缺點分析
| 面向 | 評估 |
|---|---|
| 優點 | 直觀易懂;資料準備門檻低;效果穩定可預期 |
| 缺點 | 需要大量高品質標記資料;容易過擬合示範風格;無法學習「哪個更好」的概念 |
| 適用場景 | 領域知識注入、風格調整、指令遵從能力強化 |
| 資料需求 | 1,000 ~ 100,000 筆高品質配對 |
| 硬體需求 | 7B 模型:LoRA 約需 16GB VRAM;全參數約需 80GB+ |
| 訓練時長 | LoRA 7B 模型:數小時(A100 × 1) |
方法二:人類回饋強化學習(RLHF)
是什麼?
RLHF(Reinforcement Learning from Human Feedback)是 ChatGPT 成功背後的核心技術。訓練流程分三階段:
RLHF 完整流程:
Step 1: SFT(監督式微調)
Base Model → SFT → SFT Model
Step 2: Reward Model 訓練
人工標記偏好資料(A vs B 哪個更好?)
→ 訓練 Reward Model(RM)
Step 3: PPO 強化學習
SFT Model + Reward Signal → PPO 優化
→ 讓模型輸出獲得更高 Reward 的回應
┌──────────┐ prompt ┌─────────────┐
│ PPO 模型 │ ──────────→ │ Response │
└──────────┘ └──────┬──────┘
↑ │ score
policy update ↓
└──────────── Reward Model ─┘
偏好資料格式範例
1# Reward Model 訓練資料
2{
3 "prompt": "解釋量子糾纏",
4 "chosen": "量子糾纏是指兩個粒子的量子態無論相距多遠都保持關聯...\n(詳細、正確的解釋)",
5 "rejected": "量子糾纏就是兩個東西連在一起。\n(簡陋、不夠準確的回答)"
6}
使用 TRL 的 PPO 訓練
1from trl import PPOTrainer, PPOConfig, AutoModelForCausalLMWithValueHead
2from transformers import pipeline
3
4# Reward function(可用現成的 reward model)
5reward_model = pipeline(
6 "text-classification",
7 model="OpenAssistant/reward-model-deberta-v3-large-v2"
8)
9
10def compute_reward(response_texts):
11 results = reward_model(response_texts)
12 return [torch.tensor(r["score"]) for r in results]
13
14# PPO 訓練設定
15ppo_config = PPOConfig(
16 model_name="Qwen/Qwen2.5-7B-Instruct",
17 learning_rate=1.41e-5,
18 batch_size=16,
19 mini_batch_size=4,
20 gradient_accumulation_steps=4,
21 optimize_cuda_cache=True,
22 early_stopping=True,
23 target_kl=0.1, # KL 散度上限,防止模型偏離太遠
24)
25
26ppo_trainer = PPOTrainer(
27 config=ppo_config,
28 model=model,
29 ref_model=ref_model, # 參考模型(SFT 模型的凍結副本)
30 tokenizer=tokenizer,
31)
優缺點分析
| 面向 | 評估 |
|---|---|
| 優點 | 效果上限最高;可學習細緻的人類偏好;OpenAI ChatGPT 的成功驗證 |
| 缺點 | 訓練複雜(三階段);需要 PPO 穩定性調校;容易出現 reward hacking;基礎設施複雜 |
| 適用場景 | 追求最高對話品質;需要嚴格安全對齊;有充足工程資源的團隊 |
| 資料需求 | 偏好資料 10,000~100,000 筆 |
| 硬體需求 | 需同時運行 SFT 模型 + RM 模型,GPU 需求 2~4x SFT |
| 訓練時長 | 7B 模型:數天(A100 × 4-8) |
方法三:直接偏好優化(DPO — Direct Preference Optimization)
是什麼?
DPO 是 2023 年提出的革命性方法——它把 RLHF 的複雜三步驟,化簡成一個監督學習目標,不需要單獨的 Reward Model,也不需要 PPO。
RLHF vs DPO:
RLHF:Base → [SFT] → [訓練RM] → [PPO] → Final Model
複雜!
DPO: Base → [SFT] → [DPO Loss] → Final Model
↑
直接用偏好資料計算 loss,跳過中間環節
DPO 的數學直覺
DPO 直接優化這個目標:增加 chosen 回應的對數機率,同時降低 rejected 回應的對數機率,並用 KL 散度限制模型不要偏離參考模型太遠。
1from trl import DPOTrainer, DPOConfig
2
3# DPO 資料格式(與 RLHF 偏好資料相同)
4dpo_dataset = [
5 {
6 "prompt": "如何提升程式碼可讀性?",
7 "chosen": "提升可讀性的方法包括:\n1. 使用有意義的變數名稱...",
8 "rejected": "寫好一點就行了。"
9 },
10 # ...更多資料
11]
12
13# DPO 訓練(比 PPO 簡單很多!)
14dpo_config = DPOConfig(
15 output_dir="./qwen-dpo-output",
16 num_train_epochs=3,
17 per_device_train_batch_size=2,
18 gradient_accumulation_steps=8,
19 learning_rate=5e-7, # DPO 通常用更小的 LR
20 beta=0.1, # KL 懲罰係數,越大越保守
21 bf16=True,
22 loss_type="sigmoid", # 原始 DPO loss
23)
24
25dpo_trainer = DPOTrainer(
26 model=model,
27 ref_model=ref_model, # 參考模型(SFT 後的凍結版)
28 args=dpo_config,
29 train_dataset=dpo_dataset,
30 tokenizer=tokenizer,
31)
32dpo_trainer.train()
DPO 變體一覽
DPO 家族:
┌─────────────────────────────────────────────────────────────────┐
│ IPO(Identity PO) - 解決 DPO 過擬合問題 │
│ KTO(Kahneman-Tversky)- 不需要成對資料,只需單筆偏好標記 │
│ ORPO - 不需要參考模型(更省記憶體) │
│ SimPO - 更穩定,移除 reference model 依賴 │
│ CPO(Contrastive PO)- 結合對比學習 │
└─────────────────────────────────────────────────────────────────┘
優缺點分析
| 面向 | 評估 |
|---|---|
| 優點 | 比 RLHF 簡單得多;不需要獨立 RM;穩定性好;效果接近 PPO |
| 缺點 | 仍需高品質偏好資料;需要參考模型(多佔一份 VRAM);偶爾不如 PPO |
| 適用場景 | 想做偏好對齊但工程資源有限;中小型團隊首選對齊方法 |
| 資料需求 | 偏好對(chosen/rejected)5,000~50,000 筆 |
| 硬體需求 | 7B + LoRA:約 24~40GB VRAM(需同時跑參考模型) |
| 訓練時長 | 7B 模型:4~12 小時(A100 × 1-2) |
方法四:ORPO(Odds Ratio Preference Optimization)
是什麼?
ORPO 是 2024 年提出的更激進簡化——它完全不需要參考模型,把 SFT 和偏好學習合在一個步驟完成。
1from trl import ORPOTrainer, ORPOConfig
2
3# ORPO 最大優勢:不需要 ref_model!
4orpo_config = ORPOConfig(
5 output_dir="./qwen-orpo-output",
6 num_train_epochs=3,
7 per_device_train_batch_size=2,
8 gradient_accumulation_steps=8,
9 learning_rate=8e-6,
10 beta=0.1, # ORPO 中的 λ(odds ratio 係數)
11 bf16=True,
12 max_length=1024,
13 max_prompt_length=512,
14)
15
16orpo_trainer = ORPOTrainer(
17 model=model, # 注意:不需要 ref_model!
18 args=orpo_config,
19 train_dataset=dataset,
20 tokenizer=tokenizer,
21)
22orpo_trainer.train()
優缺點分析
| 面向 | 評估 |
|---|---|
| 優點 | 最省記憶體的偏好訓練方法;訓練流程最簡單;效果實驗上競爭力強 |
| 缺點 | 較新,社群驗證案例相對少;部分任務不如 DPO |
| 適用場景 | GPU 資源有限;快速實驗偏好對齊 |
| 硬體需求 | 比 DPO 省 30~40% VRAM |
方法五:持續預訓練(Continued Pre-Training / Domain Adaptive Pre-Training)
是什麼?
持續預訓練是在 Base Model 上,用大量未標記的領域語料繼續以語言建模目標訓練,注入領域知識。
適用場景:
SFT → 教模型「怎麼做」(行為)
CPT → 教模型「知道什麼」(知識)
例如:
- 法律 CPT → 讓模型吸收大量判決書、法條
- 醫療 CPT → 讓模型吸收醫學文獻、病歷格式
- 金融 CPT → 讓模型吸收財報、研究報告
1from transformers import Trainer, TrainingArguments, DataCollatorForLanguageModeling
2
3# CPT 資料準備(純文本,不需要標記)
4# 例:10GB 的台灣法律文件
5def tokenize_function(examples):
6 return tokenizer(
7 examples["text"],
8 truncation=True,
9 max_length=2048,
10 return_special_tokens_mask=True
11 )
12
13# 語言建模目標(Next Token Prediction)
14data_collator = DataCollatorForLanguageModeling(
15 tokenizer=tokenizer,
16 mlm=False, # Causal LM(不是 masked LM)
17)
18
19training_args = TrainingArguments(
20 output_dir="./qwen-cpt-output",
21 num_train_epochs=1, # CPT 通常 epoch 較少
22 per_device_train_batch_size=4,
23 gradient_accumulation_steps=8,
24 learning_rate=1e-5, # 比 SFT 更小的 LR,防止遺忘
25 warmup_ratio=0.05,
26 bf16=True,
27 dataloader_num_workers=4,
28)
29
30trainer = Trainer(
31 model=model,
32 args=training_args,
33 train_dataset=tokenized_dataset,
34 data_collator=data_collator,
35)
36trainer.train()
優缺點分析
| 面向 | 評估 |
|---|---|
| 優點 | 可注入海量領域知識;無需標記資料;提升基礎語言理解能力 |
| 缺點 | 容易導致「災難性遺忘」;需要大量語料;不直接改變行為模式 |
| 適用場景 | 垂直領域知識注入(法律、醫療、金融);模型不熟悉的語言/方言強化 |
| 資料需求 | 數 GB ~ 數百 GB 未標記語料 |
| 硬體需求 | 全參數:80GB+ VRAM;LoRA 版本:16~24GB |
| 訓練時長 | 依語料量,可能需數天到數週 |
方法六:模型合併(Model Merging)
是什麼?
模型合併是一種不需要任何訓練的「後訓練」方式——直接在參數空間對多個微調模型進行合併,取長補短。
1# 使用 mergekit 進行模型合併
2# pip install mergekit
3
4# mergekit YAML 設定(SLERP 方法)
5merge_config = """
6models:
7 - model: Qwen/Qwen2.5-7B-Instruct
8 parameters:
9 weight: 0.5
10 - model: your-org/qwen-7b-coding-finetuned
11 parameters:
12 weight: 0.3
13 - model: your-org/qwen-7b-chinese-finetuned
14 parameters:
15 weight: 0.2
16
17merge_method: linear # 可選:linear, slerp, ties, dare_ties
18
19dtype: bfloat16
20"""
21
22# 執行合併
23# mergekit-yaml merge_config.yaml ./merged-model --copy-tokenizer
主流合併方法比較
┌─────────────────────────────────────────────────────────────────┐
│ 合併方法 說明 適用場景 │
├─────────────────────────────────────────────────────────────────┤
│ Linear Merge 線性加權平均 快速實驗 │
│ SLERP 球面線性插值(更平滑) 兩模型合併 │
│ TIES 解決參數衝突問題 多模型合併 │
│ DARE+TIES 隨機丟棄冗餘參數後 TIES 合併 最佳多模型合併 │
│ Task Vectors 方向性任務向量操作 能力增減控制 │
└─────────────────────────────────────────────────────────────────┘
優缺點分析
| 面向 | 評估 |
|---|---|
| 優點 | 零訓練成本;可快速實驗組合;社群有大量開源合併模型可參考 |
| 缺點 | 效果不穩定;難以預期;可能引入不一致行為;無法注入全新知識 |
| 適用場景 | 快速原型驗證;組合現有開源微調模型;資源極度有限時 |
| 硬體需求 | 只需 CPU/RAM 足以載入模型,無需 GPU |
| 時間成本 | 分鐘級(純計算,無訓練) |
方法七:知識蒸餾(Knowledge Distillation)
是什麼?
讓小模型(Student)學習大模型(Teacher)的輸出分佈,在保留部分能力的同時大幅壓縮模型大小。
知識蒸餾流程:
Teacher(大模型):Qwen2.5-72B
↓ 生成 soft labels(完整機率分佈)
Student(小模型):Qwen2.5-7B
↓ 同時學習 hard labels 和 soft labels
損失函數:
L = α × CrossEntropy(y, student_output)
+ (1-α) × KL_Divergence(teacher_soft, student_soft)
1import torch.nn.functional as F
2
3def distillation_loss(
4 student_logits,
5 teacher_logits,
6 labels,
7 temperature=4.0,
8 alpha=0.7
9):
10 # Soft loss(學習老師的分佈)
11 soft_student = F.log_softmax(student_logits / temperature, dim=-1)
12 soft_teacher = F.softmax(teacher_logits / temperature, dim=-1)
13 kl_loss = F.kl_div(soft_student, soft_teacher, reduction="batchmean")
14 soft_loss = kl_loss * (temperature ** 2)
15
16 # Hard loss(學習正確答案)
17 hard_loss = F.cross_entropy(student_logits, labels)
18
19 return alpha * hard_loss + (1 - alpha) * soft_loss
優缺點分析
| 面向 | 評估 |
|---|---|
| 優點 | 可壓縮模型大小;保留大模型能力;適合部署資源受限環境 |
| 缺點 | 需要訪問 Teacher 模型;實作複雜;效果仍不如 Teacher |
| 適用場景 | 生產環境需要小模型;邊緣設備部署 |
| 硬體需求 | 需同時運行 Teacher + Student,記憶體需求較高 |
全方法橫向比較
┌─────────────────────────────────────────────────────────────────────────────┐
│ Post-Training 方法全比較矩陣 │
├────────────────┬──────────┬──────────┬──────────┬──────────┬────────────────┤
│ 方法 │ 資料需求 │ 算力需求 │ 實作複雜度│ 效果上限 │ 最適場景 │
├────────────────┼──────────┼──────────┼──────────┼──────────┼────────────────┤
│ SFT │ 中(標記)│ 中 │ 低 │ 中高 │ 行為/風格調整 │
│ RLHF/PPO │ 高(偏好)│ 高 │ 高 │ 最高 │ 頂級對話品質 │
│ DPO │ 中(偏好)│ 中 │ 中 │ 中高 │ 對齊首選方案 │
│ ORPO │ 中(偏好)│ 低中 │ 低 │ 中高 │ 資源有限對齊 │
│ CPT │ 高(無標)│ 高 │ 中 │ - │ 領域知識注入 │
│ 模型合併 │ 無 │ 極低 │ 低 │ 中 │ 快速原型/實驗 │
│ 知識蒸餾 │ 中 │ 高 │ 高 │ 中 │ 模型壓縮部署 │
└────────────────┴──────────┴──────────┴──────────┴──────────┴────────────────┘
如何選擇適合你的方法?
你的需求是什麼?
│
├── 想讓模型學習特定領域知識(不改行為)
│ └── → 持續預訓練(CPT)
│
├── 想讓模型遵循指令、改變輸出格式
│ └── → SFT(有時 + DPO 做二階段)
│
├── 想讓模型更「有禮貌」、更安全、更符合人類偏好
│ ├── 資源充足 → RLHF/PPO
│ ├── 一般資源 → DPO
│ └── 資源有限 → ORPO
│
├── 想快速驗證組合現有開源模型
│ └── → 模型合併(Model Merging)
│
└── 想把大模型能力壓縮到小模型
└── → 知識蒸餾
實戰建議與避坑指南
1. 資料品質 » 資料數量
❌ 錯誤做法:收集 100,000 筆低品質資料
✓ 正確做法:精心標記 1,000 筆高品質資料
研究顯示:5,000 筆高品質 SFT 資料
效果 > 50,000 筆品質不一的資料
2. 從 Instruct 模型開始,不從 Base 開始
1# 推薦:從已有指令跟隨能力的模型開始
2model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
3
4# 不建議(需更多資料才能教會指令跟隨)
5# model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B")
3. 評估策略要提前設計
1# 建立評估集(與訓練集獨立)
2eval_prompts = [
3 "請解釋什麼是量子電腦?",
4 "幫我寫一個 Python 排序函數",
5 # ... 覆蓋你的主要使用場景
6]
7
8# 使用 LLM-as-Judge 評估(如 GPT-4 or Claude 打分)
9# 避免完全依賴 Perplexity 這類訓練指標
4. 常見錯誤
| 錯誤 | 後果 | 解法 |
|---|---|---|
| LR 設太高 | 模型「遺忘」原有能力 | 先小 LR 實驗再調大 |
| epoch 過多 | 過擬合、回應多樣性降低 | 早停 + 驗證集監控 |
| 資料格式不一致 | 模型學到錯誤的 chat template | 嚴格統一使用模型原始 template |
| 跳過評估只看 loss | 不知道模型實際有沒有改善 | 訓練中定期做 human eval |
| 沒有設定 reference model | DPO 訓練不穩定 | 確保 ref_model 為 SFT 後的凍結版本 |
推薦工具生態
訓練框架:
├── TRL(HuggingFace) → SFT、DPO、PPO、ORPO 一站式
├── LLaMA-Factory → 中文社群最友好的微調框架
├── Axolotl → 高度可設定,支援多種方法
└── Unsloth → 2x 速度,0.5x 記憶體,LoRA 優化
評估框架:
├── lm-evaluation-harness → 標準 benchmark 跑分
├── MT-Bench → 多輪對話品質評估
└── OpenCompass → 中文評估最完善
模型合併:
└── mergekit → 支援所有主流合併算法
資料處理:
├── Argilla → 資料標記協作平台
└── distilabel → 合成資料生成
總結
Post-Training 不是一條路,而是一個工具箱。對大多數工程師而言:
- 起點:先做 SFT,資料品質是關鍵
- 進階:加上 DPO 提升偏好對齊,ORPO 更省資源
- 知識注入:需要領域知識才考慮 CPT
- 快速驗證:模型合併是最便宜的實驗方式
- 生產壓縮:部署資源受限時考慮蒸餾
最重要的是:先明確你的目標,再選擇方法。沒有萬能的 Post-Training 方案,適合你業務場景和資源限制的,才是最好的方案。
本文所有程式碼以 HuggingFace TRL 框架為主,以 Qwen2.5 系列模型為範例,但概念同樣適用於 LLaMA、Mistral、Gemma 等其他開源模型。
