为什么需要微调
提示工程和 RAG 是"用好现成模型",微调则是"改造模型本身"——什么时候该走这一步?
微调决策框架
graph TD
START[需要 LLM 能力] --> Q1{Prompt 能解决吗?}
Q1 -->|是| PE[提示工程]
Q1 -->|不够| Q2{需要外部知识?}
Q2 -->|是| RAG[RAG 检索增强]
Q2 -->|不是| Q3{需要什么?}
Q3 -->|特定风格/格式| FT[微调]
Q3 -->|领域专业知识| Q4{数据量够吗?}
Q3 -->|降低延迟/成本| FT
Q4 -->|少于100条| PE
Q4 -->|100-10000条| LORA[LoRA 微调]
Q4 -->|万条以上| FULL[全量微调]
PE --> DEPLOY[部署使用]
RAG --> DEPLOY
FT --> DEPLOY
LORA --> DEPLOY
FULL --> DEPLOY
style FT fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style LORA fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
style FULL fill:#fff3e0,stroke:#f57c00,stroke-width:2px
微调 vs 提示工程 vs RAG
"""
三种方案的对比与选择
"""
from dataclasses import dataclass
@dataclass
class ApproachComparison:
"""三种方案对比"""
COMPARISON = {
"维度": [
"核心思路",
"训练成本",
"推理成本",
"响应延迟",
"知识更新",
"数据需求",
"技术门槛",
"适用场景",
],
"提示工程": [
"设计好的指令引导模型",
"零",
"较高(长 Prompt)",
"较高(长上下文)",
"即时更新",
"少量示例即可",
"低",
"通用任务、快速原型",
],
"RAG": [
"检索外部知识注入上下文",
"索引构建成本",
"中等(检索+生成)",
"中等(多步骤)",
"更新索引即可",
"需要知识库文档",
"中",
"知识密集型问答",
],
"微调": [
"修改模型参数适配任务",
"GPU 训练成本",
"最低(短 Prompt)",
"最低",
"需要重新训练",
"高质量标注数据",
"高",
"特定风格/格式/领域",
],
}
# 打印对比
comp = ApproachComparison()
print("=== 方案对比 ===")
for i, dim in enumerate(comp.COMPARISON["维度"]):
print(f"\n{dim}:")
print(f" 提示工程: {comp.COMPARISON['提示工程'][i]}")
print(f" RAG: {comp.COMPARISON['RAG'][i]}")
print(f" 微调: {comp.COMPARISON['微调'][i]}")
微调的典型场景
"""
什么时候应该微调?
"""
FINETUNE_SCENARIOS = {
"✅ 应该微调": {
"统一输出格式": {
"例子": "所有回答必须用特定 JSON Schema",
"原因": "Prompt 难以 100% 保证格式一致",
},
"品牌语气风格": {
"例子": "回答必须像特定品牌的客服语气",
"原因": "风格学习需要模型内化",
},
"领域术语理解": {
"例子": "医疗/法律/金融的专业术语",
"原因": "通用模型对冷门术语理解差",
},
"降低推理成本": {
"例子": "用小模型替代 GPT-4 处理特定任务",
"原因": "微调后的 7B 模型可达 GPT-4 特定任务水平",
},
"降低延迟": {
"例子": "实时对话系统需要快速响应",
"原因": "微调后 Prompt 更短,响应更快",
},
},
"❌ 不应该微调": {
"需要最新知识": "用 RAG 而非微调",
"通用对话能力": "直接用 GPT-4o / Claude",
"数据量不足": "少于 50 条高质量数据时效果差",
"快速迭代需求": "微调周期长,Prompt 调整更灵活",
"隐私无法脱敏": "训练数据会被模型记住",
},
}
print("=== 微调决策指南 ===")
for category, items in FINETUNE_SCENARIOS.items():
print(f"\n{category}:")
if isinstance(items, dict):
for name, detail in items.items():
if isinstance(detail, dict):
print(f" {name}: {detail['例子']}")
else:
print(f" {name}: {detail}")
成本与收益分析
"""
微调成本估算工具
"""
class FinetuneCostEstimator:
"""微调成本估算"""
# OpenAI 微调定价 (2024)
OPENAI_PRICING = {
"gpt-4o-mini-2024-07-18": {
"training": 3.00, # $/M tokens
"input": 0.30, # $/M tokens
"output": 1.20, # $/M tokens
},
"gpt-4o-2024-08-06": {
"training": 25.00,
"input": 3.75,
"output": 15.00,
},
}
# 开源模型微调成本 (单卡 A100 80GB)
OPEN_SOURCE_COST = {
"Llama-3-8B + LoRA": {
"GPU": "1x A100 80GB",
"时间": "2-4 小时",
"云GPU成本": "$4-8",
"数据量": "1000-5000 条",
},
"Llama-3-70B + QLoRA": {
"GPU": "1x A100 80GB",
"时间": "8-16 小时",
"云GPU成本": "$16-32",
"数据量": "1000-5000 条",
},
"Qwen-72B + LoRA": {
"GPU": "2x A100 80GB",
"时间": "12-24 小时",
"云GPU成本": "$48-96",
"数据量": "5000-20000 条",
},
}
@staticmethod
def estimate_openai(
model: str, num_examples: int, avg_tokens: int, epochs: int = 3
) -> dict:
"""估算 OpenAI 微调成本"""
pricing = FinetuneCostEstimator.OPENAI_PRICING.get(model)
if not pricing:
return {"error": "模型不在定价表中"}
total_tokens = num_examples * avg_tokens * epochs
training_cost = total_tokens / 1_000_000 * pricing["training"]
return {
"model": model,
"training_tokens": total_tokens,
"training_cost": f"${training_cost:.2f}",
"epochs": epochs,
"note": "不含推理成本",
}
# 估算
estimator = FinetuneCostEstimator()
# OpenAI 微调
result = estimator.estimate_openai(
model="gpt-4o-mini-2024-07-18",
num_examples=1000,
avg_tokens=500,
epochs=3,
)
print(f"OpenAI 微调: {result}")
# 开源模型
print("\n=== 开源模型微调成本 ===")
for model, info in estimator.OPEN_SOURCE_COST.items():
print(f"\n{model}:")
for k, v in info.items():
print(f" {k}: {v}")
本章小结
| 方案 | 成本 | 延迟 | 适用场景 |
|---|---|---|---|
| 提示工程 | 最低 | 高 | 快速原型、通用任务 |
| RAG | 中 | 中 | 知识密集型问答 |
| LoRA 微调 | 低 | 最低 | 特定格式/风格/领域 |
| 全量微调 | 高 | 最低 | 大规模领域定制 |
下一章:微调方法详解——全量微调、LoRA、QLoRA 的原理与对比。