Deep Learning · Mid-Sem · Poora syllabus

Deep Learning ki Kahani zero se exam-ready tak

Ye notebook Deep Learning ke poore mid-sem syllabus ko ek kahani ki tarah padhati hai. Isme course ke lectures, worksheets aur saare labs ka content likha hai: pehle story, phir concept, phir formula, phir numbers se calculation, phir picture, aur wahin us topic ka PyTorch code. Kuch bhi ratna nahi hai. Bas padhte jao, dimaag me baithta jayega.

Subject
Deep Learning (Lectures 1–12 + Labs 0–15)
Exam
Mid-Sem · 70% MCQ · 30% Coding
Pages
26 chapters · har syllabus topic covered

Index

No.Topic · kya-kya covered hai
Shuruaat — neev mazboot karo
0Is notebook ko kaise padheinBoxes ka matlab · padhne ka order · exam ka game plan
1Deep Learning kya hai — machine ko seekhna sikhanaAI ⊃ ML ⊃ DL · Rules vs Data · Knobs wali machine · Feature/Label · Supervised · Regression vs Classification · Train/Val/Test · Data leakage · Parameters vs Hyperparameters · Epoch/Batch/Iteration · "Deep" kyun · Abhi kyun · Itihaas · Course ka naksha
2Math ki potli — jitna math chahiye, utna hi, zero seScalar/Vector/Matrix/Tensor · Dot product · w·x + b · Matrix multiplication · Transpose · Line · e aur ln · Derivative · Rules · Chain rule · Gradient · Minimum · Σ · Mean/Variance · Normal/Uniform · Probability · argmax
3Python + PyTorch ki grammar — code padhna aur likhna seekhoVariables · List · Dict · Loops · if · Functions · Classes & super().__init__() · import · f-string · with · lambda · NumPy · Tensors (Lab 0) · dtype · Broadcasting · @ vs * · dim · view/reshape · Indexing · cat/stack · NumPy↔Torch · Device · requires_grad
Feed Forward Neural Network
4The Perceptron — ek neuron, ek faislaBiological neuron · McCulloch–Pitts 1943 · Rosenblatt 1958 · weights & bias · step activation · decision boundary · logic gates · learning rule · convergence theorem · limitations
5Linear Models — seedhi line ki taaqat aur uski seemaLinear regression · matrix form · "linear" ka matlab · logistic regression · linear layers collapse proof · nn.Linear · Lab 1 & Lab 5 code
6The XOR Problem — jis ek sawaal ne AI ko 17 saal sula diyaXOR truth table · kyun koi line kaam nahi karti (proof) · Minsky & Papert 1969 · AI winter · XOR = AND(OR, NAND) · hidden layer space badal deti hai · 2-2-1 network · MLP · PyTorch training
7Shallow Neural Network — ek hidden layer, bahut saari taaqatDefinition · terminology · Prince (UDL) notation · ReLU joints · piecewise-linear functions · activation pattern · multiple inputs/outputs · parameter counting · capacity · nn.Module & nn.Sequential
8Forward Propagation — input se output tak ka safarFeed-forward ka matlab · z[l] = W[l]a[l−1] + b[l], a[l] = g(z[l]) · 1-1-1 aur 2-2-1 numeric forward pass · batch form (row vs column convention) · shape tracking · deep = repeat · PyTorch
9Activation Functions — neuron ka moodStep · Sigmoid · Tanh · ReLU · Leaky ReLU · PReLU · ELU · Softmax
10Universal Approximation Theorem, Depth vs WidthNetwork capacity · UAT (Cybenko 1989, Hornik 1991) · bumps & ReLU pieces · linear regions · folding · depth vs width trade-off
11Loss Functions — galti ka hisaab-kitaabLoss vs Cost · Loss landscape · Maximum Likelihood · NLL · MSE · MAE · Huber · Binary & Categorical Cross-Entropy · Hinge · PyTorch loss classes
Backpropagation
12Computational Graph, Chain Rule, Forward & Backward Pass — galti ka hisaab peeche takGradient kyun · Derivative refresher · Computational graph · Local × Upstream · Chain rule (single + multivariable) · Gate patterns · Sigmoid neuron graph · Forward vs Backward pass · PyTorch autograd
13Backpropagation Algorithm, Gradient Flow, Vanishing & Exploding Gradients — aur poora PyTorch training loop1-1-1 network by hand · 2-2-1 network by hand · δ notation & general algorithm · Gradient flow · Vanishing gradients · Exploding gradients & clipping · Lab 5 training pipeline
Neural Network Optimization
14Gradient Descent ke Types (Vanilla/Batch, SGD, Mini-batch)Loss landscape · update rule θ ← θ − η∇L · w² wala hisaab · convex / non-convex · saddle · plateau · Batch GD · SGD · Mini-batch · epoch / iteration · DataLoader
15Learning Rate: Fixed vs SchedulingStep size · too small / too large / just right · LR range test · fixed LR · step decay · exponential · 1/t · cosine annealing · warmup · ReduceLROnPlateau · cyclical / one-cycle · torch.optim.lr_scheduler
16Momentum-Based Methods: EWMA, SGD with Momentum, NAGGradient ko yaaddasht do · EWMA · bias correction · velocity · Nesterov ka look-ahead
17Adaptive Learning Rates: AdaGrad, RMSProp, Adam (+AdamW)Har parameter ka apna step size · g² ki memory · bias correction · decoupled weight decay · Optimizer Zoo
Regularization, Normalization & Initialization
18Bias–Variance: Underfitting, Overfitting, Tradeoff, Double DescentGeneralization · Train / Validation / Test · Underfitting (high bias) · Overfitting (high variance) · Bias² + Variance + σ² · U-curve · Model capacity · Learning curves · Polynomial degree · Diagnosis · Double descent
19Regularization: L1, L2, Early StoppingDefinition · Ẽ = E + λΩ · L2 / Ridge / weight decay · L1 / Lasso · sparsity · L1 diamond vs L2 circle · Elastic Net · λ ka asar · Early stopping + patience · Data augmentation · weight_decay, Adam vs AdamW · Lab 9
20Dropout aur Batch Normalization — neurons ki chhutti, marks ka scalingCo-adaptation · Dropout mask · Inverted dropout (1/(1−p)) · Ensemble of thinned networks · model.train() / model.eval() · Internal covariate shift · μB, σ²B, γ, β · Running stats · BatchNorm1d / BatchNorm2d · LayerNorm
21Weight Initialization: Zero, Constant, Random, Xavier, He — shuruaat sahi to safar aasaanSymmetry breaking · Zero & constant init · Too small / too large random · Var(z) = nin·Var(w)·Var(x) · Xavier/Glorot (2010) · He/Kaiming (2015) · PyTorch defaults · nn.init · model.apply
22Hyperparameter Tuning with OptunaParameters vs hyperparameters · Manual · Grid · Random (Bergstra & Bengio 2012) · Bayesian / TPE · Study · Trial · Objective · Sampler · Pruner · suggest_float / suggest_int / suggest_categorical · Dynamic model · Pruning · Visualization · Labs 10, 11, 13, 14
Convolutional Neural Networks
23Convolutional Neural Networks: Fundamentals — tasveer ko torch ki roshni me padhnaMLP kyun fail · image = numbers · (N, C, H, W) · convolution (cross-correlation) · filters & kernels · bias · feature maps · multi-channel filters · stride · padding · output-size formula · dilation · parameter count · local connectivity & weight sharing · pooling · equivariance vs invariance · receptive field · feature hierarchy · Conv2d / MaxPool2d / Flatten · transforms & datasets · Lab 12 Fashion-MNIST CNN · augmentation · 1×1 conv
24Classic CNN Architectures — LeNet, AlexNet, VGG, InceptionImageNet / ILSVRC · top-5 error · LeNet-5 (1998) · AlexNet (2012) · VGG-16/19 (2014) · 1×1 convolution · Inception module · GoogLeNet · Global Average Pooling · depthwise separable conv · comparison
25Deep CNNs — Shattered Gradients, Residual Connections, ResNetDegradation problem · shattered gradients · y = F(x) + x · gradient highway · ensemble of paths · exploding variance + BatchNorm · basic & bottleneck blocks · projection shortcut · ResNet-18/34/50/101/152 · PyTorch residual blocks

Chapter 0

Is notebook ko kaise padhein

Boxes ka matlab · padhne ka order · exam ka game plan

Ye notebook ek hi lambi kahani hai. Chapter 1 se shuru karke Chapter 25 tak pahunchoge to Deep Learning ka poora safar ho jayega: ek akela neuron, phir neurons ka network, phir network galti kaise naapta hai, galti se seekhta kaise hai, tez kaise seekhta hai, ratta maarne se kaise bachta hai, aur aakhir me photos kaise samajhta hai. Har chapter pichhle chapter ki baat ko aage badhata hai, isliye order me padhna best hai.

0.1 Har box ka matlab

Poori notebook me same tarah ke boxes baar-baar aayenge. Ek baar pehchaan lo, phir padhna aasaan ho jayega.

Kahani

Har topic ek chhoti si real-life story se shuru hota hai, taaki concept dimaag me "feel" ho jaaye.

Definition

Exam wali exact definition, simple shabdon me.

z = w·x + b
  • Red box = formula. Neeche har symbol ka matlab likha hota hai.

Chalo numbers se samjhein

  1. Har calculation step-by-step, 2 × 3 + 1 = 7

Jawab: double underline wala

Exam Radar

  • MCQ me jo facts poochhe ja sakte hain: ranges, defaults, saal, comparisons.

Galti mat karna

  • Common traps jahan students marks gawa dete hain.

Dimaag me aise baitha lo

Ek line ka memory hook.

PyTorch · code slip

loss.backward()   # comment Hinglish me
optimizer.step()

Diagrams ke neeche pencil me caption hota hai, aur kahin-kahin side me chhote pencil notes bhi milenge. Yellow highlighter wali lines sabse important hain.

0.2 Exam ka game plan: 70% MCQ + 30% Coding

0.3 Padhne ka tarika

Dimaag me aise baitha lo

Ratta nahi, kahani. Kahani yaad rahegi to formula khud yaad aa jayega.

Chapter 1

Deep Learning kya hai — machine ko seekhna sikhana

AI ⊃ ML ⊃ DL · Rules vs Data · Knobs wali machine · Feature/Label · Supervised · Regression vs Classification · Train/Val/Test · Data leakage · Parameters vs Hyperparameters · Epoch/Batch/Iteration · "Deep" kyun · Abhi kyun · Itihaas · Course ka naksha

1.1 AI, ML, DL — teen dabbe, ek ke andar ek

Kahani

Ek simple sawaal: kya har Mumbai wala Indian hai? Haan. Kya har Indian Mumbai wala hai? Nahi. Kyunki Mumbai Maharashtra ke andar hai, aur Maharashtra India ke andar. Bas yahi rishta hai AI, ML aur DL ka. India = AI (sabse bada dabba). Maharashtra = ML (AI ke andar ek hissa). Mumbai = DL (ML ke andar ek aur chhota, sabse "happening" hissa).

Jab bhi exam me koi pooche "Deep Learning, Machine Learning ka subset hai ya ulta?" — bas Mumbai–Maharashtra–India yaad kar lena.

Ab teeno ko ek-ek karke pakadte hain:

Definition

Artificial Intelligence (AI) = koi bhi technique jisse machine "smart" kaam kare — sochna, decide karna, khelna, bolna. Isme hand-written rules bhi aate hain. Jaise purana chess program jisme insaan ne likha "agar queen khatre me hai to bachao". Ye bhi AI hai, bhale hi machine ne kuch seekha nahi.

Machine Learning (ML) = AI ka wo hissa jisme machine rules khud data se seekhti hai, hum rules nahi likhte. Arthur Samuel (1959) ki famous line: ML is the "field of study that gives computers the ability to learn without being explicitly programmed". Tom Mitchell (1997) ki definition: "A program learns from experience E with respect to task T and performance measure P, if its performance on T, measured by P, improves with experience E." Matlab: kaam (T) pe, number (P) se naapa gaya performance, anubhav (E = data) ke saath behtar ho raha hai — tab hum kehte hain machine "seekh" rahi hai.

Deep Learning (DL) = ML ka wo hissa jo neural networks use karta hai jinme bahut saari layers hoti hain (isliye "deep"). Sabse badi khoobi: ye networks features bhi khud seekhte hain — humein batana nahi padta ki photo me "roundness" dekho ya "colour" dekho.

AI ML DL deep neural nets if-else rules, chess search (Deep Blue) SVM, decision tree, linear regression AI: koi bhi "smart" machine (hand-written rules bhi chalega) ML: rules data se seekho (explicit programming nahi) DL: bahut layers wala network (features bhi khud seekhe)
DL ⊂ ML ⊂ AI. Har DL model ML hai, har ML model AI hai — par ulta sach nahi.

Exam Radar

  • Rishta: DL ⊂ ML ⊂ AI. "Every deep learning model is a machine learning model" — TRUE. "Every ML model is deep learning" — FALSE (SVM, decision tree, linear regression ML hain par DL nahi).
  • DL = ML using multi-layer neural networks jo representations / features khud seekhte hain (Lecture 0 ka word: Representation Learning).
  • Lecture 0 line: "In DL, we don't design features; we design architectures that learn features." Aur: "We are replacing human heuristics with mathematical optimization."
  • Samuel (1959): "learn without being explicitly programmed". Mitchell: Task T, Performance P, Experience E.

Galti mat karna

  • "AI aur DL ek hi cheez hai" — galat. DL sirf ek chhota (par sabse powerful) hissa hai.
  • "ML ke liye neural network zaroori hai" — galat. Neural network sirf ek tarah ka ML model hai.

Dimaag me aise baitha lo

India ⊃ Maharashtra ⊃ Mumbai = AI ⊃ ML ⊃ DL.

1.2 Traditional programming vs Machine Learning — recipe kaun likhta hai?

Kahani

Maa ne tumhe recipe likh ke di: "2 katori chawal, 4 katori paani, 1 chammach namak, 20 minute pakao." Tumne ingredients liye, recipe follow ki, khana ban gaya. Yahan rules (recipe) insaan ne likhe, tumne bas follow kiye. Ye hai traditional programming.

Ab socho recipe kho gayi. Par tumhare paas 1000 baar ka record hai: "itna chawal, itna paani, itna time → khana accha bana / jal gaya / kaccha raha". Tum in examples ko dekh dekh ke khud recipe nikal lo. Yahan rules tumne data se seekhe. Ye hai machine learning.

Computer ki bhasha me: traditional programming me hum rules likhte hain (if-else, formulas), computer ko data dete hain, aur wo answers nikalta hai. ML me hum computer ko data aur sahi answers (labels) dono dete hain, aur computer rules khud dhoondhta hai. Ye seekhe hue rules ko hum model kehte hain.

Example: spam email pakadna. Traditional tareeka: "agar email me 'lottery' ya 'free money' likha hai to spam". Par spammers 'l0ttery' likh denge aur tumhara rule fail. ML tareeka: 10,000 emails do jin par pehle se likha hai "spam / not spam", aur machine khud pattern pakad legi. Aur photo me billi pehchaanna? Uske rules haath se likhna lagbhag impossible hai — har pixel ke liye if-else kaun likhega! Yahin ML (aur khaas kar DL) jeet-ta hai.

Traditional programming Data Rules (hum likhte) Program Answers Machine Learning Data Answers (labels) Learning algorithm Rules = Model
Upar: Data + Rules → Answers. Neeche: Data + Answers → Rules (model). ML me "rules" output hain, input nahi.

Ek baar model ban gaya, uske baad wo ek normal program ki tarah chalta hai: naya data do, answer lo. Is phase ko inference (ya prediction) kehte hain. Aur model banane wale phase ko training. Training mehngi hai (ghanton/dino tak GPU chalta hai), inference sasti aur fast.

Exam Radar

  • Traditional: Data + Rules → Answers. ML: Data + Answers → Rules (model).
  • Training = data se parameters seekhna. Inference = seekhe hue model se naye input ka answer nikalna (PyTorch me model.eval() + torch.no_grad() wala phase — Chapter 13).

Dimaag me aise baitha lo

Traditional: recipe do, khana lo. ML: khane ke examples do, recipe lo.

1.3 "Learning" ka asli matlab — knobs wali machine

Kahani

College ke gate pe Raju chai wala hai. Raju roz sochta hai: "Kal kitne cup chai banaun?" Zyada banayi to chai bekaar, kam banayi to customer wapas. Raju ne notice kiya: thand me chai zyada bikti hai, garmi me kam. Usne pichhle kuch dino ka record rakha:

10°C → 80 cup,   20°C → 60 cup,   30°C → 40 cup.

Raju ke paas ek jaadu ki machine hai jisme do knobs hain. Ek knob ka naam w (har degree badhne pe kitne cup ghatenge/badhenge), doosre ka naam b (base wala number). Machine ka kaam: temperature x lo, aur bolo ŷ = w × x + b cup. Shuru me knobs random jagah pe hain, to machine bakwaas bolti hai. Raju har baar machine ki galti dekhta hai aur knobs thoda ghumata hai, taaki galti kam ho. Bas yahi "learning" hai.

Is poore course me ye kahani baar baar aayegi, bas machine badi hoti jaayegi. Teen shabd abhi pakka kar lo:

Definition

Model = ek function (machine) jo input x leke output ŷ ("y-hat", matlab prediction) deta hai. Iske andar adjustable numbers hote hain.

Parameters = wo adjustable numbers = knobs. Neural network me inhe weights (w) aur biases (b) kehte hain. Raju ki machine me 2 parameters hain. GPT-3 jaise model me 175 billion!

Loss = ek single number jo batata hai "machine kitni galat hai". Jitna chhota, utna accha. (Poora Chapter 11 isi pe hai.)

Training / Learning = parameters ko baar baar thoda thoda badalna taaki loss kam ho. Kis direction me ghumana hai, ye gradient descent batata hai (Chapter 14), aur lakhon knobs ke liye wo direction jaldi nikalne ka tareeka backpropagation hai (Chapter 13).

ŷ = w · x + b     Loss (MSE) = 1n Σ (y − ŷ)2
  • x = input (yahan temperature), y = asli answer (asli me kitne cup bike), ŷ = machine ka guess
  • w, b = knobs (parameters) jo machine seekhegi
  • y − ŷ = galti (error). Square isliye kiya taaki minus wali galti aur plus wali galti ek doosre ko cancel na karein, aur badi galti ko zyada saza mile
  • n = kitne examples; Σ = sab ko jodo (Chapter 2 me detail); 1/n se average ho gaya. Isliye naam Mean Squared Error

Chalo numbers se samjhein

Sawaal: Raju ke data (10→80, 20→60, 30→40) pe teen alag knob settings try karo. Har baar predictions, galtiyan aur MSE nikalo.

  1. Setting 1: w = 0, b = 50. Predictions: 0×10+50 = 50, 0×20+50 = 50, 0×30+50 = 50. Galti (y − ŷ): 80−50 = 30, 60−50 = 10, 40−50 = −10. MSE: (900 + 100 + 100) / 3 = 1100/3 ≈ 366.67
  2. Raju ne dekha thand me kam bol raha hai, to w ko negative kiya: w = −1, b = 80. Predictions: −10+80 = 70, −20+80 = 60, −30+80 = 50. Galti: 10, 0, −10. MSE: (100 + 0 + 100) / 3 = 200/3 ≈ 66.67 — loss gira!
  3. Thoda aur ghumaya: w = −2, b = 100. Predictions: −20+100 = 80, −40+100 = 60, −60+100 = 40. Galti: 0, 0, 0. MSE = 0.

Jawab: loss 366.67 → 66.67 → 0. Knobs w = −2, b = 100 pe machine ne "seekh liya": har 1°C garmi pe 2 cup kam bikte hain, aur 0°C pe 100 cup bikenge.

x = temperature ŷ = w·x + b knob w knob b ŷ galti (loss) y (asli) loss dekho → knobs thoda ghumao → repeat (training)
Har neural network bas ek badi knobs wali machine hai. Red loop = training. Loop band = inference.

Python · Raju ki machine ka loss

temps = [10, 20, 30]      # x: inputs (features)
cups  = [80, 60, 40]      # y: asli answers (labels)

def predict(x, w, b):     # model: ek line
    return w * x + b

def mse(w, b):            # loss: galti ka meter
    total = 0
    for x, y in zip(temps, cups):          # har example pe
        total += (y - predict(x, w, b)) ** 2   # galti ka square jodo
    return total / len(temps)              # average

for w, b in [(0, 50), (-1, 80), (-2, 100)]:   # teen knob settings
    print(f"w={w}, b={b} -> loss={mse(w, b):.2f}")

Output

w=0, b=50 -> loss=366.67
w=-1, b=80 -> loss=66.67
w=-2, b=100 -> loss=0.00

Abhi humne knobs haath se ghumaye. Asli deep learning me computer khud ghumata hai, aur knobs lakhon-karodon hote hain. Wo kaise — Chapters 12 se 14 me.

Exam Radar

  • Model = function with parameters. Parameters = weights + biases. Loss = galti ka single number. Training = loss minimize karna by adjusting parameters.
  • Lab 5 ka model nn.Linear(30, 1) → 30 weights + 1 bias = 31 parameters.
  • Lecture 0 slide: "DL is not magic; it is Calculus and Linear Algebra acting on Data." Math = galti kaise kam karein; Code = jo derive kiya wahi banao.

Dimaag me aise baitha lo

Model = knobs wali machine. Loss = galti ka meter. Training = meter dekh dekh ke knobs ghumana.

1.4 Data ki bhasha — feature, label, sample, dataset

Kahani

Class teacher ka register socho. Har row ek student hai. Columns me likha hai: attendance %, roz padhai ke ghante, pichhle test ke marks. Aur aakhri column me: final result — Pass ya Fail. Teacher chahti hai ki agle saal ke students ke liye pehle se guess kar sake ki kaun fail ho sakta hai. Ye register hi hamara dataset hai.

TermMatlabRegister meLab 5 (breast cancer) me
Feature (input, x)Ek measurable cheez jo hum model ko dete hainattendance, padhai ke ghante, marksradius_mean, texture_mean, area_mean … (30 features)
Label / Target (y)Wo answer jo model ko predict karna haiPass / Faildiagnosis: M (malignant) / B (benign)
Sample (example, instance, data point)Ek row = ek cheez ka poora recordek studentek patient
DatasetSaare samples ka collectionpoora register569 patients
Feature vectorEk sample ke saare features ek list me[92, 3, 71]30 numbers ki list

Notation jo poore course me chalega: N = samples ki ginti, d = features ki ginti. Saare inputs ek table/matrix X me: shape (N, d) — rows = samples, columns = features. Labels ek list y me: shape (N,). i-th sample ko (x(i), y(i)) likhte hain. PyTorch ki nn.Linear bhi yahi maanti hai: input shape (batch, features).

Lab 5 · data ko X aur y me baantna

df = pd.read_csv('...breast-cancer data.csv')   # Wisconsin Breast Cancer data
df.shape                                         # (569, 33): 569 rows, 33 columns

# 'id' koi feature nahi (bas patient number), 'Unnamed: 32' khaali column hai
df.drop(columns=['id', 'Unnamed: 32'], inplace=True)
df.shape                                         # (569, 31) = 30 features + 1 label

X = df.drop(columns=['diagnosis'])   # features: (569, 30)
y = df['diagnosis']                  # label:    (569,)  values 'M' / 'B'

Output (Lab 5 notebook)

(569, 33)
(569, 31)

Ek baat: model sirf numbers samajhta hai. 'M' aur 'B' ko number banana padta hai — Lab 5 me LabelEncoder ne alphabetical order me B → 0, M → 1 kiya. Lab 5 activity (Pima diabetes) me label Outcome pehle se 0/1 tha: 768 rows, 8 features + 1 label = shape (768, 9).

Exam Radar

  • X shape = (N, d) = (samples, features). Lab 5: X_train (455, 30), y_train (455,).
  • Label ke synonyms: target, ground truth, output variable. Feature ke synonyms: input, attribute, predictor, independent variable.
  • LabelEncoder: classes ko alphabetical order me 0, 1, 2 … deta hai ('B' → 0, 'M' → 1).

Galti mat karna

  • ID column (patient number) ko feature mat banao — usme koi pattern nahi, bas noise hai. Lab 5 me isliye drop kiya.
  • Label column ko X me chhod dena = model ko answer pehle hi dikha dena. Isliye X = df.drop(columns=['diagnosis']).

Dimaag me aise baitha lo

Row = sample, column = feature, aakhri column = label. X ke andar sawaal, y ke andar jawab.

1.5 Supervised learning — answer key ke saath padhai

Kahani

Tuition me tum sawaal solve karte ho, aur sir answer key se check karke batate hain "ye galat, ye sahi". Tumhe har sawaal ke saath sahi jawab mil raha hai — ye supervised learning hai (supervisor = answer key).

Ab Diwali ki 500 photos phone me padi hain, kisi pe naam nahi likha. Tum khud unhe groups me baant dete ho — "ye sab rangoli wali, ye sab family wali". Koi answer key nahi — ye unsupervised learning hai.

Aur cycle chalana seekhna? Koi answer key nahi, bas gire to dard (punishment), balance bana to maza (reward). Ye reinforcement learning hai.

TypeData me kya milta haiGoalExample
Supervised(x, y) pairs — input aur sahi answer donof(x) ≈ y seekhnaLab 5 cancer M/B, MNIST digits, house price
Unsupervisedsirf x, koi label nahichhupa structure dhoondhna (groups, compression)clustering, autoencoders (Module 8)
Reinforcementactions ke baad reward/penaltytotal reward maximize karnaAlphaGo (self-play), game bots
Self-supervised (bonus)label data se hi ban jaata haiagla word / chhupa hissa predict karnaChatGPT jaise LLMs ki pretraining

Is mid-sem ka poora syllabus supervised learning hai: perceptron, MLP, loss, backprop, optimizers, CNN — sab me humare paas labels hain aur loss = prediction vs label ki doori.

Exam Radar

  • Supervised = labelled data. Unsupervised = unlabelled. Reinforcement = reward signal.
  • Lecture 0: Go me jeet "massive search + learning from playing against itself (self-play)" se aayi — ye reinforcement learning ki taraf ishara hai.

Dimaag me aise baitha lo

Supervised = answer key wali tuition. Unsupervised = bina naam ki photos sort karna. RL = cycle seekhna.

1.6 Regression vs Classification — number chahiye ya category?

Kahani

Weather app teen tarah ke sawaal poochti hai:

(1) "Kal kitna temperature hoga?" — answer ek number (31.4°C). Ye regression.

(2) "Kal baarish hogi ya nahi?" — answer do me se ek: haan/na. Ye binary classification.

(3) "Kal mausam kaisa: dhoop / baadal / baarish / aandhi?" — answer kai categories me se ek. Ye multi-class classification.

Definition

Regression: output ek continuous number (koi bhi value ho sakti hai). Raju ke chai cups, ghar ki price, kal ka temperature, exam marks.

Classification: output ek category / class. Binary = sirf 2 classes (spam/not spam, Malignant/Benign in Lab 5, diabetes haan/na in Lab 5 activity). Multi-class = 2 se zyada classes, aur har sample ki exactly ek class (MNIST digits 0–9 = 10 classes, Fashion-MNIST = 10 classes, CIFAR-10 = 10 classes).

(Bonus, confusion se bachne ke liye: multi-label = ek sample pe ek saath kai labels sach ho sakte hain, jaise photo me kutta bhi aur billi bhi. Ye multi-class se alag hai.)

TaskOutput kaisaLast layer (preview)Loss (preview, Ch 11)Example
Regressionkoi bhi real number1 neuron, koi activation nahi (linear)MSE / MAE (nn.MSELoss)Raju ke cups, house price
Binary classificationprobability (0, 1) → threshold 0.51 neuron + sigmoidBinary Cross-Entropy (nn.BCELoss)Lab 5: M vs B
Multi-class classificationK probabilities jo jod ke 1 → argmaxK neurons + softmaxCross-Entropy (nn.CrossEntropyLoss)MNIST 10 digits

Chalo numbers se samjhein

  1. Binary (Lab 5 style): model ne kisi patient ke liye bola P(M) = 0.83. Threshold 0.5 se bada → class 1 = Malignant. Doosre patient ke liye 0.20 < 0.5 → class 0 = Benign. Lab 5 code: (y_pred > 0.5).float().
  2. Multi-class: 3 classes (dhoop, baadal, baarish) ke liye model ne probabilities di [0.1, 0.7, 0.2]. Sabse badi 0.7 hai jo position 1 pe hai → prediction = class 1 = baadal. (Position nikalna = argmax, Chapter 2.18.)

Exam Radar

  • "Kitna / how much" → regression. "Kaunsa / which / yes-no" → classification.
  • Logistic regression naam me regression hai par ye classification algorithm hai (probability nikal ke threshold lagata hai). Lab 5 khud kehta hai: "logistic regression classifier".
  • Binary classification me out_features = 1 (ek probability kaafi, doosri = 1 − p). Lab 5: "we only need one probability (P(malignant)) per example".
  • Multi-class prediction = probabilities ka argmax.

Galti mat karna

  • Classes ko numbers (0, 1, 2) se encode karne se problem regression nahi ban jaati. Digit "7" "3" se "bada" nahi hai — bas alag category hai.
  • Multi-class ≠ multi-label. Multi-class me exactly ek sahi class; multi-label me kai.

Dimaag me aise baitha lo

Thermometer = regression. Traffic signal ka rang = classification (laal/peela/hara me se ek).

1.7 Train / Validation / Test split — practice, mock test, board exam

Kahani

Riya board exam ki taiyaari kar rahi hai. Teen tarah ke papers hain:

(1) Textbook ke exercises — inse padhai karti hai, galti karti hai, sudharti hai. (= training set)

(2) Mock tests — inse decide karti hai ki "raat ko padhun ya subah? 4 ghante ya 6?" Strategy chunne ke liye. (= validation set)

(3) Asli board paper — pehle kabhi nahi dekha. Isi ke marks batayenge ki asli me kitna aata hai. (= test set)

Ab socho kisi ne board paper pehle hi leak kar diya aur Riya ne usi ko ratta maar liya. 100/100 aa gaye — par kya isse pata chala ki use subject aata hai? Bilkul nahi! Isliye test set ko training ke dauraan kabhi chhoona nahi.

Machine learning ka asli goal training data pe accha karna nahi hai — wo to ratta maar ke bhi ho jaata hai. Asli goal hai naye, kabhi na dekhe data pe accha karna. Isko kehte hain generalization. Agar model training data pe 100% aur naye data pe 60% de, to usne ratta maara hai — isko overfitting kehte hain (poori kahani Chapter 18 me). Generalization naapne ke liye hum data ko pehle hi baant dete hain:

SetKis kaam kaKab use hota haiTypical size
Trainparameters (weights, biases) seekhnahar epoch, baar baar60–80%
Validation (dev set)hyperparameters chunna, model compare karna, early stopping (Ch 19), Optuna (Ch 22)training ke beech beech me10–20%
Testfinal, imaandaar report cardsirf ek baar, bilkul end me10–20%
Train 60% Val 20% Test 20% pura dataset (shuffle karke baanto) knobs (parameters) yahan seekhe hyperparameters yahan chune sirf end me, ek baar Lab 5 me sirf train/test (80/20) tha: 455 train + 114 test
Teen hisse, teen kaam. Test set = sealed board paper.

Chalo numbers se samjhein

Sawaal: Lab 5 me 569 patients the, test_size=0.2. Kitne train, kitne test?

  1. Test: 0.2 × 569 = 113.8. sklearn test size ko upar round karta hai (ceil) → 114.
  2. Train: 569 − 114 = 455. Isiliye Lab 5 me print aaya X_train_tensor: torch.Size([455, 30]).
  3. Lab 5 activity (Pima, 768 rows): 0.2 × 768 = 153.6 → 154 test, 768 − 154 = 614 train — wahi torch.Size([614, 8]).

Jawab: 455 train + 114 test (aur Pima me 614 + 154).

sklearn · Lab 5 split + validation ke liye do-step split

from sklearn.model_selection import train_test_split
import numpy as np

X = np.random.rand(569, 30)              # Lab 5 jitna dummy data: 569 patients, 30 features
y = np.random.randint(0, 2, 569)         # 0/1 labels
# Lab 5 wali line: 20% test alag, random_state se har baar same split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
print(X_train.shape, X_test.shape)

# Validation bhi chahiye? do baar split karo (60/20/20):
X_temp, X_te = train_test_split(np.arange(1000), test_size=0.2, random_state=42)   # 800 / 200
X_tr, X_val = train_test_split(X_temp, test_size=0.25, random_state=42)            # 800 ka 25% = 200
print(len(X_tr), len(X_val), len(X_te))

Output

(455, 30) (114, 30)
600 200 200

Arguments samjho: test_size=0.2 = 20% test me. random_state=42 = shuffle ka seed, taaki har baar same rows test me jaayein (reproducible results). shuffle=True default hai — split se pehle data shuffle hota hai, taaki agar file me pehle saare M aur baad me saare B hon to bhi dono sets me mix aaye. stratify=y doge to dono sets me class ka ratio same rahega (imbalanced data ke liye useful).

Exam Radar

  • Generalization = unseen data pe performance. Test set isi ka imaandaar estimate hai.
  • Train → parameters. Validation → hyperparameters / model selection / early stopping. Test → final evaluation, ek baar.
  • Lab 5: split scaling se pehle ("We hold out 20% of the data as a test set before any scaling or encoding happens").
  • Agar validation pe baar baar tune kiya, to model validation pe bhi "overfit" ho sakta hai — isliye alag test set rakhte hain.

Galti mat karna

  • Test set pe hyperparameters tune karna = paper leak. Test accuracy fir jhooth bolegi (zyada optimistic).
  • "Validation aur test ek hi hai" — nahi. Validation baar baar use hota hai decisions ke liye; test sirf end me.

Dimaag me aise baitha lo

Exercise se seekho, mock se strategy chuno, board paper sealed rakho.

1.8 Data leakage — StandardScaler sirf train pe fit karo (Lab 5)

Kahani

Class me teacher ne kaha: "Marks ko curve karungi — sabke marks me se class ka average ghata ke dekhungi kaun upar hai, kaun neeche." Ab agar average nikalte waqt teacher ne board exam ke marks bhi jod liye (jo abhi aaye hi nahi hone chahiye the), to curve me future ki jaankari ghus gayi. Isko kehte hain data leakage — test data ki koi bhi jaankari training process me chupke se pahunch jaana.

Pehle samjho scaling kyun chahiye. Lab 5 me radius_mean lagbhag 10–30 hota hai aur area_mean 150–2500. Itne alag scale ke features ke saath gradient descent theek se nahi chalta — bade number wala feature sab pe haavi ho jaata hai. Isliye har feature ko standardize karte hain: mean 0 aur standard deviation 1.

x′ = x − μσ
  • x = original value, x′ = scaled value
  • μ (mu) = us feature ka mean (average) — sirf training data se
  • σ (sigma) = us feature ka standard deviation (kitna faila hua hai) — sirf training data se. StandardScaler population formula (1/n) use karta hai (Chapter 2.15)
  • Matlab: "ye value average se kitne σ door hai?" Isliye result ko z-score bhi kehte hain

Chalo numbers se samjhein

Sawaal: ek feature ke training values [2, 4, 6] hain, test value 8 hai. Sahi tareeke se scale karo, aur fir dekho leak hone pe kya hota.

  1. Train mean: μ = (2 + 4 + 6) / 3 = 4
  2. Train variance: ((2−4)² + (4−4)² + (6−4)²) / 3 = (4 + 0 + 4) / 3 = 8/3 ≈ 2.6667, to σ = √2.6667 ≈ 1.6330
  3. Train scaled: (2−4)/1.6330 = −1.2247, (4−4)/1.6330 = 0, (6−4)/1.6330 = 1.2247
  4. Test (8) ko train ke μ, σ se: (8 − 4) / 1.6330 ≈ 2.4495
  5. Galat (leaky) tareeka: sab [2, 4, 6, 8] pe fit kiya → μ = 5, σ = √5 ≈ 2.2361, test → (8 − 5)/2.2361 ≈ 1.3416. Test point ne khud apne scaling ko badal diya!

Jawab: sahi scaled test value ≈ 2.4495. Leaky value 1.3416 — test ka data μ, σ me ghus gaya.

sklearn · Lab 5 style scaling aur label encoding (sahi tareeka)

import numpy as np
from sklearn.preprocessing import StandardScaler, LabelEncoder

X_train = np.array([[2.0], [4.0], [6.0]])   # chhota demo: 1 feature, 3 train samples
X_test  = np.array([[8.0]])                 # 1 test sample
y_train = np.array(['M', 'B', 'B'])
y_test  = np.array(['B'])

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)   # fit = train ka mu, sigma seekho; transform = lagao
X_test  = scaler.transform(X_test)        # sirf transform! train wale mu, sigma hi use honge
print(scaler.mean_, scaler.scale_)        # seekha hua mu aur sigma
print(X_train.ravel(), X_test.ravel())

encoder = LabelEncoder()
y_train = encoder.fit_transform(y_train)  # 'B' -> 0, 'M' -> 1 (alphabetical)
y_test  = encoder.transform(y_test)       # same mapping test pe
print(y_train, y_test, encoder.classes_)

Output

[4.] [1.63299316]
[-1.22474487  0.          1.22474487] [2.44948974]
[1 0 0] [0] ['B' 'M']

fit = statistics seekhna (μ, σ), transform = un statistics se data badalna, fit_transform = dono ek saath. Rule simple hai: fit sirf train pe, transform sab pe.

Exam Radar

  • Correct order: split → fit scaler on train → transform train & test. Lab 5 activity ka TODO bhi yahi kehta hai: "fit_transform on X_train, transform (NOT fit_transform) on X_test".
  • StandardScaler ke baad har feature ka (train pe) mean = 0, std = 1 ("zero mean, unit variance").
  • Data leakage ka effect: test score zyada accha dikhta hai jitna asli duniya me milega (over-optimistic).

Galti mat karna

  • scaler.fit_transform(X) poore data pe, split se pehle — leakage.
  • scaler.fit_transform(X_test) — test ka apna alag μ, σ; train aur test alag "ruler" se naape gaye. Galat.
  • Test ko scale karna bhool jaana — model ne scaled numbers pe seekha, raw numbers pe bakwaas bolega.

Dimaag me aise baitha lo

Ruler train se banao, usi ruler se test naapo. Test se ruler kabhi mat banao.

1.9 Parameters vs Hyperparameters — kaun seekhta hai, kaun chunta hai

Kahani

Maa dal bana rahi hain. Namak thoda daala, chakha, "thoda kam hai", aur daala, chakha… Namak ki exact quantity unhone chakh chakh ke (feedback se) seekhi. Ye hai parameter.

Par kuch cheezein unhone shuru me hi tay kar li thi: kaunsa bartan (cooker ya kadhai), gas kitni tez, kitni der pakana, kitni baar chakhna. Ye cheezein khana pakate pakate apne aap nahi seekhi jaatin — ye hyperparameters hain. Galat chuni (gas bahut tez) to dal jal jaayegi, chahe namak kitna bhi perfect ho.

Parameters

  • Model ke andar ke numbers: weights (W) aur biases (b) (aur BatchNorm ke γ, β — Ch 20)
  • Data se seekhe jaate hain — gradient descent / optimizer se
  • PyTorch me model.parameters() me milte hain, optimizer inhi ko update karta hai
  • Training ke baad save hote hain (yahi "trained model" hai)

Hyperparameters

  • Training se pehle hum chunte hain
  • learning rate, epochs, batch size, layers ki ginti, har layer me neurons, activation function, optimizer (SGD/Adam) aur uske settings (momentum β), dropout rate p, weight decay λ, initialization, CNN ka kernel size / stride / padding / filters
  • Validation set dekh ke tune hote hain (manual, grid search, random search, Optuna — Ch 22)
nn.Linear(in, out) ke parameters = in × out + out
  • in × out = weights (har input ka har output se ek connection)
  • + out = biases (har output neuron ka ek)

Chalo numbers se samjhein

  1. Lab 5 model nn.Linear(30, 1): 30 × 1 + 1 = 31 parameters
  2. Ek chhota network 2 → 4 → 1: pehli layer 2 × 4 + 4 = 12, doosri 4 × 1 + 1 = 5, total 12 + 5 = 17
  3. Dhyaan do: "4 neurons" humne chuna (hyperparameter), par usi choice se 17 parameters bane jo data se seekhe jaayenge.

PyTorch · Lab 5 style

import torch
import torch.nn as nn

# HYPERPARAMETERS: hum khud likhte hain (Lab 5)
learning_rate = 0.1
epochs = 25

# PARAMETERS: model ke andar, optimizer seekhega
model = nn.Linear(30, 1)
print(model.weight.shape, model.bias.shape)          # weights aur bias ka shape
print(sum(p.numel() for p in model.parameters()))    # total kitne knobs

net = nn.Sequential(nn.Linear(2, 4), nn.ReLU(), nn.Linear(4, 1))
print(sum(p.numel() for p in net.parameters()))      # 2 -> 4 -> 1 network

Output

torch.Size([1, 30]) torch.Size([1])
31
17

(p.numel() = tensor me kitne numbers hain. sum(... for p in ...) wali Python trick Chapter 3.11 me samjhayi hai.)

Exam Radar

  • Classic MCQ: "Which of these is NOT a hyperparameter?" → weights / biases (ye parameters hain).
  • "Which is learned during training?" → weights & biases. Learning rate, epochs, batch size, #layers, #neurons, dropout rate — sab hyperparameters.
  • Lecture 5 note: "Learning rate is the highest-leverage hyperparameter. Search it on a log scale." (0.0001, 0.001, 0.01, 0.1 …)
  • Lecture 5: "Batch size becomes another hyperparameter to choose."

Galti mat karna

  • "Number of neurons parameter hai" — nahi, wo architecture choice = hyperparameter. Parameters ki ginti us choice se nikalti hai.
  • Bias ko hyperparameter samajhna — bias bhi seekha jaata hai, wo parameter hai.

Dimaag me aise baitha lo

Jo machine khud seekhe = parameter (namak). Jo tum pehle chuno = hyperparameter (gas, bartan, time).

1.10 Epoch, Batch, Iteration — pehli mulakat

Kahani

Riya ke paas 455 pages ki guide hai (bilkul Lab 5 ke 455 training patients jitni!). Wo ek baar me poori nahi padh sakti. To wo 32-32 pages ke tukde padhti hai. Har tukda padhne ke baad 2 minute ruk ke apni notes sudhaar leti hai.

32 pages ka tukda = batch. Ek tukda padh ke notes sudhaarna = iteration (ek update). Poori guide ek baar khatam = 1 epoch. Exam se pehle poori guide 25 baar padhi = 25 epochs.

Definition

Batch (mini-batch) = training samples ka chhota group jo ek saath model me jaata hai. Uska size = batch size B.

Iteration (step) = ek batch pe: forward pass → loss → backward pass → parameters update. Ek iteration = ek parameter update.

Epoch = poore training set ka ek complete chakkar (har sample ek baar dikh gaya).

iterations per epoch = ⌈ NB ⌉     total updates = epochs × ⌈N / B⌉
  • N = training samples, B = batch size
  • ⌈ ⌉ = ceiling (upar round karo) — kyunki aakhri chhota batch bhi ek iteration hai

Chalo numbers se samjhein

  1. Lab 5 bonus: N = 455, B = 32. 455 / 32 = 14.22 → ⌈14.22⌉ = 15 iterations per epoch
  2. Kaise? 14 × 32 = 448 samples 14 poore batches me, bache 455 − 448 = 7 → aakhri batch me sirf 7.
  3. 25 epochs me total updates: 25 × 15 = 375
  4. Lab 5 ka asli loop full-batch tha (B = N = 455): ⌈455/455⌉ = 1 update per epoch → 25 epochs = 25 updates.
  5. Ek aur: N = 1000, B = 100, 5 epochs → 10 per epoch × 5 = 50 updates.
1 epoch = 455 training samples (Lab 5) 32 32 32 32 32 32 32 32 32 32 32 32 32 32 7 update 1 update 15 batch_size = 32 → 14 poore batches + 1 chhota (7) = 15 iterations 25 epochs → 25 × 15 = 375 updates
Har dabba = ek batch = ek iteration = ek update. Saare dabbe = ek epoch.

PyTorch · DataLoader se ginti (DataLoader poora Chapter 14 me)

import torch
from torch.utils.data import TensorDataset, DataLoader

X = torch.randn(455, 30)                      # 455 samples, 30 features (Lab 5 jaisa)
y = torch.randint(0, 2, (455,)).float()       # 0/1 labels
loader = DataLoader(TensorDataset(X, y), batch_size=32, shuffle=True)

print(len(loader))                            # iterations per epoch
print([xb.shape[0] for xb, yb in loader])     # har batch ka size
epochs = 25
print(epochs * len(loader))                   # total updates

Output

15
[32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 7]
375
Method (Ch 14)Batch sizeUpdates per epoch
Batch GD (full batch) — Lab 5 main loopN1
SGD (stochastic)1N
Mini-batch GDBN/B (upar round)

Exam Radar

  • Epoch = full pass over training data. Iteration = one batch = one update. Lecture 5: "With N examples, one epoch contains N/B updates."
  • DataLoader me drop_last=False default — isliye aakhri chhota batch (7) bhi aata hai.
  • Batch size aksar 2 ki power: 16, 32, 64, 128.

Galti mat karna

  • Epoch aur iteration ko same samajhna. Full-batch me hi dono barabar hote hain (1 update per epoch).
  • ⌈ ⌉ bhool ke 455/32 = 14 likh dena — aakhri 7 samples wala batch bhi ek iteration hai.

Dimaag me aise baitha lo

Batch = ek tukda, iteration = ek tukde ke baad sudhaar, epoch = poori kitaab ek baar.

1.11 "Deep" kyun — layers jo features khud seekhti hain

Kahani

Mela ki bheed me tum apne dost ko door se pehchaan lete ho. Kya tum dimaag me checklist chalate ho — "naak ki lambai 4 cm? aankh ka rang bhoora? chehra kitna gol?" Nahi! Tumhara dimaag pehle chhoti cheezein dekhta hai (kinare, lines), fir unse bade hisse banata hai (aankh, naak), fir poora chehra. Ye sab apne aap, layer by layer.

Purane ML me hum insaan ko checklist banani padti thi — Lecture 0 ke words me: "You told the machine what to look for — 'roundness', 'colour intensity'". Deep learning me hum sirf network ka dhaancha (architecture) banate hain, aur network khud dhoondhta hai ki kya dekhna hai.

Definition

Feature engineering (hand-crafted features) = insaan soch ke features banata hai (roundness, colour histogram, edge count) aur unpe ek simple model lagata hai.

Representation learning = model raw data (pixels) se khud useful features (representations) seekhta hai. Deep network me har layer pichhli layer ke output ko thoda aur "samajhdaar" (abstract) form me badalti hai.

Deep = bahut saari layers ek ke upar ek (input → kai hidden layers → output). Depth = layers ki ginti, width = ek layer me neurons ki ginti. Sirf ek hidden layer wale network ko shallow network kehte hain (Chapter 7); usse zyada hidden layers = deep network.

End-to-end learning = features aur final decision dono ek hi network ek saath, ek hi loss se seekhta hai.

Pixels Edges Textures Shapes Objects pehli layers → beech ki layers → gehri layers (zyada abstract) Classical ML: photo insaan ne chune features simple model billi? Deep Learning: photo deep network: features + decision dono khud seekhe billi?
Lecture 0 ki hierarchy: Pixels → Edges → Textures → Shapes → Objects. Neeche: hand-crafted pipeline vs end-to-end deep pipeline.

CNN (Chapter 23) me ye sach me dikhta hai: AlexNet ki pehli layer ke filters ko print karo to wo chhote chhote edge detectors aur colour blobs jaise dikhte hain, aur gehri layers aankh, pahiya, chehra jaise patterns pe "jaagti" hain. Kisi ne unhe ye nahi sikhaya — loss kam karte karte network ne khud ye features bana liye.

Exam Radar

  • Lecture 0: "In DL, we don't design features; we design architectures that learn features." Hierarchy: Pixels → Edges → Textures → Shapes → Objects.
  • Deep = many layers (multiple hidden layers). Shallow = one hidden layer. Depth vs width ki ladai Chapter 10 me.
  • Representation learning = automatic feature learning. Isi wajah se DL images, audio, text jaise raw data pe sabse accha chalta hai.

Galti mat karna

  • "Deep" ka matlab "bahut neurons ek layer me" nahi — wo wide hai. Deep = bahut layers.
  • DL me features design nahi karte, par architecture (kitni layers, kaunsi layer) abhi bhi hum design karte hain.

Dimaag me aise baitha lo

Purana ML: checklist tum banao. Deep learning: checklist network khud banaye, layer by layer.

1.12 Deep learning abhi hi kyun chala? Data + GPU + better algorithms (aur "Bitter Lesson")

Kahani

Neural networks ka idea 1940s–50s ka hai. To 2012 tak sab so kyun rahe the? Socho ek shandaar race car bana li, par na petrol tha, na road. Car = neural network idea. Petrol = data. Engine ki taakat = compute (GPU). Achhe tyre aur brakes = better algorithms/tricks. Jab teeno ek saath mile (lagbhag 2010–2012), car ud padi.

1. Data: internet, smartphones, social media ne arbon photos/text bana diye. ImageNet (Fei-Fei Li ki team, 2009) me ~14 million labelled images hain; uske competition ILSVRC me 1000 classes aur ~1.2 million training images. Deep networks ke paas bahut parameters hote hain, unhe bharne ke liye bahut data chahiye.

2. Compute (GPU): neural network ka 90% kaam matrix multiplication hai, aur GPU hazaaron chhote cores se ek saath (parallel) multiply karta hai. Lab 0 me 10000×10000 ke do matrices multiply kiye: CPU ne 16.44 second liye, GPU (Tesla T4) ne 0.56 second → 16.44 / 0.562 ≈ 29 guna tez. AlexNet (2012) do GTX 580 GPUs pe 5–6 din train hua tha.

3. Better algorithms / tricks: ReLU (sigmoid ki tarah saturate nahi hota, training tez — Ch 9), Dropout (ratta rokna — Ch 20), achha initialization (Xavier 2010, He 2015 — Ch 21), Batch Normalization (2015 — Ch 20), Adam optimizer (2014 — Ch 17), residual connections (ResNet 2015, 100+ layers train ho sake — Ch 25). Saath me software: TensorFlow (Google, 2015), PyTorch (Facebook/Meta, 2016) jinke autograd ne backprop ko ek line (loss.backward()) bana diya.

Deep Learning ka boom (2012 se) DATA internet, phones ImageNet ~14M labelled images COMPUTE GPU = parallel matmul. Lab 0: ~29× tez ALGORITHMS ReLU, Dropout, BatchNorm, Adam, He init, ResNet neev: Bitter Lesson — computation + learning jeet-te hain
Teen khambhe: data, compute, algorithms. Ek bhi hata do to chhat gir jaati.

PyTorch · Lab 0 · GPU hai ya nahi, aur kitna tez

import torch, time

if torch.cuda.is_available():                  # GPU mila?
    print("GPU is avl")
    print(f"using gpu:{torch.cuda.get_device_name(0)}")
else:
    print("GPU not avl")

device = torch.device('cuda')
size = 10000                                   # 10000 x 10000 matrices
matrix_cpu1 = torch.rand(size, size)
matrix_cpu2 = torch.rand(size, size)
matrix_gpu1 = matrix_cpu1.to(device)           # GPU pe copy
matrix_gpu2 = matrix_cpu2.to(device)

start_time = time.time()
result_cpu = torch.matmul(matrix_cpu1, matrix_cpu2)
print(f"CPU time: {time.time() - start_time} seconds")

start_time = time.time()
result_gpu = torch.matmul(matrix_gpu1, matrix_gpu2)
torch.cuda.synchronize()                       # GPU ka kaam khatam hone ka wait (warna time galat aayega)
print(f"GPU time: {time.time() - start_time} seconds")

Output (Lab 0 notebook, Colab Tesla T4 — tumhare machine pe numbers alag honge)

GPU is avl
using gpu:Tesla T4
CPU time: 16.439977169036865 seconds
GPU time: 0.562422513961792 seconds

The Bitter Lesson (Lecture 0)

Lecture 0 me ek poori kahani thi — Rich Sutton ka 2019 ka essay "The Bitter Lesson". AI research me hamesha do raaste rahe: (1) Human knowledge ka raasta — apni samajh machine me daalo (chess ke openings, endgame rules). (2) Raw computation ka raasta — general methods (search + learning) jo computation badhne ke saath behtar hote jaate hain.

Lesson: "General methods that leverage computation are ultimately the most effective, and by a large margin." Ye "bitter" (kadwa) isliye hai kyunki researchers chahte the ki unki clever insaani samajh jeete — par jeeta computation. "Search and learning" do aisi techniques hain jo computation ke saath scale karti hain.

Exam Radar

  • DL boom ke 3 reasons: big data, GPU compute, algorithmic improvements (ReLU, dropout, BN, better init, Adam, residual connections).
  • 1997: Deep Blue (IBM) ne Kasparov ko haraya — search based, neural network nahi.
  • Bitter Lesson: general methods + computation > hand-built human knowledge. Search aur learning scale karte hain.
  • Lab 0: torch.cuda.synchronize() GPU timing ke liye zaroori, kyunki GPU kaam asynchronously karta hai.

Dimaag me aise baitha lo

Car (idea) purani thi; petrol (data), engine (GPU) aur tyre (ReLU/BN/ResNet) 2012 ke aas paas mile.

1.13 Itihaas — 1943 ke neuron se ResNet tak

Kahani

Indian cricket team ka itihaas socho: kabhi golden era (1983 World Cup), kabhi bura daur, fir dobara uthna (2011). Neural networks ki kahani bilkul aisi hai. Tumhare Lecture 1 worksheet ke words me: "AI's history is not one smooth climb — it moves in golden ages and dark ages ('AI winters')." Teen golden ages, do dark ages.

Teen Golden Ages, do Dark Ages (Lecture 1 worksheet) Golden Age 1 AI winter 1 Golden Age 2 Dark Age 2 1943 1956 1957–58 1969 1986 1989 1998 McCulloch–Pitts neuron Dartmouth:"AI" born RosenblattPerceptron Minsky–PapertXOR critique BackpropRumelhart–Hinton–Williams CybenkoUAT LeCunLeNet-5 Golden Age 3 — jo abhi chal raha hai 2006 2012 2014 2015 2017 2022 Hinton: DBN,RBM pretraining AlexNet winsImageNet VGG, GoogLeNet,Adam ResNet,BatchNorm Transformer ChatGPT
Upar wali line 1943–1998, neeche wali 2006 se aaj tak. Laal = turning points (1969 ne giraya, 2012 ne uthaya).
SaalKaunKya kiyaKyun important
1943Warren McCulloch & Walter PittsNeuron ka pehla mathematical model: inputs ka weighted sum, threshold se zyada to "fire" (1), warna 0"One cell can be any gate" — AND, OR, NOT ek cell se. Par weights haath se set hote the, seekhe nahi jaate the (Ch 4)
1950Alan TuringTuring Test ("Computing Machinery and Intelligence")"Kya machine soch sakti hai?" ka test
1956Dartmouth conference (John McCarthy & others)"Artificial Intelligence" naam ka janmAI ek field bana — Golden Age 1 shuru
1957 / 1958Frank RosenblattPerceptron — pehla neuron jo apne weights data se seekhta hai (perceptron learning rule)"Who chose those weights?" ka jawab: machine khud! Worksheet 1957 likhta hai (Cornell report), famous paper 1958 ka — dono years exam me dikh sakte hain
1959–60Widrow & HoffADALINE (LMS rule)Threshold se pehle wali error pe seekhna — gradient descent jaisa idea
1969Marvin Minsky & Seymour PapertBook "Perceptrons": single-layer perceptron XOR solve nahi kar sakta (XOR linearly separable nahi)Funding gir gayi → First AI winter / First Dark Age (Ch 6)
1986David Rumelhart, Geoffrey Hinton, Ronald WilliamsBackpropagation ko popular kiya (Nature paper "Learning representations by back-propagating errors")Hidden layers wale networks train ho sake → XOR solve. Golden Age 2. (1969 se 17 saal baad)
1989George Cybenko (aur Hornik et al.)Universal Approximation Theorem (sigmoid)Ek hidden layer + kaafi neurons → koi bhi continuous function approximate (Ch 10). Revival ko theoretical neev mili
1995Cortes & VapnikSupport Vector MachinesKam computation me NNs se behtar → Second Dark Age neural nets ke liye
1998Yann LeCun et al.LeNet-5 CNN — handwritten digits (MNIST), ~60,000 parametersDark age me bhi CNN ka idea zinda rakha; cheques padhne me use hua (Ch 24)
2006Geoffrey Hinton (Osindero, Teh)Deep Belief Networks — RBM se greedy layer-wise pretrainingDeep networks train karne ka pehla kaamyaab tareeka → Golden Age 3 shuru, "deep learning" naam chal pada
2009Fei-Fei Li & teamImageNet datasetBada labelled data — DL ka petrol
2012Alex Krizhevsky, Ilya Sutskever, Geoffrey HintonAlexNet ne ILSVRC-2012 (ImageNet) jeeta: top-5 error 15.3% vs doosre number pe 26.2%"Big Bang" moment. GPUs, ReLU, dropout, data augmentation. 8 layers (5 conv + 3 FC), ~60M parameters (Ch 24)
2014Simonyan & Zisserman (Oxford); Szegedy et al. (Google); Kingma & BaVGG (16/19 layers, sirf 3×3 conv), GoogLeNet / Inception v1 (ILSVRC-2014 winner, 22 layers), Adam optimizerGehre networks; Adam aaj ka default optimizer (Ch 17, 24)
2015Kaiming He et al. (Microsoft); Ioffe & Szegedy (Google)ResNet (skip/residual connections, 152 layers, ILSVRC-2015 winner, top-5 ≈ 3.57%); Batch Normalization100+ layers train hona possible; human-level (~5%) se behtar ImageNet error (Ch 20, 25)
2017Vaswani et al.Transformer ("Attention Is All You Need")LLM race shuru: GPT-3, ChatGPT (2022), GPT-4V, DeepSeek-R1 (2025)

Lecture 0 ki ek "sad story" bhi yaad rakho — "The Unsung Architect": AlexNet ka naam jis ladke pe pada, Alex Krizhevsky (PhD student, first author), usne hi model banaya aur fast GPU code likha. Hinton ("Godfather of AI") aur Sutskever (OpenAI ke co-founder) mashhoor ho gaye. Teeno ki company DNNresearch ko Google ne khareed liya. Alex chupchaap technical kaam karte rahe. Sawaal: success = public taaliyan ya kaam ka asar?

Ek pattern bhi dekho: har dark age ek naye idea se khatam hua jo usi problem ko solve karta tha jisne andhera laaya. XOR ne 1969 me giraya → backprop + hidden layers (1986) ne XOR solve karke uthaya. Deep nets train nahi ho rahe the → pretraining (2006), fir GPUs + data + ReLU (2012) ne uthaya.

Exam Radar

  • 1943 McCulloch–Pitts (first artificial neuron, no learning) · 1957/58 Rosenblatt perceptron (learning) · 1969 Minsky & Papert "Perceptrons" (XOR) → AI winter · 1986 backprop (Rumelhart, Hinton, Williams) · 1989 UAT (Cybenko) · 1998 LeNet-5 (LeCun) · 2006 DBN (Hinton) · 2012 AlexNet · 2014 VGG, GoogLeNet, Adam · 2015 ResNet, BatchNorm.
  • AlexNet authors: Krizhevsky, Sutskever, Hinton (University of Toronto). Top-5 error 15.3% vs 26.2%.
  • Course framing: Third Golden Age "dated from Hinton's RBM pretraining breakthrough (2006) and confirmed by AlexNet's 2012 ImageNet win".
  • Bonus facts: 2018 Turing Award — Hinton, LeCun, Bengio ("godfathers of deep learning"). 2024 Physics Nobel — Hopfield & Hinton.

Galti mat karna

  • McCulloch–Pitts neuron seekhta nahi tha; seekhne wala pehla neuron Rosenblatt ka perceptron tha.
  • 1986 me backprop "invent" nahi hua (Linnainmaa 1970, Werbos 1974 ne pehle math diya) — 1986 paper ne use popular kiya aur dikhaya ki hidden layers useful representations seekhti hain. Course bhi "popularized" likhta hai.
  • Minsky–Papert ne single-layer perceptron ki limit dikhayi thi, multi-layer ki nahi.

Dimaag me aise baitha lo

43 neuron, 58 seekha, 69 XOR pe gira, 86 backprop se utha, 98 LeNet, 2012 AlexNet ka dhamaka, 2015 ResNet ki gehraai.

1.14 Poore course ka safar — ek naksha

Kahani

Rajdhani Express Mumbai se Delhi jaati hai, beech me stations aate hain. Har station pe kuch naya chadhta hai. Hamari train bhi ek chhote se neuron se chalti hai. Usko network banate hain. Network ko galti naapne ka meter (loss) dete hain. Galti se seekhne ka tareeka (backprop + gradient descent). Tez aur smart seekhna (optimizers). Ratta maarne se bachana (regularization). Shuru me knobs kahan rakhein (initialization). Photos ke liye special network (CNN). Aur aakhir me bahut gehre networks (ResNet). Ye raha naksha.

Module 1: ek neuron → network → kya seekh sakta hai 4Perceptron 5w·x + b 6XOR 7Shallow 8Forward 9Activation 10UAT, depth Module 1–2: galti naapo → seekho → tez seekho 11Loss 12Graph 13Backprop 14GD types 15LR sched 16Momentum 17Adam etc. Module 3–4: ratta roko → achha start → photos → bahut gehra 18Bias-Var 19L1/L2/ES 20Drop/BN 21Init 22Optuna 23CNN 24VGG etc. 25ResNet Chapters 1–3 (ye wale) = sab stations ke liye ticket: intro + math + Python/tensors
Ek neuron se ResNet tak — har dabba ek chapter. Box ka number = chapter number.

Aur in sab ke liye tools: Chapter 2 — Math ki potli aur Chapter 3 — Python + PyTorch. Chalo, potli kholte hain.

Dimaag me aise baitha lo

Neuron → network → loss → backprop + GD → better optimizers → regularization → init → CNN → ResNet.

Chapter 2

Math ki potli — jitna math chahiye, utna hi, zero se

Scalar/Vector/Matrix/Tensor · Dot product · w·x + b · Matrix multiplication · Transpose · Line · e aur ln · Derivative · Rules · Chain rule · Gradient · Minimum · Σ · Mean/Variance · Normal/Uniform · Probability · argmax

Lecture 0 ne saaf bola tha: "Deep Learning is not magic; it is Calculus and Linear Algebra acting on Data." Ghabrao mat — poore course me sirf ye potli chahiye. Har cheez numbers ke saath karenge, aur har jagah batayenge ki aage kis chapter me kaam aayegi.

2.1 Scalar, Vector, Matrix, Tensor — aur unka "shape"

Kahani

Riya ke Maths me 92 marks aaye — bas ek number. Ye scalar. Uske 5 subjects ke marks [92, 85, 78, 88, 95] — numbers ki ek line. Ye vector. Poori class (30 students × 5 subjects) ka register — rows aur columns ki table. Ye matrix. Aur school ke 3 sections ke 3 registers ek ke upar ek rakh do (3 × 30 × 5) — ye 3-D tensor. Bas, tensor = numbers ka dabba jisme kitni bhi dimensions ho sakti hain.

NaamDimensions (ndim / rank)Shape ka exampleDeep learning me kahan
Scalar0-D() — bas ek numberloss value, learning rate
Vector1-D(5,)ek sample ke features, bias vector
Matrix2-D(2, 3) = 2 rows, 3 columnstabular batch (455, 30), weight matrix, grayscale image (28, 28)
3-D tensor3-D(3, 32, 32)ek colour photo: (channels, height, width)
4-D tensor4-D(64, 3, 32, 32)photos ka batch: (N, C, H, W) — CNN ka input (Ch 23)

Shape = har dimension me kitne elements. Pehla number = sabse bahar wali dimension. Matrix ke liye shape (m, n) = m rows × n columns (hamesha "rows pehle"). Total numbers = shape ke saare numbers ka guna: (64, 3, 32, 32) → 64 × 3 × 32 × 32 = 196,608 numbers.

5 2 7 1 1 2 3 4 5 6 scalar0-D, shape () vector1-D, shape (3,) matrix2-D, shape (2, 3) 3-D tensorshape (3, 2, 3) photos ka batch = 4-D: (N, C, H, W), jaise (64, 3, 32, 32)
Har baar ek dimension badhao: number → line → table → tables ka dher.

PyTorch · ndim, shape, numel

import torch
s = torch.tensor(92.0)                          # scalar
v = torch.tensor([92., 85., 78., 88., 95.])     # vector
M = torch.tensor([[1, 2, 3], [4, 5, 6]])        # matrix: 2 rows, 3 cols
T = torch.zeros(3, 30, 5)                       # 3 sections x 30 students x 5 subjects
imgs = torch.zeros(64, 3, 32, 32)               # 64 colour photos, 32x32
for t in [s, v, M, T, imgs]:
    print(t.ndim, t.shape, t.numel())           # dimensions, shape, total numbers

Output

0 torch.Size([]) 1
1 torch.Size([5]) 5
2 torch.Size([2, 3]) 6
3 torch.Size([3, 30, 5]) 450
4 torch.Size([64, 3, 32, 32]) 196608

Exam Radar

  • ndim (rank) = kitni dimensions. Scalar 0, vector 1, matrix 2. Image batch (N, C, H, W) = 4-D.
  • Shape (2, 3) = 2 rows, 3 columns. Total elements = shape ka product.
  • PyTorch me shape (3,) ek 1-D vector hai (na row na column). (1, 3) = row vector (2-D), (3, 1) = column vector (2-D). Lab 5 ka bada bug isi farak se aata hai (Ch 3.16).

Dimaag me aise baitha lo

Number → line → table → tables ka dher. Shape = "har taraf kitne".

2.2 Dot product — chai ka bill, aur do vectors kitne "milte" hain

Kahani

Riya ne Raju ki stall pe order kiya: 2 chai, 1 samosa, 3 biscuit packet. Rate: chai ₹10, samosa ₹15, biscuit ₹5. Bill? 2×10 + 1×15 + 3×5 = 20 + 15 + 15 = 50. Badhai ho, tumne abhi dot product nikala! Quantities ki list aur prices ki list — position by position guna karo, sab jod do.

a · b = a1b1 + a2b2 + … + anbn = Σ ai bi
  • a, b = do vectors, dono ki length same honi chahiye (n)
  • ai bi = same position wale numbers ka guna
  • Result = ek single number (scalar), vector nahi

Chalo numbers se samjhein

  1. Lab 0 wala: [1, 2] · [3, 4] = 1×3 + 2×4 = 3 + 8 = 11
  2. Chai bill: [2, 1, 3] · [10, 15, 5] = 20 + 15 + 15 = 50
  3. Weightage (internal 30%, external 70%), marks 80 aur 90: [0.3, 0.7] · [80, 90] = 24 + 63 = 87

Jawab: 11, 50, 87 — har baar ek single number.

Dot product ke do matlab

Matlab 1 — weighted sum: ek vector "kitna" (inputs) aur doosra "kitni ahmiyat" (weights). Neuron yahi karta hai: har input ko uske weight se guna karke jodta hai. Bada weight = wo input zyada important.

Matlab 2 — similarity (kitne milte-julte): geometry me a · b = |a| |b| cos θ, jahan θ dono ke beech ka angle aur |a| = length = √(a · a). Same direction (θ chhota) → bada positive. Perpendicular (θ = 90°, cos = 0) → 0. Ulti direction → negative. Examples: [1,0]·[1,0] = 1, [1,0]·[0,1] = 0, [1,0]·[−1,0] = −1. Length: |[3, 4]| = √(9 + 16) = 5.

ab a·b > 0 (milte-julte) ab a·b = 0 (90°, perpendicular) ab a·b < 0 (ulti disha)
Dot product ka sign batata hai do vectors ek taraf hain, perpendicular hain, ya ulte.

PyTorch · Lab 0 · torch.dot

import torch
vector1 = torch.tensor([1, 2])
vector2 = torch.tensor([3, 4])
print(torch.dot(vector1, vector2))        # 1*3 + 2*4

qty   = torch.tensor([2., 1., 3.])        # chai, samosa, biscuit
price = torch.tensor([10., 15., 5.])
print(torch.dot(qty, price), (qty * price).sum())   # dono tareeke same
print(torch.linalg.norm(torch.tensor([3., 4.])))    # length = sqrt(9 + 16)

Output

tensor(11)
tensor(50.) tensor(50.)
tensor(5.)

Exam Radar

  • Dot product ka result scalar. Do vectors same length ke hone chahiye.
  • Perpendicular (orthogonal) vectors ka dot product = 0.
  • torch.dot sirf 1-D tensors pe chalta hai, aur dono ka dtype same hona chahiye (int aur float mix → error).

Dimaag me aise baitha lo

Dot product = chai ka bill: quantity × rate, sab jod do.

2.3 Weighted sum + bias: z = w·x + b — neuron ka dil

Kahani

Auto-rickshaw me baithte hi meter ₹30 dikhata hai — abhi ek meter bhi nahi chale! Fir har km ka ₹12. Kiraya = 12 × km + 30. Wo ₹30 jo input (km) zero hone pe bhi lagta hai — wahi bias hai. Aur 12 = har km ki "ahmiyat" = weight.

Neuron (Chapter 4) bilkul auto meter jaisa hai, bas inputs zyada hain: har input xi ko uske weight wi se guna karo, sab jodo (dot product), aur upar se bias b jodo. Is number ko z kehte hain — "pre-activation" ya "raw score" ya "logit".

z = w1x1 + w2x2 + … + wnxn + b = w · x + b = Σ wixi + b
  • x = inputs (features), w = weights (har input ki ahmiyat, sign bhi: negative weight = wo input z ko ghatata hai)
  • b = bias = base value, jab saare inputs 0 hon tab bhi z = b
  • Bias kyun? Bina b ke line hamesha origin (0, 0) se guzregi. b line ko upar-neeche khiskaane ki azaadi deta hai. Perceptron ka threshold θ bhi yahi hai: "Σwixi ≥ θ" ko likho "Σwixi + b ≥ 0", jahan b = −θ

Chalo numbers se samjhein

Sawaal: x = [2, 3], w = [0.5, −1], b = 1. z nikalo. Aur auto me 5 km ka kiraya?

  1. Guna: 0.5 × 2 = 1, −1 × 3 = −3
  2. Jodo: 1 + (−3) = −2
  3. Bias: z = −2 + 1 = −1
  4. Auto: 12 × 5 + 30 = 90

Jawab: z = −1; kiraya ₹90.

PyTorch · haath se aur nn.Linear se (Lab 1 style weights set karna)

import torch
import torch.nn as nn

x = torch.tensor([2.0, 3.0])
w = torch.tensor([0.5, -1.0])
b = 1.0
print(w @ x + b)                      # 1-D @ 1-D = dot product, fir bias

layer = nn.Linear(2, 1)               # 2 inputs, 1 output neuron
layer.weight.data = torch.tensor([[0.5, -1.0]])   # shape (out, in) = (1, 2)
layer.bias.data = torch.tensor([1.0])             # shape (out,) = (1,)
print(layer(torch.tensor([[2.0, 3.0]])))          # input (batch=1, features=2)

Output

tensor(-1.)
tensor([[-1.]], grad_fn=<AddmmBackward0>)

grad_fn=<AddmmBackward0> ka matlab: PyTorch ne yaad rakha ki ye number "matrix multiply + add" se bana, taaki baad me derivative nikal sake (Chapter 12). Lab 1 me isi tarah AND gate ke weights haath se set kiye the: model.weight.data = torch.tensor([[1., 1.]]), model.bias.data = torch.tensor([-1.5]).

Exam Radar

  • Neuron ka pre-activation: z = w·x + b. Iske baad activation lagta hai: a = f(z) (Ch 9).
  • nn.Linear(in, out) ka weight shape (out, in), bias shape (out,). Ye x Wᵀ + b nikalta hai.
  • Bias = −threshold. Bias line ko shift karta hai, weight usko ghumata (rotate) hai.

Dimaag me aise baitha lo

Auto meter: ₹30 base (bias) + ₹12 har km (weight × input).

2.4 Matrix multiplication — bahut saare dot products ek saath

Kahani

Do dost — Golu aur Bunty — canteen jaate hain. Golu leta hai 1 chai, 2 samosa, 3 biscuit. Bunty leta hai 4 chai, 5 samosa, 6 biscuit. Do dukaanein hain. Dukaan P ka rate: chai 7, samosa 9, biscuit 11. Dukaan Q ka rate: chai 8, samosa 10, biscuit 12. Sawaal: har dost ka har dukaan pe bill kitna? Ye 2 dost × 2 dukaan = 4 dot products hain. Inhe ek jhatke me nikalna = matrix multiplication.

(m × n) · (n × p) = (m × p)     Cij = (row i of A) · (column j of B) = Σk AikBkj
  • A ka shape m × n (yahan 2 dost × 3 items), B ka n × p (3 items × 2 dukaanein)
  • A ke columns = B ki rows (dono n) — tabhi row aur column ka dot product ban sakta hai
  • Result C ka shape m × p: har (dost, dukaan) ka ek bill

Chalo numbers se samjhein

A =
123
456
(2×3),   B =
78
910
1112
(3×2)
  1. Shape check: (2×3)·(3×2) → andar wale 3 = 3 ✓ → result 2×2
  2. C11 (Golu @ P) = row 1 · col 1: 1×7 + 2×9 + 3×11 = 7 + 18 + 33 = 58
  3. C12 (Golu @ Q) = row 1 · col 2: 1×8 + 2×10 + 3×12 = 8 + 20 + 36 = 64
  4. C21 (Bunty @ P) = row 2 · col 1: 4×7 + 5×9 + 6×11 = 28 + 45 + 66 = 139
  5. C22 (Bunty @ Q) = row 2 · col 2: 4×8 + 5×10 + 6×12 = 32 + 50 + 72 = 154
AB =
5864
139154
(2×2)

Jawab: AB = [[58, 64], [139, 154]] — Golu ko Dukaan P sasti padegi (58 < 64), Bunty ko bhi (139 < 154).

123 456 × 78 910 1112 = 5864 139154 row 1 · col 1 = 1×7 + 2×9 + 3×11 = 7 + 18 + 33 = 58 (2 × 3) (3 × 2) (2 × 2) andar wale same (3 = 3) → bahar wale bachte (2 × 2)
Highlighted row × highlighted column = highlighted answer cell. Har cell ek alag dot product.

Shape checking trick (exam ka hathiyaar)

Shapes ko paas paas likho: (2 × 3) (3 × 2). Beech wale do number (andar wale) same hone chahiye — wo "cancel" ho jaate hain. Bahar wale bach jaate hain = answer ka shape. Agar andar wale alag: (2 × 3)(2 × 3) → 3 ≠ 2 → error. Neural network me: input batch (N, in) aur nn.Linear(in, out) → (N, in) · (in, out) = (N, out). Lab 5: (455, 30) · (30, 1) = (455, 1) — ek jhatke me 455 patients ke scores!

Order matter karta hai: BA = (3×2)(2×3) = 3×3 matrix, bilkul alag cheez. Matrix multiplication commutative nahi hai: AB ≠ BA. Lab 0 check: m1 ki pehli row [9, 6, 1, 9] · m2 ka pehla column [3, 1, 5, 3] = 27 + 6 + 5 + 27 = 65 — wahi output ka pehla number tha.

PyTorch · @ aur torch.matmul (Lab 0)

import torch
A = torch.tensor([[1, 2, 3], [4, 5, 6]])          # (2, 3)
B = torch.tensor([[7, 8], [9, 10], [11, 12]])     # (3, 2)
print(A @ B)                  # (2,3)@(3,2) -> (2,2)
print(torch.matmul(A, B))     # same cheez, function wala naam
print(B @ A)                  # (3,2)@(2,3) -> (3,3): bilkul alag!
try:
    A @ A                     # (2,3)@(2,3): andar wale 3 != 2
except RuntimeError as e:
    print("RuntimeError:", e)

X = torch.randn(455, 30)      # Lab 5 jaisa batch
W = torch.randn(1, 30)        # nn.Linear(30, 1) ka weight shape (out, in)
print((X @ W.T).shape)        # (455,30)@(30,1) -> (455,1)

Output

tensor([[ 58,  64],
        [139, 154]])
tensor([[ 58,  64],
        [139, 154]])
tensor([[ 39,  54,  69],
        [ 49,  68,  87],
        [ 59,  82, 105]])
RuntimeError: mat1 and mat2 shapes cannot be multiplied (2x3 and 2x3)
torch.Size([455, 1])

Exam Radar

  • (m×n)(n×p) = (m×p). Inner dimensions must match.
  • AB ≠ BA (generally). AB exist kare to bhi BA exist kare zaroori nahi.
  • Error text yaad rakho: "mat1 and mat2 shapes cannot be multiplied" = shapes ka inner number mismatch. Lab 5 warning: in_features must equal input ke columns.
  • Ek dense layer ka forward = ek matrix multiplication + bias. GPU isi kaam me tez hai.

Galti mat karna

  • Matrix multiplication ko "position by position guna" samajhna — wo element-wise (*) hai, alag cheez (Ch 3.17).
  • Result shape (n × n) ya (m × n) likh dena — hamesha bahar wale: (m × p).

Dimaag me aise baitha lo

Row se column ka dot. Andar wale same, bahar wale result.

2.5 Transpose — register ko tircha ghuma do

Kahani

Register me students rows me hain aur subjects columns me. Principal ko ulta chahiye: subjects rows me, students columns me. Tum poore register ko kone se pakad ke ghuma dete ho — pehli row pehla column ban jaati hai. Yahi transpose hai.

(AT)ij = Aji    shape (m × n) → (n × m)    (AB)T = BTAT
  • T = transpose. Row i, column j wala number jaata hai row j, column i pe
  • Do baar transpose = wapas original: (AT)T = A
  • Product ka transpose = transposes ka product ulte order me

Chalo numbers se samjhein

Lab 0 ka f =
582
552
(2×3) → fT =
55
85
22
(3×2)

Pehli row [5, 8, 2] ab pehla column hai; shape (2, 3) → (3, 2).

Kahan kaam aata hai: vectors ko column maano to dot product = wTx (row × column = 1×1). Lab 5 ki math line z = wTx + b yahi hai. Aur nn.Linear weight ko (out, in) me rakhta hai, isliye forward me x WT karta hai.

PyTorch · Lab 0 · transpose

f = torch.tensor([[5., 8., 2.], [5., 5., 2.]])
print(torch.transpose(f, 0, 1))      # dim 0 aur dim 1 ki adla-badli
print(f.T)                           # shortcut (2-D ke liye)
print(f.shape, f.T.shape)
# A, B upar 2.4 wale hi hain
print(torch.equal((A @ B).T, B.T @ A.T))   # (AB)^T = B^T A^T check

Output

tensor([[5., 5.],
        [8., 5.],
        [2., 2.]])
tensor([[5., 5.],
        [8., 5.],
        [2., 2.]])
torch.Size([2, 3]) torch.Size([3, 2])
True

Exam Radar

  • Transpose shape ulta karta hai: (2, 3) → (3, 2). torch.transpose(x, dim0, dim1) koi bhi do dims swap karta hai; .T 2-D ke liye shortcut.
  • (AB)T = BTAT — order ulta.

Dimaag me aise baitha lo

Transpose = register ko kone se ghumana: rows ↔ columns.

2.6 Seedhi line y = mx + c — slope aur intercept (yahi w aur b hain)

Kahani

Auto ka kiraya yaad hai? Kiraya = 12 × km + 30. Agar graph banao (x-axis pe km, y-axis pe kiraya), to ek seedhi line banegi. 0 km pe line ₹30 se shuru hoti hai (y-axis ko 30 pe kaatti hai), aur har 1 km aage jaane pe 12 upar chadhti hai. Ye "kitna chadhti hai" = slope, aur "kahan se shuru" = intercept.

y = mx + c     slope m = riserun = y2 − y1x2 − x1
  • m = slope = x 1 badhne pe y kitna badhta hai. m > 0 line upar chadhti, m < 0 neeche girti, m = 0 flat
  • c = intercept = x = 0 pe y ki value (line y-axis ko kahan kaatti hai)
  • Neuron se rishta: ŷ = wx + b → w = slope, b = intercept. b badlo → line upar/neeche khiskegi. w badlo → line ghoomegi (steep/flat)

Chalo numbers se samjhein

Sawaal: y = 2x + 1 ke points nikalo aur slope verify karo.

  1. x = 0 → y = 1 (intercept), x = 1 → y = 3, x = 2 → y = 5
  2. Slope = (5 − 3) / (2 − 1) = 2/1 = 2 ✓
  3. Raju ki line (1.3) ŷ = −2x + 100: slope −2 (garmi badhe to cups ghatein), intercept 100.
x y −1 1 2 3 4 4 8 y = 2x + 1 y = −x + 6 (slope −ve) run = 1 rise = 2 c = 1 (intercept)
Slope = rise/run = 2/1 = 2. Intercept = jahan line y-axis ko kaatti hai = 1. Narangi dashed line ka slope negative.

Do inputs ho to? w1x1 + w2x2 + b = 0 bhi (x1, x2) plane me ek line hai — decision boundary. Lab 1 ka AND neuron: x₁ + x₂ − 1.5 = 0 → x₂ = 1.5 − x₁ (slope −1, intercept 1.5). Line ke ek taraf output 1, doosri taraf 0. Ek neuron = ek seedhi line; isiliye XOR (jise ek line se nahi baant sakte) ek neuron se nahi hota (Chapter 5, 6).

PyTorch · ek line pe points

x = torch.tensor([0., 1., 2.])
print(2 * x + 1)          # m = 2, c = 1 -> har x ka y ek saath (broadcasting, Ch 3.16)

Output

tensor([1., 3., 5.])

Exam Radar

  • Slope = rise/run = Δy/Δx; intercept = x = 0 pe y.
  • Weight ↔ slope (orientation), bias ↔ intercept (shift). Bias = 0 → boundary origin se guzregi.
  • Single neuron ka decision boundary hamesha linear (line / plane / hyperplane) — step ya sigmoid lagane ke baad bhi, kyunki faisla z = w·x + b ke sign/threshold pe hi hota hai.

Dimaag me aise baitha lo

Auto meter: slope = per km rate, intercept = base fare. Neuron me: w = rate, b = base.

2.7 Jaadui number e ≈ 2.718 aur ex

Kahani

Ek bank tumhe ₹1 pe saal ka 100% byaaj deta hai. Saal ke end me ₹2. Ab bank kehta hai "aadhe saal pe byaaj jod denge": (1 + 1/2)2 = ₹2.25. Har mahine jodo: (1 + 1/12)12 ≈ ₹2.6130. Har din: (1 + 1/365)365 ≈ ₹2.7146. Har pal (infinite baar): ₹2.71828… Ye number aage nahi badhta — ye hai e, "natural growth" ka number.

e = limn→∞ (1 + 1n)n ≈ 2.71828     e0 = 1    e−x = 1ex    ea+b = ea · eb
  • ex (exp(x)) = e ko x baar khud se guna (fractional x bhi chalega)
  • ex hamesha positive hai — x kitna bhi negative ho. Isi wajah se softmax me use hota hai (negative score bhi positive "taakat" ban jaata hai)
  • e−x = ulta: x badhe to tezi se 0 ki taraf girta hai (par kabhi 0 nahi hota). Sigmoid me yahi hai: 1/(1 + e−z)

Chalo numbers se samjhein

  1. e⁰ = 1, e¹ ≈ 2.7183, e² ≈ 7.3891, e⁵ ≈ 148.4132, e⁸ ≈ 2980.9580 — dekha kitni tezi se badha? (Lab 0 ka torch.exp(f) ye hi numbers deta hai.)
  2. e⁻¹ ≈ 0.3679, e⁻² ≈ 0.1353 — chhote hote ja rahe, par positive.
  3. Rule check: e¹ × e¹ = 2.7183 × 2.7183 ≈ 7.389 = e² ✓
x −2 −1 0 1 2 4 8 eˣ e⁻ˣ e⁰ = 1 e¹ ≈ 2.718 e⁻¹ ≈ 0.368
eˣ: tezi se upar (exponential growth). e⁻ˣ: tezi se neeche (decay). Dono x = 0 pe 1 pe milte hain, aur hamesha x-axis ke upar — kabhi negative nahi.

PyTorch · Lab 0 · torch.exp

print(torch.exp(torch.tensor([0., 1., 2., -1., -2.])))
f = torch.tensor([[5., 8., 2.], [5., 5., 2.]])     # Lab 0 wala f
print(torch.exp(f))                                # har element ka e^x

Output

tensor([1.0000, 2.7183, 7.3891, 0.3679, 0.1353])
tensor([[ 148.4132, 2980.9580,    7.3891],
        [ 148.4132,  148.4132,    7.3891]])

Exam Radar

  • e ≈ 2.718; e0 = 1; ex > 0 for all x; ex ka derivative ex hi hai (2.10).
  • Kahan aata hai: sigmoid 1/(1+e−z), tanh, softmax ezi/Σezj, ELU, Gaussian.
  • Bade z pe ez overflow kar sakta hai (float32 me e89 ≈ inf) — isliye libraries softmax/BCE ko "stable" tareeke se nikalti hain (Ch 11).

Dimaag me aise baitha lo

e = bank ka "har pal byaaj" wala ₹2.718. ex kabhi negative nahi hota.

2.8 Natural log (ln) — e ka ulta, aur guna ko jod me badalne ki machine

Kahani

ex poochta hai: "e ko x power do to kya milega?" ln ulta poochta hai: "e ko kitni power doon ki ye number mile?" Jaise 10 ka log poochta hai "10 ko kitni baar guna karun ki 1000 mile?" → 3. Log bade bade numbers ko chhota kar deta hai — isiliye bhookamp ka Richter scale aur awaaz ka decibel log pe chalte hain.

ln x = y  ⇔  ey = x     (sirf x > 0 ke liye)
  • ln 1 = 0 (kyunki e0 = 1), ln e = 1
  • ln(ab) = ln a + ln b — guna, jod ban gaya
  • ln(a/b) = ln a − ln b — bhaag, ghatao ban gaya
  • ln(ak) = k ln a — power, guna ban gaya
  • ln(ex) = x aur eln x = x (dono ek doosre ko cancel)
  • 0 < x < 1 → ln x negative. x → 0 → ln x → −∞. ln 0 aur ln(negative) defined nahi

Chalo numbers se samjhein

  1. ln 2 ≈ 0.6931, ln 5 ≈ 1.6094, ln 8 ≈ 2.0794, ln 10 ≈ 2.3026
  2. Product rule: ln(2 × 5) = ln 10 = 2.3026 = 0.6931 + 1.6094 ✓
  3. Quotient rule: ln(8/2) = ln 4 = 2.0794 − 0.6931 = 1.3863 ✓
  4. Power rule: ln 8 = ln 2³ = 3 × 0.6931 = 2.0794 ✓
  5. Probabilities (0 se 1 ke beech) ka log negative: ln 0.5 ≈ −0.6931, ln 0.1 ≈ −2.3026, ln 0.01 ≈ −4.6052
x 1 2 3 4 5 1 −1 −2 ln 1 = 0 ln e = 1 ln 0.5 ≈ −0.693 0 < x < 1 → ln x negative x → 0 → ln x → −∞ ln x
ln x: 1 pe zero, 1 se chhote numbers pe negative (laal zone), 0 ke paas −∞ ki taraf. Dheere dheere badhta hai.

Log ki asli superpower: guna → jod

Machine learning me hum bahut saari probabilities ka guna karte hain (Chapter 11, MLE). Teen independent cheezon ki probability 0.9, 0.8, 0.7: 0.9 × 0.8 × 0.7 = 0.504. Log lo: ln 0.9 + ln 0.8 + ln 0.7 = −0.1054 − 0.2231 − 0.3567 = −0.6852 = ln 0.504 ✓. Same jawab, bas guna ki jagah jod.

Ye kyun zaroori? 1000 probabilities (har ek 0.5) ka guna = 0.5¹⁰⁰⁰ ≈ 9.33 × 10⁻³⁰² — float64 bas bacha, aur float32 me to 0.5²⁰⁰ hi 0 ban jaata hai (underflow). Log me: 1000 × ln 0.5 = −693.15 — aaraam se store. Aur kyunki ln hamesha badhne wala (monotonic) function hai, jo cheez product ko maximize kare, wahi log-product ko bhi maximize karegi. Isi trick se MSE aur Cross-Entropy nikalte hain (Ch 11).

Ek aur intuition: −ln p ko "surprise" samjho. p = 1 → 0, p = 0.9 → 0.1054, p = 0.5 → 0.6931, p = 0.1 → 2.3026. Sahi answer ko model ne kam probability di → zyada surprise → zyada loss. Yahi cross-entropy hai.

PyTorch · Lab 0 · torch.log (ye ln hai!)

import math
print(torch.log(torch.tensor([1.0, math.e, 0.5, 0.1, 10.0])))
print(torch.log(f))                               # Lab 0: ln 5, ln 8, ln 2
print(torch.log(torch.tensor([0.0, -1.0])))       # ln 0 = -inf, ln(negative) = nan
print(torch.log10(torch.tensor(100.0)), torch.log2(torch.tensor(8.0)))  # base 10, base 2 alag functions

p = torch.tensor([0.9, 0.8, 0.7])
print(torch.prod(p), torch.log(torch.prod(p)), torch.log(p).sum())   # guna ka log = log ka jod
print(torch.tensor(0.5) ** 200)                   # float32 underflow -> 0

Output

tensor([ 0.0000,  1.0000, -0.6931, -2.3026,  2.3026])
tensor([[1.6094, 2.0794, 0.6931],
        [1.6094, 1.6094, 0.6931]])
tensor([-inf, nan])
tensor(2.) tensor(3.)
tensor(0.5040) tensor(-0.6852) tensor(-0.6852)
tensor(0.)

Exam Radar

  • torch.log / np.log = natural log (base e), base 10 nahi. Base 10 ke liye log10.
  • ln 1 = 0; ln e = 1; 0 < x < 1 → negative; ln 0 = −∞ (-inf); ln(−1) = nan.
  • Isliye cross-entropy me −ln p likhte hain (minus lagake positive banaya). Agar model sahi class ko p = 0 de de → loss ∞.
  • ln ke rules: product → sum, quotient → difference, power → multiply.

Galti mat karna

  • "log 10 = 1" sirf base 10 me. Natural log me ln 10 ≈ 2.3026.
  • ln(a + b) ≠ ln a + ln b. Rule sirf guna ke liye hai.

Dimaag me aise baitha lo

ln = "e ko kitni power?". Guna ko jod banata hai. 1 se chhota → negative.

2.9 Derivative = slope = rate of change (speedometer)

Kahani

Papa Delhi se Agra 240 km 4 ghante me gaye. Average speed 60 km/h. Par gaadi ka speedometer har pal alag dikhata hai — kabhi 80, kabhi 20 (traffic). Speedometer "isi pal" distance kitni tezi se badal raha hai, wo batata hai. Wahi hai derivative: kisi cheez ke badalne ki instant speed.

Definition

Derivative f′(x) (ya df/dx) = x ko bahut thoda sa badalne pe f(x) kitna badalta hai, uska ratio. Graph pe: us point pe curve ko sirf chhoone wali line (tangent) ka slope.

Sign ka matlab: f′ > 0 → function badh raha (chadhai), f′ < 0 → ghat raha (dhalaan), f′ = 0 → flat (choti ya ghaati). Size ka matlab: |f′| bada → bahut steep, chhota → lagbhag flat.

f′(x) = dfdx = limh→0 f(x + h) − f(x)h
  • h = chhota sa kadam; f(x+h) − f(x) = output me badlav (rise); h = input me badlav (run)
  • Ye bas rise/run hai (2.6 wala slope), par itne chhote kadam pe ki curve bhi seedhi line jaisi lagne lage
  • limh→0 = "h ko 0 ke itna paas le jao jitna chaho"

Chalo numbers se samjhein

Sawaal: f(x) = x² ka slope x = 3 pe?

  1. h = 0.1: (3.1² − 3²)/0.1 = (9.61 − 9)/0.1 = 6.1
  2. h = 0.01: (9.0601 − 9)/0.01 = 6.01
  3. h = 0.001: (9.006001 − 9)/0.001 = 6.001 → 6 ki taraf ja raha
  4. Rule (2.10) se: d/dx x² = 2x = 2 × 3 = 6 ✓
  5. Matlab: x = 3 ke paas x ko 0.01 badhao to f lagbhag 6 × 0.01 = 0.06 badhega.

Jawab: f′(3) = 6.

x −2 1 2 f(x) = x² run = 1 rise = 3 tangent at x = 1.5: slope = 2 × 1.5 = 3 slope = 0 (bottom) yahan slope −ve
Laal line curve ko x = 1.5 pe sirf chhooti hai — uska slope 3 hi derivative hai. Sabse neeche (x = 0) tangent flat: slope 0.

Python · derivative ko numbers se pakadna

def f(x):
    return x ** 2

for h in [0.1, 0.01, 0.001]:
    slope = (f(3 + h) - f(3)) / h        # (naya - purana) / chhota kadam
    print(f"h={h}: slope ~ {slope:.4f}")

Output

h=0.1: slope ~ 6.1000
h=0.01: slope ~ 6.0100
h=0.001: slope ~ 6.0010

Neural network me sawaal hota hai: "agar ek weight thoda badhaun, to loss kitna badlega?" Uska jawab = loss ka us weight ke respect me derivative. Yahi gradient descent ka khana hai.

Exam Radar

  • Derivative = instantaneous rate of change = tangent ka slope.
  • f′ > 0 increasing, f′ < 0 decreasing, f′ = 0 flat (minimum/maximum/saddle ka candidate).
  • Numerical derivative (finite difference) gradient check karne ke kaam aata hai; training me PyTorch autograd exact derivative deta hai.

Dimaag me aise baitha lo

Derivative = speedometer: "abhi is pal kitni tezi se badal raha hai".

2.10 Derivative ke rules — har ek ka example

Kahani

Har baar limit wala formula lagana waise hi hai jaise har baar 17 × 23 ungliyon pe ginna. Isliye pahaade (tables) ratte jaate hain. Derivative ke bhi "pahaade" hain — ye 8 rules. Inse poore course ke saare derivatives (sigmoid, tanh, MSE, cross-entropy) nikal jaate hain.

#RuleFormulaExampleNumber pe
1Constantd/dx (c) = 0d/dx 7 = 0flat line, koi badlav nahi
2Powerd/dx xn = n xn−1d/dx x³ = 3x²x = 2 → 12
3Constant multipled/dx [c·f] = c·f′d/dx 5x² = 10xx = 3 → 30
4Sum(f + g)′ = f′ + g′d/dx (x² + 3x + 4) = 2x + 3x = 2 → 7
5Exponentiald/dx ex = exkhud hi apna derivative!x = 1 → 2.7183
6Logd/dx ln x = 1/xd/dx ln x at 41/4 = 0.25
7Product(fg)′ = f′g + fg′d/dx [x²ex] = 2xex + x²exx = 1 → 3e ≈ 8.1548
8Quotient(f/g)′ = (f′g − fg′)/g²d/dx [x/(x+1)] = 1/(x+1)²x = 1 → 0.25

Chalo numbers se samjhein (product aur quotient step by step)

  1. Product: f = x², g = ex. f′ = 2x, g′ = eˣ. To (fg)′ = 2x·eˣ + x²·eˣ. x = 1 pe: 2·e + 1·e = 3e = 3 × 2.7183 ≈ 8.1548
  2. Quotient: f = x, g = x + 1. f′ = 1, g′ = 1. To (1·(x+1) − x·1)/(x+1)² = 1/(x+1)². x = 1 pe: 1/2² = 1/4 = 0.25
  3. Power rule ke chhote bhai: d/dx x = 1 (n = 1), d/dx (1/x) = d/dx x⁻¹ = −1/x² (n = −1), d/dx √x = d/dx x^½ = 1/(2√x)

Python · saare rules ko numbers se verify karo

import math

def d(func, x, h=1e-5):                  # central difference: dono taraf thoda chalo
    return (func(x + h) - func(x - h)) / (2 * h)

checks = [
    ("constant 7",      lambda x: 7,                 2, 0),
    ("power x^3",       lambda x: x**3,              2, 3 * 2**2),
    ("5x^2",            lambda x: 5 * x**2,          3, 10 * 3),
    ("x^2+3x+4",        lambda x: x**2 + 3*x + 4,    2, 2*2 + 3),
    ("e^x",             math.exp,                    1, math.exp(1)),
    ("ln x",            math.log,                    4, 1 / 4),
    ("x^2 * e^x",       lambda x: x**2 * math.exp(x), 1, 3 * math.e),
    ("x/(x+1)",         lambda x: x / (x + 1),       1, 1 / (1 + 1)**2),
]
for name, func, x, rule in checks:
    print(f"{name:12s} at x={x}: numeric={d(func, x):.4f}  rule={rule:.4f}")

Output

constant 7   at x=2: numeric=0.0000  rule=0.0000
power x^3    at x=2: numeric=12.0000  rule=12.0000
5x^2         at x=3: numeric=30.0000  rule=30.0000
x^2+3x+4     at x=2: numeric=7.0000  rule=7.0000
e^x          at x=1: numeric=2.7183  rule=2.7183
ln x         at x=4: numeric=0.2500  rule=0.2500
x^2 * e^x    at x=1: numeric=8.1548  rule=8.1548
x/(x+1)      at x=1: numeric=0.2500  rule=0.2500

Exam Radar

  • Ye derivatives seedhe MCQ me aate hain: (xn)′ = nxn−1, (ex)′ = ex, (ln x)′ = 1/x, constant ka 0.
  • Quotient rule + chain rule se sigmoid ka derivative nikalta hai: σ′(z) = σ(z)(1 − σ(z)) — poori derivation Chapter 9 me.
  • MSE ke ek term (y − ŷ)² ka ŷ ke respect me derivative = −2(y − ŷ) (chain rule, next section).

Galti mat karna

  • (fg)′ ≠ f′g′. Product rule me do terms hote hain.
  • Quotient rule me order: f′g minus fg′ (ulta kiya to sign ulta).

Dimaag me aise baitha lo

Power: power aage, ek kam. ex: khud hi. ln: ulta (1/x). Constant: zero.

2.11 Chain rule — gears ki kahani (backprop ki jaan)

Kahani

Cycle ke gears socho. Paddle wala gear A ek chakkar ghoomta hai to beech wala gear B 3 chakkar ghoomta hai. Aur B ke har chakkar pe pahiya C 2 chakkar ghoomta hai. Paddle ke ek chakkar pe pahiya kitna ghooma? 3 × 2 = 6 chakkar. Rates chain me guna ho jaate hain. Bas yahi chain rule hai.

agar y = f(u) aur u = g(x), to   dydx = dydu · dudx
  • u = beech wala gear (andar wala function), y = bahar wala
  • dy/du = bahar wale ka derivative (andar ko jaisa hai waisa rakh ke), du/dx = andar wale ka derivative
  • Lambi chain ho to sab local derivatives ka guna: dy/dx = (dy/du)(du/dv)(dv/dx) …

Chalo numbers se samjhein

Sawaal: y = (3x + 1)², dy/dx at x = 1?

  1. Andar wala: u = 3x + 1 → du/dx = 3
  2. Bahar wala: y = u² → dy/du = 2u
  3. Guna: dy/dx = 2u × 3 = 6u = 6(3x + 1)
  4. x = 1: u = 3(1) + 1 = 4 → dy/dx = 6 × 4 = 24
  5. Check numbers se: (3×1.001 + 1)² = 4.003² = 16.024009; (16.024009 − 16)/0.001 ≈ 24.009 ✓
  6. Do aur: d/dx e⁻ˣ = e⁻ˣ × (−1) = −e⁻ˣ (x = 0 pe −1). d/dx ln(1 + x²) = (1/(1+x²)) × 2x (x = 1 pe 2/2 = 1).

Jawab: 24.

x × 3, + 1 u square ( )² y du/dx = 3 dy/du = 2u forward: x se y tak values jaati hain → ← backward: dy/dx = 2u × 3 = 6(3x + 1) x = 1 → u = 4 → dy/dx = 24
Har dabba apna chhota (local) derivative jaanta hai. Peeche jaate hue sab guna karo — yahi backpropagation ka beej hai.

Neural network = functions ki lambi chain: input → linear → activation → linear → activation → … → loss. Loss ka kisi pehli layer ke weight ke respect me derivative chahiye? Chain rule se, peeche se aage tak local derivatives guna karte jao. Isi ko backpropagation kehte hain (Chapter 12, Chapter 13). Aur agar chain me bahut saare chhote numbers (jaise 0.25) guna hon to answer gayab ho jaata hai — vanishing gradient — ye bhi isi guna ka nateeja hai.

PyTorch · sneak peek: autograd khud chain rule lagata hai (detail Ch 12)

x = torch.tensor(1.0, requires_grad=True)   # "is x ka derivative chahiye" - bata diya
y = (3 * x + 1) ** 2                         # forward: PyTorch graph yaad rakhta hai
y.backward()                                 # backward: chain rule apne aap
print(x.grad)                                # dy/dx at x = 1

Output

tensor(24.)

Exam Radar

  • dy/dx = (dy/du)(du/dx). Backprop = chain rule applied repeatedly, output se input ki taraf.
  • Bahut saare < 1 local derivatives ka guna → vanishing gradient; > 1 ka guna → exploding gradient (Ch 13).

Galti mat karna

  • Andar wale ka derivative guna karna bhool jaana: d/dx (3x+1)² = 2(3x+1) × 3, sirf 2(3x+1) nahi.

Dimaag me aise baitha lo

Gears: paddle 1 → 3 → 6. Chain me rates guna hote hain.

2.12 Partial derivatives aur Gradient ∇ — bahut saare knobs

Kahani

Music system pe do knobs hain: bass aur treble. Tumhe jaanna hai ki awaaz kis knob se kitni badalti hai. Tum treble ko wahin rakh ke bass ko thoda ghumate ho — awaaz kitni badli? Ye "bass ka partial derivative". Fir bass rok ke treble ghumao — "treble ka partial derivative". Dono ko ek list me likh do — ye gradient hai.

∇f(w, b) = ( ∂f∂w , ∂f∂b )
  • ∂ ("del" / "partial") = baaki sab variables ko constant maan ke sirf ek variable ka derivative
  • ∇f ("nabla f", "grad f") = saare partial derivatives ka vector. Jitne knobs, utne numbers
  • ∇f us direction me point karta hai jidhar f sabse tezi se badhta hai. To −∇f = sabse tez dhalaan

Chalo numbers se samjhein

Sawaal: f(w, b) = (2w + b − 5)². (w, b) = (1, 1) pe ∇f nikalo.

  1. Andar wala naam: u = 2w + b − 5 = 2(1) + 1 − 5 = −2. f = u² = 4
  2. ∂f/∂w: b ko constant maano. Chain rule: ∂f/∂w = 2u × ∂u/∂w = 2u × 2 = 4u = 4(−2) = −8
  3. ∂f/∂b: w ko constant maano. ∂f/∂b = 2u × ∂u/∂b = 2u × 1 = 2u = −4
  4. Gradient: ∇f(1, 1) = (−8, −4)
  5. Check: f(1.001, 1) = (2.002 + 1 − 5)² = (−1.998)² = 3.992004 → (3.992004 − 4)/0.001 = −7.996 ≈ −8 ✓

Jawab: ∇f = (−8, −4). Dono negative → w aur b badhane se f ghatega. w ka number bada (−8) kyunki w ke saath 2 guna ho raha hai — wo knob zyada "sensitive" hai.

Chhupa raaz: ye f asal me ek neuron ki galti hai! Neuron ŷ = w·x + b, input x = 2, sahi answer y = 5. Galti² = (ŷ − y)² = (2w + b − 5)². To ∇f batata hai ki dono knobs ko kis taraf ghumayein ki galti kam ho. Raju ki chai machine (1.3) bilkul yahi karti hai.

PyTorch · do knobs ka gradient (autograd sneak peek)

w = torch.tensor(1.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)
f = (2 * w + b - 5) ** 2          # ek neuron ki squared error (x=2, y=5)
f.backward()                      # saare partial derivatives ek saath
print(f.item(), w.grad, b.grad)   # f, df/dw, df/db

Output

4.0 tensor(-8.) tensor(-4.)

Exam Radar

  • Partial derivative = baaki variables constant. Gradient = partials ka vector; size = parameters ki ginti.
  • Gradient = steepest ascent (chadhai) ki direction; minimize karne ke liye −gradient (steepest descent).
  • Network ke har weight ka ek partial derivative hota hai; PyTorch use param.grad me rakhta hai.

Dimaag me aise baitha lo

Ek knob ghumao, baaki pakdo = partial. Saare partials ki list = gradient.

2.13 Minimum kahan hai? Slope zero — aur "slope ke ulte chalo" (gradient descent ka trailer)

Kahani

Raat ka andhera, tum ghat (pahaadi) pe ho, gaon neeche ghaati me hai. Kuch dikh nahi raha, bas pairon se zameen ki dhalaan mehsoos ho rahi hai. Zameen daayein taraf neeche ja rahi hai? Daayein kadam lo. Baayein taraf neeche? Baayein. Har kadam pe yahi karo. Jab zameen bilkul flat lage — tum ghaati me ho. Ye gradient descent hai.

Function ke sabse neeche wale point (minimum) pe tangent flat hota hai: f′ = 0. Minimum ki taraf jaane ka rule: agar slope positive hai (daayein chadhai), to baayein jao (x ghatao). Agar slope negative hai, to daayein jao (x badhao). Dono cases ek formula me: x me se slope ka thoda hissa ghata do.

xnew = xold − η · f′(xold)     bahut knobs: θ ← θ − η ∇f(θ)
  • η ("eta") = learning rate = kadam kitna bada. Hyperparameter (1.9)
  • Minus sign = "slope ke ulte chalo" (kyunki slope chadhai ki taraf point karta hai)
  • θ = saare parameters (w, b, …) ek saath. Lab 5 ki math: θ ← θ − η · ∇θL, η = 0.1

Chalo numbers se samjhein

Sawaal 1: f(x) = x², f′ = 2x. Start x = 3, η = 0.1.

  1. Step 1: slope = 2 × 3 = 6 → x = 3 − 0.1 × 6 = 2.4, f = 5.76
  2. Step 2: slope = 4.8 → x = 2.4 − 0.48 = 1.92, f = 3.6864
  3. Step 3: slope = 3.84 → x = 1.92 − 0.384 = 1.536, f = 2.3593
  4. Har step x ← x − 0.2x = 0.8x — x 0 ki taraf, aur jaise jaise neeche aate, slope chhota → kadam apne aap chhote.

Sawaal 2 (do knobs): f(w, b) = (2w + b − 5)², start (1, 1), η = 0.05, ∇f = (4u, 2u).

  1. Step 1: ∇f = (−8, −4) → w = 1 − 0.05(−8) = 1.4, b = 1 − 0.05(−4) = 1.2. Ab u = 2.8 + 1.2 − 5 = −1, f = 1
  2. Step 2: ∇f = (−4, −2) → w = 1.6, b = 1.3, u = −0.5, f = 0.25
  3. Step 3: ∇f = (−2, −1) → w = 1.7, b = 1.35, u = −0.25, f = 0.0625

Sawaal 3 (kadam bahut bada): x2 pe η = 1.1: x ← x − 2.2x = −1.2x: 3 → −3.6 → 4.32 → −5.184 … ghaati ke aar-paar uchhal ke upar ja raha — diverge!

Jawab: sahi η pe loss 4 → 1 → 0.25 → 0.0625 (har baar ¼). Bahut bada η → ulta bhaag jaata hai. (Learning rate ki poori kahani Chapter 15.)

x = 3 2.4 1.92 1.536 slope +ve → baayein jao slope −ve → daayein jao minimum: slope = 0
Laal gend x = 3 se shuru, har kadam slope ke ulte. Neeche aate aate slope chhota, isliye kadam bhi chhote.

Python · gradient descent ka pehla loop

x = 3.0          # shuruaat
lr = 0.1         # eta: kadam ka size (learning rate)
for step in range(5):
    grad = 2 * x                 # f(x) = x^2 ka slope
    print(f"step {step}: x={x:.4f}, f(x)={x*x:.4f}, slope={grad:.4f}")
    x = x - lr * grad            # slope ke ULTE chalo

Output

step 0: x=3.0000, f(x)=9.0000, slope=6.0000
step 1: x=2.4000, f(x)=5.7600, slope=4.8000
step 2: x=1.9200, f(x)=3.6864, slope=3.8400
step 3: x=1.5360, f(x)=2.3593, slope=3.0720
step 4: x=1.2288, f(x)=1.5099, slope=2.4576

Python · do knobs ek saath (wahi 2.12 wala f)

w, b = 1.0, 1.0
lr = 0.05
for step in range(4):
    u = 2 * w + b - 5                    # andar wala hissa
    gw, gb = 2 * u * 2, 2 * u * 1        # df/dw, df/db (chain rule)
    print(f"step {step}: w={w:.2f}, b={b:.2f}, f={u*u:.4f}, grad=({gw:.1f}, {gb:.1f})")
    w, b = w - lr * gw, b - lr * gb      # dono knobs ek saath update

Output

step 0: w=1.00, b=1.00, f=4.0000, grad=(-8.0, -4.0)
step 1: w=1.40, b=1.20, f=1.0000, grad=(-4.0, -2.0)
step 2: w=1.60, b=1.30, f=0.2500, grad=(-2.0, -1.0)
step 3: w=1.70, b=1.35, f=0.0625, grad=(-1.0, -0.5)

Yahi loop Lab 5 me optimizer.step() karta hai — bas gradient haath se nahi, loss.backward() se aata hai. Ek note: slope = 0 sirf minimum pe nahi, maximum (choti) aur saddle point (ek taraf se ghaati, doosri taraf se choti — ghode ki kaathi jaisa) pe bhi hota hai. Aur ghaatiyan kai ho sakti hain: sabse gehri = global minimum, baaki = local minima (Ch 14–17).

Exam Radar

  • Update rule: θ ← θ − η ∇L. Minus = slope ke ulte. η = learning rate.
  • Minimum pe gradient = 0. Gradient zero hone ke 3 cases: minimum, maximum, saddle point.
  • η bahut chhota → bahut dheere; η bahut bada → overshoot / diverge (x² pe η > 1 se diverge, η = 1 pe ±3 me jhoolta rehta).

Galti mat karna

  • θ + η∇L likh dena — ye gradient ascent hai (loss badhayega).
  • "Gradient descent hamesha global minimum dhoondhta hai" — nahi, local minimum / saddle pe atak sakta hai.

Dimaag me aise baitha lo

Andhere me ghaati: dhalaan mehsoos karo, ulti taraf chhota kadam, repeat.

2.14 Σ (Sigma) notation — "sab jod do" ka shorthand

Kahani

Mahine ke end me kirana ka bill: doodh + aata + daal + … 30 items. "Item 1 se item 30 tak sabki keemat jodo" — ye lamba sentence math me ek chhote se nishaan se likhte hain: Σ (Greek capital sigma, "S" for Sum).

Σi=1n xi = x1 + x2 + … + xn     ∏i=1n xi = x1 × x2 × … × xn
  • i = counter (index); neeche i = 1 = kahan se shuru; upar n = kahan tak
  • xi = har baar kya jodna hai (counter ke hisaab se)
  • ∏ (capital pi) = sab ka guna. Likelihood (Ch 11) me aata hai; log lene se ∏ → Σ ban jaata hai (2.8)

Chalo numbers se samjhein

  1. Σ (i=1 to 4) i = 1 + 2 + 3 + 4 = 10
  2. Σ (i=1 to 3) i² = 1 + 4 + 9 = 14
  3. Dot product: Σ wᵢxᵢ (2.2). Mean: μ = (1/n) Σ xᵢ.
  4. MSE: y = [3, 5, 2], ŷ = [2.5, 5, 4]. Galtiyan: 0.5, 0, −2. Squares: 0.25, 0, 4. Σ = 4.25. MSE = 4.25 / 3 ≈ 1.4167

Python + PyTorch · Σ = sum()

print(sum(range(1, 5)), sum(i**2 for i in range(1, 4)))   # range(1, 5) = 1,2,3,4 (5 nahi!)

y_true = torch.tensor([3., 5., 2.])
y_pred = torch.tensor([2.5, 5., 4.])
print(torch.sum((y_true - y_pred) ** 2))    # Sigma (y - y_hat)^2
print(torch.mean((y_true - y_pred) ** 2))   # (1/n) Sigma ... = MSE

Output

10 14
tensor(4.2500)
tensor(1.4167)

Exam Radar

  • Math me index aksar 1 se n; Python/PyTorch me 0 se n−1. range(1, 5) me 5 shaamil nahi.
  • Cross-entropy me double sum aata hai: Σsamples Σclasses — andar wala pehle (Ch 11).

Dimaag me aise baitha lo

Σ = kirana bill ka total. ∏ = sabka guna.

2.15 Mean, Variance, Standard deviation — average aur "kitna bikhra hua"

Kahani

Do classes ka average 50 marks hai. Class A me sabke 48 se 52 ke beech aaye. Class B me aadhe bachchon ke 0 aur aadhe ke 100. Average same, par dono classes bilkul alag hain! Average (mean) "beech" batata hai, aur variance / standard deviation batata hai ki numbers beech se kitna bikhre hue hain.

μ = 1n Σ xi    σ² = 1n Σ (xi − μ)²    s² = 1n − 1 Σ (xi − x̄)²    σ = √σ²
  • μ (mu) = mean = sabka jod ÷ ginti
  • (xi − μ) = har number ki mean se doori (deviation). Square isliye ki + aur − cancel na hon
  • σ² = population variance (1/n): jab poora data tumhare paas ho
  • s² = sample variance (1/(n−1), "Bessel's correction"): jab data badi population ka chhota sample ho. Mean bhi isi sample se nikala, to deviations thode chhote aate hain; n−1 se bhaag dekar wo kami poori karte hain (unbiased estimate)
  • σ = standard deviation = √variance — wapas original units me (marks², nahi marks)

Chalo numbers se samjhein

Sawaal: data = [2, 4, 4, 4, 5, 5, 7, 9]. Mean, dono variance, dono std nikalo.

  1. n = 8, Σ = 2+4+4+4+5+5+7+9 = 40, μ = 40/8 = 5
  2. Deviations: −3, −1, −1, −1, 0, 0, 2, 4
  3. Squares: 9, 1, 1, 1, 0, 0, 4, 16 → Σ = 32
  4. Population: σ² = 32/8 = 4, σ = √4 = 2
  5. Sample: s² = 32/7 ≈ 4.5714, s = √4.5714 ≈ 2.1381
  6. Bonus z-score (1.8 wala StandardScaler): 9 ka (9 − 5)/2 = 2 → "9 mean se 2 standard deviation upar hai".

Jawab: μ = 5; population σ² = 4, σ = 2; sample s² ≈ 4.5714, s ≈ 2.1381.

NumPy vs PyTorch · default ALAG hai (coding trap!)

import numpy as np
marks = [2., 4., 4., 4., 5., 5., 7., 9.]
a = np.array(marks)
t = torch.tensor(marks)

print(np.mean(a), np.var(a), np.std(a))       # numpy default: 1/n (population)
print(np.var(a, ddof=1), np.std(a, ddof=1))   # ddof=1 -> 1/(n-1)
print(t.mean(), t.var(), t.std())             # torch default: 1/(n-1) (sample)!
print(t.var(unbiased=False), t.std(unbiased=False))   # torch me 1/n (naya naam: correction=0)

Output

5.0 4.0 2.0
4.571428571428571 2.138089935299395
tensor(5.) tensor(4.5714) tensor(2.1381)
tensor(4.) tensor(2.)

Exam Radar

  • Population variance ÷ n; sample variance ÷ (n − 1).
  • np.var/np.std default = population (ddof=0); torch.var/torch.std default = sample (unbiased, n−1). StandardScaler population std use karta hai.
  • BatchNorm (Ch 20) training me batch ka mean aur biased (1/n) variance use karke normalize karta hai.
  • Weight init (Ch 21) poora variance ka khel hai: Xavier Var(W) = 2/(nin + nout), He Var(W) = 2/nin.

Galti mat karna

  • Variance ko std samajh lena. Std = √variance. Variance 4 → std 2.
  • Deviations ka seedha jod (bina square) — wo hamesha 0 aata hai! (−3−1−1−1+0+0+2+4 = 0)

Dimaag me aise baitha lo

Mean = beech. Std = beech se aam taur pe kitni doori. Sample me n−1, numpy n, torch n−1.

2.16 Normal (Gaussian) aur Uniform distribution — weights kahan se aate hain

Kahani

College ke saare students ki height naapo. Zyada tar log average (lagbhag 5'6") ke aas paas, kuch thode chhote, kuch thode lambe, aur bahut kam log bahut chhote ya bahut lambe. Graph banao to ghante (bell) jaisi shape banti hai — ye normal distribution.

Ab Ludo ka paasa phenko: 1 se 6, sab ki chance barabar. Graph ekdum flat — ye uniform distribution.

Neural network banate waqt shuru me weights random rakhte hain — ya to bell curve se nikaal ke, ya flat range se. Isiliye ye dono jaanna zaroori hai (Chapter 21).

Normal 𝒩(μ, σ²):   p(x) = 1σ√2π e−(x − μ)² / (2σ²)     Uniform U(a, b):   p(x) = 1b − a
  • μ = beech (peak yahan), σ = chaudai (σ bada → bell chaudi aur neechi). 𝒩(0, 1) = standard normal
  • e−(x−μ)²/(2σ²) = beech se door jaate hi tezi se girta hai (2.7 wala e−x); aage wala 1/(σ√2π) bas area ko 1 banata hai
  • 𝒩(0,1) ka peak: 1/√(2π) ≈ 0.3989
  • 68–95–99.7 rule: μ ± 1σ me ≈ 68.27%, ± 2σ me ≈ 95.45%, ± 3σ me ≈ 99.73% data
  • Uniform U(a, b): a se b tak har value barabar; mean = (a+b)/2, variance = (b−a)²/12

Chalo numbers se samjhein

  1. U(−1, 1): height 1/(1 − (−1)) = 1/2 = 0.5; mean (−1 + 1)/2 = 0; variance (1 − (−1))²/12 = 4/12 ≈ 0.3333
  2. Xavier uniform (Ch 21) preview: U(−a, a) with a = √(6/(n_in + n_out)). nin = 3, nout = 2: a = √(6/5) ≈ 1.0954, variance a²/3 = 1.2/3 = 0.4 = 2/(3+2) ✓
  3. Heights ~ 𝒩(165 cm, 10²): 68% log 155 se 175 cm ke beech, 95% log 145 se 185 cm ke beech.
−3 −2 −1 0 1 2 3 68.27% (μ ± 1σ) peak ≈ 0.399 𝒩(0, 1) — randn U(−1, 1): flat, height 0.5 ±2σ me 95.45% ±3σ me 99.73%
Kaali ghanti = standard normal (beech me zyada, kinaaron pe kam). Narangi dabba = U(−1, 1) (sab barabar). Peela hissa = ±1σ = 68%.

PyTorch · randn = normal, rand = uniform [0, 1)

torch.manual_seed(0)                      # har baar same random numbers
z = torch.randn(100000)                   # N(0, 1) se 1 lakh numbers
print(z.mean(), z.std())                  # ~0 aur ~1
print(((z > -1) & (z < 1)).float().mean(), ((z > -2) & (z < 2)).float().mean())  # ~68%, ~95%

u = torch.rand(100000) * 2 - 1            # [0,1) ko [-1,1) me khinch ke khiskaya: a + (b-a)*rand
print(u.min(), u.max(), u.mean(), u.var())   # var ~ 1/3

h = 5 + 2 * torch.randn(100000)           # mu + sigma * randn  ->  N(5, 2^2)
print(h.mean(), h.std())

Output

tensor(-0.0024) tensor(1.0012)
tensor(0.6811) tensor(0.9547)
tensor(-1.0000) tensor(1.0000) tensor(0.0027) tensor(0.3342)
tensor(4.9919) tensor(2.0003)

Exam Radar

  • torch.randn → standard normal 𝒩(0, 1). torch.rand → uniform [0, 1). torch.randint(low, high, size) → integers, high exclusive.
  • 𝒩(μ, σ²) banane ke liye: μ + σ·randn. U(a, b): a + (b−a)·rand.
  • 68–95–99.7 rule. Uniform variance = (b−a)²/12.
  • Init functions: nn.init.normal_, nn.init.uniform_, xavier_uniform_, kaiming_normal_ (He) — Ch 21.

Galti mat karna

  • rand aur randn me confusion: "n" = normal. rand ke numbers kabhi negative nahi; randn ke ho sakte hain.
  • 𝒩(0, 0.01) me 0.01 variance hai (std 0.1). Kai jagah σ likhte hain, kai jagah σ² — dhyaan se padho.

Dimaag me aise baitha lo

Normal = heights ki ghanti (randn). Uniform = Ludo ka paasa (rand).

2.17 Probability ki basics — toss, baarish, aur Bernoulli coin

Kahani

Cricket match ka toss: heads ki chance 1/2. Do matches me dono baar heads? 1/2 × 1/2 = 1/4 — kyunki ek toss doosre ko affect nahi karta. Ab baarish: kisi bhi din baarish ki chance kam hai, par agar aasmaan me kaale baadal hain to chance kaafi badh jaati hai. "Agar … diya hua hai to" — ye conditional probability hai. Classifier bhi yahi bolta hai: "agar is patient ke ye 30 features hain, to malignant hone ki probability 0.83".

0 ≤ P(A) ≤ 1    Σ P(sab outcomes) = 1    P(not A) = 1 − P(A)    P(A and B) = P(A)·P(B) [independent]    P(A | B) = P(A and B)P(B)
  • P = probability: 0 = kabhi nahi, 1 = pakka
  • Saare possible outcomes ki probabilities ka jod hamesha 1 (softmax output isi liye jod ke 1 hota hai)
  • Independent events (ek doosre pe asar nahi) → probabilities guna. Yahi data likelihood ka product hai (Ch 11)
  • P(A | B) = "B ho chuka hai, to A ki probability" — B ki duniya me A ka hissa. ML me P(y | x) = input diya hua hai, label ki probability

Chalo numbers se samjhein

  1. Do paase, dono pe 6: 1/6 × 1/6 = 1/36 ≈ 0.0278
  2. 100 din: 30 din baadal, unme se 20 din baarish. P(baarish | baadal) = 20/30 ≈ 0.6667. Formula se: P(baarish and baadal) = 0.20, P(baadal) = 0.30 → 0.20/0.30 ≈ 0.6667 ✓
  3. Softmax output [0.1, 0.7, 0.2]: 0.1 + 0.7 + 0.2 = 1 ✓ — valid probability distribution.

Bernoulli — ek sikka, do nateeje

Jis experiment ke sirf do nateeje hon (1 ya 0: heads/tails, malignant/benign, pass/fail) use Bernoulli trial kehte hain. P(y = 1) = p, to P(y = 0) = 1 − p. Dono ko ek hi formula me likhne ka jugaad:

P(y) = py (1 − p)1 − y,    y ∈ {0, 1}
  • y = 1 rakho: p1(1−p)0 = p ✓. y = 0 rakho: p0(1−p)1 = 1 − p ✓ (kyunki kuch bhi0 = 1)
  • Log lo: ln P(y) = y ln p + (1−y) ln(1−p). Iske aage minus lagao → Binary Cross-Entropy ka ek term! (Lab 5 ka BCE formula yahi hai)

Chalo numbers se samjhein

Sawaal: model har sample ko p = 0.8 deta hai. Asli labels [1, 1, 0]. Likelihood, log-likelihood, aur average negative log-likelihood?

  1. Har sample: P(1) = 0.8, P(1) = 0.8, P(0) = 1 − 0.8 = 0.2
  2. Independent → guna: 0.8 × 0.8 × 0.2 = 0.128 (likelihood)
  3. Log: 2 ln 0.8 + ln 0.2 = 2(−0.2231) + (−1.6094) = −2.0557 = ln 0.128 ✓
  4. Average NLL: 2.0557 / 3 ≈ 0.6852

Jawab: likelihood 0.128, log-likelihood −2.0557, average NLL ≈ 0.6852 — aur nn.BCELoss bhi exactly yahi deta hai.

PyTorch · BCE = average negative log-likelihood (Ch 11 preview)

bce = nn.BCELoss()                              # Lab 5 wala loss
p = torch.tensor([0.8, 0.8, 0.8])               # model ki P(y=1)
y = torch.tensor([1., 1., 0.])                  # asli labels
print(bce(p, y))                                # mean of -[y ln p + (1-y) ln(1-p)]

Output

tensor(0.6852)

Exam Radar

  • Probabilities 0 se 1; saare outcomes ka jod 1; independent → guna; P(A|B) = P(A∩B)/P(B).
  • Binary classifier ka output = P(y = 1 | x). Bernoulli: py(1−p)1−y.
  • Maximize likelihood ⇔ minimize negative log-likelihood ⇔ minimize cross-entropy (Ch 11).

Dimaag me aise baitha lo

Toss guna hote hain, log unhe jodta hai, minus lagao to loss ban jaata hai.

2.18 max vs argmax — "sabse zyada kitna" vs "sabse zyada kaun"

Kahani

Result board pe do sawaal: "Highest marks kitne aaye?" → 98. Ye max (value). "Topper kaun hai?" → roll number 7. Ye argmax (position / kaun). Ek value deta hai, doosra uska pata (index).

maxi xi = sabse badi value     argmaxi xi = wo index i jahan sabse badi value hai
  • "arg" = argument = input/position. argmax = "kis input pe max milta hai"
  • Python/PyTorch me index 0 se shuru hota hai
  • Classification ka final answer = probabilities (ya raw scores) ka argmax

Chalo numbers se samjhein

  1. p = [0.1, 0.7, 0.2] → max = 0.7, argmax = 1 (0-based: position 0 pe 0.1, position 1 pe 0.7)
  2. Raw scores (logits) [1.0, 3.0, 2.0] → softmax [0.0900, 0.6652, 0.2447]. argmax dono ka 1 — softmax order nahi badalta (monotonic), isliye prediction ke liye softmax lagana zaroori nahi.
  3. Tie: [3, 7, 7, 1] → argmax = 1 (pehli wali 7).

PyTorch · max aur argmax (dim wala khel Ch 3.18 me)

pp = torch.tensor([0.1, 0.7, 0.2])
print(torch.max(pp), torch.argmax(pp))          # value vs index

logits = torch.tensor([1.0, 3.0, 2.0])
print(torch.softmax(logits, dim=0), torch.argmax(logits), torch.argmax(torch.softmax(logits, dim=0)))
print(torch.argmax(torch.tensor([3, 7, 7, 1])))  # tie: pehla index

Output

tensor(0.7000) tensor(1)
tensor([0.0900, 0.6652, 0.2447]) tensor(1) tensor(1)
tensor(1)

Exam Radar

  • max → value, argmax → index. Multi-class prediction = argmax(dim=1) over class scores (Ch 3.18).
  • argmax(softmax(z)) = argmax(z).
  • argmax differentiable nahi (chhota badlav → index ya to same ya jump) — isliye training softmax + cross-entropy se hoti hai, argmax sirf prediction/accuracy ke liye.

Dimaag me aise baitha lo

max = "kitne marks?", argmax = "topper kaun?".

Chapter 3

Python + PyTorch ki grammar — code padhna aur likhna seekho

Variables · List · Dict · Loops · if · Functions · Classes & super().__init__() · import · f-string · with · lambda · NumPy · Tensors (Lab 0) · dtype · Broadcasting · @ vs * · dim · view/reshape · Indexing · cat/stack · NumPy↔Torch · Device · requires_grad

Exam ka 30% coding hai, aur saare labs PyTorch me hain. Lekin PyTorch Python ke upar bana hai — jaise Hindi me chitthi likhne se pehle matra aur vyakaran aana chahiye. Is chapter ke pehle hisse (3.1–3.12) me Python ka utna hi vyakaran jitna DL code me dikhta hai, aur doosre hisse (3.13–3.24) me tensors — bilkul Lab 0 ki tarah. Har code chala ke asli output chipkaya hai.

3.1 Variables, types aur operators — Python ki ABCD

Kahani

Maa ki rasoi me dabbe hain, har dabbe pe parchi lagi hai: "cheeni", "chai patti", "namak". Dabbe ke andar cheez = value, parchi = variable ka naam. Python me lr = 0.1 likhne ka matlab: ek dabba banao, us pe parchi "lr" lagao, andar 0.1 rakh do. Dabba kis type ka hai (number, text, haan/na), Python khud samajh leta hai.

TypeKya haiExampleDL me kahan
intpoora numberepochs = 25epochs, batch_size, neurons
floatdashamlav wala numberlr = 0.1learning rate, loss value
strtext"cuda"device naam, column naam
boolTrue / Falseshuffle=Trueflags
None"kuch nahi"best_model = Noneabhi value nahi

Python · types aur operators

age = 20                 # int: poora number
lr = 0.1                 # float: dashamlav wala number
name = "Riya"            # str: text (quotes me)
is_training = True       # bool: True / False (capital T/F)
best_model = None        # None: "abhi kuch nahi hai"
print(type(age), type(lr), type(name), type(is_training), type(best_model))

print(7 / 2, 7 // 2, 7 % 2, 2 ** 3)   # divide, floor-divide, remainder, POWER
print(2 ^ 3)                          # ^ = XOR, power nahi!

Output

<class 'int'> <class 'float'> <class 'str'> <class 'bool'> <class 'NoneType'>
3.5 3 1 8
1

Operators dhyaan se: / hamesha float deta hai (7/2 = 3.5). // = floor division (neeche round: 7//2 = 3). % = remainder (7%2 = 1). ** = power (2**3 = 8). Aur ^ power NAHI hai — ye bitwise XOR hai: 2 = 010, 3 = 011, XOR = 001 = 1. Lab 0 me t1*(2^3) likha tha aur output bilkul t1 jaisa aaya — kyunki 2^3 = 1, to t1×1 = t1!

Python ka sabse bada niyam: indentation (4 spaces) hi block hai. for, if, def, class ke baad colon : aur andar wali lines 4 space aage. Brackets nahi, spaces. # ke baad sab comment.

Exam Radar

  • 2 ** 3 = 8 (power), 2 ^ 3 = 1 (XOR). Lab 0 trap.
  • 7 / 2 = 3.5, 7 // 2 = 3, 7 % 2 = 1.
  • True/False/None capital letter se. Galat indentation → IndentationError.

Dimaag me aise baitha lo

Variable = parchi wala dabba. Power = do sitare **, ek topi ^ nahi.

3.2 List — numbers ki line, ginti 0 se

Kahani

Building ka ground floor "0" hota hai, pehli manzil "1". Python me list ki ginti bhi 0 se: pehla element index 0 pe. Aur peeche se ginna ho to −1 = aakhri, −2 = second last — jaise lift me "top floor" button.

List [ ] me kuch bhi rakh sakte ho, aur baad me badal sakte ho (mutable). Slicing list[start:stop:step]: start shaamil, stop shaamil NAHI. Khaali chhodo to: start = shuru, stop = end, step = 1.

Python · indexing, slicing, append, len

losses = [0.67, 0.54, 0.46, 0.40]   # Lab 5 jaise epoch-wise losses
print(losses[0], losses[-1])        # pehla, aakhri (negative = peeche se)
print(losses[1:3])                  # index 1 se shuru, 3 se PEHLE ruk jao
print(losses[:2], losses[2:], losses[::2])   # shuru se 2 tak, 2 se end tak, har doosra
losses.append(0.36)                 # end me naya element
print(len(losses), losses)
losses[0] = 0.70                    # list badal sakti hai (mutable)
print(losses)

Output

0.67 0.4
[0.54, 0.46]
[0.67, 0.54] [0.46, 0.4] [0.67, 0.46]
5 [0.67, 0.54, 0.46, 0.4, 0.36]
[0.7, 0.54, 0.46, 0.4, 0.36]

Lab 5 me exactly yahi tha: train_losses = [] (khaali list), fir har epoch train_losses.append(loss.item()), aur end me plt.plot(range(1, epochs + 1), train_losses). Network ka architecture bhi list me rakh sakte ho: layers = [30, 16, 8, 1].

Exam Radar

  • x[1:3] → index 1 aur 2 (3 nahi). x[-1] → aakhri. len(x) → kitne elements.
  • Yahi slicing rules tensors pe bhi (3.20).

Dimaag me aise baitha lo

Ground floor = 0. Slice ka stop = darwaza, andar nahi aata.

3.3 Dictionary — naam se dhoondho, position se nahi

Kahani

Phone ki contact list: tum "Mummy" dhoondhte ho, number mil jaata hai. "Contact number 37" koi nahi dhoondhta. Dictionary = key → value ki jodiyan. Key se value nikalo.

Python · dict

params = {"lr": 0.1, "epochs": 25, "optimizer": "SGD"}   # key: value
print(params["lr"])                     # key se value nikalo
params["batch_size"] = 32               # naya key-value jodo
print(params.get("dropout", 0.0))       # key nahi hai? default 0.0 do (error nahi)
for key, value in params.items():       # saare jode ek ek karke
    print(key, "->", value)
print(list(params.keys()))

Output

0.1
0.0
lr -> 0.1
epochs -> 25
optimizer -> SGD
batch_size -> 32
['lr', 'epochs', 'optimizer', 'batch_size']

DL me dict har jagah: hyperparameter config, Optuna ka study.best_params (Ch 22) ek dict hai jaise {'lr': 0.003, 'n_layers': 2}, aur model.state_dict() = layer ke naam → weights tensor ka dict (model save karne ke liye).

Exam Radar

  • Missing key pe d["x"] → KeyError; d.get("x", default) safe hai.
  • .items() → (key, value) jodiyan, loop me sabse common.

3.4 Loops — for, range, enumerate, zip, break

Kahani

Class me teacher attendance leti hain: roll number 1, 2, 3 … ek ek karke, har ek ke saath same kaam ("present?"). Ye for loop hai. Training loop bhi yahi hai: epoch 1, 2, 3 … har baar same kaam (forward, loss, backward, step).

Python · for, range, enumerate, zip, break

for epoch in range(3):                  # 0, 1, 2  (3 shaamil nahi)
    print("epoch", epoch + 1)
print(list(range(1, 6)), list(range(0, 10, 3)))   # (start, stop) aur (start, stop, step)

items = ["chai", "samosa", "biscuit"]
for i, item in enumerate(items):        # index + value dono ek saath
    print(i, item)

prices = [10, 15, 5]
for item, price in zip(items, prices):  # do lists ko jodi me chalao
    print(item, price)

for epoch in range(100):                # break = loop beech me tod do
    if epoch == 2:                      # (early stopping me yahi hota hai, Ch 19)
        print("ruk gaye at", epoch)
        break

Output

epoch 1
epoch 2
epoch 3
[1, 2, 3, 4, 5] [0, 3, 6, 9]
0 chai
1 samosa
2 biscuit
chai 10
samosa 15
biscuit 5
ruk gaye at 2

Dimaag me aise baitha lo

range(3) = 0, 1, 2. Teen baar, par 3 kabhi nahi.

3.5 if / elif / else — traffic signal wala faisla

Kahani

Signal laal → ruko. Warna agar peela → dheere. Warna → chalo. Upar se neeche check hota hai, jo pehli condition sach mili, wahi block chalta hai, baaki skip.

Python · if / elif / else

acc = 0.85
if acc >= 0.9:
    print("Excellent")
elif acc >= 0.7:                        # pehli condition fail hui to ye check
    print("Theek hai")
else:                                   # koi bhi sach nahi to ye
    print("Aur train karo")

gpu_available = False
device = "cuda" if gpu_available else "cpu"   # ek line wala if-else
print(device)
print(3 == 3.0, 3 != 4, (acc > 0.5) and (acc < 0.9), not True)

Output

Theek hai
cpu
True True True False

Ek line wala if-else (conditional expression) — A if condition else B — exactly wahi pattern hai jo har lab me device chunne ke liye aata hai: torch.device('cuda' if torch.cuda.is_available() else 'cpu'). Comparisons: == (barabar? — ek = assignment hai!), !=, <, >=; jodne ke liye and, or, not. None check: if best_model is None:.

Dimaag me aise baitha lo

Laal → peela → hara: pehli sach condition jeet-ti hai.

3.6 Functions — def, return, default arguments

Kahani

Maa ki recipe card: "Input: chawal, paani. Process: 20 min pakao. Output: bhaat." Ek baar card likh do, roz use karo. Function bhi wahi: naam do (def), inputs lo (parameters), kaam karo, jawab wapas do (return).

Python · def, default args, multiple return

def predict(x, w, b=0.0):               # b ka DEFAULT 0.0 hai
    return w * x + b                    # return = function ka jawab

print(predict(10, -2, 100))             # positional: order se
print(predict(10, -2))                  # b nahi diya -> default 0.0
print(predict(x=10, w=-2, b=100))       # keyword: naam se (order matter nahi)

def stats(values):
    m = sum(values) / len(values)
    return m, max(values)               # do cheezein = ek tuple

mean_v, max_v = stats([2, 4, 9])        # unpacking: dono alag variables me
print(mean_v, max_v)

Output

80
-20.0
80
5.0 9

Dimaag me aise baitha lo

def = recipe card. return = plate me khana.

3.7 Classes — class, __init__, self, methods, inheritance, super().__init__()

Kahani

Raju ki chai stall itni chali ki usne franchise shuru ki. Usne ek naksha (blueprint) banaya: har stall me ek owner hoga, ek rate hoga, aur "cups bike" ka register hoga; har stall chai bech sakta hai. Is naksha = class. Naksha se bane asli stalls (Raju ka, Shyam ka) = objects (instances). Har stall ka data apna — Raju ke 5 cup bike to Shyam ke register pe asar nahi.

Jab naya stall khulta hai, ek "opening ceremony" hoti hai jisme owner ka naam, rate set hota hai aur khaali register rakha jaata hai — ye __init__ hai. Stall ke andar har kaam me "mera register, mera rate" bolna padta hai — wo "mera" = self.

Fir Meena ne Masala-chai franchise li. Usko basic stall ki saari cheezein chahiye (owner, rate, register, bechna), plus masala. Wo naksha dobara nahi likhti — basic naksha inherit karti hai. Aur opening ceremony me pehle basic franchise ka standard setup karwati hai (counter, register) — ye super().__init__() hai — fir apni masala wali cheez jodti hai. Agar standard setup bhool gayi, to register hi nahi bana, aur pehli bikri pe hi gadbad!

Definition

class = naksha; object / instance = naksha se bani asli cheez. attribute = object ka data (self.price). method = class ke andar ka function, jiska pehla parameter hamesha self (wo object khud). __init__ = constructor, object bante hi apne aap chalta hai. Inheritance = class Child(Parent): → parent ki saari attributes/methods child ko milti hain; child unhe override (naya version) kar sakta hai. super() = parent class tak pahunchne ka raasta; super().__init__(...) = parent ka constructor chalao.

Python · class, __init__, self, method

class ChaiStall:                                  # blueprint (naksha)
    def __init__(self, owner, price=10):          # constructor: object bante hi chalta hai
        self.owner = owner                        # attribute: is object ki apni cheez
        self.price = price
        self.cups_sold = 0

    def sell(self, n):                            # method: object ka kaam
        self.cups_sold += n                       # apni hi attribute badli
        return n * self.price

raju = ChaiStall("Raju")                          # object (instance) #1
shyam = ChaiStall("Shyam", price=12)              # object #2, alag data
print(raju.sell(5), shyam.sell(5))                # same method, alag jawab
print(raju.cups_sold, raju.owner, shyam.price)

Output

50 60
5 Raju 12

Dhyaan do: raju.sell(5) likhte hain, par method ki definition def sell(self, n) hai. Python khud raju ko self ki jagah bhej deta hai: raju.sell(5) = ChaiStall.sell(raju, 5).

Python · inheritance, super().__init__(), override — aur bhoolne ka nateeja

class MasalaChaiStall(ChaiStall):                 # ChaiStall se INHERIT
    def __init__(self, owner, price=15, masala="elaichi"):
        super().__init__(owner, price)            # pehle parent ka __init__ chalao!
        self.masala = masala                      # fir apni nayi attribute

    def sell(self, n):                            # method OVERRIDE (naya version)
        bill = super().sell(n)                    # parent wala sell bhi use karo
        return bill + 2 * n                       # +2 rupaye masala per cup

m = MasalaChaiStall("Meena")
print(m.sell(4), m.cups_sold, m.masala, m.owner)

class Broken(ChaiStall):
    def __init__(self, owner):
        self.masala = "adrak"                     # super().__init__() BHOOL GAYE

b = Broken("Bunty")
print(b.masala)
try:
    print(b.cups_sold)                            # parent ka setup chala hi nahi
except AttributeError as e:
    print("AttributeError:", e)

Output

68 4 elaichi Meena
adrak
AttributeError: 'Broken' object has no attribute 'cups_sold'

Broken ne apna __init__ likha par parent ka nahi chalaya, to self.cups_sold = 0 wali line kabhi chali hi nahi — register bana hi nahi. Child me __init__ likho to parent ka __init__ khud bulana padta hai.

Ab asli maal: har PyTorch model ek class hai

Lab 5 ka MySimpleNN dekho — ab har line samajh aayegi. class MySimpleNN(nn.Module) = nn.Module (PyTorch ka "basic franchise") se inherit. super().__init__() = nn.Module ka setup chalao — ye andar parameters aur sub-modules ka "register" banata hai. Uske baad self.linear = nn.Linear(...) likhne pe nn.Module chupke se is layer ko register me likh leta hai, aur isi register se model.parameters() saare weights dhoondh ke optimizer ko deta hai. Lab 5 ke words: "MUST be called first — registers the module machinery".

Aur model(x) kaise forward chala deta hai? Python me agar class me __call__ method ho, to object ko function ki tarah bula sakte ho. nn.Module ka __call__ pehle hooks waghera chalata hai, fir tumhara forward. Chhota sa demo:

Python · __call__ ka jaadu (nn.Module isi tarah forward chalata hai)

class MiniModule:
    def __call__(self, x):              # object ko function ki tarah call karo to ye chalta hai
        print("(pehle hooks/checks)")
        return self.forward(x)          # ...fir forward

class Doubler(MiniModule):
    def forward(self, x):
        return 2 * x

d = Doubler()
print(d(21))                            # d(21) -> d.__call__(21) -> d.forward(21)

Output

(pehle hooks/checks)
42

PyTorch · Lab 5 ka MySimpleNN — aur super() bhoolne ka asli error

import torch
import torch.nn as nn

class MySimpleNN(nn.Module):                      # Lab 5: nn.Module se inherit
    def __init__(self, num_features):
        super().__init__()                        # nn.Module ki machinery ON (parameter tracking)
        self.linear = nn.Linear(num_features, 1)  # sub-module: weights apne aap register
        self.sigmoid = nn.Sigmoid()

    def forward(self, features):                  # computation yahan likho
        out = self.linear(features)               # (N, 30) -> (N, 1)
        out = self.sigmoid(out)                   # probability (0, 1)
        return out

model = MySimpleNN(30)
print(model)
x = torch.randn(4, 30)                            # 4 patients
print(model(x).shape)                             # model(x), model.forward(x) nahi
print(sum(p.numel() for p in model.parameters())) # 30 weights + 1 bias

class Oops(nn.Module):
    def __init__(self):
        self.linear = nn.Linear(2, 1)             # super().__init__() bhool gaye
try:
    Oops()
except AttributeError as e:
    print("AttributeError:", e)

Output

MySimpleNN(
  (linear): Linear(in_features=30, out_features=1, bias=True)
  (sigmoid): Sigmoid()
)
torch.Size([4, 1])
31
AttributeError: cannot assign module before Module.__init__() call

Exam Radar

  • Har model: class Net(nn.Module) → __init__ me pehli line super().__init__() → layers self.xxx = ... me → forward(self, x) me computation → return.
  • Bhool gaye to: AttributeError: cannot assign module before Module.__init__() call.
  • Call model(x), model.forward(x) nahi (hooks skip ho jaate). Method ka naam exactly forward.
  • print(model) layers dikhata hai; model.parameters() sirf self. pe rakhi layers ke weights deta hai.

Galti mat karna

  • Method me self bhool jaana: def forward(x): → call pe TypeError: forward() takes 1 positional argument but 2 were given.
  • Layer forward ke andar banana (nn.Linear(30, 1)(x)) — har call pe naye random weights, kuch seekha hi nahi jaayega, aur optimizer ko dikhenge bhi nahi. Layers hamesha __init__ me.
  • __init__ ki spelling: do underscore aage, do peeche. __int__ likha to constructor chalega hi nahi.

Dimaag me aise baitha lo

Class = franchise ka naksha, object = asli stall, self = "mera", super().__init__() = "pehle head-office ka setup, fir mera masala".

3.8 import, from … import, as — toolbox kholna

Kahani

Papa ka toolbox: poora box utha lao (import torch) aur har baar bolo "box se pench-kas". Ya sirf pench-kas nikaal lo (from … import …). Aur lamba naam? Nickname rakh do — jaise "Rajesh" ko "Raju" (as).

Python · course ke standard imports

import torch                                   # poora package, naam se use: torch.zeros
import torch.nn as nn                          # lamba naam -> chhota nickname "nn"
import torch.optim as optim
import torch.nn.functional as F
import numpy as np
from torch.utils.data import TensorDataset, DataLoader   # package se sirf ye do cheezein
from sklearn.preprocessing import StandardScaler

print(nn.Linear is torch.nn.Linear)            # same cheez, bas chhota naam
print(F.relu(torch.tensor([-1.0, 2.0])))       # F = functional version

Output

True
tensor([0., 2.])

Ye wahi imports hain jo poore notebook ke code me chalenge. nn = layers aur losses (classes: nn.ReLU()), F = wahi cheezein function ki tarah (F.relu(x)), optim = optimizers (SGD, Adam), DataLoader = batches (Ch 14).

3.9 f-strings — print me values ghusana

String ke aage f lagao, aur { } ke andar koi bhi variable/expression likho — wo value ban ke chhap jaayega. Format bhi: {x:.4f} = 4 decimal, {n:02d} = 2 digit (aage zero), {a:.2%} = percent.

Python · Lab 5 ka print, aur formatting

epoch, loss = 3, 0.4555133879184723
print(f'Epoch: {epoch + 1}, Loss: {loss}')                # Lab 5 style: {} me koi bhi expression
print(f"Epoch {epoch + 1:02d} | loss {loss:.4f} | acc {0.9825:.2%}")   # format: 2 digit, 4 decimal, percent
print("Epoch", epoch + 1, "Loss", round(loss, 4))         # bina f-string wala tareeka

Output

Epoch: 4, Loss: 0.4555133879184723
Epoch 04 | loss 0.4555 | acc 98.25%
Epoch 4 Loss 0.4555

3.10 with block — "kaam khatam, safai apne aap" (aage torch.no_grad())

Kahani

Library se kitaab li. Niyam: block me padho, block khatam hote hi kitaab apne aap wapas — chahe beech me phone aa jaaye (error). with yahi guarantee deta hai: block me ghuste waqt setup, nikalte waqt cleanup. Isko context manager kehte hain.

Python + PyTorch · with open, with torch.no_grad()

with open("notes.txt", "w") as fh:      # file kholi, naam fh
    fh.write("DL ka exam kal hai!\n")
print(fh.closed)                        # block khatam -> file apne aap band (error aaye tab bhi)

x = torch.tensor(2.0, requires_grad=True)
with torch.no_grad():                   # is block ke andar gradient tracking BAND
    y = x * 3
print(y.requires_grad)                  # False: koi graph nahi bana
z = x * 3                               # block ke bahar: tracking wapas ON
print(z.requires_grad)

Output

True
False
True

Lab 5 evaluation: with torch.no_grad(): ke andar model chalaya, kyunki test pe gradient nahi chahiye — graph banana memory aur time ki barbaadi hai. Block ke bahar tracking apne aap wapas ON. (Ye model.eval() se alag cheez hai — wo dropout/BatchNorm ka mode badalta hai, Ch 20. Evaluation me dono lagte hain.)

Exam Radar

  • with torch.no_grad(): → andar ke operations ka graph nahi banta, requires_grad=False results. Evaluation/inference ke liye.
  • Lab 5 pitfall: evaluation me no_grad bhoolna → bekaar memory/compute.

3.11 lambda aur list comprehension — ek-line ke jugaad

lambda = chhota, bina naam ka function: lambda v: v ** 2 = "v do, v² lo". Jahan ek chhota function argument me dena ho (sorting key, LambdaLR scheduler Ch 15, transforms.Lambda). List comprehension = loop + list ek line me: [f(v) for v in items if condition]. Agar square brackets hata do aur seedha sum(...) me daal do, to wo generator expression hai — list banaye bina ek ek value deta hai. Parameter ginne ka famous one-liner yahi hai.

Python · lambda, comprehension, parameter count

square = lambda v: v ** 2                         # chhota, bina naam ka function
print(square(5))

nums = [1, 2, 3, 4, 5]
print([v ** 2 for v in nums])                     # list comprehension: har v ka square
print([v for v in nums if v % 2 == 0])            # filter ke saath: sirf even

model = nn.Linear(30, 1)
print(sum(p.numel() for p in model.parameters())) # generator expression: 30 + 1
print(sorted(["relu", "elu", "tanh"], key=lambda s: len(s)))   # lambda as "key"

Output

25
[1, 4, 9, 16, 25]
[2, 4]
31
['elu', 'relu', 'tanh']

Sirf trainable parameters ginne hon to: sum(p.numel() for p in model.parameters() if p.requires_grad).

3.12 NumPy minimum — PyTorch ka bada bhai

Kahani

NumPy pehle aaya, PyTorch baad me — aur PyTorch ne NumPy ki bhasha lagbhag copy ki. pandas, sklearn (Lab 5 ka StandardScaler, train_test_split) sab NumPy arrays dete hain. To data ka safar hota hai: CSV → pandas → NumPy → PyTorch tensor.

NumPy · array, shape, dtype, dot, broadcasting, astype

import numpy as np
a = np.array([1.5, 2.0, 3.0])
print(a.shape, a.dtype)                  # float64 = numpy ka default float!
b = np.array([1, 2, 3])
print(b.dtype)                           # int64
M = np.array([[1, 2, 3], [4, 5, 6]])
print(M.shape, M.ndim)
print(np.dot([1, 2], [3, 4]), M @ np.ones(3))      # dot product, matrix @ vector
print(M + 10)                            # broadcasting: scalar har element me
print(M + np.array([100, 200, 300]))     # (2,3) + (3,): har row me same vector
print(a.astype(np.float32).dtype)        # Lab 5 wali trick: float32 me badlo

Output

(3,) float64
int64
(2, 3) 2
11 [ 6. 15.]
[[11 12 13]
 [14 15 16]]
[[101 202 303]
 [104 205 306]]
float32

Exam Radar

  • NumPy ka default float = float64, PyTorch ka = float32. Isi farak se Lab 5 ka "Double vs Float" error aata hai (3.15).
  • np.array, .shape, .ndim, .dtype, np.dot, @, .astype(np.float32) — tensors me inke bhai: torch.tensor, .shape, .dim(), .dtype, torch.dot, @, .to(torch.float32).

3.13 Tensor kya hai — NumPy array + do superpowers

Kahani

Tensor ko NumPy array ka "superhero" version samjho. Shakal same (numbers ka dabba, shape, dtype), par do shaktiyaan extra: (1) GPU pe daud sakta hai (Lab 0: 29 guna tez), aur (2) apna itihaas yaad rakh sakta hai — kis operation se bana — taaki derivative apne aap nikal sake (autograd, 3.24). Deep learning me data, weights, gradients, loss — sab tensor hain.

Chapter 2.1 wali dimensions yaad karo, ab DL ke asli examples ke saath. Images ke liye PyTorch ka order fix hai: (N, C, H, W) = (batch me kitni photos, channels, height, width). "Channels first". Grayscale (MNIST, Fashion-MNIST) me C = 1, colour (CIFAR-10, ImageNet) me C = 3 (Red, Green, Blue).

PyTorch · 0-D se 4-D tak

loss = torch.tensor(0.6703)               # 0-D: ek number
bias = torch.zeros(16)                    # 1-D: 16 neurons ke bias
X_batch = torch.randn(455, 30)            # 2-D: (samples, features) - Lab 5
img = torch.rand(3, 32, 32)               # 3-D: (C, H, W) - ek colour photo (CIFAR-10)
mnist_batch = torch.rand(64, 1, 28, 28)   # 4-D: (N, C, H, W) - 64 grayscale digits
for t in [loss, bias, X_batch, img, mnist_batch]:
    print(t.dim(), tuple(t.shape))

Output

0 ()
1 (16,)
2 (455, 30)
3 (3, 32, 32)
4 (64, 1, 28, 28)
photo 1 photo 2 … photo 64 H W C N = batch size (yahan 64) (N, C, H, W) CIFAR-10: (64, 3, 32, 32) MNIST: (64, 1, 28, 28)
Har photo = C channels ki parten (H × W). 64 photos ek saath = 4-D tensor. Order hamesha N, C, H, W.

Exam Radar

  • PyTorch image format: (N, C, H, W). Ek photo (C, H, W) = 3-D; model ko dene se pehle batch dim jodo → (1, C, H, W) (3.19).
  • MNIST/Fashion-MNIST: 1 × 28 × 28; CIFAR-10: 3 × 32 × 32. Tabular (Lab 5): (N, features).
  • t.dim() = t.ndim = number of dimensions.

Dimaag me aise baitha lo

Tensor = NumPy + GPU + yaaddasht. Photos ka order: N-C-H-W = Number (kitni photos), Channels, Height, Width.

3.14 Tensor banana — Lab 0 ki poori list

FunctionKya banata haiYaad rakhne wali baat
torch.tensor(data)list/array se tensordtype data se: ints → int64, decimals → float32. Data copy hota hai
torch.zeros(2, 3) / onessab 0 / sab 1default float32
torch.rand(2, 3)uniform [0, 1)1 kabhi nahi, negative kabhi nahi
torch.randn(2, 3)normal 𝒩(0, 1)"n" = normal; negative bhi
torch.randint(low, high, size)random integershigh exclusive; size tuple me
torch.empty(2, 3)bas memory, values garbageLab 0 me 2.4242e-43 jaisi ajeeb values aayi — initialize nahi hota
torch.arange(start, end, step)barabar step wali sequenceend exclusive (Lab 0: arange(2,100,2) → 2…98)
torch.linspace(start, end, steps)steps barabar doori wale pointsend inclusive; step = (end−start)/(steps−1) = 20/9 ≈ 2.2222
torch.eye(n)identity matrixdiagonal 1, baaki 0
torch.full((2, 3), 5)sab ek valueshape tuple me pehle, value baad me
zeros_like(p), ones_like, rand_like, empty_likep jaisa shape (aur dtype)rand_like int tensor pe error (3.15)
torch.manual_seed(n)random generator ka seedsame seed → same "random" numbers (reproducible)

PyTorch · Lab 0 · fixed values wale

print(torch.tensor([[1, 2, 4], [3, 4, 4]]))   # Python list se (data copy hota hai)
print(torch.zeros(2, 3))                       # sab 0 (float)
print(torch.ones(3, 4))                        # sab 1
print(torch.eye(3))                            # identity: diagonal pe 1
print(torch.full((2, 3), 5))                   # sab ek hi value (shape tuple me!)

Output

tensor([[1, 2, 4],
        [3, 4, 4]])
tensor([[0., 0., 0.],
        [0., 0., 0.]])
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])
tensor([[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]])
tensor([[5, 5, 5],
        [5, 5, 5]])

PyTorch · Lab 0 · random wale (seed ke saath)

torch.manual_seed(100)                         # Lab 0: seed same -> random numbers same
print(torch.rand(3, 4))                        # uniform [0, 1)
print(torch.randn(2, 3))                       # normal N(0, 1): negative bhi
print(torch.randint(1, 100, (2, 5)))           # integers [1, 100): 100 kabhi nahi
print(torch.randint(size=(2, 3), low=0, high=10, dtype=torch.float32))   # Lab 0 style, float me

Output

tensor([[0.1117, 0.8158, 0.2626, 0.4839],
        [0.6765, 0.7539, 0.2627, 0.0428],
        [0.2080, 0.1180, 0.1217, 0.7356]])
tensor([[-0.3172, -0.8660,  1.7482],
        [-0.2759, -0.9755,  0.4790]])
tensor([[29, 76, 79, 40,  7],
        [ 8, 80, 69, 43, 45]])
tensor([[5., 7., 3.],
        [9., 4., 0.]])

Dekha? torch.manual_seed(100) ke baad torch.rand(3, 4) ne bilkul wahi numbers diye jo Lab 0 ke output me the (0.1117, 0.8158, …). Seed = random numbers ki "recipe ka starting point". Isiliye labs me SEED = 42, torch.manual_seed(SEED) likhte hain — taaki har baar same weights se shuru ho aur results compare ho sakein.

PyTorch · Lab 0 · sequences aur *_like

print(torch.arange(2, 12, 2))                  # start, stop (EXCLUSIVE), step
print(torch.arange(5))                         # 0..4
print(torch.linspace(0, 20, 10))               # 0 se 20 tak 10 barabar points (20 INCLUDED)
p = torch.tensor([[11, 11, 12], [2, 3, 4]])
print(torch.zeros_like(p))                     # p jaisa shape + dtype, sab 0
print(torch.ones_like(p))
print(torch.empty(2, 3).shape)                 # empty: memory mili, values garbage (kabhi print pe bharosa mat karo)

Output

tensor([ 2,  4,  6,  8, 10])
tensor([0, 1, 2, 3, 4])
tensor([ 0.0000,  2.2222,  4.4444,  6.6667,  8.8889, 11.1111, 13.3333, 15.5556,
        17.7778, 20.0000])
tensor([[0, 0, 0],
        [0, 0, 0]])
tensor([[1, 1, 1],
        [1, 1, 1]])
torch.Size([2, 3])

Exam Radar

  • arange end exclusive, linspace end inclusive. linspace(0, 20, 10) → 10 points, step 2.2222.
  • rand → U[0,1), randn → 𝒩(0,1), randint(1, 100, (4, 5)) → 1…99.
  • empty values initialize nahi karta (garbage). full((2,3), 5) → int64 (5 int hai).
  • torch.tensor([1, 2]) → int64, par torch.Tensor([1, 2]) (capital T, class) → hamesha float32.

Dimaag me aise baitha lo

arange = "darwaze tak", linspace = "darwaze ke andar tak, ginti ke points".

3.15 shape, size() aur dtype — aur Lab 5 ka "Double vs Float" error

Kahani

Do log baat kar rahe hain — ek shuddh Hindi me, ek English me. Baat same hai, par samjhenge nahi. Tensors bhi aise hi: ek float64 ("Double") me, doosra float32 ("Float") me ho to matrix multiply mana kar deta hai. Pehle dono ki bhasha same karo.

dtypePurana naamKab milta haiShortcut
torch.float32FloatPyTorch ka default float; saare layer weights.float()
torch.float64DoubleNumPy se aaya data (numpy default float64).double()
torch.int64Longintegers ka default; class labels (CrossEntropyLoss ko yahi chahiye, Ch 11).long()
torch.int32Int.int() se (Lab 1 predictions).int()
torch.bool—comparison ka result (z >= 0).bool()

PyTorch · Lab 0 · shape, size, dtype, .to()

p = torch.tensor([[11, 11, 12], [2, 3, 4]])
print(p.shape, p.size(), p.size(0), p.shape[1])   # shape == size(); size(0) = rows
print(p.dtype)                                    # integers -> int64 (Long)
q = torch.tensor([1.0, 2.0])
print(q.dtype)                                    # decimals -> float32 (Float)
print(torch.tensor([1, 2, 4], dtype=torch.float64))   # dtype khud chuno
print(p.to(torch.float32))                        # Lab 0: dtype badlo
print(p.float().dtype, q.long().dtype, q.double().dtype)   # shortcuts
try:
    torch.rand_like(p)                            # Lab 0 error: int tensor pe rand nahi
except Exception as e:
    print(type(e).__name__ + ":", e)

Output

torch.Size([2, 3]) torch.Size([2, 3]) 2 3
torch.int64
torch.float32
tensor([1., 2., 4.], dtype=torch.float64)
tensor([[11., 11., 12.],
        [ 2.,  3.,  4.]])
torch.float32 torch.int64 torch.float64
NotImplementedError: "check_uniform_bounds" not implemented for 'Long'

.shape (attribute) aur .size() (method) same cheez; .size(0) = pehli dimension (batch size) — Lab 5: X_train_tensor.shape[1] = 30 features se nn.Linear banaya. Ab Lab 5 ka famous pitfall, real error ke saath:

PyTorch · Lab 5 pitfall · numpy float64 → float32

import numpy as np
X_np = np.random.rand(4, 30)                      # numpy -> float64 (Double)
layer = nn.Linear(30, 1)                          # weights float32 (Float)
try:
    layer(torch.from_numpy(X_np))                 # Double input, Float weights
except RuntimeError as e:
    print("RuntimeError:", e)

X_t = torch.from_numpy(X_np.astype(np.float32))   # Lab 5 fix: pehle float32 karo
print(X_t.dtype, layer(X_t).shape)

Output

RuntimeError: mat1 and mat2 must have the same dtype, but got Double and Float
torch.float32 torch.Size([4, 1])

Naye PyTorch me message aata hai "mat1 and mat2 must have the same dtype, but got Double and Float"; purane version (Lab 5 ka text) me "expected scalar type Float but found Double". Matlab dono ka ek hi: input float64 hai, weights float32. torch.from_numpy dtype nahi badalta — float64 array se float64 tensor hi banega. Fix: .astype(np.float32) pehle (Lab 5), ya tensor pe .float() baad me.

Exam Radar

  • Defaults: float tensor → float32, int tensor → int64. NumPy float → float64.
  • Fix "Double vs Float": X.astype(np.float32) ya tensor.float() / .to(torch.float32).
  • BCE ke targets float chahiye (Lab 5 me labels bhi float32 kiye); CrossEntropy ke targets int64 (long).
  • rand_like int64 tensor pe → NotImplementedError: "check_uniform_bounds" not implemented for 'Long' (Lab 0).

Dimaag me aise baitha lo

NumPy Double bolta hai, PyTorch Float sunta hai — beech me .astype(np.float32) ka translator.

3.16 Element-wise operations aur Broadcasting (aur Lab 5 ka chupa bug)

Kahani

Diwali pe company ne har employee ko ₹500 bonus diya — ek number, sab pe laga. Ye scalar broadcasting. Fir bola: "har department ka alag bonus — Sales ko 100, HR ko 200, Tech ko 300", aur ye list har branch ke har employee row pe lagi. Chhoti cheez apne aap "faila di gayi" (broadcast) badi cheez ke shape tak — bina copy banaye.

Same shape ke do tensors pe + − * / ** // % position by position (element-wise) chalte hain. Shape alag ho to PyTorch broadcasting try karta hai:

Broadcasting rule: shapes ko right se milao; har jagah dono size barabar hon, ya ek ka size 1 ho (ya dimension ho hi nahi) → chalega
  • Size 1 wali (ya gayab) dimension ko doosre ke size tak "khinch" diya jaata hai (virtual copy)
  • Result shape = har dimension ka bada wala
  • Kisi jagah dono alag aur koi bhi 1 nahi → error

Chalo numbers se samjhein

  1. (2, 3) + (3,): right se — 3 vs 3 ✓; aage (3,) ke paas dim nahi → 1 maano → 2 ✓ → result (2, 3). Row vector har row me add.
  2. (2, 3) * (2, 1): 3 vs 1 ✓ (1 khinchega), 2 vs 2 ✓ → (2, 3). Har row apne number se guna.
  3. (2, 3) + (2,): right se 3 vs 2 ✗, koi 1 nahi → error.
  4. (3, 1) − (3,): right se 1 vs 3 ✓ (→3), aage 3 vs (gayab→1) ✓ (→3) → (3, 3)!! Ye Lab 5 ka bug hai.

PyTorch · Lab 0 · element-wise aur scalar

a = torch.tensor([[1., 2., 3.], [4., 5., 6.]])
b = torch.tensor([[10., 20., 30.], [40., 50., 60.]])
print(a + b)                  # position by position
print(b - a)
print(a * b)                  # element-wise guna (matrix multiply NAHI)
print(b / a)
print(a + 2)                  # scalar har element pe (Lab 0: t1 + 2)
print(a ** 2)                 # har element ka square
print(((a * 100) // 3) % 2)   # Lab 0 wala combo: floor-divide, fir remainder

Output

tensor([[11., 22., 33.],
        [44., 55., 66.]])
tensor([[ 9., 18., 27.],
        [36., 45., 54.]])
tensor([[ 10.,  40.,  90.],
        [160., 250., 360.]])
tensor([[10., 10., 10.],
        [10., 10., 10.]])
tensor([[3., 4., 5.],
        [6., 7., 8.]])
tensor([[ 1.,  4.,  9.],
        [16., 25., 36.]])
tensor([[1., 0., 0.],
        [1., 0., 0.]])

PyTorch · broadcasting row / column, aur mismatch error

a = torch.tensor([[1., 2., 3.], [4., 5., 6.]])   # (2, 3)
row = torch.tensor([100., 200., 300.])           # (3,)
print(a + row)                                   # (2,3)+(3,) -> row har row me add
col = torch.tensor([[1.], [10.]])                # (2, 1)
print(a * col)                                   # (2,3)*(2,1) -> har row apne number se guna
try:
    a + torch.tensor([1., 2.])                   # (2,3)+(2,): right se 3 vs 2 -> mismatch
except RuntimeError as e:
    print("RuntimeError:", e)

Output

tensor([[101., 202., 303.],
        [104., 205., 306.]])
tensor([[ 1.,  2.,  3.],
        [40., 50., 60.]])
RuntimeError: The size of tensor a (3) must match the size of tensor b (2) at non-singleton dimension 1
123 456 + 100200300 100200300 = 101202303 104205306 (2, 3) (3,) → virtual copy (2, 3) row vector har row pe "faila" .9.2.7 − 101 = 3×3 (3, 1) − (3,) → (3, 3): koi error nahi, chupa bug y_pred (N,1) aur y_true (N,) — Lab 5 fix: y_true.view(-1, 1) → (N, 1) MSELoss sirf warning deta, BCELoss error
Upar: (3,) wali row har row pe virtually copy. Neeche: column (3,1) aur row (3,) mil ke (3,3) grid bana dete — 9 galat differences.

PyTorch · Lab 5 ka silent broadcasting bug

y_pred = torch.tensor([[0.9], [0.2], [0.7]])   # model output: (N, 1)
y_true = torch.tensor([1., 0., 1.])            # labels: (N,)
diff = y_pred - y_true                         # (3,1) - (3,) -> (3,3) !!
print(diff.shape)                              # error nahi aaya - CHUPA bug
print(diff)
good = y_pred - y_true.view(-1, 1)             # Lab 5 fix: target ko (N, 1) banao
print(good.shape)
print(good)

Output

torch.Size([3, 3])
tensor([[-0.1000,  0.9000, -0.1000],
        [-0.8000,  0.2000, -0.8000],
        [-0.3000,  0.7000, -0.3000]])
torch.Size([3, 1])
tensor([[-0.1000],
        [ 0.2000],
        [-0.3000]])

Lab 5 ka warning bilkul yahi tha: model ka y_pred (N, 1), labels (N,). Bina .view(-1, 1) ke 455 × 455 ka grid ban jaata — loss bakwaas. Naye PyTorch me losses ka behaviour alag alag hai, dekh lo:

PyTorch · losses shape mismatch pe kya karte hain

y_pred = torch.tensor([[0.9], [0.2], [0.7]])   # (N, 1)
y_true = torch.tensor([1., 0., 1.])            # (N,)
try:
    nn.BCELoss()(y_pred, y_true)               # naya PyTorch: BCELoss seedha mana kar deta hai
except ValueError as e:
    print("ValueError:", e)
print(nn.MSELoss()(y_pred, y_true))            # MSELoss: sirf warning, (3,3) pe galat loss!
print(nn.MSELoss()(y_pred, y_true.view(-1, 1)))   # sahi shape -> sahi loss
print(nn.BCELoss()(y_pred, y_true.view(-1, 1)))   # Lab 5 wala sahi tareeka

Output

ValueError: Using a target size (torch.Size([3])) that is different to the input size (torch.Size([3, 1])) is deprecated. Please ensure they have the same size.
tensor(0.3133)
tensor(0.0467)
tensor(0.2284)

MSELoss wali line ke saath ek UserWarning bhi chhapta hai: "Using a target size (torch.Size([3])) that is different to the input size (torch.Size([3, 1])). This will likely lead to incorrect results due to broadcasting." — par code chal jaata hai aur galat loss (0.3133 vs sahi 0.0467) deta hai. Isliye shapes khud check karna seekho: loss se pehle print(y_pred.shape, y.shape).

Exam Radar

  • Broadcasting: right se compare; equal ya 1 → OK; result = max size.
  • (N, 1) − (N,) = (N, N). Fix: y.view(-1, 1) ya y.unsqueeze(1) (ya prediction pe .squeeze(1)).
  • Error text: "The size of tensor a (3) must match the size of tensor b (2) at non-singleton dimension 1".

Galti mat karna

  • "Shape alag hai to error aayega hi" — nahi! Broadcasting chupchaap chal sakta hai. Yahi sabse khatarnaak bug hai.

Dimaag me aise baitha lo

Diwali bonus sab pe faila = broadcasting. Column − row = poora grid (chupa bug).

3.17 @ (matmul) vs * (element-wise) — sabse common trap

Kahani

Do register same size ke: "*" = har dabbe ko uske saamne wale dabbe se guna (seat by seat). "@" = Chapter 2.4 wala canteen bill — row se column ka dot product. Naam me dono "multiply" hain, kaam bilkul alag.

PyTorch · Lab 0 · * vs @ vs dot vs transpose

A = torch.tensor([[1., 2.], [3., 4.]])
B = torch.tensor([[5., 6.], [7., 8.]])
print(A * B)                    # element-wise: 1*5, 2*6, 3*7, 4*8
print(A @ B)                    # matrix multiplication: row . column
print(torch.matmul(A, B))       # @ ka function wala naam
v = torch.tensor([1., 1.])
print(A @ v)                    # (2,2) @ (2,) -> (2,): har row ka jod
print(torch.dot(torch.tensor([1., 2.]), torch.tensor([3., 4.])))   # sirf 1-D
print(torch.transpose(A, 0, 1), A.T)

Output

tensor([[ 5., 12.],
        [21., 32.]])
tensor([[19., 22.],
        [43., 50.]])
tensor([[19., 22.],
        [43., 50.]])
tensor([3., 7.])
tensor(11.)
tensor([[1., 3.],
        [2., 4.]]) tensor([[1., 3.],
        [2., 4.]])

Chalo numbers se samjhein

  1. Element-wise: [[1×5, 2×6], [3×7, 4×8]] = [[5, 12], [21, 32]]
  2. Matmul (1,1): row [1, 2] · col [5, 7] = 5 + 14 = 19; (1,2): [1, 2]·[6, 8] = 6 + 16 = 22; (2,1): [3, 4]·[5, 7] = 15 + 28 = 43; (2,2): [3, 4]·[6, 8] = 18 + 32 = 50
  3. A @ [1, 1] = har row ka jod: [1+2, 3+4] = [3, 7]

Aur ab ek bahut kaam ki baat: nn.Linear andar se bas ek matmul + bias hai. Ise khud verify karo:

PyTorch · nn.Linear = x @ W.T + b (khud check)

torch.manual_seed(0)
layer = nn.Linear(3, 2)                 # in=3, out=2
x = torch.randn(4, 3)                   # batch of 4
print(layer.weight.shape, layer.bias.shape)          # (out, in), (out,)
manual = x @ layer.weight.T + layer.bias             # (4,3)@(3,2) + (2,) -> (4,2)
print(torch.allclose(layer(x), manual), layer(x).shape)

Output

torch.Size([2, 3]) torch.Size([2])
True torch.Size([4, 2])
OperationKya karta haiShapes
A * B, torch.mulelement-wise (Hadamard) productsame shape (ya broadcastable)
A @ B, torch.matmulmatrix multiplication (batch bhi)(m, n) @ (n, p) → (m, p)
torch.mmmatmul sirf 2-D ke liye(m, n), (n, p)
torch.dotdot product sirf 1-D(n,), (n,) → scalar
torch.transpose(A, 0, 1), A.Trows ↔ columns(m, n) → (n, m)

Exam Radar

  • * ≠ @. Matmul shapes: andar wale same. Lab 0: torch.matmul(m1, m2), torch.dot(vector1, vector2) → tensor(11).
  • nn.Linear(in, out): weight (out, in), bias (out,), output = x @ W.T + b, shape (N, out).

Dimaag me aise baitha lo

* = seat se seat. @ = row se column (canteen bill).

3.18 Reductions aur dim — sum, mean, max, min, argmax, argmin (sabse bada MCQ trap)

Kahani

Lab 0 ka matrix e = [[5, 6, 9], [2, 3, 1]] socho ek register: 2 students (rows) × 3 subjects (columns). Principal poochta hai: "har subject ka total?" — tum students wali dimension ko daba (collapse) dete ho, neeche ki taraf jodte ho → 3 numbers. Ye dim=0. "Har student ka total?" — subjects wali dimension dabao, right ki taraf jodo → 2 numbers. Ye dim=1.

Rule: dim=k → wahi dimension gayab (collapse) ho jaati hai. Shape (2, 3): dim=0 → (3,), dim=1 → (2,)
  • dim=0 = rows ke across (upar se neeche) chalo → har column ka answer
  • dim=1 = columns ke across (baayein se daayein) → har row ka answer
  • keepdim=True → dimension hatao mat, size 1 kar do: (2, 3) → (2, 1)
  • dim na do → poore tensor ka ek answer; argmax/argmin me tab flatten (ek line me phaila) karke index milta hai

Chalo numbers se samjhein — e = [[5, 6, 9], [2, 3, 1]]

  1. sum(e): 5+6+9+2+3+1 = 26
  2. sum(e, dim=0) (har column): [5+2, 6+3, 9+1] = [7, 9, 10] → shape (3,)
  3. sum(e, dim=1) (har row): [5+6+9, 2+3+1] = [20, 6] → shape (2,)
  4. mean(dim=0): [7/2, 9/2, 10/2] = [3.5, 4.5, 5.0]; mean(dim=1): [20/3, 6/3] = [6.6667, 2.0]
  5. max(e) = 9, min(e) = 1
  6. argmax(e) bina dim: flatten = [5, 6, 9, 2, 3, 1] (index 0…5) → 9 index 2 pe. argmin(e): 1 index 5 pe. (Lab 0 output: tensor(2), tensor(5).)
  7. argmax(e, dim=0): har column me kaunsi row badi? col0: 5 vs 2 → 0; col1: 6 vs 3 → 0; col2: 9 vs 1 → 0 → [0, 0, 0]
  8. argmax(e, dim=1): har row me kaunsa column bada? row0: max 9 at col 2; row1: max 3 at col 1 → [2, 1]
e: shape (2, 3) 569 231 7910 sum(dim=0) → shape (3,) rows daba di 206 sum(dim=1) → shape (2,) columns daba diye student 1 student 2
Laal (dim=0): upar se neeche jodo → har column ka total. Narangi (dim=1): baayein se daayein → har row ka total. Jo dim di, wahi gayab.

PyTorch · Lab 0 ka e — saare reductions

e = torch.tensor([[5., 6., 9.],
                  [2., 3., 1.]])          # Lab 0 wala matrix, shape (2, 3)
print(torch.sum(e))                       # sab ka jod
print(torch.sum(e, dim=0))                # dim 0 (rows) ko DABA do -> har column ka jod
print(torch.sum(e, dim=1))                # dim 1 (columns) ko daba do -> har row ka jod
print(e.mean(dim=0), e.mean(dim=1))
print(torch.max(e), torch.min(e))
print(torch.argmax(e), torch.argmin(e))   # bina dim: FLATTEN karke index!
print(torch.argmax(e, dim=0), torch.argmax(e, dim=1))

Output

tensor(26.)
tensor([ 7.,  9., 10.])
tensor([20.,  6.])
tensor([3.5000, 4.5000, 5.0000]) tensor([6.6667, 2.0000])
tensor(9.) tensor(1.)
tensor(2) tensor(5)
tensor([0, 0, 0]) tensor([2, 1])

PyTorch · (values, indices), keepdim, accuracy pattern, int mean error

values, indices = torch.max(e, dim=1)     # dim do to (values, indices) dono milte
print(values, indices)
print(e.sum(dim=1, keepdim=True).shape, e.sum(dim=1).shape)   # keepdim: dimension bacha ke rakho

logits = torch.tensor([[2.0, 0.5, 0.1],   # sample 0: class 0 sabse bada
                       [0.2, 0.1, 3.0],   # sample 1: class 2
                       [0.3, 1.5, 0.2]])  # sample 2: class 1
labels = torch.tensor([0, 2, 0])
preds = logits.argmax(dim=1)              # har ROW (sample) me sabse bada class
print(preds, (preds == labels).float().mean())   # accuracy = 2/3
try:
    torch.tensor([1, 2, 3]).mean()        # int tensor ka mean
except RuntimeError as e:
    print("RuntimeError:", e)

Output

tensor([9., 3.]) tensor([2, 1])
torch.Size([2, 1]) torch.Size([2])
tensor([0, 2, 1]) tensor(0.6667)
RuntimeError: mean(): could not infer output dtype. Input dtype must be either a floating point or complex dtype. Got: Long

Lab 0 me ek aur line thi: torch.softmax(f, dim=0) with f = [[5, 8, 2], [5, 5, 2]] → output [[0.5000, 0.9526, 0.5000], [0.5000, 0.0474, 0.5000]]. Dekho: har column ka jod 1 hai — kyunki dim=0 ke along normalize hua (5 vs 5 → 0.5/0.5; 8 vs 5 → 0.9526/0.0474). Classification me logits (N, K) hote hain aur hum har sample (row) ke classes pe softmax chahte hain → dim=1. Galat dim = har class ke across samples normalize — bakwaas.

Exam Radar

  • (N, K) logits: prediction = logits.argmax(dim=1); softmax = torch.softmax(logits, dim=1).
  • torch.max(x, dim=1) → (values, indices) dono; bina dim sirf value.
  • argmax bina dim → flattened index (Lab 0: argmax(e) = 2, argmin(e) = 5).
  • mean int tensor pe error → pehle .float(). Isliye accuracy me (pred == y).float().mean() (Lab 5).

Galti mat karna

  • "dim=0 matlab rows ka sum" — confusing! dim=0 rows ko daba deta hai, jawab har column ka aata hai. Shape se check karo: (2,3) → dim=0 → (3,).

Dimaag me aise baitha lo

Jo dim doge, wo pichak jaayegi. (2,3) pe dim=0 → 3 bache, dim=1 → 2 bache.

3.19 reshape / view / −1, unsqueeze / squeeze, flatten — dabba wahi, arrangement naya

Kahani

12 laddoo hain. Dabbe me 3 × 4 lagao ya 2 × 6 ya 12 ki ek line — laddoo wahi 12. Par 5 × ? nahi ho sakta (12 ko 5 se poora nahi baant sakte). view/reshape bas arrangement badalte hain, numbers nahi.

PyTorch · view, reshape, -1

x = torch.arange(12)                       # [0..11], shape (12,)
print(x.reshape(3, 4))
print(x.view(2, 6))                        # same data, naya shape (12 = 2*6)
print(x.view(-1, 4).shape, x.view(3, -1).shape)   # -1 = "baaki tum khud hisaab lagao"
y = torch.tensor([1., 0., 1.])             # (3,)
print(y.view(-1, 1).shape)                 # (3, 1) - Lab 5 wala fix
try:
    x.view(5, -1)                          # 12 ko 5 se poora nahi baant sakte
except RuntimeError as e:
    print("RuntimeError:", e)

Output

tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]])
tensor([[ 0,  1,  2,  3,  4,  5],
        [ 6,  7,  8,  9, 10, 11]])
torch.Size([3, 4]) torch.Size([3, 4])
torch.Size([3, 1])
RuntimeError: shape '[5, -1]' is invalid for input of size 12

PyTorch · unsqueeze, squeeze, flatten, view vs reshape

y = torch.tensor([1., 0., 1.])             # (3,)
print(y.unsqueeze(0).shape, y.unsqueeze(1).shape)   # nayi size-1 dim: aage / peeche
z = torch.zeros(1, 3, 1)
print(z.squeeze().shape, z.squeeze(0).shape)        # size-1 dims hatao (sab / sirf dim 0)
img = torch.rand(3, 32, 32)                # ek photo (C, H, W)
print(img.unsqueeze(0).shape)              # model ko batch chahiye: (1, 3, 32, 32)

imgs = torch.zeros(64, 1, 28, 28)          # (N, C, H, W)
print(imgs.flatten().shape)                # sab kuch ek line me
print(imgs.flatten(start_dim=1).shape)     # batch dim bacha ke: CNN -> Linear (Ch 23)
print(imgs.view(imgs.size(0), -1).shape)   # same cheez view se

t = torch.arange(6).view(2, 3).T           # transpose ke baad memory "non-contiguous"
try:
    t.view(6)
except RuntimeError as e:
    print("RuntimeError:", e)
print(t.reshape(6))                        # reshape zaroorat pade to copy kar leta hai

Output

torch.Size([1, 3]) torch.Size([3, 1])
torch.Size([3]) torch.Size([3, 1])
torch.Size([1, 3, 32, 32])
torch.Size([50176])
torch.Size([64, 784])
torch.Size([64, 784])
RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
tensor([0, 3, 1, 4, 2, 5])

Exam Radar

  • Lab 5: y_train_tensor.view(-1, 1) → (N,) se (N, 1).
  • Ek photo (3, 32, 32) model ko dene ke liye img.unsqueeze(0) → (1, 3, 32, 32).
  • Galat total → RuntimeError: shape '[5, -1]' is invalid for input of size 12.

Galti mat karna

  • Bina dim ke squeeze() batch size 1 pe batch dim bhi uda deta hai: (1, 10) → (10,). Safe: squeeze(1) jaisa specific dim do.
  • view ko transpose samajhna — view sirf numbers ko naye shape me line se bharta hai; rows-columns swap nahi karta.

Dimaag me aise baitha lo

12 laddoo: 3×4 ya 2×6, par 5×? kabhi nahi. −1 = "baaki tum gin lo".

3.20 Indexing aur slicing — Lab 0 ka smiley face

Kahani

Diwali pe aangan me 10 × 10 ka rangoli grid banaya. Kis khaane me rang bharna hai — "row 2, column 7" — bas yahi indexing hai. Poori row bharni hai? "row 0, saare columns" = face[0, :]. Lab 0 me isi tarah zeros ke canvas pe smiley banaya tha.

PyTorch · Lab 0 · tensor smiley

face = torch.zeros(10, 10)                 # Lab 0: kaala canvas
face[0, :] = 1                             # top row (row 0, saare columns)
face[9, :] = 1                             # bottom row
face[:, 0] = 1                             # left column (saari rows, column 0)
face[:, 9] = 1                             # right column
face[2, 2] = 1                             # left eye: row 2, col 2
face[2, 7] = 1                             # right eye
face[7, 2:8] = 1                           # mouth: row 7, columns 2..7 (8 nahi)
print(face.int())                          # matplotlib ki jagah numbers dekh lo
# plt.imshow(face, cmap='gray') se ye smiley dikhega

Output

tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 1, 0, 0, 0, 0, 1, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 1, 1, 1, 1, 1, 1, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]], dtype=torch.int32)
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 face[0, :] = 1 → top row face[2, 2], face[2, 7] → aankhein face[:, 9] = 1 → right column face[7, 2:8] = 1 → muh (cols 2–7) face[9, :] = 1 → bottom row
Kaale khaane = 1. Upar/neeche ki rows face[0, :], face[9, :]; side columns face[:, 0], face[:, 9]; aankhein (2, 2), (2, 7); muh row 7, columns 2–7.

PyTorch · Lab 1 · rows, columns, 0 vs 0:1, mask

X = torch.tensor([[0., 0.], [0., 1.], [1., 0.], [1., 1.]])   # Lab 1: XOR inputs
print(X[0], X[-1])              # pehli row, aakhri row
print(X[1, 1], X[1, 1].item())  # ek element (tensor) aur uska Python number
print(X[:, 0])                  # pehla column -> shape (4,)  (dim gayab)
print(X[:, 0:1])                # pehla column slice se -> shape (4, 1) (dim bachi)
print(X[1:3])                   # rows 1 aur 2

z = torch.tensor([-1.5, -0.5, -0.5, 0.5])   # Lab 1 AND ke z values
mask = z >= 0                   # boolean tensor
print(mask, z[mask], mask.int())   # mask se filter; .int() -> 0/1 prediction

Output

tensor([0., 0.]) tensor([1., 1.])
tensor(1.) 1.0
tensor([0., 0., 1., 1.])
tensor([[0.],
        [0.],
        [1.],
        [1.]])
tensor([[0., 1.],
        [1., 0.]])
tensor([False, False, False,  True]) tensor([0.5000]) tensor([0, 0, 0, 1], dtype=torch.int32)

Exam Radar

  • X[:, 0] → (N,), X[:, 0:1] → (N, 1).
  • face[7, 2:8] = 1 → columns 2, 3, 4, 5, 6, 7 (6 khaane).
  • .item() ek element ka Python number deta hai.

Dimaag me aise baitha lo

[row, col]. ":" = poori line. Number = dim gayab, slice = dim bachi.

3.21 torch.cat vs torch.stack — train ke dibbe jodna vs saath saath khadi karna

Kahani

Do trains hain, dono me 3 dibbe. cat = ek train ke peeche doosri jod do — ek hi patri pe 6 dibbo ki lambi train (dimension wahi, lambai badhi). stack = dono trains ko do alag patriyon pe saath saath khada karo — "patri number" ek nayi dimension ban gayi.

a 123 b 456 cat([a, b]) → (6,) 123 456 stack([a, b]) → (2, 3) 123 456 stack(dim=1) → (3, 2) 14 25 36 cat: purani dim lambi stack: nayi dim bani ndim 1 → 2
cat: (3,) + (3,) → (6,). stack: (3,) + (3,) → (2, 3), ya dim=1 pe (3, 2).

PyTorch · Lab 1 · cat aur stack

h_or = torch.tensor([[0.], [1.], [1.], [1.]])     # (4, 1)
h_nand = torch.tensor([[1.], [1.], [1.], [0.]])   # (4, 1)
print(torch.cat([h_or, h_nand], dim=1))          # Lab 1: columns jod do -> (4, 2)
print(torch.cat([h_or, h_nand], dim=0).shape)    # rows jod do -> (8, 1)

a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
print(torch.cat([a, b]))                         # existing dim me lambi: (6,)
print(torch.stack([a, b]))                       # NAYI dim banake: (2, 3)
print(torch.stack([a, b], dim=1))                # nayi dim position 1 pe: (3, 2)

Output

tensor([[0., 1.],
        [1., 1.],
        [1., 1.],
        [1., 0.]])
torch.Size([8, 1])
tensor([1, 2, 3, 4, 5, 6])
tensor([[1, 2, 3],
        [4, 5, 6]])
tensor([[1, 4],
        [2, 5],
        [3, 6]])

Exam Radar

  • cat: tensors ki ndim same, aur jis dim pe jod rahe ho uske alawa saari sizes same. ndim nahi badhti.
  • stack: saare tensors bilkul same shape; result ki ndim +1.
  • Lab 1: torch.cat([h_or, h_nand], dim=1) → do (4, 1) columns se (4, 2) hidden layer matrix.

Dimaag me aise baitha lo

cat = dibbe peeche jodo. stack = trains saath saath khadi karo.

3.22 NumPy ↔ PyTorch, .item(), .tolist()

Kahani

Do bhai ek hi notebook share karte hain (from_numpy): ek ne kuch likha to doosre ko bhi dikhega. Ya photocopy de do (torch.tensor): ab dono alag, ek ke badlav se doosre pe asar nahi.

PyTorch · Lab 0 · from_numpy (share) vs tensor (copy), .numpy(), .item()

import numpy as np
arr = np.array([1, 2, 3])
t_shared = torch.from_numpy(arr)       # memory SHARE (Lab 0 comment)
t_copy = torch.tensor(arr)             # data COPY
arr[0] = 100                           # numpy array badla...
print(t_shared, t_copy)                # shared wala bhi badla, copy wala nahi

t = torch.tensor([1., 2., 3.])
n = t.numpy()                          # CPU tensor -> numpy (memory share)
t[1] = 50.
print(n)

loss = torch.tensor(0.1793)
print(loss.item(), type(loss.item()))  # 1 element wala tensor -> Python float
print(torch.tensor([[1, 2], [3, 4]]).tolist())   # poora tensor -> Python list

Output

tensor([100,   2,   3]) tensor([1, 2, 3])
[ 1. 50.  3.]
0.179299995303154 <class 'float'>
[[1, 2], [3, 4]]

PyTorch · requires_grad wale tensor ko numpy banana

w = torch.tensor([1.0, 2.0], requires_grad=True)
try:
    w.numpy()                              # graph se juda tensor -> numpy nahi
except RuntimeError as e:
    print("RuntimeError:", e)
print(w.detach().numpy())                  # detach = graph se alag copy-view, fir numpy

Output

RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
[1. 2.]
FunctionDirectionMemory
torch.from_numpy(arr)NumPy → Torchshare (dtype bhi same rehta, float64 → float64)
torch.tensor(arr)NumPy/list → Torchcopy
t.numpy()Torch → NumPyshare; sirf CPU tensor; requires_grad ho to pehle .detach()
t.item()1-element tensor → Python number— (Lab 5: loss.item(), accuracy.item())
t.tolist()Tensor → Python list— (Lab 1: pred.squeeze().tolist())

Exam Radar

  • from_numpy shares memory; torch.tensor copies (Lab 0 ke comments me hi likha tha).
  • loss.item() se list me Python float daalo — tensor daaloge to graph bhi pakde rakhoge (memory badhegi).
  • .item() sirf 1 element wale tensor pe; zyada elements pe error.

3.23 Device — CPU ya GPU, aur .to(device)

Kahani

Do kitchen hain: ghar ki chhoti kitchen (CPU) aur hotel ki badi kitchen jisme 100 cook ek saath kaam karte hain (GPU). Sabzi aur cook same kitchen me hone chahiye — sabzi ghar pe aur cook hotel me ho to khana nahi banega. Data aur model, dono ek hi device pe.

PyTorch · device-agnostic code (yahan CPU machine pe chala)

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(device)                                  # GPU mila to cuda, warna cpu

x = torch.rand(2, 3).to(device)                # data device pe
model = nn.Linear(3, 1).to(device)             # model ke parameters bhi wahi
print(x.device, next(model.parameters()).device)
print(model(x).shape)                          # dono same device -> chalega

x_back = x.cpu().numpy()                       # numpy sirf CPU pe: pehle .cpu()
print(type(x_back))

Output

cpu
cpu cpu
torch.Size([2, 1])
<class 'numpy.ndarray'>

Colab pe GPU ho to pehli line cuda chhapegi aur tensors device='cuda:0' dikhayenge (Lab 0: tensor([[0.0911, 0.6344, 0.3142], …], device='cuda:0')). Lab 0 ne seedha GPU pe bhi banaya: torch.rand((2, 3), device=device). Agar model GPU pe aur data CPU pe reh gaya to error aata hai jaisa: "Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!". Aur GPU tensor ko seedha .numpy() nahi kar sakte — pehle .cpu().

Exam Radar

  • Standard line: device = torch.device('cuda' if torch.cuda.is_available() else 'cpu').
  • Tensor ke liye x = x.to(device) — naya tensor return hota hai, wapas assign karna padta hai. Model ke liye model.to(device) model ko khud move kar deta hai.
  • Training loop me har batch: xb, yb = xb.to(device), yb.to(device).
  • GPU timing ke liye torch.cuda.synchronize() (Lab 0).

Galti mat karna

  • Sirf x.to(device) likh dena bina x = ke — tensor wahin ka wahin rahega.

3.24 requires_grad — ek chhota trailer

Yaad hai tensor ki doosri superpower? Agar kisi tensor pe requires_grad=True laga do, to PyTorch uske saath hone wale har operation ki "diary" (computational graph) rakhta hai — har result pe grad_fn likha hota hai ki wo kaise bana. Fir kisi final number (loss) pe .backward() bulao, aur PyTorch chain rule (2.11) lagake har requires_grad wale tensor ka derivative uske .grad me rakh deta hai. nn.Linear jaise layers ke weights pe ye flag pehle se ON hota hai; input data pe nahi. with torch.no_grad(): (3.10) diary likhna band karta hai. Gradients jodte (accumulate) jaate hain, isliye training loop me har step optimizer.zero_grad() — poori kahani Chapter 12 aur Chapter 13 me.

PyTorch · requires_grad, grad_fn, backward, .grad

w = torch.tensor(2.0, requires_grad=True)     # "is knob ka gradient chahiye"
x = torch.tensor(3.0)                          # data: gradient nahi chahiye
y = w * x + 1
print(y, y.requires_grad)                      # grad_fn = graph ki yaad
y.backward()                                   # dy/dw nikalo
print(w.grad)                                  # dy/dw = x = 3
print(nn.Linear(2, 1).weight.requires_grad)    # layers ke weights by default True

Output

tensor(7., grad_fn=<AddBackward0>) True
tensor(3.)
True

Exam Radar

  • requires_grad=True → graph banta hai; loss.backward() → .grad bharta hai; gradients accumulate hote hain.
  • Model parameters: requires_grad=True by default. Data tensors: False.
  • grad_fn=<AddBackward0>, <AddmmBackward0> = "is operation se bana".

Dimaag me aise baitha lo

requires_grad = "is knob ki diary rakho". backward = "diary ulta padh ke har knob ka slope likh do".

Chapter 4

The Perceptron — ek neuron, ek faisla

Biological neuron · McCulloch–Pitts 1943 · Rosenblatt 1958 · weights & bias · step activation · decision boundary · logic gates · learning rule · convergence theorem · limitations

4.1 Shuruaat ek sawaal se: ek brain cell "sochta" kaise hai?

Kahani

Socho tumhari class ka WhatsApp group hai. Raat ko 10 baje message aata hai: "Kal class cancel?" Ab tum turant "haan, so jaata hoon" nahi bolte. Tum dekhte ho kaun bol raha hai. Class monitor bole to uski baat ka weight zyada hai. Wo dost jo har hafte jhooth bolta hai, uski baat ka weight kam (ya ulta, negative!). Tum sab messages ko apne dimaag me jodte ho, aur jab total "bharosa" ek limit (threshold) cross kar jaata hai, tab tum faisla lete ho: "Theek hai, alarm band, kal nahi jaana."

Bas yahi kaam tumhare dimaag ka har ek neuron (brain cell) karta hai: receive → add up → fire. Bahut saare signals lo, unko jodo, aur agar total ek threshold se upar gaya to "fire" karo (signal aage bhejo), warna chup raho. Is chapter me hum isi chhote se idea ko math me badlenge. Wahi math ka neuron kehlata hai Perceptron.

Teacher ki slides ka pehla sawaal tha: "How can a single brain cell carry out reasoning?" Aur jawab dene wala koi computer scientist nahi tha, balki ek neurologist tha: Santiago Ramón y Cajal. Lagbhag 1888 me unhone dikhaya ki brain alag-alag cells (neurons) se bana hai jo signal ek hi direction me pass karte hain. Isliye unko "father of modern neuroscience" kehte hain.

Biological neuron ke 4 hisse (exam me naam pooche jaate hain)

HissaKaam (simple bhasha)Artificial neuron me kya bana
DendritesPed ki tehniyon jaise branches; doosre neurons se signal receive karte hainInputs x1, x2, …, xn
SynapseDo neurons ke beech ka joint; kuch connections strong, kuch weakWeights w1, w2, …, wn (connection ki strength / importance)
Cell body (Soma)Saare aate signals ko jodta hai, decide karta hai fire karna hai ya nahiWeighted sum Σ wixi + b aur threshold check (activation)
AxonLambi wire; fire hua signal aage doosre neurons tak le jaata haiOutput ŷ (jo agle neuron ka input banega)
Biological neuron Dendrites: input lo Soma: jodo + decide Axon: aage bhejo Synapse: strength Artificial neuron x₁ x₂ x₃ w₁ w₂ w₃ Σ bias b ŷ z = Σ wᵢxᵢ + b step dendrites → inputs · synapse → weights soma → Σ + threshold · axon → output ŷ
Left: asli neuron (dendrites → soma → axon → synapse). Right: uska math version. Mapping yaad rakho: synapse ki strength = weight.

4.2 Biology se Math tak: McCulloch–Pitts (1943) aur Rosenblatt (1958)

Teacher ki slide kehti hai: "To carry out logic, there must be math inside the cell." Wo math hai: ek weighted sum, phir ek threshold:

fire (output 1) agar   Σ wixi ≥ θ    warna output 0
  • xi = i-th input (jaise dendrite pe aaya signal)
  • wi = us input ka weight: kitna important hai (synapse ki strength)
  • Σ wixi = har input ko uske weight se guna karke sab jodo
  • θ (theta) = threshold: kitna total chahiye "fire" karne ke liye

1943 — McCulloch & Pitts neuron (MP neuron)

Warren McCulloch (neuroscientist) aur Walter Pitts (logician) ne 1943 me is "sum-and-threshold" cell ko exact mathematics me likha aur poocha: "ye kya-kya compute kar sakta hai?" Unka shocking result: ek hi cell AND, OR, NOT jaise logic gates ban sakta hai, aur logic gates se poora computer banta hai. To aise cells ka network koi bhi logic circuit bana sakta hai.

1958 — Rosenblatt ka Perceptron

Frank Rosenblatt ne perceptron diya (idea/report 1957, famous paper 1958; isliye kahin 1957 likha milega, kahin 1958 — dono same cheez). Bada farak: weights ab data se khud seekhe jaate hain (learning rule, section 4.8). Rosenblatt ne "Mark I Perceptron" naam ki machine bhi banayi thi jo simple images pehchaanti thi. Worksheet ke timeline me ye "First Golden Age" ka hissa hai (Turing Test 1950, Dartmouth 1956, Perceptron 1957/58, ADALINE 1959), jo 1969 me Minsky & Papert ke XOR proof se khatam hua (Chapter 6 me poori kahani).

McCulloch–Pitts neuron (1943)

  • Binary inputs/outputs
  • Weights + threshold haath se set
  • No learning
  • Dikhaya: ek neuron = logic gate

Rosenblatt Perceptron (1958)

  • Real-valued inputs bhi chalenge
  • Weights + bias learnable
  • Perceptron learning rule se training
  • Ek linear classifier (seedhi line se baantna)

4.3 Perceptron ke parts: inputs, weights, bias, weighted sum

Kahani — Ramu ka "chai ya nahi" faisla

Ramu college ke bahar chai stall chalata hai. Har subah wo decide karta hai: "Aaj extra doodh mangwaun ya nahi?" Wo do cheezein dekhta hai: x1 = aaj thand hai? (1 = haan, 0 = nahi), x2 = aaj exam hai? (1 = haan; exam ke din bachche tension me zyada chai peete hain!). Ramu ke liye thand bahut important hai, to uska weight w1 = 3; exam thoda kam important, w2 = 2. Ramu ka thoda kanjoos nature hai: wo tabhi extra doodh mangwata hai jab total "score" kam se kam 4 ho. Ye 4 uska threshold hai.

Thand + exam dono: 3·1 + 2·1 = 5 ≥ 4 → mangwao. Sirf thand: 3 < 4 → mat mangwao. Ye Ramu ka dimaag = ek perceptron!

Threshold θ se bias b kaise bana? (lecture ka "small rewrite")

Teacher ne slide pe ek chhoti si trick dikhayi. Rule tha: fire agar w1x1 + w2x2 ≥ θ. Threshold ko left side le aao:

Threshold → bias, line by line

w1x1 + w2x2 ≥ θ
w1x1 + w2x2 − θ ≥ 0
let b = −θ   (b is called the bias)
w1x1 + w2x2 + b ≥ 0

"Nothing has changed — it is the same rule, written more conveniently." Ab har neuron ka sawaal ek hi hai: z ka sign kya hai? Positive (ya zero) → 1, negative → 0. Ramu ke case me θ = 4, to b = −4.

z = w1x1 + w2x2 + … + wnxn + b = Σi wixi + b = w·x + b
ŷ = φ(z) = step(z)
  • z = weighted sum, isko pre-activation ya "net input" ya "logit" bhi kehte hain
  • w·x = dot product (Chapter 2 wala): matching entries ko guna karo, sab jodo
  • b = bias = −θ (threshold ka negative)
  • φ = activation function (perceptron me step function)
  • ŷ ("y-hat") = model ka prediction; asli answer ko y likhte hain

Worksheet ka "Terminology Alert" bhi yahi likhta hai: y = φ(Σi wixi + b), jahan xi = i-th input feature, wi = uska weight, b = bias term, φ = activation function.

Bias ki kahani: strict teacher vs lenient teacher

Kahani

Do teachers ek hi answer sheet check kar rahi hain. Dono ke liye har sawaal ka weight same hai. Par Sharma ma'am strict hain: pass hone ke liye 60 chahiye. Verma ma'am lenient hain: 30 me pass. Same marks, alag result! Ye farak bias ka hai. Bias inputs ko nahi dekhta; wo bas poore faisle ko pehle se ek taraf jhuka deta hai.

  • Bada positive bias (lenient teacher): z pehle se upar hai, neuron aasani se fire karta hai.
  • Bada negative bias (strict teacher): z pehle se neeche hai, fire karne ke liye inputs ko bahut zor lagana padega.
  • Bias nahi hota to? Saare inputs 0 hone pe z = 0 hamesha. Decision boundary ko origin (0, 0) se guzarna padta. Bias line ko origin se door shift karne deta hai, jaise y = mx + c me c line ko upar-neeche karta hai.

Chalo numbers se samjhein

Sawaal: Ramu ka perceptron: w1 = 3, w2 = 2, θ = 4 (to b = −4). Teen din ke liye output nikalo.

  1. Din 1 (thand + exam, x = (1, 1)): z = 3·1 + 2·1 − 4 = 1 ≥ 0 → ŷ = 1 (doodh mangwao)
  2. Din 2 (sirf thand, x = (1, 0)): z = 3·1 + 2·0 − 4 = −1 < 0 → ŷ = 0
  3. Din 3 (sirf exam, x = (0, 1)): z = 0 + 2 − 4 = −2 < 0 → ŷ = 0

Jawab: sirf (1, 1) pe fire. Dhyan do: ye AND gate jaisa behave kar raha hai! (Section 4.6)

4.4 Step activation: haan ya na, beech me kuch nahi

Kahani

Traffic signal socho, par sirf do rang wala: lal ya hara. "Thoda sa hara" nahi hota. Perceptron ka step function bhi aisa hi hai: z zero ya usse upar → 1, warna → 0. Isko Heaviside step function ya "unit step" bhi kehte hain.

step(z) = 1  agar z ≥ 0;    0  agar z < 0
sign(z) = +1  agar z ≥ 0;    −1  agar z < 0   (±1 wala form)
  • 0/1 form: labels {0, 1}. Hamare course (lab + worksheets) me yahi use hota hai: "output 1 if z ≥ 0, else 0".
  • ±1 form (sign function): labels {−1, +1}. Kai textbooks ka original perceptron isi form me likha hota hai. Logic same hai; sirf class ke naam alag.
  • Boundary convention: z = 0 pe kuch log 1 dete hain, kuch 0. Course ka lab code (z >= 0) use karta hai, to z = 0 → 1.
step(z): 0 ya 1 1 0 z z = 0 pe 1 (bhara dot) sign(z): −1 ya +1 +1 −1 z
Step function ek "jump" hai: z = 0 pe achanak 0 se 1. Iska slope har jagah 0 hai (aur z = 0 pe defined hi nahi) — ye baat baad me badi problem banegi.

Output sirf 0 ya 1 hai, koi number nahi — ek category hai. Teacher ki slide: "Not a number — a category. That makes the perceptron a classifier." Do features (x1, x2), do classes: perceptron ka kaam hai ek boundary kheenchna jo dono colours ko alag kar de.

Galti mat karna

  • Perceptron ka output probability nahi hai. Sirf hard 0/1 (ya ±1). Probability chahiye to sigmoid lagao (Chapter 5, logistic regression).
  • Agar step ki jagah sigmoid lagao to worksheet ke hisaab se wo "structurally identical to logistic regression" ho jaata hai: "a perceptron is a single logistic regression unit".

4.5 Decision boundary: perceptron hamesha ek seedhi line kheenchta hai

Kahani

Gaon me do kheton ke beech ek baad (fence) hoti hai. Ek taraf Sharma ji ka khet, doosri taraf Verma ji ka. Fence pe khade ho to tum kisi ke khet me nahi. Perceptron bhi plane (x1, x2 wali graph sheet) pe ek fence lagata hai: ek taraf class 1, doosri taraf class 0. Teacher ki slide: "The boundary is the fence — the exact place the decision flips."

Decision poora ka poora sign check hai:

ConditionMatlabOutput
w1x1 + w2x2 + b > 0line ke "positive" side1 (class 1)
w1x1 + w2x2 + b = 0bilkul line ke upar (boundary)flip point (course convention me 1)
w1x1 + w2x2 + b < 0line ke "negative" side0 (class 0)
Decision boundary:   w1x1 + w2x2 + b = 0
(rearrange):   x2 = −w1w2x1 − bw2    (school wala y = mx + c)
  • slope m = −w1/w2: weights line ka jhukaav (tilt) decide karte hain
  • intercept c = −b/w2: bias line ko shift karta hai (bias ki kahani yaad hai?)
  • Ye equation line kyun hai? Slide: "x1 and x2 appear only to the first power — no x2, no x1x2. There is no curve anywhere in w·x + b. The straight line is baked into the formula."
  • 3 inputs → boundary ek plane; n inputs → hyperplane (n − 1 dimension ki "flat" cheez). Worksheet: "Σi wixi + b = 0 is a hyperplane (a straight line in 2D)".

Chalo numbers se samjhein (lecture ka example)

Sawaal: w1 = 2, w2 = 3, b = −12. Boundary kya hai, aur points (3, 3), (1, 1), (6, 2) kis class me hain?

  1. Rule: 2x1 + 3x2 − 12 ≥ 0 → class 1
  2. Boundary: 2x1 + 3x2 − 12 = 0. Intercepts: x1 = 0 rakho → 3x2 = 12 → x2 = 4. x2 = 0 rakho → 2x1 = 12 → x1 = 6. To line (0, 4) aur (6, 0) ko jodti hai. Slope = −2/3.
  3. Point (3, 3): z = 2·3 + 3·3 − 12 = 6 + 9 − 12 = 3 > 0 → class 1 (line ke upar)
  4. Point (1, 1): z = 2 + 3 − 12 = −7 < 0 → class 0 (line ke neeche)
  5. Point (6, 2): z = 12 + 6 − 12 = 6 > 0 → class 1
  6. Point (3, 2): z = 6 + 6 − 12 = 0 → bilkul line pe (boundary point).

Jawab: line ke upar (origin se door wali side) → class 1; neeche (origin wali side) → class 0. "One straight cut, two classes. This is a linear separation."

x₁ x₂ 123456 1234 w = (2, 3) line ke ⊥, class 1 ki taraf (3,3): z = 3 (6,2): z = 6 (1,1): z = −7 (3,2): z = 0 2x₁ + 3x₂ − 12 = 0 class 1 side: z > 0 class 0 side: z < 0
Lecture wala example. Peela area = class 1 (z > 0). Lal line = boundary (z = 0). Weight vector w = (2, 3) line pe perpendicular hai aur positive side ki taraf point karta hai.

w line pe perpendicular (⊥) kyun hota hai? — 3 line ka proof

Line pe koi bhi do points p aur q lo. Dono line pe hain, to w·p + b = 0 aur w·q + b = 0. Dono ko subtract karo: w·(p − q) = 0. Lekin p − q line ki direction hai, aur dot product zero ka matlab hai "90 degree". To w boundary ke perpendicular hai. Aur agar boundary se w ki direction me thoda chalo (x = p + tw), to z = t‖w‖2 > 0. Isliye w hamesha class 1 wali side ki taraf point karta hai. Bonus: origin se line ki doori = |b|/‖w‖ = 12/√13 ≈ 3.328 (upar wale example me).

Punchline: activation sirf sign padhta hai

Teacher ki slide ka "punchline": "The straight boundary comes entirely from the linear combination. Step or sigmoid, the line stays a line." Step lagao ya sigmoid, output kahan flip hota hai wo z = 0 pe hi decide hota hai, aur z = 0 ek seedhi line hai. Isliye ek single neuron kabhi curved boundary nahi bana sakta. Ye baat Chapter 6 (XOR) ka seed hai.

Galti mat karna

  • Weights ko double kar do (w → 2w, b → 2b) to boundary same rehti hai (0 = 0 hi rehta hai). Sirf z ka size badhta hai. Line ka "position" ratio se decide hota hai.
  • Saare weights aur bias ka sign ulta karo (w → −w, b → −b) to line same, par classes swap ho jaati hain.
  • Slope −w1/w2 hai, w1/w2 nahi. Minus sign mat bhoolo.

4.6 Ek neuron = koi bhi basic logic gate (AND, OR, NAND, NOT)

Kahani

Ghar ka main gate socho jisme do taale hain. Gate tabhi khulta hai jab dono chaabiyan lagi hon: ye AND. Ab Diwali ki lights socho jo do switch me se kisi bhi ek se jal jaati hain: ye OR. Aur ek ulta switch jo daba ho to light band, chhoda ho to jale: ye NOT. McCulloch–Pitts ka 1943 wala "electrifying result" yahi tha: "One cell can be any gate" — AND, OR, NOT in one cell. Aur gates jod-jod ke poora computer banta hai.

Weights kaise sochein? (trick)

GateLecture (threshold form)Same in bias formLab 1 / Worksheet (safe margin)
ANDw1 = 1, w2 = 1, threshold ≥ 2b = −2w = (1, 1), b = −1.5
ORw1 = 1, w2 = 1, threshold ≥ 1b = −1w = (1, 1), b = −0.5
NOTw = −1, threshold ≥ 0b = 0(safer) w = −1, b = 0.5
NANDw = (−1, −1), threshold ≥ −1.5b = 1.5w = (−1, −1), b = 1.5 (Lab 1)

"Safe margin" kya hai? Lecture ke AND (b = −2) me input (1, 1) pe z = 1 + 1 − 2 = 0, bilkul boundary pe — sirf "≥" convention ki wajah se 1 aata hai. Lab ka b = −1.5 line ko points ke beech me rakhta hai, to har point pe z = ±0.5, koi confusion nahi. Dono sahi hain; ek hi gate ke liye infinite valid weight choices hoti hain.

Poori truth table check (Lab 1 weights)

x1x2AND: z = x1 + x2 − 1.5ŷOR: z = x1 + x2 − 0.5ŷNAND: z = −x1 − x2 + 1.5ŷ
00−1.50−0.501.51
01−0.500.510.51
10−0.500.510.51
110.511.51−0.50

NOT (w = −1, b = 0, lecture): x = 0 → z = 0 ≥ 0 → 1; x = 1 → z = −1 < 0 → 0. ✔ Ulta ho gaya.

Jawab: har row ka ŷ asli truth table se match karta hai. Ek neuron = ek gate.

AND: x₁ + x₂ − 1.5 = 0 x₁x₂ (1,1) → 1 0 1 ke beech: 0.5 → 1 ke liye line OR: x₁ + x₂ − 0.5 = 0 x₁x₂ sirf (0,0) neeche wali side 1
● = output 1, × = output 0 (worksheet wala convention). Dono gates me ek seedhi line kaafi hai: AND aur OR linearly separable hain.

Definition — Linearly separable

Worksheet: "A dataset is linearly separable if a single straight line (in 2D) — or a hyperplane in higher dimensions — can perfectly separate its classes." AND, OR, NAND, NOT: sab linearly separable. XOR: nahi (Chapter 6).

Code me ye check (tensors se, Chapter 3 wala @ matmul). Lab 1 me yahi kaam nn.Linear se hota hai, wo Chapter 5 me dekhenge.

PyTorch · gates with hand-set weights

import torch

X = torch.tensor([[0., 0.],
                  [0., 1.],
                  [1., 0.],
                  [1., 1.]])          # 4 input pairs, shape (4, 2)

def perceptron(X, w, b):
    z = X @ w + b                    # (4, 2) @ (2,) -> (4,)  har row ka w·x + b
    return (z >= 0).int()            # step: z >= 0 -> 1, warna 0

print("AND :", perceptron(X, torch.tensor([1., 1.]), -1.5).tolist())
print("OR  :", perceptron(X, torch.tensor([1., 1.]), -0.5).tolist())
print("NAND:", perceptron(X, torch.tensor([-1., -1.]), 1.5).tolist())
x_not = torch.tensor([[0.], [1.]])   # NOT ka sirf ek input
print("NOT :", perceptron(x_not, torch.tensor([-1.]), 0.0).tolist())

Output

AND : [0, 0, 0, 1]
OR  : [0, 1, 1, 1]
NAND: [1, 1, 1, 0]
NOT : [1, 0]

Lekin teacher ka agla sawaal: "But who chose those weights?" Abhi tak humne haath se chune. "In a real network, no one sets them. The weights are dynamic — chosen automatically, adjusting themselves from data as the model learns." To ab seekhte hain learning.

4.7 Perceptron learning rule: galti hui? to thoda sudhaaro

Kahani — cricket coach

Naya batsman net practice kar raha hai. Ball off-side pe aayi, usne leg-side shot maara: galti. Coach bolta hai "bat thoda off ki taraf ghumao". Agli ball pe shot sahi gaya: coach kuch nahi bolta. Coach sirf galti pe correction deta hai, aur correction ki direction galti ki direction ke ulti hoti hai. Correction kitna bada ho, ye coach ka "learning rate" hai: zyada bada correction diya to batsman over-correct karke doosri taraf galti karega.

Teacher ki slide: "Made a mistake? Go back and adjust." Perceptron bhi ek-ek example dekhta hai, predict karta hai, aur sirf galti hone pe weights ko sahi direction me thoda sa khiskaata hai.

wi ← wi + η (y − ŷ) xi
b ← b + η (y − ŷ)
  • ← = "purani value ko nayi value se replace karo" (update)
  • η (eta) = learning rate: ek step kitna bada (jaise 0.1, 0.2, 1)
  • y = sahi answer (label), ŷ = perceptron ka prediction
  • (y − ŷ) = error: sirf teen values possible: 0 (sahi), +1 (bola 0, hona tha 1), −1 (bola 1, hona tha 0)
  • xi = us example ka i-th input. Bias ka "input" hamesha 1 maano, isliye bias update me x nahi dikhta.

Formula aisa kyun dikhta hai? (teen cases)

CaseError y − ŷUpdateAsar
Sahi prediction0kuch nahi"Jo theek chal raha hai use mat chhedo"
y = 1 par ŷ = 0 (z bahut chhota tha)+1w ← w + ηx, b ← b + ηis x pe z badhega
y = 0 par ŷ = 1 (z bahut bada tha)−1w ← w − ηx, b ← b − ηis x pe z ghatega

Chhota sa proof ki direction sahi hai: error +1 wale case me naya z' = (w + ηx)·x + (b + η) = z + η(‖x‖2 + 1). Kyunki η(‖x‖2 + 1) hamesha positive hai, z pakka badhta hai, yaani prediction 1 ki taraf khiskti hai. Error −1 me bilkul ulta: z pakka ghatta hai. (Ek update me hi sahi ho jaaye, ye guarantee nahi; bas sahi direction ki guarantee hai.)

Definition — Epoch

Epoch = poore training data pe ek baar poora chakkar. AND ke 4 examples hain, to 1 epoch = 4 examples ko ek-ek karke dekhna. Training tab rukti hai jab ek poore epoch me zero mistakes hon (ya max epochs khatam).

Poori training run, haath se — AND gate

Shuruaati values: w1 = 0.4, w2 = 0.4, b = −0.1, η = 0.2. Rule: ŷ = 1 agar z ≥ 0. Data order: (0,0)→0, (0,1)→0, (1,0)→0, (1,1)→1.

Epxyz = w1x1 + w2x2 + bŷy − ŷNaya (w1, w2), b
1(0,0)00 + 0 − 0.1 = −0.100(0.4, 0.4), −0.1  (no change)
1(0,1)00 + 0.4 − 0.1 = 0.31−1w₁ = 0.4 + 0.2(−1)(0) = 0.4; w₂ = 0.4 + 0.2(−1)(1) = 0.2; b = −0.1 − 0.2 = −0.3
1(1,0)00.4 + 0 − 0.3 = 0.11−1w₁ = 0.4 − 0.2 = 0.2; w₂ = 0.2; b = −0.3 − 0.2 = −0.5
1(1,1)10.2 + 0.2 − 0.5 = −0.10+1w₁ = 0.2 + 0.2 = 0.4; w₂ = 0.2 + 0.2 = 0.4; b = −0.5 + 0.2 = −0.3
2(0,0)0−0.300(0.4, 0.4), −0.3
2(0,1)00.4 − 0.3 = 0.11−1w₂ = 0.4 − 0.2 = 0.2; b = −0.3 − 0.2 = −0.5 → (0.4, 0.2), −0.5
2(1,0)00.4 − 0.5 = −0.100(0.4, 0.2), −0.5
2(1,1)10.4 + 0.2 − 0.5 = 0.110(0.4, 0.2), −0.5
3(0,0)0−0.500—
3(0,1)00.2 − 0.5 = −0.300—
3(1,0)00.4 − 0.5 = −0.100—
3(1,1)10.4 + 0.2 − 0.5 = 0.110—
  1. Epoch 1: 3 mistakes (do baar "zyada fire kiya", ek baar "kam fire kiya").
  2. Epoch 2: 1 mistake.
  3. Epoch 3: 0 mistakes → training ruk gayi. Total 4 updates.

Jawab: seekhe hue weights w1 = 0.4, w2 = 0.2, b = −0.5. Boundary: 0.4x1 + 0.2x2 − 0.5 = 0 (yaani x2 = 2.5 − 2x1). Ye lab ke (1, 1, −1.5) se alag hai, phir bhi AND sahi solve karta hai — perceptron koi ek valid line dhoondhta hai, "best" wali nahi.

x₁x₂ 011 - - shuru: 0.4x₁ + 0.4x₂ − 0.1 = 0 (x₁ + x₂ = 0.25) → 2 points galat side — end (epoch 3): 0.4x₁ + 0.2x₂ − 0.5 = 0 (x₂ = 2.5 − 2x₁) → sirf (1,1) upar 4 updates me line khisak ke sahi jagah pahunch gayi
Training ka matlab hi yahi hai: har galti pe line thodi ghoomti/khisakti hai, jab tak saare points sahi side na aa jaayein.

4.8 Code: perceptron from scratch + training loop

Ab yahi poora algorithm code me. Class (Chapter 3 wala class/__init__/self) me teen kaam: weights rakhna, predict karna, fit (train) karna.

PyTorch tensors · perceptron from scratch

import torch

class Perceptron:
    def __init__(self, w, b, lr):
        self.w = torch.tensor(w)          # weights, shape (2,)
        self.b = torch.tensor(b)          # bias, ek scalar tensor
        self.lr = lr                      # learning rate (eta)

    def predict(self, x):
        z = torch.dot(self.w, x) + self.b # z = w.x + b
        return 1 if z >= 0 else 0         # step activation

    def fit(self, X, y, max_epochs=10):
        for epoch in range(1, max_epochs + 1):
            mistakes = 0
            for xi, yi in zip(X, y):      # ek-ek example dekho
                y_hat = self.predict(xi)
                error = int(yi) - y_hat   # +1, 0 ya -1
                if error != 0:            # sirf galti pe update
                    self.w = self.w + self.lr * error * xi
                    self.b = self.b + self.lr * error
                    mistakes += 1
            print(f"Epoch {epoch}: mistakes={mistakes}, "
                  f"w={[round(v, 2) for v in self.w.tolist()]}, b={self.b.item():.2f}")
            if mistakes == 0:             # poora epoch bina galti -> ruk jao
                print("Converged! Ek poora epoch bina galti ke.")
                break

X = torch.tensor([[0., 0.], [0., 1.], [1., 0.], [1., 1.]])
y = torch.tensor([0, 0, 0, 1])           # AND ke labels

p = Perceptron(w=[0.4, 0.4], b=-0.1, lr=0.2)
p.fit(X, y)
print("Predictions:", [p.predict(xi) for xi in X])
print("Targets:    ", y.tolist())

Output

Epoch 1: mistakes=3, w=[0.4, 0.4], b=-0.30
Epoch 2: mistakes=1, w=[0.4, 0.2], b=-0.50
Epoch 3: mistakes=0, w=[0.4, 0.2], b=-0.50
Converged! Ek poora epoch bina galti ke.
Predictions: [0, 0, 0, 1]
Targets:     [0, 0, 0, 1]

Output bilkul haath wali table se match karta hai (epoch 1 ke end me w wapas (0.4, 0.4) aur b = −0.3, kyunki aakhri update ne w ko wapas badha diya tha).

4.9 Perceptron Convergence Theorem

Kahani

Agar do mohallon ke beech ek seedhi sadak bana sakna possible hai, to ek mehnati engineer (perceptron) jo har galti pe thoda adjust karta hai, pakka kuch hi koshishon me wo sadak dhoondh lega. Par agar mohalle aise ulajhe hain ki koi seedhi sadak possible hi nahi, to engineer zindagi bhar adjust karta rahega, kabhi rukega nahi.

Theorem (Rosenblatt; proof by Novikoff, 1962)

Agar training data linearly separable hai, to perceptron learning rule finite (gine-chune) updates ke baad ek aisi line/hyperplane pe pahunch jaata hai jo saare training points ko sahi classify kare — converge karna guaranteed hai. Mistakes ki sankhya ≤ (R/γ)2, jahan R = sabse bade input ki length, γ = margin (sabse kareeb point aur best line ki doori). Agar data linearly separable nahi hai (jaise XOR), to rule kabhi converge nahi karta; weights hamesha ghoomte (cycle) rehte hain.

Short me exam line: perceptron converges iff (if and only if) data linearly separable hai. Upar wale same code ko XOR labels [0, 1, 1, 0] pe chalao to dekho:

Same Perceptron class, XOR labels

y_xor = torch.tensor([0, 1, 1, 0])
p2 = Perceptron(w=[0.4, 0.4], b=-0.1, lr=0.2)
p2.fit(X, y_xor, max_epochs=6)          # kabhi "Converged!" nahi aayega

Output

Epoch 1: mistakes=1, w=[0.2, 0.2], b=-0.30
Epoch 2: mistakes=2, w=[0.0, 0.2], b=-0.30
Epoch 3: mistakes=3, w=[0.0, 0.2], b=-0.10
Epoch 4: mistakes=2, w=[0.0, 0.0], b=-0.10
Epoch 5: mistakes=2, w=[-0.2, 0.0], b=-0.10
Epoch 6: mistakes=3, w=[-0.2, 0.0], b=0.10

Har epoch me galtiyan, kabhi zero nahi. Kitne bhi epochs chalao, aisa hi chalega.

4.10 Perceptron ki limitations (kyun aage badhna pada)

  1. Sirf seedhi line (linear boundary): curved ya do-line wali boundary nahi bana sakta → XOR solve nahi kar sakta (Minsky & Papert, 1969 — Chapter 6).
  2. Non-separable data pe converge nahi karta: weights oscillate karte rehte hain (upar dekha).
  3. Step function differentiable nahi: slope har jagah 0 (aur z = 0 pe undefined). Isliye gradient descent / backpropagation se multi-layer network train nahi ho sakta. Hal: smooth activations jaise sigmoid (Chapter 9).
  4. Hard 0/1 output: koi confidence/probability nahi. "51% sure" aur "99% sure" dono 1. Hal: logistic regression (Chapter 5).
  5. Ek perceptron = sirf 2 classes (binary). Multi-class ke liye kai neurons chahiye.
  6. Jo line milti hai wo unique/best nahi: initial weights, η aur data order pe depend karti hai; margin maximize nahi karta (wo SVM karta hai).
  7. Features khud nahi bana sakta: raw inputs pe seedha ek line. Naye features (jaise OR, NAND) banane ke liye hidden layer chahiye (Chapter 6–7).

Exam Radar

  • Cajal (~1888): brain alag neurons se bana; signal one direction. Neuron: receive → sum → fire.
  • Biological → artificial: dendrites = inputs, synapse strength = weights, soma = sum + threshold, axon = output.
  • McCulloch & Pitts = 1943 (binary, fixed weights, no learning, logic gates). Rosenblatt Perceptron = 1958 (1957 report) — learnable weights. Minsky & Papert "Perceptrons" = 1969 → XOR → first AI winter.
  • z = Σ wixi + b; ŷ = step(z); b = −θ. Course convention: z ≥ 0 → 1.
  • n inputs wale perceptron me learnable parameters = n weights + 1 bias = n + 1.
  • Boundary w·x + b = 0 = line (2D) / plane (3D) / hyperplane (nD). Slope = −w1/w2, intercept = −b/w2. w ⊥ boundary, positive side ki taraf. Distance from origin = |b|/‖w‖.
  • Gates: AND (1, 1, −1.5) ya threshold 2; OR (1, 1, −0.5) ya threshold 1; NAND (−1, −1, 1.5); NOT (−1, b = 0) threshold 0.
  • Learning rule: w ← w + η(y − ŷ)x, b ← b + η(y − ŷ). Update sirf galti pe. Error ∈ {−1, 0, +1}.
  • Convergence theorem: linearly separable ⇒ finite steps me converge; non-separable (XOR) ⇒ never converges.
  • Perceptron + sigmoid = logistic regression unit. Perceptron = linear binary classifier.

Galti mat karna

  • "Perceptron koi bhi function seekh sakta hai" — galat. Single perceptron sirf linearly separable cheezein. "Network of perceptrons" (MLP) zyada kar sakta hai.
  • Learning rule me (ŷ − y) mat likh dena; sahi hai (y − ŷ) aur plus sign. (Gradient descent me minus sign aata hai, wo alag formula hai.)
  • Bias update me x nahi hota (bias ka input hamesha 1).
  • McCulloch–Pitts neuron learn nahi karta; learning Rosenblatt ne di.
  • Convergence ka matlab "zero training mistakes", "best possible line" nahi.

Dimaag me aise baitha lo

Perceptron = "jodo, check karo, faisla karo". Weights = importance, bias = mood. Ek neuron = ek seedhi line. Galti pe hi sudhaar: w += η·(y − ŷ)·x.

Chapter 5

Linear Models — seedhi line ki taaqat aur uski seema

Linear regression · matrix form · "linear" ka matlab · logistic regression · linear layers collapse proof · nn.Linear · Lab 1 & Lab 5 code

5.1 Linear regression: number predict karna

Kahani — Ramu ki chai ka hisaab

Yaad hai Ramu chai wala? Ab wo sirf "doodh mangwaun ya nahi" (haan/na) nahi, balki kitne cup bikenge ye guess karna chahta hai. Usne notice kiya: jitne zyada college students stall ke saamne se guzarte hain, utni zyada chai bikti hai; aur baarish ho to aur zyada. Usne apni diary me ek formula bana liya:

cups = 4 × (students, dahaai me) + 15 × (baarish? 1/0) + 10

"10" wo cups hain jo roz bikte hi hain (regular uncle log!) — ye bias hai. 4 aur 15 weights hain. Output ab category nahi, ek continuous number hai. Isko kehte hain regression, aur kyunki formula seedha weighted sum hai, linear regression.

ŷ = w1x1 + w2x2 + … + wdxd + b = w·x + b
  • ŷ = predicted number (cups, price, marks…) — koi bhi real value, −∞ se +∞
  • x = (x1, …, xd) = d features ka input vector
  • w = weights (har feature ka asar: "ye feature 1 unit badhe to ŷ kitna badhega")
  • b = bias/intercept (sab features 0 hon to bhi kitna)
  • Perceptron se farak: koi step function nahi. Output seedha z hi hai (activation = identity).

Chalo numbers se samjhein

Sawaal: w1 = 4, w2 = 15, b = 10. Aaj 50 students guzre (x1 = 5) aur baarish hai (x2 = 1). Kitne cup?

  1. Weights se guna: 4 × 5 = 20, 15 × 1 = 15
  2. Jodo + bias: 20 + 15 + 10 = 45

Jawab: ŷ = 45 cups.

x₁ = students (dahaai me) cups 2468 10203040 ŷ = 4x₁ + 10 (baarish nahi) slope = w₁ = 4, intercept = b = 10
Ek feature ho to linear regression = data ke beech se guzarti ek seedhi line. Training ka kaam: aisi w, b dhoondhna ki line points ke sabse kareeb ho (MSE loss chhota ho — Chapter 11).

5.2 Matrix form: ŷ = Xw + b (ek saath poora batch)

Ramu ke paas 3 din ka data hai. Har din ke liye alag-alag formula lagane ke bajaye, sab dinon ko ek matrix me rakh do: har row = ek din (sample), har column = ek feature. Phir ek hi matrix multiplication (Chapter 2) se saare answers.

ŷ = Xw + b
  • X = data matrix, shape (N, d): N samples (rows), d features (columns)
  • w = weight vector, shape (d,) — sab samples ke liye same weights
  • b = scalar bias, har row me add hota hai (PyTorch me isko broadcasting kehte hain)
  • ŷ = shape (N,): har sample ka ek prediction

Chalo numbers se samjhein

X =
51
30
81
,   w = (4, 15),   b = 10
  1. Row 1: 5·4 + 1·15 + 10 = 20 + 15 + 10 = 45
  2. Row 2: 3·4 + 0·15 + 10 = 12 + 0 + 10 = 22
  3. Row 3: 8·4 + 1·15 + 10 = 32 + 15 + 10 = 57

Jawab: ŷ = (45, 22, 57). Shape check: (3, 2) @ (2,) → (3,). ✔

Agar output bhi kai hon (jaise chai aur samose dono predict karne hain), to w vector ki jagah W matrix aa jaata hai: Ŷ = XW + b, jahan W shape (d, k) aur output (N, k). PyTorch is matrix ko transpose karke store karta hai — section 5.6 me.

5.3 "Linear" ka asli matlab kya hai?

Simple bhasha: output = inputs ka weighted sum (+ ek constant). Graph pe 1 input → seedhi line, 2 inputs → flat plane (kaagaz jaisa), zyada inputs → hyperplane. Koi mod (bend), koi curve nahi.

Function (input x me)Linear?Kyun
3x1 − 2x2 + 5Haan (affine)sirf weighted sum + constant
x2, x1x2, sin x, |x|Nahipower, product, curve, kink
σ(x), tanh(x), ReLU(x) = max(0, x)Nahiye activation functions isi liye hain: non-linearity laane ke liye
step(x)Nahiek jump hai

5.4 Logistic regression: linear + sigmoid = probability

Kahani — samosa lega ya nahi?

Ramu ab samose bhi bechta hai. Har customer ke aate hi wo andaza lagata hai: "Ye samosa lega?" Par ab wo perceptron jaisa hard "haan/na" nahi chahta, balki kitne percent chance — taaki 90% wale ko turant garam samosa dikhaye, aur 40% wale ko sirf poochhe. Do cheezein dekhta hai: x1 = bhookh kitni lag rahi hai (0–3), x2 = kitni jaldi me hai (0–3). Bhookh chance badhati hai (w1 = 0.8), jaldi chance ghatati hai (w2 = −0.4), aur b = −0.2.

Score z to koi bhi number ho sakta hai (−5, 2.3…). Use 0 se 1 ke beech dabane ke liye ek S-shaped function lagate hain: sigmoid. Bas! Linear + sigmoid = logistic regression.

z = w·x + b   →   ŷ = σ(z) = 11 + e−z = P(y = 1 | x)
class = 1 agar ŷ ≥ 0.5, warna 0    ⇔    class = 1 agar z ≥ 0
  • σ (sigma) = sigmoid/logistic function: kisi bhi real number ko (0, 1) me map karta hai
  • e ≈ 2.718 (Euler's number); e−z bada z pe chhota → σ ≈ 1; chhota z pe bada → σ ≈ 0
  • σ(0) = 1/(1 + 1) = 0.5 — isliye threshold 0.5 aur "z ≥ 0" ek hi baat hai
  • ŷ = model ka "probability ki class 1 hai"
  • Naam me "regression" hai par ye classification model hai (probability regress karta hai)

Chalo numbers se samjhein

Sawaal: z = 0.8x1 − 0.4x2 − 0.2. Customer A: (2, 1). Customer B: (0.5, 2).

  1. A ka z: 0.8·2 − 0.4·1 − 0.2 = 1.6 − 0.4 − 0.2 = 1.0
  2. A ki probability: e−1 = 0.3679 → σ = 1/(1 + 0.3679) = 1/1.3679 = 0.7311 → 0.7311 ≥ 0.5 → class 1 (samosa lega, 73% chance)
  3. B ka z: 0.8·0.5 − 0.4·2 − 0.2 = 0.4 − 0.8 − 0.2 = −0.6
  4. B ki probability: e0.6 = 1.8221 → σ = 1/(1 + 1.8221) = 1/2.8221 = 0.3543 → class 0 (35% chance)

Jawab: A → 0.7311 (class 1), B → 0.3543 (class 0).

z 1 0 threshold 0.5 (z = 0) A: z = 1.0 → 0.7311 B: z = −0.6 → 0.3543 z > 0 → class 1 z < 0 → class 0
Sigmoid ka S-curve. Output kabhi exactly 0 ya 1 nahi hota. Threshold 0.5 bilkul z = 0 pe hai — isliye boundary phir se w·x + b = 0 hi hai. (Sigmoid ki gehraai: Chapter 9.)

Decision boundary abhi bhi seedhi line kyun?

Class flip tab hota hai jab ŷ = 0.5, yaani σ(z) = 0.5, yaani z = 0, yaani 0.8x1 − 0.4x2 − 0.2 = 0 — ek seedhi line. Sigmoid sirf ye batata hai ki line se kitna door ho (kitna confident), boundary ka shape nahi badalta. Chapter 4 ka punchline: "Step or sigmoid, the line stays a line." Isliye logistic regression bhi XOR solve nahi kar sakta.

Perceptron

  • Activation: step
  • Output: hard 0/1
  • Training: perceptron rule (sirf galti pe)
  • Gradient: nahi (step flat hai)
  • Boundary: line

Logistic regression

  • Activation: sigmoid
  • Output: probability (0, 1)
  • Training: gradient descent + BCE loss (Ch 11)
  • Gradient: smooth, har jagah defined
  • Boundary: line (z = 0)

Code me (bina kisi layer ke, sirf tensors se):

PyTorch · logistic regression by hand

import torch

x = torch.tensor([[2.0, 1.0],      # customer A
                  [0.5, 2.0]])     # customer B -> shape (2, 2)
w = torch.tensor([0.8, -0.4])      # weights (bhookh +, jaldi -)
b = -0.2

z = x @ w + b                      # (2, 2) @ (2,) -> (2,)  raw scores
p = torch.sigmoid(z)               # 0 se 1 ke beech probability
pred = (p >= 0.5).int()            # threshold 0.5 -> class
print("z   :", z)
print("prob:", p)
print("pred:", pred)

Output

z   : tensor([ 1.0000, -0.6000])
prob: tensor([0.7311, 0.3543])
pred: tensor([1, 0], dtype=torch.int32)

5.5 KEY PROOF: linear layers ko stack karo, phir bhi ek hi linear layer

Kahani — photocopy ki photocopy

Ek photo ko 2× zoom karke photocopy karo, phir us copy ko 3× zoom karke photocopy karo. Result? Seedha 6× zoom wali ek photocopy. Do machines lagane ka koi extra fayda nahi hua — ek hi machine "6×" set karke wahi kar deti. Linear layers bhi bilkul aise hi hain: linear ke upar linear = linear. Kitni bhi layers laga do, bina activation ke sab milkar ek hi linear layer ban jaati hain.

Layer 1: h = W1x + b1    Layer 2: y = W2h + b2
y = W2(W1x + b1) + b2 = (W2W1)x + (W2b1 + b2) = W′x + b′
  • Step 1: h ki value layer 2 me daal di (substitute)
  • Step 2: matrix multiplication distribute hota hai: W2(A + B) = W2A + W2B
  • W′ = W2W1: do matrices ka product phir bhi ek matrix hai
  • b′ = W2b1 + b2: phir bhi ek vector hai
  • Shapes: W1 (h, d), W2 (o, h) → W′ (o, d): seedha input se output
  • Teen, chaar, sau layers? Har baar yahi trick → "Without φ: ANY depth of purely linear layers collapses to ONE linear transformation. Depth is useless without non-linearity." (teacher ki Activation slide)

Chalo numbers se samjhein

W1 =
12
01
, b1 = (1, −1),   W2 =
2−1
, b2 = 3,   input x = (1, 2).
  1. Do-layer raasta, layer 1: h₁ = 1·1 + 2·2 + 1 = 6, h₂ = 0·1 + 1·2 − 1 = 1 → h = (6, 1)
  2. Layer 2: y = 2·6 + (−1)·1 + 3 = 12 − 1 + 3 = 14
  3. Ab collapse: W′ = W₂W₁ = [2·1 + (−1)·0, 2·2 + (−1)·1] = [2, 3]
  4. b′ = W₂b₁ + b₂ = 2·1 + (−1)·(−1) + 3 = 2 + 1 + 3 = 6
  5. Ek-layer raasta: y = 2·1 + 3·2 + 6 = 2 + 6 + 6 = 14 ✔ same!

Jawab: dono raaste 14. Do layers = ek layer y = 2x1 + 3x2 + 6. Iski decision boundary ek seedhi line hi rahegi → XOR phir bhi nahi hoga.

x W₁x + b₁ W₂h + b₂ y = x W′x + b′ (sirf ek!) y W′ = W₂W₁ , b′ = W₂b₁ + b₂
Upar: do linear layers bina activation. Neeche: wahi kaam ek layer kar deti hai. Isliye har hidden layer ke baad non-linear activation (ReLU, sigmoid…) lagate hain.

PyTorch · collapse ko code me verify karo

import torch
import torch.nn as nn

torch.manual_seed(0)
layer1 = nn.Linear(2, 3)                 # 2 -> 3
layer2 = nn.Linear(3, 1)                 # 3 -> 1
x = torch.randn(5, 2)                    # 5 random samples

two_layers = layer2(layer1(x))           # bina activation ke stack

W_new = layer2.weight @ layer1.weight              # (1, 3) @ (3, 2) -> (1, 2)
b_new = layer2.weight @ layer1.bias + layer2.bias  # (1, 3) @ (3,) + (1,) -> (1,)
one_layer = x @ W_new.T + b_new                    # ek hi linear layer

print(W_new.shape, b_new.shape)
print(torch.allclose(two_layers, one_layer, atol=1e-6))   # dono same?

Output

torch.Size([1, 2]) torch.Size([1])
True

torch.allclose(a, b) = "kya dono tensors lagbhag barabar hain?" (floating point ki chhoti galti ignore karke). True aaya: proof code me bhi pakka.

5.6 nn.Linear — PyTorch ki linear layer (poori detail)

import torch.nn as nn ke andar sabse basic building block: nn.Linear. Isko "fully connected layer" ya "dense layer" bhi kehte hain. Ye ek saath kai neurons ka weighted sum nikalta hai.

nn.Linear(in_features, out_features, bias=True)
output = x @ WT + b     (PyTorch docs: y = xAT + b)
  • in_features = har sample me kitne input numbers (features). Input tensor ka last dimension isse match hona chahiye, warna shape error.
  • out_features = kitne output numbers = kitne neurons is layer me
  • bias=True (default) = bias add karo; bias=False = bias nahi (params = in·out)
  • layer.weight shape = (out_features, in_features) — dhyan do, out pehle! Har row = ek output neuron ke saare weights.
  • layer.bias shape = (out_features,) — har output neuron ka ek bias
  • Input shape (N, in) → output shape (N, out). Batch dimension N pehle aata hai (rows = samples).
  • Parameters = in·out (weights) + out (biases) = in × out + out
  • Default init: weight aur bias dono lagbhag Uniform(−1/√in, +1/√in) se random (andar se kaiming_uniform_ with a = √5). Isliye torch.manual_seed lagate hain taaki har run same ho. (Init ki poori kahani: Chapter 21.)
  • weight aur bias dono nn.Parameter hain, requires_grad=True — yaani training me ye seekhe jaayenge.

Weight (out, in) kyun? Math me hum z = Wx likhte hain jahan x ek column hai aur W ki shape (out, in) — har row ek neuron (destination). PyTorch me data rows me hota hai (har row ek sample), to wahi kaam x @ WT se hota hai. W wahi hai, bas likhne ka tareeka badla (detail: Chapter 8.5).

PyTorch · nn.Linear ko khol ke dekho

import torch
import torch.nn as nn

torch.manual_seed(0)
layer = nn.Linear(in_features=3, out_features=2)  # 3 inputs -> 2 outputs
print(layer)
print("weight shape:", layer.weight.shape)   # (out, in)
print("bias shape:  ", layer.bias.shape)     # (out,)
print("total params:", sum(p.numel() for p in layer.parameters()))  # 3*2 + 2

# random weights ki jagah apne simple weights daal do (Lab 1 wala .data tareeka)
layer.weight.data = torch.tensor([[1., 0., -1.],
                                  [2., 1.,  0.]])   # shape (2, 3)
layer.bias.data = torch.tensor([0.5, -1.])         # shape (2,)

x = torch.tensor([[1., 2., 3.],
                  [0., 1., -1.]])        # batch: 2 samples, 3 features -> (2, 3)
out = layer(x)                           # (2, 3) -> (2, 2)
manual = x @ layer.weight.T + layer.bias # wahi formula haath se
print(out)
print(torch.allclose(out, manual))

Output

Linear(in_features=3, out_features=2, bias=True)
weight shape: torch.Size([2, 3])
bias shape:   torch.Size([2])
total params: 8
tensor([[-1.5000,  3.0000],
        [ 1.5000,  0.0000]], grad_fn=<AddmmBackward0>)
True

Output haath se check

  1. Sample 1 (1, 2, 3), neuron 1 (row [1, 0, −1]): 1·1 + 0·2 + (−1)·3 + 0.5 = 1 − 3 + 0.5 = −1.5
  2. Sample 1, neuron 2 (row [2, 1, 0]): 2·1 + 1·2 + 0·3 − 1 = 3
  3. Sample 2 (0, 1, −1), neuron 1: 0 + 0 + 1 + 0.5 = 1.5; neuron 2: 0 + 1 + 0 − 1 = 0

Jawab: [[−1.5, 3], [1.5, 0]] — PyTorch se match.

5.7 Lab 1: AND, OR (aur XOR ki koshish) nn.Linear se

Lab 1 me ek single perceptron = nn.Linear(2, 1) (2 inputs → 1 output) + step threshold. Weights haath se daale jaate hain:

PyTorch · Lab 1 (AND Problem)

import torch
import torch.nn as nn

# AND gate data
X = torch.tensor([[0, 0],
                  [0, 1],
                  [1, 0],
                  [1, 1]], dtype=torch.float32)       # (4, 2)
y = torch.tensor([[0], [0], [0], [1]], dtype=torch.float32)  # (4, 1)

# Single perceptron: 2 inputs -> 1 output
model = nn.Linear(2, 1)

# Manually set weights and bias for AND
model.weight.data = torch.tensor([[1., 1.]])   # shape (1, 2) = (out, in)
model.bias.data = torch.tensor([-1.5])         # shape (1,)

z = model(X)                  # forward pass: (4, 2) -> (4, 1), z = x1 + x2 - 1.5
print(z)
pred = (z >= 0).int()         # step activation: True/False -> 1/0
print("Predicted:", pred.squeeze().tolist())   # tolist(): tensor -> Python list
print("Actual:   ", y.squeeze().int().tolist())

Output

tensor([[-1.5000],
        [-0.5000],
        [-0.5000],
        [ 0.5000]], grad_fn=<AddmmBackward0>)
Predicted: [0, 0, 0, 1]
Actual:    [0, 0, 0, 1]

5.8 Lab 5: logistic regression as a custom nn.Module (line by line)

Kahani

Lab 5 ka asli-duniya task: breast cancer diagnosis. Har patient ke tumour ki image se 30 numbers nikale gaye (radius, texture, perimeter, concavity…). Predict karna hai: Malignant (M) ya Benign (B). Ye wahi logistic regression hai jo humne Ramu ke samose ke liye kiya, bas 2 ki jagah 30 features. Dataset: Wisconsin Breast Cancer, 569 patients.

nn.Module kya hai? PyTorch me har model (ek layer se lekar GPT tak) nn.Module ka subclass hota hai. Ye teen cheezein free me deta hai: (1) encapsulation — __init__ me layers declare karo, wo automatically "sub-modules" register ho jaati hain; (2) parameter tracking — model.parameters() saare weights/biases khud dhoondh leta hai (optimizer ko yahi list chahiye); (3) forward() vs __call__ — computation forward() me likhte ho, par chalate model(x) se ho. (Chapter 7 me nn.Module aur gehraai se.)

PyTorch · Lab 5 model (MySimpleNN)

import torch
import torch.nn as nn

class MySimpleNN(nn.Module):                 # nn.Module se inherit
    def __init__(self, num_features):
        super().__init__()                   # SABSE PEHLE: Module ki machinery set karo
        # Input: (Batch_Size, num_features) -> Output: (Batch_Size, 1)
        self.linear = nn.Linear(num_features, 1)   # 30 -> 1, learnable
        self.sigmoid = nn.Sigmoid()                # score -> probability (0, 1); koi parameter nahi

    def forward(self, features):             # features: (Batch_Size, num_features)
        out = self.linear(features)          # z = x @ W.T + b   -> (N, 1)
        out = self.sigmoid(out)              # y_hat = sigmoid(z) -> (N, 1)
        return out

model = MySimpleNN(30)
print(model)
print("params:", sum(p.numel() for p in model.parameters()))   # 30*1 + 1

Output

MySimpleNN(
  (linear): Linear(in_features=30, out_features=1, bias=True)
  (sigmoid): Sigmoid()
)
params: 31

Har line ka matlab

  1. class MySimpleNN(nn.Module): apni model class, nn.Module ki saari powers ke saath.
  2. super().__init__(): parent (nn.Module) ka constructor. Bhoole to error: layers assign karte hi "cannot assign module before Module.__init__() call".
  3. self.linear = nn.Linear(num_features, 1): weight shape (1, 30), bias (1,) → 31 parameters. out_features = 1 kyunki binary classification me ek hi probability chahiye: P(malignant). Lab ka pitfall: in_features must equal input ke columns (30), warna pehle hi forward pass pe matmul shape error.
  4. self.sigmoid = nn.Sigmoid(): activation bhi ek module ho sakta hai (stateless, 0 params). torch.sigmoid(x) function wala version bhi same kaam karta hai.
  5. forward: data ka raasta: linear → sigmoid. Isko forward pass kehte hain (Chapter 8).
  6. Chalana: y_pred = model(X) — model.forward(X) mat likho; model(X) andar se forward() + hooks dono chalata hai.

Lab 5 ka baaki pipeline (preview — training loop details Chapter 13 me)

PyTorch · Lab 5 pipeline (lab ka structure)

import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, LabelEncoder

torch.manual_seed(42)
df = pd.read_csv('https://raw.githubusercontent.com/gscdit/Breast-Cancer-Detection/refs/heads/master/data.csv')
df.drop(columns=['id', 'Unnamed: 32'], inplace=True)      # (569, 33) -> (569, 31)
X = df.drop(columns=['diagnosis'])                        # 30 feature columns
y = df['diagnosis']                                       # 'M' / 'B'
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

scaler = StandardScaler()                   # har feature: (x - mean) / std
X_train = scaler.fit_transform(X_train)     # fit SIRF train pe (no leakage)
X_test = scaler.transform(X_test)           # test pe sirf transform
encoder = LabelEncoder()                    # alphabetical: B -> 0, M -> 1
y_train = encoder.fit_transform(y_train)
y_test = encoder.transform(y_test)

# numpy float64 -> float32, warna "expected scalar type Float but found Double"
X_train_tensor = torch.from_numpy(X_train.astype(np.float32))   # (455, 30)
X_test_tensor = torch.from_numpy(X_test.astype(np.float32))     # (114, 30)
y_train_tensor = torch.from_numpy(y_train.astype(np.float32))   # (455,)  1-D!
y_test_tensor = torch.from_numpy(y_test.astype(np.float32))

learning_rate = 0.1
epochs = 25
loss_function = nn.BCELoss()                # binary cross-entropy, input = probabilities

model = MySimpleNN(X_train_tensor.shape[1])                        # 30 features
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

for epoch in range(epochs):                 # full-batch: poora train set ek saath
    y_pred = model(X_train_tensor)                               # forward: (455, 30) -> (455, 1)
    loss = loss_function(y_pred, y_train_tensor.view(-1, 1))     # target (455,) -> (455, 1)
    optimizer.zero_grad()                   # purane gradients saaf
    loss.backward()                         # naye gradients nikalo
    optimizer.step()                        # weights update
    print(f'Epoch: {epoch + 1}, Loss: {loss.item()}')

with torch.no_grad():                       # evaluation: gradient tracking band
    y_pred = model(X_test_tensor)
    y_pred_labels = (y_pred > 0.5).float()  # probability -> class (threshold 0.5)
    accuracy = (y_pred_labels == y_test_tensor.view(-1, 1)).float().mean()
    print(f'Accuracy: {accuracy.item()}')

Output (Lab 5 notebook ka asli output, beech ki lines chhodi)

Epoch: 1, Loss: 0.6703537702560425
Epoch: 2, Loss: 0.5365203022956848
Epoch: 3, Loss: 0.4555133879184723
...
Epoch: 24, Loss: 0.1824541687965393
Epoch: 25, Loss: 0.1793031245470047
Accuracy: 0.9824561476707458

Exam Radar

  • Linear regression: ŷ = w·x + b, output continuous, no activation (identity). Matrix form ŷ = Xw + b, X = (N, d).
  • Logistic regression: ŷ = σ(w·x + b) ∈ (0, 1); threshold 0.5 ⇔ z ≥ 0; boundary w·x + b = 0 = linear. Classification model despite the name. Loss: BCE.
  • Perceptron with sigmoid = logistic regression unit.
  • Linear + linear = linear: W′ = W2W1, b′ = W2b1 + b2. Without activation, depth is useless.
  • nn.Linear(in, out): computes x@WT + b; weight shape (out, in); bias (out,); params in·out + out; default bias=True; init U(−1/√in, 1/√in).
  • nn.Linear(30, 1) → 31 params; nn.Linear(3, 2) → 8; nn.Linear(2, 1) → 3; nn.Linear(784, 10) → 7850.
  • nn.Module: super().__init__() first; layers in __init__; computation in forward; call model(x); model.parameters() → optimizer.
  • Lab 5: BCELoss, SGD lr=0.1, 25 epochs, full-batch, threshold 0.5, .view(-1, 1), torch.no_grad() for eval, accuracy ≈ 0.982.

Galti mat karna

  • nn.Linear(3, 2).weight.shape = (2, 3), (3, 2) nahi.
  • Logistic regression ka output curve (sigmoid) hai, par boundary linear hai. "Sigmoid lagaya to non-linear classifier ban gaya" — galat.
  • "Zyada linear layers = zyada powerful" — bina activation ke bilkul nahi.
  • nn.BCELoss ko probabilities (sigmoid ke baad) chahiye; logits pe BCEWithLogitsLoss.
  • Scaler ko test data pe fit mat karo (data leakage); sirf transform.

Dimaag me aise baitha lo

Linear = seedhi line/plane. Regression → number, logistic → probability, dono ki boundary seedhi. Linear ke upar linear = phir bhi linear (photocopy ki photocopy). nn.Linear ka weight: (out, in).

Chapter 6

The XOR Problem — jis ek sawaal ne AI ko 17 saal sula diya

XOR truth table · kyun koi line kaam nahi karti (proof) · Minsky & Papert 1969 · AI winter · XOR = AND(OR, NAND) · hidden layer space badal deti hai · 2-2-1 network · MLP · PyTorch training

6.1 XOR kya hai? Staircase light ki kahani

Kahani — seedhiyon wali light

Ghar ki seedhiyon pe ek bulb hai jiske do switch hain: ek neeche, ek upar (worksheet wala example, asli gharon me aisi wiring hoti hai). Neeche se chadhte waqt neeche wala switch dabao → light ON. Upar pahunch ke upar wala dabao → light OFF. Rule kya hua? Light tab ON hai jab dono switches alag-alag position me hon. Dono same (dono off, ya dono on) → light OFF.

Isko kehte hain XOR = "exclusive OR": "ek ya doosra, par dono nahi". OR me dono on hone pe bhi 1 tha; XOR me dono on → 0.

x1x2ORANDXOR
00000
01101
10101
11110

Ek line me: XOR = 1 jab inputs different hon. (Isko "x1 ≠ x2" bhi bol sakte ho; binary me XOR = (x1 + x2) mod 2.)

6.2 Plot karo: 4 kone, aamne-saamne

Har input pair (x1, x2) graph pe ek point hai: square ke 4 kone. Class 1 wale (0, 1) aur (1, 0) ek diagonal ke do kone hain; class 0 wale (0, 0) aur (1, 1) doosre diagonal ke. Worksheet: "the two light-on states sit on opposite corners of the truth table."

x₁x₂ (0,0) (1,1) (0,1) (1,0) ● = class 1 (XOR = 1) × = class 0 (XOR = 0) - - candidate lines: sab fail Koi bhi line ghumao/khiskao: ek × hamesha ● wali side, ya ek ● × wali side. "every line fails" — Worksheet 04
XOR ke 4 points. ● aur × aamne-saamne konon pe. Ek seedhi kaat se dono groups alag nahi hote → XOR not linearly separable ("non-linear" data).

6.3 Proof: koi bhi single perceptron XOR nahi kar sakta

Picture se yakeen ho gaya, ab pakka maths proof. Maan lo koi perceptron XOR kar raha hai, weights w1, w2, bias b, rule "z ≥ 0 → 1". Chaaron rows ki shart likho:

Chaar inequalities ka jhagda

  1. (0, 0) → 0 chahiye: 0 + 0 + b < 0 ⇒ b < 0  …(1)
  2. (0, 1) → 1 chahiye: w₂ + b ≥ 0  …(2)
  3. (1, 0) → 1 chahiye: w₁ + b ≥ 0  …(3)
  4. (1, 1) → 0 chahiye: w₁ + w₂ + b < 0  …(4)
  5. (2) aur (3) ko jodo: w₁ + w₂ + 2b ≥ 0 ⇒ w₁ + w₂ + b ≥ −b
  6. (1) se b < 0, to −b > 0. Matlab w₁ + w₂ + b ≥ −b > 0
  7. Par (4) kehta hai w₁ + w₂ + b < 0. Ek hi cheez positive bhi aur negative bhi? Impossible!

Jawab: aise w1, w2, b exist hi nahi karte. Single perceptron XOR represent nahi kar sakta. ∎

Ek aur sundar tareeka (midpoint argument): z linear hai, to do points ke beech ke midpoint pe z = dono z ka average. Class 1 wale (0,1) aur (1,0) ka midpoint = (0.5, 0.5): dono ka z ≥ 0, to average bhi ≥ 0. Class 0 wale (0,0) aur (1,1) ka midpoint bhi (0.5, 0.5): dono ka z < 0, to average < 0. Ek hi point (0.5, 0.5) pe z ≥ 0 aur z < 0 dono? Namumkin. Dono diagonals beech me cross karti hain — yahi XOR ki asli dikkat hai.

6.4 History: Minsky & Papert (1969) aur pehla AI winter

Kahani

1960s me perceptron ka bahut hype tha — akhbaar likh rahe the "machine jo sochegi". Phir 1969 me MIT ke Marvin Minsky aur Seymour Papert ne kitaab likhi: "Perceptrons". Usme mathematically dikhaya ki single-layer perceptron XOR jaisi simple cheez bhi nahi kar sakta (aur kai aur limitations). Logon ne nateeja nikaala: "neural networks bekaar hain". Funding band, research thanda: isko first AI winter (First Dark Age) kehte hain.

Hal tab bhi pata tha: hidden layer lagao (perceptrons ka network). Dikkat ye thi ki multi-layer network ke weights train kaise karein, ye kisi ko nahi pata tha. 1986 me Rumelhart, Hinton aur Williams ne backpropagation popular kiya — hidden layers train karne ka tareeka. XOR solve, neural nets wapas ("Second Golden Age"). 1969 → 1986 = lagbhag 17 saal.

SaalGhatna
1943McCulloch–Pitts neuron
1957/58Rosenblatt ka Perceptron (First Golden Age)
1969Minsky & Papert, "Perceptrons": XOR impossible for single layer → First AI winter
1986Backprop (Rumelhart, Hinton, Williams) → hidden layers trainable → revival
1989Universal Approximation Theorem (Chapter 10)

6.5 Hal ka idea: XOR = AND(OR, NAND)

Kahani — electrician ki trick

Electrician staircase light ka poora rule ek switch se nahi banata; wo do simple wiring tukdon ko jodta hai. Hum bhi yahi karenge: ek neuron se poora XOR mat karwao; do neurons se do aasan sawaal poocho, phir teesra neuron unke jawab jode.

XOR ko shabdon me padho: "kam se kam ek on hai (OR), aur dono on nahi hain (NAND)". Bas!

XOR(x1, x2) = AND( OR(x1, x2), NAND(x1, x2) )
  • h1 = x1 OR x2 (hidden neuron 1)
  • h2 = x1 NAND x2 = NOT AND: "1 unless both inputs are 1" (hidden neuron 2)
  • y = h1 AND h2 (output neuron)

Worksheet wali table ("Discover the trick yourself")

x1x2h1 = ORh2 = NANDy = h1 AND h2XOR
000100 ✔
011111 ✔
101111 ✔
111000 ✔

Jawab: last column bilkul XOR. Teen perceptrons (do hidden + ek output) ne wo kar diya jo ek nahi kar paaya.

6.6 Geometry: do lines, beech ki patti

Har hidden neuron ek line kheenchta hai. OR neuron: x1 + x2 − 0.5 = 0. NAND neuron: −x1 − x2 + 1.5 = 0, yaani x1 + x2 = 1.5. Dono lines parallel hain. XOR = 1 wala area dono lines ke beech ki patti (strip) hai: OR line ke upar AUR NAND line ke neeche. "One line was never enough — two lines are."

x₁x₂ XOR = 1 patti — OR line: x₁ + x₂ = 0.5 (h₁ = 1 iske upar) - - NAND line: x₁ + x₂ = 1.5 (h₂ = 1 iske neeche) patti = h₁ = 1 AND h₂ = 1 → dono ● andar, → dono × bahar
Input space me hidden neurons ki do lines. (0,0) OR line ke neeche (bahar), (1,1) NAND line ke upar (bahar), (0,1) aur (1,0) patti ke andar.

6.7 Asli jaadu: hidden layer space ko badal deti hai

Kahani

Socho ek kaagaz pe XOR ke 4 points hain aur tum ek seedhi lakeer se alag nahi kar pa rahe. Ab kaagaz ko mod do / naye angle se dekho — achanak points aise jagah aa gaye ki ek lakeer kaafi hai! Hidden layer yahi karti hai: wo har input ko ek naye coordinate system (h1, h2) me le jaati hai.

Input (x1, x2)→ Naya point (h1, h2) = (OR, NAND)y
(0, 0)(0, 1)0
(0, 1)(1, 1)1
(1, 0)(1, 1) ← dono class-1 points ek hi jagah aa gaye!1
(1, 1)(1, 0)0
h₁ (OR)h₂ (NAND) 011 from (0,0) from (0,1) & (1,0) from (1,1) h₁ + h₂ − 1.5 = 0 Ab EK seedhi line kaafi: upar-right → y = 1 (AND) baaki → y = 0 Hidden space me data linearly separable ho gaya.
Hidden layer ke baad wahi 4 points (h₁, h₂) space me. Teen hi jagah bachi, aur output neuron (AND) ek line se kaam khatam kar deta hai.

Worksheet ke shabdon me: "hidden layers transform the input space into a new representation, where the hidden neurons learn intermediate features (like OR and NAND). In this new feature space, the data becomes linearly separable for the final output layer." Ye poore deep learning ka core idea hai: layers achhe features banati hain, last layer simple faisla karti hai.

6.8 Haath ke weights wala 2-2-1 network (step activation)

2-2-1 matlab: 2 inputs, 2 hidden neurons, 1 output. Lab 1 ke weights:

x₁ x₂ h₁ h₂ ŷ 1 −1 −1 1 1 1 OR: b = −0.5 NAND: b = +1.5 AND: b = −1.5 step Input layer (no computation) Hidden layer (step) Output layer
Lab 1 ka XOR MLP. h₁ = step(x₁ + x₂ − 0.5) [OR], h₂ = step(−x₁ − x₂ + 1.5) [NAND], ŷ = step(h₁ + h₂ − 1.5) [AND].

Chaaron inputs pe poora verification

xzh1 = x1 + x2 − 0.5h1zh2 = −x1 − x2 + 1.5h2zout = h1 + h2 − 1.5ŷ
(0,0)−0.501.510 + 1 − 1.5 = −0.50 ✔
(0,1)0.510.511 + 1 − 1.5 = 0.51 ✔
(1,0)0.510.510.51 ✔
(1,1)1.51−0.501 + 0 − 1.5 = −0.50 ✔

Jawab: ŷ = [0, 1, 1, 0] = XOR. Network ne solve kar diya.

Doosra tareeka (Worksheet 04): OR neuron − AND neuron

Worksheet 04 me hidden layer OR aur AND nikalti hai, aur output "OR on hai par AND off" check karta hai (step: 1 if z ≥ 0):

a1[1] = step(x1 + x2 − 0.5)  (OR)    a2[1] = step(x1 + x2 − 1.5)  (AND)
ŷ = step(a1[1] − a2[1] − 0.5)    (output weights +1, −1; bias −0.5)
x1x2a1 (OR)a2 (AND)ŷ = step(a1 − a2 − 0.5)
0000step(0 − 0 − 0.5) = 0
0110step(1 − 0 − 0.5) = 1
1010step(1 − 0 − 0.5) = 1
1111step(1 − 1 − 0.5) = 0

Yahan hidden space me points (0,0), (1,0), (1,0), (1,1) ban jaate hain, aur line a1 − a2 = 0.5 class 1 wale (1, 0) ko baaki se alag kar deti hai. Ek hi problem ke kai sahi networks hote hain. Worksheet: "By combining an OR neuron and an AND neuron, the hidden layer turns a non-linearly-separable problem into a linearly-separable one for the output neuron."

6.9 Sigmoid version (Lab 1 ke numbers): bade weights = lagbhag step

Step function ka gradient zero hai, to training (backprop) ke liye smooth sigmoid chahiye. Lab 1 ne wahi network sigmoid se banaya, bas weights 20 guna kar diye: (1, 1, −0.5) × 20 = (20, 20, −10) [OR], (−1, −1, 1.5) × 20 = (−20, −20, 30) [NAND], (1, 1, −1.5) × 20 = (20, 20, −30) [AND]. Boundary lines same rehti hain (scaling se line nahi badalti — Chapter 4), par sigmoid itna steep ho jaata hai ki step jaisa dikhta hai: σ(10) = 0.99995, σ(−10) = 0.0000454.

Chalo numbers se samjhein

xzOR = 20x1 + 20x2 − 10hORzNAND = −20x1 − 20x2 + 30hNANDzout = 20hOR + 20hNAND − 30σ(zout)
(0,0)−100.000045430≈ 1.0000≈ −9.99910.0000454 ≈ 0
(0,1)100.99995100.99995≈ 9.99820.99995 ≈ 1
(1,0)100.99995100.99995≈ 9.99820.99995 ≈ 1
(1,1)30≈ 1.0000−100.0000454≈ −9.99910.0000454 ≈ 0

Example row (0,1), output: z = 20(0.9999546) + 20(0.9999546) − 30 = 19.9991 + 19.9991 − 30 = 9.9982 → σ(9.9982) ≈ 0.99995.

Jawab: probabilities ≈ [0, 1, 1, 0] → threshold 0.5 → predictions [0, 1, 1, 0] = XOR.

PyTorch · Lab 1 (XOR problem with MLP, sigmoid, hand weights)

import torch

X = torch.tensor([[0, 0],
                  [0, 1],
                  [1, 0],
                  [1, 1]], dtype=torch.float32)
y = torch.tensor([[0], [1], [1], [0]], dtype=torch.float32)

def hidden_layer_xor_forward(x):
    # Hidden neuron 1: OR       (x[:, 0:1] = pehla column, shape (4, 1) bana rehta hai)
    h_or = torch.sigmoid(20*x[:, 0:1] + 20*x[:, 1:2] - 10)
    # Hidden neuron 2: NAND
    h_nand = torch.sigmoid(-20*x[:, 0:1] - 20*x[:, 1:2] + 30)
    # Output neuron: AND of OR and NAND
    output = torch.sigmoid(20*h_or + 20*h_nand - 30)
    hidden_values = torch.cat([h_or, h_nand], dim=1)   # (4, 1) + (4, 1) -> (4, 2)
    return output, hidden_values

probs, hidden_values = hidden_layer_xor_forward(X)
preds = (probs >= 0.5).float()

print("Hidden layer values: [OR, NAND]")
print(hidden_values.numpy().round(3))
print("Hidden layer model predictions")
print("probabilities:", probs.squeeze().numpy().round(3).tolist())
print("predictions:  ", preds.squeeze().int().tolist())
print("targets:      ", y.squeeze().int().tolist())

Output

Hidden layer values: [OR, NAND]
[[0. 1.]
 [1. 1.]
 [1. 1.]
 [1. 0.]]
Hidden layer model predictions
probabilities: [0.0, 1.0, 1.0, 0.0]
predictions:   [0, 1, 1, 0]
targets:       [0, 1, 1, 0]

6.10 Naam rakh do: Multilayer Perceptron (MLP)

Definition — MLP

Worksheet 04: "An MLP is a neural-network architecture made of many neurons arranged in layers: an input layer (just holds the inputs), one or more hidden layers, and an output layer. Every hidden neuron and every output neuron does the same two-step computation — first a linear combination, then an activation function": z = w1x1 + w2x2 + … + b,   a = φ(z).

6.11 Code: XOR ko PyTorch me train karo (weights khud seekho)

Ab weights haath se nahi denge; network khud seekhega. Do models ladenge: (1) bina hidden layer (single layer = logistic regression), (2) 2-2-1 MLP. Naya tool: nn.Sequential(...) = layers ko ek line me jod deta hai; input pehli layer se aakhri tak kram se guzarta hai (detail Chapter 7). Training loop ke 5 steps Lab 5 jaise hain (poori kahani Chapter 13); optimizer optim.Adam hai (Chapter 17) — abhi bas itna samjho ki ye weights ko loss kam karne ki direction me khiskaata hai.

PyTorch · XOR training: single layer vs MLP

import torch
import torch.nn as nn
import torch.optim as optim

X = torch.tensor([[0., 0.], [0., 1.], [1., 0.], [1., 1.]])   # (4, 2)
y = torch.tensor([[0.], [1.], [1.], [0.]])                   # (4, 1) XOR labels

def train(model, epochs=1000, lr=0.1):
    loss_fn = nn.BCELoss()                                   # binary cross-entropy
    optimizer = optim.Adam(model.parameters(), lr=lr)        # Adam (Chapter 17)
    for epoch in range(epochs):
        y_pred = model(X)                  # 1. forward pass
        loss = loss_fn(y_pred, y)          # 2. loss
        optimizer.zero_grad()              # 3. purane gradients saaf
        loss.backward()                    # 4. backprop: gradients nikalo
        optimizer.step()                   # 5. weights update
        if (epoch + 1) % 250 == 0:
            print(f"  epoch {epoch + 1}: loss = {loss.item():.4f}")
    with torch.no_grad():                  # evaluation, gradient tracking band
        probs = model(X)
        preds = (probs >= 0.5).float()
        acc = (preds == y).float().mean().item()
    print("  probs:", [round(p, 3) for p in probs.squeeze().tolist()])
    print("  preds:", preds.squeeze().int().tolist(), " accuracy:", acc)

torch.manual_seed(0)
print("Single layer (no hidden layer):")
slp = nn.Sequential(nn.Linear(2, 1), nn.Sigmoid())           # 3 params
train(slp)

torch.manual_seed(1)
print("MLP 2-2-1:")
mlp = nn.Sequential(nn.Linear(2, 2), nn.Sigmoid(),           # hidden layer (2 neurons)
                    nn.Linear(2, 1), nn.Sigmoid())           # output layer
train(mlp)

Output

Single layer (no hidden layer):
  epoch 250: loss = 0.6931
  epoch 500: loss = 0.6931
  epoch 750: loss = 0.6931
  epoch 1000: loss = 0.6931
  probs: [0.5, 0.5, 0.5, 0.5]
  preds: [1, 1, 1, 1]  accuracy: 0.5
MLP 2-2-1:
  epoch 250: loss = 0.0070
  epoch 500: loss = 0.0022
  epoch 750: loss = 0.0012
  epoch 1000: loss = 0.0007
  probs: [0.001, 0.999, 0.999, 0.0]
  preds: [0, 1, 1, 0]  accuracy: 1.0

Output ko padhna seekho

Bonus · bad luck seed: 2 hidden neurons kabhi-kabhi atak jaate hain

torch.manual_seed(42)
print("MLP 2-2-1, seed 42:")
train(nn.Sequential(nn.Linear(2, 2), nn.Sigmoid(), nn.Linear(2, 1), nn.Sigmoid()))

torch.manual_seed(42)
print("MLP 2-4-1, seed 42:")
train(nn.Sequential(nn.Linear(2, 4), nn.Sigmoid(), nn.Linear(4, 1), nn.Sigmoid()))

Output

MLP 2-2-1, seed 42:
  epoch 250: loss = 0.4786
  epoch 500: loss = 0.4778
  epoch 750: loss = 0.4776
  epoch 1000: loss = 0.4775
  probs: [0.0, 0.667, 0.667, 0.667]
  preds: [0, 1, 1, 1]  accuracy: 0.75
MLP 2-4-1, seed 42:
  epoch 250: loss = 0.0069
  epoch 500: loss = 0.0023
  epoch 750: loss = 0.0012
  epoch 1000: loss = 0.0008
  probs: [0.0, 0.999, 1.0, 0.001]
  preds: [0, 1, 1, 0]  accuracy: 1.0

Seed 42 pe 2-2-1 network ek local minimum me phas gaya (loss 0.4775, ek point galat, 75%). Theory me 2 hidden neurons kaafi hain (humne haath se bana ke dikhaya), par training hamesha wahan nahi pahunchti. Thode zyada hidden neurons (2-4-1) dene se training reliable ho jaati hai — ye "capacity" ka pehla swaad hai (Chapter 10).

Exam Radar

  • XOR = 1 jab inputs different; truth table 0, 1, 1, 0. XOR is not linearly separable.
  • Single perceptron / logistic regression / koi bhi single linear layer XOR solve nahi kar sakta. Proof: 4 inequalities contradiction (b < 0, w1 + b ≥ 0, w2 + b ≥ 0, w1 + w2 + b < 0).
  • Minsky & Papert, "Perceptrons", 1969 → first AI winter. Backprop 1986 (Rumelhart, Hinton, Williams) → revival (~17 years).
  • XOR = AND(OR, NAND) = OR AND NOT(AND). Minimum: 1 hidden layer with 2 neurons (2-2-1).
  • Lab 1 step weights: OR (1, 1, −0.5), NAND (−1, −1, 1.5), output AND (1, 1, −1.5). Sigmoid version: 20, 20, −10 / −20, −20, 30 / 20, 20, −30.
  • Worksheet 04: OR (1, 1, −0.5), AND (1, 1, −1.5), output step(a1 − a2 − 0.5).
  • Hidden neurons ki boundaries: x1 + x2 = 0.5 aur x1 + x2 = 1.5 (parallel); XOR = 1 region = beech ki strip.
  • Hidden layer input space ko naye feature space me map karti hai jahan data linearly separable ho jaata hai.
  • 2-2-1 MLP params = 9. Single layer on XOR: BCE stuck at ln 2 ≈ 0.6931, outputs 0.5.
  • MLP = input layer + ≥1 hidden layer + output layer; har neuron: linear combination → activation.

Galti mat karna

  • "XOR ke liye 2 hidden layers chahiye" — galat. Ek hidden layer, do neurons kaafi.
  • "Sigmoid lagane se single neuron XOR kar lega" — galat; boundary phir bhi seedhi.
  • "Hidden layers lagao, activation ki zaroorat nahi" — galat; bina activation sab collapse hoke ek linear layer (Chapter 5.5). XOR solution me step/sigmoid ki non-linearity hi kaam kar rahi hai.
  • Minsky–Papert ne multi-layer networks ko impossible nahi kaha tha; problem single layer ki thi aur multi-layer ko train karne ka tareeka nahi tha.
  • Training me stuck hona (75%) ka matlab "MLP XOR nahi kar sakta" nahi; ye local minimum / bad initialization hai.

Dimaag me aise baitha lo

Staircase light: "alag = ON". Ek line nahi, do lines (OR, NAND) ki patti. Hidden layer = naya chashma: usse dekho to XOR bhi seedhi line se kat jaata hai.

Chapter 7

Shallow Neural Network — ek hidden layer, bahut saari taaqat

Definition · terminology · Prince (UDL) notation · ReLU joints · piecewise-linear functions · activation pattern · multiple inputs/outputs · parameter counting · capacity · nn.Module & nn.Sequential

7.1 Shallow network kya hai?

Kahani — bijli ka bill (slab system)

Maa bijli ka bill dekh ke pareshaan hain. Rate seedha nahi hai: pehle 100 units ₹3 per unit, 100 se 200 tak ₹5, aur 200 ke upar ₹8. Graph banao (units vs bill) to ye seedhi lines ke tukde hain jo 100 aur 200 pe mudte hain. Har mod (kink/joint) pe slope badal jaata hai. Aise function ko kehte hain piecewise linear (tukdon me seedha).

Kamaal ki baat: ReLU wala shallow neural network bilkul aise hi functions banata hai. Har hidden neuron ek "slab boundary" (joint) jodta hai. Jitne zyada hidden neurons, utne zyada mod, utna hi koi bhi tedha-medha curve copy kar sakte ho.

Definition — Shallow neural network

Jis neural network me exactly ek hidden layer ho, wo shallow neural network hai. Structure: input layer → ek hidden layer (linear + non-linear activation) → output layer (linear recombination). Worksheet (Prince UDL Ch. 3): "A network with exactly one hidden layer is a shallow neural network; historically, any network with at least one hidden layer is also called a multi-layer perceptron (MLP)." Ek se zyada hidden layers → deep network.

7.2 Terminology — har shabd ka matlab

x₁ x₂ h₁ h₂ h₃ y Input layer 2 neurons (= features) Hidden layer width D = 3 · z → a = φ(z) Output layer 1 neuron W[1]: 3×2, b[1]: 3 W[2]: 1×3, b[2]: 1 fully connected
Worksheet ka "Label the diagram" wala 2-3-1 network: 2 inputs, 3 hidden units, 1 output; har neuron agli layer ke har neuron se juda (fully connected). Parameters = 6 + 3 + 3 + 1 = 13.
TermMatlab
Input layerRaw features andar laati hai. Koi computation nahi, koi weight/bias nahi. Neurons = features ki sankhya (tabular data me ek column = ek neuron). Worksheet: "the number of neurons in the input layer is determined by the number of features (dimension) of the input data."
Hidden layerBeech ki layer: har unit linear combination + non-linear activation. "Hidden" kyunki iski values training data me kabhi dikhti nahi (na input me, na label me). Kaam: features extract karna, non-linear patterns pakadna, wo classification/regression karna jo single layer nahi kar sakti.
Output layerFinal prediction. Size problem pe depend: binary → 1 neuron (sigmoid), K classes → K neurons (softmax), regression → 1 neuron (linear, koi activation nahi).
Neuron / unit / nodeEk circle: weighted sum + bias + activation. "Hidden unit" = hidden layer ka neuron.
Fully connected / denseEk layer ka har neuron agli layer ke har neuron se juda. PyTorch me nn.Linear.
Pre-activation zActivation se pehle ka raw sum: z = w·x + b.
Activation a (ya h)Activation function ke baad: a = φ(z). Yahi agli layer ka input banta hai.
WidthEk layer me kitne neurons (hidden units), Prince isko D likhte hain. Ye hyperparameter hai (hum chunte hain, training nahi).
DepthKitni hidden layers (Prince: K). Shallow = 1.
ParametersSaare weights + biases = jo training me seekhe jaate hain.

Worksheet ka "five-step workflow inside every hidden neuron": receive inputs → apply weights → add the bias → pass through activation → transmit output onward.

Worksheet ke chhote sawaal

  1. Spam / not spam (binary): output neurons = 1 (sigmoid).
  2. 7 classes: output neurons = 7 (softmax).
  3. House price (regression): output neurons = 1 (linear).
  4. 10 input features, 5 categories: input layer = 10, output layer = 5 (har category ka ek score; hidden size hum chunte hain).

7.3 Prince (UDL) ka formula: 1 input, 3 hidden units

Course ka shallow network part Prince ki book "Understanding Deep Learning" (Chapter 3) follow karta hai. Wahan sabse simple shallow network: ek scalar input x, ek scalar output y, 3 hidden units, activation ReLU:

y = φ0 + φ1 a[θ10 + θ11x] + φ2 a[θ20 + θ21x] + φ3 a[θ30 + θ31x]
compact:   hd = a[θd0 + θd1x],    y = φ0 + Σd=1D φdhd
  • a[•] = activation function; yahan ReLU: a[z] = max(0, z) — negative ko 0, positive ko waisa hi (Chapter 9)
  • θd0 = hidden unit d ka bias ("0 marks the always-on input")
  • θd1 = input x se hidden unit d ka weight (doosra subscript = kaunsa input)
  • hd = hidden unit d ki activation
  • φd = hidden unit d se output ka weight; φ0 = output ka bias (offset)
  • D = hidden units ki sankhya (width); yahan D = 3. Total params = 3 (θd0) + 3 (θd1) + 3 (φd) + 1 (φ0) = 10 = 3D + 1

Teen steps me padho: (1) input ke teen alag linear functions banao (θd0 + θd1x), (2) har ek ko ReLU se guzaro (negative hissa kat ke 0), (3) teeno ko φ weights se guna karke jodo aur φ0 add karo. θ notation = w notation, sirf symbol alag (worksheet 3.2): θ10 = hidden neuron 1 ka bias, θ11 = input x se hidden neuron 1 ka weight, θ20 = hidden neuron 2 ka bias, θ21 = x se hidden neuron 2 ka weight.

7.4 Joints (kinks): ReLU network piecewise-linear function banata hai

Kahani — taar modna

Ek seedha lohe ka taar lo. Usse koi bhi shape banana hai to kya karoge? Jagah-jagah mod doge. Har mod ke beech taar seedha hi rehta hai. ReLU network bhi yahi karta hai: har hidden unit ek jagah "mod" (joint) deta hai, aur do mod ke beech output seedhi line hai.

Chalo numbers se samjhein (D = 3, ReLU)

Hidden: θ10 = −0.5, θ11 = 1  |  θ20 = 1, θ21 = −1  |  θ30 = −1.5, θ31 = 1.   Output: φ0 = 0.1, φ1 = 1, φ2 = 0.5, φ3 = −2.

Matlab: h1 = max(0, x − 0.5), h2 = max(0, 1 − x), h3 = max(0, x − 1.5),   y = 0.1 + 1·h1 + 0.5·h2 − 2·h3.

  1. Joints: h₁: x = −(−0.5)/1 = 0.5, h₂: x = −1/(−1) = 1.0, h₃: x = −(−1.5)/1 = 1.5 → 3 joints, 4 regions.
  2. x = 0: h = (max(0, −0.5), max(0, 1), max(0, −1.5)) = (0, 1, 0) → y = 0.1 + 0 + 0.5·1 − 0 = 0.6
  3. x = 0.25: h = (0, 0.75, 0) → y = 0.1 + 0.375 = 0.475
  4. x = 0.5 (joint): h = (0, 0.5, 0) → y = 0.1 + 0.25 = 0.35
  5. x = 0.75: h = (0.25, 0.25, 0) → y = 0.1 + 0.25 + 0.125 = 0.475
  6. x = 1.0 (joint): h = (0.5, 0, 0) → y = 0.1 + 0.5 = 0.6
  7. x = 1.25: h = (0.75, 0, 0) → y = 0.85
  8. x = 1.5 (joint): h = (1.0, 0, 0) → y = 1.1
  9. x = 1.75: h = (1.25, 0, 0.25) → y = 0.1 + 1.25 − 0.5 = 0.85
  10. x = 2.0: h = (1.5, 0, 0.5) → y = 0.1 + 1.5 − 1.0 = 0.6
RegionActivation pattern (on units)Slope = Σ φdθd1 (active)Check
x < 0.5h2 only0.5 × (−1) = −0.50.6 → 0.35 over 0.5 ✔
0.5 < x < 1h1, h21×1 + 0.5×(−1) = +0.50.35 → 0.6 ✔
1 < x < 1.5h1 only1 × 1 = +1.00.6 → 1.1 ✔
x > 1.5h1, h31×1 + (−2)×1 = −1.01.1 → 0.6 ✔

Jawab: y(0, 0.5, 1, 1.5, 2) = 0.6, 0.35, 0.6, 1.1, 0.6 — 3 joints, 4 seedhe tukde: neeche, upar, aur tez upar, phir neeche.

x 0.51.01.52.0 10−1 φ₁h₁ = max(0, x − 0.5) φ₂h₂ = 0.5·max(0, 1 − x) φ₃h₃ = −2·max(0, x − 1.5) y = 0.1 + sum (lal) dashed verticals = joints (har hidden unit ka ek)
Teen hidden units ke weighted contributions (kaali, narangi, dashed) aur unka sum + 0.1 = lal piecewise-linear output. Lal dots = joints at x = 0.5, 1.0, 1.5. 3 hidden units → 3 joints → 4 linear regions.

7.5 Multiple inputs aur multiple outputs

hd = a[ θd0 + Σi=1Di θdixi ],    d = 1 … D
yj = φj0 + Σd=1D φjdhd,    j = 1 … Do
  • Di = inputs ki sankhya, D = hidden units, Do = outputs ki sankhya
  • θdi = input i se hidden unit d ka weight (pehla subscript = destination, doosra = source)
  • φjd = hidden unit d se output j ka weight; φj0 = output j ka bias
  • Matrix form: h = a[θ0 + Θx], y = φ0 + Φh — yahi Chapter 8 ka z[l] = W[l]a[l−1] + b[l] hai

7.6 Parameters ginna (exam favourite)

Kahani

Shaadi me har mehmaan (neuron) ko pichhli table ke har aadmi se haath milana hai (weight), aur ek gift apna (bias). Hidden layer ka har neuron Di inputs se haath milata hai + 1 bias. Output ka har neuron D hidden units se haath milata hai + 1 bias. Input layer ke mehmaan sirf khade rehte hain: unke paas na weight, na bias.

Total parameters = (Di + 1)·D + (D + 1)·Do
Ek layer: nin × nout (weights) + nout (biases)    (Worksheet 04)
  • (Di + 1)·D = hidden layer: har hidden unit ke Di weights + 1 bias
  • (D + 1)·Do = output layer: har output ke D weights + 1 bias
  • Input layer ka koi bias nahi: "Inputs are just given values — they are not neurons, so they carry no weight and no bias. Biases begin at the first hidden layer."

Chalo numbers se samjhein

  1. 2-2-1 (XOR net): (2 + 1)·2 + (2 + 1)·1 = 6 + 3 = 9 (weights 4 + 2 = 6, biases 2 + 1 = 3)
  2. 3-3-1 (Worksheet 04 Activity 4): weights 3×3 + 3×1 = 9 + 3 = 12, biases 3 + 1 = 4, total 16 = (3 + 1)·3 + (3 + 1)·1
  3. 2-3-1 (labelled figure): (2 + 1)·3 + (3 + 1)·1 = 9 + 4 = 13
  4. 1-3-1 (Prince example): (1 + 1)·3 + (3 + 1)·1 = 6 + 4 = 10 = 3D + 1
  5. 10 inputs, 16 hidden, 5 outputs: 11·16 + 17·5 = 176 + 85 = 261
  6. MNIST 784-128-10: 785·128 + 129·10 = 100,480 + 1,290 = 101,770

Shape bhi poocha jaata hai: 2 inputs, 3 hidden → W[1] shape (3 × 2) = (destination × source), PyTorch me nn.Linear(2, 3).weight bhi (3, 2).

7.7 Network capacity: zyada hidden units = zyada mod

Capacity = model kitne complex functions bana sakta hai. Shallow ReLU network me har hidden unit ek joint deta hai, to D badhao → zyada joints → zyada tukde → koi bhi smooth curve aur achhe se copy (approximate) ho sakta hai. Isi ka formal roop hai Universal Approximation Theorem: "A feed-forward network with a single hidden layer containing a finite number of neurons can approximate continuous functions on compact subsets of ℝn" — par catch: neurons bahut zyada chahiye ho sakte hain. Depth vs width ki poori kahani Chapter 10 me; zyada capacity ka khatra (overfitting) Chapter 18 me.

7.8 Code: shallow network — nn.Module class, nn.Sequential, params

nn.Module recap + naya: __init__ me layers banao (attributes ki tarah), forward me unka order likho. nn.ReLU() = ReLU activation as a layer (0 params).

PyTorch · ShallowNet (one hidden layer)

import torch
import torch.nn as nn

class ShallowNet(nn.Module):
    def __init__(self, n_in, n_hidden, n_out):
        super().__init__()
        self.hidden = nn.Linear(n_in, n_hidden)    # theta: weight (n_hidden, n_in), bias (n_hidden,)
        self.act = nn.ReLU()                       # a[z] = max(0, z)
        self.output = nn.Linear(n_hidden, n_out)   # phi: weight (n_out, n_hidden), bias (n_out,)

    def forward(self, x):
        z = self.hidden(x)      # pre-activations: (N, n_in) -> (N, n_hidden)
        h = self.act(z)         # activations:     (N, n_hidden)
        y = self.output(h)      # output:          (N, n_hidden) -> (N, n_out)
        return y

torch.manual_seed(0)
model = ShallowNet(2, 3, 1)             # 2-3-1 network
print(model)
for name, p in model.named_parameters():    # har parameter ka naam + shape
    print(name, tuple(p.shape), p.numel())
print("total:", sum(p.numel() for p in model.parameters()))

x = torch.randn(5, 2)                   # 5 samples, 2 features
print(model(x).shape)                   # (5, 1)

Output

ShallowNet(
  (hidden): Linear(in_features=2, out_features=3, bias=True)
  (act): ReLU()
  (output): Linear(in_features=3, out_features=1, bias=True)
)
hidden.weight (3, 2) 6
hidden.bias (3,) 3
output.weight (1, 3) 3
output.bias (1,) 1
total: 13
torch.Size([5, 1])

nn.Sequential — same network, ek line me

nn.Sequential(layer1, layer2, ...) ek ready-made container hai: input ko diye gaye order me har layer se guzarta hai. Jab forward me koi "jugaad" (branching, skip connection) nahi, sirf seedhi line ho, tab ye sabse aasaan. Layers ko index se access karo: seq[0], seq[2].

PyTorch · nn.Sequential equivalent

seq = nn.Sequential(nn.Linear(2, 3),   # index 0: hidden layer
                    nn.ReLU(),         # index 1: activation (0 params)
                    nn.Linear(3, 1))   # index 2: output layer
print(seq)
print("total:", sum(p.numel() for p in seq.parameters()))

Output

Sequential(
  (0): Linear(in_features=2, out_features=3, bias=True)
  (1): ReLU()
  (2): Linear(in_features=3, out_features=1, bias=True)
)
total: 13

Custom nn.Module class

  • Poora control: forward me kuch bhi (if, skip connections, multiple inputs)
  • Layers ke naam (self.hidden)
  • super().__init__() zaroori

nn.Sequential

  • Sirf seedha chain: A → B → C
  • Kam code, layers numbered (0, 1, 2…)
  • Andar se ye bhi ek nn.Module hai

Prince wala example code me (weights khud daalo)

PyTorch · 1-3-1 ReLU network = section 7.4 ke numbers

net = nn.Sequential(nn.Linear(1, 3), nn.ReLU(), nn.Linear(3, 1))
net[0].weight.data = torch.tensor([[1.0], [-1.0], [1.0]])    # theta_d1, shape (3, 1)
net[0].bias.data = torch.tensor([-0.5, 1.0, -1.5])           # theta_d0, shape (3,)
net[2].weight.data = torch.tensor([[1.0, 0.5, -2.0]])        # phi_1..phi_3, shape (1, 3)
net[2].bias.data = torch.tensor([0.1])                       # phi_0

x = torch.tensor([[0.0], [0.25], [0.75], [1.25], [1.75], [2.0]])   # (6, 1)
h = net[1](net[0](x))        # sirf hidden activations dekhne ke liye
print(h)
print(net(x).squeeze())
print(sum(p.numel() for p in net.parameters()))   # 3D + 1 = 10

Output

tensor([[0.0000, 1.0000, 0.0000],
        [0.0000, 0.7500, 0.0000],
        [0.2500, 0.2500, 0.0000],
        [0.7500, 0.0000, 0.0000],
        [1.2500, 0.0000, 0.2500],
        [1.5000, 0.0000, 0.5000]], grad_fn=<ReluBackward0>)
tensor([0.6000, 0.4750, 0.4750, 0.8500, 0.8500, 0.6000],
       grad_fn=<SqueezeBackward0>)
10

Haath wali table se ek-ek number match. Hidden activations ki har row me dekho kaunse units on hain — wahi activation pattern hai.

Exam Radar

  • Shallow network = exactly one hidden layer. ≥1 hidden layer = MLP. >1 = deep.
  • Input layer: no computation, no weights, no bias; size = number of features.
  • Output size/activation: binary → 1 (sigmoid); K-class → K (softmax); regression → 1 (linear).
  • Prince: y = φ0 + Σ φd a[θd0 + θd1x]; θd0 = bias, θd1 = weight.
  • ReLU shallow net → piecewise linear; joint of unit d at x = −θd0/θd1; D units → ≤ D joints → ≤ D + 1 regions (1-D input). Each region = one activation pattern.
  • 2-D input: joints are lines, regions are convex polygons; max regions Σj=0Di C(D, j).
  • Params = (Di + 1)D + (D + 1)Do; 1-D in/out: 3D + 1. 2-2-1 = 9, 3-3-1 = 16, 2-3-1 = 13, 784-128-10 = 101,770.
  • W[1] shape = (hidden, inputs); PyTorch nn.Linear(in, out).weight = (out, in).
  • sum(p.numel() for p in model.parameters()); model.named_parameters() gives names + tensors.
  • Width = number of hidden units (hyperparameter). More width → more joints → more capacity (UAT).

Galti mat karna

  • Input layer ko "layer count" me mat gino: 2-3-1 network me 2 layers of weights hain (hidden + output); shallow = 1 hidden layer.
  • Params ginte waqt input layer ka bias mat jodo. Output layer ka bias mat bhoolo.
  • "D hidden units → exactly D + 1 regions" nahi; at most D + 1.
  • ReLU network smooth curve nahi banata; hamesha seedhe tukde (kinks). Sigmoid/tanh wala smooth hota hai.
  • Output layer pe ReLU mat lagao regression me (negative values kat jaayengi); Prince ke formula me output linear hai.

Dimaag me aise baitha lo

Shallow net = bijli ka slab bill: har hidden unit ek slab boundary (joint). D units → D mod → D + 1 seedhe tukde. Params: har neuron = (pichhli layer ke neurons + 1).

Chapter 8

Forward Propagation — input se output tak ka safar

Feed-forward ka matlab · z[l] = W[l]a[l−1] + b[l], a[l] = g(z[l]) · 1-1-1 aur 2-2-1 numeric forward pass · batch form (row vs column convention) · shape tracking · deep = repeat · PyTorch

8.1 Forward propagation kya hai?

Kahani — Mumbai ke dabbawale

Ghar se tiffin nikla (input). Pehla dabbawala use station tak laata hai, doosra train me, teesra office tak. Har station pe ek hi type ka kaam: dabba lo, thoda process karo (sort, label), aage do. Koi dabba wapas pichhle station nahi jaata. Aakhir me tiffin desk pe (output).

Neural network me bhi data bilkul aise chalta hai: input layer → hidden layer 1 → hidden layer 2 → … → output. Har layer pe same do kaam: (1) weighted sum (linear step), (2) activation (non-linear step). Isko kehte hain forward propagation (forward pass): input se prediction ŷ nikalna. Teacher ki slide: "Linear combination mixes the inputs. Activation decides the final output."

Definition

Forward propagation: input x ko layer-by-layer network se guzaar ke output ŷ (aur phir loss) calculate karna. Feed-forward network: jisme information sirf aage (input → output) jaati hai; koi loop/cycle nahi, koi neuron apna output khud ko ya pichhli layer ko wapas nahi bhejta. (Loops wale networks = recurrent networks, RNN — is course ke mid-sem ka hissa nahi.)

8.2 Notation: har weight ka "postal address"

Worksheet ki analogy: postal address layered hota hai — desh, sheher, gali, ghar number. Weight ka address bhi: wij[l]: [l] = "sheher" (kis layer me connection pahunchta hai), i = "gali" (is layer ka kaunsa neuron receive karta hai — destination), j = "ghar number" (pichhli layer ka kaunsa neuron bhejta hai — source).

a[0] = x
z[l] = W[l] a[l−1] + b[l]    (linear step: "collect everything addressed to this layer")
a[l] = g[l](z[l])    (non-linear step: "process it")
ŷ = a[L]
  • l = layer number (1 = pehli hidden layer, … , L = output layer). Input layer = layer 0.
  • nl = layer l me neurons; n0 = input features
  • W[l] = weight matrix, shape (nl × nl−1) = (destination × source). W[1] input → hidden 1 jodta hai, W[2] hidden → output.
  • b[l] = bias vector, shape (nl × 1) — har neuron ka ek bias
  • z[l] = pre-activation (layer ka raw output), a[l] = post-activation output; dono shape (nl × 1)
  • g (ya φ, σ) = activation function, element-wise lagta hai (har number pe alag-alag)
  • wij[l] = W[l] ki row i, column j: "layer l, into neuron i, from neuron j"

Address padhna (Worksheet 04 "worked reading")

  1. w21[1]: layer 1, into hidden neuron 2, from input 1.
  2. w12[2]: layer 2, into output neuron 1, from hidden neuron 2.
  3. b2[1]: hidden neuron 2 (layer 1) ka bias.
  4. w32: neuron 2 (pichhli layer) se neuron 3 (is layer) tak.
  5. 2 inputs, 3 hidden neurons → W[1] shape (3 × 2): 3 destinations (rows), 2 sources (columns).

Worksheet 04 ka 2-2-1 MLP, har neuron explicitly:

Har neuron alag-alag (Worksheet 04)

z1[1] = w11[1]x1 + w12[1]x2 + b1[1],   a1[1] = φ(z1[1])
z2[1] = w21[1]x1 + w22[1]x2 + b2[1],   a2[1] = φ(z2[1])
z1[2] = w11[2]a1[1] + w12[2]a2[1] + b1[2],   ŷ = a1[2] = φ(z1[2])
Matrix form me pehli do lines ek saath: z[1] =
w11w12
w21w22
x1
x2
+
b1
b2
. Row i of W = neuron i ke saare incoming weights.

Worksheet ka NumPy snippet + 0-indexing gotcha

NumPy · worksheet (Lecture 1, section 3.3)

import numpy as np

W1 = np.array([[ 0.2, -0.5],     # 3 hidden neurons (rows) x 2 inputs (cols)
               [ 0.1,  0.4],
               [-0.3,  0.7]])
b1 = np.array([0.1, 0.0, -0.2])  # har hidden neuron ka bias
x = np.array([1.0, 2.0])         # ek input (example value)

z1 = W1 @ x + b1                 # blank ka jawab: W1 @ x + b1
a1 = np.maximum(0, z1)           # phi = ReLU (example)
print(z1, a1)
print(W1[1][0])                  # math ka w21 (input 1 -> hidden 2)

Output

[-0.7  0.9  0.9] [0.  0.9 0.9]
0.1

Haath se: z₁ = 0.2·1 − 0.5·2 + 0.1 = −0.7, z₂ = 0.1 + 0.8 + 0 = 0.9, z₃ = −0.3 + 1.4 − 0.2 = 0.9. Gotcha: math me counting 1 se (w21), code me 0 se, to w21 = W1[1][0] = 0.1.

8.3 Pura forward pass by hand: 1-1-1 network (classroom projection)

Sabse chhota network: 1 input, 1 hidden neuron, 1 output. Sigmoid activation dono neurons pe, squared-error loss.

x h ŷ w₁ = 0.5 w₂ = 0.5 b₁ = 0 b₂ = 0 x = 2 z₁ = 1 h = σ(1) = 0.73106 z₂ = 0.36553 ŷ = 0.59038 L = ½(ŷ − 1)² = 0.083895 target y = 1
1-1-1 network: z₁ = w₁x + b₁, h = σ(z₁), z₂ = w₂h + b₂, ŷ = σ(z₂), L = ½(ŷ − y)².

Chalo numbers se samjhein — x = 2, y = 1, w₁ = 0.5, b₁ = 0, w₂ = 0.5, b₂ = 0

  1. Hidden neuron ka weighted input: z₁ = w₁x + b₁ = (0.5)(2) + 0 = 1
  2. Hidden activation: e−1 = 0.367879 → h = 1/(1 + 0.367879) = 1/1.367879 = 0.731059 ≈ 0.73106
  3. Output ka weighted input (hidden output hi ab input hai): z₂ = w₂h + b₂ = (0.5)(0.731059) + 0 = 0.365529 ≈ 0.36553
  4. Prediction: e−0.365529 = 0.693830 → ŷ = 1/(1 + 0.693830) = 1/1.693830 = 0.590378 ≈ 0.59038
  5. Loss: L = ½(0.590378 − 1)² = ½(−0.409622)² = ½(0.167790) = 0.083895

Jawab: ŷ = 0.59038, L = 0.083895. Interpretation (docx): "The prediction 0.59038 is below the target 1, so the network must adjust its parameters to increase the prediction and reduce the loss." Ye adjust kaise hota hai — backprop (Chapter 13); usi docx me ∂L/∂w₂ = −0.07242 aur ∂L/∂w₁ = −0.01948 nikalte hain, aur ek update (η = 0.1) ke baad loss 0.083895 → 0.082351 girta hai.

½ kyun? Squared error ka derivative nikalte waqt 2 aata hai; ½ use cancel kar deta hai: d/dŷ [½(ŷ − y)²] = (ŷ − y). Sirf convenience.

8.4 Pura forward pass by hand: 2-2-1 network (student activity)

Activity ka setup: 2 inputs, 2 hidden neurons, 1 output, sigmoid everywhere, biases 0. Given: x1 = 1, x2 = 2, y = 1, α = 0.10 (learning rate, backprop me kaam aayega). Weights: w1 = 0.10, w2 = 0.20 (into h1), w3 = 0.30, w4 = 0.40 (into h2), w5 = 0.50, w6 = 0.60 (into output). "Keep at least six decimal places."

x₁ x₂ h₁ h₂ ŷ w₁ = 0.1 w₄ = 0.4 w₃ = 0.3 w₂ = 0.2 w₅ = 0.5 w₆ = 0.6 x₁ = 1 x₂ = 2 z₁ = 0.5 h₁ = 0.622459 z₂ = 1.1 h₂ = 0.750260 z₃ = 0.761386 ŷ = 0.681655 L = 0.050672
Activity ka 2-2-1 network, sab biases 0. Har neuron pe pehle z (weighted sum), phir σ.

Step-by-step (activity ke 7 steps)

  1. Step 1 — h₁ ka weighted input: z₁ = w₁x₁ + w₂x₂ + b₁ = (0.10)(1) + (0.20)(2) + 0 = 0.1 + 0.4 = 0.500000
  2. Step 2 — h₁ activation: e−0.5 = 0.606531 → h₁ = 1/1.606531 = 0.622459
  3. Step 3 — h₂ ka weighted input: z₂ = w₃x₁ + w₄x₂ + b₂ = (0.30)(1) + (0.40)(2) + 0 = 0.3 + 0.8 = 1.100000
  4. Step 4 — h₂ activation: e−1.1 = 0.332871 → h₂ = 1/1.332871 = 0.750260
  5. Step 5 — output ka weighted input: z₃ = w₅h₁ + w₆h₂ + b₃ = (0.50)(0.622459) + (0.60)(0.750260) + 0 = 0.311230 + 0.450156 = 0.761386
  6. Step 6 — prediction: e−0.761386 = 0.467019 → ŷ = 1/1.467019 = 0.681655
  7. Step 7 — loss: L = ½(0.681655 − 1)² = ½(0.318345)² = ½(0.101343) = 0.050672

Jawab (answer key se match): z₁ = 0.500000 | h₁ = 0.622459 | z₂ = 1.100000 | h₂ = 0.750260 | z₃ = 0.761386 | ŷ = 0.681655 | L = 0.050672.

8.5 Vectorized batch form: Z = XWᵀ + b (row vs column convention)

Kahani

Exam copies check karni hain. Ek-ek copy uthake check karna (for-loop) slow hai. Teacher ne 3 helpers bitha diye, sab ek saath check karte hain (parallel) — same answer key (same weights) se. GPU yahi karta hai: poore batch ko ek hi matrix multiplication me nipta deta hai.

Problem itni hai ki "samples ko rows me rakhein ya columns me?" — do conventions chalti hain, aur exam dono se pooch sakta hai:

Column convention (math, Prince, worksheets)

  • Ek sample x = column vector (n0 × 1)
  • Batch X = samples ko columns me: (n0 × N)
  • Z[l] = W[l]A[l−1] + b[l] → (nl × N)
  • W (nl × nl−1) left side
  • b (nl × 1) har column me add (broadcast)

Row convention (PyTorch, NumPy code)

  • Ek sample = row (1 × nin)
  • Batch X = samples ko rows me: (N × nin)
  • Z = XWT + b → (N × nout)
  • W wahi (nout × nin) store, isliye transpose right side
  • b shape (nout,) har row me add (broadcast)

Dono same kyun hain? Row-convention X = column-convention X ka transpose. Transpose ka rule (AB)T = BTAT: (WXcol)T = XcolTWT = XrowWT. Isliye nn.Linear ka formula x @ W.T + b hai aur weight (out, in) store hota hai. Rows = samples (PyTorch); columns = samples (math).

Chalo numbers se samjhein — 3 samples ek saath, 2-2-1 activity network

X (rows = samples) =
12
01
20
,   W1 =
0.10.2
0.30.4
,   W2 = [0.5 0.6], biases 0.
  1. Z₁ = X W₁ᵀ: (3 × 2) @ (2 × 2) → (3 × 2). Row 1: (0.1·1 + 0.2·2, 0.3·1 + 0.4·2) = (0.5, 1.1); Row 2: (0.2, 0.4); Row 3: (0.2, 0.6).
  2. A₁ = σ(Z₁) = [[0.622459, 0.750260], [0.549834, 0.598688], [0.549834, 0.645656]]
  3. Z₂ = A₁ W₂ᵀ: (3 × 2) @ (2 × 1) → (3 × 1). Row 2: 0.5·0.549834 + 0.6·0.598688 = 0.274917 + 0.359213 = 0.634130. Row 3: 0.274917 + 0.387394 = 0.662311. Row 1: 0.761386 (pehle wala).
  4. Ŷ = σ(Z₂) = [[0.681655], [0.653425], [0.659779]]

Jawab: ek hi matmul chain me teeno predictions. Row 1 bilkul section 8.4 ka answer (0.681655) — batch me har row independent sample hai.

8.6 Shape tracking: 3-layer MLP (exam ka pakka sawaal)

Example: MNIST jaisi images, har image 28×28 = 784 pixels (flatten), batch size N = 64, network 784 → 256 → 128 → 10 (ReLU hidden layers). PyTorch row convention:

StepOperationWeight / bias shapeOutput shapeParams
InputX—(64, 784)0
Layer 1Z1 = XW1T + b1W₁ (256, 784), b₁ (256,)(64, 256)784·256 + 256 = 200,960
A1 = ReLU(Z1)—(64, 256)0
Layer 2Z2 = A1W2T + b2W₂ (128, 256), b₂ (128,)(64, 128)256·128 + 128 = 32,896
A2 = ReLU(Z2)—(64, 128)0
Layer 3 (output)Z3 = A2W3T + b3 (logits)W₃ (10, 128), b₃ (10,)(64, 10)128·10 + 10 = 1,290
Total235,146

8.7 Deep network = wahi step baar-baar

Deep network me kuch naya nahi: har layer pe "linear step + activation" repeat karo. Algorithm:

Forward pass algorithm (pseudo-code)

a ← x
for l = 1, 2, …, L:
    z[l] = W[l] a + b[l]   (store z[l] for backprop)
    a = g[l](z[l])   (store a[l])
ŷ = a

8.8 Code: manual forward (torch.matmul) vs nn.Module — same answer

torch.matmul(A, B) = matrix multiplication (A @ B bhi same). 2-D @ 1-D bhi chalta hai: (2, 2) @ (2,) → (2,).

PyTorch · manual forward pass (2-2-1 activity network)

import torch
import torch.nn as nn

x = torch.tensor([1.0, 2.0])                     # ek sample, shape (2,)
W1 = torch.tensor([[0.10, 0.20],                 # row 1: h1 me aane wale weights (w1, w2)
                   [0.30, 0.40]])                # row 2: h2 me aane wale weights (w3, w4)
b1 = torch.tensor([0.0, 0.0])
W2 = torch.tensor([[0.50, 0.60]])                # output me aane wale weights (w5, w6)
b2 = torch.tensor([0.0])

z1 = torch.matmul(W1, x) + b1                    # (2, 2) @ (2,) -> (2,)   column-style W a + b
a1 = torch.sigmoid(z1)
z2 = torch.matmul(W2, a1) + b2                   # (1, 2) @ (2,) -> (1,)
y_hat = torch.sigmoid(z2)
loss = 0.5 * (y_hat - 1.0) ** 2                  # L = 1/2 (y_hat - y)^2, y = 1
print("z1   :", z1)
print("a1   :", a1)
print("z2   :", z2)
print("y_hat:", y_hat)
print("loss :", loss)

Output

z1   : tensor([0.5000, 1.1000])
a1   : tensor([0.6225, 0.7503])
z2   : tensor([0.7614])
y_hat: tensor([0.6817])
loss : tensor([0.0507])

Hand calculation (0.622459, 0.750260, 0.761386, 0.681655, 0.050672) se match — PyTorch default 4 decimals dikhata hai.

PyTorch · same network as nn.Module + batch + manual check

class Net221(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(2, 2)               # weight (2, 2), bias (2,)
        self.fc2 = nn.Linear(2, 1)               # weight (1, 2), bias (1,)

    def forward(self, x):
        a1 = torch.sigmoid(self.fc1(x))          # layer 1: z1 -> a1
        return torch.sigmoid(self.fc2(a1))       # layer 2: z2 -> y_hat

model = Net221()
model.fc1.weight.data = W1.clone()               # wahi weights copy karo
model.fc1.bias.data = b1.clone()
model.fc2.weight.data = W2.clone()
model.fc2.bias.data = b2.clone()

X = torch.tensor([[1.0, 2.0],                    # batch: 3 samples, rows = samples
                  [0.0, 1.0],
                  [2.0, 0.0]])                   # shape (3, 2)
print(model(X))                                  # (3, 2) -> (3, 1)

Z1 = X @ W1.T + b1                               # row convention: X W^T + b -> (3, 2)
A1 = torch.sigmoid(Z1)
Y_manual = torch.sigmoid(A1 @ W2.T + b2)         # (3, 2) @ (2, 1) -> (3, 1)
print(torch.allclose(model(X), Y_manual))        # dono raaste same?

Output

tensor([[0.6817],
        [0.6534],
        [0.6598]], grad_fn=<SigmoidBackward0>)
True

PyTorch · 3-layer MLP me shapes print karo

mlp = nn.Sequential(nn.Linear(784, 256), nn.ReLU(),
                    nn.Linear(256, 128), nn.ReLU(),
                    nn.Linear(128, 10))
h = torch.randn(64, 784)                         # 64 images, har image 784 pixels
for layer in mlp:                                # Sequential pe loop: har layer kram se
    h = layer(h)
    print(f"{layer.__class__.__name__:<7} -> {tuple(h.shape)}")
print("params:", sum(p.numel() for p in mlp.parameters()))

Output

Linear  -> (64, 256)
ReLU    -> (64, 256)
Linear  -> (64, 128)
ReLU    -> (64, 128)
Linear  -> (64, 10)
params: 235146

Ye debugging trick yaad rakho: shape error aaye to layer-by-layer shapes print karo. layer.__class__.__name__ = layer ka class naam; :<7 = 7 characters me left-align (sirf sundar printing ke liye).

Exam Radar

  • Forward propagation = input → layer by layer → output (+ loss). Feed-forward = no loops/cycles. Inference = sirf forward pass.
  • z[l] = W[l]a[l−1] + b[l], a[l] = g(z[l]), a[0] = x. z = pre-activation, a = post-activation.
  • wij[l]: l = layer, i = destination (current layer), j = source (previous layer). W[l] shape (nl, nl−1), b[l] shape (nl, 1).
  • Math w21 = code W[1][0] (0-indexing).
  • 1-1-1 (x = 2, w = 0.5, 0.5, b = 0, y = 1): z₁ = 1, h = 0.73106, z₂ = 0.36553, ŷ = 0.59038, L = 0.083895.
  • 2-2-1 (x = (1, 2), w = 0.1…0.6, b = 0, y = 1): z₁ = 0.5, h₁ = 0.622459, z₂ = 1.1, h₂ = 0.750260, z₃ = 0.761386, ŷ = 0.681655, L = 0.050672.
  • Batch: math Z = WX + b (columns = samples); PyTorch Z = XWᵀ + b (rows = samples). nn.Linear weight (out, in).
  • 784-256-128-10 MLP params = 235,146; shapes (64, 784) → (64, 256) → (64, 128) → (64, 10).
  • Activation functions shape nahi badalte; inner dimensions must match for matmul.

Galti mat karna

  • W[l] ki shape (nl−1, nl) likh dena — galat; math convention me (destination, source) = (nl, nl−1).
  • PyTorch me W @ X likhna jab X ki rows samples hon — shape error; sahi X @ W.T (ya bas layer(X)).
  • Hidden activation (h) ki jagah pre-activation (z) agli layer me daal dena — common hand-calculation galti. Agli layer ko a jaata hai, z nahi.
  • Sigmoid ke andar minus bhoolna: σ(z) = 1/(1 + e−z), e+z nahi.
  • Rounding beech me mat karo; activity kehti hai "keep at least six decimal places".

Dimaag me aise baitha lo

Har layer = dabbawala station: "z = Wa + b, phir a = g(z)", aage pass. Math me columns = samples (W·X), PyTorch me rows = samples (X·Wᵀ).

Chapter 9

Activation Functions — neuron ka mood

Step · Sigmoid · Tanh · ReLU · Leaky ReLU · PReLU · ELU · Softmax

9.1 Activation function kyun chahiye? — seedhi line ka jaal

Kahani

Socho Golu ko drawing competition me ek chehra (face) banana hai, lekin uske paas sirf ek ruler (scale) hai. Ek ruler se seedhi line. Do ruler jod do — phir bhi seedhi line. Sau ruler jod do — tab bhi seedhi line hi banegi. Gol aankhein, muskurata hua curved hoth... kuch nahi ban sakta.

Ab koi usko ek chhota sa tool de deta hai jo line ko kahin pe mod (bend) de sakta hai. Bas! Ab bahut saare chhote-chhote mod jod ke wo koi bhi shape bana sakta hai. Neural network me wo "mod dene wala tool" hi activation function hai.

Yaad karo Chapter 5: ek neuron pehle weighted sum banata hai, z = w·x + b. Ye z ek seedha (linear) calculation hai. Phir is z ko ek function φ (phi) me daalte hain: a = φ(z). Yahi φ activation function hai. z ko pre-activation (ya raw score, logit) bolte hain, aur a ko activation (post-activation output).

Definition

Activation function φ ek function hai jo neuron ke weighted sum z par lagaya jaata hai: a = φ(z). Iska main kaam hai network me non-linearity laana, taaki network seedhi line/plane ke alawa curved, complicated patterns bhi seekh sake. Output layer par iska doosra kaam hai raw score ko answer ki shape me badalna (jaise probability).

z[l] = W[l] a[l−1] + b[l]   →   a[l] = φ(z[l])
  • l = layer number (kaunsi layer), a[l−1] = pichhli layer ka output (pehli layer ke liye ye input x hai)
  • W[l] = weight matrix, b[l] = bias vector — ye "linear step" hai
  • z[l] = pre-activation (raw score) — abhi tak sab seedha-saadha multiply + add
  • φ = activation function, element-wise lagta hai (har number par alag-alag) — ye "non-linear step" hai

Bina activation ke kya hota hai? (Linear trap)

Chapter 5 me humne prove kiya tha: do linear layers ko jodo to ek hi linear layer ban jaati hai, kyunki W2(W1x + b1) + b2 = (W2W1)x + (W2b1 + b2) = W′x + b′. Matlab 100 layers bhi lagao, bina activation ke network ek akela linear model hi rahega. Depth bekaar. Lecture slide ka exact message: "Without φ: ANY depth of purely linear layers collapses to ONE linear transformation."

Chalo numbers se samjhein

Sawaal: W1 = [[1, 2], [3, −1]], b1 = [1, 0], W2 = [[2, 1]], b2 = [−1]. Input x = (−1, 2). Pehle bina activation, phir ReLU ke saath (ReLU(z) = max(0, z), detail 9.6 me).

  1. Collapsed weight: W′ = W2W1 = [2·1 + 1·3, 2·2 + 1·(−1)] = [5, 3]
  2. Collapsed bias: b′ = W2b1 + b2 = 2·1 + 1·0 − 1 = 1
  3. Do layers se: z1 = W1x + b1 = [1·(−1) + 2·2 + 1, 3·(−1) + (−1)·2 + 0] = [4, −5], phir 2·4 + 1·(−5) − 1 = 2
  4. Ek layer se: 5·(−1) + 3·2 + 1 = 2 — bilkul same! Collapse ho gaya.
  5. Ab beech me ReLU: ReLU([4, −5]) = [4, 0], phir 2·4 + 1·0 − 1 = 7

Jawab: bina activation 2 (= ek linear layer ka answer), ReLU ke saath 7. ReLU ne −5 ko "kaat" diya, isliye ab koi single W′, b′ is network ko copy nahi kar sakta.

PyTorch · linear collapse vs ReLU

import torch
import torch.nn as nn

torch.manual_seed(0)
# Do Linear layers, beech me koi activation NAHI
two_linear = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 1))
with torch.no_grad():
    two_linear[0].weight.copy_(torch.tensor([[1., 2.], [3., -1.]]))   # W1
    two_linear[0].bias.copy_(torch.tensor([1., 0.]))                  # b1
    two_linear[1].weight.copy_(torch.tensor([[2., 1.]]))              # W2
    two_linear[1].bias.copy_(torch.tensor([-1.]))                     # b2

# Collapse: W' = W2 @ W1 ,  b' = W2 @ b1 + b2
W1, b1 = two_linear[0].weight, two_linear[0].bias
W2, b2 = two_linear[1].weight, two_linear[1].bias
one_linear = nn.Linear(2, 1)
with torch.no_grad():
    one_linear.weight.copy_(W2 @ W1)        # (1,2) @ (2,2) -> (1,2)
    one_linear.bias.copy_(W2 @ b1 + b2)     # (1,)

x = torch.tensor([[1., 1.], [-1., 2.]])     # 2 samples, 2 features
print("W' =", one_linear.weight.data, " b' =", one_linear.bias.data)
print("2 linear layers :", two_linear(x).squeeze().tolist())
print("1 linear layer  :", one_linear(x).squeeze().tolist())

# Ab beech me ReLU daal do
with_relu = nn.Sequential(two_linear[0], nn.ReLU(), two_linear[1])
print("with ReLU       :", with_relu(x).squeeze().tolist())

Output

W' = tensor([[5., 3.]])  b' = tensor([1.])
2 linear layers : [9.0, 2.0]
1 linear layer  : [9.0, 2.0]
with ReLU       : [9.0, 7.0]

Pehle sample (1, 1) ke liye pre-activation [4, 2] dono positive the, to ReLU ne kuch nahi badla (9 = 9). Doosre sample par ReLU ne −5 ko 0 kar diya, aur answer badal gaya (2 → 7). Yahi "mod" hai: ReLU kuch inputs par line ko modta hai, kuch par nahi.

Ek hi network, alag activation, alag shape (Lab 2 visualizer)

Lab 2 ka "Activation Functions Explorer" ek chhota 1 → 3 → 1 network leta hai, fixed weights ke saath: hidden unit k ka zk = wkx + bk, aur output y = bout + v1φ(z1) + v2φ(z2) + v3φ(z3). Defaults: w = (−1, 2, 0.65), b = (0.3, −1, −0.5), v = (2, −1, 7), bout = −0.3. Sirf φ badlo aur output dekho:

−2 −1 1 2 −4 −2 2 x y(x) linear: seedhi line ReLU: kinks wali line sigmoid: smooth curve 3 kinks (laal dots)
Same weights, sirf activation alag. Linear φ: output ek seedhi line, y = −2.2 + 0.55x. ReLU: 3 kinks (x = 0.3, 0.5, 0.769 — jahan har unit ka z = 0 hota hai), yaani tukdon wali line. Sigmoid: smooth curve.

Linear wala case khud check karo: −0.3 + 2(−x + 0.3) − (2x − 1) + 7(0.65x − 0.5) = −2.2 + 0.55x. Teen neurons the, phir bhi seedhi line — linear trap ka live proof.

Update rule: activation hi decide karta hai ki seekhna hoga ya nahi

Teacher ke worksheets ek single perceptron (ek neuron) ko ye loss dete hain: L = ½(ŷ − y)2, jahan ŷ = f(z) prediction hai aur y sahi answer (target). Chain rule (Chapter 12) se:

∂L∂w = ∂L∂ŷ · ∂ŷ∂z · ∂z∂w = (ŷ − y) · f′(z) · x
wnew = wold − α (ŷ − y) f′(z) x
  • (ŷ − y) = error — kitna galat (½ ka 2 derivative me cancel ho gaya, isliye ½ lagaya tha)
  • f′(z) = activation ka derivative (slope) us point par
  • x = input; α = learning rate (step ka size)

Teen factors milke step ka size banate hain: error, input, aur f′(z). Agar f′(z) = 0 hai to error kitna bhi bada ho, step zero. Agar f′(z) bahut chhota hai, step bahut chhota. Isliye poora chapter asal me ek sawaal ka jawab hai: kis activation ka derivative kahan kitna hai?

Exam Radar

  • Activation function ka main purpose: non-linearity introduce karna. Bina iske koi bhi depth = ek linear layer.
  • Activation element-wise lagta hai (softmax exception hai — wo poore vector par lagta hai).
  • Identity function f(z) = z ka derivative hamesha 1 hai (kabhi nahi marta), lekin ye network ko linear bana deta hai — isliye bekaar.
  • Gradient formula: ∂L/∂w = (ŷ − y) f′(z) x; activation derivative decide karta hai seekhna hoga ya nahi, aur kitna tez.

Galti mat karna

  • "Zyada layers = zyada power" tabhi sach hai jab beech me non-linear activation ho.
  • Non-linear ka matlab "curve hona" zaroori nahi: ReLU do seedhi lines se bana hai, phir bhi non-linear hai (kyunki z = 0 par mod hai). Non-linear = ek single seedhi line nahi.

Dimaag me aise baitha lo

Ruler pe ruler = ruler. Mod chahiye? Activation lagao.

9.2 Achhe activation ki wish-list

Kahani

Naya scooter khareedne jao to ek list le ke jaate ho: mileage achha ho, sasta ho, brake achhe hon, seat comfortable ho. Waise hi deep learning walon ki bhi activation function ke liye ek wish-list hai. Koi bhi function saari boxes tick nahi karta — isliye itne saare activations bane.

PropertyMatlab kyaKyun chahiye
Non-linearPoora function ek seedhi line na hoWarna layers collapse ho jaati hain (9.1)
Differentiable (kam se kam almost everywhere)Har point (ya lagbhag har point) par slope defined hoGradient descent ko f′(z) chahiye. ReLU sirf z = 0 par fail hota hai — chalta hai.
Non-zero, non-saturating gradientBade |z| par bhi slope 0 ke paas na pahuncheSaturation = vanishing gradient = deep layers seekhna band
Zero-centeredOutputs positive bhi, negative bhi; average lagbhag 0Agli layer ke weights ke gradients ek hi sign ke na hon (zig-zag se bachav, 9.4)
Cheap (computationally)Calculate karna sastaCrores of neurons, har step par; exp() mehenga, max() sasta
Sahi output range (output layer ke liye)Jaisa answer chahiye waisa rangeProbability ke liye (0, 1), multi-class ke liye sum = 1

Dimaag me aise baitha lo

"Mod ho, slope ho, slope mare nahi, zero ke aas-paas ho, sasta ho" — 5 tick boxes.

9.3 Step (threshold) function — jo seekh hi nahi sakta

Kahani

Teacher ka worksheet example: ek perceptron decide karta hai student pass hai ya fail. Input x = marks, w = 1, b = −40. Student A ke 65 marks: z = 65 − 40 = 25. Student B ke 32 marks: z = 32 − 40 = −8. Lekin sawaal tha "pass ya fail?" — aur "25" koi answer nahi hai! Kisi ko z ko answer me badalna padega. Sabse seedha tareeka: z ≥ 0 to 1 (pass), warna 0 (fail). Ye hai step function — Rosenblatt ke 1958 wale original perceptron ka activation.

f(z) = 1  agar z ≥ 0,     f(z) = 0  agar z < 0
f′(z) = 0  har z ≠ 0 par;   z = 0 par undefined (jump)
  • Output sirf do values: {0, 1} — clean yes/no decision
  • z > 0 par function constant 1 hai, constant ka slope 0; z < 0 par constant 0, slope phir 0
  • z = 0 par function jump karta hai, wahan slope exist hi nahi karta
−4 −2 2 4 0.5 1 z f(z) step: f(z) −4 −2 2 4 0.5 1 z f′(z) z = 0 par undefined baaki har jagah 0 derivative: f′(z)
Step function: khula circle (0, 0) excluded, bhara circle (0, 1) included. Derivative poori line par flat 0 hai, aur jump par undefined.

Chalo numbers se samjhein (teacher ka L2 worksheet)

Sawaal: x = 2, w = 1, b = 0, y = 0, α = 0.5. Step activation ke saath ek update karo.

  1. Weighted sum: z = (1)(2) + 0 = 2
  2. Activation: ŷ = f(2) = 1 (kyunki 2 ≥ 0)
  3. Error: ŷ − y = 1 − 0 = 1 — sabse bada possible error!
  4. Derivative: f′(2) = 0
  5. Gradient: ∂L/∂w = (1)(0)(2) = 0
  6. Update: wnew = 1 − (0.5)(0) = 1

Jawab: weight bilkul nahi hila. Error maximum hai, phir bhi learning zero. Learning rate badhane se bhi kuch nahi hoga, kyunki α × 0 = 0.

Isliye teacher ki line yaad rakho: "A usable activation function must have a non-zero derivative." Step function decision to de deta hai, lekin gradient descent ko ye nahi batata ki w ko kis direction me kitna hilana hai — derivative 0 almost everywhere hai, to koi signal hi nahi milta.

PyTorch · Lab 2 · step ka graph hi nahi banta

import torch

z = torch.tensor([-1.0, 0.0, 2.0], requires_grad=True)
step = (z >= 0).float()          # step function: 1 agar z >= 0, warna 0
print(step)
print("requires_grad:", step.requires_grad, "| grad_fn:", step.grad_fn)

sig = torch.sigmoid(z)           # smooth function: graph banta hai
print("sigmoid grad_fn:", sig.grad_fn)

Output

tensor([0., 1., 1.])
requires_grad: False | grad_fn: None
sigmoid grad_fn: <SigmoidBackward0 object at 0x...>

(z >= 0) ek comparison hai jo boolean deta hai; .float() usko 0./1. bana deta hai. Is result ka grad_fn None hai — matlab autograd ke graph se ye disconnected hai, backward() isse guzar hi nahi sakta. Lab 2 isliye step ka gradient plot karte waqt torch.zeros_like(z) khud bana ke dikhata hai ("0 a.e.; undefined at z=0", a.e. = almost everywhere).

Exam Radar

  • Step/threshold: output {0, 1}, derivative 0 (z ≠ 0), undefined at 0, max derivative 0 → gradient descent se train nahi ho sakta.
  • Historical use: original perceptron (Rosenblatt, 1958).
  • PyTorch me (z >= 0).float() ka requires_grad False aur grad_fn None hota hai.

Galti mat karna

  • "Step function non-linear hai to kaafi hai" — nahi! Non-linear hona zaroori hai, kaafi nahi. Derivative bhi useful (non-zero) chahiye.
  • Learning rate badhane se step-function perceptron nahi seekhega: 0 × kuch bhi = 0.

Dimaag me aise baitha lo

Step = seedhiyon ka flat farsh. Farsh pe khade ho ke "neeche kis taraf jaun" nahi pata chalta — dhalaan (slope) chahiye.

9.4 Sigmoid — sabko 0 aur 1 ke beech daba do

Kahani

Mohalle ki chai wali aunty ka ek "gussa meter" hai jo 0 se 1 tak jaata hai. Customer thoda late paisa de — gussa 0.3. Zyada late — 0.7. Bahut zyada late — 0.99. Lekin ek baat note karo: gussa kabhi 1 se upar nahi jaata, aur 0 se neeche bhi nahi. Aur jab gussa already 0.99 pe hai, to customer aur late kare ya na kare, meter lagbhag hilta hi nahi. Ye "ek limit ke baad fark na padna" — isko bolte hain saturation. Sigmoid bilkul yahi aunty hai: koi bhi number do, 0 aur 1 ke beech daba deti hai, aur dono kinaron pe "atak" jaati hai.

σ(z) = 11 + e−z     range (0, 1),   σ(0) = 0.5
  • z = neuron ka raw score (w·x + b), koi bhi real number
  • e ≈ 2.71828 (Euler's number); e−z hamesha positive
  • Bada positive z: e−z → 0, to σ → 1/(1 + 0) = 1. Bada negative z: e−z → ∞, to σ → 0
  • z = 0: e0 = 1, to σ(0) = 1/2 = 0.5 — beech ka "undecided" point
  • Shape: "S" (isliye naam sigmoid = S-jaisa). Symmetry: σ(−z) = 1 − σ(z)

Formula aisa kyun? Hume ek aisa function chahiye tha jo (1) hamesha positive ho, (2) 1 se kabhi bada na ho, aur (3) smooth ho. 1/(1 + positive number) teeno kaam karta hai: denominator hamesha 1 se bada, to answer hamesha 0 aur 1 ke beech. Isi wajah se output ko probability ki tarah padh sakte hain — binary classification ke liye perfect. (Logistic regression yahi hai: perceptron + sigmoid.)

z−6−4−20246
σ(z)0.00250.01800.11920.50.88080.98200.9975
σ′(z)0.00250.01770.10500.250.10500.01770.0025

Dekho: σ(−2) = 0.1192 = 1 − 0.8808 = 1 − σ(2). Symmetry kaam kar rahi hai. Aur derivative row me z = 0 par sabse bada 0.25, dono taraf jaldi-jaldi girta hai.

Derivative kaise nikla? (teacher ka L2 worksheet wala tareeka)

Step-by-step derivation

σ(z) = (1 + e−z)−1 likho. Ab chain rule (bahar ka power, andar ka derivative):

  1. Bahar ka power rule: σ′(z) = −(1 + e−z)−2 · ddz(1 + e−z)
  2. Andar ka derivative: 1 constant hai (derivative 0), aur e−z ka derivative −e−z: ddz(1 + e−z) = −e−z
  3. Substitute karo, do minus cancel: σ′(z) = e−z(1 + e−z)2
  4. Trick: numerator ko aise likho e−z = (1 + e−z) − 1: σ′(z) = (1 + e−z) − 1(1 + e−z)2
  5. Do fractions me tod do: σ′(z) = 11 + e−z − 1(1 + e−z)2
  6. Har term σ ki power hai: σ′(z) = σ − σ2 = σ(z)(1 − σ(z))

σ′(z) = σ(z) · (1 − σ(z)) — derivative nikalne ke liye sirf output chahiye, koi naya exp() nahi. Backprop me ye bahut sasta padta hai.

Max derivative 0.25 kyun? σ aur (1 − σ) do numbers hain jinka sum hamesha 1 hai. Do numbers ka sum fixed ho to unka product tab maximum hota hai jab dono barabar hon: 0.5 × 0.5 = 0.25. Aur σ = 0.5 tab hota hai jab z = 0. To σ′max = 0.25 at z = 0. Matlab sigmoid apne best point par bhi gradient ko 4 guna chhota kar deta hai: σ′(z) ≤ 0.25 har z ke liye.

−8 −4 4 8 0.5 1 z σ(z) σ(0) = 0.5 flat flat σ(z): S-curve, range (0, 1) −8 −4 4 8 0.25 1 z σ′(z) max 0.25 σ′(6) ≈ 0.0025 saturated saturated σ′(z) = σ(1 − σ)
Left: σ(z), S-curve, (0, 0.5) se guzarta hai. Right: σ′(z), ek chhota sa pahaad jiski chot (peak) sirf 0.25 hai. Grey zones (|z| > 4) = saturated: curve flat, derivative lagbhag 0.

Chalo numbers se samjhein — ek update (Lab 2 / L2 worksheet ka lecture case)

Sawaal: x = 2, w = 1, b = 0, y = 0, α = 0.5. Sigmoid ke saath ek gradient descent step.

  1. Weighted sum: z = (1)(2) + 0 = 2
  2. Activation: ŷ = σ(2) = 1/(1 + e−2) = 1/(1 + 0.135335) = 1/1.135335 = 0.880797
  3. Error: ŷ − y = 0.880797 − 0 = 0.880797
  4. Derivative: σ′(2) = 0.880797 × (1 − 0.880797) = 0.880797 × 0.119203 = 0.104994
  5. Gradient: ∂L/∂w = 0.880797 × 0.104994 × 2 = 0.184956
  6. Update: wnew = 1 − 0.5 × 0.184956 = 1 − 0.092478 = 0.907522

Jawab: w 1 → 0.907522. Loss = ½(0.880797)2 = 0.387902.

Direction check: target 0 hai par prediction positive → ŷ ghatna chahiye → σ increasing hai to z ghatna chahiye → x = 2 > 0 hai to w ghatna chahiye. Hua bhi yahi (1 → 0.9075). Step function ke saath yahi sawaal me w 1 hi reh gaya tha — sigmoid ne seekhna possible kar diya.

PyTorch · sigmoid teen tareeke se + autograd check

import torch
import torch.nn as nn

z = torch.tensor([-6.0, -2.0, 0.0, 2.0, 6.0], requires_grad=True)

a1 = torch.sigmoid(z)              # 1) functional: seedha function call
a2 = nn.Sigmoid()(z)               # 2) layer/module: nn.Sequential me daalne ke liye
a3 = 1 / (1 + torch.exp(-z))       # 3) from scratch: formula khud likha
print("sigmoid :", a1.detach())
print("same?   :", torch.allclose(a1, a2), torch.allclose(a1, a3))

# Autograd se derivative nikaalo, formula se match karo
a1.sum().backward()                # sum() isliye taaki ek scalar ho, phir backward
formula = (a1 * (1 - a1)).detach()  # sigma(z) * (1 - sigma(z))
print("autograd:", z.grad)
print("formula :", formula)

Output

sigmoid : tensor([0.0025, 0.1192, 0.5000, 0.8808, 0.9975])
same?   : True True
autograd: tensor([0.0025, 0.1050, 0.2500, 0.1050, 0.0025])
formula : tensor([0.0025, 0.1050, 0.2500, 0.1050, 0.0025])

PyTorch · Lab 2 · teacher demo: ek sigmoid update trace

import torch

# Lab 2 wala lecture case: x=2, w=1, b=0, y=0, alpha=0.5
x = torch.tensor(2.0)
w = torch.tensor(1.0, requires_grad=True)
b = torch.tensor(0.0, requires_grad=True)
target = torch.tensor(0.0)
alpha = 0.5

z = w * x + b                          # weighted sum
prediction = torch.sigmoid(z)          # activation
loss = 0.5 * (prediction - target) ** 2
loss.backward()                        # dL/dw, dL/db nikal gaye

sigmoid_slope = prediction.detach() * (1 - prediction.detach())
manual_grad = (prediction.detach() - target) * sigmoid_slope * x
with torch.no_grad():                  # update ko graph me record nahi karna
    new_w = w - alpha * w.grad

print(f"z = {z.item():.6f} | y_hat = {prediction.item():.6f} | loss = {loss.item():.6f}")
print(f"sigmoid slope = {sigmoid_slope.item():.6f}")
print(f"manual dL/dw = {manual_grad.item():.6f} | autograd dL/dw = {w.grad.item():.6f}")
print(f"new w = {new_w.item():.6f}")

Output

z = 2.000000 | y_hat = 0.880797 | loss = 0.387902
sigmoid slope = 0.104994
manual dL/dw = 0.184956 | autograd dL/dw = 0.184956
new w = 0.907522

Lab 2 ka Student Activity 2 (student_one_update) exactly yahi steps ek reusable function me likhwata hai: w, b ko requires_grad=True, z = wx + b, activation (sigmoid ya tanh), loss = ½(ŷ − y)2, loss.backward(), aur torch.no_grad() ke andar w − α·w.grad. Test case: (2, 1, 0, 0, 0.5, "sigmoid") → gradient 0.184956, new_w 0.907522. Doosra experiment: α 0.5 se 1.0 karo — gradient same rehta hai (gradient α pe depend nahi karta), sirf step double hota hai.

Problem 1: Vanishing gradient (saturation)

Chalo numbers se samjhein — worksheet 03, Part 2

Sawaal: x = 1, w = 6, b = 0, y = 0, α = 1. (Useful: e−6 = 0.00247875)

  1. z = 6 × 1 + 0 = 6
  2. ŷ = σ(6) = 1/1.00247875 = 0.99752738
  3. Error: 0.99752738 − 0 = 0.99752738 — maximum possible error 1 hai, ye 99.75% galat hai!
  4. Derivative: σ′(6) = 0.99752738 × 0.00247262 = 0.00246651
  5. Gradient: 0.99752738 × 0.00246651 × 1 = 0.00246041
  6. Update: wnew = 6 − 0.00246041 = 5.99753959

Jawab: perceptron lagbhag poora galat hai, phir bhi weight sirf 0.0025 hila. Zimmedaar factor: σ′(6) = 0.00246651 — kyunki z = 6 par curve flat (saturated) hai.

Yahi hai vanishing gradient: jab |z| bada hota hai, sigmoid flat ho jaata hai, σ′(z) ≈ 0, aur neuron khud ko sudhaar nahi paata, chahe kitna bhi galat ho. Deep network me ye aur bura hai: backprop me har layer ka σ′ multiply hota hai (Chapter 13). Best case bhi le lo (har layer 0.25): 10 layers ke baad 0.2510 ≈ 9.5 × 10−7 — pehli layers tak gradient lagbhag pahunchta hi nahi, wo seekhna band kar deti hain.

PyTorch · saturation ka saboot

import torch

def one_update(x_val, w_val, b_val, y_val, alpha, act):
    x = torch.tensor(float(x_val))
    w = torch.tensor(float(w_val), requires_grad=True)
    b = torch.tensor(float(b_val), requires_grad=True)
    y = torch.tensor(float(y_val))
    y_hat = act(w * x + b)
    loss = 0.5 * (y_hat - y) ** 2
    loss.backward()
    with torch.no_grad():
        new_w = w - alpha * w.grad
    return y_hat.item(), w.grad.item(), new_w.item()

# Worksheet 03, Part 2: x=1, w=6, b=0, y=0, alpha=1  (perceptron bahut galat hai)
y_hat, grad, new_w = one_update(1, 6, 0, 0, 1.0, torch.sigmoid)
print(f"y_hat = {y_hat:.6f}  error = {y_hat - 0:.6f}")
print(f"dL/dw = {grad:.6f}  new w = {new_w:.6f}")

Output

y_hat = 0.997527  error = 0.997527
dL/dw = 0.002460  new w = 5.997540

(float32 me last decimals thode alag aate hain; worksheet ke 8-decimal answers float64 wale hain: dL/dw = 0.00246041, new w = 5.99753959.)

Problem 2: Not zero-centered → zig-zag updates

Kahani

Ek auto-rickshaw socho jo sirf do direction me chal sakta hai: "North-East" (dono coordinates badhe) ya "South-West" (dono ghate). Tumhe jaana hai South-East (ek badhe, ek ghate). Seedha nahi ja sakte! To karoge kya — thoda North-East, phir thoda South-West, phir North-East... tedha-medha (zig-zag) raasta, bahut time waste. Sigmoid ke saath agli layer ke weights ka yahi haal hota hai.

Kyun? Agli layer ka ek neuron lo: z = w1a1 + w2a2 + b, jahan a1, a2 pichhli layer ke sigmoid outputs hain — hamesha positive (0 aur 1 ke beech). Chain rule:

∂L∂w1 = δ · a1,     ∂L∂w2 = δ · a2,     jahan δ = ∂L∂z
  • δ = is neuron ka "error signal" — ek example ke liye ek hi number, dono weights ke liye same
  • a1, a2 > 0 → dono gradients ka sign = δ ka sign. Dono + ya dono −.

Chalo numbers se samjhein

a = (0.8, 0.3) (sigmoid outputs, dono positive).

  1. Agar δ = 0.5: gradients = (0.5 × 0.8, 0.5 × 0.3) = (0.4, 0.15) → dono positive → update me dono weights ghatenge.
  2. Agar δ = −0.2: gradients = (−0.16, −0.06) → dono negative → dono weights badhenge.

Ek example ka ek update kabhi "w₁ badhe aur w₂ ghate" nahi kar sakta. Agar best weights usi direction me hain, path zig-zag hoga aur convergence slow.

w₁ w₂ start best w allowed: dono + dono − Sigmoid outputs a₁, a₂ > 0 (hamesha positive) ∂L/∂w₁ = δ·a₁ , ∂L/∂w₂ = δ·a₂ δ ek hi number → dono gradients ka sign same (++ ya −−) Chahiye: w₁ badhe, w₂ ghate (+, −) Mil sakta hai sirf ++ ya −− → seedha nahi ja sakte, zig-zag karna padta hai
(w1, w2) plane: allowed moves sirf yellow quadrants (dono +, ya dono −). Best point South-East me hai, isliye red zig-zag path. Dashed = seedha raasta jo mil hi nahi sakta.

Mini-batch me alag examples ke δ alag sign ke ho sakte hain, to sum karne pe ye problem thoda kam ho jaata hai — par khatam nahi hota. Solution: aisa activation jo zero-centered ho (tanh), ya inputs/activations ko normalize karna (Batch Norm).

Problem 3: exp() mehenga hai

Har sigmoid me ek exponential calculate hota hai. ReLU sirf ek comparison (max) hai. Lecture slide: exp() "computationally costly compared to ReLU's simple threshold check". Crores of neurons × har training step = fark padta hai.

Sigmoid ke fayde (pros)

  • Output (0, 1) — seedha probability ki tarah padho
  • Smooth, har jagah differentiable, monotonic (hamesha badhta)
  • Derivative output se hi: σ(1 − σ)

Nuksaan (cons)

  • Vanishing gradient: saturation, aur best case bhi max 0.25
  • Not zero-centered (outputs hamesha positive) → zig-zag
  • exp() se costly

Kahan use hota hai: output layer me binary classification (spam ya nahi, disease ya nahi) — 1 output unit; multi-label output (ek image me cat bhi, dog bhi — har label ke liye alag sigmoid); LSTM/GRU ke andar gates (0 = band, 1 = poora khula). Hidden layers me ab nahi (purane networks me hota tha).

Exam Radar

  • σ(z) = 1/(1 + e−z), range (0, 1) (open — exactly 0 ya 1 kabhi nahi), σ(0) = 0.5.
  • σ′(z) = σ(z)(1 − σ(z)), max 0.25 at z = 0.
  • σ(−z) = 1 − σ(z).
  • Problems: vanishing gradient (saturation), not zero-centered (zig-zag), exp cost.
  • σ(2) = 0.8808, σ′(2) = 0.1050; σ(6) = 0.9975, σ′(6) = 0.0025.
  • Code: torch.sigmoid(x), nn.Sigmoid() (no parameters).

Galti mat karna

  • σ′ ka max 0.25 hai, 0.5 nahi. 0.5 to σ(0) ki value hai.
  • "Sigmoid ka output 0 ho sakta hai" — nahi, range open (0, 1) hai; bas bahut paas ja sakta hai.
  • Saturation ka matlab output 0 ya 1 ke paas atak jaana; problem output nahi, derivative ≈ 0 hai.

Dimaag me aise baitha lo

Chai aunty ka gussa: 0 se 1 ke beech, beech me 0.5, aur "pehle se full gussa" pe aur chillane ka koi asar nahi (saturation). Slope ki chot sirf "ek chauthai" (0.25).

9.5 Tanh — zero-centered S-curve

Kahani

Negative marking wale exam yaad hain? Score −1 se +1 ke beech socho: bilkul galat = −1, kuch nahi kiya = 0, sab sahi = +1. Ab average student ka score 0 ke aas-paas hota hai — kuch upar, kuch neeche. Ye zero-centered hai. Tanh wahi sigmoid wali S-shape hai, bas usko kheench ke −1 se +1 tak phaila diya, aur beech ka point 0 par rakh diya.

tanh(z) = ez − e−zez + e−z     range (−1, 1),   tanh(0) = 0
  • Numerator me minus, denominator me plus. Denominator hamesha numerator ke absolute se bada → answer −1 aur 1 ke beech
  • Bada positive z: e−z ≈ 0, to ≈ ez/ez = 1. Bada negative: ≈ −e−z/e−z = −1
  • z = 0: (1 − 1)/(1 + 1) = 0. Odd function: tanh(−z) = −tanh(z)
  • Values: tanh(0.5) = 0.4621, tanh(1) = 0.7616, tanh(2) = 0.9640, tanh(4) = 0.9993

Derivative (quotient rule, worksheet wala tareeka)

Step-by-step derivation

u = ez − e−z aur v = ez + e−z, to tanh = u/v.

  1. Har part ka derivative: u′ = ez + e−z = v,  v′ = ez − e−z = u (dono parts aapas me jagah badal lete hain!)
  2. Quotient rule: tanh′ = u′v − uv′v2 = v·v − u·uv2
  3. Fraction todo: = v2v2 − u2v2 = 1 − (u/v)2 = 1 − tanh2(z)

tanh′(z) = 1 − tanh2(z). At z = 0: 1 − 02 = 1 = maximum. Sigmoid ke 0.25 se 4 guna bada peak.

tanh(z) = 2σ(2z) − 1 (proof)

Proof, line by line

2σ(2z) − 1 = 21 + e−2z − 1
= 2 − (1 + e−2z)1 + e−2z = 1 − e−2z1 + e−2z
Upar-neeche dono ko ez se multiply karo:
= ez − e−zez + e−z = tanh(z) ✓

Check with numbers: 2σ(2) − 1 = 2 × 0.880797 − 1 = 0.761594 = tanh(1) ✓. Matlab tanh = sigmoid ka hi rescaled + shifted version: input 2 guna tez (2z), output 2 guna lamba (×2), aur neeche khiska diya (−1). Isko differentiate karo: tanh′(z) = 2 · σ′(2z) · 2 = 4σ′(2z); z = 0 par 4 × 0.25 = 1 ✓.

−6 −3 3 6 −1 1 z f(z) tanh σ (dashed) tanh: range (−1, 1) −6 −3 3 6 0.25 1 z f′(z) z = 1.663 tanh′ max 1 σ′ max 0.25 tanh′ = 1 − tanh² vs σ′
Left: tanh (solid) vs sigmoid (dashed); tanh origin se guzarta hai. Right: tanh′ (red) ka peak 1, σ′ (dashed) ka 0.25. Dono curves z = 1.663 par cross karte hain: usse aage tanh′ sigmoid′ se bhi neeche chala jaata hai.

Chalo numbers se samjhein — sigmoid vs tanh, best point par (L2 worksheet)

Sawaal: x = 1, w = 0, b = 0, y = 1, α = 1. Dono activations ke saath ek update.

  1. Dono ke liye z = 0 × 1 + 0 = 0
  2. Sigmoid: ŷ = 0.5, error = 0.5 − 1 = −0.5, σ′(0) = 0.25, grad = (−0.5)(0.25)(1) = −0.125, wnew = 0 − (1)(−0.125) = 0.125
  3. Tanh: ŷ = 0, error = 0 − 1 = −1, tanh′(0) = 1, grad = (−1)(1)(1) = −1, wnew = 0 − (1)(−1) = 1
  4. Ratio: tanh step / sigmoid step = 1 / 0.125 = 8

8 guna = (error ratio 1/0.5 = 2) × (derivative ratio 1/0.25 = 4). Do cheezein badli thi: tanh ka output 0 se shuru (target se door, bada error) aur slope 4 guna.

Lekin tanh bhi saturate hota hai

Worksheet 03 ka Part 3 heading: "Tanh does not fix it." Teacher ke Lab 2 pair challenge ka table dekho (x = 1, y = 1, α = 1, w = 0, b = z, taaki shuru ka weighted sum exactly z ho):

PyTorch · Lab 2 · center vs saturation (teacher_reference_update)

import torch

def teacher_reference_update(z_value, activation_name):
    x = torch.tensor(1.0)
    w = torch.tensor(0.0, requires_grad=True)
    b = torch.tensor(float(z_value), requires_grad=True)   # b = z, w = 0  => shuru ka z = z_value
    target = torch.tensor(1.0)
    z = w * x + b
    prediction = torch.sigmoid(z) if activation_name == "sigmoid" else torch.tanh(z)
    loss = 0.5 * (prediction - target) ** 2
    loss.backward()
    return prediction.item(), w.grad.item(), (w - w.grad).item()   # alpha = 1

print(f"{'z':>4} {'activation':>10} {'prediction':>12} {'dL/dw':>12} {'new w':>12}")
print("-" * 56)
for z_value in [0.0, 2.0, 4.0]:
    for activation_name in ["sigmoid", "tanh"]:
        pred, grad, new_w = teacher_reference_update(z_value, activation_name)
        print(f"{z_value:4.1f} {activation_name:>10} {pred:12.6f} {grad:12.6f} {new_w:12.6f}")

sigmoid_center = teacher_reference_update(0, "sigmoid")[2]
tanh_center = teacher_reference_update(0, "tanh")[2]
print(f"\nAt z=0, tanh step / sigmoid step = {tanh_center / sigmoid_center:.1f}x")

Output

   z activation   prediction        dL/dw        new w
--------------------------------------------------------
 0.0    sigmoid     0.500000    -0.125000     0.125000
 0.0       tanh     0.000000    -1.000000     1.000000
 2.0    sigmoid     0.880797    -0.012516     0.012516
 2.0       tanh     0.964028    -0.002541     0.002541
 4.0    sigmoid     0.982014    -0.000318     0.000318
 4.0       tanh     0.999329    -0.000001     0.000001

At z=0, tanh step / sigmoid step = 8.0x

z = 0 par tanh 8 guna tez. Lekin z = 2 par tanh ka step (0.002541) sigmoid (0.012516) se chhota ho gaya, aur z = 4 par tanh ka step 0.000001 — lagbhag zero. Kyun? tanh′ ka formula 4σ′(2z) hai: tanh do guna tez saturate hota hai. z = 1.663 ke baad tanh′ < σ′. Lab ka rounded table: z = ±4 par sigmoid gradient 0.018, tanh 0.001. Worksheet ka conclusion: "The S-shape itself is the problem. No rescaling of an S-curve will fix this."

Lab note: chhota update hamesha bura nahi. Agar prediction already target ke paas hai (jaise z = 4 par tanh 0.9993 vs target 1), chhota step theek hai — model lagbhag done hai. Saturation tab problem hai jab model galat ho aur derivative phir bhi chhota ho.

PyTorch · tanh teen tareeke + identity check + autograd

import torch
import torch.nn as nn

z = torch.tensor([-4.0, -1.0, 0.0, 1.0, 4.0], requires_grad=True)

t1 = torch.tanh(z)                                              # functional
t2 = nn.Tanh()(z)                                               # layer
t3 = (torch.exp(z) - torch.exp(-z)) / (torch.exp(z) + torch.exp(-z))   # scratch
t4 = 2 * torch.sigmoid(2 * z) - 1                               # identity: 2*sigma(2z) - 1
print("tanh     :", t1.detach())
print("all same?:", torch.allclose(t1, t2), torch.allclose(t1, t3), torch.allclose(t1, t4, atol=1e-6))

t1.sum().backward()
print("autograd :", z.grad)
print("1 - tanh²:", (1 - t1**2).detach())

Output

tanh     : tensor([-0.9993, -0.7616,  0.0000,  0.7616,  0.9993])
all same?: True True True
autograd : tensor([0.0013, 0.4200, 1.0000, 0.4200, 0.0013])
1 - tanh²: tensor([0.0013, 0.4200, 1.0000, 0.4200, 0.0013])

PyTorch · Lab 2 Activity 1 · activation-gradient probe (solution)

import torch

def solution_probe(name, value):
    z = torch.tensor(float(value), requires_grad=True)    # scalar jo grad track kare
    if name == "sigmoid":
        activation = torch.sigmoid(z)
    elif name == "tanh":
        activation = torch.tanh(z)
    else:
        raise ValueError("name must be 'sigmoid' or 'tanh'")
    gradient = torch.autograd.grad(activation, z)[0]   # d(activation)/dz, formula ke bina
    return activation.item(), gradient.item()

for value in [-4.0, 0.0, 4.0]:
    s, ds = solution_probe("sigmoid", value)
    t, dt = solution_probe("tanh", value)
    print(f"z={value:5.1f} | sigmoid {s:.3f}, grad {ds:.3f} | tanh {t:.3f}, grad {dt:.3f}")

Output

z= -4.0 | sigmoid 0.018, grad 0.018 | tanh -0.999, grad 0.001
z=  0.0 | sigmoid 0.500, grad 0.250 | tanh 0.000, grad 1.000
z=  4.0 | sigmoid 0.982, grad 0.018 | tanh 0.999, grad 0.001

torch.autograd.grad(output, input) ek tuple return karta hai (isliye [0]); ye .grad me jama nahi karta, seedha gradient de deta hai. Lab ki shart thi: derivative formula khud mat likho, autograd se nikalo. Tests: sigmoid(0) → (0.5, 0.25), tanh(0) → (0.0, 1.0).

Tanh ke fayde

  • Zero-centered (−1, 1): outputs + aur − dono → zig-zag kam, sigmoid se behtar gradient flow
  • Center par steeper: max derivative 1 (sigmoid 0.25)
  • Smooth, differentiable

Nuksaan

  • Still vanishing gradient: bade |z| par saturate (sigmoid se bhi tez)
  • exp() ka cost

Kahan use hota hai: RNN aur LSTM ke hidden states (cell output), purane shallow networks. Modern deep feed-forward/CNN hidden layers me ReLU family ne jagah le li.

Exam Radar

  • tanh range (−1, 1), tanh(0) = 0, zero-centered.
  • tanh′(z) = 1 − tanh2(z), max 1 at z = 0.
  • tanh(z) = 2σ(2z) − 1; tanh′(z) = 4σ′(2z).
  • Sigmoid′ aur tanh′ cross at z ≈ 1.663; usse aage tanh′ chhota.
  • Tanh vanishing gradient fix NAHI karta (S-shape problem). Lecture slide: "same problem as Sigmoid but less severe" (center pe slope 4 guna). Worksheet 03 ka table: "saturates faster than sigmoid, so vanishing gradient is worse". Dono apni jagah sach: |z| < 1.663 pe tanh better, tails me tanh worse. MCQ me context dekho.
  • Code: torch.tanh(x), nn.Tanh().

Galti mat karna

  • Tanh zero-centered hai, isse vanishing gradient solve nahi hota — do alag problems hain. Zero-centering zig-zag theek karta hai, saturation ko nahi.
  • tanh(z) = 2σ(2z) − 1 me "2z" bhoolna common hai; 2σ(z) − 1 galat hai (check: 2σ(1) − 1 = 0.4621 = tanh(0.5), tanh(1) nahi).

Dimaag me aise baitha lo

Tanh = sigmoid ka "−1 se +1" wala bada bhai: zero pe khada, chot 1 tak, par S-shape ki bimari (saturation) wahi.

9.6 ReLU — modern default

Kahani

Ghar ki paani ki tanki ke pipe me ek one-way valve laga hai. Pressure ulta (negative) ho to valve band — paani zero. Pressure seedha (positive) ho to jitna pressure, utna paani — seedha proportional. Na koi limit, na koi "atakna". Ye valve hai ReLU (Rectified Linear Unit). "Rectify" ka matlab hi hai ulta current rokna — jaise electronics me rectifier.

ReLU(z) = max(0, z) = z  agar z > 0,    0  agar z ≤ 0
ReLU′(z) = 1  agar z > 0,    0  agar z < 0,    z = 0 par undefined (PyTorch 0 leta hai)
  • Range [0, ∞) — 0 included (bracket), upar koi limit nahi (unbounded). ReLU(0) = 0.
  • z > 0 par function z hai, jiska slope 1. z < 0 par function 0 (constant), slope 0.
  • z = 0 par corner (kink): left slope 0, right slope 1 — dono alag, isliye derivative exist nahi karta. Libraries convention se 0 le leti hain.
−4 −2 2 4 2 4 z ReLU(z) kink (corner) at 0 z < 0 → output 0 slope 1 ReLU(z) = max(0, z) −4 −2 2 4 0.5 1 z ReLU′(z) 0 (no learning) 1 (kabhi nahi ghatta) PyTorch: ReLU′(0) = 0 derivative: 0 ya 1
ReLU: negative side flat 0, positive side 45° ki line. Derivative: left 0, right 1. Dhyaan do — right side ka derivative constant 1 hai, isme z hai hi nahi, to wo kabhi ghat nahi sakta: f′(6) = 1, f′(600) = 1.

Chalo numbers se samjhein — wahi saturated sawaal, ab ReLU ke saath (worksheet 03, Part 4.3)

Sawaal: x = 1, w = 6, b = 0, y = 0, α = 1 (sigmoid ke saath step sirf 0.00246 tha).

  1. z = 6,  ŷ = max(0, 6) = 6
  2. Error: 6 − 0 = 6
  3. Derivative: f′(6) = 1
  4. Gradient: 6 × 1 × 1 = 6
  5. Update: wnew = 6 − 1 × 6 = 0
  6. Check: z = 0, ŷ = 0, error = 0

Jawab: ek hi step me error exactly zero! Step ratio ReLU/sigmoid = 6/0.00246041 ≈ 2439.

Honest note (worksheet ka): do cheezein badli. Error ≈1 se 6 hua (kyunki ReLU unbounded hai) — ≈6 guna. Derivative 0.0025 se 1 hua — ≈405 guna (1/σ′(6) = 405.43). Asli hero derivative hai.

ReLU ke fayde

ReLU ke nuksaan

PyTorch · ReLU paanch tareeke + autograd

import torch
import torch.nn as nn
import torch.nn.functional as F

z = torch.tensor([-3.0, -0.5, 0.0, 0.5, 3.0], requires_grad=True)

r1 = torch.relu(z)              # functional (torch namespace)
r2 = F.relu(z)                  # functional (F namespace) - same cheez
r3 = nn.ReLU()(z)               # layer
r4 = torch.clamp(z, min=0)      # scratch: 0 se neeche wale ko 0 kar do
r5 = torch.maximum(z, torch.zeros_like(z))   # scratch: max(0, z)
print("relu   :", r1.detach())
print("same?  :", all(torch.equal(r1, r) for r in [r2, r3, r4, r5]))

r1.sum().backward()
print("grad   :", z.grad)       # z=0 par PyTorch 0 deta hai

Output

relu   : tensor([0.0000, 0.0000, 0.0000, 0.5000, 3.0000])
same?  : True
grad   : tensor([0., 0., 0., 1., 1.])

Dying ReLU problem

Kahani

Valve wali kahani yaad hai? Ek din bahut zor ka jhatka laga (bahut bada update) aur valve "band" position me jam gaya. Ab chahe kitna bhi pressure aaye, valve nahi khulta. Aur valve kholne wala mechanic tabhi aata hai jab thoda sa paani beh raha ho (gradient) — paani zero, mechanic kabhi nahi aayega. Neuron hamesha ke liye dead.

Kya hota hai: agar kisi ReLU neuron ka z = w·x + b har training input ke liye negative ho jaaye, to wo har input pe 0 output deta hai. Uska derivative bhi har jagah 0. Chain rule me ye 0 multiply hota hai, to us neuron ke weights aur bias ka gradient exactly 0 — weights kabhi update nahi honge, neuron permanently dead. (Slide: "If a neuron outputs 0 for all training inputs, its gradient is also 0 → weight never updates → neuron is 'dead' permanently.")

Kyun hota hai (causes):

−8 −6 −4 −2 2 4 2 4 z ReLU(z) saare training examples ka z < 0 output 0, gradient 0 → weights kabhi nahi hilte yahan aana tha
Dead ReLU: har training example ka z (kaale dots) flat red zone me. Output 0, derivative 0, update 0 — koi bhi example usko bahar nikalne ka signal nahi deta.

Chalo numbers se samjhein — worksheet 03, Part 5

Sawaal: x = 1, w = −3, b = 0, y = 1, α = 1.

  1. z = −3,  ŷ = max(0, −3) = 0
  2. Error: 0 − 1 = −1 (galat hai!)
  3. Derivative: f′(−3) = 0
  4. Gradient: (−1)(0)(1) = 0,  Update: wnew = −3 − 0 = −3
  5. Agla update: phir z = −3, phir gradient 0... hamesha.
  6. α = 1,000,000 kar do: 106 × 0 = 0 — phir bhi zero.

Worksheet: "This perceptron is not slow, it is dead. The gradient is exactly zero rather than merely small, and no learning rate can rescue a product containing zero."

PyTorch · worksheet ke dono sides autograd se

import torch

def one_update(x_val, w_val, b_val, y_val, alpha, act):
    x = torch.tensor(float(x_val))
    w = torch.tensor(float(w_val), requires_grad=True)
    b = torch.tensor(float(b_val), requires_grad=True)
    y = torch.tensor(float(y_val))
    y_hat = act(w * x + b)
    loss = 0.5 * (y_hat - y) ** 2
    loss.backward()
    with torch.no_grad():
        new_w = w - alpha * w.grad
    return y_hat.item(), w.grad.item(), new_w.item()

# Positive side: worksheet Part 4.3 (x=1, w=6, b=0, y=0, alpha=1)
print("ReLU, w=6 :", one_update(1, 6, 0, 0, 1.0, torch.relu))
# Negative side: worksheet Part 5 (x=1, w=-3, b=0, y=1, alpha=1)
print("ReLU, w=-3:", one_update(1, -3, 0, 1, 1.0, torch.relu))
# Learning rate 10 lakh bhi kuch nahi karega
print("ReLU, w=-3, alpha=1e6:", one_update(1, -3, 0, 1, 1e6, torch.relu))

Output

ReLU, w=6 : (6.0, 6.0, 0.0)
ReLU, w=-3: (0.0, 0.0, -3.0)
ReLU, w=-3, alpha=1e6: (0.0, 0.0, -3.0)

Tuple = (ŷ, ∂L/∂w, new w). Positive side: ek step me w 6 → 0. Negative side: gradient 0, w −3 pe atka, chahe α 10 lakh ho.

PyTorch · Lab 3 · "Spot the dead neuron" (solution)

import torch
import torch.nn.functional as F

W_hidden = torch.tensor([[ 0.2, -0.5,  0.1,  0.8],
                         [ 0.4,  0.3, -0.7,  0.2],
                         [-0.1,  0.6,  0.5, -0.3]])   # (3 inputs, 4 neurons)
b_hidden = torch.tensor([0.1, -0.2, 0.3, 0.0])

# Part A: 100 samples, values -2 se 2 (har sample me 3 same features)
x1 = torch.linspace(-2, 2, 100).unsqueeze(1).repeat(1, 3)   # (100,) -> (100,1) -> (100,3)
z_hidden = x1 @ W_hidden + b_hidden                         # (100,3) @ (3,4) -> (100,4)
a_hidden = F.relu(z_hidden)
print("Part A: range -2 to 2")
for i in range(a_hidden.shape[1]):                          # har column = ek neuron
    is_dead = torch.all(a_hidden[:, i] == 0).item()         # poore column me sirf 0?
    print(f"Neuron {i} completely dead? {is_dead}")

# Part B: data drift - saari values 5 se neeche shift
x2 = x1 - 5.0                                               # ab range -7 to -3
a_hidden2 = F.relu(x2 @ W_hidden + b_hidden)
print("\nPart B: range -7 to -3")
for i in range(a_hidden2.shape[1]):
    is_dead = torch.all(a_hidden2[:, i] == 0).item()
    print(f"Neuron {i} completely dead? {is_dead}")
print("column sums of W_hidden:", W_hidden.sum(dim=0))

Output

Part A: range -2 to 2
Neuron 0 completely dead? False
Neuron 1 completely dead? False
Neuron 2 completely dead? False
Neuron 3 completely dead? False

Part B: range -7 to -3
Neuron 0 completely dead? True
Neuron 1 completely dead? True
Neuron 2 completely dead? False
Neuron 3 completely dead? True
column sums of W_hidden: tensor([ 0.5000,  0.4000, -0.1000,  0.7000])

Fixes for dying ReLU: Leaky ReLU / PReLU / ELU (negative side pe non-zero slope), chhota learning rate, achha initialization (He init, Chapter 21), Batch Norm (Chapter 20).

Exam Radar

  • ReLU(z) = max(0, z), range [0, ∞), ReLU(0) = 0.
  • Derivative: 1 (z > 0), 0 (z < 0), undefined at 0 → PyTorch uses 0. Max derivative 1.
  • Pros: no vanishing gradient for z > 0, cheap, sparse. Default for hidden layers (CNNs, MLPs).
  • Cons: dying ReLU (cause: large LR / large negative bias; output 0 for all inputs → gradient 0 forever), not zero-centered, unbounded (final classification layer me nahi).
  • Code: torch.relu, F.relu, nn.ReLU(inplace=False), torch.clamp(z, min=0).

Galti mat karna

  • "ReLU me vanishing gradient bilkul nahi" — sirf z > 0 side pe. Negative side pe gradient vanish nahi, seedha zero hai (aur wahi dying ReLU hai).
  • Dead neuron "slow" nahi, "dead" hai: learning rate badhane se zinda nahi hoga.
  • ReLU linear nahi hai! Do linear tukde, beech me mod → non-linear.

Dimaag me aise baitha lo

ReLU = one-way valve: ulta pressure → 0, seedha → jitna aaya utna. Valve jam = dead neuron.

9.7 Leaky ReLU (aur PReLU) — neuron ko zinda rakho

Kahani

Valve band hai, lekin plumber ne usme jaan-boojh ke ek chhota sa leak chhod diya. Ulta pressure aaye to bhi thoda-sa paani (1%) rista rehta hai. Is ristey paani se mechanic ko pata chalta rehta hai ki pipe zinda hai, aur wo valve ko theek kar sakta hai. Yahi Leaky ReLU hai.

Pehle ek galat idea (worksheet 03, Part 6): "ReLU positive side pe isliye kaam karta hai kyunki wahan f(z) = z, slope 1. To negative side pe bhi f(z) = z kar do!" Ye identity function ban jaata hai: derivative hamesha 1, koi neuron nahi marega... par ŷ = z = wx + b — activation ne kuch kiya hi nahi. Network phir ek seedhi line (9.1 ka linear trap). Hume chahiye: negative side pe non-zero slope, lekin positive side se alag slope, taaki z = 0 par mod bana rahe.

LeakyReLU(z) = max(αz, z) = z  agar z > 0,    αz  agar z ≤ 0    (α = 0.01)
LeakyReLU′(z) = 1  agar z > 0,    α  agar z < 0
  • α = chhota constant, typically 0.01 (PyTorch me naam negative_slope, default 0.01). Hyperparameter — haath se choose.
  • Range (−∞, ∞): negative side pe bhi chhote negative outputs (−3 → −0.03).
  • max(αz, z) wala form sirf 0 < α < 1 ke liye sahi hai (positive z pe z > αz, negative pe αz > z).
−4 −2 2 4 2 4 z f(z) slope α: chhota, par flat nahi Leaky ReLU = max(αz, z) −4 −2 2 4 0.5 1 z f′(z) α (default 0.01) 1 jump α → 1 derivative: α ya 1
Leaky ReLU (negative slope ko 0.1 bana ke bada dikhaya hai taaki dikhe; asli default 0.01). Left side kabhi flat nahi. Derivative α se seedha 1 pe jump karta hai (z = 0 par discontinuous).

Chalo numbers se samjhein — wahi dead sawaal (worksheet 03, Part 7.2)

Sawaal: x = 1, w = −3, b = 0, y = 1, αlr = 1, slope 0.01.

  1. z = −3 (negative branch),  ŷ = 0.01 × (−3) = −0.03
  2. Error: −0.03 − 1 = −1.03
  3. Derivative: f′(−3) = 0.01
  4. Gradient: (−1.03)(0.01)(1) = −0.0103
  5. Update: wnew = −3 − (1)(−0.0103) = −2.9897

Jawab: w −3 → −2.9897. Neuron zinda hai (ReLU me step 0 tha), lekin "crawling" — bahut dheere.

Leaky ReLU ki do kamzoriyan (worksheet): (1) negative side ka output bahut chhota (−0.03), kyunki 0.01 ek chhota fixed constant hai jo haath se chuna; (2) z = 0 par derivative 0.01 se 1 pe jump karta hai — derivative continuous nahi.

PReLU — α ko bhi seekh lo

Parametric ReLU (PReLU): formula same, max(αz, z), lekin α ek learnable parameter hai jo backprop se train hota hai (He et al., 2015). Fayda: sahi slope data khud chunta hai. Nuksaan: extra parameters train karne padte hain (aur overfitting ka thoda risk). PyTorch: nn.PReLU(num_parameters=1, init=0.25) — default ek hi α poori layer ke liye, shuru me 0.25; num_parameters=C do to har channel ka alag α.

PyTorch · Leaky ReLU + PReLU

import torch
import torch.nn as nn
import torch.nn.functional as F

z = torch.tensor([-3.0, -1.0, 0.0, 2.0], requires_grad=True)

l1 = F.leaky_relu(z, negative_slope=0.01)       # functional; default slope bhi 0.01 hai
l2 = nn.LeakyReLU(0.01)(z)                      # layer; pehla argument = negative_slope
l3 = torch.where(z > 0, z, 0.01 * z)            # scratch: z>0 to z, warna 0.01*z
print("leaky :", l1.detach())
print("same? :", torch.allclose(l1, l2), torch.allclose(l1, l3))
l1.sum().backward()
print("grad  :", z.grad)

prelu = nn.PReLU()                              # alpha ek learnable parameter hai
print("PReLU alpha (init):", prelu.weight.data, "| trainable params:",
      sum(p.numel() for p in prelu.parameters()))
print("PReLU output:", prelu(torch.tensor([-3.0, 2.0])).detach())

Output

leaky : tensor([-0.0300, -0.0100,  0.0000,  2.0000])
same? : True True
grad  : tensor([0.0100, 0.0100, 0.0100, 1.0000])
PReLU alpha (init): tensor([0.2500]) | trainable params: 1
PReLU output: tensor([-0.7500,  2.0000])

Exam Radar

  • Leaky ReLU = max(αz, z), α = 0.01; PyTorch negative_slope=0.01 default.
  • Derivative: 1 (z > 0), α (z < 0) → fixes dying ReLU. Range (−∞, ∞).
  • Cons: α hand-tuned hyperparameter; tiny negative outputs; derivative jump at 0.
  • PReLU: α learned; nn.PReLU() default init 0.25, 1 parameter. Con: extra parameters.
  • Use: GANs ke hidden layers (dead units training bigaadte hain), jab ReLU units mar rahe hon.

Galti mat karna

  • Identity function (slope 1 dono taraf) = "Leaky ReLU with α = 1" = linear = bekaar. Slopes alag hone chahiye.
  • Leaky ReLU ka α (slope) aur learning rate α alag cheezein hain.
  • PReLU ka α learn hota hai, Leaky ka fixed — MCQ ka favourite difference.

Dimaag me aise baitha lo

Leaky = valve me 1% leak. PReLU = leak kitna ho, wo pipe khud seekhe.

9.8 ELU — smooth negative side

Kahani

Kitchen ki "soft-close" drawer dekhi hai? Dhakka do to wo seedha thak se nahi lagti — dheere-dheere smoothly ruk jaati hai ek fixed point pe. ELU ka negative side bhi aisa hi hai: seedhi leak wali line nahi, balki ek smooth curve jo dheere-dheere ek floor (−α) tak jaake ruk jaati hai.

ELU(z) = z  agar z > 0,    α(ez − 1)  agar z ≤ 0    (α = 1.0 default)
ELU′(z) = 1  agar z > 0,    αez = ELU(z) + α  agar z < 0
  • ELU = Exponential Linear Unit (Clevert et al., 2015). α = scale, PyTorch alpha=1.0 default.
  • Range (−α, ∞); α = 1 ke saath (−1, ∞). ELU(0) = α(e0 − 1) = 0.
  • z → −∞: ez → 0, to ELU → α(0 − 1) = −α. Ye floor hai: negative side bounded → saturates to −α.
  • Derivative derivation: d/dz[α(ez − 1)] = α·ez − 0 = αez (ez ka derivative ez, constant −1 ka 0). Aur αez = α(ez − 1) + α = ELU(z) + α — derivative output se hi nikal aata hai.

Smooth kyun? z = 0 par left side ka derivative αe0 = α, right side ka 1. Agar α = 1, dono barabar — koi jump nahi, function "continuously differentiable" (kink-free) ban jaata hai. ReLU (0 → 1 jump) aur Leaky (0.01 → 1 jump) dono ke paas kink hai, ELU (α = 1) ke paas nahi. Lab 3: "Because the gradients match from both directions, the transition is seamless... This smoothness can help gradient descent converge faster by avoiding oscillations around the origin."

−4 −2 2 4 −1 2 4 z ELU(z) floor −α = −1 ELU(−3) = −0.95 ELU, α = 1 −4 −2 2 4 0.5 1 z ELU′(z) αeᶻ = ELU(z) + α 1 (no jump at 0) derivative: smooth
ELU (α = 1): negative side smooth curve jo −1 ke floor ki taraf jaati hai. Derivative z = 0 par neeche se exactly 1 pe aata hai — no jump.

Chalo numbers se samjhein — worksheet 03, Part 8.2

Sawaal: x = 1, w = −3, b = 0, y = 1, learning rate 1, ELU α = 1. (Useful: e−3 = 0.049787)

  1. z = −3 (negative branch)
  2. ŷ = e−3 − 1 = 0.049787 − 1 = −0.950213
  3. Error: −0.950213 − 1 = −1.950213
  4. Derivative: f′(−3) = e−3 = 0.049787 (check: ELU + α = −0.950213 + 1 = 0.049787 ✓)
  5. Gradient: (−1.950213)(0.049787)(1) = −0.097095
  6. Update: wnew = −3 + 0.097095 = −2.902905

Jawab: w = −2.902905. ELU ka step / Leaky ka step = 0.097095/0.0103 ≈ 9.4 guna. Kyun? Negative output −0.95 (lagbhag −1, sirf −0.03 nahi) aur derivative 0.0498 (fixed 0.01 nahi).

Activation (x = 1, w = −3, y = 1)ŷf′(−3)∂L/∂ww: −3 →Verdict
ReLU000−3Dead
Identity−31−41Alive, par network ek seedhi line
Leaky ReLU−0.030.01−0.0103−2.9897Alive, crawling
ELU−0.9502130.049787−0.097095−2.902905Alive, moving properly

PyTorch · worksheet ka verdict table autograd se (float64)

import torch

def one_update(x_val, w_val, b_val, y_val, alpha, act):
    x = torch.tensor(float(x_val), dtype=torch.float64)
    w = torch.tensor(float(w_val), dtype=torch.float64, requires_grad=True)
    b = torch.tensor(float(b_val), dtype=torch.float64, requires_grad=True)
    y = torch.tensor(float(y_val), dtype=torch.float64)
    y_hat = act(w * x + b)
    loss = 0.5 * (y_hat - y) ** 2
    loss.backward()
    with torch.no_grad():
        new_w = w - alpha * w.grad
    return round(y_hat.item(), 6), round(w.grad.item(), 6), round(new_w.item(), 6)

import torch.nn.functional as F
# Worksheet Part 5/7/8: x=1, w=-3, b=0, y=1, alpha=1
print("ReLU      :", one_update(1, -3, 0, 1, 1.0, F.relu))
print("Identity  :", one_update(1, -3, 0, 1, 1.0, lambda t: t))
print("LeakyReLU :", one_update(1, -3, 0, 1, 1.0, lambda t: F.leaky_relu(t, 0.01)))
print("ELU       :", one_update(1, -3, 0, 1, 1.0, lambda t: F.elu(t, alpha=1.0)))

Output

ReLU      : (0.0, 0.0, -3.0)
Identity  : (-3.0, -4.0, 1.0)
LeakyReLU : (-0.03, -0.0103, -2.9897)
ELU       : (-0.950213, -0.097095, -2.902905)

PyTorch · ELU teen tareeke + derivative = ELU + α

import torch
import torch.nn as nn
import torch.nn.functional as F

z = torch.tensor([-5.0, -1.0, 0.0, 2.0], requires_grad=True)
alpha = 1.0

e1 = F.elu(z, alpha=alpha)                               # functional
e2 = nn.ELU(alpha=alpha)(z)                              # layer
e3 = torch.where(z > 0, z, alpha * (torch.exp(z) - 1))   # scratch
print("elu   :", e1.detach())
print("same? :", torch.allclose(e1, e2), torch.allclose(e1, e3))

e1.sum().backward()
print("grad          :", z.grad)
print("ELU(z) + alpha:", (e1 + alpha).detach(), "(sirf z <= 0 wale entries match karne chahiye)")

Output

elu   : tensor([-0.9933, -0.6321,  0.0000,  2.0000])
same? : True True
grad          : tensor([0.0067, 0.3679, 1.0000, 1.0000])
ELU(z) + alpha: tensor([0.0067, 0.3679, 1.0000, 3.0000]) (sirf z <= 0 wale entries match karne chahiye)

Negative entries (−5, −1) aur z = 0 pe grad = ELU + α (0.0067, 0.3679, 1.0). Positive z = 2 pe grad 1 hai, ELU + α = 3 nahi — formula sirf negative side ka hai. F.elu(x, alpha=1.0) functional, nn.ELU(alpha=1.0) layer; dono me alpha default 1.0.

ELU ke fayde

  • Never dies: negative side pe gradient αez > 0
  • Smooth at 0 (α = 1): no kink, behtar gradient flow
  • Negative outputs → mean activation zero ke paas → Batch Norm jaisa fayda, faster convergence
  • Bounded below (−α) → negative side noise ke liye robust; aur ek seedhi line nahi, to non-linear

Nuksaan

  • exp() lagta hai (z < 0 pe) → ReLU se slow
  • Bahut negative z pe derivative phir chhota (e−10 ≈ 0.000045) — negative side saturate hota hai
  • α ek aur hyperparameter

Use: hidden layers jahan bahut saare ReLU units mar rahe hon, ya jahan faster convergence chahiye.

−4 −3 −2 −1 1 2 3 4 −1 1 2 3 z f(z) ReLU / Leaky (≈) ELU (α = 1) sigmoid tanh σ, tanh: flat ho gaye ReLU, ELU: badhte rahe
Sab ek saath (worksheet Figure 11 jaisa): sigmoid aur tanh dono kinaron pe flat. ReLU, Leaky, ELU right side pe badhte hi rehte hain — isliye wahan vanishing gradient nahi. Left side: ReLU flat 0, Leaky lagbhag 0 se thoda neeche, ELU −1 ki taraf smooth.

Exam Radar

  • ELU = z (z > 0), α(ez − 1) (z ≤ 0); α = 1.0 default; range (−α, ∞).
  • Derivative z < 0: αez = ELU(z) + α; z = 0 pe α (=1 → smooth).
  • Saturates to −α for very negative z; mean activations closer to zero; costlier (exp).
  • ELU(−1) = −0.6321, ELU(−3) = −0.9502, ELU(−5) = −0.9933.
  • Lecture slide family summary: ReLU (fast, sparse, dies), Leaky (no dying, α tune), ELU (smooth, near-zero mean, exp cost), PReLU (α learned, extra params).

Galti mat karna

  • ELU ka range (−1, ∞) sirf α = 1 ke liye; general (−α, ∞).
  • ELU "saturate nahi hota" — galat; positive side pe nahi, negative side pe −α pe saturate hota hai. Ye feature hai (noise robustness), bug nahi.
  • ELU ka derivative z < 0 pe ez − 1 nahi, αez hai (−1 constant ka derivative 0).

Dimaag me aise baitha lo

ELU = soft-close drawer: positive pe ReLU, negative pe smooth landing at −α.

9.9 Softmax — scores ko probabilities me badlo

Kahani

Teen dost — Cat, Dog, Bird — ek hi biryani ki handi share kar rahe hain. Har ek ka "bhookh score" hai: 2.0, 1.0, 0.1. Rule: handi poori baantni hai (total = 1 handi), koi khaali haath nahi jaayega (sabko kuch na kuch), aur jo zyada bhookha uska hissa kaafi zyada — sirf thoda nahi. Softmax yahi baantwara karta hai: Cat ko 65.9%, Dog ko 24.2%, Bird ko 9.9%.

Ab tak ke activations ek number lete the, ek number dete the (element-wise). Softmax alag hai: ye poora vector leta hai (har class ka ek raw score, jise logit bolte hain) aur ek probability distribution deta hai. Sigmoid ek yes/no sawaal ka jawab deta hai. Teen class (cat/dog/bird) ke liye ek sigmoid kaafi nahi, aur teen alag sigmoids bhi nahi — wo independently answer karenge aur sum 1 nahi hoga (sigmoid([2, 1, 0.1]) = [0.8808, 0.7311, 0.5250], sum 2.1368!). Hume ek function chahiye jo teeno scores ko saath me dekhe.

softmax(z)i = si = eziΣj=1K ezj
  • z = (z1, ..., zK) = final layer ke K raw scores (logits), koi bhi real numbers
  • ezi = exponential: har value positive bana deta hai, aur bade score ko bahut zyada bada (amplify)
  • Σj ezj = saare exponentials ka total; isse divide karne pe sum exactly 1
  • Har si ∈ (0, 1), aur Σi si = 1 → valid probability distribution

Chalo numbers se samjhein — worksheet 03 Part 10.3 / Lab 3

Sawaal: logits z = [2.0, 1.0, 0.1] (cat, dog, bird). Softmax nikalo.

  1. Exponentiate: e2.0 = 7.389056,  e1.0 = 2.718282,  e0.1 = 1.105171
  2. Add: Σ = 7.389056 + 2.718282 + 1.105171 = 11.212509
  3. Divide: p1 = 7.389056/11.212509 = 0.659001
  4. p2 = 2.718282/11.212509 = 0.242433
  5. p3 = 1.105171/11.212509 = 0.098566
  6. Check: 0.659001 + 0.242433 + 0.098566 = 1.000000 ✓

Jawab: ≈ [0.659, 0.242, 0.099]. Sabse bada = cat, 65.9% confidence.

Lecture slide wala example: logits [2.0, 1.0, −1.0] → e: 7.39, 2.72, 0.37; Σ = 10.48 → 70.5%, 25.9%, 3.5%. Dhyaan do: scores me fark 2 vs 1 tha (sirf 2 guna), probabilities me 70.5 vs 25.9 (≈ e1 = 2.72 guna) — exponential differences ko amplify karta hai.

1 2 raw score z 2.0 cat 1.0 dog 0.1 bird 0.5 1 probability 0.659 cat 0.242 dog 0.099 bird softmax sum = 1.000, order same
Left: raw scores (koi bhi size). Right: softmax ke baad probabilities, sum exactly 1. Order same rehta hai: sabse bada score → sabse badi probability.

PyTorch · Lab 3 · softmax chaar tareeke

import torch
import torch.nn as nn
import torch.nn.functional as F

logits = torch.tensor([2.0, 1.0, 0.1])            # cat, dog, bird ke raw scores

p1 = torch.softmax(logits, dim=0)                 # functional (torch namespace)
p2 = F.softmax(logits, dim=0)                     # functional (F namespace)
p3 = nn.Softmax(dim=0)(logits)                    # layer
p4 = torch.exp(logits) / torch.exp(logits).sum()  # scratch
print("probs :", p1)
print("sum   :", p1.sum().item())
print("same? :", torch.allclose(p1, p2), torch.allclose(p1, p3), torch.allclose(p1, p4))
print("argmax logits:", logits.argmax().item(), "| argmax probs:", p1.argmax().item())

Output

probs : tensor([0.6590, 0.2424, 0.0986])
sum   : 1.0000001192092896
same? : True True True
argmax logits: 0 | argmax probs: 0

Sum 1.0000001192... aaya, exactly 1 nahi — ye float32 ki rounding hai (Lab 3 me bhi yahi print hua). torch.softmax(x, dim=...), F.softmax(x, dim=...), nn.Softmax(dim=...) — teeno me dim batana zaroori hai: kis dimension ke across sum 1 karna hai.

Key properties

Shift invariance: sab scores me same number jodo, kuch nahi badalta

Proof

softmax(z + c)i = ezi + cΣj ezj + c = ec · eziec · Σj ezj = eziΣj ezj = softmax(z)i

ec upar-neeche dono me hai, cancel. Check: [102, 101, 100.1] ka softmax bhi [0.659001, 0.242433, 0.098566]. Softmax sirf scores ke differences ko dekhta hai, absolute level ko nahi.

Numerical stability: subtract-max trick

Chalo numbers se samjhein — overflow

Sawaal: logits [1000, 1001, 1002].

  1. Naive: e1000 computer me inf (float32 sirf e88.7 tak, float64 e709.8 tak sambhal sakta hai). inf/inf = nan → output [nan, nan, nan]. Training barbaad.
  2. Trick: max (1002) subtract karo (shift invariance se answer nahi badlega): [1000, 1001, 1002] − 1002 = [−2, −1, 0]
  3. e−2 = 0.135335, e−1 = 0.367879, e0 = 1; Σ = 1.503215
  4. 0.135335/1.503215 = 0.090031,  0.367879/1.503215 = 0.244728,  1/1.503215 = 0.665241

Jawab: [0.0900, 0.2447, 0.6652]. Max subtract karne ke baad sabse bada exponent e0 = 1 — overflow impossible, aur denominator ≥ 1, to divide-by-zero bhi nahi.

PyTorch · overflow vs stable softmax

import torch

big = torch.tensor([1000.0, 1001.0, 1002.0])

naive = torch.exp(big) / torch.exp(big).sum()     # e^1000 = inf  ->  inf/inf = nan
print("naive :", naive)

shifted = big - big.max()                         # [-2, -1, 0]: max ab 0 hai
stable = torch.exp(shifted) / torch.exp(shifted).sum()
print("stable:", stable)
print("torch :", torch.softmax(big, dim=0))       # PyTorch andar se yahi trick karta hai

# Shift invariance: har score me same constant jodo, probs same
z = torch.tensor([2.0, 1.0, 0.1])
print(torch.softmax(z, dim=0), torch.softmax(z + 100, dim=0))

Output

naive : tensor([nan, nan, nan])
stable: tensor([0.0900, 0.2447, 0.6652])
torch : tensor([0.0900, 0.2447, 0.6652])
tensor([0.6590, 0.2424, 0.0986]) tensor([0.6590, 0.2424, 0.0986])

Teacher ke Lab 2 visualizer ke JavaScript me bhi softmax aise hi likha hai: Math.exp(s - maxS). Built-in torch.softmax ye trick khud karta hai; scratch implementation me tumhe karna padega.

Temperature (slide me hai)

softmax(z/T): T = temperature. Low T (< 1) → differences bade → distribution sharp (lagbhag one-hot). High T (> 1) → differences chhote → soft/uniform. T = 1 normal softmax. LLMs ka "temperature" setting yahi hai.

0.5 1 0.86 cat 0.12 dog 0.02 bird T = 0.5 (sharp) 0.66 cat 0.24 dog 0.10 bird T = 1 (normal) 0.50 cat 0.30 dog 0.19 bird T = 2 (soft)
Same logits [2.0, 1.0, 0.1]: T = 0.5 → [0.86, 0.12, 0.02] (sharp), T = 1 → [0.66, 0.24, 0.10], T = 2 → [0.50, 0.30, 0.19] (soft). Argmax teeno me cat hi.

Softmax vs Sigmoid, aur 2-class softmax = sigmoid

ProblemOutput activationOutput unitsExample
Binary (2 class, ek sawaal)Sigmoid1Spam ya nahi
Multi-class, single-label (exactly ek class sahi)SoftmaxK (har class ka ek)Digit 0–9 → 10 units
Multi-label (kai classes ek saath sahi ho sakti)Sigmoid har unit pe (independent)KPhoto me cat bhi, dog bhi

Proof: 2-class softmax = sigmoid of the difference

s1 = ez1ez1 + ez2   (upar-neeche ez1 se divide)
= 11 + ez2 − z1 = 11 + e−(z1 − z2) = σ(z1 − z2)

Worksheet ka version: scores z aur 0 lo → softmax = ez/(ez + 1) = 1/(1 + e−z) = σ(z). "Sigmoid is simply softmax for two classes." Numbers: logits [2.0, 0.5] → softmax [0.8176, 0.1824], aur σ(2.0 − 0.5) = σ(1.5) = 0.8176 ✓.

Derivative (Jacobian) — bas pehchaan lo

∂si∂zj = si(δij − sj)
  • δij = Kronecker delta: 1 agar i = j, warna 0
  • Diagonal (i = j): si(1 − si) — bilkul sigmoid jaisa!
  • Off-diagonal (i ≠ j): −sisj — ek class ka score badhao to baaki ki probabilities ghatti hain (sum 1 jo rehna hai)
  • Output ek vector hai, input ek vector — isliye derivative ek matrix (Jacobian). Har row ka sum 0 (kyunki Σs = 1 constant).

PyTorch · 2-class = sigmoid, temperature, Jacobian check

import torch

# 2-class softmax = sigmoid(z1 - z2)
z = torch.tensor([2.0, 0.5])
print("softmax :", torch.softmax(z, dim=0))
print("sigmoid :", torch.sigmoid(z[0] - z[1]), torch.sigmoid(z[1] - z[0]))

# Temperature: softmax(z / T)
logits = torch.tensor([2.0, 1.0, 0.1])
for T in [0.5, 1.0, 2.0]:
    print(f"T={T}:", torch.softmax(logits / T, dim=0))

# Jacobian: d s_i / d z_j = s_i * (delta_ij - s_j)
s = torch.softmax(logits, dim=0)
J_auto = torch.autograd.functional.jacobian(lambda t: torch.softmax(t, dim=0), logits)
J_formula = torch.diag(s) - torch.outer(s, s)
print("Jacobian:\n", J_auto)
print("formula match?", torch.allclose(J_auto, J_formula, atol=1e-6))

Output

softmax : tensor([0.8176, 0.1824])
sigmoid : tensor(0.8176) tensor(0.1824)
T=0.5: tensor([0.8638, 0.1169, 0.0193])
T=1.0: tensor([0.6590, 0.2424, 0.0986])
T=2.0: tensor([0.5017, 0.3043, 0.1940])
Jacobian:
 tensor([[ 0.2247, -0.1598, -0.0650],
        [-0.1598,  0.1837, -0.0239],
        [-0.0650, -0.0239,  0.0889]])
formula match? True

Jacobian diagonal: 0.659(1 − 0.659) = 0.2247 ✓. torch.autograd.functional.jacobian(f, x) poora derivative matrix de deta hai; torch.outer(s, s) = s ka har element s ke har element se multiply (3×3 matrix).

The dim trap (Lab 3 pro-tip)

Ek akela vector (shape (3,)) ho to dim=0. Lekin real training me batch hota hai: shape (N, K) — rows = samples, columns = classes. Har sample ki probabilities ka sum 1 chahiye → sum columns ke across, yaani dim=1.

PyTorch · nn.Softmax(dim=1) vs galat dim=0

import torch
import torch.nn as nn

# Batch: 2 samples (rows), 3 classes (columns)  -> shape (2, 3)
logits = torch.tensor([[2.0, 1.0, 0.1],
                       [1.0, 3.0, 0.5]])

right = nn.Softmax(dim=1)(logits)     # har ROW (sample) ke andar softmax
wrong = nn.Softmax(dim=0)(logits)     # har COLUMN ke andar - galat!
print("dim=1:\n", right, "\nrow sums:", right.sum(dim=1))
print("dim=0:\n", wrong, "\nrow sums:", wrong.sum(dim=1))

Output

dim=1:
 tensor([[0.6590, 0.2424, 0.0986],
        [0.1112, 0.8214, 0.0674]]) 
row sums: tensor([1.0000, 1.0000])
dim=0:
 tensor([[0.7311, 0.1192, 0.4013],
        [0.2689, 0.8808, 0.5987]]) 
row sums: tensor([1.2516, 1.7484])

dim=0 ne har column (ek class, saare samples) ko normalize kar diya — rows ka sum 1.25 aur 1.75, bakwaas. Koi error bhi nahi aayi! Silent bug. dim=-1 (last dimension) bhi (N, K) ke liye dim=1 jaisa hi kaam karta hai.

nn.CrossEntropyLoss ko raw logits do (link Chapter 11)

PyTorch ka nn.CrossEntropyLoss andar khud log-softmax lagata hai (stable tareeke se), phir correct class ki −log probability leta hai. Isliye model ki last layer pe softmax mat lagao jab ye loss use karo — warna softmax do baar lagega.

PyTorch · double softmax ki galti

import torch
import torch.nn as nn

torch.manual_seed(0)
logits = torch.tensor([[2.0, 1.0, 0.1],
                       [1.0, 3.0, 0.5]])     # model ka raw output, (N=2, K=3)
target = torch.tensor([0, 2])               # class indices: sample0 -> cat, sample1 -> bird

ce = nn.CrossEntropyLoss()                  # andar khud log-softmax lagata hai
print("sahi  (raw logits)      :", ce(logits, target).item())
print("galat (softmax pehle)   :", ce(torch.softmax(logits, dim=1), target).item())
manual = -(torch.log(torch.softmax(logits, dim=1))[[0, 1], target]).mean()
print("manual -mean(log p_true):", manual.item())

Output

sahi  (raw logits)      : 1.5568820238113403
galat (softmax pehle)   : 1.1150377988815308
manual -mean(log p_true): 1.5568821430206299

Sahi value 1.5569 = mean(−ln 0.659001, −ln 0.067425) = mean(0.4170, 2.6967). Softmax pehle laga diya to loss 1.1150 — galat number, aur gradients bhi kamzor (probabilities pe dobara softmax sab ko flat kar deta hai). Target [0, 2] class indices hain (dtype long), one-hot nahi.

Exam Radar

  • si = ezi/Σjezj; outputs (0, 1), sum = 1; [2.0, 1.0, 0.1] → [0.659, 0.242, 0.099].
  • Shift invariant: softmax(z + c) = softmax(z) → subtract max for stability ([1000, 1001, 1002] naive → nan; stable → [0.090, 0.245, 0.665]).
  • argmax unchanged by softmax (order preserved).
  • 2-class softmax = σ(z1 − z2). Binary → sigmoid (1 unit); multi-class single-label → softmax (K units); multi-label → K sigmoids.
  • Jacobian ∂si/∂zj = si(δij − sj).
  • Temperature: low T sharp, high T soft. Output layer only, never hidden.
  • Code: torch.softmax(x, dim=1), F.softmax(x, dim=1), nn.Softmax(dim=1) for (N, K) batches. nn.CrossEntropyLoss wants raw logits + class indices.

Galti mat karna

  • Softmax ko "element-wise" mat bolna — har output saare inputs pe depend karta hai.
  • Batch (N, K) pe dim=0 lagana: error nahi aayega, answer galat aayega.
  • nn.CrossEntropyLoss se pehle softmax lagana = double softmax.
  • Multi-label problem pe softmax: galat, kyunki softmax classes ko aapas me compete karwata hai (ek badhe to doosri ghate).

Dimaag me aise baitha lo

Biryani ki ek handi: ez se bhookh badhao, total se baanto, sab ka hissa jod ke 1. Sab ki bhookh me same number jodo — baantwara same.

9.10 Badi comparison table

FunctionFormulaRangef(0)DerivativeMax f′Zero-centered?Saturates?CostMain problemTypical use
Step / threshold1 if z ≥ 0, else 0{0, 1}10 (undefined at 0)0NoFlat everywhereCheapTrain hi nahi hotaHistorical (1958 perceptron)
Linear / identityz(−∞, ∞)011YesNoCheapestNetwork linear ho jaata haiRegression output layer
Sigmoid1/(1 + e−z)(0, 1)0.5σ(1 − σ)0.25NoYes, both endsexpVanishing gradient, zig-zagBinary output, multi-label, LSTM gates
Tanh(ez − e−z)/(ez + e−z)(−1, 1)01 − tanh21YesYes, both endsexpVanishing gradientRNN/LSTM states, old nets
ReLUmax(0, z)[0, ∞)00 or 1 (PyTorch: 0 at z = 0)1NoNo (z > 0)Very cheapDying ReLUDefault hidden layer (CNN, MLP)
Leaky ReLUmax(αz, z), α = 0.01(−∞, ∞)0α or 11~ (slide: ~Yes)NoVery cheapα hand-tuned, tiny negativesHidden layers, GANs
PReLUmax(αz, z), α learned(−∞, ∞)0α or 11 (if α ≤ 1)~NoCheap + paramsExtra parametersDeep CNNs
ELUz; α(ez − 1) for z ≤ 0(−α, ∞)01 or αez (= ELU + α)1 (α = 1)~ (mean near 0)Negative side → −αexp (z < 0)Slower than ReLUHidden layers, faster convergence
Softmaxezi/Σezj(0, 1), sum = 11/K if all z = 0si(δij − sj)N/AN/AN/Aexp + sumOverflow (use max trick)Multi-class output only

9.11 Kaunsa activation kahan? — practical guide

Kahani

Ek restaurant ki kitchen socho: andar ke saare cooks (hidden layers) ek hi tez, sasti technique use karte hain — ReLU. Lekin counter pe plate (output layer) kaise serve hogi, ye customer ke order pe depend karta hai: number chahiye (bill amount) to seedha number, haan/na chahiye to sigmoid, menu me se ek dish chunni hai to softmax.

x₁ x₂ ReLU ReLU ReLU ? input hidden: ReLU default (Leaky / ELU agar units mar rahe) output kaam pe depend: number → kuch nahi (linear) yes/no → sigmoid (1 unit) K classes → softmax (K units) multi-label → sigmoid (K units)
Hidden layers: ReLU default. Output layer ka activation task decide karta hai.
Jagah / TaskActivationOutput unitsLoss (PyTorch)
Hidden layers (default)ReLUany—
Hidden, units mar rahe honLeaky ReLU / ELU / PReLUany—
Hidden in RNN/LSTM, old netstanh (states), sigmoid (gates)——
Regression (number)None (linear); ReLU agar negative ho hi nahi sakta1 (per target)nn.MSELoss / nn.L1Loss
Binary classificationSigmoid1nn.BCELoss (after sigmoid) ya nn.BCEWithLogitsLoss (raw logit)
Multi-class (single label)SoftmaxKnn.CrossEntropyLoss — raw logits do, softmax nahi
Multi-labelSigmoid per classKnn.BCEWithLogitsLoss

Worksheet ke final questions: (1) 10 digits (0–9) classify karne hain → final layer me 10 units, softmax. (2) Chest X-ray normal ya pneumonia → 1 unit, sigmoid, binary cross-entropy. Worksheet ki line: "The activation function decides what the network can output. The loss function decides how we score it. They must be chosen as a pair."

PyTorch · ek nn.Module, teen tarah ke output

import torch
import torch.nn as nn

class Net(nn.Module):
    def __init__(self, in_features, n_out, task):
        super().__init__()
        self.task = task
        self.body = nn.Sequential(
            nn.Linear(in_features, 16), nn.ReLU(),    # hidden layer 1: ReLU
            nn.Linear(16, 8), nn.ReLU(),              # hidden layer 2: ReLU
        )
        self.head = nn.Linear(8, n_out)               # output layer: raw scores (logits)

    def forward(self, x):
        out = self.head(self.body(x))
        if self.task == "regression":
            return out                                # koi activation nahi
        if self.task == "binary":
            return torch.sigmoid(out)                 # (N, 1), har value (0, 1) me
        if self.task == "multiclass":
            return torch.softmax(out, dim=1)          # (N, K), har row ka sum 1

torch.manual_seed(0)
x = torch.randn(4, 5)                                 # 4 samples, 5 features
print("regression:", Net(5, 1, "regression")(x).shape)
print("binary    :", Net(5, 1, "binary")(x).squeeze().detach())
probs = Net(5, 3, "multiclass")(x)
print("multiclass row sums:", probs.sum(dim=1).detach())

Output

regression: torch.Size([4, 1])
binary    : tensor([0.5126, 0.4880, 0.5301, 0.4966])
multiclass row sums: tensor([1., 1., 1., 1.])

Exam Radar

  • Hidden default: ReLU. Dying problem → Leaky ReLU / ELU.
  • Output: regression → none/linear; binary → sigmoid (1 unit); multi-class → softmax (K units); multi-label → sigmoid (K units).
  • ReLU final classification layer me nahi (unbounded); softmax hidden layer me nahi.
  • nn.CrossEntropyLoss = log-softmax + NLL → raw logits do.

Dimaag me aise baitha lo

Andar ReLU, bahar order ke hisaab se: number → kuch nahi, haan/na → sigmoid, ek-me-se-ek → softmax.

Chapter 10

Universal Approximation Theorem, Depth vs Width

Network capacity · UAT (Cybenko 1989, Hornik 1991) · bumps & ReLU pieces · linear regions · folding · depth vs width trade-off

10.1 Network capacity, width aur depth — pehle shabd samjho

Kahani

Golu ke paas LEGO blocks hain. Kitni tarah ki cheezein bana sakta hai — ye do baaton pe depend karta hai: (1) har manzil (floor) me kitne blocks rakh sakta hai — chaudai, aur (2) kitni manzilein upar-upar jod sakta hai — oonchai. Zyada blocks = zyada tarah ki shapes. Neural network me bhi yahi: "kitni tarah ke functions bana sakta hai" = capacity; har layer me neurons = width; kitni layers = depth.

Definitions

Capacity (expressiveness / expressive power): network kitne alag-alag, kitne complicated functions represent kar sakta hai. Zyada capacity = zyada flexible (lekin overfitting ka risk bhi, Chapter 18).

Width (UDL me D): ek hidden layer me kitne neurons (hidden units).

Depth (UDL me K): kitni hidden layers. Ek hidden layer = shallow network (Chapter 7); do ya zyada = deep network.

Lecture slide: "Neural network architectures are primarily characterized by: Depth → number of layers, Width → number of neurons per layer. Both influence model capacity, expressiveness, and computational cost — but in different ways." Is chapter ka sawaal: ek hidden layer kitna kar sakta hai, aur depth kyun chahiye?

10.2 Universal Approximation Theorem (UAT)

Kahani

Ek darzi (tailor) kehta hai: "Mujhe kaafi saare chhote kapde ke tukde do, main tumhare body ke kisi bhi shape ka kurta itna fit bana dunga ki fark ek millimetre se kam ho." Wo ye nahi kehta ki kitne tukde lagenge (shayad lakhon), na ye ki wo kitni der me banayega. Bas itna guarantee: ban sakta hai. UAT bhi aisa hi darzi hai.

UAT — simple shabdon me

Ek feed-forward network jisme sirf ek hidden layer ho, us layer me kaafi (finite) neurons hon, aur ek non-linear activation ho — wo kisi bhi continuous function ko, ek closed aur bounded region pe, jitna chaho utni accuracy se approximate kar sakta hai.

Har continuous f aur har ε > 0 ke liye aise N, vi, wi, bi exist karte hain ki
F(x) = Σi=1N vi φ(wi·x + bi)    aur    |F(x) − f(x)| < ε   har x ∈ K ke liye
  • F(x) = shallow network: N hidden units, unit i ke weights wi, bias bi, output weight vi (chaaho to output bias bhi jodo)
  • φ = non-linear activation (Cybenko: sigmoid; general: koi bhi non-polynomial, jaise ReLU, tanh)
  • K = compact set ⊂ ℝn = closed + bounded region, jaise [0, 1] ya [−π, π] ya ek box. Poori infinite real line nahi.
  • ε (epsilon) = allowed galti; jitna chhota chaho (0.1, 0.001, ...) — bas N badhta jaayega
  • "Universal" = kisi bhi continuous function ke liye; "Approximation" = exact nahi, ε ke andar

History (MCQ favourite):

Lecture slide ka exact statement: "A feed-forward network with a single hidden layer containing a finite number of neurons can approximate continuous functions on compact subsets of ℝn." Aur turant "The Catch: To approximate complex functions, that single layer might need an exponentially large number of neurons (infinite width)."

Exam Radar

  • UAT: one hidden layer, finite (but enough) neurons, non-linear activation → any continuous function on a compact (closed, bounded) set, any accuracy ε.
  • Cybenko 1989 = sigmoid; Hornik 1991 = general (architecture is what matters).
  • Linear activation ke saath UAT fail (network linear hi rahega) — non-linearity zaroori.
  • Catch: neurons ki sankhya exponentially badi ho sakti hai.

Galti mat karna

  • "UAT kehta hai ek hidden layer kaafi hai, to deep networks bekaar" — galat. UAT existence batata hai, efficiency nahi (10.5, 10.7).
  • "Any function" nahi — continuous function, compact domain pe.
  • "Exact" nahi — approximate (ε ke andar).

Dimaag me aise baitha lo

UAT = darzi ki guarantee: "ban jayega" — kitne tukde, kitna time, aur doosre insaan pe fit hoga ya nahi — ye guarantee nahi.

10.3 Intuition 1: sigmoid se "bump", bumps se koi bhi curve

Kahani

Diwali pe ghar ki deewar pe LED lights ka "skyline" banana hai. Har light ek chhota sa khamba (bar) hai — jitni chaho utni oonchai. Bahut saare patle-patle khambe paas-paas lagao, har ek ki oonchai curve ke hisaab se rakho — door se dekhne pe poora curve dikhta hai. Sigmoid neurons se aise hi "khambe" (bumps) bante hain.

Step 1 — steep sigmoid = lagbhag step. σ(k(x − a)) me k bada lo (weight bada) to S-curve bahut steep ho jaata hai: x < a pe ≈ 0, x > a pe ≈ 1. Bias se jump ki jagah (a) chunte hain: σ(kx − ka), yaani weight k, bias −ka.

Step 2 — do steps ka difference = bump. Ek step a pe upar, doosra b pe: bump(x) = σ(k(x − a)) − σ(k(x − b)). [a, b] ke andar ≈ 1, bahar ≈ 0. Ek bump = 2 hidden neurons (output weights +h aur −h, jahan h = bump ki oonchai).

Chalo numbers se samjhein

Sawaal: a = 1, b = 2, k = 10. bump(x) nikalo.

  1. x = 1.5 (andar): σ(10 × 0.5) − σ(10 × (−0.5)) = σ(5) − σ(−5) = 0.993307 − 0.006693 = 0.986614
  2. x = 1 (kinare pe): σ(0) − σ(−10) = 0.5 − 0.000045 = 0.499955
  3. x = 0 (bahar): σ(−10) − σ(−20) ≈ 0.000045 − 0.000000 = 0.000045
  4. x = 3 (bahar): σ(20) − σ(10) = 1.000000 − 0.999955 = 0.000045

Jawab: andar ≈ 0.99, bahar ≈ 0 — ek saaf bump. k aur bada karo to aur bhi rectangle jaisa.

1 2 3 0.5 1 x σ(10(x − 1)) σ(10(x − 2)) do shifted steep sigmoids 1 2 3 0.5 1 x 0.9866 ≈ 0 bahar difference = bump (1 se 2 tak)
Left: σ(10(x − 1)) aur σ(10(x − 2)). Right: unka difference = 1 se 2 tak ek bump (peak 0.9866).

Step 3 — bahut saare bumps = koi bhi curve. Domain ko N chhote tukdon me baanto, har tukde pe ek bump, oonchai = us tukde ke beech me f ki value. Ye ek "seedhi" (staircase) banata hai jo f ke paas hai. Tukde patle karo (N badhao) → staircase f ke aur paas. Yahi UAT ka dil hai.

1 2 3 0.5 1 x 4 bumps = 8 sigmoid units 1 2 3 0.5 1 x 8 bumps = 16 sigmoid units
sin(x) on [0, π] (dashed) ko 4 bumps (8 units) aur 8 bumps (16 units) se approximate kiya. Zyada bumps → zyada patli seedhiyan → better fit.
Bumps NHidden units (2N)Max error (ideal steps), sin on [0, π]
480.3827
8160.1951
16320.0980
32640.0491

Pattern: units double → error lagbhag aadha. Koi bhi ε chahiye? Bas N itna bada lo. (Numbers numpy se verify kiye — har tukde pe beech wali value, sabse buri galti tukde ke kinare pe.)

Dimaag me aise baitha lo

2 sigmoid = 1 bump (khamba). Bahut saare khambe = koi bhi skyline.

10.4 Intuition 2: ReLU ke tukdon se koi bhi curve

Chapter 7 yaad karo: shallow ReLU network ka output ek piecewise-linear function hai — seedhe tukde, aur har hidden unit ek joint (mod) deta hai jahan uska z = 0 hota hai, yaani x = −b/w. Joint ke baad us unit ka slope add ho jaata hai. To curve ko chhote seedhe tukdon se jodo — jaise chakri (polygon) se circle banate hain. Jitne zyada units, utne zyada tukde, utna better fit.

Chalo numbers se samjhein — haath se ek network banao jo [0, 2] pe y = x2 kare

Idea: points x = 0, 0.5, 1, 1.5, 2 pe x2 = 0, 0.25, 1, 2.25, 4. Inhe seedhi lines se jodo (4 tukde).

  1. Har tukde ka slope: (0.25 − 0)/0.5 = 0.5,  (1 − 0.25)/0.5 = 1.5,  (2.25 − 1)/0.5 = 2.5,  (4 − 2.25)/0.5 = 3.5
  2. Pehla unit ReLU(x) shuru ka slope deta hai: v1 = 0.5
  3. Har joint pe slope kitna badhta hai, wahi us unit ka output weight: v2 = 1.5 − 0.5 = 1,  v3 = 2.5 − 1.5 = 1,  v4 = 3.5 − 2.5 = 1
  4. Network: ŷ = 0.5·ReLU(x) + 1·ReLU(x − 0.5) + 1·ReLU(x − 1) + 1·ReLU(x − 1.5) (hidden weights sab 1, biases 0, −0.5, −1, −1.5)
  5. Check x = 0.75: 0.5(0.75) + 1(0.25) + 0 + 0 = 0.375 + 0.25 = 0.625; sach: 0.752 = 0.5625; error 0.0625
  6. Check x = 2: 0.5(2) + 1(1.5) + 1(1) + 1(0.5) = 1 + 1.5 + 1 + 0.5 = 4 ✓ (exact)

Jawab: 4 ReLU units se max error 0.0625 (tukdon ke beech me). Ye error = h2/4 hai jahan h = tukde ki lambai (0.5). 2 units (h = 1) → 0.25; 8 units (h = 0.25) → 0.015625; 16 units → 0.0039. Units double → error ÷ 4.

0.5 1 1.5 2 1 2 3 4 x 0.5·ReLU(x) 1·ReLU(x − 0.5) 1·ReLU(x − 1) 1·ReLU(x − 1.5) 4 hinge units (alag-alag) 0.5 1 1.5 2 1 2 3 4 x x² (dashed) 4 units: max error 0.0625 2 units: max error 0.25 sum ≈ x²
Left: 4 hinge units alag-alag (har ek ka apna joint). Right: unka sum (red, joints pe laal dots) x2 (dashed) ke upar baitha hai; patli orange line = sirf 2 units wala approximation.

PyTorch · haath se set kiya hua shallow ReLU net (weights khud daale)

import torch
import torch.nn as nn

# Hand-made shallow ReLU network jo [0, 2] par x^2 ko 4 seedhe tukdon se approximate kare
class HingeNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.hidden = nn.Linear(1, 4)        # 4 ReLU units, har unit ka ek "joint"
        self.out = nn.Linear(4, 1)
        with torch.no_grad():
            self.hidden.weight.fill_(1.0)                                # sab weights 1
            self.hidden.bias.copy_(torch.tensor([0.0, -0.5, -1.0, -1.5]))  # joints: 0, 0.5, 1, 1.5
            self.out.weight.copy_(torch.tensor([[0.5, 1.0, 1.0, 1.0]]))    # slope kitna badhe
            self.out.bias.fill_(0.0)

    def forward(self, x):                    # x: (N, 1)
        return self.out(torch.relu(self.hidden(x)))   # (N,1) -> (N,4) -> (N,1)

net = HingeNet()
x = torch.tensor([[0.0], [0.25], [0.5], [0.75], [1.0], [1.5], [1.75], [2.0]])
with torch.no_grad():
    y_hat = net(x)
for xi, yi in zip(x.squeeze().tolist(), y_hat.squeeze().tolist()):
    print(f"x = {xi:4.2f}   net = {yi:6.4f}   x^2 = {xi**2:6.4f}   error = {yi - xi**2:6.4f}")

Output

x = 0.00   net = 0.0000   x^2 = 0.0000   error = 0.0000
x = 0.25   net = 0.1250   x^2 = 0.0625   error = 0.0625
x = 0.50   net = 0.2500   x^2 = 0.2500   error = 0.0000
x = 0.75   net = 0.6250   x^2 = 0.5625   error = 0.0625
x = 1.00   net = 1.0000   x^2 = 1.0000   error = 0.0000
x = 1.50   net = 2.2500   x^2 = 2.2500   error = 0.0000
x = 1.75   net = 3.1250   x^2 = 3.0625   error = 0.0625
x = 2.00   net = 4.0000   x^2 = 4.0000   error = 0.0000

nn.Linear(1, 4) ki weight shape (4, 1) hai (out_features, in_features); .fill_(1.0) sab ko 1 karta hai (underscore = in-place). Joints (0.5, 1, 1.5, aur 0) pe error exactly 0, tukdon ke beech 0.0625.

Training se: zyada hidden units → better fit

Ab weights haath se nahi, gradient descent se. sin(x) on [−π, π], 200 points, shallow ReLU net, Adam (lr = 0.01), 3000 full-batch epochs, sirf hidden units badle:

PyTorch · shallow net, alag widths, sin(x) fit

import math
import torch
import torch.nn as nn
import torch.optim as optim

x = torch.linspace(-math.pi, math.pi, 200).unsqueeze(1)   # (200, 1) inputs
y = torch.sin(x)                                           # (200, 1) targets

def fit(hidden_units, epochs=3000):
    torch.manual_seed(0)                                   # har run same tareeke se shuru
    model = nn.Sequential(nn.Linear(1, hidden_units),      # 1 hidden layer (shallow)
                          nn.ReLU(),
                          nn.Linear(hidden_units, 1))
    opt = optim.Adam(model.parameters(), lr=0.01)
    loss_fn = nn.MSELoss()
    for epoch in range(epochs):                            # full-batch training
        opt.zero_grad()
        loss = loss_fn(model(x), y)
        loss.backward()
        opt.step()
    return loss.item(), sum(p.numel() for p in model.parameters())

for h in [1, 2, 4, 8, 16, 64]:
    mse, n_params = fit(h)
    print(f"hidden units = {h:3d} | params = {n_params:4d} | final MSE = {mse:.6f}")

Output (real run, CPU)

hidden units =   1 | params =    4 | final MSE = 0.162880
hidden units =   2 | params =    7 | final MSE = 0.162880
hidden units =   4 | params =   13 | final MSE = 0.148979
hidden units =   8 | params =   25 | final MSE = 0.003322
hidden units =  16 | params =   49 | final MSE = 0.000121
hidden units =  64 | params =  193 | final MSE = 0.000189
−3 −2 −1 1 2 3 −1 1 x grey (mota) = sin x H = 2: MSE 0.1629 H = 8: MSE 0.0033 H = 16: MSE 0.000121
Trained fits (same seed): H = 2 sin ki shape pakad hi nahi paaya, H = 8 kaafi paas, H = 16 lagbhag sin ke upar.

Trend saaf: 1–2 units → MSE 0.163 (bas ek tedhi line), 8 units → 0.0033, 16 units → 0.00012. Do baatein dhyaan se: (1) H = 4 ka 0.149 — 4 units me kaafi achha fit possible tha, phir bhi training atak gayi (agle section me wajah). (2) H = 64 (0.000189) aur H = 16 (0.000121) dono practically perfect; chhota fark Adam ke aakhri steps ke noise ka hai — capacity kaafi ho jaane ke baad aur units se fit aur better hona zaroori nahi.

Exam Radar

  • Shallow ReLU net = piecewise-linear function; unit i ka joint x = −bi/wi.
  • More units → more pieces → better approximation (UAT ka constructive picture).
  • Sigmoid view: 2 units = 1 bump; ReLU view: 1 unit = 1 joint.

10.5 UAT kya promise NAHI karta

Kahani

Darzi wali guarantee yaad hai? "Kurta fit ban sakta hai." Par: (1) shayad 10 lakh tukde lagein, (2) darzi ko shayad pata hi na chale ki tukde kaise jodne hain, (3) tumhare naap pe bana kurta tumhare bhai pe fit hoga ya nahi — koi guarantee nahi, (4) aur ye bhi nahi bataya ki silai ka koi aur tareeka (layers me banana) kam tukdon me kaam kar deta.

  1. Kitne neurons? Nahi batata. Zaroori neurons astronomically (exponentially) zyada ho sakte hain — khaaskar jab input dimensions zyada hon. Slide: "might need an exponentially large number of neurons (infinite width)."
  2. Training weights dhoondh payegi? Nahi. UAT sirf kehta hai sahi weights exist karte hain (existence proof). Gradient descent unhe dhoondh le — iski koi guarantee nahi (local minima, dead units, bad init).
  3. Generalization? Nahi. Training points pe perfect fit ka matlab naye data pe achha hona nahi — overfitting ho sakta hai (Chapter 18). UAT sirf "fit kar sakta hai" bolta hai, "sahi seekhega" nahi.
  4. Depth? Kuch nahi kehta. Ye nahi kehta ki ek layer best hai; deep networks wahi kaam bahut kam parameters me kar sakte hain (10.7).
  5. Domain ke bahar? Sirf compact (bounded) region pe guarantee. Range ke bahar (extrapolation) koi promise nahi — ReLU net to door jaake seedhi line ban jaata hai.

PyTorch · "weights exist" ≠ "training dhoondh legi" (H = 4)

import math
import torch
import torch.nn as nn
import torch.optim as optim

x = torch.linspace(-math.pi, math.pi, 200).unsqueeze(1)
y = torch.sin(x)

def fit_and_inspect(hidden_units, seed, epochs=3000):
    torch.manual_seed(seed)                               # alag seed = alag random init
    model = nn.Sequential(nn.Linear(1, hidden_units), nn.ReLU(), nn.Linear(hidden_units, 1))
    opt = optim.Adam(model.parameters(), lr=0.01)
    for _ in range(epochs):
        opt.zero_grad()
        loss = nn.functional.mse_loss(model(x), y)
        loss.backward()
        opt.step()
    with torch.no_grad():
        a = torch.relu(model[0](x))                       # (200, H) hidden activations
        dead = (a.sum(dim=0) == 0).sum().item()           # jo unit har x pe 0 de raha hai
    return loss.item(), dead

for seed in [0, 1]:
    mse, dead = fit_and_inspect(4, seed)
    print(f"H = 4, seed = {seed}: final MSE = {mse:.6f}, dead units = {dead}")

Output

H = 4, seed = 0: final MSE = 0.148979, dead units = 1
H = 4, seed = 1: final MSE = 0.005328, dead units = 1

Same architecture (4 units), sirf random init alag: seed 0 → MSE 0.149 (atak gaya), seed 1 → 0.0053 (28 guna better). Dono me ek unit dead ReLU nikla (har x pe output 0 — 9.6). UAT ne kaha tha 4 units kaafi achha kar sakte hain; training ne har baar wo nahi dhoondha. Yahi point #2 hai.

Galti mat karna

  • UAT ≠ learnability theorem. UAT ≠ generalization theorem.
  • "Finite neurons" ka matlab "kam neurons" nahi — finite par bahut bade ho sakte hain.

Dimaag me aise baitha lo

UAT: "ho sakta hai" (CAN). Training: "hoga ya nahi" (WILL). Generalization: "naye data pe chalega ya nahi". Teen alag sawaal.

10.6 Linear regions — shallow network kitne tukde bana sakta hai?

ReLU network input space ko linear regions me baant deta hai: har region ke andar function ek seedhi line (ya flat plane) hai, aur regions ki boundaries pe mod. Jitne zyada regions, utna complicated function — regions ki ginti capacity ka ek achha naap hai.

1-D input, ek hidden layer, D ReLU units → at most D + 1 linear regions
Parameters (1 input, 1 output): D weights + D biases + D output weights + 1 output bias = 3D + 1
  • Har unit ka ek joint (x = −b/w). D joints ek line ko zyada se zyada D + 1 tukdon me kaatte hain (jaise ek rassi pe 3 gaanth = 4 tukde).
  • "At most" isliye: do units ka joint same jagah ho, ya joint domain ke bahar ho, to kam regions.
  • Di-dimensional input pe har unit ek hyperplane boundary banata hai; regions at most Σj=0Di C(D, j). Example: 2-D input, 3 units → C(3,0) + C(3,1) + C(3,2) = 1 + 3 + 3 = 7 (teen lines ek kaagaz ko 7 hisson me kaat sakti hain). 2-D, 10 units → 1 + 10 + 45 = 56.
−2 −1 1 2 2 4 x y(x) region 1 2 3 region 4 3 ReLU units → 3 joints → 4 linear regions
Lab 2 visualizer wala network (3 ReLU units): joints x = 0.3, 0.5, 0.769 → D + 1 = 4 linear regions. Har region me output ek seedhi line.

Exam Radar

  • Shallow ReLU net, 1-D input, D units → ≤ D + 1 regions, 3D + 1 parameters. Regions width ke saath sirf linearly badhte hain.

10.7 Depth ka jaadu — composition aur "folding"

Kahani

Ek A4 kaagaz lo. Beech se modo (fold) — 2 parat. Phir modo — 4 parat. Phir — 8. Ab us mude hue kaagaz pe kainchi se ek seedha cut lagao (saari parton ke aar-paar) aur kaagaz khol do. Kitne cut dikhenge? 8! Ek baar ki mehnat, 8 jagah copy. 10 baar modo to ek cut → 1024 cuts. Deep network yahi karta hai: pehli layers input space ko fold karti hain, aur agli layer jo bhi function banati hai wo har fold pe copy ho jaata hai.

Lecture slide (UDL Chapter 4 se): "A way to think about composing networks is that the first network 'folds' the input space x back onto itself. Thus multiple inputs generate the same output. Then the second network applies a function, which is replicated at all points that were folded on top of one another. This allows us to create complex distributions using a combination of layers."

0 0.5 1 fold x = 0.2 x = 0.8 01 T(0.2) = T(0.8) = 0.4 layer 1: kaagaz kobeech se modo ab layer 2 jo bhikaregi, dono halves peek saath (copy) hoga
Fold: x = 0.2 aur x = 0.8 dono 0.4 pe aa jaate hain. Ab agli layer 0.4 ke saath jo karegi, wo dono inputs ke saath ek saath hoga — function dono halves pe mirror-copy.

Chalo numbers se samjhein — "tent" fold, sirf 2 ReLU units

Fold function: T(x) = 2·ReLU(x) − 4·ReLU(x − 0.5) on [0, 1].

  1. x = 0.2: 2(0.2) − 4(0) = 0.4
  2. x = 0.8: 2(0.8) − 4(0.3) = 1.6 − 1.2 = 0.4 — same! Fold ho gaya.
  3. x = 0.5: 2(0.5) − 0 = 1; x = 1: 2 − 4(0.5) = 0. T poore [0, 1] ko do baar [0, 1] pe map karta hai (upar jaao, neeche aao) → 2 pieces.
  4. T(T(x)): har half pe poora T dobara → 4 pieces. Teen baar → 8. k layers (har ek me 2 units) → 2k pieces, sirf 2k units.
  5. Shallow network ko 2k pieces ke liye D + 1 ≥ 2k → D ≥ 2k − 1 units chahiye.
  6. k = 10: deep = 20 units → 1024 pieces; shallow ≥ 1023 units. k = 20: deep = 40 units → 1,048,576 pieces; shallow ≥ 1,048,575 units!

Depth ke saath regions exponentially (2k) badhte hain; width ke saath sirf linearly (D + 1).

0.5 1 1 x 1 layer: 2 pieces 0.5 1 x 2 layers: 4 pieces 0.5 1 x 3 layers: 8 pieces
1 layer (2 units): 2 pieces. 2 layers (4 units): 4 pieces. 3 layers (6 units): 8 pieces. Har nayi layer pichhle pattern ko har fold pe copy karke pieces double karti hai.

PyTorch · deep "tent" network, pieces ginti

import torch
import torch.nn as nn

class TentLayer(nn.Module):
    """2 ReLU units: T(x) = 2*relu(x) - 4*relu(x - 0.5)  -> [0,1] ko 'fold' karta hai"""
    def __init__(self):
        super().__init__()
        self.hidden = nn.Linear(1, 2)
        self.combine = nn.Linear(2, 1)
        with torch.no_grad():
            self.hidden.weight.copy_(torch.tensor([[1.0], [1.0]]))
            self.hidden.bias.copy_(torch.tensor([0.0, -0.5]))
            self.combine.weight.copy_(torch.tensor([[2.0, -4.0]]))
            self.combine.bias.fill_(0.0)

    def forward(self, x):
        return self.combine(torch.relu(self.hidden(x)))

def count_pieces(model):
    x = torch.linspace(0, 1, 2**16 + 1).unsqueeze(1)
    with torch.no_grad():
        y = model(x).squeeze()
    slopes = (y[1:] - y[:-1]) / (x[1:, 0] - x[:-1, 0])
    changes = (slopes[1:] - slopes[:-1]).abs() > 1e-3     # slope kahan badla
    return changes.sum().item() + 1

for depth in [1, 2, 3, 4, 5, 6]:
    deep = nn.Sequential(*[TentLayer() for _ in range(depth)])   # depth baar fold
    units = 2 * depth
    print(f"depth {depth}: ReLU units = {units:2d}, linear pieces = {count_pieces(deep):2d}, "
          f"shallow net needs >= {2**depth - 1} units")

Output

depth 1: ReLU units =  2, linear pieces =  2, shallow net needs >= 1 units
depth 2: ReLU units =  4, linear pieces =  4, shallow net needs >= 3 units
depth 3: ReLU units =  6, linear pieces =  8, shallow net needs >= 7 units
depth 4: ReLU units =  8, linear pieces = 16, shallow net needs >= 15 units
depth 5: ReLU units = 10, linear pieces = 32, shallow net needs >= 31 units
depth 6: ReLU units = 12, linear pieces = 64, shallow net needs >= 63 units

nn.Sequential(*[TentLayer() for _ in range(depth)]) — list ke saare layers ko unpack (*) karke ek ke baad ek jodta hai. Pieces gin ne ke liye 65,537 points pe slope nikala aur dekha slope kitni baar badla. Depth 3 se aage deep network kam units me zyada pieces de raha hai, aur gap exponentially badhta hai.

UDL ka general result (slide "Number of regions vs parameters")

Deep net: 1 input, 1 output, K hidden layers × D units (D > 2) → up to (D + 1)K linear regions
Parameters = 3D + 1 + (K − 1)D(D + 1)
  • 3D + 1: pehli layer (D weights + D biases) + output layer (D weights + 1 bias) — shallow wala hi count
  • (K − 1)D(D + 1): K hidden layers ke beech K − 1 connections; har ek me D × D weights + D biases = D(D + 1)
  • Slide pe agar "(K − 1)D(D − 1)" jaisa dikhe to khud gin ke check karo: D×D weights + D biases = D(D + 1). Example se confirm: K = 5, D = 10 → 471 (UDL Fig 4.7 ka highlighted point), (D − 1) wala formula 391 deta — mismatch.

Chalo numbers se samjhein — same budget, shallow vs deep

  1. Deep, K = 5, D = 10: params = 3(10) + 1 + (5 − 1)(10)(11) = 31 + 440 = 471
  2. Regions: (10 + 1)5 = 115 = 161,051
  3. Shallow with ~471 params: 3D + 1 ≈ 471 → D = 156 (3 × 156 + 1 = 469 params)
  4. Shallow regions: D + 1 = 157

Lagbhag same parameters: shallow 157 regions, deep 161,051 regions — ~1000 guna zyada. (UDL Fig 4.7 ka doosra case: 10 inputs, K = 5, D = 50 → 10,801 parameters aur 1040 se zyada regions.)

shallow: 1 × 156 units, 469 params 157 regions deep: 5 × 10 units, 471 params 161,051 regions 10⁰ 10¹ 10² 10³ 10⁴ 10⁵ 10⁶ logscale
Log scale: ~470 parameters ke budget me shallow network 157 regions, deep (5 × 10) 161,051 regions.

10.8 Depth efficiency — kuch functions ke liye shallow ko exponential units chahiye

Depth efficiency: aise functions exist karte hain jinhe deep network thode units/parameters se bana leta hai, lekin shallow network ko unhi ke liye exponentially zyada units chahiye. Tent example isi ka proof tha (2k units vs 2k − 1). Slide: "Depth allows for compositional efficiency. A deep network can represent a function with O(N) parameters that a shallow network would need O(2N) parameters to represent." (Research: Montúfar et al. 2014 — regions; Telgarsky 2016, Eldan & Shamir 2016 — depth separation.)

Face analogy (slide): "It is easier to describe a face as (Eyes + Nose + Mouth) than to describe every pixel coordinate individually." Real-world data compositional hota hai — chhote parts milke bade parts, bade parts milke object. Deep network har layer me ek level upar ka feature banata hai, aur neeche ke features ko reuse karta hai. Shallow network ko har combination alag se yaad rakhna padta hai.

pixelsraw numbers layer 1edges, lines layer 2shapes: aankh, naak layer 3object: chehra har layer pichhli layer ke features ko jod ke bade features banati hai
Hierarchical features (CNN me clearly dikhta hai, Chapter 23): pixels → edges → shapes (aankh, naak) → poora chehra.

Dimaag me aise baitha lo

Width = zyada log ek hi floor pe kaam karein (jodna). Depth = assembly line (fold pe fold, guna-guna). Jodna linear, guna exponential.

10.9 Depth vs Width — trade-off

Wide & shallowdepth 1, width 8 Deep & narrowdepth 4, width 3 depth K = hidden layers width D = units/layer
Left: wide & shallow (1 hidden layer, 8 units). Right: deep & narrow (4 hidden layers, 3 units each).

Go Deeper (modern trend)

Pros

  • Zyada expressive per parameter (regions exponential in depth)
  • Hierarchical features: edges → shapes → objects; features reuse
  • Aksar better generalization (UDL: deep networks practice me behtar generalize karte dikhte hain)

Cons

  • Vanishing / exploding gradients: backprop me layer-dar-layer factors multiply (Chapter 13)
  • Harder optimization: loss landscape ulta-pulta, shattered gradients (Chapter 25)
  • Sequential compute: layer 2 tab tak shuru nahi jab tak layer 1 khatam nahi → zyada latency, depth ke across parallel nahi

Ilaaj (baad ke chapters): ReLU (9.6), Batch Norm (Ch 20), achha init — Xavier/He (Ch 21), residual connections — ResNet (Ch 25).

Go Wider

Pros

  • Easier to optimize (slide: "smoother landscape")
  • Parallel / GPU friendly: poori layer ek matrix multiply
  • Zyada capacity per layer, fine-grained details (slide: "Width = Memory/Capacity")
  • Kam data / simple problems pe achha (slide)

Cons

  • Params ~ width²: hidden-to-hidden layer ka D × D; width double → ~4× params
  • Memory zyada
  • Overfitting risk (slide)
  • Compositional functions ke liye inefficient — exponentially units chahiye ho sakte hain

Slide summary: "Go Deeper: Better parameter efficiency. Captures hierarchical structure. Harder to train (Gradient issues). Go Wider: Easier to optimize (smoother landscape). Easier to parallelize (GPU friendly). Risk of overfitting." Slide 35: "Width = Memory/Capacity", slide 36: "Depth = Abstraction".

10.10 Comparison table + parameter counting

AspectDeep (zyada layers)Wide (zyada units/layer)
Regions (1-D ReLU)up to (D + 1)K — exponential in KD + 1 — linear in D
Parameter efficiencyHighLow (params ~ D2)
FeaturesHierarchical, reusable (abstraction)Flat, ek hi level (memory)
OptimizationMushkil (vanishing/exploding)Aasaan (smoother)
Parallelism / latencySequential, zyada latencyParallel, GPU friendly
OverfittingGeneralization aksar betterRisk zyada
Best forImages, speech, text — compositional dataSimple / small tabular problems
Fixes neededReLU, BN, He init, ResNet skipsRegularization (L2, dropout)

Chalo numbers se samjhein — MNIST jaisa input (784 pixels → 10 classes)

Yaad rakho: nn.Linear(in, out) ke params = in × out (weights) + out (biases).

  1. Wide-shallow 784 → 512 → 10: 784 × 512 + 512 = 401,920; 512 × 10 + 10 = 5,130; total 407,050
  2. Deep-narrow 784 → 256 → 256 → 256 → 256 → 10: pehli 784 × 256 + 256 = 200,960
  3. Teen hidden-to-hidden: 3 × (256 × 256 + 256) = 3 × 65,792 = 197,376
  4. Output: 256 × 10 + 10 = 2,570; total 200,960 + 197,376 + 2,570 = 400,906
  5. Width² effect: Linear(256, 256) = 65,792 vs Linear(512, 512) = 512 × 512 + 512 = 262,656 → ≈ 3.99 guna

Jawab: dono ~4 lakh params ke (407,050 vs 400,906), lekin deep wala 4 levels ke features bana sakta hai. Width double karne se hidden-to-hidden params ~4 guna.

Toy 1-D version: 1 → 64 → 1 = 193 params, ≤ 65 regions; 1 → 8 → 8 → 8 → 1 = 169 params (3·8 + 1 + 2·8·9), ≤ 93 = 729 regions. Kam params, ~11 guna zyada regions.

PyTorch · dono networks banao, params gino

import torch
import torch.nn as nn

def count_params(model):
    return sum(p.numel() for p in model.parameters())   # numel = tensor me kitne numbers

wide_shallow = nn.Sequential(                 # 1 hidden layer, 512 units
    nn.Linear(784, 512), nn.ReLU(),
    nn.Linear(512, 10))

deep_narrow = nn.Sequential(                  # 4 hidden layers, 256 units each
    nn.Linear(784, 256), nn.ReLU(),
    nn.Linear(256, 256), nn.ReLU(),
    nn.Linear(256, 256), nn.ReLU(),
    nn.Linear(256, 256), nn.ReLU(),
    nn.Linear(256, 10))

print("wide-shallow params:", count_params(wide_shallow))
print("deep-narrow  params:", count_params(deep_narrow))

x = torch.randn(32, 784)                      # 32 images, 28*28 = 784 pixels each
print(wide_shallow(x).shape, deep_narrow(x).shape)   # dono (32, 10)

# Width double karo -> hidden-to-hidden params ~4 guna
print("Linear(256,256):", count_params(nn.Linear(256, 256)))
print("Linear(512,512):", count_params(nn.Linear(512, 512)))

Output

wide-shallow params: 407050
deep-narrow  params: 400906
torch.Size([32, 10]) torch.Size([32, 10])
Linear(256,256): 65792
Linear(512,512): 262656

Architecture design (slide "Designing the architecture" / "Let's play"): teacher ne playground.tensorflow.org dikhaya tha — wahan hidden layers aur neurons badal ke, activation (ReLU/tanh/sigmoid/linear) badal ke dekho ki spiral data pe decision boundary kaise banti hai. Linear activation ke saath kitni bhi layers lagao, boundary seedhi hi rahegi (9.1); ReLU + depth se spiral bhi kat jaata hai.

Exam Radar

  • Width = neurons per layer (D), depth = number of hidden layers (K). Shallow = 1 hidden layer.
  • Shallow 1-D ReLU: ≤ D + 1 regions, 3D + 1 params. Deep: up to (D + 1)K regions, 3D + 1 + (K − 1)D(D + 1) params. K = 5, D = 10 → 471 params, 161,051 regions.
  • Depth = composition = "folding" → regions exponential in depth; width → linear.
  • Depth efficiency: shallow may need O(2N) where deep needs O(N).
  • Deep: + param efficiency, hierarchy, generalization; − vanishing/exploding gradients, hard optimization, latency. Wide: + easy optimization, parallel; − params ~ width², memory, overfitting.
  • Fixes for deep: ReLU, BatchNorm, Xavier/He init, ResNet skip connections.

Galti mat karna

  • Depth = hidden layers ki ginti; input layer ko count mat karo (koi computation nahi karti).
  • "(D + 1)K" maximum hai, guarantee nahi.
  • "Deep hamesha better" — nahi; bina ReLU/BN/init/skip ke bahut deep plain network train hi nahi hota (ResNet ka motivation).
  • Param count me bias bhoolna: Linear(784, 512) = 401,408 + 512 = 401,920, sirf 401,408 nahi.

Dimaag me aise baitha lo

Width = yaaddasht (memory), Depth = samajh (abstraction). Kaagaz ko k baar fold karo, ek cut lagao, 2k cuts pao.

Chapter 11

Loss Functions — galti ka hisaab-kitaab

Loss vs Cost · Loss landscape · Maximum Likelihood · NLL · MSE · MAE · Huber · Binary & Categorical Cross-Entropy · Hinge · PyTorch loss classes

11.1 Loss function kya hai — teacher ki laal pen

Kahani

Yaad karo school ka maths test. Tumne answer likha, aur Sharma ma'am ne laal pen uthaya. Ma'am sirf "sahi" ya "galat" nahi likhti thi — wo dekhti thi ki kitna galat hai. Asli answer 50 tha aur tumne 49 likha? Bas aadha mark kata. Tumne 500 likh diya? Poore marks gaye, upar se daant bhi padi.

Ab socho ek robot student hai — hamara neural network. Wo bhi har sawaal ka answer guess karta hai. Usko sudharne ke liye ek "laal pen" chahiye jo har guess ko dekh kar ek number de: "itna galat hai tu". Wahi number hai loss, aur jo rule ye number nikaalta hai wo hai loss function. Robot ka poora kaam ek line ka hai: aise weights dhoondho ki ye laal-pen wala number chhota se chhota ho jaaye.

Network ko ek input x milta hai, wo apne weights θ (theta — saare weights aur biases ka ek naam) use karke ek prediction ŷ ("y-hat", matlab "y ka guess") nikaalta hai. Asli answer y hai (isko target ya true label bhi kehte hain). Loss function in dono ko compare karke ek akela number deta hai. Bada number = badi galti. Chhota number = achha guess. Zero = bilkul perfect.

Definition (teacher ki slide se)

A Loss Function L(y, ŷ) is a differentiable scalar that measures the discrepancy between the network's prediction ŷ and the true label y. Training = minimising L by adjusting weights via gradient descent.

Hindi me: loss function ek aisa formula hai jo "prediction aur asli answer me kitna farak hai" ko ek number (scalar) me badal deta hai, aur jiska derivative nikal sakte hain (differentiable), taaki gradient descent us number ko neeche la sake.

Input x Network f(x, θ) Prediction ŷ Loss L(y, ŷ) True label y gradient ∇L se weights θ update (backprop) ek number Training = is loop ko baar baar chalao, taaki L chhota hota jaaye
Slide 3 wala pipeline: x se network ŷ banata hai, loss L(y, ŷ) bataata hai kitna galat, aur ∇L (laal dotted) wapas jaake weights θ sudhaarta hai.

Is picture ko dhyaan se dekho, poora deep learning isi loop me hai: x → network → ŷ → loss → gradient → weights update → phir se. Loss function is loop ka compass hai. Agar compass galat direction bataye (galat loss chuna), to network bahut mehnat karke bhi galat cheez seekhega.

Loss vs Cost — ek bachcha vs poori class

Ma'am ek bachche ki copy check karti hai — wo us bachche ka loss hai. Phir wo poori class ka average nikaalti hai — wo cost hai. Bas itna hi farak hai.

ℓi = L(yi, ŷi)      J(θ) = 1n Σi=1n ℓi
  • ℓi = loss — sirf i-th example (ek bachche) ki galti
  • J(θ) = cost (cost function / objective) — saare n examples ki galti ka average; ye weights θ ka function hai, kyunki weights badlo to saare ŷ badlenge
  • n = examples ki ginti (poora dataset ya ek mini-batch)
  • Σ = "sabko jodo" (sum), 1n = "phir n se divide karo" (average)

Average kyun, sum kyun nahi? Kyunki sum lene par 1000 examples wale batch ka number 100 examples wale batch se apne-aap 10 guna bada ho jaata — galti badhi nahi, bas ginti badhi. Average lene se number batch size se independent ho jaata hai, aur learning rate har batch size par same tarah kaam karta hai.

PyTorch · loss (per example) vs cost (average)

import torch

y_true = torch.tensor([3.0, -0.5, 2.0, 7.0])   # asli answers (4 examples)
y_pred = torch.tensor([2.5,  0.0, 2.0, 8.0])   # model ke guesses

per_example = (y_true - y_pred) ** 2   # har example ka apna LOSS
cost = per_example.mean()              # poore batch ka COST = average
print(per_example)
print(cost)

Output

tensor([0.2500, 0.2500, 0.0000, 1.0000])
tensor(0.3750)

Achhe loss function ki khoobiyan

Accuracy ko hi loss kyun nahi bana dete?

Accuracy (kitne sahi) to sabse seedha number hai, usi ko maximize kar do? Problem: accuracy step function jaisi hai. Weight ko thoda sa hilao — prediction 0.61 se 0.62 ho gayi — par class wahi rahi, to accuracy bilkul nahi badli. Matlab accuracy ka slope (gradient) almost har jagah zero hai. L2 worksheet ka Part 3 yaad karo: threshold (step) function ka derivative 0 tha, isliye wnew = wold raha — kuch seekha hi nahi. Accuracy ko loss banaoge to wahi hoga. Isliye hum smooth loss (jaise cross-entropy) se train karte hain, aur accuracy ko sirf metric (report card) ki tarah dekhte hain.

Exam Radar

  • Loss = ek example ki galti; Cost J(θ) = poore dataset/batch ka average. Goal hamesha minimize.
  • Loss function output = scalar, differentiable, ≥ 0, perfect par 0.
  • Training = loss minimize karna gradient descent se; gradient ∇L backprop se aata hai (Chapter 12–13).
  • Course convention: error = prediction − target = ŷ − y (L3 backprop notes: "Always prediction minus target").
  • Accuracy = metric, loss nahi — kyunki uska gradient almost everywhere zero hai.

Galti mat karna

  • PyTorch me argument order: loss_fn(prediction, target). MSE me order se farak nahi padta (square symmetric hai), par BCE / CrossEntropy me order ulta kiya to galat ya error.
  • reduction='none' wala loss ek vector hai; us par seedha .backward() karoge to RuntimeError: grad can be implicitly created only for scalar outputs. Pehle .mean() ya .sum() karo.

Dimaag me aise baitha lo

Loss = laal pen ka number. Network ka ek hi sapna: laal pen ka number zero ho jaaye.

11.2 Loss landscape — pahad, ghaati aur saddle

Kahani

Socho tum raat ko Himachal ke pahadon me phans gaye ho, ghup andhera, torch bhi nahi. Tumhe neeche ghaati me gaon tak pahunchna hai. Kya karoge? Pair se zameen ka dhalaan (slope) mehsoos karoge aur jidhar dhalaan neeche jaata hai, udhar ek kadam rakhoge. Baar baar. Yahi gradient descent hai. Aur ye pahadi ilaaka? Wo hai loss landscape.

Har possible weights θ ke liye ek loss value hoti hai. Agar weights ko zameen ke coordinates maano aur loss ko uunchaai (altitude), to ek pahadi map ban jaata hai — slide 4 ke shabdon me: "The altitude is the Error. Our goal is to reach the lowest point (Global Minimum)." Is map ko J(θ) bhi likhte hain.

θ J Convex (MSE + linear) ek hi minimum θ J global local Non-convex (neural net) GD local me atak sakta hai θ J Saddle point U = ghaati, ∩ = pahad
Loss landscape (slide 32): bowl jaisa convex surface (ek hi minimum), neural net ka ubad-khabad non-convex surface (local + global minimum), aur saddle point jahan ek disha me ghaati hai aur doosri disha me pahad, phir bhi slope zero.

Yahi wajah hai ki aage chal kar hum plain gradient descent se aage badh kar Momentum aur Adam padhenge — jo plateaus aur saddles se nikalne me madad karte hain (slide 35: "Shows why Adam/momentum needed").

Exam Radar

  • Loss landscape = J(θ) parameters ke against; altitude = error; goal = global minimum.
  • Convex → har local minimum hi global minimum hai (koi jhoota gaddha nahi; strictly convex ho to minimum unique bhi), GD hamesha pahunchta hai (sahi learning rate ke saath). Linear regression + MSE convex hai.
  • Neural network ka loss landscape non-convex → local minima, saddle points, plateaus.
  • Saddle point: gradient = 0 par minimum nahi (ek direction me upar, doosri me neeche).
  • Convexity loss + model dono par depend karti hai: sigmoid + BCE (logistic regression) weights me convex hai, par sigmoid + MSE non-convex — classification me CE pasand karne ki ek aur wajah (11.16 dekho).

Dimaag me aise baitha lo

Andhere pahad me sirf dhalaan mehsoos karke neeche utarna — yahi training. Bowl me raasta pakka, ubad-khabad pahad me atakne ka darr.

11.3 Maximum Likelihood — Bunty ke sikke ki kahani

Kahani

Gully cricket me toss hamesha Bunty ke sikke se hota hai. Aaj sabne gin liya: 10 toss me 7 baar heads, 3 baar tails. Pappu chillaya, "Sikka tedha hai!" Ab sawaal ye hai: is sikke par heads aane ka asli chance p kitna maanein? 0.5 (seedha sikka)? 0.3? 0.9?

Tumhara dimaag turant bolega "0.7, aur kya!" Bilkul sahi. Par ye 0.7 kahaan se aaya? Isi "dimaag ke jugaad" ka proper naam hai Maximum Likelihood Estimation (MLE) — aur yahi poore chapter ki neev hai, kyunki MSE aur cross-entropy dono isi se paida hote hain.

Probability vs Likelihood — ek hi formula, ulta sawaal

Probability

p pata hai (maano 0.5), data abhi aaya nahi. Sawaal: "Is sikke se 7 heads aane ka chance kitna?" — parameter fixed, data variable.

Likelihood

Data aa chuka hai (7 H, 3 T), p pata nahi. Sawaal: "Ye data dekh ke, kaunsa p sabse zyada believable hai?" — data fixed, parameter variable.

i.i.d. — isliye probabilities multiply hoti hain

Har toss independent hai (pichhla toss agle ko affect nahi karta) aur sab toss identically distributed hain (sab ek hi sikke se, same p). Is jodi ko kehte hain i.i.d. (independent and identically distributed). Independent events ka "sab saath me hona" = unki probabilities ka guna (product). Ek heads ka chance p, ek tails ka (1 − p). To hamare 10 tosses ka chance:

L(p) = ∏i=110 P(tossi | p) = p · p · (1 − p) · … = p7 (1 − p)3
  • L(p) = likelihood: "agar heads ka chance p hota, to bilkul yahi data dekhne ka chance kitna"
  • ∏ = "sabko multiply karo" (Σ ka guna wala bhai)
  • p7 = 7 heads, har ek ka chance p; (1 − p)3 = 3 tails, har ek ka chance 1 − p
  • (Agar order ki parwaah na ho to aage ek constant 10C7 = 120 lagta hai. Constant se peak ki jagah nahi badalti, isliye hata dete hain.)

Chalo numbers se samjhein

Sawaal: p = 0.3, 0.5, 0.7, 0.9 ke liye L(p) = p7(1 − p)3 nikaalo. Kaunsa p jeetega?

  1. p = 0.3: 0.37 = 0.0002187, 0.73 = 0.343 → L = 0.0002187 × 0.343 = 0.0000750
  2. p = 0.5: 0.57 = 0.0078125, 0.53 = 0.125 → L = 0.0009766
  3. p = 0.7: 0.77 = 0.0823543, 0.33 = 0.027 → L = 0.0022236
  4. p = 0.9: 0.97 = 0.4782969, 0.13 = 0.001 → L = 0.0004783
  5. Compare: 0.0022236 sabse bada. p = 0.7 wala sikka is data ko seedhe sikke (0.5) se 0.0022236 / 0.0009766 ≈ 2.28 guna zyada achhe se explain karta hai.

Jawab: p = 0.7 likelihood ko maximum karta hai. Yahi MLE hai.

0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 0.5 1 1.5 2 2.5 p (heads ki probability) L(p) × 10⁻³ 0.075 0.977 2.224 0.478 peak: p = 0.7 = 7/10 L(p) = p⁷ (1 − p)³ (7 heads, 3 tails)
Likelihood curve: 10 tosses me 7 heads dekhne ke baad, har possible p ke liye L(p). Sabse ooncha point p = 0.7 par hai — wahi Maximum Likelihood Estimate. (Y-axis ko 1000 se multiply kiya hai taaki numbers padhne layak hon.)

Derivative se pakka saboot

Sirf 4 values try karna proof nahi hai. Peak nikalne ka tareeka: derivative = 0 rakho (Chapter 2 wala "slope zero = top ya bottom").

Derivation step by step

  1. Seedha L ko differentiate karo (product rule): dL/dp = 7p6(1 − p)3 − 3p7(1 − p)2 = p6(1 − p)2 [7(1 − p) − 3p]. Kaam chal gaya par gandagi dekho — 10 cheezon ka guna hota to aur bura.
  2. Smart tareeka: pehle ln lo (agle section me iski wajah aur detail): ln L = 7 ln p + 3 ln(1 − p)
  3. Differentiate (yaad hai d/dp ln p = 1/p, aur chain rule se d/dp ln(1 − p) = −1/(1 − p)): d(ln L)/dp = 7/p − 3/(1 − p)
  4. Zero rakho: 7/p = 3/(1 − p) → 7(1 − p) = 3p → 7 − 7p = 3p → 7 = 10p → p = 0.7
  5. Maximum hi hai, minimum nahi? Second derivative: −7/p2 − 3/(1 − p)2 < 0 hamesha → ulta U (∩) → peak. ✓

General rule: sikke ke liye MLE p̂ = heads ki ginti / total tosses = 7/10. Jo dimaag ne bola, maths ne prove kiya.

Definition

Maximum Likelihood Estimation (MLE): parameters θ ko aise chuno ki jo data humne sach me dekha hai, wo un parameters ke under sabse zyada probable lage: θ̂ = argmaxθ ∏i P(yi | xi, θ). ("argmax" = "wo θ jis par value max ho", value khud nahi.)

Deep learning se connection: sikke me ek parameter tha (p). Network me lakhon parameters hain (weights). Network har training input xi ke liye bolta hai "mere hisaab se answer ki probability ye hai". MLE kehta hai: aise weights dhoondho jinke under training data ke asli labels sabse zyada probable lagen. Bas, yahi training hai.

Exam Radar

  • Likelihood = P(data | parameters), data fixed maan kar parameter ka function.
  • i.i.d. assumption → poore dataset ki likelihood = har example ki probability ka product.
  • Coin, k heads in n tosses → MLE p̂ = k/n. 7/10 → 0.7.
  • MLE = argmax likelihood. Training a network = MLE over its weights (jab loss NLL ho).

Galti mat karna

  • Likelihood ko p ki probability mat samajhna. L(p) ko sab p par jodo to 1 nahi aata — ye "data kitna fit hai" ka score hai.
  • MLE ka jawab "max L ki value" (0.0022) nahi, "max wala p" (0.7) hai.

Dimaag me aise baitha lo

Data ne jo dikhaya, use sabse believable banane wala parameter hi MLE. 7 heads in 10 → 0.7.

11.4 Log-likelihood aur Negative Log-Likelihood (NLL)

Kahani

Ek chhota bachcha ₹1 ke sikke ko baar baar aadha kaat raha hai: ½, ¼, ⅛ … 50 baar ke baad itna chhota tukda bachta hai ki aankh se dikhta hi nahi — "zero" lagta hai. Computer ke saath bhi yahi hota hai. Bahut saari chhoti probabilities ka guna karo to number itna chhota ho jaata hai ki computer use exactly 0 likh deta hai. Isko kehte hain numerical underflow.

Teacher ki slide 6 bilkul yahi kehti hai: "Why not maximize likelihood directly? Because probability values are small!! Multiplying such small numbers repeatedly leads to floating point underflow issue." Training set me hazaaron examples hote hain, har ek ki probability 1 se kam. Unka product turant underflow ho jaata hai. Phir har weights ke liye likelihood = 0, kisi ko compare nahi kar sakte, gradient bhi 0. Training khatam.

Chalo numbers se samjhein

Sawaal: 50 examples, har ek ko model ne sahi label ki probability 0.1 di. Likelihood kitni?

  1. Asli jawab: 0.150 = 10−50
  2. PyTorch ka default dtype float32 hai, jo lagbhag 1.4 × 10−45 se chhota positive number store hi nahi kar sakta. To 10−50 → 0.0. (float64 me bhi 1000 aise examples → 10−1000 → 0.0.)
  3. Log lo: ln(0.150) = 50 × ln 0.1 = 50 × (−2.3026) = −115.1293 — ekdum normal number, koi problem nahi.

Product underflow karta hai (0.0), log ka sum safe rehta hai (−115.1293).

PyTorch · underflow ka live demo

import torch

probs = torch.full((50,), 0.1)       # 50 examples, har ek ki probability 0.1
print(probs.dtype)                   # PyTorch ka default float32
print(torch.prod(probs))             # sab multiply -> 1e-50 chahiye tha
print(torch.log(probs).sum())        # log ka sum -> bilkul safe number

Output

torch.float32
tensor(0.)
tensor(-115.1293)

Log hi kyun? Do jaadu

Minus kyun lagaya? (Negative)

Hamara saara optimization toolkit (gradient descent) neeche utarna jaanta hai, upar chadhna nahi. Kisi cheez ko maximize karna = uske negative ko minimize karna. Isliye −1 se multiply kar do. Bonus: probability ≤ 1 hai to ln(prob) ≤ 0, aur −ln(prob) ≥ 0 — loss ki "hamesha ≥ 0" wali khoobi apne aap mil gayi.

NLL(θ) = − Σi=1n ln P(yi | xi, θ)
  • P(yi | xi, θ) = weights θ wala model, input xi dekh kar, asli answer yi ko kitni probability deta hai
  • ln = natural log (base e); PyTorch ka torch.log bhi natural log hai
  • Σ = saare examples ka jod (product ki jagah); aage − = maximize ko minimize me badla
  • Practice me 1n se bhi divide karte hain (average NLL) — minimizer same rehta hai

maximize L(θ)  ⟺  maximize ln L(θ)  ⟺  minimize −ln L(θ) = NLL

Coin wapas

NLL(p) = −[7 ln p + 3 ln(1 − p)]:

  1. p = 0.3: −[7(−1.2040) + 3(−0.3567)] = 9.4978
  2. p = 0.5: −[10 × (−0.6931)] = 6.9315
  3. p = 0.7: −[7(−0.3567) + 3(−1.2040)] = 6.1086 ← sabse kam
  4. p = 0.9: −[7(−0.1054) + 3(−2.3026)] = 7.6453

Likelihood ka maximum (0.7) = NLL ka minimum (0.7). Same jawab, safe numbers.

PyTorch · coin ki likelihood, log-likelihood, NLL

import torch

p_values = torch.tensor([0.3, 0.5, 0.7, 0.9])   # 4 candidate p
heads, tails = 7, 3                             # data: 7 H, 3 T

likelihood = p_values**heads * (1 - p_values)**tails
log_lik = heads * torch.log(p_values) + tails * torch.log(1 - p_values)
nll = -log_lik                                  # minus laga diya

for p, L, ll, n in zip(p_values, likelihood, log_lik, nll):
    print(f"p={p:.1f}  L={L:.7f}  logL={ll:.4f}  NLL={n:.4f}")

best = p_values[torch.argmin(nll)]              # sabse kam NLL wala p
print("best p:", round(best.item(), 2))

Output

p=0.3  L=0.0000750  logL=-9.4978  NLL=9.4978
p=0.5  L=0.0009766  logL=-6.9315  NLL=6.9315
p=0.7  L=0.0022236  logL=-6.1086  NLL=6.1086
p=0.9  L=0.0004783  logL=-7.6453  NLL=7.6453
best p: 0.7

Exam Radar

  • Likelihood seedha maximize nahi karte kyunki chhoti probabilities ka product → floating point underflow (slide 6).
  • Log: product → sum, aur monotonic hone se argmax same.
  • NLL = −Σ log P(yi | xi, θ). Maximize likelihood ⟺ minimize NLL.
  • NLL ≥ 0 hota hai (discrete probabilities ke liye), perfect (prob = 1) par 0.

Dimaag me aise baitha lo

Guna underflow karta hai → log lo (guna jod ban jaata hai) → minus lagao (upar chadhna neeche utarna ban jaata hai). Likelihood → Log-likelihood → NLL.

11.5 Universal recipe — har loss isi factory se banta hai

Kahani

Maa ke paas har sabzi ki alag recipe nahi hai — ek master tadka hai: tel garam, jeera, masala, phir jo sabzi ho daal do. Aloo daalo to aloo ki sabzi, bhindi daalo to bhindi. Loss functions ke saath bhi aisa hi hai. Ek master recipe hai (MLE + NLL). Usme "Gaussian" daalo to MSE nikalta hai, "Bernoulli" daalo to Binary Cross-Entropy, "Categorical" daalo to Categorical Cross-Entropy. Loss functions ratne ki zaroorat nahi — banana aana chahiye.

The recipe (slide 8 "Universal Recipe for Loss Functions", Prince UDL ch. 5)

  1. Distribution chuno: output y kis type ka hai, uske hisaab se y par ek probability distribution P(y | parameters) chuno. Real number → Normal (Gaussian). 0/1 → Bernoulli. K me se ek class → Categorical.
  2. Network se parameters predict karao: network ka output = us distribution ka parameter. Gaussian ka mean μ = ŷ; Bernoulli ka p = ŷ; Categorical ki probability vector = ŷ.
  3. Loss = NLL: training data par negative log-likelihood −Σ ln P(yi | f(xi, θ)) ko minimize karo.
  4. Prediction (inference): naye x ke liye ya to poori distribution do, ya uska sabse probable value (mean, ya sabse badi probability wali class).
Tasky kaisa haiDistributionNetwork kya predict karta haiOutput activation (kyun)NLL banta hai
Regressionkoi bhi real numberNormal / Gaussian 𝒩(μ, σ²)mean μ = ŷkoi nahi / linear (μ koi bhi real ho sakta hai)MSE
Robust regressionreal number, outliers walaLaplacecentre μ = ŷkoi nahi / linearMAE
Binary classification0 ya 1Bernoulli(p)p = P(y = 1) = ŷsigmoid (p ko (0, 1) me rakhna hai)Binary Cross-Entropy
Multi-classK classes me se ekCategorical(p1, …, pK)probability vector ŷsoftmax (sab ≥ 0 aur jod = 1)Categorical Cross-Entropy

Ye table chapter ka "naksha" hai. Dhyaan do output activation kahaan se aayi: distribution ke parameter par jo shart (constraint) hai, activation wahi shart poori karti hai. Probability 0 aur 1 ke beech honi chahiye → sigmoid. K probabilities positive aur jod 1 → softmax. Mean pe koi shart nahi → koi activation nahi. Isi liye "regression me last layer pe sigmoid mat lagao" aur "multi-class me softmax" — ye rule ratne ki cheez nahi, recipe ka nateeja hai.

Exam Radar

  • Recipe: (1) y par distribution chuno (2) network uske parameters predict kare (3) NLL minimize (4) inference me max/mean lo.
  • Gaussian → MSE · Bernoulli → BCE · Categorical → CCE · Laplace → MAE.
  • Output activation = distribution parameter ki constraint: sigmoid (0–1), softmax (simplex), linear (koi bhi real).

Dimaag me aise baitha lo

"Distribution chuno, network se uska parameter bolwao, NLL ghatao." Teen line, chaar loss functions.

11.6 Recipe chalao #1: Gaussian noise ⇒ MSE

Kahani

Ramu chai wala har customer ke liye cheeni ka andaaza lagata hai. Uska andaaza kabhi thoda zyada, kabhi thoda kam hota hai — par zyadatar sahi ke aas-paas. Bahut badi galti kabhi kabhaar hi. Agar tum Ramu ki galtiyon ka histogram banao to ek ghanti (bell) jaisa shape banega: beech me bahut saari chhoti galtiyan, kinaron par kam. Ye bell shape hai Gaussian (Normal) distribution. Regression me hum yahi maante hain: asli answer = network ka answer + thoda Gaussian noise.

p(y | μ, σ2) = 1√2πσ2 · exp(− (y − μ)22σ2)
  • y = asli answer (jo data me hai)
  • μ (mu) = bell ka centre = mean. Recipe step 2: network yahi predict karta hai, μ = ŷ
  • σ2 (sigma square) = variance = bell kitni phaili hai (noise kitna hai). Abhi isko fixed maan lo (jaise σ = 1)
  • exp(u) = eu. (y − μ)² jitna bada, exp utna chhota → centre se door wale y kam probable
  • 1√2πσ2 = sirf ek normalising constant, taaki bell ke neeche ka area = 1 ho
−2 −1 0 1 2 3 4 5 6 0.1 0.2 0.3 0.4 y p(y | x) asli y = 3 0.3521 (ŷ = 2.5) 0.0175 (ŷ = 0.5) bell ka centre = ŷ
Network har x ke liye ek bell curve ka centre μ = ŷ bolta hai (σ = 1). Asli y = 3 par bell ki height = likelihood. ŷ = 2.5 (paas) to height 0.3521; ŷ = 0.5 (door) to sirf 0.0175. Height badhaane ka matlab hai (y − ŷ)² ghataana — yahi MSE hai.

Derivation step by step

Gaussian NLL → MSE

  1. Ek example ki NLL likho, μ = ŷi rakh ke: −ln p(yi) = −ln[ 1/√(2πσ²) ] − ln[ exp(−(yi − ŷi)²/(2σ²)) ] (log of product = sum of logs)
  2. Do log rules lagao: ln(1/a) = −ln a aur ln(eu) = u: −ln p(yi) = ½ ln(2πσ²) + (yi − ŷi)² / (2σ²)
  3. Saare n examples jodo: NLL = (n/2)·ln(2πσ²) + (1/(2σ²)) · Σ (yi − ŷi)²
  4. Weights θ sirf ŷi ko badalte hain. Pehla term (n/2)ln(2πσ²) me koi ŷ nahi → constant → minimum ki jagah par koi asar nahi → hatao.
  5. 1/(2σ²) ek positive constant multiplier hai → ye bhi minimum ki jagah nahi badalta (kisi function ko 5 se multiply karo, uska lowest point wahi rehta hai) → hatao.
  6. Bacha: minimize Σ (yi − ŷi)². n se divide karo (average) → MSE. ∎

Gaussian noise maan kar MLE karna = MSE minimize karna. MSE "random" formula nahi hai — ye Gaussian ka NLL hai.

Chalo numbers se samjhein (picture wale numbers)

σ = 1, asli y = 3. Do predictions: ŷ = 2.5 aur ŷ = 0.5.

  1. Constant: ½ ln(2π) = ½ × 1.8379 = 0.9189
  2. ŷ = 2.5: −ln p = 0.9189 + (3 − 2.5)²/2 = 0.9189 + 0.125 = 1.0439 (density = e−1.0439 = 0.3521)
  3. ŷ = 0.5: −ln p = 0.9189 + (3 − 0.5)²/2 = 0.9189 + 3.125 = 4.0439 (density = 0.0175)

Door wali prediction ka NLL bada, aur farak sirf (y − ŷ)²/2 wale hisse se aaya — squared error.

Ek aur khoobsurat connection: L2 worksheet aur L3 backprop notes me teacher ne L = ½(ŷ − y)² use kiya tha. Upar ke step 3 me σ = 1 rakho aur constant hata do — bilkul yahi ½ Σ(y − ŷ)² bachta hai. Wo ½ "jugaad" nahi, Gaussian se aaya hai (aur bonus: derivative me 2 ke saath cut ho jaata hai).

PyTorch · Gaussian NLL = constant + ½ · MSE (σ = 1)

import torch
import torch.nn as nn
import math

y     = torch.tensor([3.0, -0.5, 2.0, 7.0])
y_hat = torch.tensor([2.5,  0.0, 2.0, 8.0])   # network ka output = bell ka centre (mu)
sigma = 1.0

# Gaussian ka -ln p, har example ke liye, phir average
nll = 0.5 * math.log(2 * math.pi * sigma**2) + (y - y_hat) ** 2 / (2 * sigma**2)
mse = nn.MSELoss()(y_hat, y)

print(nll.mean())                  # average Gaussian NLL
print(0.9189 + 0.5 * mse)          # constant + (1/2) * MSE  -> same!
print(mse)

Output

tensor(1.1064)
tensor(1.1064)
tensor(0.3750)

Exam Radar

  • MSE = Gaussian (Normal) noise assumption ke under MLE. "MSE assumes Gaussian errors."
  • Network Gaussian ka mean predict karta hai; variance fixed maana.
  • Gaussian NLL = ½ ln(2πσ²) + (y − ŷ)²/(2σ²); σ = 1 par ½ ln 2π = 0.9189 constant.

Dimaag me aise baitha lo

Bell curve ke exp ke andar (y − μ)² baitha hai. Log lo, exp gaya, square bacha — MSE.

11.7 Recipe chalao #2: Bernoulli ⇒ Binary Cross-Entropy

Kahani

Lab 4 aur Lab 5 ka breast-cancer data yaad karo. Doctor ka sawaal sirf haan/na hai: tumor malignant hai (1) ya nahi (0)? Jab answer sirf do me se ek ho, to uski distribution hoti hai Bernoulli — bilkul Bunty ka sikka: heads (1) chance p, tails (0) chance 1 − p. Bas ab har patient ka apna alag "sikka" hai, aur network har patient ke liye p batata hai.

P(y | p) = py (1 − p)1 − y,    y ∈ {0, 1}
  • p = P(y = 1) = network ka output ŷ = σ(z) (sigmoid, taaki 0 aur 1 ke beech rahe)
  • Power wala jugaad ek if-else switch hai: y = 1 → p1(1 − p)0 = p ✓; y = 0 → p0(1 − p)1 = 1 − p ✓ (kyunki kuch bhi0 = 1)

Derivation step by step

  1. p = ŷ rakho: P(y | ŷ) = ŷy (1 − ŷ)1−y
  2. ln lo (ln(ab) = b ln a): ln P = y ln ŷ + (1 − y) ln(1 − ŷ)
  3. Minus lagao: −ln P = −[ y ln ŷ + (1 − y) ln(1 − ŷ) ]
  4. Saare examples par average: BCE = −(1/n) Σ [ yi ln ŷi + (1 − yi) ln(1 − ŷi) ] ∎

Bernoulli ka NLL = Binary Cross-Entropy. Wahi formula jo Lab 5 ke "Math Behind This Lab" me likha tha.

Sikke se check: Bunty ke 10 tosses ko 10 "examples" maano, jahan network har baar same ŷ = p bolta hai. 7 heads (y = 1) se 7 ln p, 3 tails (y = 0) se 3 ln(1 − p). Total NLL = −[7 ln p + 3 ln(1 − p)] — 11.4 wala exact formula! Matlab coin wala MLE asal me BCE hi tha (sum form me). Isko detail me aur numbers ke saath 11.14 me phir milenge.

Dimaag me aise baitha lo

Sikka (Bernoulli) + log + minus = Binary Cross-Entropy. y wala power sirf switch hai jo sahi term on karta hai.

11.8 Recipe chalao #3: Categorical ⇒ Cross-Entropy, aur Laplace ⇒ MAE

Categorical — sikka nahi, ludo ka dice

Kahani

Sikke me 2 outcomes the. Ludo ke dice me 6. Digits dataset (Lab 4 Part 3) me 10 (0 se 9). Jab answer K options me se ek ho, uski distribution hai Categorical: har class k ka ek chance pk, sab ≥ 0 aur sabka jod = 1. Network softmax laga ke yahi K probabilities deta hai.

Label ko one-hot vector me likhte hain: 3 classes (cat, dog, bird) me "dog" = [0, 1, 0] — sirf sahi class ki jagah 1, baaki 0.

P(y | ŷ) = ∏k=1K ŷkyk  ⇒  −ln P = −Σk=1K yk ln ŷk = −ln ŷtrue class
  • yk = one-hot label ka k-th number (sahi class par 1, baaki 0)
  • ŷk = softmax se nikli class k ki probability
  • Same switch jugaad: jis class ka yk = 0, uska term ŷk0 = 1 (log me 0) — gayab. Sirf sahi class bachti hai.

Chalo numbers se samjhein

Sahi class = dog, y = [0, 1, 0]. Model ka softmax output ŷ = [0.2, 0.7, 0.1].

  1. P = 0.20 × 0.71 × 0.10 = 1 × 0.7 × 1 = 0.7
  2. −Σ yk ln ŷk = −(0 × ln 0.2 + 1 × ln 0.7 + 0 × ln 0.1) = −ln 0.7 = 0.3567

Categorical NLL = Categorical Cross-Entropy = −ln(sahi class ki probability) = 0.3567.

Binary cross-entropy asal me K = 2 wala special case hai: do classes ki probabilities ŷ aur 1 − ŷ. Isliye ek binary problem ko 2 logits + softmax + CrossEntropy se bhi solve kar sakte ho, ya 1 logit + sigmoid + BCE se — dono mathematically same family hain.

Laplace — "kabhi kabhi badi galti normal hai" ⇒ MAE (short)

p(y | μ, b) = 12b exp(− |y − μ|b)  ⇒  −ln p = ln(2b) + |y − μ|b
  • μ = centre = network ka ŷ; b = spread (fixed maana)
  • Gaussian me exp ke andar (·)² tha, yahan |·| (absolute value) hai
  • Constants ln(2b) aur 1/b hatao → minimize Σ|yi − ŷi| → MAE

Laplace ki "poonch" (tails) Gaussian se moti hoti hai — matlab ye distribution maanti hai ki kabhi-kabhaar badi galti hona normal hai. Isliye MAE outlier dekh kar ghabraati nahi. Yahi "MAE robust hai" ka gehra kaaran hai (11.10 me numbers ke saath).

Exam Radar

  • Categorical NLL = −Σk yk ln ŷk = −ln ŷcorrect (one-hot labels ke saath).
  • BCE = CCE ka 2-class special case.
  • Laplace noise → MAE (L1). Gaussian noise → MSE (L2).
  • Saare chaar loss = NLL of some distribution. "Cross-entropy loss" aur "negative log-likelihood" classification me same cheez hain.

Dimaag me aise baitha lo

Bell (Gaussian) → square → MSE. Nukeela tent (Laplace) → absolute → MAE. Sikka → BCE. Dice → CE.

11.9 MSE — Mean Squared Error, regression ka workhorse

Kahani

School ke PT sir ka rule: line se 1 kadam bahar khade ho to 1 uthak-baithak. 2 kadam bahar to 4. 10 kadam bahar to 100! Chhoti galti par halki saza, badi galti par bahut bhaari saza — saza galti ka square hai. Yahi PT sir hain MSE. Regression me (jab answer ek number ho — ghar ka daam, kal ka temperature, cheeni ke chammach) sabse pehla loss yahi use hota hai.

MSE = 1n Σi=1n (yi − ŷi)2
  • yi = i-th example ka asli answer; ŷi = model ka guess
  • yi − ŷi = residual (r) — kitna chooka, sign ke saath
  • (·)2 = square: (1) +2 aur −2 wali galtiyan ek doosre ko cancel na karein, (2) badi galti zyada punish ho, (3) har jagah smooth (derivative milta hai), (4) Gaussian NLL se yahi nikla tha (11.6)
  • 1n Σ = average over n examples
  • Slide 14: "Also written L = (1/2n) Σ(y − ŷ)²" — ½ sirf isliye ki derivative ka 2 cancel ho jaaye. Minimum ki jagah same.

Chalo numbers se samjhein (example 1)

Sawaal: y = [3, −0.5, 2, 7], ŷ = [2.5, 0.0, 2, 8]. MSE aur RMSE nikaalo.

  1. Residuals y − ŷ: 3 − 2.5 = 0.5; −0.5 − 0 = −0.5; 2 − 2 = 0; 7 − 8 = −1
  2. Square: 0.25, 0.25, 0, 1 (minus gayab)
  3. Jod: 0.25 + 0.25 + 0 + 1 = 1.5
  4. Average: 1.5 / 4 = 0.375
  5. RMSE: √0.375 = 0.6124

Jawab: MSE = 0.375, RMSE ≈ 0.6124. (Akeli −1 wali galti ne 1.5 me se 1.0 diya — badi galti ka dabdaba.)

Teacher ka example (slide 16–17)

iTrue yPredicted ŷResidual (y − ŷ)(y − ŷ)²total ka hissa
13.02.80.20.040.45%
25.04.50.50.252.83%
32.54.0−1.52.2525.48%
48.08.2−0.20.040.45%
56.03.52.56.2570.78%
Sum →8.83100%

MSE = 8.83 / 5 = 1.766 ≈ 1.77. Akela point 5 (residual 2.5) total ka ~71% hai; point 1 aur 4 (residual 0.2) ko MSE lagbhag ignore kar deta hai. Slide ka takeaway: "MSE amplifies large residuals non-linearly."

Derivative — gradient galti ke barabar badhta hai

∂ MSE∂ ŷi = 2n (ŷi − yi) = − 2n (yi − ŷi)
  • Kaise: (y − ŷ)² ka derivative = 2(y − ŷ) × (andar ka derivative w.r.t. ŷ = −1) = −2(y − ŷ) = 2(ŷ − y). Phir 1/n saath me.
  • Gradient residual ke proportional hai: badi galti → bada dhakka (fast recovery), answer ke paas → chhota dhakka (aaram se land). ½ wale version me: (1/n)(ŷ − y).
  • Linear model ŷ = wx + b ke liye chain rule se: ∂MSE/∂w = (2/n) Σ (ŷi − yi) xi, ∂MSE/∂b = (2/n) Σ (ŷi − yi). (Detail Chapter 12–13 me.)

Example 1 par: (2/4) × [2.5 − 3, 0 − (−0.5), 2 − 2, 8 − 7] = 0.5 × [−0.5, 0.5, 0, 1] = [−0.25, 0.25, 0, 0.5]. Negative gradient matlab "ŷ badhao" (2.5 ko 3 ki taraf), positive matlab "ghatao" (8 ko 7 ki taraf).

PyTorch · MSE from scratch vs nn.MSELoss (teeno reductions)

import torch
import torch.nn as nn

y     = torch.tensor([3.0, -0.5, 2.0, 7.0])   # target (asli)
y_hat = torch.tensor([2.5,  0.0, 2.0, 8.0])   # prediction

# 1) from scratch
def mse_manual(y_true, y_pred):
    return torch.mean((y_true - y_pred) ** 2)

# 2) built-in: loss_fn(PREDICTION, TARGET) -- order yaad rakho
mse_mean = nn.MSELoss()                    # default reduction='mean'
mse_sum  = nn.MSELoss(reduction='sum')     # sab errors ka total
mse_none = nn.MSELoss(reduction='none')    # har element ka alag loss

print(mse_manual(y, y_hat))
print(mse_mean(y_hat, y))
print(mse_sum(y_hat, y))
print(mse_none(y_hat, y))
print(torch.sqrt(mse_mean(y_hat, y)))      # RMSE = sqrt(MSE)

Output

tensor(0.3750)
tensor(0.3750)
tensor(1.5000)
tensor([0.2500, 0.2500, 0.0000, 1.0000])
tensor(0.6124)

nn.MSELoss(reduction='mean') — argument samjho: reduction batata hai ki har element ke squared error ka kya karna hai. 'mean' (default) = average (yahi MSE), 'sum' = total (1.5), 'none' = har element ka alag tensor (backward se pehle khud mean/sum karna padega). Input aur target same shape aur float hone chahiye. (Purane arguments size_average, reduce deprecated hain.)

PyTorch · autograd se MSE gradient check (formula vs PyTorch)

import torch
import torch.nn as nn

y     = torch.tensor([3.0, -0.5, 2.0, 7.0])
y_hat = torch.tensor([2.5,  0.0, 2.0, 8.0], requires_grad=True)  # gradient chahiye

loss = nn.MSELoss()(y_hat, y)
loss.backward()                  # autograd: dLoss/dy_hat nikaal do (Ch 12-13)
print(loss)
print(y_hat.grad)                # formula se: (2/n)(y_hat - y)
print((2 / 4) * (y_hat.detach() - y))

Output

tensor(0.3750, grad_fn=<MseLossBackward0>)
tensor([-0.2500,  0.2500,  0.0000,  0.5000])
tensor([-0.2500,  0.2500,  0.0000,  0.5000])

Outlier sensitivity — ek galti, poora hisaab kharaab

Outlier demo

Example 1 me aakhri target galti se 7 ki jagah 27 likha gaya (data entry mistake). Prediction wahi [2.5, 0, 2, 8].

  1. Residuals: 0.5, −0.5, 0, 27 − 8 = 19
  2. Squares: 0.25, 0.25, 0, 361 → jod 361.5
  3. MSE: 361.5 / 4 = 90.375 (pehle 0.375 tha → 241 guna!)
  4. Outlier ka hissa: 361 / 361.5 = 99.86% — baaki teen points ki awaaz hi band.

Ek outlier ne MSE ko 0.375 se 90.375 kar diya. Training ab sirf us ek point ko khush karne me lag jaayegi.

1 2 3 4 5 6 7 8 5 10 15 20 x y outlier (8, 20) MSE line: ŷ = 2.00x − 2.95 (outlier ne kheench li) MAE line: ŷ = 1.0x + 0.1 (baaki 7 points ke saath)
Outlier ka asar (numbers sach me fit kiye gaye hain): 7 points y ≈ x line par hain, ek point (8, 20) outlier hai. MSE se fit karo to slope 1.0 se 2.0 ho jaata hai — line outlier ki taraf kheench gayi. MAE se fit karo to line y = 1.0x + 0.1 hi rehti hai — outlier ko zyada bhaav nahi diya.

Convex, smooth, units² aur RMSE

Constant guess ka best answer = mean (average)

Socho model bilkul buddhu hai — har input ke liye ek hi number c bolta hai. MSE ke hisaab se best c kya?

Derivation

  1. MSE(c) = (1/n) Σ (yi − c)²
  2. Derivative zero: dMSE/dc = −(2/n) Σ (yi − c) = 0 → Σ yi = n·c → c = (1/n) Σ yi = mean

MSE ka best constant = mean. Isi ko general bolte hain: MSE se trained model har x par y ka conditional mean predict karta hai (slide 33: "Optimal prediction: conditional mean").

Paanch doston ki pocket money (×100 ₹): 2, 3, 4, 5, 100. Mean = 114/5 = 22.8. MSE(22.8) = 1490.96 jabki MSE(4) = 1844.40 — MSE ko mean hi pasand hai, chahe wo ek Ambani-dost ki wajah se baaki chaaron se door ho. (Code 11.10 me.)

Exam Radar

  • MSE = (1/n)Σ(y − ŷ)²; ∂MSE/∂ŷ = (2/n)(ŷ − y) — linear in residual.
  • Error 10 ko error 1 se 100 guna zyada penalty (quadratic penalty).
  • Outlier sensitive (high). Smooth, differentiable everywhere. Linear regression ke saath convex.
  • Units = y ki unit ka square → RMSE = √MSE interpretable.
  • Optimal constant / conditional prediction = mean. Assumption: Gaussian noise. Output layer: linear (no activation).
  • PyTorch: nn.MSELoss(), default reduction='mean'; options 'sum', 'none'. a.k.a. L2 loss.
  • Example yaad rakho: y = [3, −0.5, 2, 7], ŷ = [2.5, 0, 2, 8] → MSE 0.375, MAE 0.5.

Galti mat karna

  • Regression me last layer par sigmoid/ReLU mat lagao jab target negative ya 1 se bada ho sakta hai — output fas jaayega. Linear output + MSE.
  • MSE ka unit squared hai — "MSE 4 hai to average galti 4 hai" galat. Average galti ka andaaza RMSE = 2 deta hai.
  • reduction='sum' ke saath batch size badhaoge to loss aur gradient dono bade ho jaayenge — learning rate dobara tune karna padega.

Dimaag me aise baitha lo

MSE = PT sir: galti ka square saza. Chhoti galti maaf, badi galti par qayamat. Isliye outlier se darr, aur best guess = mean.

11.10 MAE — Mean Absolute Error, outlier se na darne wala

Kahani

Auto-rickshaw ka meter: har km ka same rate. 1 km door = ₹10, 10 km door = ₹100 — seedha, imaandar hisaab. 10 km ki galti 1 km ki galti se bas 10 guna buri hai, 100 guna nahi. Ye hai MAE: galti ka sirf size (minus hata ke), koi square nahi. Isko L1 loss bhi kehte hain.

MAE = 1n Σi=1n |yi − ŷi|
  • |·| = absolute value = minus sign hata do (|−3| = 3). Ye bhi cancel hone se rokta hai, par galti ko bada nahi karta
  • Penalty linear: error 10 = error 1 ka exactly 10 guna (slide 20)
  • Unit same as y: y rupees me to MAE rupees me — "average itne ₹ ki galti" seedha padh sakte ho

Chalo numbers se samjhein

Same example: y = [3, −0.5, 2, 7], ŷ = [2.5, 0.0, 2, 8].

  1. |residuals|: |0.5| = 0.5, |−0.5| = 0.5, |0| = 0, |−1| = 1
  2. Jod: 0.5 + 0.5 + 0 + 1 = 2; average: 2 / 4 = 0.5
  3. Outlier wala (7 → 27): 0.5 + 0.5 + 0 + 19 = 20 → 20 / 4 = 5.0
  4. Teacher ka 5-row example: (0.2 + 0.5 + 1.5 + 0.2 + 2.5) / 5 = 4.9 / 5 = 0.98

MAE = 0.5. Outlier aane par MAE 0.5 → 5.0 (10 guna) jabki MSE 0.375 → 90.375 (241 guna). MAE ne outlier ko "ek aur galti" maana, "qayamat" nahi.

PyTorch · outlier par MSE vs MAE (nn.L1Loss = MAE)

import torch
import torch.nn as nn

y_hat     = torch.tensor([2.5, 0.0, 2.0, 8.0])
y_clean   = torch.tensor([3.0, -0.5, 2.0, 7.0])
y_outlier = torch.tensor([3.0, -0.5, 2.0, 27.0])   # aakhri target galti se 27

mse = nn.MSELoss()
mae = nn.L1Loss()          # L1Loss = MAE (mean absolute error)

print("MSE clean  :", mse(y_hat, y_clean).item())
print("MSE outlier:", mse(y_hat, y_outlier).item())
print("MAE clean  :", mae(y_hat, y_clean).item())
print("MAE outlier:", mae(y_hat, y_outlier).item())

Output

MSE clean  : 0.375
MSE outlier: 90.375
MAE clean  : 0.5
MAE outlier: 5.0

Gradient — hamesha ±1, size se koi matlab nahi

∂ |y − ŷ|∂ ŷ = sign(ŷ − y) = +1 agar ŷ > y,   −1 agar ŷ < y,   undefined agar ŷ = y
  • MAE (average) ke liye har element ka gradient sign(ŷi − yi)/n — magnitude constant
  • r = 0 par V ki nok (kink) hai → derivative define nahi. Optimizers wahan subgradient use karte hain (koi bhi value −1 se +1 ke beech chalti hai; PyTorch 0 leta hai, neeche output dekho)

PyTorch · MAE from scratch (Lab 4 solution) + gradient

import torch
import torch.nn as nn

y     = torch.tensor([3.0, -0.5, 2.0, 7.0])
y_hat = torch.tensor([2.5,  0.0, 2.0, 8.0], requires_grad=True)

def mae_manual(y_true, y_pred):
    return torch.mean(torch.abs(y_true - y_pred))   # Lab 4 wala solution

print(mae_manual(y, y_hat))
loss = nn.L1Loss()(y_hat, y)
print(loss)
loss.backward()
print(y_hat.grad)     # sirf sign(y_hat - y) / n  -> magnitude hamesha 1/n

Output

tensor(0.5000, grad_fn=<MeanBackward0>)
tensor(0.5000, grad_fn=<MeanBackward0>)
tensor([-0.2500,  0.2500,  0.0000,  0.2500])

Gradient [−0.25, 0.25, 0, 0.25]: 0.5 ki galti aur 1.0 ki galti dono ko same 0.25 ka dhakka mila. Aur jahan galti exactly 0 thi wahan PyTorch ne 0 diya (subgradient).

Minimum ke paas jhoolna (jumpy)

Constant gradient ka side-effect: answer ke bilkul paas pahunch kar bhi dhakka utna hi bada rehta hai. Fixed learning rate ke saath model minimum ke aar-paar jhoolta rehta hai. MSE ka gradient paas aate-aate chhota hota jaata hai, to wo aaram se land karta hai. Demo: target 4, start 0, lr 0.3:

Python · MAE vs MSE: minimum ke paas kya hota hai

import torch

target = 4.0          # asli answer
lr = 0.3              # fixed learning rate
c_mae, c_mse = 0.0, 0.0

for step in range(1, 17):
    g_mae = torch.sign(torch.tensor(c_mae - target)).item()   # MAE gradient: sirf +1/-1
    g_mse = 2 * (c_mse - target)                               # MSE gradient: 2 x error
    c_mae -= lr * g_mae
    c_mse -= lr * g_mse
    if step in (1, 2, 3, 12, 13, 14, 15, 16):
        print(f"step {step:2d}   MAE-wala c = {c_mae:.4f}   MSE-wala c = {c_mse:.4f}")

Output

step  1   MAE-wala c = 0.3000   MSE-wala c = 2.4000
step  2   MAE-wala c = 0.6000   MSE-wala c = 3.3600
step  3   MAE-wala c = 0.9000   MSE-wala c = 3.7440
step 12   MAE-wala c = 3.6000   MSE-wala c = 3.9999
step 13   MAE-wala c = 3.9000   MSE-wala c = 4.0000
step 14   MAE-wala c = 4.2000   MSE-wala c = 4.0000
step 15   MAE-wala c = 3.9000   MSE-wala c = 4.0000
step 16   MAE-wala c = 4.2000   MSE-wala c = 4.0000

MAE wala 3.9 ↔ 4.2 ke beech hamesha jhoolta rahega (har step exactly 0.3 ka, kyunki gradient sirf ±1). MSE wala har step galti ko 0.4 guna kar deta hai (4 → 1.6 → 0.64 → …) aur 4.0000 par tik jaata hai. Isliye MAE ke saath learning-rate decay (Chapter 15) madad karta hai. Slide: "convergence is slower".

Constant guess ka best answer = median

Derivation

  1. MAE(c) = (1/n) Σ |yi − c|
  2. Derivative: d/dc = (1/n) Σ sign(c − yi) = (1/n)[(c se chhote points ki ginti) − (c se bade points ki ginti)]
  3. Zero tab jab dono taraf barabar points hon → c = median (beech wala). Outlier kitna bhi door ho, wo sirf "ek point upar" gina jaata hai, uski doori ka koi asar nahi.

MAE ka best constant = median → MAE-trained model conditional median predict karta hai.

0 20 40 60 80 100 100 (Ambani ka beta) median = 4 → MAE ka best guess mean = 22.8 → MSE ka best guess (outlier ne kheencha)
Paanch doston ki pocket money (×100 rupaye): 2, 3, 4, 5, 100. Ek constant guess c karna ho to MSE ka best c = mean = 22.8 (ek ameer dost ne poora average kheench liya), MAE ka best c = median = 4 (beech wala, outlier se bekhabar).

PyTorch · mean vs median: kaun kisko pasand hai

import torch

data = torch.tensor([2.0, 3.0, 4.0, 5.0, 100.0])   # pocket money (x100 Rs)

for c in [4.0, 22.8]:          # median aur mean -- dono guess try karo
    mse = torch.mean((data - c) ** 2)
    mae = torch.mean(torch.abs(data - c))
    print(f"c={c:5.1f}  MSE={mse.item():8.2f}  MAE={mae.item():6.2f}")

print("mean  :", data.mean().item())
print("median:", data.median().item())

Output

c=  4.0  MSE= 1844.40  MAE= 20.00
c= 22.8  MSE= 1490.96  MAE= 30.88
mean  : 22.799999237060547
median: 4.0

MSE ko 22.8 (mean) pasand (1490.96 < 1844.40), MAE ko 4 (median) pasand (20.00 < 30.88). "mean" 22.799999… isliye dikha kyunki float32 me 22.8 exactly store nahi hota — ghabrana mat.

Lab 4 ka sawaal: ghar ka daam vs health

House price → MAE ki taraf

Ek bahut mehnga ghar aksar data-entry galti ya one-off luxury sale hota hai. Use sabka model bigaadne mat do. Galti ka nuksaan = paisa, jaan nahi. Outliers = noise → robust MAE.

Health (dosage, risk) → MSE ki taraf

Extreme value ek sach me critical patient ho sakta hai. Us ek patient par badi galti = khatra. Badi galti ko zyada punish karne wala MSE behtar. Pakka pata na ho ki outlier noise hai ya asli → Huber.

Exam Radar

  • MAE = (1/n)Σ|y − ŷ| = L1 loss; PyTorch nn.L1Loss() (naam me "MAE" nahi hai!).
  • Linear penalty; outlier robust; optimal constant = median (conditional median). Assumption: Laplace noise.
  • Gradient = ±1 (constant magnitude, sign of error); r = 0 par non-differentiable (kink) → subgradient; convergence slow, minimum ke paas oscillation.
  • Units same as y → interpretable.
  • MSE vs MAE slide 21: MSE best for clean data jahan badi galti matter kare; MAE best for noisy/outlier data, interpretability.

Galti mat karna

  • "MAE differentiable nahi hai to use nahi kar sakte" — galat. Sirf ek point (r = 0) par kink hai; subgradient se kaam chal jaata hai, PyTorch bina error ke chalata hai.
  • nn.L1Loss ko L1 regularization (weights par |w| penalty, Chapter 19) se mat milao. Dono me |·| hai, par ek prediction errors par, doosra weights par.

Dimaag me aise baitha lo

MAE = auto meter: seedha per-km hisaab, Ambani dost se koi farak nahi, best guess median. Bas minimum ke paas thoda jhoolta hai.

11.11 Huber / Smooth L1 — dono ka best

Kahani

Ek samajhdaar class teacher: chhoti shararat par pyaar se samjhaati hai (narm, smooth — MSE jaisa), par badi galti par bhi sirf fixed rate se saza deti hai, gussa nahi karti (MAE jaisa) — taaki ek bigda bachcha poori class ka mahaul kharaab na kare. Ye teacher hai Huber loss.

Lδ(r) = ½ r2  agar |r| ≤ δ,     Lδ(r) = δ (|r| − ½δ)  agar |r| > δ
  • r = y − ŷ (residual); δ (delta) = threshold — kitni galti tak "chhoti" maanni hai
  • Andar (|r| ≤ δ): MSE jaisa parabola → smooth, minimum ke paas gradient chhota (no jhoolna)
  • Bahar (|r| > δ): MAE jaisi seedhi line, slope δ → outlier ka gradient δ par capped
  • −½δ isliye ki |r| = δ par dono tukde same value (½δ²) aur same slope (δ) par milein — koi kink nahi, poora differentiable

Chalo numbers se samjhein (δ = 1)

  1. r = 0.5 (chhoti): ½ × 0.5² = 0.125
  2. r = 3 (badi): 1 × (3 − 0.5) = 2.5  (MSE hota 9, MAE hota 3)
  3. Example 1 (saare |r| ≤ 1): (0.125 + 0.125 + 0 + 0.5)/4 = 0.1875 = MSE/2
  4. Outlier wala (r = 19): (0.125 + 0.125 + 0 + 18.5)/4 = 4.6875  (MSE 90.375, MAE 5.0)

Huber outlier par MAE ki tarah shaant (4.6875), aur chhoti galtiyon par MSE ki tarah smooth.

−3 −2 −1 0 1 2 3 1 2 3 4 loss −3 −2 −1 1 2 3 −4 −2 2 4 ∂loss/∂r r = y − ŷ (residual) laal: MSE r² · ink: MAE |r| · orange: Huber gradient vs r laal: 2r · ink: ±1 · orange: clip(r, −1, 1)
Left: teen loss curves residual r ke against. MSE (laal) parabola — bade r par tezi se upar; MAE (ink) V-shape — seedha badhta hai; Huber (orange dashed) beech me parabola, bahar seedhi line. Right: unke gradients — MSE ka gradient r ke saath badhta hai, MAE ka hamesha ±1 (0 par jump/kink), Huber ka gradient ±δ par ruk jaata hai.

PyTorch · Huber from scratch vs nn.HuberLoss vs nn.SmoothL1Loss

import torch
import torch.nn as nn

y_hat     = torch.tensor([2.5, 0.0, 2.0, 8.0])
y_clean   = torch.tensor([3.0, -0.5, 2.0, 7.0])
y_outlier = torch.tensor([3.0, -0.5, 2.0, 27.0])

huber = nn.HuberLoss(delta=1.0)        # default delta = 1.0
sl1   = nn.SmoothL1Loss(beta=1.0)      # default beta = 1.0

def huber_manual(y_true, y_pred, delta=1.0):
    r = torch.abs(y_true - y_pred)
    quad = 0.5 * r ** 2                     # chhoti galti: MSE jaisa
    lin  = delta * (r - 0.5 * delta)        # badi galti: MAE jaisa
    return torch.where(r <= delta, quad, lin).mean()

print(huber_manual(y_clean, y_hat), huber(y_hat, y_clean), sl1(y_hat, y_clean))
print(huber_manual(y_outlier, y_hat), huber(y_hat, y_outlier), sl1(y_hat, y_outlier))

Output

tensor(0.1875) tensor(0.1875) tensor(0.1875)
tensor(4.6875) tensor(4.6875) tensor(4.6875)

Exam Radar

  • Huber = |r| ≤ δ par quadratic (MSE-like), bahar linear (MAE-like). Robust + smooth + differentiable.
  • Huber ka gradient = r ko [−δ, δ] me clip.
  • PyTorch: nn.HuberLoss(delta=1.0), nn.SmoothL1Loss(beta=1.0); δ = β = 1 par equal.

Dimaag me aise baitha lo

Huber = andar parabola, bahar seedhi line. Chhoti galti pe MSE, badi galti pe MAE.

11.12 Lab 4 replay — California housing par MSE vs MAE

Ab wahi experiment jo Lab 4 Part 1 me kiya tha, line by line. Data: California housing ke 400 ghar, ek feature MedInc (median income) se MedHouseVal (ghar ki median value) predict karna. 3 targets (index 5, 120, 300) me +20 jod ke nakli outliers banaye. Model: ek untrained nn.Linear(1, 1) — loss sirf "naapne" ke liye, training abhi nahi.

Lab 4 · Part 1 (imports: numpy as np, torch, torch.nn as nn, sklearn.datasets.fetch_california_housing)

# Lab 4 Part 1 (condensed): California housing, 1 feature, 3 outliers
housing = fetch_california_housing(as_frame=True)
df_sub = housing.frame.sample(n=400, random_state=42).reset_index(drop=True)
X_reg = df_sub[["MedInc"]].values.astype(np.float32)          # (400, 1)
y_reg = df_sub["MedHouseVal"].values.astype(np.float32)       # (400,)

y_reg_outliers = y_reg.copy()
y_reg_outliers[[5, 120, 300]] += 20.0      # 3 targets ko door phenk do

X_reg_t     = torch.tensor(X_reg)
y_clean_t   = torch.tensor(y_reg)
y_outlier_t = torch.tensor(y_reg_outliers)

torch.manual_seed(42)
model = nn.Linear(in_features=1, out_features=1)   # y_hat = w*x + b (untrained)
y_pred_t = model(X_reg_t).squeeze(1)               # (400, 1) -> (400,)  target se match

def mse_manual(y_true, y_pred):
    return torch.mean((y_true - y_pred) ** 2)

def mae_manual(y_true, y_pred):
    return torch.mean(torch.abs(y_true - y_pred))

mse_loss_fn = nn.MSELoss()
mae_loss_fn = nn.L1Loss()
print(f"MSE clean {mse_manual(y_clean_t, y_pred_t).item():.4f} | built-in {mse_loss_fn(y_pred_t, y_clean_t).item():.4f}")
print(f"MSE outl. {mse_manual(y_outlier_t, y_pred_t).item():.4f} | built-in {mse_loss_fn(y_pred_t, y_outlier_t).item():.4f}")
print(f"MAE clean {mae_manual(y_clean_t, y_pred_t).item():.4f} | built-in {mae_loss_fn(y_pred_t, y_clean_t).item():.4f}")
print(f"MAE outl. {mae_manual(y_outlier_t, y_pred_t).item():.4f} | built-in {mae_loss_fn(y_pred_t, y_outlier_t).item():.4f}")

Output

MSE clean 4.4830 | built-in 4.4830
MSE outl. 6.9770 | built-in 6.9770
MAE clean 1.8151 | built-in 1.8151
MAE outl. 1.9398 | built-in 1.9398
MetricClean dataOutlier dataChange
MSE4.48306.9770+2.4940 (+55.6%)
MAE1.81511.9398+0.1247 (+6.9%)

400 me se sirf 3 points (0.75%) badle, aur MSE 55.6% uchhal gaya; MAE sirf 6.9%. Dhyaan do .squeeze(1) par: nn.Linear ka output (400, 1) hai aur target (400,). Shape match karne ke liye prediction ko (400,) kiya. (Lab 5 ne ulta kiya tha — target ko .view(-1, 1) se (N, 1). Dono tareeke sahi; important ye hai ki shapes same hon — 11.18 me iska khatarnaak bug.)

Lab 4 · loss landscape sweep (weight vs loss)

# Lab 4: loss landscape sweep -- bias 0 fix, weight ko -1 se 1 tak ghumao
weights = np.linspace(-1, 1, 100)

def sweep_loss(loss_fn, X, y, weights, bias):
    losses = []
    for w in weights:
        preds = w * X.squeeze() + bias                       # (N,)
        preds_t = torch.tensor(preds, dtype=torch.float32)
        losses.append(loss_fn(preds_t, torch.tensor(y, dtype=torch.float32)).item())
    return np.array(losses)

mse_clean = sweep_loss(nn.MSELoss(), X_reg, y_reg, weights, 0.0)
mse_out   = sweep_loss(nn.MSELoss(), X_reg, y_reg_outliers, weights, 0.0)
mae_clean = sweep_loss(nn.L1Loss(),  X_reg, y_reg, weights, 0.0)
mae_out   = sweep_loss(nn.L1Loss(),  X_reg, y_reg_outliers, weights, 0.0)
# phir in chaaron ko plot kiya (plotly) -- weight vs loss

Plot me kya dikha (Lab 4 ki interpretation):

Dimaag me aise baitha lo

Lab 4 ka ek-line sabak: "MSE doesn't just have a higher loss with outliers — it changes where the best weight is. MAE's loss goes up too, but it keeps pointing to roughly the same answer."

11.13 Cross-entropy ki intuition — "surprise" = −log p

Kahani

TV wale mausam uncle ne kaha: "Kal baarish hone ka chance sirf 1% hai." Tum bina chhatri nikle aur jam ke baarish hui. Kitna surprise? Bahut zyada (aur bheeg bhi gaye)! Agar unhone 50-50 bola hota to thoda surprise. Agar 99% baarish bola hota aur hui, to koi surprise nahi. Jo hua, uski predicted probability jitni kam, surprise utna zyada. Is surprise ko naapne ka formula hai −log p — aur cross-entropy bas "sahi answer par model kitna surprised hua" ka average hai.

surprise(p) = −ln p
  • p = jo sach me hua (sahi class), us par model ne kitni probability lagayi thi
  • p = 1 → −ln 1 = 0: bilkul surprise nahi
  • p → 0 → −ln p → ∞: "jo hona namumkin bataya tha wahi ho gaya" — infinite surprise
  • log kyun? Do independent ghatnaon ka surprise judna chahiye, par unki probabilities multiply hoti hain. Sirf log hi guna ko jod me badalta hai (wahi 11.4 wala jaadu).

Teacher ke numbers (slide 23)

  1. p = 0.99 (confident & correct): −ln 0.99 = 0.0101 ≈ 0.01
  2. p = 0.70 (correct but unsure): −ln 0.70 = 0.3567 ≈ 0.36
  3. p = 0.30 (mostly wrong): −ln 0.30 = 1.2040 ≈ 1.20
  4. p = 0.01 (confident & WRONG): −ln 0.01 = 4.6052 ≈ 4.60

0.99 → 0.01 se 0.01 → 4.6: probability 99 guna giri, surprise 458 guna badha. Confident galti ki saza sabse bhaari.

0 0.2 0.4 0.6 0.8 1 1 2 3 4 5 p (sahi jawab ko di gayi probability) −ln p 0.70 → 0.36 0.30 → 1.20 0.01 → 4.61 confident & WRONG → bahut bada surprise confident & correct → surprise ≈ 0 0.99 → 0.01
Surprise curve −ln p: jab model sahi class ko 1 ke paas probability deta hai, loss ≈ 0; jab sahi class ko 0 ke paas probability deta hai, loss aasman chhoo leta hai (p → 0 par −ln p → ∞). Points slide 23 wale hain.

Information theory wala naam: Shannon ke hisaab se −log2 p = utne bits jitne us ghatna ko encode karne me lagte hain. Slide 23: "Cross-Entropy measures the average number of bits needed to encode the true label under the model's predicted probability distribution. Lower CE = model assigns high probability to the correct class." Deep learning / PyTorch me natural log (ln) use hota hai, unit "nats" — base badalne se loss ek constant se multiply hota hai, minimum ki jagah nahi badalti.

H(p, q) = − Σk p(k) log q(k)
  • p = asli distribution (labels — one-hot), q = model ki predicted distribution (ŷ)
  • "Cross" kyunki do alag distributions mix hain: weights p se, log q ka
  • Entropy H(p) = −Σ p log p; KL divergence = H(p, q) − H(p) ≥ 0. One-hot p ki entropy 0 hai, to cross-entropy minimize karna = KL divergence minimize karna = model ko asli distribution ke paas laana

Dimaag me aise baitha lo

Cross-entropy = sahi answer dekh kar model ka surprise. Confident aur sahi → 0. Confident aur galat → aasmaan.

11.14 Binary Cross-Entropy (BCE) — haan/na wale sawaalon ka loss

Kahani

Spam filter: har email ke liye model bolta hai "spam hone ka chance ŷ". Email sach me spam (y = 1) tha aur model ne 0.9 bola — shabaash, chhota loss. Model ne 0.1 bola — "mujhe 90% yakeen tha ki spam nahi hai" — aur wo spam nikla! Bada loss. Email spam nahi tha (y = 0) to ulta: model ko ŷ chhota bolna chahiye tha.

BCE = − 1n Σi=1n [ yi log ŷi + (1 − yi) log(1 − ŷi) ]
  • yi ∈ {0, 1} = asli label
  • ŷi = σ(zi) ∈ (0, 1) = model ki "y = 1 hone ki probability" (sigmoid output)
  • y log ŷ = sirf tab on jab y = 1 (warna 0 × kuch = 0); (1 − y) log(1 − ŷ) = sirf tab on jab y = 0. Har example me ek hi term zinda rehta hai — wahi Bernoulli switch (11.7)
  • Aage − : log (0,1) ki value negative hoti hai, minus se loss positive
  • log = natural log

Chalo numbers se samjhein — single cases (y = 1)

  1. ŷ = 0.9: −[1 × ln 0.9 + 0 × ln 0.1] = −ln 0.9 = 0.1054
  2. ŷ = 0.5: −ln 0.5 = 0.6931 (= ln 2: "pata nahi" bolne ka loss)
  3. ŷ = 0.1: −ln 0.1 = 2.3026
  4. Mirror (y = 0): ŷ = 0.1 → −ln(1 − 0.1) = −ln 0.9 = 0.1054; ŷ = 0.9 → −ln 0.1 = 2.3026

Confidence sahi disha me → loss 0.1054; galat disha me → 2.3026 (22 guna).

Batch average

Sawaal: y = [1, 0, 1, 0], ŷ = [0.9, 0.2, 0.6, 0.4]. BCE?

  1. Ex 1 (y = 1): −ln 0.9 = 0.1054
  2. Ex 2 (y = 0): −ln(1 − 0.2) = −ln 0.8 = 0.2231
  3. Ex 3 (y = 1): −ln 0.6 = 0.5108
  4. Ex 4 (y = 0): −ln(1 − 0.4) = −ln 0.6 = 0.5108
  5. Average: (0.1054 + 0.2231 + 0.5108 + 0.5108) / 4 ≈ 1.3502 / 4 = 0.3375 (full precision sum 1.35016)

BCE = 0.3375.

0 0.2 0.4 0.6 0.8 1 1 2 3 4 ŷ (model ki P(y = 1)) loss (0.1, 2.3026) (0.5, 0.6931) (0.9, 0.1054) y = 1: loss = −ln ŷ y = 0: loss = −ln(1 − ŷ)
BCE ke do chehre: y = 1 ho to ink wali curve (ŷ → 0 par loss → ∞), y = 0 ho to laal dashed curve (ŷ → 1 par loss → ∞). Dono curves ek doosre ka mirror image hain, aur ŷ = 0.5 par dono ka loss 0.6931 = ln 2.

BCE ka gradient (w.r.t. ŷ)

∂ BCE∂ ŷ = − yŷ + 1 − y1 − ŷ = ŷ − yŷ(1 − ŷ)
  • Kaise: d/dŷ (ln ŷ) = 1/ŷ, d/dŷ ln(1 − ŷ) = −1/(1 − ŷ); common denominator ŷ(1 − ŷ) lo, numerator −y(1 − ŷ) + (1 − y)ŷ = ŷ − y
  • Denominator ŷ(1 − ŷ) confident galti par (jaise y = 1, ŷ → 0) bahut chhota → gradient bahut bada. Sahi hone par numerator ≈ 0 → gradient ≈ 0. Slide 25: "large when model is confidently wrong, near-zero when model is correct. Perfect for Sigmoid output."
  • Batch example par (har element, 1/n se pehle): [−1.1111, 1.2500, −1.6667, 1.6667]

PyTorch · BCE from scratch vs nn.BCELoss vs nn.BCEWithLogitsLoss

import torch
import torch.nn as nn

y     = torch.tensor([1.0, 0.0, 1.0, 0.0])     # targets FLOAT hone chahiye
probs = torch.tensor([0.9, 0.2, 0.6, 0.4])     # sigmoid ke baad wali probabilities

def bce_manual(y_true, p, eps=1e-7):
    p = torch.clamp(p, eps, 1 - eps)            # log(0) se bachao
    return -(y_true * torch.log(p) + (1 - y_true) * torch.log(1 - p)).mean()

logits = torch.log(probs / (1 - probs))         # ulta sigmoid: wahi probs dene wale logits
print(logits)
print(bce_manual(y, probs))
print(nn.BCELoss()(probs, y))                   # input = probabilities
print(nn.BCEWithLogitsLoss()(logits, y))        # input = raw logits
print(nn.BCELoss(reduction='none')(probs, y))   # har example ka alag loss

Output

tensor([ 2.1972, -1.3863,  0.4055, -0.4055])
tensor(0.3375)
tensor(0.3375)
tensor(0.3375)
tensor([0.1054, 0.2231, 0.5108, 0.5108])

Teeno 0.3375 — same. nn.BCELoss(weight=None, reduction='mean'): input = probabilities (sigmoid ke baad, [0, 1] me), target = float 0./1. (ya soft labels [0, 1] me), dono same shape. weight = har element ka rescaling weight. nn.BCEWithLogitsLoss(weight=None, reduction='mean', pos_weight=None): input = raw logits (koi bhi real number), sigmoid andar khud lagata hai; pos_weight = positive class ko extra weight (imbalanced data, jaise 1 positive par 10 negative ho to pos_weight = 10). Target long (int) diya to error — BCELoss: RuntimeError: Found dtype Long but expected Float; BCEWithLogitsLoss: RuntimeError: result type Float can't be cast to the desired output type Long — isliye Lab 5 me y.astype(np.float32) kiya tha.

Exam Radar

  • BCE = −[y log ŷ + (1 − y) log(1 − ŷ)]; output activation sigmoid; label y ∈ {0, 1}.
  • ŷ = 0.5 par BCE = ln 2 = 0.6931 (koi bhi y ho). Untrained binary model ka loss ≈ 0.69 — Lab 5 epoch 1 loss 0.670 isi ke paas tha.
  • ∂BCE/∂ŷ = (ŷ − y)/(ŷ(1 − ŷ)); sigmoid ke saath ∂BCE/∂z = ŷ − y (11.16).
  • nn.BCELoss ← probabilities; nn.BCEWithLogitsLoss ← logits (sigmoid + BCE fused, numerically stable). Target float, same shape.

Dimaag me aise baitha lo

BCE: y = 1 ho to −log ŷ, y = 0 ho to −log(1 − ŷ). "Jo sach hai, us par kitni probability di?" ka minus log.

11.15 Categorical Cross-Entropy — K classes, softmax ke saath

Kahani

Ab model ko photo dekh kar batana hai: cat, dog ya bird? Model har class ko ek probability deta hai (softmax se, jod = 1). Teacher ka laal pen ab sirf ek cheez dekhta hai: "sahi class ko tune kitni probability di?" Baaki classes ko kitna diya, iski seedhi parwaah nahi — wo apne aap kam ho jaata hai kyunki total 1 hi hai.

CCE = − Σk=1K yk log ŷk = − log ŷcorrect     (batch: − 1N Σi Σc yi,c log p̂i,c)
  • K = classes ki ginti; yk = one-hot label (sahi class 1, baaki 0)
  • ŷk = softmax(z)k = ezk / Σj ezj (Chapter 9), z = logits
  • One-hot ki wajah se sum me sirf sahi class ka term bachta hai → −log(sahi class ki probability)
  • Slide 27: "Requires Softmax output … Pairing: Softmax + CCE."

Teacher ka example (slide 26)

Prediction: Cat 0.7, Dog 0.2, Bird 0.1. True class: Cat → y = [1, 0, 0].

  1. CCE = −(1 × ln 0.7 + 0 × ln 0.2 + 0 × ln 0.1) = −ln 0.7 = 0.3567 ≈ 0.36
  2. Agar sahi class ko sirf 0.01 diya hota: −ln 0.01 = 4.6052
  3. Gradient w.r.t. logits = ŷ − y: [0.7 − 1, 0.2 − 0, 0.1 − 0] = [−0.3, +0.2, +0.1]

CCE = 0.36. Gradient descent gradient ka ulta chalta hai: Cat ka gradient −0.3 → Cat ka logit badhega; Dog (+0.2), Bird (+0.1) ke logits ghatenge. Sabse zyada dhakka usko jo sabse galat hai.

Logits se shuru — poora hisaab

Sawaal: logits z = [2, 1, 0.1], true class = 0. CE kitna?

  1. Exponentials: e2 = 7.3891, e1 = 2.7183, e0.1 = 1.1052
  2. Jod: 7.3891 + 2.7183 + 1.1052 = 11.2125
  3. Softmax: [7.3891/11.2125, 2.7183/11.2125, 1.1052/11.2125] = [0.6590, 0.2424, 0.0986] (jod = 1 ✓)
  4. Loss: −ln 0.6590 = 0.4170
  5. Shortcut (log-sum-exp): CE = −ztrue + ln Σ ezj = −2 + ln 11.2125 = −2 + 2.4170 = 0.4170 ✓
  6. Gradient: ŷ − y = [0.6590 − 1, 0.2424, 0.0986] = [−0.3410, 0.2424, 0.0986]
  7. Agar true class 2 hota: −ln 0.0986 = 2.3170

CE = 0.4170 (class 0 sahi), 2.3170 (class 2 sahi hota).

Logits kya hote hain?

Logits = last nn.Linear layer ke raw, unnormalised scores — softmax/sigmoid se pehle. Koi bhi real number (−∞ se +∞), jod 1 hona zaroori nahi. Bada logit = us class par zyada bharosa. Binary case me logit = log-odds: z = ln(p/(1 − p)), jo sigmoid ka ulta hai (11.14 code me torch.log(probs / (1 - probs)) se wapas logits banaye the: 0.9 → 2.1972, 0.5 → 0).

PyTorch · CE from scratch vs nn.CrossEntropyLoss vs log_softmax + nn.NLLLoss

import torch
import torch.nn as nn

logits = torch.tensor([[2.0, 1.0, 0.1],     # example 1: 3 classes ke raw scores
                       [0.5, 2.5, 0.3]])    # example 2
target = torch.tensor([0, 1])               # class INDEX, dtype long (int64)
print(target.dtype)

# --- from scratch: log_softmax -> sahi class ka -log -> average
log_probs = logits - torch.logsumexp(logits, dim=1, keepdim=True)
manual = -log_probs[torch.arange(2), target].mean()

# --- built-in
ce = nn.CrossEntropyLoss()                  # raw logits + class indices
nll = nn.NLLLoss()                          # log-probabilities + class indices

print(torch.softmax(logits, dim=1))
print(manual)
print(ce(logits, target))
print(nll(torch.log_softmax(logits, dim=1), target))

Output

torch.int64
tensor([[0.6590, 0.2424, 0.0986],
        [0.1086, 0.8025, 0.0889]])
tensor(0.3185)
tensor(0.3185)
tensor(0.3185)

Example 1 ka loss 0.4170 (upar haath se nikala), example 2 ka 0.2200; average 0.3185. Teeno raaste same jawab. Iski poori PyTorch kahani (shapes, dtypes, galtiyan) 11.19 me.

Exam Radar

  • CCE = −Σ yk log ŷk = −log ŷcorrect; one-hot labels; softmax output.
  • Softmax + CE ka gradient w.r.t. logits = ŷ − y ("predicted minus true distribution", slide 27).
  • Untrained K-class model (sab classes ~barabar) ka loss ≈ ln K: K = 10 → 2.3026 (Lab 4 digits me 2.1846 aaya — paas).
  • Sirf sahi class ki probability loss me directly aati hai; baaki softmax ke denominator ke through.

Dimaag me aise baitha lo

CE = −log(sahi class ki probability). Gradient = predicted − true. Bas do line.

11.16 Classification me CE, MSE kyun nahi? (derivation + numbers)

Kahani

Do tuition teachers. Bachche ne 2 + 2 = 400 likha (bahut confident, bahut galat). Pehli teacher (sigmoid + MSE) itni thaki hui hai ki bas halka sa "hmm" bolti hai — bachcha kuch nahi seekhta. Doosri teacher (sigmoid + CE) seedha bolti hai "tu 396 se galat hai" — galti jitni badi, dhakka utna bada. Classification me hume doosri teacher chahiye.

Derivation 1: sigmoid + MSE — σ′(z) ka zeher

Chain rule

  1. L = ½(ŷ − y)², ŷ = σ(z) (L2 worksheet wala setup)
  2. ∂L/∂z = ∂L/∂ŷ · ∂ŷ/∂z = (ŷ − y) · σ′(z) = (ŷ − y) · ŷ(1 − ŷ)
  3. Confidently wrong: y = 0 par z bada → ŷ ≈ 1 → σ′(z) = ŷ(1 − ŷ) ≈ 0 → gradient ≈ 0. Sabse badi galti par sabse chhota sudhaar. Learning atak jaati hai (saturation). σ′ ka max bhi sirf 0.25 hai (worksheet Part 7), to best case me bhi gradient 4 guna chhota.

Derivation 2: sigmoid + BCE — σ′ cancel ho jaata hai

Chain rule

  1. ∂L/∂ŷ = (ŷ − y) / (ŷ(1 − ŷ)) (11.14)
  2. ∂ŷ/∂z = σ′(z) = ŷ(1 − ŷ)
  3. Multiply: ∂L/∂z = (ŷ − y)/(ŷ(1 − ŷ)) × ŷ(1 − ŷ) = ŷ − y — ŷ(1 − ŷ) upar-neeche se kat gaya!
  4. Gradient = seedha galti. Confidently wrong (y = 0, ŷ ≈ 1) → gradient ≈ 1, poora dhakka.

Derivation 3: softmax + CE — wahi khoobsurat jawab

Log-sum-exp form se

  1. Sahi class c: L = −ln( ezc / Σj ezj ) = −zc + ln Σj ezj
  2. zk se differentiate: pehla term sirf k = c par −1 deta hai; doosra: ∂/∂zk ln Σ ezj = ezk / Σ ezj = ŷk
  3. Total: ∂L/∂zk = ŷk − yk (yk = 1 sirf sahi class par)

Sigmoid + BCE aur Softmax + CE, dono ka gradient w.r.t. logits = ŷ − y. Isi liye ye jodiyan "standard" hain.

L2 worksheet ke numbers par takkar

Worksheet Part 5: x = 2, w = 1, b = 0, y = 0, α = 0.5.

  1. z = 1 × 2 + 0 = 2; ŷ = σ(2) = 0.880797; σ′(2) = 0.880797 × 0.119203 = 0.104994
  2. ½-MSE (worksheet ka): ∂L/∂w = (ŷ − y) σ′(z) x = 0.880797 × 0.104994 × 2 = 0.184956 → wnew = 1 − 0.5 × 0.184956 = 0.907522
  3. BCE: ∂L/∂w = (ŷ − y) x = 0.880797 × 2 = 1.761594 → wnew = 1 − 0.5 × 1.761594 = 0.119203
  4. Ratio: 1.761594 / 0.184956 = 9.52 = 1/σ′(2)

Same galti, BCE ka dhakka 9.5 guna bada. Model jitna zyada confidently galat, ye farak utna bada (neeche table).

z (asli y = 0)ŷ = σ(z)σ′(z)½-MSE: ∂L/∂zBCE: ∂L/∂zBCE / MSE
00.50.250.1250.54
20.8807970.1049940.0924780.8807979.5
40.9820140.0176630.0173450.98201456.6
60.9975270.0024670.0024600.997527405
80.9996650.0003350.0003350.9996652983
−4 −2 0 2 4 6 8 0.25 0.50 0.75 1.00 z (logit), sach me y = 0 |∂L/∂z| BCE: 0.8808 MSE: 0.0925 BCE: 0.9975 MSE: 0.0025 BCE: ∂L/∂z = ŷ − y = σ(z) ½-MSE: ∂L/∂z = (ŷ − y)·σ′(z) z bada = 'confidently wrong'
Asli label y = 0 hai, par z jitna bada utna model 'confidently wrong'. BCE ka gradient (ink) badhta jaata hai aur 1 ke paas tik jaata hai — galti badi to dhakka bada. Sigmoid + MSE ka gradient (laal) σ′(z) ki wajah se ulta mar jaata hai — sabse badi galti par sabse chhota dhakka. Worksheet wala point z = 2: 0.8808 vs 0.0925.

PyTorch · autograd se check: worksheet numbers par MSE vs BCE gradient

import torch
import torch.nn as nn

x, w, b, y = 2.0, 1.0, 0.0, 0.0          # L2 worksheet ke numbers

# --- sigmoid + (1/2) squared error
w1 = torch.tensor(w, requires_grad=True)
y_hat = torch.sigmoid(w1 * x + b)
loss_mse = 0.5 * (y_hat - y) ** 2
loss_mse.backward()
print("y_hat      :", round(y_hat.item(), 6))
print("MSE  dL/dw :", round(w1.grad.item(), 6))

# --- sigmoid + BCE (BCEWithLogitsLoss ke andar sigmoid hai)
w2 = torch.tensor(w, requires_grad=True)
z = (w2 * x + b).reshape(1)
loss_bce = nn.BCEWithLogitsLoss()(z, torch.tensor([y]))
loss_bce.backward()
print("BCE  dL/dw :", round(w2.grad.item(), 6))

Output

y_hat      : 0.880797
MSE  dL/dw : 0.184956
BCE  dL/dw : 1.761594

Aur wajahein

Exam Radar

  • Sigmoid + MSE: ∂L/∂z = (ŷ − y)σ′(z) → confident galti par vanishing (σ′ ≤ 0.25, saturation par ≈ 0).
  • Sigmoid + BCE: ∂L/∂z = ŷ − y. Softmax + CE: ∂L/∂zk = ŷk − yk.
  • Isliye: classification → cross-entropy; regression → MSE/MAE.

Dimaag me aise baitha lo

MSE + sigmoid me σ′ beech me baith ke gradient kha jaata hai. CE me σ′ upar-neeche se kat jaata hai → bacha sirf ŷ − y.

11.17 log(0) ka khatra, logits aur numerical stability

Kahani

Calculator me log 0 dabao — "Math ERROR". Computer bhi yahi karta hai: log(0) = −∞, aur ek bhi ∞ ya nan (not a number) loss me aaya to poori training barbaad — saare weights nan ho jaate hain. Cross-entropy me log hai, to ye khatra hamesha saath hai.

Kab hota hai? Jab model kisi probability ko exactly 0 ya 1 bol de. Sigmoid sach me kabhi 1 nahi deta (maths me), par float32 ki precision limited hai: 1 ke paas wo ~6 × 10−8 se chhota farak nahi dekh sakta. σ(20) = 1/(1 + e−20) = 1 − 2 × 10−9 → float32 me round ho ke exactly 1.0. Ab agar y = 0, to BCE me log(1 − 1) = log 0 — boom.

Fix 1: clamp / epsilon (from-scratch code me)

Probability ko [ε, 1 − ε] me daba do, jaise ε = 10−7: p = torch.clamp(p, eps, 1 - eps) (11.14 ke bce_manual me yahi kiya). PyTorch ka nn.BCELoss andar khud log ko −100 par clamp karta hai, isliye uska ek element ka loss max 100 hota hai — infinite nahi, par galat zaroor.

Fix 2 (best): logits par seedha kaam karo

BCE(z, y) = max(z, 0) − z · y + log(1 + e−|z|)
  • z = logit (sigmoid se pehle). Ye −[y log σ(z) + (1 − y) log(1 − σ(z))] ka hi algebra se bana roop hai: log σ(z) = −log(1 + e−z), log(1 − σ(z)) = −z − log(1 + e−z)
  • Is form me na kabhi log(0) banta hai, na ebada positive (overflow) — kyunki e−|z| hamesha ≤ 1
  • nn.BCEWithLogitsLoss yahi karta hai. Docs: "This loss combines a Sigmoid layer and the BCELoss in one single class" — ek saath karne se ye stable trick lag paati hai (Lab 5 ka conceptual question 2 ka jawab)

PyTorch · confident galti: BCELoss vs BCEWithLogitsLoss

import torch
import torch.nn as nn

z = torch.tensor([20.0])          # bahut bada logit
y = torch.tensor([0.0])           # par asli label 0 -- model confidently WRONG

p = torch.sigmoid(z)
print(p)                          # float32 me exactly 1.0 ban gaya!
print(nn.BCELoss()(p, y))         # log(1 - 1) = log(0) -> PyTorch ne -100 pe clamp kiya
print(nn.BCEWithLogitsLoss()(z, y))   # stable formula -> sahi jawab ~20

Output

tensor([1.])
tensor(100.)
tensor(20.)

Asli jawab −log(1 − σ(20)) = log(1 + e20) ≈ 20.000000002. BCEWithLogitsLoss ne 20 diya (sahi). BCELoss ko sigmoid ne pehle hi 1.0 de diya tha, log(0) bana, clamp ne 100 kar diya — galat number, aur clamp wale region me gradient bhi bekaar.

Softmax ka bhi yahi drama — log-sum-exp trick

Logits [1000, 999] ho to e1000 = inf (float overflow), inf/inf = nan. Trick: sabse bada logit ghata do (softmax shift-invariant hai — sab logits me same number jodo/ghatao to softmax nahi badalta): log_softmax(z)k = zk − max(z) − log Σj ezj − max(z). torch.softmax, torch.log_softmax aur nn.CrossEntropyLoss andar yahi karte hain.

PyTorch · naive softmax overflow vs built-in

import torch

z = torch.tensor([1000.0, 999.0])             # bahut bade logits

naive = torch.exp(z) / torch.exp(z).sum()     # e^1000 = inf  ->  inf/inf = nan
print(torch.exp(z))
print(naive)
print(torch.softmax(z, dim=0))                # PyTorch andar max ghata deta hai
print(torch.log_softmax(z, dim=0))            # z - max - log(sum(exp(z - max)))

Output

tensor([inf, inf])
tensor([nan, nan])
tensor([0.7311, 0.2689])
tensor([-0.3133, -1.3133])

Exam Radar

  • log(0) = −∞ → loss inf/nan. Fix: clamp(ε, 1 − ε) ya logits-based losses.
  • nn.BCELoss log outputs ko ≥ −100 clamp karta hai → max element loss 100.
  • nn.BCEWithLogitsLoss = sigmoid + BCE fused → numerically stable (log-sum-exp trick). PyTorch recommend karta hai.
  • nn.CrossEntropyLoss = log_softmax + NLLLoss → stable; isliye raw logits do.
  • float32 me σ(z) ≈ 17 se upar ke z par exactly 1.0 ban jaata hai.

Dimaag me aise baitha lo

Probability banao hi mat — logits seedha loss ko do. "WithLogits" aur "CrossEntropy" andar se safe kaam kar lenge.

11.18 PyTorch me binary classification: BCELoss vs BCEWithLogitsLoss (Lab 4 + Lab 5)

Kahani

Maa ne kaha, "daal me namak daal dena." Tumne daal diya. Phir didi aayi, usne bhi daal diya — kisi ko pata nahi tha ki pehle daal chuka hai. Daal kharaab, aur kisi ne shor bhi nahi machaya. Sigmoid ke saath bhi yahi hota hai: agar model ke andar sigmoid lag chuka hai aur loss bhi andar sigmoid lagata hai, to sigmoid do baar lagta hai — koi error nahi, bas chupchaap galat jawab.

nn.BCELoss()

Input = probabilities (0–1). Sigmoid tumhe khud lagana hai (model ke andar nn.Sigmoid() ya torch.sigmoid). Target float, same shape. Logits de diye to error: all elements of input should be between 0 and 1.

nn.BCEWithLogitsLoss()

Input = raw logits. Sigmoid andar (stable tarike se, 11.17). Model ke end me sigmoid mat lagao. Probabilities de diye to koi error nahi — double sigmoid, galat number.

Lab 4 replay: double sigmoid ki galti

Lab 4 · Part 2 (breast cancer, X_test_bc_t scaled, y_test_bc_t float)

# Lab 4 Part 2: breast cancer (30 features), untrained logistic model
torch.manual_seed(42)
model_bc = nn.Linear(in_features=30, out_features=1)      # 30 features -> 1 logit

with torch.no_grad():
    test_logits = model_bc(X_test_bc_t[:5]).squeeze(1)     # (5, 1) -> (5,)
    test_probs = torch.sigmoid(test_logits)                # probabilities

print("Raw logits:   ", test_logits)
print("Probabilities:", test_probs)

bce_loss_fn = nn.BCELoss()                                 # probabilities chahiye
bce_result = bce_loss_fn(test_probs, y_test_bc_t[:5])

bce_logits_loss_fn = nn.BCEWithLogitsLoss()                # logits chahiye
bce_logits_result = bce_logits_loss_fn(test_probs, y_test_bc_t[:5])         # GALTI: probs de diye
bce_logits_fixed  = bce_logits_loss_fn(test_logits, y_test_bc_t[:5])        # FIX: logits do

print(f"BCELoss(probabilities)    : {bce_result.item():.6f}")
print(f"BCEWithLogitsLoss(probs)  : {bce_logits_result.item():.6f}   <- double sigmoid")
print(f"BCEWithLogitsLoss(logits) : {bce_logits_fixed.item():.6f}")
print("sigmoid applied AGAIN:", torch.sigmoid(test_probs))

Output

Raw logits:    tensor([ 1.1869, -0.1040, -0.2470,  0.4076,  2.0179])
Probabilities: tensor([0.7662, 0.4740, 0.4386, 0.6005, 0.8827])
BCELoss(probabilities)    : 1.085942
BCEWithLogitsLoss(probs)  : 0.846862   <- double sigmoid
BCEWithLogitsLoss(logits) : 1.085942
sigmoid applied AGAIN: tensor([0.6827, 0.6163, 0.6079, 0.6458, 0.7074])

Kya hua? BCEWithLogitsLoss ne 0.7662 jaise probabilities ko logit samjha aur un par phir se sigmoid lagaya → 0.6827. Probability ka input 0 aur 1 ke beech hai, to doosri sigmoid ka output hamesha σ(0) = 0.5 aur σ(1) = 0.7311 ke beech phans jaata hai. Model ki asli confidence (0.8827) dabb ke 0.7074 ban gayi — sab kuch 0.5 ki taraf squash. Loss 1.085942 ki jagah 0.846862 aaya, aur PyTorch ne koi warning nahi di. Fix: logits do → 1.085942, BCELoss se exactly match.

Lab 5 replay: sigmoid andar + BCELoss + .view(-1, 1)

Lab 5 · MySimpleNN training loop (X_train_tensor: (455, 30) float32, y_train_tensor: (455,) float32)

# Lab 5: nn.Module + Sigmoid ANDAR + BCELoss  (breast cancer, 455 train rows x 30 features)
class MySimpleNN(nn.Module):
    def __init__(self, num_features):
        super().__init__()
        self.linear = nn.Linear(num_features, 1)   # (N, 30) -> (N, 1)
        self.sigmoid = nn.Sigmoid()                 # logit -> probability

    def forward(self, features):
        out = self.linear(features)
        out = self.sigmoid(out)                     # output already probability
        return out

model = MySimpleNN(X_train_tensor.shape[1])
loss_function = nn.BCELoss()                        # isliye BCELoss (probabilities leta hai)
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

for epoch in range(25):
    y_pred = model(X_train_tensor)                                  # (455, 1)
    loss = loss_function(y_pred, y_train_tensor.view(-1, 1))        # target (455,) -> (455, 1)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    print(f'Epoch: {epoch + 1}, Loss: {loss.item()}')

with torch.no_grad():
    y_pred = model(X_test_tensor)
    y_pred_labels = (y_pred > 0.5).float()                          # 0.5 threshold
    accuracy = (y_pred_labels == y_test_tensor.view(-1, 1)).float().mean()
    print(f'Accuracy: {accuracy.item()}')

Output

Epoch: 1, Loss: 0.6703537702560425
Epoch: 2, Loss: 0.5365203022956848
Epoch: 3, Loss: 0.4555133879184723
...
Epoch: 24, Loss: 0.1824541687965393
Epoch: 25, Loss: 0.1793031245470047
Accuracy: 0.9824561476707458

Lab 5 ne pattern A use kiya: model ke andar nn.Sigmoid(), isliye loss nn.BCELoss(). Loss 0.6704 se shuru (≈ ln 2 = 0.6931, "pata nahi" wali state) aur 0.1793 tak gira, test accuracy 98.25%. Ab us line ko dekho jahan sabse zyada log phisalte hain:

loss = loss_function(y_pred, y_train_tensor.view(-1, 1)) — model ka output y_pred shape (455, 1) hai (har row ek column), aur labels y_train_tensor shape (455,) (1-D). .view(-1, 1) ka matlab: "column 1 rakho, rows khud gin lo (−1)" → (455, 1). Ab dono same shape.

Shape mismatch hua to kya hota hai? (asli behaviour)

Lab 5 ka note kehta hai ki mismatch par PyTorch (N,) ko (N, 1) ke against broadcast karke (N, N) bana deta hai aur chupchaap bakwaas loss deta hai. Broadcasting ka matlab: (3, 1) aur (3,) ko jodo to PyTorch (3,) ko (1, 3) maan kar dono ko (3, 3) tak phaila deta hai — har prediction har label se compare, 3 sahi jodiyon ki jagah 9 jodiyan. Naye PyTorch me asli behaviour loss ke hisaab se alag hai — dono yaad rakho:

PyTorch · shape mismatch: MSELoss chupchaap galat, BCELoss error

import torch
import torch.nn as nn

y_pred = torch.tensor([[0.9], [0.2], [0.6]])   # model output: shape (3, 1)
y_true = torch.tensor([1.0, 0.0, 1.0])          # labels: shape (3,)

print(nn.MSELoss()(y_pred, y_true))             # warning + chupchaap (3,3) broadcasting
print(nn.MSELoss()(y_pred, y_true.view(-1, 1))) # sahi: dono (3, 1)

try:
    nn.BCELoss()(y_pred, y_true)                # BCELoss shape check karta hai
except ValueError as e:
    print("ValueError:", e)
print(nn.BCELoss()(y_pred, y_true.view(-1, 1)))

Output

UserWarning: Using a target size (torch.Size([3])) that is different to the input size (torch.Size([3, 1])). This will likely lead to incorrect results due to broadcasting. Please ensure they have the same size.
tensor(0.3144)
tensor(0.0700)
ValueError: Using a target size (torch.Size([3])) that is different to the input size (torch.Size([3, 1])) is deprecated. Please ensure they have the same size.
tensor(0.2798)

Recommended pattern: model logits de, loss sigmoid lagaye

Neeche poora chhota training loop hai (forward → loss → zero_grad() → backward() → step(), bilkul Lab 5 jaisa). Loop ki har line ki poori kahani Chapter 13 me hai — yahan sirf loss wali lines par dhyaan do: model sigmoid nahi lagata, loss BCEWithLogitsLoss hai, target float (N, 1), aur probability sirf prediction ke time torch.sigmoid se.

PyTorch · full mini training loop with BCEWithLogitsLoss (synthetic data)

import torch
import torch.nn as nn
import torch.optim as optim

torch.manual_seed(0)
X = torch.randn(200, 2)                          # 200 points, 2 features
y = (X[:, 0] + X[:, 1] > 0).float().view(-1, 1)  # label 0/1, shape (200, 1) FLOAT

model = nn.Linear(2, 1)            # NOTE: sigmoid nahi lagaya -- raw logit
loss_fn = nn.BCEWithLogitsLoss()   # sigmoid loss ke andar hai
optimizer = optim.SGD(model.parameters(), lr=0.5)

for epoch in range(50):
    logits = model(X)              # (200, 2) -> (200, 1)
    loss = loss_fn(logits, y)      # dono (200, 1)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if epoch % 10 == 0 or epoch == 49:
        print(f"epoch {epoch:2d}  loss {loss.item():.4f}")

with torch.no_grad():
    probs = torch.sigmoid(model(X))          # probability chahiye to ab sigmoid lagao
    preds = (probs > 0.5).float()
    print("accuracy:", round((preds == y).float().mean().item(), 4))

Output

epoch  0  loss 0.6202
epoch 10  loss 0.3590
epoch 20  loss 0.2903
epoch 30  loss 0.2546
epoch 40  loss 0.2314
epoch 49  loss 0.2162
accuracy: 0.975
Model ka last stepLossNateeja
nn.Sigmoid() (probabilities)nn.BCELoss()Sahi (Lab 5 pattern)
kuch nahi (logits)nn.BCEWithLogitsLoss()Sahi + stable (recommended)
nn.Sigmoid() (probabilities)nn.BCEWithLogitsLoss()Galat, chupchaap — double sigmoid (Lab 4 galti)
kuch nahi (logits)nn.BCELoss()Error — input 0–1 me nahi (ya kabhi kabhi galti se range me ho to galat number)

Exam Radar

  • BCELoss ← sigmoid outputs; BCEWithLogitsLoss ← raw logits (sigmoid + BCE ek class me, numerically stable).
  • Dono me target float (0./1.) aur input ke same shape. Binary model: nn.Linear(features, 1) → output (N, 1) → target .view(-1, 1).
  • Prediction: probability = torch.sigmoid(logits), class = (prob > 0.5).float() (logit > 0 ka bhi yahi matlab).
  • Double sigmoid outputs (0.5, 0.7311) me phas jaate hain.
  • Imbalanced binary data: nn.BCEWithLogitsLoss(pos_weight=torch.tensor([k])).

Galti mat karna

  • Model me nn.Sigmoid() + nn.BCEWithLogitsLoss() — double sigmoid, koi error nahi.
  • Target long/int rakha → BCELoss: RuntimeError: Found dtype Long but expected Float (BCEWithLogitsLoss: "result type Float can't be cast to the desired output type Long"). .float() karo.
  • (N, 1) vs (N,) — BCE me error, MSE me chupchaap galat. Hamesha print(pred.shape, target.shape).

Dimaag me aise baitha lo

Namak ek hi baar: sigmoid ya to model me (BCELoss), ya loss me (WithLogits) — dono me kabhi nahi. Aur shapes ka jodha milao: (N, 1) ke saath (N, 1).

11.19 PyTorch me multi-class: nn.CrossEntropyLoss aur nn.NLLLoss

Kahani

Railway ticket counter par do khidkiyan hain. Pehli khidki (CrossEntropyLoss) "kachcha maal" leti hai — raw logits — aur andar khud softmax, log, sab kar deti hai. Doosri khidki (NLLLoss) sirf "pakka maal" leti hai — log-probabilities. Galat khidki par galat form diya to ya to bhaga diya jaayega (error), ya ticket galat banega (silent bug).

nn.CrossEntropyLoss()(logits, target) = nn.NLLLoss()( log_softmax(logits, dim=1), target )
  • logits: shape (N, C), float — N examples, C classes, raw scores (softmax nahi lagana)
  • target: shape (N,), dtype torch.long (int64), har value 0 se C − 1 tak ki class index (one-hot nahi)
  • log_softmax(dim=1): har row (ek example) ke C logits ko log-probabilities me badalna (stable, 11.17)
  • NLLLoss: har row me target wali jagah ki value uthao, minus lagao, average: −(1/N) Σi logp[i, targeti]
  • Lab 8: "nn.CrossEntropyLoss() — combines log-softmax + negative log-likelihood in one step, the standard choice for multi-class classification."

Arguments jo MCQ me aate hain: nn.CrossEntropyLoss(weight=None, ignore_index=-100, reduction='mean', label_smoothing=0.0). weight = shape (C,) class weights (rare class ko zyada importance); ignore_index = is label wale examples ko loss me gino hi mat (default −100); reduction = 'mean'/'sum'/'none'; label_smoothing = 0.0 se 1.0, one-hot ko thoda "naram" karna (overconfidence kam). Naye PyTorch me target class-probabilities (N, C) float bhi ho sakta hai (soft labels) — one-hot float diya to wahi jawab (0.3185) aata hai — par standard aur exam wala tareeka = class indices.

Lab 4 Part 3 replay: digits (10 classes)

model_dig = nn.Linear(64, 10) (8×8 image = 64 pixels → 10 logits). Pehle 5 test images ke logits shape (5, 10), targets y_test_d_t[:5] shape (5,) int64. nn.CrossEntropyLoss()(test_logits_d, y_test_d_t[:5]) = 2.1846 — untrained model ke liye ln 10 = 2.3026 ke paas, bilkul expected. Phir lab ne torch.softmax(test_logits_d, dim=1) se probabilities banayi, jinka jod 1.0000001192092896 aaya (float32 rounding — 1 hi samjho). Lab yahin rukti hai, par BCE wali kahani se agla sawaal saaf hai: "ye probabilities CrossEntropyLoss ko de dein to?"

PyTorch · softmax lagaya, phir CrossEntropyLoss — double softmax

import torch
import torch.nn as nn

logits = torch.tensor([[2.0, 1.0, 0.1],
                       [0.5, 2.5, 0.3]])
target = torch.tensor([0, 1])
ce = nn.CrossEntropyLoss()

probs = torch.softmax(logits, dim=1)        # GALTI: pehle khud softmax laga diya
print(ce(logits, target))                   # sahi:  0.3185
print(ce(probs, target))                    # galat: softmax do baar lag gaya

Output

tensor(0.3185)
tensor(0.7450)

Error nahi aaya, bas loss 0.3185 se 0.7450 ho gaya. Andar softmax doosri baar laga: [0.6590, 0.2424, 0.0986] → [0.4484, 0.2956, 0.2560] — sab barabar ki taraf squash. Aur bura: probabilities 0 se 1 ke beech hain, to "logits" kabhi 1 se zyada farak nahi kar paate. 3 classes me best case [1, 0, 0] → softmax [0.5761, 0.2119, 0.2119] → loss kabhi 0.5514 se neeche nahi ja sakta; 10 classes me 1.4612 se neeche nahi. Model kabhi confident nahi ban paayega, gradients chhote, training dheemi ya atki. Silent bug.

NLLLoss — sirf log-probabilities

PyTorch · nn.NLLLoss ko sahi aur galat input

import torch
import torch.nn as nn

probs = torch.tensor([[0.7, 0.2, 0.1]])     # teacher ka Cat/Dog/Bird
target = torch.tensor([0])                  # Cat = class 0

nll = nn.NLLLoss()
print(nll(torch.log(probs), target))        # sahi: log-probabilities do
print(nll(probs, target))                   # galat: probabilities de diye -> -0.7 (negative loss!)

Output

tensor(0.3567)
tensor(-0.7000)

nn.NLLLoss khud koi log nahi lagata — bas target wali value utha ke minus lagata hai. Log-probs diye → −ln 0.7 = 0.3567 (sahi). Probabilities diye → −0.7 = negative loss (bakwaas; minimize karne par model probability ko infinity ki taraf dhakelega). Raw logits diye → normalisation hi nahi hua, wo bhi galat. Isliye NLLLoss ka jodidaar: model ke end me nn.LogSoftmax(dim=1). LogSoftmax + NLLLoss = CrossEntropyLoss (bina softmax ke logits par).

Target ki galtiyan aur unke error messages

PyTorch · CrossEntropyLoss ke target errors + fix

import torch
import torch.nn as nn

logits = torch.tensor([[2.0, 1.0, 0.1],
                       [0.5, 2.5, 0.3]])
ce = nn.CrossEntropyLoss()

wrong_targets = {
    "float (N,)  ": torch.tensor([0.0, 1.0]),      # dtype galat
    "long (N, 1) ": torch.tensor([[0], [1]]),      # shape galat
}
for name, t in wrong_targets.items():
    try:
        ce(logits, t)
    except RuntimeError as e:
        print(name, "->", e)

t = torch.tensor([[0.0], [1.0]])      # maan lo labels aise aaye: float, (N, 1)
t = t.squeeze(1).long()               # FIX: (N,) aur long
print(t, t.dtype, ce(logits, t))

try:
    nn.BCELoss()(torch.tensor([2.0, -1.0]), torch.tensor([1.0, 0.0]))   # logits de diye
except RuntimeError as e:
    print("BCELoss ->", e)

Output

float (N,)   -> expected target dtype to be Long or Byte, but got Float
long (N, 1)  -> 0D or 1D target tensor expected, multi-target not supported
tensor([0, 1]) torch.int64 tensor(0.3185)
BCELoss -> all elements of input should be between 0 and 1

Poora multi-class training loop

Same loop (Chapter 13 me detail), bas multi-class wale badlaav: last layer 3 logits deti hai, loss CrossEntropyLoss, target long class indices.

PyTorch · 3-class classifier with CrossEntropyLoss (synthetic data)

import torch
import torch.nn as nn
import torch.optim as optim

torch.manual_seed(0)
centers = torch.tensor([[0.0, 1.5], [1.5, -1.0], [-1.5, -1.0]])
y = torch.arange(3).repeat_interleave(50)        # 150 labels: 0,0,..,1,..,2  (long)
X = centers[y] + torch.randn(150, 2)             # har class ke points apne centre ke paas

model = nn.Sequential(nn.Linear(2, 16), nn.ReLU(), nn.Linear(16, 3))  # 3 logits, NO softmax
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.5)

for epoch in range(30):
    logits = model(X)              # (150, 3)
    loss = loss_fn(logits, y)      # target (150,) long
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if epoch % 10 == 0 or epoch == 29:
        print(f"epoch {epoch:2d}  loss {loss.item():.4f}")

with torch.no_grad():
    logits = model(X)
    preds = logits.argmax(dim=1)                 # sabse bada logit = predicted class
    print("accuracy:", round((preds == y).float().mean().item(), 4))
    torch.set_printoptions(sci_mode=False)       # e-04 jaisa notation band
    print(torch.softmax(logits[:2], dim=1))      # probabilities sirf dekhne ke liye

Output

epoch  0  loss 1.2316
epoch 10  loss 0.2730
epoch 20  loss 0.2339
epoch 29  loss 0.2226
accuracy: 0.9133
tensor([[0.5050, 0.0348, 0.4602],
        [0.9408, 0.0219, 0.0373]])

Teen baatein note karo: (1) model ke end me koi softmax nahi — nn.Linear(16, 3) seedha logits; (2) target y long (N,), logits (N, 3); (3) prediction ke liye logits.argmax(dim=1) — softmax monotonic hai (bada logit → badi probability), to argmax ke liye softmax ki zaroorat hi nahi. Softmax sirf tab jab probabilities dikhani hon.

Exam Radar

  • nn.CrossEntropyLoss: input raw logits (N, C) float; target class indices (N,) long; andar log_softmax + NLLLoss; softmax pehle mat lagao.
  • nn.NLLLoss: input log-probabilities (log_softmax output); target indices long.
  • Defaults: reduction='mean', ignore_index=-100, label_smoothing=0.0, weight=None.
  • Untrained C-class model ka CE ≈ ln C (10 classes → ≈ 2.30).
  • Prediction = logits.argmax(dim=1); probabilities = torch.softmax(logits, dim=1).
  • Binary ko 2-class CE se bhi kar sakte ho: nn.Linear(f, 2) + CrossEntropyLoss + long targets.

Galti mat karna

  • Model ke end me nn.Softmax + nn.CrossEntropyLoss = double softmax, silent bug, loss floor (3 classes: 0.5514).
  • Target float ya (N, 1) ya one-hot long — teeno error. Target = (N,) long indices.
  • NLLLoss ko probabilities ya logits dena — galat (negative ya bina-normalised loss).
  • softmax(dim=0) likh diya (batch ke across) — har example ki classes par normalise karna hai: dim=1.

Dimaag me aise baitha lo

CrossEntropyLoss = kachcha maal (logits) + roll number (long index). NLLLoss = pakka maal (log-probs). Softmax sirf dikhane ke liye.

11.20 Hinge loss — margin wala loss (SVM style)

Kahani

Board exam me passing marks 33 hain, par class teacher kehti hai "mujhe 40+ chahiye, tab tum safe ho". 40 se upar laaye — teacher kuch nahi kehti, bilkul chain (loss 0). 33 aur 40 ke beech — pass to ho, par "border pe ho, aur padho" (thoda loss). Fail — daant, aur jitne neeche utni zyada daant (loss seedha badhta hai). Wo 33 se 40 tak ka "safety buffer" hai margin, aur ye teacher hai hinge loss.

Hinge = max(0, 1 − y · ŷ),    y ∈ {−1, +1}
  • y = label, lekin 0/1 nahi — −1 / +1 (0/1 labels ho to y± = 2y − 1)
  • ŷ (ya s) = raw decision score — koi bhi real number, probability nahi, sigmoid nahi
  • y · ŷ = margin: positive = sahi side, negative = galat side, bada = zyada confident
  • y · ŷ ≥ 1 → loss 0 ("correct AND confident by margin ≥ 1 … good enough", slide 30)
  • y · ŷ < 1 → loss 1 − y · ŷ > 0 — galat side, ya sahi side par margin ke andar
  • Gradient w.r.t. ŷ: −y agar y·ŷ < 1, warna 0. y·ŷ = 1 par kink (non-differentiable)

Chalo numbers se samjhein

  1. y = +1, ŷ = 2.0: margin 2 → max(0, 1 − 2) = 0 (safe)
  2. y = +1, ŷ = 1.0: margin 1 → max(0, 0) = 0 (bilkul border par, phir bhi safe)
  3. y = +1, ŷ = 0.5: margin 0.5 → 1 − 0.5 = 0.5 (sahi, par margin ke andar)
  4. y = +1, ŷ = −1.0: margin −1 → 1 − (−1) = 2 (galat side)
  5. y = −1, ŷ = −3.0: margin 3 → 0
  6. y = −1, ŷ = 0.2: margin −0.2 → 1.2
  7. Average: (0 + 0 + 0.5 + 2 + 0 + 1.2)/6 = 3.7/6 = 0.6167

Hinge (mean) = 0.6167. Sirf woh examples loss dete hain jo margin ke andar ya galat side hain.

PyTorch · hinge loss from scratch

import torch

y = torch.tensor([1.0, 1.0, 1.0, 1.0, -1.0, -1.0])   # labels +1 / -1
s = torch.tensor([2.0, 1.0, 0.5, -1.0, -3.0, 0.2])   # raw scores (koi bhi real number)

margin = y * s
hinge = torch.clamp(1 - margin, min=0)      # max(0, 1 - y*s)
print(margin)
print(hinge)
print(hinge.mean())

Output

tensor([ 2.0000,  1.0000,  0.5000, -1.0000,  3.0000, -0.2000])
tensor([0.0000, 0.0000, 0.5000, 2.0000, 0.0000, 1.2000])
tensor(0.6167)
−2 −1 0 1 2 3 1 2 3 margin m = y · s loss m = 1 (margin) Hinge: max(0, 1 − m) Logistic/BCE: ln(1 + e^(−m)) hinge = 0: 'good enough'
Hinge (laal) m ≥ 1 hote hi bilkul 0 — model sahi hai aur margin se sahi hai, aur kuch seekhne ko nahi. Logistic/BCE (ink dashed) kabhi exactly 0 nahi hota — hamesha thoda aur confident hone ko dhakelta rehta hai. m < 0 matlab galat side.

Exam Radar

  • Hinge = max(0, 1 − y·ŷ); labels ±1; raw scores; zero loss when y·ŷ ≥ 1.
  • Use: SVMs, ranking; "margin maximiser". Non-differentiable at margin = 1. Gradient −y (inside margin) ya 0.
  • Confident galti ko linearly punish karta hai (CE: unbounded/log-wala, slide ki bhasha me "exponentially").

Dimaag me aise baitha lo

Hinge = "40+ laao to chain". Margin ≥ 1 → loss 0. Labels ±1, output raw score.

11.21 Master table, "kaunsa loss kab" aur aakhri checklist

Badi table: har loss ka poora biodata

LossFormulaTaskOutput activationPyTorch classInput expectsTarget expects
MSE (L2)(1/n)Σ(y − ŷ)²Regressionnone (linear)nn.MSELoss()float predictions, koi bhi realfloat, same shape
MAE (L1)(1/n)Σ|y − ŷ|Regression with outliersnone (linear)nn.L1Loss()float predictionsfloat, same shape
Huber / Smooth L1½r² if |r| ≤ δ, else δ(|r| − ½δ)Robust + smooth regressionnone (linear)nn.HuberLoss(delta=1.0), nn.SmoothL1Loss(beta=1.0)float predictionsfloat, same shape
Binary CE−[y log ŷ + (1 − y) log(1 − ŷ)]Binary (aur multi-label)sigmoid (model me)nn.BCELoss()probabilities [0, 1], e.g. (N, 1)float 0./1., same shape
BCE with logitssame, sigmoid andarBinary (aur multi-label)none (raw logit)nn.BCEWithLogitsLoss()raw logits, e.g. (N, 1)float 0./1., same shape
Categorical CE−Σk yk log ŷk = −log ŷcorrectMulti-class (ek label)none (softmax loss ke andar)nn.CrossEntropyLoss()raw logits (N, C)class indices (N,), long, 0 … C−1
NLL−log pcorrectMulti-classLogSoftmax (model me)nn.NLLLoss()log-probabilities (N, C)class indices (N,), long
Hingemax(0, 1 − y·ŷ)Binary, margin (SVM)none (raw score)manual torch.clamp(1 - y*s, min=0).mean(); multi-class nn.MultiMarginLoss()raw scoreslabels −1/+1

Teacher ki comparison table (slide 33)

DimensionMSEMAECross-EntropyHinge
Formula(1/n)Σ(y − ŷ)²(1/n)Σ|y − ŷ|−Σ y log ŷmax(0, 1 − yŷ)
Task typeRegressionRegressionClassificationClassification
Output layerLinearLinearSigmoid / SoftmaxRaw score
Label formatContinuous yContinuous yProbability / 1-hoty ∈ {−1, +1}
Outlier sensitivityHighLowMediumMedium
Differentiable?Yes (smooth)No at r = 0Yes (smooth)No at margin y·ŷ = 1
Gradient at errorLinear in residualConstant ±1ŷ − y (probabilistic)−y if y·ŷ < 1, else 0
Optimal predictionConditional meanConditional medianProbability of classMaximum margin
Penalises confident wrongs?QuadraticallyLinearly"Exponentially" (−log p → ∞)Linearly (margin)
Classic use caseLinear regression, CNNsRobust regressionNeural nets, LLMsSVMs, ranking

Kaunsa loss kab? (slide 34 ka decision tree)

Exam Radar — poore chapter ke "pakke" facts

  • Loss = ek example; cost = average. Loss scalar, ≥ 0, differentiable. Goal minimize.
  • MLE: likelihood = i.i.d. probabilities ka product; log se sum (underflow se bachao); minus se NLL; maximize likelihood ⟺ minimize NLL.
  • Coin 7/10 heads → MLE p = 0.7 (L = 0.0022236 vs 0.0009766 at 0.5).
  • Gaussian → MSE; Laplace → MAE; Bernoulli → BCE; Categorical → CCE.
  • MSE optimal = mean; MAE optimal = median. MSE outlier-sensitive; MAE robust.
  • ∂MSE/∂ŷ = (2/n)(ŷ − y); ∂MAE/∂ŷ = sign(ŷ − y)/n; ∂BCE/∂ŷ = (ŷ − y)/(ŷ(1 − ŷ)); sigmoid+BCE aur softmax+CE: ∂/∂z = ŷ − y.
  • Sigmoid + MSE: gradient me σ′(z) (max 0.25) → confident galti par vanishing → classification me CE.
  • BCE at ŷ = 0.5: 0.6931 (ln 2). CE untrained K-class: ≈ ln K (10 → 2.3026).
  • −ln 0.9 = 0.1054, −ln 0.5 = 0.6931, −ln 0.1 = 2.3026, −ln 0.01 = 4.6052, −ln 0.7 = 0.3567.
  • Example yaad: y = [3, −0.5, 2, 7], ŷ = [2.5, 0, 2, 8] → MSE 0.375, MAE 0.5, RMSE 0.6124, Huber(δ=1) 0.1875.
  • Logits [2, 1, 0.1], class 0 → softmax [0.6590, 0.2424, 0.0986] → CE 0.4170.
  • nn.CrossEntropyLoss = LogSoftmax + NLLLoss; logits (N, C) + long (N,). nn.BCEWithLogitsLoss = Sigmoid + BCELoss (stable). nn.BCELoss log ko −100 par clamp.
  • Default reduction='mean' sab losses me. nn.L1Loss = MAE. HuberLoss default δ = 1.0, SmoothL1Loss default β = 1.0.

Galti mat karna — master list

  • Softmax ke baad CrossEntropyLoss → double softmax, koi error nahi, loss floor (3 classes: 0.5514), slow/stuck training. Logits seedha do.
  • Galat target dtype: CrossEntropyLoss/NLLLoss ko long chahiye (float → "expected target dtype to be Long…"); BCELoss/BCEWithLogitsLoss ko float chahiye (long → BCELoss: "Found dtype Long but expected Float"; BCEWithLogitsLoss: "result type Float can't be cast to the desired output type Long").
  • BCELoss ko logits → "all elements of input should be between 0 and 1". BCEWithLogitsLoss ko probabilities (ya model me sigmoid + WithLogits) → double sigmoid, chupchaap galat (Lab 4).
  • Shape mismatch broadcasting: (N, 1) vs (N,) — MSELoss/L1Loss warning ke saath (N, N) bana ke galat loss (0.3144 vs 0.0700); BCE family ValueError. Fix .view(-1, 1) / .squeeze(1) (Lab 5).
  • CE target (N, 1) → "0D or 1D target tensor expected". CE ko (N,) chahiye, BCE ko (N, 1) jaisa output-shape — ulta yaad mat karna.
  • NLLLoss ko probabilities → negative loss bakwaas. Pehle log_softmax.
  • Regression output par sigmoid lagaya aur target 1 se bada → model kabhi pahunch nahi paayega.
  • Classification ke liye MSE → σ′ wala vanishing gradient, non-convex. CE use karo.
  • From-scratch BCE/CE me clamp/log_softmax bhoolna → log(0) = −inf → nan.
  • reduction='none' par seedha backward() → "grad can be implicitly created only for scalar outputs".
  • Argument order: loss_fn(prediction, target).

Dimaag me aise baitha lo

Number → MSE/MAE (linear output). Haan/na → BCEWithLogits (float target, same shape). Kai me se ek → CrossEntropy (logits + long index). Margin → Hinge. Aur sab ke peeche ek hi maa: MLE → NLL.

Chapter 12

Computational Graph, Chain Rule, Forward & Backward Pass — galti ka hisaab peeche tak

Gradient kyun · Derivative refresher · Computational graph · Local × Upstream · Chain rule (single + multivariable) · Gate patterns · Sigmoid neuron graph · Forward vs Backward pass · PyTorch autograd

12.1 Gradient kyun chahiye? — kaunsa knob, kis taraf, kitna

Kahani

Yaad hai apna chai wala? Uski chai me teen "knobs" hain: cheeni kitni, patti kitni, doodh kitna. Customer ek sip leta hai aur muh bana ke bolta hai: "Bhaiya, maza nahi aaya, 10 me se 4." Ye "kitna bura laga" wala number hi hamara loss hai.

Ab chai wale ke paas teen sawaal hain:

  • Kaunsa knob? — Galti cheeni ki hai, patti ki, ya doodh ki? (kis cheez ka asar sabse zyada hai)
  • Kis taraf? — Cheeni badhaun ya ghataun?
  • Kitna? — Ek chammach ya aadha chammach?

Agar wo andaaze se har knob ghumata rahe, to 100 customer kharab ho jayenge. Lekin agar koi use bata de: "cheeni ko thoda badhane se rating itni badhegi, patti badhane se itni ghategi" — to wo seedha sahi taraf, sahi matra me knob ghuma dega. Yahi "kis knob ko thoda hilane se loss kitna hilta hai" wali jaankari ka naam hai gradient.

Neural network me ye knobs hain weights (w) aur biases (b) — ek chhote network me 9, ek bade model me crore. Loss function (Chapter 11) batata hai "kitna galat". Training ka matlab hai: knobs ko aise ghumao ki loss kam ho. Aur har knob ke liye "kis taraf, kitna" ka jawab deta hai us knob ka partial derivative ∂L/∂w.

Definition

Gradient = loss ke saare partial derivatives ki list (vector), har parameter ke liye ek: ∇L = [∂L/∂w1, ∂L/∂w2, …, ∂L/∂b]. Har entry batati hai: "ye ek knob thoda sa badhao (baaki sab same rakho) to loss kitni rate se badlega".

Gradient ki ek entry teen sawaalon ka jawab ek saath deti hai:

SawaalGradient se jawabExample: ∂L/∂w = −4
Kaunsa knob zyada important?Jiska |∂L/∂w| bada, uska asar bada|−4| = 4, kaafi asar hai
Kis taraf ghumaun?Sign ke ulta: gradient negative → w badhao; positive → w ghatao−4 negative hai → w badhao
Kitna ghumaun?Step = learning rate × gradientη = 0.1 → step = 0.4
wnew = wold − η · ∂L∂w
  • wold = abhi ka weight (knob ki abhi ki position)
  • ∂L/∂w = gradient: w ko thoda badhane par loss kitni tezi se badhta hai (slope)
  • η (eta, kabhi α bhi likhte hain) = learning rate: ek baar me kitna bada kadam lena hai
  • Minus kyun? Gradient "uphill" (loss badhne wali) direction batata hai. Hume loss ghatana hai, isliye ulti taraf chalte hain. Isi liye naam hai gradient descent (utarna).

Ye update rule detail me Chapter 14 me aayega. Is chapter ka kaam hai: ye ∂L/∂w nikalte kaise hain — har weight ke liye, efficiently. Us tareeke ka naam hai backpropagation.

Chalo numbers se samjhein

Sawaal: Maan lo loss sirf ek knob pe depend karta hai: L(w) = (w − 3)2 + 1. Abhi w = 1 hai. Gradient se batao kis taraf jaana hai, aur η = 0.1 se ek step lo.

  1. Abhi ka loss: L(1) = (1 − 3)2 + 1 = 4 + 1 = 5
  2. Derivative (power rule + chain rule): dL/dw = 2(w − 3)
  3. w = 1 pe slope: 2(1 − 3) = −4 → negative hai, matlab w badhane se loss ghatega. To w badhao.
  4. Update: wnew = 1 − 0.1 × (−4) = 1 + 0.4 = 1.4
  5. Naya loss: L(1.4) = (1.4 − 3)2 + 1 = 2.56 + 1 = 3.56

Jawab: loss 5 → 3.56 ho gaya. Sirf slope ka sign aur size dekh ke sahi taraf chale. Agar w = 5 hota to slope = 2(5 − 3) = +4 hota → w ghatana padta.

w L(w) w badhao w ghatao slope = −4 slope = +4 minimum: slope = 0 w = 1 w = 3 w = 5
Slope (red tangent) ka sign batata hai kis taraf jaana hai: slope negative → right (w badhao), slope positive → left (w ghatao). Bottom pe slope 0 — wahi minimum hai.

Teacher ke Lecture 4 slides isi sawaal se shuru hote hain: "Why do we even care about the derivatives of the loss?" Jawab: derivatives batate hain ki parameters me chhota sa change karne par loss kitna badlega; optimization algorithm (jaise SGD) isi jaankari se better parameters dhoondhta hai; aur backpropagation algorithm ye derivatives compute karta hai. Slides me network ko aise likha hai: h1 = a[β0 + Ω0x], h2 = a[β1 + Ω1h1], h3 = a[β2 + Ω2h2], f = β3 + Ω3h3 (β = bias vectors, Ω = weight matrices, a[•] = activation). SGD update ke liye har layer k ke har βk aur Ωk ka derivative, batch ke har example i ke liye chahiye. Lakhon parameters × har step — isliye ek efficient tareeka chahiye. Wahi backprop hai.

Exam Radar

  • Gradient = vector of partial derivatives of the loss w.r.t. every parameter. Direction of steepest increase of loss; hum negative gradient ki taraf chalte hain.
  • Sign → direction; magnitude → kitna sensitive. Gradient 0 → flat point (minimum / maximum / saddle).
  • Backpropagation gradient compute karta hai; gradient descent (optimizer) us gradient se weights update karta hai. Do alag kaam.
  • Teacher ki slide notation: βk = bias vector, Ωk = weight matrix, ϕ = {β0, Ω0, …, β3, Ω3} = saare parameters (Prince UDL book wali notation).

Galti mat karna

  • "Backprop weights update karta hai" — MCQ me ye trap ho sakta hai. Strictly: backprop sirf ∂L/∂w nikalta hai; update optimizer.step() (gradient descent) karta hai. (Teacher ki slide loosely kehti hai "backprop updates weights to minimize the loss" — matlab poora training process; agar option me "computes gradients" alag se diya ho, wahi zyada sahi hai.)
  • Gradient positive hai to weight ghatega (minus sign ki wajah se). Ulta mat padhna.

Dimaag me aise baitha lo

Gradient = knob ka GPS: sign bole "kidhar", size bole "kitna zor se". Hum hamesha ulti taraf chalte hain, kyunki pahaad se neeche utarna hai.

12.2 Derivative refresher — "thoda sa hilao, kitna hila?"

Kahani

Auto-rickshaw ka meter socho. Tum 1 km aur chale to meter ₹15 aur badha. To "fare ka distance ke saath derivative" = ₹15 per km. Derivative bas itna hai: input ko thoda sa badhao, output kitne guna badha? Rate of change. Speedometer bhi yahi hai — distance ka time ke saath derivative.

Lecture slide ki line yaad rakho: "A derivative tells us: if I change the input slightly, how much does the output change?" Isko formula me likhte hain:

dfdx ≈ f(x + h) − f(x)h   (h bahut chhota, jaise 0.0001)
  • h = input ko kitna hilaya (nudge)
  • f(x + h) − f(x) = output kitna hila
  • Dono ka ratio = "per unit input change, output change" = slope. h → 0 karne par ye exact derivative ban jata hai.
  • Central difference (zyada accurate): [f(x + h) − f(x − h)] / 2h. Isi se "gradient checking" karte hain — backprop ka answer sahi hai ya nahi, ye verify karne ke liye.

Chalo numbers se samjhein

Sawaal: f(x) = 3x2 − 4x + 5. (Ye wahi famous example hai jo Karpathy ke micrograd lecture me hai; teacher ki slide ka "x = 2/3 pe derivative ≈ 0, wahi minimum" wala point isi tarah ka function hai.) x = 3 pe slope kya hai? Aur minimum kahan hai?

  1. Value: f(3) = 3(9) − 12 + 5 = 27 − 12 + 5 = 20
  2. Numerically hilao, h = 0.0001: f(3.0001) = 20.00140003 → (20.00140003 − 20) / 0.0001 = 14.0003
  3. Formula se (power rule: d(xn)/dx = nxn−1): f′(x) = 6x − 4 → f′(3) = 18 − 4 = 14. Numeric 14.0003 ≈ 14. Match!
  4. Minimum kahan? Jahan slope 0: 6x − 4 = 0 → x = 4/6 = 2/3 ≈ 0.6667

Jawab: x = 3 pe slope 14 (yahan x badhao to f 14 guna tezi se badhega). x = 2/3 pe slope 0 → wahi minimum. Bilkul slide wali baat: "at x = 2/3 = 0.6667 the derivative is ≈ 0; this is where the function has a minimum."

Python · numerical derivative (Karpathy / Lecture 4 style)

# numerical derivative: "thoda hilao, dekho kitna hila"
def f(x):
    return 3*x**2 - 4*x + 5

h = 0.0001
x = 3.0
print("f(3)          =", f(x))
print("slope (numeric)=", (f(x + h) - f(x)) / h)          # one-sided
print("slope (central)=", (f(x + h) - f(x - h)) / (2*h))  # zyada accurate
print("slope at 2/3  =", (f(2/3 + h) - f(2/3 - h)) / (2*h))

Output

f(3)          = 20.0
slope (numeric)= 14.000300000063248
slope (central)= 14.000000000038426
slope at 2/3  = 0.0

Partial derivative — baaki sab ko freeze karo

Jab function me kai variables hon, jaise f(a, b) = a · b, to ∂f/∂a (padho "del f by del a") ka matlab: sirf a ko hilao, b ko constant maan lo. Yahan ∂f/∂a = b aur ∂f/∂b = a. Symbol "∂" (curly d) bas yaad dilata hai ki aur bhi variables hain jo freeze kiye gaye hain.

Backprop me hume sirf chhote-chhote operations ke derivatives yaad hone chahiye. Ye rahi poori "local derivative" ki potli:

OperationForwardLocal derivative
Addz = x + y∂z/∂x = 1, ∂z/∂y = 1
Multiplyz = x · y∂z/∂x = y, ∂z/∂y = x
Add constantz = x + c1
Scale by constantz = axa
Squarez = x22x
Exponentialz = exex
Reciprocalz = 1/x−1/x2
Logz = ln x1/x
Sigmoidz = σ(x)σ(x)(1 − σ(x)), max 0.25 at x = 0
Tanhz = tanh(x)1 − tanh2(x), max 1 at x = 0
ReLUz = max(0, x)1 if x > 0, else 0
Maxz = max(x, y)1 for the bigger one, 0 for the other
Half squared errorL = ½(ŷ − y)2∂L/∂ŷ = ŷ − y

Exam Radar

  • Derivative = rate of change = slope of tangent. Slope 0 → minimum/maximum/saddle point.
  • Numerical gradient (finite difference) ek check ke liye accha hai, lekin training ke liye slow hai: har parameter ke liye alag forward pass chahiye. Backprop saare gradients ek hi backward pass me deta hai.
  • Central difference error ~h2, one-sided error ~h → central zyada accurate.
  • σ′ ka max = 0.25 (at 0); tanh′ ka max = 1 (at 0); ReLU′ = 1 for positive input. Ye teen numbers vanishing gradient (13.6) ki jaan hain.

Dimaag me aise baitha lo

Derivative = "kitna hilaya" ke upar "kitna hila". Partial derivative = baaki sabko pause karke sirf ek ko hilao.

12.3 Computational graph — factory ki assembly line

Kahani

Ek biscuit factory socho. Assembly line pe har worker ka ek hi chhota kaam hai: pehla aata aur cheeni milata hai, doosra ghee daalta hai, teesra shape kaatta hai, chautha bake karta hai, paanchva pack karta hai. Box line pe aage badhta jaata hai — ye hai forward direction.

Ab customer complaint karta hai: "biscuit bahut meetha hai!" Manager complaint ko ulti taraf bhejta hai: packing wale se baker, baker se shape wala, ... aur har worker sirf ye sochta hai: "mere chhote se kaam ne is complaint me kitna hissa dala?" Kisi worker ko poori factory samajhne ki zaroorat nahi — sirf apna kaam. Relay race jaisa: baton ek haath se doosre haath tak jaata hai.

Computational graph yahi factory line hai, aur backprop yahi complaint ko peeche bhejne ka tareeka.

Definition

Computational graph = kisi bhi calculation ka naksha (graph), jisme nodes = operations (+, ×, σ, exp, ...) aur edges = values (numbers/tensors) jo ek operation se nikal ke agle operation me jaati hain. Inputs aur parameters (x, w, b) graph ke shuruaati "leaf" points hote hain, aur end me ek single number — loss L. Ye ek DAG (Directed Acyclic Graph) hai: arrows ek hi direction me, koi loop nahi.

Teacher ki slide ka example: f = xy + zk. Variables hain x, y, z, k. Hum do intermediate variables banate hain: r = x · y aur s = z · k (dono × operator se), phir final node f = r + s (+ operator se).

x y z k × × r = xy s = zk + f node = operation edge = value
f = xy + zk ka computational graph. Circle = operation (node), arrow = value jo bahar nikal ke aage jaati hai (edge). r aur s intermediate values hain.

Itna tod-phod kyun? Kyunki f ka seedha derivative nikalna mushkil lag sakta hai, lekin har chhote node ka derivative bachpan wala hai: × ka derivative "doosra input", + ka derivative "1". Graph me har node sirf apna chhota sa local derivative jaanta hai, aur chain rule (12.6) un sabko jod deta hai. Neural network bhi bas ek bahut bada computational graph hai: matmul → add bias → activation → matmul → ... → loss.

Exam Radar

  • Nodes = operations, edges = values (tensors). Graph = DAG (no cycles).
  • Forward pass graph ko left → right (inputs → loss) chalata hai; backward pass right → left (loss → inputs).
  • PyTorch ka graph dynamic hai ("define-by-run"): har forward pass me naya graph banta hai, isliye Python ka if/for model ke andar chal jata hai. Purana TensorFlow 1.x static graph ("define-and-run") use karta tha.
  • Course orientation slide: "We don't just call libraries; we implement the computational graph to verify our mathematical intuition."

Dimaag me aise baitha lo

Graph = assembly line. Har worker (node) ek chhota kaam, ek chhota derivative. Complaint (gradient) ulti line me travel karti hai.

12.4 Classic worked graph: f = (x + y) · z

Kahani

Cricket ka hisaab: do openers ne runs banaye x aur y; dono ka total q = x + y. Phir ek "multiplier" z lagta hai (jaise koi bonus/penalty factor). Final score f = q · z. Sawaal: agar x ek run zyada banata, to final score kitna badalta? Yahi backward pass batayega.

Ye cs231n (Stanford) ka classic example hai: f(x, y, z) = (x + y) · z at x = −2, y = 5, z = −4. Graph me do nodes: ek add node jo q = x + y banata hai, aur ek multiply node jo f = q · z banata hai.

Chalo numbers se samjhein

Forward pass (left se right, values nikalo aur yaad rakho):

  1. Add node: q = x + y = −2 + 5 = 3
  2. Multiply node: f = q · z = 3 × (−4) = −12

Backward pass (right se left, gradients nikalo):

  1. Shuruaat hamesha 1 se: ∂f/∂f = 1 (f ko 1 badhao to f 1 badhega — obviously)
  2. Multiply node ke local derivatives: ∂f/∂z = q = 3 aur ∂f/∂q = z = −4
  3. Add node ke local derivatives: ∂q/∂x = 1, ∂q/∂y = 1
  4. Chain rule se x tak: ∂f/∂x = ∂f/∂q × ∂q/∂x = (−4) × 1 = −4
  5. Usi tarah y: ∂f/∂y = (−4) × 1 = −4

Jawab: ∂f/∂x = −4, ∂f/∂y = −4, ∂f/∂z = 3. Matlab: x ko thoda badhao to f 4 guna tezi se ghatega; z ko badhao to f 3 guna tezi se badhega.

Check by nudging: x = −1.999 (0.001 badhaya) → q = 3.001 → f = 3.001 × (−4) = −12.004. Change = −0.004 = −4 × 0.001. Bilkul gradient jaisa!

x y z + × f −2 −4 5 −4 q = 3 −4 −4 3 −12 1 ink = forward value red = gradient ∂f/∂(that value)
Har edge pe upar ink me forward value, neeche red me us value ka gradient. Output pe gradient 1 se shuru; multiply node ne inputs ko "swap" karke bheja (q ko z = −4, z ko q = 3); add node ne −4 dono taraf as-it-is baant diya.

Galti mat karna

  • Backward pass ki shuruaat output pe gradient 1 se hoti hai (∂f/∂f = 1), 0 se nahi.
  • Multiply node pe gradient "apna hi input" nahi, doosre input ka value hota hai: ∂f/∂z = q (z nahi).
  • Forward values yaad rakhna zaroori hai — multiply node ko backward me q aur z dono ki value chahiye. Isliye forward pass values cache karta hai.

12.5 Local gradient × Upstream gradient = Downstream gradient

Kahani

Shaadi me khaana pass karne ki line socho. Har insaan ke paas sirf ek chhoti si jaankari hai: "main jo plate aage bhejta hoon, wo mere haath me aayi plate ka kitna guna hai" (local rule). Backward me jab "complaint ka number" peeche aata hai, har insaan bas itna karta hai: aaya hua number × apna local rule, aur peeche pass. Kisi ko poori line samajhne ki zaroorat nahi.

Graph ke kisi bhi node ko dekho: inputs x, y aate hain, output z = g(x, y) jaata hai. Backward pass me ye node teen cheezein handle karta hai:

∂L∂x = ∂L∂z × ∂z∂x     i.e.   downstream = upstream × local
  • ∂L/∂z = upstream: z ka loss pe asar (peeche ki taraf aa raha hai)
  • ∂z/∂x = local: x ka z pe asar (node ka apna formula)
  • ∂L/∂x = downstream: x ka loss pe asar = dono asar ka product. Ye chain rule hi hai, bas har node pe ek-ek kadam.
node g z = g(x, y) x y z upstream ∂L/∂z downstream ∂L/∂x = ∂L/∂z · ∂z/∂x downstream ∂L/∂y = ∂L/∂z · ∂z/∂y local: ∂z/∂x, ∂z/∂y
Har node ek chhoti factory: aage se upstream gradient aaya, apne local gradient se multiply kiya, peeche downstream bhej diya. Poora backprop = ye hi chhota sa kaam, har node pe, ulte order me.

12.4 ke graph me dekho: add node pe upstream = ∂f/∂q = −4, local = ∂q/∂x = 1, to downstream = −4 × 1 = −4. Multiply node pe upstream = 1, local (for z) = q = 3, to downstream = 3.

Teacher ki slide ka example: L = ((a · b) + c) · f

Lecture 4 slide 7–9 me ye graph hai: a, b, c, f inputs, L final output ("think: Loss"). Slide ke answers: ∂L/∂a = 6.0, ∂L/∂b = −4.0, ∂L/∂c = −2.0, ∂L/∂f = 4.0. Ye chaar gradients sirf ek hi set of values se aate hain: a = 2, b = −3, c = 10, f = −2 (Karpathy micrograd wale same numbers). Chalo khud nikaalte hain.

Chalo numbers se samjhein

Intermediate names: e = a · b, d = e + c, L = d · f.

Forward:

  1. e = 2 × (−3) = −6
  2. d = −6 + 10 = 4
  3. L = 4 × (−2) = −8

Backward (upstream × local, har node pe):

  1. Start: ∂L/∂L = 1
  2. Multiply node L = d · f: ∂L/∂f = 1 × d = 4, ∂L/∂d = 1 × f = −2
  3. Add node d = e + c (local = 1): ∂L/∂c = −2 × 1 = −2, ∂L/∂e = −2 × 1 = −2
  4. Multiply node e = a · b: ∂L/∂a = −2 × b = −2 × (−3) = 6, ∂L/∂b = −2 × a = −2 × 2 = −4

Jawab: ∂L/∂a = 6, ∂L/∂b = −4, ∂L/∂c = −2, ∂L/∂f = 4 — slide se exact match.

Slide 9 ka matlab: "a ko 1 badhao to L lagbhag 6 badhega; b ko 1 badhao to L 4 ghatega" (chhote change ke liye linear andaaza). L ghatana ho to har input ko uske gradient ki ulti taraf hilao: a ghatao, b badhao, c badhao, f ghatao. Step 0.01 lo:

a = 2 − 0.01(6) = 1.94   b = −3 − 0.01(−4) = −2.96   c = 10 − 0.01(−2) = 10.02   f = −2 − 0.01(4) = −2.04
L = (1.94 × (−2.96) + 10.02) × (−2.04) = (−5.7424 + 10.02) × (−2.04) = 4.2776 × (−2.04)
= −8.7263   (pehle −8 tha → L ghat gaya)
a b c f × + × L 26 −3−4 e = −6−2 10−2 d = 4−2 −24 −81
Teacher ka slide-7 graph, ab bhara hua: ink = forward value, red = ∂L/∂(us value). Multiply nodes ne gradient ko "doosre input" se scale kiya; add node ne −2 dono taraf barabar baanta.

PyTorch · slide example verify

import torch
# Teacher ka slide example: L = ((a*b) + c) * f
a = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(-3.0, requires_grad=True)
c = torch.tensor(10.0, requires_grad=True)
f = torch.tensor(-2.0, requires_grad=True)

e = a * b          # -6
d = e + c          #  4
L = d * f          # -8
L.backward()       # poora backward pass ek line me
print("L =", L.item())
print("dL/da =", a.grad.item(), "| dL/db =", b.grad.item())
print("dL/dc =", c.grad.item(), "| dL/df =", f.grad.item())

Output

L = -8.0
dL/da = 6.0 | dL/db = -4.0
dL/dc = -2.0 | dL/df = 4.0

Exam Radar

  • Downstream gradient = upstream gradient × local gradient. Har node sirf local computation karta hai — isi wajah se backprop modular aur fast hai.
  • Gradient ka matlab (slide 9): "∂L/∂a = 6 → a ko 1 badhao to L ~6 badhega". Negative gradient → badhane se L ghatega.
  • L kam karne ke liye: har variable = variable − (step × gradient).

Dimaag me aise baitha lo

"Aaya hua × apna = aage bhejo." Upstream × local = downstream. Bas yahi ek line poora backprop hai.

12.6 Chain rule (single variable) — gears aur currency conversion

Kahani

Tum US trip se lautkar dollars ko yen me badalna chahte ho, lekin counter pe seedha rate nahi hai. Maan lo rate hai: 1 dollar = 80 rupaye, aur 1 rupaya = 2 yen. To 1 dollar = 80 × 2 = 160 yen. Rates multiply hote hain.

Cycle ke gears bhi aise hi: pedal wala gear 1 ghoomta hai to beech wala 3 baar, aur beech wala 1 ghoome to pichhla pahiya 4 baar → pedal ke 1 chakkar pe pahiya 3 × 4 = 12 chakkar. Chain rule ka poora funda yahi hai: ek ke baad ek jude hue badlaav ki rates ko multiply karo.

agar y = f(u) aur u = g(x)  ⇒  dydx = dydu · dudx
  • du/dx = x badalne se u kitni rate se badla (pehla gear / dollar → rupee rate)
  • dy/du = u badalne se y kitni rate se badla (doosra gear / rupee → yen rate)
  • Lambi chain ho to: dL/dx = dL/dun · dun/dun−1 · … · du1/dx — har link ka product. Neural network me ye chain layer-dar-layer lambi hoti hai.
  • Fraction jaisa dikhta hai: du "cancel" hota hua lagta hai. Yaad rakhne ki trick ke liye ye theek hai.
USD (x) INR (u) JPY (y) × 80 × 2 du/dx = 80 dy/du = 2 dy/dx = 80 × 2 = 160
Chain rule = conversion rates ka product. (Rates sirf samjhane ke liye maan liye hain.)

Chalo numbers se samjhein

Sawaal: y = (3x + 1)2. x = 2 pe dy/dx nikalo — chain rule se.

  1. Andar wala tukda naam do: u = 3x + 1 = 3(2) + 1 = 7, phir y = u2 = 49
  2. Bahar wala link: dy/du = 2u = 2 × 7 = 14
  3. Andar wala link: du/dx = 3
  4. Multiply: dy/dx = 14 × 3 = 42
  5. Nudge se check (h = 0.001): (3 × 2.001 + 1)2 = 7.0032 = 49.042009 → (49.042009 − 49) / 0.001 = 42.009 ≈ 42

Jawab: dy/dx = 42 at x = 2.

Neural network wala version: ek neuron ka output ŷ = σ(z), z = wx + b, loss L = ½(ŷ − y)2. To w se L tak ki chain: w → z → ŷ → L, aur

∂L∂w = ∂L∂ŷ · ∂ŷ∂z · ∂z∂w = (ŷ − y) · σ(z)(1 − σ(z)) · x
  • (ŷ − y) = "loss output ko kaise mehsoos karta hai" (½ aur square ka 2 cancel)
  • σ(z)(1 − σ(z)) = "output sum ko kaise mehsoos karta hai" (sigmoid ka slope)
  • x = "sum weight ko kaise mehsoos karta hai" (weight ka partner input)

Ye exactly Lab 2 ka "manual chain rule vs autograd" wala calculation hai: x = 2, w = 1, b = 0, target 0 → z = 2, ŷ = 0.880797, σ′ = 0.104994, ∂L/∂w = 0.880797 × 0.104994 × 2 = 0.184956, aur α = 0.5 se wnew = 1 − 0.5 × 0.184956 = 0.907522.

Dimaag me aise baitha lo

Chain rule = conversion rates ka product. Jitni lambi chain, utne factors multiply — aur yahi baad me vanishing/exploding ka raaz banega.

12.7 Multivariable chain rule — saare raaston ka total

Kahani

Chai wala chai ka daam p badhata hai. Profit pe asar do raaston se aata hai: (1) har cup pe margin badha (accha), (2) customer thode kam aaye (bura). Total asar = raasta 1 ka asar + raasta 2 ka asar. Ek raasta ignore kiya to hisaab galat. Jab ek variable kai raaston se output tak pahunchta hai, har raaste ka product nikalo aur sabko JODO.

agar f = f(u, v),  u = u(x),  v = v(x)  ⇒  dfdx = ∂f∂ududx + ∂f∂vdvdx
  • Pehla term = raasta 1 (x → u → f) ke links ka product
  • Doosra term = raasta 2 (x → v → f) ke links ka product
  • General rule: ∂L/∂x = Σ (over all paths from x to L) ∏ (local derivatives along that path)
  • Neural net me: ek hidden neuron kai output neurons ko feed karta hai → uska gradient = sab outputs se aaye gradients ka sum. Yahi WTδ wale formula me "sum" ban ke aata hai (13.4).

Chalo numbers se samjhein

Sawaal: x = 2. Do branches: u = x2, v = 3x, aur f = u · v. df/dx nikalo.

  1. Forward: u = 4, v = 6, f = 4 × 6 = 24
  2. Multiply node ke local gradients: ∂f/∂u = v = 6, ∂f/∂v = u = 4
  3. Raasta 1 (via u): ∂f/∂u × du/dx = 6 × 2x = 6 × 4 = 24
  4. Raasta 2 (via v): ∂f/∂v × dv/dx = 4 × 3 = 12
  5. Jodo: df/dx = 24 + 12 = 36
  6. Check: f = x2 · 3x = 3x3 → f′ = 9x2 = 9 × 4 = 36. Match!

Jawab: df/dx = 36. Agar sirf ek raasta lete to 24 ya 12 aata — dono galat.

x ( )² ×3 × f 2 24 + 12 = 36 6 × 4 = 24 4 × 3 = 12 u = 4 ∂f/∂u = v = 6 v = 6 ∂f/∂v = u = 4 24 1 local 2x = 4 local 3
x do raaston se f tak jaata hai. Har raaste ka product (24 aur 12) nikal ke JODA → 36. Jahan graph branch hota hai, wahan backward me gradients add hote hain.

Galti mat karna

  • Ek path pe links ko multiply; alag-alag paths ko add. Ulta karna sabse common galti hai.
  • Agar ek weight/neuron ka output do jagah use hua, to dono jagah se aaye gradients jodne hain — ek bhool gaye to gradient adhoora.

Dimaag me aise baitha lo

"Raaste ke andar guna, raaston ke beech jod." (Along a path multiply, across paths add.)

12.8 Gate patterns — add = distributor, multiply = swapper, max = router, copy = adder

Kahani

Factory me chaar type ke worker hain, aur complaint (gradient) aane par har type ka reaction fix hai:

  • Add worker = mithai baantne wala: jo aaya, sabko barabar poora de deta hai (distributor).
  • Multiply worker = jodi wala: apne partner ki value se scale karke deta hai — x ko y milta hai, y ko x (swapper).
  • Max worker = traffic police: poora gradient sirf us raaste ko bhejta hai jo forward me jeeta tha; baaki ko 0 (router).
  • Copy/branch = ek hi cheez do jagah gayi: dono jagah se aayi complaints jud jaati hain (adder).
GateForwardBackward (upstream = g)Yaad rakhne ka naam
Addz = x + y∂L/∂x = g, ∂L/∂y = gDistributor — same gradient sabko
Multiplyz = x · y∂L/∂x = g · y, ∂L/∂y = g · xSwapper — doosre input se scale
Maxz = max(x, y)bade input ko g, chhote ko 0Router — sirf winner ko
Copy / branchx do jagah use∂L/∂x = g1 + g2Adder — gradients jodo
ReLUmax(0, x)g if x > 0 else 0Max gate with 0 — "gate band ya khula"

Chalo numbers se samjhein

Sawaal (cs231n ka example): f = 2 · (x · y + max(z, w)) at x = 3, y = −4, z = 2, w = −1. Saare gradients nikalo.

Forward:

  1. x · y = 3 × (−4) = −12
  2. max(2, −1) = 2 (z jeeta)
  3. sum = −12 + 2 = −10
  4. f = 2 × (−10) = −20

Backward:

  1. ×2 gate (scale by constant): ∂f/∂sum = 1 × 2 = 2
  2. Add = distributor: ∂f/∂(xy) = 2, ∂f/∂max = 2
  3. Multiply = swapper: ∂f/∂x = 2 × y = 2 × (−4) = −8, ∂f/∂y = 2 × x = 2 × 3 = 6
  4. Max = router: ∂f/∂z = 2 (winner), ∂f/∂w = 0 (loser)

Jawab: ∂f/∂x = −8, ∂f/∂y = 6, ∂f/∂z = 2, ∂f/∂w = 0.

x y z w × max + ×2 f 3−8 −46 22 −10 −122 22 −102 −201 add: 2 dono ko mul: partner se scale max: sirf winner (z) ko
Teeno gate patterns ek graph me. w forward me haar gaya (max me), isliye uska gradient 0 — dashed raasta band.

PyTorch · gate patterns verify

import torch
# f = 2 * (x*y + max(z, w))  -> add, multiply, max teeno gates ek saath
x = torch.tensor(3.0, requires_grad=True)
y = torch.tensor(-4.0, requires_grad=True)
z = torch.tensor(2.0, requires_grad=True)
w = torch.tensor(-1.0, requires_grad=True)

f = 2 * (x * y + torch.max(z, w))   # torch.max(a, b) = element-wise max
f.backward()
print("f =", f.item())
print("x.grad =", x.grad.item(), "| y.grad =", y.grad.item())
print("z.grad =", z.grad.item(), "| w.grad =", w.grad.item())

# copy gate: same a do jagah use hua -> gradients ADD
a = torch.tensor(3.0, requires_grad=True)
g = a * a            # a ki do copies multiply gate me gayi
g.backward()
print("d(a*a)/da =", a.grad.item())   # 3 + 3 = 6 = 2a

Output

f = -20.0
x.grad = -8.0 | y.grad = 6.0
z.grad = 2.0 | w.grad = 0.0
d(a*a)/da = 6.0

Multiply gate ka ek gehra sabak: x ka gradient = upstream × y. Agar ek input bahut bada ho (jaise unscaled feature "area = 2000"), to doosre (weight) ka gradient bhi bahut bada ho jayega. Neuron me z = w · x, to ∂L/∂w ∝ x. Isi liye Lab 5 me StandardScaler lagate hain — warna bade features wale weights ke gradients bahut bade, chhote wale ke bahut chhote, aur training ladkhadati hai. (Teacher ki slide "Observation 1" bhi yahi kehti hai: weight ka asar source activation se amplify ya attenuate hota hai.)

Exam Radar

  • Add gate gradient ko unchanged sab inputs me copy karta hai (local gradient 1). Isi wajah se residual connection (x + F(x)) gradient ka "highway" banta hai — Chapter 25.
  • Multiply gate: gradient = upstream × other input. Bade inputs → bade gradients → feature scaling zaroori.
  • Max gate / ReLU / max-pooling: gradient sirf max wale element ko jaata hai, baaki ko 0.
  • Branch (fan-out) pe gradients add hote hain. PyTorch me .grad accumulate hone ka bhi yahi logic hai.

Dimaag me aise baitha lo

Add baante, Multiply adla-badli kare, Max sirf jeetne wale ko de, Copy sab jod de.

12.9 Worked sigmoid neuron graph — f = σ(w₀x₀ + w₁x₁ + w₂)

Kahani

Ab hamari factory ek asli neuron banati hai. Line pe workers: do multiply wale (w0x0, w1x1), do add wale (jodna + bias), aur end me "sigmoid packing machine". Sigmoid machine ko andar se kholo to wo bhi chhote workers ki line hai: ×(−1), exp, +1, 1/x. Hum pehle har chhote worker ke saath backward karenge, phir dikhayenge ki poori sigmoid machine ka ek shortcut hai.

Ye cs231n ka famous example hai. Neuron: f(w, x) = σ(w0x0 + w1x1 + w2), jahan w2 bias ka kaam karta hai. Values: w = [2, −3, −3], x = [−1, −2]. Sigmoid ko todke likho: σ(z) = 1 / (1 + e−z) → gates: ×(−1) → exp → +1 → 1/x.

Chalo numbers se samjhein

Forward pass (har gate ki output value):

  1. w0x0 = 2 × (−1) = −2, w1x1 = (−3) × (−2) = 6
  2. −2 + 6 = 4, phir bias: z = 4 + (−3) = 1
  3. ×(−1): −1   exp: e−1 = 0.3679   +1: 1.3679   1/x: 1/1.3679 = 0.7311

Output f ≈ 0.73.

Backward pass (upstream × local, right se left):

  1. Start: ∂f/∂f = 1
  2. 1/x gate, local = −1/x2 at x = 1.3679: 1 × (−1/1.36792) = 1 × (−0.5344) = −0.5344
  3. +1 gate (local 1): −0.5344 × 1 = −0.5344
  4. exp gate, local = ex at x = −1, i.e. 0.3679: −0.5344 × 0.3679 = −0.1966
  5. ×(−1) gate (local −1): −0.1966 × (−1) = 0.1966 ← ye ∂f/∂z hai
  6. Add gates (distributor): w2, w0x0, w1x1 sabko 0.1966. So ∂f/∂w2 = 0.1966
  7. Multiply gates (swapper): ∂f/∂w0 = 0.1966 × x0 = 0.1966 × (−1) = −0.1966, ∂f/∂x0 = 0.1966 × w0 = 0.1966 × 2 = 0.3932
  8. ∂f/∂w1 = 0.1966 × x1 = 0.1966 × (−2) = −0.3932, ∂f/∂x1 = 0.1966 × w1 = 0.1966 × (−3) = −0.5898

Jawab: f ≈ 0.7311; ∂f/∂w = [−0.1966, −0.3932, 0.1966] ≈ [−0.20, −0.39, 0.20]; ∂f/∂x = [0.3932, −0.5898] ≈ [0.39, −0.59].

w0 = 2−0.20 x0 = −10.39 w1 = −3−0.39 x1 = −2−0.59 w2 = −30.20 × × + + ×−1 exp +1 1/x f −20.20 60.20 40.20 10.20 −1−0.20 0.37−0.53 1.37−0.53 0.731.00 ink = gate ki output value red = ∂f / ∂(us output) ye poora dabba = σ(z); shortcut: σ(1 − σ) = 0.73 × 0.27 = 0.20
Sigmoid neuron ka circuit. Orange dabbe ke chaar gates ka combined local gradient = σ(z)(1 − σ(z)) = 0.1966 — ek hi step me, bina chaar steps ke.

Shortcut: σ′(z) = σ(z)(1 − σ(z))

Chaar gates ka kaam ek line me ho sakta hai, kyunki sigmoid ka derivative khud sigmoid se likha ja sakta hai (derivation Chapter 9 me hai): σ′(z) = σ(z)(1 − σ(z)). Yahan: 0.7311 × (1 − 0.7311) = 0.7311 × 0.2689 = 0.1966 — bilkul wahi jo chaar steps se aaya tha! Aur forward me σ(z) to nikal hi chuke hain, to backward me exp dobara calculate karne ki zaroorat bhi nahi — sirf stored output se kaam ho gaya.

Sabak: graph ko kitna tod ke banana hai, ye hamari choice hai. Jis tukde ka local derivative simple ho, use ek "bada gate" maan lo. PyTorch ka torch.sigmoid bhi ek hi node (SigmoidBackward0) hai jo backward me σ(1 − σ) use karta hai.

PyTorch · sigmoid neuron verify

import torch

# sigmoid neuron: f = sigmoid(w0*x0 + w1*x1 + w2)
w = torch.tensor([2.0, -3.0, -3.0], requires_grad=True)   # w2 = bias
x = torch.tensor([-1.0, -2.0], requires_grad=True)

z = w[0]*x[0] + w[1]*x[1] + w[2]     # dot product + bias = 1.0
f = torch.sigmoid(z)                   # 0.7311
print("z =", z.item(), "| f =", round(f.item(), 4))

f.backward()
print("df/dw =", w.grad)               # [x0, x1, 1] * sigma'(z)
print("df/dx =", x.grad)               # [w0, w1] * sigma'(z)

# shortcut check: sigma'(z) = f*(1-f)
s = f.item()
print("sigma'(z) = f(1-f) =", round(s*(1-s), 4))

Output

z = 1.0 | f = 0.7311
df/dw = tensor([-0.1966, -0.3932,  0.1966])
df/dx = tensor([ 0.3932, -0.5898])
sigma'(z) = f(1-f) = 0.1966

Exam Radar

  • Output ≈ 0.73 (σ(1) = 0.7311). ∂f/∂z = 0.1966 ≈ 0.20. dw = [−0.20, −0.39, 0.20], dx = [0.39, −0.59].
  • Weight ka gradient = (∂f/∂z) × uska input; input ka gradient = (∂f/∂z) × uska weight. Bias ka gradient = ∂f/∂z (kyunki ∂z/∂b = 1).
  • σ′(z) = σ(z)(1 − σ(z)) — sirf output se nikal jata hai, isliye sigmoid backward sasta hai. Max value 0.25.
  • Gates ko group karke "bada gate" banana allowed hai, agar uska local derivative pata ho.

Galti mat karna

  • σ′ ko sum z pe evaluate karna hai, output pe nahi: σ′(z) = ŷ(1 − ŷ). Agar galti se σ′(ŷ) = σ(ŷ)(1 − σ(ŷ)) likh diya to answer galat. (Teacher ki worksheet bhi warn karti hai: "σ′ is evaluated at the SUM, not at the output.")
  • Bias ka gradient "0" nahi hota — uska local partner 1 hai, to gradient = ∂f/∂z.

Dimaag me aise baitha lo

Sigmoid ka backward: "output × (1 − output)". 0.73 × 0.27 ≈ 0.20 — yaad rakho, ye number MCQ me aata hai.

12.10 Forward pass vs Backward pass — kya store hota hai, aur kyun

Kahani

Trekking pe jaate waqt tum har mod pe ek chhota sa nishaan (chalk ka teer) lagate ho, aur likhte ho "yahan se left mude, 200 m chale". Wapas aate waqt wahi nishaan dekh ke ulta raasta pakadte ho. Agar jaate waqt nishaan nahi lagaye, to wapsi me har mod pe dobara soch-soch ke dhoondhna padega.

Forward pass = jaate waqt nishaan lagana (values store karna). Backward pass = wapsi me unhi nishaanon se gradient nikalna.

Forward pass

  • Direction: input → layers → output → loss (left to right)
  • Kaam: har node ki value compute karna: z = Wa + b, a = g(z), ... end me L
  • Store (cache) karta hai: har layer ke inputs a[l−1], pre-activations z[l], activations a[l] (jo bhi backward ko chahiye)
  • Inference (prediction) me sirf yahi chalta hai
  • Teacher slide: "Extracting information in 1st pass"

Backward pass

  • Direction: loss → output → layers → input (right to left)
  • Kaam: har node pe upstream × local se ∂L/∂(har value) aur ∂L/∂(har parameter)
  • Use karta hai forward ki stored values (× gate ko doosra input chahiye, σ gate ko σ(z) chahiye, ReLU ko z > 0 tha ya nahi)
  • Sirf training me chalta hai
  • Teacher slide: "Extracting more information in 2nd pass"
x layer 1 layer 2 loss L forward → : values compute + cache (z, a) ← backward : gradients, cached values reuse karke cache: z1, a1 cache: z2, a2
Forward ink arrows values aage le jaate hain aur har layer ke z, a cache me rakhte hain; backward red arrows unhi cached values se gradients peeche le jaate hain.

Teacher ke slides ka toy example (Prince UDL, Chapter 7 style)

Lecture 4 ke slides "Toy Example → Extracting information in 1st pass → Extracting more information in 2nd pass" Prince ki UDL book ke backprop chapter ko follow karte hain. Wahan ka toy model: f[x, ϕ] = β3 + ω3 · cos[β2 + ω2 · exp[β1 + ω1 · sin[β0 + ω0 · x]]], loss ℓ = (f − y)2. Isko chhote tukdon me todo:

1st pass (forward) — compute & store2nd pass (backward) — ulte order me
f0 = β0 + ω0x∂ℓ/∂f0 = cos(f0) · ∂ℓ/∂h1
h1 = sin(f0)∂ℓ/∂h1 = ω1 · ∂ℓ/∂f1
f1 = β1 + ω1h1∂ℓ/∂f1 = exp(f1) · ∂ℓ/∂h2  (= h2 · ∂ℓ/∂h2, stored h2 reuse!)
h2 = exp(f1)∂ℓ/∂h2 = ω2 · ∂ℓ/∂f2
f2 = β2 + ω2h2∂ℓ/∂f2 = −sin(f2) · ∂ℓ/∂h3
h3 = cos(f2)∂ℓ/∂h3 = ω3 · ∂ℓ/∂f3
f3 = β3 + ω3h3∂ℓ/∂f3 = 2(f3 − y)  ← yahan se shuru
ℓ = (f3 − y)2Parameters: ∂ℓ/∂βk = ∂ℓ/∂fk,   ∂ℓ/∂ωk = hk · ∂ℓ/∂fk (with h0 = x)

Dhyaan do: (1) backward ka har row pichhle row ka result reuse karta hai — kuch bhi dobara calculate nahi hota. (2) Har row ko forward ki stored value chahiye (f0, f2, hk). (3) Weight ka gradient = "source value hk × destination ka gradient" — yahi pattern 13.4 me δ × activation banega.

Exam Radar

  • Forward pass: computes outputs and loss, caches intermediate activations. Backward pass: computes gradients using chain rule, from loss to inputs, using the cache.
  • Training memory zyada lagti hai kyunki saari activations store karni padti hain (memory ∝ batch size × layers × width). Inference me (torch.no_grad()) ye storage nahi hota → kam memory.
  • Backprop = reverse-mode automatic differentiation. Ek scalar loss aur lakhon parameters ke liye reverse mode best hai: ek backward pass me saare gradients.
  • Forward pass training aur inference dono me; backward sirf training me.

Dimaag me aise baitha lo

Jaate waqt nishaan lagao (forward + cache), aate waqt nishaan padho (backward). Nishaan nahi to wapsi mehngi.

12.11 PyTorch autograd — graph khud banta hai, gradient khud nikalta hai

Kahani

Purani dukaan ke munim ji yaad karo — har len-den ek bahi-khata me likhte the: "itna aaya, kahan se aaya, kis hisaab se". Mahine ke end me agar hisaab milana ho, to bahi ko ulta padhte the. PyTorch ka autograd yahi munim ji hai: tum tensors pe jo bhi operation karte ho (+, ×, sigmoid, matmul), wo chupchaap ek bahi (computational graph) me likh leta hai. Jab tum loss.backward() bolte ho, wo bahi ulti padh ke har parameter ka gradient nikal deta hai.

Paanch keywords jo pakke yaad hone chahiye

CheezMatlabNote
requires_grad=True"Is tensor ka gradient chahiye — iske upar hone wale operations record karo"Manually banaye tensor me default False. nn.Linear ke weight/bias me default True.
dynamic graphGraph forward pass chalte-chalte banta hai (define-by-run), har iteration me nayaBackward ke baad graph free ho jaata hai (memory bachane ke liye)
grad_fnKis operation ne ye tensor banaya — uska "backward function" nodeAddBackward0, MulBackward0, SigmoidBackward0...; leaf tensor ka grad_fn = None
leaf tensorJo tensor user ne khud banaya (kisi operation ka result nahi) — jaise weights, inputs.is_leaf == True. Sirf leaf tensors (with requires_grad) ka .grad bharta hai
.backward() / .gradbackward() graph ulta chala ke gradients nikalta hai; result leaf ke .grad me add hota haibackward() scalar pe call hota hai (loss). Intermediate ka grad chahiye to .retain_grad()

PyTorch · 12.4 wala graph f = (x + y) * z

import torch

# 1) leaf tensors banao — requires_grad=True matlab "inka gradient chahiye"
x = torch.tensor(-2.0, requires_grad=True)
y = torch.tensor(5.0, requires_grad=True)
z = torch.tensor(-4.0, requires_grad=True)

# 2) forward pass — PyTorch chupke se graph bana raha hai
q = x + y          # add node
f = q * z          # multiply node
print("q =", q)
print("f =", f)
print("f.grad_fn =", f.grad_fn)
print("q.grad_fn =", q.grad_fn)
print("x.is_leaf:", x.is_leaf, "| q.is_leaf:", q.is_leaf)
print("x.grad_fn =", x.grad_fn)

# 3) backward pass — chain rule apne aap
f.backward()
print("df/dx =", x.grad)
print("df/dy =", y.grad)
print("df/dz =", z.grad)

Output

q = tensor(3., grad_fn=<AddBackward0>)
f = tensor(-12., grad_fn=<MulBackward0>)
f.grad_fn = <MulBackward0 object at 0x7f9f60b03a90>
q.grad_fn = <AddBackward0 object at 0x7f9f61933a30>
x.is_leaf: True | q.is_leaf: False
x.grad_fn = None
df/dx = tensor(-4.)
df/dy = tensor(-4.)
df/dz = tensor(3.)

Exactly haath wale answers: −4, −4, 3. (Memory address 0x7f... har run me alag aayega.) q non-leaf hai, to q.grad by default None rahega — uska gradient chahiye to backward se pehle q.retain_grad() likho.

Gradient ACCUMULATION — sabse important trap

Autograd naya gradient purane .grad me jodta hai (+=), replace nahi karta. Kyun? Kyunki "copy gate" (12.8) me ek hi tensor ke do raaston ke gradients jodne hote hain — PyTorch yahi kaam is accumulation se karta hai. Side effect: agar tum do baar backward chalao (do alag iterations me), to gradients jud jaate hain.

Chalo numbers se samjhein

x = 3, y = x2 → dy/dx = 2x = 6.

  1. Pehla backward: x.grad = 0 + 6 = 6
  2. Doosra backward (bina zero kiye): x.grad = 6 + 6 = 12 ← galat! Sahi gradient abhi bhi 6 hai.
  3. x.grad.zero_(): x.grad = 0
  4. Naya forward + backward: x.grad = 0 + 6 = 6 ← sahi

PyTorch · accumulation demo

import torch

x = torch.tensor(3.0, requires_grad=True)

y = x ** 2                       # dy/dx = 2x = 6
y.backward(retain_graph=True)    # graph ko mat todo, dobara backward karna hai
print("1st backward:", x.grad)

y.backward()                     # same graph pe dobara backward
print("2nd backward:", x.grad)   # 6 + 6 = 12  (ACCUMULATE hua!)

x.grad.zero_()                   # underscore = in-place, grad ko 0 kar do
print("after zero_():", x.grad)

y = x ** 2                       # naya forward = naya graph
y.backward()
print("fresh backward:", x.grad) # phir se sahi 6

Output

1st backward: tensor(6.)
2nd backward: tensor(12.)
after zero_(): tensor(0.)
fresh backward: tensor(6.)

retain_graph=True kyun? Default me backward ke baad graph (aur uski cached values) free ho jaata hai. Same graph pe doosri baar backward karoge to error:

Output (retain_graph ke bina doosra backward)

RuntimeError: Trying to backward through the graph a second time (or directly access saved tensors after ...

Training loop me ye problem nahi aati kyunki har iteration me naya forward → naya graph banta hai. Lekin .grad to parameter ke saath rehta hai, isliye har iteration me backward se pehle optimizer.zero_grad() zaroori hai (ye sab parameters ka .grad saaf karta hai; PyTorch 2.x me default set_to_none=True — grad ko 0 ki jagah None bana deta hai, effect same). Bhool gaye to kya hota hai — 13.8 me numbers ke saath.

torch.no_grad() aur .detach() — recording band karo

PyTorch · no_grad, detach, manual update

import torch

w = torch.tensor(2.0, requires_grad=True)

# (a) normal: graph banta hai
y = w * 3
print("normal   :", y.requires_grad, y.grad_fn)

# (b) torch.no_grad(): is block me graph NAHI banta
with torch.no_grad():
    y2 = w * 3
print("no_grad  :", y2.requires_grad, y2.grad_fn)

# (c) .detach(): same value, but graph se kata hua naya tensor
y3 = (w * 3).detach()
print("detach   :", y3.requires_grad, y3.grad_fn)

# (d) .numpy() ke liye detach zaroori
print("numpy    :", (w * 3).detach().numpy())

# (e) manual update hamesha no_grad ke andar
loss = (w * 3 - 9) ** 2       # L = (3w - 9)^2, dL/dw = 6(3w - 9) = -18 at w = 2
loss.backward()
print("w.grad   :", w.grad)
with torch.no_grad():
    w -= 0.01 * w.grad        # in-place update, graph me record nahi hoga
print("new w    :", w)

Output

normal   : True <MulBackward0 object at 0x7f6920c5b4c0>
no_grad  : False None
detach   : False None
numpy    : 6.0
w.grad   : tensor(-18.)
new w    : tensor(2.1800, requires_grad=True)

Vector output pe backward — pehle scalar banao

.backward() bina argument ke sirf scalar (ek number) pe chalta hai — kyunki "gradient of a single number w.r.t. sab cheezein" hi well-defined hai. Agar output vector ho (jaise Lab 2/3 me activation ki curve), to .sum() karke backward karo: sum ka har element ke saath derivative 1 hai, to har element ko apna local derivative mil jaata hai. Lab 2/3 isi liye torch.autograd.grad(y.sum(), z)[0] use karte hain — ye .grad me jodne ke bajaye gradient seedha return karta hai.

PyTorch · vector output (Lab 2/3 style)

import torch
z = torch.tensor([-1.0, 0.0, 2.0], requires_grad=True)
y = torch.sigmoid(z)            # shape (3,) -> scalar nahi hai
try:
    y.backward()
except RuntimeError as e:
    print("RuntimeError:", e)
y.sum().backward()              # sum ka gradient = har element ka apna sigma'(z)
print(z.grad)
g = torch.autograd.grad(torch.sigmoid(z).sum(), z)[0]   # Lab 2/3 wala style
print(g)

Output

RuntimeError: grad can be implicitly created only for scalar outputs
tensor([0.1966, 0.2500, 0.1050])
tensor([0.1966, 0.2500, 0.1050])

Dekho: σ′(0) = 0.25 (maximum), σ′(−1) = 0.1966, σ′(2) = 0.1050. Vector functions ke liye "local gradient" ek matrix (Jacobian) ban jaata hai; autograd poora Jacobian banaye bina "vector × Jacobian" product nikalta hai — isi liye ye fast hai (Lecture 4 slide: "Generalizing for vector functions in Deep NNs").

Exam Radar

  • requires_grad default False for torch.tensor(...); True for nn.Parameter / layer weights.
  • .grad sirf leaf tensors ka populate hota hai. Leaf ka grad_fn = None. Result tensors ka grad_fn = <XxxBackward0>.
  • Gradients accumulate by default → optimizer.zero_grad() (ya tensor.grad.zero_()) har step pe.
  • Same graph pe 2nd backward → RuntimeError, jab tak retain_graph=True na do.
  • backward() non-scalar pe → "grad can be implicitly created only for scalar outputs".
  • torch.no_grad() = graph recording off (eval, manual update). .detach() = graph se alag tensor. .item() = Python number.
  • PyTorch = dynamic graph (define-by-run).

Galti mat karna

  • w = w - lr * w.grad bina no_grad ke likhoge to naya w non-leaf ban jayega (graph ka hissa) aur agli baar uska .grad None aayega. Manual update hamesha with torch.no_grad(): me, in-place (-=).
  • Requires-grad wale tensor pe seedha .numpy() → error. Pehle .detach().
  • zero_() ka underscore matlab in-place. x.grad.zero() naam ka function nahi hai.

Dimaag me aise baitha lo

Autograd = munim ji ki bahi: requires_grad se likhna shuru, backward() se ulta padhna, .grad me jama — aur jama hota hi rehta hai jab tak zero_grad() se saaf na karo.

Chapter 13

Backpropagation Algorithm, Gradient Flow, Vanishing & Exploding Gradients — aur poora PyTorch training loop

1-1-1 network by hand · 2-2-1 network by hand · δ notation & general algorithm · Gradient flow · Vanishing gradients · Exploding gradients & clipping · Lab 5 training pipeline

13.1 Backpropagation kya hai — ek line me, phir detail me

Kahani

India match haar gayi — 10 run se. Coach ko har player ka "blame" nikalna hai. Wo result se peeche chalta hai: aakhri over ke bowler ne kitne diye, usse pehle wale ne, batsman ne kitne dot balls khele... Har player ka hissa uske kaam aur uske baad ke asar dono se banta hai. Ye "result se ulta chal ke har player ka hissa (blame) nikalna" hi backpropagation hai — blame assignment. Phir coach har player ko bolta hai: "tum itna sudharo" — wo hai gradient descent.

Definition

Backpropagation (backward propagation of errors) = chain rule ko computational graph pe output se input ki taraf systematically lagane ka algorithm, jo ek forward + ek backward pass me loss ka gradient har weight aur bias ke respect me nikal deta hai. Teacher ki slide: "Backpropagation is crucial for training neural networks. It is an efficient method for computing the gradient. The concept relies on the chain rule of calculus."

Ek training step ke chaar kadam (MLP worksheet + Lab 5 dono yahi kehte hain):

  1. Forward pass: input se prediction ŷ (beech ki values store karo).
  2. Loss: ŷ ko target y se compare karo → L.
  3. Backward pass (backprop): L se peeche chalte hue ∂L/∂w, ∂L/∂b sab parameters ke liye.
  4. Update (gradient descent): w ← w − η ∂L/∂w. Phir agla example/batch, phir se.

Teacher ki slide ke do "Intuitive Observations" yaad rakho — poora algorithm inhi do baaton pe khada hai:

Exam Radar

  • Backprop popular hua Rumelhart, Hinton & Williams (1986) ke Nature paper "Learning representations by back-propagating errors" se. (Reverse-mode differentiation ke ideas pehle Linnainmaa 1970, Werbos 1974 me bhi the.)
  • Backprop = chain rule + dynamic programming (intermediate gradients reuse). Gradient compute karta hai; update optimizer karta hai.
  • Backprop ko differentiable operations chahiye. Step function (perceptron) ka derivative almost everywhere 0 hai → backprop se train nahi hota. Isliye sigmoid/ReLU jaise activations (Chapter 9).

13.2 1-1-1 network — poora backprop haath se (classroom projection numbers)

Kahani

Teen dost line me khade hain: Input (x), Hidden (h), Output (ŷ). Input ek number hidden ko deta hai (weight w1 ke saath), hidden apna mood (sigmoid) laga ke output ko deta hai (weight w2 ke saath), output bhi sigmoid laga ke final answer deta hai. Teacher (loss) bolti hai: "jawab 1 hona chahiye tha". Ab dekhte hain kis dost ka kitna kasoor hai.

Ye exactly classroom projection document aur MLP worksheet (Part 6) ka network hai: ek input, ek hidden neuron, ek output; sigmoid activation; squared-error loss.

z1 = w1x + b1,   h = σ(z1),   z2 = w2h + b2,   ŷ = σ(z2),   L = ½(ŷ − y)2
  • x = input, y = true label (target)
  • w1, b1 = input → hidden ka weight aur hidden ka bias; z1 = hidden ka weighted input
  • h = hidden ki activation (worksheet me isko a[1] likha hai)
  • w2, b2 = hidden → output; z2 = output ka weighted input; ŷ = a[2] = prediction
  • ½ isliye ki differentiate karne pe square ka 2 cancel ho jaaye: ∂L/∂ŷ = ŷ − y

Initial values: x = 2, y = 1, w1 = 0.5, b1 = 0, w2 = 0.5, b2 = 0, learning rate η = 0.1.

Step 1 — Forward propagation

  1. Hidden weighted input: z1 = (0.5)(2) + 0 = 1
  2. Hidden activation: h = σ(1) = 1/(1 + e−1) = 1/1.36788 = 0.73106
  3. Output weighted input: z2 = (0.5)(0.73106) + 0 = 0.36553
  4. Prediction: ŷ = σ(0.36553) = 1/(1 + e−0.36553) = 0.59038
  5. Loss: L = ½(0.59038 − 1)2 = ½(−0.40962)2 = ½(0.16779) = 0.083895

Prediction 0.59038 target 1 se kam hai → network ko parameters aise badalne hain ki prediction badhe aur loss ghate.

Step 2 — Backward: pehle saare local derivatives

Classroom document ka tareeka: pehle poora derivative path likho, phir har local derivative substitute karo.

  1. Loss → output: ∂L/∂ŷ = ŷ − y = 0.59038 − 1 = −0.40962
  2. Output sigmoid: ∂ŷ/∂z2 = ŷ(1 − ŷ) = 0.59038 × 0.40962 = 0.24183
  3. Sum → weight: ∂z2/∂w2 = h = 0.73106 (weight ka partner)
  4. Sum → bias: ∂z2/∂b2 = 1
  5. Sum → hidden: ∂z2/∂h = w2 = 0.5 ← ye raasta hai jisse error hidden tak jaata hai
  6. Hidden sigmoid: ∂h/∂z1 = h(1 − h) = 0.73106 × 0.26894 = 0.19661
  7. Hidden sum → weight: ∂z1/∂w1 = x = 2; → bias: ∂z1/∂b1 = 1

Step 3 — Gradients (chain = local derivatives ka product)

w₂ — path: w2 → z2 → ŷ → L

∂L/∂w2 = ∂L/∂ŷ × ∂ŷ/∂z2 × ∂z2/∂w2 = (ŷ − y) × ŷ(1 − ŷ) × h
= (−0.40962)(0.59038)(0.40962)(0.73106)
= (−0.09906)(0.73106) = −0.07242

b₂ — same path, last link 1:

∂L/∂b2 = (ŷ − y) × ŷ(1 − ŷ) × 1 = (−0.40962)(0.24183) = −0.09906

w₁ — lamba path: w1 → z1 → h → z2 → ŷ → L

∂L/∂w1 = ∂L/∂ŷ × ∂ŷ/∂z2 × ∂z2/∂h × ∂h/∂z1 × ∂z1/∂w1
= (ŷ − y) × ŷ(1 − ŷ) × w2 × h(1 − h) × x
= (−0.40962)(0.59038)(0.40962)(0.5)(0.73106)(0.26894)(2)
= (−0.09906)(0.5)(0.19661)(2) = −0.01948

b₁ — same lamba path, last link 1:

∂L/∂b1 = (−0.09906)(0.5)(0.19661)(1) = −0.009738

Saare char gradients negative → char parameters badhenge (minus × minus = plus). Prediction kam thi, badhni chahiye — consistent!

Dhyaan se dekho: (ŷ − y) × ŷ(1 − ŷ) = −0.09906 chaaron gradients me common hai. Isko naam do δ2 = ∂L/∂z2 ("output neuron ki error"). Aur δ2 × w2 × h(1 − h) = −0.009738 = δ1 = ∂L/∂z1 ("hidden neuron ki error"). Phir:

Yahi δ wala pattern 13.4 me general algorithm banega. δ ek baar nikalo, sab jagah reuse karo — yahi backprop ki efficiency hai.

x = 2 z1 = 1 h = 0.7311 z2 = 0.3655 ŷ = 0.5904 L = 0.0839 σ σ y = 1 w1 = 0.5 b1 = 0 w2 = 0.5 b2 = 0 −0.0195 −0.0097 −0.0724 −0.0991 δ1 = −0.0097 −0.0495 δ2 = −0.0991 −0.4096 1 red = ∂L/∂(us box ki value)
1-1-1 ka computation graph (classroom document ka Figure 2 jaisa): ink = forward values, red = gradients. w2 ka path L → ŷ → z2 → w2; w1 ka path L → ŷ → z2 → h → z1 → w1 (lamba, aur gradient chhota).

Step 4 — Update (η = 0.1) aur verification

Rule: new parameter = old parameter − η × gradient

  1. w2 = 0.5 − 0.1(−0.07242) = 0.5 + 0.007242 = 0.50724
  2. w1 = 0.5 − 0.1(−0.01948) = 0.5 + 0.001948 = 0.50195
  3. b2 = 0 − 0.1(−0.09906) = 0.009906
  4. b1 = 0 − 0.1(−0.009738) = 0.000974

Naye parameters se dobara forward pass:

z1 = 0.50195 × 2 + 0.000974 = 1.00487 → h = σ(1.00487) = 0.73202
z2 = 0.50724 × 0.73202 + 0.009906 = 0.38121 → ŷ = σ(0.38121) = 0.59417
L = ½(0.59417 − 1)2 = 0.082351

Loss 0.083895 → 0.082351. Ghata! Gradients ne parameters ko sahi direction me hilaya. Training = yahi step hazaaron baar.

ParameterGradientInitialUpdated
w1−0.019480.500000.50195
b1−0.0097380.000000.000974
w2−0.072420.500000.50724
b2−0.099060.000000.009906

Ek aur cheez notice karo: |∂L/∂w1| = 0.0195 < |∂L/∂w2| = 0.0724. Pehli layer ka gradient chhota hai, kyunki uske path me do extra factors hain: w2 = 0.5 aur h(1 − h) = 0.1966. Ye "peeche jaate-jaate gradient chhota hona" hi vanishing gradient ka beej hai (13.6).

Code (a): manual backprop vs autograd — same answer

PyTorch · 1-1-1 manual chain rule vs autograd (Lab 2 style assert)

import torch

x, y = torch.tensor(2.0), torch.tensor(1.0)
w1 = torch.tensor(0.5, requires_grad=True)
b1 = torch.tensor(0.0, requires_grad=True)
w2 = torch.tensor(0.5, requires_grad=True)
b2 = torch.tensor(0.0, requires_grad=True)

# ---------- forward pass (values store ho rahe hain) ----------
z1 = w1 * x + b1
h = torch.sigmoid(z1)
z2 = w2 * h + b2
y_hat = torch.sigmoid(z2)
L = 0.5 * (y_hat - y) ** 2
print(f"forward: h={h.item():.6f}  y_hat={y_hat.item():.6f}  L={L.item():.6f}")

# ---------- manual backward (haath se chain rule) ----------
with torch.no_grad():
    dL_dyhat = y_hat - y                         # dL/dy_hat
    delta2 = dL_dyhat * y_hat * (1 - y_hat)      # dL/dz2
    g_w2 = delta2 * h                            # dz2/dw2 = h
    g_b2 = delta2 * 1                            # dz2/db2 = 1
    delta1 = delta2 * w2 * h * (1 - h)           # dL/dz1 (w2 se peeche aaya)
    g_w1 = delta1 * x                            # dz1/dw1 = x
    g_b1 = delta1 * 1

# ---------- autograd backward ----------
L.backward()

print("param   manual      autograd")
for name, m, p in [("w1", g_w1, w1), ("b1", g_b1, b1), ("w2", g_w2, w2), ("b2", g_b2, b2)]:
    print(f"{name}   {m.item():+.6f}   {p.grad.item():+.6f}")
assert all(torch.allclose(m, p.grad) for m, p in [(g_w1, w1), (g_b1, b1), (g_w2, w2), (g_b2, b2)])
print("Manual chain rule == autograd")

# ---------- ek gradient-descent step, eta = 0.1 ----------
eta = 0.1
with torch.no_grad():
    for p in (w1, b1, w2, b2):
        p -= eta * p.grad
    y_new = torch.sigmoid(w2 * torch.sigmoid(w1 * x + b1) + b2)
    L_new = 0.5 * (y_new - y) ** 2
print(f"new: w1={w1.item():.5f} b1={b1.item():.6f} w2={w2.item():.5f} b2={b2.item():.6f}")
print(f"loss: {L.item():.6f} -> {L_new.item():.6f}")

Output

forward: h=0.731059  y_hat=0.590378  L=0.083895
param   manual      autograd
w1   -0.019476   -0.019476
b1   -0.009738   -0.009738
w2   -0.072418   -0.072418
b2   -0.099060   -0.099060
Manual chain rule == autograd
new: w1=0.50195 b1=0.000974 w2=0.50724 b2=0.009906
loss: 0.083895 -> 0.082351

Line-by-line: x, y data hain (gradient nahi chahiye) to requires_grad nahi diya; parameters pe diya. Manual part no_grad me hai taaki ye extra calculations graph me na judein. torch.allclose float ke chhote rounding farak ko ignore karke compare karta hai. Update bhi no_grad me in-place (-=), taaki parameters leaf hi rahein.

Exam Radar

  • 1-1-1 (x = 2, y = 1, w = 0.5, b = 0): h = 0.73106, ŷ = 0.59038, L = 0.083895; ∂L/∂w2 = −0.07242, ∂L/∂w1 = −0.01948, ∂L/∂b2 = −0.09906, ∂L/∂b1 = −0.009738; η = 0.1 ke baad L = 0.082351.
  • ∂z/∂w = us weight ka input (source activation); ∂z/∂b = 1; ∂z2/∂h = w2 (error hidden tak weight ke through jaati hai).
  • Saare gradients negative → saare parameters badhe → ŷ badha (target 1 ki taraf).
  • Pehli layer ka gradient chhota (extra factors w2·h(1−h) ≈ 0.098).

Galti mat karna

  • ∂L/∂w1 me w2 (purana value 0.5) use hota hai, updated nahi. Pehle saare gradients purane parameters se nikalo, phir sab ek saath update karo.
  • Sigmoid derivative hidden me h(1 − h) aur output me ŷ(1 − ŷ) — dono ko mix mat karo.
  • Bias gradient me last factor 1 hai (input x nahi).

Dimaag me aise baitha lo

Weight gradient = "aage ki error (δ) × peeche ki activation". Output pe δ = (ŷ − y)·ŷ(1 − ŷ); hidden pe δ = aage ka δ × weight × h(1 − h).

13.3 2-2-1 network — saare chhe weights ka backprop (student activity)

Kahani

Ab class project me do students (x1, x2) do group leaders (h1, h2) ko report dete hain, aur dono leaders milke principal (ŷ) ko final report dete hain. Result kharab aaya to blame do parallel branches me baTega: h1 wali branch aur h2 wali branch. Har branch apne students tak blame pahunchayegi.

Guided worksheet ka network: 2 inputs, 2 hidden neurons, 1 output, 6 weights. Notation worksheet jaisa: w1, w2 → h1 me; w3, w4 → h2 me; w5, w6 → output me.

z1 = w1x1 + w2x2 + b1,   z2 = w3x1 + w4x2 + b2,   z3 = w5h1 + w6h2 + b3
  • h1 = σ(z1), h2 = σ(z2), ŷ = σ(z3), L = ½(ŷ − y)2
  • Rule: w(new) = w(old) − α(∂L/∂w); σ′(z) = σ(z)[1 − σ(z)]
  • Given: x1 = 1, x2 = 2, y = 1, α = 0.10; w1…w6 = 0.10, 0.20, 0.30, 0.40, 0.50, 0.60; b1 = b2 = b3 = 0 (is activity me biases update nahi karne)
  • Worksheet ki salah: calculation me kam se kam 6 decimal places rakho.

Part 2 — Forward propagation

  1. z1 = (0.10)(1) + (0.20)(2) + 0 = 0.500000
  2. h1 = σ(0.5) = 0.622459
  3. z2 = (0.30)(1) + (0.40)(2) + 0 = 1.100000
  4. h2 = σ(1.1) = 0.750260
  5. z3 = (0.50)(0.622459) + (0.60)(0.750260) + 0 = 0.311230 + 0.450156 = 0.761386
  6. ŷ = σ(0.761386) = 0.681655
  7. L = ½(0.681655 − 1)2 = ½(0.101343) = 0.050672

Checkpoint: prediction target se kam hai → expect karo ki chhe weights mostly badhenge.

Part 3 — Output weights w₅, w₆

  1. ∂L/∂ŷ = ŷ − y = 0.681655 − 1 = −0.318345
  2. ∂ŷ/∂z3 = ŷ(1 − ŷ) = (0.681655)(0.318345) = 0.217002
  3. Output signal (dono output weights share karte hain): δ3 = (−0.318345)(0.217002) = −0.069081
  4. ∂L/∂w5 = δ3 × h1 = (−0.069081)(0.622459) = −0.043000 → w5 = 0.50 − 0.10(−0.043000) = 0.504300
  5. ∂L/∂w6 = δ3 × h2 = (−0.069081)(0.750260) = −0.051829 → w6 = 0.60 − 0.10(−0.051829) = 0.605183

Checkpoint: h₂ (0.7503) > h₁ (0.6225), isliye |∂L/∂w₆| > |∂L/∂w₅|. Jiska source zyada "on" hai, uska weight zyada blame leta hai (Observation 1).

Part 4 — h₁ branch: w₁, w₂

  1. Local derivatives: ∂z3/∂h1 = w5 = 0.50, ∂h1/∂z1 = h1(1 − h1) = (0.622459)(0.377541) = 0.235004, ∂z1/∂w1 = x1 = 1, ∂z1/∂w2 = x2 = 2
  2. ∂L/∂w1 = (−0.318345)(0.217002)(0.50)(0.235004)(1) = −0.008117 → w1 = 0.10 − 0.10(−0.008117) = 0.100812
  3. ∂L/∂w2 = (−0.318345)(0.217002)(0.50)(0.235004)(2) = −0.016234 → w2 = 0.20 − 0.10(−0.016234) = 0.201623

Checkpoint: x₂ = 2x₁, isliye ∂L/∂w₂ = 2 × ∂L/∂w₁ (−0.016234 = 2 × −0.008117).

Part 5 — h₂ branch: w₃, w₄

  1. Local derivatives: ∂z3/∂h2 = w6 = 0.60, ∂h2/∂z2 = (0.750260)(0.249740) = 0.187370, ∂z2/∂w3 = x1 = 1, ∂z2/∂w4 = x2 = 2
  2. ∂L/∂w3 = (−0.318345)(0.217002)(0.60)(0.187370)(1) = −0.007766 → w3 = 0.30 − 0.10(−0.007766) = 0.300777
  3. ∂L/∂w4 = (−0.318345)(0.217002)(0.60)(0.187370)(2) = −0.015533 → w4 = 0.40 − 0.10(−0.015533) = 0.401553

Checkpoint: phir se x₂ = 2x₁ ⇒ ∂L/∂w₄ = 2 × ∂L/∂w₃.

x1 x2 h1 h2 ŷ w1 = 0.1−0.0081 w3 = 0.3−0.0078 w2 = 0.2−0.0162 w4 = 0.4−0.0155 w5 = 0.5−0.0430 w6 = 0.6−0.0518 x1 = 1 x2 = 2 h1 = 0.6225 h2 = 0.7503 δ1 = −0.0081 δ2 = −0.0078 ŷ = 0.6817 δ3 = −0.0691 L = 0.0507 y = 1
2-2-1 network: har weight ke tag me upar ink = purana value, neeche red = ∂L/∂w. Nodes ke paas δ (red) = ∂L/∂z. Biases sab 0. Saare gradients negative → saare weights badhenge.
WeightOld valueFull chainGradientUpdated value
w10.100000δ3 · w5 · h1(1−h1) · x1−0.0081170.100812
w20.200000δ3 · w5 · h1(1−h1) · x2−0.0162340.201623
w30.300000δ3 · w6 · h2(1−h2) · x1−0.0077660.300777
w40.400000δ3 · w6 · h2(1−h2) · x2−0.0155330.401553
w50.500000δ3 · h1−0.0430000.504300
w60.600000δ3 · h2−0.0518290.605183

Part 6 — Verification after one update

z1(new) = 0.100812(1) + 0.201623(2) = 0.504059 → h1(new) = 0.623413
z2(new) = 0.300777(1) + 0.401553(2) = 1.103883 → h2(new) = 0.750987
z3(new) = 0.504300(0.623413) + 0.605183(0.750987) = 0.768872 → ŷ(new) = 0.683277
L(new) = ½(0.683277 − 1)2 = 0.050157

L: 0.050672 → 0.050157. Loss ghata → update sahi direction me tha. Saare chhe gradients negative the, saare chhe weights badhe.

Bias ka kya? Is activity me biases freeze the, lekin unke gradients bhi bas δ hain: ∂L/∂b3 = δ3 = −0.069081, ∂L/∂b1 = δ1 = −0.008117, ∂L/∂b2 = δ2 = −0.007766 (jahan δ1 = δ3·w5·h1(1−h1), δ2 = δ3·w6·h2(1−h2)). Dekho, ∂L/∂w1 = δ1 × x1 = δ1 × 1, isliye number same aaya.

Exam Radar

  • 2-2-1 (x = [1, 2], y = 1, w = 0.1…0.6, α = 0.1): ŷ = 0.681655, L = 0.050672, δ3 = −0.069081; L(new) = 0.050157.
  • Hidden weight ka gradient path me 5 factors: (ŷ − y) · ŷ(1−ŷ) · wout · h(1−h) · x.
  • Same neuron me aane wale weights ka gradient ratio = unke inputs ka ratio (x2 = 2x1 ⇒ gradient double).
  • 2-2-1 me kitne parameters? Har layer: nin×nout + nout → (2×2 + 2) + (2×1 + 1) = 6 + 3 = 9 (6 weights + 3 biases).

Galti mat karna

  • h1 branch me w5 aata hai, h2 branch me w6. Branch mix ki to gradient galat.
  • Update karte waqt purani values use karo; w5 update karke uska naya value w1 ke gradient me mat daalo.

13.4 General algorithm — δ notation, kisi bhi network ke liye

Kahani

Ek company me complaint upar se (CEO = loss) aati hai. Har manager ka "blame" (δ) aise nikalta hai: uske neeche ke jin logon ko usne kaam diya tha, unke blame ko connection ki strength (weight) se weight karke JODO, phir multiply karo manager ki apni "sensitivity" (activation ka slope g′) se. Aur kisi ek connection (weight) ka blame = "jisko report gayi uska blame × jisne report bheji uska output". Bas — har layer pe yahi do kaam, upar se neeche.

Notation (Backprop Worksheet 03 aur MLP worksheet wali)

Definition

δ[l]j = ∂L/∂z[l]j = neuron j (layer l) ki "error" — agar is neuron ka weighted sum thoda badhe to loss kitna badhega. Ye neuron ki property hai, kisi ek weight ki nahi. Ek baar nikalo, us neuron ke saare weights aur bias ke liye reuse karo.

(BP1)   δ[L] = ∂L∂a[L] ⊙ g′(z[L])     (output layer)
(BP2)   δ[l] = (W[l+1]T δ[l+1]) ⊙ g′(z[l])     (hidden layers, peeche ki taraf)
(BP3)   ∂L∂W[l] = δ[l] a[l−1]T    i.e.   ∂L∂w[l]jk = δ[l]j · a[l−1]k
(BP4)   ∂L∂b[l] = δ[l]
  • ⊙ = element-wise (Hadamard) multiply — har neuron apne hi g′ se multiply hota hai
  • g′(z[l]) = activation ka slope us layer pe (sigmoid: a(1 − a); tanh: 1 − a2; ReLU: 1 if z > 0 else 0)
  • W[l+1]T (transpose) kyun? Forward me neuron k (layer l) ne layer l+1 ke saare neurons j ko feed kiya tha. Multivariable chain rule (12.7): uski error = saare raaston ka sum = Σj w[l+1]jk δ[l+1]j. Ye exactly WTδ ka row k hai. Matlab error same edges pe ulti chalti hai.
  • BP3: "gradient = error at destination × signal at source" (worksheet ki line). Weight ka local derivative uska source activation hai (multiply gate = swapper).
  • BP4: bias ka partner 1 hai, to gradient = δ khud.

Output layer ka δ — loss + activation ki jodi pe depend

Output activation + Lossδ[L] = ∂L/∂z[L]Kyun
Sigmoid + BCEŷ − y∂L/∂ŷ = (ŷ − y)/(ŷ(1 − ŷ)); × σ′ = ŷ(1 − ŷ) → cancel
Sigmoid + MSE (½ wala)(ŷ − y) · σ′(z) = (ŷ − y)ŷ(1 − ŷ)σ′ cancel nahi hota (hamare 1-1-1, 2-2-1, 2-3-2 examples)
Softmax + Cross-Entropyŷ − y (vector, y one-hot)Softmax ka Jacobian aur log cancel ho jaate hain
Linear (identity) + MSE (½)ŷ − yg′ = 1

Chalo numbers se samjhein — BCE ka magic cancel

Sawaal: ŷ = 0.8, y = 1, sigmoid output. BCE: L = −[y ln ŷ + (1 − y) ln(1 − ŷ)]. δ nikalo.

  1. ∂L/∂ŷ = −y/ŷ + (1 − y)/(1 − ŷ) = −1/0.8 + 0 = −1.25
  2. σ′(z) = ŷ(1 − ŷ) = 0.8 × 0.2 = 0.16
  3. δ = −1.25 × 0.16 = −0.2   aur shortcut: ŷ − y = 0.8 − 1 = −0.2. Same!

Ab saturation wala case dekho (activation worksheet): z = 6, ŷ = 0.99753, y = 0. MSE ke saath δ = (ŷ − y)σ′(z) = 0.99753 × 0.00247 ≈ 0.00246 — network "99.75% galat" hai fir bhi gradient chhota! BCE ke saath δ = ŷ − y = 0.99753 — bada, seedha galti ke barabar. Isi liye classification me sigmoid + BCE preferred hai.

Worked: 2-3-2 network (Backprop Worksheet 03) — output layer, phir "page 2" hidden layer

Worksheet ka fixed network: x = (1, 2), targets y = (1, 0), α = 0.5, sigmoid everywhere, loss L = ½[(ŷ1 − y1)2 + (ŷ2 − y2)2]. Weights:

Layer 1 (into hidden): rows = neuron j

0.20.1
0.30.2
−0.4−0.6

b[1] = (0.1, 0.3, 0.1)

Layer 2 (into outputs): rows = output neuron (destination)

0.5−0.30.8
−0.60.40.2

b[2] = (0.2, −0.1)

12 weights + 5 biases = 17 parameters. (Sheet har step ko 4 decimals pe round karti hai; isliye kahin-kahin last digit me full-precision se 1 ka farak dikhega.)

Forward (sheet ke numbers)

  1. z[1] = (0.2·1 + 0.1·2 + 0.1,  0.3·1 + 0.2·2 + 0.3,  −0.4·1 − 0.6·2 + 0.1) = (0.5, 1.0, −1.5)
  2. a[1] = σ(z[1]) = (0.6225, 0.7311, 0.1824)
  3. z[2]1 = 0.3113 − 0.2193 + 0.1459 + 0.2 ≈ 0.4379 (full precision 0.43785), z[2]2 = −0.3735 + 0.2924 + 0.0365 − 0.1 = −0.1446
  4. ŷ = (σ(0.43785), σ(−0.1446)) = (0.6077, 0.4639)
  5. L = ½[(−0.3923)2 + (0.4639)2] = ½(0.1539 + 0.2152) = ½(0.3691) ≈ 0.1846 (sheet; full precision 0.1845)

Output 1 bahut kam hai (chahiye 1), output 2 bahut zyada hai (chahiye 0).

Output layer — BP1, BP3

  1. σ′(z[2]1) = 0.6077 × 0.3923 = 0.2384, σ′(z[2]2) = 0.4639 × 0.5361 = 0.2487
  2. δ[2]1 = (ŷ1 − y1)σ′ = (−0.3923)(0.2384) = −0.0935
  3. δ[2]2 = (0.4639)(0.2487) = 0.1154
  4. BP3 (δ × source activation):
    ∂L/∂w[2]11 = (−0.0935)(0.6225) = −0.0582   ∂L/∂w[2]12 = (−0.0935)(0.7311) = −0.0684   ∂L/∂w[2]13 = (−0.0935)(0.1824) = −0.0171
    ∂L/∂w[2]21 = (0.1154)(0.6225) = 0.0718   ∂L/∂w[2]22 = (0.1154)(0.7311) = 0.0844   ∂L/∂w[2]23 = (0.1154)(0.1824) = 0.0210
  5. Update (α = 0.5): w[2]11 = 0.5 − 0.5(−0.0582) = 0.5291, w[2]12 = −0.3 + 0.0342 = −0.2658, w[2]13 = 0.8086, w[2]21 = −0.6 − 0.0359 = −0.6359, w[2]22 = 0.3578, w[2]23 = 0.1895

Sheet ka result: sirf ye chhe output weights badle to ŷ = (0.6183, 0.4502) aur L: 0.1846 → 0.1742. Output 1 (too low) ke saare weights UP gaye; output 2 (too high) ke saare DOWN. Sign δ me rehta hai; size source activation se aata hai — hidden 3 barely on (0.1824) tha, to uske weights barely hile.

Sheet ka "open question" tha: "Every δ we used came from a target. A hidden neuron has no target of its own. So where does its error come from?" Jawab = BP2: aage wale neurons ke δ, unhi weights se ulte bheje gaye.

Hidden layer — BP2 (sheet ka "page 2")

  1. Pehle W[2]Tδ[2] (har hidden neuron ko dono outputs se aayi error ka sum):
    hidden 1: (0.5)(−0.0935) + (−0.6)(0.1154) = −0.04675 − 0.06924 = −0.11599
    hidden 2: (−0.3)(−0.0935) + (0.4)(0.1154) = 0.02805 + 0.04616 = 0.07421
    hidden 3: (0.8)(−0.0935) + (0.2)(0.1154) = −0.07480 + 0.02308 = −0.05172
  2. σ′(z[1]) = a(1 − a): 0.6225 × 0.3775 = 0.2350, 0.7311 × 0.2689 = 0.1966, 0.1824 × 0.8176 = 0.1491
  3. ⊙ multiply: δ[1] = (−0.11599 × 0.2350,  0.07421 × 0.1966,  −0.05172 × 0.1491) = (−0.02726, 0.01459, −0.00771) ≈ (−0.0273, 0.0146, −0.0077)
  4. BP3 with source = input x = (1, 2):
    ∂L/∂W[1] = δ[1] xT = row 1: (−0.0273, −0.0545)   row 2: (0.0146, 0.0292)   row 3: (−0.0077, −0.0154)
  5. BP4: ∂L/∂b[1] = δ[1] = (−0.0273, 0.0146, −0.0077)

Interesting: hidden 2 ka δ positive hai. Kyun? Hidden 2 output 1 ko −0.3 se (neeche) dhakelta hai jabki output 1 ko upar jaana hai, aur output 2 ko +0.4 se (upar) dhakelta hai jabki output 2 ko neeche jaana hai. Dono taraf nuksaan → hidden 2 ko "kam on" hona chahiye → positive δ → uske incoming weights ghatenge.

h1 h2 h3 o1 o2 × 0.5 × (−0.6) δ = −0.0935 δ = 0.1154 a = 0.6225 σ′ = 0.2350 δ = −0.0273 δ = 0.0146 δ = −0.0077 δ[1]1 = (0.5·(−0.0935) + (−0.6)·0.1154) × 0.2350 = −0.11599 × 0.2350 = −0.0273
BP2 ek neuron ke liye: h1 ne forward me o1 aur o2 dono ko feed kiya tha, to backward me dono ke δ, unhi weights (0.5, −0.6) se, wapas h1 me aakar JUDTE hain; phir h1 ke apne slope σ′ = 0.2350 se multiply. Dashed pencil = baaki neurons ke same raaste.

PyTorch · vectorized manual backprop (BP1–BP4) vs autograd, 2-3-2 network

import torch

torch.set_printoptions(precision=4)
# 2-3-2 network (Backprop Worksheet 03 ke numbers)
x  = torch.tensor([1.0, 2.0])                       # a[0] = x, shape (2,)
y  = torch.tensor([1.0, 0.0])
W1 = torch.tensor([[0.2, 0.1], [0.3, 0.2], [-0.4, -0.6]], requires_grad=True)  # (3, 2)
b1 = torch.tensor([0.1, 0.3, 0.1], requires_grad=True)                         # (3,)
W2 = torch.tensor([[0.5, -0.3, 0.8], [-0.6, 0.4, 0.2]], requires_grad=True)    # (2, 3)
b2 = torch.tensor([0.2, -0.1], requires_grad=True)                             # (2,)

# ----- forward -----
z1 = W1 @ x + b1;  a1 = torch.sigmoid(z1)          # (3,)
z2 = W2 @ a1 + b2; yh = torch.sigmoid(z2)          # (2,)
L = 0.5 * ((yh - y) ** 2).sum()
print("a1 =", a1.detach(), "| y_hat =", yh.detach(), "| L =", round(L.item(), 4))

# ----- manual backward (BP1-BP4) -----
with torch.no_grad():
    d2 = (yh - y) * yh * (1 - yh)                  # BP1: delta[2], (2,)
    gW2 = torch.outer(d2, a1)                       # BP3: delta[2] a[1]^T, (2, 3)
    gb2 = d2                                        # BP4
    d1 = (W2.T @ d2) * a1 * (1 - a1)               # BP2: (W^T delta) * sigma'(z1), (3,)
    gW1 = torch.outer(d1, x)                        # (3, 2)
    gb1 = d1
print("delta2 =", d2, "| delta1 =", d1)
print("dL/dW2 =\n", gW2)
print("dL/dW1 =\n", gW1)

# ----- autograd se match -----
L.backward()
print("match:", all(torch.allclose(a, b) for a, b in
      [(gW2, W2.grad), (gb2, b2.grad), (gW1, W1.grad), (gb1, b1.grad)]))

Output

a1 = tensor([0.6225, 0.7311, 0.1824]) | y_hat = tensor([0.6077, 0.4639]) | L = 0.1845
delta2 = tensor([-0.0935,  0.1154]) | delta1 = tensor([-0.0273,  0.0146, -0.0077])
dL/dW2 =
 tensor([[-0.0582, -0.0684, -0.0171],
        [ 0.0718,  0.0843,  0.0210]])
dL/dW1 =
 tensor([[-0.0273, -0.0545],
        [ 0.0146,  0.0292],
        [-0.0077, -0.0154]])
match: True

Full precision me L = 0.1845 aur ∂L/∂w[2]22 = 0.0843 (sheet me step-rounding se 0.1846 aur 0.0844). torch.outer(a, b) = column × row = matrix (yahi δaT hai). W2.T = transpose. * = element-wise (⊙).

Mini-batch me shapes (PyTorch convention)

PyTorch me data rows me hota hai: X shape (N, nin), aur nn.Linear karta hai Z = XWT + b. Tab δ bhi (N, nout) shape ka matrix hai (har example ki ek row), aur:

CheezShapeFormula (batch)
A[l−1] (input to layer)(N, nin)forward me store
W[l](nout, nin)∂L/∂W = ΔT A[l−1] → (nout, nin)
b[l](nout,)∂L/∂b = Δ.sum(dim=0)
Δ[l] (deltas)(N, nout)Δ[l] = (Δ[l+1] W[l+1]) ⊙ g′(Z[l])

Rule of thumb: kisi bhi cheez ka gradient us cheez ke shape ka hi hota hai. W.grad.shape == W.shape hamesha. Loss mean ho (reduction='mean', default) to batch ka 1/N factor bhi δ me aa jaata hai.

Pseudo-code — poora algorithm

Backpropagation · pseudo-code

# FORWARD (cache sab kuch)
a[0] = x
for l in 1 .. L:
    z[l] = W[l] @ a[l-1] + b[l]          # store z[l]
    a[l] = g(z[l])                        # store a[l]
loss = Loss(a[L], y)

# BACKWARD (ulte order me)
delta[L] = dLoss/da[L] * g'(z[L])        # sigmoid+BCE / softmax+CE: a[L] - y
for l in L .. 1:
    dW[l] = outer(delta[l], a[l-1])       # error at destination x signal at source
    db[l] = delta[l]
    if l > 1:
        delta[l-1] = (W[l].T @ delta[l]) * g'(z[l-1])   # error ko peeche bhejo

# UPDATE (gradient descent - optimizer ka kaam)
for l in 1 .. L:
    W[l] -= lr * dW[l]
    b[l] -= lr * db[l]

Teacher ki slide notation (Prince UDL) me same cheez

Slides me: f0 = β0 + Ω0x, hk = a[fk−1], fk = βk + Ωkhk. Yahan fk = pre-activation (hamara z), hk = activation (hamara a), β = bias, Ω = weight matrix, ℓ = loss. Backward equations:

UDL / slide formδ formMatlab
∂ℓ/∂fkδ[k+1]pre-activation ka gradient
∂ℓ/∂βk = ∂ℓ/∂fk∂L/∂b = δbias gradient = δ
∂ℓ/∂Ωk = (∂ℓ/∂fk) hkT∂L/∂W = δ aTerror × source activation
∂ℓ/∂fk−1 = 𝕀[fk−1 > 0] ⊙ (ΩkT ∂ℓ/∂fk)δ[l] = (WTδ[l+1]) ⊙ g′(z)ReLU ka g′ = indicator 𝕀[z > 0] (1 if positive else 0)

Backward pass kitna mehnga? — "almost free"

Exam Radar

  • δ[l] = ∂L/∂z[l]. Output: sigmoid+BCE → ŷ − y; sigmoid+MSE → (ŷ − y)σ′(z); softmax+CE → ŷ − y.
  • δ[l] = (W[l+1]T δ[l+1]) ⊙ g′(z[l]) — transpose of the next layer's weights, element-wise with own activation slope.
  • ∂L/∂W[l] = δ[l] a[l−1]T (outer product); ∂L/∂b[l] = δ[l]. Gradient shape = parameter shape.
  • Notation w[l]jk: j = destination (this layer), k = source (previous layer).
  • Backward cost ≈ forward cost (same order); backprop = reverse-mode autodiff = chain rule + reuse.
  • MSE with sigmoid output → saturated output pe slow learning; BCE removes σ′ factor.

Galti mat karna

  • BP2 me W[l+1] (agli layer ka) transpose lagta hai, apni layer ka W[l] nahi.
  • BP2 me ⊙ (element-wise) hai, matrix multiply nahi. Shapes check karo: (nl,) ⊙ (nl,).
  • BP3 me source activation a[l−1] (pichhli layer) aata hai, apni activation a[l] nahi. Sheet ka hint: "ŷ1 is the destination's output, not the source's."
  • Sigmoid+BCE ka δ = ŷ − y hai; agar code me nn.Sigmoid + nn.BCELoss lagaya to autograd khud ye cancel handle karta hai — tumhe manually σ′ multiply nahi karna.

Dimaag me aise baitha lo

"δ peeche aata hai Wᵀ ke raaste, apne g′ se guna hota hai; weight ka gradient = δ (aage) × a (peeche); bias ka = δ."

13.5 Gradient flow — gradient = raaste ke saare factors ka product

Kahani

Paani ki pipeline socho jo tanki (loss) se shuru hoke ghar-ghar (layers) jaati hai. Har ghar pe ek valve hai jo paani ko kuch percent aage jaane deta hai. Agar har valve sirf 25% aage bhejta hai, to 10 ghar baad paani ki boond bhi nahi bachegi. Agar har valve pump hai jo 1.5 guna kar deta hai, to 20 ghar baad pipe phat jayegi. Backward pass me gradient bhi aise hi har layer ke "valve" se guzarta hai.

Ek simple chain network lo (har layer me ek neuron): x → (w1, σ) → a1 → (w2, σ) → a2 → (w3, σ) → ŷ → L. Chain rule se:

∂L∂w1 = ∂L∂ŷ · σ′(z3) · w3 · σ′(z2) · w2 · σ′(z1) · x
  • Har layer jo gradient ne cross ki, wo ek factor wk · σ′(zk−1) jod deti hai ("valve")
  • General: δ[l] = (W[l+1]Tδ[l+1]) ⊙ g′(z[l]) ko baar-baar lagao → δ[1] me L−1 baar (weight × activation slope) multiply hota hai
  • Product ka size: factors < 1 → gradient vanish; factors > 1 → gradient explode; factors ≈ 1 → healthy flow
x σ σ σ L w1 w2 w3 ŷ ∂L/∂ŷ × σ′(z3) × w3 × σ′(z2) × w2 × σ′(z1) × x ∂L/∂w1 = ∂L/∂ŷ · σ′(z3) · w3 · σ′(z2) · w2 · σ′(z1) · x
Gradient (red) loss se shuru hokar peeche jaata hai aur har layer pe "w × σ′" se multiply hota hai (orange). Pehli layer tak pahunchte-pahunchte jitne factors, utna bada asar.

Chalo numbers se samjhein

1-1-1 network (13.2) me dekho: δ2 = −0.09906 aur δ1 = −0.009738. Ek layer peeche jaane me gradient kitna chhota hua?

  1. Valve factor: w2 × σ′(z1) = 0.5 × 0.19661 = 0.0983
  2. Check: δ1/δ2 = −0.009738 / −0.09906 = 0.0983

Ek layer me gradient ~10 guna chhota! Agar aisi 5 layers hoti (har baar ~0.1), to pehli layer tak 0.15 = 0.00001 bachta.

Dimaag me aise baitha lo

Gradient = valves ka product. Har valve = (weight) × (activation ka slope). Valve < 1 → sukha; valve > 1 → baadh.

13.6 Vanishing gradients — gradient peeche jaate-jaate gayab

Kahani

Class me "kaan me baat" (Chinese whispers) game khela hai? Pehla bachcha bolta hai "kal picnic jayenge", aur 15 bachchon ke baad aakhri tak kuch aur hi pahunchta hai — ya bas hulki si fusfusahat. Deep sigmoid network me loss ka sandesh (gradient) bhi pehli layers tak pahunchte-pahunchte itna halka ho jaata hai ki pehli layers ko pata hi nahi chalta unhe kya sudharna hai. Wo bechari random hi reh jaati hain.

Definition

Vanishing gradient problem: deep network me backprop ke dauraan gradients peeche ki (early) layers tak pahunchte-pahunchte exponentially chhote ho jaate hain (≈ 0), kyunki chain rule me bahut saare < 1 factors multiply hote hain. Result: early layers ke weights almost update hi nahi hote — wo seekhna band kar dete hain.

σ′(z) = σ(z)(1 − σ(z)) ≤ 0.25   ⇒   n sigmoid layers ke baad gradient ≤ (0.25)n × (weights ka product)
  • 0.25 = sigmoid ke slope ka maximum (sirf z = 0 pe, jab σ = 0.5: 0.5 × 0.5 = 0.25). Baaki har jagah isse kam.
  • Saturation me (|z| bada) to aur bhi kam: σ′(6) = 0.00247 (activation worksheet ka example).
  • Agar |w| ≤ 1 bhi ho to har layer ka factor ≤ 0.25 — depth ke saath exponential decay.
Layers n (sigmoid)Best case 0.25nMatlab
10.25chauthai bacha
20.0625
30.015625
59.77 × 10−4hazaar guna chhota
109.54 × 10−7~ ek million guna chhota
209.09 × 10−13practically zero

Calculation: 0.2510 = (1/4)10 = 1/1,048,576 ≈ 9.54 × 10−7. Ye bhi "best case" hai — har neuron exactly z = 0 pe ho tab. Asli me aur kam.

10⁴ 1 10⁻⁴ 10⁻⁸ 10⁻¹² 0 5 10 15 20 layers peeche → gradient size (log scale) factor 0.25 (sigmoid): vanish factor 1 (ideal): steady factor 1.5: explode (1.5²⁰ ≈ 3325)
Log scale pe har layer ka factor: 0.25 wali line seedhi neeche gir rahi hai (20 layers me 10⁻¹² ke paas), 1.5 wali upar chadh rahi hai, sirf factor ≈ 1 healthy hai. Isi liye init/activation/normalization sab ka goal: per-layer factor ≈ 1.

Symptoms — kaise pehchaanein?

Causes — kyun hota hai?

Fixes — aur kis chapter me

FixKaise madad karta haiChapter
ReLU family (ReLU, Leaky ReLU, ELU)Positive side pe derivative exactly 1 → factor shrink nahi hotaCh 9
Xavier / He initializationWeights ka variance aisa ki per-layer signal/gradient factor ≈ 1 (Xavier: tanh/sigmoid, He: ReLU)Ch 21
Batch NormalizationHar layer ke z ko normalize → saturation zone se bahar, healthy gradientsCh 20
Residual / skip connectionsy = x + F(x) → ∂y/∂x = 1 + F′ — add gate ka "1" gradient highwayCh 25
LSTM / GRU (RNNs ke liye)Gates + additive cell state → time ke saath gradient multiply-multiply karke nahi marta(syllabus ke bahar, naam yaad rakho)
Output pe sigmoid + BCE (MSE nahi)Output δ = ŷ − y, σ′ factor hat jaata haiCh 11

Code (b): deep Sigmoid MLP vs deep ReLU MLP — layer-wise gradient norms

PyTorch · gradient norm per layer (10 hidden layers, width 64)

import torch
import torch.nn as nn

def make_mlp(act, depth=10, width=64, he=False):
    layers, n_in = [], 20
    for _ in range(depth):
        lin = nn.Linear(n_in, width)
        if he:   # He (Kaiming) init: ReLU ke liye variance 2/fan_in
            nn.init.kaiming_normal_(lin.weight, nonlinearity='relu')
            nn.init.zeros_(lin.bias)
        layers += [lin, act()]
        n_in = width
    layers.append(nn.Linear(n_in, 1))           # output layer
    return nn.Sequential(*layers)

def layer_grad_norms(model):
    torch.manual_seed(0)
    X = torch.randn(256, 20)                     # 256 fake samples, 20 features
    y = torch.randn(256, 1)
    loss = nn.MSELoss()(model(X), y)
    model.zero_grad()
    loss.backward()
    # har nn.Linear ke weight ka gradient norm (L2)
    return [m.weight.grad.norm().item() for m in model if isinstance(m, nn.Linear)]

torch.manual_seed(42); sig = layer_grad_norms(make_mlp(nn.Sigmoid))
torch.manual_seed(42); rel = layer_grad_norms(make_mlp(nn.ReLU))
torch.manual_seed(42); he  = layer_grad_norms(make_mlp(nn.ReLU, he=True))

print("layer   Sigmoid      ReLU(default)  ReLU+He")
for i, (a, b, c) in enumerate(zip(sig, rel, he), start=1):
    print(f"{i:>5}   {a:.2e}     {b:.2e}       {c:.2e}")
print(f"first/last ratio: sigmoid {sig[0]/sig[-1]:.1e} | relu {rel[0]/rel[-1]:.1e} | relu+he {he[0]/he[-1]:.1e}")

Output

layer   Sigmoid      ReLU(default)  ReLU+He
    1   2.03e-09     7.83e-05       1.07e+00
    2   2.82e-08     1.90e-04       4.38e+00
    3   2.12e-07     2.90e-04       4.83e+00
    4   1.60e-06     4.72e-04       5.30e+00
    5   1.00e-05     7.90e-04       5.41e+00
    6   6.90e-05     2.03e-03       6.42e+00
    7   5.11e-04     3.77e-03       8.50e+00
    8   3.58e-03     1.13e-02       7.95e+00
    9   2.66e-02     2.52e-02       7.46e+00
   10   1.73e-01     6.59e-02       7.49e+00
   11   1.27e+00     1.76e-01       2.33e+01
first/last ratio: sigmoid 1.6e-09 | relu 4.4e-04 | relu+he 4.6e-02

Padho isko (layer 1 = input ke paas, layer 11 = output layer):

Naye APIs: nn.init.kaiming_normal_(tensor, nonlinearity='relu') = He init in-place (underscore); nn.init.zeros_ bias zero; p.grad.norm() = gradient ka L2 norm (√ of sum of squares) — gradient health check karne ka standard tareeka; isinstance(m, nn.Linear) se Sequential me se sirf Linear layers chuni.

Exam Radar

  • σ′ max = 0.25 ⇒ n sigmoid layers ⇒ gradient ≤ 0.25n; 0.2510 ≈ 9.5 × 10−7.
  • Vanishing hits early layers (input ke paas) sabse zyada; ye sabse dheere seekhti hain.
  • tanh better than sigmoid (max slope 1, zero-centered) lekin phir bhi vanish karta hai. ReLU: slope 1 for z > 0 → "no vanishing gradient for positive inputs" (teacher slide), lekin dying ReLU (z < 0 pe slope 0) ka risk → Leaky ReLU.
  • Fixes: ReLU family, Xavier/He init, BatchNorm, residual connections, LSTM/GRU (RNN), gradient-friendly loss (BCE with sigmoid).
  • History: Hochreiter (1991) aur Bengio, Simard & Frasconi (1994) ne vanishing gradient formally dikhaya.
  • Learning rate badhane se vanishing theek nahi hota — early aur late layers ka ratio wahi rehta hai.

Galti mat karna

  • Vanishing gradient ka matlab "loss zero ho gaya" nahi hai. Loss high pe atka hota hai; gradient zero ke paas hota hai.
  • Vanishing gradient (bahut saare chhote factors ka product) aur dying ReLU (neuron hamesha z < 0, gradient exactly 0) alag cheezein hain.
  • "Last layers pe vanish hota hai" — galat. Gradient loss ke paas (last layers) sabse bada, input ke paas (first layers) sabse chhota.

Dimaag me aise baitha lo

"0.25 ka pahaada": 0.25, 0.0625, 0.0156... 10 layers me ek-millionth. Sigmoid ko hidden layers se nikaalo, ReLU + He lagao.

13.7 Exploding gradients — gradient ka visphot, aur gradient clipping

Kahani

Shaadi ke function me mic speaker ke paas le jao — "kuuuuueeeee!" Speaker ki awaaz mic me jaati hai, amplify hoke speaker se nikalti hai, phir mic me... har round me awaaz badhti hai, aur kuch hi second me kaan phaad screech. Ye hai feedback loop with gain > 1. Deep/recurrent network me agar har layer ka factor 1 se bada ho, to gradient bhi aise hi har layer me badh-badh ke "inf" ho jaata hai, aur weights NaN ban jaate hain.

Definition

Exploding gradient problem: backprop me gradients layers (ya RNN me time steps) ke through multiply hote-hote exponentially bade ho jaate hain. Result: weight updates itne bade ki training unstable — loss upar-neeche koodta hai, aur aakhir me inf/NaN.

per-layer factor |w · g′(z)| > 1   ⇒   gradient ∝ (factor)n → ∞
  • Example: w = 6 aur σ′ = 0.25 → factor 6 × 0.25 = 1.5 per layer
  • ReLU/linear layers me factor = |w| (active part) → bade weights seedhe explode
  • RNN me same weight matrix har time step pe multiply → 100 time steps = 100 factors
Layers n1.5n
11.5
57.59
1057.67
203325.3 ≈ 3.3 × 103
301.92 × 105
506.38 × 108

Sirf 1.5 ka chhota sa factor, aur 20 layers me gradient 3325 guna! Learning rate 0.01 ho to bhi step 33 ka — weights kahan ke kahan.

Symptoms

Causes

Fixes

FixKaiseChapter
Gradient clipping (by norm / by value)Backward ke baad, step se pehle, gradient ka size cap kar doyahin, neeche
Lower learning rate / LR scheduleChhote steps, feedback loop todoCh 15
Proper init (Xavier/He)Per-layer factor ≈ 1 rakhoCh 21
Batch NormalizationActivations ka scale controlCh 20
Weight decay (L2)Weights ko bada hone se rokta haiCh 19
Input scalingStandardScaler: features ~ mean 0, std 113.8

Gradient clipping — do tareeke

By norm:   agar ‖g‖ > c   to   g ← g · c‖g‖
By value:   gi ← clamp(gi, −c, c)   (har component alag se)
  • ‖g‖ = saare parameters ke gradients ko ek lamba vector maan ke L2 norm: √(Σ gi2)
  • c = threshold (max_norm ya clip_value), jaise 1.0
  • By norm: direction same, sirf length chhoti — isliye zyada popular (Pascanu et al., 2013, RNNs ke liye)
  • By value: har component ±c pe kaata → direction badal sakti hai
  • Agar ‖g‖ ≤ c to clip-by-norm kuch nahi karta

Chalo numbers se samjhein

Sawaal: gradient g = (3, 4). (a) max_norm = 1 se clip by norm; (b) clip_value = 0.5 se clip by value.

  1. Norm: ‖g‖ = √(32 + 42) = √25 = 5 > 1, to clip karna hai
  2. (a) Scale factor: 1/5 = 0.2 → g = (3 × 0.2, 4 × 0.2) = (0.6, 0.8), naya norm = √(0.36 + 0.64) = 1. Direction same (ratio 3:4 bacha).
  3. (b) Har component ko [−0.5, 0.5] me kaato: (min(3, 0.5), min(4, 0.5)) = (0.5, 0.5). Ratio 3:4 se 1:1 ho gaya → direction badal gayi.

Jawab: by norm → (0.6, 0.8); by value → (0.5, 0.5).

g = (3, 4), ‖g‖ = 5 by norm: (0.6, 0.8), same direction by value: (0.5, 0.5), direction badli dashed arc = norm 1 circle, dashed box = ±0.5 box g₁ g₂
Red = original gradient. Ink = clip by norm (circle pe aa gaya, same direction). Orange = clip by value (box ke corner pe, direction 45° ho gayi).

PyTorch · clip_grad_norm_ vs clip_grad_value_

import torch
import torch.nn as nn

# ek chhota parameter jiska gradient hum khud set karenge: g = [3, 4] -> norm 5
p = nn.Parameter(torch.zeros(2))
p.grad = torch.tensor([3.0, 4.0])
total = torch.nn.utils.clip_grad_norm_([p], max_norm=1.0)
print("norm before:", total)          # returns the ORIGINAL total norm
print("after clip_grad_norm_ :", p.grad, "| new norm:", p.grad.norm())

p.grad = torch.tensor([3.0, 4.0])
torch.nn.utils.clip_grad_value_([p], clip_value=0.5)
print("after clip_grad_value_:", p.grad)

p.grad = torch.tensor([0.3, 0.4])     # norm 0.5 < 1 -> kuch nahi badlega
torch.nn.utils.clip_grad_norm_([p], max_norm=1.0)
print("small grad untouched  :", p.grad)

Output

norm before: tensor(5.)
after clip_grad_norm_ : tensor([0.6000, 0.8000]) | new norm: tensor(1.0000)
after clip_grad_value_: tensor([0.5000, 0.5000])
small grad untouched  : tensor([0.3000, 0.4000])

API details: torch.nn.utils.clip_grad_norm_(parameters, max_norm, norm_type=2.0) — parameters = model.parameters() (ya list), max_norm = threshold, norm_type default 2 (L2). Saare parameters ka combined norm nikalta hai, in-place scale karta hai (underscore), aur clipping se pehle wala total norm return karta hai (logging ke liye useful). clip_grad_value_(parameters, clip_value) har gradient element ko [−clip_value, clip_value] me clamp karta hai, kuch return nahi karta.

Clipping training loop me kahan? — backward() ke BAAD, step() se PEHLE

PyTorch · exploding vs clipped training

import torch
import torch.nn as nn

def train(clip):
    torch.manual_seed(0)
    X = torch.randn(64, 3) * 10            # bade-bade input (scaling nahi kiya!)
    y = X.sum(dim=1, keepdim=True)         # target bhi bada: (64, 1)
    model = nn.Sequential(nn.Linear(3, 16), nn.ReLU(), nn.Linear(16, 1))
    opt = torch.optim.SGD(model.parameters(), lr=0.01)
    loss_fn = nn.MSELoss()
    out = []
    for epoch in range(1, 31):
        model.train()
        pred = model(X)                    # forward
        loss = loss_fn(pred, y)            # loss
        opt.zero_grad()                    # purane grads saaf
        loss.backward()                    # backward: grads bharo
        if clip:
            nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)  # backward ke BAAD, step se PEHLE
        opt.step()                         # update
        if epoch in (1, 2, 3, 5, 10, 30):
            out.append(f"{epoch}:{loss.item():.4g}")
    return out

print("no clip  :", "  ".join(train(False)))
print("with clip:", "  ".join(train(True)))

Output

no clip  : 1:293.2  2:15.38  3:19.2  5:246.4  10:inf  30:nan
with clip: 1:293.2  2:290.9  3:288.7  5:284.2  10:273.1  30:229.5

Bina clipping: loss 293 → 15 → 19 → 246 → inf → nan — exploding ke saare lakshan (wild jumps, phir inf/NaN). Clipping ke saath: dheere lekin stable girawat. (Asli ilaaj yahan inputs ko StandardScaler se scale karna aur LR kam karna bhi hai — clipping emergency brake hai.) Clip ko backward() se pehle lagaoge to clip karne ko gradient hi nahi hoga; step() ke baad lagaoge to update pehle hi bade gradient se ho chuka hoga.

VanishingExploding
Per-layer factor< 1 (e.g. σ′ ≤ 0.25)> 1 (e.g. 1.5)
Gradient at early layers≈ 0huge / inf
Loss behaviourplateau, bahut slowwild jumps, NaN/inf
Common causesigmoid/tanh saturation, small weights, depthlarge weights, RNNs, high LR
Main fixesReLU, He/Xavier, BN, residual, LSTM/GRUclipping, lower LR, init, BN, weight decay
Clipping helps?Nahi (chhote gradient ko bada nahi karta)Haan

Exam Radar

  • 1.520 ≈ 3325; 1.510 ≈ 57.7. Factor > 1 → explode.
  • Gradient clipping: torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) — between loss.backward() and optimizer.step().
  • Clip by norm keeps direction; clip by value may change direction. Clipping exploding ke liye hai, vanishing ke liye nahi.
  • RNNs me exploding + vanishing dono common (same weight repeated) → clipping (exploding) + LSTM/GRU (vanishing).
  • Symptom NaN loss → pehle LR kam karo / clip / input scaling check karo.

Galti mat karna

  • clip_grad_norm_ ka order: zero_grad → backward → clip → step. Clip ko step ke baad likhna bekaar hai.
  • Underscore wala naam (clip_grad_norm_) in-place version hai; purana clip_grad_norm (bina underscore) deprecated hai.
  • max_norm poore model ke combined norm pe lagta hai, har layer pe alag-alag nahi.

Dimaag me aise baitha lo

Mic-speaker screech = exploding; kaan-me-baat = vanishing. Screech ka ilaaj volume knob (clipping + kam LR); fusfusahat ka ilaaj behtar raasta (ReLU, He, BN, residual).

13.8 Poora PyTorch training pipeline (Lab 5) — har line kyun hai

Kahani

Maa biryani banati hai to teen phase hote hain: tayyari (chawal dhona, sabzi kaatna, masale naapna), pakana (baar-baar chakhna aur namak-mirch adjust karna), aur parosna (mehmaan ko dena — ab adjust nahi karte, bas khilate hain). Deep learning pipeline bhi bilkul aisa: data ki tayyari → training loop (forward = chakhna, backward = kya kam-zyada hai, step = adjust) → evaluation (bas test, koi adjust nahi).

Lab 5 ka goal (unke shabdon me): "By the end of this lab, the training loop and the custom model class should no longer feel like magic — you should be able to explain why every line exists." Task: Wisconsin Breast Cancer dataset — 569 patients, 30 numeric features, label M (malignant) / B (benign). Model: logistic regression as nn.Module (nn.Linear(30, 1) + nn.Sigmoid), BCE loss, SGD, lr = 0.1, 25 epochs, full-batch.

load CSV train_test_split StandardScalerfit: train only LabelEncoder tensorsfloat32 model + loss + optimizer training loop (har epoch / batch) forward loss zero_grad backward step repeat for epochs model.eval() torch.no_grad() accuracy on test
Pipeline: tayyari (upar ki row) → training loop ke 5 kadam (orange dabba) → evaluation (bina gradient). Scaler sirf train pe fit; tensors float32.

PyTorch · Lab 5 full pipeline (breast cancer, nn.Module, full-batch GD)

import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, LabelEncoder

# ---------- Step 1: reproducibility ----------
SEED = 42
np.random.seed(SEED)                 # numpy ka random fix
torch.manual_seed(SEED)              # torch ka random fix (weights ka init same aayega)

# ---------- Step 2: load + clean ----------
url = 'https://raw.githubusercontent.com/gscdit/Breast-Cancer-Detection/refs/heads/master/data.csv'
df = pd.read_csv(url)                                  # (569, 33)
df.drop(columns=['id', 'Unnamed: 32'], inplace=True)   # id = sirf naam, Unnamed: 32 = khaali column -> (569, 31)
X = df.drop(columns=['diagnosis'])                     # 30 features
y = df['diagnosis']                                    # 'M' / 'B'

# ---------- Step 3: split PEHLE, scaling BAAD me ----------
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=SEED)

scaler = StandardScaler()                 # x' = (x - mean) / std
X_train = scaler.fit_transform(X_train)   # mean/std SIRF train se seekho
X_test = scaler.transform(X_test)         # test pe wahi mean/std lagao (fit nahi!)

encoder = LabelEncoder()                  # alphabetical: 'B' -> 0, 'M' -> 1
y_train = encoder.fit_transform(y_train)
y_test = encoder.transform(y_test)

# ---------- Step 4: numpy -> tensors (float32!) ----------
X_train_tensor = torch.from_numpy(X_train.astype(np.float32))   # (455, 30)
X_test_tensor = torch.from_numpy(X_test.astype(np.float32))     # (114, 30)
y_train_tensor = torch.from_numpy(y_train.astype(np.float32))   # (455,)  1-D!
y_test_tensor = torch.from_numpy(y_test.astype(np.float32))     # (114,)
print("X_train_tensor:", X_train_tensor.shape, X_train_tensor.dtype)
print("y_train_tensor:", y_train_tensor.shape)

# ---------- Step 5: model as nn.Module ----------
class MySimpleNN(nn.Module):
    def __init__(self, num_features):
        super().__init__()                          # nn.Module ki machinery register karo
        self.linear = nn.Linear(num_features, 1)    # (N, 30) -> (N, 1), weight (1, 30) + bias (1,)
        self.sigmoid = nn.Sigmoid()                 # raw score -> probability (0, 1)

    def forward(self, features):
        out = self.linear(features)                 # z = w.x + b
        out = self.sigmoid(out)                     # y_hat = sigmoid(z)
        return out

model = MySimpleNN(X_train_tensor.shape[1])         # 30 features
loss_function = nn.BCELoss()                        # probabilities + float targets, SAME shape
optimizer = optim.SGD(model.parameters(), lr=0.1)   # model.parameters() = sab weights/biases
epochs = 25

# ---------- Step 6: training loop ----------
for epoch in range(epochs):
    model.train()                                   # training mode (dropout/BN on)
    y_pred = model(X_train_tensor)                  # 1. forward: (455, 30) -> (455, 1)
    loss = loss_function(y_pred, y_train_tensor.view(-1, 1))   # 2. loss: target (455,) -> (455, 1)
    optimizer.zero_grad()                           # 3. purane gradients saaf
    loss.backward()                                 # 4. backprop: har parameter ka .grad bharo
    optimizer.step()                                # 5. update: w = w - lr * w.grad
    if epoch in (0, 1, 2, 9, 24):
        print(f'Epoch: {epoch + 1}, Loss: {loss.item():.4f}')

# ---------- Step 7: evaluation ----------
model.eval()                                        # evaluation mode
with torch.no_grad():                               # graph mat banao: memory + time bachao
    y_prob = model(X_test_tensor)                   # (114, 1) probabilities
    y_pred_labels = (y_prob > 0.5).float()          # threshold 0.5 -> 0/1
    accuracy = (y_pred_labels == y_test_tensor.view(-1, 1)).float().mean()
print(f'Accuracy: {accuracy.item():.4f}')

Output

X_train_tensor: torch.Size([455, 30]) torch.float32
y_train_tensor: torch.Size([455])
Epoch: 1, Loss: 0.6704
Epoch: 2, Loss: 0.5365
Epoch: 3, Loss: 0.4555
Epoch: 10, Loss: 0.2672
Epoch: 25, Loss: 0.1793
Accuracy: 0.9825

Ye numbers Lab 5 notebook ke run se exact match karte hain (Epoch 1: 0.6704 … Epoch 25: 0.1793, Accuracy 0.9825). Loss har epoch girta gaya — training kaam kar rahi hai.

Har line ka role — ek table me

LineKya karti haiHata do to?
torch.manual_seed(42)Random init fix → har run same resultHar run me alag loss/accuracy
df.drop(columns=[...])Non-feature columns (id, khaali column) hataoid ek bekaar "feature" ban jaata; NaN column error deta
train_test_split(..., test_size=0.2)20% data test ke liye alag (455 train / 114 test)Model ko unseen data pe judge hi nahi kar paoge
scaler.fit_transform(X_train)Train ka mean/std seekh ke scale karoUnscaled features (area ~2000 vs smoothness ~0.1) → bade-chhote gradients, slow/unstable training
scaler.transform(X_test)Test pe train wala mean/std lagaoTest pe fit kiya to data leakage (test ki info training me ghus gayi)
LabelEncoder'B'/'M' strings → 0/1 numbersStrings tensor nahi ban sakti
.astype(np.float32)numpy float64 → float32RuntimeError: Double vs Float
super().__init__()nn.Module ki machinery (parameter registry) chaluLayers assign karte hi error
self.linear = nn.Linear(30, 1)Learnable w (1×30) + b; auto-registeredKoi parameter hi nahi
forward()Computation define karta hai; call model(x) se (hooks bhi chalte hain)Model kuch compute nahi karega
nn.BCELoss()Binary cross-entropy: probabilities vs 0/1 targetsLoss hi nahi → backward kis cheez ka?
optim.SGD(model.parameters(), lr=0.1)Optimizer ko parameters ki list aur step sizestep() kise update kare?
model.train()Training mode (Dropout/BatchNorm training behaviour)Is model me farak nahi (dropout/BN nahi); bade models me galat behaviour
y_pred = model(X)Forward pass + graph banta hai—
.view(-1, 1)Target (455,) → (455, 1), prediction ke shape jaisaBCELoss ValueError; MSE-type losses me silent (N, N) broadcasting bug
optimizer.zero_grad()Sab .grad saaf (accumulation rokna)Gradients jud-jud ke galat updates (neeche demo)
loss.backward()Backprop: har parameter ka ∂L/∂θ .grad me.grad None/purana → step kuch nahi / galat karega
optimizer.step()θ ← θ − lr · θ.grad (SGD)Weights kabhi nahi badlenge, loss constant
loss.item()Tensor → Python float (logging)Tensor store karoge to graph bhi pakda rahega (memory)
model.eval()Evaluation mode (Dropout off, BN running stats)Dropout wale models me random, galat predictions
torch.no_grad()Eval me graph mat banaoMemory + compute waste (galat answer nahi, par mehnga)
(y_prob > 0.5).float()Probability → class label (threshold 0.5)Accuracy compare nahi hoga
(... == ...).float().mean()Sahi predictions ka fraction = accuracy—

Paanch kadam ka loop — andar kya hota hai

  1. Forward y_pred = model(X): z = XWT + b, ŷ = σ(z). Autograd graph banata hai aur X, ŷ jaisi values cache karta hai.
  2. Loss loss = loss_function(...): ek scalar; graph ka last node.
  3. zero_grad: model.parameters() ke sabka .grad saaf (PyTorch 2.x: None set). Lab ka note: "The call must happen every epoch, before backward() — the exact position relative to forward()/loss computation does not matter." Isliye Lab 6 me order zero_grad → forward → loss → backward → step hai aur Lab 5 me forward → loss → zero_grad → backward → step — dono sahi.
  4. Backward loss.backward(): graph ulta chalta hai (13.4 wala BP1–BP4 automatically); har leaf parameter ke .grad me ∂L/∂θ (shape = θ ka shape). Graph phir free.
  5. Step optimizer.step(): SGD ke liye θ ← θ − lr · θ.grad, har parameter pe (andar no_grad me). Momentum/Adam ka step alag formula (Ch 16, Ch 17), lekin loop same.

Mini-batch version (Lab 6 Part 2 style) — preview

Lab 5 full-batch hai (455 rows ek saath, 1 update per epoch). Lab 6 me DataLoader(batch_size=32, shuffle=True) se mini-batches — loop me ek andar wala for aa jaata hai. Details Chapter 14 me; yahan sirf shape dekho:

PyTorch · same pipeline, mini-batch loop

from torch.utils.data import TensorDataset, DataLoader

torch.manual_seed(42)
model = MySimpleNN(X_train_tensor.shape[1])                    # fresh model
optimizer = optim.SGD(model.parameters(), lr=0.1)

train_ds = TensorDataset(X_train_tensor, y_train_tensor.view(-1, 1))   # (455, 30), (455, 1)
train_loader = DataLoader(train_ds, batch_size=32, shuffle=True)

for epoch in range(5):
    model.train()
    epoch_loss = 0.0
    for xb, yb in train_loader:                 # har batch: xb (32, 30), yb (32, 1)
        y_pred = model(xb)                      # forward
        loss = loss_function(y_pred, yb)        # loss
        optimizer.zero_grad()                   # grads saaf
        loss.backward()                         # backprop
        optimizer.step()                        # update
        epoch_loss += loss.item() * xb.size(0)  # batch loss x batch size
    print(f"Epoch {epoch + 1}: avg loss = {epoch_loss / len(train_ds):.4f}, updates = {len(train_loader)}")

Output

Epoch 1: avg loss = 0.3472, updates = 15
Epoch 2: avg loss = 0.1901, updates = 15
Epoch 3: avg loss = 0.1534, updates = 15
Epoch 4: avg loss = 0.1337, updates = 15
Epoch 5: avg loss = 0.1226, updates = 15

455/32 = 14.2 → 15 batches (aakhri batch 7 rows ka) → 15 updates per epoch. Isliye 5 epochs me hi loss 0.12 tak, jabki full-batch me 25 epochs ke baad 0.18.

model.train() vs model.eval()

Kuch layers training aur testing me alag behave karti hain: Dropout (training me random neurons band karta hai, eval me sab on) aur BatchNorm (training me batch ke mean/var, eval me running averages) — details Chapter 20. model.train() / model.eval() sirf ek flag (module.training) set karte hain jo ye layers padhti hain.

PyTorch · train vs eval mode (Dropout demo)

import torch
import torch.nn as nn

torch.manual_seed(3)
drop = nn.Dropout(p=0.5)
x = torch.ones(6)

drop.train()                 # training mode (default)
print("train mode:", drop(x))
drop.eval()                  # evaluation mode
print("eval mode :", drop(x))
print("drop.training =", drop.training)

Output

train mode: tensor([2., 2., 0., 2., 0., 0.])
eval mode : tensor([1., 1., 1., 1., 1., 1.])
drop.training = False

Training me aadhe zero, baaki ×2 (1/(1−p) scaling, taaki expected value same rahe); eval me sab as-it-is. model.eval() aur torch.no_grad() alag kaam hain: eval() layers ka behaviour badalta hai, no_grad() gradient recording band karta hai. Evaluation me dono chahiye (Lab 6: "use model.eval() + torch.no_grad() for evaluation"). Aur eval ke baad training dobara shuru karo to model.train() wapas lagana mat bhoolo — isliye loop ke andar har epoch ki shuruaat me likhte hain.

Lab ke pitfalls — numbers ke saath

Pitfall 1: zero_grad() bhool gaye.

PyTorch · with vs without zero_grad

import torch
import torch.nn as nn

def run(use_zero_grad):
    torch.manual_seed(0)
    X = torch.randn(100, 2)
    y = (X @ torch.tensor([[2.0], [-1.0]])) + 0.5      # simple linear target, (100, 1)
    model = nn.Linear(2, 1)
    opt = torch.optim.SGD(model.parameters(), lr=0.1)
    losses = []
    for epoch in range(1, 21):
        loss = nn.MSELoss()(model(X), y)
        if use_zero_grad:
            opt.zero_grad()
        loss.backward()
        opt.step()
        if epoch in (1, 3, 5, 10, 20):
            losses.append(f"{epoch}:{loss.item():.3f}")
    return "  ".join(losses)

print("with zero_grad   :", run(True))
print("WITHOUT zero_grad:", run(False))

Output

with zero_grad   : 1:4.992  3:2.658  5:1.428  10:0.308  20:0.015
WITHOUT zero_grad: 1:4.992  3:1.710  5:0.196  10:3.956  20:1.465

Bina zero_grad: har step ka gradient = ab tak ke saare gradients ka sum → step bade hote jaate hain → pehle tez girta hai, phir overshoot karke loss upar-neeche (0.196 → 3.956). Sahi version smoothly 0.015 tak. (Lab 5 activity ka reflection sawaal: symptom = loss curve erratic/upar-neeche.)

Pitfall 2: (N,) vs (N, 1) shape — broadcasting. Model output (N, 1), labels (N,).

PyTorch · shape aur dtype pitfalls

import warnings
import numpy as np
import torch
import torch.nn as nn
warnings.simplefilter("always")

pred = torch.tensor([[0.9], [0.2], [0.8], [0.3]])   # (4, 1)  model output
y    = torch.tensor([1.0, 0.0, 1.0, 0.0])           # (4,)    labels

print("(pred - y).shape :", (pred - y).shape)        # broadcasting -> (4, 4) !!

with warnings.catch_warnings(record=True) as w:
    bad = nn.MSELoss()(pred, y)
    print("MSELoss mismatched:", round(bad.item(), 4), "| warning:", str(w[0].message)[:60], "...")
good = nn.MSELoss()(pred, y.view(-1, 1))
print("MSELoss matched   :", round(good.item(), 4))

try:
    nn.BCELoss()(pred, y)
except ValueError as e:
    print("BCELoss mismatched -> ValueError:", str(e)[:70], "...")
print("BCELoss matched   :", round(nn.BCELoss()(pred, y.view(-1, 1)).item(), 4))

# Double vs Float
layer = nn.Linear(3, 1)
x64 = torch.from_numpy(np.ones((2, 3)))             # numpy default float64
print("dtype:", x64.dtype)
try:
    layer(x64)
except RuntimeError as e:
    print("RuntimeError:", e)
print("fixed:", layer(x64.float()).dtype)

Output

(pred - y).shape : torch.Size([4, 4])
MSELoss mismatched: 0.345 | warning: Using a target size (torch.Size([4])) that is different to t ...
MSELoss matched   : 0.045
BCELoss mismatched -> ValueError: Using a target size (torch.Size([4])) that is different to the input s ...
BCELoss matched   : 0.2271
dtype: torch.float64
RuntimeError: mat1 and mat2 must have the same dtype, but got Double and Float
fixed: torch.float32

Pitfall 3: Double vs Float. NumPy default float64 ("Double"), nn.Linear weights float32 ("Float"). Error message purane version me "expected scalar type Float but found Double", naye me "mat1 and mat2 must have the same dtype". Ilaaj: .astype(np.float32) before torch.from_numpy, ya torch.tensor(arr, dtype=torch.float32), ya .float().

Pitfall 4: eval me torch.no_grad() bhool gaye.

PyTorch · no_grad ke bina eval

import torch
import torch.nn as nn

model = nn.Linear(3, 1)
X_test = torch.randn(4, 3)

out = model(X_test)                      # bina no_grad
print("without no_grad:", out.requires_grad, out.grad_fn)

with torch.no_grad():
    out = model(X_test)                  # no_grad ke andar
print("with no_grad   :", out.requires_grad, out.grad_fn)

Output

without no_grad: True <AddmmBackward0 object at 0x7fde0e90ce20>
with no_grad   : False None

Bina no_grad har eval forward graph banata hai (AddmmBackward0 = matmul + add ka backward node) aur activations memory me rakhta hai — bade test set pe memory khatam ho sakti hai. Answer galat nahi hota, bas faltu kharcha.

Pitfall 5: data leakage. scaler.fit_transform poore data pe (split se pehle) ya test pe → test ka mean/std training me ghus jaata hai, accuracy jhoothi achhi. Rule: split pehle, fit sirf train pe, transform dono pe.

Aur chhote traps: nn.BCELoss ko probabilities chahiye (sigmoid ke baad) — agar model raw logits deta hai to nn.BCEWithLogitsLoss use karo (sigmoid + BCE ek saath, numerically zyada stable; Lab 5 post-lab Q2). Targets float hone chahiye (isliye labels bhi float32). model(x) likho, model.forward(x) nahi (hooks skip ho jaate hain).

Exam Radar

  • Loop order: forward → loss → zero_grad → backward → step (zero_grad kahin bhi backward se pehle chalega).
  • loss.backward() gradients compute karta hai; optimizer.step() parameters update karta hai; optimizer.zero_grad() gradients reset karta hai.
  • Gradients accumulate by default → zero_grad zaroori. (Kabhi jaan-boojh ke accumulate karte hain: bada effective batch, "gradient accumulation".)
  • Scaler: fit_transform on train, transform on test. LabelEncoder alphabetical: B → 0, M → 1.
  • Lab 5 numbers: 569 rows, 30 features, split 455/114, lr 0.1, 25 epochs, final loss 0.1793, test accuracy 0.9825.
  • Shapes: nn.Linear(30, 1): input (N, 30) → output (N, 1); weight shape (1, 30), bias (1,). Labels ko .view(-1, 1).
  • Evaluation: model.eval() + with torch.no_grad(); prediction = (prob > 0.5).float(); accuracy = (pred == y).float().mean().
  • model.train()/eval() Dropout aur BatchNorm ka behaviour badalte hain; no_grad gradient tracking.

Galti mat karna

  • optimizer.step() ko loss.backward() se pehle mat likhna — step ko gradient chahiye.
  • model.eval() ko torch.no_grad() ka replacement mat samajhna (aur ulta bhi nahi).
  • Test set pe fit_transform = leakage. Test pe sirf transform.
  • train_losses.append(loss) (tensor) ke bajaye loss.item() append karo.
  • Optimizer banate waqt model.parameters() pass karna — model nahi.

Dimaag me aise baitha lo

"Forward, Loss, Zero, Backward, Step" — "Falooda Lene Zaroor Bazaar Saath" chalo. Aur khilate waqt (eval) chakhna band: eval() + no_grad().

Chapter 14

Gradient Descent ke Types (Vanilla/Batch, SGD, Mini-batch)

Loss landscape · update rule θ ← θ − η∇L · w² wala hisaab · convex / non-convex · saddle · plateau · Batch GD · SGD · Mini-batch · epoch / iteration · DataLoader

Ab tak humne seekha: model ek guess deta hai, loss batata hai ki guess kitna galat hai (Chapter 11), aur backprop / autograd batata hai ki har weight ke liye loss ka gradient kya hai (Chapter 12, Chapter 13). Training loop me ek line thi optimizer.step(). Is chapter me hum dekhenge ki wo line andar se kya karti hai, aur ek bahut important sawaal: ek update ke liye kitna data use karein? Saara data? Ek row? Ya chhota group? Isi se teen naam bante hain: Batch GD, SGD, Mini-batch GD.

14.1 Kahani: kohre wala pahad (loss landscape)

Kahani

Socho tum Manali ke ek pahad par ho. Raat hai, ghana kohra (fog) hai, aur tumhari aankhon par patti bandhi hai. Tumhe neeche gaon tak pahunchna hai, jo ghaati (valley) ki sabse neechi jagah par hai. Tum poora pahad dekh nahi sakte. Bas ek cheez kar sakte ho: pairon se mehsoos karo ki zameen kis taraf dhalaan (slope) le rahi hai.

Smart tareeka kya hoga? Jis taraf zameen sabse tez upar ja rahi hai, uska ulta pakdo, aur ek chhota kadam lo. Phir ruk kar dobara mehsoos karo. Phir kadam. Phir mehsoos. Jab zameen bilkul sapaat lage (slope ≈ 0), samjho tum neeche pahunch gaye.

Bas, yahi Gradient Descent hai. Pahad = loss surface. Tumhari position = model ke weights. Uchaai (height) = loss. Pairon se slope mehsoos karna = gradient nikalna. Kadam = update.

Ab is kahani ko model ki duniya me translate karte hain:

Kahani meDeep Learning me
Pahad ki zameen (poora terrain)Loss landscape / loss surface: har possible weight-combination par loss kitna hai
Tum kahan khade ho (latitude, longitude)Parameters θ (saare weights aur biases)
Wahan ki uchaaiLoss L(θ) — ek number
Pairon se dhalaan mehsoos karnaGradient ∇L(θ) nikalna (backprop / loss.backward())
Kadam ki lambaiLearning rate η (eta), code me lr
Ek kadam lenaEk update (optimizer.step())
Gaon (sabse neechi jagah)Minimum — jahan loss sabse kam hai

Training ka asli goal ek line me: woh θ dhoondho jisse loss sabse chhota ho. Isko math me likhte hain:

θ̂ = argminθ L(θ)
  • argmin = "woh value batao jahan cheez minimum hoti hai" (minimum value nahi, balki kahan minimum hai — uska address).
  • θ̂ (theta-hat) = sabse achhe parameters. Lecture slides me isi ko ϕ̂ = argminϕ L[ϕ] likha hai (Prince ki book ki notation, ϕ = phi = parameters).

Seedha saare options try kyun nahi kar lete?

Teacher ke lecture me ek delivery company ka example tha: ek hi parameter w (minutes per km). Unhone w ki 561 alag values par loss nikaal kar poora bowl-shaped curve bana diya, aur neeche wala point aankh se dikh gaya (bottom w ≈ 2.56 par tha). Par ye luxury sirf 1 parameter me milti hai. Ek neural network me laakhon parameters hote hain. Agar har parameter ki sirf 10 values bhi try karo, to 1010,00,000 combinations — brahmand khatam ho jayega, gin nahi paoge. Isliye humein chahiye ek smart, iterative search: jahan khade ho, wahin ki jaankari se agla kadam decide karo.

Aur dhyaan do: ek jagah ka loss (sirf uchaai) se direction nahi pata chalti. "Loss = 28.149" sunke tum nahi bata sakte ki w badhana hai ya ghatana. Direction ke liye chahiye aas-paas ki jaankari = slope = gradient.

parameter θ (weight) loss L(θ) tum (θ₀) ∇L : uphill step: −η∇L (downhill) minimum (gradient = 0) kohra (fog): poora pahad nahi dikhta, sirf pairon ke neeche ki dhalaan (slope) mehsoos hoti hai
Ek parameter wala loss curve. Peela point = abhi ki position. Laal teer = gradient (uphill). Narangi teer = humara kadam, gradient ke ulte (downhill). Sabse neeche gradient = 0.

14.2 Gradient upar ki taraf, isliye ulta chalo — update rule

Yaad karo Chapter 2: derivative = slope. Agar slope positive hai, matlab w badhane se loss badhta hai. Gradient (saare partial derivatives ka vector) hamesha steepest increase, yaani sabse tez uphill direction point karta hai. Humein neeche jaana hai, to hum gradient ka ulta (negative) lete hain.

Definition

Gradient Descent: ek iterative optimization algorithm jo baar-baar do kaam karta hai — (1) abhi ki position par loss ka gradient nikalo, (2) gradient ke ulte direction me ek chhota kadam lo. Ye tab tak chalta hai jab tak gradient lagbhag zero na ho jaye (ya steps khatam na ho jayein).

θt+1 = θt − η · ∇L(θt)
  • θt = abhi ke parameters (step number t par). θt+1 = update ke baad naye parameters.
  • ∇L(θt) = "nabla L" = gradient: har parameter ke liye ∂L/∂θ ka vector. Ye direction deta hai (uphill), aur iska size batata hai slope kitna tez hai.
  • η (eta) = learning rate, ek chhota positive number (jaise 0.1, 0.01). Ye kadam ka size control karta hai.
  • minus (−) sign = "ulta chalo". Gradient uphill bolta hai, hum downhill jaate hain. Yahi poore formula ki jaan hai.

Ek hi formula, alag-alag kapde: course ke alag files me ye same rule alag notation me likha hai. MCQ me koi bhi aa sakta hai, sab ek hi cheez hain:

Toh α = η = lr = learning rate, aur L = J = loss = cost. Bas naam alag hain.

Sign ka khel (MCQ favourite)

Gradient ek saath do kaam karta hai: direction AUR distance

Teacher ke lecture me ek mazedaar experiment tha. Maan lo koi tumhe sirf direction bata de ("left jao" ya "right jao") aur tum hamesha fixed size c ka kadam lo. Kya hoga? Tum bottom ke paas pahunchoge, usko cross karoge, palat ke wapas aaoge, phir cross karoge... hamesha ke liye bottom ke aas-paas lagbhag c chaudi patti (band) me ulat-palat karte rahoge. Lecture ki table: c = 2.1 kabhi 0.2 ke andar nahi aaya, c = 0.36 nau steps me pahuncha phir hamesha 0.36 ki patti me uchhalta raha, c = 0.0069 itna chhota ki 200 steps me pahuncha hi nahi. Fixed step size ki koi achhi value nahi hoti: bada c = jaldi pahunchta par kabhi settle nahi hota; chhota c = accurate par khatam hi nahi hota.

Gradient is problem ko apne aap solve karta hai: jab tum minimum se door ho, slope bada hai → kadam bada; jab paas ho, slope chhota → kadam chhota; bottom par slope 0 → kadam 0. Lecture ki table (lr = 0.30, start w = 6): step size 2.054 → 0.828 → 0.334 → 0.134 → ... step 20 tak 0.00000. Kisi ne kadam chhota karne ko nahi bola, slope khud chhota hota gaya. Isliye update me η × gradient hota hai, sirf η nahi.

GD kab rokte hain? (stopping rules)

Exam Radar

  • Gradient = direction of steepest ascent (uphill). Negative gradient = steepest descent. Isliye update me minus sign.
  • Update rule: θ ← θ − η∇L(θ). η (ya α) = learning rate = step size; gradient = direction (+ slope ki tezi).
  • Agar gradient positive hai to θ ghatega (negative direction me move karega) — Worksheet 05 ka MCQ.
  • GD ek local, iterative, first-order method hai: sirf jahan khade ho wahan ka slope (first derivative) padhta hai. Poora landscape kabhi nahi dekhta.
  • Gradient har update ke baad nayi position par dobara nikalna padta hai.
  • η badalne se loss curve ki shape nahi badalti; sirf curve par kaunse points visit honge, wo badalta hai.

Galti mat karna

  • "θ + η∇L" likh diya to tum gradient ascent kar rahe ho — loss badhega. Descent me hamesha minus.
  • Gradient zero hone ka matlab "pakka global minimum" nahi hai. Wo local minimum, maximum, saddle point ya plateau bhi ho sakta hai.
  • Learning rate gradient ki direction nahi badalta (η positive hai); sirf kadam lamba-chhota karta hai.

Dimaag me aise baitha lo

"Gradient bole upar, hum chalein neeche. Slope bada to kadam bada, slope chhota to kadam chhota." θ ← θ − η∇L.

14.3 Haath se GD: f(w) = w², w₀ = 5, η = 0.1

Kahani

Sabse simple pahad lo: ek katori (bowl). Loss L(w) = w². Iska minimum sabko pata hai: w = 0 par L = 0. Par GD ko ye pata nahi. Use bas ek starting point diya gaya hai, w₀ = 5, aur learning rate η = 0.1. Dekhte hain wo khud neeche kaise aata hai.

L(w) = w²  ⇒  dLdw = 2w  ⇒  wnew = w − η · 2w
  • 2w = slope (power rule: w² ka derivative 2w).
  • η = 0.1 rakha to wnew = w − 0.2w = 0.8 w. Matlab har step me w apna 80% reh jata hai.

Chalo numbers se samjhein

Sawaal: L(w) = w², w₀ = 5, η = 0.1. Pehle 6 updates karo.

  1. Step 1: gradient g = 2 × 5 = 10, update w₁ = 5 − 0.1 × 10 = 5 − 1 = 4, loss 4² = 16 (pehle 25 tha).
  2. Step 2: g = 2 × 4 = 8, w₂ = 4 − 0.8 = 3.2, loss 3.2² = 10.24.
  3. Step 3: g = 6.4, w₃ = 3.2 − 0.64 = 2.56, loss 6.5536.
  4. Step 4: g = 5.12, w₄ = 2.56 − 0.512 = 2.048, loss 4.1943.
  5. Step 5: g = 4.096, w₅ = 2.048 − 0.4096 = 1.6384, loss 2.6844.
  6. Step 6: g = 3.2768, w₆ = 1.6384 − 0.32768 = 1.31072, loss 1.7180.
twtL = wt²gradient 2wtkadam η·gwt+1
05251014
141680.83.2
23.210.246.40.642.56
32.566.55365.120.5122.048
42.0484.19434.0960.40961.6384
51.63842.68443.27680.32771.3107
61.31071.71802.62140.26211.0486

Pattern dekho: wt = 5 × 0.8t. 10 steps baad w = 5 × 0.810 ≈ 0.5369, 20 steps baad ≈ 0.0576, 50 steps baad ≈ 0.00007. Kadam (last-se-pehle column) har baar chhota ho raha hai — 1, 0.8, 0.64, ... — kyunki slope chhota ho raha hai. Minimum (0) ke paas aate-aate GD apne aap dheema ho jata hai.

Jawab: w: 5 → 4 → 3.2 → 2.56 → 2.048 → 1.6384 → 1.3107; loss: 25 → 16 → 10.24 → 6.5536 → 4.1943 → 2.6844 → 1.7180.

14.4 η badlo, kismat badlo: wnew = (1 − 2η)·w

Upar wale formula ko thoda general karo: wnew = w − η·2w = (1 − 2η)·w. Har step par w ek fixed factor r = (1 − 2η) se multiply hota hai. Ab poori kahani is factor par tik gayi:

wt = (1 − 2η)t · w₀   (sirf L = w² ke liye)
  • Agar |1 − 2η| < 1 → har step w chhota → converge (0 tak pahunchega). Ye tab hota hai jab 0 < η < 1.
  • Agar 1 − 2η negative hai (η > 0.5) → sign har step palat-ta hai → zig-zag (minimum ke is paar, us paar).
  • Agar |1 − 2η| = 1 (η = 1) → size kabhi nahi badalta → hamesha oscillation.
  • Agar |1 − 2η| > 1 (η > 1) → size har step badhta → divergence (loss phat jata hai).

Chalo numbers se samjhein — same start w₀ = 5, alag η

ηfactor (1 − 2η)w₀w₁w₂w₃w₄Kya hua
0.010.9854.94.8024.7064.612bahut dheema (100 steps baad bhi ≈ 0.663)
0.10.8543.22.562.048smooth, aaram se converge
0.5050000ek hi step me seedha minimum!
0.75−0.55−2.51.25−0.6250.3125zig-zag, par converge
1.0−15−55−55hamesha oscillate, loss hamesha 25
1.1−1.25−67.2−8.6410.368diverge: loss 25 → 36 → 51.84 → 74.65 → 107.5
  1. η = 0.5: w₁ = 5 − 0.5 × (2 × 5) = 5 − 5 = 0. Gradient ab 2 × 0 = 0, to aage koi move nahi. Ek jump me bottom. (Ye "perfect" η sirf is ek function ke liye hai.)
  2. η = 1.0: w₁ = 5 − 1 × 10 = −5, w₂ = −5 − 1 × (−10) = 5. 5 aur −5 ke beech ping-pong forever. L(5) = L(−5) = 25, to loss ek inch bhi nahi girta.
  3. η = 1.1: w₁ = 5 − 1.1 × 10 = −6, w₂ = −6 − 1.1 × (−12) = 7.2, w₃ = 7.2 − 1.1 × 14.4 = −8.64. Har baar minimum ko itna zyada overshoot kiya ki doosri taraf pehle se bhi upar pahunch gaye. Thode aur steps aur numbers inf/NaN.

Jawab: L = w² ke liye: 0 < η < 0.5 → ek taraf se smooth converge; η = 0.5 → ek step me 0; 0.5 < η < 1 → zig-zag karke converge; η = 1 → hamesha oscillate; η > 1 → diverge.

-6 0 6 0 20 40 w η = 0.1 : dheere-dheere 0 tak 5 → 4 → 3.2 → 2.56 → ... -6 0 6 0 20 40 w η = 1.0 : 5 ↔ −5 ping-pong loss hamesha 25, kabhi nahi girta -13 0 13 0 80 160 w η = 1.1 : bahar bhaag gaya 5 → −6 → 7.2 → −8.64 → 10.37
Same katori L = w², same start w₀ = 5. Left: η = 0.1 dheere-dheere neeche. Beech: η = 1.0 me 5 aur −5 ke beech hamesha jhoola. Right: η = 1.1 me har jump pehle se bada (dhyaan do, right panel ka scale bada hai).

14.5 Teacher ke worksheet wale numericals: L(w) = (w − 3)²

Worksheet 05 aur Colab notebook dono me pahad thoda khiska hua hai: L(w) = (w − 3)². Minimum w = 3 par hai. Derivative chain rule se: dL/dw = 2(w − 3). Jab w < 3, gradient negative → minus-minus plus → w badhta hai (3 ki taraf). Jab w > 3, gradient positive → w ghatta hai. Dono taraf se 3 ki taraf.

Chalo numbers se samjhein — Worksheet 05 (solved + "your turn")

Sawaal: L(w) = (w − 3)², w₀ = 0, α = 0.25. Chaar updates karo.

  1. g₀ = 2(0 − 3) = −6, w₁ = 0 − 0.25(−6) = 1.5, L(w₁) = (1.5 − 3)² = 2.25
  2. g₁ = 2(1.5 − 3) = −3, w₂ = 1.5 − 0.25(−3) = 2.25, L(w₂) = 0.5625
  3. (Your turn wala) g₂ = 2(2.25 − 3) = −1.5, w₃ = 2.25 − 0.25(−1.5) = 2.625, L(w₃) = (−0.375)² = 0.140625
  4. g₃ = 2(2.625 − 3) = −0.75, w₄ = 2.625 + 0.1875 = 2.8125, L(w₄) = 0.03515625

Haan, L(w₃) = 0.1406 < L(w₂) = 0.5625, kyunki hum minimum ki taraf chal rahe hain aur step itna chhota hai ki 3 ko cross nahi kiya. Dhyaan do: yahan har step distance-to-3 aadha ho raha hai (3 → 1.5 → 0.75 → 0.375 → 0.1875), kyunki factor = 1 − 2α = 1 − 0.5 = 0.5.

Jawab: g₂ = −1.5, w₃ = 2.625, L(w₃) = 0.1406 — loss gira.

Worksheet ka sawaal: α = 0.25 se 1.2 kar diya to?

Factor = 1 − 2(1.2) = −1.4. |−1.4| > 1 → har step distance 1.4 guna aur side palat-ti hai. w₀ = 0 se: w₁ = 0 − 1.2 × (−6) = 7.2, w₂ = 7.2 − 1.2 × 8.4 = −2.88, phir 11.232, −8.5248, 19.1347 ... Loss: 17.64 → 34.57 → 67.77 → 132.8 → 260.3. 3 ke aas-paas bhi pahunche to agla kadam use aur door phek dega — divergence.

Jawab: α = 1.2 par w minimum (3) ko baar-baar overshoot karke oscillate karte hue bahar bhaagega; loss badhta jayega.

Aur teen chhote sawaal (Worksheet Q1 + Colab)

  1. Worksheet Q1: L = (w − 4)², w₀ = 0, α = 0.25: g₀ = 2(0 − 4) = −8, w₁ = 0 − 0.25 × (−8) = 2.
  2. Colab STOP 3: L = (w − 3)², w = −2, lr = 0.10: g = 2(−2 − 3) = −10, w_new = −2 − 0.1 × (−10) = −1, loss 25 → 16. Derivative negative tha, to w right (badha) gaya.
  3. Colab TODO 2: w = −1, η = 0.20: g = 2(−1 − 3) = −8, w_new = −1 − 0.2 × (−8) = 0.6, J(0.6) = (0.6 − 3)² = 5.76 (pehle 16 tha).

Colab ka boundary rule bhi yaad rakho: is quadratic (w − 3)² ke liye 0 < η < 1 converge, η = 1 same distance par sign flip (forever), η > 1 diverge. Ye boundary sirf is function ki hai, koi universal rule nahi.

Yahi worksheet loop Python me

Worksheet me blanks the: "measure loss, measure slope, step downhill". Bhar ke dekho:

Python · Worksheet 05 ka training loop

w = 0.0          # starting weight
alpha = 0.25     # learning rate
for step in range(4):
    loss = (w - 3) ** 2        # 1. loss napo (abhi ki uchaai)
    grad = 2 * (w - 3)         # 2. slope napo (dL/dw)
    w = w - alpha * grad       # 3. neeche ki taraf kadam
    print(step, w, loss)       # dhyaan: naya w, par loss purane w ka

Output

0 1.5 9.0
1 2.25 2.25
2 2.625 0.5625
3 2.8125 0.140625

Printed loss ek step peeche chalta hai, kyunki loss update se pehle nikala gaya. Real PyTorch loop me bhi aisa hi hota hai: jo loss print karte ho, wo update se pehle wale weights ka hota hai.

Exam Radar

  • L = w² ke liye: wnew = (1 − 2η)w. η = 0.5 → ek step me 0. η = 1 → 5, −5, 5, −5 (hamesha oscillation). η > 1 → diverge.
  • L = (w − 3)² ke liye bhi same factor (1 − 2η), bas distance (w − 3) par lagta hai.
  • Minimum ke paas gradient chhota → step chhota → GD apne aap dheema ho jata hai (fixed η par bhi).
  • "Loss rises rapidly; may become NaN" → η much too high. "Loss bounces near minimum" → η thoda zyada. "Loss falls very slowly" → η too low.

Galti mat karna

  • (w − 3)² ka derivative 2(w − 3) hai, 2w nahi. Andar wala hissa mat bhoolo.
  • Negative gradient ko minus karte waqt sign: w − α(−6) = w + 6α. Yahan sabse zyada galti hoti hai.
  • "η = 0.5 hamesha best" galat hai — ye sirf L = w² jaise curvature-2 bowl ke liye ek-step value hai.

Dimaag me aise baitha lo

w² par har step w ko (1 − 2η) se guna karo: 0.8 = aaram se, 0 = seedha ghar, −1 = jhoola, −1.2 = bhaag gaya.

14.6 Linear regression par ek poora GD step (3 data points)

Kahani

Chai wale Raju bhaiya ka hisaab: 1 kg cheeni par ₹3 ka kharcha aata hai, 2 kg par ₹5, 3 kg par ₹7 (sachchai: kharcha = 2 × kg + 1, par model ko ye nahi pata). Model ek seedhi line hai: ŷ = w·x + b. Ab do parameters hain, w (slope) aur b (bias / intercept). Pahad ab 2-D hai — ek katori jiske do axes w aur b hain. Chalo dekhte hain ek GD step kaise dono ko ek saath sudhaarta hai.

L(w, b) = 1N Σi (ŷi − yi)²,   ŷi = wxi + b
∂L∂w = 2N Σi (ŷi − yi)·xi     ∂L∂b = 2N Σi (ŷi − yi)
  • N = data points ki ginti (yahan 3). (ŷi − yi) = error ei (prediction minus truth).
  • Chain rule: (e)² ka derivative 2e, aur e = wx + b − y ka w ke respect me derivative x, b ke respect me 1. Isliye w wale gradient me extra xi aata hai, b wale me nahi.
  • Update dono par saath: w ← w − η·∂L/∂w,   b ← b − η·∂L/∂b. Dono gradients purane (w, b) se nikalte hain, phir dono ek saath update.

Chalo numbers se samjhein

Sawaal: data (1, 3), (2, 5), (3, 7). Start w = 0, b = 0, η = 0.1. Ek full-batch GD step karo.

  1. Predictions: ŷ = 0·x + 0 = [0, 0, 0]
  2. Errors: e = ŷ − y = [0 − 3, 0 − 5, 0 − 7] = [−3, −5, −7]
  3. Loss: L = (9 + 25 + 49) / 3 = 83 / 3 = 27.6667
  4. ∂L/∂w: (2/3) × [(−3)(1) + (−5)(2) + (−7)(3)] = (2/3) × (−3 − 10 − 21) = (2/3) × (−34) = −22.6667
  5. ∂L/∂b: (2/3) × (−3 − 5 − 7) = (2/3) × (−15) = −10
  6. Update w: w = 0 − 0.1 × (−22.6667) = 2.2667
  7. Update b: b = 0 − 0.1 × (−10) = 1.0
  8. Check — nayi predictions: [3.2667, 5.5333, 7.8], errors [0.2667, 0.5333, 0.8], naya loss (0.0711 + 0.2844 + 0.64) / 3 = 0.9956 / 3 = 0.3319

Dono gradients negative the (model sab kuch kam predict kar raha tha), to dono parameters badhe. Ek hi step me loss 27.67 se 0.33 aa gaya. Agle steps me w thoda 2 ke upar-neeche hoga aur b dheere-dheere 1 ki taraf jayega (200 steps baad w ≈ 2.0003, b ≈ 0.9992).

Jawab: ∂L/∂w = −22.6667, ∂L/∂b = −10; naye w = 2.2667, b = 1.0; loss 27.6667 → 0.3319.

14.7 Yahi kaam PyTorch me: pehle manual, phir optim.SGD

Hand calculation ko ab tensors + autograd (Chapter 13) se karte hain. Gradients PyTorch nikalega, update hum khud likhenge. Isse saaf dikhega ki optimizer.step() andar kya karta hai.

PyTorch · manual gradient descent (autograd + haath ka update)

import torch

# Raju bhaiya ka data: asli rishta y = 2x + 1 (model ko nahi pata)
X = torch.tensor([1.0, 2.0, 3.0])          # shape (3,)
y = torch.tensor([3.0, 5.0, 7.0])          # shape (3,)

# do parameters, dono 0 se shuru; requires_grad=True -> autograd inka gradient rakhega
w = torch.tensor(0.0, requires_grad=True)
b = torch.tensor(0.0, requires_grad=True)
lr = 0.1                                   # learning rate (eta)

for step in range(1, 201):
    y_hat = w * X + b                      # forward: (3,) predictions
    loss = ((y_hat - y) ** 2).mean()       # MSE: saare 3 points ka average (full batch)
    loss.backward()                        # autograd: w.grad = dL/dw, b.grad = dL/db

    if step == 1:
        print(f"step 1 grads: dL/dw = {w.grad.item():.4f}, dL/db = {b.grad.item():.4f}")

    with torch.no_grad():                  # update ko graph me record mat karo
        w -= lr * w.grad                   # w = w - lr * dL/dw
        b -= lr * b.grad                   # b = b - lr * dL/db

    w.grad.zero_()                         # PyTorch grads ko JODTA hai, isliye har step saaf karo
    b.grad.zero_()

    if step in (1, 2, 3, 10, 50, 100, 200):
        print(f"step {step:3d} | loss = {loss.item():.4f} | w = {w.item():.4f} | b = {b.item():.4f}")

Output

step 1 grads: dL/dw = -22.6667, dL/db = -10.0000
step   1 | loss = 27.6667 | w = 2.2667 | b = 1.0000
step   2 | loss = 0.3319 | w = 2.0178 | b = 0.8933
step   3 | loss = 0.0053 | w = 2.0439 | b = 0.9076
step  10 | loss = 0.0009 | w = 2.0347 | b = 0.9210
step  50 | loss = 0.0001 | w = 2.0131 | b = 0.9702
step 100 | loss = 0.0000 | w = 2.0039 | b = 0.9912
step 200 | loss = 0.0000 | w = 2.0003 | b = 0.9992

Dekho, step 1 ke grads bilkul hamare haath wale hain: −22.6667 aur −10. Aur step 1 ke baad w = 2.2667, b = 1.0 — wahi jo humne kaagaz par nikala. (Print hua loss update se pehle ka hai, isliye step 2 par 0.3319 dikh raha hai.)

Ab wahi cheez optimizer se

Haath ka update har baar likhna boring hai. optim.SGD yahi kaam karta hai: optimizer.step() = har parameter par p ← p − lr·p.grad, aur optimizer.zero_grad() = saare grads saaf.

PyTorch · same GD, optim.SGD + nn.MSELoss ke saath

import torch
import torch.nn as nn
import torch.optim as optim

X = torch.tensor([1.0, 2.0, 3.0])
y = torch.tensor([3.0, 5.0, 7.0])
w = torch.tensor(0.0, requires_grad=True)
b = torch.tensor(0.0, requires_grad=True)

optimizer = optim.SGD([w, b], lr=0.1)   # params ki list + learning rate
loss_fn = nn.MSELoss()                  # default reduction='mean' -> average

for step in range(1, 201):
    optimizer.zero_grad()               # 1. purane grads saaf
    y_hat = w * X + b                   # 2. forward
    loss = loss_fn(y_hat, y)            # 3. loss
    loss.backward()                     # 4. gradients
    optimizer.step()                    # 5. w = w - 0.1*w.grad ; b = b - 0.1*b.grad
    if step in (1, 2, 200):
        print(f"step {step:3d} | loss = {loss.item():.4f} | w = {w.item():.4f} | b = {b.item():.4f}")

Output

step   1 | loss = 27.6667 | w = 2.2667 | b = 1.0000
step   2 | loss = 0.3319 | w = 2.0178 | b = 0.8933
step 200 | loss = 0.0000 | w = 2.0003 | b = 0.9992

Bilkul same numbers. Matlab optim.SGD (bina momentum ke) = plain gradient descent ka update rule. Naam me "Stochastic" hai, par ye class khud ye decide nahi karti ki kitna data use hoga — wo hum aage dekhenge (14.15). Yahan humne saare 3 points ek saath diye, to ye full-batch GD tha.

Exam Radar

  • MSE gradients: ∂L/∂w = (2/N)Σ(ŷ − y)x, ∂L/∂b = (2/N)Σ(ŷ − y). Bias ke gradient me x nahi hota.
  • Training loop ka order: zero_grad() → forward → loss → backward() → step(). (zero_grad ko backward se pehle kahin bhi rakh sakte ho; Lab 5/6 me forward ke baad bhi likha hai.)
  • optim.SGD(params, lr=...): pehla argument parameters (ya model.parameters()), lr = learning rate. Momentum default 0 (momentum Chapter 16 me).
  • Gradients accumulate hote hain — zero_grad bhoolna = galat (bade) updates.

Galti mat karna

  • w = w - lr * w.grad (bina no_grad, bina in-place) likhne se w ek naya non-leaf tensor ban jata hai aur agle step me w.grad None milega. Manual update me with torch.no_grad(): w -= lr * w.grad likho.
  • w aur b dono ke gradient purani values se nikalo, phir update. Pehle w update karke naye w se b ka gradient nikalna galat hai (autograd me ye apne aap sahi hota hai).

14.8 Pahad ki shakal: convex vs non-convex, local/global minima, saddle, plateau

Kahani

Do jagah socho. Pehli: ek bada sa kadhai (wok) — kahin se bhi ball chhodo, wo beech me hi aakar rukegi. Doosri: Himachal ke pahad — kai ghaatiyan, kuch chhoti, kuch gehri, beech me sapaat maidan, aur kahin-kahin ghode ki kaathi (saddle) jaisi jagah jahan ek taraf se dekho to neeche, doosri taraf se dekho to upar. Kohre me patti baandh ke chalne wala aadmi kadhai me hamesha ghar pahunchega. Pahadon me? Wo kis ghaati me pahunchega, ye is par depend karta hai ki usne shuru kahan se kiya.

Definition

Convex function: bowl jaisa. Graph ke kinhi bhi do points ko seedhi line se jodo, to wo line graph ke neeche kabhi nahi jati. Iska fayda: har local minimum hi global minimum hai. (Strictly convex ho to minimum unique bhi hai.)

Non-convex function: ubad-khabad. Kai ghaatiyan (valleys), ridges, sapaat maidan aur saddle points ho sakte hain.

w loss ek hi global minimum Convex: katori (bowl) kahin se bhi shuru karo → wahi bottom w loss global min local min Non-convex: ubad-khabad pahad start point decide karta hai kaunsi ghaati milegi
Left: convex katori — ek hi neeche wala point. Right: Colab notebook ka non-convex curve L = 0.1w² + sin(2.2w) + 0.25 sin(0.7w) — chaar minima — teen sirf local (narangi) aur ek global (laal, w ≈ −0.71).

Saddle point — ghode ki kaathi

Saddle point wo jagah hai jahan gradient zero hai, par wo na minimum hai na maximum: ek direction me upar jata hai, doosri me neeche. Classic example: L(x, y) = x² − y². Point (0, 0) par: x ki taraf chalo to loss badhta hai (valley jaisa), y ki taraf chalo to loss ghatta hai (hill jaisa). Gradient = (2x, −2y) = (0, 0).

w loss plateau: gradient ≈ 0 chal rahe ho par aage nahi badh rahe Plateau (sapaat maidan) Saddle point: L = x² − y² x y ∇L = 0 laal: x ki taraf chalo to L badhta (valley jaisa) dashed: y ki taraf chalo to L ghatta (hill jaisa) ghode ki kaathi: ek taraf upar, doosri taraf neeche
Left: plateau — lamba sapaat hissa jahan gradient ≈ 0, training rengti hai. Right: L = x² − y² ke contour; beech (0,0) saddle point hai: laal rekhayein x-disha me upar jaati hain, dashed y-disha me neeche.

Chalo numbers se samjhein — saddle ke paas GD kaise atakta hai

Sawaal: L = x² − y², start (x, y) = (1, 0.001), η = 0.1. Update: x ← x − 0.1·(2x) = 0.8x, y ← y − 0.1·(−2y) = 1.2y.

stepxyL = x² − y²gradient ka size
010.0010.9999992.0
50.32770.00250.10740.6554
100.10740.00620.01150.2151
200.01150.0383−0.00130.0801
300.00120.2374−0.05630.4748

Pehle 20 steps GD saddle ki taraf khinchta hai: gradient chhota hota jata hai (2.0 → 0.08), loss lagbhag flat. Dekhne wale ko lagega "converge ho gaya!". Par y chupke se 1.2 guna badh raha hai, aur step 30 tak GD saddle se nikal kar neeche (negative loss ki taraf) bhaagne lagta hai. Aur agar y shuru me bilkul 0 hota, to y hamesha 0 rehta aur GD saddle par hamesha ke liye atak jata. Thoda sa noise (jaise SGD ka) ise dhakka de deta.

Jawab: saddle ke paas gradient ≈ 0 → training rengti hai aur "converged" lagti hai; noise ya chhoti si disturbance ise nikaal deti hai.

Plateau — sapaat maidan

Plateau ek lamba flat area hai jahan gradient lagbhag zero hai (par wo minimum nahi). Kadam = η × (lagbhag 0) = lagbhag 0. Training curve me loss bahut der tak sapaat dikhta hai, phir achanak girta hai. Saturated sigmoid/tanh neurons (Chapter 9) aise flat regions banate hain.

JagahGradientAas-paas ki shakalPlain GD ka kya hota hai
Global minimum0har taraf uparruk jata hai — yahi chahiye
Local minimum0har taraf upar (sirf locally)ruk jata hai — par better jagah kahin aur hai
Saddle point0kuch disha upar, kuch neechebahut dheema ho jata hai, atak sakta hai
Plateau≈ 0sab taraf lagbhag flatrengta hai, loss curve flat
Local maximum0har taraf neecheGD yahan rukna nahi chahta; zara sa hilte hi neeche

Kaun bachata hai? Lecture slide ka seedha message: local minima aur saddle points dono me gradient zero hai — wahi signal jisse hum rukte hain. Isliye randomness (noise) add karna motivated hai: SGD ka noise ghaati se ghaati koodne me madad karta hai (Prince Fig. 6.5: plain GD galat ghaati me phas gaya, SGD noise ki wajah se global minimum tak pahuncha). Momentum (Chapter 16) bhi flat/saddle jagah se "lurhak" ke nikalne me madad karta hai.

Exam Radar

  • Convex: har local minimum = global minimum; GD (sahi η ke saath) global minimum tak pahunchta hai. Linear regression with MSE = convex.
  • Neural network loss = non-convex. GD global minimum ki guarantee nahi deta; result initialization (start point) par depend karta hai.
  • Saddle point: gradient 0, ek direction me minimum jaisa, doosri me maximum jaisa (L = x² − y² at origin). High-dimensional networks me bure local minima se zyada common problem saddle points hain.
  • Gradient ≈ 0 ho sakta hai: minimum, maximum, saddle ya plateau par — sirf gradient dekh kar pata nahi chalta kaunsa hai.
  • 3-D loss plot ke do horizontal axes = do parameters (training examples nahi); vertical axis = loss.

Galti mat karna

  • "Non-convex hai to training impossible" — galat. Non-convex ka matlab bas itna ki kai valleys/saddles ho sakte hain; deep learning roz non-convex problems solve karti hai.
  • "Zyada weights = non-convex" — galat. Linear regression 1000 weights ke saath bhi convex hai. Non-convexity non-linear layers ke composition se aati hai.
  • Plateau aur minimum ko confuse mat karo: dono jagah loss curve flat dikhta hai, par plateau ke baad loss phir gir sakta hai.

Dimaag me aise baitha lo

Kadhai (convex) me ball hamesha beech me; pahad (non-convex) me ball wahan jahan se chhodi. Saddle = ghode ki kaathi, plateau = sapaat maidan — dono par gradient so jata hai.

14.9 Teen tarike data se poochne ke: poori class, ek student, chhota group

Kahani

Class me 60 bachche hain aur class monitor ko decide karna hai ki picnic kis taraf jaana hai. Har bachcha ek "opinion" hai ki kis taraf jaana behtar hai. Teen tarike hain:

(1) Poori class se poocho, sabka average nikaalo, phir ek kadam lo. Bilkul sahi direction, par 60 logon se poochne me poora period nikal gaya. (2) Kisi ek random bachche se poocho aur turant kadam lo. Bahut tez, par ek bachcha ajeeb soch wala bhi ho sakta hai — direction hil-dul karti hai. (3) 4-5 bachchon ka chhota group poocho, unka average lo, kadam lo. Tez bhi, aur kaafi had tak sahi bhi.

Worksheet 05 ki bhasha me: Vanilla GD = "ask the whole class", SGD = "ask one student", Mini-batch = "ask a small group". Har training example ek student hai jo bata raha hai ki downhill kidhar hai.

Asal baat ye hai: humara loss saare N examples ka average hai, to sachcha gradient bhi saare per-example gradients ka average hai:

L(θ) = 1N Σi=1N Li(θ)  ⇒  ∇L(θ) = 1N Σi=1N ∇Li(θ)
  • Li(θ) = sirf i-th example ka loss (jaise (ŷi − yi)²). ∇Li = us ek example ka gradient = us ek student ki "rai".
  • Teeno types me update rule same hai (θ ← θ − η × gradient). Farak sirf itna: gradient kitne examples se estimate kiya — N, 1, ya B.

Definition — chaar shabd jo exam me pakke aayenge

Batch (ya mini-batch): examples ka wo group jo ek update ke liye use hota hai. Batch size (B): us group me kitne examples.

Iteration / step / update: ek batch par forward → loss → backward → optimizer.step() = ek parameter update.

Epoch: poore training dataset ka ek complete pass — har example ek baar dikh gaya. Ek epoch me kai iterations ho sakte hain.

Slide ki line yaad rakho: training = choose data → compute loss → compute gradient → update parameters. Sirf "choose data" ka size badalta hai.

Batch GD (B = N = 10) 1 2 3 4 5 6 7 8 9 10 → 1 update / epoch SGD (B = 1) 1 2 3 4 5 6 7 8 9 10 → 10 updates / epoch Mini-batch (B = 4) 1 2 3 4 5 6 7 8 9 10 → 3 updates / epoch har lal jhoola (arc) = ek group = ek gradient = ek update (θ ← θ − η∇L) teeno me ek epoch = saare 10 rows ek baar dekhna; farak sirf "kitne rows ke baad update"
Same 10 rows, teen tareeke. Har laal arc = ek group = ek update. Batch GD: 1 update/epoch, SGD: 10 updates/epoch, Mini-batch (B = 4): groups 4, 4, 2 → 3 updates/epoch (ceil(10/4) = 3).

14.10 Vanilla / Batch (Full-batch) Gradient Descent

Definition

Batch Gradient Descent (Vanilla GD, Full-batch GD): har update se pehle saare N training examples par forward pass, unka average loss, ek exact gradient, phir ek update. Ek epoch = ek update.

θ ← θ − α · 1N Σi=1N ∇Li(θ)
  • N = poore training set ka size. Σ saare examples par chalta hai.
  • Slide 25 ka flow: All N rows → forward pass → average error/loss → calculate gradient → θ ← θ − η∇J. Key idea: poora dataset dekhne ke baad hi ek update.

14.6 wala linear regression step asal me Batch GD hi tha: humne teeno points ka average gradient leke ek update kiya. Us example me N = 3 tha, to sasta tha. Ab socho N = 50,000 (Worksheet 05 ka sawaal): full-batch ka gradient accurate hota hai, par har ek update ke liye 50,000 examples dekhne padte hain → har update mehenga (expensive).

Lecture ka memory wala jhatka

Lecture me 2,000,000 customers × 100 features ka churn data tha aur model 100 → 512 → 256 → 1. Har layer ka output har row ke liye memory me rakhna padta hai (backward ke liye). Ginti: input 0.80 GB + fc1 output 4.10 GB + act1 4.10 GB + fc2 2.05 GB + act2 2.05 GB = 13.09 GB sirf forward; backward me lagbhag utna hi aur → ~26.18 GB ek step ke liye. GPU me sirf 14.7 GB free tha → CUDA out of memory. Lecture ka point: batch GD yahan "slow" nahi tha, impossible tha. Sahi algorithm hamare hardware par chal hi nahi sakta.

Fayde (Advantages)

  • Exact gradient — sabse accurate downhill direction.
  • Smooth, stable convergence, koi jitter nahi.
  • Deterministic / reproducible: same start → har run me same path.
  • Chhote datasets (jo memory me fit hon) ke liye badhiya.
  • Convex problem par sahi η ke saath global minimum tak pahunchta hai.

Nuksaan (Disadvantages)

  • Ek update ke liye poora dataset process — bade data par bahut slow.
  • Poora dataset (aur uski activations) ek saath memory me chahiye.
  • Deterministic path → local minima / saddle points me hamesha ke liye phas sakta hai (koi noise nahi jo dhakka de).
  • Deep learning ke laakhon examples ke liye impractical. Ek epoch me sirf 1 update, to bahut epochs chahiye.

14.11 Stochastic Gradient Descent (SGD) — ek student se poocho

Definition

Stochastic Gradient Descent: har step par ek randomly chuna gaya example lo, sirf uska gradient nikalo, aur turant update karo. N examples → N updates per epoch. "Stochastic" = random.

θ ← θ − α · ∇Li(θ),    i = random example
  • ∇Li = sirf ek example ka gradient. Ye sachche gradient ka noisy estimate hai: average me sahi, kisi ek step par galat (ek customer hamesha thoda "ajeeb" hota hai).
  • Epoch ke shuru me data shuffle karte hain, phir ek-ek karke saare examples (without replacement) — taaki har epoch me order alag ho.

Chalo numbers se samjhein — SGD ka ek epoch (wahi 3 points)

Sawaal: data (1, 3), (2, 5), (3, 7), start w = 0, b = 0, η = 0.1, order 1 → 2 → 3. Ek example ka loss (ŷ − y)², gradients: ∂/∂w = 2e·x, ∂/∂b = 2e.

  1. Example 1 (x = 1, y = 3): ŷ = 0, e = −3, g_w = 2(−3)(1) = −6, g_b = −6 → w = 0 + 0.6 = 0.6, b = 0 + 0.6 = 0.6
  2. Example 2 (x = 2, y = 5): ŷ = 0.6×2 + 0.6 = 1.8, e = −3.2, g_w = 2(−3.2)(2) = −12.8, g_b = −6.4 → w = 0.6 + 1.28 = 1.88, b = 0.6 + 0.64 = 1.24
  3. Example 3 (x = 3, y = 7): ŷ = 1.88×3 + 1.24 = 6.88, e = −0.12, g_w = 2(−0.12)(3) = −0.72, g_b = −0.24 → w = 1.88 + 0.072 = 1.952, b = 1.24 + 0.024 = 1.264
  4. Epoch ke baad poore data par loss: errors [0.216, 0.168, 0.12], L = (0.046656 + 0.028224 + 0.0144)/3 = 0.0298

Compare karo: Batch GD ne ek epoch me 1 update karke loss 0.3319 tak pahunchaya (14.6). SGD ne usi ek epoch me 3 updates karke 0.0298. Yahi Lab 6 ka sabak hai: same epochs me SGD ka loss zyada tezi se girta hai, kyunki updates zyada hain.

Jawab: ek SGD epoch (3 updates) ke baad w = 1.952, b = 1.264, loss ≈ 0.0298.

Noise: bimari bhi, dawai bhi

Toh SGD tez hai ya slow? (sabse bada confusion)

Do alag sawaal hain, Lab 6 ne dono alag kiye:

SGD ke fayde

  • Har step bahut sasta — sirf ek example.
  • Ek epoch me N updates → learning jaldi shuru.
  • Noise local minima / saddle se nikaalne me madad karta hai.
  • Memory bahut kam (lecture: ~13.1 KB per update); achha generalize karta hai.
  • Online learning: data aate-aate seekh sakta hai.

SGD ke nuksaan

  • Bahut noisy path — loss smoothly girne ke bajaye uchhalta hai, kabhi upar bhi.
  • Kabhi poori tarah settle nahi hota; minimum ke aas-paas ghoomta rehta hai.
  • Hardware (GPU) ka efficient use nahi — koi parallel kaam nahi.
  • Converge karne ke liye usually decreasing learning rate chahiye.

14.12 Mini-batch Gradient Descent — chhota group, deep learning ka default

Definition

Mini-batch GD: data ko B size ke chhote random groups (mini-batches) me baanto. Har mini-batch ka average gradient → ek update. Ek epoch = ceil(N / B) updates. B usually 32 se 512 (slide), aksar powers of 2: 32, 64, 128, 256.

∇̂L(θ) = 1B Σi ∈ 𝓑t ∇Li(θ),    θ ← θ − α · ∇̂L(θ)
  • 𝓑t = step t ka mini-batch (B examples ka set). ∇̂ (hat) = "estimate", kyunki ye poore data ka nahi, sirf batch ka gradient hai.
  • B examples ka average → single example se kam noisy, saare examples se bahut sasta.
  • Batches without replacement nikalte hain jab tak saara data use na ho jaye (yahi ek epoch).
  • Do special cases: B = 1 → SGD, B = N → Batch GD. Mini-batch dono ka general roop hai — sab ek hi dial hai: batch size.

Chalo numbers se samjhein — mini-batch B = 2 (wahi 3 points)

Sawaal: data (1, 3), (2, 5), (3, 7), start w = b = 0, η = 0.1, B = 2. Updates per epoch = ceil(3/2) = 2; doosre batch me sirf 1 example bachega.

  1. Shuffle ne order diya: batch 1 = {ex 1, ex 2}, batch 2 = {ex 3}.
  2. Batch 1: ŷ = [0, 0], e = [−3, −5], L = (9 + 25)/2 = 17; g_w = (2/2)[(−3)(1) + (−5)(2)] = −13, g_b = (2/2)(−3 − 5) = −8 → w = 1.3, b = 0.8
  3. Batch 2: ŷ = 1.3×3 + 0.8 = 4.7, e = −2.3; g_w = 2(−2.3)(3) = −13.8, g_b = −4.6 → w = 1.3 + 1.38 = 2.68, b = 0.8 + 0.46 = 1.26; poore data par loss = (0.94² + 1.62² + 2.3²)/3 = 2.9327
  4. Ab agar shuffle ne doosra order diya hota: batch 1 = {ex 1, ex 3}: e = [−3, −7], g_w = (−3·1 − 7·3) = −24, g_b = −10 → w = 2.4, b = 1.0; batch 2 = {ex 2}: ŷ = 5.8, e = 0.8, g_w = 3.2, g_b = 1.6 → w = 2.08, b = 0.84, loss = 0.0043

Same data, same η, same start — sirf shuffle ka order badla aur ek epoch ke baad result alag (2.9327 vs 0.0043). Yahi "stochastic noise" hai. Batch GD me ye kabhi nahi hota: saare examples ka average order par depend nahi karta, to wo hamesha same (deterministic) hai. Kayi epochs ke baad mini-batch bhi minimum ke paas pahunch jata hai — noise lambi race me average ho jata hai.

Jawab: B = 2, N = 3 → 2 updates/epoch (sizes 2 aur 1); result shuffle order par depend karta hai.

Mini-batch kyun jeet-ta hai

Lecture ka nichod: teen constraints hain — memory me fit ho, hardware busy rahe, updates kaafi frequent hon. Full batch memory me fail (26.18 GB chahiye), SGD speed me fail (32.7 s/epoch). Mini-batch of 128: sirf 1.7 MB memory, 3,907 updates/epoch, 0.41 s/epoch. Mini-batch koi "compromise" nahi, ye akela option hai jo teeno shart ek saath poori karta hai. Isliye har framework ka default yahi hai, aur batch_size=32 machine learning ki sabse zyada copy hone wali line hai.

Fayde

  • SGD ki speed + Batch GD ki stability ka balance.
  • Itna noise ki minima se nikal sake, itna kam ki smoothly converge kare.
  • GPU parallelism ke liye perfect — poora batch ek saath vectorized (matrix multiply).
  • Memory se bade datasets par bhi chalta hai.

Tune karne wali cheezein

  • Batch size ek naya hyperparameter ban jata hai.
  • Bahut bade batches helpful noise kho dete hain aur kabhi worse generalize karte hain.
  • Learning rate ko aksar batch size ke saath scale karna padta hai (bada batch → thoda bada LR).
  • Gradient ab bhi exact nahi — par practice me ye theek hai.

14.13 Hisaab-kitaab: epoch, iteration, batch (N = 1000, B = 32)

iterations per epoch = ⌈ N / B ⌉     total updates = epochs × ⌈ N / B ⌉
  • ⌈ ⌉ = ceil = upar ki taraf round (3.2 → 4). Kyunki bache hue examples bhi ek (chhota) batch banate hain.
  • Aakhri batch ka size = N − (⌈N/B⌉ − 1) × B (agar N, B se poora divide na ho).
  • drop_last=True ho to aakhri adhoora batch phenk diya jata hai → iterations = ⌊N / B⌋ (floor, neeche round).

Chalo numbers se samjhein

Sawaal: N = 1000 training examples, batch size B = 32, 10 epochs. Kitne iterations per epoch? Aakhri batch kitna bada? Total updates? drop_last=True ho to?

  1. 1000 / 32 = 31.25 → poore 32-size ke batches: 31 (31 × 32 = 992 examples)
  2. Bache: 1000 − 992 = 8 examples → ek aur chhota batch of 8
  3. Iterations per epoch: ⌈31.25⌉ = 32 (31 full + 1 batch of 8)
  4. 10 epochs me total updates: 10 × 32 = 320
  5. drop_last=True: ⌊31.25⌋ = 31 iterations/epoch, har epoch 8 examples skip (shuffle ki wajah se har epoch alag 8), 10 epochs me 310 updates

Jawab: 32 iterations/epoch (aakhri batch 8 ka), 320 updates in 10 epochs; drop_last=True → 31/epoch, 310 total.

SituationNBUpdates / epoch
Worksheet MCQ1,000501000/50 = 20
Batch GD (Worksheet)50,00050,0001 (har update me 50,000 examples)
Lab 6 (Social Network Ads, train rows)320320 / 1 / 321 / 320 / 10
Lecture timing table500,00032 / 128 / 1,024 / 8,19215,625 / 3,907 / 489 / 62
MNIST-size data60,00064⌈937.5⌉ = 938 (aakhri batch 32)

Ek baat hamesha yaad rakho: ek epoch me har type har example ko ek hi baar dekhta hai (total N samples processed). Farak sirf ye hai ki un N ko kitne updates me baanta gaya.

14.14 Teeno ka side-by-side comparison

PropertyBatch (Full / Vanilla)Stochastic (SGD)Mini-batch
Data per updateSaare N1 exampleB examples (e.g. 32)
Updates per epoch1N⌈N / B⌉
GradientExact (true average)Bahut noisy estimateThoda noisy estimate
Convergence pathSmooth, seedhaBahut zig-zag / jitteryHalka wiggle (mildly noisy)
Cost per updateBahut mehengaBahut sastaModerate
MemoryHigh (saara data + activations)Bahut kamModerate
Wall-clock per epochSabse kam (agar memory me fit ho)Sabse zyada (overhead)Kam (full batch ke kareeb)
Loss drop per epochSabse kam (1 update)Sabse zyadaBeech me, kaafi achha
DeterminismDeterministic (same path har run)Random (shuffle par depend)Random
Escapes local minima / saddlesNahiHaanHaan
Minimum par settleHaan (sahi η par)Nahi, aas-paas ghoomta; LR decay chahiyeLagbhag; LR decay se behtar
GPU efficiencyAchhaKharabExcellent
Kab use kareinChhota dataset jo memory me fit hoOnline/streaming data; akela rarelyDeep learning ka default
minimum start Batch GD: smooth, seedha Mini-batch: halka wiggle SGD: zig-zag, noisy contour lines = same loss wali rekhayein (map jaisi); beech me minimum
Contour map (har ellipse = same loss). Same start se: Batch GD (kaali moti line) seedha smooth path; Mini-batch (narangi) halka sa hilta hua; SGD (laal patli) zig-zag karta, minimum ke paas bhi kaanpta rehta hai.

14.15 PyTorch me: TensorDataset + DataLoader — batch_size hi type decide karta hai

Kahani

Maa ne 1000 laddoo banaye hain aur tumhe mehmaanon ko serve karne hain. TensorDataset = laddoo ka bada dabba (har laddoo = ek (x, y) pair). DataLoader = serving wala bhaiya jo dabbe se laddoo nikal ke thaali me rakhta hai — kitne ek thaali me (batch_size), pehle hila ke (shuffle) ya line se, aur aakhri adhoori thaali rakhni hai ya nahi (drop_last). Optimizer ko farak nahi padta thaali me kitne laddoo hain — wo bas har thaali ke baad ek update karta hai.

Sabse important baat: PyTorch me teeno types ke liye optimizer same hai — optim.SGD. Kaunsa type chalega, ye DataLoader ka batch_size decide karta hai: batch_size=len(dataset) → Batch GD, batch_size=1 → SGD, batch_size=32 → Mini-batch GD.

API ka matlab

PyTorch · DataLoader ka batch_size = GD ka type

import torch
from torch.utils.data import TensorDataset, DataLoader

torch.manual_seed(0)
X = torch.randn(1000, 3)          # 1000 rows, 3 features  -> (1000, 3)
y = torch.randn(1000, 1)          # (1000, 1)
dataset = TensorDataset(X, y)     # dataset[i] = (X[i], y[i])
print(len(dataset))
print(dataset[0][0].shape, dataset[0][1].shape)

for bs in [len(dataset), 1, 32]:  # Batch GD, SGD, Mini-batch
    loader = DataLoader(dataset, batch_size=bs, shuffle=True, drop_last=False)
    sizes = [xb.shape[0] for xb, yb in loader]      # har batch ka size
    print(f"batch_size={bs:4d} -> len(loader) = {len(loader):4d} updates/epoch, last batch = {sizes[-1]}")

loader = DataLoader(dataset, batch_size=32, shuffle=True, drop_last=True)
print("drop_last=True ->", len(loader), "updates/epoch")
xb, yb = next(iter(loader))       # ek batch nikaal ke shape dekho
print(xb.shape, yb.shape)         # (32, 3) aur (32, 1)

Output

1000
torch.Size([3]) torch.Size([1])
batch_size=1000 -> len(loader) =    1 updates/epoch, last batch = 1000
batch_size=   1 -> len(loader) = 1000 updates/epoch, last batch = 1
batch_size=  32 -> len(loader) =   32 updates/epoch, last batch = 8
drop_last=True -> 31 updates/epoch
torch.Size([32, 3]) torch.Size([32, 1])

Poora nested loop: bahar epochs, andar batches

Ab ek hi train() function, ek hi optimizer, sirf batch_size badal ke teeno chalate hain. Data: 1000 points, y = 2x + 1 (bina noise). Model nn.Linear(1, 1) (Chapter 5), sabko 0 se shuru karte hain taaki comparison fair ho (Lab 6 me build_model() isi liye har run ko fresh model deta tha).

PyTorch · same epochs, teen batch sizes

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader

N = 1000
X = torch.linspace(0, 2, N).view(-1, 1)    # (1000, 1)  x = 0 se 2
y = 2 * X + 1                              # (1000, 1)  asli rishta y = 2x + 1
dataset = TensorDataset(X, y)

def train(batch_size, epochs=20, lr=0.1):
    model = nn.Linear(1, 1)
    with torch.no_grad():                  # demo ke liye: sab runs 0 se shuru
        model.weight.fill_(0.0)
        model.bias.fill_(0.0)
    loader = DataLoader(dataset, batch_size=batch_size, shuffle=True, drop_last=False)
    optimizer = optim.SGD(model.parameters(), lr=lr)   # teeno ke liye SAME optimizer
    loss_fn = nn.MSELoss()
    updates = 0
    for epoch in range(epochs):            # bahar ka loop: epochs
        for xb, yb in loader:              # andar ka loop: batches = iterations
            optimizer.zero_grad()          # 1. purane grads saaf
            loss = loss_fn(model(xb), yb)  # 2-3. forward + batch ka average loss
            loss.backward()                # 4. batch ka gradient
            optimizer.step()               # 5. ek update
            updates += 1
    with torch.no_grad():                  # poore data par final loss
        full_loss = loss_fn(model(X), y).item()
    print(f"batch_size={batch_size:4d} | updates = {updates:5d} | "
          f"w = {model.weight.item():.4f} | b = {model.bias.item():.4f} | loss = {full_loss:.4f}")

train(batch_size=N)    # Batch GD   : 1 update / epoch
train(batch_size=1)    # SGD        : 1000 updates / epoch
train(batch_size=32)   # Mini-batch : 32 updates / epoch

Output

batch_size=1000 | updates =    20 | w = 1.8163 | b = 1.2168 | loss = 0.0124
batch_size=   1 | updates = 20000 | w = 2.0000 | b = 1.0000 | loss = 0.0000
batch_size=  32 | updates =   640 | w = 2.0000 | b = 1.0000 | loss = 0.0000

Same 20 epochs. Batch GD ko sirf 20 updates mile — abhi bhi w = 1.8163 (target 2). SGD ko 20,000 aur mini-batch ko 640 updates mile — dono ne asli line (2, 1) pakad li. Ye data noise-free hai, isliye yahan SGD bhi aakhir me exact settle ho gaya (har point same line par hai, to perfect w par har example ka gradient bhi 0). Real noisy data par SGD minimum ke aas-paas kaanpta rehta. Agar tum time.time() se time napoge to SGD wala run sabse slow niklega — 20,000 chhote steps ka overhead.

14.16 Lab 6 jaisa: haath se batching (randperm) aur custom Dataset

Lab 6 Part 1 me DataLoader nahi tha. Batching haath se ki gayi thi: har epoch torch.randperm(n) se rows ka random order, phir batch_size ke tukdon me slice. Ye samajhna zaroori hai kyunki DataLoader andar yahi karta hai.

PyTorch · Lab 6 Part 1 ka fit() (Social Network Ads: Age, Salary → Purchased)

def fit(model, epochs, batch_size, lr=0.1):
    optimizer = torch.optim.SGD(model.parameters(), lr=lr)
    criterion = nn.BCELoss()                       # sigmoid output + BCE (binary)

    for epoch in range(epochs):
        perm = torch.randperm(n_train)             # har epoch rows ka naya random order (shuffle)
        epoch_loss, n_batches = 0.0, 0
        for i in range(0, n_train, batch_size):    # 0, B, 2B, ... tak kood
            idx = perm[i:i + batch_size]           # is batch ke indices
            xb, yb = X_train_t[idx], y_train_t[idx]

            optimizer.zero_grad()                  # 1. purane grads saaf
            loss = criterion(model(xb), yb)        # 2+3. forward + loss
            loss.backward()                        # 4. dL/dw
            optimizer.step()                       # 5. W_n = W_0 - lr * dL/dw

            epoch_loss += loss.item()
            n_batches += 1
        print(f'Epoch {epoch + 1:2d}/{epochs} - loss: {epoch_loss / n_batches:.4f}')

# Batch GD : fit(build_model(), epochs=10, batch_size=n_train)  -> 1 update/epoch
# SGD      : fit(build_model(), epochs=10, batch_size=1)        -> 320 updates/epoch

range(0, n_train, batch_size) kitni baar chalega? ⌈320/B⌉ baar — B = 320 par 1, B = 1 par 320, B = 32 par 10. Lab ke asli printed results (10 epochs each):

Run (Lab 6 notebook output)Epoch 1 lossEpoch 10 lossTotal time
Batch GD (batch_size = 320)0.66610.62690.01 s
SGD (batch_size = 1)0.37400.24732.03 s

Lab 6 Part 2: apna Dataset class

TensorDataset ready-made hai. Par kabhi data CSV/images se aata hai — tab apni Dataset class likhte hain. Usme teen methods: __init__ (data rakh lo), __len__ ("kitne samples?"), __getitem__(index) ("sample i do" — ek (features, label) pair). DataLoader in dono ko call karke batches khud banata hai.

PyTorch · Lab 6 Part 2 style CustomDataset

import torch
from torch.utils.data import Dataset, DataLoader

class CustomDataset(Dataset):
    def __init__(self, features, labels):
        self.features = features
        self.labels = labels

    def __len__(self):
        return self.features.shape[0]                      # kitne samples

    def __getitem__(self, index):
        return self.features[index], self.labels[index]    # EK (features, label) pair

X_toy = torch.arange(10, dtype=torch.float32).view(5, 2)  # 5 samples, 2 features
y_toy = torch.tensor([0, 1, 0, 1, 1])
toy = CustomDataset(X_toy, y_toy)
print(len(toy), toy[2])

for xb, yb in DataLoader(toy, batch_size=2, shuffle=False):
    print(xb.tolist(), '->', yb.tolist())

Output

5 (tensor([4., 5.]), tensor(0))
[[0.0, 1.0], [2.0, 3.0]] -> [0, 1]
[[4.0, 5.0], [6.0, 7.0]] -> [0, 1]
[[8.0, 9.0]] -> [1]

5 samples, batch_size 2 → ⌈5/2⌉ = 3 batches (2, 2, 1). Lab 6 ne yahi class 10-row toy set aur 569-row breast-cancer data (30 features, batch_size=32, shuffle=True for train, False for test, 25 epochs, test accuracy 0.9783) dono par use ki. Evaluation me model.eval() + torch.no_grad().

Exam Radar

  • Batch GD: 1 update/epoch, exact gradient, smooth, deterministic, memory-heavy, slow on big data, stuck in local minima/saddles.
  • SGD: 1 sample/update, N updates/epoch, noisy zig-zag, cheap updates, escapes shallow minima, fluctuates near minimum → needs LR decay, shuffle each epoch, GPU-inefficient.
  • Mini-batch: B samples, ⌈N/B⌉ updates/epoch, typical B = 32/64/128/256 (powers of 2, compulsory nahi), GPU-vectorized, deep learning default. B = 1 → SGD, B = N → Batch GD.
  • N = 1000, B = 32 → 32 iterations/epoch (last batch 8); 10 epochs → 320 updates; drop_last=True → 31/epoch. N = 1000, B = 50 → 20.
  • Noise ka order: SGD > mini-batch (128) > full batch. Mini-batch ka main purpose: cost aur gradient noise ka balance.
  • PyTorch: optim.SGD teeno ke liye; type DataLoader(batch_size=...) decide karta hai. DataLoader defaults: batch_size=1, shuffle=False, drop_last=False.
  • Custom Dataset: __len__ aur __getitem__ zaroori; __getitem__ ek sample deta hai, batching DataLoader karta hai.
  • 2 million images + GPU → mini-batch (memory me fit, GPU busy, kaafi updates). Full batch memory me fit nahi, true SGD hardware waste.

Galti mat karna

  • "Epoch = iteration" — galat. Epoch = poora data ek baar; iteration = ek batch ka ek update. 1 epoch = ⌈N/B⌉ iterations.
  • Updates per epoch ke liye floor nahi, ceil (jab tak drop_last=True na ho). 1000/32 → 32, 31 nahi.
  • optim.SGD ka matlab ye nahi ki batch size 1 hai. Naam historical hai; asal batch size DataLoader me hai.
  • Validation/test loader me shuffle=True ki zaroorat nahi; train loader me shuffle bhoolna (khaas kar sorted data, jaise pehle saare class-0 phir class-1) training bigaad deta hai.
  • "SGD ek epoch me jaldi khatam hota hai" — galat. Per epoch wall-clock me SGD sabse slow hai; sirf per-epoch learning me aage hai.

Dimaag me aise baitha lo

Poori class (Batch) = sahi par dheere, ek student (SGD) = tez par kaanpta, chhota group (Mini-batch) = dono ka maza. Teeno ka optimizer ek — farak sirf thaali me kitne laddoo (batch_size).

Chapter 15

Learning Rate: Fixed vs Scheduling

Step size · too small / too large / just right · LR range test · fixed LR · step decay · exponential · 1/t · cosine annealing · warmup · ReduceLROnPlateau · cyclical / one-cycle · torch.optim.lr_scheduler

Chapter 14 me humne direction (gradient) aur data (batch size) samjha. Ab bacha ek knob jo sab kuch bana bhi sakta hai aur bigaad bhi sakta hai: learning rate. Lecture slide ka title hi tha: "The one knob you must tune". Is chapter me: η kaise chunein, fixed rakhein ya time ke saath badlein (scheduling), aur PyTorch me schedulers kaise lagate hain.

15.1 Kahani: learning rate = kadam ka size

Kahani

Sardi ki subah, bathroom me geyser wala nal. Paani ka temperature theek karna hai. Agar tum knob ko bahut thoda-thoda ghumao, to sahi temperature tak pahunchne me 10 minute lag jayenge aur tum thand me kaanpte rahoge. Agar ek jhatke me poora ghuma do, to paani ubalta garam, phir ghabra ke ulta poora ghumaya to barf jaisa thanda — garam, thanda, garam, thanda — kabhi sahi nahi. Samajhdaar insaan shuru me thoda bada ghumata hai, aur jaise-jaise sahi temperature ke paas aata hai, chhote-chhote adjustments karta hai.

Kohre wale pahad par bhi yahi: kadam bahut chhote → raat bhar chalte raho; kadam bahut lambe → ghaati ke paar doosre pahad par chadh jaoge; shuru me lambe, neeche aate-aate chhote → sabse achha. Ye "kadam ka size" hi learning rate hai, aur "dheere-dheere chhota karna" hi learning rate scheduling.

Definition

Learning rate (η, α, lr): ek positive hyperparameter jo gradient ko scale karke batata hai ki har update me parameters kitna move karein: step = η × gradient. Gradient direction deta hai, learning rate distance ka multiplier.

Hyperparameter = wo setting jo hum training se pehle chunte hain (model khud nahi seekhta), jaise lr, batch size, epochs.

step = η × ∇L(θ),    θt+1 = θt − ηt ∇L(θt)
  • Fixed LR: ηt = η har step par same.
  • Scheduled LR: ηt time (step/epoch number t) ke saath badalta hai — worksheet: "Instead of a constant, α becomes αt."
  • η badalne se loss curve ki shakal nahi badalti (Colab ka "crucial distinction"); sirf ye badalta hai ki curve par kaunse points visit honge.

15.2 Too small, too large, just right

Colab notebook ka experiment: same loss J(w) = (w − 3)², same start w = −4, 12 steps, sirf η badla. Factor yaad hai? Har step (w − 3) ko (1 − 2η) se guna hota hai.

Chalo numbers se samjhein

ηfactor 1 − 2ηpehle kuch w12 steps baad wloss (start 49)Diagnosis
0.030.94−4, −3.58, −3.185, −2.814−0.33111.0985Too small: sahi direction, par rengta hai (stagnation)
0.150.70−4, −1.9, −0.43, 0.5992.9030.0094Just right: tez aur stable
0.65−0.30−4, 5.1, 2.37, 3.1893.000≈ 0Bada: 3 ko cross karke oscillate karta hai, phir bhi converge
1.05−1.10−4, 10.7, −5.47, 12.317−18.969482.6369Too large: har bounce pehle se door — divergence
  1. η = 0.03, pehla step: g = 2(−4 − 3) = −14, w = −4 − 0.03 × (−14) = −4 + 0.42 = −3.58 — chhota sa kadam.
  2. η = 1.05, pehla step: w = −4 − 1.05 × (−14) = −4 + 14.7 = 10.7 — 3 ko itna cross kiya ki doosri taraf pehle se zyada door (7.7 vs 7). Agla: g = 2(10.7 − 3) = 15.4, w = 10.7 − 16.17 = −5.47 (8.47 door). Har baar 1.1 guna door.

Jawab: η = 0.03 slow, 0.15 achha, 0.65 oscillate-but-converge, 1.05 diverge (is function ke liye boundary η = 1).

-6 3 12 w loss η = 0.03 : too small 12 steps baad bhi w = −0.33 -6 3 12 w loss η = 0.15 : just right 12 steps me w = 2.90 (≈3) -8 3 14 w loss η = 1.05 : too large 10.7, −5.47, 12.3 ... phat gaya
Same bowl (w − 3)², same start −4. Left: η = 0.03 — dots ek hi taraf chhote kadam, 12 steps me aadha raasta bhi nahi. Beech: η = 0.15 — aaram se bottom (3) tak. Right: η = 1.05 — har jump doosri taraf aur upar: 10.7, −5.47, 12.3...
0 3 6 9 12 0.001 0.1 10 1000 step (update number) loss (log scale) η=0.03 stagnation η=0.15 achha η=0.65 zig-zag η=1.05 divergence
Loss vs step (log scale) — upar wale chaaron η ke liye. Dashed: η = 0.03 dheere girta (stagnation). Kaali: η = 0.15 seedhi girawat. Narangi: η = 0.65 bahut tez gira (yahan 0.001 par chipka dikh raha hai kyunki graph 0.001 par kata hai). Laal: η = 1.05 upar hi upar — divergence.

Lecture ka delivery problem (60 steps, start w = 6, bottom w = 2.56)

lr60 steps baad wKya hua
0.02012.8571abhi tak nahi pahuncha (too small)
1.05531049.9766gaya! (divergence)
1.00506.0000boundary: 6.000 ↔ −0.880 ke beech hamesha jhoola, loss har step bilkul same (28.1487)
0.30152.5600pahunch gaya (just right)

Lecture ka "interview trap": exactly boundary par (lr = 1.0050) na converge, na blow-up — do points ke beech forever bounce, loss constant. Hamare w² example me ye boundary η = 1.0 thi (5 ↔ −5). Aur lecture ka sabak: window narrow hai — too small me rengta hai, boundary ke paar bina warning ke phat jata hai.

Loss curve dekh ke diagnosis (Worksheet 05 word bank)

ObservationDiagnosisη kaisa haiIlaaj
Loss bahut epochs tak bahut dheere girta hai (train aur val dono)StagnationToo lowη badhao (e.g. 10×), ya schedule/warmup ke saath bada start
Loss overall girta hai par minimum ke paas upar-neeche uchhalta haiOscillationThoda zyada (slightly too high)η ghatao ya decay schedule lagao
Loss tezi se badhta hai, values NaN/inf ho jaati hainDivergenceBahut zyada (much too high)Sabse pehle η kam karo (lecture: NaN aaye to pehla kaam LR down); data/gradients check

Galti mat karna

  • Loss NaN ho gaya to "LR badha ke bad region se bahar nikalo" — bilkul galat. Pehle LR kam karo.
  • "η badhane se loss curve steeper ho jata hai" — galat. Curve fixed hai; η sirf kadam badalta hai.
  • Oscillation hamesha divergence nahi hai: η = 0.65 oscillate karke bhi converge hua. Divergence tab jab |1 − 2η| > 1 (is quadratic ke liye η > 1).

15.3 Typical values aur LR kaise chunein (LR range test)

LR range test (LR finder)

Worksheet 05 ka "practical way to choose a starting rate": bahut chhote LR (jaise 10−6) se shuru karo, aur har mini-batch ke baad LR ko thoda badhate jao (aksar exponentially, jaise har batch ×1.1) — ek hi chhote training run me. Har LR par loss note karo aur loss vs LR (log scale) plot karo.

1e-5 1e-4 1e-3 1e-2 0.1 1 learning rate (log scale) → har batch pe badhta hai loss yahan se chuno (steep girawat) minimum loss — isse ~10× chhota lo bahut chhota LR: kuch nahi hota loss phat gaya →
LR range test ka typical curve: left me kuch nahi hota, beech me tez girawat (peela point — achha starting LR), laal point par loss minimum, uske baad LR itna bada ki loss phat jata hai.

15.4 Fixed learning rate: simple, par ek size sab par fit nahi

Fixed LR = poori training me ek hi η. Worksheet ki line: "one step size must serve two different phases: travelling quickly when far away, and settling precisely when close to the minimum."

Fixed LR ke fayde

  • Sabse simple — tune karne ko sirf ek number.
  • Behaviour samajhna aur reproduce karna aasaan.
  • Full-batch GD par convex problem me (sahi η) gradient khud chhota hota hai, to fixed η se bhi converge ho jata hai (14.3).
  • Adam jaise adaptive optimizers aksar fixed lr par bhi theek chalte hain.

Fixed LR ke nuksaan

  • Bada η: shuru me tez, par end me minimum ke aas-paas uchhalta rehta hai.
  • Chhota η: end me precise, par shuru me bahut slow, aur shallow local minima me phas sakta hai.
  • SGD / mini-batch noise: minimum par bhi batch gradient zero nahi, to fixed η par model kabhi settle nahi hota — hover karta hai.
  • Training ke alag phases ke liye alag η chahiye, fixed se nahi milta.

Lecture ki comparison (start lr = 0.994975, 200 steps, ek bahut bouncy setup): constant lr kabhi minimum ke 0.01 ke andar nahi aaya (final distance 0.0605), jabki step decay 42 steps me aur cosine 49 steps me pahunch gaye aur exact minimum (distance 0) par khatam hue; exponential decay (×0.97 har step) 13 steps me. Lecture ka nichod: "Gradient step ko apne aap chhota karta hai; schedule use doosri baar, hamare timetable par chhota karta hai. Practice me dono chahiye." Kyun dono? Kyunki agar η itna bada hai ki hum baar-baar overshoot karte hain, to hum kabhi paas pahunchte hi nahi, aur gradient ko chhota hone ka mauka hi nahi milta.

15.5 Scheduling kyun: pehle explore, phir refine

Kahani

Worksheet ka example: highway par gaadi full speed me chalana theek hai, par parking karte waqt? Dheere, inch-inch. Delhi se Agra highway par 100 ki speed, ghar ki gali me 10 ki. Training bhi aisi hi hai: shuru me model ghaati se bahut door hai — bade kadam chahiye. End me model minimum ke paas hai — chhote kadam chahiye taaki overshoot na ho.

Definition

Learning rate schedule: ek rule jo training ke dauraan (har epoch ya har step ke hisaab se) learning rate badalta hai, η → ηt. Do family: time-based (pehle se tay timetable: step, exponential, 1/t, cosine, warmup, one-cycle) aur performance-based (validation loss dekh ke: ReduceLROnPlateau).

15.6 Step decay — seedhi (staircase) ki tarah girna

Kahani

Board exam ki taiyaari: pehle 10 din poora syllabus tezi se padha (bada LR). Agle 10 din sirf weak chapters, dhyaan se (LR 10 guna kam). Aakhri 10 din sirf revision ki chhoti-chhoti galtiyan (aur 10 guna kam). Har phase me speed constant, phir ek jhatke me kam — bilkul seedhi ke steps.

ηepoch = η0 · γ⌊ epoch / step_size ⌋
  • η0 = shuruaati learning rate (initial LR).
  • γ (gamma) = drop factor, 0 < γ < 1 (aksar 0.1 ya 0.5). Har drop par LR γ se guna hota hai.
  • step_size = kitne epochs ke baad drop (e.g. har 10 epoch).
  • ⌊ ⌋ = floor: epoch/step_size ka poora hissa = ab tak kitne drops ho chuke. Isi floor ki wajah se graph seedhi jaisa hai — beech me flat, boundary par jhatka.

Chalo numbers se samjhein

Sawaal: η0 = 0.1, γ = 0.1, step_size = 10. Epoch 0, 9, 10, 15, 20, 29, 30 par LR?

  1. Epoch 0–9: ⌊9/10⌋ = 0 → 0.1 × 0.1⁰ = 0.1
  2. Epoch 10–19: ⌊15/10⌋ = 1 → 0.1 × 0.1¹ = 0.01
  3. Epoch 20–29: ⌊29/10⌋ = 2 → 0.1 × 0.01 = 0.001
  4. Epoch 30: ⌊30/10⌋ = 3 → 0.1 × 0.001 = 0.0001

Ek aur (γ = 0.5, step_size = 2): epochs 0..7 → 0.1, 0.1, 0.05, 0.05, 0.025, 0.025, 0.0125, 0.0125 — har 2 epoch baad aadha.

Jawab: 0.1 (epochs 0–9), 0.01 (10–19), 0.001 (20–29), 0.0001 (30 se).

Multi-step decay: drops barabar gap par nahi, balki chuni hui epochs (milestones) par. Jaise milestones = [30, 80], γ = 0.1: epochs 0–29 → 0.1, 30–79 → 0.01, 80 se → 0.001. ResNet jaise classic ImageNet training me aisa hi hota tha (har ~30 epoch par LR ÷ 10).

PyTorch · StepLR aur MultiStepLR

import torch
import torch.nn as nn
import torch.optim as optim

model = nn.Linear(10, 1)
optimizer = optim.SGD(model.parameters(), lr=0.1)
# step_size=10: har 10 epoch baad, gamma=0.1: LR ko 0.1 se guna (yaani /10)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)

for epoch in range(31):
    # ... yahan poora epoch ka training (har batch par optimizer.step()) ...
    optimizer.step()                                   # demo: ek step
    if epoch in (0, 9, 10, 19, 20, 29, 30):
        print(f"epoch {epoch:2d}: lr = {optimizer.param_groups[0]['lr']:.4f}")
    scheduler.step()                                   # epoch ke END me, ek baar

# milestones par drop: epoch 30 aur 80
optimizer2 = optim.SGD(model.parameters(), lr=0.1)
multi = optim.lr_scheduler.MultiStepLR(optimizer2, milestones=[30, 80], gamma=0.1)
for epoch in range(81):
    optimizer2.step()
    if epoch in (29, 30, 79, 80):
        print(f"MultiStep epoch {epoch}: lr = {optimizer2.param_groups[0]['lr']:.4f}")
    multi.step()

Output

epoch  0: lr = 0.1000
epoch  9: lr = 0.1000
epoch 10: lr = 0.0100
epoch 19: lr = 0.0100
epoch 20: lr = 0.0010
epoch 29: lr = 0.0010
epoch 30: lr = 0.0001
MultiStep epoch 29: lr = 0.1000
MultiStep epoch 30: lr = 0.0100
MultiStep epoch 79: lr = 0.0100
MultiStep epoch 80: lr = 0.0010

15.7 Exponential decay — har epoch same percentage kam

Kahani

Diwali ke baad mithai ka dabba: roz bache hue ka 5% kha lete ho. Pehle din zyada mithai, to 5% bhi zyada; baad me thodi bachi, to 5% bhi thoda. Kabhi jhatka nahi, smooth girawat — har din pichhle ka 95%.

ηt = η0 · γt    ya    ηt = η0 · e−kt
  • t = epoch (ya step) number. γ = har epoch ka multiplier, 0 < γ < 1 (jaise 0.95 = har epoch 5% kam).
  • Doosra roop: k = decay constant. Dono same hain jab γ = e−k (ya k = −ln γ). Jaise k = 0.05 → γ = e−0.05 ≈ 0.9512; k = 0.1 → γ ≈ 0.9048.
  • Step decay me jhatke, exponential me har step chhota smooth "percentage-style" fall (worksheet: "a smooth percentage-style fall").

Chalo numbers se samjhein

Sawaal: η0 = 0.1, γ = 0.95. Epoch 0–5, 10, 20, 50 par LR?

  1. t=1: 0.1 × 0.95 = 0.095
  2. t=2: 0.095 × 0.95 = 0.09025
  3. t=3: 0.09025 × 0.95 = 0.0857375, t=4: 0.081451, t=5: 0.077378
  4. t=10: 0.1 × 0.95¹⁰ = 0.1 × 0.598737 = 0.059874
  5. t=20: 0.1 × 0.358486 = 0.035849, t=50: 0.1 × 0.076945 = 0.007694

e−kt form, k = 0.05: t=10: 0.1 × e−0.5 = 0.1 × 0.606531 = 0.060653, t=20: 0.1 × e−1 = 0.036788 — γ = 0.9512 wale ke barabar, 0.95 se thoda upar.

Jawab: 0.1, 0.095, 0.09025, 0.0857, 0.0815, 0.0774, ... 0.0599 (t=10), 0.0358 (t=20), 0.0077 (t=50).

PyTorch · ExponentialLR

# model, optim upar wale StepLR code se
optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)  # gamma REQUIRED (koi default nahi)

for epoch in range(21):
    optimizer.step()
    if epoch in (0, 1, 2, 3, 4, 5, 10, 20):
        print(f"epoch {epoch:2d}: lr = {scheduler.get_last_lr()[0]:.6f}")
    scheduler.step()                  # har call par lr = lr * 0.95

Output

epoch  0: lr = 0.100000
epoch  1: lr = 0.095000
epoch  2: lr = 0.090250
epoch  3: lr = 0.085737
epoch  4: lr = 0.081451
epoch  5: lr = 0.077378
epoch 10: lr = 0.059874
epoch 20: lr = 0.035849

15.8 Time-based (1/t) decay

ηt = η01 + k · t
  • k = decay rate (hyperparameter). t = epoch/iteration number.
  • Shuru me tez girta hai (t = 0 → 1 par sabse bada farak), baad me bahut dheere — exponential se "lambi poonchh" (heavy tail). Ye kabhi poora 0 nahi hota.
  • Theory me SGD ke convergence ke liye aise "1/t" schedules use hote hain (Σ ηt = ∞ par Σ ηt² < ∞ — kaafi chalne ki taaqat, par noise ka total asar seemit). Purane Keras me optimizer ka decay argument yahi formula per iteration lagata tha.

Chalo numbers se samjhein

Sawaal: η0 = 0.1, k = 0.5. t = 0 se 4, aur 10?

  1. t=0: 0.1 / (1 + 0) = 0.1
  2. t=1: 0.1 / 1.5 = 0.066667
  3. t=2: 0.1 / 2 = 0.05, t=3: 0.1 / 2.5 = 0.04, t=4: 0.1 / 3 = 0.033333
  4. t=10: 0.1 / 6 = 0.016667

Jawab: 0.1, 0.0667, 0.05, 0.04, 0.0333, ..., 0.0167 (t=10). t = 2 par LR aadha (kyunki 1 + k·t = 2).

PyTorch me iski alag class nahi hai — LambdaLR se banate hain (15.10 me LambdaLR samjhenge): LambdaLR(optimizer, lr_lambda=lambda epoch: 1 / (1 + 0.5 * epoch)).

15.9 Cosine annealing — smooth soft landing

Kahani

Hawai jahaz ki landing: shuru me speed lagbhag full rehti hai, beech me tezi se kam hoti hai, aur runway chhoote waqt bahut dheere-dheere zero ki taraf — ek smooth soft landing, koi jhatka nahi. Cosine curve ka upar wala aadha hissa bilkul aisa hi dikhta hai.

ηt = ηmin + ½ (ηmax − ηmin) (1 + cos(πtT))
  • ηmax = shuruaati (sabse bada) LR. ηmin = aakhri (sabse chhota) LR, aksar 0. PyTorch me eta_min.
  • T = kul kitne epochs me neeche aana hai (PyTorch me T_max). t = current epoch (0 se T).
  • Kyun aisa formula? cos(πt/T) t = 0 par 1 aur t = T par −1 hota hai. To (1 + cos) 2 se 0 tak jata hai, ½ se guna karke 1 se 0 — yaani ek "0 se 1" ka smooth dial. Ise (ηmax − ηmin) range me scale karke ηmin par khada kar diya.
  • Shape: shuru me flat (LR der tak bada), beech me sabse tez gira, end me phir flat (dheere zero par land). t = T/2 par exactly beech: (ηmax + ηmin)/2.

Chalo numbers se samjhein

Sawaal: ηmax = 0.1, ηmin = 0, T = 10. Har epoch ka LR?

  1. t=0: 0.05 × (1 + cos 0) = 0.05 × 2 = 0.1
  2. t=3: 0.05 × (1 + cos(0.3π)) = 0.05 × (1 + 0.587785) = 0.079389
  3. t=5: 0.05 × (1 + cos(0.5π)) = 0.05 × (1 + 0) = 0.05 (bilkul beech)
  4. t=7: 0.05 × (1 − 0.587785) = 0.020611
  5. t=10: 0.05 × (1 + cos π) = 0.05 × (1 − 1) = 0
t012345678910
ηt0.10.09760.09050.07940.06550.050.03450.02060.00950.00240

Dekho: pehle epoch me sirf 0.0024 gira, beech (4→5, 5→6) me ~0.0155 per epoch, end me phir 0.0024. Symmetric "S" ulta.

Jawab: 0.1 → 0.0976 → 0.0905 → 0.0794 → 0.0655 → 0.05 → 0.0345 → 0.0206 → 0.0095 → 0.0024 → 0.

PyTorch · CosineAnnealingLR

optimizer = optim.SGD(model.parameters(), lr=0.1)        # yahi lr = eta_max
# T_max=50: 50 epochs me 0.1 se eta_min tak; eta_min default 0.0
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50, eta_min=0)

for epoch in range(51):
    optimizer.step()
    if epoch in (0, 10, 25, 40, 49, 50):
        print(f"epoch {epoch:2d}: lr = {optimizer.param_groups[0]['lr']:.6f}")
    scheduler.step()

Output

epoch  0: lr = 0.100000
epoch 10: lr = 0.090451
epoch 25: lr = 0.050000
epoch 40: lr = 0.009549
epoch 49: lr = 0.000099
epoch 50: lr = 0.000000

15.10 Linear warmup — shuru me dheere, phir full speed

Kahani

Sardi me subah purani bike ya auto-rickshaw start karte ho to seedha 80 par nahi bhagate — pehle engine thoda garam karte ho (warm-up), dheere-dheere speed badhate ho. Thande engine par full race doge to jhatke lagenge, band bhi ho sakta hai. Neural network ka shuruaati hissa bhi "thanda engine" hai.

Warmup kyun?

ηt = ηmax · t + 1W   (agar t < W),    warna ηt = ηmax (ya decay schedule)
  • W = warmup ki length (kitne epochs/steps). ηmax = target LR jahan tak pahunchna hai.
  • (t + 1)/W ek line hai jo 1/W se 1 tak jaati hai — isliye "linear" warmup. (Kuch log t/W likhte hain jo exact 0 se shuru hota hai; idea same.)
  • Warmup ke baad aam taur par decay (cosine / linear / step) lagate hain: "warmup followed by decay" (Colab).

Chalo numbers se samjhein

Sawaal: ηmax = 0.1, W = 5, phir epochs 5–19 me cosine se 0 tak (total 20 epochs).

  1. Warmup: t=0: 0.1 × 1/5 = 0.02, t=1: 0.04, t=2: 0.06, t=3: 0.08, t=4: 0.1
  2. Cosine phase (progress p = (t − 5)/15): t=5: p=0 → 0.1, t=10: p=1/3 → 0.05 × (1 + cos 60°) = 0.05 × 1.5 = 0.075, t=15: p=2/3 → 0.05 × (1 − 0.5) = 0.025, t=19: p=14/15 → 0.001093

Jawab: 0.02, 0.04, 0.06, 0.08, 0.1 (warmup), phir 0.1 → 0.075 (t=10) → 0.025 (t=15) → ≈ 0.0011 (t=19).

0 5 10 15 20 0 0.1 epoch η warmup cosine decay Linear warmup (5) + cosine 0 30 100 0 0.1 training step (%) η ↑ upar (warm-up) neeche (anneal) One-cycle: ek baar upar, ek baar neeche
Left: 5 epoch linear warmup (peela hissa) phir cosine decay — upar wali table ka graph. Right: one-cycle — training ke ~30% tak LR upar, baaki 70% me bahut neeche tak (15.12).

LambdaLR — apna khud ka schedule

LambdaLR(optimizer, lr_lambda): tum ek function do jo epoch number lekar ek multiplier lautaye. PyTorch karta hai: lr = initial_lr × lr_lambda(epoch). Isse warmup, 1/t, ya koi bhi custom shape ban jata hai. Dhyaan: scheduler banate hi epoch 0 ka lambda lag jata hai (initial_lr × lr_lambda(0)).

PyTorch · LambdaLR se linear warmup (aur 1/t decay)

optimizer = optim.SGD(model.parameters(), lr=0.1)       # 0.1 = target (initial_lr)
W = 5                                                    # warmup epochs

def warmup(epoch):                                       # multiplier lautao, LR nahi
    if epoch < W:
        return (epoch + 1) / W                           # 0.2, 0.4, 0.6, 0.8, 1.0
    return 1.0                                           # warmup ke baad full lr

scheduler = optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=warmup)

for epoch in range(7):
    optimizer.step()
    print(f"epoch {epoch}: lr = {optimizer.param_groups[0]['lr']:.4f}")
    scheduler.step()

# 1/t decay bhi LambdaLR se:
opt2 = optim.SGD(model.parameters(), lr=0.1)
inv_t = optim.lr_scheduler.LambdaLR(opt2, lr_lambda=lambda epoch: 1 / (1 + 0.5 * epoch))
print("1/t epoch 0 lr:", round(opt2.param_groups[0]['lr'], 4))

Output

epoch 0: lr = 0.0200
epoch 1: lr = 0.0400
epoch 2: lr = 0.0600
epoch 3: lr = 0.0800
epoch 4: lr = 0.1000
epoch 5: lr = 0.1000
epoch 6: lr = 0.1000
1/t epoch 0 lr: 0.1

15.11 ReduceLROnPlateau — jab progress ruke, tab brake

Kahani

Gym trainer ka rule: "Jab tak tumhara weight har hafte gir raha hai, plan mat badlo. Agar 2 hafte tak kuch nahi badla, to teesre hafte bhi nahi badla to diet ko aur halka kar denge." Trainer calendar dekh ke nahi, result dekh ke faisla karta hai. ReduceLROnPlateau bhi aisa hi hai: validation loss ko dekhta rehta hai, aur jab wo kuch epochs tak sudhrta nahi (plateau), tab LR ghata deta hai.

Definition

ReduceLROnPlateau: performance-based schedule. Ek metric (aam taur par validation loss) monitor karta hai. Agar patience epochs tak metric me sudhaar nahi hua, to LR ko factor se guna kar deta hai (kabhi min_lr se neeche nahi). Ye pehle se tay timetable nahi — isliye total epochs pata na ho tab bhi chalta hai.

agar (bad epochs lagataar) > patience  ⇒  ηnew = max(η × factor, min_lr)
  • mode='min': metric jitna kam utna achha (loss). 'max': jitna zyada utna achha (accuracy).
  • factor: LR kitne se guna ho (default 0.1; new_lr = lr × factor). Factor < 1 hona chahiye.
  • patience: kitne "bad" (bina sudhaar ke) epochs bardasht karne hain (default 10). patience = 2 → 2 bad epochs chalenge, teesre lagataar bad epoch par LR cut.
  • min_lr: LR ki neeche ki seema (default 0). threshold (default 1e-4): itna chhota sudhaar "sudhaar" nahi gina jata. cooldown (default 0): cut ke baad kitne epochs tak counter band.
  • LR cut hote hi bad-epoch counter 0 se dobara shuru.

Chalo numbers se samjhein

Sawaal: lr = 0.1, factor = 0.5, patience = 2. Validation losses: 0.90, 0.75, 0.70, 0.71, 0.70, 0.72, 0.69, 0.69, 0.70, 0.71, 0.68. LR kab-kab girega?

  1. Epoch 0–2: 0.90 → 0.75 → 0.70, har baar naya best. bad = 0, lr = 0.1
  2. Epoch 3: 0.71 > best 0.70 → bad = 1. Epoch 4: 0.70 = best (barabar sudhaar nahi hai) → bad = 2. Abhi bhi bardasht (2 ≤ patience).
  3. Epoch 5: 0.72 → bad = 3 > 2 → lr = 0.1 × 0.5 = 0.05, counter reset 0.
  4. Epoch 6: 0.69 naya best → bad = 0. Epoch 7 (0.69), 8 (0.70) → bad 1, 2.
  5. Epoch 9: 0.71 → bad = 3 > 2 → lr = 0.05 × 0.5 = 0.025. Epoch 10: 0.68 naya best.

Jawab: LR epoch 5 par 0.1 → 0.05 aur epoch 9 par 0.05 → 0.025.

PyTorch · ReduceLROnPlateau (step me metric dena zaroori)

import torch
import torch.nn as nn
import torch.optim as optim

model = nn.Linear(10, 1)
optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.5, patience=2)

# maan lo ye har epoch ke baad mila validation loss hai
val_losses = [0.90, 0.75, 0.70, 0.71, 0.70, 0.72, 0.69, 0.69, 0.70, 0.71, 0.68]

for epoch, val_loss in enumerate(val_losses):
    optimizer.step()                         # (training ho chuki)
    scheduler.step(val_loss)                 # METRIC pass karna zaroori
    print(f"epoch {epoch:2d} | val_loss = {val_loss:.2f} | "
          f"bad epochs = {scheduler.num_bad_epochs} | lr = {optimizer.param_groups[0]['lr']:.4f}")

Output

epoch  0 | val_loss = 0.90 | bad epochs = 0 | lr = 0.1000
epoch  1 | val_loss = 0.75 | bad epochs = 0 | lr = 0.1000
epoch  2 | val_loss = 0.70 | bad epochs = 0 | lr = 0.1000
epoch  3 | val_loss = 0.71 | bad epochs = 1 | lr = 0.1000
epoch  4 | val_loss = 0.70 | bad epochs = 2 | lr = 0.1000
epoch  5 | val_loss = 0.72 | bad epochs = 0 | lr = 0.0500
epoch  6 | val_loss = 0.69 | bad epochs = 0 | lr = 0.0500
epoch  7 | val_loss = 0.69 | bad epochs = 1 | lr = 0.0500
epoch  8 | val_loss = 0.70 | bad epochs = 2 | lr = 0.0500
epoch  9 | val_loss = 0.71 | bad epochs = 0 | lr = 0.0250
epoch 10 | val_loss = 0.68 | bad epochs = 0 | lr = 0.0250

Exam wala standard roop: scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=5) aur har epoch ke end me scheduler.step(val_loss). Yahan 5 bad epochs bardasht, chhatha lagataar bad epoch → LR ÷ 10.

15.12 Cyclical LR aur One-cycle (short me)

Ab tak sab schedules LR ko sirf ghatate the. Leslie Smith ka idea: LR ko upar-neeche bhi le jao.

15.13 PyTorch me schedulers: order, LR padhna, poora loop

Golden rule: pehle optimizer.step(), phir scheduler.step()

PyTorch · galat order ka nateeja

model = nn.Linear(1, 1)
opt = optim.SGD(model.parameters(), lr=0.1)
sch = optim.lr_scheduler.StepLR(opt, step_size=1, gamma=0.5)
for epoch in range(3):
    sch.step()          # GALAT: optimizer.step() se pehle
    opt.step()
    print(epoch, opt.param_groups[0]['lr'])

Output

UserWarning: Detected call of `lr_scheduler.step()` before `optimizer.step()`. In PyTorch 1.1.0 and later,
you should call them in the opposite order: `optimizer.step()` before `lr_scheduler.step()`.  Failure to do
this will result in PyTorch skipping the first value of the learning rate schedule. ...
0 0.05
1 0.025
2 0.0125

Pehle epoch ko 0.1 milna chahiye tha, mila 0.05 — schedule ki pehli value gayab. (Warning ka text yahan line-wrap karke chhota kiya hai.)

LR padhne ke do tareeke

Saare schedules ek saath: har epoch ka LR

PyTorch · 10 epochs, base lr 0.1, alag schedulers

import torch
import torch.nn as nn
import torch.optim as optim

def lr_history(make_scheduler, epochs=10, base_lr=0.1):
    model = nn.Linear(1, 1)
    opt = optim.SGD(model.parameters(), lr=base_lr)
    sch = make_scheduler(opt)
    lrs = []
    for epoch in range(epochs):
        lrs.append(round(opt.param_groups[0]['lr'], 4))   # is epoch me use hua LR
        opt.step()                                         # (training)
        sch.step()                                         # epoch khatam
    return lrs

S = optim.lr_scheduler
print("StepLR     ", lr_history(lambda o: S.StepLR(o, step_size=3, gamma=0.5)))
print("MultiStepLR", lr_history(lambda o: S.MultiStepLR(o, milestones=[2, 6], gamma=0.1)))
print("Exponential", lr_history(lambda o: S.ExponentialLR(o, gamma=0.8)))
print("1/t (k=0.5)", lr_history(lambda o: S.LambdaLR(o, lr_lambda=lambda e: 1 / (1 + 0.5 * e))))
print("Cosine     ", lr_history(lambda o: S.CosineAnnealingLR(o, T_max=10, eta_min=0)))
print("Warmup(4)  ", lr_history(lambda o: S.LambdaLR(o, lr_lambda=lambda e: min(1.0, (e + 1) / 4))))

Output

StepLR      [0.1, 0.1, 0.1, 0.05, 0.05, 0.05, 0.025, 0.025, 0.025, 0.0125]
MultiStepLR [0.1, 0.1, 0.01, 0.01, 0.01, 0.01, 0.001, 0.001, 0.001, 0.001]
Exponential [0.1, 0.08, 0.064, 0.0512, 0.041, 0.0328, 0.0262, 0.021, 0.0168, 0.0134]
1/t (k=0.5) [0.1, 0.0667, 0.05, 0.04, 0.0333, 0.0286, 0.025, 0.0222, 0.02, 0.0182]
Cosine      [0.1, 0.0976, 0.0905, 0.0794, 0.0655, 0.05, 0.0345, 0.0206, 0.0095, 0.0024]
Warmup(4)   [0.025, 0.05, 0.075, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]

Har line ko formula se milao: StepLR = 0.1 × 0.5⌊e/3⌋; Exponential = 0.1 × 0.8e (0.04096 → 0.041 round hua); 1/t = 0.1/(1 + 0.5e); Cosine = 15.9 wali table; Warmup = 0.1 × (e+1)/4 jab tak 1 na ho.

0 10 20 30 0 0.05 0.1 epoch learning rate η step (γ=0.5, har 10) exponential (γ=0.9) 1/t (k=0.2) cosine (T=30)
30 epochs, η₀ = 0.1. Kaali seedhi = step decay (γ = 0.5 har 10 epoch). Narangi = exponential (γ = 0.9) — shuru me tez gira. Dashed = 1/t (k = 0.2) — lambi poonchh. Laal = cosine (T = 30) — shuru me flat, beech me tez, end me soft landing on 0.

Poora training loop: DataLoader + optimizer + scheduler + validation

PyTorch · complete loop (mini-batch SGD + StepLR)

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader

torch.manual_seed(0)
X = torch.randn(512, 3)                                   # (512, 3)
true_w = torch.tensor([[2.0], [-1.0], [0.5]])             # asli weights
y = X @ true_w + 0.1 * torch.randn(512, 1)                # (512, 1), thoda noise
train_ds = TensorDataset(X[:400], y[:400])                # 400 train rows
val_ds = TensorDataset(X[400:], y[400:])                  # 112 validation rows
train_loader = DataLoader(train_ds, batch_size=32, shuffle=True)   # ceil(400/32) = 13 batches
val_loader = DataLoader(val_ds, batch_size=64, shuffle=False)

model = nn.Sequential(nn.Linear(3, 16), nn.ReLU(), nn.Linear(16, 1))
loss_fn = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.05)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.5)

for epoch in range(15):
    model.train()
    for xb, yb in train_loader:                # 13 updates per epoch
        optimizer.zero_grad()
        loss = loss_fn(model(xb), yb)
        loss.backward()
        optimizer.step()                       # har BATCH ke baad
    model.eval()
    with torch.no_grad():                      # validation: koi gradient nahi
        val_loss = sum(loss_fn(model(xb), yb).item() * len(xb)
                       for xb, yb in val_loader) / len(val_ds)
    lr_used = optimizer.param_groups[0]['lr']  # is epoch me kaunsa LR chala
    scheduler.step()                           # har EPOCH ke baad, sirf ek baar
    print(f"epoch {epoch:2d} | lr = {lr_used:.4f} | val_loss = {val_loss:.4f}")

# ReduceLROnPlateau wala version: upar scheduler ki jagah
#   scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=5)
# aur loop me:  scheduler.step(val_loss)

Output

epoch  0 | lr = 0.0500 | val_loss = 0.1484
epoch  1 | lr = 0.0500 | val_loss = 0.0971
epoch  2 | lr = 0.0500 | val_loss = 0.0887
epoch  3 | lr = 0.0500 | val_loss = 0.0792
epoch  4 | lr = 0.0500 | val_loss = 0.0748
epoch  5 | lr = 0.0250 | val_loss = 0.0718
epoch  6 | lr = 0.0250 | val_loss = 0.0688
epoch  7 | lr = 0.0250 | val_loss = 0.0657
epoch  8 | lr = 0.0250 | val_loss = 0.0663
epoch  9 | lr = 0.0250 | val_loss = 0.0630
epoch 10 | lr = 0.0125 | val_loss = 0.0633
epoch 11 | lr = 0.0125 | val_loss = 0.0617
epoch 12 | lr = 0.0125 | val_loss = 0.0620
epoch 13 | lr = 0.0125 | val_loss = 0.0626
epoch 14 | lr = 0.0125 | val_loss = 0.0617

LR har 5 epoch par aadha (0.05 → 0.025 → 0.0125), aur val loss girta gaya. Upar wale numbers random seed par depend karte hain (tumhare run me thode alag ho sakte hain), par LR column hamesha yahi rahega. Dhyaan do: val_loss ko batch size se weight karke average kiya (.item() * len(xb)), kyunki aakhri val batch chhota (48) hai.

15.14 Schedules ka comparison

ScheduleFormulaShape / pehchaanTotal epochs pata hone chahiye?Val loss dekhta hai?PyTorchKab use karein
Fixedηt = ηseedhi flat lineNahiNahisirf optimizer ka lrSimple baseline; Adam ke saath aksar theek
Step decayη₀ γ⌊e/s⌋staircase, achanak dropsRoughly (milestones chunne ke liye)NahiStepLR(opt, step_size, gamma=0.1), MultiStepLR(opt, milestones, gamma=0.1)Classic CNN training (SGD + momentum)
Exponentialη₀ γt = η₀ e−ktsmooth percentage-style fallNahiNahiExponentialLR(opt, gamma)Smooth steady decay chahiye
Time-based 1/tη₀ / (1 + k t)shuru me tez, lambi poonchhNahiNahiLambdaLR(opt, lambda e: 1/(1+k*e))Classic SGD theory / simple decay
Cosine annealingηmin + ½(ηmax − ηmin)(1 + cos(πt/T))half-cosine, smooth soft landingHaan (T_max)NahiCosineAnnealingLR(opt, T_max, eta_min=0)Known budget; modern default (aksar warmup ke saath)
Linear warmupηmax(t+1)/W, t < Wnear-zero se upar chadhnaSirf WNahiLambdaLR, LinearLRAdam / Transformers / large batch; unstable early updates
ReduceLROnPlateaubad > patience ⇒ η × factortab drop jab val loss atkeNahiHaanReduceLROnPlateau(opt, mode='min', factor=0.1, patience=10) + step(val_loss)Total epochs pata nahi; feedback-based
Cyclical / One-cyclebase ↔ max (triangle) / ek baar upar-neecheupar-neeche wave / ek pahadOne-cycle: haanNahiCyclicLR, OneCycleLR (per batch step)Fast training, super-convergence

Exam Radar

  • Learning rate = step size; sabse important hyperparameter. Log scale par search (1e-4, 1e-3, 1e-2, 1e-1). Adam default lr = 1e-3.
  • Too small → slow / stagnation; slightly high → oscillation near minimum; much too high → divergence, NaN. NaN aaye to pehle LR kam karo.
  • L = w² ya (w − 3)² par: 0 < η < 1 converge, η = 1 forever oscillate, η > 1 diverge; boundary par loss constant rehta hai.
  • Fixed LR ki problem: ek size do phases (door se tez aana, paas aake settle) serve nahi kar sakta; SGD/mini-batch noise ki wajah se kabhi settle nahi hota.
  • Scheduling: high LR = explore (valley to valley), low LR = refine (settle). Decay bahut bade starting LR ko nahi bacha sakta.
  • Step: η₀γ⌊e/s⌋ (staircase). Exponential: η₀γt = η₀e−kt, γ = e−k. 1/t: η₀/(1+kt). Cosine: ηmin + ½(ηmax − ηmin)(1 + cos(πt/T)); t = T/2 par exactly beech.
  • Warmup: chhote LR se shuru karke badhana — early instability rokta hai (random weights, Adam ke kachche estimates, large batch).
  • ReduceLROnPlateau: val loss monitor; defaults mode='min', factor=0.1, patience=10, min_lr=0; scheduler.step(val_loss); patience = p → (p+1)-th lagataar bad epoch par cut.
  • StepLR gamma default 0.1; ExponentialLR gamma required; CosineAnnealingLR eta_min default 0; LambdaLR: lr = initial_lr × lambda(epoch).
  • Order: optimizer.step() phir scheduler.step(); epoch-based scheduler epoch me ek baar; OneCycleLR/CyclicLR har batch. LR padho: optimizer.param_groups[0]['lr'] ya scheduler.get_last_lr()[0].
  • Pehchaan: staircase = step; smooth percentage fall = exponential; half-cosine soft landing = cosine; near-zero se rise = warmup; val loss atke tab cut = reduce on plateau.

Galti mat karna

  • scheduler.step() ko batch loop ke andar daal diya (epoch-based scheduler ke liye) → StepLR(step_size=10) har 10 batches par girega, epochs par nahi. LR bahut jaldi khatam.
  • ReduceLROnPlateau me scheduler.step() bina metric ke → error. Isko scheduler.step(val_loss) chahiye. Aur training loss nahi, validation loss do.
  • get_last_lr() list lautata hai, number nahi — [0] lagao.
  • Cosine me T_max total epochs se chhota rakha to baad me LR wapas badhne lagega.
  • LambdaLR ka function LR nahi, multiplier lautata hai: lambda 0.5 lautaye to lr = initial_lr × 0.5.
  • "Exponential decay me har epoch same amount kam hota hai" — galat. Same percentage (ratio) kam hota hai; amount chhota hota jata hai. Har epoch same amount = linear decay.
  • patience = 5 ka matlab "5th bad epoch par cut" nahi — 5 bad epochs tak bardasht, 6th lagataar bad epoch par cut.

Dimaag me aise baitha lo

Geyser ka nal: shuru me bada ghumao, paas aake chhota. Step = seedhi, Exponential = mithai ka 5% roz, Cosine = jahaz ki soft landing, Warmup = thande engine ko garam karna, Plateau = trainer jo result dekh ke diet badle. Aur hamesha: optimizer.step() pehle, scheduler.step() baad me.

Chapter 16

Momentum-Based Methods: EWMA, SGD with Momentum, NAG

Gradient ko yaaddasht do · EWMA · bias correction · velocity · Nesterov ka look-ahead

Chapter 14 aur Chapter 15 me humne seekha: gradient batata hai ki upar kidhar hai, aur hum ulti taraf ek chhota kadam lete hain: w ← w − α·g. Ye rule bilkul sahi hai, par ek dikkat hai: plain SGD bhulakkad hai. Har step pe wo sirf abhi wala gradient dekhta hai, aur pichhle saare gradients bhool jaata hai. Teacher ki worksheet ki pehli line yaad rakhna: "A gradient step is useful; a remembered gradient can be better."

Is chapter ki poori kahani ek seedhi line hai (Worksheet 06 isi order me chalti hai):

Agla chapter (Chapter 17) isi memory ko size ke liye use karega (AdaGrad, RMSProp, Adam). Pehle samjho ki plain SGD phasta kahan hai.

16.1 Plain SGD kahan-kahan phasta hai

Kahani

Raat ka time hai, light chali gayi hai, aur tum ek lambe, patle naale (drain) ke andar khade ho. Naale ki dono deewarein bahut tedhi (steep) hain, aur naale ka farsh bahut halka sa dhalaan le kar door nadi ki taraf jaata hai. Tumhe nadi tak pahunchna hai (wahi minimum hai). Andhere me tum sirf pair ke neeche ki dhalaan feel kar sakte ho.

Problem: pair ke neeche sabse tedhi dhalaan hamesha saamne wali deewar ki taraf hoti hai, nadi ki taraf nahi. To tum ek deewar se doosri deewar pe uchhalte rehte ho — left, right, left, right — aur nadi ki taraf bas zara-zara khisakte ho. Yahi plain gradient descent ka zig-zag hai.

Plain SGD (aur plain GD) ki paanch badi problems hain. Inhi ko theek karne ke liye saare "advanced optimizers" bane:

Problem 1: Ravine (lambi patli ghaati) — zig-zag across, slow along

Jab loss surface ek direction me bahut steep ho aur doosri me bahut flat (isko ravine, ya elongated valley, ya ill-conditioned surface kehte hain), to contour lines lambe patle ellipse ban jaate hain. Gradient hamesha contour ke perpendicular hota hai, isliye wo zyada-tar steep deewar ki taraf point karta hai, minimum ki taraf nahi.

Lab 7 (Optimizer Zoo) ka ravine yaad rakhna: f(w0, w1) = 0.5·(0.1w02 + 2.0w12). Gradient = (0.1w0, 2w1). Yaani w1 wali direction 20 guna steeper hai.

Chalo numbers se samjhein

Is ravine pe ek GD step: w0 ← w0 − α(0.1w0) = (1 − 0.1α)w0 aur w1 ← (1 − 2α)w1. Har step pe dono coordinate ek fixed number se multiply hote hain.

  1. α = 0.9 lo. Steep direction ka multiplier: 1 − 2(0.9) = −0.8. Minus sign ka matlab: har step pe w1 ka sign palat jaata hai — yahi zig-zag hai (deewar se deewar).
  2. Flat direction ka multiplier: 1 − 0.1(0.9) = 0.91. Yaani har step me w0 sirf 9% minimum ki taraf khisakta hai — bahut slow.
  3. Kya α badha dein? Agar α > 1 hua to steep multiplier |1 − 2α| > 1 ho jaayega, aur w1 har step bada hota jaayega — diverge. Matlab learning rate ki upper limit steep direction tay karti hai, aur flat direction ko us chhote α ke saath ghisatna padta hai.

Ek hi learning rate dono directions ke liye theek nahi: bada α → steep wall pe uchhalna/diverge; chhota α → flat floor pe rengna.

minimum start plain GD (α = 0.9): deewar se deewar zig-zag momentum: zig-zag cancel, aage tak pahuncha steep: w₁ (upar-neeche) flat: w₀ (left-right)
Ravine f = 0.5(0.1w02 + 2w12) ke contours, dono 14 steps. Red (plain GD, α = 0.9) har step pe steep direction me sign palatta hai aur sirf w0 ≈ −1.23 tak pahuncha. Ink (momentum, PyTorch form, lr = 0.5, β = 0.6) ki upar-neeche hil-dol ~2.6 guna kam hai aur wo w0 ≈ −0.56 tak pahuncha (Python se compute kiye paths).

Problem 2: Noisy gradients — har sample ki apni raay

SGD / mini-batch me gradient poore data se nahi, ek chhote sample se aata hai, to wo noisy estimate hota hai. Teacher ka Worksheet 07 example (delivery data) dekho. Model: ŷ = w·x (minutes per km), loss ek sample pe L = ½(ŷ − y)2, gradient g = x(ŷ − y). Chhe deliveries, w = 2.0000 pe har sample ka gradient:

samplex (km)y (min)ŷ = 2xerror ŷ − ygradient x(ŷ − y)ye sample kya kehta hai
1224+2+4w ghatao
2478+1+4w ghatao
3132−1−1w badhao
4356+1+3w ghatao
5254−1−2w badhao
65910+1+5w ghatao

Full-batch gradient = (4 + 4 − 1 + 3 − 2 + 5)/6 = 13/6 = +2.1667 — hamesha ek hi sign. Par SGD ek-ek sample pe chalta hai (α = 0.05, cycle order me), to wo jo gradient haath me hai usi taraf bhaagta hai: 12 steps me gradient ka sign 11 me se 8 transitions pe palta. 10 epochs (60 steps) me 59 me se 40 reversals, kul chala 6.3909 units, par net displacement sirf 0.2217! (Least-squares best answer w* = 1.7797.) Jo information SGD phenk raha hai wo current gradient me nahi, pichhle bhule hue gradients me hai.

Problem 3: Plateau (sapaat maidaan)

Kuch jagah loss surface lagbhag flat hoti hai — gradient bahut chhota. Step = α × (chhota gradient) = bahut chhota step. Training curve kai epochs tak "atki" dikhti hai. Jo optimizer pichhli speed yaad rakhe (momentum), wo plateau pe bhi chalta rehta hai.

Problem 4: Saddle point (ghode ki kaathi)

Saddle point pe gradient zero hota hai, par wo minimum nahi hai: ek direction me upar jaata hai, doosri me neeche (jaise ghode ki kaathi ya Pringles chip). High-dimensional neural nets me saddle points local minima se kahin zyada common hote hain. Lab 7 ka mini-challenge: f = 0.2w02 − 0.5w12, start [−2.0, 0.01]. w1 ka gradient = −w1 ≈ −0.01, itna chhota ki SGD (lr = 0.1) ko escape karne me bahut time lagta hai: 10 steps baad w1 ≈ 0.026, 40 steps baad ≈ 0.45. Adam (lr = 0.2) 10 steps me hi w1 ≈ 1.86 tak nikal jaata hai (numbers: PyTorch ke update rules numpy me chala ke). Kyun? Ye Chapter 17 me samjhoge.

Problem 5: Local minima

Agar loss me do ghaatiyan hain aur tum chhoti (shallow) ghaati me utar gaye, to wahan gradient zero hai aur plain GD wahin ruk jaata hai. Teacher ka two-valley example (16.7 me) dikhayega ki momentum kabhi-kabhi is chhoti ghaati se bahar nikal jaata hai.

Exam Radar

  • Ravine / ill-conditioned surface pe plain GD: steep direction me oscillate (zig-zag), flat direction me slow progress.
  • Learning rate ki upper limit steep direction tay karti hai (quadratic λw2/2 pe GD tab diverge karta hai jab α > 2/λ).
  • Saddle point aur local minimum dono pe gradient = 0. Plateau pe gradient ≈ 0.
  • Mini-batch gradient = full gradient ka noisy estimate; SGD "no memory" hai.

16.2 EWMA — ek number me poori history

Kahani

Mohalle ki cricket team ke coach hain Pappu bhaiya. Unhe har batsman ka "form" pata rehta hai. Kya wo har match ka score diary me likh ke rakhte hain? Nahi! Unke dimaag me har player ke liye bas ek number hota hai — "iska form kaisa chal raha hai". Naya match hua to wo us number ko thoda update kar dete hain:

naya form = 0.8 × purana form + 0.2 × aaj ka score

Kal ka match sabse zyada matter karta hai, pichhle hafte wala thoda kam, pichhle mahine wala bahut kam. Purane matches dheere-dheere fade hote hain, achanak delete nahi hote. Yahi hai Exponentially Weighted Moving Average (EWMA). (Worksheet 06 me bhi yahi analogy hai: "a coach judging a player's form".)

Simple average kyun nahi?

Socho "pichhle k values ka plain average" le lete. Teacher ke Worksheet 07 ke hisaab se ye do jagah fail hota hai:

Definition

EWMA (Exponentially Weighted Moving Average) ek running average hai jisme har purani value ka weight har step pe β se multiply ho kar exponentially (geometrically) ghatta hai. Isko store karne ke liye sirf ek state number chahiye.

Vt = β·Vt−1 + (1 − β)·θt
  • θt = step/din t ka naya observation (aaj ka temperature, aaj ka score, ya aage chal ke aaj ka gradient). Worksheet isi ko xt likhti hai.
  • Vt = naya observation dekhne ke baad ka average — yahi ek number hum yaad rakhte hain. Worksheet me iska naam st.
  • Vt−1 = pichhle step ka average (purana form).
  • β (beta) = purane average ka weight, 0 ≤ β < 1. Bada β = lambi memory.
  • (1 − β) = naye observation ka weight. Jo β nahi leta, wo isko milta hai.
  • V0 = 0 hamesha (average khaali shuru hota hai).

Formula aisa kyun dikhta hai? Ye do cheezon ka weighted average hai: "jo main pehle maanta tha" (Vt−1) aur "aaj jo dikha" (θt). Dono weights ka sum β + (1 − β) = 1 hai, isliye average observations ke hi scale pe rehta hai (temperature ka average temperature jaisa hi number hoga). Zor se padho: "naya average = β hissa purane average ka + (1 − β) hissa naye observation ka."

Chalo numbers se samjhein — Worksheet 06 ladder

Sawaal: s0 = 0, β = 0.8, x1 = 5, x2 = 1. Rule: st = 0.8st−1 + 0.2xt.

  1. Initial memory: s0 = 0
  2. Pehla observation: s1 = 0.8(0) + 0.2(5) = 0 + 1.0 = 1.0
  3. Doosra: s2 = 0.8(1.0) + 0.2(1) = 0.8 + 0.2 = 1.0
  4. Direction check — s2 me kiska asar zyada? Kholo: s2 = 0.2·x2 + 0.8·0.2·x1 = 0.2·x2 + 0.16·x1. Naye x2 ka coefficient 0.2 > purane x1 ka 0.16.

Jawab: s1 = 1.0, s2 = 1.0; naya sample zyada influence karta hai (0.2 vs 0.16). EWMA saari history store nahi karta — sirf ek state st.

Practice (worksheet): β = 0.6, x1 = 4, x2 = 2 → s1 = 0.6(0) + 0.4(4) = 1.6, s2 = 0.6(1.6) + 0.4(2) = 0.96 + 0.8 = 1.76. Ye 4 aur 2 ka simple average (3) kyun nahi? Kyunki naye 2 ko weight 0.4, purane 4 ko 0.24, aur bacha hua 0.36 weight abhi bhi shuruaati s0 = 0 pe baitha hai: 0.4(2) + 0.24(4) + 0.36(0) = 1.76.

Chalo numbers se samjhein — Worksheet 07: smooth hona hi point hai

Sawaal: s0 = 0, β = 0.8, observations 5, 3, 1 (tezi se gir rahe hain).

  1. s1 = 0.8(0) + 0.2(5) = 0 + 1.0 = 1.0000
  2. s2 = 0.8(1.0000) + 0.2(3) = 0.8 + 0.6 = 1.4000
  3. s3 = 0.8(1.4000) + 0.2(1) = 1.12 + 0.2 = 1.3200

Observations 5 → 3 → 1 gire, average 1.0 → 1.4 → 1.32 aaram se chala. Average apne data se zyada smooth hai — yahi poora point hai.

Par dhyaan do: 3 observations ke baad average 1.32 hai, plain average 3.0 ke paas bhi nahi! Kyunki s0 = 0 ka weight abhi bhi 0.83 = 0.512 hai. Ye yaad rakhna — yahi problem Adam ka bias correction theek karta hai (16.4 me).

Chalo numbers se samjhein — ek ulta observation sign nahi palatta

Sawaal (Worksheet 07 "your turn"): β = 0.8, s0 = 0, observations 6, 6, −2.

  1. s1 = 0.8(0) + 0.2(6) = 0 + 1.2 = 1.2000
  2. s2 = 0.8(1.2000) + 0.2(6) = 0.9600 + 1.2000 = 2.1600
  3. Ab pehla negative observation. s3 = 0.8(2.1600) + 0.2(−2) = 1.7280 − 0.4000 = 1.3280
  4. Direction check: average 2.1600 se 1.3280 gira, yaani drop = 0.8320 (38.5%), par negative nahi hua, kyunki naye observation ka weight sirf 0.2 tha.

s = 1.2000 → 2.1600 → 1.3280. Ek ulti value average ko kam karti hai, palat-ti nahi. Isi property ki wajah se hum gradients ko EWMA me daalne wale hain.

Python · EWMA by hand (worksheet numbers verify karo)

def ewma(values, beta, s0=0.0):
    s = s0                         # memory khaali shuru: s0 = 0
    history = []
    for x in values:
        s = beta * s + (1 - beta) * x   # purana hissa + naya hissa
        history.append(round(s, 4))
    return history

print(ewma([5, 1], 0.8))       # Worksheet 06
print(ewma([4, 2], 0.6))       # Worksheet 06 practice
print(ewma([5, 3, 1], 0.8))    # Worksheet 07 worked example
print(ewma([6, 6, -2], 0.8))   # Worksheet 07 your turn

Output

[1.0, 1.0]
[1.6, 1.76]
[1.0, 1.4, 1.32]
[1.2, 2.16, 1.328]

16.3 EWMA ko khol ke dekho: weights, memory length aur β ka khel

Unroll karo (substitute karte jao)

Rule me st−1 = βst−2 + (1 − β)xt−1 daalo, phir st−2 ko bhi kholo, aur aise hi peeche chalte jao:

st = (1−β)xt + β(1−β)xt−1 + β2(1−β)xt−2 + β3(1−β)xt−3 + ··· + βts0
  • k step purane observation ka weight = (1 − β)βk. Har step peeche jaane pe ek aur β ka factor lagta hai → weights geometrically / exponentially girte hain. Isi liye naam "exponentially weighted".
  • Koi observation kabhi poori tarah delete nahi hota — bas fade hota jaata hai.
  • Worksheet 06 ka subjective Q1: xt−2 ka coefficient = β2(1 − β).
kitna puranaweight, β = 0.8weight, β = 0.9
newest (aaj)0.20000.1000
1 step purana0.16000.0900
2 steps purana0.12800.0810
3 steps purana0.10240.0729
4 steps purana0.08190.0656
5 steps purana0.06550.0590

β = 0.8 tezi se bhoolta hai (0.2 → 0.0655), β = 0.9 aaram se (0.1 → 0.059). Bada β = har purani value ko zyada der tak yaad rakhna.

Saare weights ka total = 1 − βt

Asli observations ke weights jodo: (1−β)(1 + β + β2 + ··· + βt−1). Ye geometric series hai, iska sum = 1 − βt. Bacha hua βt weight shuruaati s0 = 0 pe baitha hai. Ye line 16.4 (bias correction) ki neev hai.

Memory kitni lambi? Effective window ≈ 1/(1 − β)

effective memory ≈ 11 − β steps
  • Seedhi soch: newest ka weight (1 − β) hai. Agar itne hi weight wale N recent observations hon to unka total 1 tab hoga jab N = 1/(1 − β).
  • Doosri soch: 1/(1 − β) steps peeche weight lagbhag 1/e ≈ 0.37 guna reh jaata hai (jaise 0.910 = 0.3487). Usse purana "kam matter karta hai".
  • Half-life (weight aadha hone me steps) = ln 0.5 / ln β; β = 0.9 pe ≈ 6.58 steps.
βnewest weight (1 − β)effective memory 1/(1 − β)kaisa behave karta hai
0.50.500~2 stepsbahut jumpy, turant react
0.80.200~5 stepsbeech ka
0.90.100~10 stepstypical choice (pichhle ~10 din ka temperature)
0.980.020~50 stepsbahut smooth, par der se react (lag)

β ka trade-off: smoothness vs lag

Bada β → curve smooth (noise dab jaata hai) par lag zyada: data badal jaaye to average der se maanta hai. Chhota β → turant react karta hai par noisy. Neeche 40 din ka temperature hai jisme din 21 pe mausam achanak ~20°C se ~30°C ho jaata hai:

102030 110203040 din (day) °C β = 0.5 (~2 din ki memory): jumpy, turant react β = 0.9 (~10 din): smooth, par lag din 21: mausam achanak garam
Pencil dots = roz ka temperature. Din 23 pe β = 0.5 wali EWMA 28.6°C bata rahi hai (naya mausam pakad liya), jabki β = 0.9 wali abhi 23.25°C pe hai (lag). Din 31 tak bhi β = 0.9 sirf 27.2°C. Dono lines bias-corrected (16.4) hain taaki start ka effect na dikhe; values Python se compute ki.

Dimaag me aise baitha lo

β = "kitna purana yaad rakhna hai" ka knob. 1/(1 − β) = kitne steps ki memory. 0.9 → 10, 0.98 → 50, 0.5 → 2.

16.4 Start-up bias aur bias correction

Kahani

Naye class register me teacher ne galti se har bachche ke naam ke aage pehle se kuch imaginary zero marks likh diye. Ab pehle test me Golu ko 25/25 mile, par average nikalte waqt wo zeros bhi gine gaye — to average aaya 2.5! Golu ka asli performance 25 hai, register 2.5 bata raha hai. Ilaaj: un imaginary zeros ka weight hata do (Worksheet 06 ki analogy).

EWMA me V0 = 0 se shuru karna convenient hai, par shuru ke kuch steps me average me "bahut saara zero" mila hota hai, isliye Vt asli value se chhota (biased towards zero) aata hai. Ise start-up bias ya initialization bias kehte hain.

Chalo numbers se samjhein — temperature (classroom notebook)

Sawaal: temperatures 25, 13, 17, 31 °C, β = 0.9, V0 = 0. Raw EWMA aur corrected EWMA nikalo.

  1. V1 = 0.9(0) + 0.1(25) = 2.5 — 25°C ke din average 2.5°C? Bilkul galat! Bias.
  2. V2 = 0.9(2.5) + 0.1(13) = 2.25 + 1.3 = 3.55
  3. V3 = 0.9(3.55) + 0.1(17) = 3.195 + 1.7 = 4.895
  4. V4 = 0.9(4.895) + 0.1(31) = 4.4055 + 3.1 = 7.5055
  5. Ab har ek ko (1 − 0.9t) se divide karo:
    V̂1 = 2.5 / (1 − 0.9) = 2.5 / 0.1 = 25.0000
    V̂2 = 3.55 / (1 − 0.81) = 3.55 / 0.19 = 18.6842
    V̂3 = 4.895 / (1 − 0.729) = 4.895 / 0.271 = 18.0627
    V̂4 = 7.5055 / (1 − 0.6561) = 7.5055 / 0.3439 = 21.8247
  6. Check V̂2: asli weights 25 pe 0.09 aur 13 pe 0.1 the, total 0.19. Weighted average = (0.09×25 + 0.1×13)/0.19 = (2.25 + 1.3)/0.19 = 18.6842. Matlab correction ne bas weighted sum ko total weight se divide kiya — isi se wo sachcha average bana.

Raw: 2.5, 3.55, 4.895, 7.5055 (bekaar, bahut chhote). Corrected: 25.0, 18.68, 18.06, 21.82 (asli temperature range me).

V̂t = Vt1 − βt
  • V̂t = bias-corrected average ("V-hat").
  • t = step number (pehla update t = 1, doosra t = 2 ...). βt yaani β ki power t: 0.91 = 0.9, 0.92 = 0.81, 0.93 = 0.729.
  • 1 − βt = asli data pe ab tak laga total weight (16.3 wali geometric series). Usse divide karna = weights ko 1 tak normalize karna.

Chalo numbers se samjhein — constant signal test (Worksheet 08)

Har step observation = 1 (hamesha). Imaandaar average 1 hona chahiye. β = 0.9:

  1. m1 = 0.9(0) + 0.1(1) = 0.1000
  2. m2 = 0.9(0.1000) + 0.1(1) = 0.0900 + 0.1000 = 0.1900
  3. m3 = 0.9(0.1900) + 0.1(1) = 0.1710 + 0.1000 = 0.2710
  4. Dekho: 0.1, 0.19, 0.271 bilkul 1 − 0.91, 1 − 0.92, 1 − 0.93 hain. Constant g ke liye rule (1 − βt)g deta hai, kabhi g nahi.
  5. Correction: 0.2710 / (1 − 0.93) = 0.2710 / 0.2710 = 1.0000 ✓

Average galat nahi tha, adhoora tha: 3 steps baad 0.729 weight abhi bhi m0 = 0 pe baitha tha.

0102030 151015 din (t) raw Vₜ (V₀ = 0 se): din 1 pe 2.09! corrected V̂ₜ = Vₜ / (1 − 0.9ᵗ)
Wahi temperature data (pehle 15 din, β = 0.9). Red dashed raw EWMA 0 se shuru ho kar dheere-dheere upar aati hai — 15 din baad bhi sirf 15.7°C, jabki asli ~20°C. Ink wali corrected EWMA pehle din se sahi level pe hai.
t steps baad1 − βt (β = 0.9)1 − βt (β = 0.999)matlab
10.10000.00100.9 wala 90% chhota, 0.999 wala ~100% chhota
20.19000.0020
30.27100.0030
100.65130.01000.9 wala abhi 35% chhota
500.99480.04880.9 lagbhag theek; 0.999 abhi 95% chhota
3001.00000.2593
30001.00000.95030.999 bhi ab sirf 5% chhota

Correction khud band ho jaata hai: t bada hone pe βt → 0, to 1 − βt → 1, aur 1 se divide karne se kuch nahi badalta. Isliye bias correction shuruaati steps me hi important hai — aur jitna bada β, utni der tak important (β = 0.999 pe hazaaron steps tak!). Yahi wajah hai ki Adam me bias correction zaroori hai (uska β2 = 0.999), jabki plain momentum (β = 0.9) me practice me log aksar correction nahi lagate — PyTorch ka optim.SGD bhi momentum pe koi bias correction nahi karta; 10–20 steps baad farak khatam.

Python · EWMA + bias correction (classroom temperatures)

temps = [25, 13, 17, 31]
beta = 0.9
V = 0.0                                   # V0 = 0: yahin se bias aata hai
for t, theta in enumerate(temps, start=1):    # t = 1, 2, 3, 4 (step number)
    V = beta * V + (1 - beta) * theta     # raw EWMA
    V_hat = V / (1 - beta ** t)           # bias correction: total weight se divide
    print(f"t={t}: raw V={V:.4f}  corrected V_hat={V_hat:.4f}")

Output

t=1: raw V=2.5000  corrected V_hat=25.0000
t=2: raw V=3.5500  corrected V_hat=18.6842
t=3: raw V=4.8950  corrected V_hat=18.0627
t=4: raw V=7.5055  corrected V_hat=21.8247

Exam Radar

  • EWMA: Vt = βVt−1 + (1 − β)θt; V0 = 0; dono weights ka sum 1.
  • k step purane observation ka weight = (1 − β)βk → exponential decay.
  • Effective window ≈ 1/(1 − β): 0.5 → 2, 0.8 → 5, 0.9 → 10, 0.98 → 50, 0.999 → 1000.
  • β badhao → smoother + longer memory + slower reaction (more lag). β ghatao → noisy par fast.
  • Bias correction: V̂t = Vt/(1 − βt). Wajah: V0 = 0 → early estimates zero ki taraf biased (too small). Correction early steps me estimate bada karta hai; large t pe effect khatam.
  • t = 1 pe: V̂1 = θ1 exactly (correction pehle observation ko hi lauta deta hai).

Galti mat karna

  • "β bada = naye data ko zyada weight" — ulta hai. β purane average ka weight hai; naye ka weight (1 − β) hai.
  • Bias correction divide karta hai (1 − βt) se, multiply nahi. 1 − βt < 1 hai, to divide karne se value badi hoti hai.
  • Exponent t step number hai, βt ko β·t mat padhna.

Dimaag me aise baitha lo

Register me imaginary zeros = V0 = 0. Unka hissa hai βt. Asli hissa 1 − βt. Asli hisse se divide karo, sachcha average mil jaayega.

16.5 SGD with Momentum — gradients ka EWMA = velocity

Kahani

Pahadi dhalaan pe ek bhaari gend (ball) chhodo. Gend har pal sirf apne neeche ki dhalaan nahi dekhti — uske paas pehle se ek speed (velocity) hai. Lambi seedhi dhalaan ho to speed badhti jaati hai. Raaste me chhota gaddha ya thodi ulti dhalaan aaye to wo turant palat-ti nahi; bas thodi dheemi hoti hai aur aage nikal jaati hai. Naale (ravine) me left-right uchhalne wali harkatein ek doosre ko cancel kar deti hain, aur neeche ki taraf wali harkat jud-ti jaati hai.

Worksheet 06 ki line: "A rolling bicycle does not instantly face a new direction after one small wobble." Plain SGD wo cycle hai jo har wobble pe handle ghuma deti hai.

Idea ek line ka hai: 16.2 me EWMA kisi bhi sequence pe chal sakta tha. Ab sequence = gradients. Gradients ka EWMA hi "velocity" vt hai, aur hum raw gradient ki jagah velocity ki direction me chalte hain. Classroom notebook ka key idea: Momentum = EWMA of gradients.

vt = β·vt−1 + (1 − β)·gt     wt = wt−1 − α·vt
  • gt = is step pe naapa gaya gradient ∂L/∂w (noisy observation).
  • vt = velocity = ab tak ke gradients ka running (EWMA) average = jis direction me hum asal me chal rahe hain.
  • v0 = 0 (pehle step pe koi history nahi).
  • β = momentum coefficient: purani velocity ka kitna hissa bachega. Ye tay karta hai kitne purane gradients abhi bhi matter karte hain (direction). Typical 0.9.
  • α = learning rate: velocity ki direction me kitna door jaana hai (distance). Lab/PyTorch me isi ko η ya lr kehte hain; kai books me parameter ko θ likhte hain.

Ye course ka (Worksheets 06–07, classroom notebook, Prince UDL Ch. 6) convention hai. Doosri line bilkul plain GD jaisi hai, bas gt ki jagah vt. Do hyperparameters, do alag kaam: β batata hai kidhar chalna hai, α batata hai kitna. (PyTorch wala form thoda alag dikhta hai — 16.6 me teeno forms milayenge.)

Chalo numbers se samjhein — Worksheet 07 ka "hook" (noisy gradients)

Sawaal: delivery data ke pehle teen gradients +4, +4, −1 (fixed maan lo). w0 = 2.0000, v0 = 0, β = 0.9, α = 0.1. Do baar sample "w ghatao" bole, teesra thoda sa "w badhao". Kya optimizer ko poora palat jaana chahiye?

  1. Step 1: 0.9 × 0 = 0, 0.1 × 4 = 0.4000 → v1 = 0.4000; w1 = 2.0000 − 0.1(0.4000) = 1.9600
  2. Step 2: 0.9 × 0.4000 = 0.3600, 0.1 × 4 = 0.4000 → v2 = 0.7600; w2 = 1.9600 − 0.0760 = 1.8840
  3. Step 3 (gradient ulta): 0.9 × 0.7600 = 0.6840, 0.1 × (−1) = −0.1000 → v3 = 0.5840; w3 = 1.8840 − 0.0584 = 1.8256
  4. Plain SGD, same gradients, same α: 2.0 − 0.1(4) = 1.6 → 1.6 − 0.1(4) = 1.2 → 1.2 − 0.1(−1) = 1.3 ← palat gaya!
step 1step 2step 3w* = 1.7797 se doori
plain SGD1.60001.20001.30000.4797 (1 reversal)
Momentum1.96001.88401.82560.0459 (0 reversal)

Jawab: Nahi palatna chahiye. v3 = 0.5840 abhi bhi positive hai — ulte gradient ne velocity ka sirf 23.2% kaata, 100% nahi. Momentum w* ke ~10 guna zyada paas hai.

Teacher ne poora 10-epoch (60 steps, α = 0.05) run bhi naapa: plain SGD final 1.7783, wobble (last 12 ka sd) 0.0795, 40/59 reversals, 6.3909 units chala; Momentum β = 0.9: final 1.7960, wobble 0.0073, 21/59 reversals, sirf 0.7309 units chala. Momentum ki wobble 11× chhoti aur wo 8.7× kam chala. SGD sahi jagah pahunch ke bhi kabhi rukta nahi.

Chalo numbers se samjhein — Worksheet 06 ladder (state pehle, parameter baad me)

Sawaal: v0 = 0, θ0 = 2.00, β = 0.9, α = 0.1, g1 = 4, g2 = −2, phir g3 = −2.

  1. Purani velocity ka hissa: βv0 = 0.9(0) = 0; naye gradient ka hissa: (1 − β)g1 = 0.1(4) = 0.4
  2. v1 = 0 + 0.4 = 0.4; parameter me velocity use karo, raw gradient nahi: θ1 = 2.00 − 0.1(0.4) = 1.96
  3. Ulta gradient: v2 = 0.9(0.4) + 0.1(−2) = 0.36 − 0.2 = 0.16; θ2 = 1.96 − 0.1(0.16) = 1.944
  4. Direction check: g2 negative hai, par yaad wali velocity positive → θ ab bhi ghat raha hai, bas dheere.
  5. Ek aur ulta gradient: v3 = 0.9(0.16) + 0.1(−2) = 0.144 − 0.2 = −0.056; θ3 = 1.944 − 0.1(−0.056) = 1.9496

Do lagataar ulte gradients ke baad hi velocity ka sign palta (−0.056) aur θ badhne laga. Momentum gradients ki list nahi, ek direction yaad rakhta hai.

Chalo numbers se samjhein — asli function pe momentum (Worksheet 07 "your turn")

Ek sample x = 2, y = 2: L = ½(2w − 2)2, to g(w) = x(wx − y) = 2(2w − 2) = 4(w − 1). Minimum w = 1 pe. Start w0 = 2.0000, v0 = 0, β = 0.8, α = 0.25. Rules: vt = 0.8vt−1 + 0.2gt, wt = wt−1 − 0.25vt. Har naye w pe gradient dobara nikalo.

  1. Step 1: g1 = 4(2.0000 − 1) = 4.0000; v1 = 0.8(0) + 0.2(4.0000) = 0.8000; w1 = 2.0000 − 0.25(0.8000) = 1.8000 (velocity nahi thi, to pehla step chhota)
  2. Step 2: g2 = 4(1.8000 − 1) = 3.2000; v2 = 0.6400 + 0.6400 = 1.2800; w2 = 1.8000 − 0.3200 = 1.4800
  3. v2 = 1.28 > v1 = 0.8 kyun? Gradient to chhota hua (4 → 3.2)! Kyunki do gradients lagataar ek hi direction me the, to wo jud gaye.
  4. Step 3: g3 = 4(1.4800 − 1) = 1.9200; v3 = 0.8(1.2800) + 0.2(1.9200) = 1.0240 + 0.3840 = 1.4080; w3 = 1.4800 − 0.25(1.4080) = 1.1280
  5. Ab minimum sirf 0.128 door hai, par velocity 1.4080 hai! Step 4: g4 = 4(1.1280 − 1) = 0.5120; v4 = 1.1264 + 0.1024 = 1.2288; w4 = 1.1280 − 0.3072 = 0.8208 → minimum se 0.1792 aage nikal gaya.
  6. Run chalate raho: step 5 → 0.6109, step 6 → 0.5208 (sabse neeche, minimum se 0.4792 paar). Phir wapas aata hai, aur w = 1 ke 0.01 ke andar step 42 se hi tikta hai.

Momentum: 1.8000 → 1.4800 → 1.1280 → 0.8208. Step 4 pe gradient lagbhag khatam (4 → 0.512) tha, par velocity (1.2288) ne w ko minimum ke paar dhakel diya. Ye momentum ki apni problem hai: overshoot.

Comparison ke liye plain GD ko same pehla step do (α = 0.05, kyunki momentum ka pehla step α(1 − β)g = 0.25 × 0.2 × 4 = 0.2 tha): w ← w − 0.05·4(w − 1) deta hai 1.8000 → 1.6400 → 1.5120 → 1.4096. Har step bas 20% paas. Momentum ne speed pakdi aur 4 steps me minimum tak (balki paar) pahunch gaya. Is table me NAG wala column 16.9 me bharenge:

stepplain GD (α = 0.05)Momentum (α = 0.25, β = 0.8)momentum ki velocity v
02.00002.00000
11.80001.80000.8000
21.64001.48001.2800
31.51201.12801.4080
41.40960.8208 (paar!)1.2288

16.6 Momentum ke teen roop — ek hi cheez, alag scaling

MCQ me ya PyTorch docs me tumhe momentum teen tarah likha milega. Ghabrana nahi — ye learning rate ki rescaling tak ek hi algorithm hain.

formvelocity updateweight updatekahan milega
(c) EWMA formvt = βvt−1 + (1 − β)gtw ← w − αvthamara course (worksheets, Prince UDL), Andrew Ng
(b) PyTorch formvt = βvt−1 + gtw ← w − ηvtPyTorch optim.SGD, Lab 7, Keras-style "framework note"
(a) classic formvt = βvt−1 + ηgtw ← w − vtSutskever et al. 2013, cs231n notes

Inhe barabar kaise dikhayein?

  1. (b) aur (a): (b) ki velocity equation ko η se multiply karo: ηvt = β(ηvt−1) + ηgt. Naam do u = ηv. To ut = βut−1 + ηgt aur w ← w − ut — yahi (a) hai. Same η pe bilkul same weights (sirf velocity ka scale alag).
  2. (c) aur (b): (c) ki velocity = (1 − β) × (b) ki velocity. To (c) ka update αv(c) = α(1 − β)v(b). Matlab (c) with α ≡ (b) with η = α(1 − β). Example: course ka α = 0.1, β = 0.9 ≡ PyTorch lr=0.01, momentum=0.9.

Chalo numbers se samjhein — teeno forms same weights dete hain

g(w) = 4(w − 1), w0 = 2, β = 0.9. Form (c) α = 0.1; forms (b) aur (a) η = 0.1 × (1 − 0.9) = 0.01.

step(c): v, w(b): v, w(a): v, w
10.4, 1.964.0, 1.960.04, 1.96
20.744, 1.88567.44, 1.88560.0744, 1.8856
31.02384, 1.78321610.2384, 1.7832160.102384, 1.783216
41.234742, 1.65974212.347424, 1.6597420.123474, 1.659742

Weights har form me identical; velocity sirf scale (1 : 10 : 0.1) me alag. (Python se verify.)

Terminal velocity — "momentum = 10× learning rate" wali baat

Agar gradient hamesha same g rahe (lambi seedhi dhalaan), velocity kahan tak badhegi? Steady state me v = βv + (...) solve karo:

Iska practical matlab: PyTorch me momentum on karte hi agar purana lr hi rakha to effective step kaafi bada ho sakta hai — isliye momentum ke saath lr aksar chhota rakhte hain (Lab 7: plain SGD lr 0.18, momentum ke saath 0.08).

Galti mat karna

  • PyTorch ka dampening argument velocity me (1 − dampening)·g jodta hai, par pehle step pe buffer = g hi hota hai (dampening nahi lagta). Isliye dampening=0.8, momentum=0.8, lr=0.25 course ke numbers nahi dega (pehla step hi 2.0 → 1.0 ho jaata hai). Course numbers chahiye to lr = α(1 − β) aur dampening = 0 rakho.
  • MCQ me formula dekh ke pehchano: "(1 − β)" dikhe to EWMA form; sirf "+ g" ho to PyTorch form; "+ η g" ho to classic form.

16.7 Momentum kyun kaam karta hai — aur kahan fail hota hai

Fayda 1: Consistent direction me speed badhti hai (acceleration)

Unroll karo: vt = (1−β)[gt + βgt−1 + β2gt−2 + ...]. Teacher ke slide (Prince) ki line: "gradient step is an infinite weighted sum of all the previous gradients... The effective learning rate increases if all these gradients are aligned over multiple iterations but decreases if the gradient direction repeatedly changes as the terms in the sum cancel out." Jab saare terms same sign ke hon, wo jud-te hain — ravine ke flat floor pe yahi hota hai.

Fayda 2: Oscillations cancel hoti hain

Ravine ki steep direction me gradient ka sign har step palatta hai: +3, −3, +3, −3. Inka average ≈ 0 — to velocity me ye direction lagbhag gayab. Lab 7 ke shabdon me: "Gradients that oscillate back and forth (along w1) have alternating signs and cancel out in the velocity accumulator. Gradients pointing in the same continuous direction (along w0) accumulate and accelerate progress." 16.1 wali figure me yahi dikha.

Fayda 3: Chhote gaddhe / shallow local minimum se nikal sakta hai

Teacher ka Worksheet 07 Part 4: do ghaati wala loss f(w) = 0.25(w2 − 1)2 + 0.2w, f′(w) = w3 − w + 0.2. Flat points: w = −1.0880 (global minimum, f = −0.2092), w = +0.2091 (ridge/deewar, f = 0.2704), w = +0.8789 (local minimum, f = 0.1887). Local minimum se ridge tak chadhai = 0.2704 − 0.1887 = 0.0817. Start w0 = 1.4, α = 0.5, 500 steps (EWMA-form momentum):

−1.5−1−0.500.511.5 w f(w) = 0.25(w² − 1)² + 0.2w β = 0.9 ki gend: ridge ke upar se start w₀ = 1.4 ridge 0.2091 β = 0.8 yahan tak (0.4669) local min 0.8789: β = 0, 0.5, 0.8 yahin atke global min −1.088 ← β = 0.9, 0.95, 0.98
β = 0.9 wali gend outer wall se ludhakte hue itni velocity jama karti hai ki local minimum ke paar ridge (0.2091) tak chadh jaati hai aur global minimum (−1.0880) pe pahunchti hai. β = 0.8 sirf 0.4669 tak chadh paati hai (ridge se 0.2578 kam), phir wapas shallow ghaati me gir jaati hai.
β500 steps baad kahan (w₀ = 1.4, α = 0.5)
0 (plain GD)+0.8789 — shallow ghaati me atka
0.5+0.8789 — atka
0.8+0.8789 — atka
0.9−1.0880 — global minimum
0.95−1.0880 — global minimum
0.98−1.0880 — global minimum

β = 0.9 run ko dhyaan se dekho (teacher ki table J): step 5 pe w = 0.7547, gradient lagbhag zero (0.0168), par velocity 0.2732 badi hai. Step 6 se 17 tak, 12 lagataar steps, gradient ulta (peeche) bolta raha aur momentum phir bhi aage chalta raha — velocity dheere-dheere khatam hoti rahi (step 9: 0.1229, step 14: 0.0385) aur step 17 pe w = 0.2022 pe ridge paar. Energy kahan se aayi? Outer wall pe ludhakte waqt jama hue gradients se, jo v me store the.

Imaandaar caveat (teacher ka): Ye isliye chala kyunki flat jagah aane se pehle v badi thi. Agar start hi w0 = 0.95 ho (shallow ghaati ke bilkul andar), to har β atak jaata hai. Momentum search strategy nahi hai, memory hai — aur memory tabhi kaam aati hai jab yaad rakhne layak kuch ho.

Nuksaan

Exam Radar

  • Momentum = EWMA of gradients; stores velocity (a gradient average), not labels / Hessian.
  • Typical β (momentum) = 0.9. Effective memory ~10 steps.
  • Same-direction gradients → accumulate → acceleration; alternating gradients → cancel → less zig-zag.
  • Ek ulta gradient velocity ko turant reverse nahi karta (weight sirf 1 − β).
  • PyTorch form me constant gradient pe velocity → g/(1 − β) = 10g (β = 0.9).
  • Main drawback: overshoot near the minimum. History: heavy-ball method, Polyak 1964; backprop me Rumelhart, Hinton & Williams (1986).

16.8 PyTorch me momentum: optim.SGD(..., momentum=0.9)

PyTorch me momentum alag class nahi hai — optim.SGD ka ek argument hai:

API

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# poora signature (important args):
# optim.SGD(params, lr=1e-3, momentum=0, dampening=0, weight_decay=0, nesterov=False)
PyTorch: bt = μ·bt−1 + (1 − τ)·gt  (pehle step pe b1 = g1)    w ← w − lr·bt
  • b = "momentum buffer" (velocity), har parameter ke liye optimizer ki state me store hota hai.
  • μ = momentum, τ = dampening. τ = 0 pe ye exactly form (b) hai.

PyTorch · course ke worksheet numbers PyTorch se reproduce karo

import torch
import torch.optim as optim

# ek sample x=2, y=2  ->  L = 0.5*(w*x - y)^2  ->  grad = 4(w - 1)
x, y = 2.0, 2.0
w = torch.tensor([2.0], requires_grad=True)

alpha, beta = 0.25, 0.8                       # course (EWMA form) ke numbers
optimizer = optim.SGD([w], lr=alpha * (1 - beta), momentum=beta)   # lr = 0.05

for step in range(1, 5):
    optimizer.zero_grad()                     # purane .grad saaf
    loss = 0.5 * (w * x - y) ** 2             # forward
    loss.backward()                           # w.grad = 4(w - 1)
    optimizer.step()                          # buffer update + weight update
    print(f"step {step}: w = {w.item():.4f}")

Output (asli PyTorch run)

step 1: w = 1.8000
step 2: w = 1.4800
step 3: w = 1.1280
step 4: w = 0.8208

Bilkul worksheet wale numbers! Kyunki form (c) with α = 0.25 ≡ PyTorch with lr = 0.25 × (1 − 0.8) = 0.05.

Python · manual momentum (course ka EWMA form), bina PyTorch

def grad(w):
    return 4 * (w - 1)            # dL/dw for x=2, y=2

w, v = 2.0, 0.0                   # v0 = 0
alpha, beta = 0.25, 0.8
for t in range(1, 7):
    g = grad(w)                               # 1) gradient abhi wale w pe
    v = beta * v + (1 - beta) * g             # 2) velocity = EWMA of gradients
    w = w - alpha * v                         # 3) velocity ki direction me kadam
    print(f"t={t}  g={g:.4f}  v={v:.4f}  w={w:.4f}")

Output

t=1  g=4.0000  v=0.8000  w=1.8000
t=2  g=3.2000  v=1.2800  w=1.4800
t=3  g=1.9200  v=1.4080  w=1.1280
t=4  g=0.5120  v=1.2288  w=0.8208
t=5  g=-0.7168  v=0.8397  w=0.6109
t=6  g=-1.5565  v=0.3604  w=0.5208

Training loop me kuch nahi badalta — sirf optimizer wali line: optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9), baaki zero_grad → forward → loss → backward → step wahi (Chapter 13).

Galti mat karna

  • optim.Momentum naam ki koi class nahi hai. Momentum = optim.SGD(..., momentum=0.9).
  • Momentum buffer optimizer ke andar rehta hai. Har epoch naya optimizer banaoge to velocity reset ho jaayegi — optimizer loop ke bahar ek baar banao.
  • optimizer.zero_grad() bhoole to .grad jud-te jaayenge (ye momentum nahi, bug hai).

Dimaag me aise baitha lo

Momentum = ludhakti gend. β = kitni speed bachi rehti hai, α = kitna lamba kadam. Seedhi dhalaan pe tez, uchhal-kood cancel, par minimum ke paas brake nahi.

16.9 NAG (Nesterov Accelerated Gradient) — look before you leap

Kahani

Raju auto-rickshaw wala tez chala raha hai. Aage speed-breaker hai. Naya driver tab brake marta hai jab pahiya speed-breaker pe chadh chuka hota hai — dhadaam! Raju experienced hai: wo dekhta hai ki is speed se main 2 second baad kahan hounga, wahan speed-breaker dikhta hai, to pehle se brake laga deta hai. Worksheet 06 me yahi cyclist ki kahani hai jo sharp mod (bend) aane se pehle aage dekh ke dheema ho jaata hai.

Momentum naya driver hai: gradient wahin naapta hai jahan abhi khada hai. NAG Raju hai: gradient wahan naapta hai jahan velocity use le ja rahi hai.

Look-ahead point aaya kahan se? (teacher ka derivation)

Momentum ki dono lines ko jodo (velocity ko weight wali line me substitute karo):

wt = wt−1 − α( βvt−1 + (1 − β)gt )
= wt−1 − αβvt−1 − α(1 − β)gt
       ↑ pehle se tay (committed)     ↑ naya gradient ka contribution

Pehle term me gt hai hi nahi! Naya gradient kuch bhi ho, step ka ye hissa to hona hi hai. To gradient naapne ki imaandaar jagah us committed hisse ke end pe hai: wlook = wt−1 − αβvt−1. Gradient wahan naapo.

1 predict: wlook = wt−1 − αβ·vt−1
2 measure: g = ∇L(wlook)
3 correct: vt = β·vt−1 + (1 − β)·g
4 move: wt = wt−1 − α·vt
  • wlook = look-ahead point: jahan step ka already-committed hissa tumhe le jaayega.
  • αβvt−1 = wahi committed hissa. Factor αβ hai, sirf α nahi, kyunki velocity ka sirf βvt−1 hissa naya gradient aane se pehle fix hai.
  • g = gradient, wlook pe naapa gaya, wt−1 pe nahi. Poora farak bas yahi hai.
  • vt, wt, α, β — sab momentum jaise. Actual update hamesha asli wt−1 se hota hai, look-ahead point se nahi.

Teacher ki line: "The order is the method. Swap actions 1 and 2 and you are back to plain momentum." Aur Worksheet 06: "Momentum asks: what is the slope here? NAG asks: what is the slope where momentum is taking me?"

Pehla step hamesha same: v0 = 0 hai, to wlook = w0. NAG aur momentum ka step 1 identical hota hai; farak step 2 se shuru hota hai jab velocity ho.

Doosra common form (Sutskever / cs231n)

vt = β·vt−1 + η·∇L(θt−1 − βvt−1)     θt = θt−1 − vt
  • Ye momentum ka form (a) hai, bas gradient look-ahead point θ − βv pe. Yahan look-ahead "−βv" hai (−αβv nahi) kyunki is form me v ke andar η pehle se ghusa hua hai.
  • 16.6 jaisa hi: course form (α, β) ≡ is form ke saath η = α(1 − β). Lab 7 isko "gradient computed at wt − ηβvt" likhta hai (PyTorch-form velocity ke saath).
Momentum NAG (Nesterov) minimum A (abhi yahan) ① committed −αβv ② gradient (A pe naapa) bolta: "aage chalo" actual step: minimum ke paar! minimum A (abhi yahan) ① pehle jump (predict) L (look-ahead) ② gradient L pe naapa bolta: "wapas aao" (brake) actual step: minimum ke paas ruka
Dono me committed hissa (① pencil) same hai. Momentum gradient A pe naapta hai, jahan slope abhi bhi "aage chalo" kehta hai, to dono jud ke minimum ke paar chale jaate hain. NAG pehle L tak jhaank leta hai; L minimum ke paar hai, wahan ka gradient peeche point karta hai, to velocity kam hoti hai aur step minimum ke paas khatam hota hai.

Chalo numbers se samjhein — Worksheet 06 ladder (ek step)

Sawaal: θt = 2.000, vt = 0.500, α = 0.1, β = 0.9, aur look-ahead pe gradient ∇L(θlook) = 3.000. (Worksheet index t → t+1 use karti hai; matlab same.)

  1. Predict: θlook = 2.000 − 0.1(0.9)(0.500) = 2.000 − 0.045 = 1.955
  2. Correct ke do terms: βvt = 0.9(0.500) = 0.450, (1 − β)∇L(θlook) = 0.1(3.000) = 0.300
  3. Jodo: vt+1 = 0.450 + 0.300 = 0.750
  4. Move (asli parameter se): θt+1 = 2.000 − 0.1(0.750) = 1.925

θ: 2.000 → 1.925. Velocity ne look-ahead point ko downhill bheja; wahan ka gradient next velocity ko correct karta hai.

Practice: θt = 1.500, vt = 0.200, α = 0.1, β = 0.8, ∇L(θlook) = −1.000 → θlook = 1.500 − 0.1(0.8)(0.200) = 1.484, vt+1 = 0.8(0.200) + 0.2(−1) = 0.160 − 0.200 = −0.040, θt+1 = 1.500 − 0.1(−0.040) = 1.504.

Chalo numbers se samjhein — momentum vs NAG, same problem (Worksheet 07 solved)

Wahi 16.5 wala: g(w) = 4(w − 1), minimum w = 1, w0 = 2.0000, v0 = 0, α = 0.25, β = 0.8. Shortcut: αβ = 0.25 × 0.8 = 0.2, to wlook = w − 0.2v.

  1. Step 1 — predict wlook = 2.0000 − 0.2(0) = 2.0000; measure g = 4(2.0000 − 1) = 4.0000; correct v1 = 0.8(0) + 0.2(4.0000) = 0.8000; move w1 = 2.0000 − 0.25(0.8000) = 1.8000. Momentum ne bhi 1.8000 diya — expected.
  2. Step 2 — wlook = 1.8000 − 0.2(0.8000) = 1.6400; g = 4(1.6400 − 1) = 2.5600 (momentum ne 3.2000 use kiya tha); v2 = 0.6400 + 0.5120 = 1.1520; w2 = 1.8000 − 0.2880 = 1.5120. Momentum 1.4800 pe tha — NAG thoda peeche hai: aage chhota gradient dikha, to kam velocity banayi.
  3. Step 3 — wlook = 1.5120 − 0.2(1.1520) = 1.2816; g = 4(0.2816) = 1.1264; v3 = 0.9216 + 0.2253 = 1.1469; w3 = 1.5120 − 0.25(1.1469) = 1.2253
  4. Step 4 (payoff) — wlook = 1.2253 − 0.2(1.1469) = 0.9959 ← minimum ke paar ja chuka! g = 4(0.9959 − 1) = −0.0164 ← negative, peeche bolta hai; v4 = 0.8(1.1469) + 0.2(−0.0164) = 0.9175 − 0.0033 = 0.9142; w4 = 1.2253 − 0.25(0.9142) = 1.2253 − 0.2286 = 0.9967
  5. Yahi brake hai: w = 1.2253 pe asli gradient 4(0.2253) = +0.9012 tha ("aur neeche jao"), par look-ahead minimum cross kar chuka tha, to wahan ka gradient negative aaya aur velocity badhne ke bajaye ghati. Momentum ne apne step 4 pe current point (1.1280) pe gradient +0.512 naapa, velocity 1.2288 rakhi, aur 0.8208 pe gira.

NAG: 1.8000 → 1.5120 → 1.2253 → 0.9967 (minimum se sirf 0.0033 paar). Momentum: → 0.8208 (0.1792 paar).

stepplain GD (α = 0.05)MomentumNAG
11.80001.80001.8000
21.64001.48001.5120 (NAG early me peeche)
31.51201.12801.2253
41.40960.82080.9967
lowest w reached— (kabhi paar nahi)0.52080.7877
overshoot past w = 100.47920.2123 (56% chhota)
w = 1 ke 0.01 ke andar kab se—step 42step 17 (2.5× jaldi)

Teacher ki line: "NAG gives up a little on the way in and saves a lot at the bottom." Dono baatein table me hain — exam me dono bolna.

Chalo numbers se samjhein — Worksheet 07 "NAG by hand"

Naya sample x = 2, y = 6 → g(w) = 2(2w − 6) = 4(w − 3), minimum w = 3. w0 = 5.0000, v0 = 0, β = 0.8, α = 0.25, αβ = 0.2.

stepw_look = w − 0.2vg = 4(w_look − 3)v = 0.8v + 0.2gw = w − 0.25v (NAG)Momentum ka w
15.00008.00001.60004.60004.6000
24.6000 − 0.3200 = 4.28005.12001.2800 + 1.0240 = 2.30404.02403.9600
34.0240 − 0.4608 = 3.56322.25281.8432 + 0.4506 = 2.29383.45053.2560
43.4505 − 0.4588 = 2.9917−0.03321.8350 − 0.0066 = 1.82842.99342.6416

Step 4 pe momentum minimum se 0.3584 paar (2.6416), NAG sirf 0.0066 paar (2.9934) — 54× chhota overshoot, sirf ek badlaav se: gradient wlook pe naapna.

Exam Radar

  • NAG gradient look-ahead point pe naapta hai: wlook = w − αβv (course form) ya θ − βv (classic form).
  • 4 ordered actions: predict → measure → correct → move. Pehle do swap kiye = plain momentum.
  • Pehla step momentum ke barabar (v0 = 0). NAG early me thoda peeche, par overshoot kam, settle jaldi.
  • NAG fixes momentum's overshoot ("brakes"); still one global learning rate for all parameters.
  • Yuri Nesterov, 1983 (convex optimization); deep learning me Sutskever et al. 2013 ne popular kiya.

16.10 NAG in PyTorch: nesterov=True + manual implementation

API

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)

PyTorch · nesterov=True, worksheet wala problem

import torch
import torch.optim as optim

x, y = 2.0, 2.0                                   # grad = 4(w - 1)
w = torch.tensor([2.0], requires_grad=True)
optimizer = optim.SGD([w], lr=0.25 * (1 - 0.8), momentum=0.8, nesterov=True)

for step in range(1, 6):
    optimizer.zero_grad()
    loss = 0.5 * (w * x - y) ** 2
    loss.backward()
    optimizer.step()
    print(f"step {step}: w = {w.item():.4f}")

Output (asli PyTorch run)

step 1: w = 1.6400
step 2: w = 1.2816
step 3: w = 0.9959
step 4: w = 0.8139
step 5: w = 0.7346

Dhyaan se dekho: 1.6400, 1.2816, 0.9959 — ye bilkul upar wale hand-calculation ke wlook values hain (step 2, 3, 4 ke)! PyTorch ka parameter "ek kadam aage jhaanka hua" point hai. Same algorithm, alag bookkeeping.

Python · manual NAG (course form) — hand numbers verify

def grad(w):
    return 4 * (w - 1)

w, v = 2.0, 0.0
alpha, beta = 0.25, 0.8
for t in range(1, 5):
    w_look = w - alpha * beta * v          # 1) predict: committed hissa aage
    g = grad(w_look)                       # 2) measure: gradient WAHAN
    v = beta * v + (1 - beta) * g          # 3) correct: velocity update
    w = w - alpha * v                      # 4) move: asli w se kadam
    print(f"t={t}  w_look={w_look:.4f}  g={g:.4f}  v={v:.4f}  w={w:.4f}")

Output

t=1  w_look=2.0000  g=4.0000  v=0.8000  w=1.8000
t=2  w_look=1.6400  g=2.5600  v=1.1520  w=1.5120
t=3  w_look=1.2816  g=1.1264  v=1.1469  w=1.2253
t=4  w_look=0.9959  g=-0.0164  v=0.9142  w=0.9967

Chalo numbers se samjhein — PyTorch form me sab ek saath (f(θ) = θ²)

f(θ) = θ2, gradient 2θ, θ0 = 1, lr η = 0.1, momentum 0.9 (PyTorch form b). Plain GD: θ ← θ − 0.1(2θ) = 0.8θ.

  1. Momentum step 1: g = 2, v = 2, θ = 1 − 0.1(2) = 0.8
  2. Step 2: g = 1.6, v = 0.9(2) + 1.6 = 3.4, θ = 0.8 − 0.34 = 0.46
  3. Step 3: g = 0.92, v = 3.06 + 0.92 = 3.98, θ = 0.46 − 0.398 = 0.062
  4. Step 4: g = 0.124, v = 3.582 + 0.124 = 3.706, θ = 0.062 − 0.3706 = −0.3086 (overshoot)
  5. Classic NAG (form a, η = 0.1, look-ahead θ − 0.9v, yahan v me η shaamil): θ = 0.8, 0.496, 0.1779, −0.0867.
stepGDMomentum (PyTorch)classic NAG θPyTorch nesterov=True print
10.80000.80000.80000.6200
20.64000.46000.49600.2224
30.51200.06200.1779−0.1084
40.4096−0.3086−0.0867−0.3248

Momentum ne 4 steps me GD se kahin tez safar kiya par −0.3086 tak overshoot kiya; classic NAG ka overshoot sirf −0.0867. PyTorch ka nesterov column classic NAG ke look-ahead points (θ − 0.9v) hain: e.g. step 1 pe 0.8 − 0.9(0.2) = 0.62.

Galti mat karna

  • nesterov=True akela kaafi nahi — momentum bhi dena padega (default 0 hai, to error).
  • NAG ka actual update hamesha current w se hota hai; look-ahead point sirf gradient naapne ke liye hai.
  • Look-ahead factor course form me αβv hai; αv (poora step) nahi.

16.11 Chapter 16 ek nazar me

Plain SGDSGD + MomentumNAG
yaad kya rakhta haikuch nahivelocity = gradients ka EWMAvelocity + look-ahead measurement
gradient kahan naapta haicurrent wcurrent ww − αβv (look-ahead)
extra memory / parameter01 (velocity)1 (velocity)
hyperparametersαα, β (0.9)α, β (0.9)
problem jo fix kiya—zig-zag, noise, slow flat directionsmomentum ka overshoot
bachi hui problemnoise, zig-zagovershoot; same α sabke liyesame α sabke liye
PyTorchoptim.SGD(p, lr)optim.SGD(p, lr, momentum=0.9)optim.SGD(p, lr, momentum=0.9, nesterov=True)

Exam Radar

  • Increasing EWMA β gives a smoother (and longer) memory. (Worksheet 06 MCQ.)
  • Momentum stores a gradient average. NAG measures its gradient at the look-ahead point.
  • Course formulas: vt = βvt−1 + (1 − β)gt, wt = wt−1 − αvt; NAG wlook = wt−1 − αβvt−1.
  • PyTorch: optim.SGD(params, lr=0.01, momentum=0.9); nesterov=True needs momentum > 0, dampening = 0. SGD defaults: momentum 0, dampening 0, weight_decay 0, nesterov False.
  • Course form (α, β) ≡ PyTorch form with lr = α(1 − β).
  • Diagnosis map (Worksheet 06): noisy direction → Momentum; overshoot risk → NAG.

Dimaag me aise baitha lo

SGD reacts, EWMA remembers, Momentum rolls, NAG peeks. "Momentum: slope yahan kya hai? NAG: slope wahan kya hai jahan main ja raha hoon?"

Chapter 17

Adaptive Learning Rates: AdaGrad, RMSProp, Adam (+AdamW)

Har parameter ka apna step size · g² ki memory · bias correction · decoupled weight decay · Optimizer Zoo

Momentum ne direction theek ki — kidhar chalna hai. Par kitna chalna hai, wo abhi bhi har parameter ke liye same α se tay hota hai. Teacher ke Worksheet 08 ka title hi yahi kehta hai: "One Learning Rate Is Not Enough — every parameter gets its own step size, measured from its own gradients." Is chapter ka safar: SGD → AdaGrad (g² ka total) → RMSProp (g² ka EWMA) → Adam (EWMA of g + EWMA of g² + bias correction) → AdamW (weight decay alag).

17.1 Ek learning rate sabke liye kyun nahi chalta

Kahani

Board exam aa rahe hain. Golu ki mummy uski padhai sambhal rahi hain. Physics ke sawaal roz aate hain, har din bade-bade galtiyan dikhti hain (bade gradients) — mummy kehti hain "Physics me ab chhote-chhote sudhaar karo, bahut ho gaya". Sanskrit hafte me ek baar padha jaata hai, galtiyan chhoti aur kabhi-kabhi dikhti hain (chhote, rare gradients) — mummy kehti hain "jab bhi Sanskrit ka mauka mile, zor laga ke padho". Har subject ka apna "kitna zor" — uske apne record ke hisaab se.

Ek aur example: WhatsApp group ki chat se language model seekh raha hai. "hai", "the", "ok" jaise words har message me aate hain (frequent features, unke weights ko roz update milta hai). "Kanyakumari" ya "Dhoni" kabhi-kabhi aate hain (rare / sparse features). Rare word ko jab bhi update mile, wo bada hona chahiye, warna wo kabhi seekh hi nahi paayega.

Chalo numbers se samjhein — Worksheet 08 Part 1

Ek network ke do parameters: w1 steep direction pe, w2 flat direction pe. Dono 1.0000 se shuru. Measured gradients: w1 ke 3 phir 4; w2 ke 0.3 phir 0.4 (har step 10 guna chhote). Plain SGD, α = 0.1:

  1. w1: 1.0000 − 0.1(3) = 0.7000, phir 0.7000 − 0.1(4) = 0.3000
  2. w2: 1.0000 − 0.1(0.3) = 0.9700, phir 0.9700 − 0.1(0.4) = 0.9300

w1 0.7000 chala, w2 sirf 0.0700 — das guna farak, kyunki step = α × gradient, aur kuch nahi.

α ghatao to w1 sudhar jaata hai par w2 ruk jaata hai; badhao to w2 chalta hai par w1 overshoot/diverge. Teacher ki animation ka measurement (steep aur flat direction ko finish hone me kitne steps):

αsteep direction ke stepsflat direction ke stepsflat, momentum β = 0.9 ke saath
0.022123012268
0.083574539
0.1944241200
0.22diverges165—

α = 0.08 pe steep direction 3 steps me khatam, flat ko 574 chahiye. Momentum 574 → 539 (sirf 6% sudhaar). Koi bhi ek α dono ke liye sahi nahi. Jawab (Worksheet 08): har parameter ka step uske apne gradients ke "running size" se divide karo. Jis parameter ke gradients lagataar bade rahe (steep), wo chhote kadam le; jiske chhote rahe (flat), wo bade kadam le. Ab poora chapter ek sawaal hai: wo running measure kya ho?

Definition

Adaptive learning rate method: optimizer jo har parameter ke liye alag effective learning rate rakhta hai, jo us parameter ke pichhle gradients ke size (aksar squared gradients) se automatically set hota hai. Examples: AdaGrad, RMSProp, Adam, AdamW.

17.2 AdaGrad — apni history se divide karo

AdaGrad (Adaptive Gradient) — Duchi, Hazan & Singer, 2011 (JMLR). Gradient ka size naapne ka sabse simple tareeka: har parameter ke saare squared gradients jodte jao. Square isliye ki sign cancel na ho (−10 bhi +10 jitna bada gradient hai).

Gt = Gt−1 + gt2     wt = wt−1 − α√Gt + ε·gt
  • gt = is parameter ka current gradient; gt2 = uska square (element-wise: har parameter apna-apna, ⊙).
  • Gt = accumulator: ab tak ke saare squared gradients ka sum, G0 = 0. Har parameter ka apna G (worksheet 06 ise rt, classroom notebook st likhta hai).
  • α/(√Gt + ε) = us parameter ka effective (adaptive) learning rate.
  • ε (epsilon) ≈ 10−8 (PyTorch Adagrad me default 10−10): bas zero se divide hone se bachane ke liye; hand calculation me ignore karte hain. Kuch books √(G + ε) likhti hain — idea same.

√ kyun, G se seedha divide kyun nahi? Units! G gradient² ki units me hai; √G wapas gradient ki units me. To g/√G ek pure number ban jaata hai (size-free), aur step ka size α tay karta hai. Worksheet 08 ki line: "The gradient now sets the direction of the step. It no longer sets the distance."

Chalo numbers se samjhein — Worksheet 08 Part 2 (do parameters)

Sawaal: w1 = w2 = 1.0000, α = 0.1, G0 = 0. Gradients w1: 3, 4; w2: 0.3, 0.4. ε ignore.

  1. Step 1, w1: G1 = 0 + 32 = 9.0000, √9 = 3.0000, step = 0.1(3)/3.0000 = 0.1000
  2. Step 1, w2: G1 = 0 + 0.32 = 0.0900, √0.09 = 0.3000, step = 0.1(0.3)/0.3000 = 0.1000. Gradients 10 guna alag, steps bilkul barabar!
  3. Step 2, w1: G2 = 9.0000 + 42 = 25.0000, √25 = 5.0000, step = 0.1(4)/5.0000 = 0.0800; w1 = 0.9000 − 0.0800 = 0.8200
  4. Step 2, w2: G2 = 0.0900 + 0.42 = 0.2500, √0.25 = 0.5000, step = 0.1(0.4)/0.5000 = 0.0800; w2 = 0.8200
G₁step 1G₂step 2w after
w₁9.00000.100025.00000.08000.8200
w₂0.09000.10000.25000.08000.8200

Plain SGD ne inhe 0.3000 aur 0.9300 pe chhoda; AdaGrad ne dono ko 0.8200 aur 0.8200 pe. Parameter ke saare gradients ko 10 se multiply karo to √G bhi 10 guna, ratio g/√G wahi.

Pehla step hamesha exactly α: t = 1 pe G1 = g12, to step = αg1/|g1| = ±α, gradient chahe kuch bhi ho.

Chalo numbers se samjhein — Worksheet 06 (vector, ek saath)

Sawaal: rt−1 = (0, 0), gt = (4, 1), ε ignore.

  1. Square each coordinate: g2 = (42, 12) = (16, 1)
  2. Accumulator: rt = (0, 0) + (16, 1) = (16, 1)
  3. Square root: √rt = (√16, √1) = (4, 1)
  4. Normalise: g/√r = (4/4, 1/1) = (1, 1) — raw gradient 4 wala coordinate bhi effective 1, aur 1 wala bhi 1.
  5. Agla identical gradient (4, 1): r = (32, 2), g/√r = (4/5.657, 1/1.414) = (0.707, 0.707) — step chhota ho gaya.

AdaGrad baar-baar bade gradient wale coordinate ko "cautious" banata hai. Drawback: cumulative denominator kabhi bhoolta nahi.

Chalo numbers se samjhein — step kaise sikudta hai (classroom notebook, 4 steps)

Sawaal: w0 = 5, s0 = 0, α = 0.1, har step gradient g = 2.

  1. s1 = 0 + 22 = 4, step = 0.1 × 2/√4 = 0.1000, w1 = 4.9000
  2. s2 = 4 + 4 = 8, step = 0.1 × 2/√8 = 0.2/2.8284 = 0.0707, w2 = 4.8293
  3. s3 = 12, step = 0.2/3.4641 = 0.0577, w3 = 4.7716
  4. s4 = 16, step = 0.2/4 = 0.0500, w4 = 4.7216

Gradient same (2) raha, phir bhi step 0.1 → 0.0707 → 0.0577 → 0.05. Constant gradient pe step = α/√t.

AdaGrad kya tod deta hai: learning rate mar jaata hai

g2 kabhi negative nahi, to G sirf badhta hai. Steady gradient g ke liye Gt = tg2, to effective step = α/√(tg2) = (α/g)·(1/√t):

t steps baadGteffective step α/√Gt
1g²1.0000 α/g
44g²0.5000 α/g
2525g²0.2000 α/g
100100g²0.1000 α/g
400400g²0.0500 α/g
25002500g²0.0200 α/g
1000010000g²0.0100 α/g

Teacher ne Part 1 wale loss pe naapa (α = 0.1): flat direction ka effective step 1 step baad 0.1000, 10 baad 0.0325, 100 baad 0.0113, 1000 baad 0.0049 (α ka sirf 4.9%). AdaGrad ko flat direction 1% tak laane me 10590 steps lage, jabki plain SGD (α = 0.19) ne 241 me kar diya. "Equalising the two parameters cost us the ability to finish." Wajah: measure ke paas bhoolne ka koi tareeka nahi — step 5 ka gradient aur step 5000 ka gradient G me barabar gina jaata hai, jabki parameter tab tak bahut aage nikal chuka.

Fayde: per-parameter learning rate khud set hota hai (manual tuning kam); sparse features ke liye bahut accha (NLP word embeddings, recommendation — rare features ka G chhota rehta hai, to unhe bade updates milte hain); gradient scale se fark nahi padta. Nuksaan: G sirf badhta hai → effective LR → 0 → learning ruk jaati hai (deep nets ki lambi training me pehle hi); har parameter ke liye ek extra accumulator (memory). Lab 7 ke chikne quadratic bowl pe yahi "weakness" faayda ban gayi (decaying step ne bina overshoot ke minimum pe utaar diya — 17.8 dekho).

PyTorch · optim.Adagrad — Worksheet 08 numbers reproduce

import torch
import torch.optim as optim

w = torch.tensor([1.0, 1.0], requires_grad=True)     # [w1 (steep), w2 (flat)]
optimizer = optim.Adagrad([w], lr=0.1)               # defaults: lr_decay=0, initial_accumulator_value=0, eps=1e-10

for g in [[3.0, 0.3], [4.0, 0.4]]:                   # worksheet ke measured gradients
    w.grad = torch.tensor(g)          # gradient seedha daal diya (optimizer sirf .grad padhta hai)
    optimizer.step()
    print(w.tolist())

Output (asli PyTorch run)

[0.8999999761581421, 0.8999999761581421]
[0.8199999928474426, 0.8199999928474426]

Float32 ki wajah se 0.8999999... dikhta hai — matlab 0.9000 aur 0.8200. Trick note karo: optimizer.step() sirf p.grad padhta hai, to hum w.grad me koi bhi gradient daal ke hand-calculation check kar sakte hain. Normal training me ye loss.backward() bharta hai.

numpy · manual AdaGrad (element-wise, vector parameters)

import numpy as np

w = np.array([1.0, 1.0])
G = np.zeros_like(w)              # har parameter ka apna accumulator, G0 = 0
lr, eps = 0.1, 1e-8
for g in [np.array([3.0, 0.3]), np.array([4.0, 0.4])]:
    G = G + g ** 2                            # squared gradients ka running SUM
    step = lr * g / (np.sqrt(G) + eps)        # har coordinate ka apna effective lr
    w = w - step
    print("G =", G, " step =", step.round(4), " w =", w.round(4))

Output

G = [9.   0.09]  step = [0.1 0.1]  w = [0.9 0.9]
G = [25.    0.25]  step = [0.08 0.08]  w = [0.82 0.82]

Exam Radar

  • AdaGrad: accumulates sum of squared gradients forever (cumulative g²), per parameter.
  • Effective LR = α/(√G + ε): frequent/large-gradient parameters → small steps; rare/small → relatively large steps → sparse data (NLP) ke liye best.
  • Main drawback: learning rate monotonically decays to ~0 → training premature stop. Constant gradient pe step ∝ 1/√t.
  • First step = exactly α (sign of g). PyTorch defaults: lr 0.01, eps 1e-10.

17.3 RMSProp — aisa average jo bhoolna jaanta hai

Kahani

Chhata (umbrella) le ke jaayein ya nahi? AdaGrad wala aadmi pichhle 10 saal ki har weather report jod ke faisla karta hai — 2016 ka toofan aaj bhi uske faisle me utna hi bhaari hai. RMSProp wala aadmi haal-filhaal ka mausam yaad rakhta hai; mahino purana toofan dheere-dheere fade ho jaata hai (Worksheet 06 ki analogy). Pahadi trekker bhi aisa hi: dheeli patthar wali jagah chhote kadam, pakki zameen aayi to phir normal kadam — purani dheeli zameen ki yaad hamesha ke liye nahi.

RMSProp (Root Mean Square Propagation) — Geoffrey Hinton ne apne Coursera course "Neural Networks for Machine Learning" ke Lecture 6e (2012) me propose kiya (Tieleman & Hinton). Ye kabhi formal paper me publish nahi hua — famous "unpublished" optimizer. Idea: Chapter 16 ka EWMA gradients pe chala ke direction smooth ki thi; wahi EWMA squared gradients pe chalao to size track ho jaayega.

E[g2]t = β·E[g2]t−1 + (1 − β)·gt2     wt = wt−1 − α√E[g2]t + ε·gt
  • E[g2]t = squared gradients ka EWMA (running mean of g²). Worksheet 08 ise vt (decay β2), Worksheet 06 st (decay ρ), Lab 7 aur PyTorch decay ko alpha, Keras rho kehte hain. Sab ek hi cheez.
  • √E[g2] = "Root of Mean of Squares" = RMS of recent gradients → naam RMSProp.
  • β = decay rate, typical 0.9 (Hinton), PyTorch default alpha=0.99. ~1/(1 − β) recent gradients "recent" maane jaate hain.
  • α = learning rate (Hinton ne 0.001 suggest kiya; PyTorch default lr = 0.01). ε ≈ 10−8.

Teacher: "Only the accumulator changed. The update line is AdaGrad's." AdaGrad har squared gradient jodta hai; RMSProp recent waalon ka average leta hai. Classroom notebook: "That one change is the core evolution from Adagrad to RMSProp."

AdaGrad

Gt = Gt−1 + gt2
saare purane g² hamesha ke liye
late training: aur darpok (step → 0)

RMSProp

vt = βvt−1 + (1 − β)gt2
purana scale baar-baar discount
late training: step wapas aa sakta hai

Chalo numbers se samjhein — Worksheet 08 Part 4 (β₂ = 0.9)

Sawaal: w1 = 1.0000, v0 = 0, α = 0.1, β2 = 0.9, gradients 3 phir 4 (squared 9, 16). (w2 ke 0.3, 0.4 bilkul same steps denge.)

  1. Step 1: v1 = 0.9(0) + 0.1(9.0000) = 0.9000; √0.9000 = 0.9487; step = 0.1(3)/0.9487 = 0.3162; w1 = 1.0000 − 0.3162 = 0.6838
  2. Dhyaan: α = 0.1 tha, step 0.3162 hua — 3.16 guna! (Wajah neeche.)
  3. Step 2: v2 = 0.9(0.9000) + 0.1(16.0000) = 0.8100 + 1.6000 = 2.4100; √2.4100 = 1.5524; step = 0.1(4)/1.5524 = 0.2577; w1 = 0.6838 − 0.2577 = 0.4261
v₁step 1v₂step 2w after
w₁0.90000.31622.41000.25770.4261
w₂0.00900.31620.02410.25770.4261

Sum ki jagah exponentially weighted average aaya, aur step decay hona band hua (AdaGrad: 0.1, 0.08; RMSProp: 0.3162, 0.2577).

RMSProp ki apni problem: pehla step bahut bada

v0 = 0, to v1 = (1 − β2)g12 — ye g12 ka estimate nahi, uska sirf das-va hissa hai (β₂ = 0.9 pe). Square root 3.16 guna chhota, to step 3.16 guna bada:

first step = αg1√(1 − β2)g12 = α√1 − β2
  • Gradient cancel ho gaya — error ka size sirf β2 pe depend karta hai. Ye wahi 16.4 wala start-up bias hai, ab denominator me.
β₂1 − β₂RMSProp ka pehla step (α ki units me)
0.50.5001.41 ×
0.90.1003.16 ×
0.99 (PyTorch default)0.01010.00 ×
0.9990.00131.62 ×

Mazedaar check: f(θ) = θ2, θ0 = 1, optim.RMSprop(lr=0.1) (alpha 0.99): pehla step = 10 × 0.1 = 1.0, to θ seedha 0 pe (asli PyTorch run: 5.96e-08, yaani float32 me lagbhag 0). PyTorch ka RMSprop bias correction nahi karta.

Chalo numbers se samjhein — Worksheet 06 (purana scale fade hota hai)

Sawaal: st−1 = (16, 1), gt = (4, 1), ρ = 0.9, ε ignore.

  1. Square: g2 = (16, 1)
  2. Purane scale ka 90%: ρst−1 = 0.9(16, 1) = (14.4, 0.9)
  3. Naye squared gradient ka 10%: (1 − ρ)g2 = 0.1(16, 1) = (1.6, 0.1) → st = (14.4 + 1.6, 0.9 + 0.1) = (16, 1)
  4. Normalise: g/√s = (4/√16, 1/√1) = (1, 1)
  5. Practice: st−1 = (4, 9), g = (2, 3), ρ = 0.5 → s = 0.5(4, 9) + 0.5(4, 9) = (4, 9), g/√s = (2/2, 3/3) = (1, 1)

Is step me s AdaGrad jaisa (16, 1) hi aaya, to kya dono same hain? Nahi! RMSProp me bahut purana squared gradient har step 0.9 se multiply ho ke ghatta jaata hai; AdaGrad me wo hamesha poora gina jaata hai.

Chalo numbers se samjhein — recovery: gradient achanak chhota ho jaaye

Pehle 20 steps gradient 10 (bade-bade), phir step 21 se hamesha 1. α = 0.1, RMSProp β = 0.9. Gradient 1 wale steps ka size dekho (Python se compute):

step tAdaGrad GAdaGrad stepRMSProp vRMSProp step
20 (g = 10)2000.00.022487.840.1067
21 (g = 1)2001.00.002279.160.0112
302010.00.002231.280.0179
402020.00.002211.560.0294
602040.00.00222.2840.0662
802060.00.00221.1560.0930

AdaGrad purane toofan (g = 10) ko kabhi nahi bhoolta: step hamesha ~0.0022 (α ka 2%). RMSProp ka v purane bade gradients ko bhool jaata hai aur step wapas ~α (0.1) ki taraf aata hai.

01α2α3α 11020304050 step t (gradient hamesha same) RMSProp (β = 0.9): pehla step 3.16α, phir α pe settle Adam: hamesha exactly α AdaGrad: α/√t → 0 (t = 50 pe 0.14α)
Ek hi gradient baar-baar aaye to har optimizer ka step (α ki units me). AdaGrad ka step 1/√t se marta jaata hai; RMSProp ka shuru me bias ki wajah se bahut bada (α/√(1 − 0.9t)), phir α; Adam (bias-corrected) shuru se hi α — yahi 17.4 ka topic hai.

PyTorch · optim.RMSprop — Worksheet 08 numbers

w = torch.tensor([1.0, 1.0], requires_grad=True)
optimizer = optim.RMSprop([w], lr=0.1, alpha=0.9)     # alpha = DECAY rate (beta2), learning rate nahi!

for g in [[3.0, 0.3], [4.0, 0.4]]:
    w.grad = torch.tensor(g)
    optimizer.step()
    print([round(v, 4) for v in w.tolist()])

Output (asli PyTorch run)

[0.6838, 0.6838]
[0.4261, 0.4261]

numpy · manual RMSProp + AdaGrad vs RMSProp accumulator (classroom notebook)

import numpy as np

w = np.array([1.0, 1.0])
v = np.zeros_like(w)
lr, beta, eps = 0.1, 0.9, 1e-8
for g in [np.array([3.0, 0.3]), np.array([4.0, 0.4])]:
    v = beta * v + (1 - beta) * g ** 2        # g^2 ka EWMA (bhoolne wala average)
    step = lr * g / (np.sqrt(v) + eps)
    w = w - step
    print("v =", v.round(4), " step =", step.round(4), " w =", w.round(4))

g = 2.0                                       # constant gradient: dono accumulators compare
s_ada, s_rms = 0.0, 0.0
print("step | Adagrad s | RMSProp s")
for t in range(1, 11):
    s_ada = s_ada + g**2                      # sirf badhta hai
    s_rms = beta*s_rms + (1-beta)*g**2        # g^2 = 4 ki taraf jaata hai, usse upar nahi
    print(f"{t:>4} | {s_ada:>9.3f} | {s_rms:>9.3f}")

Output

v = [0.9   0.009]  step = [0.3162 0.3162]  w = [0.6838 0.6838]
v = [2.41   0.0241]  step = [0.2577 0.2577]  w = [0.4261 0.4261]
step | Adagrad s | RMSProp s
   1 |     4.000 |     0.400
   2 |     8.000 |     0.760
   3 |    12.000 |     1.084
   4 |    16.000 |     1.376
   5 |    20.000 |     1.638
   6 |    24.000 |     1.874
   7 |    28.000 |     2.087
   8 |    32.000 |     2.278
   9 |    36.000 |     2.450
  10 |    40.000 |     2.605

AdaGrad ka s 4, 8, 12 ... seedha badhta gaya; RMSProp ka s 4 (= g2) ki taraf jaata hai aur wahin rukta hai — bounded. (Classroom notebook ka hi example: w0 = 5, α = 0.01, β = 0.9, g = 2, 2 → s1 = 0.4, w1 = 5 − 0.01 × 2/√0.4 ≈ 4.9684, s2 = 0.9(0.4) + 0.1(4) = 0.76, w2 ≈ 4.9684 − 0.0229 = 4.9455.)

Exam Radar

  • RMSProp = AdaGrad with EWMA of g² instead of sum → fixes AdaGrad's dying learning rate; old scale fades, step can recover.
  • Proposed by Geoffrey Hinton, Coursera lecture 6e (2012), unpublished. Typical decay 0.9; PyTorch alpha default 0.99, lr default 0.01.
  • No first-moment (direction) memory by itself; no bias correction → first step = α/√(1 − β) (too large).
  • Match: AdaGrad → "squared gradients accumulated forever"; RMSProp → "fading squared-gradient scale".

Galti mat karna

  • PyTorch RMSprop ka alpha learning rate nahi hai (learning rate lr hai). Lab 7 me {'lr': 0.15, 'alpha': 0.9}.
  • RMSProp me numerator abhi bhi raw gradient gt hai (koi momentum average nahi) — wo Adam me aayega.

17.4 Adam — Momentum + RMSProp + bias correction

Kahani

Yaad hai Pappu bhaiya coach (16.2)? Ab unhone ek assistant rakh liya. Coach do register rakhte hain: (1) form register — batsman haal me kis direction me ja raha hai (runs badh rahe ya ghat rahe) = momentum; (2) "kitna uthal-puthal" register — is batsman ke scores aam taur pe kitne bade-chhote hote hain = RMSProp. Aur naye season ki shuruaat me dono registers me imaginary zeros hote hain, to wo unhe bias correct karke padhte hain. Faisla: "is player ko kitna push karna hai" = direction ÷ usual size.

Adam = Adaptive Moment estimation — Diederik Kingma & Jimmy Ba, 2014 (arXiv; ICLR 2015 me publish). Classroom notebook ki line: "Adam ≈ Momentum in the numerator + RMSProp in the denominator", plus bias correction.

RMSProp me kya kami thi? (Worksheet 08 Part 5)

Dono halves humare paas pehle se hain: momentum ne gradient ka average liya (direction), RMSProp ne squared gradient ka (size). Ek square kyun karta hai, doosra kyun nahi? Gradients lo +3, −3, +3, −3: g ka total 0 ("koi bharosemand direction nahi" — sahi!), g2 ka total 36 ("gradients bade the" — ye bhi sahi!). m ko sign rakhna hai (palat-ti direction pe bharosa nahi karna), v ko sign phenkna hai (−10 bhi +10 jitna bada).

Teacher ka tareeka — RMSProp ki update line me ek-ek cheez badlo:

start:    wt = wt−1 − α gt / (√vt + ε)   (RMSProp)
change 1: wt = wt−1 − α mt / (√vt + ε)   (raw gradient ki jagah averaged direction)
change 2: wt = wt−1 − α m̂t / (√v̂t + ε)   (dono averages corrected) = Adam
1 direction: mt = β1·mt−1 + (1 − β1)·gt
2 size: vt = β2·vt−1 + (1 − β2)·gt2
3 correct: m̂t = mt1 − β1t    v̂t = vt1 − β2t
4 step: wt = wt−1 − α·m̂t√v̂t + ε
  • mt = first moment estimate (gradient ka mean, EWMA of g) — momentum wala, sign rakhta hai. PyTorch state me naam exp_avg.
  • vt = second (raw) moment estimate (EWMA of g2, "uncentered variance") — RMSProp wala, hamesha ≥ 0. PyTorch me exp_avg_sq. (Dhyaan: Adam ka v squared gradients hai, momentum wala velocity nahi!)
  • m̂, v̂ = bias-corrected versions (16.4 wala hi formula); t = step number (PyTorch state step).
  • β1 = 0.9 (direction ki memory ~10 steps), β2 = 0.999 (size ki memory ~1000 steps), α (lr) = 0.001, ε = 10−8 — paper aur PyTorch dono ke defaults.
  • m̂/(√v̂ + ε) = "direction ÷ usual size" — ek pure number, aksar ±1 ke aas-paas.
linekya haikahan mile the
1gradients ka averageMomentum (Chapter 16)
2squared gradients ka averageRMSProp (17.3)
3har average ko uske applied weight se dividebias correction (16.4)
4m̂ ki direction me, √v̂ se scaled stepAdaGrad ki update line (17.2)

Teacher: "There is no new idea on this page. Adam is the three previous parts, assembled." β₂ > β₁ kyun? Direction ko mod pe badal paana chahiye (chhoti memory, 0.9); size estimate denominator me baitha hai, use steady hona chahiye (lambi memory, 0.999) — jumpy denominator har step jumpy bana deta hai.

Chalo numbers se samjhein — Worksheet 06 ladder (defaults wale betas)

Sawaal: m0 = 0, v0 = 0, g1 = 4, β1 = 0.9, β2 = 0.999, α = 0.01. ε ignore.

  1. First moment (direction memory): m1 = 0.9(0) + 0.1(4) = 0.4
  2. Second moment (scale memory): v1 = 0.999(0) + 0.001(42) = 0.016
  3. Bias-correct m: m̂1 = 0.4/(1 − 0.9) = 0.4/0.1 = 4 (correction ne estimate bada kiya — wahi chahiye tha, kyunki 0.4 zero ki taraf biased tha)
  4. Bias-correct v: v̂1 = 0.016/(1 − 0.999) = 0.016/0.001 = 16
  5. Normalised direction: m̂1/√v̂1 = 4/√16 = 4/4 = 1
  6. Movement: Δθ1 = 0.01(1) = 0.01, θ1 = θ0 − 0.01
  7. Continuation, g2 = 2: m2 = 0.9(0.4) + 0.1(2) = 0.36 + 0.2 = 0.56, v2 = 0.999(0.016) + 0.001(22) = 0.015984 + 0.004 = 0.019984
  8. Poora step 2: 1 − 0.92 = 0.19 → m̂2 = 0.56/0.19 = 2.9474; 1 − 0.9992 = 0.001999 → v̂2 = 0.019984/0.001999 = 9.9970; √9.9970 = 3.1618; ratio = 2.9474/3.1618 = 0.9322; Δθ2 = 0.01 × 0.9322 = 0.0093

Adam ka m remembered direction deta hai, v adaptive scale. θ0 = 0 se: 0 → −0.0100 → −0.0193 (asli PyTorch run bhi yahi deta hai).

Chalo numbers se samjhein — Worksheet 08 "Adam by hand" (β₁ = β₂ = 0.9)

Sawaal: w1 = 1.0000, m0 = v0 = 0, α = 0.1, β1 = β2 = 0.9 (sheet ne 0.9 isliye liya taaki do steps me correction ka kaam dikhe), gradients 3 phir 4.

  1. Step 1, dono averages: m1 = 0.9(0) + 0.1(3) = 0.3000 (direction, bahut chhota), v1 = 0.9(0) + 0.1(9.0000) = 0.9000 (size, ye bhi chhota)
  2. Correct: 1 − 0.91 = 0.1000 → m̂1 = 0.3000/0.1000 = 3.0000 (= g1), v̂1 = 0.9000/0.1000 = 9.0000 (= g12)
  3. Step: √9.0000 = 3.0000, Δw = 0.1 × 3.0000/3.0000 = 0.1000, w1 = 0.9000
  4. Step 2: m2 = 0.9(0.3000) + 0.1(4) = 0.2700 + 0.4000 = 0.6700, v2 = 0.9(0.9000) + 0.1(16.0000) = 0.8100 + 1.6000 = 2.4100
  5. Correction factor ab: 1 − 0.92 = 0.1900 → m̂2 = 0.6700/0.1900 = 3.5263, v̂2 = 2.4100/0.1900 = 12.6842
  6. √12.6842 = 3.5615, Δw = 0.1(3.5263)/3.5615 = 0.0990, w2 = 0.9000 − 0.0990 = 0.8010
  7. Bina correction (line 3 hata do): step 1 = 0.1(0.3000)/√0.9000 = 0.03/0.9487 = 0.0316, step 2 = 0.1(0.6700)/√2.4100 = 0.067/1.5524 = 0.0432 → w = 0.9252.
step 1step 2w after
w₁ (Adam)0.10000.09900.8010
w₂ (Adam, gradients 0.3, 0.4)0.10000.09900.8010
w₁, correction off0.03160.04320.9252

Adam: 1.0000 → 0.9000 → 0.8010. Plain SGD gradient 4 pe 0.4000 chalta; Adam sirf 0.0990 — kyunki 4 "is parameter ke liye usual" hai, aur v ye jaanta hai. Correction off wala run 2 steps me sirf 0.0748 chala.

Sabse important insight: pehla step = α × sign(g)

t = 1:   m̂1 = (1 − β1)g11 − β1 = g1,   v̂1 = (1 − β2)g121 − β2 = g12  ⇒  m̂1√v̂1 = g1|g1| = ±1
  • Dono betas cancel, gradient ka size bhi cancel. Pehla step exactly α · sign(g1), gradient 0.001 ho ya 1000 (ε ignore karke).
  • Compare: RMSProp ka pehla step α/√(1 − β2) tha (β₂ pe depend). AdaGrad ka bhi pehla step α tha.
  • Aage bhi m̂/√v̂ aksar ±1 ke aas-paas → Adam ka har step lagbhag α size ka. Isi liye lr = 0.001 ka matlab hai "har weight ek step me ~0.001 hilega".

Teacher ka one-liner: "A step is not big because the gradient is big. It is big when the gradient is bigger than usual for this parameter." Parameter A ke gradients aam taur pe ~100, B ke ~0.01; dono apne usual size ka gradient report karein to dono ~α chalenge. Lab 7 saddle point pe Adam isi wajah se nikal bhaagta hai: w1 ka gradient sirf −0.01 tha, par Adam ne use normalize karke poora ~α (0.2) ka kadam diya, jabki SGD ne 0.1 × 0.01 = 0.001 ka.

Bias correction kyun zaroori hai — bina uske numbers

Uncorrected Adam ka step, steady gradient pe, sahi step ka (1 − β1t)/√(1 − β2t) guna hota hai (gradient cancel, sirf betas ki property). Default betas pe pehla step: 0.1/√0.001 = 0.1/0.0316 = 3.16 × α.

β₂ (β₁ = 0.9)uncorrected step 1sabse zyada galat5% ke andar kab se
0.90.32 ×0.32 × (step 1)step 23
0.991.00 ×2.13 × (step 13)step 237
0.999 (default)3.16 ×6.57 × (step 12)step 2375

Default β₂ = 0.999 pe uncorrected Adam 6.6 guna tak bade steps leta, aur step 2374 tak 5% se zyada galat rehta. Do divisions sab theek kar dete hain. Agar sirf m correct karo aur v nahi: t = 1 pe √v1 = √0.001·|g| = 0.0316|g| → step = α/0.0316 ≈ 31.6α — dhamaka! Dono correct karna zaroori.

Chalo numbers se samjhein — Adam on f(θ) = θ² (defaults, lr = 0.1)

θ0 = 1, gradient 2θ, β1 = 0.9, β2 = 0.999.

  1. t = 1: g = 2, m = 0.2, v = 0.004, m̂ = 0.2/0.1 = 2, v̂ = 0.004/0.001 = 4, step = 0.1 × 2/2 = 0.1 → θ = 0.9
  2. t = 2: g = 1.8, m = 0.18 + 0.18 = 0.36, v = 0.003996 + 0.00324 = 0.007236; m̂ = 0.36/0.19 = 1.8947, v̂ = 0.007236/0.001999 = 3.6198, √v̂ = 1.9026; step = 0.1 × 0.9959 = 0.0996 → θ = 0.8004
  3. t = 3: g = 1.6008, m = 0.4841, v = 0.009791; m̂ = 0.4841/0.271 = 1.7863, v̂ = 0.009791/0.002997 = 3.2671, √ = 1.8075; step = 0.1 × 0.9883 = 0.0988 → θ = 0.7016

θ: 1 → 0.9000 → 0.8004 → 0.7016 → 0.6039 (4th). Har step ~0.1 = lr, gradient chahe 2 se 1.4 tak gira.

PyTorch · optim.Adam — Worksheet 06 aur 08 dono

import torch
import torch.optim as optim

# Worksheet 08: betas = (0.9, 0.9), lr = 0.1
w = torch.tensor([1.0, 1.0], requires_grad=True)
optimizer = optim.Adam([w], lr=0.1, betas=(0.9, 0.9))
for g in [[3.0, 0.3], [4.0, 0.4]]:
    w.grad = torch.tensor(g)
    optimizer.step()
    print([round(v, 4) for v in w.tolist()])

# Worksheet 06: default betas (0.9, 0.999), lr = 0.01
theta = torch.tensor([0.0], requires_grad=True)
optimizer = optim.Adam([theta], lr=0.01)
for g in [4.0, 2.0]:
    theta.grad = torch.tensor([g])
    optimizer.step()
    print(round(theta.item(), 4))
print(optimizer.state[theta])        # andar kya store hai?

Output (asli PyTorch run)

[0.9, 0.9]
[0.801, 0.801]
-0.01
-0.0193
{'step': tensor(2.), 'exp_avg': tensor([0.5600]), 'exp_avg_sq': tensor([0.0200])}

State me exp_avg = m2 = 0.56 aur exp_avg_sq = v2 = 0.019984 (display me 0.0200) — bilkul worksheet ke numbers. Har parameter ke liye do extra numbers → Adam ko SGD se do extra copies of the parameters jitni memory chahiye (momentum ko ek).

Fayde: tez, robust, kam tuning (lr = 1e-3 aksar chal jaata hai), noisy/sparse gradients aur saddle points pe accha, har parameter ka apna scale. Isi liye "default first choice". Nuksaan: 2× extra memory; kabhi-kabhi well-tuned SGD+momentum se worse generalization (Lab 7 cheat-sheet); simple convex problems pe zaroori nahi ki jeete (17.8); L2 weight decay ke saath gadbad → AdamW.

Exam Radar

  • Adam = Adaptive Moment Estimation, Kingma & Ba 2014 (ICLR 2015) = Momentum (1st moment) + RMSProp (2nd moment) + bias correction.
  • Defaults: lr 0.001, β₁ 0.9, β₂ 0.999, ε 1e-8. β₂ > β₁.
  • Signed direction state = mt; scale state vt ≥ 0.
  • Bias correction: m̂ = m/(1 − β₁t), v̂ = v/(1 − β₂t); needed because m0 = v0 = 0; matters early, vanishes later.
  • First Adam step = α · sign(g) (magnitude exactly α). Memory: 2 states per parameter.

Galti mat karna

  • Adam ka vt = squared gradients ka average; Chapter 16 ka vt = velocity. Same letter, alag cheez!
  • Bias correction me exponent t = step count (1, 2, 3 ...), epoch nahi.
  • ε denominator me √v̂ ke baad judta hai (PyTorch: √v̂ + ε).
  • betas=0.9 galat; betas=(0.9, 0.999) sahi (tuple).

17.5 AdamW — weight decay ko alag rakho (decoupled)

Kahani

Cycle chalate waqt do alag controls hain: handle (manzil ki taraf steering) aur lane ke beech me rehna (drift na karna). Agar dono ek hi lever se ho, to tez mod pe lane-control bhi utna hi ghoom jaayega. AdamW dono ko alag rakhta hai — adaptive steering (Adam) alag, weights ko zero ki taraf kheenchna (decay) alag. (Worksheet 06 ki analogy.)

Weight decay / L2 regularization (Chapter 19) ka kaam: weights ko chhota rakhna (overfitting kam). Plain SGD me "loss me λw2/2 jodo" aur "har step weight ko (1 − αλ) se shrink karo" — ek hi cheez hain. Adam me nahi!

θt+1 = (1 − αλ)·θt − α·m̂t√v̂t + ε  =  θt − α( m̂t√v̂t + ε + λθt )
  • (1 − αλ)θt = decay term: parameter ko khud shrink karta hai, adaptive scale se bilkul independent.
  • αm̂/(√v̂ + ε) = adaptive term: normal Adam move; m̂, v̂ sirf loss ke gradient se bane (λθ unme nahi gaya).
  • λ = weight decay coefficient (PyTorch AdamW default 0.01).

Chalo numbers se samjhein — Worksheet 06 AdamW ladder

Sawaal: θt = 1.00, α = 0.01, λ = 0.1, normalised Adam direction m̂/√v̂ = 0.50. Decay pehle, adaptive move baad me.

  1. Decay-only: (1 − αλ)θt = (1 − 0.01 × 0.1)(1) = (1 − 0.001)(1) = 0.999
  2. Adaptive move: α(0.50) = 0.01 × 0.50 = 0.005
  3. Combine: θt+1 = 0.999 − 0.005 = 0.994
  4. Direction check: agar normalised direction negative hoti, to −α(negative) θ ko upar le jaata; phir bhi decay term θ ko zero ki taraf hi kheenchta.

θ: 1.00 → 0.994 (0.001 decay se, 0.005 Adam move se).

Chalo numbers se samjhein — Adam + L2 me decay "gayab" (asli PyTorch, ek step)

θ = 1.0, gradient 4, lr 0.01, λ = 0.1, pehla step.

  1. AdamW(lr=0.01, weight_decay=0.1): decay 1.0 × (1 − 0.001) = 0.999, phir Adam ka pehla step exactly lr: 0.999 − 0.01 = 0.989
  2. Adam(lr=0.01, weight_decay=0.1): gradient 4 + 0.1 × 1.0 = 4.1 → pehla Adam step = lr × sign = 0.01 → 1.0 − 0.01 = 0.990. Decay ka koi asar hi nahi dikha — normalization ne use nigal liya!
  3. Lambi training me (rough estimate): Adam+L2 me decay hissa ≈ αλθ/√v̂. √v̂ = 10 wale weight ka decay ≈ 0.01 × 0.1/10 = 0.0001; √v̂ = 0.1 wale ka ≈ 0.01 — 100 guna farak. AdamW me dono ka exactly αλθ = 0.001.

AdamW: 0.989, Adam+L2: 0.990 (asli PyTorch outputs). AdamW ka regularization har weight pe barabar aur predictable.

PyTorch · L2 vs true weight decay (Regularisation worksheet ki do lines)

# L2 penalty gradient ke andar (Adam ise bhi rescale kar deta hai)
optimizer = optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)

# true (decoupled) weight decay
optimizer = optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2)
# AdamW signature: optim.AdamW(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0.01)
# Naye PyTorch me optim.Adam(..., weight_decay=λ, decoupled_weight_decay=True) bhi AdamW jaisa hi hai

AdamW better kyun? (1) Regularization strength sab weights pe barabar, gradient history pe depend nahi; (2) λ aur lr ka tuning zyada independent ho jaata hai; (3) practice me better generalization — isi liye BERT/GPT jaise transformers ka standard optimizer AdamW hai.

Exam Radar

  • AdamW decouples weight decay from the adaptive (gradient-based) update. Loshchilov & Hutter (2017/2019).
  • Adam's weight_decay = L2 inside gradient (rescaled by √v̂); AdamW's = true weight decay.
  • Defaults: Adam weight_decay = 0; AdamW weight_decay = 0.01.
  • Rule: θ ← (1 − αλ)θ − αm̂/(√v̂ + ε).

17.6 Poori evolution ek nazar me + badi comparison table

SGDRobbins–Monro 1951 MomentumPolyak 1964 NAGNesterov 1983 AdaGradDuchi et al. 2011 RMSPropHinton 2012 AdamKingma & Ba 2014 AdamW2017 zig-zag overshoot ek α sabke liye → per-parameter α LR → 0 m (direction) v (scale) decay alag + bias correction upar ki line: direction sudhaaro neeche ki line: step size sudhaaro
Do dhaarayein: upar wali (Momentum → NAG) direction theek karti hai, neeche wali (AdaGrad → RMSProp) har parameter ka step size. Adam dono ko jodta hai (m + v + bias correction), AdamW usme weight decay ko alag karta hai. Har arrow pe likha hai ki pichhle method ki kaunsi problem ne agla method banwaya.
kadamproblem jo fix huikaisenayi problem jo bachi
SGD → Momentumravine me zig-zag, noisy gradients, flat direction me slowgradients ka EWMA (velocity)overshoot; ek α sabke liye
Momentum → NAGovershootgradient look-ahead point pe naapoek α sabke liye
SGD → AdaGradek hi α steep aur flat / rare aur frequent parameters ke liyeΣg² se divide: per-parameter LRG hamesha badhta → LR → 0
AdaGrad → RMSProplearning rate ka mar jaanaΣ ki jagah EWMA of g² (bhoolo)zero-start bias (bada first step), raw-gradient direction
Momentum + RMSProp → Adamdirection aur scale dono ek saath; start-up biasm̂/√v̂ + bias correctionL2 penalty bhi rescale; 2× memory
Adam → AdamWweight decay ka adaptive scaling me ulajhnadecay ko update se decouple— (aaj ka standard)
OptimizerUpdate rule (short)extra memory / parameterkey hyperparameters (PyTorch defaults)ProsConskab use karein
SGDw ← w − α g0lr (naye PyTorch me default 1e-3; hamesha khud do)simple, sasta; tuned ho to acchi generalizationnoisy, zig-zag, plateau/saddle pe slow, ek αbaseline; schedule ke saath
SGD + Momentumv ← βv + g; w ← w − η v1 (velocity)momentum=0.9 (default 0), dampening=0ravine me tez, oscillation cancel, bumps paarovershoot, extra βCNNs (e.g. ResNet: SGD 0.9 + LR schedule)
NAGgradient at look-ahead w − βv (classic form a; course form me w − αβv)1nesterov=True (momentum > 0, dampening = 0)kam overshoot, jaldi settleek α sabke liyejab bhi momentum use karo
AdaGradG ← G + g²; w ← w − α g/(√G + ε)1 (G)lr=0.01, eps=1e-10, initial_accumulator_value=0per-param LR, sparse features, kam tuningLR → 0, learning ruk jaatisparse data (NLP, recsys), convex problems
RMSPropv ← βv + (1−β)g²; w ← w − α g/(√v + ε)1 (+1 agar momentum)lr=0.01, alpha=0.99, eps=1e-8, momentum=0, centered=FalseAdaGrad ka decay fix; non-stationary problemsno bias correction, no direction memoryRNNs, RL; Adam se pehle ka favourite
Adamm, v EWMAs; w ← w − α m̂/(√v̂ + ε)2 (m, v)lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0tez, robust, kam tuning, noisy/sparse/saddle pe accha2× memory; kabhi worse generalization; L2 couplingdefault first choice (most DL)
AdamWAdam + (1 − αλ)θ decay2lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0.01sahi weight decay, better generalizationek aur hyperparameter λtransformers/LLMs; Adam + regularization

Classroom notebook ka formula pattern yaad rakhna: Momentum = EWMA(g); AdaGrad = g/√(Σg²); RMSProp = g/√(EWMA(g²)); Adam = EWMA(g)/√(EWMA(g²)) + bias correction. Aur 30-second trick: Momentum → direction yaad; AdaGrad → saare squared gradients yaad; RMSProp → recent squared gradients yaad; Adam → recent gradients + recent squared gradients.

17.7 Teeno ko haath se likho (numpy-style) aur PyTorch se match karo

Coding exam me "implement AdaGrad/RMSProp/Adam manually" aa sakta hai. Neeche teeno ek simple function f(θ) = θ2 pe (gradient 2θ, θ0 = 1, lr = 0.1). Structure har baar same: gradient → state update → (bias correction) → parameter update.

numpy · manual AdaGrad, RMSProp, Adam

import numpy as np

def grad_f(theta):                 # f(theta) = theta^2  ->  f'(theta) = 2*theta
    return 2 * theta

def adagrad(theta, lr=0.1, steps=4, eps=1e-10):
    G = 0.0
    out = []
    for t in range(1, steps + 1):
        g = grad_f(theta)
        G = G + g ** 2                                  # sum of g^2
        theta = theta - lr * g / (np.sqrt(G) + eps)
        out.append(round(float(theta), 4))
    return out

def rmsprop(theta, lr=0.1, beta=0.9, steps=4, eps=1e-8):
    v = 0.0
    out = []
    for t in range(1, steps + 1):
        g = grad_f(theta)
        v = beta * v + (1 - beta) * g ** 2              # EWMA of g^2
        theta = theta - lr * g / (np.sqrt(v) + eps)
        out.append(round(float(theta), 4))
    return out

def adam(theta, lr=0.1, beta1=0.9, beta2=0.999, steps=4, eps=1e-8):
    m, v = 0.0, 0.0
    out = []
    for t in range(1, steps + 1):                       # t = step number (bias correction ke liye)
        g = grad_f(theta)
        m = beta1 * m + (1 - beta1) * g                 # 1st moment: direction
        v = beta2 * v + (1 - beta2) * g ** 2            # 2nd moment: scale
        m_hat = m / (1 - beta1 ** t)                    # bias correction
        v_hat = v / (1 - beta2 ** t)
        theta = theta - lr * m_hat / (np.sqrt(v_hat) + eps)
        out.append(round(float(theta), 4))
    return out

print("AdaGrad:", adagrad(1.0))
print("RMSProp:", rmsprop(1.0))
print("Adam   :", adam(1.0))

Output

AdaGrad: [0.9, 0.8331, 0.7805, 0.7362]
RMSProp: [0.6838, 0.4989, 0.3692, 0.2728]
Adam   : [0.9, 0.8004, 0.7016, 0.6039]

Ab wahi function PyTorch ke asli optimizers se, ek loop me (Lab 7 / Optimizer Zoo style: same problem, same start, sirf optimizer badlo):

PyTorch · optimizers ka loop (f(θ) = θ²)

import torch
import torch.optim as optim

configs = {
    "SGD":      lambda p: optim.SGD(p, lr=0.1),
    "Momentum": lambda p: optim.SGD(p, lr=0.1, momentum=0.9),
    "NAG":      lambda p: optim.SGD(p, lr=0.1, momentum=0.9, nesterov=True),
    "Adagrad":  lambda p: optim.Adagrad(p, lr=0.1),
    "RMSprop":  lambda p: optim.RMSprop(p, lr=0.1, alpha=0.9),
    "Adam":     lambda p: optim.Adam(p, lr=0.1),
}
for name, make in configs.items():
    theta = torch.tensor([1.0], requires_grad=True)      # har optimizer same start
    optimizer = make([theta])
    values = []
    for step in range(4):
        optimizer.zero_grad()
        loss = (theta ** 2).sum()                        # f(theta) = theta^2
        loss.backward()
        optimizer.step()
        values.append(round(theta.item(), 4))
    print(f"{name:9s}", values)

Output (asli PyTorch run)

SGD       [0.8, 0.64, 0.512, 0.4096]
Momentum  [0.8, 0.46, 0.062, -0.3086]
NAG       [0.62, 0.2224, -0.1084, -0.3248]
Adagrad   [0.9, 0.8331, 0.7805, 0.7362]
RMSprop   [0.6838, 0.4989, 0.3692, 0.2728]
Adam      [0.9, 0.8004, 0.7016, 0.6039]

Manual aur PyTorch ke Adagrad/RMSprop/Adam numbers bilkul same. Notice: AdaGrad aur Adam ka pehla step exactly lr (1 → 0.9); RMSProp ka pehla step 3.16 × lr (1 → 0.6838, bias wali baat); momentum tez par 4th step pe −0.3086 tak overshoot; NAG column PyTorch ke look-ahead bookkeeping wala (16.10).

17.8 Lab 7 — The Optimizer Zoo

Lab 7 ka setup: ill-conditioned ravine f(w0, w1) = 0.5(0.1w02 + 2.0w12), start [−4.5, 4.0], 60 steps, PyTorch ke native optimizers. Lab ka evolutionary path: Plain SGD (oscillates) → Momentum (builds speed) → NAG (brakes) → AdaGrad (stalls out) → RMSProp (adapts strides) → Adam ("the king").

PyTorch · Lab 7 style Optimizer Zoo (ravine)

import torch
import torch.optim as optim

def ravine_loss(w):
    return 0.5 * (0.1 * w[0]**2 + 2.0 * w[1]**2)     # w0 flat, w1 steep (20x)

def track_optimizer(optimizer_cls, opt_kwargs, start_pos=[-4.5, 4.0], steps=60):
    w = torch.tensor(start_pos, dtype=torch.float32, requires_grad=True)
    optimizer = optimizer_cls([w], **opt_kwargs)     # class + kwargs dict se optimizer banao
    loss_history = []
    for _ in range(steps):
        optimizer.zero_grad()
        loss = ravine_loss(w)
        loss.backward()
        optimizer.step()
        loss_history.append(loss.item())             # step se PEHLE ka loss record
    return w.detach(), loss_history

optimizers_config = {
    "Plain SGD":      (optim.SGD,     {'lr': 0.18}),
    "SGD + Momentum": (optim.SGD,     {'lr': 0.08, 'momentum': 0.9}),
    "NAG":            (optim.SGD,     {'lr': 0.08, 'momentum': 0.9, 'nesterov': True}),
    "AdaGrad":        (optim.Adagrad, {'lr': 1.5}),
    "RMSProp":        (optim.RMSprop, {'lr': 0.15, 'alpha': 0.9}),
    "Adam":           (optim.Adam,    {'lr': 0.35, 'betas': (0.9, 0.999)}),
}

for name, (opt_cls, kwargs) in optimizers_config.items():
    w_end, losses = track_optimizer(opt_cls, kwargs)
    print(f"{name:15s} loss@1={losses[0]:.4f}  loss@10={losses[9]:.2e}  final={losses[-1]:.2e}")

Output (asli PyTorch run)

Plain SGD       loss@1=17.0125  loss@10=7.35e-01  final=1.19e-01
SGD + Momentum  loss@1=17.0125  loss@10=4.63e+00  final=1.55e-02
NAG             loss@1=17.0125  loss@10=1.15e+00  final=1.22e-03
AdaGrad         loss@1=17.0125  loss@10=2.99e-02  final=8.31e-15
RMSProp         loss@1=17.0125  loss@10=3.84e+00  final=3.20e-11
Adam            loss@1=17.0125  loss@10=1.10e+00  final=1.26e-02

Adam yahan kyun nahi jeeta? (Lab 7 ka apna note.) Ranking: AdaGrad < RMSProp < NAG < Adam < Momentum < SGD (final loss). Ye ravine ek noise-free, convex quadratic bowl hai — sabse dostana landscape. Yahan AdaGrad ki "weakness" (girta hua step) annealing ban jaati hai aur wo bina overshoot ke minimum pe utar jaata hai; NAG ka look-ahead smooth quadratic pe almost ideal hai. Adam ko noisy, non-stationary, non-convex asli deep-learning ke liye banaya gaya tha; uske moving averages yahan bas thoda "lag" add karte hain. "Adam the king" ka matlab: messy real training me consistent aur robust — har problem pe winner nahi. Saath hi: har optimizer ka lr alag chuna gaya hai, to ye fair benchmark nahi, intuition demo hai.

Mini-challenge (saddle): f = 0.2w02 − 0.5w12, start [−2.0, 0.01], 40 steps. Asli PyTorch run: SGD (lr 0.1) 10 steps baad w1 = 0.0259, 40 baad 0.4526 (flat zone me ghisat raha); Adam (lr 0.2) 10 steps baad w1 = 1.8645, 40 baad 9.1195 — saddle se tezi se bahar. Wajah: Adam chhote gradient ko bhi normalize karke ~lr ka kadam deta hai (17.4).

Asli neural network pe zoo (same model + same data + same init)

Classroom notebook ka fair-comparison rule: same dataset, same architecture, same initial weights, same epochs aur batch size — sirf optimizer badlo. PyTorch me same init ke liye ek baar state_dict() copy karke har model me load_state_dict karo.

PyTorch · Optimizer Zoo on a small MLP

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader

torch.manual_seed(0)
X = torch.randn(400, 2)                                  # (400, 2) do features
y = ((X[:, 0] * X[:, 1]) > 0).float().unsqueeze(1)       # (400, 1) XOR jaisa pattern

def make_model():
    return nn.Sequential(
        nn.Linear(2, 16), nn.ReLU(),
        nn.Linear(16, 16), nn.ReLU(),
        nn.Linear(16, 1)                                 # raw logit
    )

torch.manual_seed(42)
base = make_model()
init_state = {k: v.clone() for k, v in base.state_dict().items()}   # sabke liye SAME shuruaati weights

optimizer_factories = {
    "SGD":      lambda p: optim.SGD(p, lr=0.05),
    "Momentum": lambda p: optim.SGD(p, lr=0.05, momentum=0.9),
    "NAG":      lambda p: optim.SGD(p, lr=0.05, momentum=0.9, nesterov=True),
    "Adagrad":  lambda p: optim.Adagrad(p, lr=0.05),
    "RMSprop":  lambda p: optim.RMSprop(p, lr=0.01),
    "Adam":     lambda p: optim.Adam(p, lr=0.01),
    "AdamW":    lambda p: optim.AdamW(p, lr=0.01, weight_decay=0.01),
}

loss_fn = nn.BCEWithLogitsLoss()
results = {}
for name, make_opt in optimizer_factories.items():
    model = make_model()
    model.load_state_dict(init_state)                    # same start
    optimizer = make_opt(model.parameters())
    g = torch.Generator().manual_seed(0)                 # same batch order
    loader = DataLoader(TensorDataset(X, y), batch_size=32, shuffle=True, generator=g)
    history = []
    for epoch in range(30):
        model.train()
        total = 0.0
        for xb, yb in loader:
            optimizer.zero_grad()
            loss = loss_fn(model(xb), yb)
            loss.backward()
            optimizer.step()
            total += loss.item() * len(xb)
        history.append(total / len(X))                   # epoch ka average loss
    model.eval()
    with torch.no_grad():
        acc = ((model(X) > 0).float() == y).float().mean().item()
    results[name] = (history, acc)
    print(f"{name:9s} epoch1={history[0]:.4f}  epoch10={history[9]:.4f}  epoch30={history[-1]:.4f}  acc={acc:.3f}")

Output (asli PyTorch run, CPU)

SGD       epoch1=0.7044  epoch10=0.6378  epoch30=0.3061  acc=0.980
Momentum  epoch1=0.6971  epoch10=0.0930  epoch30=0.1142  acc=0.967
NAG       epoch1=0.6957  epoch10=0.0873  epoch30=0.0400  acc=0.990
Adagrad   epoch1=0.5525  epoch10=0.0928  epoch30=0.0479  acc=0.993
RMSprop   epoch1=0.4334  epoch10=0.0667  epoch30=0.0257  acc=0.993
Adam      epoch1=0.6664  epoch10=0.0681  epoch30=0.0298  acc=0.988
AdamW     epoch1=0.6665  epoch10=0.0693  epoch30=0.0318  acc=0.985

Padhna kaise hai: plain SGD (lr 0.05) 10 epochs baad bhi 0.64 pe atka (abhi plateau se nikal raha), momentum/NAG ne 10 epochs me 0.09 tak gira diya; adaptive wale (RMSprop, Adam) pehle epoch se hi tez. Momentum ka epoch 30 loss (0.1142) epoch 10 (0.0930) se zyada hai — overshoot/oscillation ka chhota sa sabooth; NAG zyada smooth (0.0400). Classroom notebook ki warning yaad rakho: "optimizer learning rates are hyperparameters... do not interpret this toy run as a universal ranking." Look at how quickly loss girta hai, sirf final number nahi. (Tumhare machine pe PyTorch version ke hisaab se aakhri decimals thode alag ho sakte hain.)

Galti mat karna

  • Har optimizer ke liye naya model (same init ke saath) banao. Ek hi model pe ek ke baad ek optimizer chalaoge to doosra optimizer pehle se trained model se shuru karega — comparison bekaar.
  • Optimizer ko model.parameters() model banne ke baad do; naya model banaya to naya optimizer bhi.
  • nn.BCEWithLogitsLoss raw logits leta hai (sigmoid andar hi hai) — model ke end me sigmoid mat lagao.

17.9 Kaunsa optimizer kab? + final Exam Radar

Worksheet 06 ka decision map: pehle poocho "is training me mushkil kya hai?"

diagnosisstarting choicekyun
noisy direction (gradients same direction me par noisy)Momentumdirection ko smooth karta hai
overshoot riskNAGgradient prediction point pe naapta hai
uneven coordinates (alag-alag scales)AdaGrad ya RMSPropper-coordinate scale
adaptive baseline chahiyeAdamdono memories (m + v)
adaptive + explicit weight decayAdamWdecay alag, adaptive update alag

Scenario (worksheet): noisy mini-batch gradients + layers with very different scales + explicit weight decay chahiye → AdamW. Justification: mt noise ko average karta hai, vt har layer/parameter ke scale ko normalize karta hai, aur (1 − αλ)θ wala decay adaptive scaling se independent rehta hai. Teacher ki chetavani: "These are starting hypotheses, not universal rules. An optimizer is a memory design."

Practical default: pehle optim.Adam(model.parameters(), lr=1e-3) (ya AdamW). CNN ko last-mile accuracy chahiye to optim.SGD(..., lr=0.1, momentum=0.9) + LR schedule (Chapter 15). Optimizer khud bhi ek hyperparameter hai — Optuna labs me trial.suggest_categorical("optimizer", ["Adam", "SGD", "RMSprop"]) isi liye hota hai (Chapter 22).

Exam Radar

  • Match (Worksheet 06): AdaGrad → squared gradients accumulated forever; RMSProp → fading squared-gradient scale; Momentum → smoothed direction carried forward; NAG → gradient at predicted point; AdamW → separate weight decay.
  • AdaGrad's denominator grows forever because it uses cumulative g²; RMSProp differs by using a fading (EWMA) g² scale; Adam's signed direction state is mt; AdamW decouples decay and adaptive update.
  • Memory per parameter (beyond weights): SGD 0, Momentum/NAG 1, AdaGrad 1, RMSProp 1, Adam/AdamW 2.
  • PyTorch defaults: SGD lr 1e-3 (naye versions), momentum 0 · Adagrad lr 0.01, eps 1e-10 · RMSprop lr 0.01, alpha 0.99, eps 1e-8 · Adam lr 1e-3, betas (0.9, 0.999), eps 1e-8, wd 0 · AdamW same but wd 0.01.
  • First step: AdaGrad = α, Adam = α (sign g), RMSProp (no correction) = α/√(1 − β).
  • Authors/years: AdaGrad Duchi–Hazan–Singer 2011; RMSProp Hinton 2012 (Coursera, unpublished); Adam Kingma–Ba 2014 (ICLR 2015); AdamW Loshchilov–Hutter 2017 (ICLR 2019).
  • Adam is robust on noisy non-convex problems, but on a clean convex quadratic (Lab 7) AdaGrad/RMSProp/NAG can beat it; Adam can sometimes generalize worse than tuned SGD+momentum.

Galti mat karna

  • "Adaptive" ka matlab lr khatam nahi hota — Adam me bhi lr (α) hai aur wo hi step ka size tay karta hai (~α per step).
  • AdaGrad/RMSProp/Adam me state har parameter ki alag hoti hai (element-wise), poore network ka ek number nahi.
  • RMSProp ka denominator √(EWMA of g²) hai, √(g²) ka EWMA nahi; aur square root ke baad ε judta hai.
  • optim.Adam(weight_decay=...) ≠ AdamW. True decoupled decay ke liye optim.AdamW.

Dimaag me aise baitha lo

Adam = α × (smoothed direction ÷ usual size). AdaGrad sab yaad rakhta hai (isliye thak jaata hai), RMSProp bhoolna jaanta hai, Adam dono register rakhta hai aur imaginary zeros hata deta hai, AdamW lane-control alag rakhta hai.

Chapter 18

Bias–Variance: Underfitting, Overfitting, Tradeoff, Double Descent

Generalization · Train / Validation / Test · Underfitting (high bias) · Overfitting (high variance) · Bias² + Variance + σ² · U-curve · Model capacity · Learning curves · Polynomial degree · Diagnosis · Double descent

18.1 Generalization — asli imtihaan naya data hai

Kahani

Board exam se pehle do dost hain, Golu aur Chintu. Dono ke paas pichhle 5 saal ke question papers hain. Chintu ne kya kiya? Har past paper ke answers ratt liye, bilkul word-to-word. Golu ne kya kiya? Har sawaal ke peechhe ka method samjha.

Mock test me (jo past papers se hi bana tha) dono ke 100/100 aaye. Lekin board exam ke din paper naya tha. Chintu ka ratta kisi kaam ka nahi tha, wo 40 pe atak gaya. Golu ne method lagaya aur 92 le aaya.

Machine learning me bhi bilkul yahi hota hai. Past papers = training data. Board exam = naya, unseen data. Aur naye paper pe achha karna = generalization.

Model ko training data pe train karte hain, lekin usko kaam naye data pe karna hai — kal aane wali photo, agle mahine ka customer, naya patient. Training data pe score achha aana koi badi baat nahi; ratta maar ke bhi aa sakta hai. Asli sawaal hai: naye data pe kaisa karega?

Definition

Generalization = model ki wo quality jisse wo unseen (naye) data pe bhi achha perform kare. Generalization error (ya test error) = naye data pe model ki expected galti.

Generalization gap = (validation/test error) − (training error). Gap bada = model ne training data "ratta" maara hai.

Data ke teen hisse — kaun kya karta hai?

Course ke Model Capacity Lab me ye bilkul saaf likha hai: training data weights update karta hai, validation data model chunta hai, aur test set sirf ek baar, aakhir me khulta hai.

SetKaamAnalogyKab use?
Training setOptimizer isi pe gradient nikal ke weights (θ) update karta haiRoz ki padhai, practice questionsHar epoch, baar-baar
Validation setKabhi train nahi hota; model / hyperparameter choose karne ke liye (4 vs 16 vs 64 neurons? λ kitna? kab rukna hai?)Mock testDevelopment ke dauraan, compare karne ke liye
Test setSaare faisle ho jaane ke baad ek baar final, unbiased reportBoard examSirf ek baar, end me

Lab ka sawaal tha: "4, 16 aur 64 hidden neurons wale networks me se choose karna hai — kaunsa split decide karega?" Jawab: validation set. Training set se choose karoge to hamesha sabse bada (sabse zyada ratta maarne wala) model jeetega. Test set se choose karoge to test set "dekha hua" ho jaayega aur final number jhootha (optimistic) hoga.

Signal + Noise

Real data hamesha pattern (signal) + noise hota hai. Do ghar same size ke hain, phir bhi alag daam pe bikte hain — kyunki kuch cheezein humne measure hi nahi ki (padosi, kismat, dealer ka mood). Ye unexplained hissa noise hai. Model ko nahi pata ki kis point ka kaunsa hissa pattern hai aur kaunsa noise — use sirf points dikhte hain. Jo model har point ko exactly fit kar de, usne noise bhi yaad kar liya, aur noise naye data pe repeat nahi hota.

Chalo numbers se samjhein (course worksheet 09 ka example)

Training set ke actual values: 5, 7, 6, 8. Test set ke actual values: 6, 9. Do models hain:

Training pointsTest points
Actual576869
Model A (smooth)667778
Model B (wiggly)576896

MSE = squared errors ka average, yaani (actual − predicted)² ka mean.

  1. Model A training MSE: errors 1, 1, 1, 1 → squares 1, 1, 1, 1 → (1+1+1+1)/4 = 1
  2. Model B training MSE: sab exact match → (0+0+0+0)/4 = 0
  3. Model A test MSE: (6−7)² = 1, (9−8)² = 1 → (1+1)/2 = 1
  4. Model B test MSE: (6−9)² = 9, (9−6)² = 9 → (9+9)/2 = 9

Jawab: Model B training pe "perfect" (MSE 0) dikha, lekin naye data pe Model A se 9 guna kharab (9 vs 1). Deploy Model A karo.

Exam Radar

  • Goal = low error on new/unseen data, not low training error.
  • Model/hyperparameter choose karne ke liye → validation set. Test set → sirf final report, ek baar.
  • Generalization gap = validation (ya test) error − training error.
  • Data = signal + noise; perfect training fit = noise bhi fit ho gaya.

Galti mat karna

  • "Training loss gir raha hai, matlab model sudhar raha hai" — galat. Validation curve dekho.
  • Test set pe baar-baar check karke hyperparameter choose karna = test set ka "leak". Uske baad test score bharose layak nahi.

Dimaag me aise baitha lo

Past paper pe 100 aana kamaal nahi; naye paper pe achha aana kamaal hai. Wahi generalization hai.

18.2 Underfitting — jab model bahut seedha-saadha hai (High Bias)

Kahani

Pappu ne exam ki taiyaari me sirf chapter ke titles padhe. "Trigonometry — haan triangles ke baare me hai." Bas. Ab exam me koi bhi sawaal aaye, Pappu ka ek hi jawab: "triangle ke angles ka sum 180 hota hai."

Practice questions me bhi fail, board exam me bhi fail. Problem ye nahi ki usne ratta maara — problem ye hai ki usne kuch seekha hi nahi. Uska dimaag (model) itna simple hai ki pattern pakad hi nahi sakta.

Underfitting tab hota hai jab model asli pattern capture hi nahi kar pata. Jaise ek seedhi line se sine wave fit karne ki koshish. Line kitni bhi ghumao, wave nahi banegi. Model ki capacity (kitne tarah ke functions bana sakta hai) zaroorat se kam hai.

Definition

Underfitting: model training data ko bhi achhe se fit nahi kar pata. Training error HIGH aur validation error bhi HIGH (aur dono paas-paas). Isse usually high bias, low variance kehte hain.

Course slide: "Model capacity is too small to fit the training dataset as well as generalize to new dataset. High bias, low variance."

Course ke Model Capacity Lab ke numbers

Ek hi dataset, ek shallow ReLU network, sirf hidden neurons badle:

Hidden neuronsTrain MSEValidation MSEDiagnosis
1 (tiny)0.1840.201Dono high → Underfitting
4 (balanced)0.0360.044Dono low, paas-paas → Good fit
32 (large)0.0040.138Train bahut low, val high → Overfitting

1 neuron wale model ke liye lab ne poocha: "500 epochs ke baad training error high hai aur gir bhi nahi raha. Sabse direct agla experiment?" Jawab: capacity badhao. Zyada epochs se kuch nahi hoga, kyunki training error already ruk chuka hai — model ki aukaat hi itni hai.

Underfitting ke ilaaj

Slide ne mazaak me likha: "It's so simple, just turn it into an overfit model!" Matlab capacity itni badhao ki pehle training data fit ho jaaye; phir overfitting ko regularization se sambhalo. Worksheet ka recipe bhi yahi kehta hai: pehle aisa model banao jo overfit kar sake; agar wo training data hi fit nahi kar paata, to regularization se koi fayda nahi.

Exam Radar

  • Underfitting = high train error + high val error = high bias, low variance.
  • Cause: capacity too small (ya bahut zyada regularization, ya bahut kam training).
  • Fix: capacity ↑ (layers/neurons), features ↑, train longer, regularization ↓.
  • Regularization add karna underfitting ko aur bura karta hai.

Galti mat karna

  • Underfit model me "more data" daalna zyada madad nahi karta — line ko lakh points do, wo phir bhi wave nahi banegi. More data variance ka ilaaj hai, bias ka nahi.

Dimaag me aise baitha lo

Underfit = Pappu ne sirf titles padhe: practice me bhi fail, exam me bhi fail.

18.3 Overfitting — jab model ratta maar leta hai (High Variance)

Kahani

Chintu ko pichhle saal ki answer key mil gayi. Usne poori ratt li — including teacher ki typing mistakes! Question 7 ke answer me galti se "45" likha tha (sahi 54 tha); Chintu ne wo bhi yaad kar liya.

Same paper repeat hota to 100/100. Lekin naye paper me sawaal thoda badla, aur Chintu ka ratta toot gaya. Usne pattern ke saath noise (typing mistakes) bhi yaad kiya tha, aur samjha kuch nahi.

Overfitting me model training data ki har chhoti detail — including noise aur galat labels — yaad kar leta hai. Training error lagbhag zero, lekin naye data pe error bahut zyada. Model ki capacity zaroorat se bahut zyada hai, to wo pattern ke bajaye points ko "memorize" karta hai.

Definition

Overfitting: training error bahut LOW, validation error usse kaafi HIGH (bada generalization gap). Isse usually low bias, high variance kehte hain.

Course slide: "Model capacity is so big that it adapts too well to training samples, unable to generalize well to new, unseen samples. Low bias, high variance."

Overfitting ke teen ingredients (worksheet 09)

Chalo numbers se samjhein: kitni freedom hai?

Worksheet lab network: 2 inputs → 50 neurons → 50 neurons → 1 output. Har layer me weights = inputs × outputs, plus har output ka ek bias.

  1. Layer 1: 2 × 50 + 50 = 150
  2. Layer 2: 50 × 50 + 50 = 2550
  3. Layer 3: 50 × 1 + 1 = 51
  4. Total: 150 + 2550 + 51 = 2751 parameters
  5. Training points sirf 40 → 2751 / 40 ≈ 68.8 ≈ 69 parameters per point

Jawab: har training point ke liye ~69 "knobs" — ratta maarne ke liye bharpoor freedom.

Overfitting kahan se aata hai? Bade weights se

Ek neuron z = w·x compute karke sigmoid me daalta hai. w ka size decide karta hai ki output kitni tezi se badlega. Worksheet ke sigmoid values: σ(−5) = 0.007, σ(−0.1) = 0.475, σ(0) = 0.5, σ(0.1) = 0.525, σ(5) = 0.993.

Chalo numbers se samjhein

Teen paas-paas ke inputs x = −0.1, 0, 0.1 pe σ(w·x):

  1. w = 1: σ(−0.1) = 0.475, σ(0) = 0.5, σ(0.1) = 0.525 → halka sa badlaav (smooth slope)
  2. w = 50: z = 50 × (−0.1) = −5 → σ(−5) = 0.007, σ(0) = 0.5, σ(5) = 0.993 → 0.1 ka chhota sa kadam aur prediction poora ulat gaya!

Bada weight smooth slope ko cliff (khai) bana deta hai. Sharp bends aur "islands" banane ke liye bade weights chahiye. Chhote weights sirf gentle, smooth boundaries bana sakte hain.

Course ki interactive worksheet me ek noisy point (P: x₁ = −1.8, galat label class 1, class-0 group ke beech) ke liye overfit model ne uske chaaron taraf ek chhota "pocket/island" bana diya — training mistakes 0. Ek neuron ka illustration: overfit neuron z = 12x₁ + 9x₂ − 0.2 (bade weights), generalized neuron z = 1.2x₁ + 0.8x₂ − 0.2 (moderate weights, ek training mistake accept ki, boundary simple rakhi). Classification me overfitting = islands aur sharp bends individual points ke around.

Overfitting ke ilaaj

Exam Radar

  • Overfitting = train error very low + val error much higher = low bias, high variance.
  • Clearest warning sign (lab ka sawaal): training error low · validation error high.
  • Ingredients: capacity zyada, data kam, training bahut lambi.
  • Big weights → sharp/cliff-like function → islands → overfitting.
  • Slide: "By adding a penalty term to the cost function, which discourages overly complex models" = regularization.

Galti mat karna

  • Har overfit model ke weights bade hon, aisa zaroori nahi — bade weights ek mechanism hai, akela nahi (worksheet side-note).
  • Overfit model ko aur epochs dena ya bada karna aag me ghee daalna hai.

Dimaag me aise baitha lo

Overfit = Chintu ne answer key ki typing mistakes tak ratt li: mock me 100, board me dhadaam.

18.4 Good Fit — teeno ko ek table me dekho

Good fit wo model hai jo pattern pakadta hai, noise chhod deta hai. Training aur validation dono errors low, aur dono ke beech chhota gap. Course slide "Issues in Model Fitting" ka table:

UnderfitOverfitGood Fit
Training datasetPoorVery GoodGood
Evaluation datasetVery PoorPoorGood
BiasHighLowLow
VarianceLowHighLow (acceptable)
Curve shapeSeedhi line, wave missHar point se guzarti wiggly lineSmooth curve, asli pattern ke paas

Lab ka "final checkpoint" — chaar cases, tum diagnose karo:

CaseEvidenceDiagnosis
Atrain .210, val .238 — dono plateau pe highUnderfit
Btrain .003, val .146 — bada gapOverfit
Ctrain .035, val .041 — dono low aur paasGood fit
Dtrain .000, test peak ke baad gir raha, capacity interpolation se kaafi aageSecond-descent regime (double descent, 18.12)

Dimaag me aise baitha lo

High–High = underfit. Low–High = overfit. Low–Low (chhota gap) = good fit.

18.5 Bias aur Variance — exact matlab kya hai?

Kahani

Diwali mela me dart game hai. Chaar dost khel rahe hain, har ek 8 dart phenkta hai.

Raju ke saare dart bullseye ke aas-paas, ek tight gucchha. Sonu ke dart bullseye ke charon taraf bikhre hue — average pe beech me hai, lekin har dart kahin aur. Monu ke saare dart ek tight gucchhe me, lekin bullseye se door, upar-right me — uski aankh (ya dart) hi tedhi hai. Golu ke dart door bhi aur bikhre bhi.

Monu ki problem bias hai: systematic galti, har baar ek hi taraf. Sonu ki problem variance hai: har baar alag result, bharosa nahi. Raju dono me achha hai.

Ab socho dart = ek trained model ki prediction. "Har dart" ka matlab: duniya se alag-alag training set lo (same distribution se, lekin alag samples), har ek pe same model type train karo, aur ek fixed test point pe predict karo. Har training set ek alag model dega — wahi alag-alag darts hain.

Definition (course slide, Bishop PRML)

Bias: "Represents the extent to which the average prediction over all datasets differs from the desired regression function." Simple shabdon me: model ki systematic galti, jo galat/zyada simple assumptions se aati hai (jaise "data ek seedhi line hai" maan lena). Kitne bhi datasets pe train karo, average prediction sahi jawab se door hi rahega.

Variance: "Represents the extent to which the model is sensitive to the particular choice of data set." Simple shabdon me: agar training set thoda sa badal do, to model kitna badal jaata hai. High variance model har naye dataset pe alag hi kahani sunata hai.

Slide notes: variance ke do common sources — (1) training data ka noise, (2) learning algorithm ki randomness (random initialization, shuffling, dropout).

Low bias · Low variance Low bias · High variance High bias · Low variance High bias · High variance
Dartboard: bullseye = sahi jawab (true function). Har laal dot = ek alag training set pe trained model ki prediction. Gucchha bullseye se kitna door = bias. Gucchha kitna bikhra = variance.
CombinationDartboard peModel example
Low bias, low varianceBullseye pe tight gucchhaSahi capacity, achha data — ideal (good fit)
Low bias, high varianceBullseye ke charon taraf bikhraBahut bada model, kam data — overfitting
High bias, low varianceDoor, lekin tight gucchhaBahut simple model (line for a wave) — underfitting
High bias, high varianceDoor bhi, bikhra bhiGalat model + kam/noisy data — sabse bura

Lab ka pocket-reference point dhyaan se padho: "Variance requires imagining or performing retraining on different sampled datasets." Ek hi trained model ko dekh ke variance nahi dikhta; variance ek distribution ki property hai — kai datasets pe retrain karne pe predictions kitni hilti hain.

Exam Radar

  • Bias = average prediction (over datasets) vs true function ka difference. Wrong/simple assumptions se aata hai.
  • Variance = prediction ki sensitivity training set ke choice pe.
  • Variance sources: data noise + algorithm randomness.
  • Underfitting ↔ high bias, low variance. Overfitting ↔ low bias, high variance.

Galti mat karna

  • Yahan "bias" ka matlab neuron ka bias term b (w·x + b) nahi hai. Naam same, cheez alag. Yahan bias = systematic error.
  • High variance ka matlab "data me variance zyada" nahi — matlab model ki predictions dataset badalne pe bahut badalti hain.

Dimaag me aise baitha lo

Bias = nishana hi tedha. Variance = haath kaanp raha hai.

18.6 Error ka bantwara: Bias² + Variance + Noise

Ab ek bahut important formula. Kisi test point x pe hamare model ki expected squared error ko teen tukdon me toda ja sakta hai. Ye squared-error (MSE) loss ke liye exact identity hai.

E[(y − f̂(x))²] = Bias[f̂(x)]² + Var[f̂(x)] + σ²
Bias[f̂(x)] = E[f̂(x)] − f(x)     Var[f̂(x)] = E[(f̂(x) − E[f̂(x)])²]
  • y = f(x) + ε = asli observed target. f(x) = true function (signal), ε = noise with mean 0, variance σ².
  • f̂(x) = hamare trained model ki prediction. Ye ek random cheez hai, kyunki ye random training set pe depend karti hai.
  • E[ ] = expectation = "average over" saare possible training sets (aur noise). E[f̂(x)] = "average model" ki prediction.
  • Bias² = average model sahi jawab se kitna door (square kiya, taaki + ya − dono penalize hon).
  • Variance = alag-alag training sets ke models apne average ke aas-paas kitna bikharte hain.
  • σ² = irreducible error (noise variance). Data me hi galti hai; koi bhi model isse neeche nahi ja sakta. Perfect model (f̂ = f) ka bhi error σ² hi hoga.

Formula aisa kyun dikhta hai? (chhota sa derivation)

Shortcut notation: f = f(x), f̂ = f̂(x), m = E[f̂] (average prediction).

  1. y − f̂ = (f + ε) − f̂ = ε + (f − f̂). Square karo aur average lo: E[ε²] + 2E[ε(f − f̂)] + E[(f − f̂)²].
  2. Naya noise ε training set se independent hai aur uska mean 0 hai, isliye beech wala term 0. Aur E[ε²] = σ². Bacha: σ² + E[(f − f̂)²].
  3. Ab f − f̂ = (f − m) + (m − f̂). Square + average: (f − m)² + 2(f − m)E[m − f̂] + E[(m − f̂)²]. Beech wala term phir 0, kyunki E[f̂] = m.
  4. Result: (m − f)² + E[(f̂ − m)²] + σ² = Bias² + Variance + σ².

Chalo numbers se samjhein

Ek test point pe sahi jawab f(x) = 10 hai. Noise ka variance σ² = 1. Humne 5 alag training sets pe same model train kiya; un 5 models ki predictions: 12, 13, 11, 14, 10.

  1. Average prediction: m = (12 + 13 + 11 + 14 + 10)/5 = 60/5 = 12
  2. Bias = m − f = 12 − 10 = 2 → Bias² = 4
  3. Variance = average of (prediction − m)²: (0² + 1² + (−1)² + 2² + (−2)²)/5 = (0 + 1 + 1 + 4 + 4)/5 = 10/5 = 2
  4. Noise: σ² = 1
  5. Total: 4 + 2 + 1 = 7
  6. Cross-check seedha: (f − f̂)² = 4, 9, 1, 16, 0 → average 30/5 = 6, plus noise 1 = 7. Same!

Jawab: Expected error 7 = Bias² 4 + Variance 2 + Noise 1. Yahan bias sabse bada hissa hai, to model ko zyada flexible banana chahiye.

Simulation se verify karo (numpy)

Theory pe bharosa nahi? Computer se 1000 alag training sets banwao (har baar naya noise), har ek pe polynomial fit karo, aur bias², variance, noise alag-alag naapo. Phir seedha naye noisy data pe test MSE naapo. Dono barabar aane chahiye.

Python · numpy · bias–variance simulation

import numpy as np
from numpy.polynomial import Polynomial

f = lambda x: np.sin(2 * np.pi * x)       # asli function
sigma = 0.3                                # noise ka std -> sigma^2 = 0.09
rng = np.random.default_rng(0)
x = np.linspace(0, 1, 20)                  # 20 training x (fixed)
x_test = np.linspace(0, 1, 101)            # test inputs

for degree in [1, 3, 9, 15]:
    preds, test_err = [], []
    for k in range(1000):                  # 1000 alag-alag training sets
        y = f(x) + rng.normal(0, sigma, 20)          # naya noise, naya dataset
        p = Polynomial.fit(x, y, degree)
        preds.append(p(x_test))
        y_new = f(x_test) + rng.normal(0, sigma, 101)   # fresh test labels
        test_err.append(np.mean((y_new - p(x_test)) ** 2))
    preds = np.array(preds)                          # shape (1000, 101)
    avg_pred = preds.mean(axis=0)                    # "average model"
    bias2 = np.mean((avg_pred - f(x_test)) ** 2)     # avg model kitna door
    variance = np.mean(preds.var(axis=0))            # models aapas me kitna hilte
    print(f"deg {degree:2d} | bias^2 {bias2:.4f} | var {variance:.4f} | "
          f"noise {sigma**2:.4f} | sum {bias2 + variance + sigma**2:.4f} | "
          f"measured {np.mean(test_err):.4f}")

Output

deg  1 | bias^2 0.2074 | var 0.0092 | noise 0.0900 | sum 0.3066 | measured 0.3057
deg  3 | bias^2 0.0053 | var 0.0162 | noise 0.0900 | sum 0.1115 | measured 0.1119
deg  9 | bias^2 0.0001 | var 0.0423 | noise 0.0900 | sum 0.1324 | measured 0.1322
deg 15 | bias^2 0.0019 | var 0.8673 | noise 0.0900 | sum 0.9592 | measured 0.9631

Output padho: degree 1 (seedhi line) me bias² 0.2074 hi sabse bada — underfitting. Degree 3 ka total sabse kam (0.1115) — sweet spot. Degree 9 aur 15 me bias lagbhag 0, lekin variance badhta jaata hai (0.0423 → 0.8673) — overfitting. Aur har row me "sum" ≈ "measured": formula sach me kaam karta hai. Noise 0.09 har row me same — koi bhi degree use kam nahi kar sakti.

Exam Radar

  • Expected test MSE = Bias² + Variance + σ² (irreducible noise).
  • σ² ko koi model kam nahi kar sakta; ye error ka "floor" hai.
  • Bias square hota hai formula me (Bias², not Bias). Variance square nahi hota (wo already squared deviation ka average hai).
  • Ye decomposition squared-error loss ke liye exact hai.

Galti mat karna

  • "Irreducible error ko zyada data se kam kar lenge" — nahi. Zyada data variance kam karta hai; noise σ² wahi rehta hai.

Dimaag me aise baitha lo

Galti = nishane ka tedhapan² + haath ka kaampna + hawa ka jhonka (jo kisi ke control me nahi).

18.7 Bias–Variance Tradeoff — U-curve aur sweet spot

Kahani

Maa daal me namak daal rahi hai. Bilkul kam namak (simple model): daal pheeki — har baar pheeki (bias). Bahut zyada experiment (har baar mood ke hisaab se alag masale): kabhi badhiya, kabhi bekaar (variance). Best daal beech me hai: itna flexible ki swaad aaye, itna disciplined ki har din consistent rahe.

Jaise-jaise model ki complexity (capacity) badhaate ho: bias girta hai (model zyada patterns bana sakta hai), lekin variance badhta hai (model data ke chhote-chhote badlaav pe bhi react karta hai). Total error = bias² + variance + noise, isliye total ek U-shape banata hai. U ka sabse neecha point = sweet spot, jahan bias aur variance ka balance sabse achha hai. Course lab: "Increasing capacity often lowers bias but raises variance. The preferred model minimizes expected unseen error — not training loss alone."

0.2 0.4 0.6 0.8 sweet spot (total = 0.60) Bias² Variance noise σ² (fixed 0.08) Total error ← Underfitting zone (high bias) Overfitting zone (high variance) → Model complexity / capacity → Error
Course lab ke formulas se banaya curve: Bias² = 0.08 + 0.72(1 − t)², Variance = 0.08 + 0.72t², noise = 0.08, jahan t = complexity (0 se 1). Total ka minimum t = 0.5 pe: 0.26 + 0.26 + 0.08 = 0.60.

Chalo numbers se samjhein (lab ke formulas)

Bias²(t) = 0.08 + 0.72(1 − t)², Variance(t) = 0.08 + 0.72t², Noise = 0.08.

  1. t = 0.18 (lab ka starting marker): Bias² = 0.08 + 0.72 × 0.82² = 0.08 + 0.72 × 0.6724 = 0.564; Variance = 0.08 + 0.72 × 0.0324 = 0.103; Total = 0.564 + 0.103 + 0.08 = 0.747
  2. t = 0.5: Bias² = 0.08 + 0.72 × 0.25 = 0.26; Variance = 0.26; Total = 0.26 + 0.26 + 0.08 = 0.60 ← minimum
  3. t = 0.75: Bias² = 0.08 + 0.72 × 0.0625 = 0.125; Variance = 0.08 + 0.72 × 0.5625 = 0.485; Total = 0.69

t = 0.18 pe bias² dominate karta hai (simpler side), t = 0.75 pe variance (complex side). Minimum (0.60) wahan jahan dono barabar (0.26 = 0.26) — is symmetric example me t = 0.5.

Lab ka doosra activity — saat networks, "simulator ka god view" (asli zindagi me ye breakdown nahi dikhta, sirf simulator me):

Hidden unitsBias²VarianceNoiseExpected test MSETraining MSE
1.220.010.030.260.214
2.140.020.030.190.148
4.070.040.030.140.081
8.030.070.030.130 (minimum).042
16.015.110.030.155.018
32.008.160.030.198.006
64.004.220.030.254.002

Dekho: training MSE har baar girta hai (.214 → .002), lekin expected test MSE pehle girta (.260 → .130) phir badhta (→ .254). Sweet spot = 8 hidden units ("Bias has fallen substantially, but variance has not yet taken over"). Agar training MSE dekh ke choose karte, to 64 units chunte — sabse bura test error ke kareeb!

Lab ke "architecture challenge" me bhi depth, width aur epochs teeno mila ke best config 2 layers × 8 neurons, 80 epochs tha — 20 epochs pe underfit (training adhoori), 300 epochs pe overfit. Yaani training time bhi capacity ka ek knob hai.

Exam Radar

  • Complexity ↑ ⇒ bias ↓, variance ↑, total error U-shaped.
  • Training error complexity ke saath (lagbhag) hamesha girta hai — isliye model choice training error se nahi, validation error se.
  • Optimal model = minimum expected unseen error, not minimum training loss.

Dimaag me aise baitha lo

Bias aur variance seesaw pe baithe hain: ek neeche to doosra upar. Hum us jagah baithte hain jahan dono ka total sabse halka ho.

18.8 Model Capacity — model ki "yaad karne ki taakat"

Model capacity = model kitni variety ke functions represent/fit kar sakta hai. Kam capacity = sirf simple shapes (line). Zyada capacity = koi bhi wiggly shape, yahan tak ki random noise bhi. Capacity badhane ke tareeke:

Aur capacity kam karne wale: regularization (L1/L2), dropout, early stopping. Ye architecture ko chhote kiye bina effective capacity ghatate hain. Worksheet ki line: "Regularisation is any extra preference that trades a little training fit for better test performance — without shrinking the architecture by hand."

Chalo numbers se samjhein: course notebook ke teen models

Course ka "Underfitting, Overfitting and Regularisation" notebook: make_moons data (2 inputs, binary), sirf 150 training points. nn.Linear(in, out) ke params = in × out + out.

  1. 1 hidden layer, 4 units: (2×4 + 4) + (4×1 + 1) = 12 + 5 = 17 params
  2. 1 hidden layer, 32 units: (2×32 + 32) + (32×1 + 1) = 96 + 33 = 129 params
  3. 4 hidden layers × 256: (2×256 + 256) + 3×(256×256 + 256) + (256×1 + 1) = 768 + 3×65792 + 257 = 768 + 197376 + 257 = 198401 params

17 params → underfit, 129 → theek, 198,401 params for 150 points (≈ 1323 params per point!) → overfit.

Exam Radar

  • Capacity ↑: more layers, more neurons, higher degree, longer training.
  • Effective capacity ↓: L1/L2, dropout, early stopping, data augmentation.
  • Course slide: underfitting ka ilaaj = "increase the capacity" (layers, neurons) → lower bias.

18.9 Polynomial degree experiment — underfit, good fit, overfit ek saath

Sabse classic demo (Bishop PRML ka favourite): asli function y = sin(2πx), usme noise (σ = 0.3) daal ke sirf 20 training points banao. Phir degree 1, 3 aur 15 ke polynomials fit karo. Degree jitni zyada, utne zyada coefficients (degree d → d + 1 parameters), yaani utni zyada capacity.

Python · numpy · degree 1 vs 3 vs 15

import numpy as np
from numpy.polynomial import Polynomial

rng = np.random.default_rng(31)                 # seed fix -> har baar same data
f = lambda x: np.sin(2 * np.pi * x)             # asli pattern (signal)

x_train = np.sort(rng.uniform(0, 1, 20))        # sirf 20 training points
y_train = f(x_train) + rng.normal(0, 0.3, 20)   # signal + noise (sigma = 0.3)
x_val = rng.uniform(0, 1, 200)                  # 200 naye points (validation)
y_val = f(x_val) + rng.normal(0, 0.3, 200)

for degree in [1, 3, 15]:
    p = Polynomial.fit(x_train, y_train, degree)    # least-squares fit
    train_mse = np.mean((p(x_train) - y_train) ** 2)
    val_mse = np.mean((p(x_val) - y_val) ** 2)
    print(f"degree {degree:2d} | train MSE {train_mse:.4f} | val MSE {val_mse:.4f}")

Output

degree  1 | train MSE 0.2694 | val MSE 0.3210
degree  3 | train MSE 0.0787 | val MSE 0.1002
degree 15 | train MSE 0.0066 | val MSE 0.3907
degree 1: underfit train 0.2694 · val 0.3210 degree 3: good fit train 0.0787 · val 0.1002 degree 15: overfit train 0.0066 · val 0.3907 dashed = asli sin(2πx) · dots = 20 noisy training points · laal = fitted polynomial
Wahi 20 points, teen models. Degree 1 wave ko miss karta hai. Degree 3 asli dashed curve ke paas. Degree 15 points ke beech pagal ki tarah upar-neeche jaata hai (graph ke bahar tak).

Saare degrees 0 se 19 tak chalao (same data) to poora U dikhta hai:

Degree0135912151719
Train MSE0.42140.26940.07870.05600.03730.01300.00660.00180.0000
Val MSE0.56080.32100.10020.12300.15170.31160.39071037.18≈ 6.3 × 10⁸

Training MSE har step pe neeche (degree 19 pe exactly 0 — 20 coefficients, 20 points, curve har point se guzar jaati hai). Validation MSE degree 3 pe minimum, phir badhta hai, aur degree 17–19 pe phat jaata hai. Degree 19 = "parameters = training points" — is point ko interpolation threshold kehte hain; iske baare me 18.12 Double Descent me aur baat karenge.

Galti mat karna

  • Degree ko training MSE dekh ke choose mat karna — wo hamesha sabse badi degree bolega. Validation MSE dekho.

18.10 Learning curves padhna — train vs validation loss

Kahani

Cricket coach har hafte do cheezein note karta hai: net practice me kitne runs (training) aur practice match me kitne runs (validation). Agar net me runs badh rahe hain aur match me bhi — badhiya. Agar net me runs badh rahe hain lekin match me gir rahe hain — player sirf net ke bowlers ki aadat daal raha hai (ratta). Wahi pal hai rukne ka.

Training ke dauraan har epoch ke baad training loss aur validation loss dono note karo aur plot karo. Ye learning curves hain. Dono curves ke beech ka faasla = generalization gap, aur gap ka badhna = variance (overfitting) badh raha hai.

Underfit epochs → dono upar, paas-paas Good fit epochs → dono neeche, chhota gap Overfit epochs → train ↓, val U-turn best epoch kaali line = training loss · laal dashed = validation loss
Teen pattern (worksheet 09): Underfit — dono curves upar aur paas. Good fit — dono neeche, chhota gap. Overfit — training girta rehta hai, validation ek lowest point ke baad U-turn leke badhta hai.
Curves me kya dikhaDiagnosisKya karein
Dono high, paas-paas, flat ho gayeUnderfitting (high bias)Capacity ↑, train longer, regularization ↓
Training ↓ lagatar, validation ek point ke baad ↑, gap ↑Overfitting (high variance)Regularization ↑, more data, early stopping
Dono low, chhota gapHealthy / good fitTuning band karo, test ek baar check karo
Dono abhi bhi gir rahe hainAbhi training adhooriAur epochs

Chalo numbers se samjhein (Model Capacity Lab ka training timeline)

EpochTrain MSEValidation MSEGap (val − train)
10.2140.2260.012
200.0820.1010.019
800.0260.0410.015
3000.0040.1370.133
  1. Epoch 1 → 20 → 80: dono gir rahe hain → model pattern seekh raha hai.
  2. Epoch 80 → 300: train 0.026 → 0.004 (aur gira), val 0.041 → 0.137 (badha!) → ab model noise yaad kar raha hai.
  3. Gap: 0.137 − 0.004 = 0.133 at epoch 300 vs 0.041 − 0.026 = 0.015 at epoch 80.

Early stopping epoch 80 rakhega (lowest validation MSE). Regularisation worksheet ka same sawaal: Lval = 0.40, 0.18, 0.12, 0.21 at epochs 1, 20, 80, 300 → t* = arg min Lval = epoch 80.

Apni aankhon se overfitting dekho — PyTorch demo (course notebook style)

Course ka notebook Keras me tha (make_moons(n_samples=2150, noise=0.3, random_state=0), 150 train + 2000 validation, Adam 0.003, full batch, 400 epochs). Wahi experiment PyTorch me. Naye API yahan:

PyTorch · overfitting demo · make_moons

import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_moons

# ---- data: course notebook jaisa hi ----
X, y = make_moons(n_samples=2150, noise=0.3, random_state=0)
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32).unsqueeze(1)   # (N,) -> (N, 1) for BCE
X_tr, y_tr = X[:150], y[:150]        # sirf 150 training points
X_va, y_va = X[150:], y[150:]        # 2000 validation points (kabhi train nahi honge)

def make_mlp(widths):
    # widths = [4] ya [32] ya [256, 256, 256, 256]
    layers, in_dim = [], 2
    for w in widths:
        layers += [nn.Linear(in_dim, w), nn.ReLU()]
        in_dim = w
    layers.append(nn.Linear(in_dim, 1))       # 1 logit (sigmoid loss ke andar)
    return nn.Sequential(*layers)

def train(widths, epochs=400):
    torch.manual_seed(0)
    model = make_mlp(widths)
    n_params = sum(p.numel() for p in model.parameters())
    criterion = nn.BCEWithLogitsLoss()        # sigmoid + binary cross-entropy ek saath
    optimizer = optim.Adam(model.parameters(), lr=0.003)
    best_val, best_epoch = float('inf'), 0
    for epoch in range(1, epochs + 1):
        model.train()
        optimizer.zero_grad()
        loss = criterion(model(X_tr), y_tr)   # full batch: saare 150 ek saath
        loss.backward()
        optimizer.step()
        model.eval()
        with torch.no_grad():
            val_loss = criterion(model(X_va), y_va).item()
        if val_loss < best_val:
            best_val, best_epoch = val_loss, epoch
    with torch.no_grad():
        tr_acc = ((model(X_tr) > 0).float() == y_tr).float().mean().item()
        va_acc = ((model(X_va) > 0).float() == y_va).float().mean().item()
    print(f"{str(widths)}: {n_params} params")
    print(f"  train loss {loss.item():.4f} acc {tr_acc:.3f} | val loss {val_loss:.4f} acc {va_acc:.3f}")
    print(f"  lowest val loss {best_val:.4f} at epoch {best_epoch}")

for widths in ([4], [32], [256, 256, 256, 256]):
    train(widths)

Output

[4]: 17 params
  train loss 0.3434 acc 0.873 | val loss 0.3511 acc 0.859
  lowest val loss 0.3511 at epoch 400
[32]: 129 params
  train loss 0.2271 acc 0.907 | val loss 0.2431 acc 0.906
  lowest val loss 0.2431 at epoch 400
[256, 256, 256, 256]: 198401 params
  train loss 0.0023 acc 1.000 | val loss 1.6270 acc 0.882
  lowest val loss 0.2562 at epoch 30

Bade model ke learning curve ke kuch points (same run, har epoch log kiya):

Epoch110203050100200300400
Train loss0.69350.36990.24300.18780.12550.08020.06690.01360.0023
Val loss0.64470.28610.27640.25620.36110.74000.63021.35011.6270

Shuru me val loss train se kam bhi dikh sakta hai (epoch 1, 10) — train loss weight update se pehle naapa gaya aur data noisy hai. Asli kahani epoch 30 ke baad: train loss lagatar neeche, val loss upar (thoda zig-zag ke saath). Ye wahi "U-turn" hai. Ilaaj → Chapter 19.

Exam Radar

  • Gap between train and val curves = variance indicator. Gap growing with epochs = overfitting starting.
  • Overfit curve: training loss keeps falling; validation loss falls, hits minimum, then rises.
  • Early stopping checkpoint = epoch with lowest validation loss (lab: epoch 80).
  • BCEWithLogitsLoss = sigmoid + BCE; model outputs raw logits.

Galti mat karna

  • Validation loss ke chhote zig-zag (ek epoch upar, agla neeche) ko overfitting mat samjho — trend dekho. Isi liye early stopping me "patience" hota hai (19.7).
  • Dropout ke saath training loss validation se upar ho sakta hai (training me neurons band hain) — wo bug nahi (Chapter 20).

Dimaag me aise baitha lo

Do lines: training hamesha neeche jaana chahti hai. Validation jis din upar mude, samjho ratta shuru.

18.11 Diagnosis table — symptom → bimari → ilaaj

Doctor pehle report padhta hai, phir dawai deta hai. Hum bhi pehle (train error, val error) ka jodaa padhte hain:

SymptomsProblemBias / VarianceRemedies
Train error high, val error high (dono paas)UnderfittingHigh bias, low varianceBigger model (layers/neurons), better features, train longer, less regularization (λ ↓, dropout ↓), better LR/optimizer
Train error very low, val error much higher (gap bada)OverfittingLow bias, high varianceMore data, data augmentation, L2/L1, dropout, early stopping, simpler model, ensembles
Dono low, gap chhotaGood fitLow bias, low varianceRuk jao; test set pe ek baar final score
Train error high, val error aur bhi bahut zyadaDono problemHigh bias + high variancePehle bias theek karo (fit training), phir variance
Train ≈ 0, test peak ke baad gir raha, capacity bahut badiOver-parameterized regimeSecond descentDouble descent (18.12) — bada model bhi theek ho sakta hai, guarantee nahi

Worksheet ka practical recipe (6.2): (1) pehle aisa model jo overfit kar sake; (2) safety net ke liye early stopping + best-weight restore; (3) L2 (AdamW ke saath) ya dropout me se ek add karo, log scale pe tune karo; (4) har change ke baad curves aur gap dobara padho; (5) test score ek baar, end me.

Dimaag me aise baitha lo

Pehle training data jeeto (bias), phir naya data jeeto (variance).

18.12 Double Descent — classical U-curve ke aage ki kahani

Kahani

Diwali pe aangan me 10 rangoli dots bane hain, aur tumhe ek line kheenchni hai jo saare dots se guzre.

Pehle tumhare paas ek stiff ruler hai (chhota model) — saare dots se guzar hi nahi sakta. Phir ek thodi si lachili patti milti hai, jo bas mushkil se saare dots chhoo sakti hai (parameters ≈ dots) — use har dot tak pahunchane ke liye itna marodna padta hai ki beech me patti pagal ki tarah upar-neeche jaati hai. Bilkul bekaar design.

Phir tumhe ek bahut lambi, bahut lachili rassi milti hai (huge model). Ab saare dots se guzarne ke hazaaron tareeke hain. Aur tum (aalsi insaan, ya SGD) automatically sabse seedha, smooth tareeka chunte ho. Result? Design phir se sundar.

Yaani: chhota model theek → thoda bada theek → "bas barabar" wala model sabse bura → bahut bada model phir theek! Error do baar girta hai. Isliye naam: double descent.

Classical bias–variance theory (18.7) kehti hai: capacity badhao to test error pehle girega, phir badhta hi rahega (U-curve). Lekin modern deep learning me log bahut bade networks (training points se kahin zyada parameters) use karte hain jo training data ko poora memorize kar lete hain (train error ≈ 0) — aur phir bhi test pe badhiya karte hain. Ye classical U se match nahi karta. Belkin et al. (2019) ne iska jawab diya: test error curve asal me "double descent" banata hai.

Definition

Double descent: model size badhane pe test error (1) pehle girta hai, (2) phir badhta hai aur interpolation threshold pe peak karta hai, (3) phir threshold ke aage (over-parameterized regime me) dobara girta hai — kai baar classical sweet spot se bhi neeche.

Interpolation threshold: wo capacity jahan model training data ko bas mushkil se exactly fit kar paata hai (training error pehli baar ≈ 0). Roughly: number of parameters ≈ number of training points.

Under-parameterized = threshold se pehle (classical regime). Over-parameterized = threshold ke baad (modern regime).

0.2 0.4 0.6 20 60 120 180 200 300 500 800 1600 interpolation threshold (params ≈ 200 = data points) classical U (under-parameterized) second descent (over-parameterized) train error = 0 (sab yaad) test error number of parameters (model size) → [200 training examples] MSE
Course ke Model Capacity Lab ke numbers (200 training examples). Laal = test MSE: girta (0.30 → 0.17), threshold (200 params) pe peak 0.60, phir girta 0.14 tak. Kaali dashed = train MSE: 200 params pe 0 ho jaata hai aur 0 hi rehta hai.
ParamsParams / dataRegimeTrain MSETest MSE
200.1×under-parameterized0.2500.300
600.3×under-parameterized0.1700.220
1200.6×under-parameterized0.1000.170 (classical sweet spot)
1800.9×near interpolation0.0250.290
2001×interpolation threshold00.600 (peak)
3001.5×over-parameterized00.400
5002.5×over-parameterized00.250
8004×over-parameterized00.180
16008×highly over-parameterized00.140 (sabse kam!)

Lab ke teen sawaal aur jawab: (1) Double descent ka path? Falls → peaks → falls. (2) Training error pehli baar ≈ 0 kahan hota hai? Interpolation threshold pe (minimum test error pe nahi!). (3) Kya iska matlab har bada network behtar generalize karega? Nahi — setup-dependent. "Double descent is a possible pattern — not a promise that increasing capacity always helps."

Peak kyun aata hai, aur doosri baar error kyun girta hai? (intuition)

Teen tarah ka double descent (Nakkiran et al., 2019, "Deep Double Descent")

TypeKya badalte hainKya dikhta hai
Model-wiseModel size (width/params), training fixedTest error: girta → threshold pe peak → girta (upar wala curve)
Epoch-wiseBada model fixed, training epochs badhaate jaoTest error: girta → badhta → phir girta. Lambi training effective capacity badhati hai, to model time ke saath threshold cross karta hai
Sample-wiseModel fixed, training data ki quantity badhaoThreshold ke paas zyada data se test error badh bhi sakta hai! ("more data can hurt") Kyunki zyada samples threshold ko aage khiskate hain, aur fixed model ab exactly threshold pe aa jaata hai

Nakkiran et al. ne ise effective model complexity (EMC) se samjhaya: wo maximum number of samples jinhe model (is training procedure ke saath) lagbhag zero training error tak fit kar sake. Jab EMC ≈ number of training samples, tab critical regime aur peak. Unhone ye bhi dekha ki label noise jitna zyada, peak utna ooncha aur saaf.

Numbers se verify karo — numpy me double descent

Ek simple linear regression lo: 40 training points, 400 possible features (asli signal saare features me thoda-thoda baanta hua). Model sirf pehle p features use karta hai. Har p ke liye minimum-norm least squares (np.linalg.pinv) se fit karo — jab exact fit ke kai solutions hon, pinv sabse chhote norm wala deta hai (wahi jo gradient descent zero se start karke deta).

Python · numpy · model-wise double descent

import numpy as np

n, D = 40, 400                       # 40 training points, 400 possible features
ps = [10, 30, 38, 40, 42, 60, 100, 400]
test_mse = {p: [] for p in ps}
train_mse = {p: [] for p in ps}
for rep in range(100):               # 100 baar experiment, median lenge
    r = np.random.default_rng(rep)
    beta = r.normal(0, 1, D) / np.sqrt(D)          # asli weights
    X = r.normal(0, 1, (n, D));    y = X @ beta + r.normal(0, 0.3, n)
    Xt = r.normal(0, 1, (2000, D)); yt = Xt @ beta + r.normal(0, 0.3, 2000)
    for p in ps:                     # model sirf pehle p features use karta hai
        w = np.linalg.pinv(X[:, :p]) @ y           # min-norm least squares
        train_mse[p].append(np.mean((X[:, :p] @ w - y) ** 2))
        test_mse[p].append(np.mean((Xt[:, :p] @ w - yt) ** 2))
for p in ps:
    print(f"params {p:3d} (p/n = {p/n:4.2f}) | train {np.mean(train_mse[p]):.4f} | test {np.median(test_mse[p]):.4f}")

Output

params  10 (p/n = 0.25) | train 0.7627 | test 1.4092
params  30 (p/n = 0.75) | train 0.2372 | test 3.7082
params  38 (p/n = 0.95) | train 0.0521 | test 16.1839
params  40 (p/n = 1.00) | train 0.0000 | test 79.8153
params  42 (p/n = 1.05) | train 0.0000 | test 20.8103
params  60 (p/n = 1.50) | train 0.0000 | test 2.6452
params 100 (p/n = 2.50) | train 0.0000 | test 1.4960
params 400 (p/n = 10.00) | train 0.0000 | test 0.9852

Saaf dikh raha hai: p = 40 = n pe training error exactly 0 aur test error ka bhayanak peak (79.8). Uske baad p badhao to test error girta jaata hai, aur p = 400 pe 0.9852 — jo is setup ke kisi bhi under-parameterized model (jaise p = 10 pe 1.4092) se kam hai. Is setup me pehla "U" chhota hai (signal saare features me barabar baanta hai, isliye shuru se hi variance haavi ho jaata hai) — isi liye lab kehta hai: pattern setup pe depend karta hai.

Implications — exam aur practice ke liye

Exam Radar

  • Double descent: test error falls → peaks at interpolation threshold → falls again in over-parameterized regime.
  • Interpolation threshold: #params ≈ #training samples; training error first reaches ≈ 0 here.
  • Term "double descent": Belkin et al., 2019 (PNAS). "Deep Double Descent: Where Bigger Models and More Data Hurt": Nakkiran et al., 2019 (OpenAI).
  • Three kinds: model-wise, epoch-wise, sample-wise.
  • Reason for second descent: many interpolating solutions; SGD's implicit regularization picks smooth / small-norm ones. At threshold only one (wiggly, huge-norm) solution.
  • Label noise makes the peak bigger. Not guaranteed — setup-dependent.

Galti mat karna

  • Minimum test error interpolation threshold pe nahi hota — wahan to peak (maximum) hota hai. Threshold = jahan train error pehli baar 0 hota hai.
  • Double descent bias–variance decomposition ko "galat" nahi karta; formula ab bhi sahi hai. Bas over-parameterized regime me variance phir se kam ho jaata hai (implicit regularization ki wajah se).

Dimaag me aise baitha lo

Chhoti rassi: dots tak nahi pahunchti. Bas-barabar rassi: marod-marod ke pahunchti hai (peak). Bahut lambi rassi: aaraam se, seedhe pahunchti hai (second descent).

Chapter 19

Regularization: L1, L2, Early Stopping

Definition · Ẽ = E + λΩ · L2 / Ridge / weight decay · L1 / Lasso · sparsity · L1 diamond vs L2 circle · Elastic Net · λ ka asar · Early stopping + patience · Data augmentation · weight_decay, Adam vs AdamW · Lab 9

19.1 Regularization kya hai? Model pe lagaam

Kahani

Mohalle ki cricket me ek naya rule aaya: "Har six maarne pe ₹10 ka fine, team fund me." Ab batsman tabhi six maarega jab sach me zaroori ho — last over, jeetne ke liye. Faltu dikhaawe ke six band. Khel zyada disciplined ho gaya.

Regularization bilkul yahi hai. Model ko kehte hain: "Bade weights rakh sakte ho, lekin har bade weight ka fine bharna padega." Ab model bada weight tabhi rakhega jab wo data loss me utni kami laaye jitna uska fine hai. Noise ke liye island banana mehenga pad jaata hai, to model simple rehta hai.

Definition (course slide, Goodfellow et al.)

"Regularization is any modification we make to a learning algorithm that is intended to reduce its generalization error but not its training error."

Matlab: koi bhi badlaav jo test error kam kare, bhale hi training error kam na kare (aksar training error thoda badh hi jaata hai). Worksheet: "Regularisation is any change that makes a model prefer a simpler explanation, even if it fits the training data slightly worse."

Ẽ(θ) = E(θ) + λ · Ω(θ)
  • E(θ) = normal data loss (MSE, cross-entropy...) — data ko kitna achha fit kiya.
  • Ω(θ) = penalty / preference term — model kitna "complex" hai (L2: Σw², L1: Σ|w|).
  • λ (lambda) = regularization strength = hyperparameter. λ = 0 → normal training. λ bahut bada → model sirf weights chhote rakhne me laga rahega → underfit.
  • Ẽ(θ) = total objective jise optimizer minimize karta hai.

Chalo numbers se samjhein (regularisation worksheet)

E = 0.40, λ = 0.10, Ω = 2.0. Ẽ = ?

  1. Expression: Ẽ = 0.40 + (0.10)(2.0)
  2. Penalty: 0.10 × 2.0 = 0.20
  3. Total: 0.40 + 0.20 = 0.60

Jawab: Ẽ = 0.60

λ kaun chunega? Training set nahi! Agar λ ko training loss se chunoge to hamesha λ = 0 jeetega (penalty jitni kam, training loss utna kam). Worksheet ka rule: "Training updates θ. Validation chooses λ, p, patience. Test is opened once." λ ko gradient descent se train nahi karte; use validation pe tune karte hain.

Weights chhote kyun? Kyunki chhote weights = smooth function

Slide ka chain: Weight penalties → smaller weights → simpler model → less overfit. Yaad karo 18.3: w = 50 pe sigmoid cliff ban gaya (0.007 → 0.993), w = 1 pe gentle slope. Neuron ka output input ke har unit badlaav pe lagbhag w jitna badalta hai. Chhote w → paas-paas ke inputs ka output bhi paas-paas → boundary sharp mod nahi le sakti, ek single point ke around island nahi bana sakti.

Regularization ke parivaar (family)

TechniqueEk line meKahan padhein
L2 (Ridge / weight decay)Bade weights pe fine; sab weights proportionally chhote19.2
L1 (Lasso)Har weight pe fine; faltu weights exactly zero19.3
Early stoppingRatta shuru hone se pehle ruk jao ("delete training time")19.7
DropoutHar step random neurons band ("delete activations for one step")Chapter 20
Batch normActivations ko minibatch stats se re-scale; mild regularizing side effectChapter 20
Data augmentation / more dataNaye training examples banao19.8
Ensembles (bagging)Kai models ka average → variance kam19.8

Course slide ka summary: regularization ke do type — (1) Model optimization: model ki complexity kam karo (L1, L2, dropout, early stopping, batch norm); (2) Data augmentation: training data ka size badhao. Prince (UDL ch. 9) ki bhasha me: explicit regularization (loss me penalty jodna, jaise L2) vs implicit regularization (optimizer khud, stopping time, SGD ka noise — bina koi term jode simple solutions ki taraf jhukaav).

Exam Radar

  • Definition: reduce generalization (test) error, not (necessarily) training error — Goodfellow.
  • Ẽ(θ) = E(θ) + λΩ(θ); λ = hyperparameter, tune on validation; λ = 0 → no regularization.
  • "L" in L1/L2 = Lebesgue.
  • Two types (slide): model optimization (complexity ↓) vs data augmentation (data ↑).

Galti mat karna

  • Loss me koi bhi constant jodne se regularization nahi hoti! L + 10 ka gradient wahi hai jo L ka (constant ka derivative 0) — weights pe koi asar nahi. Penalty weights pe depend karni chahiye (λW²), tabhi gradient badlega. (Interactive worksheet ka "missing link".)
  • Regularization se training loss thoda badh sakta hai — ye problem nahi, yahi to trade hai.

Dimaag me aise baitha lo

Har six pe fine: six tabhi maaro jab zaroori ho. Har bade weight pe fine: bada weight tabhi rakho jab zaroori ho.

19.2 L2 Regularization — Ridge / Weight Decay

Kahani

Hostel me bijli ka bill slab wala hai: 1 unit pe ₹1, lekin 10 unit pe ₹100 — kharcha units ke square me badhta hai. Thodi bijli lagbhag free, zyada bijli bahut mehengi. Sab log thoda-thoda use karte hain, koi bhi heater pura din nahi chalata.

L2 yahi karta hai: weight ka fine = weight². Chhote weights pe fine na ke barabar, bade weights pe bhaari. Result: saare weights chhote aur fail-fail ke (spread out), koi ek weight bahut bada nahi.

L2 regularization (statistics me Ridge regression, deep learning frameworks me weight decay) loss me weights ke squares ka sum jodta hai. Slide: "L2 adds squared magnitude of coefficient as penalty term to the loss function."

Ltotal = Ldata + λ Σi wi²    →    ∂Ltotal/∂w = ∂Ldata/∂w + 2λw
wnew = w − η(gdata + 2λw) = (1 − 2ηλ)·w − η·gdata
  • Ldata = normal prediction loss; gdata = ∂Ldata/∂w (data ka gradient).
  • λ Σw² = L2 penalty; square ki wajah se negative weights bhi penalize ((−3)² = 9).
  • 2λw = penalty ka gradient (d/dw of λw² = 2λw) = gpenalty. Ye w ke proportional hai: bada weight → bada dhakka zero ki taraf.
  • η (ya α) = learning rate. (1 − 2ηλ) = shrink / decay factor, 1 se thoda chhota.

Doosra common form (λ/2 wala) — worksheet 09 aur Bishop/Goodfellow isi ko use karte hain: Ltotal = L + λ2 Σw². ½ isliye lagaya taaki derivative saaf aaye: gradient me λw (2 cancel), aur update wnew = (1 − ηλ)w − ηgdata. Dono forms ka idea same hai; bas λ ki value ka matlab 2 ke factor se badal jaata hai. Worksheet: "Keras writes the penalty as λΣw², without the ½. That changes the number you type, not the idea." Aur PyTorch ka weight_decay gradient me seedha weight_decay × w jodta hai — yaani λ/2 wala form, λ = weight_decay (neeche code me verify karenge).

Chalo numbers se samjhein — penalty value (interactive worksheet)

(a) Ek weight: Ldata = 4, W = 3, λ = 0.1.

  1. W² = 3² = 9
  2. λW² = 0.1 × 9 = 0.9
  3. Ltotal = 4 + 0.9 = 4.9 (ye 0.9 random nahi; W = 3 ki wajah se aaya)

(b) Kai weights: W₁ = 2, W₂ = −3, W₃ = 1, λ = 0.1, Ldata = 5.

  1. 2² + (−3)² + 1² = 4 + 9 + 1 = 14
  2. penalty = 0.1 × 14 = 1.4
  3. Ltotal = 5 + 1.4 = 6.4

(c) λ/2 form, 10 guna bade weights (worksheet 09): λ = 0.1 → λ/2 = 0.05.

  1. Weights [3, −1, 2]: 9 + 1 + 4 = 14 → 0.05 × 14 = 0.7
  2. Weights [30, −10, 20]: 900 + 100 + 400 = 1400 → 0.05 × 1400 = 70

Weights 10 guna bade → penalty 100 guna (0.7 → 70). Square ka kamaal: ek bahut bada weight kai chhote weights se zyada mehenga.

Chalo numbers se samjhein — ek GD step, L2 ke saath aur bina (interactive worksheet)

W = 5, α = 0.1, λ = 0.5, teaching loss Ldata = (W − 3)² (bina penalty ke best W = 3).

Ordinary GD:

  1. Ldata = (5 − 3)² = 4
  2. gdata = 2(W − 3) = 2(5 − 3) = 4
  3. Wnew = 5 − 0.1 × 4 = 4.6; naya data loss (4.6 − 3)² = 2.56

GD + L2:

  1. Penalty: λW² = 0.5 × 25 = 12.5 → Ltotal = 4 + 12.5 = 16.5
  2. Gradients: gdata = 4, gpenalty = 2λW = 2 × 0.5 × 5 = 5
  3. Total gradient: 4 + 5 = 9
  4. Wnew = 5 − 0.1 × 9 = 4.1
  5. Decay form se check: (1 − 2 × 0.1 × 0.5) × 5 − 0.1 × 4 = 0.9 × 5 − 0.4 = 4.5 − 0.4 = 4.1 ✓
  6. Step ke baad: data loss (4.1 − 3)² = 1.21, penalty 0.5 × 4.1² = 0.5 × 16.81 = 8.405, total 9.615

Ordinary GD: 5 → 4.6. GD + L2: 5 → 4.1. Extra 0.5 ki kami gpenalty = 5 ki wajah se. Logic: "weight-dependent penalty → extra gradient → |W| neeche", na ki "loss bada hua to weight chhota".

Isse "weight decay" kyun kehte hain?

Update ko rearrange karo: Wnew = (1 − 2αλ)W − αgdata. Har step pe weight pehle (1 − 2αλ) se multiply hota hai — thoda sa shrink — phir normal step. α = 0.1, λ = 0.1 → factor 1 − 2(0.1)(0.1) = 0.98: data gradient ko ignore karo to W → 0.98W har step. Ye baar-baar ka shrink hi "decay" hai — jaise radioactive decay me har second ek fixed fraction kam hota hai.

Chalo numbers se samjhein — λ/2 form (worksheet 09)

w = 2, ∂L/∂w = 0.5, α = 0.1, λ = 1 (λ/2 form, shrink = 1 − αλ).

  1. Shrink factor: 1 − 0.1 × 1 = 0.9
  2. With L2: 0.9 × 2 − 0.1 × 0.5 = 1.8 − 0.05 = 1.75
  3. Without L2: 2 − 0.1 × 0.5 = 1.95
  4. Ek bekaar weight (∂L/∂w = 0), shrink 0.9: 2 → 1.8 → 1.62 → 1.458 → dheere-dheere fade.
  5. Ek kaam ka weight: data loss ½(w − 4)² (bina penalty best w = 4). Penalty ke saath gradient (w − 4) + λw = 0 → w = 4/(1 + λ) → λ = 0, 1, 3 pe 4, 2, 1.

Bada λ → chhota weight, lekin kabhi exactly zero nahi. Kaam ka weight wahan settle hota hai jahan data ka dhakka aur shrink barabar ho jaayein.

Ridge ka closed form — do points pe (regularisation worksheet board calculation)

Model ŷ = wx, points (1, 2) aur (2, 3). SSE(w) = (2 − w)² + (3 − 2w)² = 5w² − 16w + 13.

  1. λ = 0: derivative 10w − 16 = 0 → w = 1.6000, SSE = (0.4)² + (−0.2)² = 0.16 + 0.04 = 0.2000
  2. λ = 1: Ẽ = SSE + λw² → derivative 10w − 16 + 2λw = 0 → w = 16/(10 + 2λ) = 8/(5 + λ) = 8/6 = 1.3333
  3. Naya SSE: (2 − 1.3333)² + (3 − 2.6667)² = 0.4444 + 0.1111 = 0.5556

Training SSE 0.2 → 0.5556 (badha), |w| 1.6 → 1.3333 (ghata). "A little bias, less variance." General ridge formula: ŵ = (XᵀX + λI)⁻¹Xᵀy — har eigenvalue me λ jud jaata hai, kamzor (chhote eigenvalue wali) directions sabse zyada shrink hoti hain.

Island banana worth hai? (worksheet 09, figure 2.5)

Ek noisy point ke around island banane se data loss 0.3 kam hota hai, lekin sum of squared weights 20 badh jaata hai. Penalty (λ/2) × 20.

  1. λ = 0.01: extra penalty 0.005 × 20 = 0.1 < 0.3 → island reh jaata hai.
  2. λ = 0.1: extra penalty 0.05 × 20 = 1.0 > 0.3 → island gayab.
  3. Break-even: (λ/2) × 20 = 0.3 → λ = 0.03

L2 islands ko forbid nahi karta; unka bill banata hai. Ek noisy point ka island bill nahi chuka paata; kai points wala asli pattern chuka deta hai, isliye wo bachta hai.

L2 ka asar — ek line me

Do practical rules (worksheet 09)

PyTorch me L2: optimizer ka weight_decay

Sabse aasaan tareeka (Lab 9, Lab 10–14 sab yahi use karte hain):

PyTorch · L2 via weight_decay (Lab 9 style)

import torch.optim as optim

# weight_decay = L2 strength. Har step gradient me weight_decay * w jud jaata hai.
optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=1e-4)

# Lab 9: same model, same loop, sirf ye argument on kiya
optimizer = optim.SGD(model.parameters(), lr=0.1, weight_decay=1e-4)   # L2 regularization

# Labs 10/11: Optuna weight_decay bhi tune karta hai
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

Kya weight_decay sach me "loss me (λ/2)Σw²" ke barabar hai (plain SGD me)? Khud check karo:

PyTorch · weight_decay == (λ/2)·Σw² in loss (SGD)

import torch
import torch.nn as nn
import torch.optim as optim

torch.manual_seed(0)
x = torch.randn(8, 3)
y = torch.randn(8, 1)

model_a = nn.Linear(3, 1)
model_b = nn.Linear(3, 1)
model_b.load_state_dict(model_a.state_dict())   # dono ka starting point bilkul same

lam = 0.1
criterion = nn.MSELoss()

# Tarika A: optimizer ka weight_decay
opt_a = optim.SGD(model_a.parameters(), lr=0.5, weight_decay=lam)
opt_a.zero_grad()
criterion(model_a(x), y).backward()
opt_a.step()

# Tarika B: loss me khud (lam/2) * sum(w^2) jodo
opt_b = optim.SGD(model_b.parameters(), lr=0.5)
opt_b.zero_grad()
l2 = sum(p.pow(2).sum() for p in model_b.parameters())   # weight + bias dono
loss_b = criterion(model_b(x), y) + (lam / 2) * l2
loss_b.backward()
opt_b.step()

print(model_a.weight.data)
print(model_b.weight.data)
print(torch.allclose(model_a.weight, model_b.weight), torch.allclose(model_a.bias, model_b.bias))

Output

tensor([[-0.2745,  0.5641,  0.7313]])
tensor([[-0.2745,  0.5641,  0.7313]])
True True

Bilkul same. Yaani PyTorch me weight_decay=λ ≡ loss me (λ/2)Σw² (ya λΣw² form me λloss = weight_decay/2). p.pow(2).sum() = us tensor ke saare elements ka square-sum; generator ke upar sum(...) saare layers ka total deta hai.

Adam vs AdamW — "L2" aur "weight decay" kab alag ho jaate hain

Plain SGD me "loss me L2 jodo" aur "har step weights shrink karo" ek hi cheez hain (upar dekha). Lekin Adam me nahi. Adam har gradient ko apne running √v se divide karke rescale karta hai (Chapter 17). Agar penalty loss ke andar hai (optim.Adam(..., weight_decay=...)), to penalty ka gradient λw bhi rescale ho jaata hai — barabar weights barabar shrink nahi hote, bade gradients wale weights kam regularize hote hain. AdamW (Loshchilov & Hutter, "decoupled weight decay") Adam step sirf data gradient se leta hai aur weights ko alag se shrink karta hai: w ← w − ηλw.

PyTorch · Adam(weight_decay) vs AdamW · sirf decay ka asar

import torch
import torch.optim as optim

for name in ["Adam", "AdamW"]:
    w = torch.nn.Parameter(torch.tensor([2.0, 0.2]))      # ek bada, ek chhota weight
    opt = getattr(optim, name)([w], lr=0.1, weight_decay=0.5)
    for step in range(3):
        opt.zero_grad()
        w.grad = torch.zeros(2)          # data gradient = 0 (sirf decay ka asar dekhna hai)
        opt.step()
    print(name, [round(v, 4) for v in w.data.tolist()])

Output

Adam [1.7006, -0.0672]
AdamW [1.7147, 0.1715]

AdamW: har step × (1 − 0.1 × 0.5) = × 0.95 → 2 × 0.95³ = 1.7147, 0.2 × 0.95³ = 0.1715. Dono proportional shrink — sachcha weight decay. Adam + weight_decay: penalty gradient normalize ho gaya, to har weight lagbhag same size ka kadam leta hai (~lr) — chhota weight 0.2 zero ko cross karke −0.0672 pe pahunch gaya! Isi liye worksheet: "Use AdamW for true weight decay."

PyTorch · worksheet 09 ka note

# L2 inside the loss (Adam ise rescale kar deta hai)
optim.Adam(params, lr=1e-3, weight_decay=1e-4)

# true (decoupled) weight decay
optim.AdamW(params, lr=1e-3, weight_decay=1e-2)

Exam Radar

  • L2 = Ridge = weight decay. Penalty λΣw² → gradient +2λw; update w ← (1 − 2ηλ)w − η∇L.
  • λ/2 form: penalty (λ/2)Σw² → gradient +λw; update (1 − ηλ)w − η∇L.
  • PyTorch weight_decay=λ adds λw to the gradient (≡ (λ/2)Σw² in loss for SGD). Default 0 (SGD/Adam/RMSprop), AdamW default 0.01.
  • L2 effect: small, diffuse weights; smooth function; rarely exact zeros. Default regularizer in DL.
  • Ridge closed form: ŵ = (XᵀX + λI)⁻¹Xᵀy.
  • Don't regularize biases; scale features first.
  • Adam + L2 ≠ weight decay (penalty gets rescaled). AdamW = decoupled weight decay.

Galti mat karna

  • 2λw vs λw: question me penalty λΣw² hai ya (λ/2)Σw² — pehle dekho, phir gradient likho.
  • "L2 weights ko zero kar deta hai" — galat; L2 chhota karta hai, zero L1 karta hai.
  • optim.Adam(..., weight_decay=1e-2) likh ke "maine AdamW use kiya" mat samajhna.

Dimaag me aise baitha lo

L2 = slab wala bijli bill: thoda use free, zyada use bahut mehenga → sab thoda-thoda use karte hain.

19.3 L1 Regularization — Lasso (faltu weights ko seedha zero)

Kahani

Society ka naya rule: har ghar ke har AC pe fixed ₹500 mahina "AC tax", chahe chhota ho ya bada, chahe 1 ghanta chale ya 10. Ab log sochte hain: "Guest room ka AC saal me 2 din chalta hai, uske liye ₹500 mahina? Hata do!" Jo AC sach me kaam ka hai (bedroom wala), wo rehta hai; jo kabhi-kabhi wala hai, wo poora hata diya jaata hai.

L1 yahi karta hai: har weight pe uske size ke hisaab se seedha (linear) fine, aur chhote weights pe bhi same rate. Kamzor weights ke liye fine bharna worth nahi, to wo exactly zero ho jaate hain. Result: sparse model.

Slide: "L1 adds absolute value of magnitude of coefficient as penalty term to the loss function."

Ltotal = Ldata + λ Σi |wi|    →    ∂Ltotal/∂w = gdata + λ · sign(w)
wnew = w − ηgdata − ηλ · sign(w)
  • |w| = absolute value; |4| = |−4| = 4, sign ka farak nahi padta.
  • sign(w) = +1 agar w > 0, −1 agar w < 0 (|w| ka slope). w = 0 pe |w| ka "kona" hai — derivative defined nahi; subgradient koi bhi value [−1, 1] me (PyTorch torch.sign(0) = 0 leta hai).
  • ηλ · sign(w) = har step pe fixed size ka dhakka zero ki taraf, weight chhota ho ya bada. L2 proportional shrink karta hai; L1 constant subtract karta hai.

Chalo numbers se samjhein — penalty value (interactive worksheet)

(a) Ldata = 5, W = −4, λ = 0.1: |−4| = 4 → λ|W| = 0.1 × 4 = 0.4 → Ltotal = 5 + 0.4 = 5.4

(b) W = 2, −3, 1, λ = 0.1, Ldata = 5: |2| + |−3| + |1| = 6 → 0.1 × 6 = 0.6 → Ltotal = 5.6 (L2 me same weights ka penalty 1.4 tha.)

(c) Cost comparison (λ chhod ke): w = 0.1, 1, 10 → L2 cost w² = 0.01, 1, 100; L1 cost |w| = 0.1, 1, 10. L2 chhote weights pe lagbhag kuch charge nahi karta aur bade pe bahut; L1 har unit ka same rate, chhote weights ko bhi nahi chhodta.

Chalo numbers se samjhein — ek GD step L1 ke saath (interactive worksheet)

W = 5, α = 0.1, gdata = 4, λ = 2.

  1. Ordinary GD: 5 − 0.1 × 4 = 4.6
  2. W positive → sign(W) = +1 → L1 gradient λ · sign(W) = 2 × 1 = 2
  3. Total gradient: 4 + 2 = 6
  4. GD + L1: 5 − 0.1 × 6 = 4.4

4.6 vs 4.4: L1 ne extra 0.2 (= αλ) ka dhakka diya. Agar W = −5 hota to sign = −1, dhakka ulta (+ direction), phir bhi zero ki taraf.

Zero tak pahunchne ki race: L2 vs L1

Push ka size (worksheet 09, λ = 0.5, λ/2-form L2 push = λw)

w4210.50.1
L2 push λw210.50.250.05
L1 push λ0.50.50.50.50.5

L2 ka push weight chhota hote hi kamzor padta jaata hai; L1 ka push zero tak same rehta hai.

Walk it to zero (worksheet 09)

Ek bekaar weight (∂L/∂w = 0) w = 1 se start, α = 0.1, λ = 2.5 → αλ = 0.25. L2 (λ/2 form): har step × (1 − 0.25) = × 0.75. L1: har step −0.25, aur 0 pe ruk jao.

Step012348
L210.750.56250.42190.31640.1001
L110.750.500.2500

L2 kabhi zero nahi pahunchta (0.75k hamesha > 0). L1 4 steps me zero aur wahin ruka. Slide ka example bhi yahi: L1 me gradient constant (±1) → w₁: 5 → 0 sirf 10 steps me (har step 0.5); L2 me gradient time ke saath chhota → w₂ ko 5 se 0 ke paas aane me bahut zyada steps.

Soft-thresholding — L1 ka exact jawab

Ek weight ke liye problem: minimize ½(w − z)² + λ|w| (z = bina penalty wala best weight). Iska exact minimizer soft-thresholding hai:

Sλ(z) = sign(z) · max(|z| − λ, 0)
  • |z| > λ: z ko λ se zero ki taraf khiskao (bada weight bachta hai, thoda chhota hoke).
  • |z| ≤ λ: output exactly 0 (kamzor weight khatam).

Chalo numbers se samjhein (regularisation worksheet)

  1. λ = 2, z = 1.5: sign(1.5) · max(1.5 − 2, 0) = 1 · max(−0.5, 0) = 0
  2. λ = 2, z = 3.0: sign(3) · max(3 − 2, 0) = 1 · 1 = 1.0
  3. λ = 2, z = −3.0: sign(−3) · max(3 − 2, 0) = −1

Ridge (L2) ka equivalent z/(1 + λ) hota — kabhi zero nahi. Lasso threshold se neeche sab kuch zero kar deta hai.

Sparsity aur automatic feature selection

Slide: "L1 zeros out coefficients, which leads to a sparse model. L1 can be used for feature (coefficient) selection: unimportant ones have zero coefficients. L2 will produce small values for almost all coefficients." Slide ka example — 4 weights wali layer pe:

L1

0.8, 0, 1, 0 — do weights exactly zero (sparse)

L2

0.3, 0.1, 0.3, 0.2 — sab chhote, sab zinda

Decoy feature example (regularisation worksheet board calculation)

Paanch points, do features; doosra feature decoy (bekaar). XᵀX = [[15, 1], [1, 2]], Xᵀy = [29.4, 2.3].

  1. OLS (no penalty): w = (XᵀX)⁻¹Xᵀy = (1.9483, 0.1759)
  2. Ridge, λ = 2: w = (XᵀX + 2I)⁻¹Xᵀy; [[17, 1], [1, 4]] ka det = 68 − 1 = 67 → w₁ = (4 × 29.4 − 2.3)/67 = 115.3/67 = 1.7209, w₂ = (−29.4 + 17 × 2.3)/67 = 9.7/67 = 0.1448
  3. Lasso, λ = 2: w = (1.8933, 0.0000) (w₂ = 0 rakh ke: w₁ = (29.4 − λ/2)/15 = 28.4/15 = 1.8933)

Ridge ne dono chhote kiye, koi zero nahi. Lasso ne decoy ko exactly 0 kar diya aur asli feature ko kam chheda (1.8933 vs ridge ka 1.7209). "Weak weights become exactly zero. Strong weights lose less than under L2."

PyTorch me L1 (aur manual L2) — loss me khud jodo

PyTorch optimizers me L1 ka koi argument nahi hai, isliye L1 hamesha loss me manually jodte hain. Same tareeke se manual L2 bhi:

PyTorch · manual L1 / L2 penalty in the training loop

# model, criterion, optimizer pehle se bane hain
l1_lambda = 1e-4
l2_lambda = 1e-4

for xb, yb in train_loader:
    optimizer.zero_grad()
    outputs = model(xb)
    loss = criterion(outputs, yb)

    # L1: saare parameters ke |w| ka sum
    l1_penalty = sum(p.abs().sum() for p in model.parameters())
    # L2: saare parameters ke w^2 ka sum
    l2_penalty = sum(p.pow(2).sum() for p in model.parameters())

    loss = loss + l1_lambda * l1_penalty          # L1 chahiye to ye
    # loss = loss + l2_lambda * l2_penalty        # L2 chahiye to ye

    loss.backward()        # autograd khud sign(w) aur 2w wale gradients nikaal lega
    optimizer.step()

# Sirf weights (biases nahi) penalize karne ho to:
l1_weights_only = sum(p.abs().sum() for name, p in model.named_parameters()
                      if 'weight' in name)

Ab dekho L1 aur L2 asal me weights ke saath kya karte hain. 8 input features, lekin sirf pehle 3 kaam ke (true weights 3, −2, 1.5, baaki 0):

PyTorch · L1 vs L2 on a model with 5 useless features

import torch
import torch.nn as nn
import torch.optim as optim

torch.manual_seed(0)
X = torch.randn(200, 8)                                   # 8 input features
true_w = torch.tensor([3.0, -2.0, 1.5, 0, 0, 0, 0, 0])    # sirf pehle 3 kaam ke
y = (X @ true_w + 0.5 * torch.randn(200)).unsqueeze(1)    # (200,) -> (200, 1)

def fit(penalty, lam):
    torch.manual_seed(1)
    model = nn.Linear(8, 1)
    optimizer = optim.SGD(model.parameters(), lr=0.05)
    criterion = nn.MSELoss()
    for epoch in range(500):
        optimizer.zero_grad()
        loss = criterion(model(X), y)
        if penalty == "L1":
            loss = loss + lam * model.weight.abs().sum()       # lam * sum|w|
        elif penalty == "L2":
            loss = loss + lam * model.weight.pow(2).sum()      # lam * sum w^2
        loss.backward()
        optimizer.step()
    return model.weight.data.squeeze()

for penalty in ["none", "L1", "L2"]:
    w = fit(penalty, lam=0.1)
    print(f"{penalty:4s}:", [round(v, 3) for v in w.tolist()])

Output

none: [3.044, -1.952, 1.472, -0.053, -0.02, 0.007, -0.005, 0.001]
L1  : [2.982, -1.899, 1.42, -0.002, 0.003, -0.002, 0.001, 0.001]
L2  : [2.709, -1.746, 1.34, -0.053, -0.024, 0.029, 0.0, 0.012]

Ek aur slide point — robustness: L1 > L2. Squared cheez me bade outlier ka cost bahut tezi se (slide ne "exponentially" likha; asal me quadratically) badhta hai, absolute me linearly. Isliye squared form outliers pe zyada mehnat (zyada react) karta hai → kam robust. (Ye baat loss functions MSE vs MAE pe bhi same lagti hai — Chapter 11.)

Exam Radar

  • L1 = Lasso: penalty λΣ|w|; gradient λ·sign(w) (constant size); update w − ηg − ηλ·sign(w).
  • L1 → sparsity: exact zeros → feature selection, compression. L2 → small but non-zero.
  • |w| non-differentiable at 0; subgradient ∈ [−1, 1].
  • Soft-thresholding: Sλ(z) = sign(z)·max(|z| − λ, 0).
  • PyTorch me L1 ke liye optimizer argument nahi; loss me sum(p.abs().sum() for p in model.parameters()).
  • Robustness to outliers: L1 > L2. Sparsity: L1 > L2.

Galti mat karna

  • L1 ka gradient λw nahi, λ·sign(w) hai — weight ke size pe depend nahi karta.
  • Correlated features me Lasso aksar ek ko chunta hai aur doosre ko zero karta hai (dono ko barabar nahi rakhta).

Dimaag me aise baitha lo

L2 divide karta hai (sab chhote, koi zero nahi). L1 subtract karta hai (kamzor seedha zero).

19.4 Geometry — L2 ka circle, L1 ka diamond

Regularization ko ek budget ki tarah bhi soch sakte ho: "data loss minimize karo, lekin weights ek budget ke andar rahein". L2 me budget w₁² + w₂² ≤ t² — ek circle (disk). L1 me |w₁| + |w₂| ≤ t — ek diamond jiske kone (corners) axes pe hain.

Data loss ke contours (barabar loss wali lines) unregularized best point ŵ ke around ellipses hain. Ellipse ko dheere-dheere bada karte jao; jahan wo pehli baar budget region ko chhooti hai, wahi regularized solution hai.

w₁ w₂ ŵ (no reg) (0.41, 1.13) L2 (Ridge): circle w₁ w₂ ŵ (no reg) (0, 1.20): w₁ = 0! L1 (Lasso): diamond
Same loss (ŵ = (1.4, 2.5)), same budget t = 1.2. L2 circle ko ellipse off-axis chhooti hai: (0.41, 1.13) — dono weights zinda. L1 diamond ko kone (0, 1.2) pe chhooti hai: w₁ exactly 0. Laal ellipses = loss contours, peela = allowed region, kaala dot = touch point.

Dimaag me aise baitha lo

Gol roti ko ellipse kahin bhi chhoo le; samosa (diamond) ko pehle uska kona hi chubhta hai — aur kona axis pe hai, matlab ek weight zero.

19.5 L1 vs L2 — poori comparison, Elastic Net, kab kya

FeatureL1 (Lasso)L2 (Ridge / weight decay)
PenaltyλΣ|w|λΣw² (ya (λ/2)Σw²)
UsesAbsolute weightSquared weight
Gradient of penaltyλ·sign(w) — constant size2λw (ya λw) — proportional to w
Update ka asarFixed amount subtractFixed fraction multiply (decay)
Exact zerosHaan, common (sparse)Lagbhag kabhi nahi
Feature selectionHaan (automatic)Nahi
Weights ka patternKuch bade, baaki 0 (0.8, 0, 1, 0)Sab chhote, spread (0.3, 0.1, 0.3, 0.2)
Constraint shapeDiamondCircle / sphere
Differentiable at 0?Nahi (kona)Haan (smooth)
Closed formNahi (soft-thresholding / iterative)Haan: (XᵀX + λI)⁻¹Xᵀy
Outlier robustness (slide)Zyada robustKam robust
Correlated featuresEk ko chunta, baaki zeroCredit baant deta hai
Tuning"Cliff" — thoda λ badla, bahut weights achanak zeroGradual, aasaan
PyTorchLoss me manual: p.abs().sum()weight_decay= (ya manual p.pow(2).sum())
Deep learning meSpecific jobs: first-layer feature selection, compressionDefault

Elastic Net = L1 + L2 dono ek saath: L + λ₁Σ|w| + λ₂Σw². Jab thoda sparsity bhi chahiye aur correlated features me credit bhi baantna hai (Lasso akela ek ko chunta hai; elastic net group ko saath rakhta hai).

Kab kya use karein? (worksheet 09, 3.6)

Dimaag me aise baitha lo

L1 = "kaun zaroori hai?" chunta hai. L2 = "sab thoda-thoda" rakhta hai. Elastic Net = dono ka mix.

19.6 λ ka asar — kitna regularization?

λ ek dial hai. λ = 0 → koi regularization nahi (overfit ka khatra). λ bahut bada → model sirf weights chhote karne me laga, data ignore → underfit. Beech me kahin sweet spot. Bishop PRML fig. 1.7 bhi yahi dikhata hai: "Large λ underfits; small λ overfits."

Chalo numbers se samjhein — degree-9 polynomial + ridge, λ sweep (numpy se compute kiya)

12 noisy points (sin(2πx) + noise σ = 0.25), degree-9 polynomial (10 coefficients), ridge penalty λΣw², 300 validation points:

λTrain MSEVal MSELargest |w|Diagnosis
00.02200.215728171.95Overfit (bade weights!)
1e-60.05610.1353247.31Kam overfit
1e-40.10990.114415.39Achha
1e-30.12290.11369.57Best (sweet spot)
1e-20.18020.15213.44Thoda underfit
1e-10.21440.20121.10Underfit
10.29290.33230.45Underfit
100.40020.48780.09Bahut underfit (curve lagbhag flat)

Training MSE λ ke saath hamesha badhta hai; validation MSE pehle girta (0.2157 → 0.1136) phir badhta (→ 0.4878). Largest weight 28172 se 0.09 tak — λ jitna bada, weights utne chhote. Wahi U-curve, bas x-axis pe ab capacity ki jagah λ (ulta direction).

Galti mat karna

  • "Zyada regularization = hamesha behtar" — nahi. Underfit model me regularization kam karna hai (18.2).

Dimaag me aise baitha lo

λ = namak. Zero: pheeka overfit. Mutthi bhar: kadwa underfit. Chutki bhar: perfect.

19.7 Early Stopping — ratta shuru hone se pehle ruk jao

Kahani

Maa chai bana rahi hai. Har minute ek chammach se chakh ke dekhti hai. Pehle 4 minute chai har baar behtar hoti gayi. 5th minute pe ekdum perfect. 6th minute pe thodi kadwi, 7th pe aur kadwi. Maa samajhdaar hai: "Ek minute ki kadwahat se ghabraana nahi, kabhi-kabhi chakhne me galti ho jaati hai. 3 minute tak sudhaar nahi hua to gas band." Aur agar unke paas magic hota, to wo 5th minute wali chai ka "photo" save karke wahi wapas parosti!

Yahi early stopping hai: validation loss = chai ka swaad, patience = 3 minute ka intezaar, best weights save + restore = perfect chai ki photocopy.

Course slide: "There is a point during training a large neural net when the model will stop generalizing (performing well on test/validation sets) and only focus on learning the statistical noise in the training dataset. Solution: stop whenever generalization error increases."

Definition

Early stopping: har epoch ke baad validation loss naapo. Jab tak gir raha hai, training chalu rakho. Jab wo sudharna band kar de (lagatar patience epochs tak koi naya best nahi), training rok do aur sabse kam validation loss wale epoch ke weights wapas load karo.

Formally: θ̂ = θt*, jahan t* = arg mint Lval(θt).

Training ke do phase hote hain (worksheet 09): pehle model pattern seekhta hai — dono losses girte hain. Phir noise yaad karne lagta hai — training loss girta rehta hai, validation loss rukta aur badhne lagta hai. Early stopping in dono phases ki seema (border) pe rukta hai. Worksheet lab me ye border epoch 73 pe tha (unregularised run, lowest validation loss).

Pehle hi bure epoch pe kyun nahi rukte? Patience

Validation loss noisy hota hai — ek epoch thoda kharab aane ka matlab ye nahi ki memorization shuru ho gaya. Pehle hi rise pe ruke to aksar bahut jaldi ruk jaoge. Isliye intezaar karte hain. Patience = lagatar kitne epochs bina naye best ke bardaasht karenge. Jab bhi naya best aaye, weights save karo aur counter 0 kar do.

Chalo numbers se samjhein — poora epoch table (worksheet 09, figure 4.2, patience = 3)

12 epochs ke validation losses: 0.90, 0.72, 0.61, 0.55, 0.52, 0.53, 0.50, 0.51, 0.52, 0.54, 0.53, 0.56.

EpochVal lossBest so farBest epochPatience counterAction
10.900.9010Naya best → save
20.720.7220Naya best → save
30.610.6130Naya best → save
40.550.5540Naya best → save
50.520.5250Naya best → save
60.530.5251Sudhaar nahi (0.53 ≥ 0.52)
70.500.5070Naya best → save, counter reset
80.510.5071Sudhaar nahi
90.520.5072Sudhaar nahi
100.540.5073Counter = patience → STOP
11, 12—Kabhi train hi nahi hue
  1. Training epoch 10 ke baad rukti hai. Model me abhi epoch 10 ke weights hain (val loss 0.54).
  2. Saved best = epoch 7 (val loss 0.50) → load_state_dict se wapas.

Patience 3 → stop after epoch 10, restore epoch 7 (asli minimum). Patience 1 → epoch 6 pe hi ruk jaata (0.53 > 0.52), restore epoch 5 — jaldi haar maan li, epoch 7 miss. Patience 5 → bhi epoch 7 hi milta (stop after epoch 12), bas 2 extra epochs ka kharcha.

0.5 0.6 0.7 0.8 0.9 1 2 3 4 5 6 7 8 9 10 11 12 best: epoch 7 (0.50), weights SAVE patience counter 1, 2, 3 STOP after epoch 10 restore epoch 7 kabhi trainnahi hue epoch → (validation loss, patience = 3) val loss
Worksheet ka noisy validation loss. Epoch 7 = best (save). Grey zone = 3 epochs ka intezaar (counter 1, 2, 3). Epoch 10 ke baad stop, epoch 7 ke weights restore. Epoch 6 ka chhota sa rise patience ki wajah se ignore hua.

Ek aur (regularisation worksheet ka check)

Patience 2, validation losses: 0.80, 0.60, 0.62, 0.59, 0.61, 0.63.

  1. Epoch 1: 0.80 best. Epoch 2: 0.60 best. Epoch 3: 0.62 → counter 1.
  2. Epoch 4: 0.59 < 0.60 → naya best, counter 0.
  3. Epoch 5: 0.61 → counter 1. Epoch 6: 0.63 → counter 2 = patience → stop.

Stops after epoch 6, restores epoch 4 (0.59).

Interactive worksheet ka chhota table (train loss, val loss): epoch 1 (0.90, 0.95), 2 (0.70, 0.72), 3 (0.55, 0.58), 4 (0.43, 0.50) ← best validation, 5 (0.35, 0.56), 6 (0.29, 0.64). Epoch 4 se 6 tak training loss girta raha, validation badhta raha — "lower training loss does not always mean better model". Checkpoint: epoch 4.

Hamesha best weights restore karo

Jab training rukti hai, model me aakhri epoch ke weights hote hain, best wale nahi (upar: epoch 10 ka 0.54, na ki epoch 7 ka 0.50). Saved weights wapas daalne padte hain. Keras me EarlyStopping(monitor="val_loss", patience=20, restore_best_weights=True) — dhyaan do, restore_best_weights ka default False hai (slide: Keras implementation me BEST_WEIGHT save karne ka option hai). PyTorch me koi built-in nahi; khud likhte hain:

PyTorch · early stopping loop (worksheet + Lab 14 style)

import copy
import torch
import torch.nn as nn
import torch.optim as optim
# X_tr, y_tr, X_va, y_va aur make_mlp() 18.10 wale demo se

torch.manual_seed(0)
model = make_mlp([256, 256, 256, 256])        # wahi bada model jo overfit karta hai
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(model.parameters(), lr=0.003)

best_val_loss = float('inf')   # ab tak ka sabse kam val loss
best_state = None              # us waqt ke weights ki photocopy
best_epoch = 0
patience = 20                  # kitne epoch bina sudhaar ke bardaasht karenge
wait = 0                       # patience counter

for epoch in range(1, 401):
    model.train()
    optimizer.zero_grad()
    loss = criterion(model(X_tr), y_tr)
    loss.backward()
    optimizer.step()

    model.eval()
    with torch.no_grad():
        val_loss = criterion(model(X_va), y_va).item()

    if val_loss < best_val_loss:          # naya record?
        best_val_loss = val_loss
        best_epoch = epoch
        best_state = copy.deepcopy(model.state_dict())   # weights save
        wait = 0                          # counter reset
    else:
        wait += 1                         # ek aur bekaar epoch
        if wait >= patience:
            print(f"Stopping at epoch {epoch}: no improvement for {patience} epochs")
            break

model.load_state_dict(best_state)         # best weights wapas daalo
model.eval()
with torch.no_grad():
    val_loss = criterion(model(X_va), y_va).item()
    val_acc = ((model(X_va) > 0).float() == y_va).float().mean().item()
print(f"Restored epoch {best_epoch} | val loss {val_loss:.4f} | val acc {val_acc:.3f}")

Output

Stopping at epoch 50: no improvement for 20 epochs
Restored epoch 30 | val loss 0.2562 | val acc 0.912

Early stopping regularization kyun hai?

Pros (slide)

Bahut simple. Lagbhag free (validation pe ek forward pass per epoch). "Highly recommended to use for all training along with other techniques" — safety net. Compute bhi bachata hai.

Cons (slide)

"May not work so well if the test set accuracy inflection point is low enough." Validation set ab model choose karne me use ho gaya, to final score ke liye alag test set chahiye. Noisy val loss → patience tune karni padti hai. (Epoch-wise double descent me pehle minimum pe ruk sakta hai.)

Exam Radar

  • Monitor validation loss/accuracy each epoch; stop when no improvement for patience epochs; restore best weights.
  • t* = arg mint Lval(θt) (lab: epoch 80; worksheet: epoch 7 with patience 3).
  • Patience too small → stops too early (noise). Too large → wasted epochs (but same best if restore on).
  • Keras restore_best_weights default = False. PyTorch: manual with copy.deepcopy(model.state_dict()) + load_state_dict.
  • Early stopping ≈ L2 (limits weight growth / effective capacity); "deletes training time".
  • Never use test set to pick the stopping epoch.

Galti mat karna

  • best_state = model.state_dict() (bina deepcopy) — classic bug.
  • Stop ke baad restore bhool jaana → last (kharab) weights ship ho jaate hain.
  • Training loss pe early stopping — bekaar, wo to girta hi rahega.

Dimaag me aise baitha lo

Chai chakhte raho; 3 baar lagatar behtar na ho to gas band, aur best wali chai ki photocopy paroso.

19.8 Data Augmentation aur baaki regularizers

Kahani

Chhote bhai ko "billi" pehchaanna sikhana hai, lekin ghar me billi ki sirf 5 photos hain. Tum smart ho: wahi photo thodi ghuma ke dikhate ho, thodi zoom karke, mirror me ulti karke, thodi left khiska ke. Ab 5 photos se 50 "nayi" photos ban gayi, aur bhai seekh gaya ki billi ulti ho ya tedhi, billi hi hai. Usne kisi ek photo ki detail (jaise "billi hamesha left me baithti hai") ratt nahi li.

Worksheet: "More data: the best regulariser of all." Asli naya data mehenga hai, isliye data augmentation — existing training images pe label-preserving random transformations lagao, taaki model ko har epoch thoda alag version dikhe. Ye effective dataset size badhata hai aur model ko invariances (rotation, shift, flip) sikhata hai. Course slide: regularization ka doosra type = "Data augmentation: increase the size of training data" (slide "Increase the data").

PyTorch · torchvision transforms for augmentation (Labs 12–14 style)

import torch
from torchvision import transforms
from PIL import Image
import numpy as np

# Training ke liye: random badlaav (har epoch naya version)
train_transform = transforms.Compose([
    transforms.RandomHorizontalFlip(p=0.5),          # 50% chance left-right flip
    transforms.RandomRotation(degrees=10),           # -10 se +10 degree random ghumao
    transforms.RandomAffine(degrees=8, translate=(0.08, 0.08), scale=(0.95, 1.05)),
    transforms.ToTensor(),                           # PIL (H, W) 0-255 -> tensor (C, H, W) 0-1
    transforms.Normalize((0.5,), (0.5,)),            # [0, 1] -> [-1, 1]
])

# Validation/test ke liye: koi randomness nahi
eval_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,)),
])

img = Image.fromarray(np.random.default_rng(0).integers(0, 256, (28, 28), dtype=np.uint8))  # dummy 28x28 grayscale
torch.manual_seed(0)
a = train_transform(img)
b = train_transform(img)
c = eval_transform(img)
print(a.shape, c.shape)
print("two augmented copies equal?", torch.equal(a, b))
print("eval twice equal?", torch.equal(eval_transform(img), c))
print("range:", round(c.min().item(), 3), round(c.max().item(), 3))

Output

torch.Size([1, 28, 28]) torch.Size([1, 28, 28])
two augmented copies equal? False
eval twice equal? True
range: -1.0 1.0

Galti mat karna

  • Augmentation sirf training pe. Validation/test transform me randomness nahi (Lab 14 ka test: "Evaluation transform must not be random").
  • Label-preserving hona chahiye: digits (SVHN, MNIST) ko horizontal flip mat karo — mirror kiya "2" ya "7" asli digit nahi rehta. Lab 14 SVHN: "Do not use horizontal flips; mirrored digits are not label-preserving." Billi ki photo flip karo — theek; "6" ko 180° ghumao to "9" ban jaata hai — galat!

Baaki regularizers ek nazar me

Regularisation worksheet ka one-line map: L1 deletes weights. L2 shrinks weights. Dropout deletes activations (at train). BN re-scales activations. Early stopping deletes training time.

Dimaag me aise baitha lo

Data kam ho to photo ko ghumao-khiskao-zoom karo — lekin "6" ko "9" mat banao.

19.9 Lab 9 — overfit MyNN ko regularize karo (aur sab kuch ek saath)

Lab 9 ka setup: Fashion-MNIST (CSV, 60,000 rows × 785 columns: label + 784 pixels), 80/20 split, pixels /255, batch 32. Baseline MyNN (784 → 128 → 64 → 10) overfit karta hai: ~98% train vs ~89% test. Phir "keep everything the same" (same widths, lr = 0.1, 100 epochs, same loop) aur sirf regularization badlo: har Linear ke baad nn.BatchNorm1d, har ReLU ke baad nn.Dropout(0.3), aur optimizer me weight_decay=1e-4.

PyTorch · Lab 9 · baseline vs regularized MyNN

import torch
import torch.nn as nn
import torch.optim as optim

# ---------- Step 1: baseline (no regularization) ----------
class MyNN(nn.Module):
    def __init__(self, num_features):
        super().__init__()
        self.model = nn.Sequential(
            nn.Linear(num_features, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 10)                  # 10 classes ke raw logits
        )

    def forward(self, x):
        return self.model(x)

# ---------- Step 2: SAME class + BatchNorm + Dropout ----------
class MyNNReg(nn.Module):
    def __init__(self, num_features):
        super().__init__()
        self.model = nn.Sequential(
            nn.Linear(num_features, 128),
            nn.BatchNorm1d(128),               # BatchNorm (Chapter 20)
            nn.ReLU(),
            nn.Dropout(0.3),                   # Dropout (Chapter 20)
            nn.Linear(128, 64),
            nn.BatchNorm1d(64),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        return self.model(x)

learning_rate = 0.1
epochs = 100
model = MyNNReg(784).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate, weight_decay=1e-4)   # L2

for epoch in range(epochs):
    model.train()                      # Dropout ON, BatchNorm batch-stats use/update
    total_epoch_loss = 0
    for batch_features, batch_labels in train_loader:
        batch_features, batch_labels = batch_features.to(device), batch_labels.to(device)
        outputs = model(batch_features)
        loss = criterion(outputs, batch_labels)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_epoch_loss += loss.item()
    if (epoch + 1) % 10 == 0:
        print(f'Epoch: {epoch + 1} , Loss: {total_epoch_loss/len(train_loader):.4f}')

model.eval()                           # Dropout OFF, BatchNorm running stats
correct, total = 0, 0
with torch.no_grad():
    for batch_features, batch_labels in test_loader:
        batch_features, batch_labels = batch_features.to(device), batch_labels.to(device)
        _, predicted = torch.max(model(batch_features), 1)   # har row ka max-logit class
        total += batch_labels.shape[0]
        correct += (predicted == batch_labels).sum().item()
print(f'test: {correct/total:.4f}')

Output (Lab 9 notebook ke asli logs)

Baseline (no regularization):
Epoch: 10 , Loss: 0.2747     ...     Epoch: 100 , Loss: 0.0621
train: 0.9825 , test: 0.8898          <- gap ≈ 0.093 (overfit)

Regularized (BN + Dropout 0.3 + weight_decay 1e-4):
Epoch: 10 , Loss: 0.3660     ...     Epoch: 100 , Loss: 0.2592

Ek page comparison (worksheet 09, 6.4)

L2L1Early stoppingDropout
Kya karta haiSaare weights proportionally shrinkFixed amount subtract; kuch weights 0Noise seekhne se pehle ruk jaata haiRandom neurons band
BoundarySmoothSmooth, bekaar inputs ignorePehle wala, simpler epochSmooth, robust
Dialλ (weight_decay)λpatiencerate p
Dhyaan rakhoAdamW use karo; features scale karoHidden layers pe kam madad; cliffBest weights restore karoZyada epochs; chhote network ko hurt
PyTorchweight_decay=1e-4loss + lam*sum(p.abs().sum() ...)deepcopy(state_dict()) + load_state_dictnn.Dropout(0.3)

Practitioner ka recipe (worksheet 09, 6.2)

  1. Pehle aisa model banao jo training data fit kar sake (overfit bhi kar sake). Agar training data hi fit nahi hota, regularization se kuch nahi hoga.
  2. Early stopping + best-weight restore as a safety net.
  3. Ek add karo: L2 (AdamW ke saath) ya dropout. Log scale pe tune karo, coarse phir fine.
  4. Har change ke baad curves aur gap dobara padho.
  5. Test score ek baar, bilkul end me.

Worksheet Lab 6 ke rough numbers (train acc / val loss / val acc): nothing 100 / 1.59 / 80; patience 50 only 88 / 0.47 / 85; dropout 0.5 only 88 / 0.78 / 87; L2 0.03 + dropout 0.5 → 80 / 0.53 / 77 (underfitting — do regularizers ne zyada dabaa diya). Defaults by architecture (regularisation worksheet): residual conv net → batch norm on, weight decay ~1e-4, early stopping, dropout sirf agar ab bhi overfit; linear model with correlated features → lasso / elastic net; transformer → LayerNorm, residual dropout, weight decay, early stopping.

Exam Radar

  • Lab 9: nn.BatchNorm1d after Linear, nn.Dropout(0.3) after ReLU, optim.SGD(..., lr=0.1, weight_decay=1e-4).
  • Baseline gap: 0.9825 train vs 0.8898 test.
  • Regularized model's training loss is higher — normal.
  • Regularization goal = smaller generalization gap, not lower training loss.

Dimaag me aise baitha lo

Pehle model ko taakat do (fit kare), phir lagaam do (generalize kare) — aur lagaam ek-ek karke kaso.

Chapter 20

Dropout aur Batch Normalization — neurons ki chhutti, marks ka scaling

Co-adaptation · Dropout mask · Inverted dropout (1/(1−p)) · Ensemble of thinned networks · model.train() / model.eval() · Internal covariate shift · μB, σ²B, γ, β · Running stats · BatchNorm1d / BatchNorm2d · LayerNorm

20.1 Problem kya hai? Neurons ek-doosre ke bharose baith jaate hain

Kahani

Socho tumhari gully cricket team me ek star batsman hai, Raju. Har match me baaki sab sochte hain, "Raju hai na, wo sambhal lega." Koi aur batting practice seriously nahi karta. Jis din Raju bimaar pada, poori team 20 runs pe all-out. Team ek insaan pe depend ho gayi thi.

Ab coach ek naya rule banata hai: har practice session me coach random se kuch players ko bench pe bitha dega. Aaj Raju bench pe, kal Sonu, parso Pinky. Kisi ko pata nahi kal kaun khelega. Result? Har player ko khud batting, bowling, fielding seekhni padti hai, kyunki partner kabhi bhi gayab ho sakta hai. Match ke din (test time) coach poori team utaarta hai, aur ab team zyada strong hai, kyunki har player akele bhi kaam ka hai.

Group project me bhi yahi hota hai: agar pata ho ki kisi bhi din koi bhi member absent ho sakta hai, to har member ko poora project samajhna padta hai. Bas yahi Dropout ka idea hai.

Yaad hai Chapter 18 ka overfitting? Model training data ko ratta maar leta hai, noise tak yaad kar leta hai, aur naye data pe fail ho jaata hai. Chapter 19 me humne iske teen ilaaj dekhe: L2 (bade weights pe fine), L1 (faltu weights ko zero), aur early stopping (ratta shuru hone se pehle ruk jao). Course ki slide me regularization ki definition ye thi:

Definition (lecture slide, Goodfellow)

Regularization is any modification we make to a learning algorithm that is intended to reduce its generalization error but not its training error. Matlab: training pe thoda kam perfect chalega, lekin naye data (test) pe zyada achha chalega.

Overfit model ke andar ek khaas cheez hoti hai. Course ki worksheet (L8 Part 2) kehti hai: training data ke ek noisy point ke around "island" banane ke liye network kai neurons ko milkar ek bahut nazuk, finely-balanced combination banana padta hai. Ek neuron doosre ki galti ko cancel karta hai, doosra teesre ki. Is aapsi nirbharta ko kehte hain co-adaptation. Ye combination training data pe perfect hai, lekin bahut fragile hai: naya data aaya, balance toot gaya.

Definition

Co-adaptation = jab neurons akele koi useful feature nahi seekhte, balki ek-doosre ki quirks (khaas aadaton) pe depend karke sirf group me kaam karte hain. Ye overfitting ka ek bada kaaran hai.

Is chapter me do tools hain:

Teacher ki notes ka ek-line map yaad rakhna: L1 weights delete karta hai. L2 weights shrink karta hai. Dropout training me activations delete karta hai. BN activations ko re-scale karta hai. Early stopping training time delete karta hai.

20.2 Dropout ka mechanism — har step pe naya random "bench"

Dropout ko Srivastava, Hinton, Krizhevsky, Sutskever aur Salakhutdinov ne paper "Dropout: A Simple Way to Prevent Neural Networks from Overfitting" (JMLR, 2014) me formally pesh kiya (Hinton ke group ka pehla version 2012 me aaya tha). Idea bahut simple hai:

  1. Har training step (har mini-batch) pe, ek layer ke har neuron ke liye ek sikka uchhalo.
  2. Probability p se wo neuron drop (output = 0), warna keep.
  3. Agle step pe naya random set drop hoga. Ek hi neuron kabhi on, kabhi off.
  4. Dropped neuron ka us step me agli layer pe koi asar nahi, aur backward pass me usme se gradient bhi nahi guzarta (gradient = 0), to us step me uske incoming weights update nahi hote (plain SGD me; lecture slide: "Dropped neurons are not updated in backward training").
  5. Test time pe: koi dropout nahi. Saare neurons on. (Weights delete nahi hote, sirf us step ke liye activation zero hoti hai.)

Sikka uchhalne ko math me Bernoulli random variable kehte hain: ek variable m jo 1 ya 0 hota hai. Har neuron ka apna m. In sab m ki list ko dropout mask kehte hain.

mi ~ Bernoulli(1 − p)   ·   h̃i = mi · hi  (abhi scaling ke bina)
  • hi = neuron i ka asli output (activation ke baad)
  • mi = mask: 1 matlab "khelega", 0 matlab "bench pe"; 1 aane ki probability (1 − p)
  • p = drop probability (PyTorch ka nn.Dropout(p=0.5) wala p), default 0.5
  • h̃i = dropout ke baad jo value agli layer ko jaati hai

Galti mat karna: p ka matlab har jagah alag hai!

  • PyTorch nn.Dropout(p): p = probability of an element to be zeroed (drop). p=0.3 matlab 30% band, 70% chalu.
  • Course worksheet L8 Part 2: r = dropout rate (drop probability), scale = 1/(1 − r). Ye PyTorch ke p jaisa hi hai.
  • Course ki Regularisation lecture sheet aur original paper (Srivastava et al.): p = keep probability! Wahan formula h̃ = (m ⊙ h)/p likha hai aur "typical keep probability p ≈ 0.5 on hidden units, 0.8–0.9 on inputs".
  • MCQ me hamesha dekho: "probability of dropping" ya "probability of retaining/keeping"? Keep = 1 − drop. Keep probability 0.8 = drop rate 0.2.

20.3 Scaling ki problem aur Inverted Dropout

Kahani (cricket continued)

Practice me aadhe players bench pe hain, to jo 5–6 khel rahe hain wo mil ke ~100 runs banate hain. Match ke din poore 11 utarte hain to ~200 runs. Agar team ne practice ke "100 runs" ko normal maan liya tha, to match ka 200 usko bilkul alag duniya lagega. Coach ka jugaad: practice me jo khel rahe hain unke runs ko double gino (kyunki aadhe hi khel rahe hain). Ab practice ka "scaled" score bhi ~200, match ka bhi ~200. Dono duniya same scale pe.

Yahi problem neurons me hai. Agli layer apne input ka ek typical size seekh leti hai. Agar training me aadhe neurons off hain to agli layer ko lagbhag aadha signal milta hai, lekin test pe sab on hain to double signal. Train aur test ka mismatch. Ilaaj: training ke time bache hue (kept) neurons ko 1/(1 − p) se multiply karo. Isko inverted dropout kehte hain, aur PyTorch exactly yahi karta hai.

Training:  h̃i = mi · hi1 − p      Test:  h̃i = hi (kuch nahi badla)
  • p = drop probability (PyTorch), to (1 − p) = keep probability
  • mi ∈ {0, 1}, P(mi = 1) = 1 − p
  • 1/(1 − p) = scale factor: p = 0.5 → ×2; p = 0.2 → ×1.25; p = 0.25 → ×1.3333; p = 0.3 → ×1.4286
  • Kyun 1/(1 − p)? Taaki expected value (average) training me bhi utni hi rahe jitni test pe: E[h̃i] = (1 − p) · hi1 − p + p · 0 = hi

Chalo numbers se samjhein

Sawaal: Ek hidden layer ke 4 neurons ke activations hain h = [2, 4, 6, 8]. Dropout p = 0.5. Is step ka random mask nikla m = [1, 0, 1, 0]. Inverted dropout ke baad output kya hoga? Aur expected value check karo.

  1. Scale factor: 1/(1 − p) = 1/(1 − 0.5) = 1/0.5 = 2
  2. Mask lagao (element-wise multiply): m ⊙ h = [1×2, 0×4, 1×6, 0×8] = [2, 0, 6, 0]
  3. Survivors ko scale karo: [2×2, 0×2, 6×2, 0×2] = [4, 0, 12, 0]
  4. Is ek step ka sum: 4 + 0 + 12 + 0 = 16, jabki asli sum 2 + 4 + 6 + 8 = 20. Ek step me barabar nahi! Dropout noisy hai, aur yahi noise regularization karta hai.
  5. Expected value, neuron 1 ke liye: E[h̃₁] = P(keep) × (2/0.5) + P(drop) × 0 = 0.5 × 4 + 0.5 × 0 = 2 = asli h1. Neuron 4: 0.5 × 16 + 0.5 × 0 = 8. Har neuron ke liye average = asli value.
  6. Python se check kiya: p = 0.5 pe saare 24 = 16 possible masks barabar probable hain; un 16 outputs ka average exactly [2, 4, 6, 8] aata hai.

Jawab: is step ka output [4, 0, 12, 0]; average over masks = [2, 4, 6, 8] = test-time output. Train aur test "on average" match.

Teacher ki board calculation (Regularisation sheet, keep prob 0.5)

Wahan h = (1.2, −0.4, 2.0, 0.8), mask m = (1, 0, 1, 0), aur formula h̃ = m h/p jahan p = keep probability = 0.5.

  1. Unit 1: (1)(1.2)/0.5 = 2.4
  2. Unit 2: (0)(−0.4)/0.5 = 0
  3. Unit 3: (1)(2.0)/0.5 = 4.0
  4. Unit 4: (0)(0.8)/0.5 = 0

Jawab: (2.4, 0, 4.0, 0). "Kept units double." Test pe wahi layer (1.2, −0.4, 2.0, 0.8) aage bhejegi, bina mask, bina 1/p.

Ek aur worksheet sawaal: dropout rate 0.25, kept neuron ka output 2.0 → training me 2.0 / (1 − 0.25) = 2.0 / 0.75 = 2.6667 (option 2.67). Dropped hota to 0.

Training, no scaling 1 off 1 off Σ Σ = 2 aadha neuron, aadha sum Test time (sab on) 1 1 1 1 Σ Σ = 4 Training, × 1/(1−p) = × 2 2 off 2 off Σ Σ = 4 agli layer yahi expect karti hai survivors ×2, sum match
Course worksheet (L8 Part 2, Fig 5.3) wala scene: chaar neurons, har ek ka output 1.0, drop rate 0.5. Bina scaling ke training me agli layer ko ~2 milta hai par test pe 4. Inverted dropout me bache hue neurons ko 1/(1 − p) = 2 se multiply karte hain, to training ka sum bhi 4: train aur test match.

Ab PyTorch me dekho. nn.Dropout(p=0.5, inplace=False) ek layer (module) hai. Arguments: p = har element ke zero hone ki probability (default 0.5, range 0 se 1); inplace = True karoge to input tensor ko hi badal dega (memory bachti hai, default False). Training mode me ye mask lagata hai aur survivors ko 1/(1 − p) se multiply karta hai; eval mode me input ko jaisa ka taisa lauta deta hai.

PyTorch · nn.Dropout train vs eval

import torch
import torch.nn as nn

torch.manual_seed(25)
drop = nn.Dropout(p=0.5)          # p = DROP hone ki probability
x = torch.tensor([2.0, 4.0, 6.0, 8.0])

drop.train()                      # training mode (naya module default me train mode me hi hota hai)
print("train  :", drop(x))        # mask lagega, survivors x 2
print("train  :", drop(x))        # dobara chalao -> NAYA random mask

drop.eval()                       # evaluation / test mode
print("eval   :", drop(x))        # koi dropout nahi, koi scaling nahi

Output

train  : tensor([ 4.,  0., 12.,  0.])
train  : tensor([ 0.,  8.,  0., 16.])
eval   : tensor([2., 4., 6., 8.])

Dekho: pehli call me exactly hamara worked example aaya (mask [1, 0, 1, 0] → [4, 0, 12, 0]). Doosri call me naya mask [0, 1, 0, 1] → [0, 8, 0, 16]. Survivors hamesha ×2 hain. Eval me input bilkul same.

PyTorch · expected value ka proof, 10 lakh neurons pe

import torch
torch.manual_seed(1)
drop = torch.nn.Dropout(p=0.3)
x = torch.ones(1_000_000)                      # 10 lakh neurons, sab ka output 1
y = drop(x)                                    # train mode
print("zero fraction :", (y == 0).float().mean().item())   # ~0.3 drop hue
print("survivor value:", y[y != 0][0].item())  # 1/(1-0.3) = 1.4286
print("mean of output:", y.mean().item())     # ~1.0 = input ka mean, scaling ka kamaal

Output

zero fraction : 0.30008599162101746
survivor value: 1.4285714626312256
mean of output: 0.9998772740364075

20.4 Purana tareeka: test time pe weights ko (1 − p) se scale karo

Original paper me scaling test time pe hoti thi, training pe nahi. Isko "standard" (non-inverted) dropout kehte hain:

Same example, purane tareeke se

  1. Training (mask [1, 0, 1, 0], no scaling): [2, 0, 6, 0]; expected value per neuron = 0.5 × h = [1, 2, 3, 4]
  2. Test (sab on, × (1 − p) = × 0.5): [2, 4, 6, 8] × 0.5 = [1, 2, 3, 4]

Training ka average [1, 2, 3, 4] aur test [1, 2, 3, 4]: match. Bas scaling "doosri taraf" shift ho gayi.

Inverted dropout (PyTorch, Keras)Standard / original dropout
Trainingmask, phir ÷ (1 − p) (survivors bade)sirf mask
Testkuch nahi, plain forward passoutputs/weights × (1 − p)
Expected value match?HaanHaan
FaydaDeployment code ko dropout ke baare me kuch pata hone ki zaroorat nahiPaper ka original formulation

20.5 Dropout kaam kyun karta hai? Teen kaaran

Kaaran 1: Co-adaptation khatam (koi partner pe bharosa nahi)

Kisi bhi step pe koi bhi partner gayab ho sakta hai, isliye har neuron ko aisa feature seekhna padta hai jo akele bhi useful ho. "Main tumhari galti cancel karunga, tum meri" wale nazuk combinations reliable nahi rehte, to model unpe depend karna chhod deta hai. Worksheet ka sawaal: "Do hidden units jo sirf cancelling pair ki tarah kaam karte hain, dropout training ke baad?" Jawab: discouraged (reinforce nahi, aur weights exactly zero bhi nahi hote).

Kaaran 2: Ek network me hazaaron networks ka ensemble

Ensemble (bagging) yaad karo: kai alag models train karo aur unka average lo; har model ki alag galtiyan average me cancel ho jaati hain, variance kam hota hai. Lekin neural networks ke liye 100 alag networks train karna bahut mehenga hai (lecture slide: "computationally prohibitive"). Dropout sasta jugaad hai: har mask ek alag chhota network ("thinned network") banata hai, aur sab thinned networks same weights share karte hain. n neurons hon to 2n possible masks: 3 neurons → 2³ = 8 networks, 100 neurons → 2100 networks. Test pe poora network chalana in sab ka (approximately) average lene jaisa hai.

Full network (test time) Step 1 ka mask Step 2 ka mask Step 3 ka mask Har step pe alag "thinned" network train hota hai, lekin sab ke weights SAME (shared) hain. n droppable neurons → 2ⁿ possible sub-networks; yahan 6 hidden neurons → 2⁶ = 64
Dropout = ek network ke andar chhupa hua ensemble. Pehla panel test-time ka poora network hai (sab neurons on). Baaki teen panels training ke teen alag steps: laal × wale neurons us step me band hain, unke saare connections bhi gayab. Prince UDL fig 9.8 aur Goodfellow fig 7.6 bhi yahi dikhate hain.

Teacher ka ensemble example (Worksheet L8 Part 2, Fig 5.5)

Do neurons output ko 1 aur 3 contribute karte hain (yani bina dropout output = 1 + 3 = 4). Drop rate r = 0.5, to scale = 2. Chaar possible masks, har ek ki probability 0.25:

MaskCalculationOutput
dono on [1, 1]2 × (1 + 3)8
sirf pehla [1, 0]2 × 12
sirf doosra [0, 1]2 × 36
dono off [0, 0]00
  1. Average: (8 + 2 + 6 + 0) / 4 = 16 / 4 = 4
  2. Test-time full network: 1 + 3 = 4

Jawab: 4 = 4. Saare thinned networks ka average exactly full network ke barabar. Ye linear layer ke liye exact hai; activation (non-linearity) aane pe sirf approximate rehta hai.

Bagging (normal ensemble)

Har model alag train hota hai, alag data subset pe, alag parameters. Prediction: sab models chalao aur average karo. Mehenga.

Dropout

Sab sub-networks ek saath implicitly train hote hain, parameters shared. Prediction: ek hi full network chalao (sab units on) jo average jaisa kaam karta hai. Sasta.

Kaaran 3: Noise injection

Lecture slide: "Each hidden unit is multiplied by a random value at each step of training → adds noise to the training process". Har step pe hidden features me random noise aata hai, to model ko aisi cheezein seekhni padti hain jo noise ke bawajood sahi rahein. Ye ek tarah ka hidden-layer data augmentation hai. (BN bhi batch statistics ki wajah se aisa thoda noise daalta hai, wo aage dekhenge.)

20.6 Dropout kahan lagayein aur p kitna rakhein?

Course labDropout kahanValue
Lab 9 (Fashion-MNIST MLP)har ReLU ke baad0.3
Lab 12 (Fashion-MNIST CNN), Lab 13 (CIFAR-10)classifier me har ReLU ke baad0.4 (Lab 13 Optuna range 0.20–0.55, best ≈ 0.27)
Lab 14 KMNIST CNNFlatten ke baad, final Linear se pehle0.30 default, Optuna 0.1–0.5
Lab 14 SVHN ResNetFlatten ke baad0.20 default, Optuna 0.05–0.40

PyTorch · nn.Dropout2d poore channel band karta hai

import torch
import torch.nn as nn

torch.manual_seed(3)
drop2d = nn.Dropout2d(p=0.5)                 # channel-wise dropout
x = torch.ones(1, 4, 2, 2)                   # (N=1, C=4, H=2, W=2)
y = drop2d(x)                                # train mode
for c in range(4):
    print(f"channel {c}:", y[0, c].flatten().tolist())

Output

channel 0: [2.0, 2.0, 2.0, 2.0]
channel 1: [2.0, 2.0, 2.0, 2.0]
channel 2: [0.0, 0.0, 0.0, 0.0]
channel 3: [2.0, 2.0, 2.0, 2.0]

Channel 2 ke saare 4 pixels ek saath zero; baaki channels poore ke poore ×2. Input shape (N, C, H, W) chahiye.

20.7 Dropout ki keemat (costs aur surprises)

20.8 model.train() vs model.eval() — sabse zaroori line

Kahani

Practice me coach players ko bench pe bithata hai, theek hai. Lekin agar coach final match me bhi aadhi team bench pe bitha de, to team haar jayegi aur har baar alag result aayega. Practice mode aur match mode ka switch yaad rakhna coach ki zimmedari hai. PyTorch me ye switch tum ho: model.train() aur model.eval().

Har nn.Module ke paas ek boolean flag hota hai: self.training. Naya model banate hi ye True hota hai.

PyTorch · same input, dono modes

import torch
import torch.nn as nn
import torch.nn.functional as F

class DropNet(nn.Module):
    def __init__(self, p=0.5):
        super().__init__()
        self.fc1 = nn.Linear(4, 8)
        self.drop = nn.Dropout(p=p)     # module wala dropout
        self.fc2 = nn.Linear(8, 1)

    def forward(self, x):
        x = F.relu(self.fc1(x))         # (N, 4) -> (N, 8)
        x = self.drop(x)                # train me zero+scale, eval me kuch nahi
        return self.fc2(x)              # (N, 8) -> (N, 1)

torch.manual_seed(42)
model = DropNet(p=0.5)
x = torch.tensor([[1.0, 2.0, 3.0, 4.0]])   # ek hi sample, shape (1, 4)

model.train()
print("train mode:", [round(model(x).item(), 4) for _ in range(3)])

model.eval()
with torch.no_grad():
    print("eval  mode:", [round(model(x).item(), 4) for _ in range(3)])
print("model.training =", model.training)

Output

train mode: [0.1854, 1.5576, 1.1825]
eval  mode: [0.9575, 0.9575, 0.9575]
model.training = False

Same input, train mode me har baar alag jawab (random mask). Eval mode me hamesha same, deterministic jawab. Agar evaluation se pehle model.eval() bhool gaye to test accuracy random aur kam aayegi.

Functional dropout: F.dropout

F.dropout(input, p=0.5, training=True, inplace=False) ek function hai, module nahi. Iska training argument default me True hai, aur ye model ka mode apne aap nahi dekhta. Isliye forward ke andar hamesha training=self.training pass karo.

PyTorch · F.dropout ka sahi aur galat use

import torch
import torch.nn as nn
import torch.nn.functional as F

class FuncDropNet(nn.Module):
    def __init__(self, p=0.5):
        super().__init__()
        self.fc1 = nn.Linear(4, 8)
        self.fc2 = nn.Linear(8, 1)
        self.p = p

    def forward(self, x):
        x = F.relu(self.fc1(x))
        # SAHI: training flag model ke mode se aata hai (model.train()/eval() isko set karte hain)
        x = F.dropout(x, p=self.p, training=self.training)
        return self.fc2(x)

torch.manual_seed(42)
net = FuncDropNet()
x = torch.tensor([[1.0, 2.0, 3.0, 4.0]])
net.train()
print("train:", round(net(x).item(), 4), round(net(x).item(), 4))   # alag-alag
net.eval()
print("eval :", round(net(x).item(), 4), round(net(x).item(), 4))   # dono baar same

# GALAT: training flag diya hi nahi -> F.dropout ka default training=True
h = torch.ones(6)
torch.manual_seed(3)
print(F.dropout(h, p=0.5))      # model eval me ho tab bhi ye drop karega!

Output

train: 0.1854 1.5576
eval : 0.9575 0.9575
tensor([2., 2., 0., 2., 0., 0.])

Labs ka standard loop pattern (Lab 9, Lab 12–14): har epoch ki shuruaat me model.train(), evaluation se pehle model.eval() + torch.no_grad(). Lab 9 ke comments: "training mode (Dropout on, BatchNorm updates)" aur "evaluation mode (Dropout off, BatchNorm uses running stats)". Evaluate karne ke baad agle epoch me wapas model.train() bhoolna bhi ek common bug hai, isliye loop ke andar har epoch ke start me hi likho.

Lab pattern · training loop skeleton

for epoch in range(epochs):
    model.train()                       # Dropout ON, BN batch-stats + running update
    for xb, yb in train_loader:
        optimizer.zero_grad()
        loss = criterion(model(xb), yb)
        loss.backward()
        optimizer.step()

    model.eval()                        # Dropout OFF, BN running stats
    with torch.no_grad():               # gradients ki zaroorat nahi
        val_loss = criterion(model(X_val), y_val).item()

Exam Radar · Dropout

  • Paper: Srivastava, Hinton, Krizhevsky, Sutskever, Salakhutdinov, JMLR 2014, "Dropout: A Simple Way to Prevent Neural Networks from Overfitting".
  • Dropout ek regularization technique hai; sirf training me active; test pe full network.
  • PyTorch nn.Dropout(p=0.5): p = drop probability, default 0.5; training me survivors × 1/(1 − p) (inverted dropout); eval me identity.
  • Scale examples: p = 0.5 → ×2, p = 0.2 → ×1.25, p = 0.25 → ×1.3333, p = 0.3 → ×1.4286, p = 0.4 → ×1.6667.
  • Inverted dropout: scaling training me; standard dropout: test pe weights × (1 − p) (keep prob). Dono me expected value match.
  • Kyun kaam karta hai: co-adaptation rokta hai; 2n thinned networks ka implicit ensemble (weights shared, bagging se alag); noise injection.
  • Typical: hidden 0.2–0.5, input 0–0.2 (keep prob: hidden 0.5–0.8, input 0.8–0.9). Conv me kam / Dropout2d (poore channels).
  • Costs: slow convergence, noisy loss, zyada epochs; training loss > validation loss possible.
  • F.dropout ka training default True; use training=self.training.
  • Dropped neuron ke weights us step me update nahi hote (gradient zero).

Galti mat karna

  • "Dropout weights ko permanently delete karta hai" — galat. Sirf ek step ke liye activation zero; stored weights dense rehte hain (wo L1 hai jo weights zero karta hai).
  • "Test pe bhi dropout lagta hai" — galat (default practice me). Test = full network, no mask, inverted me no scaling.
  • "Inverted dropout me test pe ÷(1 − p) karte hain" — galat. Inverted me scaling sirf training me.
  • Keep probability aur drop probability mix mat karo. PyTorch ka p = drop.
  • Output layer ke baad dropout mat lagao; chhote network pe bhari dropout = underfitting.
  • model.eval() ≠ torch.no_grad(). Pehla layers ka behaviour badalta hai, doosra gradient tracking band karta hai.

Dimaag me aise baitha lo

Practice me random players bench pe, survivors ka score double gino; match me poori team, normal score. Train: mask + ×1/(1−p). Eval: kuch nahi.

20.9 Batch Normalization — motivation: har layer ka "input" hilta rehta hai

Kahani

School me ek scholarship ke liye teen sections ke toppers compare karne hain. Section A ki teacher bahut strict hai: marks 30–60 ke beech. Section B ki teacher dil-khol ke deti hai: 70–100. Section C ka paper out of 50 tha. Ab "75 marks" ka matlab har section me alag hai! Principal kya karta hai? Har section ke marks ko us section ke average aur spread se rescale karta hai: "class average se kitna upar, kitne spread ke units me?" Ab sab ek common scale pe aa gaye, comparison fair.

Lekin principal ye bhi jaanta hai ki scholarship committee ko shayad "out of 100" wala scale pasand hai. To normalize karne ke baad wo apni pasand ka scale (stretch) aur center (shift) laga deta hai. Bas: normalize karo, phir apna scale aur shift seekho. Yahi Batch Normalization hai.

Batch Normalization ko Sergey Ioffe aur Christian Szegedy (Google) ne 2015 me paper "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift" (ICML 2015) me diya.

Original motivation: Internal Covariate Shift (ICS)

Definition (lecture slide)

Internal Covariate Shift: The change in the distribution of network activations due to the change in network parameters during training.

Simple bhasha me: layer 5 apne input (layer 4 ke outputs) pe kaam karna seekh rahi hai. Lekin har step pe layers 1–4 ke weights bhi badal rahe hain, to layer 5 ke inputs ka distribution (mean, spread) hilta rehta hai. Layer 5 ek "moving target" ke peeche bhaag rahi hai; usko baar-baar naye distribution ke hisaab se adapt karna padta hai. Slide ke hisaab se problems: slower training aur bada learning rate use karna mushkil. BN ka jawab: har layer ke inputs ka mean aur variance fix kar do (har mini-batch pe 0 aur 1), to distribution itna nahi hilega.

Baad ki samajh: loss landscape smooth hota hai

2018 me Santurkar et al. ("How Does Batch Normalization Help Optimization?") ne dikhaya ki BN ka asli fayda shayad ICS kam karna nahi, balki loss landscape ko smoother banana hai: gradients zyada predictable aur stable ho jaate hain, isliye bada learning rate safe ho jaata hai aur training fast hoti hai. Teacher ki notes: "Two stories: internal covariate shift (Ioffe & Szegedy) versus a smoother loss (Santurkar et al.). Operationally you need neither to implement the four lines." Exam ke liye dono yaad rakho: paper ka title ICS bolta hai; modern view smoother optimization bolta hai.

20.10 BN algorithm — chaar lines

Ek mini-batch me m samples hain. Ek feature (ek neuron ka output, ya CNN me ek channel) lo. Uski m values: x1, …, xm. BN ye chaar kaam karta hai (har feature ke liye alag-alag):

1) μB = 1m Σi=1..m xi
2) σ²B = 1m Σi=1..m (xi − μB)²
3) x̂i = xi − μB√σ²B + ε
4) yi = γ · x̂i + β
  • m = mini-batch size (kitne samples ek saath aaye)
  • μB = batch mean ("B" = batch). Is feature ka batch me average
  • σ²B = batch variance, biased / population form: divide by m, not m − 1 (teacher: "Use the population variance (divide by N)")
  • ε (epsilon) = chhota number (PyTorch default 1e−5) taaki variance 0 ho to bhi division by zero na ho
  • x̂i = normalized value: ab is feature ka batch me mean 0, variance ≈ 1
  • γ (gamma) = learnable scale (PyTorch me bn.weight, init 1)
  • β (beta) = learnable shift (PyTorch me bn.bias, init 0)
  • yi = BN ka final output, jo agli layer/activation ko jaata hai

Step 3 asal me "z-score" hai: x apne batch ke average se kitne standard deviations door hai. Step 4 network ko azaadi deta hai ki wo apna pasandeeda mean (β) aur spread (γ) khud seekhe. γ aur β backprop se baaki weights ki tarah train hote hain.

20.11 Worked example: batch [2, 4, 6, 8]

Chalo numbers se samjhein

Sawaal: Ek neuron ka output ek mini-batch (m = 4) me x = [2, 4, 6, 8] hai. γ = 2, β = 1, ε = 1e−5. BN output nikalo.

  1. Mean: μ = (2 + 4 + 6 + 8)/4 = 20/4 = 5
  2. Deviations: x − μ = [2−5, 4−5, 6−5, 8−5] = [−3, −1, 1, 3]
  3. Squares: [9, 1, 1, 9], sum = 20
  4. Biased variance: σ² = 20/4 = 5 (agar /3 karte to 6.6667 aata: wo unbiased hai, yahan nahi chahiye)
  5. Denominator: √(5 + 0.00001) = √5.00001 ≈ 2.2361 (ε ke bina √5 = 2.236068; ε ka asar 4 decimals pe nahi dikhta)
  6. Normalize: x̂ = [−3, −1, 1, 3] / 2.2361 ≈ [−1.3416, −0.4472, 0.4472, 1.3416]
  7. Check: x̂ ka mean = 0; x̂ ka variance = (1.8 + 0.2 + 0.2 + 1.8)/4 = 4/4 = 1 (≈ 0.999998 ε ki wajah se)
  8. Scale & shift: y = 2·x̂ + 1
    y₁ = 2(−1.341639) + 1 = −2.683279 + 1 = −1.683279 ≈ −1.6833
    y₂ = 2(−0.447213) + 1 = −0.894426 + 1 = 0.105574 ≈ 0.1056
    y₃ = 2(0.447213) + 1 = 0.894426 + 1 = 1.894426 ≈ 1.8944
    y₄ = 2(1.341639) + 1 = 2.683279 + 1 = 3.683279 ≈ 3.6833

Jawab: y ≈ [−1.6833, 0.1056, 1.8944, 3.6833]. Inka mean = β = 1, std = γ = 2.

Note: teacher ki sheet ne rounded x̂ (1.3416) se 2 × 1.3416 + 1 = 3.6832 likha hai; exact calculation 3.68328 → 3.6833 deta hai. MCQ me −1.683 / 3.683 dono ka matlab same hai.

Teacher ka board example: u = (1, 3, 5, 7), ε = 0, γ = 2, β = 1

  1. μB = 16/4 = 4
  2. σ²B = (9 + 1 + 1 + 9)/4 = 5, √5 ≈ 2.2361
  3. û ≈ (−1.3416, −0.4472, +0.4472, +1.3416)
  4. y = 2û + 1 ≈ (−1.6832, 0.1056, 1.8944, 3.6832)

Dhyan do: (1, 3, 5, 7) aur (2, 4, 6, 8) ka x̂ bilkul same hai! Kyunki saare numbers +1 shift hue, mean bhi +1 shift hua, aur (x − μ) me shift cancel ho gaya. BN kisi bhi constant shift ko mita deta hai. Ye baat 20.15 me "bias=False" samjhayegi.

1) Raw batch: x = [2, 4, 6, 8] 0 2 4 6 8 μ = 5, σ² = 5 2) Normalize: x̂ = (x − μ)/√(σ² + ε) 0 −1.34 −0.45 0.45 1.34 mean 0, var ≈ 1 3) Scale & shift: y = γx̂ + β (γ = 2, β = 1) 0 −1.68 0.11 1.89 3.68 mean β = 1, std γ = 2 −μ, ÷σ ↓ ×γ, +β ↓
BatchNorm ek feature ke liye, ek mini-batch pe: (1) batch ke values kahin bhi ho sakte hain; (2) mean ghatao aur std se divide karo: center 0, spread ≈ 1 (peela band = ±1); (3) learnable γ (stretch) aur β (shift) se network apni pasand ka scale aur center chun leta hai. Laal dashed line = har stage ka center.

20.12 γ aur β kyun chahiye? Normalization ko "undo" karne ki azaadi

Agar BN sirf normalize karta (step 3 pe ruk jaata), to har layer ka input hamesha mean 0, variance 1 pe forced rehta. Ye kabhi-kabhi network ki representational power (kya-kya represent kar sakta hai) chheen leta hai. Example: sigmoid ke baad BN... ulta socho, BN ke baad sigmoid. x̂ zyada tar −2 se +2 ke beech hai, jahan sigmoid lagbhag seedhi line jaisa hai. Matlab non-linearity ka fayda hi khatam! γ aur β se network chahe to values ko sigmoid ke curved hisse me bhej sakta hai.

Sabse important property: agar network ko lage ki normalization bekaar hai, to wo γ = √(σ² + ε) aur β = μ seekh ke normalization ko poori tarah undo kar sakta hai:

Undo check

Batch [2, 4, 6, 8]: γ = √(5 + 0.00001) ≈ 2.2361, β = μ = 5 rakho.

  1. y₁ = 2.2361 × (−1.3416) + 5 ≈ −3 + 5 = 2
  2. y₄ = 2.2361 × 1.3416 + 5 ≈ 3 + 5 = 8

Output = [2, 4, 6, 8] = original input (Python se verify kiya). Yaani BN laga ke network ne kuch "khoya" nahi; worst case me wo identity ban sakta hai. Isliye γ, β "restore representational power".

Note: γ aur β per feature hain (har neuron/channel ka apna ek γ aur ek β), poore layer ke liye ek nahi.

20.13 BatchNorm1d vs BatchNorm2d — kis cheez ka average?

features C → n1 n2 n3 n4 features C → n1 n2 n3 n4 BatchNorm1d input (N, C) ek column = ek feature μ, σ² batch (N) ke upar C features → C jode (γ, β) LayerNorm input (N, C) ek row = ek sample μ, σ² features ke upar batch pe depend nahi BatchNorm2d input (N, C, H, W) n1 n2 n3 c0 c1 c2 channel: N × H × W values C channels → C jode (γ, β) peela = BN ek baar me jin numbers ka μ, σ² nikalta hai · orange = LayerNorm ka group
Kis axis pe average hota hai: BatchNorm1d har feature (column) ko batch ke upar normalize karta hai; LayerNorm har sample (row) ko uske apne features ke upar; BatchNorm2d har channel ko (N, H, W) teeno ke upar, yaani ek channel ke saare images ke saare pixels milkar ek μ aur ek σ² dete hain.

Shape aur count ka example

Sawaal: 8 images ka batch, 16 channels, 32×32 size. nn.BatchNorm2d(16) kitne numbers se ek channel ka mean nikalta hai? Kitne learnable params?

  1. Ek channel ke values: N × H × W = 8 × 32 × 32 = 8192
  2. Kitne alag (μ, σ²) pairs: C = 16
  3. Learnable params: 16 γ + 16 β = 32; buffers: 16 running_mean + 16 running_var (+ num_batches_tracked)

Jawab: 8192 values per channel, 32 learnable parameters. Output shape input jaisa hi: (8, 16, 32, 32).

PyTorch signature (exam ke liye defaults yaad karo): nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True). affine=True matlab γ, β learnable hain (False karoge to sirf normalize). track_running_stats=True matlab running mean/var maintain karo (eval ke liye). BatchNorm2d ke same arguments hain.

PyTorch · manual BN = nn.BatchNorm1d (train mode)

import torch
import torch.nn as nn

x = torch.tensor([[2.0], [4.0], [6.0], [8.0]])   # (N=4, C=1): 4 samples, 1 feature

bn = nn.BatchNorm1d(num_features=1)              # eps=1e-5, momentum=0.1 default
with torch.no_grad():
    bn.weight.fill_(2.0)                          # gamma = 2
    bn.bias.fill_(1.0)                            # beta  = 1

# ---- manual BN (train mode wala formula) ----
mu = x.mean(dim=0)                                # batch mean, per feature (dim=0 = batch axis)
var = x.var(dim=0, unbiased=False)                # BIASED variance (divide by m)
x_hat = (x - mu) / torch.sqrt(var + 1e-5)
y_manual = 2.0 * x_hat + 1.0

bn.train()
y_torch = bn(x)
print("mu, var :", mu.item(), var.item())
print("x_hat   :", x_hat.flatten())
print("manual  :", y_manual.flatten())
print("nn.BN   :", y_torch.detach().flatten())
print("same?   :", torch.allclose(y_manual, y_torch, atol=1e-6))

Output

mu, var : 5.0 5.0
x_hat   : tensor([-1.3416, -0.4472,  0.4472,  1.3416])
manual  : tensor([-1.6833,  0.1056,  1.8944,  3.6833])
nn.BN   : tensor([-1.6833,  0.1056,  1.8944,  3.6833])
same?   : True

Hamara haath ka hisaab, manual code aur nn.BatchNorm1d: teeno same. unbiased=False bahut zaroori hai; default x.var() unbiased (÷ (m − 1)) hota hai aur match nahi karega.

PyTorch · BatchNorm1d (multi-feature) aur BatchNorm2d

import torch
import torch.nn as nn

torch.manual_seed(0)
# BatchNorm1d: (N, C) -> har column (feature) ka apna mean/var
x = torch.randn(32, 3) * torch.tensor([1.0, 10.0, 100.0]) + torch.tensor([5.0, -20.0, 300.0])
bn1 = nn.BatchNorm1d(3)
y = bn1(x)                                  # naya module train mode me hota hai
print("before mean:", x.mean(0))
print("after  mean:", y.mean(0).detach())
print("after  std :", y.std(0, unbiased=False).detach())
for name, p in bn1.named_parameters():
    print("param ", name, tuple(p.shape))
for name, b in bn1.named_buffers():
    print("buffer", name, tuple(b.shape))

# BatchNorm2d: (N, C, H, W) -> har CHANNEL ka mean/var, (N, H, W) ke upar
imgs = torch.randn(8, 16, 32, 32) * 3 + 7
bn2 = nn.BatchNorm2d(16)
out = bn2(imgs)
print(out.shape)                                             # shape same rehta hai
print("ch-0 mean/std:", round(out[:, 0].mean().item(), 4), round(out[:, 0].std(unbiased=False).item(), 4))
print("values per channel:", 8 * 32 * 32)
print("learnable params:", sum(p.numel() for p in bn2.parameters()))   # 16 gamma + 16 beta
print(bn2)

Output

before mean: tensor([  4.9911, -21.6251, 341.0513])
after  mean: tensor([-5.2154e-07, -3.3528e-08, -8.9407e-08])
after  std : tensor([1.0000, 1.0000, 1.0000])
param  weight (3,)
param  bias (3,)
buffer running_mean (3,)
buffer running_var (3,)
buffer num_batches_tracked ()
torch.Size([8, 16, 32, 32])
ch-0 mean/std: -0.0 1.0
values per channel: 8192
learnable params: 32
BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, bias=True, track_running_stats=True)

Teen features bilkul alag scale pe the (≈5, ≈−21, ≈341), BN ke baad teeno ka mean ≈ 0 (e−07 matlab practically zero) aur std 1. (Purane PyTorch versions print(bn2) me bias=True nahi dikhate; Lab 13 ka output waisa hai, Lab 12 ka aisa.)

20.14 Train vs Eval: running mean aur running variance

Kahani

Principal wala marks scaling yaad karo. Wo section ke average se scale karta tha. Lekin ab ek akela naya student admission ke liye aaya, uska koi section nahi, koi "batch" nahi. Uske marks ko kis average se scale karein? Principal ke paas pichhle saalon ka running average likha hua hai: "hamare school me typically average 65, spread 12". Wahi use karo.

Test time pe aksar ek hi image aati hai (batch of 1), ya batches ka composition random hota hai. Agar BN test pe bhi batch stats use kare to ek image ka prediction is baat pe depend karega ki uske saath batch me aur kaun-si images thi! Ye galat hai. Isliye training ke dauran BN har feature ke liye ek running (exponential moving) average rakhta hai, aur model.eval() me wahi use karta hai.

running_mean ← (1 − momentum) · running_mean + momentum · μB
running_var ← (1 − momentum) · running_var + momentum · σ²B, unbiased
Eval:  y = γ · x − running_mean√running_var + ε + β
  • momentum = 0.1 (PyTorch default): naye batch ko 10% weight, purane running value ko 90%. (Chapter 16 ka EWMA yaad karo; yahan βEWMA = 1 − momentum = 0.9.)
  • Shuruaat: running_mean = 0, running_var = 1 (har feature ke liye)
  • σ²B, unbiased = Σ(x − μ)²/(m − 1): running_var me PyTorch unbiased variance daalta hai, jabki training ke normalization me biased (÷ m) use hota hai
  • Eval me batch ka μB, σ²B nahi lagta; har sample alag-alag, deterministic tareeke se normalize hota hai

Chalo numbers se samjhein: do batches ke baad running stats

Ek feature, nn.BatchNorm1d(1), γ = 1, β = 0 (defaults). Batch 1 = [2, 4, 6, 8], batch 2 = [1, 3, 5, 7]. Phir eval mode me x = 2, 6, 8 ka output?

  1. Start: running_mean = 0, running_var = 1
  2. Batch 1: μ = 5; unbiased var = 20/(4 − 1) = 6.6667
  3. Update: running_mean = 0.9 × 0 + 0.1 × 5 = 0.5; running_var = 0.9 × 1 + 0.1 × 6.6667 = 0.9 + 0.6667 = 1.5667
  4. Batch 2: μ = 4; deviations [−3, −1, 1, 3], unbiased var = 20/3 = 6.6667
  5. Update: running_mean = 0.9 × 0.5 + 0.1 × 4 = 0.45 + 0.4 = 0.85; running_var = 0.9 × 1.5667 + 0.1 × 6.6667 = 1.41 + 0.6667 = 2.0767
  6. Eval, x = 6: (6 − 0.85)/√(2.0767 + 0.00001) = 5.15/1.4411 = 3.5737
  7. Eval, x = 2: (2 − 0.85)/1.4411 = 1.15/1.4411 = 0.7980; x = 8: 7.15/1.4411 = 4.9616

Jawab: running_mean = 0.85, running_var ≈ 2.0767; eval outputs ≈ [0.7980, 3.5737, 4.9616]. Sirf 2 batches hue hain, isliye running_mean (0.85) abhi data ke asli mean (dono batches ka mean 4.5) se kaafi door hai, kyunki wo 0 se shuru hua tha; hazaaron batches ke baad running stats asli data ke stats ke paas pahunch jaate hain.

PyTorch · running stats ka update aur eval output

import torch
import torch.nn as nn

bn = nn.BatchNorm1d(1)                     # gamma=1, beta=0 (default)
print("start   :", bn.running_mean.item(), bn.running_var.item())   # 0 aur 1 se shuru

bn.train()
bn(torch.tensor([[2.0], [4.0], [6.0], [8.0]]))      # batch 1: mean 5, unbiased var 6.6667
print("batch 1 :", bn.running_mean.item(), round(bn.running_var.item(), 4))

bn(torch.tensor([[1.0], [3.0], [5.0], [7.0]]))      # batch 2: mean 4, unbiased var 6.6667
print("batch 2 :", round(bn.running_mean.item(), 4), round(bn.running_var.item(), 4))
print("batches :", bn.num_batches_tracked.item())

bn.eval()                                  # ab running stats use honge
x_test = torch.tensor([[2.0], [6.0], [8.0]])
with torch.no_grad():
    print("eval out:", bn(x_test).flatten())
manual = (x_test - 0.85) / torch.sqrt(torch.tensor(2.0766667) + 1e-5)
print("manual  :", manual.flatten())

Output

start   : 0.0 1.0
batch 1 : 0.5 1.5667
batch 2 : 0.85 2.0767
batches : 2
eval out: tensor([0.7980, 3.5737, 4.9616])
manual  : tensor([0.7980, 3.5737, 4.9616])

Batch size 1 ka panga

Training me agar batch me sirf 1 sample hai to variance = 0 aur normalized value hamesha 0 (x − μ = 0). Isliye PyTorch train mode me error deta hai. Eval me koi dikkat nahi (running stats).

PyTorch · batch of 1 in train mode

import torch
import torch.nn as nn

bn = nn.BatchNorm1d(4)
one = torch.randn(1, 4)              # batch me sirf 1 sample
bn.train()
try:
    bn(one)
except ValueError as e:
    print("ValueError:", e)
bn.eval()
print(bn(one).shape)                 # eval me chalega: running stats use hote hain

Output

ValueError: Expected more than 1 value per channel when training, got input size torch.Size([1, 4])
torch.Size([1, 4])

Teacher ke MCQs: "Test pe ek image pass karte ho, BN kisse normalize kare?" → running mean aur variance. "BN ko train mode me chhod ke ek-ek image test ki?" → predictions batch-dependent ya degenerate ho jaate hain. (Ek practical tip: DataLoader me drop_last=True se aakhri chhota batch, jo shayad size 1 ka ho, skip ho jaata hai.)

20.15 BN kahan lagayein? Aur bias=False kyun?

bias=False ka raaz

Linear/Conv ka bias b har sample me same constant jodta hai. BN step 1–3 me mean ghatata hai, aur constant shift mean me bhi aa jaata hai, to cancel: (x + b) − mean(x + b) = x − mean(x). Matlab BN se pehle bias bekaar hai (uska gradient bhi 0 ho jaata hai). BN ka β already "shift" ka kaam karta hai. Isliye bias=False likh ke ek redundant parameter bacha lo (Lab 14 ka note: "Use bias=False for convolutions followed by batch normalization").

PyTorch · constant shift BN ke baad gayab

import torch
import torch.nn as nn

bn = nn.BatchNorm1d(1)
x = torch.tensor([[2.0], [4.0], [6.0], [8.0]])
print(bn(x).detach().flatten())
print(bn(x + 10).detach().flatten())      # +10 bias jaisa shift: output SAME

Output

tensor([-1.3416, -0.4472,  0.4472,  1.3416])
tensor([-1.3416, -0.4472,  0.4472,  1.3416])

PyTorch · Lab 14 style conv-BN-ReLU block

import torch
import torch.nn as nn

def conv_bn_relu(in_c, out_c):
    return nn.Sequential(
        nn.Conv2d(in_c, out_c, kernel_size=3, padding=1, bias=False),  # bias ki zaroorat nahi
        nn.BatchNorm2d(out_c),                                          # beta hi bias ka kaam karega
        nn.ReLU(inplace=True)
    )

block = conv_bn_relu(3, 32)
print(block)
print("params:", sum(p.numel() for p in block.parameters()))
print(block(torch.randn(4, 3, 28, 28)).shape)

Output

Sequential(
  (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, bias=True, track_running_stats=True)
  (2): ReLU(inplace=True)
)
params: 928
torch.Size([4, 32, 28, 28])

Params: conv weights 32 × 3 × 3 × 3 = 864 + BN 32 γ + 32 β = 64 = 928. Bias hota to 32 aur (bekaar) params.

Lab 9 ka regularized MyNN (BN + Dropout + weight decay)

PyTorch · Lab 9 · Fashion-MNIST MLP with BatchNorm1d + Dropout

import torch
import torch.nn as nn

class MyNN(nn.Module):
    def __init__(self, num_features):
        super().__init__()
        self.model = nn.Sequential(
            nn.Linear(num_features, 128),
            nn.BatchNorm1d(128),     # Linear ke baad, ReLU se pehle (num_features = 128)
            nn.ReLU(),
            nn.Dropout(0.3),         # activation ke baad
            nn.Linear(128, 64),
            nn.BatchNorm1d(64),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(64, 10)        # output layer: na BN, na Dropout (raw logits)
        )

    def forward(self, x):
        return self.model(x)

model = MyNN(784)
print(model)
total = sum(p.numel() for p in model.parameters())
print("trainable params:", total)
bn_params = sum(p.numel() for m in model.modules() if isinstance(m, nn.BatchNorm1d) for p in m.parameters())
print("BN params (gamma+beta):", bn_params)
x = torch.randn(32, 784)                     # (N=32, 784) batch
print(model(x).shape)                        # (32, 10)
# Lab 9 me optimizer: optim.SGD(model.parameters(), lr=0.1, weight_decay=1e-4)  -> L2 bhi

Output

MyNN(
  (model): Sequential(
    (0): Linear(in_features=784, out_features=128, bias=True)
    (1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, bias=True, track_running_stats=True)
    (2): ReLU()
    (3): Dropout(p=0.3, inplace=False)
    (4): Linear(in_features=128, out_features=64, bias=True)
    (5): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, bias=True, track_running_stats=True)
    (6): ReLU()
    (7): Dropout(p=0.3, inplace=False)
    (8): Linear(in_features=64, out_features=10, bias=True)
  )
)
trainable params: 109770
BN params (gamma+beta): 384
torch.Size([32, 10])

Count: 784×128 + 128 = 100480, BN(128) = 256, 128×64 + 64 = 8256, BN(64) = 128, 64×10 + 10 = 650; total = 109770. BN ne sirf 384 parameters joda. Lab 9 ka baseline (bina BN/Dropout) 98.25% train vs 88.98% test pe overfit kar raha tha; regularization ka maqsad yahi gap kam karna tha.

20.16 BN ke fayde aur nuksaan

Fayde (lecture slide "Batch Norm Recap" + paper)

  • Networks train faster (kam epochs me converge)
  • Higher learning rates allow karta hai bina diverge hue
  • Weights initialize karna aasaan: bad init ka asar kam (Chapter 21 dekho)
  • Zyada activation functions viable: sigmoid/tanh saturate kam karte hain kyunki inputs ~N(0, 1) range me rehte hain; isse vanishing/exploding gradients me bhi madad
  • Mild regularization: har sample ka normalization batch ke baaki samples pe depend karta hai, ye random noise jaisa hai ("may replace Dropout" kuch cases me; slide: "a form of data augmentation")

Nuksaan

  • Chhote batch (1, 2, 4) pe batch stats bahut noisy/unreliable; batch 1 pe training me chalta hi nahi
  • Train aur test me alag calculation (batch stats vs running stats): eval() bhoole to bug
  • Online learning (ek-ek sample) ke liye theek nahi
  • RNN/LSTM me awkward (har time step ke stats alag); wahan aur transformers me LayerNorm use hota hai
  • Thoda extra compute aur memory

Teacher ki notes: "Batch normalisation: mainly speeds up training, with a mild regularising side effect." Aur architecture defaults: "Residual conv net: batch norm (or LayerNorm) on, weight decay ~10⁻⁴, early stopping, dropout only if you still overfit."

20.17 LayerNorm — BN ka cousin jo batch pe depend nahi karta

Layer Normalization (Ba, Kiros, Hinton, 2016) wahi chaar lines karta hai, lekin μ aur σ² ek hi sample ke saare features ke upar nikalta hai, batch ke upar nahi. Isliye: batch size 1 pe bhi chalta hai, train aur test me same calculation, koi running stats nahi. Transformers aur RNNs me yahi standard hai (teacher: "Very small batches are why LayerNorm won in transformers").

Chalo numbers se samjhein

Batch me 2 samples, 3 features: row 1 = [1, 2, 3], row 2 = [3, 6, 9]. (ε ko ignore karo.)

  1. BN (column-wise): column 1 = [1, 3] → μ = 2, σ² = 1 → [−1, +1]. Column 2 = [2, 6] → μ = 4, σ² = 4 → [−1, +1]. Column 3 = [3, 9] → μ = 6, σ² = 9 → [−1, +1].
  2. BN output: [[−1, −1, −1], [1, 1, 1]]
  3. LN (row-wise): row 1 = [1, 2, 3] → μ = 2, σ² = (1 + 0 + 1)/3 = 0.6667, √ = 0.8165 → [−1.2247, 0, 1.2247]. Row 2 = [3, 6, 9] → μ = 6, σ² = (9 + 0 + 9)/3 = 6, √ = 2.4495 → [−1.2247, 0, 1.2247].
  4. LN output: [[−1.2247, 0, 1.2247], [−1.2247, 0, 1.2247]]

Same input, bilkul alag outputs: BN ne samples ko ek-doosre se compare kiya, LN ne har sample ke andar features ko.

PyTorch · BatchNorm1d vs LayerNorm

import torch
import torch.nn as nn

x = torch.tensor([[1.0, 2.0, 3.0],
                  [3.0, 6.0, 9.0]])          # (N=2 samples, 3 features)

bn = nn.BatchNorm1d(3)                       # column-wise: har feature, batch ke upar
ln = nn.LayerNorm(3)                         # row-wise: har sample, apne features ke upar
print("BatchNorm:\n", bn(x).detach())
print("LayerNorm:\n", ln(x).detach())

Output

BatchNorm:
 tensor([[-1.0000, -1.0000, -1.0000],
        [ 1.0000,  1.0000,  1.0000]])
LayerNorm:
 tensor([[-1.2247,  0.0000,  1.2247],
        [-1.2247,  0.0000,  1.2247]])

nn.LayerNorm(normalized_shape, eps=1e-05, elementwise_affine=True): normalized_shape = last dimension(s) ka size jinke upar normalize karna hai; elementwise_affine=True matlab har feature ka γ, β.

BatchNormLayerNorm
μ, σ² kiske uparek feature/channel, poore batch ke upar (BN2d: N, H, W)ek sample, uske saare features ke upar
Batch size pe depend?Haan (chhota batch = noisy; 1 = error in train)Nahi
Train vs testAlag (batch stats vs running stats)Same calculation
Running statsHaan (running_mean, running_var)Nahi
γ, βper feature/channelper feature (normalized_shape)
Kahan useCNNs (ResNet, VGG-BN), MLPsTransformers, RNN/LSTM
PaperIoffe & Szegedy 2015Ba, Kiros & Hinton 2016

Exam Radar · Batch Normalization

  • Paper: Ioffe & Szegedy, 2015, "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift".
  • ICS = change in distribution of layer activations due to change in earlier layers' parameters during training. Modern view: BN loss landscape ko smooth karta hai (Santurkar et al. 2018).
  • Algorithm: μB → σ²B (biased, ÷m) → x̂ = (x − μ)/√(σ² + ε) → y = γx̂ + β. γ, β learnable (init 1 aur 0).
  • γ = √(σ² + ε), β = μ se BN identity ban sakta hai (representational power restore).
  • [2, 4, 6, 8] → μ = 5, σ² = 5, x̂ ≈ [−1.3416, −0.4472, 0.4472, 1.3416]; γ = 2, β = 1 → [−1.6833, 0.1056, 1.8944, 3.6833].
  • Defaults: eps = 1e−5, momentum = 0.1, affine = True, track_running_stats = True.
  • running = 0.9 · running + 0.1 · batch_stat; running_var me unbiased variance; init running_mean = 0, running_var = 1.
  • Eval mode me running stats; train mode me batch stats. Single test image → running stats.
  • BN1d: (N, C) per feature over N. BN2d: (N, C, H, W) per channel over N, H, W. Learnable params = 2C, buffers = 2C (+ num_batches_tracked).
  • Placement: Linear/Conv → BN → ReLU (standard); bias=False before BN (β redundant karta hai bias ko).
  • Benefits: faster training, higher LR, less init-sensitive, mild regularization, helps vanishing/exploding. Cons: small batches, train/test difference, RNN/online learning.
  • LayerNorm: per sample over features; batch-independent; transformers.

Galti mat karna

  • Training normalization me biased variance (÷ m); running_var me unbiased (÷ (m − 1)). Ulta mat karna.
  • BN ka momentum 0.1 = naye batch ka weight. Optimizer momentum 0.9 = purane velocity ka weight. Alag cheezein.
  • "BN ke baad output hamesha mean 0, var 1" — sirf γ = 1, β = 0 pe. Seekhe hue γ, β ke baad mean = β, std = γ.
  • BatchNorm2d per channel hai, per pixel nahi. BatchNorm2d(C) ka argument channels ki sankhya hai, H ya W nahi.
  • γ, β ek-ek poore layer ke liye nahi, har feature/channel ka apna.
  • LayerNorm batch ke upar average nahi karta, aur uske paas running stats nahi hote.
  • Evaluation se pehle model.eval() bhoole to BN batch stats use karega aur predictions batch pe depend karengi.

Dimaag me aise baitha lo

Teen teachers ke marks: "average ghatao, spread se bhaago, phir apna γ-stretch aur β-shift lagao". Training me section ka average, akela student aaye to school ka running average.

Dropout vs BatchNorm: ek nazar me

DropoutBatch Normalization
Main kaamRegularization (overfitting kam)Training fast/stable (side me mild regularization)
Kya karta haiRandom activations ko 0, survivors × 1/(1−p)Har feature ko batch stats se normalize, phir γx̂ + β
Learnable params02 per feature/channel (γ, β)
Train modeRandom mask + scalingBatch μ, σ²; running stats update
Eval modeIdentity (kuch nahi)Running μ, σ²
Typical jagahFC layers me activation ke baad; CNN classifierLinear/Conv ke baad, activation se pehle
PyTorchnn.Dropout(p), nn.Dropout2d(p), F.dropoutnn.BatchNorm1d(C), nn.BatchNorm2d(C)
Saal2014 (Srivastava, Hinton et al.)2015 (Ioffe, Szegedy)

Chapter 21

Weight Initialization: Zero, Constant, Random, Xavier, He — shuruaat sahi to safar aasaan

Symmetry breaking · Zero & constant init · Too small / too large random · Var(z) = nin·Var(w)·Var(x) · Xavier/Glorot (2010) · He/Kaiming (2015) · PyTorch defaults · nn.init · model.apply

21.1 Initialization kyun matter karta hai?

Kahani

Maa ne naya tiffin service shuru kiya, 10 cooks rakhe. Pehle din sabko ek hi recipe, ek hi masala, ek hi quantity di. Feedback aaya "namak kam", to sabne same badlaav kiya. Mahine baad bhi 10 cooks ek hi dish bana rahe hain: 10 log, lekin variety ek ki! Agar shuru me har cook ko thoda alag starting point diya hota (koi rajma, koi chhole, koi paneer), to sab alag specialty seekhte.

Doosri galti: masale ki quantity. Ek cook ko itna kam masala diya ki khana feeka, aur har agla step usko aur feeka karta gaya. Doosre ko itna zyada diya ki khana jal gaya. Shuruaat ka scale bhi sahi chahiye.

Training gradient descent hai: ek starting point se chhote steps. Starting point = initial weights, jo pehle update se pehle hi choose hote hain. Teacher ki sheet: "Before a network learns anything, its weights have already set the stage." Initialization ke teen goals:

  1. Symmetry todna (symmetry breaking): ek layer ke alag-alag neurons alag-alag features seekhein, photocopies na banein.
  2. Forward signal ka scale stable: layer-dar-layer activations na shrink hon (sab 0) na explode hon (sab huge), aur sigmoid/tanh saturate na hon.
  3. Backward gradient ka scale stable: gradients bhi layers ke through vanish ya explode na hon (Chapter 13 ka vanishing/exploding gradient problem).

Notation (teacher ki sheet wala): ek layer z = W a + b, phir anext = φ(z). Yahan z = pre-activation, φ = activation function. nin (fan-in) = ek neuron me kitne inputs aate hain; nout (fan-out) = layer ke kitne outputs. PyTorch me nn.Linear(in, out) ka weight shape (out, in) = (fan_out, fan_in). σ = weight ka standard deviation, σ² = variance. Signal naapne ke liye teacher q = E[a²] (second moment, average squared activation) use karti hai; zero-mean activations ke liye q = Var(a).

21.2 Zero initialization — sab neurons photocopy

Sabse seedha idea: saare weights 0 se shuru karo. Problem: ek layer ke saare neurons ke incoming weights same (0), bias same, to same input pe sabka z same, output same. Backprop me unke outgoing weights bhi same hain, to sabko same δ (error signal) milta hai, aur same update. Update ke baad bhi wo same rehte hain. Hamesha ke liye identical. Ise symmetry problem kehte hain. 100 neurons ki layer effectively 1 neuron ban jaati hai.

∂L∂Wij = δi · xj
  • Wij = input j se hidden neuron i tak ka weight (matrix ki row i = neuron i ke saare incoming weights)
  • δi = ∂L/∂zi = neuron i ka error signal (loss uske pre-activation pe kitna sensitive hai)
  • xj = input j (ya pichhli layer ki activation)
  • Agar saare δi barabar to saari rows ka gradient barabar (columns alag ho sakte hain kyunki xj alag hain)

Teacher ka quick check: x = [1, 2], δ = [0.5, 0.5, 0.5] → ∂L/∂W11 = δ1x1 = 0.5 × 1 = 0.5, ∂L/∂W21 = 0.5 × 1 = 0.5. Teeno rows ka gradient [0.5, 1.0]. Rows identical, columns alag.

Chalo numbers se samjhein: 2-2-1 network, sab kuch zero

Setup: input x = [1, 2], target y = 1. Hidden layer: 2 sigmoid neurons (weights W 2×2, bias b). Output: 1 sigmoid neuron (weights v = [v₁, v₂], bias c). Loss = binary cross-entropy. Learning rate η = 1. Sab weights aur biases = 0. (Sigmoid + BCE ka output error δout = ŷ − y hota hai, Chapter 11.)

Step 1 (forward)

  1. z₁ = 0·1 + 0·2 + 0 = 0, z₂ = 0 → h₁ = h₂ = σ(0) = 0.5
  2. zout = 0·0.5 + 0·0.5 + 0 = 0 → ŷ = 0.5, loss = −ln(0.5) = 0.6931

Step 1 (backward)

  1. δout = ŷ − y = 0.5 − 1 = −0.5
  2. Output weights: ∂L/∂v₁ = δout · h₁ = −0.5 × 0.5 = −0.25, ∂L/∂v₂ = −0.25 (same!), ∂L/∂c = −0.5
  3. Hidden deltas: δi = δout · vi · hi(1 − hi) = −0.5 × 0 × 0.25 = 0 (kyunki v = 0)
  4. Hidden weight gradients: ∂L/∂W = 0 (poori matrix), ∂L/∂b = 0
  5. Update (η = 1): v₁ = v₂ = 0 − 1 × (−0.25) = 0.25, c = 0.5, W abhi bhi 0

Step 2

  1. Forward: h₁ = h₂ = 0.5 (W abhi 0); zout = 0.25×0.5 + 0.25×0.5 + 0.5 = 0.75 → ŷ = σ(0.75) = 0.6792, loss = 0.3869
  2. δout = 0.6792 − 1 = −0.3208
  3. δ₁ = δ₂ = −0.3208 × 0.25 × 0.25 = −0.0201 (dono same, kyunki v₁ = v₂ aur h₁ = h₂)
  4. Row 1: ∂L/∂W₁ = δ₁ × [1, 2] = [−0.0201, −0.0401]; Row 2: [−0.0201, −0.0401]
  5. Update: dono rows W₁ = W₂ = [0.0201, 0.0401]

Jawab: dono hidden neurons ke weights, outputs aur gradients har step pe exactly barabar. Loss gir raha hai (0.6931 → 0.3869 → 0.2506) lekin network sirf "ek neuron jitna" seekh raha hai. Teesre step ke baad bhi rows [0.0427, 0.0854] aur [0.0427, 0.0854]: photocopies.

saare W = 0 v₁ = v₂ = 0 x₁ = 1 x₂ = 2 h₁ h₂ ŷ ŷ = σ(0) = 0.5 mirror dono ka z = 0, h = σ(0) = 0.5 Step 2 ke gradients (∂L/∂W) row 1 (h₁ ke weights): [−0.0201, −0.0401] row 2 (h₂ ke weights): [−0.0201, −0.0401] bilkul same! Same weights → same z → same h → same δ → same update → hamesha same
Zero-initialized 2-2-1 network: dono hidden neurons ek-doosre ki photocopy hain. Unke incoming weights, outputs aur gradients hamesha barabar rehte hain, isliye 2 neurons hone ke bawajood network effectively 1 hidden neuron jaisa hai (symmetry problem).

PyTorch · zero init ka symmetry demo (upar wala exact example)

import torch
import torch.nn as nn

torch.manual_seed(0)
net = nn.Sequential(nn.Linear(2, 2), nn.Sigmoid(), nn.Linear(2, 1))   # 2-2-1
for m in net.modules():
    if isinstance(m, nn.Linear):
        nn.init.zeros_(m.weight)          # ZERO init (weights)
        nn.init.zeros_(m.bias)            # bias bhi zero

x = torch.tensor([[1.0, 2.0]])
y = torch.tensor([[1.0]])
loss_fn = nn.BCEWithLogitsLoss()          # sigmoid + BCE ek saath (logit lena)
opt = torch.optim.SGD(net.parameters(), lr=1.0)

for step in range(1, 4):
    opt.zero_grad()
    loss = loss_fn(net(x), y)
    loss.backward()
    print(f"step {step}: loss {loss.item():.4f}")
    print("  grad of hidden W:\n", net[0].weight.grad)
    opt.step()
print("hidden W after 3 steps:\n", net[0].weight.data)
print("output v after 3 steps:", net[2].weight.data)

Output

step 1: loss 0.6931
  grad of hidden W:
 tensor([[0., 0.],
        [0., 0.]])
step 2: loss 0.3869
  grad of hidden W:
 tensor([[-0.0201, -0.0401],
        [-0.0201, -0.0401]])
step 3: loss 0.2506
  grad of hidden W:
 tensor([[-0.0227, -0.0453],
        [-0.0227, -0.0453]])
hidden W after 3 steps:
 tensor([[0.0427, 0.0854],
        [0.0427, 0.0854]])
output v after 3 steps: tensor([[0.5279, 0.5279]])

Zero init + ReLU: aur bhi bura

Course Colab ka point: zero weights (aur zero bias) se z = 0. ReLU(0) = 0, to saare hidden outputs 0. Aur PyTorch me ReLU ka derivative z = 0 pe 0 liya jaata hai. To:

Colab ka chain: Zero weights → z = 0 → ReLU gradient = 0 → no useful weight update → learning can stop. Colab result (make_moons, 2→64→64→64→64→2 ReLU): final loss 0.693147, test accuracy 50.00%. 0.693147 = ln 2 = do classes me pure 50-50 guess ka cross-entropy. Matlab model ne kuch nahi seekha.

Zero BIAS bilkul theek hai

Symmetry tootne ke liye weights ka random hona kaafi hai. Agar weights random hain to har neuron ka z pehle se alag hai; bias 0 se shuru karne me koi problem nahi. Isliye standard practice: weights random (Xavier/He), biases zero (Colab ke saare initializers me nn.init.zeros_(layer.bias)). Ulta case: weights sab 0 aur bias sab barabar (chahe 0 ya koi aur constant) → har neuron ka z = b, sab same → symmetry nahi tootti (teacher ka concept check).

21.3 Constant initialization — same problem, upar se scale ka jhanjhat

Saare weights ek constant c (jaise 0.5) rakho? Zero se alag hai, lekin sab rows phir bhi same: same z, same output, same gradient, same update. Symmetry wahi ki wahi. Colab: "Same constant weights → same neuron behavior → same gradients → neurons remain identical → reduced learning capacity". Ek layer jo edge, curve, corner alag-alag seekh sakti thi, wo ek hi feature ko repeat karti hai.

Teacher ka worked example

Input [1, 2], har neuron ki weight row [c, c], tanh activation.

  1. Har neuron: z = c·1 + c·2 = 3c
  2. c = 0: tanh(0) = 0 (sab neurons 0)
  3. c = 2: z = 6, tanh(6) ≈ 0.999988 (sab neurons 0.999988)

Non-zero constant se output badla, lekin neurons phir bhi identical. Upar se bada constant tanh ko saturate kar deta hai (tanh′(6) = 1 − 0.999988² ≈ 0.0000246, gradient lagbhag zero).

Scale ka jhanjhat: Colab ka DeepMLP, sab weights 0.5, ReLU

Input [1, 1], bias 0, har hidden layer me 64 neurons.

  1. Layer 1: har neuron z = 0.5×1 + 0.5×1 = 1 → ReLU = 1
  2. Layer 2: har neuron 64 inputs (sab 1) → z = 64 × 0.5 × 1 = 32
  3. Layer 3: 64 × 0.5 × 32 = 1024; Layer 4: 64 × 0.5 × 1024 = 32768
  4. Output: dono class logits = 64 × 0.5 × 32768 = 1,048,576

Har layer pe ×32 explosion, aur phir bhi dono logits barabar (dono output rows same) → softmax 50-50. Colab result: loss 0.693151, accuracy 50.00%.

21.4 Random initialization — symmetry toot gayi, ab scale ka sawaal

Har weight ko random number do (jaise normal distribution N(0, σ²) se, mean 0, std σ). Ab har neuron ka starting point alag, to symmetry tooti. Colab: "Random initialization solves the symmetry problem because different neurons start with different weights." Lekin teacher ki line yaad rakho: "Random is a start. Scale is the difference." σ kitna ho?

Kahani: Chinese whispers

Class me 10 bachche line me baithe hain, message kaan me aage pass karna hai. Agar har bachcha dheere bole (aawaaz aadhi kar de), to 10ve tak message sunai hi nahi dega (vanishing). Agar har bachcha chillaa ke double aawaaz me bole, to 10ve tak kaan phat jayenge (exploding). Message tabhi sahi pahunchega jab har bachcha utni hi aawaaz me bole jitni usne suni. Har layer ek bachcha hai; weights ka scale uski "aawaaz ka multiplier".

Too small: N(0, 0.01²)

Har layer pe signal ka multiplier = nin σ² (aage derive karenge). Width 256 aur σ = 0.01: 256 × 0.0001 = 0.0256 per layer (std ke hisaab se √0.0256 = 0.16, yaani har layer pe std ×0.16). 10 layers ke baad std 0.1610 ≈ 1.1 × 10−8. Activations practically 0, aur backprop me gradients bhi isi tarah shrink hote hain → vanishing gradient, learning bahut slow/band. Width 64 pe: 64 × 0.0001 = 0.0064 per layer → 10 layers me 0.006410 ≈ 1.15 × 10−22 (second moment). Colab ka "Naive Random" (std = 0.005) ka result: loss 0.693147, accuracy 50.20%, yaani zero-init jaisa bekaar, jabki symmetry tooti hui thi! Matlab random hona kaafi nahi.

Too large: N(0, 1) with many inputs

Width 256, σ = 1: multiplier 256 × 1 = 256. Pre-activation z ka variance 256, std 16. Ab:

Teacher ka numerical check: fan-in 64, inputs unit variance, weights N(0, 1) → Var(z) = 64 × 1 × 1 = 64. Aur width 64 pe: σ = 1 → multiplier 64; σ = 0.125 → 64 × 0.125² = 64 × 0.015625 = 1 (perfect); σ = 0.0625 → 64 × 0.0625² = 0.25, aur 3 linear layers ke baad q₃ = 0.25³ = 0.015625. σ aadha karne se multiplier chauthai hota hai (σ² ki wajah se).

21.5 Variance analysis: Var(z) = nin · Var(w) · Var(x)

Ab wo formula derive karte hain jisse Xavier aur He dono nikle. Ek neuron (bias = 0):

z = w1x1 + w2x2 + … + wnxn = Σj=1..nin wjxj
  • Assumptions: saare wj independent, mean 0, same variance Var(w); saare xj independent, same distribution; weights aur inputs ek-doosre se independent; abhi ke liye x bhi zero-mean

Step-by-step derivation

  1. Ek term ka variance: Var(w x) = E[w²x²] − (E[w x])² (variance = square ka mean − mean ka square)
  2. Independence se: E[w²x²] = E[w²]·E[x²] aur E[w x] = E[w]·E[x] = 0·E[x] = 0
  3. Zero mean ke liye E[w²] = Var(w) aur E[x²] = Var(x), to Var(w x) = Var(w)·Var(x)
  4. Independent terms ke sum ka variance = variances ka sum: Var(z) = Σj Var(wjxj) = nin·Var(w)·Var(x)
  5. Hum chahte hain signal na bade na ghate: Var(z) = Var(x) ⇒ nin·Var(w) = 1 ⇒ Var(w) = 1/nin

Forward rule: nin·Var(w) ≈ 1. Ye LeCun initialization hai (LeCun et al. 1998): Var(w) = 1/nin, std = 1/√nin.

Teacher ki sheet isko second moment me likhti hai (jo non-zero-mean inputs, jaise ReLU outputs, pe bhi sahi hai): E[z²] = nin Var(w) E[a²], aur linear layers ke liye recurrence qℓ = nin σ² qℓ−1. Equal-width linear network me qL = (nσ²)L q0: multiplier 1 se upar → exponential growth, 1 se neeche → exponential decay. Depth jitni zyada, chhoti galti utni badi.

Backward direction: backprop me error signal ulti disha me jaata hai; ek input ka gradient nout neurons ke δ ka weighted sum hai. Same logic se: Var(δin) = nout·Var(w)·Var(δout) ⇒ gradients stable rakhne ke liye nout·Var(w) = 1. Jab nin ≠ nout, dono shartein ek saath poori nahi ho sakti.

21.6 Xavier / Glorot initialization (2010) — tanh/sigmoid ke liye

Xavier Glorot aur Yoshua Bengio ne paper "Understanding the difficulty of training deep feedforward neural networks" (AISTATS 2010) me compromise diya: forward (nin) aur backward (nout) dono ka average lo.

Var(w) = 2nin + nout
Xavier normal: w ~ N(0, σ²),  σ = √2 / (nin + nout)
Xavier uniform: w ~ U[−a, a],  a = √6 / (nin + nout)
  • nin = fan_in (inputs per neuron), nout = fan_out (outputs of the layer)
  • Formula variance deta hai; normal sampling ke liye uska square root (std) lo
  • 2/(nin + nout) = 1/nin aur 1/nout ke beech ka compromise (nin = nout = n ho to exactly 1/n)
  • Wider layer → zyada contributions add hote hain → chhote weights chahiye

Uniform me 6 kahan se aaya?

Derivation

  1. U[−a, a] ki density = 1/(2a) (total area 1), mean = 0
  2. Var = E[w²] = ∫−aa w² · (1/(2a)) dw = (1/(2a)) · [w³/3]−aa = (1/(2a)) · (2a³/3) = a²/3
  3. Target variance se barabar karo: a²/3 = 2/(nin + nout) ⇒ a² = 6/(nin + nout)

a = √(6/(nin + nout)). "6 = 3 × 2": 3 uniform ke variance (a²/3) se, 2 Xavier ke numerator se.

Chalo numbers se samjhein

  1. Colab example, fan_in = fan_out = 100: σ = √(2/200) = √0.01 = 0.1 → W ~ N(0, 0.1²); ek possible sample [0.04, −0.08, 0.12, −0.03, 0.07]
  2. 256 → 128 layer: Var = 2/(256 + 128) = 2/384 = 0.005208; σ = √0.005208 = 0.0722; uniform a = √(6/384) = √0.015625 = 0.125
  3. 784 → 256 layer (MNIST pehli layer): Var = 2/1040 = 0.001923; σ = 0.0439; a = √(6/1040) = 0.0760
  4. Teacher: 128 → 128: Var = 1/128, σ ≈ 0.08839. 256 → 512: Var = 2/768 = 1/384 ≈ 0.00260, σ ≈ 0.0510 (MCQ trap: 0.00260 variance hai, std nahi). Is layer ka forward multiplier 256/384 = 2/3, backward 512/384 = 4/3: compromise dono ko 1 ke paas rakhta hai, exactly nahi. 128 → 512: σ = √(2/640) = 0.0559, uniform bounds ±0.0968.

Kab use karo: tanh, sigmoid, linear (symmetric, zero ke paas linear activations). Teacher: "For tanh, it is a useful starting point, not a promise of constant variance at every depth." Hamare experiment me tanh + Xavier (gain 1) ka activation std 10 layers me 0.628 → 0.224 tak dheere-dheere ghata (tanh signal ko compress karta hai). PyTorch me isliye gain argument hai: nn.init.calculate_gain('tanh') = 5/3 ≈ 1.6667; xavier_uniform_(w, gain=5/3) se tanh network me spread lagbhag stable rehta hai (numpy check: 10 layers me ~0.65 pe tika rahta hai). Sigmoid ka gain 1.

ReLU ke saath Xavier: ReLU aadha signal maar deta hai (aage dekho), to Xavier (gain 1) + ReLU me har layer pe q aadha: (½)10 = 1/1024 ≈ 0.000977 after 10 layers. Isliye ReLU ke liye He.

21.7 He / Kaiming initialization (2015) — ReLU ke liye

Kahani

Chinese whispers me ek naya rule: har bachcha sirf "positive" shabd aage bolta hai, negative wale nigal jaata hai (ReLU). Aadha message har step pe gayab! To har bachche ko double zor se bolna padega taaki total aawaaz utni hi rahe. He init yahi "double" hai.

Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun (Microsoft Research) ne paper "Delving Deep into Rectifiers" (ICCV 2015) me ReLU ke liye formula diya (isi paper me PReLU bhi aaya).

Var(w) = 2nin   →   He normal: σ = √2 / nin   ·   He uniform: a = √6 / nin
Leaky ReLU (negative slope α): Var(w) = 2(1 + α²) nin
  • nin = fan_in (mode='fan_in'); sirf inputs count hote hain
  • "2" = ReLU ki wajah se khoye aadhe signal ki bharpaai
  • α = leaky ReLU ka negative slope; α = 0 pe wapas 2/nin

Factor 2 kahan se? (step by step)

  1. z zero ke around symmetric hai (jaise N(0, v)). Positive aur negative half E[z²] me barabar contribute karte hain.
  2. ReLU negative half ko 0 kar deta hai: E[ReLU(z)²] = ½ E[z²]
  3. Ek layer: qout = ½ · nin · Var(w) · qin
  4. qout = qin chahiye ⇒ ½ · nin · Var(w) = 1 ⇒ Var(w) = 2/nin
  5. Check: nin × (2/nin) × ½ = 1

He = Xavier-jaisa forward rule × 2. Leaky ReLU me negative side α² hissa bachata hai, to retained fraction (1 + α²)/2 ⇒ Var(w) = 2/((1 + α²) nin).

Chalo numbers se samjhein

  1. Colab, fan_in = 100: σ = √(2/100) = √0.02 ≈ 0.141 (Xavier same layer pe 0.1 deta). Sample: [0.06, −0.13, 0.18, −0.05, 0.11]. He ka spread bada hai, lekin iska matlab "zyada positive weights" nahi: dono taraf bade magnitudes.
  2. 256 → 128 layer: Var = 2/256 = 0.0078125; σ = 0.0884; uniform bound √(6/256) = √0.0234375 = 0.1531
  3. 784 → 256 layer: Var = 2/784 = 0.002551; σ = 0.0505; bound √(6/784) = 0.0875
  4. Teacher, fan_in = fan_out = 100: He Var = 2/100 = 0.02, Xavier Var = 2/200 = 0.01, ratio He/Xavier = 2 (variance ka ratio; std ka ratio √2 ≈ 1.414)
  5. Teacher, width 128, qin = 1: pre-activation variance 128 × (2/128) × 1 = 2; ReLU output q = ½ × 2 = 1. Lekin ReLU outputs ka mean positive hai: E[a] = √(v/2π) = √(2/2π) = 1/√π ≈ 0.5642, to Var(a) = 1 − 1/π ≈ 0.6817. Second moment 1 preserve hota hai, variance 0.68 (ReLU "variance aadha karta hai" kehna technically galat; second moment aadha hota hai).
  6. Colab ki pehli layer fan_in = 2: σ = √(2/2) = 1; isliye Colab ke He weights [−0.6696, 0.3045, …] bade dikhte hain (printed std 1.0558).

21.8 Ek table me saare initializers

InitFormula (Var(w))Normal std / Uniform boundBest withProblem it solves / problem it has
Zero0sab 0sirf biases ke liyeSymmetry: sab neurons identical; ReLU ke saath gradients bhi 0
Constant (c)0 (sab same)sab ckahin nahi (hidden weights)Symmetry wahi; bada c → saturation/explosion
Naive random smallσ² (jaise 0.01²)N(0, 0.01²)controlled experimentsSymmetry tooti, lekin signal shrink → vanishing
Naive random largeσ² (jaise 1)N(0, 1)kahin nahitanh/sigmoid saturate (vanishing), ReLU explode
LeCun1/nin√(1/nin) / √(3/nin)linear, SELU, tanh (approx)Forward variance preserve
Xavier / Glorot (2010)2/(nin + nout)√(2/(nin+nout)) / √(6/(nin+nout))tanh, sigmoid, linearForward + backward ka compromise; ReLU ke saath signal har layer aadha
He / Kaiming (2015)2/nin√(2/nin) / √(6/nin)ReLU, leaky ReLU (2/((1+α²)nin))ReLU ke aadhe signal ki bharpaai; linear activation ke saath signal har layer double
PyTorch default (Linear/Conv)1/(3·nin)U(−1/√nin, 1/√nin)chhote/medium networks, BN wale networksKaam chalaau; deep plain ReLU nets ke liye He behtar

Teacher ki "field guide" ka saar: Zero/constant → sirf biases, watch for symmetric units. Naive normal → width-dependent growth/decay. Xavier → linear/tanh, watch for ReLU signal decay at gain 1. He → ReLU, watch for linear signal growth.

21.9 PyTorch ke defaults: agar tum kuch na karo to?

nn.Linear aur nn.Conv2d banate hi weights already random hote hain. Andar ka code (reset_parameters) roughly ye karta hai:

PyTorch source ka idea (nn.Linear.reset_parameters)

nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))     # weight
bound = 1 / math.sqrt(fan_in)                              # bias
nn.init.uniform_(self.bias, -bound, bound)

a = √5 ka matlab kya nikla?

  1. kaiming_uniform_ ka default nonlinearity 'leaky_relu' hai, gain = √(2/(1 + a²)) = √(2/(1 + 5)) = √(1/3) ≈ 0.5774
  2. Kaiming uniform bound = gain × √(3/fan_in) = √(1/3) × √3/√fan_in = 1/√fan_in
  3. To default weight ~ U(−1/√fan_in, 1/√fan_in), variance (1/fan_in)/3 = 1/(3·fan_in). Bias bhi U(−1/√fan_in, 1/√fan_in).
  4. Example nn.Linear(256, 128): bound = 1/16 = 0.0625, std = 0.0625/√3 ≈ 0.0361. nn.Linear(784, 256): bound ≈ 0.0357.

PyTorch default = U(−1/√fan_in, +1/√fan_in), He/LeCun se chhota scale. Conv2d me bhi same, bas fan_in = in_channels × kH × kW. BatchNorm ke defaults: γ = 1, β = 0, running_mean = 0, running_var = 1.

Humne course ka Colab experiment (neeche 21.12) bina kisi explicit init ke bhi chalaya: PyTorch default se test accuracy 81.60% (loss 0.578856) aayi: Xavier (82.60%) ke aas-paas, He (87.80%) se kam. Plain deep ReLU network me He ka fayda saaf dikhta hai.

21.10 nn.init toolbox — har function ek nazar me

Saare functions torch.nn.init me hain, naam ke end me underscore (_) = in-place: tensor ko wahin badal dete hain (andar se no_grad me chalte hain, to Parameter pe seedha laga sakte ho).

PyTorch · har initializer ko Linear(256, 128) pe chala ke dekho

import math
import torch
import torch.nn as nn

torch.manual_seed(0)
layer = nn.Linear(256, 128)          # weight shape (128, 256) = (fan_out, fan_in)
w = layer.weight
print("default : max", round(w.max().item(), 4), " std", round(w.std().item(), 4),
      " (theory bound 1/sqrt(256) =", 1 / math.sqrt(256), ")")

def show(name):
    print(f"{name:15s} mean {w.mean().item():+.4f}  std {w.std().item():.4f}  "
          f"min {w.min().item():+.4f}  max {w.max().item():+.4f}")

nn.init.zeros_(w)                               # sab 0
show("zeros_")
nn.init.constant_(w, 0.5)                       # sab 0.5
show("constant_ 0.5")
nn.init.normal_(w, mean=0.0, std=0.01)          # N(0, 0.01^2)
show("normal_ 0.01")
a = math.sqrt(6 / (256 + 128))                  # Xavier ka bound = 0.125
nn.init.uniform_(w, -a, a)                      # U(-a, a) haath se
show("uniform_ +-a")
nn.init.xavier_uniform_(w)                      # wahi cheez built-in (gain=1)
show("xavier_uniform_")
nn.init.xavier_normal_(w)                       # std = sqrt(2/(fan_in+fan_out))
show("xavier_normal_")
nn.init.kaiming_uniform_(w, mode='fan_in', nonlinearity='relu')   # bound = sqrt(6/fan_in)
show("kaiming_unif_")
nn.init.kaiming_normal_(w, mode='fan_in', nonlinearity='relu')    # std = sqrt(2/fan_in)
show("kaiming_norm_")
nn.init.zeros_(layer.bias)                      # bias zero: bilkul theek
print("theory: xavier std", round(math.sqrt(2 / 384), 4), " he std", round(math.sqrt(2 / 256), 4),
      " he bound", round(math.sqrt(6 / 256), 4))
print("gains:", nn.init.calculate_gain('relu'), nn.init.calculate_gain('tanh'), nn.init.calculate_gain('sigmoid'))

Output

default : max 0.0625  std 0.036  (theory bound 1/sqrt(256) = 0.0625 )
zeros_          mean +0.0000  std 0.0000  min +0.0000  max +0.0000
constant_ 0.5   mean +0.5000  std 0.0000  min +0.5000  max +0.5000
normal_ 0.01    mean +0.0000  std 0.0100  min -0.0407  max +0.0456
uniform_ +-a    mean +0.0002  std 0.0723  min -0.1250  max +0.1250
xavier_uniform_ mean +0.0006  std 0.0722  min -0.1250  max +0.1250
xavier_normal_  mean +0.0003  std 0.0720  min -0.2986  max +0.2733
kaiming_unif_   mean +0.0006  std 0.0882  min -0.1531  max +0.1531
kaiming_norm_   mean -0.0003  std 0.0888  min -0.3355  max +0.3384
theory: xavier std 0.0722  he std 0.0884  he bound 0.1531
gains: 1.4142135623730951 1.6666666666666667 1

Dekho: haath ka U(−0.125, 0.125) aur xavier_uniform_ same range deta hai (256 → 128 ke liye a = 0.125, jo humne 21.6 me nikala tha). Uniform aur normal dono ka std ≈ 0.0722 (same variance), bas normal ke kuch values bound se bahar ja sakte hain (±0.30). He ke values std 0.0884 aur bound 0.1531, exactly hamare calculations.

Poore model pe init lagana: model.apply() pattern

model.apply(fn) model ke har sub-module (recursively, children pehle, phir khud model) pe fn chalata hai. Function ke andar isinstance se check karo ki layer kis type ki hai.

PyTorch · init_weights + model.apply

import torch
import torch.nn as nn

def init_weights(m):
    # model.apply() har sub-module ko yahan bhejega
    if isinstance(m, nn.Linear):
        nn.init.kaiming_normal_(m.weight, mode='fan_in', nonlinearity='relu')
        if m.bias is not None:              # bias=False wali layer pe bias None hota hai
            nn.init.zeros_(m.bias)

torch.manual_seed(0)
model = nn.Sequential(
    nn.Linear(784, 256), nn.ReLU(),
    nn.Linear(256, 128), nn.ReLU(),
    nn.Linear(128, 10)
)
model.apply(init_weights)          # recursively har layer pe init_weights chalega

for name, p in model.named_parameters():
    print(f"{name:9s} shape {str(tuple(p.shape)):11s} std {p.std().item():.4f}")

Output

0.weight  shape (256, 784)  std 0.0505
0.bias    shape (256,)      std 0.0000
2.weight  shape (128, 256)  std 0.0884
2.bias    shape (128,)      std 0.0000
4.weight  shape (10, 128)   std 0.1207
4.bias    shape (10,)       std 0.0000

Theory se match: 784 → 256 pe √(2/784) = 0.0505, 256 → 128 pe √(2/256) = 0.0884, 128 → 10 pe √(2/128) = 0.125 (sample std 0.1207, sirf 1280 numbers hain to thoda farak). CNN ke liye isinstance(m, (nn.Conv2d, nn.Linear)) likho. Course Colab ka style bhi yahi hai, bas apply ki jagah loop:

Course Colab style · model.modules() loop

def initialize_he(model):
    for layer in model.modules():                  # saare modules, nested bhi
        if isinstance(layer, nn.Linear):
            nn.init.kaiming_normal_(layer.weight, mode="fan_in", nonlinearity="relu")
            nn.init.zeros_(layer.bias)

def initialize_xavier(model):
    for layer in model.modules():
        if isinstance(layer, nn.Linear):
            nn.init.xavier_normal_(layer.weight)   # Xavier NORMAL
            nn.init.zeros_(layer.bias)

def initialize_naive_random(model, std=0.005):
    for layer in model.modules():
        if isinstance(layer, nn.Linear):
            nn.init.normal_(layer.weight, mean=0.0, std=std)
            nn.init.zeros_(layer.bias)

21.11 Experiment: 10 layers ke through activations ka haal

Ab sab theory ko apni aankhon se dekhte hain. Ek 256-wide, 10-layer network (har layer Linear(256, 256) + activation, bias 0), 1000 random inputs (mean 0, std 1). Har init ke liye har layer ke baad activation ka std record karo, aur backward me pre-activation gradient (∂L/∂z) ka std layer 10 aur layer 1 pe dekho (upar se N(0, 1) gradient bheja).

PyTorch · activation std through 10 layers (tanh aur ReLU)

import torch
import torch.nn as nn

def build(act, init, width=256, depth=10):
    layers = []
    for _ in range(depth):
        lin = nn.Linear(width, width)
        if init == "zero":
            nn.init.zeros_(lin.weight)
        elif init == "small":                              # bahut chhote weights
            nn.init.normal_(lin.weight, mean=0.0, std=0.01)
        elif init == "large":                              # bahut bade weights
            nn.init.normal_(lin.weight, mean=0.0, std=1.0)
        elif init == "xavier":
            nn.init.xavier_normal_(lin.weight)
        elif init == "he":
            nn.init.kaiming_normal_(lin.weight, mode='fan_in', nonlinearity='relu')
        nn.init.zeros_(lin.bias)                           # bias hamesha 0
        layers += [lin, nn.Tanh() if act == "tanh" else nn.ReLU()]
    return nn.Sequential(*layers)

torch.manual_seed(123)
x = torch.randn(1000, 256)                       # 1000 samples, mean 0, std 1
for act in ["tanh", "relu"]:
    print("=====", act)
    for init in ["zero", "small", "large", "xavier", "he"]:
        torch.manual_seed(0)
        net = build(act, init)
        h, stds, zs = x, [], []
        for layer in net:
            h = layer(h)
            if isinstance(layer, nn.Linear):
                h.retain_grad()                  # pre-activation z ka gradient bhi rakhna
                zs.append(h)
            else:
                stds.append(h.std().item())      # activation ke baad std record
        h.backward(torch.randn_like(h))          # upar se aata gradient ~ N(0, 1)
        g10, g1 = zs[-1].grad.std().item(), zs[0].grad.std().item()
        print(f"{init:7s}", " ".join(f"{s:.3g}" for s in stds),
              f"| mean L10 {h.mean().item():.3g} | dL/dz std: L10 {g10:.3g} -> L1 {g1:.3g}")

Output (har row: layer 1 se 10 tak activation std | layer 10 ka mean | gradient std)

===== tanh
zero    0 0 0 0 0 0 0 0 0 0 | mean L10 0 | dL/dz std: L10 1 -> L1 0
small   0.157 0.0251 0.00401 0.000638 0.000102 1.62e-05 2.59e-06 4.11e-07 6.57e-08 1.04e-08 | mean L10 -4.78e-11 | dL/dz std: L10 1 -> L1 6.62e-08
large   0.975 0.974 0.974 0.974 0.974 0.975 0.974 0.974 0.974 0.974 | mean L10 0.00291 | dL/dz std: L10 0.186 -> L1 3.13e+03
xavier  0.628 0.486 0.408 0.355 0.32 0.292 0.269 0.251 0.237 0.224 | mean L10 -0.00106 | dL/dz std: L10 0.952 -> L1 0.267
he      0.721 0.633 0.595 0.575 0.566 0.56 0.557 0.555 0.555 0.553 | mean L10 -0.00256 | dL/dz std: L10 0.745 -> L1 0.779
===== relu
zero    0 0 0 0 0 0 0 0 0 0 | mean L10 0 | dL/dz std: L10 0 -> L1 0
small   0.0939 0.0101 0.00113 0.000128 1.48e-05 1.67e-06 1.67e-07 1.89e-08 2.14e-09 2.2e-10 | mean L10 1.4e-10 | dL/dz std: L10 0.681 -> L1 1.95e-09
large   9.39 101 1.13e+03 1.28e+04 1.48e+05 1.67e+06 1.67e+07 1.89e+08 2.14e+09 2.2e+10 | mean L10 1.4e+10 | dL/dz std: L10 0.681 -> L1 1.95e+09
xavier  0.587 0.396 0.275 0.196 0.141 0.0994 0.0623 0.0439 0.0311 0.0201 | mean L10 0.0127 | dL/dz std: L10 0.681 -> L1 0.0283
he      0.83 0.793 0.777 0.783 0.8 0.795 0.705 0.702 0.703 0.642 | mean L10 0.407 | dL/dz std: L10 0.681 -> L1 0.641
Activation + InitL1L2L3L5L7L10mean L10∂L/∂z std: L10 → L1Verdict
tanh + zero00000001 → 0Kuch nahi bacha
tanh + N(0, 0.01²)0.1570.02510.004010.0001022.59e−61.04e−8≈ 01 → 6.6e−8Har layer ×0.16: vanishing
tanh + N(0, 1)0.9750.9740.9740.9740.9740.9740.0030.186 → 3130±1 pe saturated; gradient unstable
tanh + Xavier0.6280.4860.4080.320.2690.224−0.0010.952 → 0.267Dheere ghatta, zinda
tanh + He0.7210.6330.5950.5660.5570.553−0.0030.745 → 0.779Stable
ReLU + zero00000000 → 0Dead network
ReLU + N(0, 0.01²)0.09390.01010.001131.48e−51.67e−72.2e−101.4e−100.681 → 1.95e−9Vanishing
ReLU + N(0, 1)9.3910111301.48e51.67e72.2e101.4e100.681 → 1.95e9Exploding
ReLU + Xavier0.5870.3960.2750.1410.06230.02010.01270.681 → 0.0283Har layer ~×0.7: dheere vanishing
ReLU + He0.830.7930.7770.800.7050.6420.4070.681 → 0.641Stable (best match)
Layer 1 Layer 4 Layer 10 tanh + N(0, 0.01²) std 0.157 −1 +1 std 6.4e−4 −1 +1 std 1.0e−8 −1 +1 tanh + N(0, 1) std 0.975 −1 +1 std 0.974 −1 +1 std 0.974 −1 +1 tanh + Xavier std 0.628 −1 +1 std 0.355 −1 +1 std 0.224 −1 +1 ReLU + He std 0.83 0 3 std 0.783 0 3 std 0.642 0 3 Bars ki height har panel me normalized hai; chaudai (spread) dekho.
256-wide, 10-layer network, 1000 random inputs (PyTorch se nikale asli histograms). Row 1: chhote weights se distribution har layer pe patla hota gaya, layer 10 tak sab kuch 0 pe ek spike (vanishing). Row 2: bade weights se tanh ±1 pe chipak gaya (U-shape = saturation). Row 3: Xavier se tanh ka spread dheere-dheere ghatta hai par zinda rehta hai. Row 4: He + ReLU ka shape har layer pe lagbhag same (yahan sirf positive values dikhaye hain; lagbhag aadhe outputs exact 0 hote hain).

21.12 Course Colab ka fair comparison: accuracy pe asar

Colab setup: make_moons (2000 points, noise 0.22, 75/25 split), network 2 → 64 → 64 → 64 → 64 → 2 (ReLU), nn.CrossEntropyLoss, full-batch torch.optim.SGD(lr=0.03), 100 epochs, set_seed(7) har method ke liye. Sirf initialization badla, baaki sab same (fair comparison).

Course Colab · training function (har init ke liye same)

def train_model(method, epochs=100, learning_rate=0.03):
    set_seed(7)                                  # har method ko same starting randomness
    model = DeepMLP()                            # 2-64-64-64-64-2, ReLU
    apply_initialization(model, method)          # sirf ye line method ke hisaab se badalti hai

    loss_fn = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
    history = []
    for epoch in range(epochs):
        model.train()
        logits = model(X_train_t)                # full batch
        loss = loss_fn(logits, y_train_t)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        history.append(loss.item())

    model.eval()
    with torch.no_grad():
        predictions = model(X_test_t).argmax(dim=1)
        accuracy = (predictions == y_test_t).float().mean().item()
    return model, history, accuracy
InitializationWeights kaiseFinal train lossTest accuracyKyun
Zerozeros_0.69314750.00%ReLU(0) aur ReLU′(0) = 0: kuch nahi seekha; loss = ln 2
Constant 0.5constant_(w, 0.5)0.69315150.00%Symmetry: dono logits barabar, 50-50
Naive Randomnormal_(std=0.005)0.69314750.20%Signal 4 layers me hi mar gaya (vanishing)
Xavierxavier_normal_0.49303482.60%Theek scale, par ReLU ke saath thoda chhota
Hekaiming_normal_(relu)0.27467887.80%ReLU ke liye bana: best
(PyTorch default)kuch nahi kiya0.57885681.60%U(±1/√fan_in): kaam chalaau (humne same setup me chalaya)

Colab ka expected result: He / Xavier > Zero / Constant / Naive Random. Humne ye Colab dobara chalaya (PyTorch 2.14, CPU): pehli paanch rows ke numbers bilkul same aaye. Important lesson: Naive Random me symmetry tooti hui thi, phir bhi 50.20%: randomness zaroori hai, lekin kaafi nahi; scale bhi sahi chahiye.

Aage ke chapters se connection: BatchNorm har layer pe activations ko wapas ~N(0, 1) pe le aata hai, isliye BN wale networks init ke prati kam sensitive hote hain (lecture slide: "make weights easier to initialize"). ResNets (Chapter 25) me conv ke baad BN aur He init dono hote hain. Conv layers ke liye fan_in = in_channels × k × k yaad rakhna (Chapter 23).

Exam Radar · Weight Initialization

  • Goals: symmetry breaking + forward activation variance stable + backward gradient variance stable.
  • Zero/constant init → symmetry problem: same output, same gradient, same update, neurons identical forever. Zero weights + ReLU → saare gradients 0. Zero bias theek hai.
  • ∂L/∂Wij = δixj: equal δ → equal rows. x = [1, 2], δ = 0.5 → row [0.5, 1.0].
  • Var(z) = nin·Var(w)·Var(x) (independent, zero-mean). Forward: ninVar(w) = 1. Backward: noutVar(w) = 1. Linear stack: qL = (nσ²)Lq0.
  • Fan-in 64, N(0, 1) → Var(z) = 64. Width 64: σ = 0.125 → multiplier 1; σ = 0.0625 → 0.25.
  • LeCun: 1/nin. Xavier/Glorot (2010, Glorot & Bengio): 2/(nin + nout), normal std √(2/(nin+nout)), uniform a = √(6/(nin+nout)); tanh/sigmoid.
  • Uniform U[−a, a] ka variance = a²/3 → "6" ka source.
  • He/Kaiming (2015, He, Zhang, Ren, Sun): 2/nin, std √(2/nin), bound √(6/nin); ReLU halves second moment; leaky: 2/((1 + α²)nin).
  • Numbers: 100/100 → Xavier σ = 0.1, He σ ≈ 0.141, variance ratio 2. 256 → 128: Xavier σ 0.0722, a = 0.125; He σ 0.0884, bound 0.1531. 784 → 256: Xavier σ 0.0439; He σ 0.0505.
  • Xavier + ReLU: q × ½ per layer → (½)10 ≈ 0.000977; 50 layers 2−50 ≈ 8.88 × 10−16. He + linear: q doubles.
  • He width 128: E[a] = 1/√π ≈ 0.5642, Var(a) = 1 − 1/π ≈ 0.6817, second moment 1.
  • PyTorch default Linear/Conv2d: kaiming_uniform_(a=√5) = U(−1/√fan_in, 1/√fan_in); bias bhi same range.
  • calculate_gain: relu √2, tanh 5/3, sigmoid 1, linear 1.
  • Conv fan_in = Cin × kh × kw; fan_out = Cout × kh × kw.
  • Colab: Zero 50%, Constant 50%, Naive (0.005) 50.2%, Xavier 82.6%, He 87.8%.

Galti mat karna

  • Formula variance deta hai; normal_(std=...) me square root daalo. 2/768 = 0.0026 variance hai, std 0.051.
  • "Random init se sab theek" — nahi. Galat scale wala random (0.005 ya 1.0) bhi fail hota hai.
  • Constant non-zero init symmetry nahi todta. Sirf zero se alag hona kaafi nahi; neurons ek-doosre se alag hone chahiye.
  • Xavier me nin + nout; He me sirf nin (mode='fan_in'). Inko mix mat karo.
  • ReLU ke saath Xavier "galat" nahi chalta, bas deep networks me signal decay karta hai; He better match hai. tanh ke saath He bhi chal jaata hai par classic jodi tanh ↔ Xavier, ReLU ↔ He.
  • nn.init.uniform_(w) ka default range (0, 1) hai, symmetric nahi. Hamesha -a, a do.
  • nn.Linear(in, out).weight ka shape (out, in) hai: fan_in = weight.shape[1].
  • Kaiming functions ka default nonlinearity='leaky_relu' (a = 0 pe gain √2, ReLU jaisa); clarity ke liye 'relu' likho.

Dimaag me aise baitha lo

Photocopy cooks (zero/constant) kuch naya nahi banate; Chinese whispers me har bachcha utni hi aawaaz me bole jitni suni: n·Var(w) = 1. Xavier = 2/(in + out) for tanh; He = 2/in for ReLU (aadha signal, double variance).

Chapter 22

Hyperparameter Tuning with Optuna

Parameters vs hyperparameters · Manual · Grid · Random (Bergstra & Bengio 2012) · Bayesian / TPE · Study · Trial · Objective · Sampler · Pruner · suggest_float / suggest_int / suggest_categorical · Dynamic model · Pruning · Visualization · Labs 10, 11, 13, 14

22.1 Parameters vs Hyperparameters — khiladi vs coach

Kahani

Cricket academy me ek naya batsman aaya. Practice ke dauraan uske haath, kalaai, footwork khud-ba-khud adjust hote hain — har galat shot ke baad thoda sudhaar. Ye uski "seekhi hui" cheezein hain: parameters.

Lekin kuch faisle coach practice shuru hone se pehle leta hai: roz kitne net sessions (epochs), har session me kitni balls (batch size), har galti pe kitna zor se correct karna (learning rate), kaunsa coaching style (optimizer), kitne din aaram (dropout/regularization). Batsman ye khud nahi seekh sakta — ye hyperparameters hain. Aur galat plan ho to sabse talented batsman bhi bekaar khelega.

Lab 10 ka overview bilkul yahi kehta hai: "A neural network has two kinds of knobs: Parameters — the weights the network learns from data during training. Hyperparameters — settings we choose before training: number of layers, layer width, optimizer, learning rate, dropout, and so on." Aur: "a strong architecture with a bad learning rate loses to a simple one that's well tuned."

ParametersHyperparameters
Kaun set karta haiTraining (gradient descent / backprop)Hum (ya Optuna), training se pehle
ExamplesWeights W, biases b, BatchNorm ke γ, βlr, batch size, #layers, #neurons, dropout, optimizer, weight decay, epochs, activation, conv channels
Kahan dikhtemodel.parameters()Constructor arguments, optimizer arguments, DataLoader, loop range
Kaise milteLoss ka gradientSearch: manual / grid / random / Bayesian (Optuna)
Kis data pe judgeTraining set pe fitValidation set pe score

Labs me tune hone wale hyperparameters

HyperparameterCode me kahanOptuna call (Lab 10 ANN)Range
Hidden layersMyNN loopsuggest_int1 – 5
Neurons / layernn.Linear(in, n)suggest_int(step=8)8 – 128
Epochsfor epoch in range(epochs)suggest_int(step=10)10 – 50
Learning rateoptim.X(..., lr=)suggest_float(log=True)1e-5 – 1e-1
Dropoutnn.Dropout(p)suggest_float(step=0.1)0.1 – 0.5
Batch sizeDataLoader(batch_size=)suggest_categorical16, 32, 64, 128
OptimizerAdam / SGD / RMSpropsuggest_categoricalAdam, SGD, RMSprop
Weight decay (L2)optim.X(..., weight_decay=)suggest_float(log=True)1e-5 – 1e-3

Exam Radar

  • Parameters = learned (weights, biases). Hyperparameters = chosen before training (lr, batch size, layers, neurons, dropout, optimizer, weight decay, epochs).
  • Hyperparameters ko gradient descent se nahi seekhte; search karte hain aur validation score se judge karte hain.
  • "Hyperparameters are searched, not learned" (Lab 10 takeaway).

Galti mat karna

  • Learning rate "parameter" nahi, hyperparameter hai. Weight decay λ bhi hyperparameter hai.

Dimaag me aise baitha lo

Khiladi jo practice me khud seekhe = parameter. Coach jo practice se pehle tay kare = hyperparameter.

22.2 Manual tuning — "babysitting" aur uski problems

Sabse pehla tareeka: khud haath se. lr = 0.01 try karo, accuracy dekho; 0.001 try karo; phir neurons badlo... Lab 13: "Instead of manually trying values such as learning rate = 0.01, learning rate = 0.001, learning rate = 0.0005 … we define a search space and let Optuna decide which combinations are worth trying."

Sabse important rule: tuning validation pe, test pe nahi

Lab 13 (CIFAR-10): "Important: Optuna uses the validation set, not the test set. The test set is kept untouched until final evaluation." 50,000 training images ko 80% train / 20% validation me baanto (train_test_split(..., test_size=0.20, stratify=y_full)), official 10,000 test images end tak band. Lab 10/11 ke notebooks simplicity ke liye objective me "test accuracy" return karte hain — exam/practice me yaad rakho ki sahi tareeka validation hai, warna test score optimistic (leaked) ho jaata hai. Lab 14: "Keep the test results for final reporting only; do not use them to choose hyperparameters."

Dimaag me aise baitha lo

Mock test se strategy chuno, board exam sirf ek baar do.

22.3 Grid Search — har combination, ek-ek karke

Kahani

Naye shehar me best samosa dhoondhna hai. Grid search wala dost kehta hai: "Har gali (10 galiyaan) × har dukaan (10 dukaanein per gali) — sab try karenge." 100 samose. Theek hai. Ab agar chutney ke 5 type, chai ke 4 type bhi combine karne hon: 100 × 5 × 4 = 2000 tastings. Dost ka pet phat jaayega.

Grid search: har hyperparameter ke liye kuch values ki list banao, aur saare combinations (Cartesian product) try karo. Simple, parallel, reproducible — lekin combinations ki ginti exponentially badhti hai.

#trials = k1 × k2 × … × kd   (sab ke k values hon to kd)
  • ki = hyperparameter i ki kitni values try kar rahe ho.
  • d = kitne hyperparameters. d exponent me hai → "curse of dimensionality".

Chalo numbers se samjhein — Lab 10 ka search space grid me

  1. Layers 1–5: 5 values. Neurons 8–128 step 8: (128 − 8)/8 + 1 = 16. Epochs 10–50 step 10: 5.
  2. lr (continuous; maan lo 5 grid points: 1e-5, 1e-4, 1e-3, 1e-2, 1e-1): 5. Dropout 0.1–0.5 step 0.1: 5.
  3. Batch size: 4. Optimizer: 3. Weight decay (maan lo 3 points): 3.
  4. Total: 5 × 16 × 5 × 5 × 5 × 4 × 3 × 3 = 360,000 trainings!
  5. Har training 1 minute: 360,000 min ÷ 60 = 6,000 hours ÷ 24 = 250 days.
  6. Chhota grid bhi: 8 hyperparameters × 3 values each = 3⁸ = 6,561 trials.

Ek hyperparameter jodo → trials guna ho jaate hain. Grid search sirf 2–3 hyperparameters tak practical hai.

Doosri problem: continuous hyperparameter (lr) ke liye grid sirf kuch fixed points dekhta hai; best value do grid points ke beech ho to kabhi nahi milegi. Aur agar ek hyperparameter bekaar hai (accuracy pe asar nahi), to grid uski har value ke liye poora training dobara karta hai — budget barbaad.

Dimaag me aise baitha lo

Grid = har gali ki har dukaan ka samosa. Do-teen galiyon tak theek, poore shehar me pet phat jaayega.

22.4 Random Search — Bergstra & Bengio (2012)

Random search: har trial me har hyperparameter ki value uske range se random uthao (lr ke liye log-uniform). Fixed budget (jaise 25 trials) do. Paper: Bergstra & Bengio, "Random Search for Hyper-Parameter Optimization", JMLR 2012 — dikhaya ki same budget me random search aksar grid se behtar hai.

Kyun behtar? "Low effective dimensionality"

Zyada tar problems me sirf kuch hi hyperparameters sach me matter karte hain (jaise lr), baaki ka asar kam hota hai. 3 × 3 grid (9 trials) me important hyperparameter ki sirf 3 alag values test hoti hain — baaki 6 trials unimportant parameter badalne me barbaad. Random 9 trials me important parameter ki 9 alag values test hoti hain.

Grid layout (9 trials) important parameter (jaise lr) → unimportant parameter important param ki 3 alag values try hui Random layout (9 trials) important parameter (jaise lr) → unimportant parameter important param ki 9 alag values try hui
Bergstra & Bengio wala idea: upar laal curve = important parameter ka asar (peak beech me kahin), left pencil line = unimportant parameter (flat, koi asar nahi). Grid ke 9 trials important axis pe sirf 3 jagah girte hain aur peak miss karte hain; random ke 9 trials 9 alag jagah — peak ke paas pahunchne ka zyada mauka.

Chalo numbers se samjhein — kitne random trials kaafi?

Maan lo search space ka top 5% "achha zone" hai. Ek random trial ke achha zone me na girne ki probability 0.95. n independent trials me ek bhi achha na mile: 0.95n.

  1. n = 10: P(kam se kam ek hit) = 1 − 0.95¹⁰ = 1 − 0.5987 = 0.4013
  2. n = 25: 1 − 0.95²⁵ = 1 − 0.2774 = 0.7226
  3. n = 60: 1 − 0.95⁶⁰ = 1 − 0.0461 = 0.9539

~60 random trials se 95% chance hai ki top-5% wali config mil jaaye — chahe kitne bhi hyperparameters hon! Grid me ye guarantee dimensions ke saath kharab hoti jaati hai.

Kamzori: random search yaad nahi rakhta. Trial 20 ko nahi pata ki trial 1–19 me kya achha chala. Achhe zone me gire to luck. Isi ka ilaaj → Bayesian optimization.

Exam Radar

  • Random search: Bergstra & Bengio, JMLR 2012. Same budget me usually better than grid.
  • Reason: only a few hyperparameters matter (low effective dimensionality); random tests more distinct values of each.
  • Grid trials = product of value counts → exponential in #hyperparameters.
  • Grid aur random dono past trials se nahi seekhte.

Dimaag me aise baitha lo

Grid ek hi teen jagah baar-baar khodta hai; random nau alag jagah khodta hai — sona milne ka chance zyada.

22.5 Bayesian Optimization aur TPE — jo pichhle trials se seekhe

Kahani

Samosa hunt, teesra dost. Pehle 8–10 dukaanein random try karta hai (explore). Notice karta hai: "Station road ki dukaanein achhi nikli, market wali bekaar." Ab wo zyada tar station road ke aas-paas try karta hai (exploit), beech-beech me ek-aadh nayi jagah bhi (taaki koi chhupa hua heera miss na ho). 25 samoson me se zyada tar badhiya — aur best samosa bhi usi ko mila.

Bayesian optimization ka idea: purane trials ke (hyperparameters → score) data se ek "andaaza model" (surrogate) banao ki search space me kahan score achha hone ki umeed hai; phir agla trial wahan chuno jahan umeed sabse zyada ho (acquisition function, jaise expected improvement). Ye exploration (naya area try) aur exploitation (achhe area ke paas try) ka balance hai.

TPE — Optuna ka default sampler

TPE = Tree-structured Parzen Estimator (Bergstra et al., 2011). Optuna me optuna.samplers.TPESampler — default. Kaam aise karta hai:

  1. Shuru ke n_startup_trials (default 10) trials random (exploration; abhi seekhne ke liye data nahi).
  2. Ab tak ke trials ko score ke hisaab se do group me baanto: "good" (top γ hissa — Optuna default: top 10%, yaani ceil(0.1 × n), maximum 25 trials) aur "bad" (baaki).
  3. Har group ke hyperparameter values ka density (smooth histogram) banao: l(x) = good trials ki density, g(x) = bad trials ki density.
  4. Kai candidate x (default n_ei_candidates=24) l(x) se sample karo, aur wo chuno jiska l(x) / g(x) sabse bada — yaani jo "good" jaisa zyada aur "bad" jaisa kam dikhe. Ye expected improvement maximize karne ke barabar hai.
  5. Trial chalao, score record karo, repeat.
Grid search (blind) 4 / 25 good · best 0.84 Random search (luck) 3 / 25 good · best 0.86 1 2 3 4 5 6 7 8 Optuna TPE (learns) 17 / 25 good · best 0.94
Lab 11 ki sheet "Grid vs random vs Optuna — same 25 trials, same search space": peela ellipse = good zone, laal = good-zone trial, khokhla = exploring/bekaar trial. Grid 4/25, random 3/25, Optuna (TPE) 17/25 — pehle 8 (numbered) broad exploration, phir jo kaam kiya usi pe commit. Best accuracy 0.84 / 0.86 / 0.94 (sheet ke illustrative numbers).

Sheet ka "why": "Grid and random search try trial N without knowing anything about trials 1 through N−1. Optuna's sampler reads every past result before choosing the next trial — so instead of spreading its budget evenly (or randomly) across the whole space, it spends most of its trials where the evidence says the good region is. Same budget, same space — Optuna just doesn't forget."

Optuna · TPE vs Random on a cheap fake objective (real run)

import math
import optuna
optuna.logging.set_verbosity(optuna.logging.WARNING)

def fake_accuracy(trial):
    # asli training ki jagah ek sasta formula: best lr = 1e-3, best dropout = 0.2
    lr = trial.suggest_float("lr", 1e-5, 1e-1, log=True)
    dropout = trial.suggest_float("dropout", 0.0, 0.5)
    return 0.95 - 0.04 * (math.log10(lr) + 3) ** 2 - 0.8 * (dropout - 0.2) ** 2

for name in ["RandomSampler", "TPESampler"]:
    bests, good = [], []
    for seed in range(10):
        sampler = getattr(optuna.samplers, name)(seed=seed)
        study = optuna.create_study(direction="maximize", sampler=sampler)
        study.optimize(fake_accuracy, n_trials=30)
        bests.append(study.best_value)
        good.append(sum(t.value > 0.93 for t in study.trials))   # "good zone" trials
    print(f"{name:14s} avg best = {sum(bests)/10:.4f} | avg good-zone trials = {sum(good)/10:.1f} / 30")

Output

RandomSampler  avg best = 0.9469 | avg good-zone trials = 6.3 / 30
TPESampler     avg best = 0.9496 | avg good-zone trials = 11.0 / 30

10 alag seeds ka average: TPE ne lagbhag double trials achhe zone me daale (11.0 vs 6.3) aur best bhi thoda behtar mila. (Pehle 10 trials dono ke random hote hain — TPE ka startup — baaki 20 me TPE seekh ke focus karta hai.) Asli training me har trial minutes/ghante leta hai, to "kam trials barbaad" = bahut bada fayda.

ManualGrid searchRandom searchBayesian / TPE (Optuna)
Kaise chunta haiInsaan ka andaazaSaare combinationsRandom samplesPichhle trials se seekh ke
Pichhle results use?Haan (dimaag se)NahiNahiHaan
Cost scalingInsaan ka timeExponential (kd)Budget fixed (n_trials)Budget fixed (n_trials)
Continuous rangesHaanSirf grid pointsHaanHaan (log scale bhi)
ParallelMushkilBahut aasaanBahut aasaanThoda kam natural (sequential learning)
Early stop of bad trialsHaanNahi (by default)Nahi (by default)Haan — pruners
Kab usePehli intuition1–3 hyperparameters, chhota spaceBada space, simple baselineMehenge trials, bada space — default choice

Exam Radar

  • Optuna default sampler = TPESampler (Tree-structured Parzen Estimator), Bayesian-style; learns from past trials.
  • TPE: split trials into good/bad, model l(x), g(x), pick x maximizing l(x)/g(x).
  • TPESampler(seed=42) → reproducible; n_startup_trials=10 random trials first (default).
  • Other samplers: RandomSampler, GridSampler, CMA-ES (CmaEsSampler).
  • Explore vs exploit balance.

Dimaag me aise baitha lo

Grid = andha, Random = kismat, TPE = yaaddasht.

22.6 Optuna ki bhasha — study, trial, objective, sampler, pruner

Kahani

School ka annual function: Principal (study) poore event ka incharge hai, saare results ka register rakhta hai. Selection teacher (sampler) decide karti hai agla kaunsa bachcha kya perform karega — pichhle performances dekh ke. Har performance (trial) ek baar hoti hai. Judge ka rule (objective function) har performance ko ek number deta hai. Aur ek sakht teacher (pruner) hai jo bekaar chal rahi performance ko beech me hi "bas, baith jao" bol deta hai, taaki time bache.

STUDY (optuna.create_study: direction, sampler, pruner) Sampler (TPE) kya try karein? Trial #k trial.suggest_*() objective(trial) build, train, eval Pruner (Median) trial.should_prune() Trial history saare purane scores trial.report(val_acc, epoch) prune? return score (ya TrialPruned) seekhta hai study.optimize(objective, n_trials=N) = ye loop N baar
Ek optimization loop (Lab 11 "Anatomy of an Optuna study"): study apne sampler se hyperparameters maangta hai, unhe trial ke andar tumhare objective ko deta hai, aur jo score wapas aata hai use yaad rakhta hai. Sampler hi wo cheez hai jo saare trials yaad rakhti hai. Pruner beech ke scores dekh ke bure trial ko jaldi rok sakta hai.
TermKya haiCode me kaise milta
StudyPoori search (container): sampler ka maalik, har trial chalata hai, best yaad rakhta haioptuna.create_study()
TrialEk attempt = hyperparameters ka ek specific combination, exactly ek baar tryobjective(trial) ka argument (Optuna khud pass karta hai)
Objective functionTumhara function: trial se values maango, model banao, train karo, score return karodef objective(trial): ... return accuracy
SamplerStrategy jo decide kare agla kya try karna hai; har pichhle trial se seekhta haistudy.sampler — default TPESampler
PrunerBeech ke (intermediate) scores dekh ke bekaar trials ko jaldi band karta haioptuna.pruners.MedianPruner() — default
directionScore bada achha ('maximize', accuracy) ya chhota achha ('minimize', loss)create_study(direction='maximize') — default minimize
n_trialsObjective kitni baar chalana hai (budget)study.optimize(objective, n_trials=20)
suggest_*()Sampler se ek value maangta hai aur us value ka search space define karta haitrial.suggest_float / _int / _categorical
best_trialAb tak ka sabse achha trialstudy.best_trial, study.best_value, study.best_params

Objective ka sabse chhota roop (Lab 11 sheet, FIG. 2):

Optuna · objective function ka anatomy

def objective(trial):                                   # 1. Optuna har trial pe call karta hai
    lr = trial.suggest_float("lr", 1e-5, 1e-1, log=True)   # 2. search space + value maango
    layers = trial.suggest_int("layers", 1, 5)
    model = build_model(layers)                         # 3. normal code: banao...
    train(model, lr=lr)                                 #    ...aur train karo
    accuracy = evaluate(model)                          # 4. unseen data pe naapo
    return accuracy                                     # 5. Optuna isi number ko max/min karega

Lab ka key takeaway: "The objective function is the contract: take a trial, train a model, return a score. Everything else is ordinary PyTorch." Optuna ko tumhara model, data, loss — kuch nahi pata; use sirf suggest calls aur return value dikhti hai.

Ye define-by-run style hai: search space pehle se ek dictionary me fix nahi karte; objective chalte-chalte suggest_* call karke space banata hai. Isliye conditional spaces aasaan: "agar 3 layers aaye to 3 widths poochho" (22.8).

Dimaag me aise baitha lo

Study = principal, Sampler = selection teacher, Trial = ek performance, Objective = judge, Pruner = "bas, baith jao" wali teacher.

22.7 Search space ka API — suggest_float, suggest_int, suggest_categorical

trial.suggest_float(name, low, high, *, step=None, log=False)
trial.suggest_int(name, low, high, *, step=1, log=False)
trial.suggest_categorical(name, choices)
  • name (string): hyperparameter ka naam; study.best_params me isi key se aayega. Ek trial me har naam ek baar (same naam dobara = same value).
  • low, high: range; dono inclusive (high bhi aa sakta hai).
  • log=True: value log scale pe uniformly sample (har "decade" — 1e-5..1e-4, 1e-4..1e-3 — ko barabar chance). Shart: low > 0.
  • step: discrete jumps (low, low+step, ...). suggest_float me step aur log ek saath allowed nahi.
  • choices: list/tuple of options (str, int, float, bool, None) — bina order ke categories.
  • * ka matlab: step/log keyword-only hain, naam se likhne padenge (step=32, log=True).
CallKya values aa sakti hainKab use
trial.suggest_float('lr', 1e-5, 1e-1, log=True)0.00001 se 0.1 tak koi bhi float, log-uniformlr, weight decay — orders of magnitude me phaile
trial.suggest_float('dropout', 0.1, 0.5, step=0.1)0.1, 0.2, 0.3, 0.4, 0.5Dropout (discrete)
trial.suggest_float('dropout', 0.20, 0.55)0.20–0.55 ka koi bhi float (uniform)Lab 13 dropout
trial.suggest_int('n_layers', 1, 3)1, 2, 3Depth
trial.suggest_int('units', 32, 256, step=32)32, 64, 96, 128, 160, 192, 224, 256 ((256 − 32)/32 + 1 = 8 values)Width
trial.suggest_int('neurons_per_layer', 8, 128, step=8)8, 16, ..., 128 (16 values)Lab 10 width
trial.suggest_categorical('optimizer', ['Adam', 'SGD', 'RMSprop'])Teen mein se ek stringOptimizer, activation
trial.suggest_categorical('batch_size', [16, 32, 64, 128])Char me se ek intBatch size (powers of 2)

Log scale kyun? (Lab 10 ka "Why log=True?")

"Learning rate and weight decay span orders of magnitude — 0.001 vs 0.01 matters more than 0.05 vs 0.06 — so log-scale sampling spends the budget where it counts." Range 1e-5 se 1e-1 me uniform sampling karoge to ~90% samples 0.01–0.1 wale decade me girenge (kyunki wo range ka 90% hissa hai), aur 1e-5..1e-3 lagbhag kabhi test nahi hoga. Log scale har decade (1e-5..1e-4, 1e-4..1e-3, 1e-3..1e-2, 1e-2..1e-1) ko 25% chance deta hai.

Optuna · suggestions dekho (real run, TPESampler seed=42)

import optuna
optuna.logging.set_verbosity(optuna.logging.WARNING)   # sirf warnings/errors dikhao

def objective(trial):
    lr = trial.suggest_float("lr", 1e-5, 1e-1, log=True)          # log scale
    n_layers = trial.suggest_int("n_layers", 1, 3)                 # 1, 2 ya 3
    units = trial.suggest_int("units", 32, 256, step=32)           # 32, 64, ..., 256
    opt_name = trial.suggest_categorical("optimizer", ["Adam", "SGD", "RMSprop"])
    dropout = trial.suggest_float("dropout", 0.0, 0.5, step=0.1)   # 0.0, 0.1, ..., 0.5
    print(trial.number, f"lr={lr:.1e}", n_layers, units, opt_name, dropout)
    return 0.0                                                     # dummy score

study = optuna.create_study(sampler=optuna.samplers.TPESampler(seed=42))
study.optimize(objective, n_trials=5)

Output

0 lr=3.1e-04 3 192 Adam 0.0
1 lr=2.9e-02 2 192 SGD 0.1
2 lr=5.3e-05 1 96 Adam 0.30000000000000004
3 lr=3.6e-05 1 96 SGD 0.30000000000000004
4 lr=2.3e-03 1 160 RMSprop 0.5

Galti mat karna

  • suggest_float('x', 0.0, 1.0, log=True) → error: "`low > 0` must hold for `log=True`".
  • suggest_float(..., step=0.1, log=True) → error: "The parameter `step` is not supported when `log` is true."
  • Purane tutorials me suggest_uniform, suggest_loguniform, suggest_discrete_uniform dikhenge — ye deprecated hain; naye code me suggest_float(..., log=True) / suggest_float(..., step=...) likho.
  • suggest_categorical me optimizer ka naam (string) do, class object nahi.

22.8 Trial se model banana — dynamic architecture

Optuna layers ki ginti aur width badalta hai, to model ko arguments se banna chahiye, hard-coded nahi. Labs 10/11 ka MyNN: "Because the shape is driven by arguments, Optuna can reshape the whole network just by changing numbers."

PyTorch · Lab 10/11 MyNN (parameterized network) — boilerplate TODO ka solution

import torch.nn as nn

class MyNN(nn.Module):
    def __init__(self, input_dim, output_dim, num_hidden_layers, neurons_per_layer, dropout_rate):
        super().__init__()
        layers = []                                   # khaali list, isme layers jodte jaayenge
        for _ in range(num_hidden_layers):            # har hidden block:
            layers.append(nn.Linear(input_dim, neurons_per_layer))
            layers.append(nn.BatchNorm1d(neurons_per_layer))
            layers.append(nn.ReLU())
            layers.append(nn.Dropout(dropout_rate))
            input_dim = neurons_per_layer             # agle block ka input = is block ka output
        layers.append(nn.Linear(neurons_per_layer, output_dim))   # final layer: raw logits
        self.model = nn.Sequential(*layers)           # list ko unpack karke Sequential

    def forward(self, x):
        return self.model(x)

model = MyNN(784, 10, num_hidden_layers=2, neurons_per_layer=64, dropout_rate=0.2)
print(model)

Output

MyNN(
  (model): Sequential(
    (0): Linear(in_features=784, out_features=64, bias=True)
    (1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, bias=True, track_running_stats=True)
    (2): ReLU()
    (3): Dropout(p=0.2, inplace=False)
    (4): Linear(in_features=64, out_features=64, bias=True)
    (5): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, bias=True, track_running_stats=True)
    (6): ReLU()
    (7): Dropout(p=0.2, inplace=False)
    (8): Linear(in_features=64, out_features=10, bias=True)
  )
)

Har layer ki alag width (conditional search space)

Optuna · trial ke andar model banana (define-by-run)

def build_model(trial, in_dim=64, out_dim=10):
    n_layers = trial.suggest_int("n_layers", 1, 3)
    dropout = trial.suggest_float("dropout", 0.0, 0.5, step=0.1)
    layers = []
    for i in range(n_layers):
        units = trial.suggest_int(f"units_l{i}", 32, 256, step=32)   # har layer ka apna naam
        layers += [nn.Linear(in_dim, units), nn.ReLU(), nn.Dropout(dropout)]
        in_dim = units
    layers.append(nn.Linear(in_dim, out_dim))
    return nn.Sequential(*layers)

f-string f"units_l{i}" se har layer ka alag hyperparameter ("units_l0", "units_l1", ...). Agar trial ne n_layers = 1 diya, to "units_l1" poocha hi nahi jaayega — yahi define-by-run ka conditional space hai. (Agar sab layers ke liye ek hi naam "units" use karo to sabko same width milegi.)

Optimizer naam se chunna

PyTorch · labs ka if/elif vs getattr shortcut

import torch.optim as optim

optimizer_name = trial.suggest_categorical("optimizer", ["Adam", "SGD", "RMSprop"])

# Tarika 1 (Labs 10/11): if / elif / else
if optimizer_name == "Adam":
    optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)
elif optimizer_name == "SGD":
    optimizer = optim.SGD(model.parameters(), lr=learning_rate, weight_decay=weight_decay)
else:
    optimizer = optim.RMSprop(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

# Tarika 2: getattr(module, "naam") -> optim.Adam class, phir usko call
optimizer = getattr(optim, optimizer_name)(model.parameters(), lr=learning_rate,
                                           weight_decay=weight_decay)

getattr(optim, "Adam") = optim.Adam (class object); uske baad (model.parameters(), lr=lr) se instance banta hai. Naam exactly class jaisa hona chahiye ("RMSprop", na ki "RMSProp"). Lab ka comment: "Each branch ASSIGNS optimizer = ... so the suggested optimizer, learning_rate and weight_decay are actually used during training." Lab 13 me SGD branch momentum=0.9 ke saath; Lab 14 SVHN me momentum=0.9, nesterov=True.

DataLoader objective ke andar kyun? Lab notes: "the DataLoader is created later, inside the objective function — because batch_size is a hyperparameter Optuna will tune." Aur har trial me naya (fresh) model — warna pichhle trial ke trained weights se shuru hoga aur comparison unfair.

Dimaag me aise baitha lo

List me ek-ek layer jodo, aakhir me nn.Sequential(*layers) — star mat bhoolna.

22.9 Study banao, chalao, result padho

Optuna · create_study + optimize + results

import optuna

optuna.logging.set_verbosity(optuna.logging.WARNING)   # har trial ki INFO line band (optional)

study = optuna.create_study(
    direction="maximize",                                 # accuracy: bada behtar
    sampler=optuna.samplers.TPESampler(seed=42),          # reproducible TPE
    pruner=optuna.pruners.MedianPruner(),                 # bure trials jaldi band
)
study.optimize(objective, n_trials=20)                    # objective 20 baar

print(study.best_value)          # best score (jaise 0.9806)
print(study.best_params)         # dict: {'lr': ..., 'n_layers': ..., ...}
print(study.best_trial.number)   # best trial ka index
print(len(study.trials))         # saare trials (FrozenTrial objects)
df = study.trials_dataframe(attrs=("number", "value", "params", "state"))   # pandas table

Default logging (INFO) me har trial ki line aisi dikhti hai (Lab 10 ke logs jaisi):

Output (format)

[I 2026-10-13 16:05:35,890] A new study created in memory with name: no-name-128d60ae-c910-45e6-b0c1-fd5f936610fe
[I 2026-10-13 16:05:42,635] Trial 0 finished with value: 0.7833333333333333 and parameters: {'n_layers': 2, 'dropout': 0.5, ...}. Best is trial 0 with value: 0.7833333333333333.
[I 2026-10-13 16:05:48,954] Trial 5 pruned. 

optuna.logging.set_verbosity(optuna.logging.WARNING) ye INFO lines chhupa deta hai (Labs 11, 13, 14 isi se output saaf rakhte hain). "in memory" = study RAM me hai; notebook band to study gayab (permanent rakhna ho to storage="sqlite:///study.db" de sakte ho).

Galti mat karna

  • study.optimize(objective(), ...) — galat! Function khud pass karo, call karke nahi: study.optimize(objective, n_trials=20).
  • Objective ko return karna bhool gaye → value None → trial FAIL.

22.10 Pruning — bekaar trial ko beech me hi rok do

Kahani

Talent show me 10 minute ka slot hai. Sakht teacher har 2 minute pe dekhti hai: "Ab tak ke saare poore hue performances me, is waqt tak median kitna tha? Ye bachcha usse bhi neeche hai? Bas, baith jao." Isse bure acts pe time barbaad nahi hota, aur achhe acts ko poora time milta hai.

Kuch trials pehle 1–2 epochs me hi bata dete hain ki wo kharab hain (jaise SGD with lr = 1e-5: accuracy 12%). Unhe 50 epochs tak chalana waste hai. Pruning = training ke beech me trial ka performance Optuna ko batao, aur Optuna ke kehne pe trial band kar do.

Optuna · pruning inside the epoch loop

for epoch in range(EPOCHS):
    train_one_epoch(model, train_loader, optimizer)
    val_acc = evaluate(model, val_loader)

    trial.report(val_acc, epoch)        # "is epoch pe mera score ye hai"
    if trial.should_prune():            # pruner: median se neeche?
        raise optuna.TrialPruned()      # trial PRUNED mark, agla trial shuru

return val_acc

MedianPruner ka rule

optuna.pruners.MedianPruner(n_startup_trials=5, n_warmup_steps=0, interval_steps=1)
  • Step s pe trial ka ab tak ka best intermediate value, pichhle COMPLETE trials ke step s wale values ke median se kharab hai (maximize me: kam) → prune.
  • n_startup_trials (default 5): jab tak itne trials complete na hon, pruning band (median ke liye data chahiye).
  • n_warmup_steps (default 0): har trial me pehle itne steps tak prune nahi (shuru me sab kharab dikhte hain).
  • interval_steps (default 1): har kitne steps pe check.

Optuna · MedianPruner ko kaam karte dekho (fake learning curves, real run)

import math
import optuna
optuna.logging.set_verbosity(optuna.logging.WARNING)

quality = [0.90, 0.80, 0.85, 0.60, 0.95, 0.70]     # har trial ka "final accuracy" (pretend)

def objective(trial):
    q = quality[trial.number]
    for epoch in range(5):
        val_acc = q * (1 - math.exp(-(epoch + 1) / 2))     # dheere-dheere badhta curve
        trial.report(val_acc, epoch)                       # Optuna ko beech ka score
        if trial.should_prune():
            print(f"trial {trial.number}: pruned at epoch {epoch} (val_acc {val_acc:.3f})")
            raise optuna.TrialPruned()
    print(f"trial {trial.number}: completed, val_acc {val_acc:.3f}")
    return val_acc

study = optuna.create_study(
    direction="maximize",
    pruner=optuna.pruners.MedianPruner(n_startup_trials=3, n_warmup_steps=1),
)
study.optimize(objective, n_trials=6)
print("best:", study.best_trial.number, round(study.best_value, 3))

Output

trial 0: completed, val_acc 0.826
trial 1: completed, val_acc 0.734
trial 2: completed, val_acc 0.780
trial 3: pruned at epoch 1 (val_acc 0.379)
trial 4: completed, val_acc 0.872
trial 5: pruned at epoch 1 (val_acc 0.442)
best: 4 0.872

Chalo numbers se samjhein — pruner ne kya socha

Epoch 1 (step 1) pe val_acc = q × (1 − e−1) = q × 0.6321: trial 0 → 0.569, trial 1 → 0.506, trial 2 → 0.537, trial 3 → 0.379, trial 4 → 0.601, trial 5 → 0.442.

  1. Trials 0, 1, 2: n_startup_trials=3 — abhi 3 complete trials nahi the, isliye koi pruning nahi; teeno complete.
  2. Trial 3, step 0: n_warmup_steps=1 → step 0 pe check hi nahi. Step 1: complete trials (0, 1, 2) ke step-1 values 0.569, 0.506, 0.537 → median = 0.537. Trial 3: 0.379 < 0.537 → PRUNE.
  3. Trial 4, step 1: complete trials ab bhi 0, 1, 2 (trial 3 pruned, wo median me nahi ginta) → median 0.537; 0.601 > 0.537 → chalta raha → complete, 0.872 (best).
  4. Trial 5, step 1: complete = 0, 1, 2, 4 → values 0.506, 0.537, 0.569, 0.601 → median = (0.537 + 0.569)/2 = 0.553; 0.442 < 0.553 → PRUNE.

Do bure trials (q = 0.60, 0.70) sirf 2 epochs me band — 3 epochs × 2 trials ka compute bacha, aur best trial (4) poora chala.

Labs me: Lab 13 → MedianPruner(n_startup_trials=3, n_warmup_steps=1); Lab 14 → MedianPruner(n_startup_trials=2, n_warmup_steps=1); dono trial.report(val_accuracy, step=epoch) karte hain. Lab 13 ke results table me 10 me se 2 trials PRUNED state me the.

Galti mat karna

  • Report wahi cheez karo jo direction ke saath match kare. Lab 11 QuickDraw solution me trial.report(loss.item(), epoch) hai jabki study direction="maximize" hai. Pruner maanta hai "bada = achha", to kam loss wale (achhe!) trials median se "kharab" dikhenge aur prune ho sakte hain. Sahi: maximize study me validation accuracy report karo (Lab 13/14 jaisa), ya loss report karna hai to study minimize ho.
  • Report kiya lekin should_prune() check nahi kiya → pruning kabhi nahi hogi.
  • return optuna.TrialPruned() nahi — raise karna hai.

Dimaag me aise baitha lo

Report karo → poochho → raise karo. Aur jo report karo, wo study ki direction wali bhasha me ho.

22.11 Visualization — kya seekha, kaunsa knob matter karta hai

Optuna · plots (Lab 11)

# !pip install plotly -q        # Optuna ke interactive plots plotly pe bante hain
from optuna.visualization import plot_optimization_history, plot_param_importances

fig1 = plot_optimization_history(study)   # har trial ka score + "best so far" line
fig1.show()

fig2 = plot_param_importances(study)      # kaunsa hyperparameter kitna important
fig2.show()

22.12 Poora runnable example — MLP + Optuna + pruning + retrain (real run)

Ab sab kuch ek jagah, labs ke style me, lekin chhota taaki CPU pe ek minute me chal jaaye. Dataset: sklearn ka built-in digits (8×8 grayscale digit images, flatten = 64 features, 10 classes, 1797 images) — Olivetti/QuickDraw jaisa hi flatten-images-into-MLP setup, bas download nahi chahiye. Split: train / validation (Optuna isse judge karega) / test (sirf end me).

PyTorch + Optuna · complete example (digits MLP)

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader
import optuna
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

torch.manual_seed(42)
np.random.seed(42)

# ---------- 1. Data: 8x8 digit images, flatten = 64 features, 10 classes ----------
X, y = load_digits(return_X_y=True)                      # X (1797, 64), y (1797,)
X_trval, X_test, y_trval, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42)    # test: last me sirf 1 baar
X_train, X_val, y_train, y_val = train_test_split(
    X_trval, y_trval, test_size=0.25, stratify=y_trval, random_state=42)

scaler = StandardScaler().fit(X_train)                   # fit sirf train pe (no leakage)
def to_ds(X_, y_):
    return TensorDataset(torch.tensor(scaler.transform(X_), dtype=torch.float32),
                         torch.tensor(y_, dtype=torch.long))
train_ds, val_ds, test_ds = to_ds(X_train, y_train), to_ds(X_val, y_val), to_ds(X_test, y_test)
print("train/val/test:", len(train_ds), len(val_ds), len(test_ds))

# ---------- 2. Model jo trial se banta hai (define-by-run) ----------
def build_model(trial, in_dim=64, out_dim=10):
    n_layers = trial.suggest_int("n_layers", 1, 3)
    dropout = trial.suggest_float("dropout", 0.0, 0.5, step=0.1)
    layers = []
    for i in range(n_layers):
        units = trial.suggest_int(f"units_l{i}", 32, 256, step=32)   # har layer ki apni width
        layers += [nn.Linear(in_dim, units), nn.ReLU(), nn.Dropout(dropout)]
        in_dim = units                       # agli layer ka input = is layer ka output
    layers.append(nn.Linear(in_dim, out_dim))   # raw logits (CrossEntropyLoss ke liye)
    return nn.Sequential(*layers)

def accuracy(model, loader):
    model.eval()
    correct = total = 0
    with torch.no_grad():
        for xb, yb in loader:
            correct += (model(xb).argmax(dim=1) == yb).sum().item()
            total += yb.size(0)
    return correct / total

EPOCHS = 15

# ---------- 3. Objective: trial -> train -> validation accuracy ----------
def objective(trial):
    torch.manual_seed(42)                    # har trial same randomness se shuru
    model = build_model(trial)
    lr = trial.suggest_float("lr", 1e-5, 1e-1, log=True)
    optimizer_name = trial.suggest_categorical("optimizer", ["Adam", "SGD", "RMSprop"])
    batch_size = trial.suggest_categorical("batch_size", [16, 32, 64])
    weight_decay = trial.suggest_float("weight_decay", 1e-6, 1e-3, log=True)

    optimizer = getattr(optim, optimizer_name)(model.parameters(), lr=lr,
                                               weight_decay=weight_decay)
    criterion = nn.CrossEntropyLoss()
    train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True)
    val_loader = DataLoader(val_ds, batch_size=256)

    for epoch in range(EPOCHS):
        model.train()
        for xb, yb in train_loader:
            optimizer.zero_grad()
            loss = criterion(model(xb), yb)
            loss.backward()
            optimizer.step()
        val_acc = accuracy(model, val_loader)
        trial.report(val_acc, epoch)          # beech ka score Optuna ko batao
        if trial.should_prune():              # median se neeche? -> band karo
            raise optuna.TrialPruned()
    return val_acc

# ---------- 4. Study banao aur chalao ----------
study = optuna.create_study(
    direction="maximize",
    sampler=optuna.samplers.TPESampler(seed=42),
    pruner=optuna.pruners.MedianPruner(n_startup_trials=5, n_warmup_steps=3),
)
study.optimize(objective, n_trials=20)

pruned = [t for t in study.trials if t.state == optuna.trial.TrialState.PRUNED]
complete = [t for t in study.trials if t.state == optuna.trial.TrialState.COMPLETE]
print("finished:", len(study.trials), "| complete:", len(complete), "| pruned:", len(pruned))
print("best trial number:", study.best_trial.number)
print("best val accuracy:", round(study.best_value, 4))
print("best params:", study.best_params)

# ---------- 5. Best params se FRESH model, phir test sirf ek baar ----------
torch.manual_seed(42)
best = study.best_params
final_model = build_model(optuna.trial.FixedTrial(best))     # same architecture dobara
final_opt = getattr(optim, best["optimizer"])(final_model.parameters(), lr=best["lr"],
                                              weight_decay=best["weight_decay"])
criterion = nn.CrossEntropyLoss()
train_loader = DataLoader(train_ds, batch_size=best["batch_size"], shuffle=True)
for epoch in range(EPOCHS):
    final_model.train()
    for xb, yb in train_loader:
        final_opt.zero_grad()
        criterion(final_model(xb), yb).backward()
        final_opt.step()
print("final val acc :", round(accuracy(final_model, DataLoader(val_ds, batch_size=256)), 4))
print("final test acc:", round(accuracy(final_model, DataLoader(test_ds, batch_size=256)), 4))
df = study.trials_dataframe(attrs=("number", "value", "state"))
print(df.sort_values("value", ascending=False).head(5).to_string(index=False))

Output (lambi INFO lines me parameter dicts ko "{...}" se chhota kiya)

train/val/test: 1077 360 360
[I 2026-10-13 16:05:35,890] A new study created in memory with name: no-name-128d60ae-c910-45e6-b0c1-fd5f936610fe
[I 2026-10-13 16:05:42,635] Trial 0 finished with value: 0.7833333333333333 and parameters: {...}. Best is trial 0 with value: 0.7833333333333333.
[I 2026-10-13 16:05:43,496] Trial 1 finished with value: 0.9722222222222222 and parameters: {...}. Best is trial 1 with value: 0.9722222222222222.
[I 2026-10-13 16:05:48,727] Trial 4 finished with value: 0.5638888888888889 and parameters: {...}. Best is trial 1 with value: 0.9722222222222222.
[I 2026-10-13 16:05:48,954] Trial 5 pruned. 
[I 2026-10-13 16:05:57,479] Trial 11 finished with value: 0.9805555555555555 and parameters: {...}. Best is trial 11 with value: 0.9805555555555555.
...
finished: 20 | complete: 13 | pruned: 7
best trial number: 11
best val accuracy: 0.9806
best params: {'n_layers': 3, 'dropout': 0.0, 'units_l0': 128, 'units_l1': 32, 'units_l2': 96, 'lr': 0.0009528146938834286, 'optimizer': 'RMSprop', 'batch_size': 16, 'weight_decay': 2.9343230753252416e-06}
final val acc : 0.9778
final test acc: 0.9694
 number    value    state
     15 0.980556 COMPLETE
     19 0.980556 COMPLETE
     11 0.980556 COMPLETE
     13 0.975000 COMPLETE
     18 0.972222 COMPLETE

Exam Radar

  • Flow: data split (train/val/test) → objective(trial) (suggest → build → train → report/prune → return val metric) → create_study(direction, sampler, pruner) → optimize(objective, n_trials) → best_params → fresh model retrain → test once.
  • Fresh model har trial me; DataLoader objective ke andar (batch size tuned).
  • FixedTrial(params) = fixed values wala trial (testing / rebuild).

22.13 CNN tuning — Lab 13 (CIFAR-10) aur Lab 14 (KMNIST, SVHN)

CNN me bhi Optuna ka contract wahi hai; bas model Conv2d wala hai (Conv details: Chapter 23) aur training mehengi hai, isliye kuch smart tricks.

Lab 13 — CIFAR-10, two-block CNN

Input (3, 32, 32) → Conv2d 3→32 (padding 'same') + ReLU + BatchNorm2d + MaxPool → (32, 16, 16) → Conv2d 32→64 + ReLU + BN + MaxPool → (64, 8, 8) → Flatten 64 × 8 × 8 = 4096 → Linear 4096→128 + ReLU + Dropout → Linear 128→64 + ReLU + Dropout → Linear 64→10. dropout constructor argument hai "because Optuna will tune it". Tuned: lr, dropout, weight decay, batch size, optimizer.

PyTorch + Optuna · Lab 13 objective (CIFAR-10)

N_TRIALS = 10
TUNE_EPOCHS = 2                      # tuning me sirf 2 epochs (fast)
TUNE_TRAIN_SAMPLES = 12000           # 40,000 me se subset
TUNE_VAL_SAMPLES = 3000

rng = np.random.default_rng(42)
train_indices = rng.choice(len(train_dataset), size=TUNE_TRAIN_SAMPLES, replace=False)
val_indices = rng.choice(len(val_dataset), size=TUNE_VAL_SAMPLES, replace=False)
tuning_train_dataset = Subset(train_dataset, train_indices.tolist())   # sirf ye indices
tuning_val_dataset = Subset(val_dataset, val_indices.tolist())

def build_optimizer(name, model, learning_rate, weight_decay):
    if name == 'Adam':
        return optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)
    else:
        return optim.SGD(model.parameters(), lr=learning_rate, momentum=0.9,
                         weight_decay=weight_decay)

def objective(trial):
    # 1. Optuna se ek set of hyperparameters
    learning_rate = trial.suggest_float('learning_rate', 1e-4, 3e-2, log=True)
    dropout = trial.suggest_float('dropout', 0.20, 0.55)
    weight_decay = trial.suggest_float('weight_decay', 1e-6, 1e-3, log=True)
    batch_size = trial.suggest_categorical('batch_size', [32, 64, 128])
    optimizer_name = trial.suggest_categorical('optimizer', ['Adam', 'SGD'])

    # 2. Har trial ke liye FRESH CNN
    trial_model = MyNN(input_channels=3, dropout=dropout, num_classes=10).to(device)
    trial_optimizer = build_optimizer(optimizer_name, trial_model, learning_rate, weight_decay)

    generator = torch.Generator().manual_seed(42 + trial.number)   # reproducible shuffling
    trial_train_loader = DataLoader(tuning_train_dataset, batch_size=batch_size,
                                    shuffle=True, generator=generator)
    trial_val_loader = DataLoader(tuning_val_dataset, batch_size=256, shuffle=False)

    # 3. Thoda train karo, VALIDATION accuracy naapo
    val_accuracy = 0.0
    for epoch in range(TUNE_EPOCHS):
        train_one_epoch(trial_model, trial_train_loader, trial_optimizer)
        val_accuracy = evaluate_model(trial_model, trial_val_loader)
        trial.report(val_accuracy, step=epoch)       # pruning ke liye
        if trial.should_prune():
            raise optuna.TrialPruned()
    return val_accuracy

sampler = optuna.samplers.TPESampler(seed=42)
pruner = optuna.pruners.MedianPruner(n_startup_trials=3, n_warmup_steps=1)
study = optuna.create_study(direction='maximize', sampler=sampler, pruner=pruner)
study.optimize(objective, n_trials=N_TRIALS)

Output (Lab 13 notebook)

Best validation accuracy: 0.5457

Best hyperparameters:
  learning_rate: 0.0001656558044088478
  dropout: 0.26859400184670085
  weight_decay: 1.3667272915456224e-06
  batch_size: 64
  optimizer: Adam

Lab 14 — KMNIST CNN: conv channels tune karna

Model width ek hi number se: base_channels = C, stages C → 2C → 4C; global average pooling (nn.AdaptiveAvgPool2d(1)) se flatten size sirf 4C (image size pe depend nahi). Optuna C ko categorical se chunta hai.

PyTorch + Optuna · Lab 14 KMNIST CNN + objective (verified on dummy data)

class KMNISTCNN(nn.Module):
    def __init__(self, base_channels=32, dropout=0.30, num_classes=10):
        super().__init__()
        c = base_channels
        def conv(in_c, out_c):                       # Conv -> BN -> ReLU ka chhota block
            return nn.Sequential(
                nn.Conv2d(in_c, out_c, 3, padding=1, bias=False),
                nn.BatchNorm2d(out_c), nn.ReLU(inplace=True))
        self.features = nn.Sequential(
            conv(1, c), conv(c, c), nn.MaxPool2d(2),          # 28 -> 14
            conv(c, 2*c), conv(2*c, 2*c), nn.MaxPool2d(2),    # 14 -> 7
            conv(2*c, 4*c), nn.AdaptiveAvgPool2d(1))          # (4c, 7, 7) -> (4c, 1, 1)
        self.classifier = nn.Sequential(
            nn.Flatten(), nn.Dropout(dropout), nn.Linear(4*c, num_classes))

    def forward(self, x):
        return self.classifier(self.features(x))             # raw logits

def build_optimizer(name, model, learning_rate, weight_decay):
    if name == "AdamW":
        return optim.AdamW(model.parameters(), lr=learning_rate, weight_decay=weight_decay)
    if name == "Adam":
        return optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)
    raise ValueError(f"Unsupported optimizer: {name}")

def objective(trial):
    torch.manual_seed(42)
    p = {
        "base_channels": trial.suggest_categorical("base_channels", [16, 24, 32, 40]),
        "dropout": trial.suggest_float("dropout", 0.1, 0.5),
        "learning_rate": trial.suggest_float("learning_rate", 1e-4, 5e-3, log=True),
        "weight_decay": trial.suggest_float("weight_decay", 1e-6, 1e-3, log=True),
        "batch_size": trial.suggest_categorical("batch_size", [64, 128, 256]),
        "optimizer": trial.suggest_categorical("optimizer", ["Adam", "AdamW"]),
    }
    model = KMNISTCNN(base_channels=p["base_channels"], dropout=p["dropout"])
    optimizer = build_optimizer(p["optimizer"], model, p["learning_rate"], p["weight_decay"])
    criterion = nn.CrossEntropyLoss()
    train_loader = DataLoader(tune_train, batch_size=p["batch_size"], shuffle=True)
    val_loader = DataLoader(tune_val, batch_size=256)
    best_accuracy = 0.0
    for epoch in range(TUNE_EPOCHS):
        run_epoch(model, train_loader, criterion, optimizer)        # train mode
        _, accuracy = run_epoch(model, val_loader, criterion)       # eval mode
        best_accuracy = max(best_accuracy, accuracy)
        trial.report(accuracy, step=epoch)
        if trial.should_prune():
            raise optuna.TrialPruned()
    return float(best_accuracy)            # trial ka BEST val accuracy

# objective ko bina study ke test karo (Lab 14 Test 7A jaisa)
fixed = optuna.trial.FixedTrial({"base_channels": 16, "dropout": 0.25, "learning_rate": 1e-3,
                                 "weight_decay": 1e-5, "batch_size": 64, "optimizer": "Adam"})
score = objective(fixed)

sampler = optuna.samplers.TPESampler(seed=42, n_startup_trials=2)
pruner = optuna.pruners.MedianPruner(n_startup_trials=2, n_warmup_steps=1)
study = optuna.create_study(direction="maximize", sampler=sampler, pruner=pruner)
study.optimize(objective, n_trials=N_TRIALS, n_jobs=1, gc_after_trial=True)

(Upar ka code humne chhote random tensors pe chala ke check kiya: FixedTrial score float [0, 1] me aaya, KMNISTCNN(base_channels=16) ke logits (4, 10) shape ke aur parameters 35,674 — lab ka test 10,000 < params < 2,000,000 maangta hai. run_epoch(model, loader, criterion, optimizer=None) yahan Lab 14 ka helper hai: optimizer diya to train mode + backward, warna eval mode with torch.set_grad_enabled(False); return (mean loss, accuracy).)

22.14 Lab boilerplates — har TODO aur uska solution

Lab 8 → Lab 10: Olivetti Faces (base pipeline, phir Optuna)

Lab 8 (face recognition MLP) pipeline: fetch_olivetti_faces() → X (400, 4096), y (400,) → 40 people, 10 images each. TODO solutions: missing values check np.isnan(X).any(); split train_test_split(X, y, test_size=0.2, stratify=y, random_state=42) (stratify taaki har insaan dono sets me ho); StandardScaler sirf train pe fit_transform, test pe transform (warna data leakage); tensors float32 (X) aur long (y); FaceMLP: 4096 → 512 → ReLU → 128 → ReLU → 40 (no softmax); nn.CrossEntropyLoss(); optim.Adam(model.parameters(), lr=0.001); manual batching: perm = torch.randperm(n_samples), idx = perm[i:i+batch_size], xb, yb = X_train_t[idx], y_train_t[idx]; eval me model.eval() + torch.no_grad() + accuracy_score. Lab 8 note: "No Dropout here — intentionally left out … this makes some train/test overfitting gap likely" — wahi gap jo Chapter 18–19 ka topic hai.

Lab 10 Olivetti TODOSolution
MyNN: loop me 4 layers appendnn.Linear(input_dim, neurons_per_layer), nn.BatchNorm1d(neurons_per_layer), nn.ReLU(), nn.Dropout(dropout_rate); phir input_dim = neurons_per_layer
Final layerlayers.append(nn.Linear(neurons_per_layer, output_dim))
self.model = Noneself.model = nn.Sequential(*layers)
forward return Nonereturn self.model(x)
8 suggest_* callssuggest_int("num_hidden_layers", 1, 5); suggest_int("neurons_per_layer", 32, 256, step=32); suggest_int("epochs", 10, 50, step=10); suggest_float("learning_rate", 1e-4, 1e-2, log=True); suggest_float("dropout_rate", 0.0, 0.5, step=0.1); suggest_categorical("batch_size", [16, 32, 64, 128]); suggest_categorical("optimizer", ["Adam", "SGD", "RMSprop"]); suggest_float("weight_decay", 1e-6, 1e-3, log=True)
Given (already written)input_dim = 4096 (64 × 64), output_dim = 40; DataLoaders objective ke andar; if/elif optimizer; train loop; test accuracy return
Studystudy = optuna.create_study(direction='maximize'); study.optimize(objective, n_trials=10)

Lab 10 Olivetti result: best_value = 0.9875 with {num_hidden_layers: 1, neurons_per_layer: 160, epochs: 50, lr ≈ 8.4e-4, dropout 0.1, batch 16, Adam, wd ≈ 8.2e-6}. Dekho: SGD wale trials 0.125 aur 0.1875 pe atke (chhote lr + kam epochs pe plain SGD seekh hi nahi paaya) — optimizer + lr ka chunaav kitna bada farak daalta hai. Sirf 320 training images → chhota network (1 hidden layer) jeeta; data kam ho to capacity kam (Chapter 18!). Lab table ka hint: "Why smaller batch sizes than the Fashion-MNIST example? The training split here has only ~320 images — a batch of 128 would barely give a handful of updates per epoch." Lab 10 Fashion-MNIST ANN: 48,000 / 12,000 split, best 0.8951 (5 layers × 112, Adam, lr ≈ 1.5e-4).

Lab 11: QuickDraw (10 animal classes × 2000 = 20,000 doodles, 28 × 28 = 784)

TODOSolution
Splittrain_test_split(X, y, test_size=0.2, random_state=SEED, stratify=y) → 16,000 / 4,000
CustomDataset__init__: torch.tensor(features, dtype=torch.float32), torch.tensor(labels, dtype=torch.long); __len__: return len(self.features); __getitem__: return self.features[index], self.labels[index]
MyNNSame as Lab 10 (Linear → BatchNorm1d → ReLU → Dropout loop + final Linear + Sequential(*layers))
TODO 1: suggestlayers 1–4; neurons 64–256 step 32; epochs 10–50 step 10; lr 1e-5–1e-1 log; dropout 0.1–0.5 step 0.1; batch [16, 32, 64, 128]; optimizer [Adam, SGD, RMSprop]; wd 1e-5–1e-3 log
TODO 2: DataLoadersDataLoader(train_dataset, batch_size=batch_size, shuffle=True, pin_memory=pin); test loader shuffle=False; pin = (device.type == "cuda")
TODO 3: modelinput_dim = X_train.shape[1] (784), output_dim = len(classes) (10), MyNN(...).to(device)
TODO 4: loss + optimizernn.CrossEntropyLoss(); if/elif/else Adam / SGD / RMSprop with lr + weight_decay
TODO 5: training loopmodel.train(); epochs × batches: forward → loss → zero_grad → backward → step; har epoch ke baad trial.report(...) + should_prune → raise optuna.TrialPruned()
Studyoptuna.logging.set_verbosity(optuna.logging.WARNING); study = optuna.create_study(direction="maximize"); study.optimize(objective, n_trials=10)
Step 8 (given)study.best_value, study.best_params; plot_optimization_history, plot_param_importances (plotly)

(Yaad rakho 22.10: solution me trial.report(loss.item(), epoch) likha hai — maximize study ke saath ye ulta hai; better: validation accuracy report karo.)

Lab 14: KMNIST / SVHN student exercise ka TODO map

TODOKya likhna hai
1 — transformstrain_transform = Compose([RandomAffine(degrees=8, translate=(0.08, 0.08), scale=(0.95, 1.05)), ToTensor(), Normalize((0.5,), (0.5,))]); eval_transform = Compose([ToTensor(), Normalize((0.5,), (0.5,))]) (SVHN: 3-channel tuples, no flips)
2A/2B — splittrain_indices, val_indices = train_test_split(all_indices, test_size=VAL_FRACTION, random_state=SEED, stratify=all_targets) (VAL_FRACTION = 0.15); train_dataset = Subset(full_train_aug, train_indices.tolist()) (augmented images); val_dataset = Subset(full_train_eval, val_indices.tolist()) (deterministic transform)
3 — make_loadersTrain loader shuffle=True (RandomSampler), val/test shuffle=False (SequentialSampler)
4A/4B — CNNKMNISTCNN upar wala (≥ 5 convs, BN, 2 maxpools, AdaptiveAvgPool2d, Dropout, Linear(4C, 10)); forward raw logits
5A/5B — run_epoch, fitrun_epoch: train/eval mode, sample-weighted mean loss, accuracy; fit: history dict (train_loss, train_acc, val_loss, val_acc), best val acc pe copy.deepcopy(model.state_dict()), end me load_state_dict
6 — baselinebatch 128, base 32, dropout 0.30, Adam(lr=1e-3, weight_decay=1e-5)
7A/7B/7C — Optunabuild_optimizer (Adam/AdamW), objective (6 suggests, fresh model, report, prune, return best val acc), TPESampler(seed, n_startup_trials=2) + MedianPruner(n_startup_trials=2, n_warmup_steps=1) + create_study(direction="maximize") + optimize(objective, n_trials=N_TRIALS, n_jobs=1, gc_after_trial=True)
8 — retrainFresh model from study.best_params, full loaders, fit
9A/9B/9C — evaluate + savetorch.no_grad() predictions + confidence (softmax max), metrics, confusion pairs, checkpoint dict save

Exam Radar

  • Coding question ka skeleton: def objective(trial): → trial.suggest_* → model/optimizer/DataLoader → train loop → (report + should_prune + raise TrialPruned) → return metric.
  • optuna.create_study(direction='maximize', sampler=optuna.samplers.TPESampler(seed=42), pruner=optuna.pruners.MedianPruner()); study.optimize(objective, n_trials=20).
  • Results: study.best_params (dict), study.best_value (float), study.best_trial (FrozenTrial).
  • Defaults: direction = minimize; sampler = TPESampler; pruner = MedianPruner (n_startup_trials=5, n_warmup_steps=0); TPE n_startup_trials = 10.
  • log=True for lr / weight decay; needs low > 0; no step with log.
  • Tune on validation; retrain fresh model with best params; test once.
  • Visualization: plot_optimization_history, plot_param_importances (plotly).

Galti mat karna

  • Objective ke bahar ek hi model bana ke har trial me reuse karna — har trial ko fresh model chahiye.
  • direction bhoolna (default minimize) jab accuracy return kar rahe ho.
  • Test set pe tune karna aur wahi test number report karna (leakage).
  • study.best_params se seedha "final model" maan lena — best params sirf recipe hain; model dobara (fresh) train karna padta hai (Lab 13: "we throw away the temporary trial models").

Dimaag me aise baitha lo

Objective = "trial lo, model banao, train karo, score lautao." Study = "ye N baar karo aur best yaad rakho." Bas itna hi Optuna hai.

Chapter 23

Convolutional Neural Networks: Fundamentals — tasveer ko torch ki roshni me padhna

MLP kyun fail · image = numbers · (N, C, H, W) · convolution (cross-correlation) · filters & kernels · bias · feature maps · multi-channel filters · stride · padding · output-size formula · dilation · parameter count · local connectivity & weight sharing · pooling · equivariance vs invariance · receptive field · feature hierarchy · Conv2d / MaxPool2d / Flatten · transforms & datasets · Lab 12 Fashion-MNIST CNN · augmentation · 1×1 conv

23.1 Shuruaat: image ke liye MLP kyun kaafi nahi?

Kahani

Golu ke phone ki gallery me ek magic hai: "People" folder khol do, to Maa ki saari photos ek jagah aa jaati hain. Kisi photo me Maa left me khadi hain, kisi me right me, kisi me chhoti si door, kisi me close-up. Phir bhi phone pehchaan leta hai: "ye Maa hain". Golu sochta hai: "Maine to ab tak MLP padha hai. Kya phone ne photo ke saare pixels ek line me laga kar ek bada MLP chala diya?"

Chalo dekhte hain ki agar hum aisa karein to kya musibat aati hai, aur scientists ne is musibat ka kya jugaad nikala. Wahi jugaad hai Convolutional Neural Network (CNN). Ye chapter poora is ek idea ke around ghoomega: poori photo ek saath mat dekho; ek chhoti si torch le kar tukda-tukda dekho, aur har jagah wahi same torch use karo.

Pehle thoda history: dimaag pixels nahi, edges dekhta hai

Teacher ki slides ki shuruaat neuroscience se hoti hai. 1959–1962 ke aas-paas do scientists, David Hubel aur Torsten Wiesel, ne billi (cat) ke dimaag ke visual cortex (aankh se aane wale signal ko process karne wala hissa) me electrodes laga kar dekha ki kaunsa neuron kab "fire" karta hai. Unhe 1981 ka Nobel Prize mila. Unki findings (slide 5–7):

Yaad rakhna: CNN ka poora design isi biology se inspired hai. Filters = simple cells jaise, pooling = complex cells jaise, layers ki seedhi = edges → shapes → objects.

Ab sawaal: "Image ko flatten karke MLP me kyun na daal dein?"

Ye teacher ki slide 9 ka exact sawaal hai. MLP me input ek vector hota hai. To 28×28 ki photo ko 784 numbers ki line me khol do (isko flatten karna kehte hain) aur nn.Linear(784, 128) laga do. Chalta to hai (Fashion-MNIST par MLP ~88% tak jaata hai), lekin teen badi problems hain.

Problem 1: Parameter explosion (weights ka pahaad)

Fully connected (dense) layer me har input ka har neuron se ek alag weight hota hai. Photo badi hui to weights ka pahaad ban jaata hai.

Dense layer params = (H × W × C) × nhidden + nhidden = (H·W·C + 1) × nhidden
  • H, W = image ki height aur width (pixels me)
  • C = channels (grayscale = 1, colour RGB = 3)
  • H·W·C = flatten ke baad kitne input numbers bane
  • nhidden = pehli hidden layer ke neurons; "+1" = har neuron ka ek bias

Chalo numbers se samjhein

Sawaal: 224×224 ki colour (RGB) photo ko flatten karke 1000 neurons wali hidden layer me daalo. Sirf pehli layer me kitne weights?

  1. Inputs gino: 224 × 224 × 3 = 50,176 × 3 = 150,528
  2. Har input ka har neuron se ek weight: 150,528 × 1000 = 150,528,000 weights
  3. Biases jodo: 150,528,000 + 1000 = 150,529,000

Jawab: ≈ 150.5 million (15 crore!) parameters, sirf ek layer me. Aur abhi to ek bhi "smart" kaam nahi hua.

Teacher ke L10 worksheet wale numbers bhi yaad kar lo: 32×32 grayscale → 128 neurons: 1,024 × 128 + 128 = 131,200; 224×224×3 → 128 neurons: 150,528 × 128 + 128 = 19,267,712. Lab 12 ke notes ka sawaal: 100×100×3 = 30,000 inputs × 1000 neurons = 30 million weights. Aur worksheet ka chhota sawaal: 4 inputs → 2 neurons = 4 × 2 + 2 = 10 parameters.

Itne zyada weights ka matlab: (a) bahut memory aur compute, (b) training ke liye bahut zyada data chahiye, (c) overfitting ka khatra (model photos ratt leta hai — Chapter 18 yaad karo).

Problem 2: Spatial structure kho jaata hai (kaun kiska padosi?)

Photo me paas-paas wale pixels aapas me jude hote hain: aankh ke pixels naak ke pixels ke upar hote hain, ek edge ke pixels ek line me hote hain. Slide 11 kehti hai: "A fully connected network doesn't have any concept of neighborhood & treats all inputs equally." Flatten karte hi 2D ka padosi-pan khatam: neeche diagram me pixel 6 ke theek neeche pixel 10 hai, lekin vector me dono 4 position door baithe hain. Dense layer ke liye pixel 6 aur pixel 10 utne hi "anjaan" hain jitne pixel 6 aur pixel 16.

4×4 image 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 6 aur 10: upar-neeche padosi Flatten 1 6 10 16 5 hidden neurons Vector me 6 aur 10 ab door-door baithe hain Har pixel → har neuron: 16 × 5 = 80 weights 224×224×3 → 1000 neurons: 150,528,000 weights! "Kaun kiska padosi" — MLP ko koi khabar nahi
Flatten karte hi 2D padosi (6 ke theek neeche 10) vector me 4 jagah door ho jaate hain, aur har pixel ka har neuron se alag weight ban jaata hai.

Problem 3: Translation (khisakna) handle nahi hota

Slide 11 ka example: ek ped (tree) ki photo ko thoda right khisaka do. Saare pixel values badal gaye, lekin ped to ped hi hai. Dense layer ke weights fixed pixel positions se bandhe hote hain (teacher ka L10 MCQ: "MLP weights attach to… pixel positions"). Edge thoda khiska to wo bilkul alag weights se takrayega, aur neuron chup ho sakta hai. Matlab MLP ko "7 left me" aur "7 right me" alag-alag seekhna padega. Bahut wasteful.

(Slide "Translational and Rotational Invariance" bhi kehti hai. Dhyan do: CNN translation ke liye built-in madad deta hai; rotation ke liye nahi — uske liye data augmentation use hota hai, 23.22 me dekhenge.)

Jugaad: chhote detectors jo poori photo me ghoomte hain

Slides 13–18 ka solution: "We need to somehow detect features without looking at all the pixels simultaneously. Exploit image spatial structure, learn patches and build them up." Example: chidiya (bird) pehchaanne ke liye uski chonch (beak) dhoondo. Chonch photo ke chhote se hisse me hoti hai, to poori photo dekhne ki zaroorat nahi: ek chhota "beak detector" kaafi hai. Aur chonch upar-left me ho ya beech me, detector to wahi chahiye; isliye "upper-left beak detector" aur "middle beak detector" ko same parameters me compress kar sakte hain.

Isse CNN ke teen bade ideas nikalte hain (slide 69: "A CNN compresses a fully connected network by: reducing number of connections, shared weights, max pooling further reduces the complexity"):

IdeaMatlab (simple bhasha)Kaunsi problem solve hui
Local connectivity (sparse interactions)Har output neuron sirf ek chhote patch (jaise 3×3) ko dekhta haiKam weights + padosi pixels ka structure use hota hai
Weight sharing (parameter sharing)Wahi chhota detector (filter) poori photo par har jagah use hota haiAur bhi kam weights + pattern kahin bhi ho, pakda jaata hai (equivariance)
PoolingChhote area ka summary (max/average) le kar map chhota karoKam compute, thoda khisakne se farak nahi (local invariance)
Hierarchy (layers ki seedhi)Edges → textures → parts → objectsBadi cheezein chhoti cheezon se banti hain

Exam Radar

  • Hubel & Wiesel: simple cells (specific angle + specific spot), complex cells (same line anywhere in receptive field → translation invariance); visual cortex is layered. Nobel 1981.
  • MLP for images ki 3 limitations: (1) bahut zyada parameters → compute + overfitting, (2) spatial/neighbourhood structure lost (flatten), (3) translation invariance nahi (weights tied to pixel positions).
  • 224×224×3 = 150,528 inputs; × 1000 neurons ≈ 150.5 M weights. 100×100×3 × 1000 = 30 M. 32×32 → 128 = 131,200 params.
  • Flatten values ko nahi khota; wo sirf 2D arrangement/neighbourhood ki jaankari ko model ke design se hata deta hai.
  • CNN = "neural network with some convolutional layers (and some other layers)" (slide 26).

Galti mat karna

  • "MLP images par kaam hi nahi karta" — galat. Karta hai (chhoti images par theek-thaak), bas inefficient hai aur structure use nahi karta.
  • "CNN rotation invariant hota hai" — galat. Built-in help sirf translation (khisakne) ke liye hai; rotation/scale ke liye augmentation chahiye.

Dimaag me aise baitha lo

MLP = principal jo poore school ke har bachche ko ek saath ghoorta hai. CNN = ek chhota monitor jo har bench par jaa kar wahi checklist chalata hai.

23.2 Computer ke liye photo = numbers ka grid

Kahani

Nani ke ghar ka purana black-and-white TV yaad karo. Screen par chhote-chhote dots hote the: koi poora kaala, koi poora safed, koi beech ka grey. Ab socho har dot par ek number likh do: kaala = 0, safed = 255, grey = beech ka koi number. Bas! Computer ke liye photo yahi hai: numbers ki ek table. Colour TV me har dot ke paas teen bulb hote hain (laal, hara, neela) — to har dot ke teen numbers.

Teacher ke L10 worksheet ka pehla sawaal: "What does a computer actually receive when it sees a photo?" Jawab: a grid of pixels. Pixel (picture element) = photo ka sabse chhota dot.

Definition

Grayscale image: har pixel ka ek number (intensity). Shape = H × W (height × width), yani 1 channel.
RGB (colour) image: har pixel ke teen numbers — Red, Green, Blue. Shape = H × W × 3.
Channel: values ka ek poora grid. RGB me teen channels hote hain; teeno same location ko describe karte hain.

Grayscale: H × W × 1 0 50 200 255 0 60 210 255 10 50 220 250 0 40 200 255 0 = kaala, 255 = safed 4 × 4 × 1 = 16 numbers 30 30 200 210 30 30 200 210 30 30 200 210 30 30 200 210 40 45 90 95 40 45 90 95 40 45 90 95 40 45 90 95 255 250 20 25 255 250 20 25 255 250 20 25 255 250 20 25 B G R RGB: H × W × 3 4 × 4 × 3 = 48 numbers ek pixel = 3 values (R, G, B) colour me 3 parat
Grayscale photo = ek grid (1 channel). Colour photo = teen grids (R, G, B channels) ek ke peeche ek. Laal dabba = ek pixel jiske paas teen numbers hain.

Normalization: 0–255 ko chhota kyun karte hain?

Training me hum pixel values ko chhote, ek jaise range me laate hain. Kyun? Bade numbers (jaise 255) se weighted sums bade bante hain, gradients bhi bade/asamaan hote hain, aur training dheere ya unstable hoti hai. Chhote, consistent numbers = smooth aur fast training (yahi baat BatchNorm chapter me bhi thi).

Scaling: x′ = x255   (range 0–1)     Standardization: x″ = x′ − meanstd
  • x = original pixel (0–255)
  • x′ = 0–1 wala pixel (Lab 12 me X_train / 255.0 yahi karta hai)
  • mean, std = jo centre aur spread hum chahte hain hatana; mean = 0.5, std = 0.5 lo to range [0, 1] → [−1, 1] ban jaati hai (23.20 me detail)

Chalo numbers se samjhein

  1. Pixel 0: 0/255 = 0 → (0 − 0.5)/0.5 = −1
  2. Pixel 128: 128/255 ≈ 0.5020 → (0.5020 − 0.5)/0.5 ≈ 0.0039
  3. Pixel 255: 255/255 = 1 → (1 − 0.5)/0.5 = 1

Jawab: 0–255 → 0–1 → −1 se +1.

PyTorch ka layout: (N, C, H, W)

Ab bahut important baat, coding me roz kaam aayegi. PyTorch ki conv layers input ko is order me chahti hain:

input shape = (N, C, H, W)
  • N = batch size (ek saath kitni photos)
  • C = channels (grayscale 1, RGB 3; aage ki layers me jitne filters utne)
  • H = height (rows), W = width (columns)

Isko channels-first kehte hain. Lekin numpy, PIL, matplotlib aur CIFAR-10 ka raw data channels-last hota hai: (H, W, C) — Lab 13 me X_full.shape = (50000, 32, 32, 3) aaya tha. Isliye Lab 13 me .permute(2, 0, 1) se (32, 32, 3) → (3, 32, 32) kiya gaya. Lab 12 me CSV ki flat 784 values ko .reshape(-1, 1, 28, 28) se (N, 1, 28, 28) banaya gaya: −1 = "jitni photos hain", 1 = ek channel (grayscale), 28, 28 = height, width.

PyTorch · image shapes

import torch

gray = torch.randint(0, 256, (28, 28))          # ek grayscale photo: H x W
rgb_hwc = torch.randint(0, 256, (32, 32, 3))    # numpy/PIL style: H x W x C (channels-last)
rgb_chw = rgb_hwc.permute(2, 0, 1)              # dims ka order badlo: (H,W,C) -> (C,H,W)
batch = torch.stack([rgb_chw, rgb_chw, rgb_chw, rgb_chw])   # 4 photos -> (N, C, H, W)

print(gray.shape)
print(rgb_hwc.shape)
print(rgb_chw.shape)
print(batch.shape)

x = gray.float() / 255.0                        # int 0-255 -> float 0-1 (Lab 12 jaisa)
print(x.dtype, x.min().item() >= 0, x.max().item() <= 1)
print(x.reshape(1, 1, 28, 28).shape)            # Lab 12 trick: (N=1, C=1, H, W)
print(gray.unsqueeze(0).unsqueeze(0).shape)     # same kaam: aage do dims jodo

Output

torch.Size([28, 28])
torch.Size([32, 32, 3])
torch.Size([3, 32, 32])
torch.Size([4, 3, 32, 32])
torch.float32 True True
torch.Size([1, 1, 28, 28])
torch.Size([1, 1, 28, 28])

Exam Radar

  • Pixel range 0–255 (8-bit, 256 levels). Grayscale = 1 channel, RGB = 3 channels; shape = H × W × C.
  • PyTorch conv input: (N, C, H, W) (channels-first). numpy/PIL/matplotlib/TensorFlow default: (H, W, C) (channels-last).
  • permute(2, 0, 1): HWC → CHW. unsqueeze(0): ek photo me batch dimension jodna → (1, C, H, W).
  • Fashion-MNIST: 28×28×1, 10 classes, 60k/10k. CIFAR-10: 32×32×3, 10 classes, 50k/10k.
  • 4×4 RGB image me total values = 48; ek RGB pixel me three values hote hain (teacher ka MCQ).

Galti mat karna

  • Grayscale batch ko (N, 28, 28) shape me Conv2d me mat bhejo. Channel dim chahiye: (N, 1, 28, 28). Warna error aayega (23.18 me real error dekhenge).
  • reshape se HWC → CHW mat karo! Reshape sirf numbers ko naye dabbon me bharta hai, axes ko ghumata nahi; photo khichdi ban jaayegi. Axes badalne ke liye permute.
  • Int tensor ko seedha model me mat daalo; .float() karo. Conv weights float32 hote hain.

Dimaag me aise baitha lo

"Nani Chai Hot Wali" — N, C, H, W. Pehle kitni photos, phir kitne parat (channels), phir lambai-chaudai.

23.3 Convolution operation — andhere kamre me torch

Kahani

Diwali ki raat, light chali gayi. Deewar par ek badi rangoli ki painting lagi hai, aur Golu ke haath me ek chhoti si torch hai jo ek baar me sirf painting ka chhota sa hissa roshan karti hai. Poori painting samajhne ke liye Golu torch ko left se right khiskata hai, row khatam hone par thoda neeche aata hai aur phir left se shuru karta hai — bilkul kitaab padhne jaise. Har jagah wo ek hi sawaal poochta hai: "Is chhote tukde me mera wala pattern kitna dikh raha hai?" Aur jawab ek number me likh leta hai.

Ye teacher ki slide 25 ki exact kahani hai: "Imagine a large painting in a dark room... we have a small flashlight, which can illuminate only a certain portion... We can slide the illuminated area." Torch = kernel/filter. Torch ka roshan hissa = patch. Har jagah ka likha number = output. Saare numbers ki table = feature map.

Step-by-step recipe (teacher ke L10 worksheet se)

  1. Patch dhundho: kernel (jaise 3×3 numbers ki chhoti table) ko input ke top-left kone par rakho.
  2. Matching positions multiply karo: patch ka har number apne upar rakhe kernel weight se guna hota hai. Ye element-wise multiplication hai, matrix multiplication nahi.
  3. Sab products jodo + bias: 3×3 patch ke 9 products ka jod + ek bias = ek output number.
  4. Likho aur khisko: wo number output grid ki matching jagah par likho. Kernel ko ek step (stride) khiskao aur repeat karo. Wahi same weights aur bias har jagah.
z[i, j] = Σu=0K−1 Σv=0K−1 K[u, v] · x[i + u, j + v] + b
  • x = input image (ya pichhli layer ka map); x[r, c] = row r, column c ka pixel
  • K[u, v] = kernel ke andar row u, column v ka weight; K (size) = kernel ki side (3 for 3×3)
  • i, j = kernel ka top-left input par kahan baitha hai = output me kaunsa khaana bharna hai
  • u, v = kernel ke andar ghoomne wale counters (0 se K−1)
  • b = bias (ek hi number, poore filter ke liye shared)
  • z[i, j] = output feature map ka ek khaana

Formula aisa kyun? Ye bas "dot product" hai patch aur kernel ka. Dot product tab bada hota hai jab patch ka pattern kernel ke pattern jaisa ho (bade numbers bade weights se milein). Isliye kernel ek pattern detector hai: jahan pattern mila, wahan output bada. Har output ek purana dost hai: w·x + b, jo perceptron me tha — bas ab inputs ek chhote patch se aate hain aur weights har jagah same hain.

Step 1: patch 1 1 1 0 1 1 0 0 1 ⊙ Step 2: kernel 1 0 1 0 1 0 1 0 1 = Step 3: products 1 0 1 0 1 0 0 0 1 Step 4: sab jodo 1+0+1+0+1+0+0+0+1 = 4 (+ bias 0) Element-wise multiply (matching khaane ka guna), matrix multiplication NAHI.
Ek output number kaise banta hai: patch aur kernel ke matching khaanon ka guna (⊙), phir saare 9 products ka jod.

Chalo numbers se samjhein — poora 5×5 ⊛ 3×3 example (stride 1, padding 0)

Input X (5×5) aur kernel K (3×3):

Input X

11100
01110
00111
00110
01100

Kernel K

101
010
101

Output size pehle: har direction me kernel 5 − 3 + 1 = 3 jagah fit hota hai (left edge 0, 1, 2 par). To output 3×3, yani 9 positions. Bias = 0.

  1. Position (0, 0) — rows 0–2, cols 0–2. Patch = [1 1 1; 0 1 1; 0 0 1]. Products ek-ek karke:
    1×1 = 1,  1×0 = 0,  1×1 = 1
    0×0 = 0,  1×1 = 1,  1×0 = 0
    0×1 = 0,  0×0 = 0,  1×1 = 1
    Jod = 1+0+1+0+1+0+0+0+1 = 4
  2. Shortcut dekho: kernel me 1 sirf chaar konon + centre par hai, baaki 0. To har output = patch ke 4 kone + centre ka jod. Baaki 8 positions isi tarah:
Output cellPatch (rows, cols)4 koneCentreJod
(0, 0)rows 0–2, cols 0–21, 1, 0, 114
(0, 1)rows 0–2, cols 1–31, 0, 0, 113
(0, 2)rows 0–2, cols 2–41, 0, 1, 114
(1, 0)rows 1–3, cols 0–20, 1, 0, 102
(1, 1)rows 1–3, cols 1–31, 1, 0, 114
(1, 2)rows 1–3, cols 2–41, 0, 1, 013
(2, 0)rows 2–4, cols 0–20, 1, 0, 102
(2, 1)rows 2–4, cols 1–30, 1, 1, 013
(2, 2)rows 2–4, cols 2–41, 1, 1, 014

(Kone ka order: top-left, top-right, bottom-left, bottom-right.) Ek full check aur: (1, 2) ka patch = [1 1 0; 1 1 1; 1 1 0]; products = [1 0 0; 0 1 0; 1 0 0]; jod = 1 + 1 + 1 = 3. ✓

Jawab: Output feature map = [[4, 3, 4], [2, 4, 3], [2, 3, 4]] (3×3). Kul kaam: 9 positions × 9 multiplications = 81 multiplications.

Input X (5×5) 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 1 1 0 0 1 1 0 0 ⊙ Kernel K (3×3) 1 0 1 0 1 0 1 0 1 = Output (3×3) 4 3 4 2 4 3 2 3 4 laal window → out[0][0] = 4 orange window → out[1][2] = 3 Har direction me 5 − 3 + 1 = 3 jagah → 3 × 3 = 9 positions
Torch ki roshni (3×3 window) 5×5 input par: har position par patch ⊙ kernel ka jod ek output number banata hai. Kul 3 × 3 = 9 positions.

MCQ ka favourite: "Convolution" asal me cross-correlation hai

Maths/signal processing wali asli convolution me kernel ko pehle 180° ghuma (flip) diya jaata hai (upar-neeche aur left-right dono), phir slide karte hain: x[i − u, j − v]. Deep learning libraries (PyTorch nn.Conv2d, F.conv2d, TensorFlow) kernel ko flip nahi karti: x[i + u, j + v]. Is operation ka sahi naam cross-correlation hai, lekin naam "convolution" chipak gaya. Farak kyun nahi padta? Kyunki kernel ke weights seekhe jaate hain: agar flip wala version chahiye tha, network seedha flipped weights seekh lega. (Hamare upar wale kernel me to farak dikhega hi nahi, kyunki wo 180° ghumane par bhi same hai. Asymmetric kernel par farak 23.4 me dekhenge.)

Code: hamara hand-calculation PyTorch se verify

F.conv2d(input, weight, bias=None, stride=1, padding=0) — functional (bina class ke) convolution. input ka shape (N, Cin, H, W) aur weight ka shape (Cout, Cin, kH, kW) hona chahiye; isliye hum 2D tables ko 4D me reshape karte hain.

PyTorch · F.conv2d se 5×5 example check

import torch
import torch.nn.functional as F

X = torch.tensor([[1,1,1,0,0],
                  [0,1,1,1,0],
                  [0,0,1,1,1],
                  [0,0,1,1,0],
                  [0,1,1,0,0]], dtype=torch.float32)
K = torch.tensor([[1,0,1],
                  [0,1,0],
                  [1,0,1]], dtype=torch.float32)

x = X.reshape(1, 1, 5, 5)   # (N=1 photo, C_in=1 channel, H=5, W=5)
w = K.reshape(1, 1, 3, 3)   # (C_out=1 filter, C_in=1, kH=3, kW=3)

out = F.conv2d(x, w)        # default stride=1, padding=0, bias nahi
print(out.shape)            # (1, 1, 3, 3): 5 - 3 + 1 = 3
print(out)

Output

torch.Size([1, 1, 3, 3])
tensor([[[[4., 3., 4.],
          [2., 4., 3.],
          [2., 3., 4.]]]])

Hamari table se bilkul match. Chaar brackets [[[[ isliye kyunki output bhi 4D hai: (N, C, H, W).

Convolution ke types: 1D, 2D, 3D (slide 27)

TypeKernel kitni directions me slide karta haiData examplePyTorch
1D convolution1 (sirf time/length ke along)audio, ECG, stock prices, text sequencesnn.Conv1d, input (N, C, L)
2D convolution2 (height aur width)photos (grayscale/RGB)nn.Conv2d, input (N, C, H, W)
3D convolution3 (depth/time, height, width)video, CT/MRI scans (volumes)nn.Conv3d, input (N, C, D, H, W)

Dhyan do: RGB photo par bhi 2D convolution hi hoti hai, bhale kernel 3 channels deep ho. "D" ka matlab hai kernel kitni directions me slide karta hai, channels kitne hain ye nahi.

Exam Radar

  • Convolution = patch ⊙ kernel (element-wise multiply) → sum → + bias → ek number; phir slide. Ye matrix multiplication nahi hai.
  • DL "convolution" = cross-correlation (kernel flip nahi hota). Learned weights ki wajah se flip irrelevant.
  • n×n input, k×k kernel, stride 1, no padding → output (n − k + 1) × (n − k + 1). 5×5 ⊛ 3×3 → 3×3. 6×6 ⊛ 3×3 → 4×4. 8×8 ⊛ 3×3 → 6×6 (36 patches). 7×7 ⊛ 3×3 → 5×5 (25 placements).
  • Ek 3×3 output ke liye 9 multiplications; har output ek dot product hai (w·x + b).
  • Kernel = filter = feature detector = "learnable weights". Output of one filter = feature map.
  • 1D (signals/text), 2D (images), 3D (video/volumes).

Galti mat karna

  • Patch aur kernel ka matrix product mat karo (row × column wala). Sirf same position wale numbers ka guna.
  • Output ka size input jitna nahi hota (bina padding ke) — har baar K − 1 kam hota hai.
  • Kernel har position par badalta nahi. Alag positions = alag patches, detector wahi.

Dimaag me aise baitha lo

"Rakho, guna karo, jodo, likho, khisko." — Torch rakho, matching khaane guna, sab jod, output me likho, aage khisko.

23.4 Filters / kernels — chhote stencil jo pattern pakadte hain

Kahani

Maa ke paas rangoli ke stencils (saanche) ka ek set hai: ek me seedhi khadi line, ek me leti hui line, ek me phool. Golu ek stencil uthata hai aur rangoli par jagah-jagah rakh kar dekhta hai: "Yahan stencil fit baitha? Haan → bada number. Nahi → chhota number." Har stencil ek alag pattern ka jasoos hai. Teacher ki slide 38 kehti hai: "The filter is basically a feature detector. It 'lights up' (activates) when it sees a specific pattern (like a vertical edge or a curve)."

Kernel aur filter ek hi cheez ke do naam hain (exam me dono aate hain): weights ki ek chhoti table, jaise 3×3 ya 5×5. Teacher ki slide 37 me 6×6 image ke saath do filters diye the — Filter 1 = [1 −1 −1; −1 1 −1; −1 −1 1] (diagonal "\" line ka detector) aur Filter 2 = [−1 1 −1; −1 1 −1; −1 1 −1] (beech me khadi line ka detector) — aur likha tha: "These are the network parameters to be learned. Each filter detects a small pattern (3 × 3)."

Pehle haath se bane (classical) kernels — taaki samajh aaye kernel "kya" dekhta hai

KernelValuesKya karta haiWeights ka jod
Identity
000
010
000
Centre pixel copy kar deta hai (photo jaisi thi waisi)1
Vertical edge (left − right)
10−1
10−1
10−1
Khadi (vertical) edges: left roshan, right andhera → positive0
Horizontal edge (top − bottom)
111
000
−1−1−1
Leti (horizontal) edges: upar roshan, neeche andhera → positive0
Sobel-x (right − left)
−101
−202
−101
Vertical edges; beech wali row ko double weight (2) — thoda noise-robust. Right side roshan → positive0
Sobel-y
−1−2−1
000
121
Horizontal edges (neeche − upar)0
Box blur(1/9) ×
111
111
111
9 pixels ka average → photo dhundhli/smooth; noise kam1
Sharpen
0−10
−15−1
0−10
Centre ko padosiyon se zyada ubhaarta hai → details tez1

Jod wala column bahut kaam ka hai. Edge kernels ke weights ka jod 0 hai, isliye flat (ek jaisi) jagah par output 0 aata hai: +10 aur −10 cancel. Blur aur sharpen ka jod 1 hai, isliye flat jagah par brightness waisi hi rehti hai (10 → 10). Fark sirf wahan dikhta hai jahan pixels badalte hain.

Chalo numbers se samjhein — edge detection (strong response at the edge)

6×6 image: pehle 3 columns roshan (10), baaki 3 andhere (0). Har row = [10, 10, 10, 0, 0, 0]. Kernel = vertical edge [1 0 −1] har row me. Output 6 − 3 + 1 = 4 → 4×4.

  1. Window cols 0–2 (poora roshan): har row ka contribution 10×1 + 10×0 + 10×(−1) = 0; 3 rows → 0.
  2. Window cols 1–3 (edge ke upar): har row 10×1 + 10×0 + 0×(−1) = 10; 3 rows → 30.
  3. Window cols 2–4: har row 10×1 + 0×0 + 0×(−1) = 10 → 30.
  4. Window cols 3–5 (poora andhera): 0 + 0 + 0 = 0 → 0.
  5. Har row ke liye same (image upar-neeche ek jaisi hai).

Jawab: har row [0, 30, 30, 0]. Edge ke paas strong response 30, flat jagah 0.

Aur dekho: (a) isi image par horizontal edge kernel lagao to saare 0 — kyunki upar-neeche koi badlaav nahi. (b) Image ulti karo (left andhera, right roshan) to response −30: sign batata hai edge ki direction (andhera→roshan ya roshan→andhera), magnitude batata hai edge kitni strong. (c) Sobel-x isi original image par −40 deta hai: (−1)(10) + (−2)(10) + (−1)(10) = −40 (right − left, aur yahan right andhera hai).

Input: left roshan (10), right andhera (0) 10 10 10 0 0 0 10 10 10 0 0 0 10 10 10 0 0 0 10 10 10 0 0 0 10 10 10 0 0 0 10 10 10 0 0 0 edge yahan Vertical edge kernel 1 0 −1 1 0 −1 1 0 −1 left − right → Feature map (4×4) 0 30 30 0 0 30 30 0 0 30 30 0 0 30 30 0 30 = edge mila! 0 = flat area, koi edge nahi orange window: 10+10+10 − (0+0+0) = 30
Vertical edge kernel (left column + , right column −) sirf wahan bada number deta hai jahan left aur right me farak hai — yani edge par. Flat jagah par +10 aur −10 cancel ho jaate hain.

Teacher ka L10 example — Sobel-x par ek 6×6 bar

Input (6×6, values 0/1): beech me 2 columns ki khadi patti (rows 1–4, cols 2–3 = 1, baaki 0). Kernel Sobel-x = [−1 0 1; −2 0 2; −1 0 1], bias 0.

  1. Pehla patch (rows 0–2, cols 0–2) = [0 0 0; 0 0 1; 0 0 1].
  2. Products: 0×(−1) + 0×0 + 0×1 + 0×(−2) + 0×0 + 1×2 + 0×(−1) + 0×0 + 1×1 = 2 + 1 = 3
  3. Poora 4×4 map (sab positions isi tarah): [[3, 3, −3, −3], [4, 4, −4, −4], [4, 4, −4, −4], [3, 3, −3, −3]].

Jawab: patti ke left edge par positive (andhera → roshan, right side brighter), right edge par negative. "Reverse the contrast, and the sign reverses."

Worksheet ka exercise (haath se)

Patch = [1 2 0; 0 1 2; 3 0 1], kernel = [1 0 −1; 1 0 −1; 1 0 −1].

  1. Row 1: 1·1 + 2·0 + 0·(−1) = 1
  2. Row 2: 0·1 + 1·0 + 2·(−1) = −2
  3. Row 3: 3·1 + 0·0 + 1·(−1) = 2

Jawab: 1 − 2 + 2 = 1.

Ek patch, alag-alag kernels

Patch = [10 10 10; 10 50 10; 10 10 10] (beech me ek chamakta dot).

  1. Identity: sirf centre = 50
  2. Box blur: (8×10 + 50)/9 = 130/9 ≈ 14.4444 — chamak phail kar dhimi ho gayi
  3. Sharpen: 5×50 − (10+10+10+10) = 250 − 40 = 210 — chamak aur tez
  4. Flat patch (saare 10): blur = 10, sharpen = 5×10 − 40 = 10, vertical edge = 0, Sobel = 0.

Cross-correlation vs convolution — asymmetric kernel par farak

Vertical edge kernel [1 0 −1] ko 180° flip karo to [−1 0 1] ban jaata hai. Asli maths-convolution flipped kernel use karti, to hamari edge image par −30 aata; PyTorch (cross-correlation) +30 deta hai. Neeche code me torch.flip se ye farak dekh lo. Training me ye matter nahi karta, kyunki network jo bhi sign chahiye wahi weights seekh leta hai.

PyTorch · edge kernels with F.conv2d

import torch
import torch.nn.functional as F

img = torch.tensor([[10,10,10,0,0,0]] * 6, dtype=torch.float32).reshape(1, 1, 6, 6)
vert = torch.tensor([[1,0,-1],
                     [1,0,-1],
                     [1,0,-1]], dtype=torch.float32).reshape(1, 1, 3, 3)
horiz = vert.transpose(2, 3)      # rows <-> cols: [1 1 1; 0 0 0; -1 -1 -1]

print(F.conv2d(img, vert))                            # vertical edge pakdo
print(F.conv2d(img, horiz))                           # horizontal edge: yahan koi nahi
print(F.conv2d(img, torch.flip(vert, dims=[2, 3])))   # "asli" convolution = flipped kernel

Output

tensor([[[[ 0., 30., 30.,  0.],
          [ 0., 30., 30.,  0.],
          [ 0., 30., 30.,  0.],
          [ 0., 30., 30.,  0.]]]])
tensor([[[[0., 0., 0., 0.],
          [0., 0., 0., 0.],
          [0., 0., 0., 0.],
          [0., 0., 0., 0.]]]])
tensor([[[[  0., -30., -30.,   0.],
          [  0., -30., -30.,   0.],
          [  0., -30., -30.,   0.],
          [  0., -30., -30.,   0.]]]])

CNN me kernels haath se nahi banate — seekhe jaate hain

Classical image processing

Insaan filters design karta tha (Sobel, blur...). Values fixed. Kaunsa feature chahiye, ye insaan decide karta tha.

Convolutional Neural Network

Kernel ke 9 numbers = trainable weights (aur bias bhi). Random init se shuru, backprop + optimizer unhe update karte hain. Task decide karta hai kaunse detectors useful hain; wo Sobel jaise dikhein, zaroori nahi.

Ek shared weight poori image par bahut jagah use hota hai, to backprop me uska gradient = har position se aaye gradients ka jod (teacher: "Backpropagation combines gradients from every use of a shared weight"). Trained CNNs ki pehli layer ke filters aksar edge, colour-blob aur stripe detectors jaise dikhte hain — Hubel–Wiesel ke simple cells jaise!

Kernel size odd kyun (3, 5, 7)? Odd size me ek centre pixel hota hai, to output ko "is pixel ke aas-paas" ka jawab maan sakte hain, aur padding (K − 1)/2 dono taraf barabar baith jaati hai. Sabse common: 3×3.

Exam Radar

  • Filter = kernel = feature detector = learnable weights ("network parameters to be learned").
  • Sobel-x = [−1 0 1; −2 0 2; −1 0 1] (vertical edges), Sobel-y = Sobel-x ka transpose (horizontal edges). Box blur = (1/9)·ones. Sharpen = centre 5, chaar padosi −1.
  • Edge kernels ke weights ka sum = 0 → uniform region par response 0. Blur/sharpen ka sum = 1 → brightness preserve.
  • Response ka sign = edge ki direction (contrast reverse → sign reverse); magnitude = edge ki strength.
  • Classical: fixed hand-designed filters. CNN: filters learned by backprop; shared weight ka gradient = sab positions ka sum.
  • Kernel sizes usually odd; 3×3 most common.

Galti mat karna

  • Negative output ka matlab "edge nahi" nahi hai — wo ulti direction ki edge hai. 0 ka matlab hai "koi badlaav nahi".
  • "CNN Sobel filters use karta hai" — nahi; CNN apne filters khud seekhta hai. Sobel sirf samjhane ke liye.

Dimaag me aise baitha lo

Edge kernel = "left wale ko plus, right wale ko minus" — dono barabar hain to zero, farak hai to number. Edge = farak.

23.5 Bias, feature map, aur "N filters → N channels"

Kahani

School me talent hunt hai. Teen judges hain: ek singing dhoondhta hai, ek dance, ek cricket. Har judge poore school ki har class me jaata hai aur apni ek alag list (naksha) banata hai: "kis class me kitna talent". Teen judges → teen naksha. Aur har judge ka apna mood bhi hai: ek thoda lenient hai, sabko +2 extra deta hai; ek strict hai, sabse −1. Ye extra = bias. Judge = filter, naksha = feature map, teen naksha ka gattha = output ke 3 channels.

Bias: har filter ka ek number

Convolution ke jod me ek bias b judta hai: z = Σ (patch ⊙ kernel) + b. Ye bias har filter ka sirf ek hota hai, aur us filter ke har output position par wahi add hota hai (bias bhi shared hai). Kaam: filter ka "threshold" khiskana — jaise perceptron me bias decision ko easy/hard banata tha. Agar upar wale edge example me b = −5 hota, to map [0, 30, 30, 0] → [−5, 25, 25, −5] ban jaata.

Feature map / activation map

Definition

Feature map: ek filter ko input ki har position par chala kar jo 2D grid of responses milta hai. Har value ek neighbourhood ka jawab hai: bada positive = "pattern strongly mila", aur map batata hai pattern kahan mila. Jab is map par activation (ReLU) laga dete hain to isko activation map bhi kehte hain (kai log dono naam same cheez ke liye use karte hain). Ye values "activations" hain, image ke rang nahi.

Ek filter → ek feature map. Ek layer me bahut saare filters hote hain (har ek alag pattern ka jasoos). N filters → N feature maps → output ke N channels. Output ka shape: Hout × Wout × N. Teacher ki slide 67 ka line: "Each filter is a channel." Lab 12 me Conv 1 ke 32 filters → 32 feature maps; Conv 2 ke 64 filters → 64 maps.

Input 6×6×1 Filter 1 (+b₁) map 1 (4×4) Filter 2 (+b₂) map 2 (4×4) Filter 3 (+b₃) map 3 (4×4) 4×4×3 volume 3 filters → 3 feature maps → output ke 3 channels
Har filter poori image par ghoom kar apna ek feature map banata hai; sab maps ek ke peeche ek rakh do to output volume. Filters ki ginti = output channels.

Teacher ki slide ka example: 6×6 image, do filters

Image (6×6): rows = [1 0 0 0 0 1], [0 1 0 0 1 0], [0 0 1 1 0 0], [1 0 0 0 1 0], [0 1 0 0 1 0], [0 0 1 0 1 0]. Filter 1 = [1 −1 −1; −1 1 −1; −1 −1 1], Filter 2 = [−1 1 −1; −1 1 −1; −1 1 −1], stride 1, bias 0.

  1. Filter 1, top-left patch = [1 0 0; 0 1 0; 0 0 1] (ek perfect diagonal!). Products: diagonal par 1×1 teen baar = 3, baaki sab 0 (kyunki patch ke baaki pixels 0 hain) → 3. Pattern poora match → sabse bada value.
  2. Filter 2, wahi patch: row 1: 1×(−1) + 0×1 + 0×(−1) = −1; row 2: 0×(−1) + 1×1 + 0×(−1) = 1; row 3: 0 + 0×1 + 1×(−1) = −1; jod = −1.
  3. Saari 16 positions karne par:
    Filter 1 map: [3 −1 −3 −1], [−3 1 0 −3], [−3 −3 0 1], [3 −2 −2 −1]
    Filter 2 map: [−1 −1 −1 −1], [−1 −1 −2 1], [−1 −1 −2 1], [−1 0 −4 3]

Jawab: 6×6 image + 2 filters → 4×4×2 output volume. Filter 1 ke map me "3" wahan hain jahan diagonal line hai (top-left aur bottom-left).

Conv ke baad ReLU

Har conv ke baad usually ReLU lagta hai: max(0, z), har value par alag-alag (element-wise). Teacher ka L10 example: [−3, 2, −1, 4, 0, 1] → ReLU → [0, 2, 0, 4, 0, 1]. Teen baatein yaad rakho: (1) ReLU ke 0 trainable parameters; (2) shape same rehta hai (height, width, channels kuch nahi badalta); (3) ye non-linearity deta hai — bina iske kitni bhi conv layers stack karo, sab milkar ek hi linear (affine) operation ban jaati hain. Filter 1 map par ReLU: [3 0 0 0], [0 1 0 0], [0 0 0 1], [3 0 0 0] — sirf "mila" wale signals bache.

Activations ≠ parameters

Teacher ka ek tricky point: ek conv layer jisme 6 filters 5×5 ke hain (1 input channel), sirf (5×5×1 + 1)×6 = 156 parameters rakhti hai, lekin 32×32 image par 28×28×6 = 4,704 output values (activations) banati hai. Badi image → zyada activations aur compute, lekin parameters wahi.

PyTorch · ek conv layer = bahut saare filters

import torch
import torch.nn as nn
import torch.nn.functional as F

conv = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=3)   # 6 filters, har ek 3x3
x = torch.randn(1, 1, 28, 28)        # ek grayscale photo
fmap = conv(x)

print(conv.weight.shape)   # (6 filters, 1 input channel, 3, 3)
print(conv.bias.shape)     # har filter ka EK bias -> 6
print(fmap.shape)          # 6 feature maps, har ek 28-3+1 = 26 x 26

act = F.relu(fmap)         # activation map; shape same, negatives 0
print(act.shape, act.min().item())
print(fmap[0].numel())     # ek photo ke liye 6*26*26 activations

print(torch.relu(torch.tensor([-3., 2., -1., 4., 0., 1.])))   # teacher ka example

Output

torch.Size([6, 1, 3, 3])
torch.Size([6])
torch.Size([1, 6, 26, 26])
torch.Size([1, 6, 26, 26]) 0.0
4056
tensor([0., 2., 0., 4., 0., 1.])

Exam Radar

  • One bias per filter (per output channel), shared across all positions. conv.bias.shape = (out_channels,).
  • Number of filters = number of output channels = number of feature maps = out_channels.
  • Feature map records response by location (teacher MCQ) — "pattern kahan mila".
  • ReLU: 0 params, shape unchanged, adds non-linearity (without it, stacked convs = one linear map).
  • Activations depend on image size; parameters don't.

Galti mat karna

  • Har output pixel ka alag bias nahi hota; poore filter ka ek.
  • "Feature map = filter" nahi. Filter = weights (chhota, 3×3); feature map = filter chalane ka result (bada, 26×26).

Dimaag me aise baitha lo

Jitne judge, utne naksha. Jitne filters, utne channels.

23.6 Multi-channel input — filter ek "cube" hota hai

Kahani

Golu ki colour photo ko printer ne teen transparent sheets par chhaapa: ek par sirf laal, ek par hara, ek par neela. Teeno sheets ek ke upar ek rakho to poori photo. Ab stencil bhi teen parat (layer) ka banana padega — har sheet ke liye ek parat — aur teeno ek saath dabaye jaayenge. Har parat apni sheet se "match score" nikalegi, aur teeno scores jod kar ek hi number likha jaayega. Teen sheets, lekin jawab ek.

Teacher ki slide 49: "For a grayscale image (2D matrix of pixels), we have 2D filters, like 3×3, 5×5. For 3-channel image (RGB), we have 3D filters (like 3D volume). 3D filters are basically stacked 2D filters. One 2D filter per channel." Aur L10 worksheet: "Each filter spans all input channels. Sum those responses to form one output feature map."

z[i, j] = Σc=1Cin Σu Σv K[c, u, v] · x[c, i + u, j + v] + b
  • c = channel number (RGB me 1, 2, 3); Cin = input channels
  • K[c, u, v] = filter ki c-th parat ka (u, v) weight; ek filter ka shape = (Cin, K, K)
  • Teen Σ = channels ke upar bhi jod, rows ke upar bhi, columns ke upar bhi → ek number; b = filter ka ek bias

Lab 12 ke notes me likha hai: "N×N×C * M×M×C = (N − M + 1) × (N − M + 1) (single channel). You get a single-channel output because one filter computes the dot product across all C input channels simultaneously and sums those results into a single scalar value for each spatial position."

Ab poori layer: Cout filters, har ek (Cin, K, K) ka → saare weights ek 4D tensor me:

weight shape = (Cout, Cin, K, K)     bias shape = (Cout)
  • Cout = filters ki ginti (= output channels); Cin = input channels (= har filter ki depth)
  • Example: Lab 12 ka nn.Conv2d(32, 64, 3) → weight (64, 32, 3, 3), bias (64)
B G R Input: H × W × 3 laal = ek 3×3×3 patch Filter: 3 parat 3×3×3 = 27 weights + 1 bias Σ sab channels + b SIRF EK feature map (H_out × W_out × 1) C_out filters → C_out maps weight tensor: (C_out, C_in, K, K) Filter depth = C_in (automatic). Tum sirf K aur C_out chunte ho.
RGB input par filter bhi 3 parat (R, G, B) ka hota hai. Teeno parat apne-apne channel se guna-jod karte hain, phir teeno jod + bias ek number. Isliye ek filter = ek 2D map, chahe input me kitne bhi channels hon.

Chalo numbers se samjhein — RGB input, ek filter

Input 3×3×3 (teen channels, har ek 3×3). Filter = 3 parat, har parat 2×2 (filter shape (3, 2, 2)), bias = 1, stride 1. Output = (3 − 2 + 1) × (3 − 2 + 1) × 1 = 2×2×1.

Input channels

R = [1 2 0; 0 1 3; 2 1 0]
G = [0 1 1; 2 0 1; 1 0 2]
B = [1 0 2; 1 1 0; 0 2 1]

Filter parat

KR = [1 0; 0 1]
KG = [0 1; 1 0]
KB = [1 −1; 0 1]
bias b = 1

  1. Top-left, R parat: patch [1 2; 0 1] → 1×1 + 2×0 + 0×0 + 1×1 = 2
  2. Top-left, G parat: patch [0 1; 2 0] → 0×0 + 1×1 + 2×1 + 0×0 = 3
  3. Top-left, B parat: patch [1 0; 1 1] → 1×1 + 0×(−1) + 1×0 + 1×1 = 2
  4. Channels ka jod + bias: 2 + 3 + 2 + 1 = 8
  5. Baaki positions ke "partial maps" (har channel ka apna jod):
    R part = [2 5; 1 1]
    G part = [3 1; 1 1]
    B part = [2 −2; 2 2]
    Total = R + G + B + 1 = [2+3+2+1, 5+1−2+1; 1+1+2+1, 1+1+2+1] = [8 5; 5 5]

Jawab: ek filter → ek 2×2 map [[8, 5], [5, 5]]. Teen channels ke teen partial maps jud kar ek ho gaye.

Teacher ka L10 count

RGB input, 3×3 kernels, 4 filters. Har filter = 3 × 3 × 3 = 27 weights + 1 bias. Total = (27 + 1) × 4 = 112 trainable parameters, aur output me 4 channels. Worksheet ka line bhi: agli layer agar 64 maps padh rahi hai, to uska ek 3×3 filter = 64 × 3 × 3 = 576 weights (per output channel).

PyTorch · multi-channel conv verify

import torch
import torch.nn as nn
import torch.nn.functional as F

R = [[1,2,0],[0,1,3],[2,1,0]]
G = [[0,1,1],[2,0,1],[1,0,2]]
B = [[1,0,2],[1,1,0],[0,2,1]]
x = torch.tensor([R, G, B], dtype=torch.float32).unsqueeze(0)   # (1, 3, 3, 3) = (N, C_in, H, W)

w = torch.tensor([[[[1, 0], [0, 1]],       # R parat
                   [[0, 1], [1, 0]],       # G parat
                   [[1,-1], [0, 1]]]],     # B parat
                 dtype=torch.float32)       # (C_out=1, C_in=3, 2, 2)
b = torch.tensor([1.0])                     # ek filter -> ek bias

print(x.shape, w.shape)
print(F.conv2d(x, w, bias=b))              # channels apne aap jud jaate hain

conv = nn.Conv2d(3, 4, kernel_size=3)       # RGB -> 4 filters
print(conv.weight.shape, conv.bias.shape, sum(p.numel() for p in conv.parameters()))

Output

torch.Size([1, 3, 3, 3]) torch.Size([1, 3, 2, 2])
tensor([[[[8., 5.],
          [5., 5.]]]])
torch.Size([4, 3, 3, 3]) torch.Size([4]) 112

Exam Radar

  • Filter depth = input channels, hamesha (automatic). Tum sirf kernel size K aur filters ki ginti Cout chunte ho.
  • Ek filter kitne bhi input channels par chale → ek output map (channels sum ho jaate hain).
  • Conv2d weight shape = (out_channels, in_channels, kH, kW); bias = (out_channels,). (nn.Linear ka weight bhi (out, in) hota hai — same pattern.)
  • Agli layer ka in_channels = pichhli layer ka out_channels.
  • (3×3×3 + 1) × 4 = 112; 3×3 on 64 maps = 576 weights per filter.

Galti mat karna

  • "RGB par ek filter 3 maps deta hai" — galat. Ek filter = ek map. (Har channel ko alag rakhna "depthwise convolution" hai — wo alag cheez hai, next chapters me.)
  • Weight shape me order ulta mat likho: (Cout, Cin, K, K), na ki (Cin, Cout, ...).

Dimaag me aise baitha lo

Teen sheets, teen parat ka stencil, ek hi jawab. "Channel andar jaate hain, jud jaate hain; filters bahar nikalte hain, channel bante hain."

23.7 Stride — torch kitna kood kar khiskti hai

Kahani

Golu seedhiyan chadh raha hai. Ek-ek seedhi chadhe to har seedhi par pair padta hai (koi seedhi miss nahi). Do-do seedhi kood kar chadhe to jaldi pahunchega, lekin beech wali seedhiyon par kabhi pair nahi padega. Torch ke saath bhi yahi: har baar 1 pixel khiskao ya 2 pixel kood jao. Kitna koodna hai = stride.

Definition

Stride (S): filter ek position se agli position tak kitne pixel khiskta hai — horizontally bhi aur vertically bhi. Slide 35: "Stride is the amount of shift between subsequent convolution." Default S = 1.

Stride S = 1 pila = window ka top-left kahan-kahan 5 × 5 = 25 positions → 5×5 output Stride S = 2 3 × 3 = 9 positions → 3×3 output laal → orange: S=1: 1 khaana S=2: 2 khaane 7×7, K=3
7×7 input par 3×3 kernel: stride 1 me har khaane se window shuru hoti hai (25 jagah), stride 2 me ek chhod kar (9 jagah). Bada stride = chhota output.

Chalo numbers se samjhein

Sawaal 1: Hamara 5×5 input aur wahi 3×3 kernel ([1 0 1; 0 1 0; 1 0 1]), lekin stride 2.

  1. Size: ⌊(5 − 3)/2⌋ + 1 = ⌊1⌋ + 1 = 2 → 2×2 output.
  2. Window ke top-left: rows {0, 2} × cols {0, 2} → (0,0), (0,2), (2,0), (2,2).
  3. Ye wahi values hain jo stride-1 map me in jagahon par thi: (0,0) = 4, (0,2) = 4, (2,0) = 2, (2,2) = 4.

Jawab: [[4, 4], [2, 4]]. Stride 2 ne stride-1 map ke beech wale khaane skip kar diye.

Sawaal 2 (worksheet): H = 7, K = 3, P = 0.

  1. S = 1: ⌊(7 − 3)/1⌋ + 1 = 5 → 25 visits.
  2. S = 2: ⌊4/2⌋ + 1 = 3 → origins 0, 2, 4 → 9 visits (25 nahi).
  3. S = 3: ⌊4/3⌋ + 1 = ⌊1.33⌋ + 1 = 2 → origins 0, 3 → 4 visits. Origin 6 par kernel bahar latak jaata (6, 7, 8), isliye aakhri column bach gaya, use nahi hua. Isi ke liye formula me floor hai.

PyTorch · stride

x, w = X.reshape(1, 1, 5, 5), K.reshape(1, 1, 3, 3)   # 23.3 wale 5x5 aur 3x3 (beech ke code ne x, w badal diye the)
print(F.conv2d(x, w, stride=2))          # stride=2: do-do khaane kood kar

conv_s2 = nn.Conv2d(3, 6, kernel_size=3, stride=2, padding=1)   # layer me bhi stride argument
print(conv_s2(torch.zeros(1, 3, 32, 32)).shape)                 # 32 -> 16

Output

tensor([[[[4., 4.],
          [2., 4.]]]])
torch.Size([1, 6, 16, 16])

Stride-2 convolution vs pooling: dono map chhota karte hain, lekin stride wali conv ab bhi seekhe hue weights ka dot product nikalti hai, jabki pooling ek fixed rule (max/average) hai, koi weight nahi. Modern networks (ResNet etc.) aksar pooling ki jagah stride-2 conv se downsample karte hain. AlexNet ki pehli layer ka stride 4 tha (bahut aggressive downsampling).

Exam Radar

  • nn.Conv2d me stride default = 1. (Pooling me default stride = kernel_size — farak yaad rakho!)
  • Stride ↑ → output ↓, compute ↓, parameters same. "Larger stride usually gives smaller spatial output" (teacher MCQ).
  • Stride tuple bhi ho sakta hai: stride=(2, 1) = height me 2, width me 1.
  • 7×7, K = 3: S = 1, 2, 3 → 5, 3, 2.

Galti mat karna

  • Stride sirf right ki taraf nahi; neeche jaate waqt bhi utna hi koodta hai.
  • Stride lagane se pehle wala stride-1 map "compute karke drop" nahi hota; skipped positions compute hi nahi hoti.

23.8 Padding — photo ke chaaron taraf zero ka frame

Kahani

Golu ki torch ka centre kabhi bhi painting ke kone wale pixel par nahi aa paata — torch ka aadha hissa deewar se bahar latak jaata. Natija: kone ke pixels ko bahut kam baar dekha jaata hai, aur har baar torch chalane par painting chhoti hoti jaati hai. Jugaad: painting ke chaaron taraf ek khaali (zero) frame laga do. Ab torch kone par bhi centre ho sakti hai, aur output painting jitna hi bada aata hai.

Teacher ki slide 57 padding ke do purpose batati hai:

  1. Over-shrinkage rokna: har valid 3×3 conv border ka 1 pixel kha jaata hai (K − 1 = 2 kam). 32×32 par das 3×3 valid layers: 32 − 2×10 = 12. Deep network me photo gayab hi ho jaaye!
  2. Kone/border ki jaankari bachana: "the pixels at the corner are barely looked at. Central pixels are repeatedly calculated, and network becomes almost blind to corners." 5×5 input, 3×3 kernel, no padding: kone ka pixel (0,0) sirf 1 window me aata hai, centre pixel (2,2) saari 9 windows me. P = 1 ke saath kone wala pixel 4 windows me aata hai.

Definition

Zero padding (P): input ke har side (upar, neeche, left, right) P rows/columns zeros ki jod do. Naya size = (H + 2P) × (W + 2P). Padding se koi parameter nahi judta.
'valid' padding = koi padding nahi (P = 0); output chhota hota hai.
'same' padding = itni padding ki output size = input size (stride 1 par): P = (K − 1)/2.

Psame = K − 12   (stride 1, odd K)
  • K = 3 → P = 1; K = 5 → P = 2; K = 7 → P = 3
  • Kyun? Output = W − K + 2P + 1. Isko W ke barabar rakho: −K + 2P + 1 = 0 → P = (K − 1)/2.
Padded input: 5×5 + P=1 → 7×7 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 grey = zero padding (nakli border) 3×3 K Output: 5×5 (size bacha!) 2 2 3 1 1 1 4 3 4 1 1 2 4 3 3 1 2 3 4 1 0 2 2 1 1 kona (0,0) = 2 [0 0 0; 0 1 1; 0 0 1] orange beech wala 3×3 = purana valid output [4 3 4; 2 4 3; 2 3 4]
Zero padding (P = 1) se 5×5 input 7×7 ban gaya; 3×3 kernel ke baad output phir 5×5. Kone wale pixel bhi ab window ke centre me aa sakte hain.

Chalo numbers se samjhein

Sawaal 1 (worksheet): input [[1, 2], [3, 4]], P = 1.

  1. Padded grid 4×4: [0 0 0 0; 0 1 2 0; 0 3 4 0; 0 0 0 0] — asli numbers beech me, zeros ka rim.
  2. Ispar 3×3 identity kernel: (4 − 3)/1 + 1 = 2 → 2×2 output = [[1, 2], [3, 4]] — original photo wapas, kyunki har original pixel ab window ka centre ban sakta hai.

Sawaal 2: hamara 5×5 input, kernel [1 0 1; 0 1 0; 1 0 1], P = 1, S = 1.

  1. Size: (5 − 3 + 2×1)/1 + 1 = 5 → size bach gaya.
  2. Kona (0, 0): padded window = [0 0 0; 0 1 1; 0 0 1]. Kernel ke 4 kone + centre: 0 + 0 + 0 + 1 (bottom-right) + 1 (centre) = 2
  3. Full output: [[2, 2, 3, 1, 1], [1, 4, 3, 4, 1], [1, 2, 4, 3, 3], [1, 2, 3, 4, 1], [0, 2, 2, 1, 1]]
  4. Dekho, beech wala 3×3 = [[4, 3, 4], [2, 4, 3], [2, 3, 4]] — bilkul purana valid output. Padding ne bas border ke naye jawab jod diye.

Sawaal 3 (worksheet): H = 5, K = 3, S = 1 → same ke liye P = (3 − 1)/2 = 1. H = 7, K = 5 → P = 2. H = 7, K = 3, P = 0 → ⌊(7 − 3)/1⌋ + 1 = 5. Aur 7×7 par P = 1: output 7×7 (9×9 nahi!) — padding ne 2 joda, 3×3 ne 2 kha liya.

PyTorch · padding

print(F.conv2d(x, w, padding=1))                    # 5x5 -> 5x5

x32 = torch.zeros(1, 3, 32, 32)
print(nn.Conv2d(3, 6, kernel_size=5)(x32).shape)               # valid: 32-5+1 = 28
print(nn.Conv2d(3, 6, kernel_size=3, padding=1)(x32).shape)    # P=(3-1)/2=1 -> 32
print(nn.Conv2d(3, 6, kernel_size=3, padding='same')(x32).shape)   # Lab 12 style

try:
    nn.Conv2d(3, 6, kernel_size=3, stride=2, padding='same')   # 'same' + stride 2?
except Exception as e:
    print(type(e).__name__, ":", e)

Output

tensor([[[[2., 2., 3., 1., 1.],
          [1., 4., 3., 4., 1.],
          [1., 2., 4., 3., 3.],
          [1., 2., 3., 4., 1.],
          [0., 2., 2., 1., 1.]]]])
torch.Size([1, 6, 28, 28])
torch.Size([1, 6, 32, 32])
torch.Size([1, 6, 32, 32])
ValueError : padding='same' is not supported for strided convolutions

padding argument int (sab sides same), tuple (pH, pW), 'valid' ya 'same' le sakta hai. Zeros ke alawa padding_mode = 'reflect', 'replicate', 'circular' bhi hote hain (default 'zeros'). Lab 12 ne padding='same' use kiya: "with stride 1 + same padding, a 28×28 image stays 28×28 after a conv layer. We deliberately leave the shrinking to the pooling layers."

Exam Radar

  • Same padding (stride 1): P = (K − 1)/2. 3×3 → 1, 5×5 → 2, 7×7 → 3. Valid = no padding.
  • Padding ke 2 purpose: size preserve (over-shrinkage roko) + border/corner pixels ko barabar mauka.
  • nn.Conv2d default padding=0; padding='same' sirf stride 1 ke saath (warna ValueError).
  • Padding, stride: zero trainable parameters.
  • P per side hota hai; formula me 2P judta hai.

Galti mat karna

  • Formula me sirf P jodna (2P ki jagah) — sabse common galti.
  • Padding ko "naya data" mat samjho; ye bas zeros hain. (Isse border par halka sa "border artefact" ban sakta hai.)
  • "Same" ka matlab output channels same nahi — sirf height/width same.

Dimaag me aise baitha lo

Stride = kitna koodo. Padding = kitna frame lagao. 3×3 ke saath "1 ka frame, 1 ka kadam" = size same.

23.9 Output size ka master formula

Kahani

Mohalle ki deewar 7 haath lambi hai, aur Golu ko 3 haath ka poster lagana hai — har baar 2 haath aage khiska kar. Pehla poster 0 se shuru. Kitne poster lagenge? Poster ke liye khisakne ki jagah = 7 − 3 = 4 haath. 2-2 haath kood → 4/2 = 2 kood. Plus pehla wala → 3 poster. Bas yahi convolution ka output-size formula hai: "Jagah me se kernel ghatao, stride se bhaag do, neeche round karo, ek jodo."

O = ⌊ W − K + 2PS ⌋ + 1
  • O = output ki side (height ya width)
  • W = input ki side (height ke liye H daalo, width ke liye W; dono alag-alag calculate hote hain)
  • K = kernel ki side (filter size); pooling me window size
  • P = padding per side (isliye 2P: dono taraf judti hai)
  • S = stride
  • ⌊ ⌋ = floor (neeche round karo, jaise 13.5 → 13). Jo border bach gaya aur poora window nahi baith saka, wo use nahi hota.
  • "+1" = shuru wali position (fence-post counting)
  • Output channels is formula me nahi aate: wo = filters ki ginti (out_channels)

Teacher ke materials me yahi formula alag-alag roop me milega — sab ek hi hain: L10 worksheet: "Output size = floor((input + 2 × padding − kernel) / stride) + 1"; L11–12: Hout = ⌊(H + 2p − k)/s⌋ + 1; Lab 12 notes: (n − f + 1) (no padding, stride 1), (n − f + 2p + 1) (padding, stride 1), [(N − F)/S] + 1 (stride). PyTorch docs ka general version dilation D ke saath: O = ⌊(W + 2P − D·(K − 1) − 1)/S⌋ + 1; D = 1 par ye wahi upar wala ban jaata hai.

1D me socho: W = 7 khaane, K = 3, S = 2, P = 0 1 2 3 4 5 6 7 travel = W − K = 4 moves = travel / S = 4 / 2 = 2 kood positions = moves + 1 (shuru wali jagah) = 3 ← start 1, 3, 5 par window O = ⌊(W − K + 2P)/S⌋ + 1 = ⌊(7 − 3 + 0)/2⌋ + 1 = 3 Padding ho to W ki jagah W + 2P
Output size = kitni jagah window fit hoti hai: pehle khisakne ki jagah (travel) nikalo, stride se bhaag do (kitni kood), aur shuru ki position ke liye +1.

Chalo numbers se samjhein — teen important cases

Case A (AlexNet ki pehli layer): W = 227, K = 11, S = 4, P = 0, 96 filters.

  1. 227 − 11 + 0 = 216
  2. 216 / 4 = 54 (exact, floor ki zaroorat nahi)
  3. 54 + 1 = 55 → output 55 × 55 × 96

Worksheet ka "case file": agar input 224 ho aur P = 0: ⌊213/4⌋ + 1 = ⌊53.25⌋ + 1 = 54. P = 2 ke saath: ⌊(224 + 4 − 11)/4⌋ + 1 = ⌊54.25⌋ + 1 = 55. Isliye hamesha input size aur padding dono saath me batao.

Case B (ResNet jaisi stem): W = 224, K = 7, P = 3, S = 2.

  1. 224 − 7 + 2×3 = 223
  2. 223 / 2 = 111.5 → floor → 111
  3. 111 + 1 = 112 → exactly aadha (224 → 112)

Case C (non-integer, floor ka kamaal): W = 6, K = 3, P = 0, S = 2.

  1. (6 − 3 + 0)/2 = 1.5 → floor → 1
  2. 1 + 1 = 2 → 2×2. Windows columns 0–2 aur 2–4 par; column 5 kabhi kisi window me nahi aaya (agli window 4–6 bahar latakti).

Jawab: 55, 112, 2. Teeno PyTorch ne bhi confirm kiye (neeche code).

Input WKPSCalculationOutputNote
32501(32 − 5 + 0)/1 + 128valid conv; "32 − 5 + 1" (worksheet MCQ)
28311(28 − 3 + 2)/1 + 128same padding (Lab 12)
2271104216/4 + 155AlexNet conv1
2241104⌊213/4⌋ + 1 = 53 + 154floor case
73024/2 + 13worksheet
224732⌊223/2⌋ + 1 = 111 + 1112halving
6302⌊3/2⌋ + 1 = 1 + 12non-integer → floor
32312⌊31/2⌋ + 1 = 15 + 116teacher solved: 32×32×3, 8 filters → 16×16×8
28512⌊25/2⌋ + 1 = 12 + 113teacher quiz: 6 filters → 13×13×6
7303⌊4/3⌋ + 1 = 1 + 12worksheet
83015/1 + 16worksheet 8×8 digit → 6×6
2820226/2 + 1142×2 max pool (pooling bhi same formula)
15312⌊14/2⌋ + 1 = 7 + 18teacher pooling quiz
5530252/2 + 127AlexNet overlapping pool 3×3, s2

Ulta sawaal (reverse problems) — MCQ me aate hain

Python + PyTorch · formula aur asli layer dono se check

import math
import torch
import torch.nn as nn

def out_size(W, K, P=0, S=1, D=1):
    # PyTorch docs ka formula; D=1 par teacher wala formula
    return math.floor((W + 2*P - D*(K - 1) - 1) / S) + 1

print(out_size(32, 5))            # valid conv
print(out_size(28, 3, P=1))       # same padding
print(out_size(227, 11, S=4))     # AlexNet conv1
print(out_size(224, 11, S=4))     # floor case
print(out_size(224, 7, P=3, S=2)) # halving
print(out_size(6, 3, S=2))        # non-integer -> floor
print(out_size(7, 3, D=2))        # dilation (23.10)

print(nn.Conv2d(3, 96, kernel_size=11, stride=4)(torch.zeros(1, 3, 227, 227)).shape)
print(nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)(torch.zeros(1, 3, 224, 224)).shape)
print(nn.Conv2d(1, 1, kernel_size=3, stride=2)(torch.zeros(1, 1, 6, 6)).shape)

Output

28
28
55
54
112
2
3
torch.Size([1, 96, 55, 55])
torch.Size([1, 64, 112, 112])
torch.Size([1, 1, 2, 2])

Exam Radar

  • O = ⌊(W − K + 2P)/S⌋ + 1. Height aur width alag-alag. Channels = filters.
  • Default rounding = floor (PyTorch conv me hamesha; pooling me ceil_mode=True karke ceil le sakte ho).
  • Fixed numbers ratt lo: 32/5/0/1 → 28; 28/3/1/1 → 28; 227/11/0/4 → 55; 224/11/0/4 → 54; 7/3/0/2 → 3; 224/7/3/2 → 112; 28/5/1/2 → 13; 32/3/1/2 → 16.
  • "Output size independent of kernel values" — sirf size/stride/padding se.

Galti mat karna

  • "+1" bhoolna. (32 − 5)/1 = 27 galat; 27 + 1 = 28 sahi.
  • P ki jagah 2P na lagana; ya stride se poore (W + 2P) ko divide karke K baad me ghatana. Pehle W − K + 2P, phir ÷ S.
  • Floor ki jagah round karna: 13.5 → 13 (14 nahi).

Dimaag me aise baitha lo

"Wall − Kernel + 2Padding, Stride se bhaag, floor, plus one." Poster wali deewar yaad karo.

23.10 Dilation — chhed wala kernel (teacher ka teesra "knob")

Kahani

Golu ko ek badi thaali ki garmi check karni hai, lekin haath me sirf 3 ungliyan "sensor" hain. Ungliyan chipka kar rakhe to chhota area; ungliyan phaila de to wahi 3 ungliyan bada area cover kar leti hain — beech ki jagah chhoot jaati hai, lekin ungliyan (weights) utni hi. Ye hai dilation.

Teacher ki slide 34–35 ka title hi "The Knobs" tha: Kernel size, Stride, Dilation. "Kernel size is the size of input region for which weighted sum is calculated. Stride is the amount of shift between subsequent convolution. Dilation rate is determined by the number of 0s interspersed between kernel weights. Dilation rate of 1 is the normal convolution. Dilation rate of 2 skips 1 pixel (i.e. insert one 0 between kernel weights)."

Keff = D·(K − 1) + 1
  • D = dilation rate (1 = normal); K = asli kernel size
  • Keff = kernel jitna area cover karta hai; output formula me K ki jagah Keff daalo
  • Kyun aisa? K weights ke beech (K − 1) gaps hain; har gap ab D pixel chauda hai → (K − 1)·D, plus ek pixel shuru ka.
Normal 3×3 (D = 1) Dilated 3×3 (D = 2) w 0 w 0 w 0 0 0 0 0 w 0 w 0 w 0 0 0 0 0 w 0 w 0 w 9 weights hi hain, beech me 0 (chhed) K_eff = D(K − 1) + 1 = 2 × 2 + 1 = 5 5×5 area dekhta hai
Dilation 2: kernel ke weights ke beech ek-ek khaana chhod do. Weights 9 hi rahe, lekin ab 5×5 area cover hota hai.

Chalo numbers se samjhein

  1. K = 3, D = 2: Keff = 2×(3 − 1) + 1 = 5. K = 3, D = 3: 3×2 + 1 = 7.
  2. 7×7 input, K = 3, D = 2, P = 0, S = 1: 7 − 5 + 1 = 3 → 3×3 output.
  3. 32×32, K = 3, D = 2, P = 0: 32 − 5 + 1 = 28. Size bachana ho to P = 2: 32 − 5 + 4 + 1 = 32.
  4. Hamara 5×5 input + kernel [1 0 1; 0 1 0; 1 0 1] with D = 2: Keff = 5 → output 1×1. Taps sirf rows/cols 0, 2, 4 par; weight 1 wale taps = 4 kone (0,0), (0,4), (4,0), (4,4) + centre (2,2) = 1 + 0 + 0 + 0 + 1 = 2. PyTorch F.conv2d(x, w, dilation=2) bhi 2 deta hai.

Exam Radar

  • nn.Conv2d(..., dilation=1) default. Dilation se parameters nahi badhte (3×3 = 9 weights hi), lekin receptive field tez badhta hai — bina stride/pooling ke resolution khoye.
  • Keff = D(K − 1) + 1. Uses: semantic segmentation (DeepLab "atrous" convolution), audio (WaveNet).
  • Teacher ke 3 knobs: kernel size, stride, dilation (+ padding shape control ke liye).

23.11 Parameter count — kitne weights seekhne hain?

Kahani

Darzi (tailor) ke paas stencils ka set hai. Set ki keemat kis par depend karti hai? Har stencil kitna bada hai aur kitni parat ka hai, aur kitne stencils hain. Kapda 1 metre ka ho ya 100 metre ka — stencils wahi! Kapda bada ho to bas kaam (mehnat) zyada. Conv layer bhi aisi hi hai: parameters image ke size par depend nahi karte.

Conv params = (K × K × Cin + 1) × Cout
  • K × K = ek parat ke weights (3×3 = 9)
  • × Cin = filter me jitni parat (input channels) — ek filter ke saare weights
  • + 1 = us filter ka ek bias
  • × Cout = itne filters (output channels)
  • Sirf weights (bina bias): K²·Cin·Cout. Worksheet notation: "#params = Cout · Cin · K · K + Cout if you count biases".
  • H, W, stride, padding — kuch bhi formula me nahi! Yahi weight sharing ka kamaal hai.

Teacher (L10) ne do formulas saath me diye the: Convolution parameters = (kernel height × kernel width × input channels + 1) × filters; Dense parameters = (inputs + 1) × outputs. "+1" har filter/neuron ka bias. ReLU, standard pooling aur flatten = 0 parameters.

Chalo numbers se samjhein

  1. 3×3 conv, 3 → 64 (VGG ki pehli layer): 3×3×3 = 27; 27 + 1 = 28; 28 × 64 = 1,792 (bina bias 27 × 64 = 1,728 — worksheet).
  2. 5×5 conv, 6 → 16: 5×5×6 = 150; 150 + 1 = 151; 151 × 16 = 2,416.
  3. 5×5 conv, 1 → 6: (25 + 1) × 6 = 156.
  4. Worksheet: 16 kernels, 3 input channels, 3×3: 16 × 3 × 9 = 432 weights; bias ke saath 432 + 16 = 448.
  5. Worksheet final review: 16 filters, 32 input channels, 3×3, bias: 16 × 32 × 9 + 16 = 4,608 + 16 = 4,624.
  6. Lab 12: Conv2d(1, 32, 3) → (9×1 + 1) × 32 = 320; Conv2d(32, 64, 3) → (9×32 + 1) × 64 = 289 × 64 = 18,496.
  7. AlexNet conv1, 96 filters 11×11 on RGB: ek filter = 11×11×3 = 363 weights; total (363 + 1) × 96 = 34,944.

Fully connected se comparison — farak dekh kar chakkar aa jaayega

Same output, teen designs (worksheet ka example)

32×32 grayscale input (1,024 pixels), 3×3 kernel, stride 1, no padding → 30×30 = 900 outputs, ek channel. Bias chhod ke sirf weights:

  1. Dense (har input → har output): 1,024 × 900 = 921,600 weights.
  2. Locally connected, unshared (har output sirf apne 3×3 patch ko dekhe, lekin har output ke apne alag 9 weights): 9 × 900 = 8,100.
  3. Convolution (local + shared): sirf 9 weights, sab 900 jagah reuse.

Jawab: 921,600 → 8,100 → 9. Locality ne ~114 guna bachaya (921,600 / 8,100 ≈ 113.8), sharing ne aur 900 guna.

Lab 12 ka conv1 bhi dekho: input 28×28×1 (784), output 28×28×32 = 25,088 values. Agar ye dense layer hoti: 784 × 25,088 = 19,668,992 weights (bias ke saath 19,694,080). Conv1 me sirf 320.

Parameters vs activations vs compute

PyTorch · parameters gino

import torch.nn as nn

for cin, cout, k in [(3, 64, 3), (6, 16, 5), (1, 6, 5), (32, 16, 3), (1, 32, 3), (32, 64, 3)]:
    conv = nn.Conv2d(cin, cout, kernel_size=k)
    n = sum(p.numel() for p in conv.parameters())      # weight + bias ke saare numbers
    print(f"Conv2d({cin}, {cout}, {k}) -> weight {tuple(conv.weight.shape)}, params = {n}")

fc = nn.Linear(28 * 28, 32 * 28 * 28)                    # agar conv1 ki jagah dense hota
print("Linear(784, 25088) params =", sum(p.numel() for p in fc.parameters()))
print(sum(p.numel() for p in nn.BatchNorm2d(32).parameters()),   # 2 x 32
      sum(p.numel() for p in nn.MaxPool2d(2).parameters()))      # 0

Output

Conv2d(3, 64, 3) -> weight (64, 3, 3, 3), params = 1792
Conv2d(6, 16, 5) -> weight (16, 6, 5, 5), params = 2416
Conv2d(1, 6, 5) -> weight (6, 1, 5, 5), params = 156
Conv2d(32, 16, 3) -> weight (16, 32, 3, 3), params = 4624
Conv2d(1, 32, 3) -> weight (32, 1, 3, 3), params = 320
Conv2d(32, 64, 3) -> weight (64, 32, 3, 3), params = 18496
Linear(784, 25088) params = 19694080
64 0

Exam Radar

  • Conv params = (K·K·Cin + 1)·Cout. Must-know: 3×3, 3→64 = 1,792; 5×5, 6→16 = 2,416; 5×5, 1→6 = 156; 3×3, 32→16 = 4,624.
  • Params independent of H, W, stride, padding. Activations aur compute dependent.
  • Ek grayscale 3×3 filter = 9 weights + 1 bias = 10 params, chahe image me kitni bhi positions hon (teacher).
  • CNN me zyaadatar parameters FC layers me hote hain (Lab 12: fc1 akela 401,536 out of 429,450 ≈ 93.5%).

Galti mat karna

  • Cin bhoolna: RGB par 3×3 filter = 27 weights, 9 nahi.
  • Bias ko K·K·Cin ke saath multiply karna — bias per filter sirf 1.
  • H × W se multiply kar dena — wo activations/compute hai, parameters nahi.

23.12 Local connectivity + parameter sharing — CNN itna efficient kyun?

Kahani

School ka principal akela 1000 bachchon par nazar rakhe (fully connected) to pagal ho jaaye. Isliye har row ka ek monitor hai jo sirf apne aas-paas ke bachche dekhta hai (local connectivity). Aur sab monitors ke paas ek hi rulebook hai — "baat karna = minus, homework = plus" — row 1 me bhi wahi niyam, row 20 me bhi (parameter sharing). Principal ko 1000 alag rulebooks nahi likhni padti; ek likhi, sab jagah chala di.

Definition

Local connectivity (sparse interactions): har output neuron sirf input ke ek chhote patch (K×K×Cin, uska receptive field) se juda hai, poori image se nahi. Slide 59: "Only connect to 9 inputs, not fully connected → fewer parameters!"
Parameter sharing (weight sharing / tied weights): wahi kernel weights har position par use hote hain. Slide 60: "Shared weights → even fewer parameters."

Goodfellow ki book in teen ideas ko CNN ki taakat kehti hai: sparse interactions, parameter sharing, aur equivariant representations (23.14). Aur Prince (UDL) ka Fig 10.4 (teacher ki slide 61): 6 inputs → 6 hidden units. Fully connected me weight matrix me 36 weights. Kernel size 3 wali conv layer me matrix "fully connected ka special case hai jisme bahut saare weights zero hain aur baaki repeat hote hain". Stride 2 karo to har doosri row bachti hai — ek aur sparse structure.

Fully connected: 6 → 6 w11 w12 w13 w14 w15 w16 w21 w22 w23 w24 w25 w26 w31 w32 w33 w34 w35 w36 w41 w42 w43 w44 w45 w46 w51 w52 w53 w54 w55 w56 w61 w62 w63 w64 w65 w66 36 alag weights Convolution (K = 3, padding 1) b c 0 0 0 0 a b c 0 0 0 0 a b c 0 0 0 0 a b c 0 0 0 0 a b c 0 0 0 0 a b sirf 3 alag weights (a, b, c), baaki 0
Prince (UDL) Fig 10.4 jaisa: conv layer ek special fully connected layer hai jiske weight matrix me zyada tar zeros hain (local connectivity) aur baaki weights har row me repeat hote hain (weight sharing).

PyTorch · conv = sparse, shared matrix ka guna (1D example)

import torch
import torch.nn.functional as F

x = torch.tensor([1., 2., 3., 4., 5., 6.])
k = torch.tensor([1., 2., 3.])                    # a=1, b=2, c=3
out = F.conv1d(x.view(1, 1, 6), k.view(1, 1, 3), padding=1)
print(out)

Wm = torch.zeros(6, 6)                            # wahi kaam ek 6x6 matrix se
for i in range(6):
    for j, kv in zip(range(i - 1, i + 2), k):     # row i me sirf 3 jagah weights
        if 0 <= j < 6:
            Wm[i, j] = kv
print(Wm)
print(Wm @ x)                                     # same answer!

Output

tensor([[[ 8., 14., 20., 26., 32., 17.]]])
tensor([[2., 3., 0., 0., 0., 0.],
        [1., 2., 3., 0., 0., 0.],
        [0., 1., 2., 3., 0., 0.],
        [0., 0., 1., 2., 3., 0.],
        [0., 0., 0., 1., 2., 3.],
        [0., 0., 0., 0., 1., 2.]])
tensor([ 8., 14., 20., 26., 32., 17.])

Check ek value: output[1] = 1×1 + 2×2 + 3×3 = 1 + 4 + 9 = 14 ✓. Pehli row me sirf (2, 3) kyunki left me padding ka zero tha.

Ye "efficient" kyun hai?

Exam Radar

  • CNN compresses an FC network by: (1) reducing connections (local), (2) shared weights, (3) max pooling (slide 69).
  • Conv layer = FC layer whose weight matrix is sparse (zeros) + has repeated (tied) weights.
  • Locally connected layer = local but unshared (beech ka design); conv = local + shared.
  • "Which idea lets a detector work at every location: locality or sharing?" → sharing.

Galti mat karna

  • Weight sharing ka matlab "har jagah same output" nahi. Weights same, patches alag → outputs alag.
  • Sharing aur locality do alag decisions hain; dono milke conv banta hai.

Dimaag me aise baitha lo

Monitor sirf apni bench dekhta hai (local), aur sab monitors ki rulebook ek (shared). Yahi do cheezein CNN ko sasta banati hain.

23.13 Pooling — har mohalle ka ek representative

Kahani

Class me 16 bachche 4×4 me baithe hain, aur har 2×2 bench-group me 4 bachche. Principal ko poori list nahi chahiye, bas summary: "har group ka topper kitne laaya?" (max pooling) ya "har group ka average kitna?" (average pooling). 16 numbers → 4 numbers. Is kaam me koi seekhna nahi — bas fixed niyam. Aur agar topper apne hi group me ek seat idhar-udhar baith jaaye, summary wahi rehti hai.

Definition

Pooling: ek window (jaise 2×2) feature map par stride ke saath slide karti hai aur har window ka ek fixed summary nikalti hai — max (sabse bada) ya mean (average). Har channel par alag-alag lagta hai, isliye channels same rehte hain; sirf H aur W chhote hote hain. Standard max/avg pooling ke koi trainable weights ya bias nahi.

y = max{ xwindow }   ya   y = mean{ xwindow };    O = ⌊(W − K + 2P)/S⌋ + 1
  • xwindow = window ke andar ki values (2×2 me 4 values)
  • K = pooling window size, S = stride — wahi output formula, kernel ki jagah window
  • Typical: 2×2, stride 2 → H aur W aadhe (28 → 14 → 7), 75% values drop
Input 4×4 (ek channel) 1 3 2 4 5 6 1 8 9 2 0 1 3 4 5 2 2×2 window, stride 2 max avg Max pool → 2×2 6 8 9 5 Avg pool → 2×2 3.75 3.75 4.5 2 poora map Global avg pool 3.5 56 / 16 = 3.5 (global max = 9)
Teacher ka L10 example: har rang ka 2×2 group ek number ban jaata hai — max pool me sabse bada, avg pool me average. Global average pooling poore map ka ek average deta hai.

Chalo numbers se samjhein — teacher ka L10 4×4

Input = [1 3 2 4; 5 6 1 8; 9 2 0 1; 3 4 5 2], window 2×2, stride 2, padding 0. Size: ⌊(4 − 2)/2⌋ + 1 = 2 → 2×2.

  1. Top-left window (1, 3, 5, 6): max = 6; avg = (1+3+5+6)/4 = 15/4 = 3.75
  2. Top-right window (2, 4, 1, 8): max = 8; avg = 15/4 = 3.75
  3. Bottom-left window (9, 2, 3, 4): max = 9; avg = 18/4 = 4.5
  4. Bottom-right window (0, 1, 5, 2): max = 5; avg = 8/4 = 2
  5. Global average pooling: sab 16 ka jod = 56; 56/16 = 3.5. Global max = 9.

Jawab: Max pool = [[6, 8], [9, 5]], Avg pool = [[3.75, 3.75], [4.5, 2]], GAP = 3.5.

Worksheet ka 4×4 (aur "ek number badlo" wala sawaal)

Input = [1 3 2 0; 4 1 0 2; 2 2 8 1; 0 5 1 3], 2×2 max, stride 2.

  1. max(1,3,4,1) = 4, max(2,0,0,2) = 2, max(2,2,0,5) = 5, max(8,1,1,3) = 8
  2. Top-left average: (1+3+4+1)/4 = 9/4 = 2.25; bottom-right window [[8, 1], [1, 3]] ka average 13/4 = 3.25 (max 8).
  3. Agar 8 ko 1 kar do? Sirf bottom-right badlega: max(1, 1, 1, 3) = 3. Baaki teen windows me wo number tha hi nahi.

Jawab: [[4, 2], [5, 8]].

Slide ka poora chain: conv → max pool

23.5 wale do 4×4 maps par 2×2 max pool: Filter 1 map [3 −1 −3 −1; −3 1 0 −3; −3 −3 0 1; 3 −2 −2 −1] → [[3, 0], [3, 1]]. Filter 2 map → [[−1, 1], [0, 3]]. Slide 67: "6×6 image → Conv → Max Pooling → 2×2 image. Each filter is a channel. New image but smaller." Yani 6×6×1 → 4×4×2 → 2×2×2.

Overlapping pooling aur Global Average Pooling (GAP)

Pooling kyun? (Lab 12 notes + slides)

  1. Downsampling → kam compute aur memory: 28×28 → 14×14 = 4 guna kam values; aage ki layers sasti, aur flatten ke baad FC ke parameters bhi kam.
  2. Bada receptive field: pooling ke baad ka 3×3 conv original image ka zyada bada hissa dekhta hai (23.15).
  3. Thodi translation invariance: feature window ke andar thoda khiske to max wahi rehta hai (23.14). Lab notes: "the network can still recognize a feature even if it shifts slightly."
  4. Overfitting kam (kam numbers aage jaate hain). Slide 64: "Subsampling pixels will not change the object" — chhoti ki hui chidiya bhi chidiya hai.

Nuksaan (Lab 12 notes): bahut saari jaankari phenk di jaati hai, khaas kar exact location. Image segmentation jaise tasks me, jahan har pixel ki location zaroori hai, ye problem hai. Teacher: "Pooling trades spatial detail for a compact representation. It does not guarantee translation invariance."

Max pooling

Sabse strong response rakhta hai ("keeps a local peak"). Dominant features (edges) ke liye best. Sabse common. Backprop: gradient sirf max wali jagah jaata hai.

Average pooling

Mean strength rakhta hai; smooth karta hai; akela peak dhima ho jaata hai. Backprop: gradient sab 4 me barabar (1/4) batta hai. GAP ka base.

PyTorch

nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False). stride ka default None matlab stride = kernel_size — isliye nn.MaxPool2d(2) = 2×2 window, stride 2. (Conv2d me stride default 1 tha — ye trap MCQ me aata hai.) nn.AvgPool2d me bhi kernel_size, stride (default = kernel_size), padding, ceil_mode same hain (bas dilation/return_indices nahi). nn.AdaptiveAvgPool2d(output_size) me tum window nahi, output size batate ho; (1, 1) = global average pooling. ceil_mode=True ho to floor ki jagah ceil.

PyTorch · pooling layers

import torch
import torch.nn as nn

A = torch.tensor([[1,3,2,4],
                  [5,6,1,8],
                  [9,2,0,1],
                  [3,4,5,2]], dtype=torch.float32).reshape(1, 1, 4, 4)

mp = nn.MaxPool2d(kernel_size=2)            # stride diya hi nahi
print(mp)                                   # dekho: stride=2 apne aap
print(mp(A))
print(nn.AvgPool2d(kernel_size=2, stride=2)(A))
print(nn.AdaptiveAvgPool2d((1, 1))(A))      # global average pooling
print(nn.AdaptiveMaxPool2d((1, 1))(A))      # global max pooling
print(sum(p.numel() for p in mp.parameters()))   # 0 parameters
print(nn.MaxPool2d(kernel_size=2, stride=1)(A))  # overlapping windows -> 3x3

fm = torch.randn(8, 64, 7, 7)                    # batch of 8, 64 channels
print(nn.MaxPool2d(2)(fm).shape)                 # floor(5/2)+1 = 3
print(nn.MaxPool2d(2, ceil_mode=True)(fm).shape) # ceil -> 4
print(nn.AdaptiveAvgPool2d((1, 1))(fm).shape)    # (8, 64, 1, 1)
print(nn.Flatten()(nn.AdaptiveAvgPool2d((1, 1))(fm)).shape)   # (8, 64) -> seedha Linear(64, ...)
print(fm.mean(dim=(2, 3)).shape)                 # GAP ka manual tareeka

Output

MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
tensor([[[[6., 8.],
          [9., 5.]]]])
tensor([[[[3.7500, 3.7500],
          [4.5000, 2.0000]]]])
tensor([[[[3.5000]]]])
tensor([[[[9.]]]])
0
tensor([[[[6., 6., 8.],
          [9., 6., 8.],
          [9., 5., 5.]]]])
torch.Size([8, 64, 3, 3])
torch.Size([8, 64, 4, 4])
torch.Size([8, 64, 1, 1])
torch.Size([8, 64])
torch.Size([8, 64])

PyTorch · pooling ke through gradient kahan jaata hai

A = A.clone().requires_grad_()
nn.MaxPool2d(2)(A).sum().backward()
print(A.grad[0, 0])          # sirf har window ke max (6, 8, 9, 5) ki jagah 1

B = A.detach().clone().requires_grad_()
nn.AvgPool2d(2)(B).sum().backward()
print(B.grad[0, 0])          # sab ko 1/4

Output

tensor([[0., 0., 0., 0.],
        [0., 1., 0., 1.],
        [1., 0., 0., 0.],
        [0., 0., 1., 0.]])
tensor([[0.2500, 0.2500, 0.2500, 0.2500],
        [0.2500, 0.2500, 0.2500, 0.2500],
        [0.2500, 0.2500, 0.2500, 0.2500],
        [0.2500, 0.2500, 0.2500, 0.2500]])

Exam Radar

  • Pooling: 0 learnable parameters; channels unchanged; 2×2/s2 halves H and W.
  • nn.MaxPool2d(kernel_size=2) → stride default = kernel_size = 2. Conv2d stride default = 1.
  • Same output formula; 28×28×6 → 2×2/s2 → 14×14×6; 15×15, window 3, s2, p1 → 8×8 (channels same).
  • Max pool keeps local peak; avg pool smooths; GAP: (N, C, H, W) → (N, C, 1, 1); nn.AdaptiveAvgPool2d((1,1)).
  • Max-pool backprop: gradient only to argmax position (others 0). Avg: equal share 1/k².
  • Pooling = "parameter-free downsample", "not the same as stride-2 convolution" (conv has learned weights).

Galti mat karna

  • nn.MaxPool2d(2, 1) likh diya to stride 1 → size sirf 1 kam hoga, aadha nahi.
  • "Pooling channels kam karta hai" — galat. Channels 1×1 conv kam karta hai; pooling sirf H, W.
  • AdaptiveAvgPool2d(2) ka matlab window 2 nahi, output 2×2.

Dimaag me aise baitha lo

Pooling = class ka summary: har bench se topper (max) ya average. Rule fixed, seekhna kuch nahi, aur bench ke andar seat badlo to farak nahi.

23.14 Translation equivariance vs invariance

Kahani

India–Australia match chal raha hai. TV ka camera-man ball ke peeche-peeche camera ghumata hai: ball right gayi to screen par ball ki tasveer bhi right gayi. Input khiska, output bhi utna hi, usi taraf khiska — ye hai equivariance. Doosri taraf scoreboard: ball long-on se gayi ya long-off se, likha aayega "SIX". Input khiska, output bilkul nahi badla — ye hai invariance.

Definition

Translation equivariance: input ko khiskao to output bhi waise hi khisakta hai. Shift-then-convolve = convolve-then-shift.
Translation invariance: input ko (thoda) khiskao to output same rehta hai.

Equivariance: f(TΔ x) = TΔ f(x)      Invariance: f(TΔ x) = f(x)
  • x = input image; f = layer/network ka operation
  • TΔ = "Δ pixel khiska do" wala operation (translation/shift)
  • Equivariance me shift bahar bhi aata hai (output khiska); invariance me shift gayab (output wahi)

Kaun kya deta hai?

OperationTranslation ke saath behaviourKyun
Convolution (stride 1)Equivariant (border se door)Same weights har jagah (sharing): detector ko pata hi nahi wo khiska hai
Max/avg pooling (local)Thodi (local) invarianceWindow ke andar khiskne par max same; window boundary cross kiya to badal sakta hai
Global pooling (GAP/global max)Invariance (kahan se farak nahi, bas "kya")Poore map ka ek number; location hat gayi
Dense layer on flattened pixelsNeitherHar position ke apne weights
Original 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 conv 0 2 0 0 0 0 3 0 0 0 0 2 0 0 0 global max 3 2 pixel right khiskaya 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 conv 0 0 0 2 0 0 0 0 3 0 0 0 0 2 0 global max 3 map bhi 2 khiska = EQUIVARIANCE 3 = 3 = INVARIANCE Dense neuron (weights sirf column 2 par): original → 3 shifted → 0 (gayab!) Filter: 3×3, beech wala column 1, baaki 0
Teacher ka L10 demo: line 2 column khiski to conv ka response map bhi 2 column khiska (equivariance). Global max pooling ke baad jawab 3 hi raha (invariance). Fixed-weight dense neuron 3 se 0 ho gaya.

Chalo numbers se samjhein — teacher ka L10 demo + ek 1D pooling demo

Input 5×7, beech me ek khadi line (rows 1–3, column 2 par 1, baaki 0). Filter 3×3: beech wala column = 1, baaki 0, bias 0. Output 3×5.

  1. Window jiska beech wala column = column 2 (yani window cols 1–3): rows 0–2 → 0 + 1 + 1 = 2; rows 1–3 → 1 + 1 + 1 = 3; rows 2–4 → 1 + 1 + 0 = 2. Baaki sab 0.
  2. Map = [0 2 0 0 0; 0 3 0 0 0; 0 2 0 0 0]. Peak 3.
  3. Line ko column 4 par khiskao (2 pixel right): map = [0 0 0 2 0; 0 0 0 3 0; 0 0 0 2 0]. Peak bhi 2 column right → equivariance.
  4. Dense neuron jiske weights column 2 ke teen pixels par 1 the: original → 3, shifted → 0. "Dense weights belong to fixed pixel positions."
  5. Global max dono maps ka = 3 → invariance.

Ab 1D max pool (window 2, stride 2) ki "local" invariance:

  1. [3, 0, 0, 0, 0, 0] → pool → [3, 0, 0]
  2. 1 khiskao: [0, 3, 0, 0, 0, 0] → [3, 0, 0] — same! (3 apni hi window me raha)
  3. 2 khiskao: [0, 0, 3, 0, 0, 0] → [0, 3, 0] — badal gaya (window boundary cross)
  4. Global max teeno me 3.

Jawab: conv = equivariant; local pooling = chhote shift tak invariant; global pooling = invariant; dense = neither.

PyTorch · shift-then-conv == conv-then-shift

import torch
import torch.nn.functional as F

img = torch.zeros(1, 1, 5, 7)
img[0, 0, 1:4, 2] = 1                      # column 2 par khadi line
k = torch.zeros(1, 1, 3, 3)
k[0, 0, :, 1] = 1                          # beech wala column = 1

m1 = F.conv2d(img, k)
shifted = torch.roll(img, shifts=2, dims=3)   # width (dim 3) me 2 pixel right
m2 = F.conv2d(shifted, k)
print(m1[0, 0])
print(m2[0, 0])
print(torch.equal(torch.roll(m1, shifts=2, dims=3), m2))   # equivariance check
print(m1.amax().item(), m2.amax().item())                 # global max: invariant

Output

tensor([[0., 2., 0., 0., 0.],
        [0., 3., 0., 0., 0.],
        [0., 2., 0., 0., 0.]])
tensor([[0., 0., 0., 2., 0.],
        [0., 0., 0., 3., 0.],
        [0., 0., 0., 2., 0.]])
True
3.0 3.0

Kaunse task ko kya chahiye? (slide 12, Prince Fig 10.1)

Image classification → invariance

"Mountain" photo thodi khiski to bhi label "mountain" hi. Network ka final prediction invariant chahiye.

Semantic segmentation → equivariance

Har pixel ko label dena hai. Object khiska to output mask bhi waise hi khisakna chahiye.

Worksheet ka important line: "Classification wants invariance at the end, not at the first layer — you still need to know that the ear is next to the eye." Isliye CNN pehle equivariant conv layers se features nikalta hai, aur end me pooling/FC se invariance banata hai. Hubel–Wiesel ke complex cells bhi yahi kar rahe the.

Exam Radar

  • Convolution (stride 1, away from borders) = translation equivariant. Pooling = step towards (local) invariance. FC on pixels = neither.
  • Equivariance comes from weight sharing.
  • CNN rotation/scale ke liye na equivariant hai na invariant → data augmentation.
  • Stride > 1, pooling aur borders exact equivariance tod sakte hain.

Galti mat karna

  • "Convolution translation invariant hai" — MCQ me ye trap hai. Conv equivariant hai; invariance pooling/global pooling se (aur wo bhi approximately).
  • "Pooling guarantees invariance" — nahi. 2 pixel ka shift 2×2 window cross kar jaata hai.

Dimaag me aise baitha lo

Camera ball ke saath ghoomta hai (equi-variant: "saath me vary"). Scoreboard "SIX" hi bolta hai (in-variant: "vary nahi").

23.15 Receptive field — ek neuron asal me kitna dekhta hai?

Kahani

Fauj (army) me ek sipahi sirf apni gali ke 3 ghar dekhta hai. Uske upar ka havaldar 3 padosi sipahiyon ki report sunta hai — to havaldar ko 5 gharon ki khabar hai (gaaliyan overlap karti hain). Captain 3 havaldaron ki sunta hai → 7 ghar. Aur upar jaate-jaate general ko poore shehar ki khabar ho jaati hai — jabki kisi ki bhi "aankh" 3 ghar se zyada nahi dekhti! CNN ki deeper layers bhi aise hi badi tasveer dekhti hain.

Definition

Receptive field (RF): input image ka wo region jo kisi ek feature (neuron) ki value ko affect karta hai. Slide 70: "Receptive field is defined as the size of the region in the input that produces the feature. In ANN, every hidden unit has access to whole input. In CNNs, the RF of units in successive layers increases."

RFl = RFl−1 + (Kl − 1) · Jl−1,     Jl = Jl−1 · Sl,    shuru: RF0 = 1, J0 = 1
  • RFl = layer l ke ek unit ka receptive field (input pixels me, ek side)
  • Kl = layer l ka kernel size (pooling ho to window size)
  • Jl−1 = "jump": pichhli layer ke do padosi units input par kitne pixel door hain = pichhli saari layers ke strides ka product
  • Sl = layer l ka stride (ye apni layer ka RF nahi badhata, agli layers ka jump badhata hai)
  • Kyun? Naya kernel pichhli layer ke (K − 1) extra padosi jodta hai; aur har padosi input par J pixel door hai → (K − 1)·J naye pixels.
Layer 3: RF = 7 Layer 2: RF = 5 Layer 1: RF = 3 Input pixels Har layer: K = 3, S = 1. Laal neuron neeche ke 7 pixels par depend karta hai.
Receptive field ki seedhi: har 3×3 layer pichhli layer ke 3 neighbours ko jodti hai, to input par dekhne ka area 3 → 5 → 7 badhta jaata hai (har layer +2).

Chalo numbers se samjhein

Case A: teen 3×3 conv, sab stride 1.

  1. Layer 1: RF = 1 + (3 − 1)×1 = 3, J = 1×1 = 1
  2. Layer 2: RF = 3 + 2×1 = 5, J = 1
  3. Layer 3: RF = 5 + 2×1 = 7, J = 1  → general rule: n layers of 3×3 (stride 1) → RF = 1 + 2n

Case B (worksheet): teen 3×3, sab stride 2.

  1. L1: RF = 1 + 2×1 = 3, J = 2
  2. L2: RF = 3 + 2×2 = 7, J = 4 (do stride-1 layers sirf 5 tak pahunchti)
  3. L3: RF = 7 + 2×4 = 15, J = 8

Case C (pooling ke saath): conv 3×3 (s1) → max pool 2×2 (s2) → conv 3×3 (s1).

  1. conv1: RF = 1 + 2×1 = 3, J = 1
  2. pool: RF = 3 + (2 − 1)×1 = 4, J = 1×2 = 2 (worksheet: "Pooling with K = 2, S = 2 grows RF by 1 × J and then doubles J")
  3. conv2: RF = 4 + 2×2 = 8, J = 2 — pool ke baad wahi 3×3 ab +4 jodta hai, +2 nahi!

Case D (Lab 12 ka network): conv3 → pool2 → conv3 → pool2: RF = 3 → 4 → 8 → 10 (J = 1, 2, 2, 4). Matlab final 7×7×64 map ka har number 28×28 photo ke ek 10×10 hisse ko dekhta hai. (Padding theoretical RF ka size nahi badalti.)

Case E (worksheet, AlexNet): conv1 K = 11, S = 4 → RF = 11, J = 4. Uske baad ek 3×3 stride 1: 11 + 2×4 = 19.

Case F (slide 71, Prince Fig 10.6): 11 inputs wala 1D network, kernel 3: H1 → 3, H2 → 5, H3 (kernel 3, stride 2) → 7, aur H4 → 7 + 2×2 = 11 = poora input cover.

Jawab ka saar: stride-1 3×3 layers RF ko +2 se badhati hain; stride/pooling ke baad har layer zyada tezi se badhti hai.

Python · receptive field calculator

def receptive_field(layers):
    rf, jump = 1, 1                      # input pixel khud ko dekhta hai: RF=1, J=1
    for name, k, s in layers:
        rf = rf + (k - 1) * jump         # naya kernel (k-1) padosi jodta hai, har ek 'jump' pixel door
        jump = jump * s                  # stride agli layers ke liye doori badhata hai
        print(f"{name:8s} K={k:2d} S={s}  ->  RF={rf:2d}, jump={jump}")
    return rf

receptive_field([("conv1", 3, 1), ("conv2", 3, 1), ("conv3", 3, 1)])
print("---")
receptive_field([("conv1", 3, 1), ("pool1", 2, 2), ("conv2", 3, 1), ("pool2", 2, 2)])
print("---")
receptive_field([("conv1", 11, 4), ("conv2", 3, 1)])

Output

conv1    K= 3 S=1  ->  RF= 3, jump=1
conv2    K= 3 S=1  ->  RF= 5, jump=1
conv3    K= 3 S=1  ->  RF= 7, jump=1
---
conv1    K= 3 S=1  ->  RF= 3, jump=1
pool1    K= 2 S=2  ->  RF= 4, jump=2
conv2    K= 3 S=1  ->  RF= 8, jump=2
pool2    K= 2 S=2  ->  RF=10, jump=4
---
conv1    K=11 S=4  ->  RF=11, jump=4
conv2    K= 3 S=1  ->  RF=19, jump=4

Teen 3×3 vs ek 7×7 (VGG ka idea — preview)

Dono ka RF = 7. Lekin weights (ek channel, bina bias): teen 3×3 = 3 × 9 = 27, ek 7×7 = 49. Do 3×3 vs ek 5×5: 18 vs 25. C channels har jagah ho to dono C² se guna: 27C² vs 49C². Upar se stack me 3 ReLU (zyada non-linearity) vs 1. Isliye modern CNNs chhote 3×3 kernels stack karte hain. (VGG ki poori kahani Chapter 24 me.)

Exam Radar

  • Stacked 3×3 (stride 1): RF = 3, 5, 7 (1 + 2n). Two 3×3 → 5×5 RF (teacher L10).
  • RFl = RFl−1 + (Kl − 1)·(product of strides of previous layers).
  • 3×3 s2 stack: 3, 7, 15. AlexNet 11/s4 then 3×3: 11 → 19. conv3-pool2-conv3: 3 → 4 → 8.
  • ANN unit sees whole input; CNN unit sees only its RF, which grows with depth.
  • 3 × (3×3) = 27 weights vs 7×7 = 49, same RF 7.

Galti mat karna

  • (K − 1) ko jump se guna karna bhool jaana (pool ke baad sab galat ho jaata hai).
  • Kisi layer ke apne stride ko uske apne RF me lagana — stride sirf agli layers ka jump badhata hai.

Dimaag me aise baitha lo

Sipahi 3, havaldar 5, captain 7 — har level "+2", aur jahan stride/pool aaya wahan se "+2 × jump".

23.16 Hierarchical features — edges se objects tak

Kahani

Golu ne padhna kaise seekha? Pehle akshar (a, b, c), phir akshar jod kar shabd, shabd jod kar vaakya, aur vaakya jod kar poori kahani. Koi seedha kahani nahi padhta. CNN bhi photo ko aise hi padhta hai: pixels → edges → textures → parts → object.

Hubel–Wiesel ka "layered visual cortex" (23.1) yahan code me ban jaata hai. Lab 12 notes ka cat vs dog example:

1. Edges 2. Textures/corners 3. Parts 4. Object early layers deep layers Receptive field ↑ · spatial size ↓ · channels ↑ · features zyada abstract
CNN ki seedhi: pehli layers chhoti lines/edges pakadti hain, phir unse corners/textures, phir parts (aankh, kaan, naak), aur aakhri layers poora object (billi ka chehra).

Ye seedhi bani kaise? Do cheezon se: (1) receptive field badhta hai — deeper unit bada area dekhta hai, to bade patterns pakad sakta hai; (2) beech me non-linearity (ReLU) — bina iske saari layers mil kar ek hi linear filter ban jaati, aur "edges ka combination" jaisi complex cheez ban hi nahi paati. Zeiler & Fergus (2014) ne trained CNN ki layers visualize karke yahi dikhaya tha: layer 1 me edges/colours, beech me textures, aage object parts.

Saath me ek pattern bhi dekho: jaise-jaise gehre jaate hain, spatial size ghatta hai, channels badhte hain — Lab 12: 28×28×1 → 14×14×32 → 7×7×64. "Kam jagah, lekin zyada tarah ke features."

Exam Radar

  • Early layers = low-level, generic features (edges/colours); deep layers = high-level, task-specific (parts/objects).
  • Isi wajah se pretrained CNN ki early layers doosre tasks me reuse hoti hain (transfer learning).
  • Hierarchy possible because RF grows with depth + non-linear activations between layers.
  • Typical trend: H, W ↓ aur channels ↑ (jaise 64 → 128 → 256...).

Dimaag me aise baitha lo

Akshar → shabd → vaakya → kahani = edges → textures → parts → object.

23.17 Typical CNN pipeline aur poori shape-tracking

Kahani

Mithai ki factory socho. Kachcha maal aata hai → machine 1 tukde kaatti hai (conv) → kharab tukde hatate hain (ReLU) → line pe ek jaisa size karte hain (BatchNorm) → dabba chhota karke pack karte hain (pool) → ye steps 2–3 baar → aakhir me saare dabbe khol kar ek lambi line me (flatten) → quality manager dekh kar label lagata hai "kaju katli" ya "barfi" (fully connected + output). Worksheet bhi isi "assembly line" ki baat karta hai.

Input → [ Conv → (BN) → ReLU → Pool ] × n → Flatten → FC (→ ReLU → Dropout) … → FC → logits
  • Conv: local patterns nikalo (learnable). BN (optional): har channel normalize, training stable. ReLU: non-linearity. Pool: H, W chhote.
  • × n: block repeat — slide 62: "Convolution → Max Pooling → Convolution → Max Pooling → … can repeat many times → Flattened → Fully connected feedforward network → cat, dog …"
  • Flatten: (C, H, W) cube ko ek vector banao (0 params). FC: features jod kar class scores (logits). Softmax nn.CrossEntropyLoss ke andar hota hai.

Lab 12 notes ka version: "Input Image → Conv Layer (Feature Extraction) → ReLU (Non-linearity) → Pooling Layer → Flatten → Fully Connected Layer → Output (Softmax/Sigmoid)". Code me ye do hisson me banta hai: self.features (conv part) aur self.classifier (dense part).

BN kahan lagaye? BatchNorm paper aur zyada tar architectures: Conv → BN → ReLU. Lab 12/13 ka code: Conv → ReLU → BN → Pool. Dono chalte hain; exam me jo code diya ho, us order ko pehchaan lena. Ek baareek fact: agar conv ke turant baad BN hai to conv ka bias bekaar hai (BN mean ghata deta hai), isliye log bias=False likhte hain.

28×28×1 28×28×32 14×14×32 14×14×64 7×7×64 3136 Flatten 128 64 10 logits → CrossEntropy self.features: [Conv→ReLU→BN→Pool]×2 self.classifier: Flatten→FC×3 Spatial 28 → 14 → 7 ghatta hai, channels 1 → 32 → 64 badhte hain
Lab 12 ka CNN: do conv blocks (feature extractor) jo photo ko 7×7×64 ke features me badalte hain, phir flatten karke dense layers (classifier) 10 class scores dete hain.

Lab 12 CNN — har layer ka shape aur parameters

LayerOutput shape (N=1)Shape kaise aayaParamsParams calculation
Input(1, 1, 28, 28)grayscale 28×280—
Conv2d(1, 32, 3, padding='same')(1, 32, 28, 28)(28 − 3 + 2×1)/1 + 1 = 28; 32 filters320(3×3×1 + 1) × 32
ReLU(1, 32, 28, 28)shape same0—
BatchNorm2d(32)(1, 32, 28, 28)shape same64γ, β per channel: 2 × 32
MaxPool2d(2, 2)(1, 32, 14, 14)(28 − 2)/2 + 1 = 140—
Conv2d(32, 64, 3, padding='same')(1, 64, 14, 14)same padding; 64 filters18,496(3×3×32 + 1) × 64 = 289 × 64
ReLU(1, 64, 14, 14)—0—
BatchNorm2d(64)(1, 64, 14, 14)—1282 × 64
MaxPool2d(2, 2)(1, 64, 7, 7)(14 − 2)/2 + 1 = 70—
Flatten(1, 3136)64 × 7 × 7 = 31360—
Linear(3136, 128)(1, 128)—401,5363136 × 128 + 128
ReLU, Dropout(0.4)(1, 128)—0—
Linear(128, 64)(1, 64)—8,256128 × 64 + 64
ReLU, Dropout(0.4)(1, 64)—0—
Linear(64, 10)(1, 10)10 logits65064 × 10 + 10
Total429,450conv part sirf 19,008; fc1 akela 93.5%

Ye table maine haath se nahi maana — neeche code se har row verify hui hai. Same trick tum kisi bhi model par chala sakte ho: dummy input do aur layer-by-layer shape print karo.

PyTorch · Lab 12 model ka shape + params walk

import torch
import torch.nn as nn

class MyNN(nn.Module):
    def __init__(self, input_features):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(input_features, 32, kernel_size=3, padding='same'),
            nn.ReLU(),
            nn.BatchNorm2d(32),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(32, 64, kernel_size=3, padding='same'),
            nn.ReLU(),
            nn.BatchNorm2d(64),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(64 * 7 * 7, 128),
            nn.ReLU(),
            nn.Dropout(p=0.4),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Dropout(p=0.4),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        return x

model = MyNN(1)
x = torch.zeros(1, 1, 28, 28)                     # dummy photo
print("input".ljust(12), tuple(x.shape))
for layer in list(model.features) + list(model.classifier):
    x = layer(x)                                   # ek-ek layer chalao
    n = sum(p.numel() for p in layer.parameters())
    print(layer.__class__.__name__.ljust(12), tuple(x.shape), n)
print("Total params:", sum(p.numel() for p in model.parameters()))

Output

input        (1, 1, 28, 28)
Conv2d       (1, 32, 28, 28) 320
ReLU         (1, 32, 28, 28) 0
BatchNorm2d  (1, 32, 28, 28) 64
MaxPool2d    (1, 32, 14, 14) 0
Conv2d       (1, 64, 14, 14) 18496
ReLU         (1, 64, 14, 14) 0
BatchNorm2d  (1, 64, 14, 14) 128
MaxPool2d    (1, 64, 7, 7) 0
Flatten      (1, 3136) 0
Linear       (1, 128) 401536
ReLU         (1, 128) 0
Dropout      (1, 128) 0
Linear       (1, 64) 8256
ReLU         (1, 64) 0
Dropout      (1, 64) 0
Linear       (1, 10) 650
Total params: 429450

Exam Radar

  • Pipeline: [Conv → (BN) → ReLU → Pool] × n → Flatten → FC → logits. Lab order: Conv → ReLU → BN → Pool.
  • Lab 12: 28 → 28 (same) → 14 → 14 → 7; flatten = 64 × 7 × 7 = 3136; total params 429,450.
  • Lab 13 (CIFAR-10, 3×32×32): 32 → 16 → 8; flatten = 64 × 8 × 8 = 4096; conv1 = (27 + 1) × 32 = 896; total 552,906.
  • Conv layers: few params, lots of compute. FC layers: most params.
  • Last layer outputs raw logits (no softmax) when using nn.CrossEntropyLoss.

Galti mat karna

  • Flatten se pehle wale shape me batch dimension mat gino: in_features = C × H × W (64 × 7 × 7), N nahi.
  • Pooling ke baad H, W aadha karna mat bhoolo jab in_features nikal rahe ho.

Dimaag me aise baitha lo

"Kaato, saaf karo, barabar karo, pack karo — do baar; phir kholo aur label lagao." Conv, ReLU, BN, Pool ×2 → Flatten → FC.

23.18 PyTorch toolkit: Conv2d, pooling, Flatten, BatchNorm2d, Dropout

Kahani

Ab tak humne kaam samjha; ab auzaar (tools) pehchaante hain. Jaise mistri ke bag me hathoda, pechkas, ari — har ek ka naam, kaam aur "kahan galti hoti hai" pata hona chahiye. Exam ka 30% coding hai, aur MCQs me defaults poochhe jaate hain. Chalo har auzaar ko ek-ek karke uthaate hain.

1. nn.Conv2d

Signature (yaad rakhne wale defaults)

nn.Conv2d(in_channels, out_channels, kernel_size,
          stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')

2. Pooling layers

3. nn.Flatten

nn.Flatten(start_dim=1, end_dim=-1): dim 1 se aakhir tak sab ek kar do, batch dim (0) ko chhod kar. (N, 64, 7, 7) → (N, 3136). Dhyan: function torch.flatten(x) ka default start_dim=0 hai — wo batch ko bhi mila dega! Model ke andar torch.flatten(x, 1) ya x.view(x.size(0), -1) likho. Flatten "rearranges feature values into a vector; it learns nothing" (teacher).

4. nn.BatchNorm2d

nn.BatchNorm2d(num_features) — num_features = channels (C), H×W nahi. Har channel ko batch ke saare N × H × W values par normalize karta hai, phir learnable γ (weight) aur β (bias) se scale-shift: 2C parameters. Running mean/var "buffers" hain (eval me use). Lab printout: BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True). Train aur eval me alag behave karta hai → model.train() / model.eval() zaroori (Chapter 20).

5. nn.Dropout

nn.Dropout(p=0.5) default p = 0.5; training me har element ko probability p se 0 karta hai aur bache hue ko 1/(1 − p) se scale; eval me kuch nahi karta. Lab 12 ne classifier me Dropout(p=0.4) lagaya. CNN feature maps ke liye nn.Dropout2d bhi hota hai jo poore channel drop karta hai.

LayerLearnable paramsChannels badle?H, W badle?Default / trap
nn.Conv2d(K·K·Cin + 1)·CoutHaan (→ out_channels)Formula sestride=1, padding=0
nn.ReLU0NahiNahi—
nn.BatchNorm2d(C)2CNahiNahiarg = channels
nn.MaxPool2d / AvgPool2d0NahiHaan (formula)stride default = kernel_size
nn.AdaptiveAvgPool2d((1,1))0Nahi→ 1×1output size deta hai
nn.Flatten0(C,H,W) → C·H·W—start_dim=1
nn.Dropout0NahiNahip=0.5; eval me off
nn.Linear(in + 1)·out——in_features exact chahiye

Common errors — asli error messages pehchaano

PyTorch · teen classic galtiyan

import torch
import torch.nn as nn

# Galti 1: in_channels match nahi (RGB photo, lekin Conv2d(1, ...))
try:
    nn.Conv2d(1, 32, 3)(torch.zeros(4, 3, 28, 28))
except RuntimeError as e:
    print("RuntimeError:", e)

# Galti 2: grayscale batch me channel dim bhool gaye: (64, 28, 28)
try:
    nn.Conv2d(1, 32, 3)(torch.zeros(64, 28, 28))   # 3D ko "unbatched (C,H,W)" samjha -> C=64
except RuntimeError as e:
    print("RuntimeError:", e)

# Galti 3: torch.flatten ne batch bhi mila diya
x = torch.zeros(8, 64, 7, 7)
print(torch.flatten(x).shape, nn.Flatten()(x).shape)

Output

RuntimeError: Given groups=1, weight of size [32, 1, 3, 3], expected input[4, 3, 28, 28] to have 1 channels, but got 3 channels instead
RuntimeError: Given groups=1, weight of size [32, 1, 3, 3], expected input[1, 64, 28, 28] to have 1 channels, but got 64 channels instead
torch.Size([25088]) torch.Size([8, 3136])

Exam Radar

  • Conv2d defaults: stride=1, padding=0, dilation=1, bias=True. MaxPool2d: stride=None → kernel_size.
  • BatchNorm2d argument = number of channels; params = 2C.
  • nn.Flatten start_dim=1 (batch safe); torch.flatten start_dim=0.
  • Dropout default p = 0.5; train me active, eval me identity.
  • Functional versions: F.conv2d, F.max_pool2d(x, 2), F.avg_pool2d, F.adaptive_avg_pool2d(x, 1), F.relu — weights khud dene padte hain (conv me), class wale khud banate hain.

Galti mat karna

  • nn.BatchNorm2d(28) likhna kyunki image 28×28 hai — galat; channels do (32).
  • nn.BatchNorm1d ko conv ke baad lagana — conv ke baad 2d, Linear ke baad 1d.

23.19 Pehle Linear ka in_features — dummy tensor ki trick

Kahani

Darzi ko kurta silna hai lekin naap (measurement) nahi pata. Wo kya karta hai? Dukaan ka putla (mannequin) laata hai, kapda us par chadha ke naap le leta hai. Hum bhi yahi karenge: ek nakli (dummy) photo network ke conv part se guzaarenge aur dekh lenge ki flatten ke baad kitne numbers bache.

Problem: nn.Linear(in_features, out_features) ko flatten ke baad wala exact size chahiye. Lab 12 notes: "Getting this number wrong is the #1 source of CNN bugs — always trace the shapes!"

Tareeka 1: haath se (formula)

  1. Lab 12 (same padding): 28 →conv→ 28 →pool→ 14 →conv→ 14 →pool→ 7; channels 64 → 64 × 7 × 7 = 3136
  2. Valid padding wala SmallCNN: Conv2d(1, 8, 5): 28 − 5 + 1 = 24 → pool: 24/2 = 12 → Conv2d(8, 16, 3): 12 − 3 + 1 = 10 → pool: 5 → 16 × 5 × 5 = 400

Jawab: Lab 12 → Linear(3136, …); SmallCNN → Linear(400, …).

Galat number daala to forward pass par ye error aata hai (yahan Conv2d(1, 8, 5) + pool ke baad asal me 8 × 12 × 12 = 1152 tha, lekin Linear ko 3136 bata diya):

Output

RuntimeError: mat1 and mat2 shapes cannot be multiplied (1x1152 and 3136x10)

Error padhna seekho: "1x1152" = tumhara data (batch 1, 1152 features); "3136x10" = Linear ka weight (usne 3136 expect kiye). Pehla number jo batch ke baad hai (1152) hi sahi in_features hai.

PyTorch · Tareeka 2: dummy tensor se in_features (__init__ ke andar)

import torch
import torch.nn as nn

class SmallCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 8, kernel_size=5),      # (1,28,28) -> (8,24,24)
            nn.ReLU(),
            nn.MaxPool2d(2),                     # -> (8,12,12)
            nn.Conv2d(8, 16, kernel_size=3),     # -> (16,10,10)
            nn.ReLU(),
            nn.MaxPool2d(2),                     # -> (16,5,5)
        )
        with torch.no_grad():                    # sirf naap lena hai, gradient nahi
            dummy = torch.zeros(1, 1, 28, 28)    # nakli photo: (N=1, C=1, 28, 28)
            n_flat = self.features(dummy).numel()   # 1*16*5*5 = 400
        print("n_flat =", n_flat)
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(n_flat, num_classes),      # ab galti ho hi nahi sakti
        )

    def forward(self, x):
        return self.classifier(self.features(x))

model = SmallCNN()
out = model(torch.randn(4, 1, 28, 28))           # 4 photos ka batch
print(out.shape)                                 # (4, 10) logits
print(sum(p.numel() for p in model.parameters()))
x = torch.zeros(1, 1, 28, 28)
for layer in model.features:                     # shape walk
    x = layer(x)
    print(f"{layer.__class__.__name__:10s} {tuple(x.shape)}")

Output

n_flat = 400
torch.Size([4, 10])
5386
Conv2d     (1, 8, 24, 24)
ReLU       (1, 8, 24, 24)
MaxPool2d  (1, 8, 12, 12)
Conv2d     (1, 16, 10, 10)
ReLU       (1, 16, 10, 10)
MaxPool2d  (1, 16, 5, 5)

Parameters check: conv1 (25×1 + 1)×8 = 208, conv2 (9×8 + 1)×16 = 1,168, fc 400×10 + 10 = 4,010 → total 5,386 ✓. .numel() = tensor me kul kitne numbers (batch 1 hai isliye seedha C×H×W). Batch > 1 ho to out.flatten(1).shape[1] use karo.

Ek aur shortcut: nn.LazyLinear(10) — in_features pehle forward pass par khud infer ho jaata hai (first forward ke baad print karo to Linear(in_features=1152, out_features=10, bias=True) jaisa dikhta hai). Lekin exam/labs me explicit number (ya dummy trick) hi expected hai.

Exam Radar

  • in_features = C × H × W of the last feature map (batch nahi). Lab 12: 3136; Lab 13: 4096.
  • Dummy: torch.zeros(1, 1, 28, 28) (grayscale) ya torch.zeros(1, 3, 32, 32) (CIFAR) — torch.no_grad() ke andar.
  • Error "mat1 and mat2 shapes cannot be multiplied (B×A and X×Y)" → A hi sahi in_features hai.

Dimaag me aise baitha lo

Naap nahi pata? Putla chadha do. torch.zeros(1, C, H, W) → features → .numel().

23.20 Data pipeline: transforms, datasets, DataLoader

Kahani

Maa khana banane se pehle sabziyan dhoti hai, ek jaise size me kaatti hai (preprocessing), aur phir thaali me ek fixed ginti me parosti hai (batches). Model ko bhi photos aise hi chahiye: tensor me badli hui, normalize ki hui, aur batches me.

transforms.ToTensor() — do kaam ek saath

  1. Range: uint8 pixels 0–255 → float 0.0–1.0 (255 se divide).
  2. Layout: PIL image / numpy array (H × W × C) → tensor (C × H × W). Grayscale 28×28 → (1, 28, 28).

transforms.Normalize(mean, std)

xout = xin − meanstd   (har channel ke liye alag)
  • xin = ToTensor ke baad wali value (0–1)
  • mean, std = tuples, har channel ka ek number: grayscale (0.5,), RGB (0.5, 0.5, 0.5)
  • mean = std = 0.5 → [0, 1] → [−1, 1]. Dataset ke asli stats bhi use hote hain, jaise MNIST (0.1307,), (0.3081,); Fashion-MNIST ke liye aksar (0.2860,), (0.3530,)

Chalo numbers se samjhein

2×2 grayscale photo [[0, 128], [255, 64]].

  1. ToTensor: 0/255 = 0; 128/255 ≈ 0.5020; 255/255 = 1; 64/255 ≈ 0.2510, shape (1, 2, 2)
  2. Normalize(0.5, 0.5): (0 − 0.5)/0.5 = −1; (0.5020 − 0.5)/0.5 ≈ 0.0039; (1 − 0.5)/0.5 = 1; (0.2510 − 0.5)/0.5 ≈ −0.4980

Jawab: [[−1, 0.0039], [1, −0.4980]].

PyTorch · transforms (real run)

import numpy as np
from PIL import Image
from torchvision import transforms

arr = np.array([[0, 128], [255, 64]], dtype=np.uint8)
img = Image.fromarray(arr)                       # ek chhota grayscale PIL image

t1 = transforms.ToTensor()(img)                  # 0-255 -> 0-1, HWC -> CHW
print(t1.shape, t1.dtype)
print(t1)

transform = transforms.Compose([                 # steps ek ke baad ek, isi order me
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))         # (x - 0.5) / 0.5 -> [-1, 1]; comma zaroori!
])
print(transform(img))

Output

torch.Size([1, 2, 2]) torch.float32
tensor([[[0.0000, 0.5020],
         [1.0000, 0.2510]]])
tensor([[[-1.0000,  0.0039],
         [ 1.0000, -0.4980]]])

datasets.FashionMNIST + DataLoader

PyTorch · dataset aur loaders

from torchvision import datasets, transforms
from torch.utils.data import DataLoader

transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize((0.5,), (0.5,))])

train_set = datasets.FashionMNIST(root='./data',      # kis folder me save/load
                                  train=True,          # True: 60,000 train images
                                  download=True,       # nahi hai to internet se lao
                                  transform=transform) # har image par ye steps
test_set = datasets.FashionMNIST(root='./data', train=False,   # False: 10,000 test images
                                 download=True, transform=transform)

train_loader = DataLoader(train_set, batch_size=64, shuffle=True)    # har epoch naya order
test_loader = DataLoader(test_set, batch_size=64, shuffle=False)     # test me order matter nahi

print(len(train_set), len(test_set))
img, label = train_set[0]                 # ek item = (image tensor, int label)
print(img.shape, type(label))            # label ek int (0-9)
images, labels = next(iter(train_loader)) # ek batch
print(images.shape, labels.shape)
print(train_set.classes)

Output (expected — standard Fashion-MNIST sizes; is notebook ke sandbox me download blocked tha)

60000 10000
torch.Size([1, 28, 28]) <class 'int'>
torch.Size([64, 1, 28, 28]) torch.Size([64])
['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

Lab 12 ka apna tareeka (DataFrame / CSV route)

Lab 12 ne dataset ko pehle ek DataFrame (CSV jaisa: har row = 1 label + 784 pixels) me badla, phir khud ka Dataset banaya. Ye bhi exam me aa sakta hai:

PyTorch · Lab 12 style custom Dataset

from sklearn.model_selection import train_test_split
from torch.utils.data import Dataset, DataLoader

X = df.iloc[:, 1:].values      # saare pixel columns (N, 784)
y = df.iloc[:, 0].values       # label column

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
X_train = X_train / 255.0      # 0-255 -> 0-1 (ToTensor jaisa scaling)
X_test = X_test / 255.0

class CustomDataset(Dataset):
    def __init__(self, features, labels):
        # (N, 784) -> (N, 1, 28, 28): CNN ko channel dim chahiye
        self.features = torch.tensor(features, dtype=torch.float32).reshape(-1, 1, 28, 28)
        self.labels = torch.tensor(labels, dtype=torch.long)     # CrossEntropy ko long labels

    def __len__(self):                    # dataset me kitne items
        return len(self.features)

    def __getitem__(self, index):         # ek (image, label) jodi
        return self.features[index], self.labels[index]

train_loader = DataLoader(CustomDataset(X_train, y_train), batch_size=32, shuffle=True)
test_loader = DataLoader(CustomDataset(X_test, y_test), batch_size=32, shuffle=False)

Lab 12 me isse 48,000 train + 12,000 test images bani (60,000 ka 80/20 split). Lab 13 (CIFAR-10) me numpy images (32, 32, 3) thi, to __getitem__ me torch.from_numpy(img).permute(2, 0, 1).float() / 255.0 kiya — wahi do kaam jo ToTensor karta hai.

Exam Radar

  • ToTensor: 0–255 → 0–1 aur HWC → CHW. Normalize: (x − mean)/std per channel. Order: ToTensor pehle, Normalize baad me (Normalize tensor par chalta hai).
  • Normalize((0.5,), (0.5,)) → range [−1, 1]. Grayscale ke liye 1-element tuple, RGB ke liye 3-element.
  • datasets.FashionMNIST(root, train, download, transform): train=True → 60k, False → 10k.
  • DataLoader: train shuffle=True, test shuffle=False; batch shape (B, 1, 28, 28), labels (B,).
  • Custom Dataset ke 3 methods: __init__, __len__, __getitem__.

Galti mat karna

  • Normalize((0.5), (0.5)) — bina comma ke ye tuple nahi, float hai. Habit: (0.5,).
  • Test data par bhi same Normalize lagao (warna train/test ka scale alag).
  • Labels float mat banao; CrossEntropyLoss ko torch.long class indices chahiye.

Dimaag me aise baitha lo

ToTensor = "0–255 ko 0–1, aur channel aage". Normalize = "beech me laao, phailao barabar".

23.21 Poora Lab 12: CNN class + training loop + accuracy

Kahani

Ab saare tukde jod kar asli match khelte hain. Hamara CNN 60,000 kapdon ki photos dekhega aur seekhega ki T-shirt, trouser, sneaker, bag… me farak kaise karein. Ye wahi template hai jo coding exam me sabse zyada kaam aayega — har line samajh lo.

PyTorch · Lab 12 complete (Fashion-MNIST CNN)

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

torch.manual_seed(42)                                   # har baar same result
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# ---------- 1. Data ----------
transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize((0.5,), (0.5,))])
train_set = datasets.FashionMNIST(root='./data', train=True, download=True, transform=transform)
test_set = datasets.FashionMNIST(root='./data', train=False, download=True, transform=transform)
train_loader = DataLoader(train_set, batch_size=32, shuffle=True)
test_loader = DataLoader(test_set, batch_size=32, shuffle=False)

# ---------- 2. Model (Lab 12 ka MyNN) ----------
class MyNN(nn.Module):
    def __init__(self, input_features):
        super().__init__()
        self.features = nn.Sequential(                   # conv part: feature extractor
            nn.Conv2d(input_features, 32, kernel_size=3, padding='same'),  # (1,28,28)->(32,28,28)
            nn.ReLU(),
            nn.BatchNorm2d(32),
            nn.MaxPool2d(kernel_size=2, stride=2),       # -> (32,14,14)
            nn.Conv2d(32, 64, kernel_size=3, padding='same'),              # -> (64,14,14)
            nn.ReLU(),
            nn.BatchNorm2d(64),
            nn.MaxPool2d(kernel_size=2, stride=2)        # -> (64,7,7)
        )
        self.classifier = nn.Sequential(                 # dense part: decision maker
            nn.Flatten(),                                # (64,7,7) -> 3136
            nn.Linear(64 * 7 * 7, 128),
            nn.ReLU(),
            nn.Dropout(p=0.4),                           # 40% neurons training me band
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Dropout(p=0.4),
            nn.Linear(64, 10)                            # 10 logits (softmax nahi!)
        )

    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        return x

model = MyNN(1).to(device)
criterion = nn.CrossEntropyLoss()                        # logits + class indices; softmax andar
optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=1e-4)   # L2 penalty bhi
epochs = 12

# ---------- 3. Accuracy function ----------
def evaluate(loader):
    model.eval()                                         # BN running stats use, Dropout off
    correct, total = 0, 0
    with torch.no_grad():                                # gradient ki zaroorat nahi
        for images, labels in loader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)                      # (B, 10)
            _, predicted = torch.max(outputs, 1)         # har row ka max index = class
            correct += (predicted == labels).sum().item()
            total += labels.size(0)
    return correct / total

# ---------- 4. Training loop ----------
for epoch in range(epochs):
    model.train()                                        # Dropout on, BN batch stats
    total_loss, correct, total = 0.0, 0, 0
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)                          # forward
        loss = criterion(outputs, labels)                # loss
        optimizer.zero_grad()                            # purane gradients saaf
        loss.backward()                                  # backprop
        optimizer.step()                                 # weights update

        total_loss += loss.item()
        _, predicted = torch.max(outputs, 1)
        correct += (predicted == labels).sum().item()
        total += labels.size(0)

    print(f"Epoch {epoch + 1:2d}/{epochs} | Loss: {total_loss / len(train_loader):.4f} | "
          f"Train Acc: {correct / total:.4f} | Test Acc: {evaluate(test_loader):.4f}")

print(f"Final Train Accuracy: {evaluate(train_loader):.4f}")
print(f"Final Test  Accuracy: {evaluate(test_loader):.4f}")

Output (Lab 12 notebook ka asli run — wahan data CSV route se aaya tha: 0–1 scaling, 48k/12k split; isliye tumhare numbers thode alag ho sakte hain)

Epoch  1/12 | Loss: 0.6509 | Train Acc: 0.7746 | Test Acc: 0.8748
Epoch  2/12 | Loss: 0.3863 | Train Acc: 0.8675 | Test Acc: 0.8979
Epoch  3/12 | Loss: 0.3246 | Train Acc: 0.8893 | Test Acc: 0.8958
Epoch  4/12 | Loss: 0.2907 | Train Acc: 0.8990 | Test Acc: 0.9075
Epoch  5/12 | Loss: 0.2657 | Train Acc: 0.9100 | Test Acc: 0.9008
Epoch  6/12 | Loss: 0.2464 | Train Acc: 0.9149 | Test Acc: 0.9100
Epoch  7/12 | Loss: 0.2277 | Train Acc: 0.9211 | Test Acc: 0.9055
Epoch  8/12 | Loss: 0.2137 | Train Acc: 0.9259 | Test Acc: 0.9160
Epoch  9/12 | Loss: 0.1956 | Train Acc: 0.9321 | Test Acc: 0.9067
Epoch 10/12 | Loss: 0.1917 | Train Acc: 0.9332 | Test Acc: 0.9143
Epoch 11/12 | Loss: 0.1781 | Train Acc: 0.9376 | Test Acc: 0.9193
Epoch 12/12 | Loss: 0.1669 | Train Acc: 0.9414 | Test Acc: 0.9204
Final Train Accuracy: 0.9625
Final Test  Accuracy: 0.9204

Output ko padhna seekho

Ek nayi photo par prediction

PyTorch · single image predict (Lab 12 style)

img, true_label = test_set[15]              # ek (1, 28, 28) image
x = img.unsqueeze(0).to(device)             # batch dim jodo -> (1, 1, 28, 28)
# (Lab 12 CSV route: torch.tensor(pixels / 255.0).reshape(1, 1, 28, 28))

model.eval()                                # zaroori: Dropout off, BN running stats
with torch.no_grad():
    logits = model(x)                       # (1, 10)
    probs = torch.softmax(logits, dim=1)    # probabilities chahiye to (sum = 1)
    pred = torch.argmax(logits, dim=1).item()

print("Predicted:", train_set.classes[pred], "| Actual:", train_set.classes[true_label])

Lab 12 ke run me index 15 wali photo par output aaya tha: "Model predicted : Sandal / Actual label : Sandal".

Exam Radar

  • Training step order: forward → loss → zero_grad → backward → step (zero_grad ka kaam pichhle batch ke gradients mitaana hai, kyunki PyTorch gradients jodta rehta hai; isliye har backward() se pehle).
  • torch.max(outputs, 1) returns (values, indices); indices = predicted class. outputs.argmax(dim=1) same (Lab 13 style).
  • model.eval() + torch.no_grad() for evaluation; model.train() wapas training se pehle.
  • CrossEntropyLoss: input raw logits (N, 10), target class indices (N,) long. Last layer par softmax mat lagao.
  • Lab 12 hyperparameters: SGD, lr = 0.01, weight_decay = 1e-4, epochs = 12, batch 32, Dropout 0.4. Result ≈ 92% test.
  • Single image ko model me bhejne se pehle batch dim: (1, 1, 28, 28).

Galti mat karna

  • model.eval() bhoolna → Dropout prediction ke time bhi neurons band karega, BN batch stats use karega → galat/random jaisa result.
  • loss.item() ki jagah loss ko list me jodte jaana → graph memory me atka rehta hai.
  • .to(device) sirf model ko karna, data ko nahi (ya ulta) → device mismatch error.

Dimaag me aise baitha lo

"Data → Model → Loss/Optim → Loop (train) → Evaluate (eval + no_grad)". Paanch dabbe, har exam me wahi.

23.22 CIFAR-10 (Lab 13) aur data augmentation

Kahani

Chhote bhai ko "billi" pehchaanna sikhana hai. Agar use sirf ek hi photo dikhao (billi beech me, seedhi khadi), to wo ulti taraf mudi billi ya kone me baithi billi par confuse hoga. Samajhdaar tareeka: wahi billi ko alag-alag tarah dikhao — mirror me, thoda left, thoda right, thoda zoom. Har baar label wahi "billi". Ye hai data augmentation: naya data collect kiye bina, purani photos ke label-preserving naye roop banana.

Lab 13: Fashion-MNIST se CIFAR-10 par kya badla?

PartFashion-MNIST (Lab 12)CIFAR-10 (Lab 13)
Image28×28, 1 channel (grayscale)32×32, 3 channels (RGB)
Input to CNN(batch, 1, 28, 28)(batch, 3, 32, 32)
First convConv2d(1, 32, 3, padding='same')Conv2d(3, 32, 3, padding='same')
Spatial sizes28 → 14 → 732 → 16 → 8
Flatten64 × 7 × 7 = 313664 × 8 × 8 = 4096
Data prepreshape(-1, 1, 28, 28), /255permute(2, 0, 1), /255
Hyperparametersmanually (SGD, lr 0.01)Optuna se tuned (val set par) — Chapter 22
Resulttest ≈ 0.92train 0.8889, val 0.7068, test 0.7118 (gap 0.1771)

CIFAR-10 mushkil hai: rang, background, alag-alag angles, aur ek class ke andar bahut variety (classes: airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck). Train–test gap 0.18 = clear overfitting. Lab 13 ke "next improvements" me sabse pehla: data augmentation (phir deeper CNN, LR scheduling, early stopping, zyada Optuna trials, pretrained ResNet).

Augmentation ke common transforms

PyTorch · train vs test transforms (CIFAR-10)

from torchvision import datasets, transforms

train_transform = transforms.Compose([
    transforms.RandomCrop(32, padding=4),              # random shift (pad 4, phir 32x32 crop)
    transforms.RandomHorizontalFlip(p=0.5),            # random mirror
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))   # 3 channels -> 3 values
])
test_transform = transforms.Compose([                  # test par koi randomness NAHI
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

train_set = datasets.CIFAR10(root='./data', train=True, download=True, transform=train_transform)
test_set = datasets.CIFAR10(root='./data', train=False, download=True, transform=test_transform)
print(train_set[0][0].shape)        # har baar access par nayi random augmented copy

Output (augmentation pipeline ko ek 32×32 RGB image par chala kar check kiya)

torch.Size([3, 32, 32])

Exam Radar

  • CIFAR-10: 32×32×3, 10 classes, 50k train / 10k test. Lab 13 flatten = 4096.
  • Augmentation = label-preserving random transforms, training only, reduces overfitting.
  • RandomCrop(32, padding=4) = random translation up to 4 px; RandomHorizontalFlip(p=0.5) default p = 0.5.
  • Horizontal flip digits/text ke liye galat (label badal sakta hai).

Galti mat karna

  • Test transform me Random... daalna — evaluation random aur unfair ho jaayega.
  • Normalize ke tuples me RGB ke liye 1 hi value dena — 3 chahiye.

Dimaag me aise baitha lo

Augmentation = ek hi billi ki alag-alag selfie. Label wahi, roop naya.

23.23 1×1 convolution — ek chhota preview

Kahani

Juice wale ke paas har glass me 64 alag ras (channels) ki boondein hain. Wo padosi glasses ko dekhta hi nahi; bas har glass ke andar in 64 ras ko alag-alag anupaat me mila kar 32 naye flavours bana deta hai. Glass jitne the utne hi rahe (H × W same), bas flavours (channels) badal gaye. Ye hai 1×1 convolution.

y[o, i, j] = Σc w[o, c] · x[c, i, j] + b[o]
  • x[c, i, j] = input ke channel c me pixel (i, j); o = output channel number
  • w[o, c] = filter o ka channel c wala weight (kernel 1×1, isliye bas ek number per channel)
  • Matlab: har pixel par saare input channels ka weighted sum — ek per-pixel fully connected layer, jo har pixel par shared hai
  • Params = (Cin + 1) × Cout; e.g. 64 → 32: (64 + 1) × 32 = 2,080
Input: H × W × 4 (C_in = 4) laal = ek pixel ki 4-channel "dandi" w₁ w₂ w₃ w₄ 1×1×4 filter Σ + b Output: H × W × C_out H, W same; sirf channels badle har pixel par ek chhota fully connected layer (shared)
1×1 convolution: har pixel position par us jagah ke saare input channels ka weighted sum. Padosi pixels nahi dekhta (RF nahi badhta), sirf channels mix karta hai.

Chalo numbers se samjhein

Input: 2 channels, har ek 2×2: A = [1 2; 3 4], B = [5 6; 7 8]. Do 1×1 filters, bias 0: filter 1 = (0.5, 0.5), filter 2 = (1, −1).

  1. Filter 1, pixel (0,0): 0.5×1 + 0.5×5 = 3; poora map = average of A, B = [3 4; 5 6]
  2. Filter 2, pixel (0,0): 1×1 + (−1)×5 = −4; poora map = A − B = [−4 −4; −4 −4]

Jawab: 2×2×2 → 2×2×2; H, W same, channels "mix" ho gaye.

PyTorch · 1×1 conv

import torch
import torch.nn as nn
import torch.nn.functional as F

A = torch.tensor([[[1., 2.], [3., 4.]], [[5., 6.], [7., 8.]]]).unsqueeze(0)   # (1, 2, 2, 2)
w11 = torch.tensor([[[[0.5]], [[0.5]]],      # filter 1: average
                    [[[1.0]], [[-1.0]]]])    # filter 2: difference
print(w11.shape)                             # (C_out=2, C_in=2, 1, 1)
print(F.conv2d(A, w11))

c11 = nn.Conv2d(64, 32, kernel_size=1)       # 64 channels -> 32 channels
print(c11(torch.randn(1, 64, 7, 7)).shape, sum(p.numel() for p in c11.parameters()))

Output

torch.Size([2, 2, 1, 1])
tensor([[[[ 3.,  4.],
          [ 5.,  6.]],

         [[-4., -4.],
          [-4., -4.]]]])
torch.Size([1, 32, 7, 7]) 2080

Kaam kahan aata hai? Mehnge 3×3/5×5 conv se pehle channels kam karne (bottleneck) — L11–12 worksheet: 192 → 128 ka seedha 3×3 = 9 × 192 × 128 = 221,184 weights, jabki pehle 1×1 se 192 → 96 karke phir 3×3: 192 × 96 + 9 × 96 × 128 = 129,024. Ye idea Network-in-Network (2013), Inception (GoogLeNet) aur ResNet bottleneck/projection shortcuts me use hota hai — poori kahani Chapter 24 aur Chapter 25 me.

Exam Radar

  • 1×1 conv: channels mix/change karta hai, H × W same (stride 1), receptive field nahi badhta.
  • Ye saare input channels ko mix karta hai (sirf ek channel ko nahi) — worksheet ka MCQ.
  • Params = (Cin + 1)·Cout; cheap way to reduce channels (dimensionality reduction) + extra ReLU non-linearity.

Dimaag me aise baitha lo

1×1 conv = har glass ke andar ka mixer: padosi nahi dekhta, sirf flavours (channels) badalta hai.

Chapter 24

Classic CNN Architectures — LeNet, AlexNet, VGG, Inception

ImageNet / ILSVRC · top-5 error · LeNet-5 (1998) · AlexNet (2012) · VGG-16/19 (2014) · 1×1 convolution · Inception module · GoogLeNet · Global Average Pooling · depthwise separable conv · comparison

24.1 Kahani ek competition ki: ImageNet aur ILSVRC

Kahani

Socho har saal ek bada "Photo pehchano" cricket tournament hota hai. Har team ko ek machine banani hai. Machine ko ek photo dikhayi jaati hai (kutta, billi, tractor, pizza, jellyfish...) aur machine ko batana hai photo me kya hai. Total 1000 possible jawab hain. Jo team sabse kam galti kare, wahi champion.

2010 aur 2011 me teams purane "haath se banaye features" (hand-crafted features) wale tareeke use karti thi, aur har 4 photo me se lagbhag 1 galat hota tha. Fir 2012 me ek team aayi jisne CNN (wahi convolution wala network jo Chapter 23 me padha) ko bahut bada bana kar GPU pe train kiya. Galti ekdum se 26% se 15% pe aa gayi. Poori duniya hil gayi. Us din se "deep learning ka zamana" shuru hua. Fir har saal nayi team, naya architecture, aur galti aur kam: 2015 tak to machine insaan se bhi kam galti karne lagi.

Ye chapter usi tournament ke champions ki kahani hai: LeNet (dada ji, 1998), AlexNet (2012 ka hero), VGG (2014, "simple par gehra"), GoogLeNet/Inception (2014 champion, "chauda aur smart"). Agla chapter (Chapter 25) 2015 ke champion ResNet ka hai.

Definition

ImageNet: Fei-Fei Li ki team (2009) ka banaya hua ek bahut bada labelled image dataset: lagbhag 14 million images, ~20,000+ categories (WordNet ke naamon par), labelling Amazon Mechanical Turk workers ne ki.

ILSVRC (ImageNet Large Scale Visual Recognition Challenge, 2010–2017): ImageNet ka ek subset jisme 1000 classes, lagbhag 1.2 million (≈1.28M) training images, 50,000 validation aur 100,000 test images hain. Har saal ispe competition hota tha. Jab koi bole "trained on ImageNet", to usually isi 1000-class, 1.2M-image wale subset ki baat hoti hai.

Top-1 error vs Top-5 error (MCQ ka favourite)

Model har photo ke liye 1000 classes ka score (softmax probability) deta hai. Hum scores ko bade se chhote order me lagate hain.

Top-5 kyun? Kyunki ImageNet me kai photos me ek se zyada cheezein hoti hain (ek photo me "keyboard" bhi hai aur "mouse" bhi) aur kuch classes bahut milti-julti hain ("Siberian husky" vs "Eskimo dog"). Isliye 5 guesses ki chhoot di jaati thi.

Chalo numbers se samjhein

Sawaal: Ek photo ka sahi label hai "Egyptian cat". Model ke scores ke hisaab se top-5 guesses hain: 1. tabby cat (0.41), 2. tiger cat (0.22), 3. Egyptian cat (0.19), 4. lynx (0.06), 5. Persian cat (0.03). 4 aur photos ke top-1 guesses sahi the. Kul 5 photos par top-1 aur top-5 error kya hoga?

  1. Is photo ka pehla guess "tabby cat" hai, sahi label nahi. To top-1 ke hisaab se galat.
  2. Sahi label "Egyptian cat" 3rd number par hai, yaani top-5 ke andar. To top-5 ke hisaab se sahi.
  3. Baaki 4 photos dono hisaab se sahi (jab top-1 sahi hai to top-5 apne aap sahi).
  4. Top-1 error = 1/5 = 20%, Top-5 error = 0/5 = 0%

Jawab: top-1 error 20%, top-5 error 0%. Isliye leaderboards par top-5 number hamesha chhota dikhta hai.

Saal-dar-saal winners (ILSVRC classification, top-5 error)

SaalTeam / ModelTop-5 errorKya khaas tha
2010NEC-UIUC (Lin et al.)≈ 28.2%Hand-crafted features (SIFT, LBP) + linear SVM. No deep learning.
2011XRCE (Perronnin et al.)≈ 25.8%Fisher vectors + SVM. Abhi bhi shallow.
2012AlexNet (SuperVision: Krizhevsky, Sutskever, Hinton)15.3% (runner-up ≈ 26.2%)Pehla bada deep CNN, ReLU, dropout, 2 GPUs. ~11 percentage points ki chhalaang.
2013ZFNet / Clarifai (Zeiler & Fergus)≈ 11.7%AlexNet ko tune kiya: pehli layer 11×11 stride 4 ki jagah 7×7 stride 2; "deconvnet" se filters visualize kiye.
2014GoogLeNet (Inception v1, Szegedy et al.) — winner6.67%Inception module, 1×1 bottleneck, 22 layers, sirf ~5–7M params.
2014VGG (Simonyan & Zisserman) — 2nd7.3% (competition entry; baad me 6.8%)Sirf 3×3 conv, 16–19 layers. Localization task me 1st.
2015ResNet (He, Zhang, Ren, Sun — Microsoft)3.57%Residual (skip) connections, 152 layers. Insaan se behtar.
—Human (Andrej Karpathy ka khud par experiment, 2014)≈ 5.1%Reference line: "human-level".
ILSVRC top-5 error (%) — har saal galti kam 0 10 20 30 28.2 25.8 15.3 11.7 7.3 6.67 3.57 human ≈ 5.1 2010SVM 2011SVM 2012AlexNet 2013ZFNet 2014VGG (2nd) 2014GoogLeNet 2015ResNet deep learning yahan se! grey = hand-crafted features · orange/yellow/red = CNNs
Har saal top-5 error ghata. 2012 (AlexNet) ka gap sabse bada tha — wahi "ImageNet moment" hai. 2015 me ResNet insaan (≈5.1%) se neeche chala gaya.

Ek pattern note karo: har winner pichhle wale se zyada gehra (deeper) tha: AlexNet 8 layers → VGG 16/19 → GoogLeNet 22 → ResNet 152. Hamari slides bhi yahi kehti hain: "there was a general trend for several years for performance on this task to improve as the depth of the networks increased, and this is evidence that depth is important." Par depth badhana muft nahi tha: har architecture ne depth ki kisi na kisi problem ka jugaad nikaala. Wahi jugaad is chapter ka asli syllabus hai.

Exam Radar

  • ILSVRC: 1000 classes, ~1.2M training images, 50k validation, 100k test. Full ImageNet: ~14M images, 20k+ classes.
  • Top-5 error ≤ top-1 error, hamesha. Top-5 = "sahi label top 5 predictions me nahi hai".
  • AlexNet 2012: 15.3% (winning entry). Hamari slides (UDL book) AlexNet ke liye 16.4% top-5, 38.1% top-1 likhti hain — ye 5 CNNs ke average ka number hai bina extra data ke. Dono numbers pehchano: "≈15–16%".
  • VGG: slides me 6.8% top-5, 23.7% top-1 (post-competition best); competition entry 7.3%. GoogLeNet 6.67% ne 2014 jeeta. ResNet 3.57% (2015).
  • Human top-5 error ≈ 5.1% → ResNet (2015) pehla winner jo isse neeche gaya.

Galti mat karna

  • "VGG ne 2014 jeeta" — galat. 2014 classification GoogLeNet ne jeeta; VGG 2nd tha (par localization me 1st).
  • "Top-5 accuracy" aur "top-5 error" ulte hain: accuracy = 100 − error.

Dimaag me aise baitha lo

"28 → 26 → 15 → 12 → 7 → 3.6": SVM, SVM, Alex, ZF, VGG/Google, ResNet. Har kadam = zyada depth + ek naya jugaad.

24.2 Kisi bhi architecture ko padhne ka tareeka (3 formulas + 1 template)

Kahani

Maa jab naya recipe dekhti hain to pehle 3 cheezein dekhti hain: kya-kya dalega (ingredients), kis order me, aur kitni matra me. Architecture bhi ek recipe hai: kaunsi layer, kis order me, kitne filters. Agar tumhe 3 formulas yaad hain, to tum LeNet se ResNet tak har table khud bhar sakte ho — exam me bhi yahi poocha jaata hai.

Output size: O = ⌊ W − K + 2PS ⌋ + 1
Conv params = (K · K · Cin + 1) · Cout   |   FC params = (Nin + 1) · Nout
  • W = input ki height/width, K = kernel (filter) size, P = padding, S = stride, ⌊ ⌋ = neeche round (floor).
  • Cin = input channels (filter ki depth hamesha input ki depth ke barabar), Cout = filters ki ginti = output channels.
  • "+1" = har filter / har neuron ka ek bias.
  • Pooling, ReLU, Flatten, Dropout, Softmax, (original) LRN: 0 parameters. Pooling channels nahi badalta.

Ye teeno Chapter 23 me derive ho chuke hain; yahan sirf use karenge. Do aur zaroori baatein:

Classic CNN template

Saare classic CNNs ek hi dhaanche par bane hain: [CONV → ReLU/tanh → (CONV …) → POOL] × n → FLATTEN → FC → FC → softmax. Jaise jaise aage badhte hain: spatial size (H×W) chhota hota jaata hai, channels (depth) badhte jaate hain. Matlab network "kahan hai" wali detail chhodta jaata hai aur "kya hai" wali samajh (rich features) badhata jaata hai. Teacher ke L11 page ke "Three ideas" yahi the: (1) spatial size aur channel count alag kaam karte hain; (2) activations aur parameters alag cheezein hain; (3) fully connected layers parameter budget pe kabza kar leti hain.

24.3 LeNet-5 (1998) — CNN ke dada ji, bank ke cheque padhne wale

Kahani

1990s ka America. Banks me roz laakhon cheques aate the, aur har cheque pe haath se likha amount kisi insaan ko padh kar computer me type karna padta tha. Thakaau, mehenga, galti wala kaam. AT&T Bell Labs me Yann LeCun aur saathiyon (Léon Bottou, Yoshua Bengio, Patrick Haffner) ne ek chhota sa network banaya jo haath se likhe digits padh leta tha. Paper: "Gradient-Based Learning Applied to Document Recognition", Proceedings of the IEEE, 1998. Paper ke mutabik ye system US ke kai banks me har mahine laakhon cheques padh raha tha.

Iska naam tha LeNet-5. Aaj ke standard se ye bachcha hai (sirf ~60 hazaar parameters), par isme wo saari cheezein pehli baar ek saath dikhi jo aaj har CNN me hain: local receptive field, weight sharing, pooling (subsampling), feature hierarchy, aur end-to-end backprop training.

Dataset: MNIST

MNIST = haath se likhe digits 0–9 ka dataset: 60,000 training + 10,000 test images, har image 28×28 grayscale (1 channel). LeNet-5 ka input 32×32 tha: digit ko 32×32 frame ke beech me rakha jaata tha (2 pixel ka border), taaki stroke ke end-points aur corners bhi filters ke receptive field ke beech me aa sakein. Isliye MNIST ke saath modern code me pehli conv me padding=2 lagate hain (28 + 2·2 = 32 jaisa effect).

Architecture: C1 → S2 → C3 → S4 → C5 → F6 → Output

Original paper me naam aise the: C = convolution layer, S = subsampling (pooling) layer, F = fully connected. Pattern yaad rakho: conv → pool → conv → pool → conv(fc) → fc → output.

LeNet-5: 32×32×1 se 10 classes tak digit 32×32×1 Input 28×28×6 C1 5×5 156 params 14×14×6 S2 2×2 10×10×16 C3 5×5 1,516 (orig) 2,416 (full) 5×5×16 S4 pool 120 C5 48,120 84 F6 10,164 10 Output 850* conv-pool- conv-pool- fc-fc-out size: 32→28→14 →10→5→1 depth: 1→6→16→120 * modern Linear(84,10)+softmax = 850; original me 10 RBF units the original total ≈ 60,000 trainable params · modern full-C3 version = 61,706
LeNet-5 ki patti: har stage par spatial size ghatta hai, depth badhti hai. Red numbers = parameters.

Layer-by-layer table (original LeNet-5, 1998)

LayerKya karta haiOutput shapeParameters (calculation)
Inputgrayscale digit32×32×10
C16 filters, 5×5, stride 1, no padding28×28×6  (32−5+1 = 28)(5·5·1 + 1)·6 = 156
S22×2 subsampling, stride 2 (har map ka 1 trainable coefficient + 1 bias)14×14×62·6 = 12 (modern avg/max pool me 0)
C316 filters, 5×5, partial connections to S2 maps10×10×16  (14−5+1 = 10)1,516 original  |  (5·5·6 + 1)·16 = 2,416 agar full connected
S42×2 subsampling, stride 25×5×162·16 = 32 (modern me 0)
C5120 filters, 5×5×16 (filter = poora input!)1×1×120  (5−5+1 = 1)(5·5·16 + 1)·120 = 48,120
F6fully connected, 84 neurons, tanh84(120 + 1)·84 = 10,164
Output10 units (original: Euclidean RBF; modern: Linear + softmax)10modern: (84 + 1)·10 = 850 (original RBF ke 84·10 = 840 fixed values)

Chalo numbers se samjhein — total ≈ 60k kaise?

  1. C3 ke partial connections (paper ka table): pehle 6 maps, S2 ke 3 lagatar maps se jude; agle 6 maps 4 lagatar maps se; agle 3 maps 4 non-lagatar maps se; aakhri 1 map saare 6 se. Ek map jo k input maps dekhta hai uske params = k·25 + 1.
    6·(3·25+1) + 6·(4·25+1) + 3·(4·25+1) + 1·(6·25+1)
    = 6·76 + 6·101 + 3·101 + 151
    = 456 + 606 + 303 + 151 = 1,516
  2. Ab original trainable params jodo: 156 + 12 + 1,516 + 32 + 48,120 + 10,164 = 60,000. Paper bhi exactly yahi kehta hai: "340,908 connections, but only 60,000 trainable free parameters". (Connections zyada kyunki ek hi weight bahut jagah reuse hota hai — weight sharing.)
  3. Modern version (C3 full, pooling me 0 params, Linear(84,10)): 156 + 0 + 2,416 + 0 + 48,120 + 10,164 + 850 = 61,706.
  4. Teacher ke L11 page ka split: conv (C1 + C3) = 156 + 2,416 = 2,572; baaki "fully connected" (400→120, 120→84, 84→10) = 48,120 + 10,164 + 850 = 59,134. 59,134 / 61,706 = 95.8% params FC part me!

Jawab: original ≈ 60,000 params; modern PyTorch/Keras version = 61,706 params, jisme ~95.8% fully connected hisse me.

Original LeNet ki khaas baatein (jo aaj badal gayi)

Receptive field kaise badhta hai (LeNet study notes ka table)

StageInputC1S2C3S4C5
Original image me receptive field1×15×56×614×1416×1632×32 (poori image)

C1 ka ek neuron sirf ek chhota stroke dekhta hai; C5 ka ek neuron poora digit "dekh" sakta hai. Yahi feature hierarchy hai: edges/strokes → stroke combinations → poora digit.

RGB input ho to kya badlega?

Agar input 32×32×3 ho, to C1 ka har filter 5×5×3 hoga (filter depth = input depth). (5·5·3 + 1)·6 = 76·6 = 456 params. Output phir bhi 28×28×6 (6 filters → 6 maps; ek filter RGB teeno ko jod kar ek hi map banata hai). C1 ke baad ki layers bilkul same.

PyTorch: modern LeNet-5 for 28×28 MNIST

Naya API yahan: nn.AvgPool2d(kernel_size, stride) — window ka average leta hai (max ki jagah). Default stride = kernel_size. Params: 0. nn.Tanh() — tanh activation as a layer.

PyTorch · LeNet-5 (teacher notebook ka Keras model, PyTorch me)

import torch
import torch.nn as nn

class LeNet5(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 6, kernel_size=5, padding=2),   # C1: (N,1,28,28) -> (N,6,28,28)  padding=2 se 28 hi raha
            nn.Tanh(),                                   # original jaisa tanh (modern: nn.ReLU())
            nn.AvgPool2d(kernel_size=2, stride=2),       # S2: -> (N,6,14,14)
            nn.Conv2d(6, 16, kernel_size=5),             # C3: -> (N,16,10,10)   14-5+1 = 10
            nn.Tanh(),
            nn.AvgPool2d(kernel_size=2, stride=2),       # S4: -> (N,16,5,5)
        )
        self.classifier = nn.Sequential(
            nn.Flatten(),                                # 16*5*5 = 400 numbers ki line
            nn.Linear(16 * 5 * 5, 120),                  # C5 ka kaam: 400 -> 120
            nn.Tanh(),
            nn.Linear(120, 84),                          # F6: 120 -> 84
            nn.Tanh(),
            nn.Linear(84, num_classes),                  # output logits (softmax CrossEntropyLoss ke andar)
        )

    def forward(self, x):
        x = self.features(x)
        return self.classifier(x)

model = LeNet5()
x = torch.randn(1, 1, 28, 28)            # ek fake MNIST image: (batch, channel, H, W)
for layer in list(model.features) + list(model.classifier):
    x = layer(x)                         # layer by layer chalao aur shape dekho
    print(f"{layer.__class__.__name__:10s} -> {tuple(x.shape)}")

total = sum(p.numel() for p in model.parameters())   # numel() = tensor me kitne numbers
print("Total params:", total)

Output

Conv2d     -> (1, 6, 28, 28)
Tanh       -> (1, 6, 28, 28)
AvgPool2d  -> (1, 6, 14, 14)
Conv2d     -> (1, 16, 10, 10)
Tanh       -> (1, 16, 10, 10)
AvgPool2d  -> (1, 16, 5, 5)
Flatten    -> (1, 400)
Linear     -> (1, 120)
Tanh       -> (1, 120)
Linear     -> (1, 84)
Tanh       -> (1, 84)
Linear     -> (1, 10)
Total params: 61706

Parameter counting ka one-liner yaad kar lo: sum(p.numel() for p in model.parameters()). Sirf trainable chahiye to: sum(p.numel() for p in model.parameters() if p.requires_grad). model.parameters() saare weight aur bias tensors deta hai; p.numel() us tensor ke elements ginta hai (jaise C1 weight ka shape (6,1,5,5) → 150).

Training bilkul wahi standard loop hai (Chapter 13): criterion = nn.CrossEntropyLoss(), optimizer = optim.Adam(model.parameters(), lr=1e-3), phir har batch par zero_grad → forward → loss → backward → step. Teacher ke Keras notebook me (tanh + average pooling, Adam, batch 128) sirf 5 epochs me MNIST test accuracy 98.35% aayi thi.

Exam Radar

  • LeNet-5: LeCun, Bottou, Bengio, Haffner, 1998; task: handwritten digits / bank cheque reading; dataset MNIST.
  • Input 32×32×1; C1 6@5×5 → 28×28×6; S2 → 14×14×6; C3 16@5×5 → 10×10×16; S4 → 5×5×16; C5 120 → 1×1×120; F6 84; output 10.
  • Params: C1 156, C3 1,516 (original) / 2,416 (full), C5 48,120, F6 10,164, output 850 (modern). Total ≈ 60k (modern 61,706).
  • Activation: tanh/sigmoid (scaled tanh 1.7159·tanh(2a/3)); pooling: average (subsampling). No ReLU, no dropout, no max-pool in original.
  • Pattern: conv-pool-conv-pool-fc-fc-output. Spatial 32→28→14→10→5→1, depth 1→6→16→120.
  • ~95.8% params FC hisse me (modern version) — "FC layers dominate the parameter budget".

Galti mat karna

  • C3 ke params pooche to dekho source "original" bol raha hai (1,516) ya "modern Conv2d" (2,416). Dono sahi ho sakte hain — context padho.
  • RGB input pe C1 filter 5×5 nahi, 5×5×3 hai; ek filter = ek feature map (teen nahi).
  • 28×28 MNIST pe padding=0 lagaya to C1 ke baad 24×24, aakhir me 4×4×16 = 256 aayega, aur nn.Linear(400,120) shape error dega. Ya padding=2 lagao ya Linear(256,120).

Dimaag me aise baitha lo

"6-16-120-84-10" — LeNet ka phone number. Aur "156 · 2416 · 48120 · 10164 · 850" = 61,706.

24.4 AlexNet (2012) — jisne deep learning ka zamana shuru kiya

Kahani

Toronto University me Geoffrey Hinton ke student Alex Krizhevsky aur Ilya Sutskever ne socha: "LeNet wala idea hi lo, par isko bahut bada karo, aur 1.2 million rangeen photos pe train karo." Problem: itna bada network us zamane ke CPU pe mahino chalta. Jugaad: gaming wale graphics cards! Unhone network ko do NVIDIA GTX 580 GPUs (3 GB each) pe baant diya aur ~5–6 din train kiya.

Result: ILSVRC 2012 me top-5 error 15.3%, jabki doosre number ki team (purane hand-crafted features) 26.2% pe thi. Socho cricket me ek team 350 banaye aur doosri 180 — sab log samajh gaye ki khel badal gaya hai. Paper: "ImageNet Classification with Deep Convolutional Neural Networks", Krizhevsky, Sutskever, Hinton, NIPS 2012.

Slides (UDL book) ke shabdon me: "AlexNet ... consists of eight hidden layers with ReLU activation functions, of which the first five are convolutional and the rest fully connected." Matlab 8 learned layers = 5 conv + 3 FC. Paper ke hisaab se ~60 million parameters aur ~650,000 neurons.

Layer table (single-GPU "standard" version, input 227×227×3)

Paper me input 224×224 likha hai, par us par formula integer nahi deta: (224 − 11)/4 + 1 = 54.25. Isliye textbooks/teacher ka notebook 227×227×3 lete hain: (227 − 11)/4 + 1 = 216/4 + 1 = 55 ✓. (torchvision 224 + padding 2 leta hai: ⌊(224 − 11 + 4)/4⌋ + 1 = 55.)

LayerSettingsOutput shapeSize calculationParameters
InputRGB227×227×3—0
Conv196 filters 11×11, stride 4, pad 0, ReLU (+LRN)55×55×96(227−11)/4+1 = 55(11·11·3+1)·96 = 34,944
MaxPool13×3, stride 2 (overlapping)27×27×96⌊(55−3)/2⌋+1 = 270
Conv2256 filters 5×5, pad 2, ReLU (+LRN)27×27×256(27−5+4)/1+1 = 27(5·5·96+1)·256 = 614,656
MaxPool23×3, stride 213×13×256⌊(27−3)/2⌋+1 = 130
Conv3384 filters 3×3, pad 1, ReLU13×13×384(13−3+2)+1 = 13(3·3·256+1)·384 = 885,120
Conv4384 filters 3×3, pad 1, ReLU13×13×38413(3·3·384+1)·384 = 1,327,488
Conv5256 filters 3×3, pad 1, ReLU13×13×25613(3·3·384+1)·256 = 884,992
MaxPool33×3, stride 26×6×256⌊(13−3)/2⌋+1 = 60
Flatten—9,2166·6·2560
FC64096, ReLU, dropout 0.54096—(9216+1)·4096 = 37,752,832
FC74096, ReLU, dropout 0.54096—(4096+1)·4096 = 16,781,312
FC81000, softmax1000—(4096+1)·1000 = 4,097,000
Total (teacher ke L11 page ka number bhi yahi)62,378,344 ≈ 62.4M

Chalo numbers se samjhein — paisa kahan gaya?

  1. Saare conv layers: 34,944 + 614,656 + 885,120 + 1,327,488 + 884,992 = 3,747,200 (≈ 3.7M).
  2. Saare FC layers: 37,752,832 + 16,781,312 + 4,097,000 = 58,631,144 (≈ 58.6M).
  3. FC ka hissa: 58,631,144 / 62,378,344 = 94.0%. Akela FC6: 37,752,832 / 62,378,344 = 60.5%.
  4. Original paper me Conv2, Conv4, Conv5 do GPUs me bate the (har filter sirf apne GPU ke aadhe channels dekhta tha — PyTorch me groups=2). Tab: Conv2 = (5·5·48+1)·256 = 307,456; Conv4 = (3·3·192+1)·384 = 663,936; Conv5 = (3·3·192+1)·256 = 442,624 → total 60,965,224 ≈ 61M. Isliye paper "60 million" bolta hai.

Jawab: ~62.4M (single-tower) / ~61M (original 2-GPU). 94% params FC me; sabse bhaari layer FC6 (~37.7M). Teacher ke L11 quiz ka jawab: "Which layer contributes the most trainable parameters? → FC 1 (yaani FC6)".

AlexNet: 5 conv + 3 FC = 8 learned layers photo 227×227×3 11×11 55×55×96 Conv1 s4 27×27×96 pool 3/2 5×5 27×27×256 Conv2 13×13 pool 345 13×13×384/384/256 Conv3-4-5 (3×3) 6×6×256 409640961000 FC6FC7FC8 FC6 akela ≈ 37.7M params dropout 0.5 FC6, FC7 me filters: 96 → 256 → 384 → 384 → 256 · FC: 4096 → 4096 → 1000 · ReLU har jagah kernels: 11×11 → 5×5 → 3×3 (bade se chhote) · total ≈ 60–62M params
AlexNet ki patti. Pehli layer bada 11×11 filter stride 4 ke saath image ko jaldi chhota karti hai; aakhri teen FC layers params ka ~94% kha jaati hain.

AlexNet ke 7 innovations (MCQ yahin se aate hain)

#IdeaKya kiya / kyun
1ReLU activationtanh/sigmoid ki jagah max(0, x). Positive side pe saturate nahi hota → gradient zinda. Paper: 4-layer CNN ne CIFAR-10 pe 25% training error tak ~6× tezi se pahuncha tanh ke mukable. (Chapter 9)
2Dropout 0.5FC6 aur FC7 me. 60M params ke saath overfitting ka bada dar tha. Dropout ne overfitting roka par convergence ke liye ~2× iterations lage. (Chapter 20)
3Data augmentation(a) 256×256 image se random 224×224 crops + horizontal flips (data ~2048× "bada"); (b) PCA colour augmentation: RGB intensities me principal components ke along random badlav (lighting change jaisa) — top-1 error 1% se zyada kam. Test time pe 10 patches (4 corners + centre, aur unke flips) ka average.
4LRN (Local Response Normalization)Conv1, Conv2 ke ReLU ke baad. Ek position par padosi channels ke beech "lateral inhibition": bada response baakiyon ko daba deta hai. Top-1/top-5 me ~1.4%/1.2% sudhaar. 0 params. Baad me (VGG ne dikhaya ki khaas fayda nahi; aur BatchNorm ne replace kar diya) chhod diya gaya.
5Overlapping max poolingWindow 3×3, stride 2 (window > stride, to windows overlap). Non-overlapping 2×2/2 ke mukable top-1/top-5 ~0.4%/0.3% behtar, overfitting thoda kam.
6Multi-GPU trainingNetwork 2 × GTX 580 (3GB) pe split; GPUs sirf kuch layers pe baat karte the. ~5–6 din training.
7Training recipeSGD + momentum 0.9, weight decay 0.0005, batch size 128, lr = 0.01, validation error ruk jaye to lr ÷ 10 (3 baar hua), ~90 epochs. Weights init N(0, 0.01²).

LeNet-5 (1998)

32×32×1 digits · 2 conv + 3 FC · tanh/sigmoid · average pooling · ~60k params · CPU · no dropout · MNIST (10 classes)

AlexNet (2012)

227×227×3 photos · 5 conv + 3 FC · ReLU · overlapping max pooling · ~60M params (1000× zyada) · 2 GPUs · dropout + augmentation · ImageNet (1000 classes)

Teacher ke notebook ki line yaad rakho: "LeNet: CNN works → AlexNet: CNN scales → VGG: CNN goes deeper."

PyTorch: compact AlexNet (ImageNet shape, single tower)

Naye arguments: nn.ReLU(inplace=True) — output alag tensor me banane ki jagah input ko hi overwrite karta hai (memory bachti hai, result same). nn.Dropout(p=0.5) — training me 50% neurons random band (Chapter 20); model.eval() me automatically band.

PyTorch · AlexNet (227×227 input)

import torch
import torch.nn as nn

class AlexNet(nn.Module):
    def __init__(self, num_classes=1000):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 96, kernel_size=11, stride=4),          # (N,3,227,227) -> (N,96,55,55)
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),               # -> (N,96,27,27)  overlapping pool
            nn.Conv2d(96, 256, kernel_size=5, padding=2),        # -> (N,256,27,27)
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),               # -> (N,256,13,13)
            nn.Conv2d(256, 384, kernel_size=3, padding=1),       # -> (N,384,13,13)
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 384, kernel_size=3, padding=1),       # -> (N,384,13,13)
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),       # -> (N,256,13,13)
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),               # -> (N,256,6,6)
        )
        self.classifier = nn.Sequential(
            nn.Flatten(),                                        # -> (N,9216)
            nn.Dropout(p=0.5),
            nn.Linear(256 * 6 * 6, 4096),                        # FC6 (sabse bhaari)
            nn.ReLU(inplace=True),
            nn.Dropout(p=0.5),
            nn.Linear(4096, 4096),                               # FC7
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),                        # FC8 -> logits (softmax loss ke andar)
        )

    def forward(self, x):
        return self.classifier(self.features(x))

model = AlexNet()
x = torch.randn(2, 3, 227, 227)                     # dummy batch of 2 images
print("features out:", tuple(model.features(x).shape))
print("logits      :", tuple(model(x).shape))
total = sum(p.numel() for p in model.parameters())
fc6 = model.classifier[2].weight.numel() + model.classifier[2].bias.numel()
print(f"Total params: {total:,}")
print(f"FC6 params  : {fc6:,}  ({100*fc6/total:.1f}% of total)")

Output

features out: (2, 256, 6, 6)
logits      : (2, 1000)
Total params: 62,378,344
FC6 params  : 37,752,832  (60.5% of total)

Exam Radar

  • Krizhevsky, Sutskever, Hinton — 2012, ILSVRC 2012 winner, top-5 15.3% (slides: 16.4% top-5, 38.1% top-1).
  • 8 layers = 5 conv + 3 FC. Filters 96→256→384→384→256; FC 4096→4096→1000.
  • Conv1: 96 @ 11×11, stride 4 → 55×55×96. Pools: 3×3 stride 2 (overlapping). Last conv output 6×6×256 = 9216.
  • Params ≈ 60–62M; FC6 ≈ 37.7M (largest); ~94% FC.
  • Innovations: ReLU (≈6× faster than tanh), dropout 0.5, data augmentation (crops, flips, PCA colour), LRN, overlapping pooling, 2 GPUs (GTX 580).
  • Training: SGD momentum 0.9, weight decay 5e-4, lr 0.01 ÷10 on plateau, batch 128.

Galti mat karna

  • "AlexNet ne pehli baar CNN banaya" — nahi! CNN (LeNet) 1998 se tha. AlexNet ne dikhaya ki CNN bade data + GPU + ReLU + dropout ke saath scale hota hai.
  • 224 vs 227: formula 224 pe integer nahi deta; 227 lo (ya 224 + padding 2).
  • AlexNet me BatchNorm nahi tha (BN 2015 me aaya). Uski normalization = LRN.

Dimaag me aise baitha lo

"11-4-96" (pehli layer), "R-D-A-L-O-G": ReLU, Dropout, Augmentation, LRN, Overlapping pool, GPUs.

24.5 VGG (2014) — "ek hi chhoti eent, bahut oonchi deewar"

Kahani

AlexNet ek "jugaadu" building jaisa tha: kahin 11×11 ki khidki, kahin 5×5, kahin 3×3; kahin LRN, kahin nahi. Oxford University ke Visual Geometry Group (VGG) ke Karen Simonyan aur Andrew Zisserman ne socha: "Agar hum sirf ek hi size ki chhoti eent (3×3 conv) use karein, aur deewar ko bas oonchi (deeper) karte jaayein, to kya hoga?" Jaise Lego me ek hi tarah ke chhote bricks se poora mahal ban jaata hai.

Result: 16 aur 19 layers ke networks — VGG-16 aur VGG-19 — jo AlexNet se kaafi behtar the. Paper: "Very Deep Convolutional Networks for Large-Scale Image Recognition" (arXiv 2014, ICLR 2015). ILSVRC 2014 me classification me 2nd (7.3%, baad me 6.8%) aur localization me 1st. Iski simplicity ki wajah se aaj bhi ye teaching aur transfer learning ka favourite hai.

VGG ke design rules (poora architecture inhi 4 lines me)

1. Har convolution: 3×3 kernel, stride 1, padding 1 → spatial size same rehta hai: (224 − 3 + 2·1)/1 + 1 = 224.
2. Har block ke end me 2×2 max-pool, stride 2 → size aadha.
3. Har pool ke baad channels double: 64 → 128 → 256 → 512 → 512 (512 pe cap).
4. Aakhir me 3 FC: 4096 → 4096 → 1000 (+ softmax). Har conv/FC ke baad ReLU. FC1, FC2 me dropout 0.5.

Paper ne 5 configurations try ki (A se E): A = 11 layers (VGG-11), B = 13, C = 16 (kuch 1×1 conv ke saath), D = 16 layers (VGG-16), E = 19 layers (VGG-19). Params: A ≈ 133M, D ≈ 138M, E ≈ 144M. Depth badhane se error ghatta gaya (D aur E sabse achhe) — "depth matters" ka saboot.

VGG-16 full table (config D, input 224×224×3)

Config list jo code me bhi likhenge: [64, 64, M, 128, 128, M, 256, 256, 256, M, 512, 512, 512, M, 512, 512, 512, M] (M = maxpool). Blocks me conv ki ginti: 2, 2, 3, 3, 3 = 13 conv. Plus 3 FC = 16 weight layers. (5 max-pools aur ReLUs "16" me nahi gine jaate.)

BlockLayerOutput shapeParameters = (3·3·Cin + 1)·Cout
1Conv1_1 (3→64)224×224×64(27 + 1)·64 = 1,792
Conv1_2 (64→64)224×224×64(576 + 1)·64 = 36,928
MaxPool112×112×640
2Conv2_1 (64→128)112×112×12873,856
Conv2_2 (128→128)112×112×128147,584
MaxPool56×56×1280
3Conv3_1 (128→256)56×56×256295,168
Conv3_2 (256→256)56×56×256590,080
Conv3_3 (256→256)56×56×256590,080
MaxPool28×28×2560
4Conv4_1 (256→512)28×28×5121,180,160
Conv4_2 (512→512)28×28×5122,359,808
Conv4_3 (512→512)28×28×5122,359,808
MaxPool14×14×5120
5Conv5_1 (512→512)14×14×5122,359,808
Conv5_2 (512→512)14×14×5122,359,808
Conv5_3 (512→512)14×14×5122,359,808
MaxPool7×7×5120
ClassifierFlatten7·7·512 = 25,0880
FC1 (25,088→4096)4096(25,088 + 1)·4096 = 102,764,544
FC2 (4096→4096)409616,781,312
FC3 (4096→1000)10004,097,000
Total (13 conv = 14,714,688; 3 FC = 123,642,856)138,357,544 ≈ 138.4M

Chalo numbers se samjhein

  1. Conv1_2 ke params Conv1_1 se zyada kyun, jabki dono 64 channels banate hain? Kyunki Conv1_2 ka input 64 channels hai (Conv1_1 ka sirf 3): (3·3·64 + 1)·64 = 577·64 = 36,928 vs (3·3·3 + 1)·64 = 28·64 = 1,792. (Teacher ka L11 quiz bhi yahi tha: "It receives 64 input channels instead of 3".)
  2. Conv4_2: ek filter = 3·3·512 = 4,608 weights, +1 bias = 4,609; 512 filters → 4,609 × 512 = 2,359,808.
  3. FC1: (25,088 + 1) × 4,096 = 102,764,544. Ye akela poore network ka 102,764,544 / 138,357,544 = 74.3% hai!
  4. Teeno FC: 123,642,856 / 138,357,544 = 89.4%. Conv backbone sirf 14.7M (10.6%).

Jawab: VGG-16 = 138,357,544 params; ~124M (89.4%) teen FC layers me, FC1 akela ~102.8M. VGG-19 (3 extra conv 512-channel layers) = 143,667,240 ≈ 144M.

Sabse important sawaal: sirf 3×3 hi kyun?

Socho tum chhat pe khade ho aur neeche ka ilaaka dekhna hai. Ek tareeka: ek badi 7×7 khidki. Doosra: 3 chhoti 3×3 khidkiyan ek ke upar ek. Teen fayde:

  1. Same receptive field: pehli 3×3 layer ka ek neuron input ka 3×3 dekhta hai. Doosri 3×3 layer ka neuron pehli layer ke 3×3 neurons dekhta hai, jinme har ek 3×3 dekhta hai aur wo ek-ek pixel khiske hue hain → input ka (3 + 2) = 5×5. Teesri layer → (5 + 2) = 7×7. General: n stacked 3×3 (stride 1) ka receptive field = 2n + 1.
  2. Kam parameters (maan lo input aur output dono me C channels, bias ignore):
    Ek 5×5 conv: 5·5·C·C = 25C² vs do 3×3: 2·(3·3·C·C) = 18C² → 28% kam
    Ek 7×7 conv: 7·7·C·C = 49C² vs teen 3×3: 3·(9C²) = 27C² → 45% kam
    C = 64 pe: 25C² = 102,400 vs 18C² = 73,728 · 49C² = 200,704 vs 27C² = 110,592
  3. Zyada non-linearity: teen layers = teen ReLU, ek 7×7 = sirf ek ReLU. Zyada ReLU → decision function zyada "discriminative" (complex patterns pakad sakta hai). Paper isko ek tarah ka regularization bhi bolta hai: 7×7 filter ko majboor kiya ki wo 3×3 ke combination me toote.
Do 3×3 layers = ek 5×5 jitna nazaara input red neuron → 5 input pixels layer 1 (3×3) layer 2 (3×3) ek 5×5 conv: 25C² weights, 1 ReLU do 3×3 conv: 18C² weights, 2 ReLU teen 3×3 conv = 7×7 nazaara: 27C² vs 49C², 3 ReLU vs 1 receptive field (n layers of 3×3) = 2n + 1 → 3, 5, 7, 9 ...
1-D me dikhaya hai (2-D me har direction me yahi hota hai): layer 2 ka ek neuron, layer 1 ke 3 neurons ke through input ke 5 pixels dekhta hai.

Receptive field VGG-16 me kitna bada hota hai

VGG study notes ka table (theoretical receptive field, original image pixels me): Conv1_1: 3 · Conv1_2: 5 · Pool1: 6 · Conv2_2: 14 · Pool2: 16 · Conv3_3: 40 · Pool3: 44 · Conv4_3: 92 · Pool4: 100 · Conv5_3: 196 · Pool5: 212. Matlab aakhri conv ka ek neuron 224×224 image ka lagbhag poora hissa "dekh" sakta hai — chhote filters, bada nazaara.

VGG ki kamzoriyan (drawbacks)

Training recipe (AlexNet jaisa hi): mini-batch SGD, batch 256, momentum 0.9, weight decay 5·10−4, dropout 0.5 (FC1, FC2), lr 0.01 ÷ 10 jab validation accuracy ruk jaaye (3 baar), ~74 epochs, 4 GPUs pe 2–3 hafte. Input: 224×224 RGB jisme training set ka mean RGB subtract kiya.

PyTorch: config list se VGG banana (make_layers)

Ye trick exam me favourite hai: list ke har number pe conv+ReLU, har 'M' pe maxpool. nn.Sequential(*layers) me * list ko unpack karke alag-alag arguments bana deta hai.

PyTorch · VGG-16 from a config list

import torch
import torch.nn as nn

cfg_vgg16 = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M',
             512, 512, 512, 'M', 512, 512, 512, 'M']

def make_layers(cfg, in_channels=3):
    layers = []
    for v in cfg:
        if v == 'M':
            layers.append(nn.MaxPool2d(kernel_size=2, stride=2))   # size aadha
        else:
            layers.append(nn.Conv2d(in_channels, v, kernel_size=3, padding=1))  # size same
            layers.append(nn.ReLU(inplace=True))
            in_channels = v                                         # agle conv ka C_in = ye v
    return nn.Sequential(*layers)

class VGG16(nn.Module):
    def __init__(self, num_classes=1000):
        super().__init__()
        self.features = make_layers(cfg_vgg16)          # 13 conv layers
        self.classifier = nn.Sequential(                 # 3 FC layers
            nn.Flatten(),
            nn.Linear(512 * 7 * 7, 4096), nn.ReLU(inplace=True), nn.Dropout(0.5),
            nn.Linear(4096, 4096),        nn.ReLU(inplace=True), nn.Dropout(0.5),
            nn.Linear(4096, num_classes),
        )
    def forward(self, x):
        return self.classifier(self.features(x))

model = VGG16()
x = torch.randn(1, 3, 224, 224)
print("after features:", tuple(model.features(x).shape))
print("logits        :", tuple(model(x).shape))
n_conv = sum(isinstance(m, nn.Conv2d) for m in model.modules())   # modules() = saari sub-layers
n_fc = sum(isinstance(m, nn.Linear) for m in model.modules())
conv_p = sum(p.numel() for p in model.features.parameters())
total = sum(p.numel() for p in model.parameters())
print("conv layers:", n_conv, "| fc layers:", n_fc)
print(f"conv params: {conv_p:,} | total: {total:,}")

Output

after features: (1, 512, 7, 7)
logits        : (1, 1000)
conv layers: 13 | fc layers: 3
conv params: 14,714,688 | total: 138,357,544

VGG-19 banana ho to sirf list badlo: [64,64,'M',128,128,'M',256,256,256,256,'M',512,512,512,512,'M',512,512,512,512,'M'] (blocks 2,2,4,4,4 = 16 conv + 3 FC = 19). Wahi function, naya network — yahi VGG ki khoobsurti hai.

torchvision ka ready-made VGG + transfer learning

torchvision.models.vgg16(weights=...): weights=None → random init; weights=models.VGG16_Weights.IMAGENET1K_V1 → ImageNet pe trained weights download. torchvision ke VGG me features (13 conv), avgpool (AdaptiveAvgPool2d((7,7))) aur classifier (index 0 se 6; aakhri Linear classifier[6]) hote hain. Transfer learning: pretrained conv backbone ko freeze karo (requires_grad = False), sirf naya head train karo.

PyTorch · VGG-16 transfer learning (5 classes)

import torch
import torch.nn as nn
from torchvision import models

vgg = models.vgg16(weights=None)   # asli kaam me: weights=models.VGG16_Weights.IMAGENET1K_V1 (~528 MB)
for p in vgg.features.parameters():
    p.requires_grad = False        # conv backbone freeze: ye weights update nahi honge
vgg.classifier[6] = nn.Linear(4096, 5)   # 1000 classes ki jagah humari 5 classes

trainable = sum(p.numel() for p in vgg.parameters() if p.requires_grad)
frozen = sum(p.numel() for p in vgg.parameters() if not p.requires_grad)
print(f"trainable: {trainable:,} | frozen: {frozen:,}")
print(vgg(torch.randn(1, 3, 224, 224)).shape)

Output

trainable: 119,566,341 | frozen: 14,714,688
torch.Size([1, 5])

Optimizer ko sirf trainable params do: optim.Adam(filter(lambda p: p.requires_grad, vgg.parameters()), lr=1e-4) (ya seedha vgg.classifier.parameters()). Baad me chaho to deeper conv blocks unfreeze karke chhote lr se fine-tune karo.

Lab 15 ka VGG-style (CIFAR-10): sirf 3×3 convs (64→128→256→256→512→512→512→512), har conv ke baad BatchNorm2d (original VGG me BN nahi tha; BN 2015 me aaya — torchvision me alag model vgg16_bn hai), aur chhota head (sirf Linear(512, 10)). Params ≈ 9.2M, FC params sirf ~5k. Result: train 96.68%, test 81.76% — lab ka best test accuracy.

Exam Radar

  • Simonyan & Zisserman, Oxford (Visual Geometry Group), 2014. ILSVRC 2014: classification 2nd (7.3%), localization 1st. Slides: 6.8% top-5, 23.7% top-1.
  • Sirf 3×3 conv (stride 1, pad 1) + 2×2 max-pool (stride 2); channels 64→128→256→512→512; spatial 224→112→56→28→14→7.
  • VGG-16 = 13 conv + 3 FC; blocks 2-2-3-3-3; VGG-19 = 16 conv + 3 FC; blocks 2-2-4-4-4.
  • Params: VGG-16 ≈ 138M (138,357,544), VGG-19 ≈ 144M. FC ≈ 124M (89%), FC1 ≈ 102.8M (74%). Flatten = 7·7·512 = 25,088.
  • Two 3×3 = 5×5 RF (18C² vs 25C²); three 3×3 = 7×7 RF (27C² vs 49C²) + more ReLUs.
  • Drawbacks: memory, slow, huge FC head, no skip connections. Use today: transfer learning / feature extractor.

Galti mat karna

  • VGG me conv layers size nahi ghatate (padding 1, stride 1); size sirf pooling se ghatta hai. (L11 quiz: "Where does the image become spatially smaller? → At the max-pooling layer".)
  • "VGG-16 me 16 conv layers" — galat; 13 conv + 3 FC.
  • Slides ki line "VGG used 19 hidden layers and 144 million parameters" VGG-19 ki baat hai; VGG-16 = 138M.

Dimaag me aise baitha lo

"Teen-teen, do-do, aadha-aadha": 3×3 conv, 2×2 pool, har pool pe size aadha aur channels double. "2-2-3-3-3 + 3 = 16".

24.6 Inception / GoogLeNet (2014) — "kaunsa filter? sab ek saath!"

Kahani

Socho tum cricket team ke captain ho aur pitch ka mood pata nahi. Spinner daalo? Fast bowler? Medium pacer? Ek hi bowler chunna risky hai. Ek smart captain kahega: "Sabko thoda-thoda try karo, jo kaam kare uska zyada use karo."

CNN me bhi yahi dikkat hai. Har layer pe decide karna padta hai: 1×1, 3×3 ya 5×5 filter? Photo me kutta kabhi paas se (bada, poori photo me) dikhta hai, kabhi door (chhota sa kona). Chhota detail chhote filter se pakadta hai, badi shape bade filter se. Google ke Christian Szegedy aur team ne kaha: "Choose hi mat karo. Ek hi input pe 1×1, 3×3, 5×5 conv aur 3×3 max-pool sab parallel chalao, aur output ko channels ke along jod do (concatenate). Network training me khud seekh lega kaunsa raasta kitna kaam ka hai." Is block ka naam: Inception module. Is se bana network: GoogLeNet (LeNet ko salaam — capital "L"). Paper: "Going Deeper with Convolutions", Szegedy et al., 2014 (CVPR 2015). ILSVRC 2014 winner, top-5 6.67%.

Slides (Advanced CNN deck) ki line: Inception ne paradigm badla — "from simply stacking layers deeper to building wider networks that process information at multiple scales simultaneously." Naam "Inception" movie ke "we need to go deeper" meme aur "Network in Network" paper se aaya.

Step 1: Naive Inception module (seedha idea)

Input (maan lo 28×28×192) chaar branches me jaata hai, sab stride 1 + "same" padding ke saath taaki sabka H×W same rahe (warna concatenate nahi ho sakta):

Y = concatchannels( B1(X), B3(X), B5(X), Bpool(X) )   ⇒   Cout = C1 + C3 + C5 + Cpool
  • B = ek branch ka operation; C1, C3, C5 = un branches ke filters; Cpool = pool branch ke channels.
  • Concatenation me channels judte hain (add nahi hote). Shart: sabka H×W same ho; channels alag-alag ho sakte hain. (ResNet me ulta: shape poora same chahiye kyunki wahan addition hota hai.)

Teacher ke L12 page ka chhota example: RGB input 28×28×3; teen conv branches 4-4 filters (1×1 pad 0, 3×3 pad 1, 5×5 pad 2) aur pool branch (stride 1, pad 1) apne 3 channels rakhti hai → concat = 4 + 4 + 4 + 3 = 15 channels: output 28×28×15.

Step 2: Problem — compute ka bill phat jaata hai

Chalo numbers se samjhein (multiplications ginna)

Sawaal: Input 28×28×192. Hume 5×5 conv se 32 output channels chahiye (same padding). Kitne multiplications? Fir pehle 1×1 conv se 16 channels tak "pichkaa" (reduce) karke 5×5 lagaayein to?

  1. Ek output number banane ke liye ek 5×5×192 filter = 5·5·192 = 4,800 multiplications.
  2. Output me kitne numbers? 28·28·32 = 25,088. Isliye direct cost:
    28 · 28 · 32 · 5 · 5 · 192 = 120,422,400 ≈ 120.4 million
  3. Bottleneck raasta, step A — 1×1 conv 192 → 16: har output number = 1·1·192 multiplications:
    28 · 28 · 16 · 192 = 2,408,448 ≈ 2.4 million
  4. Step B — ab 5×5 conv sirf 16 channels pe:
    28 · 28 · 32 · 5 · 5 · 16 = 10,035,200 ≈ 10.0 million
  5. Total: 2,408,448 + 10,035,200 = 12,443,648 ≈ 12.4 million. Bachat: 120,422,400 / 12,443,648 ≈ 9.7× (lagbhag 10 guna sasta).

Jawab: direct ≈ 120.4M vs 1×1 bottleneck ke saath ≈ 12.4M multiplications. Output shape dono me same: 28×28×32.

Naive module ki doosri problem: pool branch input ke saare channels (192) wapas de deti hai. To naive module ka output = 64 + 128 + 32 + 192 = 416 channels — har module ke baad channels badhte jaayenge aur agla module aur mehnga. Hamare example ke naive 3a (1×1→64, 3×3→128, 5×5→32 seedha 192 channels pe) ki cost ≈ 303M multiplications; bottleneck wale asli 3a ki ≈ 128M.

Step 3: Hero — 1×1 convolution

Definition

1×1 convolution: kernel ka spatial size 1×1 hai, par depth = saare input channels (1×1×Cin). Har pixel position pe ye us pixel ke saare channels ka weighted sum leta hai (+ bias, fir ReLU). Matlab har pixel par ek chhota sa fully connected layer across channels — wahi weights har position pe. Isliye "Network in Network" (Lin, Chen & Yan, 2013) se idea aaya.

Chalo numbers se samjhein (teacher ke L12 page se)

Ek position pe 4 input channels ki values: 2, 0, 1, 3. Ek 1×1 filter ke weights: ½, ¼, 0, ½ (bias 0).

  1. ½·2 + ¼·0 + 0·1 + ½·3 = 1 + 0 + 0 + 1.5 = 2.5
  2. Doosra filter apne weights se doosra channel banata hai. 16 filters → 16 output channels. Same filters har position pe lagte hain.

Jawab: output = 2.5. 1×1 conv ne 4 numbers ko 1 me "mix" kar diya — padosi pixels ko nahi dekha.

1×1 conv = har pixel pe channels ka mixer 28×28×192 192 numbers 1×1×192 filter × 16 dot + ReLU 28×28×16 H×W same depth 192→16 params (192+1)·16 = 3,088
1×1 conv ek pixel ke saare channels ka weighted sum leta hai. Spatial size wahi rehta hai, sirf depth badalti hai — isliye ise "bottleneck" ke liye use karte hain.

Teacher ka L12 example (parameters ginna, 64 channels)

Input 28×28×64, chahiye 28×28×32 via 5×5.

  1. Direct: ek filter = 5·5·64 = 1,600 weights + 1 bias = 1,601; 32 filters → 1,601 × 32 = 51,232.
  2. Reduce pehle (1×1, 16 filters): (1·1·64 + 1)·16 = 1,040.
  3. Fir 5×5 on 16 channels: (5·5·16 + 1)·32 = 401·32 = 12,832.
  4. Jod: 1,040 + 12,832 = 13,872.

Jawab: 51,232 → 13,872 params (same output shape). Kyun sasta? Kyunki ab har 5×5 filter 64 ki jagah sirf 16 channels span karta hai. (L12 quiz ka jawab yahi tha.)

Worksheet ka 3×3 branch (weights only, bias nahi): direct 192 → 128 = 9 · 192 · 128 = 221,184. Pehle r = 96 tak reduce: 192·96 + 9·96·128 = 18,432 + 110,592 = 129,024 → 41.7% kam. Par dhyan: bottleneck hamesha sasta nahi! Sasta tabhi jab r(Cin + k²Cout) < k²CinCout. Yahan break-even r = 221,184 / (192 + 1,152) ≈ 164.6; agar r = 192 rakha to 258,048 weights — ulta mehnga. Aur bahut chhota r information bhi kho sakta hai (bottleneck = sankri gali).

Step 4: Asli Inception module (v1, dimension reduction ke saath)

Inception module (3a): 28×28×192 → 28×28×256 Previous layer 28×28×192 1×1 conv, 96reduce (bottleneck) 1×1 conv, 16reduce (bottleneck) 3×3 max-poolstride 1, pad 1 1×1 conv, 64 3×3 conv, 128 5×5 conv, 32 1×1 conv, 32pool projection 28×28×64 28×28×128 28×28×32 28×28×32 Filter concatenation (channels) 64 + 128 + 32 + 32 = 256 → 28×28×256
Yellow = 1×1 convs (reduce / project), orange = asli feature banane wale convs. Har conv ke baad ReLU. Saari branches same 28×28 rakhti hain, isliye concat ho paata hai.

Dhyan do: pool branch me 1×1 conv pool ke baad lagta hai ("pool projection") — taaki pool branch 192 channels wapas na de, sirf 32 de. Aur 3×3 / 5×5 ke pehle 1×1 lagta hai. Asli 3a ke params: (192+1)·64 + (192+1)·96 + (9·96+1)·128 + (192+1)·16 + (25·16+1)·32 + (192+1)·32 = 163,696 (naive module ke 387,296 ke mukable).

GoogLeNet: poora network (Inception v1)

StageKyaOutput
InputRGB224×224×3
Stem7×7 conv 64, stride 2 → 3×3 max-pool s2 → 1×1 conv 64 → 3×3 conv 192 → 3×3 max-pool s2112×112×64 → 56×56×64 → 56×56×192 → 28×28×192
Inception 3a, 3b2 modules28×28×256 → 28×28×480
Max-pool s2—14×14×480
Inception 4a–4e5 modules (4a ke baad aur 4d ke baad auxiliary classifiers)512, 512, 512, 528, 832 channels @14×14
Max-pool s2—7×7×832
Inception 5a, 5b2 modules7×7×832 → 7×7×1024
Global average poolhar 7×7 map ka average1×1×1024
Dropout (40%) → Linear → softmaxek hi FC1000

Module widths (notebook ka table; ratta zaroori nahi, pattern samjho):

Module1×13×3 reduce3×35×5 reduce5×5pool projOutput
3a6496128163232256
3b128128192329664480
4a19296208164864512
4b160112224246464512
4c128128256246464512
4d112144288326464528
4e25616032032128128832
5a25616032032128128832
5b384192384481281281024

Output check (3b): 128 + 192 + 96 + 64 = 480 ✓. Rule: output = 1×1 + 3×3 + 5×5 + pool proj (reduce wale numbers output me nahi judte — wo andar ke hain).

Global Average Pooling (GAP) — bade FC head ki chhutti

GAP:   zc = 1H·W Σi,j Ac[i, j]    →   (H×W×C) se C numbers
  • Ac = channel c ka feature map; zc = us poore map ka ek average number. 0 parameters.

Chalo numbers se samjhein

  1. Teacher ke L12 page me GoogLeNet ka feature map #6 (7×7 = 49 values) ka jod 339.586 tha: z₆ = 339.586 / 49 = 6.930. Aise 1024 channels → 1024 numbers.
  2. Agar VGG ki tarah flatten + FC karte: 7·7·1024 = 50,176 inputs → 1000 classes: (50,176 + 1)·1000 = 50,177,000 params.
  3. GAP ke baad: (1,024 + 1)·1000 = 1,025,000 params. Bachat ≈ 49×.

Jawab: GAP se classifier ~50M se ~1M params pe aa gaya. Fayde: kam params, kam overfitting, aur input size thoda alag ho tab bhi chal jaata hai. (Idea: Network in Network, 2013. ResNet bhi yahi use karta hai.)

PyTorch me: nn.AdaptiveAvgPool2d((1, 1)) — tum output size batate ho (1×1), kernel wo khud nikaal leta hai; input chahe 7×7 ho ya 4×4. Uske baad nn.Flatten() → (N, C). (torch.mean(x, dim=(2, 3)) bhi same kaam karta hai.)

Auxiliary classifiers — beech me "chhote teachers"

22 layers us zamane me bahut gehra tha; darr tha ki vanishing gradient (Chapter 13) se shuruaati layers tak signal nahi pahunchega. Jugaad: beech me (4a aur 4d ke output pe) do chhote classifiers lagao: 5×5 avg-pool (stride 3) → 1×1 conv 128 → FC 1024 → dropout 70% → FC 1000 softmax. Ye bhi same label predict karte hain aur unka loss 0.3 weight ke saath total loss me judta hai:

Ltotal = Lmain + 0.3 · ( Laux1 + Laux2 )
  • Aux losses beech ki layers ko seedha extra gradient dete hain (aur thoda regularization). Inference/test time pe aux classifiers hata diye jaate hain — prediction sirf main classifier se.

PyTorch: InceptionModule class

Naya API: torch.cat(tensors, dim) — list of tensors ko ek dimension ke along jodta hai; baaki saari dimensions same honi chahiye. PyTorch shape (N, C, H, W) hai, isliye channels = dim=1. (Keras me channels last hote hain, isliye notebook me Concatenate(axis=-1) tha.)

PyTorch · Inception module (v1)

import torch
import torch.nn as nn

class InceptionModule(nn.Module):
    def __init__(self, in_ch, c1, r3, c3, r5, c5, pool_proj):
        super().__init__()
        # Branch 1: sirf 1x1
        self.b1 = nn.Sequential(nn.Conv2d(in_ch, c1, kernel_size=1), nn.ReLU(inplace=True))
        # Branch 2: 1x1 reduce -> 3x3 (padding=1 taaki 28x28 bana rahe)
        self.b2 = nn.Sequential(
            nn.Conv2d(in_ch, r3, kernel_size=1), nn.ReLU(inplace=True),
            nn.Conv2d(r3, c3, kernel_size=3, padding=1), nn.ReLU(inplace=True))
        # Branch 3: 1x1 reduce -> 5x5 (padding=2)
        self.b3 = nn.Sequential(
            nn.Conv2d(in_ch, r5, kernel_size=1), nn.ReLU(inplace=True),
            nn.Conv2d(r5, c5, kernel_size=5, padding=2), nn.ReLU(inplace=True))
        # Branch 4: 3x3 maxpool (stride=1, padding=1) -> 1x1 projection
        self.b4 = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_ch, pool_proj, kernel_size=1), nn.ReLU(inplace=True))

    def forward(self, x):
        y1, y2, y3, y4 = self.b1(x), self.b2(x), self.b3(x), self.b4(x)   # sab ko SAME x milta hai
        return torch.cat([y1, y2, y3, y4], dim=1)   # dim=1 = channel axis (N, C, H, W)

inc3a = InceptionModule(192, 64, 96, 128, 16, 32, 32)
x = torch.randn(1, 192, 28, 28)
out = inc3a(x)
print("branch shapes:", [tuple(b(x).shape) for b in [inc3a.b1, inc3a.b2, inc3a.b3, inc3a.b4]])
print("module output:", tuple(out.shape))
print("params:", sum(p.numel() for p in inc3a.parameters()))

Output

branch shapes: [(1, 64, 28, 28), (1, 128, 28, 28), (1, 32, 28, 28), (1, 32, 28, 28)]
module output: (1, 256, 28, 28)
params: 163696

torchvision: models.googlenet(weights=None, aux_logits=True). Train mode me ye ek GoogLeNetOutputs deta hai jisme .logits, .aux_logits1, .aux_logits2 hote hain; eval mode me sirf logits tensor. Loss aise:

PyTorch · GoogLeNet training loss with auxiliary heads

import torch
import torch.nn as nn
from torchvision import models

g = models.googlenet(weights=None, aux_logits=True, init_weights=True)
g.train()
out = g(torch.randn(2, 3, 224, 224))           # train mode: 3 outputs
labels = torch.tensor([3, 7])
ce = nn.CrossEntropyLoss()
loss = ce(out.logits, labels) + 0.3 * (ce(out.aux_logits1, labels) + ce(out.aux_logits2, labels))
print(type(out).__name__, out.logits.shape, out.aux_logits1.shape, out.aux_logits2.shape)
g.eval()
print(type(g(torch.randn(1, 3, 224, 224))).__name__)   # eval: sirf main logits

Output

GoogLeNetOutputs torch.Size([2, 1000]) torch.Size([2, 1000]) torch.Size([2, 1000])
Tensor

Inception ke baad ke versions (v2, v3, v4)

Inception ki kamzoriyan (Advanced CNN deck)

Exam Radar

  • GoogLeNet = Inception v1, Szegedy et al. (Google), 2014, ILSVRC 2014 winner, 6.67% top-5.
  • Module: 1×1, 3×3, 5×5 conv + 3×3 max-pool parallel, outputs concatenated along channels (dim=1 in PyTorch). H×W same (stride 1 + same padding).
  • 1×1 conv: channel reduction (bottleneck), cross-channel mixing, extra ReLU non-linearity, spatial size same. From "Network in Network".
  • Cost example: 28×28×192 → 5×5×32: ≈120.4M mults direct vs ≈12.4M with 1×1 to 16 (≈10× less).
  • 3a: 64 + 128 + 32 + 32 = 256 channels. 22 layers deep, 9 inception modules, ~5–7M params (12× fewer than AlexNet), GAP instead of big FC, dropout 40%.
  • 2 auxiliary classifiers (after 4a, 4d), loss weight 0.3, purpose: fight vanishing gradients + regularize; removed at inference.
  • "VGG = deeper, Inception = wider / multi-scale, ResNet = shortcuts."

Galti mat karna

  • Inception branches ko add nahi karte, concatenate karte hain. Add = ResNet.
  • Module ke output channels me "reduce" wale filters (96, 16) mat jodo — sirf branch ke aakhri outputs (64+128+32+32).
  • Pooling branch ka stride 1 hota hai (size same rakhne ke liye) — ye downsampling ke liye nahi hai.
  • 1×1 conv "kuch nahi karta" — galat! Wo channels mix karta hai, depth badalta hai aur ReLU ke saath non-linearity laata hai.

Dimaag me aise baitha lo

"Sabko bulao, sabki suno, sabko jodo" — 1, 3, 5, pool → concat. Aur "pehle 1×1 se patla karo, phir bada filter chalao" = bottleneck.

24.7 1×1 ka ek aur kamaal: Depthwise Separable Convolution (MobileNet idea)

Kahani

Worksheet ka kitchen example: dhaba me pehle har ingredient alag-alag taiyaar hota hai (pyaaz alag kaata, tamatar alag pees), phir aakhir me sab mila kar dish banti hai. Standard convolution ek hi step me dono kaam karta hai — padosi pixels dekhna (spatial filtering) aur channels milana (channel mixing). Depthwise separable conv ye do kaam alag karta hai, aur bahut sasta pad jaata hai.

Pstandard = k²MN   |   Pseparable = k²M + MN   |   PseparablePstandard = 1N + 1k²
  • M = input channels, N = output channels, k = kernel size; sirf weights (bias/BN chhod ke). MACs (multiply-accumulates) chahiye to dono ko output grid H′·W′ se multiply karo.

Chalo numbers se samjhein (worksheet ke numbers)

  1. k = 3, M = 32, N = 64. Standard: 9 · 32 · 64 = 18,432.
  2. Depthwise: 9 · 32 = 288; pointwise: 32 · 64 = 2,048; total 2,336.
  3. Ratio: 2,336 / 18,432 = 0.1267 = 1/64 + 1/9 → ~7.89× kam. 28×28 output pe MACs: 14,450,688 vs 1,831,424.
  4. Worksheet ka "check yourself": 3×3, 64 → 128: 9·64·128 = 73,728 vs 9·64 + 64·128 = 576 + 8,192 = 8,768 → ~8.41× kam.

Jawab: depthwise separable ≈ 8–9× sasta (3×3 kernels ke liye ratio ≈ 1/9 jab N bada ho). Trade-off: har filter "D[u,v,m]·P[m,n]" jaisa factorized form hi ban sakta hai — expressivity kam; accuracy aur device pe asli speed measure karni padti hai.

Worksheet ka chhota numeric: do 3×3 channels A = [1..9] aur B = [9..1], har channel pe 2×2 all-ones depthwise filter (stride 1, valid) → A′ top-left = 1+2+4+5 = 12, B′ top-left = 9+8+6+5 = 28; pointwise weights [1, −1] → 1·12 + (−1)·28 = −16. Channels pehli baar pointwise step me mile.

PyTorch me depthwise = nn.Conv2d(M, M, k, groups=M). groups argument channels ko groups me baant deta hai; groups=M → har channel akela (AlexNet ka 2-GPU split = groups=2 tha).

PyTorch · standard vs depthwise separable

import torch.nn as nn
standard = nn.Conv2d(32, 64, kernel_size=3, padding=1, bias=False)
depthwise = nn.Conv2d(32, 32, kernel_size=3, padding=1, groups=32, bias=False)  # har channel ka apna 3x3
pointwise = nn.Conv2d(32, 64, kernel_size=1, bias=False)                        # 1x1: channels mix
count = lambda m: sum(p.numel() for p in m.parameters())
print("standard :", count(standard))
print("depthwise:", count(depthwise), "| pointwise:", count(pointwise),
      "| total:", count(depthwise) + count(pointwise))
print("ratio    :", round((count(depthwise) + count(pointwise)) / count(standard), 4))

Output

standard : 18432
depthwise: 288 | pointwise: 2048 | total: 2336
ratio    : 0.1267

Exam Radar

  • Depthwise separable = depthwise (k²M) + pointwise 1×1 (MN); ratio = 1/N + 1/k². MobileNet (Howard et al., 2017) ne popular kiya; Xception bhi isi pe.
  • Channels pointwise step me mix hote hain, depthwise me nahi. Depthwise separable ≠ spatially separable (3×1 + 1×3).

24.8 Badi comparison table + sabke parameters ek saath ginna

LeNet-5AlexNetVGG-16GoogLeNet (Inception v1)ResNet (Ch 25)
Saal19982012201420142015
AuthorsLeCun, Bottou, Bengio, HaffnerKrizhevsky, Sutskever, HintonSimonyan, Zisserman (Oxford VGG)Szegedy et al. (Google)He, Zhang, Ren, Sun (Microsoft)
Depth (weight layers)5 (2 conv + 3 FC*) — paper: 7 layers8 (5 conv + 3 FC)16 (13 conv + 3 FC); VGG-19: 1922 (9 inception modules)18 / 34 / 50 / 101 / 152
Params≈ 60k (61,706 modern)≈ 60–62M≈ 138M (VGG-19 ≈ 144M)≈ 5–7M11.7M (18) … 25.6M (50) … 60.2M (152)
Input32×32×1227×227×3 (paper: 224)224×224×3224×224×3224×224×3
Kernels5×511×11, 5×5, 3×3sirf 3×31×1, 3×3, 5×5 parallel3×3 (+1×1 in bottleneck), 7×7 stem
Key ideaconv + pooling + weight sharing, end-to-endscale up: ReLU, dropout, augmentation, GPUs, LRNdepth via stacked small 3×3 filtersmulti-scale parallel branches, 1×1 bottleneck, GAP, aux classifiersresidual / skip connections, BN, bottleneck
Activationtanh / sigmoidReLUReLUReLUReLU
Poolingaverage (subsampling)max 3×3/2 (overlapping)max 2×2/2max + global averagemax (stem) + global average
Classifier headFC 120-84-10FC 4096-4096-1000FC 4096-4096-1000GAP + 1 FCGAP + 1 FC
Normalization / reg.—LRN, dropout 0.5dropout 0.5 (no BN originally)dropout 0.4, aux losses (later versions BN)BN after every conv, no dropout
ILSVRC top-5— (MNIST 0.95% error)15.3% (slides 16.4%)7.3% (later 6.8%)6.67% (winner 2014)3.57% (winner 2015)

Lab 15 (CIFAR-10, 10 epochs, Adam lr 0.001) ka scoreboard — teacher ka trend "params FC se conv me shift hue, depth badhi, test accuracy badhi":

ModelTotal paramsFC paramsTrain accTest acc
Baseline CNN (2 conv + big FC)1.97M1.96M (99%)97.97%72.23%
AlexNet (CIFAR version)35.9M33.6M (94%)88.87%77.34%
VGG-style + BN9.2M≈0.005M96.68%81.76%
ResNet-18 (CIFAR version)11.2M≈0.005M93.91%80.61%

PyTorch · torchvision models ke params ek loop me

import torchvision.models as models
for name in ["alexnet", "vgg16", "vgg19", "googlenet", "resnet18", "resnet50"]:
    kwargs = {"aux_logits": False, "init_weights": False} if name == "googlenet" else {}
    m = getattr(models, name)(weights=None, **kwargs)       # weights=None: random init, download nahi
    n = sum(p.numel() for p in m.parameters())
    print(f"{name:10s} {n:>12,}")

Output

alexnet      61,100,840
vgg16       138,357,544
vgg19       143,667,240
googlenet     6,624,904
resnet18     11,689,512
resnet50     25,557,032

getattr(models, name) = string naam se function nikaalna (models.vgg16 jaisa). {n:>12,} = 12 jagah me right-align aur hazaar pe comma.

Dimaag me aise baitha lo

"LeNet ne chalna sikhaya, AlexNet ne daudna, VGG ne ooncha kiya, Inception ne chauda kiya, ResNet ne shortcut diya." Params: 60k → 60M → 138M → 6M → 25M (ResNet-50).

Chapter 25

Deep CNNs — Shattered Gradients, Residual Connections, ResNet

Degradation problem · shattered gradients · y = F(x) + x · gradient highway · ensemble of paths · exploding variance + BatchNorm · basic & bottleneck blocks · projection shortcut · ResNet-18/34/50/101/152 · PyTorch residual blocks

25.1 Kahani: "Chinese whispers" (telephone game) aur depth ki deewar

Kahani

School me ek khel khela hai? 30 bachche line me baithe hain. Pehla bachcha doosre ke kaan me phusphusata hai: "Kal maths ka test hai, chapter 5 padhna." Doosra teesre ko, teesra chauthe ko... 30ve bachche tak pahunchte-pahunchte message ban jaata hai: "Kal match hai, paanch baje aana." Har bachche ne thoda sa badla, aur 30 baar ka thoda-thoda milke poora message barbaad. Line jitni lambi, message utna kharaab — aur ulta, teacher ka "feedback" peeche bhejo to wo bhi raste me bigad jaata hai.

Ab ek jugaad: har 2 bachchon ke baad ek shortcut — asli message ki ek photocopy seedha aage pass hoti hai, aur bachchon ko sirf itna bolna hai ki "isme kya sudhaar/badlav jodna hai." Ab 30 kya, 150 bachche bhi ho to original message zinda pahunchta hai. Bas, yahi ResNet hai: har block ke saath ek identity shortcut, aur block sirf "badlav" (residual) seekhta hai.

Chapter 24 ka pattern yaad karo: AlexNet (8) → VGG (19) → GoogLeNet (22): depth badhi, accuracy badhi. To kisi ne poocha: "Agar 20 layers achhi hain, to 56 ya 100 layers aur achhi hongi?" Jawab ne sabko chauka diya. Is chapter me teen sawaal hain: (1) plain deep network kyun fail hota hai (degradation + shattered gradients), (2) residual connection ise kaise theek karta hai (forward me information, backward me gradient), (3) ResNet architecture aur uska PyTorch code.

25.2 Degradation problem — zyada layers, zyada training error!

He, Zhang, Ren, Sun (Microsoft Research, 2015), paper "Deep Residual Learning for Image Recognition" (CVPR 2016 best paper). Unhone CIFAR-10 pe do "plain" (seedhe stacked, bina shortcut) networks train kiye: 20 layers aur 56 layers. Result: 56-layer network ka training error bhi zyada aur test error bhi zyada. ImageNet pe bhi 34-layer plain net 18-layer plain net se kharaab tha.

Training error (%) Test error (%) iterations →iterations → 20-layer 56-layer 20-layer 56-layer training error bhi zyada → overfitting NAHI (schematic, He et al. 2015 Fig. 1 jaisa)
Degradation: 56-layer plain net ki dono curves (red) 20-layer (ink) ke upar. Beech ka chhota jhatka = learning rate ÷10 hone pe gira hua error.

Ye overfitting kyun nahi hai?

Overfitting (Chapter 18) me model training data ratta maar leta hai: training error kam, test error zyada. Yahan to deep model training data pe hi kharaab hai — matlab wo training set ko fit hi nahi kar pa raha. Ye optimization difficulty hai: optimizer (SGD) itne gehre network ke achhe weights dhoondh hi nahi pa raha. Slides (UDL) ki line: "the decrease is present for both the training set and the test set, which implies that the problem is training deeper networks rather than the inability of deeper networks to generalize."

"Construction" wala tark — deeper model kam se kam utna achha ho sakta hai

Socho 20-layer network ne 8% error laaya. Ab 56-layer banao: pehli 20 layers bilkul wahi copy karo, aur baaki 36 layers ko identity (input = output, kuch mat karo) bana do. Ye 56-layer network exactly 20-layer jaisa hi kaam karega → error 8%. Matlab ek aisa solution exist karta hai jisme deeper network shallow se buri nahi. Phir bhi SGD use dhoondh nahi paata. Kyun? Kyunki stacked non-linear layers (conv + BN + ReLU) ke liye exact identity mapping seekhna mushkil hai — weights ko ekdum sahi values pe lana padega. Agar layers ko "identity" by default mil jaaye to? Yahi residual learning ka seed hai.

Teacher ka L12 experiment (MNIST, Adam lr 0.001, 18 epochs, no BN)

ModelParametersFinal training loss
Shallow plain · 4 layers (stem + 1 block of two 3×3 convs + linear)16,9380.150
Deep plain · 26 layers (12 blocks)29,7862.300
Deep residual · 26 layers (same 12 blocks + shortcuts)29,7860.0068 (test acc 95.3%)
  1. Params check: stem (3·3·1+1)·8 = 80; har block 2·(3·3·8+1)·8 = 1,168; classifier (14·14·8+1)·10 = 15,690. Shallow: 80 + 1,168 + 15,690 = 16,938. Deep: 80 + 12·1,168 + 15,690 = 29,786 ✓.
  2. 2.300 kitna bura hai? 10 classes pe agar model har class ko 10% probability de (yaani kuch nahi seekha), to cross-entropy = −ln(0.1) = ln 10 ≈ 2.3026. Deep plain ka loss 2.2998 ≈ ln 10 → wo random guess pe atka hai. (L12 page me deep plain net "7" ko sirf 11.1% de raha tha — sab classes ~10%.)
  3. Same params, same data, same init — bas shortcuts jode — loss 2.300 se 0.0068! Farak sirf wiring ka hai.

Jawab: zyada capacity (29,786 > 16,938) ≠ behtar training. Problem optimization ki hai, aur residual connection usko theek karta hai.

Exam Radar

  • Degradation problem: depth badhane pe accuracy saturate hoke girti hai; deeper plain net ka training error zyada (He et al. 2015: 56-layer vs 20-layer on CIFAR-10).
  • Cause: optimization difficulty, not overfitting (overfitting me training error kam hota). Vanishing gradient bhi poori wajah nahi — BN ke saath bhi degradation dikha.

Galti mat karna

  • "Deep network ka test error zyada hai to overfitting hai" — pehle training error dekho. Training bhi kharaab = underfitting/optimization problem.

25.3 Shattered gradients — gradient zinda hai, par "static" ban gaya

Kahani

Worksheet ka radio example: tum pahaad pe raasta bhatak gaye ho aur radio pe koi direction bata raha hai. Do tarah ki dikkat ho sakti hai: (1) awaaz bahut dheemi hai (whisper) — ye vanishing gradient jaisa hai: magnitude problem. (2) awaaz tez hai par sirf khar-khar static hai — har second alag disha bolta hai. Awaaz ka volume badhane se static theek nahi hoga! Ye shattered gradient hai: coherence (correlation) problem.

Definition

Shattered gradients problem (Balduzzi et al., 2017, "The Shattered Gradients Problem: If resnets are the answer, then what is the question?"; UDL book ch. 11): deep plain network me output ka input ke saath gradient, input me chhote se badlav pe bhi bilkul alag ho jaata hai. Paas-paas ke inputs ke gradients ka correlation depth ke saath tezi se zero ki taraf girta hai — gradient white noise jaisa dikhne lagta hai. Gradient ka size (norm) theek ho sakta hai, par uski disha me koi useful information nahi.

g(x) = ∂f(x)∂x     ρ = Cov( g(x), g(x + δ) )σg(x) · σg(x+δ)
  • f = network ka output; x = input; δ = input me chhota sa khisakna (displacement).
  • g(x) = gradient (input ke wrt): "input thoda badlo to output kitna aur kis disha me badlega".
  • ρ = correlation: +1 = dono jagah gradient ek jaisa (smooth), 0 = koi rishta nahi (shattered), −1 = ulta. Cov = covariance, σ = standard deviation. Ye statistic bahut saare paired samples par nikalte hain, ek pair par nahi.

Chalo numbers se samjhein (worksheet ka example)

Gradient samples A = [1, −1, 1, −1] aur B = [1, 1, −1, −1].

  1. RMS (size): √((1+1+1+1)/4) = 1 dono ka. Size bilkul healthy — vanishing nahi!
  2. Mean dono ka 0. Covariance: (1·1 + (−1)·1 + 1·(−1) + (−1)·(−1))/4 = (1 − 1 − 1 + 1)/4 = 0.
  3. Correlation ρ = 0 / (1 · 1) = 0.

Jawab: size same (RMS 1) par correlation 0. Sirf gradient norm check karne se ye bimaari nahi pakdi jaati. Aur har gradient ko 20 se multiply karna bhi kuch nahi badlega — correlation scale se nahi badalta ("static ko loud karne se direction nahi milti").

Shallow (1 hidden layer) Deep plain (24 layers) Deep residual (24 blocks) input x (−2 … 2) input x (−2 … 2) input x (−2 … 2) dy/dx corr(Δx=0.05) ≈ 0.93 corr(Δx=0.05) ≈ 0.65 corr(Δx=0.05) ≈ 0.99 asli torch run: width 100, He-init ReLU nets, autograd gradient (normalized)
Ek scalar input x ke liye gradient ∂y/∂x. Shallow net me gradient dheere-dheere badalta hai; 24-layer plain net me zig-zag (shattered); 24 residual blocks wale net me phir se smooth. Neeche likha correlation = Δx = 0.05 door ke gradients ka autocorrelation.

Ye gradient descent ke liye zeher kyun hai?

Gradient descent (Chapter 14) maan ke chalta hai ki loss surface smooth hai: jahan abhi khade ho wahan ka gradient thoda aage tak bhi sahi disha batayega. Slides ki line: "Any reasonable choice of step size may move to a place with a completely different and unrelated gradient; the loss surface looks like an enormous range of tiny mountains rather than a single smooth structure that is easy to descend." Jab har kadam ke baad gradient ka koi rishta pichhle se nahi, to optimizer bas ghoomta rehta hai.

Kyun hota hai? (plain bhasha me)

Plain network ka input-gradient = har layer ke Jacobian (Chapter 12 wala chain rule) ka product. ReLU network me har layer ke Jacobian me ek "gate" hota hai — kaunse neurons ON hain. Input thoda sa badla to kuch neurons ON se OFF ho jaate hain; har layer me thode-thode switch milke deep network me poora raasta (activation pattern) badal deta hai → gradient bilkul naya. Depth jitni zyada, switches utne zyada.

Vanishing / exploding

Magnitude ki problem: gradient bahut chhota (whisper) ya bahut bada. Fix: ReLU, achha init (He/Xavier), BN, gradient clipping.

Shattered

Correlation / coherence ki problem: size theek, par nearby inputs ke gradients unrelated (static). Fix: skip/residual connections (+ BN). Ye dono saath-saath bhi ho sakte hain.

Exam Radar

  • Shattered gradient = correlation between gradients (of nearby inputs) drops to zero → loss surface "chaotic white noise" jaisa (Advanced deck ka quiz answer).
  • Shallow net: gradient input ke saath dheere badalta hai; deep plain net: chhota input change → bilkul alag gradient. Measure: autocorrelation function of the gradient.
  • Plain: ~2−L decay; ResNet: ~1/√L. Paper: Balduzzi et al., 2017.

Galti mat karna

  • Shattered ≠ vanishing. Gradient norm healthy ho sakta hai aur phir bhi shattered.
  • "Learning rate badha do / gradient ko scale kar do" se shattering theek nahi hoti — scaling correlation nahi badalti.

Dimaag me aise baitha lo

Vanishing = dheemi awaaz. Shattered = tez static. Dono ka ilaaj ek shortcut wire.

25.4 Residual connection: y = F(x) + x

Kahani

Tumne ek achha essay paragraph likha hai. Teacher bolti hain "thoda improve karo." Kya tum poora paragraph mita kar zero se dobara likhoge? Nahi! Tum original rakhoge aur usme edits jodoge. Agar paragraph pehle se perfect hai to edit = "kuch nahi". Plain layer ko har baar poora paragraph naye sire se likhna padta hai; residual layer ko sirf edit likhna hai.

H(x) = F(x) + x    ⇔    F(x) = H(x) − x
  • x = block ka input (pehle se bana hua representation).
  • H(x) = jo mapping hume asal me chahiye (block ka desired output). Teacher ke notebook me yahi notation hai; kai jagah output ko y likhte hain: y = F(x) + x.
  • F(x) = residual = "kitna badlav jodna hai" — ise block ke andar ki normal conv layers (conv → BN → ReLU → conv → BN) seekhti hain. Isliye naam residual ("bacha hua farak").
  • "+ x" = identity shortcut / skip connection: input ko bina chhue aage pahuncha do. Isme koi parameter nahi. Addition element-wise hota hai, isliye F(x) aur x ka shape (C, H, W) bilkul same hona chahiye.

Slides (UDL 11.2) ki definition: "Residual or skip connections are branches in the computational path, whereby the input to each network layer f[•] is added back to the output." Isse har block representation me ek additive change karta hai.

Identity seekhna mushkil, zero seekhna aasaan — kyun?

Chalo numbers se samjhein

  1. Teacher ke notebook se: x = [5, 2], block ne seekha F(x) = [1, −0.5]. y = [5, 2] + [1, −0.5] = [6, 1.5]. Conv path ne sirf badlav [1, −0.5] diya; [5, 2] shortcut se aaya.
  2. Worksheet se: humare paas [2, 1, 0, 3] hai, chahiye [2.4, 1.2, 0, 3.6]. Plain layer ko poora mapping seekhna padega. Residual ko sirf F(x) = 0.2x = [0.4, 0.2, 0, 0.6] seekhna hai: [2, 1, 0, 3] + [0.4, 0.2, 0, 0.6] = [2.4, 1.2, 0, 3.6] ✓.
  3. ReLU addition ke baad lagta hai (original ResNet): x = [2, −1], F(x) = [0.5, 0.2] → sum = [2.5, −0.8] → ReLU → [2.5, 0].
  4. Agar F(x) = 0 → y = x (ReLU ke baad bhi non-negative features waise hi). L12 page ka quiz: "If the residual F(x) is zero, what reaches the addition output? → The input x."

Jawab: [6, 1.5]; [2.4, 1.2, 0, 3.6]; [2.5, 0]. Residual block = "input + correction".

ResNet: element-wise ADD

56×56×64 + 56×56×64 → 56×56×64. Shape nahi badhta. Shapes exactly match hone chahiye. Shortcut ke liye 0 params (identity).

Inception / DenseNet: CONCATENATE

56×56×64 ⊕ 56×56×64 → 56×56×128. Channels judte hain. Sirf H×W match chahiye. Standard ResNet shortcut ye nahi karta.

25.5 Gradient highway: "+1" jo gradient ko marne nahi deta

Ab backward pass dekho (chain rule, Chapter 12). y = F(x) + x ko x se differentiate karo:

∂y∂x = ∂F(x)∂x + 1    ⇒    ∂L∂x = ∂L∂y · ( ∂F∂x + 1 )
  • L = loss. ∂L/∂y = upar se aaya gradient. ∂F/∂x = learned branch ka derivative (chhota, bada, kuch bhi).
  • +1 = shortcut ka derivative (∂x/∂x = 1). Iska matlab upar ka gradient kam se kam ek baar bina ghate neeche pahunchta hai — "gradient highway".
  • Vector/matrix form (worksheet): Jy = I + JF, aur ∇xL = (I + JF)T ∇yL. I = identity matrix, J = Jacobian.

Bahut saare blocks ho to? He et al. (2016) ne dikhaya: agar har block xl+1 = xl + F(xl) ho, to kisi bhi deeper block L tak xL = xl + Σi=lL−1 F(xi), aur

∂L∂xl = ∂L∂xL · ( 1 + ∂∂xl Σi=lL−1 F(xi) )
  • Pehla term (1 wala) deep layer ka gradient seedha shallow layer tak le jaata hai — kisi weight matrix se multiply hue bina. Plain net me ye term hota hi nahi; wahan sirf weights ke Jacobians ka lamba product hota hai.

Chalo numbers se samjhein

  1. Plain chain (teacher notebook): 4 layers, har ek ka local derivative 0.1: 0.1 × 0.1 × 0.1 × 0.1 = 0.0001. Gradient lagbhag mar gaya.
  2. Residual chain: har block ka derivative (1 + 0.1): 1.1 × 1.1 × 1.1 × 1.1 = 1.4641. Gradient zinda!
  3. Expand karke dekho (2 blocks): (1 + a)(1 + b) = 1 + a + b + ab. "1" = seedha shortcut raasta, a aur b = ek-ek block se guzarne wale raaste, ab = dono blocks wala raasta. Plain net me sirf ab wala term hota (= 0.01 for 0.1·0.1).
  4. Worksheet ka scalar: F(x) = 0.2x, x = 2. Plain y = 0.2x = 0.4, dy/dx = 0.2. Residual y = x + 0.2x = 2.4, dy/dx = 1 + 0.2 = 1.2.
  5. Chetavni: agar F(x) = −x seekh le to dy/dx = 1 − 1 = 0 — dono raaste cancel. Shortcut guarantee nahi deta, bas gradient ke zinda rehne ke chances bahut badha deta hai. (ReLU gates bhi derivative me aate hain.)

Jawab: 0.0001 vs 1.4641 — product of small numbers mar jaata hai, product of (1 + small) zinda rehta hai.

PyTorch · autograd se "+1" dekhna

import torch

x = torch.tensor(2.0, requires_grad=True)
a = 0.2
y_plain = a * x               # plain layer: y = F(x) = a*x
y_res = x + a * x             # residual: y = x + F(x)
g_plain, = torch.autograd.grad(y_plain, x)   # dy/dx autograd se (.backward() ki jagah, seedha value deta hai)
g_res, = torch.autograd.grad(y_res, x)
print(f"plain   : y = {y_plain.item():.4f}, dy/dx = {g_plain.item():.4f}")
print(f"residual: y = {y_res.item():.4f}, dy/dx = {g_res.item():.4f}")

d = 0.1                        # 4 layers, har ek ka local derivative 0.1
print(f"plain chain    (0.1)^4 = {d**4:.4f}")
print(f"residual chain (1.1)^4 = {(1 + d)**4:.4f}")

Output

plain   : y = 0.4000, dy/dx = 0.2000
residual: y = 2.4000, dy/dx = 1.2000
plain chain    (0.1)^4 = 0.0001
residual chain (1.1)^4 = 1.4641

Experiment: deep plain vs residual — pehli layer tak kitna gradient pahunchta hai?

Ek MLP banate hain jisme depth hidden blocks hain (Linear → [BN] → ReLU). Plain me h = block(h), residual me h = h + block(h). PyTorch default init, random data, ek backward pass, aur pehli layer ke weight-gradient ka norm naapte hain. Naya API: nn.ModuleList — layers ki Python list jaisa, par PyTorch unke parameters register karta hai (plain list me rakhoge to model.parameters() unhe nahi dekhega!). nn.Identity() — "kuch mat karo" layer (input hi output). nn.BatchNorm1d(width) — features ke liye BN (Chapter 20).

PyTorch · first-layer gradient norm experiment

import torch
import torch.nn as nn

class DeepNet(nn.Module):
    def __init__(self, depth, residual=False, use_bn=False, width=64):
        super().__init__()
        self.residual = residual
        self.inp = nn.Linear(20, width)                       # pehli layer: isi ka gradient napenge
        self.blocks = nn.ModuleList([
            nn.Sequential(nn.Linear(width, width),
                          nn.BatchNorm1d(width) if use_bn else nn.Identity(),
                          nn.ReLU())
            for _ in range(depth)])
        self.out = nn.Linear(width, 1)

    def forward(self, x):
        h = torch.relu(self.inp(x))
        for block in self.blocks:
            h = h + block(h) if self.residual else block(h)   # residual: x + F(x); plain: F(x)
        return self.out(h)

torch.manual_seed(42)
x = torch.randn(256, 20); y = torch.randn(256, 1)            # random regression data
print(f"{'depth':>5} | {'plain':>9} | {'residual':>9} | {'plain+BN':>9} | {'resid+BN':>9}")
for depth in [5, 20, 50, 100]:
    row = []
    for residual, use_bn in [(False, False), (True, False), (False, True), (True, True)]:
        torch.manual_seed(0)                                   # same init har model ke liye
        model = DeepNet(depth, residual, use_bn)
        loss = nn.MSELoss()(model(x), y)
        loss.backward()
        row.append(model.inp.weight.grad.norm().item())        # first-layer gradient ka size
    print(f"{depth:5d} | " + " | ".join(f"{v:9.2e}" for v in row))

Output

depth |     plain |  residual |  plain+BN |  resid+BN
    5 |  3.02e-03 |  9.06e-01 |  1.13e+00 |  2.53e+00
   20 |  3.52e-09 |  1.72e+02 |  1.99e+01 |  1.24e+01
   50 |  1.28e-20 |  7.69e+07 |  5.22e+03 |  2.73e+01
  100 |  0.00e+00 |  1.24e+18 |  3.50e+07 |  4.42e+01

Exam Radar

  • Residual block: y = F(x) + x; F = residual mapping (learned), x = identity shortcut (0 params); element-wise addition, same shape required.
  • ∂y/∂x = ∂F/∂x + 1 → gradient ko direct path milta hai; vanishing gradient kam.
  • Residual learning: F(x) → 0 seekhna, identity mapping seekhne se aasaan. Extra layers "at worst" identity ban sakti hain.

Galti mat karna

  • Skip connection ka output x + F(x) hai, x * F(x) ya concat nahi.
  • "Shortcut se gradient kabhi vanish/explode nahi hota" — galat; ye guarantee nahi, sirf bahut madad. (F = −x cancellation; bina BN variance explode.)

25.6 Ensemble-of-paths view: ResNet = bahut saare chhote networks ka jod

3 residual blocks ko "khol" ke (unravel) dekho: x1 = x0 + f1(x0), x2 = x1 + f2(x1), x3 = x2 + f3(x2). Har block pe signal ke paas do options hain: block ke andar se jao ya shortcut se nikal jao. n blocks → 2n raaste (paths). 3 blocks → 8 paths; slides ka figure (UDL Fig 11.4): 4 blocks → 16 paths; 54 blocks → 254 ≈ 1.8 × 1016 paths!

3 blocks = 2³ = 8 raaste (input se output tak) x₀ y f₁ + f₂ + f₃ + sabse chhota raasta: sirf shortcuts (identity) — length 0 y = x₀ + f₁ + f₂(·) + f₃(·) ... unravel karo to 8 terms: har block "andar ya bahar" n blocks → 2ⁿ paths, lengths 0 se n tak (zyaadatar beech ki length ke)
Har residual block ek "do-raasta chauraha" hai. Isliye ResNet ek gehra network kam, aur alag-alag gehraai ke 2ⁿ chhote networks ka ensemble zyada hai.

25.7 Exploding variance problem — aur BatchNorm kyun "enabler" hai

Kahani

Har din tum piggy bank me jitne paise hain utne hi aur daal do (double). 10 din me 210 = 1024 guna! Residual network ke forward pass me variance ke saath yahi hota hai.

He initialization (Chapter 21) ReLU layer ke baad variance ko same rakhta hai. Ek residual block me: shortcut se variance σ² aaya, branch F se bhi (He init se) lagbhag σ², aur dono (uncorrelated) jode to Var(x + F(x)) ≈ σ² + σ² = 2σ². Har block pe double → K blocks ke baad 2K guna — exponential growth. (Upar ke experiment me "residual bina BN" ka 1018 yahi tha.)

Chalo numbers se samjhein

  1. Bina kisi fix ke, K = 10 blocks, shuru ka variance 1: 210 = 1024. K = 50: 250 ≈ 1.1 × 1015.
  2. Fix 1: har block ke baad 1/√2 se scale karo: variance 2σ² × (1/√2)² = σ² — constant.
  3. Fix 2 (BatchNorm): block ke andar BN branch ke input/output ko unit variance pe le aata hai (γ = 1, δ = 0 init). To har block sirf ~1 variance jodta hai: 1 → 2 → 3 → … → K + 1. K = 10 pe 11 (1024 ki jagah) — linear growth.

Jawab: bina BN 2K (exponential), BN ke saath ~K + 1 (linear). Side effect: shuru me har block ka contribution total ke saamne chhota → deep layers lagbhag identity ki tarah behave karti hain — jo training ke liye achha hai.

Advanced deck ka quiz: "Why is Batch Normalization considered an enabler for deep ResNets? → It prevents the exponential growth of variance that occurs when adding the input back to the output in residual blocks. Without BN, variance doubles at each layer, growing 2K." BN ka formula (slides): batch ke mean/variance se standardize karo, phir γ se scale aur δ (ya β) se shift — dono learnable (Chapter 20).

Dimaag me aise baitha lo

"Shortcut gradient bachata hai, BN variance bachata hai" — ResNet ko dono chahiye.

25.8 Basic residual block (ResNet-18/34) — PyTorch me

Ab theory ko asli block me badlo. Basic block (He et al. 2015) ka main path: conv 3×3 → BN → ReLU → conv 3×3 → BN → (+ shortcut) → ReLU. Shortcut identity hai jab shapes same hon.

Identity shortcut Projection shortcut x: 56×56×64 conv 3×3, 64, s1 BN → ReLU conv 3×3, 64, s1 BN + x identity 0 params ReLU out: 56×56×64 x: 56×56×64 conv 3×3, 128, s2 BN → ReLU conv 3×3, 128, s1 BN + 1×1, 128stride 2+ BN 8,192 wts ReLU out: 28×28×128
Left: shapes same → shortcut seedha x. Right: stage ki shuruaat me main path size aadha + channels double karta hai, isliye shortcut ko bhi 1×1 conv (stride 2) + BN se 28×28×128 banana padta hai.

Lab 14 (SVHN) ka exact style: main path ek nn.Sequential, shortcut nn.Identity() ya projection, aur forward me activation(main(x) + shortcut(x)).

PyTorch · ResidualBlock (Lab 14 SVHN solution style)

import torch
import torch.nn as nn

class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()
        self.main = nn.Sequential(                                   # F(x): residual branch
            nn.Conv2d(in_channels, out_channels, 3, stride=stride, padding=1, bias=False),
            nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True),
            nn.Conv2d(out_channels, out_channels, 3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels))                            # yahan ReLU NAHI
        self.shortcut = nn.Identity()                                # shapes same: x as-it-is
        if stride != 1 or in_channels != out_channels:               # shape badla: projection
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, 1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels))
        self.activation = nn.ReLU(inplace=True)

    def forward(self, x):
        return self.activation(self.main(x) + self.shortcut(x))      # ReLU(F(x) + x)

torch.manual_seed(0)
identity_block = ResidualBlock(16, 16, stride=1)
projection_block = ResidualBlock(16, 32, stride=2)
x = torch.randn(2, 16, 32, 32, requires_grad=True)
print("identity  :", tuple(identity_block(x).shape), "| shortcut =", identity_block.shortcut.__class__.__name__)
print("projection:", tuple(projection_block(x).shape), "| shortcut =", projection_block.shortcut[0])
count = lambda m: sum(p.numel() for p in m.parameters())
print("params identity block (16->16):", count(identity_block))
print("params projection block (16->32, s=2):", count(projection_block))
print("params basic block 64->64:", count(ResidualBlock(64, 64)))
(identity_block(x).mean() + projection_block(x).mean()).backward()
print("x.grad mila?", x.grad is not None)

Output

identity  : (2, 16, 32, 32) | shortcut = Identity
projection: (2, 32, 16, 16) | shortcut = Conv2d(16, 32, kernel_size=(1, 1), stride=(2, 2), bias=False)
params identity block (16->16): 4672
params projection block (16->32, s=2): 14528
params basic block 64->64: 73984
x.grad mila? True

Hisaab check (16→32, stride 2): conv1 9·16·32 = 4,608 + conv2 9·32·32 = 9,216 + 2 BN (32·2 each) = 128 + shortcut 1×1 16·32 = 512 + BN 64 → 4,608 + 9,216 + 128 + 512 + 64 = 14,528 ✓.

Lab 15 wala style (same kaam, alag likhawat) bhi pehchano: self.conv1, self.bn1, self.conv2, self.bn2 alag-alag, self.shortcut = nn.Sequential() (khaali Sequential = identity), aur forward me out = self.relu(self.bn1(self.conv1(x))), out = self.bn2(self.conv2(out)), out = out + self.shortcut(x), out = self.relu(out).

Galti mat karna (coding)

  • out += x ko nn.ReLU(inplace=True) ke saath mix karne se kabhi-kabhi autograd "modified by an inplace operation" error deta hai. Safe: out = out + self.shortcut(x).
  • Stride sirf pehle conv me (aur shortcut me); doosra conv stride 1. Dono me stride 2 diya to size 4 guna chhota ho jaayega aur add fail.
  • Shortcut condition yaad rakho: stride != 1 or in_channels != out_channels.

25.9 Shape mismatch: projection shortcut (Option A vs B vs C)

Stage badalte waqt main path size aadha (stride 2) aur channels double karta hai: 56×56×64 → 28×28×128. Ab x (56×56×64) ko 28×28×128 me add nahi kar sakte — height, width, channels teeno alag. Teen options (He et al. 2015):

OptionKaiseParamsNote
A — zero-padding identityShortcut me stride 2 se subsample (har doosra pixel), aur extra channels zeros se bhar do (64 asli + 64 zero)0Parameter-free; CIFAR ResNets me use hua.
B — projection jahan zarooriSirf dimension badalne wale shortcuts me 1×1 conv, stride 2 (+ BN); baaki sab identitythodeStandard choice (torchvision me downsample).
C — sab projectionHar shortcut 1×1 convzyadaB se marginally behtar, par extra params/memory — faayda nahi maana gaya.

Chalo numbers se samjhein

  1. 1×1 conv, stride 2, padding 0 on 56: ⌊(56 − 1 + 0)/2⌋ + 1 = ⌊27.5⌋ + 1 = 28. 128 filters → 28×28×128 ✓.
  2. Weights: 1·1·64·128 = 8,192 (bias/BN chhod ke) — worksheet ka answer.
  3. Main path ka pehla conv 3×3 stride 2 padding 1: ⌊(56 − 3 + 2)/2⌋ + 1 = ⌊27.5⌋ + 1 = 28 ✓. Dono raaste 28×28×128 → ab add ho sakta hai.

Jawab: projection shortcut = 1×1 conv (128 filters, stride 2) + BN; y = F(x) + P(x), aur ab shortcut ka Jacobian identity nahi rehta (JP).

Lab 14 ki reflection question: "What problem does a projection shortcut solve?" → jab main path stride ya channel count badalta hai, tab shortcut ko same shape me laana taaki element-wise addition ho sake.

25.10 Bottleneck block (ResNet-50/101/152) — 1×1 se patla, 3×3, phir 1×1 se mota

Kahani

Bheed wale bazaar me 256 log ek saath ek sankri gali se nahi guzar sakte. Jugaad: gali ke shuru me sabko 64 ki line me laga do (1×1 reduce), gali paar karo (mehnga 3×3 kaam sirf 64 pe), aur gali ke baad phir 256 me phaila do (1×1 expand). Yahi bottleneck hai — Inception wala 1×1 trick, ab ResNet me.

Bahut gehre ResNets (50+) me basic block bahut mehnga padta, isliye: 1×1 (reduce 256→64) → 3×3 (64) → 1×1 (expand 64→256), har conv ke baad BN, pehle do ke baad ReLU, phir + shortcut, phir ReLU. Teen conv per block. Expansion factor = 4 (output = 4 × middle width).

Bottleneck block: 256 → 64 → 64 → 256 x: 56×56×256 1×1, 64 (reduce) · BN · ReLU 3×3, 64 · BN · ReLU 1×1, 256 (expand) · BN + identity (256) ReLU → 56×56×256 weights: 256·64 = 16,384 9·64·64 = 36,864 64·256 = 16,384 total 69,632 vs two 3×3 @256: 1,179,648 (≈17×) patla karo asli kaam mota karo
Mehnga 3×3 conv sirf 64 channels pe chalta hai; 1×1 convs channels ghatate/badhate hain. Shortcut 256-d pe identity.

Chalo numbers se samjhein (weights only, bias/BN chhod ke)

  1. Bottleneck: 256·64 + 3·3·64·64 + 64·256 = 16,384 + 36,864 + 16,384 = 69,632.
  2. Agar 256 channels pe hi do 3×3 conv (basic block) lagate: 2 × 3·3·256·256 = 2 × 589,824 = 1,179,648.
  3. Ratio: 1,179,648 / 69,632 ≈ 16.9× sasta.
  4. Paper wala fair comparison: 64-channel basic block = 2 × 9·64·64 = 73,728 ≈ bottleneck ke 69,632 — matlab lagbhag same cost me bottleneck 4× chaude (256-d) features pe kaam karta hai aur 2 ki jagah 3 layers (zyada depth) deta hai.

Jawab: 69,632 vs 1,179,648 (~17× kam). Isliye ResNet-50 (25.6M params) ResNet-34 (21.8M) se thoda hi bhaari hai jabki 16 layers zyada gehra hai.

PyTorch · Bottleneck block (expansion = 4)

import torch
import torch.nn as nn

class Bottleneck(nn.Module):
    expansion = 4                                    # output channels = mid * 4
    def __init__(self, in_channels, mid_channels, stride=1):
        super().__init__()
        out_channels = mid_channels * self.expansion
        self.main = nn.Sequential(
            nn.Conv2d(in_channels, mid_channels, 1, bias=False),              # 1x1 reduce
            nn.BatchNorm2d(mid_channels), nn.ReLU(inplace=True),
            nn.Conv2d(mid_channels, mid_channels, 3, stride=stride, padding=1, bias=False),  # 3x3
            nn.BatchNorm2d(mid_channels), nn.ReLU(inplace=True),
            nn.Conv2d(mid_channels, out_channels, 1, bias=False),             # 1x1 expand
            nn.BatchNorm2d(out_channels))
        self.shortcut = nn.Identity()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, 1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels))
        self.activation = nn.ReLU(inplace=True)

    def forward(self, x):
        return self.activation(self.main(x) + self.shortcut(x))

block = Bottleneck(256, 64)                          # 256 -> 64 -> 64 -> 256
x = torch.randn(1, 256, 56, 56)
print("output:", tuple(block(x).shape))
conv_w = sum(m.weight.numel() for m in block.modules() if isinstance(m, nn.Conv2d))
print("conv weights:", conv_w, "| all params (with BN):", sum(p.numel() for p in block.parameters()))
two_3x3 = nn.Sequential(nn.Conv2d(256, 256, 3, padding=1, bias=False), nn.Conv2d(256, 256, 3, padding=1, bias=False))
print("two 3x3 convs at 256 ch:", sum(p.numel() for p in two_3x3.parameters()))

Output

output: (1, 256, 56, 56)
conv weights: 69632 | all params (with BN): 70400
two 3x3 convs at 256 ch: 1179648

(BN params: 64·2 + 64·2 + 256·2 = 768 → 69,632 + 768 = 70,400.) torchvision ke ResNet-50 me stride wala conv 3×3 hai (jaisa upar) — ise "ResNet v1.5" kehte hain; original paper me stride pehle 1×1 me tha.

25.11 Poora ResNet architecture (ImageNet) — 18 se 152 tak

ResNet-18: stem → 4 stages → GAP → FC image 224×224×3 7×7, 64stride 2 112×112×64 pool3×3 /2 56×56×64 conv2_x2 blocks64 56×56×64 conv3_x2 blocks128 28×28×128 conv4_x2 blocks256 14×14×256 conv5_x2 blocks512 7×7×512 GAPFC conv3_x, conv4_x, conv5_x ka pehla block: stride 2 + projection shortcut (size ½, channels ×2) 1 (stem) + 8 blocks × 2 conv + 1 (FC) = 18 layers
Har stage ki shuruaat me downsampling (stride 2) aur channels double — VGG wala hi rule, bas har do conv ke saath ek shortcut.
Stage (output)ResNet-18ResNet-34ResNet-50ResNet-101ResNet-152
conv1 (112×112)7×7, 64, stride 2 (+BN, ReLU)
pool (56×56)3×3 max-pool, stride 2
conv2_x (56×56)[3×3, 64]×2 ×2×3[1×1,64 · 3×3,64 · 1×1,256] ×3×3×3
conv3_x (28×28)[3×3, 128]×2 ×2×4[1×1,128 · 3×3,128 · 1×1,512] ×4×4×8
conv4_x (14×14)[3×3, 256]×2 ×2×6[1×1,256 · 3×3,256 · 1×1,1024] ×6×23×36
conv5_x (7×7)[3×3, 512]×2 ×2×3[1×1,512 · 3×3,512 · 1×1,2048] ×3×3×3
headglobal average pool → FC 1000 → softmax
Block typebasicbasicbottleneckbottleneckbottleneck
Blocks[2,2,2,2][3,4,6,3][3,4,6,3][3,4,23,3][3,8,36,3]
Layer count1 + 8·2 + 1 = 181 + 16·2 + 1 = 341 + 16·3 + 1 = 501 + 33·3 + 1 = 1011 + 50·3 + 1 = 152
Params (torchvision)11.7M (11,689,512)21.8M (21,797,672)25.6M (25,557,032)44.5M (44,549,160)60.2M (60,192,808)
FLOPs (paper)1.8 × 10⁹3.6 × 10⁹3.8 × 10⁹7.6 × 10⁹11.3 × 10⁹

Exam Radar

  • ResNet: He, Zhang, Ren, Sun — Microsoft Research, 2015 (CVPR 2016). ILSVRC 2015 winner: 152 layers, 3.57% top-5.
  • Basic block (18/34): conv3×3-BN-ReLU-conv3×3-BN-(+x)-ReLU. Bottleneck (50/101/152): 1×1 reduce → 3×3 → 1×1 expand (×4).
  • Block counts: 18 [2,2,2,2], 34 [3,4,6,3], 50 [3,4,6,3] (bottleneck), 101 [3,4,23,3], 152 [3,8,36,3].
  • Params: 11.7M, 21.8M, 25.6M, 44.5M, 60.2M. Stem: 7×7/2 conv 64 + 3×3/2 max-pool; channels 64/128/256/512; GAP + FC 1000.
  • Downsampling: stride 2 in the first conv of a stage (no pooling between stages) + projection shortcut (1×1, stride 2, BN).
  • BN after every conv, no dropout, He init, SGD lr 0.1, momentum 0.9, wd 1e-4.

25.12 PyTorch: chhota ResNet for 32×32 images + torchvision ResNet

Lab 14 (SVHN — Street View House Numbers, 32×32 RGB digits) ka CompactResNet: 3×3 RGB stem (C channels) → stage1 C→C (no downsample) → stage2 C→2C (stride 2) → stage3 2C→4C (stride 2) → AdaptiveAvgPool2d(1) → Flatten → Dropout → Linear(4C, 10). Koi max-pool nahi — downsampling sirf stride-2 convs se. Naya cheez: @staticmethod — class ke andar ka function jo self nahi leta (sirf helper).

PyTorch · CompactResNet (Lab 14 SVHN style) — uses ResidualBlock from 25.8

class CompactResNet(nn.Module):
    def __init__(self, base_channels=16, blocks_per_stage=2, dropout=0.25, num_classes=10):
        super().__init__()
        c = base_channels
        self.stem = nn.Sequential(                                  # (N,3,32,32) -> (N,c,32,32)
            nn.Conv2d(3, c, 3, padding=1, bias=False),
            nn.BatchNorm2d(c), nn.ReLU(inplace=True))
        self.stage1 = self._make_stage(c, c, blocks_per_stage, 1)          # 32x32, c
        self.stage2 = self._make_stage(c, 2 * c, blocks_per_stage, 2)      # 16x16, 2c
        self.stage3 = self._make_stage(2 * c, 4 * c, blocks_per_stage, 2)  # 8x8, 4c
        self.pool = nn.AdaptiveAvgPool2d(1)                                # global average pool
        self.classifier = nn.Sequential(
            nn.Flatten(), nn.Dropout(dropout), nn.Linear(4 * c, num_classes))

    @staticmethod
    def _make_stage(in_channels, out_channels, blocks, first_stride):
        layers = [ResidualBlock(in_channels, out_channels, first_stride)]   # pehla block: stride/projection
        layers += [ResidualBlock(out_channels, out_channels) for _ in range(blocks - 1)]  # baaki identity
        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.stem(x)
        x = self.stage3(self.stage2(self.stage1(x)))
        return self.classifier(self.pool(x))

torch.manual_seed(0)
model = CompactResNet(base_channels=16, blocks_per_stage=2, dropout=0.25)
x = torch.randn(4, 3, 32, 32)                       # 4 SVHN/CIFAR jaisi RGB images
h = model.stem(x);   print("stem  :", tuple(h.shape))
h = model.stage1(h); print("stage1:", tuple(h.shape))
h = model.stage2(h); print("stage2:", tuple(h.shape))
h = model.stage3(h); print("stage3:", tuple(h.shape))
h = model.pool(h);   print("pool  :", tuple(h.shape))
logits = model(x);   print("logits:", tuple(logits.shape))
print("residual blocks:", sum(isinstance(m, ResidualBlock) for m in model.modules()))
print("params:", sum(p.numel() for p in model.parameters()))
loss = nn.CrossEntropyLoss()(logits, torch.tensor([0, 1, 2, 3]))   # logits + class indices
loss.backward()
print("stem conv grad norm:", round(model.stem[0].weight.grad.norm().item(), 4))

Output

stem  : (4, 16, 32, 32)
stage1: (4, 16, 32, 32)
stage2: (4, 32, 16, 16)
stage3: (4, 64, 8, 8)
pool  : (4, 64, 1, 1)
logits: (4, 10)
residual blocks: 6
params: 175258
stem conv grad norm: 1.4477

Lab 14 ke baaki hisse: training augmentation transforms.RandomAffine(degrees=6, translate=(0.06, 0.06), scale=(0.95, 1.05)) + ToTensor() + Normalize((0.5,0.5,0.5), (0.5,0.5,0.5)); horizontal flip nahi (digit ulta karne se label badal sakta hai — mirrored digits label-preserving nahi). Optuna objective ne tune kiya: base_channels ∈ {16, 24, 32}, blocks_per_stage 1–2, dropout 0.05–0.4, lr 2e-4–2e-2 (log), weight_decay 1e-6–2e-3 (log), batch size {64, 128, 256}, optimizer {AdamW, SGD(momentum 0.9, nesterov=True)}, MedianPruner ke saath (Chapter 22). Test set Optuna objective se bahar rakhna — warna leakage.

Lab 15 ka ResNet-18 (CIFAR-10): stem 3×3 conv 64 stride 1 (ImageNet wala 7×7/2 + maxpool 32×32 image ko bahut jaldi chhota kar deta), phir BasicBlock(64,64)×2, BasicBlock(64,128,stride=2) + (128,128), (128→256, s2) + (256,256), (256→512, s2) + (512,512) → 4×4×512 → AdaptiveAvgPool2d((1,1)) → Flatten → Linear(512, 10). Params 11,173,962 (≈11.2M); 10 epochs: train 93.91%, test 80.61%.

torchvision ResNet-18: stages dekhna + apne classes ke liye head badalna

torchvision ResNet ke parts ke naam: conv1, bn1, relu, maxpool, layer1…layer4, avgpool, fc. Har stage ke pehle block ka projection shortcut downsample naam se hota hai.

PyTorch · torchvision.models.resnet18

import torch
import torch.nn as nn
from torchvision import models

net = models.resnet18(weights=None)        # pretrained chahiye: weights=models.ResNet18_Weights.IMAGENET1K_V1
x = torch.randn(1, 3, 224, 224)
h = net.maxpool(net.relu(net.bn1(net.conv1(x)))); print("stem  :", tuple(h.shape))
for name in ["layer1", "layer2", "layer3", "layer4"]:
    h = getattr(net, name)(h); print(f"{name}:", tuple(h.shape))
print("avgpool:", tuple(net.avgpool(h).shape), "| fc:", net.fc)
print(net.layer2[0].downsample)            # stage 2 ke pehle block ka projection shortcut
net.fc = nn.Linear(net.fc.in_features, 10) # apne 10 classes ke liye naya head
print("new output:", tuple(net(x).shape), "| params:", f"{sum(p.numel() for p in net.parameters()):,}")

Output

stem  : (1, 64, 56, 56)
layer1: (1, 64, 56, 56)
layer2: (1, 128, 28, 28)
layer3: (1, 256, 14, 14)
layer4: (1, 512, 7, 7)
avgpool: (1, 512, 1, 1) | fc: Linear(in_features=512, out_features=1000, bias=True)
Sequential(
  (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)
  (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, bias=True, track_running_stats=True)
)
new output: (1, 10) | params: 11,181,642

Check: 11,689,512 − (512·1000 + 1000) + (512·10 + 10) = 11,689,512 − 513,000 + 5,130 = 11,181,642 ✓. net.fc.in_features = 512 (ResNet-18/34), 2048 (ResNet-50+). Transfer learning same VGG wala: backbone freeze, sirf net.fc train.

Galti mat karna (coding)

  • ResNet me nn.Flatten() ke baad Linear ka in_features = channels (512), H·W nahi — kyunki pehle GAP ne 7×7 ko 1×1 kar diya.
  • nn.CrossEntropyLoss ko raw logits do (softmax mat lagao) aur labels class indices (0–9).
  • BN wale model me evaluation se pehle model.eval() zaroor — warna BN batch statistics use karega aur dropout chalta rahega.

25.13 ResNet ke baad: Pre-activation ResNet, DenseNet, aur aage ki duniya

25.14 Decks ka bonus: Upsampling, U-Net, R-CNN, YOLO (short me)

Lecture 11 aur "Advanced CNN Architectures and applications" decks me classification ke aage ki duniya bhi thi. MCQ ke liye zaroori baatein:

Upsampling — chhote feature map ko bada karna

Classification me hum size ghatate hain (pool, stride). Segmentation (har pixel ka label) me wapas original size chahiye. Slides ke tareeke:

Hout = (Hin − 1)·S − 2P + K + output_padding   (PyTorch nn.ConvTranspose2d, dilation 1)
  • Example: 4×4, K = 2, S = 2, P = 0 → (4 − 1)·2 + 2 = 8. Ya K = 3, S = 2, P = 1, output_padding = 1 → 3·2 − 2 + 3 + 1 = 8. Normal conv formula ka "ulta".

PyTorch · upsampling layers

import torch
import torch.nn as nn

x = torch.randn(1, 64, 4, 4)                                    # chhota feature map
up1 = nn.ConvTranspose2d(64, 32, kernel_size=2, stride=2)       # learnable upsampling
up2 = nn.ConvTranspose2d(64, 32, kernel_size=3, stride=2, padding=1, output_padding=1)
up3 = nn.Upsample(scale_factor=2, mode='nearest')               # bina params: pixel copy
print("ConvTranspose2d k2 s2      :", tuple(up1(x).shape), "| params:", sum(p.numel() for p in up1.parameters()))
print("ConvTranspose2d k3 s2 p1 op1:", tuple(up2(x).shape))
print("Upsample nearest x2        :", tuple(up3(x).shape))
skip = torch.randn(1, 32, 8, 8)                                 # encoder ka same-size feature map
print("U-Net style concat         :", tuple(torch.cat([up1(x), skip], dim=1).shape))

Output

ConvTranspose2d k2 s2      : (1, 32, 8, 8) | params: 8224
ConvTranspose2d k3 s2 p1 op1: (1, 32, 8, 8)
Upsample nearest x2        : (1, 64, 8, 8)
U-Net style concat         : (1, 64, 8, 8)

(ConvTranspose2d ka weight shape (in, out, K, K) = (64, 32, 2, 2) → 8,192 + 32 bias = 8,224.)

U-Net (Ronneberger et al., 2015) — "kya" bhi aur "kahan" bhi

Deck: ResNet ki "shortcoming" uski logic me nahi, balki ye ki wo pixel-perfect localization (semantic segmentation) ke liye nahi bana. U-Net biomedical image segmentation ke liye bana: har pixel ko class do. Shape "U" jaisa: encoder (downsampling: conv + pool/stride → semantic features, "what") → decoder (upsampling / transposed conv → resolution wapas, "where"), aur encoder ke har level se decoder ke same level tak skip connections jo feature maps ko concatenate karte hain (ResNet ki tarah add nahi) — taaki downsampling me khoyi fine details (edges) wapas mil jaayein. Application: medical imaging (cells, tumours).

Object detection: R-CNN family aur YOLO

Exam Radar

  • Transposed convolution = learnable upsampling; nearest/bilinear = non-learnable; max-unpooling = max-pool positions yaad rakh ke.
  • U-Net: encoder–decoder + concatenation skip connections, semantic segmentation, biomedical (2015).
  • R-CNN (~2,000 proposals, slow) → Fast R-CNN (one pass + RoI pooling) → Faster R-CNN (RPN). YOLO = single pass, real-time.

25.15 Aakhri side-by-side: VGG vs Inception vs ResNet

VGGInception (GoogLeNet)ResNet
Ek linedeeper sequential stackwider: parallel multi-scale branchesshortcut connections
Merge operation— (seedha chain)concatenate (channels jodo)add (element-wise)
Shape rule—H×W same, channels alag chal saktepoora shape same (warna projection)
1×1 conv ka role(config C me thoda)bottleneck before 3×3/5×5, pool projectionbottleneck (50+), projection shortcut
Head3 bade FCGAP + 1 FCGAP + 1 FC
Max depth (ImageNet)1922152 (CIFAR: 1202)

Exam Radar

  • Pre-activation (v2): BN-ReLU-conv order, pure identity path, 1001 layers. DenseNet: concatenation instead of addition.
  • Teacher notebook ki one-liner: "Inception = parallel branches + concatenate · ResNet = residual path + shortcut + add."
  • Problems ↔ fixes (worksheet field guide): decorrelation with depth → preserve routes (shattering ≠ vanishing); hard to learn useful change → residual x + F(x); scale it → ResNet stages + projections; several scales → Inception branches (concat); expensive conv → depthwise + pointwise.

Dimaag me aise baitha lo

"Copy rakho, sirf correction seekho": y = F(x) + x. Backward me "+1" ki seedhi sadak, forward me BN ka speed-breaker. Aur numbers: "2-2-2-2, 3-4-6-3, 3-4-23-3, 3-8-36-3".

Notebook khatam. Ab tum ready ho. All the best!