来源

还是之前这个AMD+Datawahle体验项目
https://ailc.datawhale.cn/hall/group/100000144

云服务器内置了一个notebook,一路enter就能跑通LoRA微调
https://ailc.datawhale.cn/hall/group/100000144/task/100000037

LoRA微调简介

数学定义

一个已经训练好的LLM一般有几十,甚至几百B的参数,想增加数据重新训练的开销是不可接受的,即使是专业计算卡,如果不是大厂的集群,也是训不起来的。我们现在只有一个AMD 48G计算卡,只能尝试LoRA微调来重训模型。

LoRA的数学操作是,对于每个要微调的参数矩阵,假设原本是d * d的,我们不去更新这个大矩阵的参数,而是弄两个小矩阵a,b,大小分别为d* r,r * d,r通常很小,比如这里我们设为r=16,这个r就是LoRA里的rank(秩)

然后这俩小矩阵相乘,可以张成一个d * d的矩阵,和原始大参数矩阵一样大。我们在训练过程中,就更新a,b这两个小矩阵不动大矩阵。可以实现d/r的训练加速比和显存节约率。

更具体来说,forward公式是
Output=(X⋅W)+(X⋅A⋅B)×αr\text{Output} = (X \cdot W) + (X \cdot A \cdot B) \times \frac{\alpha}{r}Output=(XW)+(XAB)×rα

在这里插入图片描述

训练时forward也会调用原始矩阵W进行推理,但是不计算W的梯度,冻结W的参数,只计算AB的梯度,反向传播更新AB的参数。其中alpha是一个缩放比例,决定LoRA参数的权重。

训练结束后,保存AB参数以备推理。这里并不会保存AB+W,进行W矩阵的更新,一是浪费显存,存储,只保存AB可以实现相同的效果,推理时临时读入计算就可以。二是LoRA模块可能会被当成热插拔模块,不同需求是快速切换不同的LoRA结果,如果是固化到W矩阵上,切换功能时要把W载入内存,很慢,而只载入AB,计算时合并则很快

推理时,调用A,B,W矩阵,使用以下公式计算。根据矩阵乘法结合律,可以先计算W+AB,这样可以减少矩阵乘法次数。
X⋅W+X⋅A⋅B=X⋅(W+A⋅B)X \cdot W + X \cdot A \cdot B = X \cdot (W + A \cdot B)XW+XAB=X(W+AB)

优点

观察上面的式子,可能会发现,还是载入大矩阵W进行forward了,还额外增加了AB矩阵,那节省时间和显存省在哪了?

虽然W载入显存,推理是不能省的。但是除了模型参数本身,训练时还有一些数据占用显存,比如参数的梯度,几乎是正比于参数量,还有训练优化器保存的历史信息,也是为每个参数都保存的,比如Adam优化器,就要保存动量信息,这部分信息甚至是原始权重W的好几倍,
这些显存占用,都是用了更新参数的,正比如需要更新的参数量,所以我们如果把需要更新的参数量降低,这部分显存占用也会等比例降低。

所以尽管推理显存不变,甚至增加一点,但是由于更新参数变少了,训练显存大幅降低,基本可以降低d/r倍

理论分析

LoRA的全称是Low-Rank Adaptation,从理论上,他的正确性来源于一个实验得到的事实:大模型微调中,参数矩阵的改变量ΔW\Delta WΔW一般是一个低秩矩阵,也就是很多行可以相互表示,如果高斯消元化简后,能得到的线性无关组的行数很少。

比如
M=[123246102030]M = \begin{bmatrix} 1 & 2 & 3 \\ 2 & 4 & 6 \\ 10 & 20 & 30 \end{bmatrix}M= 121024203630

显然化简后等价于
[123000000]\begin{bmatrix} 1 & 2 & 3 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \end{bmatrix} 100200300

而这样的一个低秩矩阵,可以表示为两个小矩阵的乘法。比如这里这个矩阵就可以写成,两个向量的外积(转置相乘)
u⋅vT=[1210][123]=[123246102030]u \cdot v^T = \begin{bmatrix} 1 \\ 2 \\ 10 \end{bmatrix} \begin{bmatrix} 1 & 2 & 3 \end{bmatrix} = \begin{bmatrix} 1 & 2 & 3 \\ 2 & 4 & 6 \\ 10 & 20 & 30 \end{bmatrix}uvT= 1210 [123]= 121024203630

更一般的情况是:
如果一个大小为 d×dd \times dd×d 的大矩阵 MMM,它的秩为 rrr(其中 r≤dr \le drd),那么它一定可以被分解为两个矩阵的乘积:

M=A⋅BM = A \cdot BM=AB

其中:

  • 矩阵 AAA 的大小是 d×rd \times rd×r(长瘦型矩阵,一共有 rrr 列)
  • 矩阵 BBB 的大小是 r×dr \times dr×d(矮胖型矩阵,一共有 rrr 行)
  • 而且,这两个小矩阵 AAABBB 的秩也刚好都是 rrr,所以叫满秩分解(Full-Rank Decomposition)。

所以在微调训练时,由于参数矩阵W的改变量近似于一个低秩矩阵,我们可以用两个小矩阵[d,r][r,d]的乘积来近似改变量。具体r等于多少,取决于你认为这次微调的调整量大不大,如果比较小的任务,可以8,16,如果是学会一个脚本语言或者DSL,可以调大到32甚至64

LoRA微调过程

别的包都比较基础,trl和peft是LoRA核心包
在这里插入图片描述
modelscope下载模型,类似huggingface,只不过服务器在国内下载更快
在这里插入图片描述
modelscope也能下载数据集,这里选一个情绪分类数据集,输入是文本,label是文本的情绪
在这里插入图片描述
gemma模型被训练为对话模型,所以训练数据最好是用户:回答:一问一答的形式。这样能让模型更倾向于直接输出情感。我们把所有数据构造成这个格式
在这里插入图片描述
构造前后的格式对比,之前就是文本,label的格式。后面嵌套了询问,对话
在这里插入图片描述
在这里插入图片描述
加载模型
在这里插入图片描述
推理函数,就是调用模型进行一次情绪推理,然后解析文本,抓取回答里的情感关键词,考虑健壮性,如果模型乱回答,标记为无效回答
在这里插入图片描述
评估函数,计算f1等常见指标,调用上面的推理函数,考虑到时间,这只是个baseline,只跑400条
在这里插入图片描述
先跑一下原始模型,看看效果
在这里插入图片描述
LoRA框架的强大之处,定义配置文件,即可声明式启动LoRA训练
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到训练中的准确率非常高了
在这里插入图片描述
保存微调的lora结果
在这里插入图片描述
微调后评估,准确率等指标大幅提升,从0.6到0.9
在这里插入图片描述
更直观的对比
在这里插入图片描述
保存了lora结果后,以后随时可以直接推理,不用重复训练,只要加载模型参数和lora参数即可在这里插入图片描述

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐