随着模型规模的不断扩大,微调模型的所有参数(所谓full fine-tuning)的可行性变得越来越低。以GPT-3的175B参数为例,每增加一个新领域就需要完整微调一个新模型,代价和成本很高。
为解决微调大规模语言模型到不同领域和任务的挑战,已有多种方案,比如部分微调、使用adapters和prompting。但这些方法存在如下问题:
- Adapters引入额外的推理延迟 (由于增加了模型层数)
- Prefix-Tuning难于训练,且预留给prompt的序列挤占了下游任务的输入序列空间,影响模型性能
Adapter Layers
在transformer block后面添加参数很少的层或者norm层。这样做,虽然tuning时要更新的参数少了,但由于加了网络深度,对于latency不友好。
Prompt tuning/Prefix tuning
这个方法,一是很难优化,二是会降低下游能处理的序列长度。 Prefix-Tuning难于训练,模型性能也并非总是稳步提升。预留一些sequence做adaption会让处理下游任务的可用sequence长度变少,一定程度上会影响模型性能。
[参数有效性学习-Adapter和Prefix-Tuning 以及 UniPELT框架]
LoRA: Low-Rank Adaptation of Large Language Models
LoRA的思路
paper:Armen Aghajanyan的Intrinsic Dimensionality Explains the Effectiveness of Language Model Fine-Tuning.
这篇文章尝试去回答一个问题:为什么用几千几百条样本就能去finetune一个百万量级的模型。
答案是:We empirically show that common pre-trained models have a very low intrinsic dimension; in other words, there exists a low dimension reparameterization that is as effective for fine-tuning as the full parameter space。即只有一些关键维度对finetune起效果。
作者做了个实验:by optimizing only 200 trainable parameters randomly projected back into the full space, we can tune a RoBERTa model to achieve 90% of the full parameter performance levels on MRPC.即将原始权重映射到一个很小的子集再映射回去,依然能训得很好。即只需要更新低秩矩阵,在节省大量资源和时间的情况下,仍能获得不错的效果。
GPT的本质是对训练数据的有效压缩,从而发现数据内部的逻辑与联系,LoRA的思想与之有相通之处,原模型虽大,但起核心作用的参数是低秩的。
LoRA的思想也很简单,在原始PLM旁边增加一个旁路,做一个降维再升维的操作,来模拟所谓的 intrinsic rank
。训练的时候固定PLM的参数,只训练降维矩阵A与升维矩阵B。而模型的输入输出维度不变,输出时将BA与PLM的参数叠加。原始参数的维度是d∗d, 则低秩分解后的参数量级是2∗r∗d,因为这里的r<<d,因此可以起到大幅降低微调参数量级的效果。
用随机高斯分布初始化A,用0矩阵初始化B,保证训练的开始此旁路矩阵依然是0矩阵。通过随机高斯分布初始化A,可以引入随机性和非线性,而将B初始化为零矩阵可以确保记忆状态的空白和初始状态。这样可以有效地初始化LoRA模型的参数,为后续的训练和学习过程提供良好的起点。[ailake]
两种推断/部署方法
方法1:将预训练的大模型的checkpoint统一部署,将特定任务训练的 BA 单独部署。当下游任务很多时,存储空间也不会增加很多。
方法2:在推断时可以利用重参数(reparametrization)思想,将AB与W合并,这样就不会在推断时引入额外的计算了。 即在生产环境部署时,LoRA可以不引入推理延迟,只需要将预训练模型参数 W0 与LoRA参数进行合并(模型合并)即可得到微调后的模型参数: W=W0+BA ,在生产环境中像以前一样进行推理,微调前是计算 h=W0x ,现在计算 h=Wx ,没有额外延迟和原预训练模型是一样的参数量和latency。现在不少模型仅发布LoRA权重,需要本地与基模型进行模型合并才能使用的原理就在于此。
LoRA对哪些参数进行微调
1 LoRA 已经被作者打包到了loralib中:pip install loralib
可以选择用loralib中实现的对应层来替换一些层。
2 基于Transformer结构,LORA只对每层的Self-Attention的部分进行微调,有W_q, W_k, W_v, W_O四个映射层参数可以进行微调。消融实验显示只微调W_q效果略差,微调W_q, W_v的效果和微调W_q, W_k, W_v, W_O的效果相似。
仅在QKV attention的计算中增加一个旁路,而不动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.
对一个参数用比较大的rank更新,不如对多个参数用很小的rank更新,low_rank更新合理。
Rank的取值
作者对比了1-64,效果上Rank在4-8之间最好,再高并没有效果提升。不过论文的实验是面向下游单一监督任务的,因此在指令微调上根据指令分布的广度,Rank选择还是需要在8以上的取值进行测试。
关于∆W
1, ∆W has a stronger correlation with W compared to a random matrix, indicating that ∆W amplifies some features that are already in W.
2,instead of repeating the top singular directions of W, ∆W only amplifies directions that are not
emphasized in W.
3, the amplification factor is rather huge: 21.5 ≈ 6.91/0.32 for r = 4.
参数减少量
On GPT-3 175B, we reduce the VRAM consumption during training from 1.2TB to 350GB.
With r = 4 and only the query and value projection matrices being adapted, the checkpoint size is reduced by roughly 10,000× (from 350GB to 35MB). (We still need the 350GB model during deployment; however, storing 100 adapted models only requires 350GB + 35MB * 100 ≈ 354GB as opposed to 100 * 350GB ≈ 35TB.)
代码解析
核心LoraLayer,定义了LoRA的各种超参数以及模型层对应更新方法:
[https://github.com/huggingface/peft/blob/main/src/peft/tuners/lora.py#L123]
线性类,是LoRA模型的具体实现:
[https://github.com/huggingface/peft/blob/main/src/peft/tuners/lora.py#L735]
[代码: GitHub - microsoft/LoRA: Code for loralib]
from:https://blog.csdn.net/pipisorry/article/details/130978244
ref: [LoRA: Low-Rank Adaptation of Large Language Models]
[LoRA: Low-Rank Adaptation of Large Language Models 简读 - 知乎]**
[微软LoRA: Low-Rank Adaptation of Large Language Models 代码解读 - 知乎]