什么是LORA微调?

LoRA 全称为 low-rank adaptation, 低秩自适应微调方法。

paper:LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS
大概思路:借鉴知乎 https://www.zhihu.com/tardis/zm/art/623543497?source_id=1005 的这个图说明一下:

  • LoRA微调什么? 预训练模型是 左边, Lora微调得到的模型是 右边。
    LoRA相当于训练一个新的额外的参数,来学习原参数的知识。
  • LoRA优点:相比于全量微调(full finetune)方法的优点是 LoRA通常只需要更新不到1%的参数就能达到与全量微调相当的效果。
    full finetune:更新所有的预训练网络参数。
    在这里插入图片描述
    例如在下面的例子中,peftmodel 显示lora微调训练的参数量是原来的0.62倍,十分小。
model.print_trainable_parameters()

在这里插入图片描述

为什么我会接触LoRA?

最近在2张40G的GPU上,想要实现基于imdb数据集微调Xlnet-large模型,发现直接load pre-trained model 全量微调压根跑不起来,内存直接爆炸。

  • 内存/存储空间不够

因此,想通过一些高效低参的微调的方法来实现,如LoRA, bitfit(只微调网络中的bias 参数,比lora效果差得多)。

接下来直接上代码介绍怎么基于LoRA微调xlnet-base-cased吧。很简单的过程,但是你要能连接Huggingface,不能连接的话就把数据和模型下到本地吧。

依赖库 提供方 手册
transformer Huggingface https://huggingface.co/docs/transformers/pipeline_tutorial
datasets Huggingface https://huggingface.co/docs/datasets/load_hub
evaluate Huggingface https://huggingface.co/docs/evaluate/transformers_integrations
peft Huggingface https://huggingface.co/docs/peft/quicktour

建议在conda 虚拟环境里跑哈 使用的环境如下:

Framework versions
PEFT 0.9.0
Transformers 4.38.2
Pytorch 2.2.1
Datasets 2.18.0
Tokenizers 0.15.2
python 3.10

1. 安装库

执行以下代码安装相关的库函数,需要翻墙,能够连接上Huggingface :

pip install transformers datasets evaluate peft

2.开始LoRA微调

下载xlnet-base-cased模型-- 用peft封装模型- - 训练参数 — 训练模型 – 上传模型到hub

Train with LoRA
def train_lora():
	# 如果需要将模型推送到hub上的话,需要登录一下hub
    login(token='hf_XXX')  ##hf_XXX 替换成你自己的hub账号的token
    peft_config = LoraConfig(task_type=TaskType.SEQ_CLS,
                             target_modules=['layer_1','layer_2'],
                              inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1)
   #下载xlnet-base-cased模型
    model_base = 'xlnet-base-cased'
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

    model  = AutoModelForSequenceClassification.from_pretrained(model_base, num_labels=2)
    print(model)
	#用peft封装模型
    model = get_peft_model(model,peft_config)
    model.print_trainable_parameters()
    model = model.to(device)
  
    tokenizer = AutoTokenizer.from_pretrained(model_base,max_length=350)
    def preocess_tokenize(exam):
        return tokenizer(exam['text'],truncation=True,padding=True)
    #下载数据集
    imdb = load_dataset('imdb')
    tokenized_imdb = imdb.map(preocess_tokenize,batched=True)
    #设置评估指标
    accuracy = evaluate.load("accuracy")
    def compute_metrics(eval_pred):
        predictions, labels = eval_pred
        predictions = np.argmax(predictions, axis=1)
        return accuracy.compute(predictions=predictions, references=labels)
   #设置训练参数
    bts = 1
    accumulated_step = 2
    training_args = TrainingArguments(
        output_dir=f"imdb_{model_base.replace('-','_')}",
        learning_rate=2e-5,
        per_device_train_batch_size=bts,
        per_device_eval_batch_size=bts,
        num_train_epochs=1,
        weight_decay=0.01,
        evaluation_strategy="epoch",
        save_strategy="epoch",
        load_best_model_at_end=True,
        push_to_hub=True,
        gradient_accumulation_steps=accumulated_step,
    )
   #配置训练器
    trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_imdb["train"],
    eval_dataset=tokenized_imdb["test"],
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
    )
    #训练并上传
    print(f'Start to Train {model_base}')
    trainer.train()
    print('Success!')
    trainer.push_to_hub()
    print('Success to push the model to huggingface')
if __name__ =='__main__':
    train_lora()
inference

以下是我训练号一个peftmodel = Siki-77/imdb_xlnet_base_cased,使用它去判断其他样本的过程如下

from peft import PeftModel, PeftConfig
from transformers import AutoModelForSequenceClassification

config = PeftConfig.from_pretrained("Siki-77/imdb_xlnet_base_cased")
model = AutoModelForSequenceClassification.from_pretrained("xlnet-base-cased")
model = PeftModel.from_pretrained(model, "Siki-77/imdb_xlnet_base_cased")
tokenizer = AutoTokenizer.from_pretrained(model_base,max_length=350)
inputs = tokenizer("Preheat the oven to 350 degrees and place the cookie dough", return_tensors="pt")

with torch.no_grad():
    logits = model(**inputs).logits
label = logits.argmax().item()  # 这里是2分类,因此 label=0 或1 

以上例子结束,代码完全跑的通,很简单的代码流程。

说明下target_modules这个参数(可选)

这一小节回答以下问题:

  • 任意的模型进行LoRA微调的话,需要怎么设置target modules 这个参数呢??
  • 省流回答:target module 只能选择以下几类层的名称
    -torch.nn.Linear, torch.nn.Embedding, torch.nn.Conv2d, transformers.pytorch_utils.Conv1D

本节主要补充一下 peft_config两个关键参数说明:

  peft_config = LoraConfig(
      task_type=TaskType.SEQ_CLS,
      target_modules=['layer_1','layer_2'],
      inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1)
  • task_type表示要微调的模型类型,TaskType.SEQ_CLS表示文本分类模型,
  • target_modules 表示用更少参数代替的层(只允许 linear
  • 其他参数默认

在这里插入图片描述
如,我用的pre-trained model = xlnet-base-cased,它的模型结构如上图所示,一般lora压缩的是堆叠好几层的模块的 linear/conv层,这里只有名为“layer-1”“layer-2”的线性层,因此

target_modules=['layer_1','layer_2'],

如果我设置了unsupport layer的名称为 target modules,将会报错,如下

peft_config = LoraConfig(
               r=16,
               lora_alpha=32,
               lora_dropout=0.05,
               target_modules=["layer_norm"],## "layer_norm" is non-linear layer
               task_type=TaskType.SEQ_CLS, # this is necessary
               )

报错信息如下

Currently, only the following modules are supported: torch.nn.Linear, torch.nn.Embedding, torch.nn.Conv2d, transformers.pytorch_utils.Conv1D

谈一下LoRA的当下的应用

随着GPT-3.5以来,越来越多大模型发布,甚至开源。如果要在本地微调这些大模型的话,基本全量微调是希望渺茫的(除非你的GPU内存真的超足),这种情况下,LoRA简直是天选之子,微调的首选方式。
真的十分有效,当然LoRA也不一定能解决存储不足的问题。
还有一个流行的“微调” promt,不微调参数,而是直接引导模型为我所用,很玄也很难定义怎么prompt才能“为我所用”。
相比于prompt微调,个人感觉LoRA还是可控性比较高的微调。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐