11.9 微调实操流程
前面 8 节讲完了微调要做什么和为什么。这一节讲怎么做——从零开始走一遍完整的微调流程,到部署上线。
整个流程分 6 步。每一步都给出了具体工具、参数和检查点。配套的完整可运行代码在项目文件夹里(见关联链接)。
第 1 步:选基座模型
选对基座模型,事半功倍。选错了,后面怎么调都很难弥补。
按任务选架构:
| 你的任务 | 推荐的基座模型类型 | 2026 年推荐选择 |
|---|---|---|
| 通用对话 | 对话模型(Chat/Instruct) | Qwen2.5-Chat、LLaMA-3-Chat、Gemma 3 |
| 代码生成 | 代码模型 | DeepSeek-Coder-v2、CodeLlama |
| 中文任务 | 中文预训练占比较高的模型 | Qwen 系列、ChatGLM、DeepSeek |
| 数学推理 | 推理增强模型 | DeepSeek-R1、Qwen3-Math |
| 多语言 | 多语言预训练模型 | Qwen、LLaMA-3、Aya |
按你的硬件选大小:
| 你的显卡 | 最大能跑的模型 (LoRA) | 最大能跑的模型 (QLoRA) |
|---|---|---|
| RTX 3060 (12GB) | 3B | 13B |
| RTX 4070 (12GB) | 3B | 13B |
| RTX 4090 (24GB) | 7B | 70B(需 offloading) |
| A100 (80GB) | 70B | 180B+ |
| Apple M2/M3 (16GB) | 3B | 7B |
选型检查点: 先把基座模型不做任何微调,用你的任务数据测一下。如果效果已经不错了,你可能根本不需要微调——只需要更好的 Prompt。这一步不要跳过。
第 2 步:准备数据
数据准备是整个流程中最花时间的一步——通常比训练本身更久。
数据格式。 最常见的三种格式,选一种即可:
Alpaca 格式(最简单,单轮问答):
{
"instruction": "用简单的语言解释什么是光合作用",
"input": "",
"output": "光合作用是植物利用阳光、水和二氧化碳制造食物的过程。植物叶片里的叶绿素吸收阳光,把空气中的二氧化碳和根部吸收的水转化成葡萄糖,同时放出氧气。"
}
ShareGPT 格式(多轮对话):
{
"conversations": [
{"from": "human", "value": "法国的首都是什么?"},
{"from": "gpt", "value": "法国的首都是巴黎。"},
{"from": "human", "value": "那里有什么著名的地标?"},
{"from": "gpt", "value": "巴黎有埃菲尔铁塔、卢浮宫、凯旋门等著名地标。"}
]
}
ChatML 格式(微软标准,角色标签明确):
<|im_start|>system
你是一个有帮助的助手。<|im_end|>
<|im_start|>user
解释光合作用<|im_end|>
<|im_start|>assistant
光合作用是植物利用阳光...<|im_end|>
数据清洗清单。 训练前必须逐项检查:
- 去重:完全相同的样本只保留一份;高度相似的样本(MinHash 相似度大于 0.8)只保留一条
- 去 PII:检查数据中是否含真实姓名、电话、邮箱、身份证号等,用 Presidio 等工具自动检测删除
- 格式校验:每条数据都能正确解析为 JSON;ShareGPT 格式的 from/value 字段完整
- 长度过滤:去掉 output 为空或只有几个字的样本;去掉单条超过模型上下文窗口 80% 的样本
- 一致性检查:同一 instruction 下有多个 output 时,确认内容一致而非矛盾
- 混入通用数据:在领域数据中混入 10-20% 的通用对话数据,防止模型微调后只会回答领域问题不会正常聊天(灾难性遗忘的对策)
数据量经验法则 1:
| 任务类型 | 最低数据量 | 推荐数据量 | 数据不够怎么办 |
|---|---|---|---|
| 指令跟随(改格式) | 100 条 | 500-1000 条 | 减少到 50-100 条 + 更小 rank + 更少 epoch |
| 领域问答 | 500 条 | 2000-5000 条 | 合成数据扩充(让 GPT-5/Claude 生成更多问答对) |
| 风格转换 | 300 条 | 1000-3000 条 | 生成少量高质量改写对比大量粗糙改写更有效 |
| 复杂推理 | 2000 条 | 5000-10000 条 | 数据质量比数量更重要,精选 2000 条 > 粗糙 10000 条 |
切分训练集/验证集/测试集: 80/10/10 或 80/20。按来源或时间切分,不随机切(防止数据泄露)。验证集至少 50K token 才能给出稳定评估。
第 3 步:选微调方法
这一步回答”用全量微调、LoRA 还是 QLoRA”。
| 你的情况 | 推荐方法 | 典型配置 |
|---|---|---|
| 第一次做微调,想快速体验 | QLoRA | 基座 4-bit NF4 + LoRA rank=16 + alpha=32 + SFT 2 轮 |
| 有 24GB 单卡,模型 7-13B | LoRA (fp16) | rank=16 + alpha=32 |
| 有 24GB 单卡,模型 70B | QLoRA(唯一选择) | NF4 + 双重量化 + 梯度检查点 |
| 有多张 A100,几万条数据 | 先 LoRA 验证,效果好考虑全量微调 | LoRA rank=16 → full FT lr=1e-5 |
| 只想让模型按特定风格回答 | LoRA rank=8 就够 | rank=8 + alpha=16 |
| 想让模型学会大量新知识 | 全量微调(LoRA 不适合知识注入) | 全量 FT + 混入通用数据防遗忘 |
| 没有 GPU,但有 API 额度 | OpenAI/Claude API 内置微调 | 上传 JSONL 数据,调用 API 即可 |
决策速记:显存不够 → QLoRA。显存够 → LoRA。LoRA 效果不够且资源充足 → 全量微调。只是改风格 → LoRA。要灌大量新知识 → 可能 RAG 比微调更合适。
第 4 步:训练
LoRA/QLoRA 的典型训练配置 2:
| 超参数 | 推荐起始值 | 调参方向 |
|---|---|---|
| rank (r) | 16 | 过拟合 → 降到 8;欠拟合 → 升到 32 |
| alpha | 32 (即 2r) | 和 rank 一起调。通常 alpha = 2r |
| 学习率 | 2e-4 | 不稳定 → 降到 1e-4;训练太慢且 loss 在降 → 升到 5e-4 |
| 训练轮数 | 2-3 | 数据少 (小于 1000 条) → 3-5 轮;数据多 → 1-2 轮 |
| batch size | 2-4 (每卡) | 显存不够 → 2 + 增大 gradient_accumulation_steps |
| 序列最大长度 | 512-1024 | 长文档任务 → 2048 或更高 |
| LoRA dropout | 0.05 | 过拟合时用,不过拟合设 0 |
| 学习率调度 | cosine | cosine + warmup 通用性最好 |
| 优化器 | adamw_8bit | 比标准 Adam 显存少 75%,效果接近 |
框架选型(2026 年):
| 框架 | 适合 | 特点 |
|---|---|---|
| Unsloth | 个人开发者首选 | 2-5x 快,80% 省显存,免费 Colab Notebook |
| LLaMA-Factory | 快速实验 | 100+ 模型,配置式操作,Web UI |
| Axolotl | 进阶用户 | YAML 配置,灵活但需要更多经验 |
| HuggingFace TRL + PEFT | 自定义需求 | 最灵活,但需要自己写训练代码 |
训练检查点:
- 训练开始前截一张 nvidia-smi 的图(确认显存没超)
- 前 50 步盯着 loss:如果爆炸或变成 NaN,立刻停,降学习率
- 每个 epoch 结束在验证集上测一次:验证 loss 如果不降了,考虑早停
- 保存每个 epoch 的 checkpoint(QLoRA 的 checkpoint 只有几十 MB,不用省空间)
第 5 步:评估
三个层次,由快到慢:
自动评估。用脚本跑验证集的困惑度(PPL)和 token F1。PPL 越低越好,但不要只看 PPL——PPL 低只能说明模型会”预测下一个词”,不代表会”回答你的问题”。ROUGE-L 和 BLEU 可以提供额外的字面匹配参考。
人工抽检。从验证集中随机抽 20-30 条,对比原始模型和微调后模型的回答。至少要有两个维度打分:
- 是否按要求的格式回答(格式分)
- 回答内容是否准确有用(内容分)
A/B 对比。让原始模型和微调模型分别回答同一条问题,把两个答案并列放在一起(不做标记),让人或另一个模型判断哪个更好。如果微调模型没有在 60% 以上的对比中胜出,重新评估”微调”这个决策。
评估检查点: 如果自动指标看起来不错但人工抽检发现很多问题,信任人工判断。自动指标会被”学会的格式”骗过去——模型只是学会了写好看的格式,内容可能仍然是乱的。
第 6 步:部署
部署指的是把训练好的模型变成可以被其他地方调用的服务。
三种部署方式:
合并权重部署。把 LoRA 权重合并回基座模型,得到一个完整的独立模型。合并后不再需要 PEFT 库,可以像普通模型一样加载。最常用,适合大部分场景。
merged_model = model.merge_and_unload()
merged_model.save_pretrained("./merged_model")
动态 Adapter 部署。不合并,推理时动态加载 LoRA adapter。适合”一个基座模型服务多个场景”——每个场景一个 adapter,共享同一个基座模型。vLLM 和 SGLang 2026 年都支持推理时热切换 adapter。显存效率极高:一个 70B 基座 (140GB) 配 10 个 adapter (每个几十 MB) 覆盖 10 个场景。
Ollama 部署。把合并后的模型转为 GGUF 格式,用 Ollama 一键部署到本地。适合个人使用和小团队内部服务。
部署检查点:
- 推理前清理 GPU 显存中的训练缓存
- 用与训练完全相同的对话模板进行推理(tokenizer.apply_chat_template)
- 上线前做一次完整的端到端测试:输入一条真实的用户问题,看输出是否正常
- 监控首 token 延迟(TTFT)和吞吐量(tokens/s),建立 baseline 方便后续对比
上一节:11.8 常见错误 | 下一节:11.14 什么时候微调 vs 什么时候用 RAG vs 什么时候用 Prompt