当前位置:   article > 正文

参数高效微调PEFT(三)快速入门LoRA、AdaLoRA_使用peft进行adalora

使用peft进行adalora

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

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

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

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

  • 今天我们继续了解大火的高效微调方法LoRA以及改进的AdaLoRA。
  • 另外,QLoRA是模型量化 (Quantilization) 与LoRA的结合,我们会在后面低精度微调时(微调Chat GLM3的7B模型)进行介绍。
  • 注意:我们到目前都是以单精度FP32加载模型,模型本身占用的显存大小并没有改变。

1 LoRA

  • 将小型网络模块添加到PLMs中,保持基础模型保持不变的情况下仅针对每个任务微调这些模块,可以用于所有任务。
  • 这样,只需引入和更新少量任务特定的参数,就可以适配下游的任务,大大提高了预训练模型的实用性。如:Adapter tuning、Prefix tuning、Prompt Tuning等。
  • 这类方法虽然大大减少了内存消耗,但是这些方法存在一些问题。
    • 比如:Adapter tuning引入了推理延时;
    • Prefix tuning或Prompt tuning直接优化Prefix和Prompt是非单调的,比较难收敛,并且消耗了输入的token。
  • LoRA训练完成后,可以将两个低秩矩阵与原始模型中的权重进行合并,合并后的模型与原始模型无异,避免了推理期间Prompt系列方法带来的额外计算量

1.1 LoRA简介

  • 论文地址:LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS (2106)

  • LORA的核心思想就是通过低秩分解来模拟参数的改变量,从而以极小的参数量来实现大模型的间接训练。

    • 预训练模型中存在一个极小的内在维度,这个内在维度是发挥核心作用的地方。
    • 在微调的过程中,权重的更新依然也有如此特点,即也存在一个内在维度 (内在秩)。
  • 如下图所示,可训练层维度和预训练模型层维度一致为d,先将维度d通过全连接层降维至r,再从r通过全连接层映射回d维度。其中,r<<d,r是矩阵的秩,这样矩阵计算就从 d × d d × d d×d变为 d × r + r × d d × r + r × d d×r+r×d,参数量减少很多。

    • 在下游任务训练时,固定模型的其他参数,只优化新增的两个矩阵的权重参数,将PLM跟新增的通路两部分的结果加起来作为最终的结果(两边通路的输入、输出维度都是一致的),即 h = W x + B A x h=Wx+BAx h=Wx+BAx
    • 第一个矩阵的A的权重参数会通过高斯函数初始化(注意:A矩阵不能初始化为零矩阵);
    • 第二个矩阵的B的权重参数则会初始化为零矩阵,这样能保证训练开始时新增的通路 B A = 0 BA=0 BA=0,从而对模型结果没有影响。

在这里插入图片描述

  • 训练完成后,可以将两个低秩矩阵与原始模型中的权重进行合并,合并后的模型与原始模型无异,避免了推理期间Prompt系列方法带来的额外计算量

  • 我们知道Transformer的权重矩阵包括Attention模块里用于计算query,key,value的Wq、Wk、Wv、多头attention的Wo以及MLP层的权重矩阵。如下图,LoRA只应用于Attention模块中的4种权重矩阵,而且通过消融实验发现同时调整 Wq 和 Wv 会产生最佳结果。

在这里插入图片描述

  • 如下图所示,可以发现保证权重矩阵的种类的数量比起增加隐藏层维度r更为重要,增加r并不一定能覆盖更加有意义的子空间。

在这里插入图片描述

  • 通常情况下,r选择为4,8,16即可,peft库中默认为8。

在这里插入图片描述

  • 在众多数据集上LoRA在只训练极少量参数的前提下,最终在性能上能和全量微调匹配,甚至在某些任务上优于全量微调。

1.2 LoRA源码分析

我们在peft库默认配置下(LoraConfig(task_type=TaskType.CAUSAL_LM)),进行源码分析。

from peft import LoraConfig, TaskType, get_peft_model
config = LoraConfig(task_type=TaskType.CAUSAL_LM)
model = get_peft_model(model, config)
  • 1
  • 2
  • 3

LoRA的代码主要分为三个部分:初始化、推理和参数合并。

1.2.1 LoRA初始化分析

我们先看下LoRA初始化,默认设置下,LoRA为线性层class Linear(nn.Linear, LoRALayer)

  • LoRA仅支持nn.Linear、nn.Embedding和nn.Conv2d,微调线性层是常见的做法。

  • 通过MAPPING找到PeftModelForCausalLM进行初始化

# peft\mapping.py

def get_peft_model(model: PreTrainedModel, peft_config: PeftConfig, adapter_name: str = "default") -> PeftModel:
    ......
    # 1、判断任务类型 如果不是['SEQ_CLS', 'SEQ_2_SEQ_LM', 'CAUSAL_LM', 'TOKEN_CLS', 'QUESTION_ANS', 'FEATURE_EXTRACTION']里类型 而且 不是prompt_learning
    if peft_config.task_type not in MODEL_TYPE_TO_PEFT_MODEL_MAPPING.keys() and not peft_config.is_prompt_learning:
        return PeftModel(model, peft_config, adapter_name=adapter_name)
    # 2、prompt_learning
    if peft_config.is_prompt_learning:
        peft_config = _prepare_prompt_learning_config(peft_config, model_config)
    # 3、通过mapping找到peft.peft_model.PeftModelForCausalLM
    return MODEL_TYPE_TO_PEFT_MODEL_MAPPING[peft_config.task_type](model, peft_config, adapter_name=adapter_name)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 然后我们会进入PeftModelForCausalLM的初始化,此时会调用父类PeftModel的初始化方法
# peft\peft_model.py
class PeftModelForCausalLM(PeftModel):
    
    def __init__(self, model, peft_config: PeftConfig, adapter_name="default"):
        # 调用父类PeftModel的初始化方法
        super().__init__(model, peft_config, adapter_name)
        self.base_model_prepare_inputs_for_generation = self.base_model.prepare_inputs_for_generation
    
    ......

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 父类PeftModel的初始化中,会进行LoraModel的初始化;在LoraModel中,会进行父类BaseTuner的初始化
# peft\peft_model.py
class PeftModel(PushToHubMixin, torch.nn.Module):

    def __init__(self, model: PreTrainedModel, peft_config: PeftConfig, adapter_name: str = "default"):
        super().__init__()
        self.base_model = model
        self.config = getattr(self.base_model, "config", {"model_type": "custom"})
        self.modules_to_save = None
        self.peft_config = {}
        self.active_adapter = adapter_name
        self.peft_type = peft_config.peft_type
        if not peft_config.is_prompt_learning:
            # 不是prompt_learning,我们进入此分支
            self.peft_config[adapter_name] = peft_config
            # 通过PEFT_TYPE_TO_MODEL_MAPPING我们获取peft.tuners.lora.LoraModel
            # 然后会进入LoraModel的初始化
            self.base_model = PEFT_TYPE_TO_MODEL_MAPPING[peft_config.peft_type](
                self.base_model, self.peft_config, adapter_name
            )
            self.set_additional_trainable_modules(peft_config, adapter_name)
        else:
            self.add_adapter(adapter_name, peft_config)
        ......

        
# peft\tuners\lora.py
class LoraModel(BaseTuner):
    
    def __init__(self, model, config, adapter_name) -> None:
        # 进行BaseTuner的初始化
		super().__init__(model, config, adapter_name)    
        
    .....    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 在父类BaseTuner中,会调用inject_adapter方法。
  • inject_adapter中遍历每一个key,调用_create_and_replace,
  • target默认为线性层,会调用_create_new_module方法,返回一个new_module,然后将旧的module替换为new_module ,即加了LoRA后的module。
  • create_new_module会进行new_module = Linear(adapter_name, in_features, out_features, bias=bias, **kwargs),即peft\tuners\lora.py中Linear的初始化。
# peft\tuners\tuners_utils.py
class BaseTuner(nn.Module, ABC):
    def __init__(self, model, peft_config: Union[PeftConfig, dict[str, PeftConfig]], adapter_name: str) -> None:
        super().__init__()

        self.model = model
        ......
        # 会掉用inject_adapter方法
        self.inject_adapter(self.model, adapter_name)

        # Copy the peft_config in the injected model.
        self.model.peft_config = self.peft_config
    ......     
    
    
 # peft\tuners\lora.py
 def inject_adapter(self, model: nn.Module, adapter_name: str):
        peft_config = self.peft_config[adapter_name]
        is_target_modules_in_base_model = False
        key_list = [key for key, _ in model.named_modules()]

        model_config = getattr(model, "config", {"model_type": "custom"})
        if hasattr(model_config, "to_dict"):
            model_config = model_config.to_dict()
        # 获取高效微调的默认设置项
        peft_config = self._prepare_adapter_config(peft_config, model_config)
        
        # 遍历每一个key
        for key in key_list:
            
            is_target_modules_in_base_model = True
            # 例如,key= 'transformer.h.0.self_attention.query_key_value'
            # parent = BloomAttention(
            #  (query_key_value): Linear(in_features=64, out_features=192, bias=True)
            #  (dense): Linear(in_features=64, out_features=64, bias=True)
            #  (attention_dropout): Dropout(p=0.0, inplace=False)
            # )
            
            # target = Linear(in_features=64, out_features=192, bias=True)
            # target_name = 'query_key_value'
            parent, target, target_name = _get_submodules(model, key)

            optionnal_kwargs = {
                "loaded_in_8bit": getattr(model, "is_loaded_in_8bit", False),
                "loaded_in_4bit": getattr(model, "is_loaded_in_4bit", False),
                "current_key": key,
            }
            # 
            self._create_and_replace(peft_config, adapter_name, target, target_name, parent, **optionnal_kwargs)

        ......

                    
    # peft\tuners\lora.py
	def _create_and_replace(
        self,
        lora_config,
        adapter_name,
        target,
        target_name,
        parent,
        **optionnal_kwargs,
    ):
        ......

        # TODO: better deal with that
        if isinstance(target, LoraLayer) and isinstance(target, torch.nn.Conv2d):
            target.update_layer_conv2d(
                adapter_name,
                lora_config.r,
                lora_config.lora_alpha,
                lora_config.lora_dropout,
                lora_config.init_lora_weights,
            )
        ......
        else:
            # target默认为线性层,会进入此分支
            new_module = self._create_new_module(lora_config, adapter_name, target, **kwargs)   
            # 此方法会将原始module进行替换,替换为加上lora后的
            self._replace_module(parent, target_name, new_module, target)                    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
# 高效微调的默认设置项
LoraConfig(
    peft_type=<PeftType.LORA: 'LORA'>           # peft的类型
  , auto_mapping=None
  , base_model_name_or_path=''
  , revision=None
  , task_type=<TaskType.CAUSAL_LM: 'CAUSAL_LM'> # 任务类型
  , inference_mode=False
  , r=8                                # 秩大小,一般4,8,16,小数据集一般设置更小的r(1,2)
  , target_modules=['query_key_value'] # 指定应用lora的目标模块,Bloom模型默认为query_key_value,可以使用正则
  , lora_alpha=8         # 尺度缩放参数,ΔW按α/r缩放,即scaling[adapter_name]=lora_alpha/r,默认为1
  , lora_dropout=0.0     # lora层的dropout比率
  , fan_in_fan_out=False
  , bias='none'          # 偏差可以是“无”、“全部”或“lora_only”。如果是“all”或“lora_only”,则相应的偏差将在训练期间更新
  , modules_to_save=None # 全量微调时,模型的layer名称
  , init_lora_weights=True
  , layers_to_transform=None
  , layers_pattern=None
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • Linear初始化中,重要的代码就是update_layer

  • update_layer中,就会初始化秩为r的可训练参数A和B

  • new_module = Linear(
      in_features=64, out_features=192, bias=True
      (lora_dropout): ModuleDict(
        (default): Identity()
      )
      (lora_A): ModuleDict(
        (default): Linear(in_features=64, out_features=8, bias=False)
      )
      (lora_B): ModuleDict(
        (default): Linear(in_features=8, out_features=192, bias=False)
      )
      (lora_embedding_A): ParameterDict()
      (lora_embedding_B): ParameterDict()
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 上面就是Linear初始化后的模块,即new_module。初始化后,就会将之前的module进行替换。

# 替换前
BloomAttention(
  (query_key_value): Linear(in_features=64, out_features=192, bias=True)
  (dense): Linear(in_features=64, out_features=64, bias=True)
  (attention_dropout): Dropout(p=0.0, inplace=False)
)

# 通过下面代码进行替换
# peft\tuners\lora.py中的_create_and_replace方法
new_module = self._create_new_module(lora_config, adapter_name, target, **kwargs)
self._replace_module(parent, target_name, new_module, target)

# 替换后
BloomAttention(
  (query_key_value): Linear(
    in_features=64, out_features=192, bias=True
    (lora_dropout): ModuleDict(
      (default): Identity()
    )
    (lora_A): ModuleDict(
      (default): Linear(in_features=64, out_features=8, bias=False)
    )
    (lora_B): ModuleDict(
      (default): Linear(in_features=8, out_features=192, bias=False)
    )
    (lora_embedding_A): ParameterDict()
    (lora_embedding_B): ParameterDict()
  )
  (dense): Linear(in_features=64, out_features=64, bias=True)
  (attention_dropout): Dropout(p=0.0, inplace=False)
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 上面就是替换前后BloomAttention模块的结构。
# peft\tuners\lora.py
class Linear(nn.Linear, LoraLayer):
    # Lora implemented in a dense layer
    def __init__(
        self,
        adapter_name: str,
        in_features: int,
        out_features: int,
        r: int = 0,
        lora_alpha: int = 1,
        lora_dropout: float = 0.0,
        fan_in_fan_out: bool = False,  # Set this to True if the layer to replace stores weight like (fan_in, fan_out)
        is_target_conv_1d_layer: bool = False,
        **kwargs,
    ):
        init_lora_weights = kwargs.pop("init_lora_weights", True)

        nn.Linear.__init__(self, in_features, out_features, **kwargs)
        LoraLayer.__init__(self, in_features=in_features, out_features=out_features)
        # Freezing the pre-trained weight matrix
        self.weight.requires_grad = False

        self.fan_in_fan_out = fan_in_fan_out
        if fan_in_fan_out:
            self.weight.data = self.weight.data.T

        nn.Linear.reset_parameters(self)
        # 重要代码
        self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights)
        self.active_adapter = adapter_name
        self.is_target_conv_1d_layer = is_target_conv_1d_layer
        ......
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
 def update_layer(self, adapter_name, r, lora_alpha, lora_dropout, init_lora_weights):
        self.r[adapter_name] = r
        self.lora_alpha[adapter_name] = lora_alpha
        if lora_dropout > 0.0:
            # dropout
            lora_dropout_layer = nn.Dropout(p=lora_dropout)
        else:
            # 默认设置
            lora_dropout_layer = nn.Identity()

        self.lora_dropout.update(nn.ModuleDict({adapter_name: lora_dropout_layer}))
        # Actual trainable parameters
        if r > 0:
            # 如果秩大于0,就初始化秩为R的可训练参数A和B
            self.lora_A.update(nn.ModuleDict({adapter_name: nn.Linear(self.in_features, r, bias=False)}))
            self.lora_B.update(nn.ModuleDict({adapter_name: nn.Linear(r, self.out_features, bias=False)}))
            # 指定lora_alpha参数,用于平衡LORA 和 基础模型的贡献
            # 这里scaling默认为1
            self.scaling[adapter_name] = lora_alpha / r
        if init_lora_weights:
            self.reset_lora_parameters(adapter_name)
        self.to(self.weight.device)        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

1.2.2 前向过程

  • 前向过程中,最终会调用BloomForCausalLM的前向传播方法。
LoraModel(
  (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(
            # 在BloomAttention的query_key_value中,使用LoRA进行微调
            (query_key_value): Linear(
              in_features=64, out_features=192, bias=True
              (lora_dropout): ModuleDict(
                (default): Identity()
              )
              (lora_A): ModuleDict(
                (default): Linear(in_features=64, out_features=8, bias=False)
              )
              (lora_B): ModuleDict(
                (default): Linear(in_features=8, out_features=192, bias=False)
              )
              (lora_embedding_A): ParameterDict()
              (lora_embedding_B): ParameterDict()
            )
            (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(in_features=256, out_features=64, bias=True)
          )
        )
      )
      (ln_f): LayerNorm((64,), eps=1e-05, elementwise_affine=True)
    )
    (lm_head): Linear(in_features=64, out_features=250880, bias=False)
  )
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 在BloomAttention中,会调用peft\tuners\lora.py中Linear的前向传播(如下代码)。
  • 注意:self.lora_A和self.lora_B类型为ModuleDict,这是因为LoRA有高级用法,我们以后再介绍:
    • 一个主模型,可以使用多个适配器(默认self.active_adapter=default)
    • 可以切换适配器
    • 可以禁用适配器,获取原始结果
    # peft\tuners\lora.py
    def forward(self, x: torch.Tensor):
        
        previous_dtype = x.dtype
        if self.active_adapter not in self.lora_A.keys():
            return F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)
        
        if self.disable_adapters:
            if self.r[self.active_adapter] > 0 and self.merged:
                self.unmerge()
            result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)
        # 不禁用adapters,且秩r > 0,且不合并   
        elif self.r[self.active_adapter] > 0 and not self.merged:
            # 1、result = torch.Size([1, 48, 192])
            result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)

            x = x.to(self.lora_A[self.active_adapter].weight.dtype)
            # 2、核心代码
            # x = torch.Size([1, 48, 64])
            # self.lora_A['default'] = Linear(in_features=64, out_features=8, bias=False)
            # x经过self.lora_A,torch.Size([1, 48, 8])
            # self.lora_B['default'] = Linear(in_features=8, out_features=192, bias=False)
            # x经过self.lora_B,torch.Size([1, 48, 192])
            # 然后result相加合并,即h = Wx + BAx
            result += (
                self.lora_B[self.active_adapter](
                    self.lora_A[self.active_adapter](self.lora_dropout[self.active_adapter](x))
                )
                * self.scaling[self.active_adapter]
            )
        else:
            result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)

        result = result.to(previous_dtype)

        return result
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

1.3 LoRA轻量微调bloom模型

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

from peft import LoraConfig, TaskType, get_peft_model

config = LoraConfig(
	task_type=TaskType.CAUSAL_LM
	# , target_modules=".*\.1.*query_key_value" 指定要添加LoRA的目标模块(支持正则)
	# , modules_to_save=["word_embeddings"] 全量微调时,模型的layer名称
	, modules_to_save=None
)

model = get_peft_model(model, config)

# 打印可训练参数
model.print_trainable_parameters()

trainable params: 786,432 || all params: 346,555,392 || trainable%: 0.22692822508443325
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
(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:41:00.0 Off |                  N/A |
| 35%   59C    P2   143W / 250W |   2810MiB / 11264MiB |     31%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

1.4 模型保存

LoRA训练完成后,可以将两个低秩矩阵与原始模型中的权重进行合并,合并后的模型与原始模型无异,避免了推理期间Prompt系列方法带来的额外计算量

from transformers import AutoModelForCausalLM, AutoTokenizer

from peft import PeftModel


# 1、加载基础模型
model_path = r'/root/autodl-fs/models/langboat/bloom-389m-zh'

model = AutoModelForCausalLM.from_pretrained(model_path, low_cpu_mem_usage=True)
tokenizer = AutoTokenizer.from_pretrained(model_path)

# 2、加载PeftModel
p_model = PeftModel.from_pretrained(model, model_id="./chatbot/checkpoint-500/")

# 3、模型合并
merge_model = p_model.merge_and_unload()

# 4、完整模型保存,此时大小就很大了,下次加载时候可以用AutoModelForCausalLM直接加载
merge_model.save_pretrained("./chatbot/merge_model")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2 AdaLoRA

2.1 AdaLoRA简介

  • 论文地址:ADAPTIVE BUDGET ALLOCATION FOR PARAMETER EFFICIENT FINE-TUNING(23.03)

  • LoRA可以达到与完全微调几乎相当的性能,但是也存在一些问题。

    • LoRA需要预先指定每个增量矩阵的本征秩 r 相同,忽略了在微调预训练模型时,权重矩阵的重要性在不同模块和层之间存在显著差异
    • 并且只训练了Attention,没有训练FFN,事实上FFN更重要。
  • 如下图a所示,将可微调参数全部放在FFN的效果要好于放在attention矩阵中的效果;如下图b所示,同时微调高层参数的效果会比微调底层参数的效果更好。因此,作者提出了AdaLoRA,它根据权重矩阵的重要性得分,在权重矩阵之间自适应地分配参数预算。

在这里插入图片描述

  • AdaLORA主要包含两个模块:

    • (i) SVD形式参数更新(SVD-based adaptation):直接将增量矩阵Δ参数化为SVD的形式,避免了在训练过程中进行SVD计算带来的资源消耗;

    在这里插入图片描述

    • (ii) 基于重要程度的参数分配(Importance-aware rank allocation): 裁剪一些冗余的奇异值。

      具体原理可以参考:AdaLoRA(Adaptive LoRA)详解

2.2 AdaLoRA轻量微调bloom模型

  • 这里我们没有分析AdaLoRA的源码,直接进行轻量微调,有兴趣的可以分析下源码。

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

from peft import get_peft_model, TaskType, AdaLoraConfig

config = AdaLoraConfig(task_type=TaskType.CAUSAL_LM
                            , r=8
                            , lora_alpha=8
                            , lora_dropout=0
                            , target_modules=["query_key_value"]
                            )


model = get_peft_model(model, config)

# 打印可训练参数
model.print_trainable_parameters()

trainable params: 1,179,936 || all params: 346,948,920 || trainable%: 0.3400892557901607
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
(base) root@autodl-container-adbc11ae52-f2ebff02:~/autodl-tmp/transformers-code/03-PEFT# 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:41:00.0 Off |                  N/A |
| 33%   54C    P2   119W / 250W |   2816MiB / 11264MiB |     32%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/909156
推荐阅读
相关标签
  

闽ICP备14008679号