AI 模型微调进阶:LoRA、QLoRA、训练评估和上线流程
前两篇讲了两个基础问题:
1 | 什么时候该微调 |
到了第三篇,就可以进入工程层面了。
如果你微调的是开源大模型,最常见的路线不是全量微调,而是参数高效微调:
1 | LoRA / QLoRA / PEFT |
它们的目标很现实:
1 | 不要训练整个大模型 |
这篇文章讲一个从训练到上线的完整思路。
不是追求论文细节,而是回答工程上真正会遇到的问题。
一、为什么很少直接全量微调
全量微调的意思是:
1 | 把模型所有参数都参与训练。 |
这听起来最直接。
但对大模型来说,成本很高:
- 显存压力大
- 训练速度慢
- checkpoint 很大
- 每个任务都要保存一份完整模型
- 容易过拟合
- 部署和回滚成本高
如果你只是想让模型适配一个客服分类、固定 JSON 输出、内部写作风格,没必要动整个模型。
这就是 LoRA 这类方法流行的原因。
二、LoRA 的直觉理解
LoRA 的核心思想是:
1 | 冻结原始模型权重 |
你可以把它理解成:
1 | 基础模型保持不动 |
这样有几个好处:
- 训练参数少
- 显存需求更低
- 训练更快
- 每个任务只保存 adapter
- 可以切换不同 adapter
- 对基础模型破坏更小
LoRA 论文里强调,它通过低秩矩阵来近似权重更新,从而显著减少可训练参数。
工程上你不一定要先懂完整数学。
先理解这句话就够了:
1 | LoRA 不是重新训练整个模型,而是在模型旁边训练一组小插件。 |
三、QLoRA 又是什么
QLoRA 可以粗略理解为:
1 | 量化基础模型 + LoRA 训练 adapter |
也就是把基础模型用更低位宽加载,进一步降低显存占用,然后训练 LoRA adapter。
它适合:
- 单卡资源有限
- 想微调 7B、14B、32B 这类模型
- 训练成本敏感
- 任务不需要全量微调
但也要注意:
1 | 显存省了,不代表训练就没有成本。 |
你仍然要处理数据、tokenizer、上下文长度、batch size、学习率、评估和上线。
QLoRA 只是降低了门槛,不会替你解决数据质量问题。
四、一个典型训练流程
开源模型微调可以按这个流程走:
1 | 1. 选择基础模型 |
这里最容易出问题的是第 2、3、9 步。
也就是:
1 | 数据格式 |
工具本身反而不是最难的。
五、示意代码:Hugging Face + PEFT + TRL
下面是一个简化示例,只展示结构。
真实项目里你还要处理数据读取、模板格式、显存配置、日志、checkpoint、评估脚本等。
1 | pip install -U transformers datasets peft trl accelerate bitsandbytes |
训练代码示意:
1 | from datasets import load_dataset |
这段代码不是万能模板。
不同模型的 target_modules 可能不一样。
不同版本的 TRL / Transformers 参数名也可能变化。
正式项目里,不要复制完就直接跑生产训练。先用 20 条样本跑通最小流程。
六、几个重要参数怎么理解
1. r
r 是 LoRA 的秩。
可以粗略理解为 adapter 的表达能力。
越大,参数越多,可能能力更强,但也更占资源、更容易过拟合。
常见可以从:
1 | 8、16、32 |
开始试。
不要一上来就拉很大。
2. lora_alpha
lora_alpha 会影响 LoRA 更新的缩放。
常见会配合 r 使用。
比如:
1 | r = 16 |
不用一开始就纠结它。
先固定一组常见配置,把注意力放在数据和 eval。
3. lora_dropout
lora_dropout 用来降低过拟合风险。
数据少时可以适当保留。
如果数据非常小,还训练很多 epoch,模型很容易记住样本。
4. learning_rate
学习率太大,模型可能学坏。
学习率太小,训练变化不明显。
LoRA 微调常见会比全量微调学习率更大一些,但具体要看模型、数据和任务。
不要靠感觉。
看验证集和固定 eval。
5. epoch
epoch 不是越多越好。
数据少时,多跑几轮很容易过拟合。
一个实用做法:
1 | 每个 epoch 保存 checkpoint |
不要默认最后一个 checkpoint 最好。
七、评估不要只看 loss
训练 loss 降低,不代表业务效果一定变好。
微调评估至少要看:
- 固定测试集准确率
- 输出格式成功率
- JSON 解析成功率
- 安全拒答是否稳定
- 幻觉是否增加
- 老任务是否退化
- 延迟和成本是否可接受
比如一个 JSON 抽取任务,可以用脚本自动评估:
1 | def score(pred, expected): |
同时还要人工抽查。
尤其是开放式生成任务,只靠自动指标很容易误判。
八、上线前要做 A/B 对比
微调模型上线前,不要直接全量替换。
更稳的方式是:
1 | 基础模型:baseline |
用同一批线上真实请求离线回放。
比较:
- 成功率
- 格式稳定性
- 人工评分
- 错误类型
- 延迟
- token 消耗
- 失败样本
如果 candidate 明显更好,再灰度上线。
上线后继续观察。
不要因为“训练成功”就认为“业务成功”。
训练成功只说明流程跑完了。
业务成功要靠指标证明。
九、部署 adapter 还是合并模型
LoRA 训练后通常得到的是 adapter。
部署有两种方式:
1. 挂载 adapter
推理时加载基础模型,再加载 adapter。
优点:
- 方便切换任务
- 存储成本低
- 回滚简单
- 可以保留多个 adapter
缺点:
- 推理服务需要支持 adapter
- 管理上多一层复杂度
2. 合并 adapter 到基础模型
把 adapter 权重合并回模型权重,导出一个新模型。
优点:
- 推理部署更简单
- 不需要运行时管理 adapter
缺点:
- 模型文件更大
- 多任务切换不方便
- 回滚和版本管理要更谨慎
我的建议:
1 | 开发和灰度阶段挂载 adapter |
如果你的推理框架原生支持 adapter,多数场景可以一直不合并。
十、版本管理一定要做好
微调产物不是一个文件。
至少要记录:
- 基础模型版本
- tokenizer 版本
- 训练数据版本
- 训练脚本版本
- LoRA 配置
- 超参数
- checkpoint
- eval 结果
- 上线时间
- 回滚方式
可以建立一个训练记录:
1 | run_id: ft-20260623-001 |
没有版本记录,模型上线后出了问题,你会很难定位:
1 | 是数据变了? |
十一、常见失败原因
1. 数据太少或太脏
这是最常见原因。
不要先怪 LoRA。
先看数据。
2. prompt 模板和训练模板不一致
训练时一种格式,上线时另一种格式,效果会明显下降。
3. 只看训练集,不看 holdout
模型把训练样本背熟了,不代表能处理新输入。
4. 训练太久导致过拟合
数据少时尤其明显。
表现是训练集很好,验证集和真实输入变差。
5. 忘记比较基础模型
如果微调模型只是“看起来不错”,但没有超过基础模型,那这次微调就没有意义。
微调不是为了拥有一个自定义模型。
微调是为了在明确指标上超过 baseline。
十二、我的建议
开源模型微调可以按这个策略起步:
1 | 先选一个小而明确的任务 |
真正成熟的微调流程,不是一次训练脚本。
而是一整套闭环:
1 | 数据版本 |
微调越往后,越不像“模型技术”,越像“工程系统”。
把这个系统搭好,比纠结某个单独参数更重要。