LLaMA Factory微调实战 - 理论讲义
目录
微调的本质 - 为什么需要微调?
预训练模型的能力与局限
类比理解:
- 预训练模型 = 大学毕业生
- 有广博的知识(从海量文本学习)
- 有通用的能力(理解、推理、生成)
- 但缺乏专业经验(特定领域、特定风格)
- 微调 = 岗前培训
- 学习公司的业务流程
- 适应特定的工作风格
- 掌握专业术语和规范
实际案例:
| 场景 | 预训练模型表现 | 微调后表现 |
|---|---|---|
| 客服对话 | 回答正确但不专业 ”可以退货,联系客服” | 回答专业且规范 ”您可以在订单页面点击’申请退货’…” |
| 法律咨询 | 给出通用建议 ”建议咨询律师” | 引用具体法条 ”根据《民法典》第XXX条…” |
| 代码生成 | 生成基础代码 | 符合公司代码规范 包含完整注释和错误处理 |
什么时候需要微调?
决策树:
是否需要微调?
│
├─ 需要特定领域知识? → 是 → 微调
│ └─ 例:医疗诊断、法律文书、金融分析
│
├─ 需要特定输出格式? → 是 → 微调
│ └─ 例:结构化报告、特定JSON格式
│
├─ 需要特定语言风格? → 是 → 微调
│ └─ 例:品牌语气、古文风格、儿童语言
│
├─ 需要遵循特定流程? → 是 → 微调
│ └─ 例:客服SOP、诊断流程、审批流程
│
└─ 通用任务 + Prompt工程够用? → 否 → 不需要微调
└─ 例:简单问答、文本摘要、翻译
微调 vs 其他方案对比
| 方案 | 成本 | 效果 | 适用场景 | 示例 |
|---|---|---|---|---|
| Prompt工程 | 💰 最低 | ⭐⭐ | 简单任务、快速验证 | ”你是客服,请回答…” |
| RAG检索增强 | 💰💰 低 | ⭐⭐⭐ | 需要实时知识、文档问答 | 查询知识库后回答 |
| 微调 | 💰💰💰 中 | ⭐⭐⭐⭐ | 特定领域、固定模式 | 今天的实验 |
| 预训练 | 💰💰💰💰💰 极高 | ⭐⭐⭐⭐⭐ | 从零构建基础模型 | Qwen、GPT的训练 |
关键洞察:
微调不是改变模型的知识,而是调整模型的行为模式
微调方法对比
Full Fine-tuning(全量微调)
原理:
┌─────────────────┐
│ 预训练模型 │
│ (70亿参数) │ ← 全部参数都更新
└─────────────────┘
↓
训练数据 ↓
┌─────────────────┐
│ 微调后模型 │
│ (70亿参数) │ ← 全部参数都改变
└─────────────────┘
特点:
| 优点 | 缺点 |
|---|---|
| ✅ 效果最好 | ❌ 显存需求大(7B模型需要>80GB) |
| ✅ 适应能力强 | ❌ 训练时间长 |
| ✅ 实现简单 | ❌ 容易过拟合 |
| ❌ 每个任务需要完整模型副本 |
资源需求(7B模型):
- 训练显存:~80GB(需要A100或多卡)
- 存储空间:~14GB(完整模型)
- 训练时间:数小时到数天
LoRA(Low-Rank Adaptation)
核心思想:
不改变原模型,只训练小的”补丁”
原理图解:
原始权重矩阵 W (4096×4096)
┌─────────────────────────┐
│ │
│ 冻结不动 │ ← 16M参数
│ (预训练权重) │
│ │
└─────────────────────────┘
+
A (4096×8) × B (8×4096) ← 只训练这两个小矩阵
└──────┬──────┘
LoRA补丁 (只有65K参数!)
最终输出 = W·x + A·B·x
数学直觉:
- 原始矩阵:4096×4096 = 16,777,216 参数
- LoRA矩阵:(4096×8) + (8×4096) = 65,536 参数
- 参数量减少 256倍!
关键参数:
# LoRA配置示例
lora_config = {
"r": 8, # rank:矩阵的秩(越大效果越好,但参数越多)
"lora_alpha": 16, # 缩放因子(通常设为 r 的 2倍)
"lora_dropout": 0.05,# dropout率(防止过拟合)
"target_modules": ["q_proj", "v_proj"] # 对哪些层应用LoRA
}
参数选择指南:
| rank (r) | 参数量 | 适用场景 | 训练时间 |
|---|---|---|---|
| 4 | 很少 | 简单任务、数据少 | 最快 |
| 8 | 少 | 通用推荐 | 快 |
| 16 | 中等 | 复杂任务 | 中等 |
| 32+ | 较多 | 需要强适应性 | 较慢 |
资源需求(7B模型):
- 训练显存:~24GB(单张3090/4090可以)
- 存储空间:~50MB(LoRA权重)
- 训练时间:10-30分钟(小数据集)
QLoRA(Quantized LoRA)
核心创新:
LoRA + 量化 = 更低的显存需求
原理:
步骤1: 量化基础模型
┌─────────────────┐
│ FP16 模型 │ 28GB显存
│ (70亿参数) │
└─────────────────┘
↓
量化到4bit
┌─────────────────┐
│ INT4 模型 │ 7GB显存 (减少75%!)
│ (70亿参数) │
└─────────────────┘
步骤2: 在量化模型上训练LoRA
↓
┌─────────────────┐
│ INT4 模型 │ 冻结
│ + LoRA (FP16) │ 训练
└─────────────────┘
三种方法对比:
| 方法 | 显存需求 | 训练速度 | 效果 | 存储 | 推荐场景 |
|---|---|---|---|---|---|
| Full FT | 80GB | 慢 | ⭐⭐⭐⭐⭐ | 14GB | 有A100资源 |
| LoRA | 24GB | 快 | ⭐⭐⭐⭐ | 50MB | 通用推荐 |
| QLoRA | 12GB | 中等 | ⭐⭐⭐⭐ | 50MB | 显存受限 |
实际选择建议:
你的GPU显存是多少?
│
├─ 80GB+ (A100/H100)
│ └─ 可以尝试 Full Fine-tuning
│
├─ 24GB+ (3090/4090)
│ └─ 使用 LoRA ✅ (今天的实验)
│
├─ 12-16GB (3060/4060)
│ └─ 使用 QLoRA
│
└─ <12GB
└─ 考虑更小的模型或云服务
数据准备
数据格式
标准格式(Alpaca):
{
"instruction": "任务描述",
"input": "具体输入(可选)",
"output": "期望输出"
}
三种常见场景:
场景1: 问答对(无额外输入)
{
"instruction": "什么是机器学习?",
"input": "",
"output": "机器学习是人工智能的一个分支..."
}
场景2: 带上下文的任务
{
"instruction": "将以下文本翻译成英文",
"input": "今天天气很好",
"output": "The weather is nice today"
}
场景3: 多轮对话
{
"conversations": [
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!有什么可以帮助你的?"},
{"role": "user", "content": "介绍一下你自己"},
{"role": "assistant", "content": "我是AI助手..."}
]
}
数据质量要求
黄金法则:
质量 > 数量。100条高质量数据 > 1000条低质量数据
高质量数据的特征:
| 维度 | 好的示例 | 差的示例 |
|---|---|---|
| 准确性 | ”根据《劳动法》第XX条…" | "好像是这样…” |
| 完整性 | 包含步骤、注意事项、示例 | ”可以退货” |
| 一致性 | 风格统一、术语统一 | 有的正式、有的口语 |
| 多样性 | 覆盖各种问法和场景 | 重复相似的问题 |
数据清洗检查清单:
✅ 检查项目:
□ 没有HTML标签或特殊字符
□ 没有过长的文本(>2048 tokens)
□ 没有空白的output
□ 没有明显的错别字
□ 格式符合JSON规范
□ instruction和output对应正确
数据量需求
经验法则:
| 任务类型 | 最少数据量 | 推荐数据量 | 说明 |
|---|---|---|---|
| 风格迁移 | 50-100条 | 200-500条 | 如:改变语气、格式 |
| 简单分类 | 100-200条 | 500-1000条 | 如:情感分析、意图识别 |
| 知识注入 | 500-1000条 | 2000-5000条 | 如:领域问答 |
| 复杂推理 | 1000+条 | 5000+条 | 如:代码生成、数学题 |
实用建议:
阶段1: 先准备50-100条核心数据
↓
快速微调测试
↓
阶段2: 根据效果补充数据
↓
迭代优化
常见数据问题
问题1: 数据泄露
// ❌ 错误:output中包含了不该有的信息
{
"instruction": "用户问:如何退货?",
"output": "根据我们的内部文档XYZ..."
}
// ✅ 正确
{
"instruction": "如何退货?",
"output": "您可以在订单页面申请退货..."
}
问题2: 过拟合特定模式
// ❌ 所有问题都是"如何..."开头
"如何查询订单?"
"如何申请退货?"
"如何联系客服?"
// ✅ 多样化的问法
"查询订单的方法"
"我想退货"
"联系客服"
"订单在哪里看"
问题3: 分布不均
❌ 100条数据中:
- 80条关于退货
- 15条关于物流
- 5条关于支付
✅ 均衡分布:
- 30条关于退货
- 30条关于物流
- 30条关于支付
- 10条其他
超参数解析
学习率 (Learning Rate)
类比理解:
- 学习率 = 学习的步伐大小
- 太大 = 跑太快,容易摔倒(不收敛)
- 太小 = 走太慢,到不了终点(训练太慢)
可视化:
Loss曲线对比
学习率太大 (1e-3): 学习率合适 (5e-5): 学习率太小 (1e-6):
Loss Loss Loss │ ╱╲╱╲ │ ╲ │ ╲___ │ ╱ ╲ │ ╲___ │ ╲___ │╱ ╲ │ ╲___ │ ╲___ └─────────→ Steps └─────────→ Steps └─────────→ Steps 震荡不收敛 平稳下降 ✅ 下降太慢
推荐值:
| 模型大小 | Full FT | LoRA | QLoRA |
|---|---|---|---|
| 7B | 1e-5 | 5e-5 | 1e-4 |
| 13B | 5e-6 | 3e-5 | 5e-5 |
| 70B+ | 1e-6 | 1e-5 | 3e-5 |
调优策略:
1. 从推荐值开始
2. 观察loss曲线:
- 震荡 → 减小学习率(÷2)
- 下降太慢 → 增大学习率(×2)
3. 使用学习率调度器(cosine/linear)
Batch Size 与 Gradient Accumulation
概念:
Batch Size = 每次喂给模型多少条数据
Gradient Accumulation = 累积多少次才更新参数
实际Batch Size = Batch Size × Gradient Accumulation
示例:
# 配置1: 直接使用大batch
per_device_train_batch_size = 8
gradient_accumulation_steps = 1
# 实际batch = 8
# 配置2: 累积梯度(推荐)
per_device_train_batch_size = 2
gradient_accumulation_steps = 4
# 实际batch = 2×4 = 8(效果相同,但显存需求小)
显存与Batch Size的关系:
| Batch Size | 显存占用 | 训练速度 | 推荐场景 |
|---|---|---|---|
| 1 | 最低 | 慢 | 显存紧张 |
| 2-4 | 低 | 中等 | 通用推荐 |
| 8+ | 高 | 快 | 显存充足 |
实用公式:
目标实际Batch Size = 16-32 (经验值)
如果显存不够:
per_device_batch_size = 2
gradient_accumulation = 16 # 实际batch=32
如果显存充足:
per_device_batch_size = 8
gradient_accumulation = 4 # 实际batch=32
Epoch(训练轮数)
定义:
1 epoch = 模型看完所有训练数据一遍
选择指南:
| 数据量 | 推荐Epoch | 原因 |
|---|---|---|
| <100条 | 5-10 | 数据少,需要多看几遍 |
| 100-1000条 | 3-5 | 通用推荐 |
| 1000-10000条 | 2-3 | 数据多,避免过拟合 |
| 10000+条 | 1-2 | 大数据集,一遍就够 |
过拟合判断:
训练集Loss vs 验证集Loss
正常情况: 过拟合:
Loss Loss │ ╲___训练集 │ ╲训练集 │ ╲验证集 │ ╲╱ 验证集开始上升 │ ╲ │ ╱ └─────────→ Epoch └─────────→ Epoch
其他重要参数
Warmup Steps:
作用:训练开始时逐渐增大学习率
学习率变化: ↗ warmup阶段(逐渐增大)
╱
╱ ╲___ 正常训练(逐渐衰减)
推荐值:总步数的 5-10%
Max Length / Cutoff Length:
作用:限制输入文本的最大长度
选择依据:
- 数据中最长文本的长度
- 显存限制
- 模型的最大支持长度
推荐值:
- 对话任务:512-1024
- 文档任务:2048-4096
保存策略:
save_strategy = "steps" # 按步数保存
save_steps = 100 # 每100步保存一次
save_total_limit = 3 # 只保留最近3个checkpoint
快速参考卡片
微调配置速查表
# 🚀 快速开始配置(7B模型 + 3090/4090)
model: Qwen2.5-7B-Instruct
method: LoRA
# LoRA参数
lora_rank: 8
lora_alpha: 16
lora_dropout: 0.05
# 训练参数
learning_rate: 5e-5
batch_size: 2
gradient_accumulation: 4 # 实际batch=8
epochs: 3
max_length: 1024
# 数据要求
min_samples: 50
recommended_samples: 200-500
# 预期结果
training_time: 10-20分钟
lora_size: ~50MB
memory_usage: ~24GB
问题诊断流程
训练遇到问题?
Loss不下降?
├─ 检查学习率(是否太小?)
├─ 检查数据质量(是否有错误?)
└─ 增加训练轮数
显存不足?
├─ 减小batch_size
├─ 增大gradient_accumulation
├─ 减小max_length
└─ 尝试QLoRA
过拟合?
├─ 减少epoch
├─ 增加数据量
├─ 增大lora_dropout
└─ 使用验证集早停
训练太慢?
├─ 增大batch_size
├─ 减少logging频率
└─ 使用更快的GPU
总结:核心要点
- 微调的本质:调整模型行为,不是灌输知识
- 方法选择:LoRA是性价比最高的方案
- 数据为王:100条高质量 > 1000条低质量
- 超参数:从推荐值开始,根据loss曲线调整
- 迭代优化:小步快跑,快速验证
记住这个公式:
微调成功 = 高质量数据 × 合适的方法 × 正确的参数 × 充分的迭代
课后思考题
- 为什么LoRA能用这么少的参数达到接近全量微调的效果?
- 如果你有一个客服场景,只有30条对话数据,应该怎么做?
- 训练时loss从2.5降到0.3,是好事还是坏事?