BPTT — Backprop through time
এই পাঠে যা শিখবেন
- RNN unrolled — কেন এটা একটি deep network
- BPTT-এর গাণিতিক রূপ — chain rule সময়ের পেছনে
- Shared weight-এর gradient — কেন timestep-এ যোগ
- Truncated BPTT — practical training trick
- Gradient clipping — exploding-এর সমাধান
- PyTorch-এ BPTT কীভাবে handle হয় (autograd magic)
১ · Recap — RNN forward
পূর্ববর্তী পাঠ থেকে — RNN forward equations:
$$h_t = \tanh(W_h h_{t-1} + W_x x_t + b)$$ $$y_t = W_y h_t$$ $$L = \sum_{t=1}^{T} \mathcal{L}(y_t, y_t^*)$$
Loss $L$ পুরো sequence-এর সব output-এর যোগফল (অথবা শেষেরটা — depends on task)। আমরা $\partial L / \partial W_h, \partial L / \partial W_x$ চাই — gradient descent-এর জন্য।
২ · Unrolled view — একটি deep network
RNN-কে timestep-এ unroll করুন। $T = 4$ হলে network কার্যত ৪ স্তরের deep network — কিন্তু সব স্তরে weight share। এটা মাথায় রেখে standard backprop apply করুন — এটাই BPTT।
BPTT কোনো নতুন algorithm নয় — এটা শুধু "RNN-কে unroll করুন, তারপর regular backprop"। Magic হলো — shared weight-এর কারণে gradient সব timestep থেকে যোগ হয়।
৩ · Chain rule — সময়ের পেছনে
একটি timestep $t$-এ loss $L_t$-এর কথা ভাবুন। $W_h$-এর প্রতি gradient:
$$\frac{\partial L_t}{\partial W_h} = \frac{\partial L_t}{\partial h_t} \cdot \sum_{k=1}^{t} \frac{\partial h_t}{\partial h_k} \cdot \frac{\partial h_k}{\partial W_h}$$
যেখানে:
$$\frac{\partial h_t}{\partial h_k} = \prod_{j=k+1}^{t} \frac{\partial h_j}{\partial h_{j-1}} = \prod_{j=k+1}^{t} \text{diag}(\tanh'(\cdot)) \cdot W_h$$
লক্ষ্যণীয়: এই product-এ $t-k$টি $W_h$ গুণ। Long sequence-এ $W_h^{T}$ কার্যত — eigenvalue $< 1$ হলে vanish, $> 1$ হলে explode।
৪ · Total gradient — সব timestep-এর যোগফল
পুরো loss $L = \sum_t L_t$-এর জন্য:
$$\frac{\partial L}{\partial W_h} = \sum_{t=1}^{T} \frac{\partial L_t}{\partial W_h}$$
Shared weight-এর কারণে — প্রতিটি timestep $W_h$-কে "ব্যবহার" করছে, তাই প্রতিটির gradient যোগ। CNN-এ একই pattern — একই kernel image-এর সব position-এ ব্যবহৃত, gradient সব position থেকে যোগ।
৫ · Computational graph — visualization
৬ · PyTorch-এ BPTT — autograd handle করে
Modern framework-এ BPTT manual implement করতে হয় না — autograd computational graph automatically build ও backward করে। নিচের কোডে দেখুন।
import torch
import torch.nn as nn
torch.manual_seed(0)
rnn = nn.RNN(10, 20, batch_first=True)
linear = nn.Linear(20, 5)
optimizer = torch.optim.SGD(
list(rnn.parameters()) + list(linear.parameters()),
lr=0.01)
x = torch.randn(4, 8, 10) # batch=4, seq=8, feat=10
y = torch.randint(0, 5, (4,)) # 4 labels (many-to-one)
# Forward
out, h = rnn(x) # out: (4, 8, 20)
last_h = out[:, -1, :] # (4, 20)
logits = linear(last_h)
loss = nn.functional.cross_entropy(logits, y)
# Backward — BPTT happens here (autograd unrolls)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Loss: {loss.item():.4f}")
print(f"W_h grad norm: {rnn.weight_hh_l0.grad.norm():.4f}")
loss.backward() call-এ PyTorch autograd full unrolled graph traverse করে — সব timestep থেকে $W_h$-এ gradient যোগ। Manual chain rule writing লাগে না।
৭ · Truncated BPTT (TBPTT)
Long sequence-এ (যেমন ১০,০০০ token-এর book) full BPTT — memory ও compute prohibitive। সমাধান — truncated BPTT: শুধু শেষ $k$ timestep-এ backward।
import torch
import torch.nn as nn
rnn = nn.RNN(10, 20, batch_first=True)
optimizer = torch.optim.SGD(rnn.parameters(), lr=0.01)
# একটি দীর্ঘ sequence — ১০০০ timestep
long_seq = torch.randn(1, 1000, 10)
chunk_size = 50 # truncation length
h = None
for i in range(0, 1000, chunk_size):
chunk = long_seq[:, i:i+chunk_size, :]
if h is not None:
h = h.detach() # ⚡ gradient flow এখানে কাটো
out, h = rnn(chunk, h)
loss = out.pow(2).mean() # dummy loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
print("TBPTT training complete — memory bounded")
Key trick: h.detach() — hidden state-এর value রাখে কিন্তু gradient connection break করে। প্রতিটি chunk-এ শুধু সেই chunk-এর gradient compute হয়।
৮ · Gradient clipping — exploding-এর সমাধান
Pascanu et al. (২০১৩) — gradient norm threshold-এ cap। সহজ কিন্তু কার্যকর।
import torch.nn.utils as utils
loss.backward()
# Method 1 — norm-based (recommended)
utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# Method 2 — value-based (each element clipped)
# utils.clip_grad_value_(model.parameters(), clip_value=0.5)
optimizer.step()
Norm clipping — gradient direction preserve, magnitude cap। Most production RNN training-এ ব্যবহৃত।
৯ · BPTT-এর resource cost
- Memory: $O(T \cdot H)$ — সব timestep-এ activation store (backward-এর জন্য)। ১০K sequence + ১K hidden = ১০M float = ৪০MB per sample।
- Compute: $O(T \cdot H^2)$ — forward + backward। Sequential — parallelize hard।
- Gradient checkpointing: activation save না করে recompute। Memory-compute trade-off।
- Mixed precision: FP16 — memory অর্ধেক, speed double।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ BPTT-এ shared weight gradient — কেন "সমষ্টি" (sum), "গড়" (average) না? Mathematical justification কী?
এটি BPTT বুঝার সবচেয়ে subtle পয়েন্ট। উত্তর — calculus-এর chain rule-এর সরাসরি ফলাফল।
Multivariable chain rule:
- $L$ একটি function — যা $W_h$-কে multiple time use করে।
- Total derivative — প্রতিটি ব্যবহার-এর contribution-এর যোগফল।
- $\frac{\partial L}{\partial W_h} = \sum_{t} \frac{\partial L}{\partial W_h^{(t)}}$ — যেখানে $W_h^{(t)}$ হলো timestep $t$-এ "instance"।
- Constraint $W_h^{(1)} = W_h^{(2)} = \ldots = W_h$ — তাই সব contribution একই variable-এ যোগ।
কেন average না:
- Calculus rule — derivative additive, not averaging।
- $f(x) = x^2 + x^3$ — $f'(x) = 2x + 3x^2$, average না (সমষ্টি)।
- একই principle BPTT-এ।
- Average নিতে চাইলে — gradient $T$ দিয়ে ভাগ করতে পারেন (effectively learning rate scale), কিন্তু এটা design choice, calculus rule না।
Practical implication:
- Long sequence — gradient magnitude বড়, $T$-এর সমানুপাতিক।
- Learning rate adjust করতে হতে পারে।
- Gradient clipping এজন্যই critical।
- Effective batch size — sequence length অন্তর্ভুক্ত।
CNN-এর সাথে analogy:
- CNN-এ একই kernel image-এর সব position-এ। Gradient সব position-এর contribution-এর যোগ।
- Image-এ ১০০x১০০ position = ১০,০০০ contribution।
- RNN-এ $T$ timestep = $T$ contribution।
- Same mathematical principle।
Numerical analysis:
- Sum-এর কারণে — long sequence-এ effective gradient large।
- Adam-এর adaptive learning rate এই variation handle করে কিছুটা।
- SGD-তে — manual learning rate tune।
Misconception:
- "Average নিলে stable হবে" — না, calculus rule break।
- Gradient meaning বদলে যাবে।
- Convergence guarantee হারাবে।
মূল উপলব্ধি: Sum vs average — গভীর mathematical question। Calculus chain rule additive। Shared weight-এর জন্য — সব ব্যবহার-এর gradient সরাসরি যোগ। Learning rate স্পেশাল tune করুন। সরল সূত্রের পেছনে গভীর mathematical structure — DL-এ ভালভাবে বুঝে কাজ করুন।
প্র ০২ Truncated BPTT — short window-এ train। Long-range dependency কীভাবে শেখে তাহলে? Window choice trade-off কী?
TBPTT — practical compromise। Theoretically lossy, কিন্তু empirically powerful। Trade-off গভীর।
TBPTT কী miss করে:
- $k$-step বাইরে gradient signal নেই।
- $k+1$-step আগের information শেখা সম্ভব না।
- "৫০০ শব্দ আগে subject — verb-এ agree" — gradient miss।
তাহলে কীভাবে long-range শেখে:
- Hidden state propagation: hidden state-এ পুরো history-এর information। Forward pass-এ flow।
- Indirect learning: short-window pattern শিখে — generalize হয়।
- Cumulative effect: overlapping window — boundary থেকে boundary information leak।
Window choice trade-offs:
- ছোট ($k=10$): fast, less memory। কিন্তু long-range miss।
- মাঝারি ($k=50-100$): typical sweet spot। Balance।
- বড় ($k=500+$): long-range capture। কিন্তু memory expensive, training slow।
Domain-specific guidelines:
- Sentence-level NLP: $k$ = sentence length (~20-40)।
- Document classification: $k$ = ১০০-৫০০।
- Audio frame-level: $k$ = ৫০-২০০ frame।
- Stock price daily: $k$ = ৩০-৯০ day।
- Char-level language model: $k$ = ১০০-২৫০ char।
Hyperparameter sensitivity:
- $k$ কমালে — under-fitting risk।
- $k$ বাড়ালে — overfitting + slow।
- Loss curve plateau দেখলে — $k$ বাড়ান।
- Memory limit-এ — gradient accumulate use।
Alternative — overlapping windows:
- Window-এ overlap রাখুন (e.g., ৫০% overlap)।
- Boundary information loss কম।
- Compute slightly more।
- Practical-এ helpful।
Modern alternative — attention:
- Self-attention — direct connection across long range।
- Gradient flow $O(1)$ regardless of distance।
- Transformer eliminates TBPTT issue।
- Memory $O(n^2)$ — সমস্যা ভিন্ন।
Hybrid approach:
- RNN + attention — RNN local, attention long-range।
- Memory-augmented network।
- Hierarchical RNN — multiple time-scale।
মূল উপলব্ধি: TBPTT — practical compromise। Long-range learning indirect — hidden state forward propagate। Window choice domain-specific। Modern attention এই limitation overcome করে। RNN-এ গভীর understanding শিখলে — modern architecture-এর design choice clearer।
প্র ০৩ Gradient clipping value কীভাবে choose করব? Value-based vs norm-based — কোনটা better এবং কেন?
Gradient clipping — RNN training-এ standard practice। Choice subtle ও empirical।
Norm-based clipping:
- $\|g\|_2 > c$ হলে $g \leftarrow g \cdot c / \|g\|_2$।
- Direction preserve, magnitude cap।
- Pascanu et al. (২০১৩) recommended।
Value-based clipping:
- প্রতিটি element clip — $g_i \leftarrow \text{clamp}(g_i, -v, v)$।
- Direction বদলে যেতে পারে।
- Less common, less principled।
কেন norm better:
- Gradient direction = best descent direction।
- Magnitude শুধু "step size" determine করে।
- Direction বদলালে — wrong direction-এ যাবে।
- Norm preserve করলে — same direction, smaller step।
Threshold value কীভাবে choose:
- Start: ১.০ (most common)।
- Logging: training-এ gradient norm log করুন।
- Quantile-based: ৯০-percentile gradient norm-এর কিছুটা উপর।
- Too low — under-fitting।
- Too high — clipping ineffective।
Gradient norm patterns:
- Training শুরু — norm large (random init)।
- Mid-training — norm settle।
- Spike — exploding event। Clipping save করল।
- Logarithmic plot — pattern সহজে দেখা।
Domain-specific values:
- NLP RNN: ০.২৫-৫.০।
- Char-level: ৫.০-১০.০ (longer sequence)।
- Speech: ১.০-৫.০।
- Reinforcement learning: ০.৫।
Per-parameter clipping:
- Each layer-এ আলাদা norm।
- More fine-grained control।
- Modern practice — global norm common।
Adam-এ clipping:
- Adam adaptive — কিন্তু extreme spike-এ confused।
- Clipping + Adam — stable।
- Both compatible — different purposes।
Modern alternatives:
- Gradient noise: add noise — implicit regularization।
- Adaptive clipping: running statistics-based threshold।
- Layer norm: implicitly stabilize।
মূল উপলব্ধি: Norm-based clipping — direction preserve, magnitude cap। Value-based — direction distort। Threshold ১.০ default safe। Training metrics-এ gradient norm monitor। Combination clipping + careful init + appropriate architecture (LSTM) = stable training। Modern Transformer-ও এই lesson follow।
প্র ০৪ Bangladesh-এ একটি startup — Bangla SMS spam ১ সপ্তাহের data ১ লক্ষ message। RNN train কীভাবে? Memory-compute budget কী?
Practical scenario — Bangladesh-এর telecom companies (GP, Robi, Banglalink) এই problem face করে।
Data analysis:
- ১ লক্ষ message — modest size।
- Average length — ১০-৫০ token।
- Class imbalance — spam ~২-৫%, ham ~৯৫%।
- Bangla + English mix — code-switch।
- SMS-এর special vocabulary — abbreviation, slang।
Architecture choice:
- Bi-LSTM — short SMS, full BPTT possible।
- Hidden ১২৮, ২ layer, dropout ০.৩।
- Embedding ১০০-১৫০-D।
- Subword tokenization — OOV handle।
Training pipeline:
import torch
import torch.nn as nn
class SMSClassifier(nn.Module):
def __init__(self, vocab=10000, embed=128,
hidden=128, num_classes=2):
super().__init__()
self.embed = nn.Embedding(vocab, embed,
padding_idx=0)
self.lstm = nn.LSTM(embed, hidden,
num_layers=2,
bidirectional=True,
dropout=0.3,
batch_first=True)
self.fc = nn.Linear(hidden * 2, num_classes)
def forward(self, x):
emb = self.embed(x)
_, (h, _) = self.lstm(emb)
h = torch.cat([h[-2], h[-1]], dim=1)
return self.fc(h)
Memory budget:
- Average length ৩০, max ১০০।
- Batch ৬৪ — ৬৪x১০০x১২৮ float = ৩.২MB activation।
- Model parameters — ~৫MB।
- GPU 4GB — comfortable।
Compute budget:
- ১ epoch — ১০০K message / ৬৪ batch = ১৫০০ batch।
- Per batch — ~১০ms GPU।
- Per epoch — ১৫ second।
- ২০ epoch — ৫ মিনিট।
- Even CPU-তে — ১ ঘণ্টা।
Class imbalance handling:
# Class-weighted loss
weights = torch.tensor([0.1, 0.9]) # ham, spam
criterion = nn.CrossEntropyLoss(weight=weights)
# Or — focal loss
def focal_loss(pred, target, gamma=2):
ce = F.cross_entropy(pred, target, reduction='none')
pt = torch.exp(-ce)
return ((1 - pt) ** gamma * ce).mean()
BPTT consideration:
- SMS short (~৩০ token avg) — full BPTT fine।
- TBPTT লাগে না।
- Pad-to-max + masking।
- Pack padded sequence efficient।
Gradient clipping:
import torch.nn.utils as utils
loss.backward()
utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
Evaluation strategy:
- Train/val/test split — ৮০/১০/১০।
- Time-based split — newer SMS validate।
- Metrics — F1 (imbalance), recall, precision।
- FP costly (legitimate SMS block) — high precision target।
Production deployment:
- Model size — ~১০MB।
- Inference latency — <১০ms per SMS।
- Telecom-এ ১M+ SMS/sec — batch processing।
- ONNX export, C++ inference।
Continuous learning:
- Spam pattern evolves।
- Daily/weekly retrain।
- Online learning option।
- Drift detection — monitor production accuracy।
Bangla-specific care:
- Banglish (English alphabet দিয়ে Bangla) — common।
- Number-token (e.g., "৫০০ tk" — money)।
- URL/phone — feature enrich।
- Cultural spam pattern — "lottery", "ব্যাংক transaction"।
Ethical consideration:
- Privacy — SMS content sensitive।
- On-device inference preferred।
- Data retention policy।
- User consent।
Realistic numbers:
- Bi-LSTM — F1 ~০.৮৫-০.৯০।
- BanglaBERT fine-tune — ~০.৯২-০.৯৫।
- Production threshold — F1 ০.৯০।
- Continuous improvement।
মূল উপলব্ধি: Bangladesh SMS spam — modest data, short sequence, full BPTT। Class imbalance critical। Bi-LSTM compact, mobile-friendly। Production — F1 prioritize, FP minimize। Continuous learning essential — adversary evolve। Bangla NLP — practical impact, telecom industry-এ direct revenue।
অনুশীলন
-
Manual gradient: $T=2$, $h_0=0$, $W_h=0.5$, $W_x=1$, $b=0$, $x_1=x_2=1$, loss $L = (h_2 - 1)^2$। Linear approx ব্যবহার ($\tanh' \approx 1$)। $\partial L / \partial W_h$ কত?
$h_1 = 0.5 \cdot 0 + 1 \cdot 1 = 1$। $h_2 = 0.5 \cdot 1 + 1 \cdot 1 = 1.5$।
$\partial L / \partial h_2 = 2(h_2 - 1) = 1$।
$\partial h_2 / \partial W_h$ = $h_1 + W_h \cdot \partial h_1 / \partial W_h = 1 + 0.5 \cdot 0 = 1$ (since $\partial h_1 / \partial W_h = h_0 = 0$)।
$\partial L / \partial W_h = 1 \cdot 1 = 1$।
-
TBPTT code: ১০০০-step input, $k=20$ truncation। PyTorch-এ implement।
x = torch.randn(1, 1000, 10) h = None for i in range(0, 1000, 20): if h is not None: h = h.detach() out, h = rnn(x[:, i:i+20], h) loss = out.pow(2).mean() loss.backward() optim.step() optim.zero_grad() -
Clip vs no-clip: একটি toy RNN-এ random init, ৫০-step sequence। Clipping ছাড়া vs সহিত — gradient norm trajectory plot।
norms_no_clip, norms_clip = [], [] for step in range(100): loss = train_step(no_clip=True) norms_no_clip.append(grad_norm()) # ... and with clipping plt.plot(norms_no_clip, label='No clip') plt.plot(norms_clip, label='Clip 1.0') plt.yscale('log'); plt.legend()সাধারণত — no-clip ক্ষেত্রে spike, clip-এ smooth।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ২৭ · LSTM — gate-এর সাহায্যে স্মৃতি পরবর্তী পাঠ Vanishing gradient-এর সমাধান — gate এর জাদু।
- পাঠ ২৫ · RNN — ক্রমিক ডেটা আগের পাঠ Forward computation revisit।
- পাঠ ১৪ · Vanishing/Exploding gradient সম্পর্কিত Deep network-এ সাধারণ সমস্যা — আরও গভীরে।
- সব AI Courses দেখুন ABCL TECH Python, ML, DL, NLP, CV, GenAI, RL, MLOps — সব AI কোর্স একসাথে।