参数高效微调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.py
    def update_layer(self, adapter_name, init_ia3_weights):
        # Actual trainable parameters
        if 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模块添加IA3
                in_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.dtype

        if 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.linear
            if 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_model

config = 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) * 10


print(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()))

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux基础命令[28]-chmod

文章目录 1. chmod 命令说明2. chmod 命令语法3. chmod 命令示例3.1 使用数字赋权3.2 使用字母赋权3.3 -R(一起修改子目录) 4. 总结 1. chmod 命令说明 chmod:为文件或文件夹赋予权限。Linux-文件或目录权限。基本信息如下: Usa…

目标2亿欧元!四年两次募资,全球最早专注于量子投资的Quantonation再次加码

Quantonation Ventures 是全球第一家专注于深度物理和量子技术的早期风险投资公司。4月10日,该公司宣布其第二只专门用于量子技术的早期基金 Quantonation II 首次募资完成,目前已募资 7000 万欧元,而目标为 2 亿欧元。 首次募资就募到了将…

redis之发布与订阅

华子目录 什么是发布与订阅?常用命令psubscribe pattern1 [pattern2...]subscribe channel1 [channel2...]publish channel messagepunsubscribe pattern1 [pattern2...]unsubscribe [channel1 [channel2...]]pubsub subcommand argument1 [argument2...] 示例1示例…

C/C++动态内存管理(new与delete)

目录 1. 一图搞懂C/C的内存分布 2. 存在动态内存分配的原因 3. C语言中的动态内存管理方式 4. C内存管理方式 4.1 new/delete操作内置类型 4.2 new/delete操作自定义类型 1. 一图搞懂C/C的内存分布 说明: 1. 栈区(stack):在…

微信视频号上是怎么开店铺的?从入驻到发货,具体流程来了!

这两年,视频号逐渐走入大家视野 单价高,商家又少,很多类目都属于空白无商家入驻阶段,于是好多商家,都准备去视频号小店这个新“黑马”发展发展 那视频号是怎么开店卖东西的?今天我就来给大家分享一下&…

list~模拟实现

目录 list的介绍及使用 list的底层结构 节点类的实现 list的实现 构造函数 拷贝构造 方法一:方法二: 析构函数 赋值重载 insert / erase push_/pop_(尾插/尾删/头插/头删) begin和end(在已建立迭代器的基础上) 迭代…

“神经网络之父”和“深度学习鼻祖”Geoffrey Hinton

“神经网络之父”和“深度学习鼻祖”Geoffrey Hinton在神经网络领域数十年如一日的研究,对深度学习的推动和贡献显著。 一、早期贡献与突破 反向传播算法的引入:Hinton是将反向传播(Backpropagation)算法引入多层神经网络训练的…

客观评价一下GPT-4o

评价GPT-4o(即OpenAI发布的升级版语言模型),以下是上大学网(www.sdaxue.com)从技术能力与创新性、性能与效率、功能实用性与用户体验等几个维度进行评价,不周之出,请大家指正。 技术能力与创新性…

百万总奖池 | 浦源大模型挑战赛(夏季赛)·安全可信赛道正式启动!

随着技术的不断进步,人工智能技术正迅速成为促进社会进步的新质生产力,大模型也成为了“炙手可热”的日常工具,彻底改变了我们与信息之间的互动方式。 然而,随着大模型能力的日益增强,其潜在的风险也日渐凸显&#xf…

网络编程(七)

网络编程(七) UNIX域套接字(本地间进程间通信的技术)(S文件)基于TCP传输基于UDP传输 UNIX域套接字(本地间进程间通信的技术)(S文件) socket同样也可以用于本…

Python打印当前目录下,所有文件名的首字母

代码如下: #!/usr/bin/env python3 """ 按顺序打印当前目录下,所有文件名的首字母(忽略大小写) """ import sys from pathlib import Pathdef main() -> None:ps Path(__file__).parent.glob(…

计算机网络(网络原理及引用)之路由器的基本配置(详细~)

实验目的 熟悉路由各接口的外观、接口的功能、接口的表示方法;掌握带外的管理方法:通过接口console配置;掌握带内的管理方法:通过方式telnet配置;掌握带内的管理方法:通过方式web配置; 路由器…

i.MX8MP平台开发分享(GPC控制器篇)

1.概述 整体来说,i.MX8MP中的电源是由General Power Controller (GPC) 来控制的。GPC可以提供各种电源模式的控制,如低功耗模式、深度睡眠模式等等。GPC包含两个模块,一个是系统模式控制器(SMC),控制系统的…

项目优化方案之---实现邮箱用户登录

之前的项目中我写的基于SpringBoot和Vue的全栈项目已经实现了基本的用户接口开发, 不过其代码的功能单一,而且写的也是有不少漏洞(基本就像刚接手的代码*山一样) 那之后的几篇文章都来分享一下如何优化项目(每一章都独…

【数据结构】链式二叉树(超详细)

文章目录 前言二叉树的链式结构二叉树的遍历方式二叉树的深度优先遍历前序遍历(先根遍历)中序遍历(中根遍历)后序遍历(后根遍历) 二叉树的广度优先遍历层序遍历 二叉树链式结构接口实现二叉树结点个数二叉树叶子结点个数二叉树的深度(高度)二叉树第k层结…

植物大战僵尸杂交版下载链接

前言 植物大战僵尸杂交版是 潜艇伟伟迷 制作并免费向大家开放畅玩并且持续更新关卡。 下载教程 1.打开作者主页:https://space.bilibili.com/97213827/dynamic 2.作者置顶发布的是最新版,直接打开链接安装就好了 3.下载链接:https://pan.qu…

DL-33G电流继电器 新型导轨安装 JOSEF约瑟

用途 DL-30系列电流继电器,用于电机、变压器和输电线的过负荷和短路保护线路中,作为起动元件。 技术参数 按整定值的范围来分:每整定值的动作误差不大于6% 继电器刻度极限误差不大于6%。 动作值的变差不大于6% 对于DL-31、32、33、34电流继电器的返…

【第3章】SpringBoot实战篇之登录接口(含JWT和拦截器)

文章目录 前言一、JWT1. 什么是JWT2. 使用场景3. 结构3.1 Header3.2 Payload3.3 Signature 4. 使用 二、案例1.引入库2.JwtUtils3. UserController14. ArticleController 三、拦截器1. 定义拦截器2. 注册拦截器 四、测试1. 登录2. 无token3. 有token4. 全局配置 总结 前言 前面…

vscode怎么点击路径直接跳转对应文件

在vue项目中经常要引入工具类、组件、模版等,想要直接去看对应文件,只能自己找到对应路径再去打开。 我们可用在js项目中创建一个 jsconfig.json文件,TS项目可以创建tsconfig.json 文件代码 {"compilerOptions": {"baseUrl&…

批量归一化(BN)和层归一化(LN)的区别

批量归一化(Batch Normalization, BN)和层归一化(Layer Normalization, LN)是深度学习中常用的两种归一化技术,它们主要用于解决训练过程中的内部协变量偏移问题,加速模型收敛和提高稳定性。 1. 为什么需要…