QLoRA
QLoRA 在以 4 位 NF4 格式存储的冻结基座模型上训练 LoRA 适配器来微调 LLM,因此可在单个 48 GB GPU 上容纳一个 65B 参数模型。
QLoRA(量化低秩适配)是一种微调方法:它将预训练的大型语言模型(LLM)冻结在 4 位精度,并在其上训练小型 LoRA适配器;反向传播会穿过量化权重,传递到适配器。Dettmers 等人在 2023 年的 QLoRA 论文中首次提出该方法,它将微调一个 650 亿参数模型所需的 GPU 内存从 780 GB 以上降至 48 GB 以下,足以在一块工作站 GPU 上完成,同时达到与 16 位微调相当的任务性能。论文中的 Guanaco 模型采用这种方式微调,在单块 GPU 上训练 24 小时后,在 Vicuna 基准上的得分达到 ChatGPT 的 99.3%。
QLoRA 的工作原理#
QLoRA 将冻结的压缩基础模型与三种节省内存的技术结合起来:
- 4 位 NormalFloat(NF4):每个权重存储为一个 4 位编码,指向正态分布分位数上的 16 个级别之一;每 64 个权重构成一个区块,并使用一个绝对最大值缩放因子。预训练模型权重大致呈正态分布,因此基于分位数的级别能在大多数权重所在的零附近提供更高分辨率。
- 二次量化:每个区块的缩放因子本身会在每 256 个缩放因子组成的区块中量化为 8 位,从而将其开销从每个参数 0.5 位降至约 0.127 位;对于 65B 模型,约可节省 3 GB。
- 分页优化器:优化器状态存储在 NVIDIA 统一内存中,并会在内存峰值期间(例如使用梯度检查点处理长序列时)移至 CPU RAM,因此训练不会因内存溢出错误而崩溃。
在前向传播中,每个被冻结的层都会在矩阵乘法之前将 NF4 权重反量化为 bfloat16,并加上 LoRA 路径的输出。反向传播会通过反量化后的权重计算梯度,但只更新两个低秩 LoRA 矩阵。论文发现,要达到与完整微调相当的效果,必须在Transformer 块的每个线性层上都添加适配器:
下面的代码片段按照 bitsandbytes 库的方式构建 NF4 级别,并将其与正态分布权重区块上的 16 个等间距级别进行比较:
import torch
torch.manual_seed(0)
normal = torch.distributions.Normal(0.0, 1.0)
# NF4: 16 levels at quantiles of a normal distribution, with an exact zero (as built in bitsandbytes)
positive = normal.icdf(torch.linspace(0.9677083, 0.5, 9)[:-1])
negative = -normal.icdf(torch.linspace(0.9677083, 0.5, 8)[:-1])
nf4 = torch.cat([negative, torch.zeros(1), positive]).sort().values
nf4 = nf4 / nf4.max()
int4 = torch.linspace(-1, 1, 16) # evenly spaced 4-bit levels for comparison
weights = torch.randn(4096, 64) * 0.02 # pretrained weights are roughly normal, in blocks of 64
absmax = weights.abs().amax(dim=1, keepdim=True) # one scale per block
for name, levels in (("NF4", nf4), ("INT4", int4)):
codes = ((weights / absmax)[..., None] - levels).abs().argmin(dim=-1) # 4-bit index per weight
restored = levels[codes] * absmax
print(f"{name} mean squared error: {(restored - weights).pow(2).mean():.3e}")输出结果显示,NF4 的均方误差为 3.395e-06,等间距级别则为 4.053e-06:在级别数和区块缩放因子相同的情况下,对于正态分布权重,NF4 的分位数间距将重构误差降低了约 16%。实际使用时,Hugging Face Transformers通过 bitsandbytes以 NF4 加载模型,并使用 BitsAndBytesConfig 来设置 load_in_4bit=True、bnb_4bit_quant_type="nf4"、bnb_4bit_use_double_quant=True 和 bnb_4bit_compute_dtype=torch.bfloat16;然后由 PEFT 库添加 LoRA 适配器。
QLoRA 与相关方法的区别#
- LoRA:在保持基础模型为 16 位精度的情况下训练同类适配器。仅一个 65B 模型的 16 位权重就占用约 130 GB,而 NF4 可将其压缩到约四分之一。
- 参数高效微调(PEFT):训练少量参数的一类方法,包括 LoRA、QLoRA、适配器和提示调优。
- 训练后模型量化:压缩已完成训练、用于推理的模型。使用 QLoRA 调优的模型通常会先合并到 16 位权重中,然后再次量化以供服务使用。
- 量化感知训练(QAT):在前向传播中模拟低精度舍入,同时训练所有权重,使部署后的模型在低精度下仍能保持准确率。使用
quantize=8训练时,Ultralytics YOLO26 会运行 INT8 QAT,并通过 NVIDIA ModelOpt 在卷积层和线性层中插入伪量化,使学到的权重能够经受 INT8 导出。
权衡#
QLoRA 用速度换取内存:每次前向和反向传播都要对冻结权重进行反量化,因此训练步骤比使用 16 位 LoRA 更慢。适配器足够小,可以按任务存储和切换,但要将其合并到基础模型中,必须先将基础模型反量化到16 位。对于 65B 模型,4 位权重和二次量化缩放因子约占 34 GB,因此在 48 GB GPU 上,其余内存可用于适配器、激活值和分页优化器状态。










