📖 একটি ছোট গল্প
Image-এর জন্য CNN, কিন্তু "আমি ভাত খেয়েছি" বনাম "ভাত আমি খেয়েছি" — শব্দের ক্রম অর্থ বদলে দেয়। Sequence handle করার জন্য জন্ম নিল RNN — যার memory আছে। পরে এল LSTM, GRU — vanishing gradient-এর সমাধান হয়ে।
Vanilla RNN
hₜ = tanh(Wₕₕ hₜ₋₁ + Wₓₕ xₜ + bₕ)
yₜ = Wₕᵧ hₜ + bᵧ
একই weight প্রতিটি timestep-এ shared। Backprop Through Time (BPTT) দিয়ে train।
Vanishing/Exploding Gradient
BPTT-তে gradient T step পেছনে পাঠাতে হয়:
∂L/∂h₀ ∝ ∏ₜ Wₕₕᵀ · diag(tanh'(·))
Eigenvalue < 1 → vanish; > 1 → explode। তাই long-sequence-এ RNN দুর্বল।
LSTM
Gates দিয়ে cell state নিয়ন্ত্রণ:
fₜ = σ(W_f·[hₜ₋₁, xₜ] + b_f) (forget)
iₜ = σ(W_i·[hₜ₋₁, xₜ] + b_i) (input)
c̃ₜ = tanh(W_c·[hₜ₋₁, xₜ] + b_c)
cₜ = fₜ ⊙ cₜ₋₁ + iₜ ⊙ c̃ₜ
oₜ = σ(W_o·[hₜ₋₁, xₜ] + b_o)
hₜ = oₜ ⊙ tanh(cₜ)
Cell state-এ "+" path → gradient সরাসরি প্রবাহিত হয় → long dependency শেখা সম্ভব।
GRU
LSTM-এর simplified সংস্করণ — দুটি gate (reset, update), cell state নেই। কম parameter, প্রায় সমান performance।
Python Implementation
pythonPython · NumPy
import torch, torch.nn as nn
# Character-level LSTM
class CharLSTM(nn.Module):
def __init__(self, vocab, hidden=128):
super().__init__()
self.emb = nn.Embedding(vocab, hidden)
self.lstm = nn.LSTM(hidden, hidden, batch_first=True)
self.fc = nn.Linear(hidden, vocab)
def forward(self, x, h=None):
e = self.emb(x)
o, h = self.lstm(e, h)
return self.fc(o), h
model = CharLSTM(vocab=50)
x = torch.randint(0, 50, (8, 20)) # batch=8, seq=20
y, h = model(x)
print(y.shape) # → [8, 20, 50]Why Transformers Won
- RNN sequential → GPU underutilized।
- Long-range dependency এখনো কঠিন (১০০০+ token)।
- Transformer parallel + global attention → দ্রুত ও শক্তিশালী।
- তবু RNN/LSTM time-series, speech, edge device-এ এখনো প্রাসঙ্গিক।
Summary · সারসংক্ষেপ
- RNN = recurrence + shared weights; vanishing gradient-এ সীমিত।
- LSTM cell state + gates দিয়ে long memory সম্ভব।
- GRU = lightweight LSTM।
- Transformer যুগেও RNN-এর জ্ঞান foundational।