နားလည်ထားရမယ့် အချက်
CNN တစ်ခု ဒါမှမဟုတ် plain feedforward network တစ်ခုက fixed-size input ကို မျှော်လင့်ထားပြီး တစ်ခါတည်း process လုပ်တယ်၊ image အတွက်တော့ ရလဒ်ကောင်းပေမဲ့ length မတူညီပြီး element order က အဓိပ္ပာယ်ပါတဲ့ text ဒါမှမဟုတ် time series လို sequence အတွက်တော့ မအလုပ်ဖြစ်ဘူး။ RNN တစ်ခုကတော့ sequence ကို element တစ်ခုချင်းစီ process လုပ်ပြီး hidden state — ယခုအထိ တွေ့ခဲ့တာအားလုံးကို summarize လုပ်ထားတဲ့ vector တစ်ခု — ကို ထိန်းသိမ်းထားတယ်၊ step တစ်ခုချင်းစီမှာ current input နဲ့ previous hidden state ကို ပေါင်းစပ်ပြီး update လုပ်ကာ step နောက်တစ်ခုဆီ ဆက်ပို့ပေးတယ်။ ဒါက element နောက်ပိုင်းတွေကို သူတို့ရှေ့က အားလုံးရဲ့ context ထဲမှာ interpret လုပ်နိုင်စေတယ်၊ ဒါက language နဲ့ time series တွေ တကယ်လိုအပ်တာဖြစ်တယ် — 'bank' ဆိုတဲ့ စကားလုံးက 'river' နောက်ကလာရင် 'money' နောက်ကလာတာနဲ့ အဓိပ္ပာယ် ကွဲပြားတယ်။ ပြဿနာကတော့ training ပါပဲ — gradient တွေကို step တိုင်းကနေ ပြန် backpropagate လုပ်ရတယ် (backpropagation through time)၊ step အများကြီး ဖြတ်သန်းရင် gradient တွေက multiplicative အနေနဲ့ ကျုံ့သွားပြီး effectively သုညအထိ ဖြစ်သွားတတ်တယ်၊ ဒါကြောင့် plain RNN တစ်ခုက step အနည်းငယ်ထက် ပိုကျယ်ပြန့်တဲ့ dependency တွေကို သင်ယူရတာ ခက်ခဲတယ် — early context ကို နောက်ပိုင်း step တွေရောက်ချိန်မှာ 'မေ့' သွားတတ်တယ်။ LSTM နဲ့ GRU cell တွေကတော့ step တစ်ခုချင်းစီမှာ ဘယ်အချက်အလက်ကို ထိန်းထား/update/ပယ်ဖျက်မလဲဆိုတာကို ရှင်းလင်းစွာ ထိန်းချုပ်ပေးတဲ့ learned gating mechanism တွေနဲ့ ဒီပြဿနာကို ဖြေရှင်းပေးတယ်၊ long-range signal ကို ပိုကောင်းစွာ ထိန်းသိမ်းပေးနိုင်တယ်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Tutorial Platform က learner query ထဲက word sequence ကို သုံးပြီး search result ကို learned relevance အလိုက် rank လုပ်ချင်တယ် ဒါမှမဟုတ် learner ရဲ့ လတ်တလော activity history ကို အခြေခံပြီး နောက်ထပ် ဘာကို search လုပ်နိုင်လဲ predict လုပ်ချင်တယ်ဆိုရင် ordered sequence တွေကို ကိုင်တွယ်နေတာဖြစ်ပြီး element နောက်ပိုင်းတွေက ရှေ့က context ကို မှီခိုနေတယ် — ဒါက RNN ရဲ့ home turf အတိအကျပါပဲ။ Plain RNN တစ်ခုက query ရှည်ရှည် ဒါမှမဟုတ် session history ရှည်ရှည်ကို ဖတ်နေတဲ့အခါ sequence ရဲ့ အစပိုင်းက word အရေးကြီးတစ်ခုကို အဆုံးရောက်ချိန်မှာ လွဲသွားနိုင်တယ်။ လက်တွေ့မှာတော့ team က plain RNN အစား LSTM ဒါမှမဟုတ် GRU ကို ရွေးမှာဖြစ်တယ်၊ sequence ရှည်ရှည်တစ်လျှောက် early context ကို ရှင်သန်နေအောင် ထိန်းထားဖို့ အတွက်ပါ။
အတူတူ စမ်းရေးကြည့်မယ်
import torch
import torch.nn as nn
rnn = nn.RNN(input_size=5, hidden_size=8, batch_first=True)
# batch=2 sequences, each 6 time steps long, each step a 5-dim vector
sequence = torch.randn(2, 6, 5)
output, hidden = rnn(sequence)
print("Output shape:", output.shape) # hidden state at every time step
print("Hidden shape:", hidden.shape) # final hidden state only`Output shape: torch.Size([2, 6, 8])` (sequence နှစ်ခုစလုံးအတွက် time step 6 ခုအပြင် hidden state) နဲ့ `Hidden shape: torch.Size([1, 2, 8])` (နောက်ဆုံး hidden state တစ်ခုတည်း၊ sequence တစ်ခုစီအတွက် 8-dim vector တစ်ခု) ကို print ထုတ်တယ်။၅ မိနစ် စမ်းကြည့်
`hidden_size` ကို 16 အဖြစ် ပြောင်းပြီး ပြန် run ကြည့်ကာ `output` ရဲ့ နောက်ဆုံး dimension နဲ့ `hidden` ရဲ့ နောက်ဆုံး dimension နှစ်ခုစလုံး 16 ဖြစ်လာမလား စစ်ဆေးပါ။
သတိလေးတစ်ချက်
`batch_first=True` ကို မေ့ကျန်ခဲ့ပြီး sequence tensor ကို (batch, seq_len, features) အဖြစ်ပဲ index လုပ်ဆက်နေခြင်း — default အနေနဲ့ `nn.RNN` က (seq_len, batch, features) ကို မျှော်လင့်ထားလို့ flag ချန်ထားရင် dimension တွေရဲ့ အဓိပ္ပာယ် တိတ်တဆိတ် ပြောင်းသွားပြီး error မတက်ဘဲ ရလဒ်မှားလာမယ်။
'sequence က ဘာကို ဆိုလိုလဲ' ကို ကိုယ်စားပြုဖို့ `hidden` ကို တကယ်လိုအပ်တဲ့အချိန် `output` ရဲ့ နောက်ဆုံး time step ကို သုံးမိခြင်း (ဒါမှမဟုတ် ပြောင်းပြန်) — single-layer unidirectional RNN အတွက်တော့ ဒါနှစ်ခု တူတူဖြစ်ပေမဲ့ layer ထပ်ချသောအခါ ဒါမှမဟုတ် bidirectional RNN သုံးသောအခါ ကွဲသွားလို့ tensor မှားယူမိတာ လွယ်ကူသွားတယ်။
Wikipedia — Recurrent neural network — Deep Learning