পাঠ ২৫ · ৪০-এর মধ্যে · মডিউল ৪

RNN — ক্রমিক ডেটা

Recurrent Neural Networks — sequence modeling
৮ মিনিট পড়া মাঝারি · Intermediate PyTorch hands-on

এই পাঠে যা শিখবেন

  • ক্রমিক ডেটা কী — ও কেন MLP/CNN যথেষ্ট না
  • RNN-এর hidden state ও parameter sharing
  • RNN cell-এর গাণিতিক রূপ — forward computation
  • Three architecture pattern — sentiment, generation, tagging
  • PyTorch nn.RNN দিয়ে Bangla character-level model
  • Vanilla RNN-এর সীমাবদ্ধতা — গভীরে যাওয়ার আগে

১ · ক্রমিক ডেটা — কেন বিশেষ

একটি ছবি — pixel-এর collection, ক্রম গুরুত্বপূর্ণ না (CNN-এ permutation একটু matter কিন্তু global)। কিন্তু "আমি ভাত খাই না" বনাম "না ভাত খাই আমি" — শব্দের ক্রম বদলালে অর্থ বদলায়। Sequential dataSequential Dataএমন ডেটা যেখানে ক্রম গুরুত্বপূর্ণ — text (বাক্যে শব্দ), audio (sample-এর ক্রম), video (frame), stock price (time-series), DNA (base sequence)।-এ প্রতিটি element-এর "অবস্থান" significant।

  • Text: "ঢাকার আবহাওয়া আজ ভাল" — শব্দ-এর sequence।
  • Audio: ১৬০০০ sample/সেকেন্ড — number sequence।
  • Time-series: Bangladesh-এ চালের দাম — দৈনিক/মাসিক sequence।
  • DNA: A, T, G, C — base sequence।
MLP-এর সমস্যা

MLP fixed-size input নেয়। বাক্য ৫ শব্দ বা ৫০ শব্দ — sometimes ভিন্ন। তাছাড়া MLP-এর কাছে input-এর প্রতিটি position-এর জন্য আলাদা weight — ক্রম শেখা কঠিন। ছোট ডেটায় overfittingOverfittingমডেল training data মুখস্থ করে ফেলে — কিন্তু নতুন data-তে খারাপ পারফর্ম করে। অবধারিত।

২ · RNN — মূল ধারণা

ভাবুন একজন মানুষ একটি বই পড়ছেন। প্রতিটি শব্দ পড়ার সময় তার "মাথায়" আগের শব্দগুলোর একটি সংক্ষিপ্ত ছাপ আছে। সেই ছাপ + নতুন শব্দ = updated মাথা। RNN ঠিক এই কাজটা করে — একটি hidden state $h_t$ যা "এতক্ষণ যা পড়েছি, তার সংক্ষিপ্ত প্রতিনিধিত্ব"।

প্রতিটি timestep $t$-এ:

$$h_t = \tanh(W_h \, h_{t-1} + W_x \, x_t + b_h)$$ $$y_t = W_y \, h_t + b_y$$

লক্ষণীয়: $W_h, W_x, W_y$ — সমস্ত timestep-এ একই weight (parameter sharing)। এই কারণেই RNN যেকোনো দৈর্ঘ্যের sequence handle করতে পারে।

ভাবুন আপনি একটি গান শুনছেন। প্রতিটি note আগের note-এর সাথে relate করে আপনি melody বুঝছেন। মাথায় পুরো গান entire stored না — শুধু "এতক্ষণ পর্যন্ত মনের ভাব" থাকে। RNN-এর hidden state ঠিক সেই "মনের ভাব"।

৩ · Unrolling — সময়ের সাথে network

RNN-কে "একই cell বারবার" হিসেবে দেখলে confuse লাগতে পারে। Unrolling করলে সহজ — প্রতিটি timestep-কে আলাদা copy হিসেবে আঁকুন, সব copy-তে একই weight। তখন এটা একটি deep feed-forward network-এর মতো — কিন্তু weight-share করা।

একটি ৪-শব্দের বাক্য "আমি বাংলা ভালোবাসি" দেখুন:

  • $t=1$: $x_1$ = "আমি", $h_1 = f(h_0, x_1)$
  • $t=2$: $x_2$ = "বাংলা", $h_2 = f(h_1, x_2)$
  • $t=3$: $x_3$ = "ভালোবাসি", $h_3 = f(h_2, x_3)$

$h_3$-এ পুরো বাক্যের সংক্ষিপ্ত ছাপ — sentiment classifier-এর জন্য এটা যথেষ্ট।

৪ · তিনটি RNN architecture pattern

  • Many-to-one: পুরো input sequence → একটি output। যেমন sentiment ("এই movie ভাল" → positive)।
  • One-to-many: একটি input → output sequence। যেমন image captioning (একটি ছবি → পুরো caption)।
  • Many-to-many (sync): প্রতিটি input-এ output। যেমন POS tagging (প্রতিটি শব্দে tag)।
  • Many-to-many (encoder-decoder): input sequence → output sequence (ভিন্ন দৈর্ঘ্য)। যেমন translation। L29-এ বিস্তারিত।
RNN — সময়ের সাথে unrolled h_t = tanh(W_h · h_(t-1) + W_x · x_t + b) x₁ = "আমি" x₂ = "বাংলা" x₃ = "ভালো" x₄ = "বাসি" h₁ h₂ h₃ h₄ W_h W_h W_h y = sentiment "positive" h₀ = 0 প্রতিটি timestep — একই weight (W_h, W_x) share। শেষ hidden state-এ পুরো sequence-এর সংক্ষিপ্ত।
RNN unrolled — many-to-one pattern (sentiment classification)। Hidden state ক্রম-অনুযায়ী update হয়, শেষ state থেকে prediction।

৫ · PyTorch nn.RNN — first model

Vanilla RNN PyTorch-এ তৈরি — একটি character-level Bangla model হাতে-কলমে।

Python · PyTorch
import torch
import torch.nn as nn

class CharRNN(nn.Module):
    def __init__(self, vocab_size, hidden_size=128):
        super().__init__()
        self.embed = nn.Embedding(vocab_size, hidden_size)
        self.rnn = nn.RNN(hidden_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, vocab_size)

    def forward(self, x, h=None):
        # x: (batch, seq_len)
        emb = self.embed(x)              # (B, T, H)
        out, h = self.rnn(emb, h)        # (B, T, H), (1, B, H)
        logits = self.fc(out)            # (B, T, V)
        return logits, h

# Bangla vocab — অ, আ, ই, ঈ, ... + space
vocab = list("অআইঈউঊঋএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ় ")
vocab_size = len(vocab)
print(f"Vocab size: {vocab_size}")

model = CharRNN(vocab_size, hidden_size=64)
x = torch.randint(0, vocab_size, (2, 10))  # 2 sequences, 10 chars
logits, h = model(x)
print(f"Logits shape: {logits.shape}")     # (2, 10, vocab_size)
print(f"Hidden shape: {h.shape}")          # (1, 2, 64)

    
nn.RNN — built-in vanilla RNN। batch_first=True মানে input shape (batch, seq, feature)। Hidden state default-এ zero, চাইলে আলাদা pass করা যায়।

৬ · Manual RNN cell — internals

nn.RNN-এর ভিতরে কী হয়? নিচের কোডে নিজেই RNN cell লিখলাম — বুঝার জন্য।

Python · Manual RNN
import torch
import torch.nn as nn

class ManualRNN(nn.Module):
    def __init__(self, input_size, hidden_size):
        super().__init__()
        self.W_x = nn.Linear(input_size, hidden_size, bias=False)
        self.W_h = nn.Linear(hidden_size, hidden_size, bias=True)
        self.hidden_size = hidden_size

    def forward(self, x):
        # x: (batch, seq, input_size)
        B, T, _ = x.shape
        h = torch.zeros(B, self.hidden_size, device=x.device)
        outputs = []
        for t in range(T):
            h = torch.tanh(self.W_x(x[:, t, :]) + self.W_h(h))
            outputs.append(h)
        return torch.stack(outputs, dim=1), h

rnn = ManualRNN(input_size=10, hidden_size=20)
x = torch.randn(4, 7, 10)   # batch=4, seq=7, feat=10
out, h = rnn(x)
print(out.shape)            # (4, 7, 20)
print(h.shape)              # (4, 20)

    

৭ · Vanilla RNN-এর সমস্যা

  • Vanishing gradient: gradient backward-এ tanh derivative-এ বারবার গুণ — long sequence-এ প্রায় শূন্য। শুরুর শব্দ থেকে শেষের শব্দে gradient পৌঁছায় না।
  • Exploding gradient: বিপরীত — gradient বেড়ে যায়। Gradient clipping দিয়ে control।
  • Long-term dependency দুর্বল: "আমি ছোটবেলায় ভারতে গিয়েছিলাম, তাই আমি ভাল __ বলতে পারি" — blank-এ "হিন্দি" — vanilla RNN-এ এই দীর্ঘ সম্পর্ক ধরা কঠিন।
  • Sequential computation: parallelize করা যায় না। Modern GPU-এর সুবিধা পুরো নেয়া যায় না।
Vanilla RNN আজকের production system-এ rarely ব্যবহৃত। কিন্তু LSTM, GRU, Transformer — সব এর ভিত্তিতে দাঁড়ানো। ধারণা না বুঝলে পরের সব lesson কঠিন। L26-এ BPTT, L27-এ LSTM — সমাধান।

৮ · কোথায় RNN ব্যবহৃত — সংক্ষেপে

  • Bangla NLP: sentiment, NER, POS tagging — early models RNN/LSTM-based।
  • Speech recognition: Bangla voice assistant — RNN অংশ। আজকাল Conformer/Transformer।
  • Time-series forecast: Bangladesh-এ rice price, weather prediction।
  • Music generation: note sequence।
  • Handwriting: stroke sequence।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ RNN-এ "parameter sharing" — কেন এটা এত গুরুত্বপূর্ণ? Position-নির্ভর আলাদা weight ব্যবহার করলে কী হবে?

Parameter sharing — RNN-এর সবচেয়ে গভীর design choice। সাদৃশ্য: CNN-এ একই kernel image-এর সব position-এ ব্যবহার। RNN-এ একই $W_h, W_x$ সব timestep-এ।

কেন sharing — তিনটি মূল কারণ:

  • Variable length handle: ৫-শব্দ বা ৫০০-শব্দ বাক্য — একই network। Position-ভিত্তিক weight হলে নতুন length-এর জন্য নতুন weight train করতে হবে।
  • Generalization: "আমি ভাত খাই" বনাম "সে ভাত খায়" — "ভাত" শব্দটি position ১ এবং ২-এ আছে। একই weight হলে — একই "ভাত" representation। আলাদা weight হলে — দু'টি আলাদা embedding শিখতে হবে।
  • Sample efficiency: $T$ timestep-এর জন্য $T \times$ parameter — exponentially less data দরকার। Small dataset-এ critical।

Position-নির্ভর weight হলে কী হবে:

  • Variable length training impossible।
  • Each position separately learn — slow ও data-hungry।
  • Translation invariance ভেঙে পড়বে।
  • Practical-এ এটা শুধু "MLP on flattened input" — কিন্তু MLP variable length handle করে না।

Trade-off:

  • Sharing = less expressive। সব timestep-এ identical "rule"।
  • সমাধান — hidden state বড় করুন, যাতে context store হয়।
  • Modern attention/Transformer — sharing রেখেও position information যোগ করে (positional encoding)।

Inductive bias:

  • "একই rule সব timestep-এ" — একটি strong assumption।
  • Most language ঠিক এই assumption মেনে চলে — grammar context-dependent কিন্তু rule আগাগোড়া same।
  • Stationary process assumption।

Convolution-এর সাথে comparison:

  • CNN — spatial weight sharing।
  • RNN — temporal weight sharing।
  • Transformer — সব position-এ weight share, কিন্তু attention pattern position-aware।
  • সব modern architecture — কোনো না কোনো sharing-এর উপর দাঁড়ানো।

মূল উপলব্ধি: Parameter sharing শুধু efficiency না — এটা একটি inductive bias। Sequence-এ একই rule সর্বত্র apply হওয়ার ধারণা — language, music, time-series-এর core nature। RNN-এর শক্তির মূল উৎস। আজকের ML-এ "right inductive bias" ই সব — এই lesson lifelong।

প্র ০২ Vanishing gradient — গাণিতিকভাবে কেন ঘটে? Tanh activation-এর role কী? ReLU দিয়ে সমাধান হবে?

Vanishing gradient — RNN-এর "achilles heel"। Bengio-Simard-Frasconi (১৯৯৪) এই সমস্যা প্রথম formally identify করেন।

গাণিতিক উৎপত্তি:

  • $h_t = \tanh(W_h h_{t-1} + W_x x_t)$।
  • $\partial h_t / \partial h_{t-1} = \tanh'(\cdot) \cdot W_h$।
  • $T$-step backward — chain rule:
    $\frac{\partial L}{\partial h_1} = \prod_{t=2}^{T} \tanh'(\cdot) \cdot W_h \cdot \frac{\partial L}{\partial h_T}$।
  • $\tanh' \in (0, 1]$, $W_h$-এর largest eigenvalue $\lambda$।
  • Product $\to \lambda^T \cdot \prod \tanh'(\cdot)$।

দুটি case:

  • $\lambda < 1$ — gradient $\to 0$ exponentially। Vanishing।
  • $\lambda > 1$ — gradient $\to \infty$ exponentially। Exploding।
  • $\lambda = 1$ — fragile boundary। Practical-এ rare।

Tanh-এর role:

  • $\tanh$ — saturating activation। Input বড় হলে derivative $\to 0$।
  • প্রতিটি timestep-এ derivative ১-এর কম — accumulate-এ vanishing accelerated।
  • Sigmoid-এর চেয়ে ভাল (sigmoid' max ০.২৫), কিন্তু সমাধান নয়।

ReLU দিয়ে সমাধান হবে?

  • ReLU-এর derivative = ১ (positive), ০ (negative) — saturate করে না positive side-এ।
  • সমস্যা: $W_h$-এর spectral radius unbounded → exploding gradient সহজে।
  • Hidden state unbounded — instability।
  • IRNN (Le et al., ২০১৫): ReLU + identity init $W_h$ — কাজ করে কিন্তু sensitive।
  • সাধারণ practice — vanilla RNN-এ tanh, কিন্তু আসল সমাধান architecture পরিবর্তন।

প্রকৃত সমাধান — gating:

  • LSTM (১৯৯৭) — cell state additive update। Gradient pathway "highway"।
  • $\partial c_t / \partial c_{t-1} = f_t$ (forget gate)। যদি forget gate ≈ ১, gradient bypass।
  • GRU — similar mechanism, simpler।
  • Residual connection — same idea, gradient highway।

Practical mitigation:

  • Gradient clipping: exploding-এ norm cap। Pascanu et al. (২০১৩)।
  • Orthogonal initialization: $W_h$ orthogonal — eigenvalues ১।
  • Layer norm: hidden state stabilize।
  • Truncated BPTT: short window — fewer multiplications।

Theoretical understanding:

  • Hochreiter-এর PhD thesis (১৯৯১) — first identification।
  • Bengio-Simard-Frasconi (১৯৯৪) — formal analysis।
  • Pascanu-Mikolov-Bengio (২০১৩) — clipping ও solution-এর modern overview।

মূল উপলব্ধি: Vanishing/exploding gradient — sequence model-এর core challenge। Tanh-ই কারণ নয় — root cause হলো repeated multiplication। ReLU সাধারণ সমাধান নয় — instability। আসল সমাধান architecture (LSTM/GRU/Attention) — gradient-এর জন্য "highway" তৈরি। এই insight আধুনিক DL-এর foundational — ResNet, Transformer সব এই principle-এ।

প্র ০৩ Bangla sentiment classifier (positive/negative) — RNN ব্যবহার করব। Architecture choice, embedding strategy, training pipeline কী?

Bangla sentiment — Bangladesh-এর জন্য practical NLP project। E-commerce review (Daraz), social media (Facebook), customer feedback — সব জায়গায় ব্যবহার।

Architecture decisions:

  • Word-level vs character-level — word-level সহজ, কিন্তু OOV problem।
  • Subword (BPE, SentencePiece) — Bangla-এর জন্য আদর্শ। যুক্তাক্ষর handle।
  • RNN type — LSTM/GRU practical (vanilla RNN-এ vanishing)।
  • Bidirectional — দু'দিকে context, sentiment-এ বেশ ভাল।

Embedding strategy:

  • Random init: small data-এ poor।
  • Pretrained: FastText Bangla, BanglaBERT embedding — strong start।
  • FastText: subword-aware — OOV-এ গ্রহণযোগ্য।
  • Fine-tune vs freeze: 10K+ data-এ fine-tune, কম data-তে freeze।

Suggested architecture:

class BanglaSentiment(nn.Module):
    def __init__(self, vocab_size, embed_dim=300,
                 hidden_size=256, num_classes=2):
        super().__init__()
        self.embed = nn.Embedding(vocab_size, embed_dim,
                                   padding_idx=0)
        self.lstm = nn.LSTM(embed_dim, hidden_size,
                            num_layers=2,
                            bidirectional=True,
                            dropout=0.3,
                            batch_first=True)
        self.fc = nn.Linear(hidden_size * 2, num_classes)
        self.dropout = nn.Dropout(0.5)

    def forward(self, x, lengths):
        emb = self.embed(x)
        packed = pack_padded_sequence(emb, lengths,
                                       batch_first=True,
                                       enforce_sorted=False)
        _, (h, _) = self.lstm(packed)
        # Concat last forward + backward hidden
        h = torch.cat([h[-2], h[-1]], dim=1)
        return self.fc(self.dropout(h))

Data preparation:

  • Source — BanglaABSA, BanglaSenti dataset।
  • Cleaning — emoji, URL, code-mix English handle।
  • Tokenization — bnlp library বা SentencePiece।
  • Class balance — sometimes positive >> negative। SMOTE বা class-weighted loss।

Training pipeline:

  • Optimizer — Adam, lr=১e-৩।
  • Loss — CrossEntropyLoss (class weight সহ)।
  • Batch size — ৩২-৬৪।
  • Sequence length — pad/truncate at ১২৮।
  • Epochs — ১০-২০, early stopping।
  • Gradient clipping — max_norm=1.0।

Evaluation:

  • Accuracy — overall।
  • F1-score — class imbalance থাকলে ভাল metric।
  • Confusion matrix — error pattern।
  • Held-out test — diverse domain (review, post, comment)।

Bangla-specific challenges:

  • Code-mix — "অনেক good বইটা" — English শব্দ বাংলা বাক্যে।
  • Sarcasm — "চমৎকার, আবার সেই pothole" — surface-এ positive কিন্তু intent negative।
  • Negation — "খারাপ না" actually positive।
  • Region variation — চট্টগ্রামের শব্দ, সিলেটের শব্দ।

Production deployment:

  • Model size — bidirectional LSTM ~৫MB-১৫MB।
  • Inference speed — CPU-তে real-time।
  • Edge deployment (mobile) — quantize, distill।
  • Serving — FastAPI/Flask + REST API।

আধুনিক বিকল্প:

  • BanglaBERT fine-tune — RNN-এর চেয়ে accuracy বেশি।
  • Multilingual XLM-R — Bangla support ভাল।
  • RNN/LSTM — যেখানে latency-critical।
  • Hybrid — ছোট LSTM cache + BERT fallback।

Realistic numbers:

  • 10K train data — Bi-LSTM ~৭৮-৮৩% accuracy।
  • BanglaBERT — ~৮৭-৯২% accuracy।
  • Production threshold — ৮৫%+।
  • Domain-specific fine-tune always helps।

মূল উপলব্ধি: Bangla sentiment — practical impact। Bi-LSTM আজও legacy system-এ relevant। Pretrained embedding + LSTM + class-weighted loss + careful evaluation = production-ready। আধুনিক BanglaBERT সুপিরিয়র, কিন্তু LSTM শেখা গুরুত্বপূর্ণ — ভিত্তি বুঝতে। Bangladesh-এ NLP বাণিজ্যিক সুযোগ বিশাল।

প্র ০৪ RNN sequential — parallelize হয় না। তাহলে আজকের GPU যুগে এটা কেন গুরুত্বপূর্ণ ছিল ও এখন কী হাল?

RNN-এর "sequentiality" — এর শক্তি ও দুর্বলতা দু'টোই। Modern GPU-এর জন্য এটা bottleneck — কিন্তু ২০১৭ পর্যন্ত state-of-the-art ছিল।

Sequentiality-এর core reason:

  • $h_t$ compute করতে $h_{t-1}$ লাগে।
  • $h_{t-1}$ compute করতে $h_{t-2}$।
  • Strict ordering — parallelize impossible across timesteps।
  • Within batch — parallel, কিন্তু single sequence-এ sequential।

GPU-এর ক্ষেত্রে impact:

  • GPU — thousands of cores, throughput-optimized।
  • RNN — sequential operations, GPU under-utilized।
  • Long sequence training — slow।
  • Inference — every token wait।

Workarounds:

  • Truncated BPTT: short window — gradient computation manageable।
  • cuDNN RNN: NVIDIA-optimized kernel — significant speedup।
  • Quasi-RNN (QRNN): partial parallelism — convolution + recurrence।
  • SRU (Simple Recurrent Unit): reduce sequential dependency।

২০১৭-এ প্রবল পরিবর্তন:

  • "Attention is All You Need" (Vaswani et al.) — Transformer।
  • Self-attention — সম্পূর্ণ parallelize across positions।
  • RNN-এর dominance শেষ NLP-তে।

Transformer vs RNN — practical:

  • Training — Transformer ১০-১০০x faster।
  • Memory — Transformer attention $O(n^2)$, RNN $O(n)$।
  • Long sequence (>10K) — RNN-এর memory advantage।
  • Short-medium — Transformer dominates।

আজকের state:

  • NLP — Transformer প্রায় universal (BERT, GPT, T5)।
  • Time-series — RNN/LSTM এখনো relevant (financial, IoT)।
  • Speech recognition — Transformer/Conformer dominate।
  • Edge deployment — small RNN/GRU compact।
  • RWKV (২০২৩) — RNN-এর parallel training, retrieval-friendly inference।
  • Mamba/S4 (২০২৩-২৪) — state-space model, RNN renaissance।

RNN-এর renaissance — ২০২৪:

  • Transformer-এর $O(n^2)$ memory — long sequence-এ bottleneck।
  • Mamba — selective state space, Transformer-competitive accuracy।
  • RWKV — open-source, RNN inference efficient।
  • Hybrid — Transformer + RNN (Jamba)।
  • Active research area।

Bangladesh implications:

  • Compute-constrained — RNN/LSTM compact, mobile-friendly।
  • Edge deployment — RNN advantage।
  • Educational — Transformer-এর আগে RNN শেখা mandatory (foundation)।
  • State-of-the-art — Transformer/BanglaBERT।

Lesson learned:

  • Hardware-software co-design crucial।
  • Algorithm-এর architectural property hardware-এ pay হয়।
  • "Universal best" নেই — context-dependent।
  • Old idea-এর পুনরুজ্জীবন (Mamba) — স্মরণ রাখুন।

মূল উপলব্ধি: RNN-এর sequentiality — মানুষের চিন্তার "natural" representation, কিন্তু GPU-এর জন্য suboptimal। Transformer parallelism দিয়ে dominate করল। আজ Mamba/RWKV সেই RNN intuition-কে modern hardware-এ adapt করছে। DL-এর evolution — algorithm + hardware-এর dance। Foundation শক্ত করুন — fashion বদলায়, principle থাকে।

অনুশীলন

  1. Compute by hand: $W_h = \begin{pmatrix} 0.5 & 0 \\ 0 & 0.5 \end{pmatrix}$, $W_x = \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix}$, $b = 0$, $h_0 = (0, 0)$, $x_1 = (1, 0)$, $x_2 = (0, 1)$। $h_1, h_2$ কত? (tanh পরে apply, ছোট value-তে $\tanh(z) \approx z$।)
    • $h_1 = \tanh(W_h h_0 + W_x x_1) = \tanh((1, 0)) \approx (0.762, 0)$।
    • $h_2 = \tanh(W_h h_1 + W_x x_2) = \tanh((0.381, 1)) \approx (0.364, 0.762)$।
    • Notice — $h_1$-এর effect $h_2$-এ ০.৫ গুণ scale হলো (vanishing শুরু)।
  2. PyTorch RNN: ৪-শব্দের একটি batch তৈরি করে nn.RNN-এ pass করুন। Output shape verify।
    import torch
    import torch.nn as nn
    
    rnn = nn.RNN(input_size=10, hidden_size=20, batch_first=True)
    x = torch.randn(3, 4, 10)  # batch=3, seq=4, feat=10
    out, h = rnn(x)
    print(out.shape)   # torch.Size([3, 4, 20])
    print(h.shape)     # torch.Size([1, 3, 20])
  3. চিন্তা: Bangladesh-এ আপনার একটি startup — Bangla SMS-এর spam vs ham classify। RNN কী architecture choose করবেন? Embedding কোথা থেকে আনবেন?

    Architecture: Bi-LSTM (১-২ layer), hidden ~১২৮, output Linear → ২ class। Subword (SentencePiece, vocab ৮K) — OOV handle।

    Embedding: FastText Bangla pretrained (৩০০-D) — fine-tune করতে পারেন। Small data-তে freeze।

    Loss: spam:ham সাধারণত ১:১০ — class-weighted CE।

    Latency target: SMS-এ ১০০ms — Bi-LSTM-এ comfortably achievable।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

কোড রানার কাজ না করলে? ব্রাউজারে কাজ না করলে Google Colab ব্যবহার করুন — Google-এর ফ্রি অনলাইন Python পরিবেশ, শুধু Gmail অ্যাকাউন্ট লাগে।
পূর্ববর্তী পাঠ
পাঠ ২৪ · Data augmentation