本文將介紹使用LoRa在本地機(jī)器上微調(diào)Alpaca和LLaMA,我們將介紹在特定數(shù)據(jù)集上對(duì)Alpaca LoRa進(jìn)行微調(diào)的整個(gè)過(guò)程,本文將涵蓋數(shù)據(jù)處理、模型訓(xùn)練和使用流行的自然語(yǔ)言處理庫(kù)(如Transformers和hugs Face)進(jìn)行評(píng)估。此外還將介紹如何使用grado應(yīng)用程序部署和測(cè)試模型。
配置
首先,alpaca-lora1 GitHub存儲(chǔ)庫(kù)提供了一個(gè)腳本(finetune.py)來(lái)訓(xùn)練模型。在本文中,我們將利用這些代碼并使其在Google Colab環(huán)境中無(wú)縫地工作。
首先安裝必要的依賴:
!pip install -U pip
!pip install accelerate==0.18.0
!pip install appdirs==1.4.4
!pip install bitsandbytes==0.37.2
!pip install datasets==2.10.1
!pip install fire==0.5.0
!pip install git+https://github.com/huggingface/peft.git
!pip install git+https://github.com/huggingface/transformers.git
!pip install torch==2.0.0
!pip install sentencepiece==0.1.97
!pip install tensorboardX==2.6
!pip install gradio==3.23.0
安裝完依賴項(xiàng)后,繼續(xù)導(dǎo)入所有必要的庫(kù),并為matplotlib繪圖配置設(shè)置:
import transformers
import textwrap
from transformers import LlamaTokenizer, LlamaForCausalLM
import os
import sys
from typing import List
from peft import (
LoraConfig,
get_peft_model,
get_peft_model_state_dict,
prepare_model_for_int8_training,
)
import fire
import torch
from datasets import load_dataset
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns
from pylab import rcParams
%matplotlib inline
sns.set(rc={'figure.figsize':(10, 7)})
sns.set(rc={'figure.dpi':100})
sns.set(style='white', palette='muted', font_scale=1.2)
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
DEVICE
數(shù)據(jù)
我們這里使用BTC Tweets Sentiment dataset4,該數(shù)據(jù)可在Kaggle上獲得,包含大約50,000條與比特幣相關(guān)的tweet。為了清理數(shù)據(jù),刪除了所有以“轉(zhuǎn)發(fā)”開(kāi)頭或包含鏈接的推文。
使用Pandas來(lái)加載CSV:
df = pd.read_csv("bitcoin-sentiment-tweets.csv")
df.head()
通過(guò)清理的數(shù)據(jù)集有大約1900條推文。
情緒標(biāo)簽用數(shù)字表示,其中-1表示消極情緒,0表示中性情緒,1表示積極情緒。讓我們看看它們的分布:
df.sentiment.value_counts()
# 0.0 860
# 1.0 779
# -1.0 258
# Name: sentiment, dtype: int64
數(shù)據(jù)量差不多,雖然負(fù)面評(píng)論較少,但是可以簡(jiǎn)單的當(dāng)成平衡數(shù)據(jù)來(lái)對(duì)待:
df.sentiment.value_counts().plot(kind='bar');
構(gòu)建JSON數(shù)據(jù)集
原始Alpaca存儲(chǔ)庫(kù)中的dataset5格式由一個(gè)JSON文件組成,該文件具有具有指令、輸入和輸出字符串的對(duì)象列表。
讓我們將Pandas的DF轉(zhuǎn)換為一個(gè)JSON文件,該文件遵循原始Alpaca存儲(chǔ)庫(kù)中的格式:
def sentiment_score_to_name(score: float):
if score > 0:
return "Positive"
elif score < 0:
return "Negative"
return "Neutral"
dataset_data = [
{
"instruction": "Detect the sentiment of the tweet.",
"input": row_dict["tweet"],
"output": sentiment_score_to_name(row_dict["sentiment"])
}
for row_dict in df.to_dict(orient="records")
]
dataset_data[0]
結(jié)果如下:
{
"instruction": "Detect the sentiment of the tweet.",
"input": "@p0nd3ea Bitcoin wasn't built to live on exchanges.",
"output": "Positive"
}
然后就是保存生成的JSON文件,以便稍后使用它來(lái)訓(xùn)練模型:
import json
with open("alpaca-bitcoin-sentiment-dataset.json", "w") as f:
json.dump(dataset_data, f)
模型權(quán)重
雖然原始的Llama模型權(quán)重不可用,但它們被泄露并隨后被改編用于HuggingFace Transformers庫(kù)。我們將使用decapoda-research6:
BASE_MODEL = "decapoda-research/llama-7b-hf"
model = LlamaForCausalLM.from_pretrained(
BASE_MODEL,
load_in_8bit=True,
torch_dtype=torch.float16,
device_map="auto",
)
tokenizer = LlamaTokenizer.from_pretrained(BASE_MODEL)
tokenizer.pad_token_id = (
0 # unk. we want this to be different from the eos token
)
tokenizer.padding_side = "left"
這段代碼使用來(lái)自Transformers庫(kù)的LlamaForCausalLM類加載預(yù)訓(xùn)練的Llama 模型。load_in_8bit=True參數(shù)使用8位量化加載模型,以減少內(nèi)存使用并提高推理速度。
代碼還使用LlamaTokenizer類為同一個(gè)Llama模型加載標(biāo)記器,并為填充標(biāo)記設(shè)置一些附加屬性。具體來(lái)說(shuō),它將pad_token_id設(shè)置為0以表示未知的令牌,并將padding_side設(shè)置為“l(fā)eft”以填充左側(cè)的序列。
數(shù)據(jù)集加載
現(xiàn)在我們已經(jīng)加載了模型和標(biāo)記器,下一步就是加載之前保存的JSON文件,使用HuggingFace數(shù)據(jù)集庫(kù)中的load_dataset()函數(shù):
data = load_dataset("json", data_files="alpaca-bitcoin-sentiment-dataset.json")
data["train"]
結(jié)果如下:
Dataset({
features: ['instruction', 'input', 'output'],
num_rows: 1897
})
接下來(lái),我們需要從加載的數(shù)據(jù)集中創(chuàng)建提示并標(biāo)記它們:
def generate_prompt(data_point):
return f"""Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request. # noqa: E501
### Instruction:
{data_point["instruction"]}
### Input:
{data_point["input"]}
### Response:
{data_point["output"]}"""
def tokenize(prompt, add_eos_token=True):
result = tokenizer(
prompt,
truncation=True,
max_length=CUTOFF_LEN,
padding=False,
return_tensors=None,
)
if (
result["input_ids"][-1] != tokenizer.eos_token_id
and len(result["input_ids"]) < CUTOFF_LEN
and add_eos_token
):
result["input_ids"].append(tokenizer.eos_token_id)
result["attention_mask"].append(1)
result["labels"] = result["input_ids"].copy()
return result
def generate_and_tokenize_prompt(data_point):
full_prompt = generate_prompt(data_point)
tokenized_full_prompt = tokenize(full_prompt)
return tokenized_full_prompt
第一個(gè)函數(shù)generate_prompt從數(shù)據(jù)集中獲取一個(gè)數(shù)據(jù)點(diǎn),并通過(guò)組合指令、輸入和輸出值來(lái)生成提示。第二個(gè)函數(shù)tokenize接收生成的提示,并使用前面定義的標(biāo)記器對(duì)其進(jìn)行標(biāo)記。它還向輸入序列添加序列結(jié)束標(biāo)記,并將標(biāo)簽設(shè)置為與輸入序列相同。第三個(gè)函數(shù)generate_and_tokenize_prompt結(jié)合了前兩個(gè)函數(shù),生成并標(biāo)記提示。
數(shù)據(jù)準(zhǔn)備的最后一步是將數(shù)據(jù)集分成單獨(dú)的訓(xùn)練集和驗(yàn)證集:
train_val = data["train"].train_test_split(
test_size=200, shuffle=True, seed=42
)
train_data = (
train_val["train"].map(generate_and_tokenize_prompt)
)
val_data = (
train_val["test"].map(generate_and_tokenize_prompt)
)
我們還需要數(shù)據(jù)進(jìn)行打亂,并且獲取200個(gè)樣本作為驗(yàn)證集。generate_and_tokenize_prompt()函數(shù)應(yīng)用于訓(xùn)練和驗(yàn)證集中的每個(gè)示例,生成標(biāo)記化的提示。
訓(xùn)練
訓(xùn)練過(guò)程需要幾個(gè)參數(shù),這些參數(shù)主要來(lái)自原始存儲(chǔ)庫(kù)中的微調(diào)腳本:
LORA_R = 8
LORA_ALPHA = 16
LORA_DROPOUT= 0.05
LORA_TARGET_MODULES = [
"q_proj",
"v_proj",
]
BATCH_SIZE = 128
MICRO_BATCH_SIZE = 4
GRADIENT_ACCUMULATION_STEPS = BATCH_SIZE // MICRO_BATCH_SIZE
LEARNING_RATE = 3e-4
TRAIN_STEPS = 300
OUTPUT_DIR = "experiments"
下面就可以為訓(xùn)練準(zhǔn)備模型了:
model = prepare_model_for_int8_training(model)
config = LoraConfig(
r=LORA_R,
lora_alpha=LORA_ALPHA,
target_modules=LORA_TARGET_MODULES,
lora_dropout=LORA_DROPOUT,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)
model.print_trainable_parameters()
#trainable params: 4194304 || all params: 6742609920 || trainable%: 0.06220594176090199
我們使用LORA算法初始化并準(zhǔn)備模型進(jìn)行訓(xùn)練,通過(guò)量化可以減少模型大小和內(nèi)存使用,而不會(huì)顯著降低準(zhǔn)確性。
LoraConfig7是一個(gè)為L(zhǎng)ORA算法指定超參數(shù)的類,例如正則化強(qiáng)度(lora_alpha)、dropout概率(lora_dropout)和要壓縮的目標(biāo)模塊(target_modules)。
然后就可以直接使用Transformers庫(kù)進(jìn)行訓(xùn)練:
training_arguments = transformers.TrainingArguments(
per_device_train_batch_size=MICRO_BATCH_SIZE,
gradient_accumulation_steps=GRADIENT_ACCUMULATION_STEPS,
warmup_steps=100,
max_steps=TRAIN_STEPS,
learning_rate=LEARNING_RATE,
fp16=True,
logging_steps=10,
optim="adamw_torch",
evaluation_strategy="steps",
save_strategy="steps",
eval_steps=50,
save_steps=50,
output_dir=OUTPUT_DIR,
save_total_limit=3,
load_best_model_at_end=True,
report_to="tensorboard"
)
這段代碼創(chuàng)建了一個(gè)TrainingArguments對(duì)象,該對(duì)象指定用于訓(xùn)練模型的各種設(shè)置和超參數(shù)。這些包括:
- gradient_accumulation_steps:在執(zhí)行向后/更新之前累積梯度的更新步數(shù)。
- warmup_steps:優(yōu)化器的預(yù)熱步數(shù)。
- max_steps:要執(zhí)行的訓(xùn)練總數(shù)。
- learning_rate:學(xué)習(xí)率。
- fp16:使用16位精度進(jìn)行訓(xùn)練。
DataCollatorForSeq2Seq是transformer庫(kù)中的一個(gè)類,它為序列到序列(seq2seq)模型創(chuàng)建一批輸入/輸出序列。在這段代碼中,DataCollatorForSeq2Seq對(duì)象用以下參數(shù)實(shí)例化:
data_collator = transformers.DataCollatorForSeq2Seq(
tokenizer, pad_to_multiple_of=8, return_tensors="pt", padding=True
)
pad_to_multiple_of:表示最大序列長(zhǎng)度的整數(shù),四舍五入到最接近該值的倍數(shù)。
padding:一個(gè)布爾值,指示是否將序列填充到指定的最大長(zhǎng)度。
以上就是訓(xùn)練的所有代碼準(zhǔn)備,下面就是訓(xùn)練了
trainer = transformers.Trainer(
model=model,
train_dataset=train_data,
eval_dataset=val_data,
args=training_arguments,
data_collator=data_collator
)
model.config.use_cache = False
old_state_dict = model.state_dict
model.state_dict = (
lambda self, *_, **__: get_peft_model_state_dict(
self, old_state_dict()
)
).__get__(model, type(model))
model = torch.compile(model)
trainer.train()
model.save_pretrained(OUTPUT_DIR)
在實(shí)例化訓(xùn)練器之后,代碼在模型的配置中將use_cache設(shè)置為False,并使用get_peft_model_state_dict()函數(shù)為模型創(chuàng)建一個(gè)state_dict,該函數(shù)為使用低精度算法進(jìn)行訓(xùn)練的模型做準(zhǔn)備。
然后在模型上調(diào)用torch.compile()函數(shù),該函數(shù)編譯模型的計(jì)算圖并準(zhǔn)備使用PyTorch 2進(jìn)行訓(xùn)練。
訓(xùn)練過(guò)程在A100上持續(xù)了大約2個(gè)小時(shí)。我們看一下Tensorboard上的結(jié)果:
訓(xùn)練損失和評(píng)估損失呈穩(wěn)步下降趨勢(shì)。看來(lái)我們的微調(diào)是有效的。
如果你想將模型上傳到Hugging Face上,可以使用下面代碼,
from huggingface_hub import notebook_login
notebook_login()
model.push_to_hub("curiousily/alpaca-bitcoin-tweets-sentiment", use_auth_token=True)
推理
我們可以使用generate.py腳本來(lái)測(cè)試模型:
!git clone https://github.com/tloen/alpaca-lora.git
%cd alpaca-lora
!git checkout a48d947
我們的腳本啟動(dòng)的gradio應(yīng)用程序
!python generate.py \
--load_8bit \
--base_model 'decapoda-research/llama-7b-hf' \
--lora_weights 'curiousily/alpaca-bitcoin-tweets-sentiment' \
--share_gradio
簡(jiǎn)單的界面如下:
總結(jié)
我們已經(jīng)成功地使用LoRa方法對(duì)Llama 模型進(jìn)行了微調(diào),還演示了如何在Gradio應(yīng)用程序中使用它。
如果你對(duì)本文感興趣,請(qǐng)看原文:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-582005.html
https://avoid.overfit.cn/post/34b6eaf7097a4929b9aab7809f3cfeaa文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-582005.html
到了這里,關(guān)于在自定義數(shù)據(jù)集上微調(diào)Alpaca和LLaMA的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!