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

Backpropagation — chain rule

Backpropagation — gradients via the chain rule
৯ মিনিট পড়া মাঝারি · Intermediate PyTorch hands-on

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

  • Chain rule কী — single variable থেকে multi-variable
  • Forward ও backward pass — pseudo-code-এ
  • একটি ছোট network-এ হাতে-কলমে gradient হিসাব
  • PyTorch-এ backward() ভেতরে কী ঘটে
  • Common pitfalls — gradient zero, exploding gradient

১ · Backpropagation — কেন দরকার

একটি neural network-এর লক্ষ্য — lossLossমডেলের ভুলের পরিমাপ — একটি scalar সংখ্যা। যত কম, তত ভাল prediction। Training-এর সময় এটি minimize করা হয়। minimize করা। কিন্তু একটি GPT-3-এর ১৭৫ বিলিয়ন parameter — প্রতিটির জন্য কীভাবে বুঝব loss কমাতে কোন দিকে যেতে হবে?

উত্তর — gradientGradientএকটি function কতটা পরিবর্তিত হয় input পরিবর্তিত হলে — partial derivatives-এর vector। Loss কমানোর সঠিক দিক দেখায় (negative gradient)।। প্রতিটি weight-এর জন্য $\frac{\partial L}{\partial w}$ চাই — loss এই weight-এর সাপেক্ষে কতটা সংবেদনশীল। Backpropagation এই gradient গণনার efficient algorithm।

Backpropagation = দুই step

১) Forward pass: input → output, প্রতিটি স্তরে value store।
২) Backward pass: loss → input, chain rule প্রয়োগ করে প্রতিটি weight-এর gradient।

২ · Chain rule — gradient-এর প্রাণ

Calculus থেকে: যদি $y = f(g(x))$, তবে

$$\frac{dy}{dx} = \frac{dy}{dg} \cdot \frac{dg}{dx}$$

উদাহরণ: $y = (3x + 1)^2$। ধরা যাক $u = 3x + 1$, তাহলে $y = u^2$।

$\frac{dy}{du} = 2u$, $\frac{du}{dx} = 3$ → $\frac{dy}{dx} = 2u \cdot 3 = 6(3x+1)$।

ভাবুন একটি কারখানার assembly line। Final product-এর গুণমান একটি step-এর উপর কতটা নির্ভরশীল? — প্রতিটি step-এর local effect গুণ করে চলুন। Step A → B → C → output। C-তে A-এর প্রভাব = (A-তে B-এর প্রভাব) × (B-তে C-এর প্রভাব)। এটাই chain rule।

৩ · ছোট network — হাতে-কলমে

একটি একদম সরল network: এক input $x$, একটি hidden neuron, এক output।

$$z = wx + b, \quad a = \sigma(z), \quad L = \tfrac{1}{2}(a - y)^2$$

যেখানে $\sigma$ হলো sigmoid। প্রশ্ন — $\frac{\partial L}{\partial w}$ কত?

Chain rule:

$$\frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial w}$$

প্রতিটি local gradient হিসাব করি:

  • $\frac{\partial L}{\partial a} = (a - y)$ — error signal।
  • $\frac{\partial a}{\partial z} = \sigma(z)(1 - \sigma(z)) = a(1-a)$ — sigmoid-এর derivative।
  • $\frac{\partial z}{\partial w} = x$ — linear-এর derivative।

একসাথে:

$$\frac{\partial L}{\partial w} = (a - y) \cdot a(1-a) \cdot x$$

লক্ষ্য করুন — gradient-এ error ($a-y$), activation derivative ($a(1-a)$), ও input ($x$) — তিনটি factor। এই pattern সব layer-এ একই।

৪ · দুই-স্তর network — gradient flow

এবার একটি hidden layer যোগ করি: input $x$ → hidden $h$ → output $\hat{y}$।

$$z_1 = w_1 x + b_1, \quad h = \sigma(z_1)$$ $$z_2 = w_2 h + b_2, \quad \hat{y} = \sigma(z_2)$$ $$L = \tfrac{1}{2}(\hat{y} - y)^2$$

Output layer-এর weight $w_2$:

$$\frac{\partial L}{\partial w_2} = \underbrace{(\hat{y} - y)}_{\delta_2} \cdot \hat{y}(1-\hat{y}) \cdot h$$

Hidden layer-এর weight $w_1$: এখানে chain আরও দীর্ঘ —

$$\frac{\partial L}{\partial w_1} = \underbrace{(\hat{y}-y) \cdot \hat{y}(1-\hat{y}) \cdot w_2}_{\delta_1} \cdot h(1-h) \cdot x$$

মূল সূত্র: পরবর্তী স্তরের error signal আগের স্তরে $w_2$ দিয়ে গুণিত হয়ে আসে। এটাই "error backpropagation"।

৫ · Local gradient + upstream gradient

Backprop-এর সরল mental model — প্রতিটি node-এ ভাবুন:

প্রতিটি node-এর কাজ

Forward: input থেকে output হিসাব, এবং intermediate value সংরক্ষণ।
Backward: upstream gradient গ্রহণ → local gradient দিয়ে গুণ → downstream-এ পাঠানো।
downstream = upstream × local

যেমন একটি multiplication node $z = xy$: local gradient হলো $\frac{\partial z}{\partial x} = y$ এবং $\frac{\partial z}{\partial y} = x$। Upstream থেকে $\frac{\partial L}{\partial z}$ পেলে, downstream-এ $y \cdot \frac{\partial L}{\partial z}$ ও $x \cdot \frac{\partial L}{\partial z}$ পাঠাবে।

Backpropagation — দুই pass forward (green) computes values, backward (red) computes gradients Input x data z = Wx + b linear a = σ(z) activation ŷ = Va output L loss forward → ← backward (chain rule) ∂L/∂ŷ ∂L/∂a ∂L/∂z ∂L/∂x প্রতিটি node-এ: downstream = upstream × local ∂L/∂(input) = ∂L/∂(output) × ∂(output)/∂(input)
Forward pass values compute করে, backward pass chain rule দিয়ে gradient propagate করে।

৬ · NumPy দিয়ে scratch backprop

PyTorch ছাড়াই — একটি ছোট MLP-তে gradient হিসাব। বুঝতে শ্রেষ্ঠ উপায়।

Python · NumPy
import numpy as np

np.random.seed(0)

# একটি ছোট MLP: 2 → 3 → 1
W1 = np.random.randn(2, 3) * 0.5
b1 = np.zeros(3)
W2 = np.random.randn(3, 1) * 0.5
b2 = np.zeros(1)

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

# Sample input ও target
x = np.array([0.5, -0.2])
y = 1.0

# Forward pass
z1 = x @ W1 + b1               # (3,)
h  = sigmoid(z1)
z2 = h @ W2 + b2               # (1,)
y_hat = sigmoid(z2)

loss = 0.5 * (y_hat - y) ** 2
print(f"loss = {loss[0]:.4f}")

# Backward pass — chain rule হাতে-কলমে
dL_dyhat = (y_hat - y)                          # (1,)
dyhat_dz2 = y_hat * (1 - y_hat)                 # sigmoid'
dL_dz2 = dL_dyhat * dyhat_dz2                   # (1,)

dL_dW2 = h.reshape(-1, 1) @ dL_dz2.reshape(1, -1)   # (3, 1)
dL_db2 = dL_dz2

dL_dh  = dL_dz2 @ W2.T                          # (3,)
dh_dz1 = h * (1 - h)
dL_dz1 = dL_dh * dh_dz1

dL_dW1 = x.reshape(-1, 1) @ dL_dz1.reshape(1, -1)   # (2, 3)
dL_db1 = dL_dz1

print("dL/dW2 shape:", dL_dW2.shape)            # (3, 1)
print("dL/dW1 shape:", dL_dW1.shape)            # (2, 3)
print("dL/dW1[0]:", dL_dW1[0])

    
প্রতিটি layer-এ — local gradient × upstream gradient। PyTorch backward() ঠিক এই কাজই করে — শুধু সব operation-এ automatic।

৭ · PyTorch-এ verify

Python · PyTorch
import torch

# একই scenario — autograd দিয়ে
torch.manual_seed(0)
W1 = torch.randn(2, 3, requires_grad=True) * 0.5
b1 = torch.zeros(3, requires_grad=True)
W2 = torch.randn(3, 1, requires_grad=True) * 0.5
b2 = torch.zeros(1, requires_grad=True)

# requires_grad ঠিকভাবে সেট করতে নতুন leaf tensor
W1 = W1.detach().requires_grad_(True)
b1 = b1.detach().requires_grad_(True)
W2 = W2.detach().requires_grad_(True)
b2 = b2.detach().requires_grad_(True)

x = torch.tensor([0.5, -0.2])
y = torch.tensor([1.0])

# Forward
z1 = x @ W1 + b1
h  = torch.sigmoid(z1)
z2 = h @ W2 + b2
y_hat = torch.sigmoid(z2)

loss = 0.5 * (y_hat - y) ** 2

# Backward — autograd সব gradient compute করে
loss.backward()

print("dL/dW2:", W2.grad)
print("dL/dW1:", W1.grad)

    

হাতে হিসাব ও autograd-এর values মিলে যাবে। এই-ই backprop-এর মূল verification।

৮ · গণনার সাশ্রয় — কেন backprop efficient

Naive approach — প্রতিটি weight-এর জন্য আলাদা finite-difference $\frac{L(w+\epsilon) - L(w)}{\epsilon}$। ১০০ মিলিয়ন parameter-এ ১০০ মিলিয়ন forward pass! অসম্ভব।

Backprop: একটি forward + একটি backward — সব gradient একসাথে। Cost ≈ $2 \times$ forward। এটাই DL-কে বাস্তব করেছে।

Backprop = "reverse-mode automatic differentiation"। যখন output কম, input বেশি (যেমন loss = 1, weights = লক্ষ লক্ষ) — reverse-mode সবচেয়ে efficient। Forward-mode উল্টোটা উপযুক্ত।

৯ · Common pitfalls

  • Vanishing gradient: sigmoid/tanh-এর derivative শূন্যের কাছে — deep network-এ gradient হারিয়ে যায়। সমাধান: ReLU, Batch Norm, residual।
  • Exploding gradient: বড় weight × বড় gradient — সংখ্যা NaN। সমাধান: gradient clipping।
  • Gradient accumulation: PyTorch .grad জমা হয়। প্রতি step-এ optimizer.zero_grad() দিতে ভুলবেন না।
  • In-place ops: autograd graph corrupt করতে পারে। x += 1-এর বদলে x = x + 1।

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

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

প্র ০১ Backpropagation algorithm ১৯৬০-এর দশকে control theory-তে পরিচিত ছিল। তবু DL-এ "discovered" বলে ১৯৮৬-এর Rumelhart-Hinton-Williams পেপারকে। কী কারণে সেই পেপার এত গুরুত্বপূর্ণ?

AI history-র সবচেয়ে interesting story-গুলোর একটি। Backprop multiple times "rediscovered"। Linnainmaa (১৯৭০, master's thesis), Werbos (১৯৭৪, PhD), Parker (১৯৮৫) — সবাই algorithm-এর form ছিল। কিন্তু Rumelhart-Hinton-Williams (RHW) ১৯৮৬ Nature paper "Learning representations by back-propagating errors" ই DL revolution-এর spark।

RHW-র contribution কেন distinctive:

  • Empirical demonstration: XOR সমাধান, simple internal representation শেখা — concrete proof।
  • Connectionist framing: brain-inspired, neuro-scientific community-তে appeal।
  • Clear pedagogy: notation accessible, formula reproducible।
  • Right venue: Nature — wide reach। Werbos-এর PhD thesis কেউ পড়েনি।
  • Right moment: Symbolic AI winter-এর সময় alternative প্রয়োজন।

কেন আগের কাজ ignored:

  • Linnainmaa-এর কাজ Finnish, control theory context — AI community-এর বাইরে।
  • Werbos PhD থেকে statistics/economics তে gravitate করেছিলেন।
  • Computational power inadequate — large network train অসম্ভব ছিল।
  • Symbolic AI dominant — "neural net" considered fringe।

RHW-র পরও দীর্ঘ winter (১৯৯৫-২০১২):

  • SVM, kernel methods superior performance ছোট ডেটায়।
  • Vanishing gradient — deep network train কঠিন।
  • Hardware limitation — CPU-only।
  • Big data + GPU + ImageNet (২০১২) — তবেই DL fly।

মূল উপলব্ধি: Scientific "discovery" শুধু algorithm-এর প্রথম formulation না — community-তে adoption, demonstration, pedagogy সব। Hinton-এর persistence ৪০ বছর — DL-এর mother। ২০১৮ Turing Award তাই।

Bangladesh-এ পাঠ: ভাল idea publish + demonstrate + accessible — এই ৩-T critical। শুধু গণিত যথেষ্ট না।

প্র ০২ Forward-mode আর reverse-mode automatic differentiation — দু'টোর difference কী? Backprop = reverse-mode কেন DL-এ better?

Automatic differentiation (AD) সাধারণত দু'ভাবে — forward ও reverse। দু'টোই chain rule, কিন্তু order ভিন্ন। Choice depends on input/output dimension।

Forward-mode AD:

  • Input থেকে output — input-এর প্রতিটি variable-এর সাপেক্ষে derivative compute হয়।
  • $f: \mathbb{R}^n \to \mathbb{R}^m$ — যদি $n$ ছোট, কার্যকর।
  • প্রতি forward pass — একটি input variable-এর সব output gradient।
  • মোট cost: $n \times \text{forward cost}$।

Reverse-mode AD (backprop):

  • Output থেকে input — প্রতিটি output-এর সাপেক্ষে input-এর derivative।
  • $m$ ছোট হলে কার্যকর।
  • প্রতি backward — একটি output-এর সব input gradient।
  • মোট cost: $m \times \text{forward cost}$।

DL-এ scenario:

  • Input (parameters): $n = $ লক্ষ-কোটি।
  • Output (loss): $m = 1$ (একটি scalar)।
  • $m \ll n$ → reverse-mode বহুগুণ efficient।

উদাহরণ ResNet-50 (২৫M params):

  • Forward-mode: ২৫M × forward = অকল্পনীয়।
  • Reverse-mode: ১ × forward + ১ × backward ≈ ২× forward = চলবে।

Memory trade-off:

  • Forward-mode — minimal memory, no activation save।
  • Reverse-mode — সব intermediate activation save (memory $O(\text{depth})$)।
  • সমাধান: gradient checkpointing — কিছু activation save, বাকি recompute।

কখন forward-mode useful:

  • Few inputs, many outputs — যেমন physics-informed neural network।
  • Jacobian-vector product (JVP) compute — JAX-এর primitive।
  • Sensitivity analysis।

Hybrid approaches:

  • Forward-over-reverse: Hessian-vector product।
  • Reverse-over-forward: alternative second-order।
  • JAX jvp, vjp, hessian — composable।

Theoretical insight:

  • Backprop discovered হয়েছিল control theory ও statistics-এ — DL-এর বহু আগে।
  • Reverse-mode AD = "adjoint method" নামেও পরিচিত (continuous version)।
  • ODE-NN, neural ODE-তে adjoint method ব্যবহৃত।

মূল উপলব্ধি: Mode-এর choice — input/output dimension-এ। DL-এ scalar loss + millions params → reverse-mode king। Forward-mode niche but useful। AD theory DL-এর চেয়ে মৌলিক — physics, finance, optimization-এও।

প্র ০৩ Backprop নিয়ে Hinton-এর সাম্প্রতিক বিতর্ক — তিনি বলেছেন brain probably backprop ব্যবহার করে না। তাহলে কি backprop AI-র wrong path? বিকল্প কী আছে?

২০২২-২০২৩-এ Hinton-এর "forward-forward" paper ও multiple talks DL community-তে কাঁপন তুলেছে। ৪০ বছর backprop-এর champion এখন alternatives খুঁজছেন।

Brain-এর backprop সমস্যা:

  • Symmetric weights problem: backprop forward ও backward path-এ একই weight ব্যবহার করে। জীববৈজ্ঞানিকভাবে অসম্ভব — neuron-এর synapse direction-specific।
  • Global error signal: backprop-এ loss একটি global signal — সারা network-এ broadcast। Brain-এ এমন mechanism নেই।
  • Sequential dependency: backward pass forward pass complete-এর জন্য অপেক্ষা করতে হয়। Brain real-time, asynchronous।
  • Memory storage: Backprop-এর জন্য সব intermediate activation store দরকার। Brain তা করে না।

Biologically plausible alternatives:

  • Feedback alignment (Lillicrap, ২০১৬): backward path-এ random fixed weight ব্যবহার — তবু কাজ করে! Symmetric weight প্রয়োজন না।
  • Direct Feedback Alignment: error সরাসরি deep layer-এ — sequential dependency কমায়।
  • Equilibrium propagation: energy-based — local update only।
  • Predictive coding: top-down prediction + bottom-up correction। Hierarchical Bayesian framework।
  • Forward-forward (Hinton, ২০২২): দু'টি forward pass — positive (real data) ও negative (perturbed)। Layer-wise local objective।

Forward-forward-এর promise:

  • No backward pass — pure forward।
  • Each layer self-contained objective।
  • Asynchronous, distributed-friendly।
  • Sleep-cycle interpretation (positive day, negative dream)।

Forward-forward-এর সীমাবদ্ধতা:

  • এখনো MNIST-এ backprop থেকে দুর্বল।
  • Theoretical convergence guarantee weak।
  • Large-scale (ImageNet) test হয়নি যথাযথভাবে।

তবু backprop কেন dominate:

  • Empirically supreme — সব benchmark-এ।
  • Hardware ও software optimized।
  • Theoretical analysis well-developed।
  • "AI need not be brain-like" — কিছু researcher-এর position।

Hardware perspective:

  • Neuromorphic chip (Intel Loihi, IBM TrueNorth) — local update favor।
  • Backprop on neuromorphic hardware কঠিন।
  • If neuromorphic mainstream — alternatives essential।

মূল উপলব্ধি: Backprop "wrong" না — but possibly suboptimal long-term। Brain-inspired AI-এ alternatives রয়েছে। Hinton-এর self-questioning শেখায় — best researcher = own work-এ critical। আগামী দশকে hybrid approaches দেখব হয়তো — backprop-এর সাথে local rules-এর mix।

প্র ০৪ আপনি একটি ৫০-layer network train করছেন। Loss explode করে NaN হয়ে যাচ্ছে। কী কী cause হতে পারে, কীভাবে diagnose ও fix করবেন?

প্রতিটি DL practitioner-এর nightmare। NaN debugging — মৌলিক skill।

সম্ভাব্য কারণ:

  • Exploding gradient: backprop-এ gradient বিশাল হয়ে যায়।
  • Learning rate বেশি: বড় step, divergence।
  • Bad initialization: weight scale wrong।
  • Numerical instability: log(0), divide by zero।
  • Mixed precision overflow: FP16 range সীমিত।
  • Bad data: NaN বা Inf input।

Diagnostic steps:

  • (১) Anomaly detection:
    torch.autograd.set_detect_anomaly(True)
    # কোন operation NaN cause করেছে — stack trace
  • (২) Gradient norm monitor:
    for name, p in model.named_parameters():
        if p.grad is not None:
            print(name, p.grad.norm().item())
  • (৩) Loss explosion track:
    # Per-step loss log
    if torch.isnan(loss) or loss > 1e6:
        print("Anomaly at step", step)
  • (৪) Activation statistics:
    def hook(module, input, output):
        print(module, output.mean(), output.std(), output.max())
    model.register_forward_hook(hook)
  • (৫) Data check:
    assert not torch.isnan(X).any()
    assert not torch.isinf(X).any()

Fix strategies — priority order:

  • (১) Lower learning rate: 0.01 → 0.001 → 0.0001। সবচেয়ে সহজ ও effective।
  • (২) Gradient clipping:
    torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
    অসাধারণ — explosion থামায় immediately।
  • (৩) Better initialization:
    • Xavier — sigmoid/tanh-এর জন্য।
    • Kaiming — ReLU-এর জন্য।
    • Default PyTorch init usually decent।
  • (৪) Batch Normalization বা LayerNorm: activation distribution stabilize করে।
  • (৫) Residual connections: deep network-এ gradient flow improve।
  • (৬) Warmup schedule:
    lr = base_lr * min(1, step / warmup_steps)
    শুরুতে gentle, পরে full speed।
  • (৭) Mixed precision careful:
    scaler = torch.cuda.amp.GradScaler()
    scaler.scale(loss).backward()  # FP16 overflow protect
  • (৮) Loss function fix:
    • nn.BCEWithLogitsLoss instead of BCELoss(sigmoid(x)) — numerically stable।
    • F.cross_entropy — log-softmax fused।
    • log(x + 1e-8) — small epsilon।

Architecture-specific:

  • Transformer: pre-LN > post-LN (more stable)।
  • RNN: gradient clipping critical। LSTM/GRU > vanilla RNN।
  • GAN: separate optimizer, careful balance।

Production checklist:

  • NaN detection in training loop।
  • Checkpoint frequent — recovery easy।
  • Gradient histogram (TensorBoard/W&B)।
  • Learning rate finder (Smith ২০১৭)।

মূল উপলব্ধি: NaN debugging — systematic detective work। Lower LR + clip gradient — ৮০% case fix। বাকি — initialization, normalization, architecture। Practice দিয়েই intuition গড়ে।

অনুশীলন

  1. Chain rule: $y = \sin(x^2 + 1)$। $\frac{dy}{dx}$ কী?

    $u = x^2 + 1$ ধরলে $y = \sin(u)$। $\frac{dy}{du} = \cos(u)$, $\frac{du}{dx} = 2x$। তাই $\frac{dy}{dx} = 2x \cos(x^2+1)$।

  2. Hand backprop: $z = wx + b$, $a = \text{ReLU}(z)$, $L = a^2$। যদি $w = 2, x = 1.5, b = -1$, $\frac{\partial L}{\partial w}$ কত?

    $z = 2(1.5) - 1 = 2$ → $a = 2$ → $L = 4$।

    Chain: $\frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial w} = 2a \cdot 1 \cdot x = 2(2)(1.5) = 6$।

    (ReLU $z > 0$ হলে derivative = 1)

  3. PyTorch verify: উপরের প্রশ্নটি autograd-এ verify করুন।
    import torch
    w = torch.tensor(2.0, requires_grad=True)
    x = torch.tensor(1.5)
    b = torch.tensor(-1.0, requires_grad=True)
    z = w * x + b
    a = torch.relu(z)
    L = a ** 2
    L.backward()
    print(w.grad)  # tensor(6.)

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

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