实战Hugging Face PEFT工具
通过两个简单的案例来熟悉peft
库和 bitsandbytes
库的使用,来实现高效微调。
1.PEFT 库 LoRA 实战 - OPT-6.7B
介绍如何使用最新的 peft
库和 bitsandbytes
来以 8-bits 加载大语言模型,并对其进行高效微调。
微调方法将依赖于一种名为“低秩适配器”(LoRA)的方法,与其微调整个模型,您只需要微调这些适配器(Adapter)并在模型中正确加载它们。
(1)加载模型
Facebook opt-6.7b
模型,半精度(float16)模型权重大约需要13GB左右显存。
模型链接:Facebook opt-6.7b
下面我们以8-bits 加载它,只需要大约7GB左右显存。
import os
import torch
import torch.nn as nn
import bitsandbytes as bnb
from transformers import GPT2Tokenizer, AutoConfig, OPTForCausalLM
model_id = "./model/opt-6.7b"
from transformers import AutoModelForCausalLM, AutoTokenizer, set_seed
model = AutoModelForCausalLM.from_pretrained(model_id,load_in_8bit=True,device_map="auto")
tokenizer = GPT2Tokenizer.from_pretrained(model_id)
(2)PEFT 微调前的模型处理
在使用 peft
训练 int8 模型之前,需要进行一些预处理:
- 将所有非
int8
模块转换为全精度(fp32
)以保证稳定性 - 为输入嵌入层添加一个
forward_hook
,以启用输入隐藏状态的梯度计算 - 启用梯度检查点以实现更高效的内存训练
使用 peft
库预定义的工具函数 prepare_model_for_int8_training
,便可自动完成以上模型处理工作。
from peft import prepare_model_for_int8_training
model = prepare_model_for_int8_training(model)
获取当前模型占用的 GPU显存(差值为预留给 PyTorch 的显存)
# 获取当前模型占用的 GPU显存(差值为预留给 PyTorch 的显存)
memory_footprint_bytes = model.get_memory_footprint()
memory_footprint_mib = memory_footprint_bytes / (1024 ** 3) # 转换为 GB
print(f"{memory_footprint_mib:.2f}GB")
#6.80GB
(3)LoRA Adapter 配置
在 peft
中使用LoRA
非常简捷,借助 PeftModel
抽象,我们可以快速使用低秩适配器(LoRA)到任意模型。
通过使用 peft
中的 get_peft_model
工具函数来实现。
关于 LoRA 超参数的说明:
M
a
t
M
u
l
(
B
,
A
)
∗
S
c
a
l
i
n
g
S
c
a
l
i
n
g
=
L
o
R
A
A
l
p
h
a
/
R
a
n
k
MatMul(B,A) * Scaling \\ Scaling = LoRA_Alpha / Rank
MatMul(B,A)∗ScalingScaling=LoRAAlpha/Rank
# 从peft库导入LoraConfig和get_peft_model函数
from peft import LoraConfig, get_peft_model
# 创建一个LoraConfig对象,用于设置LoRA(Low-Rank Adaptation)的配置参数
config = LoraConfig(
r=8, # LoRA的秩,影响LoRA矩阵的大小
lora_alpha=32, # LoRA适应的比例因子
# 指定将LoRA应用到的模型模块,通常是attention和全连接层的投影
target_modules = ["q_proj", "k_proj", "v_proj", "out_proj", "fc_in", "fc_out"],
lora_dropout=0.05, # 在LoRA模块中使用的dropout率
bias="none", # 设置bias的使用方式,这里没有使用bias
task_type="CAUSAL_LM" # 任务类型,这里设置为因果(自回归)语言模型
)
# 使用get_peft_model函数和给定的配置来获取一个PEFT模型
model = get_peft_model(model, config)
# 打印出模型中可训练的参数
model.print_trainable_parameters()
#trainable params: 8,388,608 || all params: 6,666,862,592 || trainable%: 0.12582542214183376
(4)数据处理
from datasets import load_dataset
dataset = load_dataset("./data/english_quotes")
from datasets import ClassLabel, Sequence
import random
import pandas as pd
from IPython.display import display, HTML
def show_random_elements(dataset, num_examples=10):
assert num_examples <= len(dataset), "Can't pick more elements than there are in the dataset."
picks = []
for _ in range(num_examples):
pick = random.randint(0, len(dataset)-1)
while pick in picks:
pick = random.randint(0, len(dataset)-1)
picks.append(pick)
df = pd.DataFrame(dataset[picks])
for column, typ in dataset.features.items():
if isinstance(typ, ClassLabel):
df[column] = df[column].transform(lambda i: typ.names[i])
elif isinstance(typ, Sequence) and isinstance(typ.feature, ClassLabel):
df[column] = df[column].transform(lambda x: [typ.feature.names[i] for i in x])
display(HTML(df.to_html()))
show_random_elements(dataset["train"])
tokenized_dataset = dataset.map(lambda samples: tokenizer(samples["quote"]), batched=True)
from transformers import DataCollatorForLanguageModeling
# 数据收集器,用于处理语言模型的数据,这里设置为不使用掩码语言模型(MLM)
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)
(5)微调模型
from transformers import TrainingArguments, Trainer
# 设置CUDA_VISIBLE_DEVICES,指定使用哪些GPU
# os.environ["CUDA_VISIBLE_DEVICES"] = "0" # 使用cuda:0和cuda:1
model_dir = "models"
model_id='opt-6.7b'
training_args = TrainingArguments(
output_dir=f"{model_dir}/{model_id}-lora", # 指定模型输出和保存的目录
per_device_train_batch_size=8, # 每个设备上的训练批量大小
learning_rate=2e-4, # 学习率
fp16=True, # 启用混合精度训练,可以提高训练速度,同时减少内存使用
logging_steps=50, # 指定日志记录的步长,用于跟踪训练进度
# max_steps=100, # 最大训练步长
num_train_epochs=1 # 训练的总轮数
)
trainer = Trainer(
model=model, # 指定训练时使用的模型
train_dataset=tokenized_dataset["train"], # 指定训练数据集
args=training_args,
data_collator=data_collator,
)
trainer.train()
(6)保存 LoRA 模型
model_path = f"./models/opt-6.7b-lora-int8"
#trainer.save_model(model_path)
model.save_pretrained(model_path)
(7)使用 LoRA 模型
lora_model = trainer.model
text = "Two things are infinite: "
inputs = tokenizer(text, return_tensors="pt").to(0)
out = lora_model.generate(**inputs, max_new_tokens=48)
print(tokenizer.decode(out[0], skip_special_tokens=True))
2.ChatGLM实战
基于ChatGLM-6B、ChatGLM2-6B、ChatGLM3-6B模型,进行下游具体任务微调,涉及Freeze、Lora、P-tuning、全参微调等。
参考项目:github