QLoRA原理
QLoRA是一个使用量化思想对LoRA进行优化的量化算法,可以显著的降低训练大模型时所需要的显存资源。QLoRA的优化有三个核心要点:首先是定义了一种4位标准浮点数(Normal Float 4-bit,NF4)量化,基于分块的分位数量化的量化策略;其次是双重量化,包含对普通参数的一次量化和对量化常数的再一次量化,可以进一步减小缓存占用;最后是分页优化器(Page Optimizer),用来在显存过高时用一部分内存代替显存。
1 背景知识
模型量化和大模型的PEFT(Parameter-Efficient Fine-Tuning)有一个共同点是它们都希望模型计算能够更快。
1.1 模型量化
量化的本质是函数映射,根据量化过程是否线性我们可以把量化分为线性量化和非线性量化。量化过程是从一种数据类型“舍入”到另一种数据类型。举个例子,如果一种数据类型的范围为 0..9
,而另一种数据类型的范围为 0..4
,则第一种数据类型中的值 4
将舍入为第二种数据类型中的 2
。但是,如果在第一种数据类型中有值 3
,它介于第二种数据类型的 1
和 2
之间,那么我们通常会四舍五入为 2
。也就是说,第一种数据类型的值 4
和 3
在第二种数据类型中具有相同的值 2
。这充分表明量化是一个有噪过程,会导致信息丢失,是一种有损压缩。
1.2 分位数量化
一种基于数据分布的量化方法,通过将数据按其分布的分位点划分成若干区间,并将每个区间的值映射为一个固定的量化值。这种方法特别适用于处理非均匀分布的数据,可以显著提高量化的表示能力和模型性能。分位数(Quantile)在数学上的定义指的是把顺序排列的一组数据分割为若干个相等块的分割点的数值。
1.3 分块k位量化
分块 k-位量化(Blockwise kkk-bit Quantization) 是一种针对大规模深度学习模型优化的量化方法,通过将模型参数或激活值划分为多个小块,对每个块分别进行 kkk-位量化,从而在保持模型性能的同时大幅减少内存占用和计算复杂度。
1.4 LoRA
LoRA是一个基于适配器学习的PEFT算法。它指出模型往往是过参数化话,因此可以用两个低秩矩阵代替原来的密集连接,从而可以减少模型的参数量。另外LoRA的适配器是一个和原模型的网络块并行的结构,在推理时计算的是已经将适配器的参数加到原模型参数上的新参数,因此不会带来任何的推理时间的增加。
2 QLoRA
QLoRA的工作有三个,第一个工作是结合了分位数量化和分块量化的4位标准浮点数量化(4-bit NormalFloat Quantization)。第二个工作是对模型进行两次量化的双重量化(Double Quantization),它的第二次量化只作用在第一次量化产生的量化常数上,可以进一步节约显存占用。第三个工作是分页优化(Paged Optimizer),使用CPU内存代替GPU显存保存部分梯度参数。
使用上面介绍的分位数量化方法我们可以将FP2精度量化到4bit的精度,但是直接这么用的一个问题是不能保证高精度的0一定被映射到低精度的0。为了确保零点映射到0并且使用4位数据类型的全部16位,我们通过估计正负两个范围的分位数来创建一个非对称的数据类型:负数部分映射其中7位,正数部分映射8位,0占据1位,总共用满了4位数的16位。另外我们也可以使用对称的量化,其中正数和负数均使用7位,0占用2个位。我这里和论文介绍的略有不同,论文说的是正数部分取9个值,负数部分取8个值,不过它们都会取到0,所以合并时再去掉一个重复的0,这两个说法其实是一样的,只是实现方式略有差异。
当我们保存模型时我们不仅要保存量化后的结果,还要保存每个块的量化常数。虽然量化后的参数只有4bit的精度,但是这个量化常量的精度是float32。在QLoRA中,每个块的大小是64,因为块中的每个值占4比特。这相当于为了存储量化常数,模型要额外占用显存。QLoRA的双重量化就是对这个量化常数再做一次8bit的量化,在进行量化常数的量化时,QLoRA以每256个量化常数为一组再做一次量化。因此它额外增加的内存消耗有两部分组成,一部分是量化后的8bit的第一层的量化常数,第二部分是为量化常数做量化的第二层的32bit的量化常数。因为使用了双重量化,在进行反量化时我们也需要进行两次反量化才能把量化后的值还原。
分页优化是针对梯度检查点做的进一步优化,以防止在显存使用峰值时发生显存OOM的问题。QLoRA分页优化其实就是当显存不足是,将保存的部分梯度检查点转移到CPU内存上,和计算机的内存数据转移到硬盘上的常规内存分页一个道理。