Lora 微调wiki
背景
大公司或者研究机构,都是有足够资源的来开发大模型,但是对于一般的小公司或者个人来说,要想开发自己的大模型几乎不可能,要知道像 ChatGPT 这样的大模型,一次训练的成本就在上千亿美元。
目前主流的方法包括2019年 Houlsby N 等人提出的 Adapter Tuning,2021年微软提出的 LORA,斯坦福提出的 Prefix-Tuning,谷歌提出的 Prompt Tuning,2022年清华提出的 P-tuning v2
这些方法都有各自的特点,从个人使用情况来说,LORA 的效果会好于其它几种方法。其它方法都有各自的一些问题:
-
Adapter Tuning 增加了模型层数,引入了额外的推理延迟
-
Prefix-Tuning 难于训练,且预留给 Prompt 的序列挤占了下游任务的输入序列空间,影响模型性能
-
P-tuning v2 很容易导致旧知识遗忘,微调之后的模型,在之前的问题上表现明显变差
Lora介绍
原理
核心:模型是过参数化的,它们有更小的内在维度,模型主要去依赖这个低的内在维度去做任务适配。
通俗理解:你从小到大经历了很多很多的事情,比如目前正在经历繁琐又复杂的成年人🍜,但是收益最高的只有儿童时朴素快乐的时光更让你值得回味。
由此得到低秩自适应方法(Lora)

LoRA 允许我们通过优化适应过程中密集层变化的秩分解矩阵,来间接训练神经网络中的一些密集层,同时保持预先训练的权重不变。
原理流程
-
增加旁路:在原始 PLM (Pre-trained Language Model) 旁边增加一个旁路,做一个降维再升维的操作,来模拟原该层的处理逻辑。
-
旁路训练:训练的时候固定 PLM 的参数,只训练降维矩阵 A 与升维矩阵 B 。而模型的输入输出维度不变,输出时将 BA与 PLM 的参数叠加。
-
旁路初始化:用随机高斯分布初始化 A ,用 0 矩阵初始化 B ,保证训练的开始此旁路矩阵依然是 0 矩阵。
详细原理(以任意一个大模型为例)
假设要在下游任务微调一个预训练语言模型(如 GPT-3),则需要更新预训练模型参数,公式表示如下:
W0 是预训练模型初始化的参数, ΔW 就是需要更新的参数。如果是全参数微调,则它的参数量 =W0 (如果是 GPT-3,则 ΔW≈175B )。从这可以看出要全参数微调大语言模型,代价是非常高的。
而对于 LORA 来说,只需要微调 ΔW 。
具体来看,假设预训练的矩阵为 W0∈R(d×k) ,它的更新可表示为:

在 LoRA 的训练过程中, W0 是固定不变的,只有 A 和 B 是训练参数。
(秩r 这个参数很重要,需要在脚本中手动指定,一般名为lora_rank,增大该值可以增大微调参数增加微调力度)
在前向过程中, W0 与 ΔW 都会乘以相同的输入 x ,最后相加:
LORA 的这种思想有点类似于残差连接,同时使用这个旁路的更新来模拟 Full Fine-Tuning的过程。并且,Full Fine-Tuning可以被看做是 LoRA 的特例(当 r 等于 k 时)。
在推理过程中,LoRA 也几乎未引入额外的推理延迟,只需要计算 h=W0+ΔW 即可。
LoRA 与 Transformer 的结合也很简单,仅在 QKV Attention 的计算中增加一个旁路。 VisualGLM主要用到该方法。

论文实验效果和秩的选择
实验结果就不放图了,影响观感,直接贴结论
-
参数量较全参数微调(Fine-Tuning)显著降低,参数量和现有高效参数微调方法持平或更低。
-
性能优于其它参数高效微调方法,和全参数微调(Fine-Tuning)基本持平甚至更高。
-
当增加微调方法的可训练参数量时,其它微调方法都出现了性能下降的现象,只有 LORA 的性能保持了稳定
秩的选择
实验结果显示,对于一般的任务, r=1,2,4,8 就足够了。而一些领域差距比较大的任务可能需要更大的 r 。
同时无脑增加r也并不会使集很大提升微调的效果,这大概率是因为参数量的增加需要更多语料。

原理总结
基于大模型的内在低秩特性,增加旁路矩阵来模拟全参数微调,LoRA 通过简单有效的方案来达成轻量微调的目的。它的应用自不必提,可以将现在的各种大模型通过轻量微调变成各个不同领域的专业模型。
此外,考虑 OpenAI 对 GPT 模型的认知,GPT 的本质是对训练数据的有效压缩,从而发现数据内部的逻辑与联系,LoRA 的思想与之有相通之处,原模型虽大,但起核心作用的参数是低秩的,通过增加旁路,达到四两拨千斤的效果。
VisualGLM中lora分析
基础代码分析
lora_mixin.py VisualGLM-6B/lora_mixin.py at main · THUDM/VisualGLM-6B · GitHub
-
Class LoraLinear() 该类定义了lora中的置换层,用该LoraLinear来对transformer的某一层进行替换
-
self.matrix_A nn.init.kaiming_uniform_(self.matrix_A[i], a=math.sqrt(5)) 对应高斯初始化
-
self.matrix_B nn.init.zeros_(self.matrix_B[i]) 对应0初始化
-
-
replace_linear_with_lora() return LoraLinear() 该函数对tranformer进行替换,直接返回替换后的层
-
在Class LoraMixin中
-
for i in self.layer_range:
print(f'replacing layer {i} attention with lora')
parent_model.transformer.layers[i].attention.dense = replace_linear_with_lora(parent_model.transformer.layers[i].attention.dense, 1, self.r, self.lora_alpha, self.lora_dropout, qlora=self.qlora)
parent_model.transformer.layers[i].attention.query_key_value = replace_linear_with_lora(parent_model.transformer.layers[i].attention.query_key_value, 3, self.r, self.lora_alpha, self.lora_dropout, head_first=self.head_first, num_attention_heads=self.num_attention_heads, hidden_size_per_attention_head=self.hidden_size_per_attention_head, qlora=self.qlora)
-
-
merge_linear_lora() 将训练好的lora参数与原transformer层参数合并,方便模型下载和上传
-
Class LoraMixin()该类定义了上述两个函数在transfromer类模型的具体实现
代码实战
样例1 初始化一个小model进行测试
class Model(nn.Module):
def __init__(self):
super().__init__()
self.child = nn.Linear(100, 200)
def forward(self, x):
return self.child(x)
model = Model()
torch.save(model.state_dict(), "linear.pt") ##原生模型
x = torch.randn(2, 100)
out1 = model(x) ##普通输出
model.child = LoraLinear(100, 200, 10) ##将该层替换为lora层
model.load_state_dict(torch.load("linear.pt"), strict=False)
out2 = model(x) ## lora输出
torch.save(model.state_dict(), "lora.pt")
ckpt = torch.load("lora.pt")
breakpoint()
model.load_state_dict(ckpt, strict=False)
out3 = model(x) ## 还是lora输出
breakpoint()
样例2 Visualglm中lora实现
class FineTuneVisualGLMModel(VisualGLMModel):
def __init__(self, args, transformer=None, parallel_output=True, **kw_args):
super().__init__(args, transformer=transformer, parallel_output=parallel_output, **kw_args)
if args.use_ptuning:
self.add_mixin("ptuning", PTuningV2Mixin(args.num_layers, args.hidden_size // args.num_attention_heads, args.num_attention_heads, args.pre_seq_len))
if args.use_lora: ##直接在模型类的内部添加lora替换类
# If you use lora on other "normal" Transformer, just use it with head_first=False (by default)
self.add_mixin("lora", LoraMixin(args.num_layers, args.lora_rank, head_first=True, num_attention_heads=args.num_attention_heads, hidden_size_per_attention_head=args.hidden_size // args.num_attention_heads, layer_range=args.layer_range), reinit=True)
# self.get_mixin("eva").model.glm_proj = replace_linear_with_lora(self.get_mixin("eva").model.glm_proj, LoraLinear, args.lora_rank)
elif args.use_qlora:
self.add_mixin("lora", LoraMixin(args.num_layers, args.lora_rank, head_first=True, num_attention_heads=args.num_attention_heads, hidden_size_per_attention_head=args.hidden_size // args.num_attention_heads, layer_range=args.layer_range, qlora=True), reinit=True)
self.args = args
Lora是一种针对大模型的微调技术,它通过优化低秩矩阵来调整模型,减少了训练成本和推理延迟。相比全参数微调和其他高效方法,如AdapterTuning和PromptTuning,Lora在性能和参数量上更具优势。在Transformer模型中,Lora通过添加旁路并训练降维和升维矩阵来实现模型的领域适应,而不会改变原始预训练权重。

3956

被折叠的 条评论
为什么被折叠?



