微調預訓練模型
使用預訓練模型有很多好處。預訓練模型節省了你的計算開銷、你的碳排放,並且讓你能夠使用sota模型而不需要自己從頭訓練。Hugging Face Transformers為你提供了上千種預訓練模型,可廣泛用於各種任務。當你使用一個預訓練模型,你可以在任務特定數據集上訓練。這就是著名的微調,一種非常厲害的訓練技巧。在本篇教程中,你可以用Pytorch微調一個預訓練模型。
准備一個數據集
在你微調一個預訓練模型之前,下載一個數據集並將其處理成可用於訓練的形式。
首先下載一個Yelp Reviews數據集:
from datasets import load_dataset
dataset = load_dataset("yelp_review_full")
dataset[100]
'''
output:
{'label': 0,
'text': 'My expectations for McDonalds are t rarely high...}
'''
正如你所知道的那樣,你需要一個分詞器來處理文本以及填充、截斷策略來處理可變序列長度。為了一步處理你的數據集,使用Dataset的map方法,將預處理函數應用在整個數據集上。
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
你還可以使用完整數據集的一個子集來進行微調,這樣可以減少時間。
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))
用Trainer進行微調
Hugging Face Transformers提供了一個Trainer類用來優化模型的訓練,不用人工編寫自己的訓練循環就能開始訓練。Trainer AIP支持廣泛的訓練選項和特征,比如:日志、梯度積累、混合精度。
一開始要加載你的模型,並制定期望的標簽數目。從Yelp Review數據集卡片得知,總共有5個標簽:
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", num_labels=5)
你將看到一個“一些預訓練權重沒有被使用,並且一些權重被隨機初始化”的提醒。不用擔心,這非常正常!BERT模型的預訓練頭被丟棄了,取而代之的是一個隨機初始化的分類頭。你將會在你的序列分類任務上微調這個新模型頭,將預訓練模型的知識遷移過去。
訓練超參數
下一步,創建一個TrainingArguments類,這個類包含了你可以調整的所有超參數,以及激活不同訓練選項的標志。在本篇教程中,你可以從默認的訓練超參數開始,然后進一步可以盡情實驗以找到自己最佳的環境。
指定要把訓練產生的checkpoint存到哪個地址:
from transformers import TrainingArguments
training_args = TrainingArguments(output_dir="test_trainer")
評價指標
Trainer不能在訓練過程中自動評價模型的表現。你需要傳給Trainer一個函數來計算和報告指標。Datasets庫提供了一個簡單的accuracy函數,可以通過load_metric()加載。
import numpy as np
from datasets import load_metric
metric = load_metric("accuracy")
在metric上調用compute可以計算模型預測的准確率。在將你的預測值放入comput之前,你需要將預測值轉換為概率(記住所有的transformers模型都返回logits)。
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
如果你希望在微調過程中監控你的評價指標,在訓練參數中指定evaluation_strategy參數,在每一回合最后報告評價指標
from transformers import TrainingArguments
training_args = TrainingArguments(output_dir="test_trainer", evaluation_strategy="epoch")
Trainer
使用你的模型、訓練參數、訓練和測試數據集以及評價函數,創建一個Trainer類。
from transformers import Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=small_train_dataset,
eval_dataset=small_eval_dataset,
compute_metrics=compute_metrics,
)
然后通過調用train()微調你的模型:
trainer.train()
在原生Pytorch上進行微調
Trainer包含了訓練循環,並允許你使用一行代碼微調模型。對於喜歡使用自己訓練循環的用戶,你可以在原生Pytorch上微調模型。
手工后處理tokenized_dataset
- 將text列刪除,因為模型不能直接接收文本作為輸入
tokenized_datasets = tokenized_datasets.remove_columns(["text"])
- 將label列重命名為labels,因為模型期望的參數名是labels
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
- 設置數據集的格式,使得返回張量,而不是lists
tokenized_datasets.set_format("torch")
DataLoader
為你的訓練和測試數據集創建一個DataLoader,這樣你可以迭代批次的數據
from torch.utils.data import DataLoader
train_dataloader = DataLoader(small_train_dataset, shuffle=True, batch_size=8)
eval_dataloader = DataLoader(small_eval_dataset, batch_size=8)
優化器和學習率衰減
創建一個優化器和學習率規划器來微調模型。優化器使用AdamW
from torch.optim import AdamW
optimizer = AdamW(model.parameters(), lr=5e-5)
創建一個默認的學習率規划器
from transformers import get_scheduler
num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
name="linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps
)
最后,指定device使用GPU
import torch
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)
現在,你就可以訓練了。
訓練循環
為了跟蹤你的訓練過程,使用tqdm庫添加一個進度條
from tqdm.auto import tqdm
progress_bar = tqdm(range(num_training_steps))
model.train()
for epoch in range(num_epochs):
for batch in train_dataloader:
batch = {k: v.to(device) for k, v in batch.items()}
outputs = model(**batch)
loss = outputs.loss
loss.backward()
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
progress_bar.update(1)
評價指標
和Trainer一樣,在你寫自己的訓練循環時需要做相同的事情。但是這次,你需要將所有批次數據的數值累計起來,在最后計算指標。
metric = load_metric("accuracy")
model.eval()
for batch in eval_dataloader:
batch = {k: v.to(device) for k, v in batch.items()}
with torch.no_grad():
outputs = model(**batch)
logits = outputs.logits
predictions = torch.argmax(logits, dim=-1)
metric.add_batch(predictions=predictions, references=batch["labels"])
metric.compute()