2.1 LoRA 低秩微调原理与实战 本节摘要:LoRA(Low-Rank Adaptation)是目前大模型微调的主流方法。它冻结基座模型的全部参数,只在注意力层旁边并接两个小矩阵做训练,可训练参数量常压到全量的千分之一甚至万分之一,单张消费级显卡就能跑。本节讲清"低秩近似权重更新"的数学直觉、秩和目标模块这两个关键参数怎么选,以及 LoRA 训练和合并推理的工程流程。 本节地图 阅读完本节,你应当能够: 说清 LoRA 用两个低秩矩阵乘积近似权重更新的原理 给定模型规模和秩,估算可训练参数量级 解释秩的大小如何影响效果与成本的权衡 判断该把 LoRA 加在哪些模块上 描述 LoRA 适配器的训练、保存、合并、推理流程 一、问题与直觉 全量微调一个 7B 参数的模型,光模型权重就要
本节摘要:LoRA(Low-Rank Adaptation)是目前大模型微调的主流方法。它冻结基座模型的全部参数,只在注意力层旁边并接两个小矩阵做训练,可训练参数量常压到全量的千分之一甚至万分之一,单张消费级显卡就能跑。本节讲清"低秩近似权重更新"的数学直觉、秩和目标模块这两个关键参数怎么选,以及 LoRA 训练和合并推理的工程流程。
阅读完本节,你应当能够:
全量微调一个 7B 参数的模型,光模型权重就要 14GB 显存(半精度),加上优化器状态、梯度、激活值,实际需要五六十 GB——单张 24GB 的消费级卡根本塞不下,得上多卡集群。这对绝大多数团队是不现实的。
更关键的是,很多研究发现:大模型在适配下游任务时,权重的有效更新是"低秩"的——也就是说,虽然权重矩阵很大,但训练过程中真正发生的变化集中在一个低维子空间里。既然变化本身是低秩的,就没必要让所有参数都动,只要捕捉住那个低维变化就够了。
LoRA 利用的就是这个观察:不动原始权重 W,而是在它旁边加一条由两个小矩阵 A 和 B 组成的支路,训练时只更新 A 和 B。推理时可以把 A·B 算出来加回 W(合并后零额外开销),也可以保留支路动态切换(牺牲一点速度换灵活性)。这种"四两拨千斤"的做法,把微调的门槛从专业集群降到了单卡。
原始权重更新可以写成 W' = W + ΔW,全量微调直接学 ΔW(它和 W 一样大)。LoRA 把 ΔW 拆成两个小矩阵的乘积:
ΔW ≈ B × A 其中 W 是 d×d,A 是 r×d,B 是 d×r,r 远小于 d
| 符号 | 含义 | 典型量级(7B 模型) |
|---|---|---|
| W | 原始权重矩阵 | 4096×4096,约 1600 万参数 |
| A | 降秩矩阵 | r×4096,r=8 时约 3 万参数 |
| B | 升秩矩阵 | 4096×r,r=8 时约 3 万参数 |
| r | 秩 | 常取 4、8、16、64 |
一个 1600 万参数的权重,LoRA 只用 6 万参数(r=8)去逼近它的更新——参数量降到约 1/270。
💡 关键直觉:A 和 B 的乘积 B·A 仍然是一个 d×d 的矩阵,理论上能表达 W 的任意更新。限制在于秩——r 越小,能表达的更新越"简单",但越省显存。效果损失往往比想象中小,因为微调任务的有效更新本来就低秩。
初始化有个细节:A 用随机值,B 初始化为零。这样训练开始时 B·A = 0,模型输出和原始基座完全一致——训练是从"不改变"的起点出发,逐步学出适配任务的更新,保证了训练初期的稳定性。
秩 r 决定能学的更新的复杂度:
| r 值 | 效果 | 成本 | 适用 |
|---|---|---|---|
| 4–8 | 多数任务够用 | 最省 | 风格调整、格式对齐 |
| 16–32 | 留更多余量 | 中等 | 领域适配、需学新知识 |
| 64+ | 接近全量表达力 | 较高 | 复杂推理、大幅行为改变 |
⚠️ 常见坑:以为 r 越大越好。r 调到 128 甚至 256,参数量上去了但效果未必提升——因为任务的有效更新秩本来就低,多余的参数只是过拟合。先用 r=8 跑一版,不够再加。
目标模块 决定往哪些层加 LoRA。主流做法是加在注意力的 q、v 投影矩阵,也有研究表明全模块(q、k、v、o 再加前馈层)效果更好但参数更多。
LoRA 不是万能药。判断标准看任务性质:
| 场景 | 推荐 |
|---|---|
| 改输出风格、格式对齐、遵循特定指令 | LoRA 足够 |
| 注入少量领域知识 | LoRA 可行,但 RAG 可能更划算 |
| 学全新的复杂能力(如新编程语言推理) | 倾向全量微调 |
| 数据极少(几百条) | LoRA,全量微调会过拟合 |
💡 关键直觉:LoRA 擅长"调行为"(让模型按你的格式、风格、规则说话),不擅长"灌知识"(让模型学会它原本不懂的事实)。后者用 RAG 往往更直接——这也是为什么第 1 章 RAG 和本章微调常被放一起对比。
LoRA 微调的数据量不用很大,几百到几万条都能跑,但格式必须一致。常见格式是"指令—输出"对:
指令:把下面这段话总结成一句话 输入:……(原文) 输出:……(期望的摘要)
数据质量比数量重要——几百条人工精心标注的数据,往往胜过几万条噪声数据。把"期望模型怎么回答"的样例写清楚,比堆数据量更有效。
训练完的 LoRA 适配器有两种用法:
单任务长期部署,合并最省事(合并后就是普通模型);多任务需要频繁切换,动态加载更灵活。
LoRA 训练有几个超参,新手容易在每个上都纠结半天。把我常用的起手配置和调参顺序列出来,能省掉大量试错。
起手配置(多数场景直接用):秩 r=8、学习率 2e-4、训练 3 个 epoch、batch size 8(显存不够用梯度累积补到等效 8)、目标模块 q_proj 加 v_proj、warmup ratio 0.03。这套配置在风格调整、格式对齐这类常见任务上基本能跑出可用的结果,是稳妥的基线。
调参顺序(出问题按这个顺序排查,别一上来乱调):
| 优先级 | 调什么 | 触发条件 | 怎么调 |
|---|---|---|---|
| 1 | 训练轮次 | 过拟合(验证集变差) | 减到 1-2 轮,看验证 loss 拐点 |
| 2 | 学习率 | loss 不降或震荡 | 不降则升高(3e-4),震荡则降低(1e-4) |
| 3 | 秩 r | 效果天花板不够 | 从 8 升到 16、32,观察验证集 |
| 4 | 目标模块 | 还想再提一点 | 从 q/v 扩到全模块(q/k/v/o 加前馈) |
| 5 | 数据 | 以上都调过仍不理想 | 回头查数据质量,参考 2.4 节 |
一个反复验证过的经验:80% 的"效果不好"根源在数据,不在超参。新手容易在超参上耗几周,其实把数据清洗一遍、把 output 格式统一、把指令多样性补足,效果提升往往比调十组超参都明显。所以超参调到上面这套基线后,就该停下来去查数据了。
直觉提醒:超参调优的回报是递减的。从默认值调到"合理范围"能拿到大部分收益,从"合理"再精调到"最优"边际很小。把省下的时间花在数据和评估上,性价比远高于死磕超参。一个固定评估集加一套干净数据,比任何超参组合都值钱。
另一个常被问的问题:LoRA 训练要多久? 这取决于模型大小、数据量、硬件。一个参考量级:7B 模型加几千条数据,单张 A100 跑 3 个 epoch 大约半小时到一小时;同样的配置在消费级 3090 上可能要两三小时。数据量翻倍、模型翻倍,时间也大致翻倍。先用小数据(几百条)跑通流程确认没问题,再上全量数据,能避免浪费算力。
最后聊一个根本性的取舍:什么情况下 LoRA 够用,什么情况下必须上全量微调。这个判断比选 r 多少、学习率多少都重要——选错了路,参数调再精也白搭。
一个实用的判断框架是看"任务改变的是什么"。如果任务改变的是模型的表层行为——输出格式、说话风格、遵循特定指令、对某类输入给特定响应——LoRA 几乎都能搞定,因为这些变化对应的权重更新确实是低秩的,LoRA 的低秩近似能捕捉住。典型场景:让模型按 JSON 格式输出、让客服模型用礼貌话术、让模型遵循"先复述问题再回答"的流程。
如果任务改变的是模型的深层能力——学会一种全新的推理模式、掌握一门新语言的语法推理、获得在预训练时没见过的复杂技能——LoRA 的表达力可能不够,因为这类变化的有效更新秩更高,低秩矩阵逼近不了。这时要么上全量微调,要么考虑换个本身就有这种能力的更大基座。典型场景:让一个没见过代码的模型学会写 Rust、让模型掌握多步数学证明。
直觉提醒:判断用 LoRA 还是全量,本质是判断"任务需要的权重变化是不是低秩的"。行为调整多半低秩(LoRA 够),能力获取多半高秩(要全量)。拿不准时,先用 LoRA 跑一版看效果——成本低、试错快。LoRA 跑出来的效果如果离全量有明显差距,且差距集中在你期望的新能力上,那才考虑全量。
还有一点要提醒:LoRA 微调后,模型在目标任务上变强了,但在其他任务上可能有细微的能力变化——有时是变好(任务间正迁移),有时是变差(轻微遗忘)。所以微调完除了评估目标任务,最好也快速回归测一下模型原有的几项核心能力,确认没有意外退化。这个回归测试在多适配器架构里尤其重要,因为基座被多个适配器共享,任何一个适配器的训练都可能间接影响其他任务的表现。
下一节我们看 QLoRA 怎么用 4 比特量化把显存再砍一刀,以及如何用多适配器复用同一份基座。