承接上篇Parameter Efficient Fine-Tuning(PEFT)系列论文总结(二),本篇主要介绍LoRA及其各种变体的微调方法。
一、LoRA
研究表明,语言模型针对特定任务微调之后,权重矩阵通常具有很低的本征秩 (Intrinsic Rank)
。研究人员认为参数更新量即便投影到较小的子空间中,也不会影响学习的有效性。因此,提出固定预训练模型参数不变,在原本权重矩阵旁路添加低秩矩阵的乘积作为可训练参数,用以模拟参数的变化量。具体来说,假设预训练权重为${w_0\ \epsilon \ \mathbb{R}^{dk}}$,可训练参数为${\varDelta W\ =\ BA}$,其中${B\ \epsilon \ \mathbb{R}^{dr} }$,${A\ \epsilon \ \mathbb{R}^{r*d}}$,初始化时,矩阵 ${A}$ 通过高斯函数初始化,矩阵${B}$ 为零初始化,使得训练开始之前旁路对原模型不造成影响,即参数改变量为 0。对于该权重的输入 ${x}$ 来说,输出为式${h\ =\ W_0x+∆W\ x\ =W_0x+BAx}$,LoRA
算法结构方法如图:
%E7%B3%BB%E5%88%97%E8%AE%BA%E6%96%87%E6%80%BB%E7%BB%93(%E4%B8%89)/image-20240110192331072.png)
简言之,LoRA
的核心思想是用一种低秩的方式来调整这些参数矩阵。在数学上,低秩意味着一个矩阵可以用两个较小的矩阵相乘来近似。
LoRA实现步骤如下:
1、选择目标层
首先,在预训练神经网络模型中选择要应用LoRA
的目标层。这些层通常是与特定任务相关的,如自注意力机制中的查询Q
和键K
矩阵
值得注意的是,原则上,可以将LoRA
应用于神经网络中权矩阵的任何子集,以减少可训练参数的数量。在Transformer
体系结构中,Self-Attetion
模块(Wq
、Wk
、Wv
、Wo
)中有四个权重矩阵,MLP
模块中有两个权重矩阵。我们将Wq
(或Wk
,Wv
)作为维度的单个矩阵,尽管输出维度通常被切分为注意力头。
(In principle, we can apply LoRA to any subset of weight matrices in a neural network to reduce th enumber of trainable parameters. In the Transformer architecture, there are four weight matrices in the self-attention module (Wq, Wk, Wv, Wo) and two in the MLP module. We treat Wq (or Wk, Wv)as a single matrix of dimension , even though the output dimension is usually sliced into attention heads)
不过,为了简单和参数效率,将研究限制为仅适应下游任务的注意力权重,并冻结MLP
模块(因此它们不接受下游任务的训练)
(We limit our study to only adapting the attention weights for downstream tasks and freeze the MLP modules (so they are not trained in downstream tasks) both for simplicity and parameter-efficiency)
2、初始化映射矩阵和逆映射矩阵
为目标层创建两个较小的矩阵A
和B
,然后进行变换
A
是映射矩阵(一般用随机高斯分布初始化,维度上是降维)
B
是逆映射矩阵(用0矩阵初始化),维度上是升维
之后做参数变换:将目标层的原始参数矩阵W通过映射矩阵A
和逆映射矩阵B
进行变换,计算公式为:W' = W + A * B
,这里W'
是变换后的参数矩阵
3、微调模型
使用新的参数矩阵W'
替换目标层的原始参数矩阵W
,然后在特定任务的训练数据上对模型进行微调
4、梯度更新
在微调过程中,计算损失函数关于映射矩阵A
和逆映射矩阵B
的梯度,并使用优化算法(如Adam
、SGD
等)对A
和B
进行更新
注意,在更新过程中,原始参数矩阵W
保持不变
说白了,训练的时候固定原始PLM的参数,只训练降维矩阵A与升维矩阵B
且当需要切换到另一个下游任务时,可以通过减去BA
然后添加不同的B'A'
来恢复W
,这是一个内存开销很小的快速操作*(When we need to switch to another downstream task, we can recover W0 by subtracting BA and then adding a different B0A0, a quick operation with very little memory overhead )*
二、AdaLoRA
从上文介绍的LoRA
可以看出它的局限性:在一个模型的所有使用适配器的模块都使用了同一个r
,但是无论是不同深度的参数,还是同一个深度不同模块的参数,它们在模型中的重要性都是不同的。例如下图的这两个例子,作者通过只对特定的模块进行微调,得出了不同参数的重要性的可视化结果。从中我们可以看出,自注意机制的全连接层要比计算 Wq
,Wk
,Wv
的权值重要,而更深层的参数要比更浅层的参数重要。
%E7%B3%BB%E5%88%97%E8%AE%BA%E6%96%87%E6%80%BB%E7%BB%93(%E4%B8%89)/image-20240110202455667.png)
AdaLoRA的动机
因为在一个模型中,不同模块拥有着不同的贡献,那么在使用LoRA
时如果我们能够根据它们重要性的不同为不同的模块分配不同的秩,那么将会带来很多好处。首先,我们为重要性更低的模块分配更小的秩,那么将有效的减少模型的计算量。其次,如果我们能够为更重要的特征分配更大的秩,那么将能够更有效的捕捉特征的细节信息。
具体的过程数学要求有点高,先不看了
总结
AdaLoRA
通过将所有添加了适配器的模块的秩的值看做了一组超参,然后通过模型剪枝的思想对LoRA
的秩进行了自适应的计算。同时为了剪枝后模型效果的稳定,AdaLoRA
使用SVD
的三元组替代了LoRA
的二元组,充分利用了SVD
奇异矩阵的正交性和奇异向量的绝对值和特征重要性的相关性设计剪枝策略。
三、QLoRA
讲QLoRA
之前,首先得明白什么是模型量化
模型量化(Quantization)也被叫做模型的低精度表示,指的是在不大幅降低模型效果的前提下使用更低的精度来表示模型中的参数,从而缩减模型的体积和训练模型时占用的显存。量化的本质是函数映射,根据量化过程是否线性我们可以把量化分为线性量化和非线性量化。
模型量化的核心工作就是在尽量保证模型准确率的前提下优化模型的推理速度和模型体积。
与量化对应的是反量化(Dequantization
),反量化指的是将模型的低精度恢复为高精度的过程,主要用于减少量化造成的精度损失
简单来讲,模型量化是将浮点数值转化为定点数值,同时尽可能减少计算精度损失的方法。
模型量化是一种压缩网络参数的方式,它将神经网络的参数(weight
)、激活值(activation
)等原本用浮点表示的量值换用定点(整型)表示,在计算过程中,再将定点数据反量化回浮点数据,得到结果。
我们可以对模型参数(weight
)、激活值(activation
)或者梯度(gradient
)做量化。通常而言,模型的参数分布较为稳定,因此对参数 weight
做量化较为容易(比如,QLoRA
便是对weight
做量化)
至于模型的激活值往往存在异常值,直接对其做量化,会降低有效的量化格点数,导致精度损失严重,因此,激活值的量化需要更复杂的处理方法(如SmoothQuant
)
%E7%B3%BB%E5%88%97%E8%AE%BA%E6%96%87%E6%80%BB%E7%BB%93(%E4%B8%89)/image-20240112135538529.png)
QLoRA的工作有三个
1、结合了分位数量化和分块量化的4位标准浮点数量化(4-bit NormalFloat Quantization)
2、对模型进行两次量化的双重量化(Double Quantization),它的第二次量化只作用在第一次量化产生的量化常数上,可以进一步节约显存占用
3、分页优化(Paged Optimizer),使用CPU
内存代替GPU
显存保存部分梯度参数
留到之后理解更深了再更新