本文从 大模型应用开发技术路线清单 中拆分,聚焦“四、模型微调 Fine-tuning”部分。当 Prompt + RAG 都不够时,用自有数据训练模型。
什么时候需要微调?
微调不是万能的。在决定微调之前,先走完这条决策链:
用户需求 → 能用 Prompt 解决吗?
│
├── 能 → 结束(Prompt Engineering)
│
└── 不能 → 能用 RAG 解决吗?
│
├── 能 → 结束(RAG)
│
└── 不能 → 考虑微调
| 场景 | 是否需要微调 | 原因 |
|---|---|---|
| 通用问答、内容生成 | ❌ Prompt 就够 | 通用能力已经足够 |
| 特定领域知识问答 | ⚠️ 先试 RAG | 知识可以通过检索注入 |
| 特定风格/格式输出 | ✅ 微调效果更好 | 风格难以通过 Prompt 精确控制 |
| 领域术语理解(医疗/法律) | ✅ 微调提升显著 | 需要深层语义理解 |
| 低延迟 / 边缘部署 | ✅ 微调小模型 | 大模型太慢太贵 |
一、微调技术栈
1.1 SFT(监督微调)
SFT 是最基础的微调方式,用指令-回答对训练模型学会“听指令”。
数据格式:
// Alpaca 格式
{
"instruction": "将以下文本翻译成英文",
"input": "今天天气真好",
"output": "The weather is really nice today"
}
// ShareGPT 格式(多轮对话)
{
"conversations": [
{"from": "human", "value": "什么是 Python?"},
{"from": "gpt", "value": "Python 是一种高级编程语言..."},
{"from": "human", "value": "它有什么优点?"},
{"from": "gpt", "value": "Python 的优点包括..."}
]
}
1.2 LoRA / QLoRA
LoRA(Low-Rank Adaptation)是微调技术的核心突破——只训练少量参数,效果接近全量微调。
核心思想:原始权重矩阵 W 不动,只训练两个小矩阵 A 和 B,让 ΔW = A × B。
原始模型:70B 参数 → 全量微调需要 70B × 4 = 280GB 显存
LoRA: 只训练 0.1% 参数 → 显存需求降低到 ~30GB
QLoRA: 量化 + LoRA → 显存需求降低到 ~16GB(单卡 4090 可跑)
# 使用 PEFT 库实现 LoRA
from peft import LoraConfig, get_peft_model, TaskType
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=8, # LoRA 秩,越大效果越好但参数越多
lora_alpha=32, # 缩放因子
lora_dropout=0.1,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"], # 只对 Attention 层做 LoRA
)
model = get_peft_model(base_model, lora_config)
model.print_trainable_parameters()
# trainable params: 4,194,304 || all params: 7,000,000,000 || trainable%: 0.06%
LoRA 关键参数:
| 参数 | 含义 | 推荐值 |
|---|---|---|
| r | LoRA 秩,决定可训练参数量 | 8-64(越大越强越慢) |
| lora_alpha | 缩放因子 | 通常 = r × 2 或 r × 4 |
| target_modules | 对哪些层做 LoRA | q_proj, v_proj, k_proj, o_proj |
| lora_dropout | 防过拟合 | 0.05-0.1 |
1.3 DPO / GRPO(偏好对齐)
DPO(Direct Preference Optimization)直接用偏好数据优化模型,不需要训练 Reward Model。
# DPO 数据格式
{
"prompt": "解释量子计算",
"chosen": "量子计算利用量子比特的叠加和纠缠特性...",
"rejected": "量子计算就是更快的计算机..."
}
# 使用 TRL 做 DPO
from trl import DPOTrainer, DPOConfig
dpo_config = DPOConfig(
output_dir="./dpo-model",
per_device_train_batch_size=2,
learning_rate=5e-7,
num_train_epochs=3,
beta=0.1, # KL 散度系数
)
trainer = DPOTrainer(
model=model,
ref_model=ref_model, # 参考模型(原始模型的副本)
train_dataset=pref_dataset,
tokenizer=tokenizer,
args=dpo_config,
)
trainer.train()
RLHF vs DPO vs GRPO 对比:
| 方法 | 需要 Reward Model | 稳定性 | 复杂度 | 代表 |
|---|---|---|---|---|
| RLHF | ✅ | 中 | 高 | ChatGPT |
| DPO | ❌ | 高 | 中 | Zephyr |
| GRPO | ❌ | 高 | 中 | DeepSeek-R1 |
二、微调数据工程
数据质量 > 数据数量。Garbage In, Garbage Out。
2.1 数据收集策略
| 数据来源 | 优点 | 缺点 |
|---|---|---|
| 公开数据集 | 质量有保障,即用 | 不够垂直 |
| 业务数据 | 最贴合需求 | 需要清洗和脱敏 |
| 合成数据 | 可大规模生成 | 可能有模式偏差 |
2.2 数据清洗核心流程
原始数据 → 去重 → 去噪 → 格式标准化 → 质量过滤 → 输出
import hashlib
import json
def clean_dataset(data):
"""数据清洗流程"""
# 1. 去重(基于 instruction + output 的哈希)
seen = set()
unique_data = []
for item in data:
key = hashlib.md5(
(item["instruction"] + item["output"]).encode()
).hexdigest()
if key not in seen:
seen.add(key)
unique_data.append(item)
# 2. 过滤太短或太长的样本
filtered = [
item for item in unique_data
if 20 < len(item["output"]) < 4000
]
# 3. 过滤包含有害内容的样本
filtered = [
item for item in filtered
if not contains_harmful_content(item["output"])
]
print(f"清洗:{len(data)} → {len(unique_data)} → {len(filtered)}")
return filtered
2.3 合成数据生成
用强模型(GPT-4)生成训练数据:
def generate_synthetic_data(topic, n=100):
"""用 GPT-4 生成训练数据"""
prompt = f"""
请针对"{topic}"主题,生成 {n} 条高质量的指令-回答对。
格式(JSON 数组):
[
{{
"instruction": "具体问题或指令",
"input": "补充输入(可选)",
"output": "高质量的回答"
}}
]
要求:
1. 指令多样化(问答、总结、分析、翻译等)
2. 回答专业、准确、详细
3. 覆盖该主题的不同方面
"""
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"},
)
return json.loads(response.choices[0].message.content)
三、工具链
3.1 主流微调框架对比
| 框架 | 特点 | 适用场景 |
|---|---|---|
| Axolotl | YAML 配置驱动,上手快 | 快速实验,多种方法对比 |
| Unsloth | 2-5x 加速,显存更低 | 显存有限,追求效率 |
| LLaMA-Factory | 中文社区活跃,Web UI | 中文模型,低代码微调 |
| Hugging Face TRL | 官方库,RLHF/DPO/GRPO | 需要偏好对齐 |
| vLLM | 高性能推理 | 微调后部署 |
3.2 Axolotl 快速上手
# config.yaml
base_model: Qwen/Qwen2.5-7B
model_type: AutoModelForCausalLM
tokenizer_type: AutoTokenizer
load_in_4bit: true
adapter: qlora
lora_r: 16
lora_alpha: 32
lora_target_modules:
- q_proj
- v_proj
- k_proj
- o_proj
dataset:
- path: data/train.json
type: alpaca
sequence_len: 512
sample_packing: true
micro_batch_size: 2
gradient_accumulation_steps: 4
num_epochs: 3
learning_rate: 2e-5
lr_scheduler: cosine
warmup_steps: 100
output_dir: ./output/qwen-7b-sft
# 启动训练
accelerate launch -m axolotl.cli.train config.yaml
3.3 Unsloth 极速微调
from unsloth import FastLanguageModel
# 加载模型(4bit 量化)
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="Qwen/Qwen2.5-7B",
max_seq_length=2048,
load_in_4bit=True,
)
# 添加 LoRA
model = FastLanguageModel.get_peft_model(
model,
r=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_alpha=16,
use_gradient_checkpointing="unsloth", # 节省 30% 显存
)
# 训练(2-5x 快于标准实现)
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
max_seq_length=2048,
args=TrainingArguments(
per_device_train_batch_size=2,
num_train_epochs=3,
learning_rate=2e-4,
),
)
trainer.train()
四、模型蒸馏
用大模型(教师)指导小模型(学生)学习:
教师模型(GPT-4 / 72B)→ 生成高质量数据/软标签 → 训练学生模型(7B)
效果:7B 模型在特定任务上接近 72B 模型
| 蒸馏方法 | 说明 | 复杂度 |
|---|---|---|
| 数据蒸馏 | 用教师模型生成数据训练学生 | 低(最常用) |
| Logit 蒸馏 | 对齐教师和学生的输出概率分布 | 中 |
| 特征蒸馏 | 对齐中间层的特征表示 | 高 |
# 最简单的蒸馏:数据蒸馏
def distill_data(teacher_model, questions):
"""用教师模型生成训练数据"""
distilled_data = []
for q in questions:
answer = teacher_model.generate(q)
distilled_data.append({"instruction": q, "output": answer})
return distilled_data
# 用生成的数据训练学生模型
student_model = train_sft(student_model, distilled_data)
五、学习建议
- 如果你主要做应用层,微调可以先跳过。但至少要了解 LoRA 的原理和适用场景
- 微调的收益递减曲线很陡:数据质量 > 数据数量 > 模型大小 > 训练技巧
- 先把 Prompt + RAG 做到极致,再考虑微调
上一篇:三、Agent 智能体 下一篇:五、模型推理与部署 返回导航:大模型应用开发技术路线清单