参数高效微调PEFT(四)快速入门(IA)3

参数高效微调PEFT(四)快速入门(IA)3

  • 我们已经了解了HuggingFace中peft库的几种高效微调方法。

参数高效微调PEFT(一)快速入门BitFit、Prompt Tuning、Prefix Tuning

参数高效微调PEFT(二)快速入门P-Tuning、P-Tuning V2

参数高效微调PEFT(三)快速入门LoRA、AdaLoRA

  • 今天我们继续了解高效微调方法(IA)3。

1 (IA)3

  • in-context learning(ICL)简单的说就是,在冻结大模型参数的情况下,在输入时,给定一些样本包含数据和标签,同时给一个待预测数据,由模型输出这条数据的预测值。这个过程中模型的参数不发生变化。因此在应用到下游任务时,不需要更新参数,可以扩展到各种各样的任务场景。
    • 但这种模式使得,模型在每次推理过程中,都要处理一次prompts中的示例样本,这也是一种很大的计算消耗。
    • 改变prompts中例子的顺序对最终预测效果的影响很大;
    • 还有的研究发现,prompts中的例子数据和标签在没有正确配对的情况下,对带预测数据的预测结果影响不是很大。
    • 这些现象说明in-context learning 背后的机制,以及如何使in-context learning效果更加鲁棒仍然有待研究。
  • IA3论文主要对ICL和PEFT方法,在少样本场景下进行了严谨的实验对比,发现PEFT方法在取得很高精度的情况下,同时很大降低了计算消耗,可以替代ICL。
    • PEFT极大地减少了训练和保存模型所需的内存和存储需求。PEFT可以显著提高计算效率,并同时实现比ICL更高的准确性。
    • 此外,某些PEFT方法可以直接允许混合任务的批处理
      • 例如,Prompt tuning只需将不同的prompt embeddings连接到批处理中的每个示例即可使单个模型执行多个任务。
      • 混合任务的批处理指的是在一个批量处理的数据集中同时包含多个不同类型的任务或问题。在机器学习领域,通常情况下,一个模型被设计用于解决特定的任务,比如分类、回归等。
      • 而混合任务的批处理则是指在同一个训练批次中包含了多种不同类型的任务,使得模型能够同时学习多种任务之间的相关性,从而提高泛化能力和效率。这种方法可以帮助模型更好地利用数据,加速训练过程,并提高模型在多任务学习和迁移学习方面的性能。
    • 另一方面,重参数化模型的PEFT方法(例如LoRA)对于混合任务的批处理来说是昂贵或繁琐的。
    • 此外,不同的PEFT方法增加了执行推断所需的计算和内存量。例如,adapters实际上在模型中添加了额外的(小型)层,导致计算成本和内存开销的增加。

1.1 (IA)3简介

  • 论文地址:Few-Shot Parameter-Efficient Fine-Tuning is Better and Cheaper than In-Context Learning(2205)

  • 论文创新点主要有两个:

    • 提出了一个新的高效微调方法(IA)3
    • 基于T0模型提出了T-Few,在下游任务中不需要对任务进行额外模型调整,即可进行少样本学习。
  • 新的高效微调方法(IA)3

    • 虽然prompt tuning以及prefix tuning等方法可以满足下游多个任务同批次进行,但是精度不够,而精度够的方法又不允许同批次多任务处理。因此作者开发一个新的PEFT方法—(IA)3。这种PEFT方法是对模型的一些激活层进行抑制或放大,也就是通过点乘一个向量的形式对模型的一部分参数进行加权
    • 下图左侧展示了这些下游任务微调的小参数的添加位置,分别在attention机制中的Key向量和Value向量上,以及前馈神经网络的激活层后。

    在这里插入图片描述

    • 另外,有工作指出预训练这部分参数也可以进一步提高下游任务上的少样本以及零样本性能,因此作者也采纳了预训练的做法。
  • 基于T0模型提出了T-Few

    • 基于团队先前的工作T0,作者修改了损失函数以适应少样本学习的情况,称为T-Few,无需针对特定任务进行调整或修改即可应用于新任务。
    • 所以在模型训练过程,作者使用了不同loss:即上图右侧的语言模型损失 L L M L_{LM} LLM, 负例似然损失 L U L L_{UL} LUL, 长度归一化损失 L L N L_{LN} LLN

1.2 (IA)3源码分析

(IA)3的初始化,和之前LoRA一样,重要的代码就是update_layer。

update_layer中,如果是feedforward,会初始化shape为(1, self.in_features)的向量

如果不是(如query_key_value),就初始化shape为(self.out_features, 1)的向量

    # peft\tuners\ia3.pydef update_layer(self, adapter_name, init_ia3_weights):# Actual trainable parametersif self.is_feedforward:weight = torch.randn((1, self.in_features))else:weight = torch.randn((self.out_features, 1))self.ia3_l.update(nn.ParameterDict({adapter_name: nn.Parameter(weight)}))if init_ia3_weights:self.reset_ia3_parameters(adapter_name)self.to(self.weight.device)

初始化后,就会将之前的module进行替换,替换后的Model如下:

PeftModelForCausalLM((base_model): IA3Model((model): BloomForCausalLM((transformer): BloomModel((word_embeddings): Embedding(250880, 64)(word_embeddings_layernorm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)(h): ModuleList((0-1): 2 x BloomBlock((input_layernorm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)(self_attention): BloomAttention(# 1、query_key_value模块添加IA3(query_key_value): Linear(in_features=64, out_features=192, bias=True(ia3_l): ParameterDict(  (default): Parameter containing: [torch.FloatTensor of size 192x1]))(dense): Linear(in_features=64, out_features=64, bias=True)(attention_dropout): Dropout(p=0.0, inplace=False))(post_attention_layernorm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)(mlp): BloomMLP((dense_h_to_4h): Linear(in_features=64, out_features=256, bias=True)(gelu_impl): BloomGelu()(dense_4h_to_h): Linear(# 2、dense_4h_to_h模块添加IA3in_features=256, out_features=64, bias=True(ia3_l): ParameterDict(  (default): Parameter containing: [torch.FloatTensor of size 1x256])))))(ln_f): LayerNorm((64,), eps=1e-05, elementwise_affine=True))(lm_head): Linear(in_features=64, out_features=250880, bias=False)))
)

最终会调用peft\tuners\ia3.py中Linear的前向传播(如下代码)。

  • 如果是feedforward,ia3_l向量与feedforward的输入相乘,再进入F.linear

  • 如果不是,可学习向量ia3_l与注意力块的输出result相乘

    def forward(self, x: torch.Tensor):previous_dtype = x.dtypeif self.active_adapter not in self.ia3_l.keys():return F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)if self.disable_adapters:if self.merged:self.unmerge()result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)elif not self.merged:# 1、如果是feedforward,ia3_l向量与feedforward的【输入】相乘,再进入F.linearif self.is_feedforward:x = x.to(self.ia3_l[self.active_adapter].dtype)interm = x * self.ia3_l[self.active_adapter].flatten()result = F.linear(interm.to(self.weight.dtype),transpose(self.weight, self.fan_in_fan_out),bias=self.bias,)else:# 2、可学习向量ia3_l与注意力块的【输出】result相乘result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)result = result.to(self.ia3_l[self.active_adapter].dtype) * self.ia3_l[self.active_adapter].flatten()else:result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)result = result.to(previous_dtype)return result

1.3 (IA)3轻量微调bloom模型

同样,我们只需要在加载原模型后、配置训练器前加peft的代码即可。

from peft import IA3Config, TaskType, get_peft_modelconfig = IA3Config(task_type=TaskType.CAUSAL_LM,# bloom默认为["query_key_value", "mlp.dense_4h_to_h"]# 配置在TRANSFORMERS_MODELS_TO_IA3_TARGET_MODULES_MAPPING(peft\utils\other.py)target_modules=["query_key_value", "mlp.dense_4h_to_h"],inference_mode=False, # bloom默认为["mlp.dense_4h_to_h"]# 配置在TRANSFORMERS_MODELS_TO_IA3_FEEDFORWARD_MODULES_MAPPING(peft\utils\other.py)feedforward_modules=["mlp.dense_4h_to_h"]
)print(config)model = get_peft_model(model, config)# 打印可训练参数
model.print_trainable_parameters()# trainable params: 172,032 || all params: 345,940,992 || trainable%: 0.04972871211515749
  • IA3Config常用参数如下:
    • task_type:指定任务类型。如:条件生成任务(SEQ_2_SEQ_LM),因果语言建模(CAUSAL_LM)等。
    • inference_mode:是否在推理模式下使用Peft模型。
    • target_modules:要替换为 IA3 的模块名称列表或模块名称的正则表达式
    • feedforward_modules:target_modules 中被视为前馈(feedforward)层的模块名称列表或模块名称的正则表达式。注意:可学习向量与注意力块的输出激活相乘,但与经典前馈层的输入相乘
    • module_to_save:除了 IA3 层之外要设置为可训练并保存在最终检查点中的模块列表。
  • 配置训练器、模型训练及推理和参数高效微调PEFT(一)快速入门BitFit、Prompt Tuning、Prefix Tuning中2.1一样。
  • 显存消耗情况:
(base) root@autodl-container-adbc11ae52-f2ebff02:~# nvidia-smi  
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.89.02    Driver Version: 525.89.02    CUDA Version: 12.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:B2:00.0 Off |                  N/A |
| 31%   53C    P2   183W / 250W |   2836MiB / 11264MiB |     42%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------++-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

2 PEFT多适配器

2.1 自定义模型适配

如何为自定义的模型适配参数高效微调呢

import torch
from torch import nn
from peft import LoraConfig, get_peft_model, PeftModel# 1、自定义模型
net1 = nn.Sequential(nn.Linear(10, 10),nn.ReLU(),nn.Linear(10, 2)
)# 2、打印参数名
for name, param in net1.named_parameters():print(name)# 0.weight
# 0.bias
# 2.weight
# 2.bias    # 3、这里对nn.Linear(10, 10)模块高效微调
# 利用target_modules指定要添加LoRA的目标模块(支持正则)
config = LoraConfig(target_modules=["0"])
model1 = get_peft_model(net1, config)print(model1)
PeftModel((base_model): LoraModel((model): Sequential((0): Linear(# 可以看到第0层,可以已经发生了替换in_features=10, out_features=10, bias=True(lora_dropout): ModuleDict((default): Identity())(lora_A): ModuleDict((default): Linear(in_features=10, out_features=8, bias=False) # 秩默认为8)(lora_B): ModuleDict((default): Linear(in_features=8, out_features=10, bias=False))(lora_embedding_A): ParameterDict()(lora_embedding_B): ParameterDict())(1): ReLU()(2): Linear(in_features=10, out_features=2, bias=True)))
)

2.2 多适配器加载与切换

一个主模型,多个适配器的情况如何使用

# 我们定义一个主模型main_net
main_net = nn.Sequential(nn.Linear(10, 10),nn.ReLU(),nn.Linear(10, 2)
)# 保存第1个适配器lora1
config1 = LoraConfig(target_modules=["0"])
model2 = get_peft_model(main_net, config1)
model2.save_pretrained("./lora1")# 保存第2个适配器lora2
config2 = LoraConfig(target_modules=["2"])
model2 = get_peft_model(main_net, config2)
model2.save_pretrained("./lora2")
  • 在加载第一个适配器时,可以通过 PeftModel.from_pretrained 方法并指定 adapter_name 参数来给它命名。否则,将使用默认的适配器名称 default
  • 要加载另一个适配器,请使用 PeftModel 的 load_adapter() 方法,例如:model.load_adapter(peft_model_path, adapter_name)
  • 要切换适配器,请使用 PeftModel 的 set_adapter() 方法,例如:model.set_adapter(adapter_name)
# 我们重新创建模型,加载适配器
main_net = nn.Sequential(nn.Linear(10, 10),nn.ReLU(),nn.Linear(10, 2)
)# 加载第一个适配器
main_model = PeftModel.from_pretrained(main_net, model_id="./lora1/", adapter_name="lora1")for name, param in main_model.named_parameters():if name in ["base_model.model.0.lora_A.lora1.weight", "base_model.model.0.lora_B.lora1.weight"]:param.data = torch.ones_like(param) * 10print(main_model.active_adapter)
print(main_model(torch.arange(0, 10).view(1, 10).float()))
lora1
tensor([[-18845.8906,  39025.6406]])
# 加载另一个适配器
main_model.load_adapter("./lora2/", adapter_name="lora2")# 切换适配器前
print(main_model.active_adapter)
print(main_model(torch.arange(0, 10).view(1, 10).float()))# 切换适配器后
main_model.set_adapter("lora2")
print(main_model.active_adapter)
print(main_model(torch.arange(0, 10).view(1, 10).float()))
lora1
tensor([[-18845.8906,  39025.6406]]) # 和lora1结果一致
lora2
tensor([[0.3642, 0.7926]])

2.3 禁用适配器

如何获取原始模型的输出结果

  • 要禁用适配器,请使用上下文管理器 disable_adapter(),例如:with model.disable_adapter()
# 切换适配器
main_model.set_adapter("lora1")# 还是lora1结果 tensor([[-18845.8906,  39025.6406]])
print(main_model(torch.arange(0, 10).view(1, 10).float()))# 获取原始结果
with main_model.disable_adapter():# tensor([[0.3642, 0.7926]])print(main_model(torch.arange(0, 10).view(1, 10).float()))

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

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

相关文章

探索 Omost:创新的图像生成AI框架

文章目录 探索 Omost:创新的图像生成AI框架第一部分:背景第二部分:Omost是什么?第三部分:如何安装Omost?第四部分:结合具体场景使用第五部分:总结 探索 Omost:创新的图像…

OceanBase 4.3 特性解析:列存技术

在涉及大规模数据的复杂分析或即时查询时,列式存储是支撑业务负载的关键技术之一。相较于传统的行式存储,列式存储采用了不同的数据文件组织方式,它将表中的数据以列为单位进行物理排列。这种存储模式允许在分析过程中,查询计算仅…

flowable工作流 完成任务代码 及扩展节点审核人(实现多级部门主管 审核等)详解【JAVA+springboot】

低代码项目 使用flowable 工作流 完成任务代码 详解 可以看到 complete()方法 传递了流程变量参数var 前端传递此参数就可以实现 流程中 审批 更新流程变量参数var 也可以进行更多扩展 实现流程中更新表单内容功能 启动流程实例代码 实现对于流程自定义 动态节点审核人 功…

中央空调节能的分户计费系统

中央空调节能 在建筑能耗中,中央空调能耗一般占到了40%---60%的比例,因此如何有效降低空调能耗就成为建筑节能的重中之重。 项目案例描述 山东银座购物广场:为集购物中心、高级酒店式公寓和办公为一体的综合性公共建筑。整体建筑共为地下3层&…

副业变现:Midjourney绘画赚钱的6种方式

今年被称为AI元年,其中最火的两款AI工具非ChatGpt和Midjourney莫属。究其原因,无非两点:第一,它提高了生产力,之前需要两年完成的工作,使用ChatGpt两天就完成。 第二,它带来了副业收入&#x…

JavaScript异步编程简单介绍

JavaScript异步编程是一种编程模式,用于处理需要等待某些操作完成之后才能继续执行的代码。这些操作可以是网络请求、文件读取、定时器等等。 异步编程的目标是避免阻塞代码执行,在等待操作完成的同时,允许其他代码继续执行。 以下是一个使…

Springboot-RabbitMQ 消息队列使用

一、概念介绍: RabbitMQ中几个重要的概念介绍: Channels:信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的 TCP 连接内地虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅…

2021 hnust 湖科大 数字系统设计与VHDL课程 大作业 - 出租车计价器设计

2021 hnust 湖科大 数字系统设计与VHDL课程大作业-出租车计价器设计 描述 大二上的eda考查课的实验,额外实现了停车等待2分钟后收费1元/min。内含项目文件(实测可运行),代码,报告,视频和照片,…

JavaScript函数定义,函数参数,函数调用

JavaScript函数定义: 在JavaScript中,我们可以使用关键字function来定义一个函数。函数定义的一般语法如下: function functionName(parameter1, parameter2, ...){// 函数体 }其中,functionName是函数的名称,可以自定…

功能强大且专业的PDF转换软件PDF Shaper Professional 14.2

PDF Shaper Professional是一款适用于Windows的程序,可让您在计算机上处理PDF文件。 要开始使用PDF Shaper Professional,您需要在Windows计算机上下载并安装该程序。您还应该有合适的驱动程序和编解码器来处理计算机上的文本和图形。 安装程序后&#…

分享一份糟糕透顶的简历,看看跟你写的一样不

最近看了一个人的简历,怎么说呢,前几年这么写没问题,投出去就有回复,但从现在开始,这么写肯定不行了。下面我给大家分享一下内容: 目录 🤦‍♀️这是简历文档截图 🤷‍♀️这是基本…

淘宝评论API调用指南,让你购物不再困扰

一、淘宝评论API概述 淘宝评论API是淘宝开放平台提供的一种服务,它允许开发者通过调用API接口获取淘宝商品评论数据,联讯数据从而为用户提供更加丰富和实用的购物决策信息。通过使用淘宝评论API,开发者可以轻松地实现以下功能: …

SwiftUI 利用 Swizz 黑魔法为系统创建的默认对象插入新协议方法(二)

功能需求 在 SwiftUI 的开发中,我们往往需要借助底层 UIKit 的“上帝之手”来进一步实现额外的定制功能。比如,在可拖放(Dragable)SwiftUI 的实现中,会缺失拖放取消的回调方法让我们这些秃头码农们“欲哭无泪” 如上图所示,我们在拖放取消时将界面中的一切改变都恢复如初…

slf4j等多个jar包冲突绑定的排查方法使用IDEA的maven help解决

1.安装 2.使用maven help解决,找到对应包存在的冲突 使用exclude直接解决即可

【人工智能】第四部分:ChatGPT的技术实现

人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…

dnf手游版游玩感悟

dnf手游于5月21号正式上线,作为一个dnf端游老玩家,并且偶尔上线ppk,自然下载了手游版,且玩了几天。 不得不说dnf手游的优化做到了极好的程度。 就玩法系统这块,因为dnf属于城镇地下城模式,相比…

前端工程化工具系列(三)—— Stylelint(v16.6.1):CSS/SCSS 代码质量工具

Stylelint 是 CSS/SCSS 的静态分析工具,用于检查其中的违规和错误。 1. 环境要求 v16 以上的 Stylelint,支持 Node.js 的版本为 v18.12.0。 在命令行工具中输入以下内容后回车,来查看当前系统中 Node.js 的版本。 node -vNode.js 推荐使用…

Shell脚本快速入门

为什么要学shell?能做什么? 答:CI/CD 持续集成,自动化部署作业方式,需要将一系列linux命令程序化,shell 就能做到。

13. 《C语言》——【strlen函数的使用和模拟实现】

文章目录 前言strlen函数strlen函数的使用strlen函数的3种方法实现方法1方法2方法3 总结 前言 各位老板好~ , 今天我们讲解strlen函数如何去使用以及如何去模拟实现strlen函数。希望各位老板能够给一个点赞和一个大大的关注,感谢各位老板!str…

塑料焊接机熔深对激光焊接质量有什么影响

塑料焊接机的熔深对焊接质量具有直接且显著的影响。以下是熔深对焊接质量影响的详细解释: 1. 焊接强度:熔深直接决定了焊缝的截面积,从而影响焊接接头的强度。较深的熔深意味着焊缝的截面积更大,可以提供更强的结合力,…