LoRA / QLoRA:为什么低成本微调可行?
本篇是「小模型训练系列」第 7 篇:从全参微调的成本、LoRA 的低秩增量、rank/alpha/dropout 与 QLoRA 的显存账本出发,解释为什么低成本微调不是少训练一点,而是把可训练能力放在更聪明的位置上。
正在沿着「小模型训练系列」系统学习,希望把概念、工程和真实判断串起来的读者。
上一篇讲 Tokenizer 和样本构造时,我们把训练入口拆到了很细:
文本会被切成 token,聊天会被套进模板,labels 和 loss mask 会决定模型到底在哪些位置学习。
也就是说,模型不是在“读一篇文章”,而是在一格一格地看训练胶片。
现在问题来了:
如果已经知道胶片怎么放进放映机,下一步自然是:
我们到底要不要改整台放映机?
这就是微调要解决的问题。
很多人第一次听到“微调大模型”,脑子里会出现一个很朴素的想象:
原来的模型不够懂我
那我就把它重新训练一下
让它变成我的模型
这句话方向没错,但如果真的按“重新训练整台模型”去做,成本会立刻把人按在地上。
因为一个模型不是一个文档,不是一个配置文件,也不是一个可以随手改几行的脚本。
它更像一座已经建好的城市。
全参微调像是把城市里的每一条路、每一盏灯、每一栋楼都打开施工权限。理论上自由度最大,现实里也最贵、最危险、最容易把原来的秩序弄乱。
LoRA 做的事情更像:
不重修整座城市,只在关键路口加几条可学习的临时匝道。
这些匝道很小,但如果放在对的位置,就能改变车流方向。
📌 本篇聚焦的问题
- 全参微调为什么这么贵
- LoRA 为什么只训练一小部分参数,也能改变模型行为
rank、alpha、dropout到底在调什么- LoRA adapter 应该理解成什么
- QLoRA 为什么能进一步压低显存
- 显存到底花在了哪些地方
- LoRA 适合改什么,不适合改什么
- 小模型微调时,怎样在成本、效果和稳定性之间做选择
这一篇的核心观点是:
低成本微调不是“少训练一点”,而是把可训练能力放在更聪明的位置上。
🏗️ 1. 全参微调:把整栋楼都打开施工权限
先从最直观的方案说起:全参微调。
一个语言模型里面有很多参数。参数可以理解成模型内部的“旋钮”和“线路”。训练时,Loss 会告诉这些参数怎么调整,优化器再一步步更新它们。
全参微调的意思是:
模型里几乎所有参数,都允许被训练更新。
这当然很强。
因为你给了训练过程最大的自由度。模型可以在很多层、很多矩阵、很多路径上重新塑形。
但问题也很明显:
自由度越大,成本越高。
自由度越大,越容易改坏。
自由度越大,越需要大量高质量数据支撑。
像装修房子一样。
如果只是想把书房变成更适合工作的地方,不一定要把承重墙、水电、地暖、屋顶全拆了。
全拆当然能改得彻底,但也会带来三个麻烦:
- 工程量大
- 钱包吃不消
- 改完不一定比原来更稳
模型也是一样。
💸 2. 贵在哪里:不是只有模型本身占显存
很多人会以为,训练时显存主要用来放模型参数。
这只说对了一部分。
训练时,显存通常要装下好几类东西:
| 显存对象 | 可以理解成什么 |
|---|---|
| 模型权重 | 模型本体,所有已经学到的参数 |
| 梯度 | 每个可训练参数该往哪里改 |
| 优化器状态 | 优化器记账本,比如 Adam 的动量和方差 |
| 激活值 | 前向计算中间结果,反向传播要用 |
| batch / 序列 | 输入 token、mask、labels 等 |
所以训练不是“模型多大,就需要多少显存”。
更像是:
模型本体
+ 梯度
+ 优化器账本
+ 中间过程缓存
+ 输入样本
= 训练显存压力
这就是为什么一个模型“能加载”,不等于它“能训练”。
推理时只要让模型往前算;训练时还要记住怎么回头改。
如果把模型比作一支动画制作团队,推理只是播放成片,训练则要把分镜、草稿、涂层、修改记录和导演批注全摊开。
桌子一下就不够用了。
🧠 3. 为什么不一定要改所有参数
既然全参微调这么贵,那能不能只改一小部分?
答案是:很多时候可以。
原因在于,微调经常不是让模型从零学会语言,也不是让模型重新认识世界。
底座模型已经会很多东西:
- 会读句子
- 会接上下文
- 会写代码
- 会回答问题
- 会总结、翻译、推理
- 会遵守一定格式
微调通常想做的是:
让模型更像某个领域
让模型更听某种指令
让模型更稳定输出某种格式
让模型更符合某种语气和边界
这更像“方向调整”,不是“从头造人”。
如果底座已经是一辆能跑的车,微调很多时候不是重新造发动机,而是换轮胎、调悬挂、校准方向盘。
LoRA 的直觉就来自这里:
既然目标只是让模型产生一个有限的方向变化,就不一定要开放所有参数。
🪄 4. LoRA 的核心:给原模型加一条小小的增量路径
模型内部有大量矩阵。
可以把一个矩阵想象成“把输入向量变成输出向量的机器”。Attention 里面有 Q、K、V、O 等投影矩阵,MLP 里面也有各种投影矩阵。
普通微调会直接修改原始矩阵:
W 变成 W'
LoRA 不直接改 W。
它做的是:
原来的 W 冻住
另外训练一个很小的增量 ΔW
最后用 W + ΔW 来工作
可以画成这样:
flowchart LR
A[输入向量 x] --> B[冻结的原始权重 W]
A --> C[可训练的小路径 ΔW]
B --> D[原模型输出]
C --> E[增量修正]
D --> F[合并后的输出]
E --> F
这就是 LoRA 的核心味道:
原模型负责保留大能力,小增量负责学新方向。
像给一条成熟铁路旁边加一个小道岔。主轨道不拆,但列车可以在关键位置稍微改变去向。
🧩 5. “低秩”到底是什么意思
LoRA 的全名里有一个关键词:Low-Rank。
中文常翻译成“低秩”。
这个词听起来像数学课要开始点名了,但其实可以先抓住直觉。
假设一个大矩阵 W 很大:
W: 4096 × 4096
如果直接训练一个完整的 ΔW,它也要这么大:
ΔW: 4096 × 4096
这大约是 1677 万个参数。
LoRA 不直接训练这么大的 ΔW,而是把它拆成两个小矩阵相乘:
ΔW = B × A
比如 rank 设为 8:
A: 8 × 4096
B: 4096 × 8
这样参数量大概是:
8 × 4096 + 4096 × 8 = 65536
从 1677 万,变成 6.5 万左右。
直觉上,这就是:
不让模型在所有方向上乱改
只给它少数几个可学习的调整方向
如果全参微调像给城市所有道路都开施工权限,LoRA 就像只允许几个关键路口新开匝道。
路口选得对,交通就能明显变化。
🎚️ 6. Rank:给模型几支“调音旋钮”
rank 是 LoRA 里最常见的参数之一。
可以把它理解成:
允许 LoRA 学多少条独立的变化方向。
rank 越小,LoRA 越轻,能表达的变化越少。
rank 越大,LoRA 越重,能表达的变化更多,但也更容易过拟合,显存和计算成本也会上升。
大概可以这样理解:
| rank | 像什么 | 可能适合 |
|---|---|---|
| 4 | 几个很小的调音旋钮 | 简单风格、格式适配 |
| 8 | 常见的轻量适配 | 指令、语气、领域小任务 |
| 16 | 更强的表达能力 | 稍复杂领域、更多样本 |
| 32+ | 更大的改造空间 | 数据足够、任务更复杂时再考虑 |
这不是绝对规则。
更重要的是:rank 不是越大越好。
如果数据很少,rank 太大,模型可能不是“学会了能力”,而是“背住了训练集的口音”。
像给一个刚学乐器的人 64 个调音旋钮,他不一定会更会演奏,可能只是更容易把声音调乱。
🔊 7. Alpha:这条 LoRA 路径声音开多大
alpha 常常和 rank 一起出现。
它可以理解成 LoRA 增量的音量旋钮。
LoRA 的输出并不是原封不动加到模型里,而是通常会有一个缩放:
输出 = 原模型输出 + LoRA 增量 × 缩放系数
alpha 越大,LoRA 的影响越强。
alpha 太小,训练出来的 adapter 可能像声音太轻,模型几乎听不到。
alpha 太大,模型可能被 adapter 拉得太猛,原本的能力被扰动。
可以这样想:
rank 决定有几支调音旋钮
alpha 决定这些旋钮拧出来的声音有多大
所以 LoRA 调参不是玄学乱试,而是在调两个东西:
- 变化方向的数量
- 变化信号的强度
🌧️ 8. Dropout:训练时故意让 adapter 少依赖一点自己
LoRA 里有时还会看到 dropout。
它的作用可以理解成:训练时随机让一部分信号暂时失效,避免模型过度依赖某些路径。
像排练一支乐队时,偶尔让某个乐器停一下,看整支队伍还能不能稳住。
如果数据少、任务窄,适当 dropout 可能有帮助。
如果数据质量高、任务明确,dropout 太大又可能让学习变慢。
它不是必须越高越好,也不是必须开。
更准确的说法是:
dropout 是一种防止 adapter 过度记忆训练样本的保险丝。
🧷 9. Adapter:不是外挂知识库,而是行为补丁
LoRA 训练出来的那一小部分参数,常被叫做 adapter。
这个词很容易被误解。
很多人会以为 adapter 像一个小知识库:
把公司资料放进 adapter
模型就记住公司知识
这不准确。
LoRA adapter 更像一个行为补丁:
让模型在某些输入模式下
更倾向于某些输出方式
它可以让模型更像客服、更像代码助手、更像某个领域的写作风格、更稳定地遵守格式。
但它不适合承担所有事实知识更新。
比如:
| 目标 | 更适合什么 |
|---|---|
| 临时查最新价格 | RAG / 检索 |
| 企业文档经常更新 | RAG / 数据库 |
| 固定输出格式 | LoRA / SFT |
| 稳定领域术语和表达习惯 | LoRA |
| 让模型学会某种流程判断 | LoRA + 高质量样本 |
| 一次性很短的风格要求 | Prompt |
这也是后面会专门讲 RAG vs 微调的原因。
知识放在哪里,取决于它变不变、怎么用、要不要被模型内化成行为。
🧮 10. LoRA 为什么真的能省显存
回到显存账本。
全参微调时,几乎所有权重都要参与训练,所以需要:
模型权重
+ 全部参数的梯度
+ 全部参数的优化器状态
+ 激活值
LoRA 冻结原模型,只训练 adapter。
所以很多开销消失了:
原模型权重:需要加载,但不更新
原模型梯度:通常不需要
原模型优化器状态:不需要
LoRA 参数:需要训练
LoRA 梯度:需要
LoRA 优化器状态:需要
表格更清楚:
| 项目 | 全参微调 | LoRA |
|---|---|---|
| 原模型权重 | 需要 | 需要 |
| 原模型梯度 | 需要 | 大多不需要 |
| 原模型优化器状态 | 需要 | 不需要 |
| LoRA adapter | 无 | 需要 |
| adapter 梯度 | 无 | 需要 |
| adapter 优化器状态 | 无 | 需要 |
| 总体成本 | 高 | 低很多 |
这就是 LoRA 的经济学:
贵的模型本体只负责提供能力,便宜的 adapter 负责学习方向。
📦 11. QLoRA:把大模型本体压缩存放,再训练小 adapter
LoRA 已经省了很多。
但原模型本体还是要放进显存。
如果底座模型很大,哪怕不训练它,只是加载它,也会占很多空间。
QLoRA 继续往前走一步:
把冻结的底座模型用更低精度量化存放,再训练 LoRA adapter。
可以这样理解:
LoRA:
底座模型正常放着,不训练
只训练小 adapter
QLoRA:
底座模型压缩放着,不训练
只训练小 adapter
画成流程就是:
flowchart TB
A[预训练底座模型] --> B[4-bit 量化存放]
B --> C[冻结底座权重]
C --> D[前向计算提供能力]
E[LoRA Adapter] --> F[训练更新]
D --> G[模型输出]
F --> G
如果说 LoRA 是“不拆主楼,只加小改造”,那么 QLoRA 就是:
主楼用压缩档案方式存着,施工只发生在小改造模块上。
它让更大的模型有机会在更小的显卡上做微调。
但也要注意:QLoRA 不是免费午餐。
它可能带来:
- 计算过程更复杂
- 训练速度不一定更快
- 量化配置会影响稳定性
- 对实现框架和显卡支持有要求
所以 QLoRA 的关键词不是“更强”,而是“更省显存”。
🧊 12. 量化不是把模型变聪明,而是把模型放小
这里要单独停一下。
很多人会把量化理解成一种神奇优化:
量化了,所以模型更好?
不。
量化主要是把模型用更少的位数表示。
就像一张高清照片压缩成更小文件,存储更省,但细节可能有损失。
在 QLoRA 里,量化的目标是:
让冻结的底座模型更省显存
把训练预算留给 LoRA adapter
它不是让模型凭空更聪明。
如果底座模型本来能力不够,QLoRA 不会把它变成另一个等级的模型。
如果数据质量很差,QLoRA 也不会自动拯救训练目标。
如果样本构造错了,QLoRA 只会更省钱地把错误学进去。
这句话有点冷,但很重要:
QLoRA 降低的是训练门槛,不是降低训练质量要求。
🔧 13. LoRA 通常加在哪里
LoRA 可以加在模型不同位置。
常见位置包括:
- attention 的
q_proj - attention 的
k_proj - attention 的
v_proj - attention 的
o_proj - MLP 的上投影 / 下投影
不是所有位置都必须加。
如果任务只是轻度风格适配,可能加少一点就够。
如果任务涉及更复杂的领域表达和推理组织,可能需要更多位置。
如果数据很少,位置加太多反而容易过拟合。
这像给城市加匝道。
不是匝道越多越好,而是要加在真正影响车流的路口。
训练前可以问三个问题:
- 任务主要改变输出风格,还是改变领域判断?
- 数据量能支撑多大的 adapter 自由度?
- 评估集能不能发现模型被改坏?
第三个问题最容易被忽略。
没有评估集,调 LoRA 就像闭着眼给车改方向盘。
🧯 14. LoRA 常见误区
误区一:LoRA 参数越多越好
不一定。
参数越多,模型越有能力适应训练集,也越有能力记住训练集的噪声。
如果数据很小、格式单一、答案重复,rank 太高可能会让模型变成“训练集复读机”。
误区二:训练 Loss 下降就代表成功
不一定。
Loss 下降只说明模型更像训练目标。
如果训练目标有问题,Loss 下降就是模型更像错误目标。
这就是为什么下一篇必须讲评估体系。
误区三:QLoRA 一定比 LoRA 更好
QLoRA 更主要的价值是省显存。
如果显存足够,普通 LoRA 可能更简单、更稳定。
如果显存紧张,QLoRA 才特别有意义。
误区四:LoRA 可以替代 RAG
不完全可以。
LoRA 适合把稳定能力写进模型行为里。
RAG 适合处理经常变化、需要可追溯来源的知识。
把每天变化的资料硬塞进 LoRA,通常不是好主意。
误区五:一个 adapter 可以解决所有任务
不一定。
不同任务可能需要不同 adapter。客服、代码、写作、医疗问答、法律问答,本质目标不同。
一个 adapter 太贪心,最后可能什么都沾一点,什么都不够稳。
🧭 15. Prompt、RAG、LoRA、全参微调怎么选
这张表可以先当地图。
| 目标 | 优先考虑 |
|---|---|
| 临时改变语气 | Prompt |
| 固定输出格式 | Prompt / LoRA |
| 接入大量外部文档 | RAG |
| 文档经常更新 | RAG |
| 稳定领域表达习惯 | LoRA |
| 让模型学会固定流程判断 | LoRA + 高质量样本 |
| 大规模改变模型能力边界 | 全参微调 |
| 显存很紧但想训练较大底座 | QLoRA |
一个比较稳的思路是:
能用 Prompt 解决,就先别训练。
知识经常变,就优先检索。
行为要稳定内化,再考虑 LoRA。
LoRA 不够,再考虑更重的训练方案。
这不是保守,而是工程上最可靠的顺序。
训练是有成本的,错误训练也会留下痕迹。
🧪 16. 一个小模型微调的实际流程
如果要真的做一次 LoRA / QLoRA 微调,流程可以这样拆:
flowchart TB
A[选择底座模型] --> B[准备训练样本]
B --> C[统一 chat template]
C --> D[设置 LoRA target modules]
D --> E[选择 rank / alpha / dropout]
E --> F[训练 adapter]
F --> G[验证集与回归测试]
G --> H[合并或加载 adapter 推理]
H --> I[上线后收集反馈]
每一步都有一个关键问题:
| 步骤 | 要问的问题 |
|---|---|
| 选底座模型 | 它原本能力够不够 |
| 准备样本 | 数据是不是在教正确行为 |
| chat template | 训练和推理格式是否一致 |
| target modules | LoRA 加在了哪些关键位置 |
| rank / alpha | 自由度和影响力是否过大或过小 |
| 训练 | Loss 是否正常,但不过度迷信 |
| 验证 | 模型是否真的完成任务 |
| 推理 | adapter 是否正确加载或合并 |
| 反馈 | 上线后有没有退化和边界问题 |
这里最重要的不是某个参数,而是闭环。
没有闭环,微调只是一次漂亮的实验。
有闭环,微调才会变成可复用的能力工程。
📉 17. 显存账本:哪里最容易爆
小模型训练里,显存常常不是被一个东西吃掉,而是被几类东西一起挤爆。
常见压力来自:
- 底座模型大小
- 序列长度
- batch size
- optimizer 状态
- 是否启用梯度检查点
- LoRA rank 和 target modules 数量
- 精度设置
很多人一看到 OOM,就只想换显卡。
但在工程上,先看这几个旋钮:
| 旋钮 | 降显存效果 | 代价 |
|---|---|---|
| 降 batch size | 明显 | 训练更慢,梯度更抖 |
| 降 seq_len | 明显 | 长样本学不到 |
| 开 gradient checkpointing | 明显 | 计算变慢 |
| 用 LoRA | 明显 | 表达能力受 adapter 限制 |
| 用 QLoRA | 更省底座显存 | 实现更复杂,速度未必更快 |
| 降 rank | 有帮助 | adapter 能力变弱 |
| 少加 target modules | 有帮助 | 适配能力可能不足 |
所以显存优化不是一个开关,而是一张账本。
每省一块地方,都要问:
它牺牲了什么?
🧠 18. 小模型微调的判断:先别贪大
对个人学习和小团队项目来说,LoRA / QLoRA 最好的地方不是“便宜”两个字。
而是它让训练变得可试错。
全参微调太重,失败一次成本很高。
LoRA 更轻,失败了也更容易回滚。
adapter 可以保存、对比、替换、合并,也可以为不同任务保留不同版本。
这让微调从“一次豪赌”变成“多轮迭代”。
对小模型来说,这非常关键。
因为一开始很难一次就知道:
- 数据该怎么配比
- rank 该设多少
- 哪些模块该加 LoRA
- 模型会不会过拟合
- 哪些能力会退化
轻量微调的意义,就是让这些问题能被逐步回答。
🧾 19. 训练前先做这张决策表
真的开训前,可以先填这张表。
| 问题 | 如果答案不清楚,会有什么风险 |
|---|---|
| 底座模型为什么选它 | 训练成本花在错误底座上 |
| 目标能力是什么 | adapter 学成一团模糊风格 |
| 数据量和质量够不够 | rank 越大越容易过拟合 |
| prompt 是否已经能解决 | 白白训练 |
| 知识是否经常更新 | 应该用 RAG 却硬塞微调 |
| 评估集准备好了吗 | Loss 下降但不知道有没有变好 |
| 上线怎么回滚 | adapter 出问题无法快速撤回 |
这张表比很多训练参数更重要。
因为参数只决定怎么训练,决策表决定该不该训练。
🌱 20. 这一篇的核心结论
- 全参微调自由度最大,但成本高、风险也高
- 训练显存不只放模型,还要放梯度、优化器状态和中间激活
- 很多微调目标只是方向调整,不需要修改所有参数
- LoRA 冻结原模型,只训练一个低秩增量路径
rank决定可学习变化方向的数量alpha决定 LoRA 增量影响原模型的强度dropout是防止 adapter 过度记忆样本的保险丝- QLoRA 把冻结底座量化存放,再训练 LoRA adapter
- 量化主要是省显存,不是让模型自动变聪明
- LoRA 适合稳定行为适配,不适合替代所有知识检索
如果把前几篇串起来,现在训练链路已经更完整了:
flowchart LR
A[数据决定方向] --> B[Tokenizer 切分世界]
B --> C[样本构造定义目标]
C --> D[LoRA / QLoRA 决定怎么低成本改模型]
D --> E[评估体系判断是否真的变好]
低成本微调真正厉害的地方,不是“便宜地训练一个模型”。
而是:
在不破坏底座能力的前提下,用很小的可训练空间,把模型推向一个更明确的任务方向。
这也是为什么 LoRA / QLoRA 会成为个人和小团队训练小模型时最重要的工程入口之一。
它让模型训练从昂贵的重工业,变成可以反复打磨的手艺活。
📌 下一篇预告
《评估体系:模型到底有没有真的变好?》
下一篇会讲一个更现实的问题:
训练跑完了,Loss 下降了,样例看起来也不错。
但是模型真的变好了吗?
会拆开这些问题:
- training loss 和 validation loss 分别说明什么
- 为什么 Loss 下降不等于用户体验变好
- 如何设计任务评估集和回归集
- 人工验收应该看哪些维度
- 为什么要保留“训练前模型”的对照组
- 小模型上线前,怎样判断能不能用
核心观点是:模型有没有变好,不由训练日志决定,而由任务闭环决定。
🗺️ 后续章节路线
| 顺序 | 章节方向 | 要解决的问题 |
|---|---|---|
| 第 8 篇 | 评估体系:模型到底有没有真的变好? | training loss、validation loss、任务评估、回归集与人工验收 |
| 第 9 篇 | RAG vs 微调:知识应该放进检索,还是写进参数? | 知识更新频率、事实性、成本、召回边界与能力边界 |
| 第 10 篇 | 推理与部署:模型能力如何稳定跑起来? | 量化、KV Cache、吞吐、并发、延迟与部署成本 |
| 第 11 篇 | 工具调用与 Agent:模型如何从会说话变成会做事? | Function Calling、工具参数、权限边界、日志与人工确认 |
| 第 12 篇 | Memory 与工作流:模型如何承接长期任务? | 对话记忆、任务状态、上下文压缩、恢复现场与流程编排 |
| 第 13 篇 | 行业模型与业务闭环:训练如何进入真实场景? | 领域数据、流程反馈、人工审核、持续评估与灰度改进 |
| 第 14 篇 | 系列总结:从数据到 Agent 的小模型工程地图 | 把训练、微调、评估、RAG、部署和 Agent 串成完整路线 |
从这一篇开始,系列进入真正的工程选择层:
不是只问“能不能训练”,而是问“用什么成本、改哪些部分、如何证明真的变好”。
持续学习,持续记录,持续筛选
如果这篇对你有用,可以继续沿着主题读下去。
福星家和会长期记录 AI 技术、生活成长、真实好物与正向文化观察。产品体验、人物故事、教育内容、生活方式、文化作品和内容共创都欢迎邮件沟通,合作内容会清楚标注。