通俗理解大模型的各大微调方法:从LoRA、QLoRA到P-Tuning V1/V2

news2024/11/14 22:03:59

前言

PEFT 方法仅微调少量(额外)模型参数,同时冻结预训练 LLM 的大部分参数

第一部分 高效参数微调的发展史

1.1 Google之Adapter Tuning:嵌入在transformer里 原有参数不变 只微调新增的Adapter

谷歌的研究人员首次在论文《Parameter-Efficient Transfer Learning for NLP》提出针对 BERT 的 PEFT 微调方式,拉开了 PEFT 研究的序幕。他们指出

  • 在面对特定的下游任务时,如果进行 Full-fintuning(即预训练模型中的所有参数都进行微调),太过低效
  • 而如果采用固定预训练模型的某些层,只微调接近下游任务的那几层参数,又难以达到较好的效果

于是他们设计了如下图所示的 Adapter 结构

image.png

  1. 如上图左侧所示,将其嵌入 Transformer 的结构里面,在训练时,固定住原来预训练模型的参数不变,只对新增的 Adapter 结构进行微调
  2. 如上图右侧所示,同时为了保证训练的高效性(也就是尽可能少的引入更多参数),他们将 Adapter 设计为这样的结构:首先是一个 down-project 层将高维度特征映射到低维特征,然后过一个非线形层之后,再用一个 up-project 结构将低维特征映射回原来的高维特征;同时也设计了 skip-connection 结构,确保了在最差的情况下能够退化为 identity

从实验结果来看,该方法能够在只额外对增加的3.6%参数规模(相比原来预训练模型的参数量)的情况下取得和Full-finetuning接近的效果(GLUE指标在0.4%以内)

1.2 斯坦福之Prefix Tuning

Prefix Tuning方法由斯坦福的研究人员提出,与Full-finetuning更新所有参数的方式不同,该方法是在输入token之前构造一段任务相关的virtual tokens作为Prefix,然后训练的时候只更新Prefix部分的参数,而Transformer中的其他部分参数固定。该方法其实和构造Prompt类似,只是Prompt是人为构造的“显式”的提示,并且无法更新参数,而Prefix则是可以学习的“隐式”的提示

同时,为了防止直接更新Prefix的参数导致训练不稳定的情况,他们在Prefix层前面加了MLP结构(相当于将Prefix分解为更小维度的Input与MLP的组合后输出的结果),训练完成后,只保留Prefix的参数

第二部分 LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

2.1 什么是LoRA

如此文《LLaMA的解读与其微调:Alpaca-LoRA/Vicuna/BELLE/中文LLaMA/姜子牙/LLaMA 2》中的2.2.3节Alpaca-LoRA:通过PEFT库在消费级GPU上微调「基于LLaMA的Alpaca」所述,在神经网络模型中,模型参数通常以矩阵的形式表示。对于一个预训练好的模型,其参数矩阵已经包含了很多有用的信息。为了使模型适应特定任务,我们需要对这些参数进行微调

LoRA的核心思想是用一种低秩的方式来调整这些参数矩阵。在数学上,低秩意味着一个矩阵可以用两个较小的矩阵相乘来近似,通过论文《LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS》可知

  1. 选择目标层:首先,在预训练神经网络模型中选择要应用LoRA的目标层。这些层通常是与特定任务相关的,如自注意力机制中的查询Q和键K矩阵
  2. 初始化映射矩阵和逆映射矩阵:为目标层创建两个较小的矩阵A和B
    \rightarrow  A是映射矩阵(一般用随机高斯分布初始化,当然实际代码实现时,比如微软的deepspeed chat在用到LoRA时,一开始通过0矩阵占位,然后调用搭配ReLU激活函数的kaiming均匀分布初始化,虽与LoRA原始定义所用的正态分布初始化不同,但此两种初始化方式都可以工作,更多介绍见下面deepspeed chat的代码 ),维度上是降维
    \rightarrow  B是逆映射矩阵(用0矩阵初始化),维度上是升维
    其中,矩阵的大小由LoRA的秩(rank)和alpha值确定
  3. 参数变换:将目标层的原始参数矩阵W通过映射矩阵A和逆映射矩阵B进行变换。计算公式为:W' = W + A * B,这里W'是变换后的参数矩阵
  4. 微调模型:使用新的参数矩阵W'替换目标层的原始参数矩阵W,然后在特定任务的训练数据上对模型进行微调
  5. 梯度更新:在微调过程中,计算损失函数关于映射矩阵A和逆映射矩阵B的梯度,并使用优化算法(如Adam、SGD等)对A和B进行更新
    注意,在更新过程中,原始参数矩阵W保持不变,说白了,训练的时候固定原始PLM的参数,只训练降维矩阵A与升维矩阵B
  6. 重复更新:在训练的每个批次中,重复步骤3-5,直到达到预定的训练轮次(epoch)或满足收敛条件

总之,LoRA的详细步骤包括选择目标层、初始化映射矩阵和逆映射矩阵、进行参数变换和模型微调。在微调过程中,模型会通过更新映射矩阵U和逆映射矩阵V来学习特定任务的知识,从而提高模型在该任务上的性能

2.2 微软DeepSpeed-Chat中对LoRA微调的实现

继续说一下,这个LoRA的应用还是挺广的,比如后续微软推出的DeepSpeed-Chat便用了这个方法

DeepSpeed-Chat的实现中,当设置LoRA的低秩维度lora_dim(如lora_dim=128)时,即认为启用了LoRA训练,则将原始模型中名称含有“deoder.layers.”且为线性层修改为LoRA层,具体操作为:

  1. 将原始结构的weight参数冻结;
  2. 新引入了2个线性层lora_right_weight和lora_left_weight (分别对应上图中的降维矩阵A、升维矩阵B ),可实现先降维至lora_dim再升维回原维度;
  3. LoRA层主要实现了两分支通路,一条分支为已被冻结weight参数的原始结构、另一条分支为新引入的降维再升维线性层组
# applications/DeepSpeed-Chat/training/step1_supervised_finetuning/main.py
# 判断是否启用LoRA模式
if args.lora_dim > 0:
"""
如果启用,则对名称中含有“decoder.layers.”且为线性层的结构部分引入LoRA旁路(实现先降维后升维的2个线性层),
这类结构基本都是attention、信息交互用的inner线性层,
这类结构的Weight参数将被冻结,转而优化LoRA旁路的参数。
"""
    args.lora_module_name = "decoder.layers."
    model = convert_linear_layer_to_lora(model, args.lora_module_name,
                                         args.lora_dim)

# applications/DeepSpeed-Chat/training/utils/module/lora.py
def convert_linear_layer_to_lora(model,
                                 part_module_name,
                                 lora_dim=0,
                                 lora_scaling=1,
                                 lora_droppout=0):
    """
	将名称中带有"decoder.layers."的线性层转换为lora层
	"""
	"""取出模型中参数名含有decoder.layers.的线性层"""
    repalce_name = []
    for name, module in model.named_modules():
        if isinstance(module, nn.Linear) and part_module_name in name:
            repalce_name.append(name)

    for name in repalce_name:
    	"""recursive_getattr实现了从model中根据属性名取出对应原始结构"""
        module = recursive_getattr(model, name)

        """纳入原始结构的参数,实例化lora层"""
        tmp = LinearLayer_LoRA(
            module.weight, lora_dim, lora_scaling, lora_droppout,
            module.bias).to(module.weight.device).to(module.weight.dtype)

        """recursive_getattr实现了将model对应属性的结构换成lora层实例"""
        recursive_setattr(model, name, tmp)
    return model
    
# applications/DeepSpeed-Chat/training/utils/module/lora.py
class LinearLayer_LoRA(nn.Module):
	"""具体的lora层"""
	def __init__(...):
		...
		"""此处的weight和bias即为原始结构中的参数"""
		self.weight = weight
		self.bias = bias
		···

		"""冻结weight部分的参数"""
		self.weight.requires_grad = False
		···
		self.lora_right_weight = nn.Parameter(torch.zeros(columns, lora_dim))
	    self.lora_left_weight = nn.Parameter(torch.zeros(lora_dim, rows))
        ...

	    """初始化LoRA线性层的参数"""
	    self.reset_parameters()

    # 调用reset_parameters(self)做初始化
    def reset_parameters(self):
    	# 降维矩阵与LoRA原始定义所用的(0,\sigma^2)正态分布初始化不同,而是使用的kaiming均匀分布初始化
    	# kaiming服从均匀分布U(-\sqrt{1/in_feature}, +\sqrt{1/in_feature})
        # f_i是矩阵的输入维度,就是nn.Linear(in_features, out_features)中的in_features
        # 对应上面代码中的columns,而这个columns相当于基座模型的hidden_size
        nn.init.kaiming_uniform_(self.lora_right_weight, a=math.sqrt(5))

        # 升维矩阵使用全0初始化
        nn.init.zeros_(self.lora_left_weight)

    def forward(self, input):
    	"""LoRA的正向传播"""
    	···
    	else:
            # F.linear(input, self.weight, self.bias)是使用给定的权重self.weight和偏差self.bias对输入数据input进行线性变换
            # 这个操作等价于input @ self.weight.t() + self.bias,其中@表示矩阵乘法,.t()表示矩阵转置
	    	return F.linear(input, self.weight, self.bias) 
                    # 1,self.lora_dropout(input)对输入进行了随机的dropout操作,这是一种正则化手段
                    # 2,对结果进行两次线性变换,一次是@ self.lora_right_weight,然后是@ self.lora_left_weight
                    # 3,乘法部分* self.lora_scaling是对加号后面部分的结果进行缩放
	    			+ (self.lora_dropout(input) @ self.lora_right_weight @ self.lora_left_weight) * self.lora_scaling

再额外分析下 这段代码的最后部分

# applications/DeepSpeed-Chat/training/utils/module/lora.py
class LinearLayer_LoRA(nn.Module):
	"""具体的lora层"""
	···
    def forward(self, input):
    	"""LoRA的正向传播"""
    	···
    	else:
	    	return F.linear(
	                input, self.weight,
	                self.bias) + (self.lora_dropout(input) @ self.lora_right_weight
	                              @ self.lora_left_weight) * self.lora_scaling

常规部分的正向传播由transformers所定义,而LoRA部分的正向传播则由LinearLayer_LoRA(nn.Module)的forward()所定义,即“LoRA层的两条分支结果进行加和”,如下图所示『图源:LoRA,相当于在训练期间,较小的权重矩阵(下图中的A和B)是分开的,但一旦训练完成,权重可以合并到一个新权重矩阵中 

 在代码中体现为

F.linear(input, self.weight, self.bias) + (self.lora_dropout(input) @ self.lora_right_weight @ self.lora_left_weight) * self.lora_scaling

加号左侧为原结构支路,加号右侧为新增支路,self.lora_right_weight self.lora_left_weight 分别为两个新引入线性层的参数

2.3 Huggingface上PEFT库对LoRA、Prefix Tuning、P-Tuning的封装

而Huggingface公司推出的PEFT(Parameter-Efficient Fine-Tuning,即高效参数微调之意) 库也封装了LoRA这个方法,PEFT库可以使预训练语言模型高效适应各种下游任务,而无需微调模型的所有参数,即仅微调少量(额外)模型参数,从而大大降低了计算和存储成本

ModelFull FinetuningPEFT-LoRA PyTorchPEFT-LoRA DeepSpeed with CPU Offloading
bigscience/T0_3B (3B params)47.14GB GPU / 2.96GB CPU14.4GB GPU / 2.96GB CPU9.8GB GPU / 17.8GB CPU
bigscience/mt0-xxl (12B params)OOM GPU56GB GPU / 3GB CPU22GB GPU / 52GB CPU
bigscience/bloomz-7b1 (7B params)OOM GPU32GB GPU / 3.8GB CPU18.1GB GPU / 35GB CPU

且PEFT库 (peft/src/peft/peft_model.py at main · huggingface/peft · GitHub)支持以下流行的方法

  1. LoRA,PEFT对LoRA的实现封装见:peft/src/peft/tuners/lora.py at main · huggingface/peft · GitHub,比如对权重的合并代码 (和上面DSC对LoRA权重合并的实现,在本质上是一致的)
    def merge(self): 
        # 检查当前激活的适配器是否在lora_A的键中,如果不在则终止函数
        if self.active_adapter not in self.lora_A.keys():  
            return  
    
        if self.merged:  
            warnings.warn("Already merged. Nothing to do.")
            return  
    
        # 如果激活适配器的r值大于0,表示有可以合并的权重
        if self.r[self.active_adapter] > 0: 
            # 在当前的权重上加上计算得到的新权重
            self.weight.data += (  
                # 转置运算
                transpose(  
                    # 通过矩阵乘法计算新的权重
                    self.lora_B[self.active_adapter].weight @ self.lora_A[self.active_adapter].weight, 
     
                    # 这是转置运算的维度参数
                    self.fan_in_fan_out,  
                )
    
                # 然后将计算得到的权重乘以对应的缩放因子
                * self.scaling[self.active_adapter]  
            )
            self.merged = True
  2. Prefix Tuning: Prefix-Tuning: Optimizing Continuous Prompts for Generation, P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks
  3. P-Tuning: GPT Understands, Too
  4. Prompt Tuning: The Power of Scale for Parameter-Efficient Prompt Tuning

第三部分 QLoRA

// 待更

第四部分 P-Tuning V1/V2

4.1 P-Tuning V1

// 待更

4.2 P-Tuning V2:其关键所在在于引入Prefix-tuning

// 待更

参考文献与推荐阅读

  1. Google关于Adapter Tuning的论文《Parameter-Efficient Transfer Learning for NLP》
  2. 让天下没有难Tuning的大模型-PEFT技术简介
  3. PEFT:在低资源硬件上对十亿规模模型进行参数高效微调
  4. LLaMA的解读与其微调:Alpaca-LoRA/Vicuna/BELLE/中文LLaMA/姜子牙/LLaMA 2
  5. P-Tuning v2大幅提升小模型性能,NER也可promp tuning了
  6. P-tuning:自动构建模版,释放语言模型潜能
  7. Prompt-Tuning——深度解读一种新的微调范式
  8. ..

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/839005.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录算法训练营day25 | 216. 组合总和 III,17. 电话号码的字母组合

目录 216. 组合总和 III 17. 电话号码的字母组合 216. 组合总和 III 难度:medium 类型:回溯 思路: 与77组合类似的题目。 代码随想录算法训练营day24 | 回溯问题,77. 组合_Chamberlain T的博客-CSDN博客 注意两处剪枝。 代码…

__attribute__((noreturn))

GNU C 的一大特色就是__attribute__ 机制,__attribute__ 可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。 语法格式为: __attribute__((att…

gin框架学习

文章目录 配置go环境实现一个简单的web响应服务验证功能gin增加页面以及传递数据 配置go环境 去go官网下载对应的版本 go下载地址 tar -C /usr/local -xzf go1.4.linux-amd64.tar.gz 我们可以编辑 ~/.bash_profile 或者 /etc/profile,并将以下命令添加该文件的末…

linux安装Tomcat部署jpress教程

yum在线安装: 查看tomcat相关的安装包: [rootRHCE ~]# yum list | grep -i tomcat tomcat.noarch 7.0.76-16.el7_9 updates tomcat-el-2.2-api.noarch 7.0.76-16.el7_9 updat…

CI/CD—Docker初入门学习

1 docker 了解 1 Docker 简介 Docker 是基于 Go 语言的开源应用容器虚拟化技术。Docker的主要目标是build、ship and run any app,anywhere,即通过对应用组件的封装、分发、部署、运行等生命周期的管理,达到应用组件级别的一次封装、到处运…

综合与新综合与新型交通发展趋势[75页PPT]

导读:原文《综合与新综合与新型交通发展趋势[75页PPT]》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。 完整版领取方式 完整版领取方式: 如需…

程序员副业指南:怎样实现年入10w+的目标?

大家好,这里是程序员晚枫,全网同名。 今天给大家分享一个大家都感兴趣的话题:程序员可以做什么副业,年入十万? 01 推荐 程序员可以从事以下副业,以获得一年收入10w: 兼职编程:可…

Element的Dialog+Form使用问题

在Element的Dialog中使用表单时&#xff0c;可能会出现以下问题 无法清空表单校验 <el-dialog title"新建资产" :visible.sync"addAssetsFormVisible" close"resetForm(addAssets)">resetForm (formName) {this.$refs[formName].resetFie…

推荐几款主流的Css Reset

CSS Reset CSS Reset&#xff08;CSS重置&#xff09;是一种技术&#xff0c;用于消除不同浏览器之间默认样式的差异&#xff0c;以确保网页在各个浏览器中的显示一致性。由于不同浏览器对元素的默认样式有所不同&#xff0c;使用CSS Reset可以将这些默认样式归零或统一&#x…

idea添加翻译插件并配置有道翻译

1、安装Translation插件 2、 创建有道云应用 有道智云控制台 3、设置idea 4、效果&#xff08;选中文本右键翻译&#xff0c;默认快捷键CtrlShiftY&#xff09;

OpenCV学习笔记--以车道线检测入门

本笔记gitee源代码&#xff1a; https://gitee.com/hongtao-jiang/opencv_lanedetect.git 2023.8.5 文章目录 1、OpenCV安装2、图片的读入、保存3、Canny算法边缘检测4、ROI mask5、霍夫变换6、离群值过滤7、最小二乘拟合8、直线绘制9、视频流读写 1、OpenCV安装 conda管理虚…

如何用限制立方样条(RCS)做生存分析?

一、引言 在医学和统计学领域&#xff0c;生存分析是一种分析个体生命长度和生存时间的重要方法。了解人们生存的期限和影响因素&#xff0c;对于制定健康政策、优化医疗资源的分配以及个体护理方案的制定都至关重要。传统的生存分析方法如Kaplan-Meier曲线和Cox比例风险模型已…

XXL-JOB定时任务框架(Oracle定制版)

特点 xxl-job是一个轻量级、易扩展的分布式任务调度平台&#xff0c;能够快速开发和简单学习。开放源代码并被多家公司线上产品使用&#xff0c;开箱即用。尽管其确实非常好用&#xff0c;但我在工作中使用的是Oracle数据库&#xff0c;因为xxl-job是针对MySQL设计的&#xff…

北邮22信通:第五章 图 最短路径 Dijkstra算法

北邮22信通一枚~ 跟随课程进度每周更新数据结构与算法的代码和文章 持续关注作者 解锁更多邮苑信通专属代码~ 获取更多文章 请访问专栏&#xff1a; 北邮22信通_青山如墨雨如画的博客-CSDN博客 一. 算法核心思想 Dijkstra算法是用来求取图中两个结点之间最短路径的算…

mysql大表的深度分页慢sql案例(跳页分页)

1 背景 有一张表&#xff0c;内容是 redis缓存中的key信息&#xff0c;数据量约1000万级&#xff0c; expiry列上有一个普通B树索引。 -- test.top definitionCREATE TABLE top (database int(11) DEFAULT NULL,type varchar(50) DEFAULT NULL,key varchar(500) DEFAULT NUL…

java+ssm校园高校足球联赛管理系统tdl2g

随着计算机技术发展&#xff0c;计算机系统的应用已延伸到社会的各个领域&#xff0c;大量基于网络的广泛应用给生活带来了十分的便利。所以把足球联赛管理与现在网络相结合&#xff0c;利用计算机搭建足球联赛管理系统&#xff0c;实现足球联赛管理系统的信息化。则对于进一步…

直线导轨在视觉检测设备中的应用

随着科技的不断发展&#xff0c;视觉检测设备已经逐渐代替了传统的人工品检&#xff0c;成为了工业生产中的一部分&#xff0c;在五金配件、塑胶件、橡胶件、电子配件等检测工业零部件表面外观缺陷尺寸方面应用&#xff0c;视觉检测设备具有优势。 直线导轨作为视觉检测设备中重…

EVE-NG MPLS L2VPN static lsp

目录 1 拓扑 2 配置步骤 2.1 配置接口IP 和路由协议 2.2 配置MPLS LDP 2.3 配置L2VPN PW 2.4 验证L2VPN 1 拓扑 2 配置步骤 2.1 配置接口IP 和路由协议 PE1 interface LoopBack 0ip address 1.1.1.9 32 quitinterface GigabitEthernet1/0ip address 10.1.1.1 255.255…

【数理知识】求刚体旋转矩阵和平移矩阵,已知 N>=3 个点在前后时刻的坐标,且这 N>=3 点间距离始终不变代表一个刚体

序号内容1【数理知识】自由度 degree of freedom 及自由度的计算方法2【数理知识】刚体 rigid body 及刚体的运动3【数理知识】刚体基本运动&#xff0c;平动&#xff0c;转动4【数理知识】向量数乘&#xff0c;内积&#xff0c;外积&#xff0c;matlab代码实现5【数理知识】协…

【深度学习_TensorFlow】感知机、全连接层、神经网络

写在前面 感知机、全连接层、神经网络是什么意思&#xff1f; 感知机&#xff1a; 是最简单的神经网络结构&#xff0c;可以对线性可分的数据进行分类。 全连接层&#xff1a; 是神经网络中的一种层结构&#xff0c;每个神经元与上一层的所有神经元相连接,实现全连接。 神经…