跟着,ChatGPT 敏捷爆火,引发了大模型的时代革新。然而关于一般群众来说,进行大模型的预练习或者全量微调遥不可及。由此,催生了各种参数高效微调技能,让科研人员或者一般开发者有机会测验微调大模型。

因而,该技能值得咱们进行深入分析其背后的机理,之前共享了大模型参数高效微调技能原理总述的文章。下面给我们共享大模型参数高效微调技能实战系列文章,该系列共六篇文章,相关代码均放置在GitHub:llm-action。

  • 大模型参数高效微调技能实战(一)-PEFT概述及环境搭建
  • 大模型参数高效微调技能实战(二)-Prompt Tuning
  • 大模型参数高效微调技能实战(三)-P-Tuning
  • 大模型参数高效微调技能实战(四)-Prefix Tuning / P-Tuning v2
  • 大模型参数高效微调技能实战(五)-LoRA
  • 大模型参数高效微调技能实战(六)-IA3

本文为大模型参数高效微调技能实战的第五篇。要说谁是当时最盛行的高效微调技能,那毫无疑问是LoRA。之前针对大模型运用LoRA写过的文章也比较多,如下所示:

  • 足够惊艳,运用Alpaca-Lora根据LLaMA(7B)二十分钟完结微调,效果比肩斯坦福羊驼
  • 运用 LoRA 技能对 LLaMA 65B 大模型进行微调及推理
  • 从0到1根据ChatGLM-6B运用LoRA进行参数高效微调
  • 中文LLaMA&Alpaca大言语模型词表扩大+预练习+指令精调

下面将根据 Bloomz-560m 运用 PEFT 库对其进行实战解说。

LoRA 简述

LoRA(论文:LoRA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS),该办法的核心思想便是经过低秩分解来模仿参数的改变量,从而以极小的参数量来实现大模型的直接练习。

在涉及到矩阵相乘的模块,在原始的PLM周围添加一个新的通路,经过前后两个矩阵A,B相乘,第一个矩阵A担任降维,第二个矩阵B担任升维,中间层维度为r,从而来模仿所谓的本征秩(intrinsic rank)。

大模型参数高效微调技术实战(五)-LoRA

可练习层维度和预练习模型层维度一致为d,先将维度d经过全衔接层降维至r,再从r经过全衔接层映射回d维度,其间,r<<d,r是矩阵的秩,这样矩阵核算就从d x d变为d x r + r x d,参数量削减很多。

在下游使命练习时,固定模型的其他参数,只优化新增的两个矩阵的权重参数,将PLM跟新增的通路两部分的成果加起来作为终究的成果(两头通路的输入跟输出维度是一致的),即h=Wx+BAx。第一个矩阵的A的权重参数会经过高斯函数初始化,而第二个矩阵的B的权重参数则会初始化为零矩阵,这样能保证练习开始时新增的通路BA=0从而对模型成果没有影响。

大模型参数高效微调技术实战(五)-LoRA

在推理时,将左右两部分的成果加到一同即可,h=Wx+BAx=(W+BA)x,所以只要将练习完结的矩阵乘积BA跟本来的权重矩阵W加到一同作为新权重参数替换本来PLM的W即可,关于推理来说,不会添加额定的核算资源。

更加详细的介绍可参考之前的文章:大模型参数高效微调技能原理总述(五)-LoRA、AdaLoRA、QLoRA

LoRA 微调实战

为了不影响阅览体验,详细的代码放置在GitHub:llm-action 项目中 peft_lora_clm.ipynb文件,这儿仅列出关键步骤。

第一步,引进必要的库,如:LoRA 配置类 LoraConfig

from peft import get_peft_config, get_peft_model, get_peft_model_state_dict, LoraConfig, TaskType

第二步,创立 LoRA 微调办法对应的配置。

peft_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM, 
    inference_mode=False, 
    r=8, 
    lora_alpha=32, 
    lora_dropout=0.1
)

参数说明:

  • task_type:指定使命类型。如:条件生成使命(SEQ_2_SEQ_LM),因果言语建模(CAUSAL_LM)等。
  • inference_mode:是否在推理形式下运用Peft模型。
  • r: LoRA低秩矩阵的维数。关于秩的挑选,一般,运用4,8,16即可。
  • lora_alpha: LoRA低秩矩阵的缩放系数,为一个常数超参,调整alpha与调整学习率相似。
  • lora_dropout:LoRA 层的丢弃(dropout)率,取值规模为[0, 1)
  • target_modules:要替换为 LoRA 的模块称号列表或模块称号的正则表达式。针对不同类型的模型,模块称号不一样,因而,咱们需求根据详细的模型进行设置,比方,LLaMa的默许模块名为[q_proj, v_proj],咱们也可以自行指定为:[q_proj,k_proj,v_proj,o_proj]。 在 PEFT 中支持的模型默许的模块名如下所示:
TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING = {
    "t5": ["q", "v"],
    "mt5": ["q", "v"],
    "bart": ["q_proj", "v_proj"],
    "gpt2": ["c_attn"],
    "bloom": ["query_key_value"],
    "blip-2": ["q", "v", "q_proj", "v_proj"],
    "opt": ["q_proj", "v_proj"],
    "gptj": ["q_proj", "v_proj"],
    "gpt_neox": ["query_key_value"],
    "gpt_neo": ["q_proj", "v_proj"],
    "bert": ["query", "value"],
    "roberta": ["query", "value"],
    "xlm-roberta": ["query", "value"],
    "electra": ["query", "value"],
    "deberta-v2": ["query_proj", "value_proj"],
    "deberta": ["in_proj"],
    "layoutlm": ["query", "value"],
    "llama": ["q_proj", "v_proj"],
    "chatglm": ["query_key_value"],
    "gpt_bigcode": ["c_attn"],
    "mpt": ["Wqkv"],
}

Transformer的权重矩阵包含Attention模块里用于核算query, key, value的Wq,Wk,Wv以及多头attention的Wo和MLP层的权重矩阵,LoRA只应用于Attention模块中的4种权重矩阵,并且经过融化实验发现一起调整 Wq 和 Wv 会发生最佳成果,因而,默许的模块名根本都为 Wq 和 Wv 权重矩阵。

第三步,经过调用get_peft_model办法包装根底的 Transformer 模型。

model = AutoModelForCausalLM.from_pretrained(model_name_or_path)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

经过 print_trainable_parameters 办法可以查看到 LoRA 可练习参数的数量(仅为786,432)以及占比(仅为0.1404%)。

trainable params: 786,432 || all params: 560,001,024 || trainable%: 0.14043402892063284

PEFT 中 LoRA 相关的代码首要根据微软开源的LoRA的代码,并进行修正使其支持 PyTorch FSDP。 在 PEFT 中, LoRA 模型相关源码如下所示。

class LoraModel(torch.nn.Module):
    def __init__(self, model, config, adapter_name):
        super().__init__()
        self.model = model
        self.forward = self.model.forward
        self.peft_config = config
        self.add_adapter(adapter_name, self.peft_config[adapter_name])
        # transformers models have a .config attribute, whose presence is assumed later on
        if not hasattr(self, "config"):
            self.config = {"model_type": "custom"}
...

LoRA 模型类结构如下所示:

PeftModelForCausalLM(
  (base_model): LoraModel(
    (model): BloomForCausalLM(
      (transformer): BloomModel(
        (word_embeddings): Embedding(250880, 1024)
        (word_embeddings_layernorm): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
        (h): ModuleList(
          (0): BloomBlock(
            (input_layernorm): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
            (self_attention): BloomAttention(
              (query_key_value): Linear(
                in_features=1024, out_features=3072, bias=True
                (lora_dropout): ModuleDict(
                  (default): Dropout(p=0.1, inplace=False)
                )
                (lora_A): ModuleDict(
                  (default): Linear(in_features=1024, out_features=8, bias=False)
                )
                (lora_B): ModuleDict(
                  (default): Linear(in_features=8, out_features=3072, bias=False)
                )
                (lora_embedding_A): ParameterDict()
                (lora_embedding_B): ParameterDict()
              )
              (dense): Linear(in_features=1024, out_features=1024, bias=True)
              (attention_dropout): Dropout(p=0.0, inplace=False)
            )
            (post_attention_layernorm): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
            (mlp): BloomMLP(
              (dense_h_to_4h): Linear(in_features=1024, out_features=4096, bias=True)
              (gelu_impl): BloomGelu()
              (dense_4h_to_h): Linear(in_features=4096, out_features=1024, bias=True)
            )
          )
          ...
          (23): BloomBlock(
            ...
          )
        )
        (ln_f): LayerNorm((1024,), eps=1e-05, elementwise_affine=True)
      )
      (lm_head): Linear(in_features=1024, out_features=250880, bias=False)
    )
  )
)

第四步,模型练习的其余部分均无需更改,当模型练习完结之后,保存高效微调部分的模型权重以供模型推理即可。

peft_model_id = f"{model_name_or_path}_{peft_config.peft_type}_{peft_config.task_type}"
model.save_pretrained(peft_model_id)

输出的模型权重文件如下所示:

/data/nfs/llm/model/bloomz-560m_LORA_CAUSAL_LM
├── [ 447]  adapter_config.json
├── [3.0M]  adapter_model.bin
└── [ 147]  README.md
0 directories, 3 files

留意:这儿只会保存经过练习的增量 PEFT 权重。其间,adapter_config.json 为 LoRA 配置文件;adapter_model.bin 为 LoRA 权重文件。

第五步,加载微调后的权重文件进行推理。

from peft import PeftModel, PeftConfig
peft_model_id = f"{model_name_or_path}_{peft_config.peft_type}_{peft_config.task_type}"
config = PeftConfig.from_pretrained(peft_model_id)
# 加载根底模型
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)
# 加载PEFT模型
model = PeftModel.from_pretrained(model, peft_model_id)
# tokenizer编码
inputs = tokenizer(f'{text_column} : {dataset["test"][i]["Tweet text"]} Label : ', return_tensors="pt")
# 模型推理
outputs = model.generate(
        input_ids=inputs["input_ids"], 
        attention_mask=inputs["attention_mask"], 
        max_new_tokens=10, 
        eos_token_id=3
    )
# tokenizer解码
print(tokenizer.batch_decode(outputs.detach().cpu().numpy(), skip_special_tokens=True))

至此,咱们完结了 LoRA 的练习及推理。

结语

本文对 LoRA 根本原理进行了简述;一起,解说了 LoRA 进行模型练习及推理。下文将对 IA3 技能进行实战。

如果觉得我的文章可以可以给您带来协助,等待您的点赞收藏加重视~~