我们下一步是解读QLoRA这种微调技术。与LoRA、AdaLoRA不同,理解QLoRA还需要对模型量化技术有一定的了解。因此,本专栏将通过后续3~4篇文章来详细理解一下QLoRA。今天我先简单地介绍一下模型量化技术。
1.模型量化技术概览
1.1.模型参数与显存占用计算方法
在模型训练、模型推理的过程中,需要考虑GPU的显存是否满足要求,那么模型参数与GPU显存具体怎么估算呢?
- 总参数量:已Qwen2-7B-Instruct为例,7B就是参数量为7Billion,即70亿参数。
- 每个参数的显存占用:假设Qwen2-7B-Instruct的dtype选择为Float16,那么每个参数占2个字节,即16bit。
- 总显存量:总参数量 = 每个参数的显存占用 * 总参数量,因此:
- QWen2-7B-Instruct总显存占用量 = 2字节 * 70亿 = 140亿字节 = 14 * 109字节
- 总显存量换算:1GB = 230B ≈ 109字节,因此:
- Qwen2-7B-Instruct总显存占用量 = 14GB
我们将Qwen2-7B-Instruct以Float16加载到GPU中,观察一下GPU的显存占用大致也是14GB。
根据上述原理,我们可以提出一个问题:如果降低每个参数的显存占用,是不是就可以降低GPU的总显存量?
1.2.模型量化解决的问题及关键要素
模型量化(Model Quantization),就是聚焦于用更少位数的信息表示法,降低模型每个参数的显存占用量,进而降低模型的总显存占用量。
- 如:模型权重以Float32存储(即每个参数的显存占用量为32bit),假设总显存占用量M。
- 如果将每个参数量化为Float16(即每个参数的显存占用量为16bit),则只需要二分之一的总显存占用量M。
- 如果将每个参数量化为Int8(即每个参数的显存占用量为8bit),则只需要四分之一的总显存占用量M。
- 如果将每个参数量话为NF4(即每个菜单树的显存占用量为4bit),则只需要八分之一的总显存占用量M。
因此,我们可以很自然地想到模型量化技术的关键要素之一——量化精度:
- 用更少位数表示更高位数的数字,精度不会丢失吗?丢失的精度会给模型训练和模型推理带来多少误差?
- 简单地将模型的参数向量的每个元素认为是一个用32bit表达的小数转为用4bit表达,直觉上就会带来极大地误差。
另外,还有其它2个关键要素:
- 模型架构
- 硬件设备:GPU、NPU、TPU等
2.量化三步曲
量化过程包括:向量量化、算子量化、模型量化
2.1.向量量化
向量量化是所有量化的基础,量化过程包括量化函数和反量化函数。
- 假设量化函数为:X表示原始向量,INT8表示量化后向量。
- 假设反量化函数为:INT8表示量化后向量,QX表示通过量化后向量,还原的还原向量。
- 假设误差函数为:原始向量X和还原向量QX可能存在误差,误差当然越小越好,衡量这个误差的函数就是误差函数。
(1)INT4
以INT4的向量量化过程为例:
- 假设向量系数为:
- 假设原始向量为:
- 对原始向量进行计算:
- 求每个分量的绝对值的最大值,结果为1.52。
- 将每个分量除以1.52,得到过程向量[0.18, 0.38, -0.49, 1.00]。
- 对过程向量进行计算:
- 在量化系数中,找到最近的值,得到最近值向量。
- 对最近值向量进行计算,得到最终的量化后向量:
- 将最近值向量的每个分量用量化系数的索引进行替换,得到最终的量化后向量[9, 10, 4, 15]。
再来看看INT4的向量反量化过程:
- 将量化后向量,根据索引还原为过程向量:
- 将过程向量乘以稀疏1.52,得到还原向量:
- 还原向量为[0.304, 0.5016, -0.7144, 1.52]。
- 我们可以看到,原始向量和还原向量存在一定的误差:
从数据分布上看,INT4量化方法更适合如下数据分布特征(中段数据过于一致,此量化方法有局限性):
(2)FP4
FP4的计算过程不赘述,可参考:
https://huggingface.co/blog/4bit-transformers-bitsandbytes#fp4-precision-in-a-few-words
(3)NF4
NF4 Quant是QLoRA提出的一种新的数据类型(4-bit NormalFloat),量化过程中保留了零点,使用2k位表示k位数据类型。
NF4通过估计两个范围的分位数qi,创建了一个非对称的数据类型。第一个范围表示负数部分[-1, 0]的2k-1,第二个范围表示正数部分[0, 1]的2k-1+1.
这种量化方法在每个量化bin中都具备相等的期望值数量,这种以零为中心的正态分布数据在信息论中是最优的。
上述数学方法到底解决什么问题呢?预训练的权重通常具有以零为中心的正态分布(标准差为σ),缩放σ可以使得权重分布适应NF的范围,量化时的数据类型和神经网络权重的分位数最终都会被归一化到[-1, 1]的范围内。计算分位数的公式:
更详细的代码参考:
https://github.com/bitsandbytes-foundation/bitsandbytes/blob/main/csrc/kernels.cu
|
|
2.2.算子量化
在数学领域,一个函数空间到函数空间上的映射O:X→Y,称为算子。广义上,对任何函数进行某一项操作都是一个算子:
- 如:微分算子
- 如:不定积分算子
在深度学习中,各种网络结构,都是由若干的计算单元组成,这些计算单元就是算子(Operator)。
- 如:卷积层(Convolution Layer)是一个算子
- 如:全连接层(Fully-connected Layer, FC layer)中的权值求和过程,也是一个算子。
如下图所示,Conv1、Pool1、Conv2都是网络中的算子,Conv1和Conv2都是做卷积运算的算子(Convolution)。
参考:https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/80RC2alpha002/devguide/opdevg/tbeaicpudevg/atlasopdev_10_0006.html
算子量化如何开展呢?我们可以看到算子的前端有输入,算子的后端有输出。因此,算子量化就是将输入向量进行量化,对输出向量进行量化,而不修改算子的逻辑。但算子量化也有一些特殊情况:有的算子不能量化输入,有的算子不能量化输出。
2.3.网络量化
关闭冗余量化信息:
在量化神经网络过程中,需要有量化控制信息向量TQC。
在神经网络中,算子Conv连接到算子Reshape上,那么就会出现两个一模一样的TQC。
这种量化控制信息就是多余的,我们就需要消除掉Reshape侧的量化控制信息。
- 图融合
- 本质是将N个标准的小算子进行融合,如:对Conv后的激活函数进行算子融合。
- 算子融合后得到了一个新的非标的大算子,进而减少了量化控制信息。
- 如下图:为了实现FlashAttention,将图中的MatMul算子(Cube)、Scale算子(Vector)、Mask算子(Vector)、SoftMax算子(Vector)融合为一个大的算子Flash Attention。
参考:https://www.hiascend.com/doc_center/source/zh/canncommercial/80RC2/developmentguide/opdevg/Ascendcopdevg/atlas_ascendc_10_0041.html
3.总结
为了解读QLoRA的原理,我们简要地介绍了模型量化技术的概念和基本原理:
- 模型量化的第一层是向量量化,即针对向量量化,有量化函数和反量化函数。
- 模型量化的第二层是算子量化,即针对算子的输入向量和输出向量进行量化。
- 模型量化的第三层是网络量化,即针对网络去除冗余的量化控制信息、将小算子融合为大算子进行整体量化处理。
有了本文的模型量化基础知识,我们就可以进一步阅读QLoRA的论文了。
论文链接:https://arxiv.org/pdf/2303.10512