簡單的文字分類任務:不借助Trainer實現

語言: CN / TW / HK

本站內容均來自興趣收集,如不慎侵害的您的相關權益,請留言告知,我們將盡快刪除.謝謝.

攜手創作,共同成長!這是我參與「掘金日新計劃 · 8 月更文挑戰」的第30天,點選檢視活動詳情

說明

之前的一篇部落格 簡單的文字分類任務:使用Huggingface Trainer實現

為大家展示瞭如何使用Huggingface Transformers庫來快速上手實現一個現代的文字分類模型的正規化,雖然trainer很好用,但由於其封裝的太過完善,所以在某些修改方面顯得不是那么方便,我們有必要自己使用Pytorch手寫一下整個流程。

任務簡介

與前一篇部落格相同,仍進行IMDB資料集上的情感分析。不過本次實驗的不同之處在於:

    1. 為了快速實現,使用訓練資料為1000條,測試為100條;
    1. 使用BERT進行訓練而不是distil-bert。

資料預處理

一如既往,我們還是使用datasets庫載入資料集,

from datasets import load_dataset
dataset = load_dataset("imdb")

接著,初始化tokenizer然後進行預處理

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

我們使用bert的tokenizer,詞表數目為30522。

在進行分詞時,為了加快執行速度,設定最大的token序列長度為256。

def tokenize_function(examples):
    return tokenizer(examples["text"], padding="max_length",truncation=True,max_length=256)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
tokenized_datasets.set_format("torch")  #將值轉換為torch.Tensor物件

同樣的,由於資料集過大,我們這裡只選擇1000個做train的樣例,100個做evaluation樣例。

SMALL_TRAIN_SIZE = 1000
SMALL_TEST_SIZE = 100
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(SMALL_TRAIN_SIZE))
small_test_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(SMALL_TEST_SIZE))

然後我們使用Pytorch原生的Dataloader來進行資料迭代,這裡設定batch size為16,對訓練和測試資料集採取相同的batch size:

train_dataloader = DataLoader(small_train_dataset, shuffle=True, batch_size=16)
test_dataloader = DataLoader(small_test_dataset, batch_size=16)

評價指標定義

這裡,我們僅選擇準確率作為評價指標,

metric=datasets.load_metric("accuracy")

而datasets庫已經為我們封裝好了評價指標,可以直接一行程式碼呼叫,非常方便。

模型、優化器和scheduler的初始化

我們直接使用Huggingface的bert模型進行初始化,同時選擇AdamW作為優化器,scheduler則是指的學習率的排程器,用於控制學習率的變化方式,比如BERT中常用的warmup操作。

model=AutoModelForSequenceClassification.from_pretrained("bert-base-uncased",num_labels=2)
optimizer = AdamW(model.parameters(), lr=2e-5)
num_epochs = 1
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
)

這裡,我們設定訓練的epoch為1,學習率為2e-5,batch size已經在Dataloader中設定過為16.

模型的訓練與驗證

接下來,我們使用Pytorch手寫訓練和驗證流程。

progress_bar = tqdm(range(num_training_steps))
global_step = 0
print("Before training.")
metric=datasets.load_metric("accuracy")
model.eval()
for batch in test_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"])
print("step: ", global_step, metric.compute())
print()
print("Start training.")
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)
        
        if (global_step+1) % 10 == 0:
            metric=datasets.load_metric("accuracy")
            model.eval()
            for batch in test_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"])
            
            print("step: ", global_step+1, metric.compute())
        global_step += 1
        
    metric=datasets.load_metric("accuracy")
    model.eval()
    for batch in test_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"])
    print()
    print("Finish: ", global_step+1, metric.compute())

整體的程式碼分為三個部分:

    1. 訓練前的驗證:用以檢查模型在訓練之前的效能表現。
    1. 模型訓練:包括以下流程,
    1. 從Dataloader中讀取資料
    1. 輸入給模型進行前向計算
    1. 計算loss數值
    1. loss進行梯度回傳
    1. 優化器進行引數更新
    1. 優化器梯度清零
    1. 訓練完成後進行驗證:檢驗模型的最終表現

得到的列印輸出如下:

Before training.
step:  0 {'accuracy': 0.47}
Start training.
step:  10 {'accuracy': 0.53}
step:  20 {'accuracy': 0.55}
step:  30 {'accuracy': 0.59}
step:  40 {'accuracy': 0.74}
step:  50 {'accuracy': 0.77}
step:  60 {'accuracy': 0.81}
Finish:  64 {'accuracy': 0.82}

可以看到,隨著模型的訓練,預測準確率從0.47上升到了0.82。

總結

本文使用Pytorch框架實現了Transformers庫中的trainer模組對訓練和驗證的流程,自定義流程的好處在於靈活,但同樣編寫複雜;一般而言,在沒有太多的模型內部更改時,可以優先考慮使用trainer,結合datasets庫直接做到Huggingface全家桶的一站式開發,非常方便。