PEFT 文件
基於提示詞的方法
並獲得增強的文件體驗
開始使用
基於提示詞的方法
提示詞(Prompt)可以描述一項任務或提供一個你希望模型學習的任務示例。軟提示(soft prompting)方法不是手動建立這些提示詞,而是在輸入嵌入(input embeddings)中新增可學習的引數,這些引數可以針對特定任務進行最佳化,同時保持預訓練模型的引數凍結。這使得為新的下游任務微調大型語言模型(LLM)變得更快、更容易。
PEFT 庫支援多種提示詞方法(p-tuning、字首調優、提示詞調優),您可以在軟提示指南中瞭解這些方法在概念上是如何工作的。如果您有興趣將這些方法應用於其他任務和用例,請檢視我們的筆記本合集!
本指南將向您展示如何使用軟提示方法訓練一個因果語言模型,來對一條推文是否是投訴進行*分類生成*。
對訓練因果語言模型的一般過程有一些熟悉會很有幫助,這樣您就可以專注於軟提示方法。如果您是新手,我們建議您先看看 Transformers 文件中的因果語言建模指南。準備好後,再回來看看將 PEFT 引入您的訓練是多麼容易!
在開始之前,請確保您已安裝所有必要的庫。
pip install -q peft transformers datasets
資料集
在本指南中,您將使用RAFT資料集的twitter_complaints
子集。twitter_complaints
子集包含被標記為complaint
和no complaint
的推文,您可以檢視資料集檢視器來更好地瞭解資料是什麼樣的。
使用load_dataset函式載入資料集,並建立一個新的text_label
列,以便更容易理解Label
值1
和2
的含義。
from datasets import load_dataset
ds = load_dataset("ought/raft", "twitter_complaints")
classes = [k.replace("_", " ") for k in ds["train"].features["Label"].names]
ds = ds.map(
lambda x: {"text_label": [classes[label] for label in x["Label"]]},
batched=True,
num_proc=1,
)
ds["train"][0]
{"Tweet text": "@HMRCcustomers No this is my first job", "ID": 0, "Label": 2, "text_label": "no complaint"}
載入一個分詞器,定義要使用的填充詞元,並確定分詞後標籤的最大長度。
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bigscience/bloomz-560m")
if tokenizer.pad_token_id is None:
tokenizer.pad_token_id = tokenizer.eos_token_id
target_max_length = max([len(tokenizer(class_label)["input_ids"]) for class_label in classes])
print(target_max_length)
建立一個預處理函式,該函式對推文文字和標籤進行分詞,填充每個批次中的輸入和標籤,建立注意力掩碼,並將序列截斷到max_length
。然後將input_ids
、attention_mask
和labels
轉換為 PyTorch 張量。
import torch
max_length = 64
def preprocess_function(examples, text_column="Tweet text", label_column="text_label"):
batch_size = len(examples[text_column])
inputs = [f"{text_column} : {x} Label : " for x in examples[text_column]]
targets = [str(x) for x in examples[label_column]]
model_inputs = tokenizer(inputs)
labels = tokenizer(targets)
classes = [k.replace("_", " ") for k in ds["train"].features["Label"].names]
for i in range(batch_size):
sample_input_ids = model_inputs["input_ids"][i]
label_input_ids = labels["input_ids"][i]
model_inputs["input_ids"][i] = [tokenizer.pad_token_id] * (
max_length - len(sample_input_ids)
) + sample_input_ids
model_inputs["attention_mask"][i] = [0] * (max_length - len(sample_input_ids)) + model_inputs[
"attention_mask"
][i]
labels["input_ids"][i] = [-100] * (max_length - len(label_input_ids)) + label_input_ids
model_inputs["input_ids"][i] = torch.tensor(model_inputs["input_ids"][i][:max_length])
model_inputs["attention_mask"][i] = torch.tensor(model_inputs["attention_mask"][i][:max_length])
labels["input_ids"][i] = torch.tensor(labels["input_ids"][i][:max_length])
model_inputs["labels"] = labels["input_ids"]
return model_inputs
使用map函式將預處理函式應用於整個資料集,並刪除未處理的列,因為模型不需要它們。
processed_ds = ds.map(
preprocess_function,
batched=True,
num_proc=1,
remove_columns=ds["train"].column_names,
load_from_cache_file=False,
desc="Running tokenizer on dataset",
)
最後,建立一個訓練和評估的DataLoader
。如果您的資料集中的樣本在 CPU 上,您可以設定pin_memory=True
來加速訓練期間向 GPU 的資料傳輸。
from torch.utils.data import DataLoader
from transformers import default_data_collator
train_ds = processed_ds["train"]
eval_ds = processed_ds["test"]
batch_size = 16
train_dataloader = DataLoader(train_ds, shuffle=True, collate_fn=default_data_collator, batch_size=batch_size, pin_memory=True)
eval_dataloader = DataLoader(eval_ds, collate_fn=default_data_collator, batch_size=batch_size, pin_memory=True)
模型
現在,讓我們載入一個預訓練模型作為軟提示方法的基礎模型。本指南使用bigscience/bloomz-560m模型,但您可以使用任何您想要的因果語言模型。
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("bigscience/bloomz-560m")
PEFT 配置與模型
對於任何 PEFT 方法,您都需要建立一個配置,其中包含指定如何應用 PEFT 方法的所有引數。配置設定好後,將其與基礎模型一起傳遞給get_peft_model()函式,以建立一個可訓練的PeftModel。
呼叫print_trainable_parameters()方法,比較PeftModel的可訓練引數數量與基礎模型中的引數數量!
P-tuning 添加了一個可訓練的嵌入張量,其中提示詞詞元可以新增到輸入序列的任何位置。建立一個PromptEncoderConfig,其中包含任務型別、要新增和學習的虛擬詞元數量以及用於學習提示詞引數的編碼器的隱藏大小。
from peft import PromptEncoderConfig, get_peft_model
peft_config = PromptEncoderConfig(task_type="CAUSAL_LM", num_virtual_tokens=20, encoder_hidden_size=128)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
"trainable params: 300,288 || all params: 559,514,880 || trainable%: 0.05366935013417338"
訓練
設定一個最佳化器和學習率排程器。
from transformers import get_linear_schedule_with_warmup
lr = 3e-2
num_epochs = 50
optimizer = torch.optim.AdamW(model.parameters(), lr=lr)
lr_scheduler = get_linear_schedule_with_warmup(
optimizer=optimizer,
num_warmup_steps=0,
num_training_steps=(len(train_dataloader) * num_epochs),
)
將模型移動到 GPU,並建立一個訓練迴圈,報告每個週期的損失和困惑度。
from tqdm import tqdm
device = "cuda"
model = model.to(device)
for epoch in range(num_epochs):
model.train()
total_loss = 0
for step, batch in enumerate(tqdm(train_dataloader)):
batch = {k: v.to(device) for k, v in batch.items()}
outputs = model(**batch)
loss = outputs.loss
total_loss += loss.detach().float()
loss.backward()
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
model.eval()
eval_loss = 0
eval_preds = []
for step, batch in enumerate(tqdm(eval_dataloader)):
batch = {k: v.to(device) for k, v in batch.items()}
with torch.no_grad():
outputs = model(**batch)
loss = outputs.loss
eval_loss += loss.detach().float()
eval_preds.extend(
tokenizer.batch_decode(torch.argmax(outputs.logits, -1).detach().cpu().numpy(), skip_special_tokens=True)
)
eval_epoch_loss = eval_loss / len(eval_dataloader)
eval_ppl = torch.exp(eval_epoch_loss)
train_epoch_loss = total_loss / len(train_dataloader)
train_ppl = torch.exp(train_epoch_loss)
print(f"{epoch=}: {train_ppl=} {train_epoch_loss=} {eval_ppl=} {eval_epoch_loss=}")
分享你的模型
訓練完成後,您可以使用push_to_hub
方法將您的模型上傳到 Hub。您需要先登入您的 Hugging Face 帳戶,並在提示時輸入您的令牌。
from huggingface_hub import notebook_login
account = <your-hf-account-name>
peft_model_id = f"{account}/bloomz-560-m-peft-method"
model.push_to_hub(peft_model_id)
如果您檢查倉庫中的模型檔案大小,您會發現它比一個全尺寸的模型要小得多!

推理
讓我們載入模型進行推理,並在一條推文上測試它!
from peft import AutoPeftModelForCausalLM
model = AutoPeftModelForCausalLM.from_pretrained("peft_model_id").to("cuda")
tokenizer = AutoTokenizer.from_pretrained("bigscience/bloomz-560m")
i = 15
inputs = tokenizer(f'{text_column} : {ds["test"][i]["Tweet text"]} Label : ', return_tensors="pt")
print(ds["test"][i]["Tweet text"])
"@NYTsupport i have complained a dozen times & yet my papers are still thrown FAR from my door. Why is this so hard to resolve?"
呼叫generate
方法來生成預測的分類標籤。
with torch.no_grad():
inputs = {k: v.to(device) for k, v in inputs.items()}
outputs = model.generate(input_ids=inputs["input_ids"], max_new_tokens=10)
print(tokenizer.batch_decode(outputs.detach().cpu().numpy(), skip_special_tokens=True))
"['Tweet text : @NYTsupport i have complained a dozen times & yet my papers are still thrown FAR from my door. Why is this so hard to resolve? Label : complaint']"