NLP学习总结|SFT微调方法之一:LoRA微调

1. 什么是监督微调(SFT,i.e. Supervised Fine-Tuning)?

1.1 基本概念

TL;DR: 监督微调就是在目标数据集上训练一个基于预训练模型架构和参数的新的目标模型

监督微调就是指先在源数据集上训练一个神经网络模型,即源模型。普遍来说,源模型是在大量且广泛的数据集上训练的,因为可以看作是有了general的语言知识,这些语言知识可以一定程度上迁移、适用于特定的场景,不过未必会有特别优良的表现。为了适配特定的任务,我们需要再创建一个新的神经网络模型,是我们任务需要的目标模型。目标模型复制了源模型的除输出层之外的模型架构和参数(因为源模型的输出层和训练所用数据集的标签相关;不过在LLM时代,很多模型在预训练的时候都是采用无监督学习的模型进行next word prediction的预训练任务,数据集不含标签)。微调时,为目标模型添加一个输出大小为目标数据集类别个数的输出层,并随机初始化该层的模型参数。在目标数据集上训练目标模型时,将从头训练到输出层,其余层的参数都基于源模型的参数微调得到。(这样相当于我们微调时不需要从头开始训练,而是已经从一个较优的位置开始训练)

1.2 特点

通常,只有预训练模型中的一部分层被微调,例如只微调模型的最后几层或者某些中间层。原因可能是因为,如果对全部的参数都进行微调,可能会导致目标模型和源模型差距过大,在一些不那么specific的任务上表现不稳定(个人推测)。此外,想要微调效果好,需要有一定数量的标注数据。

(ps. SFT中的full parameter fine-tuning,全参数微调的模型训练模式的开启or广泛应用应该是由2018年BERT模型的发布开启的)

2. LLM时代的微调: PEFT(Parameter Efficient Fine-Tuning)

LoRA, Prefix tuning, P-tuning

2.1 LoRA - Low-Rank Adaptation of Large Language Models

TL;DR: LoRA的基本原理就是通过冻结预训练好的模型权重参数,然后往模型中加入额外的网络层,并且只训练这些新增的网络层参数。由于模型低秩性的存在,我们可以用两个低秩的矩阵来近似需要新增的网络层参数,以此来减少需要学习的参数量

2.1.1 什么是秩?

矩阵的秩(rank)分为行秩和列秩,行秩指的是矩阵的线性无关的行的个数,列秩同理。因为一个矩阵的行秩和列秩总是相等的,因此它们统一被叫做矩阵的

2.1.2 过参数化

许多研究认为现在的大模型虽然参数空间大,但实际上存在一个特征的内在维度,及特征的有效信息的真实维度,并且这个维度可能比 我们学习到的要小得多。这个内在维度就是我们解决问题所需要的维度,而LoRA就是希望通过微调这些低秩的内在维度达到类似全参数微调的效果。

2.1.3 计算原理

与adapter使用串行的算法不同,LoRA是在模型的某些矩阵旁边插入一个并行的权值矩阵\Delta W\in \mathbb{R}^{d\times k}。由于矩阵的低秩性的存在(r \ll min\{d, k\}),可以将这个矩阵拆分成两个矩阵A \in \mathbb{R}^{r \times k}B \in \mathbb{R}^{d \times r}。训练时可以只训练A, B,而推理时只需要把\Delta W加入到原参数上,不会产生额外的推理时延(公式:h = W_0x + \frac{\alpha}{r} \Delta Wx = W_0x + \frac{\alpha}{r} (BA)x,这里\alpha是scaling factor,越大,LoRA权重的影响也就越大)

2.1.4 为什么LoRA微调能带来训练效率的提升?

虽然LoRA模型中需要更新的参数量变少了,但是实际上理论计算量相比全参数微调来说并没有少,甚至还更大些(这里指的是需要计算的梯度,因为微调中模型梯度是主要的运算量来源)。

LoRA对于训练效率的提升来自于1. 显存和内存的占用减小 2. 允许更小精度的训练

显存和内存占用减小 

​​​​​​​虽然LoRA的梯度更新需要依赖于主干模型的梯度,因此这部分计算无法省略;但是由于不需要对主干模型进行更新和优化,所以主干模型部分的优化器不需要存储,这部分显存可以节省(像Adam优化器需要维护每个参数的一阶动量和二阶动量,分别是梯度的指数移动平均值和梯度平方的指数移动平均值)。(利用这一点我们可以使用fp16,甚至int8,int4等低精度的数据类型,进一步减少显存消耗。)

(note: 显存使用降低不会直接导致训练速度的加快,不过它可以减少额外的数据传输开销和延迟,从而间接地,使模型训练速度更快。)

加快模型训练速度

使用LoRA时,我们可以对主干模型进行低精度的量化,如int8或int4,这样可以减少主干模型的前向传播和反向传播的耗时。使用多卡训练(数据并行)时,我们只需要同步LoRA模型部分的梯度,这样可以大大减少卡间通信的压力,也可以提高总训练速度。

(这部分参考了:大模型轻量级微调(LoRA):训练速度、显存占用分析

2.1.5 一些实施细节

初始化 - A和B一个使用高斯初始化,一个使用零初始化

原因:如果全是零初始化的话 ,反向传播后更新的权重也相同,会导致对称性问题,网络的表达能力受限;如果全是随机初始化的话,训练的开始可能会和预训练时得到的原参数有较大的偏差,导致收敛变慢。

实施在哪些层 - 理论上所有attention layer和dense layer都可以

原论文中作者试验了在训练参数量一定的情况下,在含有W_q, W_v的组合中效果比较好(W_q, W_v的效果接近W_q, W_v, W_k, W_o)(突然忘记W_o作用是什么了,刚刚马上去翻了一下笔记:为了让不同头学到的特征更好融合在一起)

我自己项目中尝试了W_q, W_v, W_o以及其他的dense layers,发现在没改变其他超参数的情况下与加在W_q, W_v效果确实略有提升,不过训练时间也相应增加了。

秩的大小选择 - r = 1, 2, 4, 8(2的倍数)都能有不错的效果

我尝试了2,4,8,感觉8会微微胜过2和4,但差距不是很大

代码实现:

input_dim = 768  # e.g., the hidden size of the pre-trained model
output_dim = 768  # e.g., the output size of the layer
rank = 8  # The rank 'r' for the low-rank adaptation

W = ... # from pretrained network with shape input_dim x output_dim

W_A = nn.Parameter(torch.empty(input_dim, rank)) # LoRA weight A
W_B = nn.Parameter(torch.empty(rank, output_dim)) # LoRA weight B

# Initialization of LoRA weights
nn.init.kaiming_uniform_(W_A, a=math.sqrt(5))
nn.init.zeros_(W_B)

def regular_forward_matmul(x, W):
    h = x @ W
    return h

def lora_forward_matmul(x, W, W_A, W_B):
    h = x @ W  # regular matrix multiplication
    h += x @ (W_A @ W_B) * alpha # use scaled LoRA weights
    return h

参考网站:

LoRA详解

人工智能大语言模型微调技术:SFT 监督微调、LoRA 微调方法、P-tuning v2 微调方法、Freeze 监督微调方法-腾讯云开发者社区-腾讯云

  • 20
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值