PyTorch + Hugging Face: สร้างโมเดลจำแนกข้อความไทยฉบับเร่งรัด
สวัสดีทุกคน! คือปกติเวลาจะทำอะไรเกี่ยวกับ NLP (Natural Language Processing) เนี่ย โดยเฉพาะกับภาษาไทย มันก็วุ่นวายใช่ป่ะ? ต้องมาหา tokenizer เอง หาโมเดลที่พรีเทรนมาดีๆ อีก กว่าจะเทรนเสร็จกินเวลานานโคตรๆ
แต่เดี๋ยวนี้มันง่ายขึ้นเยอะละนะ ด้วยพลังของ Hugging Face Transformers นี่แหละ วันนี้จะพามาดูว่าเราจะสร้างโมเดลจำแนกข้อความง่ายๆ ด้วย PyTorch กับ Hugging Face ได้ยังไง โดยใช้โมเดลภาษาไทยยอดนิยมอย่าง wangchanberta
ทำไมต้อง Hugging Face?
Hugging Face เนี่ย มันเหมือนเป็นศูนย์รวมของโมเดล AI (โดยเฉพาะ NLP) กับเครื่องมือต่างๆ ที่ทำให้การทำงานกับพวก Transformer Model มันสะดวกขึ้นเยอะเลยอะ ไม่ว่าจะเป็นการโหลดโมเดลที่เทรนมาแล้ว การทำ fine-tuning หรือแม้แต่การ deploy คือมันโคตรสะดวกจริงๆ
เตรียมตัวให้พร้อม!
อันดับแรกก็ต้องลง Library ที่จำเป็นก่อนนะ
pip install transformers datasets accelerate torch
ถ้าใช้ GPU อย่าลืมลง torch ให้ถูกเวอร์ชั่นด้วยนะฮะ เช่น pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 (ถ้าใช้ CUDA 11.8)
ข้อมูลเราอยู่ไหน?
เพื่อความง่าย บทความนี้เราจะสร้าง Dataset แบบง่ายๆ ขึ้นมาจำลองนะ แต่ถ้าใครมีข้อมูลจริง ก็เอาไปปรับใช้ได้เลย หลักๆ คือขอให้มีคอลัมน์สำหรับ text กับ label นะ
from datasets import Dataset
# สมมติว่านี่คือข้อมูล Sentiment ของข้อความ (0 = บวก, 1 = ลบ)
data = [
{"text": "หนังเรื่องนี้ดีมาก ชอบสุดๆ", "label": 0},
{"text": "แย่ที่สุดในชีวิต ไม่ควรดู", "label": 1},
{"text": "เฉยๆ นะ ก็ดูได้เรื่อยๆ", "label": 0},
{"text": "น่าเบื่อสุดๆ เสียเวลามาก", "label": 1},
{"text": "อาหารอร่อยมาก บริการดี", "label": 0},
{"text": "ไม่ประทับใจเลย", "label": 1},
{"text": "สุดยอดจริงๆ", "label": 0},
{"text": "ผิดหวังมาก", "label": 1}
]
# สร้าง Dataset จาก List
dataset = Dataset.from_list(data)
# แบ่งข้อมูลเป็น Train กับ Test
train_test_split = dataset.train_test_split(test_size=0.2)
train_dataset = train_test_split["train"]
eval_dataset = train_test_split["test"]
print("ข้อมูลใน Train Dataset ตัวแรก:", train_dataset[0])
print("จำนวนข้อมูลใน Train:", len(train_dataset))
print("จำนวนข้อมูลใน Eval:", len(eval_dataset))
Tokenizer & Model เลือกตัวไหนดี?
สำหรับภาษาไทย โมเดล wangchanberta-base-att-lm จาก AI Research Thailand (AIST) เนี่ย ถือว่าเวิร์คมากเลยนะ โหลดมาใช้ได้เลย
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
# ชื่อโมเดลสำหรับภาษาไทย
model_name = "airesearch/wangchanberta-base-att-lm"
# โหลด Tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name)
# โหลด Model สำหรับ Classification (มี 2 Class คือ 0 กับ 1)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
# ฟังก์ชันสำหรับ Tokenize ข้อมูล
def tokenize_function(examples):
# truncation=True คือตัดข้อความถ้ามันยาวเกิน max_length ของโมเดล
# padding="max_length" คือเติม [PAD] token ให้ความยาวเท่ากัน
return tokenizer(examples["text"], truncation=True, padding="max_length")
# ใช้ .map() กับ Dataset เพื่อ Tokenize
tokenized_train_dataset = train_dataset.map(tokenize_function, batched=True)
tokenized_eval_dataset = eval_dataset.map(tokenize_function, batched=True)
# ลบคอลัมน์ 'text' ออก เพราะโมเดลไม่ต้องการแล้ว
tokenized_train_dataset = tokenized_train_dataset.remove_columns(["text"])
tokenized_eval_dataset = tokenized_eval_dataset.remove_columns(["text"])
# ตั้งค่าให้ Dataset เป็น PyTorch Tensors
tokenized_train_dataset.set_format("torch")
tokenized_eval_dataset.set_format("torch")
print("ข้อมูลที่ถูก Tokenize แล้วใน Train Dataset ตัวแรก:", tokenized_train_dataset[0])
ถึงเวลาเทรน!
ตรงนี้แหละคือความสะดวกของ Hugging Face! เราจะใช้ Trainer API ของเค้า ซึ่งมันช่วยให้เราไม่ต้องเขียน Training Loop เองเยอะแยะเลย ง่ายโคตรๆ
from transformers import TrainingArguments, Trainer
import numpy as np
from datasets import load_metric
# กำหนด Metric สำหรับการประเมินผล
metric = load_metric("accuracy")
def compute_metrics(eval_pred):
# ฟังก์ชันคำนวณ Accuracy
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
# กำหนด Arguments สำหรับการเทรน
training_args = TrainingArguments(
output_dir="./results", # ไดเรกทอรี่สำหรับเก็บผลลัพธ์
num_train_epochs=5, # จำนวน Epochs
per_device_train_batch_size=4, # ขนาด Batch สำหรับเทรนบนแต่ละ GPU (สำคัญมาก!)
per_device_eval_batch_size=4, # ขนาด Batch สำหรับ Eval บนแต่ละ GPU
warmup_steps=100, # จำนวน Steps สำหรับ Warmup Learning Rate
weight_decay=0.01,
logging_dir="./logs",
logging_steps=10,
evaluation_strategy="epoch", # ประเมินผลทุกๆ Epoch
save_strategy="epoch", # บันทึกโมเดลทุกๆ Epoch
load_best_model_at_end=True, # โหลดโมเดลที่ดีที่สุดเมื่อจบการเทรน
metric_for_best_model="accuracy", # ใช้ Accuracy ในการตัดสินใจว่าโมเดลไหนดีสุด
report_to="none", # ไม่ต้องส่ง Log ไปที่ Weights & Biases หรืออื่น ๆ
# gradient_accumulation_steps=2 # ลองใช้ถ้าติดเรื่อง CUDA out of memory
)
# สร้าง Trainer Instance
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_train_dataset,
eval_dataset=tokenized_eval_dataset,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
# เริ่มเทรน!
trainer.train()
# Save the fine-tuned model
trainer.save_model("./my_finetuned_model")
ปัญหาที่เจอบ่อยๆ (ของจริงเลยนะ!)
1. CUDA out of memory
อันนี้คือเจอประจำ! โดยเฉพาะถ้า GPU แรมน้อยๆ (แบบ 4GB, 8GB) แล้วไปใช้ batch_size ใหญ่ๆ หรือโมเดลใหญ่ๆ
วิธีแก้คือ: - ลด per_device_train_batch_size กับ per_device_eval_batch_size ลง ให้เหลือ 4, 2, หรือ 1 ไปเลย - ถ้าลด batch_size แล้วยังอยากให้ effective batch size มันใหญ่ๆ อยู่ ก็ใช้ gradient_accumulation_steps เพิ่มเข้าไปใน TrainingArguments อย่างเช่น ถ้า batch_size=4 แล้ว gradient_accumulation_steps=2 มันจะเหมือน batch_size=8 ในแง่ของการอัปเดต Weight - หรือถ้าไม่มี GPU แรงๆ จริงๆ ก็แนะนำไปรันบน Google Colab หรือ Kaggle Notebook แทนนะ เค้ามี GPU ให้ใช้ฟรี
2. Data format error
บางทีก็ลืม set_format("torch") หรือคอลัมน์ใน Dataset ไม่ตรงกับที่โมเดลคาดหวัง เช่น โมเดลต้องการ input_ids กับ attention_mask แต่เราดันมีแค่ text
Error ที่เห็นอาจจะประมาณนี้ ValueError: Expected input batch_dim=0, got batch_dim=None หรือ KeyError: 'input_ids'
ให้กลับไปเช็คขั้นตอนการ Tokenize กับการ remove_columns ดูให้ดีๆ ว่า input_ids, attention_mask, labels (สำหรับตอนเทรน) มันมีครบถ้วนและอยู่ในฟอร์แมตที่ถูกต้องแล้วรึยัง
ลองเอาไปใช้จริง
หลังจากเทรนเสร็จ เราสามารถโหลดโมเดลที่เราเทรนมาใช้ทำนายข้อความใหม่ๆ ได้เลย
# โหลดโมเดลที่เราเพิ่งเทรนเสร็จ
# model = AutoModelForSequenceClassification.from_pretrained("./my_finetuned_model")
# tokenizer = AutoTokenizer.from_pretrained("./my_finetuned_model") # tokenizer ก็ต้องโหลดจากที่เซฟด้วยนะ
text_to_classify = "ร้านนี้อาหารอร่อยมาก ไปกินอีกแน่นอน!"
# Tokenize ข้อความที่จะทำนาย
inputs = tokenizer(text_to_classify, return_tensors="pt")
# ถ้ามี GPU ให้ย้ายโมเดลและข้อมูลไปที่ GPU
if torch.cuda.is_available():
inputs = {k: v.to("cuda") for k, v in inputs.items()}
model.to("cuda")
# ทำนายผล
with torch.no_grad(): # ไม่ต้องคำนวณ Gradient ในโหมด Inferene
outputs = model(**inputs)
logits = outputs.logits
predictions = torch.argmax(logits, dim=-1)
# ต้องรู้เองว่า Label 0 กับ 1 คืออะไร (ในตัวอย่างนี้ 0=บวก, 1=ลบ)
label_map = {0: "บวก", 1: "ลบ"}
print(f"ข้อความ: '{text_to_classify}' ถูกจัดประเภทเป็น: {label_map[predictions.item()]}")
text_to_classify_2 = "ไม่คุ้มค่าเลย เสียดายเงิน"
inputs_2 = tokenizer(text_to_classify_2, return_tensors="pt")
if torch.cuda.is_available():
inputs_2 = {k: v.to("cuda") for k, v in inputs_2.items()}
with torch.no_grad():
outputs_2 = model(**inputs_2)
logits_2 = outputs_2.logits
predictions_2 = torch.argmax(logits_2, dim=-1)
print(f"ข้อความ: '{text_to_classify_2}' ถูกจัดประเภทเป็น: {label_map[predictions_2.item()]}")
สรุปนะ
จะเห็นว่าการ Fine-tune โมเดล NLP ด้วย Hugging Face Transformers และ PyTorch มันไม่ได้ยากอย่างที่คิดเลยนะ โดยเฉพาะถ้ามี Trainer API มาช่วย มันทำให้โค้ดเราสั้นลงเยอะมาก แล้วก็ไม่ต้องมานั่งจัดการเรื่อง Learning Rate Schedule, Checkpointing, Evaluation อะไรพวกนี้เองให้ปวดหัว คือเหมาะมากกับการทำ Prototype หรือสร้างโมเดลใช้งานเร็วๆ ลองเอาไปเล่นดูนะ แล้วจะติดใจ!