2023年的深度学习入门指南(12) - PEFT与LoRA

news/2024/10/30 19:37:29/

2023年的深度学习入门指南(12) - PEFT与LoRA

大家都知道,大模型的训练需要海量的算力。其实,即使是只对大模型做微调训练,也是需要大量的计算资源的。

有没有用更少的计算资源来进行微调的方法呢?研究者研发出了几种被Hugging Face统称为参数高效微调PEFT(Parameter-Efficient Fine-Tuning)的技术。

这其中常用的几个大家应该已经耳熟能详了,比如广泛应用的LoRA技术(Low Rank Adapters,低秩适配),Prefix Tuning技术,Prompt Tuning技术等等。

我们先学习如何使用,然后我们再学习其背后的原理。

用Huggingface PEFT库进行低秩适配

首先我们先安装相关的库,主要有量化用的bitsandbytes库,低秩适配器loralib库,以及加速库accelerate。
另外,PEFT库和transformers库都用最新的版本。

pip install -q bitsandbytes datasets accelerate loralib
pip install -q git+https://github.com/huggingface/transformers.git@main git+https://github.com/huggingface/peft.git

我们来尝试训练一个7B左左的模型,我们选用opt-6.7b模型,它以float16的精度存储,大小大约为13GB!如果我们使用bitsandbytes库以8位加载它们,我们需要大约7GB的显存。

但是,这只是加载用的,在实际训练的时候,16G显存都照样不够用。最终的消耗大约在20G左右。

加载大模型仍然使用我们前面学过的AutoModelForCausalLM.from_pretrained()函数,只是我们需要加上load_in_8bit=True参数来调用bitsandbytes库进行8位量化。

import osos.environ["CUDA_VISIBLE_DEVICES"] = "0"
import torch
import torch.nn as nn
import bitsandbytes as bnb
from transformers import AutoTokenizer, AutoConfig, AutoModelForCausalLMmodel = AutoModelForCausalLM.from_pretrained("facebook/opt-6.7b",load_in_8bit=True,device_map="auto",
)tokenizer = AutoTokenizer.from_pretrained("facebook/opt-6.7b")

下面PEFT就正式出场了,我们先针对所有非int8的模块进行预处理以提升精度:

from peft import prepare_model_for_int8_trainingmodel = prepare_model_for_int8_training(model)

我们再配置下LoRA的参数,参数的具体含义我们后面结合原理再讲。

from peft import LoraConfig, get_peft_modelconfig = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj", "v_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM"
)model = get_peft_model(model, config)

我们选用名人名言数据集作为训练数据:

from datasets import load_datasetdata = load_dataset("Abirate/english_quotes")
data = data.map(lambda samples: tokenizer(samples["quote"]), batched=True)

然后就可以开始训练了:

trainer = transformers.Trainer(model=model,train_dataset=data["train"],args=transformers.TrainingArguments(per_device_train_batch_size=4,gradient_accumulation_steps=4,warmup_steps=100,max_steps=200,learning_rate=2e-4,fp16=True,logging_steps=1,output_dir="outputs",),data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)
trainer.train()

最后,我们做一个推理测试下效果:

batch = tokenizer("Two things are infinite: ", return_tensors="pt")with torch.cuda.amp.autocast():output_tokens = model.generate(**batch, max_new_tokens=50)print("\n\n", tokenizer.decode(output_tokens[0], skip_special_tokens=True))

输出的结果如下:

 Two things are infinite:  the universe and human stupidity; and I'm not sure about the universe.  -Albert Einstein
I'm not sure about the universe either.

基本上,我们除了配置了一个LoRA参数之外什么也没干。

LoRA的原理

LoRA的思想是将原始的权重矩阵分解为两个低秩矩阵的乘积,这样就可以大大减少参数量。其本质思想还是将复杂的问题拆解为简单的问题的组合。
LoRA通过注入优化后的秩分解矩阵,将预训练模型参数冻结,减少了下游任务的可训练参数数量,使得训练更加高效。并且在使用适应性优化器时,降低了硬件进入门槛。
因为我们不需要计算大多数参数的梯度或维护优化器状态,而是仅优化注入的、远小于原参数量的秩分解矩阵。

光定量地这么讲,大家没有观感,我们以上面训练的例子来看看LoRA的效果。

我们写一个函数来计算模型中的可训练参数数量:

def print_trainable_parameters(model):"""Prints the number of trainable parameters in the model."""trainable_params = 0all_param = 0for _, param in model.named_parameters():all_param += param.numel()if param.requires_grad:trainable_params += param.numel()print(f"trainable params: {trainable_params} || all params: {all_param} || trainable%: {100 * trainable_params / all_param}")

运行一下:

print_trainable_parameters(model)

输出结果如下:

trainable params: 8388608 || all params: 6666862592 || trainable%: 0.12582542214183376

我们看到,原始的模型参数有66亿多个,但是我们只训练了838多万个,只占了0.125%。

所以这也就是为什么我们经常看到有6b,7b,还有13b参数的大模型了。因为这个量级的模型,刚好可以在一张40G或者80G的A100显卡上训练。甚至在24G的3090上也能训练。

下面我们来解释一下低秩更新的原理。

如图所示,输入为x,x是d维的向量,输出是h。

我们将参数分为冻结的权重 W 0 W_0 W0和可以训练的参数 Δ W \Delta W ΔW。然后我们把 Δ W \Delta W ΔW分解成A和B两个可训练参数的矩阵,其中A矩阵取随机值,而B矩阵全取0.

h = W 0 x + Δ W x = W 0 x + B A x h=W_0 x+\Delta W x=W_0 x+B A x h=W0x+ΔWx=W0x+BAx

其中, W 0 W_0 W0是一个d乘以r维的矩阵, W 0 ∈ R d × k W_0 \in \mathbb{R}^{d \times k} W0Rd×k

为了让B乘以A的结果为输入是d维而输出为k维,B矩阵我们取d行r列,而A矩阵为r行k列,这样一相乘就是d行k列:
B ∈ R d × r , A ∈ R r × k B \in \mathbb{R}^{d \times r}, A \in \mathbb{R}^{r \times k} BRd×r,ARr×k

为了让低秩后的效果更好,r要取一个远小于d和k的值。

为了减少更换r给训练带来的影响,我们再引入一个缩放参数 α \alpha α。我们给 Δ W x \Delta W x ΔWx乘以 α r \frac{\alpha}{r} rα。当使用Adam优化时,如果我们适当地缩放初始化,调整α就大致相当于调整学习率。因此,我们简单地将α设置为我们尝试的第一个r,并不对其进行调整。这种缩放有助于减少在改变r时重新调整超参数的需要。

我们来参照一下前面配置的LoRA config:

from peft import LoraConfig, get_peft_modelconfig = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj", "v_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM"
)model = get_peft_model(model, config)

我们可以看到,r选择的是16,而alpha为32。说明最开始是用32作为r来进行尝试的。后面我们再调参数的时候,就改r而不调整alpha了。

那么,我们为什么只选择了q和v两个参数进行LoRA呢?

我们来看论文中的数据:

取q和k两组参数的效果,还不如只取v一个的效果好。而把q,k,v,o全都训练了,也没有明显的优势。所以就取相对最有效率的q,v两组。

当然,这也不是金科玉律,大家可以在实践中去探索更好的LoRA策略。

小结

LoRA的一个例子就是alpaca-lora项目,其网址为:https://github.com/tloen/alpaca-lora

alpaca-lora是一个使用LoRA技术对Alpaca模型进行轻量化的项目。Alpaca模型是一个基于LLaMA 7B模型的聊天机器人,使用了Instruct数据集进行微调。alpaca-lora的优点是可以在低成本和低资源的情况下,获得与Alpaca模型相当的效果,并且可以在MacBook、Google Colab、Raspberry Pi等设备上运行。alpaca-lora使用了Hugging Face的PEFT和bitsandbytes来加速微调过程,并提供了一个脚本来下载和推理基础模型和LoRA模型。

现在,PEFT和LoRA对我们来说,已经不再陌生了。


http://www.ppmy.cn/news/61394.html

相关文章

武忠祥老师每日一题||定积分基础训练(三)

常用的基本不等式&#xff1a; sin ⁡ x < x < t a n x , x ∈ ( 0 , π 2 ) \sin x<x<\ tan x,x\in(0,\frac{\pi}{2}) sinx<x< tanx,x∈(0,2π​) e x ≥ 1 x , x ∈ ( − ∞ , ∞ ) e^x\ge1x,x\in(-\infty,\infty) ex≥1x,x∈(−∞,∞) x 1 x ≤ ln …

将Python环境迁移到另一台设备上

本方法可以将一台电脑上的python环境迁移到另一台电脑上&#xff0c;可以省去一个一个包pip的麻烦。本文以pytorch的迁移为例。 一、从源环境备份安装包 在原来的电脑的Conda控制台中使用语句 pip freeze > c:\myrequirement.txt 后面跟的参数是文件的路径和文件名&#x…

2023年最新水果DAW编曲软件fl studio21 macOS - 21.0.3.3036简体中文版免费下载支持苹果M1/M2处理器

一直梦想制作自己的音乐(无论是作为一名制作人还是艺术家)&#xff0c;你可能会想你出生在这个时代是你的幸运星。这个水果圈工作室和上一版之间的改进水平确实令人钦佩。这仅仅是FL Studio 21所提供的皮毛。你的音乐项目的选择真的会让你大吃一惊。你以前从未有过这样的多才多…

财报解读:照明行业景气上行,欧普照明已步入增长“快车道”

2023年&#xff0c;随着文旅景观照明复苏&#xff0c;教室照明市场蓬勃发展&#xff0c;双碳战略下照明数字化系统加速建设&#xff0c;照明企业迎来行业曙光。近日&#xff0c;A股绿色照明龙头企业欧普照明股份有限公司&#xff08;下称“欧普照明”&#xff0c;股票代码60351…

你了解这2类神经性皮炎吗?常常预示着这5类疾病!

神经性皮炎属于慢性皮肤病&#xff0c;患者皮肤可出现局限性苔藓样变&#xff0c;同时伴有阵发性瘙痒。神经性皮炎易发生在颈部两侧和四肢伸侧&#xff0c;中年人是高发人群。到目前为止神经性皮炎病因还并不是很明确&#xff0c;不过一部分病人发病前常常出现精神神经方面异常…

「ML 实践篇」模型训练

在训练不同机器学习算法模型时&#xff0c;遇到的各类训练算法大多对用户都是一个黑匣子&#xff0c;而理解它们实际怎么工作&#xff0c;对用户是很有帮助的&#xff1b; 快速定位到合适的模型与正确的训练算法&#xff0c;找到一套适当的超参数等&#xff1b;更高效的执行错…

No input file specified.

使用宝塔创建PHP网站&#xff0c;出现"no input file specified"怎么办&#xff1f; No input file specified. 一.IIS Noinput file specified 方法一&#xff1a;改PHP.ini中的doc_root行&#xff0c;打开ini文件注释掉此行&#xff0c;然后重启IIS 方法二&…

GUN C编译器拓展语法学习笔记(一)GNU C特殊语法部分详解

GNU C特殊语法部分详解 一、指定初始化1、数组初始化2、指定初始化结构体成员3、指定初始化的好处 二、宏构造利器&#xff1a;语句表达式1、表达式、语句和代码块2、语句表达式3、宏定义中的语句表达式 三、typeof 与container_of 宏1、typeof 关键字宏2、Linux内核中的contai…