1. 啥是 QLoRA
QLoRA(Quantized Low-Rank Adapters)是一种参数高效微调方法,先把预训练大模型的基座权重以 4-bit 量化后冻结不更新,然后在关键线性层上插入并只训练 LoRA 的低秩适配器,让梯度穿过量化的冻结模型但参数更新仅发生在少量 LoRA 权重上,从而把显存占用压到足以在单张 GPU 上微调(论文展示了 65B 模型可在 48GB GPU 上微调),同时尽量保持接近 16-bit 全量微调的效果。其关键工程点包括 NF4 4-bit 量化格式、double quantization 进一步压缩量化常量,以及 paged optimizers 用来缓解训练时的显存峰值。
在提出 QLoRA 的时候,模型量化 + LoRA 并不是没有人做,个人认为 QLoRA 的提出更大的贡献不是提出了一个全新的 LoRA 方法,而是提出了一种更适配 LoRA 的量化方式,也就是 NF4。
(关于 LoRA 可以看上一篇笔记。)
2. 简单说下模型量化
模型量化就是把模型里的数值表示从高精度(FP32/FP16)换成更低比特(常见 INT8、INT4,或 4-bit 浮点/码本等),以减少模型体积与显存占用、降低内存带宽压力,并在支持低精度算力的硬件上提升推理速度。
2. NF4
NF4(NormalFloat 4-bit)是 QLoRA 里用来做 4-bit 权重量化的一种非均匀量化数据类型,预训练模型的权重分布大多近似零均值的正态分布,所以量化时应该把更多的表示能力分配到 附近,而不是像 int4 那样等间隔。
先用标准正态分布 的分位数预先算出 (,所以 个)代表值,再把这些代表值归一化到 ,实际量化某个权重块时,用该块的绝对最大值做 rescale(把权重映射到 ),然后把每个权重映射到最接近的 NF4 码本值并用 4-bit 存索引。
用人话说就是,可以把 NF4 理解成一种更聪明的 4-bit 记数方式,专门用来存大模型的权重,让 位( 个档位)尽量少损失信息。
普通的 4-bit,比如 int4 更像尺子刻度,是等间隔的,不管权重值常不常出现,都平均分 个格子。NF4 的出发点是大模型权重大多集中在 附近,所以 附近应该刻得更密,远离 可以刻得更稀,把宝贵的 个格子优先用在最常出现的范围,从而同样 4-bit 下误差更小、效果更稳。QLoRA 把 NF4 描述为对“近似正态分布的权重”更合适的 4-bit 数据类型,并作为其 4-bit 微调效果接近 16-bit 的关键点之一。
假设某一小块权重里有这些值:
如果用等间隔 int4 ,想象:区间 平均分 档,步长大约是 。那 会被四舍五入到 或 , 会到 或 。也就是说, 附近一堆很小的数,会被同一个粗刻度糊在一起,误差相对更明显。
如果用 NF4 风格的非均匀刻度,想象:把更多档位挤在 附近,这样 可以精确落在 档, 可能落在 或 档,误差就更小,而像 这种大值,本来就少见,刻度稀一点对整体影响没那么大。
3. 双重量化(Double Quantization)
4-bit 量化本身还需要存每个 block 的量化尺度/常量,例如 scale、absmax 一类的元数据。QLoRA 进一步把这些“量化常量”再量化一遍,从而继续压缩显存占用。
4. 分页优化器(Paged Optimizers)
训练时显存会出现峰值,尤其是优化器状态、梯度等瞬时占用导致 OOM。QLoRA 引入 paged optimizer 的思路,用“分页/统一内存”的方式把一部分优化器状态在需要时换入/换出,降低显存峰值带来的崩溃风险。
使用 NVIDIA 统一内存特性,在CPU和GPU之间进行页传输。当GPU内存不足时,将部分状态转移到CPU RAM 中,并在优化器更新步骤需要内存时分页回到GPU内存中。
5. QLoRA 训练方式本身
QLoRA 的训练方式和普通 LoRA 在核心逻辑上是一样的,都把基座模型冻结,只训练插进去的低秩适配器(LoRA 的 A、B 矩阵),梯度会穿过基座模型的前向计算,但参数更新只发生在 LoRA 上。
但 QLoRA 是在量化后的模型上做微调,所以做前向(矩阵乘)时通常会把 4-bit 权重在算子内部解码/反量化到 BF16/FP16 去算,然后再继续反传到 LoRA 参数。
从参数形式上看,QLoRA 训练出来的 A、B 矩阵和普通 LoRA 是同一种东西,通常仍然以 BF16/FP16 保存;QLoRA 只是把基座模型的线性层权重量化成 4-bit(比如 NF4)来省显存,训练时只更新适配器。