Cara Menyempurnakan Model NLP Arsitektur Transformer — Visual Studio Magazine

Lab Ilmu Data

Cara Menyempurnakan Model NLP Arsitektur Transformer

Tujuannya adalah analisis sentimen — menerima teks ulasan film (seperti, “Film ini sangat membuang waktu saya.”) dan keluaran kelas 0 (ulasan negatif) atau kelas 1 (ulasan positif).

Artikel ini menjelaskan cara menyempurnakan model Arsitektur Transformer yang telah dilatih sebelumnya untuk pemrosesan bahasa alami. Lebih khusus lagi, artikel ini menjelaskan cara menyempurnakan versi ringkas dari model BERT yang telah dilatih sebelumnya untuk membuat pengklasifikasi biner untuk subset dari kumpulan data ulasan film IMDB. Tujuannya adalah analisis sentimen — menerima teks ulasan film (seperti, “Film ini sangat membuang waktu saya.”) dan keluaran kelas 0 (ulasan negatif) atau kelas 1 (ulasan positif).

Anda dapat menganggap model arsitektur transformator (TA) yang telah dilatih sebelumnya sebagai semacam ahli bahasa Inggris. Tetapi pakar TA tidak tahu apa-apa tentang film, jadi Anda memberikan pelatihan tambahan untuk menyempurnakan model sehingga memahami perbedaan antara ulasan film positif dan ulasan negatif.

Ada beberapa model TA yang telah dilatih sebelumnya untuk pemrosesan bahasa alami (NLP). Dua yang paling terkenal adalah BERT (representasi encoder dua arah dari transformator) dan GPT (transformator pra-pelatihan generatif). Model TA sangat besar, dengan jutaan bobot dan parameter bias.

Model TA telah merevolusi NLP, tetapi sistem TA sangat kompleks dan menerapkannya dari awal dapat memakan waktu ratusan atau bahkan ribuan jam kerja. Hugging Face (HF) adalah pustaka kode sumber terbuka yang menyediakan model yang telah dilatih sebelumnya dan set API untuk bekerja dengan model tersebut. Pustaka HF membuat penerapan sistem NLP menggunakan model TA jauh lebih sulit (lihat “Cara Membuat Model Arsitektur Transformer untuk Pemrosesan Bahasa Alami”).

Cara yang baik untuk melihat ke mana arah artikel ini adalah dengan melihat tangkapan layar dari program demo di Gambar 1. Program demo dimulai dengan memuat subset kecil 200 item dari dataset ulasan film IMDB ke dalam memori. Dataset lengkap memiliki 50.000 ulasan film — 25.000 ulasan untuk pelatihan dan 25.000 ulasan untuk pengujian, dengan 12.500 ulasan positif dan 12.500 ulasan negatif. Bekerja dengan set data lengkap sangat memakan waktu sehingga data demo hanya menggunakan 100 ulasan pelatihan positif pertama dan 100 ulasan pelatihan negatif pertama.

Ulasan film dalam bentuk teks mentah. Ulasan dibaca ke dalam memori kemudian diubah menjadi struktur data yang menyimpan token integer. Misalnya, kata “film” memiliki ID token = 3185. Struktur data ulasan film yang diberi token diumpankan ke objek PyTorch Dataset yang digunakan untuk mengirim kumpulan ulasan yang diberi token dan label terkaitnya ke kode pelatihan.

Setelah data ulasan film disiapkan, demo memuat model DistilBERT yang telah dilatih sebelumnya ke dalam memori. DistilBERT adalah versi kental (“suling”), tetapi masih besar, dari model BERT besar. DistilBERT versi tanpa casing memiliki 66 juta bobot dan bias. Kemudian demo menyempurnakan model yang telah dilatih dengan melatih model menggunakan teknik PyTorch standar. Demo diakhiri dengan menyimpan model fine-tuned ke file.

Gambar 1: Menyesuaikan Model BERT Terkondensasi untuk Analisis Sentimen Film
[Click on image for larger view.] Gambar 1: Menyesuaikan Model BERT Terkondensasi untuk Analisis Sentimen Film

Artikel ini mengasumsikan Anda memiliki keakraban menengah atau lebih baik dengan bahasa pemrograman keluarga-C, lebih disukai Python, dan keakraban dasar dengan PyTorch, tetapi tidak menganggap Anda tahu apa-apa tentang perpustakaan kode Hugging Face. Kode sumber lengkap untuk program demo disajikan dalam artikel ini, dan kode tersebut juga tersedia dalam unduhan file yang menyertainya.

Untuk menjalankan program demo, Anda harus menginstal Python, PyTorch dan HF di mesin Anda. Program demo dikembangkan pada Windows 10 menggunakan distribusi 64-bit Anaconda 2020.02 (yang berisi Python 3.7.6) dan PyTorch versi 1.8.0 untuk CPU yang diinstal melalui pip dan transformator HF versi 4.11.3. Instalasi tidak sepele. Anda dapat menemukan petunjuk instalasi langkah-demi-langkah rinci untuk PyTorch di posting blog saya. Memasang perpustakaan transformator HF relatif sederhana. Anda dapat mengeluarkan perintah shell “pip install transformers”.

Struktur Program Keseluruhan
Struktur program demo adalah:

# import modules and packages
device = torch.device('cpu')
class IMDbDataset(T.utils.data.Dataset): . . .
def read_imdb(root_dir): . . .
def main():
  # 0. preparation
  # 1. load raw IMDB train data into memory
  # 2. tokenize the raw data reviews text
  # 3. load tokenized text, labels into PyTorch Dataset
  # 4. load (possibly cached) pretrained HF model
  # 5. fine-tune / train model using standard PyTorch
  # 6. save trained model weights and biases
if __name__ == "__main__":
  main()

Pustaka HF dapat menggunakan pustaka PyTorch atau TensorFlow. Demo menggunakan PyTorch. IMDbDataset adalah kelas PyTorch yang ditentukan program yang menyimpan data pelatihan dan menyajikannya dalam batch. Fungsi read_imdb() adalah helper yang membaca data teks review film dari file ke memori. Semua logika program ada dalam satu fungsi main().

Kode demo lengkap, dengan beberapa pengeditan kecil untuk menghemat ruang, disajikan di Daftar 1. Saya lebih suka membuat indentasi menggunakan dua spasi daripada empat spasi standar. Karakter backslash digunakan untuk kelanjutan baris untuk memecah pernyataan panjang.

Daftar 1: Program Demo Penyetelan Lengkap yang Lengkap

# imdb_hf_01_tune.py
# fine-tune HF pretrained model for IMDB 
# zipped raw data at:
# https://ai.stanford.edu/~amaas/data/sentiment/

import numpy as np  # not used
from pathlib import Path
from transformers import DistilBertTokenizer
import torch as T
from torch.utils.data import DataLoader
from transformers import AdamW, 
  DistilBertForSequenceClassification
from transformers import logging  # suppress warnings

device = T.device('cpu')

class IMDbDataset(T.utils.data.Dataset):
  def __init__(self, reviews_lst, labels_lst):
    self.reviews_lst = reviews_lst  # list of token IDs
    self.labels_lst = labels_lst    # list of 0-1 ints

  def __getitem__(self, idx):
    item = {}  # [input_ids] [attention_mask] [labels]
    for key, val in self.reviews_lst.items():
      item[key] = T.tensor(val[idx]).to(device)
    item['labels'] = 
      T.tensor(self.labels_lst[idx]).to(device)
    return item

  def __len__(self):
    return len(self.labels_lst)

def read_imdb(root_dir):
  reviews_lst = []; labels_lst = []
  root_dir = Path(root_dir)
  for label_dir in ["pos", "neg"]:
    for f_handle in (root_dir/label_dir).iterdir():
      reviews_lst.append(f_handle.read_text(
        encoding='utf-8'))
      if label_dir == "pos":
        labels_lst.append(1)
      else:
        labels_lst.append(0)
  return (reviews_lst, labels_lst)  # lists of strings

def main():
  # 0. get ready
  print("nBegin fine-tune for IMDB sentiment ")
  logging.set_verbosity_error()  # suppress wordy warnings
  T.manual_seed(1)
  np.random.seed(1)

  # 1. load raw IMDB train data into memory
  print("nLoading IMDB train data subset into memory ")
  train_reviews_lst, train_labels_lst = 
    read_imdb(".\DataSmall\aclImdb\train") 
  print("Done ")

  # consider creating validation set here

  # 2. tokenize the raw data reviews text
  print("nTokenizing training text ")
  toker = 
    DistilBertTokenizer.from_pretrained(
    'distilbert-base-uncased')
  train_tokens = toker(train_reviews_lst, 
    truncation=True, padding=True)  # token IDs and mask

  # 3. load tokenized text and labels into PyTorch Dataset
  print("nLoading tokenized text into Pytorch Datasets ")
  train_dataset = 
    IMDbDataset(train_tokens, train_labels_lst)
  print("Done ")

  # 4. load (possibly cached) pretrained HF model
  print("nLoading pre-trained DistilBERT model ")
  model = 
    DistilBertForSequenceClassification.from_pretrained( 
    'distilbert-base-uncased')
  model.to(device)
  model.train()  # set into training mode
  print("Done ")

  # 5. fine-tune / train model using standard PyTorch
  print("nLoading Dataset bat_size = 10 ")
  train_loader = DataLoader(train_dataset, 
    batch_size=10, shuffle=True)
  print("Done ")

  print("nFine-tuning the model ")
  optim = AdamW(model.parameters(), lr=5.0e-5)  # wt decay
  for epoch in range(3):
    epoch_loss = 0.0
    for (b_ix, batch) in enumerate(train_loader):
      optim.zero_grad()
      inpt_ids = batch['input_ids']        # tensor
      attn_mask = batch['attention_mask']  # tensor
      lbls = batch['labels']               # tensor
      outputs = model(inpt_ids, 
        attention_mask=attn_mask, labels=lbls)
      loss = outputs[0]
      epoch_loss += loss.item()  # accumulate batch loss
      loss.backward()
      optim.step()
      if b_ix % 5 == 0:  # 200 items is 20 batches of 10
        print(" batch = %5d curr batch loss = %0.4f " % 
        (b_ix, loss.item()))
      # if b_ix >= xx: break  # to save time for demo
    print("end epoch = %4d  epoch loss = %0.4f " % 
      (epoch, epoch_loss))
  print("Training complete ")

  # 6. save trained model weights and biases
  print("nSaving tuned model state ")
  model.eval()
  T.save(model.state_dict(), 
    ".\Models\imdb_state.pt")  # just state
  print("Done ")
  
  print("nEnd demo ")

if __name__ == "__main__":
  main()

Mendapatkan Data Pelatihan IMDB
Data ulasan film IMDB online disimpan dalam bentuk terkompresi sebagai file aclImdb_v1.tar.gz dan pada sistem Windows harus di-unzip dan diekstraksi menggunakan program utilitas seperti WinZip atau 7-Zip. Kedua utilitas itu bagus, tetapi saya lebih suka 7-Zip.

File yang tidak di-zip akan berada di folder root bernama aclImdb (“ACL IMDB”). Folder root berisi subdirektori bernama test and train. Direktori test dan train berisi subdirektori bernama pos dan neg. Masing-masing dari dua direktori ini berisi 12.500 file teks di mana setiap file adalah satu ulasan film.

Nama file terlihat seperti 0_9.txt dan 113_3.txt di mana bagian pertama dari nama tersebut, sebelum garis bawah, adalah indeks berbasis 0 dan bagian kedua dari nama tersebut adalah peringkat numerik sebenarnya dari tinjauan tersebut. Peringkat 7, 8, 9, 10 adalah ulasan positif (semua ada di direktori “pos”) dan peringkat 1, 2, 3, 4 adalah ulasan negatif. Review film yang mendapat rating 5 dan 6 (tidak terlalu positif atau negatif) tidak termasuk dalam dataset IMDB. Perhatikan bahwa peringkat 1-10 yang sebenarnya tidak digunakan dalam demo.

Untuk mengurangi kumpulan data IMDB ke ukuran yang dapat dikelola untuk eksperimen, saya hanya menggunakan file pelatihan, dan menghapus semua ulasan kecuali untuk 100 positif pertama dan 100 negatif pertama, meninggalkan 200 ulasan pelatihan total.

Fungsi read_imdb() yang ditentukan program membaca teks ulasan dan label ke dalam memori. Ini diimplementasikan sebagai:

from pathlib import Path
def read_imdb(root_dir):
  reviews_lst = []; labels_lst = []
  root_dir = Path(root_dir)
  for label_dir in ["pos", "neg"]:
    for f_handle in (root_dir/label_dir).iterdir():
      reviews_lst.append(f_handle.read_text(
        encoding='utf-8'))
      if label_dir == "pos": labels_lst.append(1)
      else: labels_lst.append(0)
  return (reviews_lst, labels_lst)  # list of strings

Pustaka pathlib Python relatif baru (dari Python 3.4) dan sedikit lebih kuat daripada pustaka os lama (yang masih berfungsi dengan baik). Hasil pengembalian fungsi read_imdb() adalah tupel Python di mana item pertama adalah daftar Python dari ulasan yang dipisahkan koma, dan item kedua adalah daftar label kelas terkait, 0 untuk ulasan negatif dan 1 untuk positif.

Memuat Ulasan Film
Program demo memulai eksekusi dengan pernyataan berikut:

def main():
  # 0. get ready
  print("Begin fine-tune for IMDB sentiment ")
  logging.set_verbosity_error()  # suppress warnings
  T.manual_seed(1)
  np.random.seed(1)
. . .

Menekan peringatan bukanlah ide yang baik, tetapi saya melakukannya untuk menjaga agar output tetap rapi untuk tangkapan layar Gambar 1. Menyetel obor dan benih nomor acak NumPy tidak diperlukan tetapi biasanya merupakan ide yang baik untuk mencoba dan membuat program berjalan direproduksi.

Teks dan label ulasan film dimuat ke dalam memori seperti:

  # 1. load raw IMDB train data into memory
  print("Loading IMDB train data subset into memory ")
  train_reviews_lst, train_labels_lst = 
    read_imdb(".\DataSmall\aclImdb\train")  # text list
  print("Done ")

Tokenisasi Teks Ulasan
Demo membuat objek tokenizer dan kemudian menandai teks ulasan dengan dua pernyataan ini:

  # 2. tokenize the raw data reviews text
  print("Tokenizing training text ")
  toker = 
    DistilBertTokenizer.from_pretrained( 
    'distilbert-base-uncased')
  train_tokens = toker(train_reviews_lst, 
    truncation=True, padding=True)

Secara umum, setiap model HF memiliki tokenizer terkait sendiri untuk memecah teks urutan sumber menjadi token. Ini berbeda dengan sistem bahasa sebelumnya yang sering menggunakan tokenizer generik seperti spaCy. Oleh karena itu, demo memuat tokenizer distilbert-base-uncased. Hasil kembali dari pemanggilan tokenizer pada tinjauan IMDB adalah struktur data yang memiliki dua komponen: bidang input_ids yang menampung ID integer yang sesuai dengan kata-kata dalam teks ulasan, dan bidang attention_mask yang menampung 0 dan 1 yang menunjukkan token mana aktif dan token mana yang harus diabaikan (biasanya token padding).

Teks token dan topeng perhatian, dan daftar label kelas diumpankan ke konstruktor IMDbDataset:

  # 3. load tokenized text and labels into PyTorch Dataset
  print("Loading tokenized text into Pytorch Datasets ")
  train_dataset = 
    IMDbDataset(train_tokens, train_labels_lst)
  print("Done ")

Hasil yang dikembalikan adalah objek PyTorch Dataset yang dapat menyajikan item pelatihan dalam batch. Item yang mendasarinya adalah koleksi Kamus dengan kunci [input_ids], [attention_mask], [label].