LoRA 和 QLoRA 是什么?

LoRA 和 QLoRA:让普通显卡也能微调大模型
你还在为 SFT 微调大模型头疼吗?
上次聊完监督微调(SFT),你是不是已经迫不及待想动手微调自己的模型了?
但打开教程一看——"需要 80GB 显存的 A100"。
再看看自己的显卡——8GB。
说实话,这种差距挺让人绝望的。
但别急着放弃。因为有一种技术,能让你的 8GB 显卡跑起 7B 模型。
它叫 LoRA。
今天我们就来聊聊,它是怎么做到的。
微调大模型,到底难在哪
大模型参数太多了。
7B 规模的模型,光是加载到显存就要 14GB 左右。这还不算完。
微调时显存会被四样东西吃光:模型参数、梯度、优化器状态、中间激活值。
全参数微调,这四样一个都省不了。
拿 LLaMA-7B 举例。7B 参数意味着 7B 个浮点数。一个浮点数 4 字节。
光是模型本身,就要 28GB。
再加上梯度、优化器状态、激活值,轻松突破 80GB。
普通消费级显卡根本扛不住。
这就是为什么很多人想微调大模型,但最后只能干瞪眼。
资源不够,就是不够。
那有没有办法"省着点用"?
有。这就是 LoRA 干的事。
LoRA 的核心原理:低秩假设
LoRA 的全称是 Low-Rank Adaptation。
翻译过来就是"低秩适配"。
它基于一个很聪明的假设:大模型适配新任务时,权重的变化其实不需要那么多信息。
换个说法。
你的模型有 70 亿个参数。全参数微调意味着你要更新这 70 亿个参数。
但 LoRA 说:不用。
你只需要更新其中一小部分。
怎么做到的?
它把权重更新 ΔW,用两个小矩阵的乘积来近似。

看这张图。
W 是原始的冻结权重。你不去动它。
旁边加了两个小矩阵 A 和 B。
训练的时候,只有 A 和 B 在更新。
W 始终保持原样。
最后,把 AB 的结果加回到 W 上,就得到了新的权重。
但这里有个问题:为什么可以这样"偷懒"?
因为研究者发现,权重更新这个过程,本质上是低秩的。
就像一张照片,你可能以为它需要几百万像素才能表达。但其实用几十个主成分就能恢复出 95% 的信息。
神经网络也是这样。
它的权重更新,不需要触及每一个参数。只需要动几个"核心方向"就够了。
这就是低秩假设。
LoRA 到底改了什么:Attention 模块的秘密
LoRA 不是随便往模型里塞两个矩阵就完事了。
它有明确的目标位置:Attention 层。
Attention 有四个权重矩阵:Wq、Wk、Wv、Wo。
LoRA 通常只接在 Wq 和 Wv 上。
实验证明,这两个位置的效果最好。
为什么是 Wq 和 Wv?
Wq 是 Query 投影,Wv 是 Value 投影。它们直接影响模型"看什么"和"用什么信息回答"。
动这两个,相当于给模型换了一双眼睛和一套记忆系统。
效果自然最明显。
来看个数。
LLaMA-7B 有 7B 参数。
加 LoRA 后,可训练参数可以降到 0.1% 以下。
只有几百万个参数在更新。
显存占用直接从 80GB 降到 10GB 左右。
你的 8GB 显卡,终于有希望了。
而且训练完,AB 矩阵可以合并回 W。
合并后,模型和全参数微调的结果一模一样。
推理时不增加任何额外开销。
这就是 LoRA 厉害的地方:参数更新量小了这么多,效果却能和全参数微调打平。
QLoRA:在 LoRA 基础上再加一层压缩术
LoRA 已经够省了。
但有人觉得还能更省。
于是 QLoRA 出现了。
QLoRA = Quantized LoRA。
翻译过来就是"量化版的 LoRA"。
它比 LoRA 多了两步优化:量化和双量化。
先说量化。
普通模型用 FP32 或 FP16 存储。每个数占 4 字节或 2 字节。
QLoRA 用 4-bit NF4 来存。
4-bit 是什么概念?每个数只占 0.5 字节。
相当于把模型体积压缩到原来的 1/4 到 1/8。
但有个问题:4-bit 精度太低,直接计算会出错。
怎么办?
计算时临时反量化回 BF16。
用完再压缩回去。

就像你看压缩包里的文件。
文件本身是压缩状态存的,不占空间。
但你要打开看的时候,必须解压到内存里。
QLoRA 就是这个思路。
存的时候使劲压缩,用的时候临时解压。
再加上双量化技术,压缩率可以更高。
还有分页优化器,解决梯度检查点的内存峰值问题。
综合下来,QLoRA 能在 24GB 显存的显卡上微调 65B 的模型。
65B。
你的 3090 跑个 70B 模型,听起来像做梦,但 QLoRA 让它变成了可能。
显存到底被谁吃掉了:四要素拆解
说了这么多省显存,到底省在哪里?
显存被四样东西占用:模型参数、梯度、优化器状态、激活值。
我们做个对比。

全参数微调:四个都要显存。模型 28GB、梯度 28GB、优化器 112GB(Adam 需要两倍)、激活值若干。总计 150GB 以上。
LoRA:模型参数和激活值不动。只在 AB 矩阵上算梯度和优化器状态。可训练参数少了,显存自然降下来。总计 20-30GB 左右。
QLoRA:模型参数量化压缩,再加 LoRA。显存进一步降低到 10-15GB 左右。
看到区别了吗?
LoRA 省的是梯度、优化器状态这两块。
QLoRA 在此基础上,把模型参数本身也压缩了。
换方案真的能解决显存问题吗
等等。
激活值呢?
三种方案,激活值的显存占用几乎一样。
为什么?
因为前向传播时,每一层的中间结果必须保存下来,等着算反向传播。
这些中间结果就是激活值。
它和模型层数、序列长度、batch size 直接相关。
模型越大、序列越长、batch 越大,激活值越多。
这是无法避免的。
LoRA 和 QLoRA 优化的是"可训练参数"。但激活值是"必须保存的中间结果"。
这是两个完全不同的问题。

打个比方。
显存是工作台。模型参数是原材料,激活值是加工中的半成品。
LoRA 和 QLoRA 减少了原材料的副本和加工图纸的数量。
但半成品没办法省——你正在做的那个零件,总得有个地方放。
所以有时候 OOM(显存溢出)不是因为模型太大,而是因为序列太长、batch 太大。
梯度累积可以临时缓解这个问题。
但这只是把 batch 拆成几个小步,累加梯度而已。本质上没改变激活值的总量。
只是让你在小显卡上"分期付款"。
个人开发者的选择:
个人开发者,用 LoRA。效果不错,配置简单。
超大模型(70B 以上),用 QLoRA。显存压力大,必须量化。
资源充足做研究,可以考虑全参数微调。效果最好,但烧钱。
选对工具,才能少走弯路。
微调只是让模型"听话"。
那怎么让模型回答得更"贴心"呢?
比如,它怎么知道什么样的回答是"好"的?
这就不是 SFT 能解决的了。
下篇我们聊聊 RLHF 和 DPO,看看大模型是怎么学会"揣摩人心"的。
