作者以模式(列向量乘行向量)为核心概念,讲解低秩近似、LoRA 与 4 位量化的原理:LoRA 冻结原有权重只训练新增的 r 个模式,量化用 NF4 分块缩放压缩存储。文中回顾华盛顿大学 QLoRA 在单块 48GB GPU 上微调 65B 模型的结果,并估算 7B 模型全参数微调约 100 GB、LoRA 约 15 GB、QLoRA 约 5 GB 显存开销。
https://x.com/i/article/2113912265061593088
【X 长文】LoRA 与 QLoRA:用一块 GPU 微调大模型
2023 年,华盛顿大学的研究团队发表了 QLoRA。他们在一块 48GB 显存的 GPU 上微调了一个 650 亿参数(65B)的模型,并报告这种方法保持了 16 位全参数微调的任务表现。
用这种方法训练的 Guanaco 模型,在一块 GPU 上训练了 24 小时,在 Vicuna 评测中达到了 ChatGPT 表现的 99.3%。
作为对比,按常规方式做全参数微调,每个参数大约需要 16 字节显存,65B 模型约需 1 TB,还不包括激活值。
QLoRA 由两部分组成:LoRA 减少需要训练的参数数量,4 位量化 减少每个参数占用的位数。理解这两部分,需要三个基本概念:
rank(秩)、low-rank approximation(低秩近似)和 quantization(量化)。它们可以用同一个概念串起来,即矩阵中的 pattern(模式)。
这是后训练系列的第三篇。上一篇介绍了指令微调,本篇讨论如何降低微调的显存开销。
全参数微调的显存开销
全参数微调会更新模型的全部参数。训练时,显存中除了参数本身,还要存放每个参数的梯度,以及 Adam 优化器为每个参数维护的两组状态。以 32 位浮点数计算,每个参数约 16 字节:7B 模型约需 100 GB 以上,65B 模型约需 1 TB。前向计算产生的激活值还要另算。
降低显存有两个方向:减少需要训练的参数数量,这是 LoRA 的做法;减少每个参数占用的位数,这是量化的做法。
模式(pattern):一列乘一行
本文的核心概念是模式(pattern)。一个模式是一个列向量乘以一个行向量得到的矩阵,也称为秩为 1 的矩阵。矩阵中的每个元素等于对应的列元素和行元素的乘积,因此每一行都是同一个行向量按不同比例缩放的结果。
举一个销售表的例子。三家门店连续四天的销售额,每家店每天的销售节奏相同,都是 [1, 2, 2, 1],
只是门店规模不同,分别为 10、20、30。
整张 3×4 的表就是列向量 [10, 20, 30] 乘以行向量 [1, 2, 2, 1]:
12 个数只由 3 + 4 = 7 个数决定,这张表的秩为 1。
再加入一次促销:第 3、4 天额外增加 [0, 0, 5, 5],
三家店的参与程度分别为 [1, 0, 2]。
新的销售表是两个模式相加,各行不再是同一个行向量的倍数,秩为 2。
矩阵可以看作若干模式相加的结果,秩就是其中相互独立的模式的个数。
从计算的角度看,模式 u vᵀ 作用在输入向量 x 上时,先计算 x 在方向 v 上的分量,再把这个数值写到方向 u 上。一个模式读取一个输入方向,写入一个输出方向;秩为 r 的矩阵包含 r 组这样的读写方向。
近似:保留最强的模式
真实的矩阵通常由许多模式组成,强弱不一。
奇异值分解(SVD) 可以把任意矩阵分解成一组模式之和,并按强度从大到小排列,各个模式之间相互正交、没有重叠。只保留最强的 r 个模式,得到的就是所有秩为 r 的矩阵中误差最小的近似。
以一张灰度照片为例:只保留 1 个模式,只能得到横竖交错的条纹;保留 20 个,已经能认出人脸;保留 50 个,和原图相当接近。少数强模式决定整体结构,大量弱模式只贡献细节。
低秩近似 就是只用 r 个模式表示一个大矩阵,写成一个细高矩阵乘以一个扁宽矩阵。一个 d×d 的矩阵需要 d² 个数;用秩 r 表示时,只需要 2dr 个数。d = 4096、r = 8 时,数的个数从约 1,680 万减少到 65,536,约为原来的 0.4%。
LoRA:在冻结的权重上增加少量模式
LoRA(Low-Rank Adaptation,低秩适配) 保留原有权重 W 不变,把它冻结,再在旁边加上一个由 r 个模式组成的更新:
W′ = W + B·A,其中 B 是 d×r 的矩阵,A 是 r×d 的矩阵
B·A 正好是 r 个模式之和:B 的第 i 列乘以 A 的第 i 行。训练时只更新 A 和 B,W 保持不变。
为什么不直接对 W 做低秩近似?预训练得到的权重矩阵把强度分散在数百个模式上,只保留少数几个会丢失大部分信息。LoRA 的前提是:适配一个新任务时,权重的变化量主要集中在少数几个模式上。LoRA 论文对适配过程中权重更新的低秩性做了实证分析,支持了这一前提。
LoRA 保留原有权重的全部模式,只学习少量新的模式,把它们加到冻结的权重上。
训练从 B = 0、A 为随机值开始。此时 B·A = 0,模型的输出和原模型完全相同;由于 A 不为零,B 在第一步就能得到梯度,训练随即展开。训练过程通过梯度下降,寻找能够降低任务损失的 r 组读写方向。
训练完成后,B·A 和 W 形状相同,可以直接加到 W 上,推理时没有额外开销;也可以为不同任务保存不同的 A、B,按需切换。LoRA 论文报告,在 GPT-3 175B 上,与用 Adam 做全参数微调相比,可训练参数减少到约万分之一,GPU 显存需求降为约三分之一。
LoRA 也有限制。秩 r 限制了更新的表达能力,需要超过 r 个模式才能表示的变化无法完整学到。另外,它减少的是需要训练的状态,原有权重仍以 16 位存放在显存中:65B 模型的权重本身就需要约 130 GB。
量化:用更少的位数存储每个数
量化减少的是每个数占用的位数,数的个数保持不变。
位数和取值。 一个 b 位的数有 2ᵇ 种取值。bf16 使用 16 位,有 65,536 种取值;4 位只有 16 种取值。4 位量化就是让每个权重从 16 个预设的级别中选择一个,只存储级别的编号 0 到 15。
分块缩放。 16 个级别分布在 −1 到 1 之间,而真实的权重通常很小,大约在 ±0.05 的范围内。因此把权重每 64 个分成一块,每块记录一个缩放因子,即块中绝对值最大的权重。存储时,先把权重除以缩放因子,再取最近的级别,只保存编号;使用时,用级别乘以缩放因子还原。分块之后,一个异常大的权重只影响它所在的那一块。
NF4。 QLoRA 使用的 4 位格式称为 NF4(4-bit NormalFloat)。训练好的权重大致呈钟形分布,NF4 按正态分布的分位数放置 16 个级别:靠近 0 处密集,两端稀疏,并且包含精确的 0。大多数权重落在 0 附近,因此舍入误差更小。
存储和计算。 每个权重占 4 位,再加上每 64 个权重共享的缩放因子。缩放因子本身再量化为 8 位,称为 double quantization(双重量化),之后平均每个权重约占 4.13 位,约为 bf16 的四分之一。计算时,每一块权重在参与矩阵乘法之前临时还原成 bf16,用完即丢弃:存储使用 4 位,计算仍使用 16 位。
用模式比较低秩和量化
低秩近似和量化都会带来误差,但误差的结构不同:
- 低秩近似删除较弱的模式,保留下来的模式完全准确。
- 量化保留全部模式,每个模式的强度只有很小的变化,同时加入一层分散在所有方向上的细小误差,形态接近随机噪声。
低秩近似减少矩阵中模式的个数,量化减少每个数的位数。
这也解释了一个看似矛盾的现象:预训练权重很难做低秩压缩,因为它的模式很多,删去弱模式会损失大量信息;它却可以较好地量化,因为全部模式都被保留,只增加了少量噪声。
QLoRA:低秩和量化各用在合适的位置
QLoRA 把两种方法结合起来:
- 冻结的原有权重 W 用 4 位 NF4 存储,不参与更新。它保留了全部模式,只是精度降低。
- LoRA 的 A 和 B 用 16 位存储并参与训练,学习少量新的模式。
- 前向计算时,W 的每一块临时还原为 bf16,和输入相乘,再加上 LoRA 分支的输出。反向传播时,梯度经过还原后的 W 传到 A 和 B,W 本身不更新。
- paged optimizers(分页优化器) 在显存出现峰值时,把优化器状态临时移到 CPU 内存,避免显存不足。
按字节数粗略估算一个 7B 模型,只计权重、梯度和优化器状态,不含激活值:全参数微调约 100 GB,LoRA 约 15 GB,QLoRA 约 5 GB。
QLoRA 的代价:每次计算都要先还原权重,训练速度比 16 位的 LoRA 慢;量化带来少量误差,量化后的模型起点略差,需要由 LoRA 在训练中弥补;训练好的 A、B 不能直接加到 4 位权重上,需要合并到还原后的 16 位权重,或者单独保存。
小结
- 模式是一列乘一行得到的矩阵。矩阵是若干模式之和,秩是其中独立模式的个数。
- SVD 按强度找出矩阵的模式,保留最强的 r 个,得到误差最小的低秩近似。
- LoRA 冻结原有权重,只训练 r 个新增模式 B·A。依据是任务适配所需的变化集中在少数模式上。
- 量化保留全部模式,用更少的位数存储每个数:分块缩放、NF4 级别,计算时还原为 16 位。
- QLoRA 用 4 位存储冻结的权重,用 16 位训练 LoRA 的模式,使一块 GPU 就能微调很大的模型。
下一篇:偏好对齐,模型如何从回答之间的比较中学习。
来源:Dongxi 东锡 NLP · x.com