深入浅出剖析 LoRA 技术原理( 二 )


虽然看起来方便,但也存在以下两个显著劣势:
三、什么是LoRA
总结一下,全参数微调太贵,存在训练和推理延迟,难训且会减少原始训练数据中的有效文字长度,那是否有一种微调办法,能改善这些不足呢?
在这样动机的驱动下,作者提出了LoRA (Low-Rank,低秩适配器)这样一种微调方法 。我们先抛开对“低秩”、“适配器”这样抽象词语的解释,我们先来看LoRA长什么样,要怎么用 。在下一节中,我们再来详细解释“低秩”作用的原理 。
3.1 LoRA整体架构
图中左侧表示“全参数”的场景 。我们将参数分成了两个部分:
之所以这么拆分,是因为全参数可以理解成“冻住的预训练权重” + “微调过程中产生的权重更新量” 。设输入为,输出为,则有:
图中右侧表示“LoRA ”的场景 。在LoRA中,我们用矩阵A和B来近似表达:
经过这样一番拆分,我们将改写成的形式,使得微调参数量从d*d降低至2*r*d,同时不改变输出数据的维度,即在LoRA下我们有:
另外,在原论文中提到过对于两个低秩矩阵,会用超参(一个常数)来做调整,但没有说明这个超参的作用位置 。在读完LoRA的源码后,我发现这个超参是作为 rate直接和低秩矩阵相乘的,也就是最终的输出为:
在实操中,一般取,例如在LoRA源码对GPT2微调,做NLG任务时,就取 。我们会在后文详细介绍这个 rate的作用,以及“秩”的具体含义 。
A和B的初始化方法
需要注意的是,这里对采用高斯初始化,对采用零初始化的目的是,让训练刚开始时的值为0,这样不会给模型带来额外的噪声 。那么你可能想问,那我对做零初始化,对做高斯初始化行不行呢?反正看起来只要让初始化为0就行?
针对这个问题,我在 issue上找到了LoRA一作的回答:
简单来说,当前作者还没有发现转换初始化方式产生的显著区别,只要这两者中任意一者为0,另一者不为0即可 。
吃果冻不吐果冻皮
长期主义,专注AI工程化(LLM/MLOps)落地 。
3.2 LoRA的训练和推理过程
在3.1中,我们介绍了LoRA的整体架构:在原始预训练矩阵的旁路上,用低秩矩阵A和B来近似替代增量更新 。你可以在你想要的模型层上做这样的操作,比如中的、MLP层的权重、甚至是部分的权重 。在LoRA原始论文中,只对部分的参数做了低秩适配,但在实际操作中,我们可以灵活根据需要设置实验方案,找到最佳的适配方案 。
3.2.1 训练
在训练过程中,我们固定住预训练权重,只对低秩矩阵和进行训练 。在保存权重时,我们只需保存低秩矩阵的部分即可 。按照LoRA论文中的统计,这样的操作使得在微调GPT3 175B时,显存消耗从1.2TB降至350GB;当r=4时,最终保存的模型从350GB降至35MB,极大降低了训练的开销 。
关于训练部分,我们再来看一个有趣的问题:总体上来看,LoRA对显存的节约是显著的,但是在训练的每一时刻,LoRA都能做到节省显存吗?
考虑时,对计算梯度,根据(为了敲公式方便,暂时忽略掉一项),我们有:
注意这一项,你会发现,它和预训练权重的维度d*d一模一样,也就是为了计算的梯度,我们需要用到和全参数微调过程中一样大小的中间值结果 。因此对LoRA来说,这一层的峰值显存,和全量微调基本是一致的(算上一项的话则高于全量微调) 。

深入浅出剖析 LoRA 技术原理

文章插图
但是为什么LoRA又能从整体上降低显存使用呢,因为:
3.2.2 推理
在推理过程中,我们按照的方式,合并低秩矩阵和预训练权重,然后正常做推理 。这样我们完全不会更改模型的架构,因此不会像 一样产生推理上的延时 。下图展示了论文中的实验效果,推理时长的单位是,可以发现,LoRA的推理速度显著高于。