下载一个开源模型的 LoRA 适配器,你会发现它只有几十到几百兆。
对比一下:基座模型的权重动辄十几 GB。
一个不到 1% 体积的文件,加载上去之后,模型就能说另一种风格的话、做另一个领域的任务。
这件事初看很神奇,但拆开之后非常朴素。核心只有一句话:
微调时真正需要改变的参数量,可能远小于你想象的。
这篇文章要讲清三件事:
- 为什么"远小于"这个判断是成立的(原理);
- LoRA 具体怎么做的、参数怎么调(实操);
- 它到底做不到什么(边界)。
第三点往往比前两点更重要------因为选型失败的代价,通常远大于参数没调好。
第一部分:先看全参微调的代价
1.1 显存:训练要存三样东西
假设一个 70 亿参数的模型,全参微调意味着要同时存下:
| 项目 | 半精度下的量级 | 说明 |
|---|---|---|
| 模型权重 | 约 14 GB | 参数本身 |
| 梯度 | 同量级(约 14 GB) | 每个参数一个梯度 |
| 优化器状态 | 数倍于参数量 | 带一阶二阶动量的优化器(如 Adam)要存两份历史统计量 |
三者叠加,显存需求通常是推理的 4~8 倍。
这意味着全参微调基本要按"多卡"来准备。
1.2 另外两个代价
① 每个任务一份完整权重。
10 个任务就是 10 份十几 GB 的权重。切换任务要重新加载十几 GB------在服务端这意味着无法同时提供多任务。
② 灾难性遗忘风险更高。
改动面太大,原有能力容易被"挤掉"。
1.3 所以真正的问题是什么
有没有办法只改一小部分,就拿到接近的效果?
LoRA 就是这个问题的答案。
第二部分:关键假设------权重更新是"低秩"的
LoRA 的全部依据,来自一个观察。
2.1 观察到了什么
研究者注意到:
微调前后,权重矩阵的变化量(那个差值矩阵)往往具有很低的"内在秩"。
换句话说:虽然这个差值矩阵和原权重一样大(都是几千乘几千),但它包含的有效信息维度可能只有几十或几百。
2.2 用一个类比理解"低秩"
想象一张 4000 × 4000 的表格,看起来有 1600 万个格子。
但如果它每一行都是前几行的线性组合,那么真正独立的信息就只有那几行。
这种"表面很大、实际维度很低"的结构,就是低秩。
2.3 这个假设带来什么
如果假设成立,那么:
我们不需要去学习那个完整的大矩阵,只要学习两个小矩阵,让它们相乘就能逼近它。
这就是 LoRA 的全部原理。
注意一个重要的措辞 :它不是在"省参数",而是在利用参数变化本身的结构性冗余。
这个区别意味着什么? 它意味着 LoRA 的效果上限,取决于"这个假设在多大程度上成立"。对某些任务(风格、格式),假设成立得很好;对另一些任务(大量新知识),假设就撑不住了。这就是后面第六部分"做不到的四类事"的根源。
第三部分:具体做法------两个小矩阵相乘
3.1 结构
原权重矩阵记作 W,形状是"输出维度 × 输入维度"。
- 全参微调:直接更新 W 里每一个数;
- LoRA :不动 W,而是额外挂一条旁路。
旁路由两个小矩阵相乘得到:
text
W 的形状: (输出维度 × 输入维度) ← 冻结,不训练
旁路 = B × A
A 的形状: (r × 输入维度)
B 的形状: (输出维度 × r)
r 就是秩,通常取 8、16、32 这种很小的数
3.2 前向计算
text
输出 = W·x + (alpha/r) · B·A·x
↑
旁路输出,按缩放系数加权
训练时只更新 A 和 B,W 保持冻结。
3.3 参数量对比
假设 W 是 4096 × 4096:
| 项 | 参数量 |
|---|---|
| W 全部参数 | 约 1677 万 |
| 旁路 A + B(r = 8) | 4096×8 + 8×4096 = 约 6.5 万 |
减少了约 256 倍。
3.4 这解释了文件为什么这么小
一个 LoRA 文件的大小,取决于三个因素:
- 层数;
- 目标模块数量(挂在多少个投影层上);
- r 的大小。
常见是几十到几百兆,和模型本体比小得多。
第四部分:除了省显存,还有三个被低估的好处
LoRA 的价值不只是"省"。
4.1 好处一:可插拔
一个基座模型可以挂不同的 LoRA,切任务就是换个小文件。
在服务端,这甚至可以做到"同一批请求用同一份基座权重、不同 LoRA 并行处理"------显存开销远小于准备多份完整模型。
具体算一下就明白:
| 方案 | 10 个任务的显存 |
|---|---|
| 10 份完整权重 | 14 GB × 10 = 140 GB |
| 1 份基座 + 10 个 LoRA | 14 GB + 若干 GB |
差距是数量级的。
4.2 好处二:不破坏基座能力
因为原权重保持冻结,模型的基础能力不容易被"改坏"。
这在需要保留通用能力的场景里非常关键。
比如:你希望模型在某个垂直领域表现更好,但不希望它因此不会算数学了。LoRA 的结构天然更安全。
4.3 好处三:训练更稳
可训练参数少,意味着优化空间小、更不容易发散。
对数据量不大的场景,这一点比省显存更有价值------因为小数据集上的全参微调非常容易过拟合或崩掉。
4.4 所以它的定位是什么
LoRA 的定位不是"穷人的微调方案",而是"更可控的定制方案"。
这个定位的转变很重要 :如果你把它当成"省钱手段",那么当资源充足时就会想"那不如上全参";如果把它当成"更可控的方案",你会发现很多场景下它本来就是更优选择,而不只是权宜之计。
第五部分:三个关键参数怎么定
这三个参数决定了 LoRA 的成败,值得单独说。
5.1 参数一:秩 r
它控制"表达能力上限"。
| 情况 | 后果 |
|---|---|
| r 太小 | 学不动复杂任务(损失降不下去) |
| r 太大 | 参数量上升,且更容易过拟合 |
经验取值:
| 任务类型 | 建议 r |
|---|---|
| 风格 / 格式类任务 | 从 8 起步 |
| 领域知识 / 表达习惯类 | 试 16 到 64 |
| 训练损失降不下去 | 考虑增大 r |
一个重要的调参原则:
从小到大试,因为增大 r 的边际收益递减,而成本线性上升。
这句话的实用价值在于 :它给了你一个明确的搜索方向。先试 8,不够再加,而不是一上来就上 64。
5.2 参数二:缩放系数 alpha
它决定旁路输出在总输出里的权重。
常见做法是让 alpha 与 r 成比例,比如 alpha = 2r。
这样做的好处是什么?
调整 r 时不必重新调学习率。
这个设计的智慧值得体会 :它把一个二维的调参问题(r 和 alpha 各自怎么定)降成了一维(只调 r)------通过把它们绑定,减少了搜索空间。
5.3 参数三:作用在哪些模块(target modules)
这是收益/成本最敏感的一项。
| 挂载范围 | 成本 | 效果 |
|---|---|---|
| 只挂注意力部分(Q、K、V 投影) | 最低 | 基础 |
| 加上前馈层 | 参数量上升 | 通常能明显提升效果 |
实践建议:
先只做注意力,效果不够再加前馈层------多数任务上,这个顺序的性价比最高。
为什么这个顺序最优? 因为:
- 注意力层决定"看什么、关注哪里",对风格和任务格式的影响最直接;
- 前馈层承载更多知识与模式,加上它对"领域表达"帮助更大,但成本也更大。
5.4 另外两个训练参数
学习率 :LoRA 的学习率通常可以比全参微调大一到两个数量级。
原因:可训练参数少、更新方向更集中。
很多"LoRA 训不动"的案例,其实只是学习率沿用了全参微调的经验值。
训练轮数 :因为参数少、收敛快,轮数通常也更少。而且要警惕过拟合(前面提过)。
第六部分:LoRA 做不到的四类事
选型时最容易踩坑的地方在这里。
6.1 第一类:大幅改变模型知识
LoRA 擅长学"表达方式"和"任务格式",但用它往模型里灌入大量新知识,效果远不如继续预训练或检索增强。
原因很直观:
- 低秩旁路能承载的信息量有限(这是它的原理决定的);
- 它只作用于部分层(不是一个全局的知识注入)。
所以如果需求是"让它知道我们公司 500 页的产品手册",那正确答案是检索,不是 LoRA。
6.2 第二类:改变模型的推理能力上限
如果基座本身不具备某种推理能力,LoRA 通常补不上。
它更多是"把已有能力引导到目标形态"。
这个判断的实践意义 :如果你的基座模型在某个推理任务上完全做不对,不要指望微调能解决。先换一个能做对的基座。
6.3 第三类:需要修改词表或结构
涉及新增 token、改变模型结构 的需求,LoRA 做不到,必须动基座。
常见的例子:
- 需要专门的领域特殊符号;
- 需要改变注意力结构;
- 需要调整层数或隐藏维度。
6.4 第四类:对延迟极度敏感的推理
如果不把 LoRA 合并进基座权重 ,推理时要额外计算旁路,会带来一点延迟(多一次矩阵乘)。
合并之后就没有这个问题 ,但也就失去了"可插拔"的优势。
这是一个必须提前想清楚的取舍。
6.5 这个取舍该怎么选
| 场景 | 建议 |
|---|---|
| 单一任务、追求极致延迟 | 合并进基座 |
| 多任务、需要动态切换 | 保持独立、动态加载 |
| 任务少但延迟敏感 | 合并,部署多份(用显存换延迟) |
关键是要在设计阶段就决定,不要留到上线前。
第七部分:六步实操流程
把实践中最容易出错的地方整理成顺序。
7.1 第一步:先确定要不要微调
能用提示词解决就用提示词;能用检索解决就用检索。
微调应该解决"提示词和检索都解决不了"的问题------通常是风格、格式、特定领域表达。
这一条的价值在于避免最常见的浪费:花两周做微调,解决一个改几行提示词就能解决的问题。
一个简单的判断法:
| 你的问题 | 正确的工具 |
|---|---|
| 模型不知道我们的产品信息 | 检索 |
| 模型的输出格式不符合要求 | 提示词 + 约束解码 |
| 模型说话风格不像我们的客服 | 微调 |
| 模型不按我们的行业术语表达 | 微调 |
7.2 第二步:准备数据
质量远比数量重要。
几百到几千条高质量、格式统一的样本,往往比几万条脏数据效果好。
两条硬要求:
- 质量优先(宁可少而精);
- 数据格式要与推理时的输入完全一致。
第二条被违反的概率极高 ------很多人训练时用一套模板,推理时用另一套,然后困惑于"为什么没效果"。这是微调失败最常见的原因,没有之一。
7.3 第三步:先用小 r 跑通,再逐步加码
先只挂注意力层、r 取 8,跑通流程并建立基线。
再依次尝试:
- 加大 r;
- 加前馈层。
"先跑通再优化"这个顺序的价值:它让你能区分"链路问题"和"能力问题"。
一个更极端但有效的做法 :先做一次过拟合实验 ------用几十条样本 + 大学习率 + 多训几轮,看模型能不能"背下来"。能背下来说明链路是通的,再扩到全量。
7.4 第四步:一定要留验证集
LoRA 参数量少、训练快,过拟合来得也快。
如果训练损失持续下降而验证损失回升,就该停了。
这一条的紧迫性来自 LoRA 的特性 :它收敛快,所以"训过头"发生得也快。没有验证集,你根本不知道什么时候该停。
7.5 第五步:评测要包含"未训练能力"的回归
这一步最容易被省,也最容易在上线后出问题。
别只看新任务变好没有,还要看原有能力有没有掉。
具体要测的:
| 类别 | 举例 |
|---|---|
| 基础知识与常识 | 通用问答是否还能答对 |
| 基础推理能力 | 简单数学、逻辑是否还正常 |
| 安全与拒答 | 护栏有没有被训坏(最容易被忽略,也最危险) |
| 指令遵循与格式 | 还能不能听懂基本要求 |
特别注意"安全与拒答"这一条 :微调数据通常全是"正面任务",模型可能因此学会"什么都答应"。它在离线评测里不明显,但在线上是风险。
7.6 第六步:明确推理时的形式
合并进基座 (延迟低、不可切换)还是动态加载(可切换、有额外开销)?
这个决定要在设计阶段做,不要留到上线前。
为什么强调"设计阶段"? 因为这两种形式对部署架构 的要求不同------动态加载需要框架支持(比如 vLLM 的多 LoRA 支持),如果架构已经定死了,后期改的代价会很大。
常见误区
误区一:"LoRA 效果一定不如全参微调。"
在多数"风格与格式"类任务上,LoRA 可以接近全参微调;差距主要出现在需要大量新知识或深度能力改变的场景。
关键看你改什么,而不是哪个更高级。
误区二:"r 越大效果越好。"
r 增大带来的是表达能力上限提升,但代价是参数、显存与过拟合风险。
更常见的问题不是 r 太小,而是数据和 target modules 没选对。
这条在实践中被验证过太多次:调大 r 无效,最后发现是训练数据格式与推理不一致。
误区三:"LoRA 可以随便叠加,任务互不干扰。"
多个 LoRA 如果训练数据重叠或目标冲突,同时加载会互相干扰。
举例:一个 LoRA 训练成"简洁回答",一个训练成"详尽解释"------同时加载,效果是互相抵消的。
多任务场景建议分别训练、按请求路由,而不是简单叠加。
误区四:"LoRA 文件小,所以部署一定简单。"
动态加载多份 LoRA 需要框架支持,显存与调度都要规划。
简单的是"训练成本",不是"部署复杂度"。
这两件事经常被混为一谈:训练侧确实简单了,但部署侧引入了新的问题(多适配器管理、路由、显存规划)。
误区五:"LoRA 是万能的,什么定制需求都能做。"
回到第六部分:灌知识不行、改推理上限不行、改结构不行。
先判断你的需求落在哪一类,再决定用不用 LoRA。
误区六:"微调一次就能一直用。"
微调产物也需要维护:
- 基座模型升级时,LoRA 需要重新训练(因为它是相对基座的增量);
- 业务规则变化时,数据要更新;
- 发现新的失败模式时,要补充样本。
微调不是一次性项目,而是一条持续维护的链路。
实战问答
Q1:面试问"LoRA 的原理",怎么答得不只是背公式?
A:三步走。
先说假设:
微调带来的权重变化具有低秩特性------虽然差值矩阵和原权重一样大,但有效信息维度只有几十到几百。因此可以用小矩阵逼近。
再说做法:
冻结原权重,旁路挂两个小矩阵相乘(一个 r×d、一个 d×r),只训这两个。参数量可以从 1677 万降到 6.5 万。
最后说取舍:
省显存、可插拔、不破坏基座能力;但对"灌新知识"和"改变推理上限"无能为力。
最后这句边界往往最能体现理解深度 ------因为它说明你知道这个方法的适用前提是什么。
Q2:LoRA 训练损失降不下去,怎么办?
A:按这个顺序排查,从最常见到最少见。
| 顺序 | 检查项 | 为什么排这个位置 |
|---|---|---|
| ① | 数据格式是否与推理时一致 | 最常见,先查 |
| ② | 目标模块是否太窄 | 只挂了查询投影,可以加上键、值与前馈层 |
| ③ | 学习率是否过低 | 沿用了全参微调的经验值 |
| ④ | 秩是否太小 | 试 32 或 64 |
| ⑤ | 任务是否根本不适合微调 | 需要知识注入而非表达调整 |
注意我把"数据格式"排在第一 ------因为从实践经验看,它是最容易被忽略又最常见的原因。先花十分钟检查它,能省掉几天瞎调参。
Q3:怎么判断 LoRA 训过头了?
A:三个信号,出现任意一个就该停止或减少训练量。
① 损失曲线分叉:训练损失持续下降,但验证损失开始上升。
② 输出模板化 :任何输入都回同一个套路。这是过拟合最直观的表现。
③ 原有能力下降:通用任务的回答变差。
第三个信号需要你主动去测 ------如果不做回归评测,你不会发现它,直到线上用户投诉。
Q4:能不能同时加载多个 LoRA,让模型同时具备多种能力?
A :技术上可以,但效果取决于两个 LoRA 是否冲突。
| 情况 | 结果 |
|---|---|
| 训练数据领域相近、目标一致 | 叠加通常可行 |
| 目标相反(一个要求简洁、一个要求详尽) | 会互相抵消 |
更稳妥的做法是分别训练、按请求路由。
为什么推荐路由而不是叠加? 因为路由的行为是可预测的 ------你知道某个请求走了哪个 LoRA;而叠加的行为是混合的、难以归因的,出问题时很难定位。
Q5:LoRA 该合并还是不合并?
A:看三个条件。
| 条件 | 建议 |
|---|---|
| 只有一个任务 | 合并(延迟最优,没有理由不合并) |
| 多任务 + 需要随时切换 | 不合并(动态加载) |
| 多任务 + 延迟极敏感 | 合并后部署多份(用显存换延迟) |
还有一个常被忽略的中间形态 :离线批量场景 。这类场景对延迟不敏感,动态加载最划算------一份基座 + N 个 LoRA,显存占用最小。
Q6:微调数据要多少条才够?
A:没有固定数字,但有几个判断依据。
| 任务复杂度 | 参考量级 |
|---|---|
| 风格模仿、格式统一 | 几百条往往就够 |
| 领域表达习惯 | 一两千条更稳 |
| 需要覆盖多种子场景 | 每个子场景至少几十条 |
比数量更重要的是三件事:
- 格式统一(与推理时一致);
- 覆盖边界情况(异常输入也要有样本);
- 没有相互矛盾的样本(同一输入给出两种不同答案,会直接伤害训练)。
第三条尤其要注意 :如果数据里同一个问题有两种不同风格的答案,模型会学到"随机二选一"。这比数据少更糟。
Q7:怎么判断我应该用 LoRA 还是全参微调?
A:按决策顺序走。
text
第 1 步:先确认需不需要微调(提示词/检索能不能解决)
第 2 步:需要微调 → 判断任务类型
├─ 改表达/格式/风格 → LoRA 通常够用
├─ 灌入大量新知识 → 检索增强,不是微调
└─ 改变能力上限 → 换基座,不是微调
第 3 步:LoRA 够用 → 显存和部署复杂度都更友好,优先它
第 4 步:LoRA 不够 → 才考虑全参微调
注意第 2 步的两个分支都不是"选 LoRA 还是全参",而是"换一条路"------很多人卡在这里,反复调整微调方案,而问题根本不在微调上。
术语表
| 术语 | 含义 |
|---|---|
| LoRA | 冻结基座权重、只训练低秩旁路矩阵的微调方法 |
| 低秩 | 矩阵的有效独立维度远小于表面尺寸的性质 |
| 秩 r | 旁路矩阵的中间维度,控制表达能力上限 |
| alpha | LoRA 输出的缩放系数,常见取值为 r 的两倍 |
| target modules | 指定 LoRA 作用于哪些层(注意力投影、前馈层等) |
| QLoRA | 在量化后的基座上做 LoRA,进一步降低显存门槛 |
| 适配器合并 | 把 LoRA 权重并入基座,推理时无额外开销但不可插拔 |
| 灾难性遗忘 | 学习新任务时原有能力被破坏的现象 |
结语
把这篇压缩成三句话:
第一,LoRA 成立的前提是"微调的权重变化是低秩的"。 既然变化只集中在少数方向上,就没必要去改全部参数------用两个小矩阵逼近那个变化,参数量可以降两个数量级。
第二,它的价值不只是省钱。 可插拔、不破坏基座能力、训练更稳------这三点让它在很多场景下本来就是更优选择,而不只是权宜之计。
第三,它擅长改表达,不擅长灌知识。
先想清楚你的需求是哪一类,再决定用不用它。
而如果只允许带走一条实操建议,那就是:把训练数据的格式,和推理时的输入格式,用同一段代码生成。 微调失败的原因里,这一条占的比例高得惊人------而它的修复成本几乎为零。