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 หรือสร้างโมเดลใช้งานเร็วๆ ลองเอาไปเล่นดูนะ แล้วจะติดใจ!

Read more

ไอลีน กู: ตำนานนักสกีฟรีสไตล์ผู้พลิกโฉมวงการและความหมายของชัยชนะ

ไอลีน กู: ตำนานนักสกีฟรีสไตล์ผู้พลิกโฉมวงการและความหมายของชัยชนะ

เจาะลึกเรื่องราวของ Eileen Gu นักสกีฟรีสไตล์ผู้สร้างประวัติศาสตร์ในโอลิมปิก 2026 สถิติที่ไม่เคยมีมาก่อน ประเด็นถกเถียง และความแข็งแกร่งส่วนตัวที่ทำให้เธอก้าวสู่ระดับโลก

By ทีมงาน devdog
วันพระ: คู่มือฉบับสมบูรณ์สำหรับพุทธศาสนิกชนและผู้สนใจยุคใหม่

วันพระ: คู่มือฉบับสมบูรณ์สำหรับพุทธศาสนิกชนและผู้สนใจยุคใหม่

เจาะลึกวันพระและความสำคัญของวันมาฆบูชา 2569 ทั้งวันหยุดราชการ ธนาคาร กิจกรรมเวียนเทียนต้นไม้ และผลกระทบต่อบริการขนส่ง เตรียมตัววางแผนทำบุญและพักผ่อน

By ทีมงาน devdog
ถอดรหัสรักแท้: "บังมัดคลองตันต้นข้าว" เรื่องราวที่สะท้อนการให้อภัยและการเริ่มต้นใหม่

ถอดรหัสรักแท้: "บังมัดคลองตันต้นข้าว" เรื่องราวที่สะท้อนการให้อภัยและการเริ่มต้นใหม่

เจาะลึกงานวิวาห์ "บังมัดคลองตัน" กับ "ต้นข้าว มิสแกรนด์" พร้อมเหตุผลจากใจเจ้าสาวที่เลือกความรักเหนือกาลเวลาและคำวิจารณ์ สู่การเริ่มต้นชีวิตคู่ที่สะท้อนการให้อภัย

By ทีมงาน devdog
ไฮไลท์บอลไทยลีก 2: มหาสารคาม เอสบีที เอฟซี กับฟอร์มร้อนแรงสู่เส้นทางเพลย์ออฟ

ไฮไลท์บอลไทยลีก 2: มหาสารคาม เอสบีที เอฟซี กับฟอร์มร้อนแรงสู่เส้นทางเพลย์ออฟ

เจาะลึกไฮไลท์บอลไทยลีก 2 ของมหาสารคาม เอสบีที เอฟซี กับฟอร์มร้อนแรง ชัยชนะสำคัญจาก ชิตชนก และบทบาทโค้ชดุสิต สู่เส้นทางเพลย์ออฟที่น่าจับตา!

By ทีมงาน devdog