နားလည်ထားရမယ့် အချက်
စာသား sentiment ကို classify လုပ်ဖို့ ဟာသီးသန့် word index တွေကို neural network တွက်ချက်နိုင်တဲ့ ပုံစံအဖြစ် ပြောင်းပေးနိုင်တဲ့ နည်းလမ်းတစ်ခု၊ ပြီးတော့ အရှည်မတူညီတဲ့ sequence တစ်ခုကို fixed-size decision တစ်ခုအဖြစ် ချုံ့ပေးနိုင်တဲ့ နည်းလမ်းတစ်ခု လိုအပ်ပါတယ်။ One-hot vector တွေလည်း အလုပ်လုပ်နိုင်ပေမယ့် dimension တွေ ဖြုန်းနေတယ်၊ ဆက်စပ်တဲ့ word တွေကြား ဘာသတင်းအချက်အလက်မှ မမျှဝေနိုင်ဘူး — nn.Embedding ကတော့ training လုပ်နေတုန်း word index တစ်ခုစီအတွက် dense vector တစ်ခုကို သင်ယူပေးတယ်၊ ဒါကြောင့် context ထဲမှာ တူညီစွာ သုံးတတ်တဲ့ word တွေက ဆင်တူ vector တွေရရှိသွားတယ်။ အဲဒီ embedding တွေကို RNN တစ်ခုထဲ token တစ်ခုချင်း ကျွေးလိုက်ရင် word တိုင်းက update လုပ်ပေးထားတဲ့ hidden state တစ်ခုရရှိလာတယ်၊ ဒါကြောင့် နောက်ဆုံး hidden state က sentence တစ်ခုလုံးရဲ့ အနှစ်ချုပ်ကို sentence အရှည်ဘယ်လောက်ရှိရှိ ကိုယ်စားပြုနိုင်တယ်။ နောက်ဆုံးမှာ Linear layer တစ်ခုက အဲဒီ fixed-size summary ကို logit နှစ်ခု (positive/negative) အဖြစ် map လုပ်ပေးတယ်။ Naive အနေနဲ့ word embedding တွေကို average ချည်း လုပ်ပြီး RNN ကို လုံးဝကျော်သွားတာက word order ကို လျစ်လျူရှုသွားတာပါ ('not good' နဲ့ 'good not' ကွာခြားချက် မမြင်နိုင်တော့ဘူး) — RNN ရဲ့ အဆက်ဆက် update လုပ်တဲ့ ပုံစံကတော့ order နဲ့ context ကို နောက်ဆုံး representation ထဲ ထည့်ပေးနိုင်တယ်။ ဒီ embedding-then-RNN-then-classify ပုံစံဟာ transformer မတိုးမီ real sentiment model တွေရဲ့ ခြေထောက်ပါပဲ။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
ဒါဟာ Tutorial Platform ရဲ့ real feature တစ်ခုအတွက် တိုက်ရိုက် prototype တစ်ခုပါပဲ — learner feedback comment တွေကို positive ဒါမှမဟုတ် negative အဖြစ် classify လုပ်ပြီး 'confusing' ဒါမှမဟုတ် 'too fast' feedback ထပ်ခါထပ်ခါရနေတဲ့ lesson တွေကို content review အတွက် auto-flag လုပ်ပေးနိုင်တယ်၊ 'great explanation' feedback ရတဲ့ lesson တွေကိုလည်း strong teaching style ရဲ့ ဥပမာအဖြစ် ပေါ်လွင်စေနိုင်တယ်။ ဒီ toy sentence 8 ခု word 20 လုံးပါတဲ့ vocabulary ကနေ real feedback text (proper subword tokenizer နဲ့ tokenize လုပ်ထားတာ၊ embedding table ကြီးများသုံးထားတာ) အထိ scale တိုးလိုက်ရင်လည်း model shape, training loop, evaluation logic တွေက almost မပြောင်းဘဲ production feature ထဲ ဆက်သုံးနိုင်ပါတယ်။
အတူတူ စမ်းရေးကြည့်မယ်
import torch
import torch.nn as nn
import torch.optim as optim
vocab_size = 20
embed_dim = 8
hidden_dim = 16
sentences = [
[1, 2, 3, 4], # "this lesson was great"
[5, 6, 7], # "great explanation thanks"
[8, 9, 10, 11], # "this lesson was confusing"
[12, 13, 14], # "very confusing explanation"
[1, 6, 4], # "this great was"
[8, 13, 11], # "this confusing was"
[5, 2, 3],
[12, 9, 10],
]
labels = [1, 1, 0, 0, 1, 0, 1, 0]
def pad_sequences(seqs, pad_value=0):
max_len = max(len(s) for s in seqs)
return torch.tensor([s + [pad_value] * (max_len - len(s)) for s in seqs])
X = pad_sequences(sentences)
y = torch.tensor(labels)
class SentimentRNN(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
self.rnn = nn.RNN(embed_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, 2)
def forward(self, x):
embedded = self.embedding(x)
_, hidden = self.rnn(embedded)
return self.fc(hidden.squeeze(0))
model = SentimentRNN(vocab_size, embed_dim, hidden_dim)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
for epoch in range(50):
optimizer.zero_grad()
outputs = model(X)
loss = criterion(outputs, y)
loss.backward()
optimizer.step()
if (epoch + 1) % 10 == 0:
print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")10 epoch ခြားတိုင်း Loss တန်ဖိုးကို print ထုတ်ပြီး၊ 50 epoch အတွင်း Loss တန်ဖိုးက 0 နီးနီးအထိ ဆက်တိုက်ကျသွားပါလိမ့်မယ် (sentence 8 ခုသာ ရှိတာမို့ model က ချက်ခြင်းနီးပါး memorize/fit ဖြစ်သွားနိုင်တယ်)။၅ မိနစ် စမ်းကြည့်
sentences list ထဲ positive/negative sentence အသစ်နှစ်ခုကို token index အသစ်တွေနဲ့ ထည့်ကြည့်ပါ (vocab_size ကို လိုအပ်ရင် တိုးပါ) — model က training data အသစ်ပါလာတဲ့အခါ loss ဘယ်လောက် epoch နဲ့ 0 နီးနီးရောက်လဲ လေ့လာပါ။ nn.RNN ကို nn.LSTM နဲ့ အစားထိုးကြည့်ပြီး ဘာပြောင်းလဲသွားလဲ စမ်းသပ်ပါ။
သတိလေးတစ်ချက်
nn.Embedding မှာ padding_idx=0 ကို မမေ့ရဘူး (ဒါမှမဟုတ် sentence တိုတွေကို consistent ဖြစ်အောင် pad မလုပ်ရင်) — model က padding token ကနေ အဓိပ္ပါယ်မရှိတဲ့ signal ကို သင်ယူနိုင်တယ်။
RNN ရဲ့ နောက်ဆုံး output timestep နဲ့ နောက်ဆုံး hidden state ကို ရောထွေးသုံးမိတာ — single-layer, single-direction RNN တစ်ခုအတွက်တော့ hidden.squeeze(0) ကသာ အသန့်ရှင်းဆုံး fixed-size sequence summary ဖြစ်ပါတယ်။
PyTorch Docs — Text Classification Tutorial — Deep Learning