Kahani
Har topic ek chhoti si real-life story se shuru hota hai, taaki concept dimaag me "feel" ho jaaye.
Deep Learning · Mid-Sem · Poora syllabus
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.
| No. | Topic · kya-kya covered hai |
|---|---|
| Shuruaat — neev mazboot karo | |
| 0 | Is notebook ko kaise padheinBoxes ka matlab · padhne ka order · exam ka game plan |
| 1 | Deep 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 |
| 2 | Math 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 |
| 3 | Python + 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 | |
| 4 | The Perceptron — ek neuron, ek faislaBiological neuron · McCulloch–Pitts 1943 · Rosenblatt 1958 · weights & bias · step activation · decision boundary · logic gates · learning rule · convergence theorem · limitations |
| 5 | Linear 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 |
| 6 | The 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 |
| 7 | Shallow 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 |
| 8 | Forward 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 |
| 9 | Activation Functions — neuron ka moodStep · Sigmoid · Tanh · ReLU · Leaky ReLU · PReLU · ELU · Softmax |
| 10 | Universal Approximation Theorem, Depth vs WidthNetwork capacity · UAT (Cybenko 1989, Hornik 1991) · bumps & ReLU pieces · linear regions · folding · depth vs width trade-off |
| 11 | Loss Functions — galti ka hisaab-kitaabLoss vs Cost · Loss landscape · Maximum Likelihood · NLL · MSE · MAE · Huber · Binary & Categorical Cross-Entropy · Hinge · PyTorch loss classes |
| Backpropagation | |
| 12 | Computational 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 |
| 13 | Backpropagation 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 | |
| 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 |
| 15 | Learning 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 |
| 16 | Momentum-Based Methods: EWMA, SGD with Momentum, NAGGradient ko yaaddasht do · EWMA · bias correction · velocity · Nesterov ka look-ahead |
| 17 | Adaptive 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 | |
| 18 | Bias–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 |
| 19 | Regularization: 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 |
| 20 | Dropout 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 |
| 21 | Weight 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 |
| 22 | Hyperparameter 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 | |
| 23 | Convolutional 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 |
| 24 | Classic 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 |
| 25 | Deep 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
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.
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.
Chalo numbers se samjhein
Jawab: double underline wala
Exam Radar
Galti mat karna
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.
nn.Linear aur nn.Module (Chapter 5, Chapter 7) → loss classes (Chapter 11) → autograd aur poora training loop (Chapter 13) → DataLoader (Chapter 14) → optimizers aur schedulers (Chapters 15–17) → dropout, BatchNorm, early stopping, init (Chapters 19–21) → Optuna (Chapter 22) → CNN (Chapter 23) → residual block (Chapter 25).Dimaag me aise baitha lo
Ratta nahi, kahani. Kahani yaad rahegi to formula khud yaad aa jayega.
Chapter 1
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
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.
Exam Radar
Galti mat karna
Dimaag me aise baitha lo
India ⊃ Maharashtra ⊃ Mumbai = AI ⊃ ML ⊃ DL.
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.
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
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.
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).
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.
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.
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
nn.Linear(30, 1) → 30 weights + 1 bias = 31 parameters.Dimaag me aise baitha lo
Model = knobs wali machine. Loss = galti ka meter. Training = meter dekh dekh ke knobs ghumana.
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.
| Term | Matlab | Register me | Lab 5 (breast cancer) me |
|---|---|---|---|
| Feature (input, x) | Ek measurable cheez jo hum model ko dete hain | attendance, padhai ke ghante, marks | radius_mean, texture_mean, area_mean … (30 features) |
| Label / Target (y) | Wo answer jo model ko predict karna hai | Pass / Fail | diagnosis: M (malignant) / B (benign) |
| Sample (example, instance, data point) | Ek row = ek cheez ka poora record | ek student | ek patient |
| Dataset | Saare samples ka collection | poora register | 569 patients |
| Feature vector | Ek 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
LabelEncoder: classes ko alphabetical order me 0, 1, 2 … deta hai ('B' → 0, 'M' → 1).Galti mat karna
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.
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.
| Type | Data me kya milta hai | Goal | Example |
|---|---|---|---|
| Supervised | (x, y) pairs — input aur sahi answer dono | f(x) ≈ y seekhna | Lab 5 cancer M/B, MNIST digits, house price |
| Unsupervised | sirf x, koi label nahi | chhupa structure dhoondhna (groups, compression) | clustering, autoencoders (Module 8) |
| Reinforcement | actions ke baad reward/penalty | total reward maximize karna | AlphaGo (self-play), game bots |
| Self-supervised (bonus) | label data se hi ban jaata hai | agla word / chhupa hissa predict karna | ChatGPT 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
Dimaag me aise baitha lo
Supervised = answer key wali tuition. Unsupervised = bina naam ki photos sort karna. RL = cycle seekhna.
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.)
| Task | Output kaisa | Last layer (preview) | Loss (preview, Ch 11) | Example |
|---|---|---|---|---|
| Regression | koi bhi real number | 1 neuron, koi activation nahi (linear) | MSE / MAE (nn.MSELoss) | Raju ke cups, house price |
| Binary classification | probability (0, 1) → threshold 0.5 | 1 neuron + sigmoid | Binary Cross-Entropy (nn.BCELoss) | Lab 5: M vs B |
| Multi-class classification | K probabilities jo jod ke 1 → argmax | K neurons + softmax | Cross-Entropy (nn.CrossEntropyLoss) | MNIST 10 digits |
Chalo numbers se samjhein
(y_pred > 0.5).float().Exam Radar
out_features = 1 (ek probability kaafi, doosri = 1 − p). Lab 5: "we only need one probability (P(malignant)) per example".Galti mat karna
Dimaag me aise baitha lo
Thermometer = regression. Traffic signal ka rang = classification (laal/peela/hara me se ek).
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:
| Set | Kis kaam ka | Kab use hota hai | Typical size |
|---|---|---|---|
| Train | parameters (weights, biases) seekhna | har epoch, baar baar | 60–80% |
| Validation (dev set) | hyperparameters chunna, model compare karna, early stopping (Ch 19), Optuna (Ch 22) | training ke beech beech me | 10–20% |
| Test | final, imaandaar report card | sirf ek baar, bilkul end me | 10–20% |
Chalo numbers se samjhein
Sawaal: Lab 5 me 569 patients the, test_size=0.2. Kitne train, kitne test?
X_train_tensor: torch.Size([455, 30]).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
Galti mat karna
Dimaag me aise baitha lo
Exercise se seekho, mock se strategy chuno, board paper sealed rakho.
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.
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.
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
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.Dimaag me aise baitha lo
Ruler train se banao, usi ruler se test naapo. Test se ruler kabhi mat banao.
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.parameters() me milte hain, optimizer inhi ko update karta haiHyperparameters
Chalo numbers se samjhein
nn.Linear(30, 1): 30 × 1 + 1 = 31 parametersPyTorch · 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 networkOutput
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
Galti mat karna
Dimaag me aise baitha lo
Jo machine khud seekhe = parameter (namak). Jo tum pehle chuno = hyperparameter (gas, bartan, time).
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).
Chalo numbers se samjhein
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 updatesOutput
15 [32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 7] 375
| Method (Ch 14) | Batch size | Updates per epoch |
|---|---|---|
| Batch GD (full batch) — Lab 5 main loop | N | 1 |
| SGD (stochastic) | 1 | N |
| Mini-batch GD | B | N/B (upar round) |
Exam Radar
DataLoader me drop_last=False default — isliye aakhri chhota batch (7) bhi aata hai.Galti mat karna
Dimaag me aise baitha lo
Batch = ek tukda, iteration = ek tukde ke baad sudhaar, epoch = poori kitaab ek baar.
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.
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
Galti mat karna
Dimaag me aise baitha lo
Purana ML: checklist tum banao. Deep learning: checklist network khud banaye, layer by layer.
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.
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
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
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.
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.
| Saal | Kaun | Kya kiya | Kyun important |
|---|---|---|---|
| 1943 | Warren McCulloch & Walter Pitts | Neuron 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) |
| 1950 | Alan Turing | Turing Test ("Computing Machinery and Intelligence") | "Kya machine soch sakti hai?" ka test |
| 1956 | Dartmouth conference (John McCarthy & others) | "Artificial Intelligence" naam ka janm | AI ek field bana — Golden Age 1 shuru |
| 1957 / 1958 | Frank Rosenblatt | Perceptron — 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–60 | Widrow & Hoff | ADALINE (LMS rule) | Threshold se pehle wali error pe seekhna — gradient descent jaisa idea |
| 1969 | Marvin Minsky & Seymour Papert | Book "Perceptrons": single-layer perceptron XOR solve nahi kar sakta (XOR linearly separable nahi) | Funding gir gayi → First AI winter / First Dark Age (Ch 6) |
| 1986 | David Rumelhart, Geoffrey Hinton, Ronald Williams | Backpropagation 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) |
| 1989 | George 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 |
| 1995 | Cortes & Vapnik | Support Vector Machines | Kam computation me NNs se behtar → Second Dark Age neural nets ke liye |
| 1998 | Yann LeCun et al. | LeNet-5 CNN — handwritten digits (MNIST), ~60,000 parameters | Dark age me bhi CNN ka idea zinda rakha; cheques padhne me use hua (Ch 24) |
| 2006 | Geoffrey Hinton (Osindero, Teh) | Deep Belief Networks — RBM se greedy layer-wise pretraining | Deep networks train karne ka pehla kaamyaab tareeka → Golden Age 3 shuru, "deep learning" naam chal pada |
| 2009 | Fei-Fei Li & team | ImageNet dataset | Bada labelled data — DL ka petrol |
| 2012 | Alex Krizhevsky, Ilya Sutskever, Geoffrey Hinton | AlexNet 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) |
| 2014 | Simonyan & Zisserman (Oxford); Szegedy et al. (Google); Kingma & Ba | VGG (16/19 layers, sirf 3×3 conv), GoogLeNet / Inception v1 (ILSVRC-2014 winner, 22 layers), Adam optimizer | Gehre networks; Adam aaj ka default optimizer (Ch 17, 24) |
| 2015 | Kaiming He et al. (Microsoft); Ioffe & Szegedy (Google) | ResNet (skip/residual connections, 152 layers, ILSVRC-2015 winner, top-5 ≈ 3.57%); Batch Normalization | 100+ layers train hona possible; human-level (~5%) se behtar ImageNet error (Ch 20, 25) |
| 2017 | Vaswani 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
Galti mat karna
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.
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.
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
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.
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.
| Naam | Dimensions (ndim / rank) | Shape ka example | Deep learning me kahan |
|---|---|---|---|
| Scalar | 0-D | () — bas ek number | loss value, learning rate |
| Vector | 1-D | (5,) | ek sample ke features, bias vector |
| Matrix | 2-D | (2, 3) = 2 rows, 3 columns | tabular batch (455, 30), weight matrix, grayscale image (28, 28) |
| 3-D tensor | 3-D | (3, 32, 32) | ek colour photo: (channels, height, width) |
| 4-D tensor | 4-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.
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 numbersOutput
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
Dimaag me aise baitha lo
Number → line → table → tables ka dher. Shape = "har taraf kitne".
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.
Chalo numbers se samjhein
Jawab: 11, 50, 87 — har baar ek single number.
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.
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
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.
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".
Chalo numbers se samjhein
Sawaal: x = [2, 3], w = [0.5, −1], b = 1. z nikalo. Aur auto me 5 km ka kiraya?
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
nn.Linear(in, out) ka weight shape (out, in), bias shape (out,). Ye x Wᵀ + b nikalta hai.Dimaag me aise baitha lo
Auto meter: ₹30 base (bias) + ₹12 har km (weight × input).
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.
Chalo numbers se samjhein
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| 7 | 8 |
| 9 | 10 |
| 11 | 12 |
| 58 | 64 |
| 139 | 154 |
Jawab: AB = [[58, 64], [139, 154]] — Golu ko Dukaan P sasti padegi (58 < 64), Bunty ko bhi (139 < 154).
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
in_features must equal input ke columns.Galti mat karna
*) hai, alag cheez (Ch 3.17).Dimaag me aise baitha lo
Row se column ka dot. Andar wale same, bahar wale result.
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.
Chalo numbers se samjhein
| 5 | 8 | 2 |
| 5 | 5 | 2 |
| 5 | 5 |
| 8 | 5 |
| 2 | 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 checkOutput
tensor([[5., 5.],
[8., 5.],
[2., 2.]])
tensor([[5., 5.],
[8., 5.],
[2., 2.]])
torch.Size([2, 3]) torch.Size([3, 2])
TrueExam Radar
torch.transpose(x, dim0, dim1) koi bhi do dims swap karta hai; .T 2-D ke liye shortcut.Dimaag me aise baitha lo
Transpose = register ko kone se ghumana: rows ↔ columns.
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.
Chalo numbers se samjhein
Sawaal: y = 2x + 1 ke points nikalo aur slope verify karo.
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
Dimaag me aise baitha lo
Auto meter: slope = per km rate, intercept = base fare. Neuron me: w = rate, b = base.
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.
Chalo numbers se samjhein
torch.exp(f) ye hi numbers deta hai.)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^xOutput
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
Dimaag me aise baitha lo
e = bank ka "har pal byaaj" wala ₹2.718. ex kabhi negative nahi hota.
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.
Chalo numbers se samjhein
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 -> 0Output
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.-inf); ln(−1) = nan.Galti mat karna
Dimaag me aise baitha lo
ln = "e ko kitni power?". Guna ko jod banata hai. 1 se chhota → negative.
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.
Chalo numbers se samjhein
Sawaal: f(x) = x² ka slope x = 3 pe?
Jawab: f′(3) = 6.
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
Dimaag me aise baitha lo
Derivative = speedometer: "abhi is pal kitni tezi se badal raha hai".
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.
| # | Rule | Formula | Example | Number pe |
|---|---|---|---|---|
| 1 | Constant | d/dx (c) = 0 | d/dx 7 = 0 | flat line, koi badlav nahi |
| 2 | Power | d/dx xn = n xn−1 | d/dx x³ = 3x² | x = 2 → 12 |
| 3 | Constant multiple | d/dx [c·f] = c·f′ | d/dx 5x² = 10x | x = 3 → 30 |
| 4 | Sum | (f + g)′ = f′ + g′ | d/dx (x² + 3x + 4) = 2x + 3 | x = 2 → 7 |
| 5 | Exponential | d/dx ex = ex | khud hi apna derivative! | x = 1 → 2.7183 |
| 6 | Log | d/dx ln x = 1/x | d/dx ln x at 4 | 1/4 = 0.25 |
| 7 | Product | (fg)′ = f′g + fg′ | d/dx [x²ex] = 2xex + x²ex | x = 1 → 3e ≈ 8.1548 |
| 8 | Quotient | (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)
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
Galti mat karna
Dimaag me aise baitha lo
Power: power aage, ek kam. ex: khud hi. ln: ulta (1/x). Constant: zero.
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.
Chalo numbers se samjhein
Sawaal: y = (3x + 1)², dy/dx at x = 1?
Jawab: 24.
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 = 1Output
tensor(24.)
Exam Radar
Galti mat karna
Dimaag me aise baitha lo
Gears: paddle 1 → 3 → 6. Chain me rates guna hote hain.
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.
Chalo numbers se samjhein
Sawaal: f(w, b) = (2w + b − 5)². (w, b) = (1, 1) pe ∇f nikalo.
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/dbOutput
4.0 tensor(-8.) tensor(-4.)
Exam Radar
param.grad me rakhta hai.Dimaag me aise baitha lo
Ek knob ghumao, baaki pakdo = partial. Saare partials ki list = gradient.
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.
Chalo numbers se samjhein
Sawaal 1: f(x) = x², f′ = 2x. Start x = 3, η = 0.1.
Sawaal 2 (do knobs): f(w, b) = (2w + b − 5)², start (1, 1), η = 0.05, ∇f = (4u, 2u).
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.)
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 chaloOutput
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 updateOutput
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
Galti mat karna
Dimaag me aise baitha lo
Andhere me ghaati: dhalaan mehsoos karo, ulti taraf chhota kadam, repeat.
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).
Chalo numbers se samjhein
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 ... = MSEOutput
10 14 tensor(4.2500) tensor(1.4167)
Exam Radar
range(1, 5) me 5 shaamil nahi.Dimaag me aise baitha lo
Σ = kirana bill ka total. ∏ = sabka guna.
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.
Chalo numbers se samjhein
Sawaal: data = [2, 4, 4, 4, 5, 5, 7, 9]. Mean, dono variance, dono std nikalo.
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
np.var/np.std default = population (ddof=0); torch.var/torch.std default = sample (unbiased, n−1). StandardScaler population std use karta hai.Galti mat karna
Dimaag me aise baitha lo
Mean = beech. Std = beech se aam taur pe kitni doori. Sample me n−1, numpy n, torch n−1.
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).
Chalo numbers se samjhein
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.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.Dimaag me aise baitha lo
Normal = heights ki ghanti (randn). Uniform = Ludo ka paasa (rand).
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".
Chalo numbers se samjhein
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:
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?
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
Dimaag me aise baitha lo
Toss guna hote hain, log unhe jodta hai, minus lagao to loss ban jaata hai.
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).
Chalo numbers se samjhein
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 indexOutput
tensor(0.7000) tensor(1) tensor([0.0900, 0.6652, 0.2447]) tensor(1) tensor(1) tensor(1)
Exam Radar
argmax(dim=1) over class scores (Ch 3.18).Dimaag me aise baitha lo
max = "kitne marks?", argmax = "topper kaun?".
Chapter 3
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.
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.
| Type | Kya hai | Example | DL me kahan |
|---|---|---|---|
int | poora number | epochs = 25 | epochs, batch_size, neurons |
float | dashamlav wala number | lr = 0.1 | learning rate, loss value |
str | text | "cuda" | device naam, column naam |
bool | True / False | shuffle=True | flags |
None | "kuch nahi" | best_model = None | abhi 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.
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.Dimaag me aise baitha lo
Ground floor = 0. Slice ka stop = darwaza, andar nahi aata.
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
d["x"] → KeyError; d.get("x", default) safe hai..items() → (key, value) jodiyan, loop me sabse common.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)
breakOutput
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
range(n) → 0 se n−1. range(a, b) → a se b−1. range(a, b, s) → s ke kadam.enumerate(x) → (index, value). Training me: for i, (xb, yb) in enumerate(loader): — batch number bhi chahiye to.zip(a, b) → dono lists ke elements jodi me. Raju ka MSE (1.3) isi se nikla.break → loop turant band (early stopping). continue → is round ka baaki chhodo, agla round.for epoch in range(epochs):. Mini-batch me nested loop: for epoch in range(epochs): ke andar for xb, yb in loader: (Ch 14).Dimaag me aise baitha lo
range(3) = 0, 1, 2. Teen baar, par 3 kabhi nahi.
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.
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
b=0.0): na do to default. PyTorch me har jagah: nn.Linear(in, out, bias=True), DataLoader(..., shuffle=False) default.lr=0.1) — order ki tension khatam. Positional pehle, keyword baad me.return output, hidden_values aur call pe probs, hidden_values = hidden_layer_xor_forward(X).return na likha to function None deta hai. Optuna ka objective(trial) (Ch 22) ek number (accuracy/loss) return karta hai.Dimaag me aise baitha lo
def = recipe card. return = plate me khana.
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.
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
class Net(nn.Module) → __init__ me pehli line super().__init__() → layers self.xxx = ... me → forward(self, x) me computation → return.AttributeError: cannot assign module before Module.__init__() 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
self bhool jaana: def forward(x): → call pe TypeError: forward() takes 1 positional argument but 2 were given.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".
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 versionOutput
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).
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 tareekaOutput
Epoch: 4, Loss: 0.4555133879184723 Epoch 04 | loss 0.4555 | acc 98.25% Epoch 4 Loss 0.4555
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.no_grad bhoolna → bekaar memory/compute.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).
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 badloOutput
(3,) float64 int64 (2, 3) 2 11 [ 6. 15.] [[11 12 13] [14 15 16]] [[101 202 303] [104 205 306]] float32
Exam Radar
np.array, .shape, .ndim, .dtype, np.dot, @, .astype(np.float32) — tensors me inke bhai: torch.tensor, .shape, .dim(), .dtype, torch.dot, @, .to(torch.float32).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)
Exam Radar
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.
| Function | Kya banata hai | Yaad rakhne wali baat |
|---|---|---|
torch.tensor(data) | list/array se tensor | dtype data se: ints → int64, decimals → float32. Data copy hota hai |
torch.zeros(2, 3) / ones | sab 0 / sab 1 | default 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 integers | high exclusive; size tuple me |
torch.empty(2, 3) | bas memory, values garbage | Lab 0 me 2.4242e-43 jaisi ajeeb values aayi — initialize nahi hota |
torch.arange(start, end, step) | barabar step wali sequence | end exclusive (Lab 0: arange(2,100,2) → 2…98) |
torch.linspace(start, end, steps) | steps barabar doori wale points | end inclusive; step = (end−start)/(steps−1) = 20/9 ≈ 2.2222 |
torch.eye(n) | identity matrix | diagonal 1, baaki 0 |
torch.full((2, 3), 5) | sab ek value | shape tuple me pehle, value baad me |
zeros_like(p), ones_like, rand_like, empty_like | p jaisa shape (aur dtype) | rand_like int tensor pe error (3.15) |
torch.manual_seed(n) | random generator ka seed | same 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 meOutput
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".
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.
| dtype | Purana naam | Kab milta hai | Shortcut |
|---|---|---|---|
torch.float32 | Float | PyTorch ka default float; saare layer weights | .float() |
torch.float64 | Double | NumPy se aaya data (numpy default float64) | .double() |
torch.int64 | Long | integers ka default; class labels (CrossEntropyLoss ko yahi chahiye, Ch 11) | .long() |
torch.int32 | Int | .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
X.astype(np.float32) ya tensor.float() / .to(torch.float32).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.
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:
Chalo numbers se samjhein
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 remainderOutput
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 1PyTorch · 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 tareekaOutput
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
y.view(-1, 1) ya y.unsqueeze(1) (ya prediction pe .squeeze(1)).Galti mat karna
Dimaag me aise baitha lo
Diwali bonus sab pe faila = broadcasting. Column − row = poora grid (chupa bug).
@ (matmul) vs * (element-wise) — sabse common trapKahani
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
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])
| Operation | Kya karta hai | Shapes |
|---|---|---|
A * B, torch.mul | element-wise (Hadamard) product | same shape (ya broadcastable) |
A @ B, torch.matmul | matrix multiplication (batch bhi) | (m, n) @ (n, p) → (m, p) |
torch.mm | matmul sirf 2-D ke liye | (m, n), (n, p) |
torch.dot | dot product sirf 1-D | (n,), (n,) → scalar |
torch.transpose(A, 0, 1), A.T | rows ↔ 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).
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.
dim=k → wahi dimension gayab (collapse) ho jaati hai. Shape (2, 3): dim=0 → (3,), dim=1 → (2,)keepdim=True → dimension hatao mat, size 1 kar do: (2, 3) → (2, 1)Chalo numbers se samjhein — e = [[5, 6, 9], [2, 3, 1]]
sum(e): 5+6+9+2+3+1 = 26sum(e, dim=0) (har column): [5+2, 6+3, 9+1] = [7, 9, 10] → shape (3,)sum(e, dim=1) (har row): [5+6+9, 2+3+1] = [20, 6] → shape (2,)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]max(e) = 9, min(e) = 1argmax(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).)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]argmax(e, dim=1): har row me kaunsa column bada? row0: max 9 at col 2; row1: max 3 at col 1 → [2, 1]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
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
Dimaag me aise baitha lo
Jo dim doge, wo pichak jaayegi. (2,3) pe dim=0 → 3 bache, dim=1 → 2 bache.
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.
x.view(a, b) / x.reshape(a, b): naya shape, total elements same hone chahiye (a × b = numel).view(-1, 4) on 12 → (3, 4). Sirf ek −1 allowed.view original memory share karta hai aur memory "contiguous" (lagataar) honi chahiye; reshape hamesha chalta hai (zaroorat pade to copy). Transpose ke baad view fail ho sakta hai.unsqueeze(d) = position d pe size-1 dimension jodo. squeeze() = saari size-1 dims hatao; squeeze(d) = sirf d wali (agar size 1 ho).flatten(start_dim=1) = batch dim chhod ke baaki sab ek line: (64, 1, 28, 28) → (64, 784), kyunki 1 × 28 × 28 = 784. CNN → Linear ke beech yahi (Ch 23: nn.Flatten()).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 12PyTorch · 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 haiOutput
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
y_train_tensor.view(-1, 1) → (N,) se (N, 1).img.unsqueeze(0) → (1, 3, 32, 32).RuntimeError: shape '[5, -1]' is invalid for input of size 12.Galti mat karna
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".
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 dikhegaOutput
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)face[0, :], face[9, :]; side columns face[:, 0], face[:, 9]; aankhein (2, 2), (2, 7); muh row 7, columns 2–7.x[i, j] = row i, column j. : = "saare". a:b = a se b−1 (list jaisa). Negative index peeche se.0:1) se bachi rehti. Lab 1 ne XOR me x[:, 0:1] isliye likha — (4, 1) shape chahiye thi, (4,) nahi.z >= 0 se True/False tensor; z[mask] sirf True wale; .int()/.float() se 0/1 prediction (Lab 1: pred = (z >= 0).int(), Lab 5: (y_pred > 0.5).float()).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 predictionOutput
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.
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.
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.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.
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 listOutput
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 numpyOutput
RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead. [1. 2.]
| Function | Direction | Memory |
|---|---|---|
torch.from_numpy(arr) | NumPy → Torch | share (dtype bhi same rehta, float64 → float64) |
torch.tensor(arr) | NumPy/list → Torch | copy |
t.numpy() | Torch → NumPy | share; 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
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..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
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu').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.xb, yb = xb.to(device), yb.to(device).torch.cuda.synchronize() (Lab 0).Galti mat karna
x.to(device) likh dena bina x = ke — tensor wahin ka wahin rahega.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 TrueOutput
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.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
Biological neuron · McCulloch–Pitts 1943 · Rosenblatt 1958 · weights & bias · step activation · decision boundary · logic gates · learning rule · convergence theorem · limitations
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.
| Hissa | Kaam (simple bhasha) | Artificial neuron me kya bana |
|---|---|---|
| Dendrites | Ped ki tehniyon jaise branches; doosre neurons se signal receive karte hain | Inputs x1, x2, …, xn |
| Synapse | Do neurons ke beech ka joint; kuch connections strong, kuch weak | Weights w1, w2, …, wn (connection ki strength / importance) |
| Cell body (Soma) | Saare aate signals ko jodta hai, decide karta hai fire karna hai ya nahi | Weighted sum Σ wixi + b aur threshold check (activation) |
| Axon | Lambi wire; fire hua signal aage doosre neurons tak le jaata hai | Output ŷ (jo agle neuron ka input banega) |
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:
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.
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)
Rosenblatt Perceptron (1958)
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!
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
"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.
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.
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.
Chalo numbers se samjhein
Sawaal: Ramu ka perceptron: w1 = 3, w2 = 2, θ = 4 (to b = −4). Teen din ke liye output nikalo.
Jawab: sirf (1, 1) pe fire. Dhyan do: ye AND gate jaisa behave kar raha hai! (Section 4.6)
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.
(z >= 0) use karta hai, to z = 0 → 1.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
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:
| Condition | Matlab | Output |
|---|---|---|
| w1x1 + w2x2 + b > 0 | line ke "positive" side | 1 (class 1) |
| w1x1 + w2x2 + b = 0 | bilkul line ke upar (boundary) | flip point (course convention me 1) |
| w1x1 + w2x2 + b < 0 | line ke "negative" side | 0 (class 0) |
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?
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."
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).
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
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.
| Gate | Lecture (threshold form) | Same in bias form | Lab 1 / Worksheet (safe margin) |
|---|---|---|---|
| AND | w1 = 1, w2 = 1, threshold ≥ 2 | b = −2 | w = (1, 1), b = −1.5 |
| OR | w1 = 1, w2 = 1, threshold ≥ 1 | b = −1 | w = (1, 1), b = −0.5 |
| NOT | w = −1, threshold ≥ 0 | b = 0 | (safer) w = −1, b = 0.5 |
| NAND | w = (−1, −1), threshold ≥ −1.5 | b = 1.5 | w = (−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)
| x1 | x2 | AND: z = x1 + x2 − 1.5 | ŷ | OR: z = x1 + x2 − 0.5 | ŷ | NAND: z = −x1 − x2 + 1.5 | ŷ |
|---|---|---|---|---|---|---|---|
| 0 | 0 | −1.5 | 0 | −0.5 | 0 | 1.5 | 1 |
| 0 | 1 | −0.5 | 0 | 0.5 | 1 | 0.5 | 1 |
| 1 | 0 | −0.5 | 0 | 0.5 | 1 | 0.5 | 1 |
| 1 | 1 | 0.5 | 1 | 1.5 | 1 | −0.5 | 0 |
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.
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.
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.
| Case | Error y − ŷ | Update | Asar |
|---|---|---|---|
| Sahi prediction | 0 | kuch nahi | "Jo theek chal raha hai use mat chhedo" |
| y = 1 par ŷ = 0 (z bahut chhota tha) | +1 | w ← w + ηx, b ← b + η | is x pe z badhega |
| y = 0 par ŷ = 1 (z bahut bada tha) | −1 | w ← 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.
| Ep | x | y | z = w1x1 + w2x2 + b | ŷ | y − ŷ | Naya (w1, w2), b |
|---|---|---|---|---|---|---|
| 1 | (0,0) | 0 | 0 + 0 − 0.1 = −0.1 | 0 | 0 | (0.4, 0.4), −0.1 (no change) |
| 1 | (0,1) | 0 | 0 + 0.4 − 0.1 = 0.3 | 1 | −1 | w₁ = 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) | 0 | 0.4 + 0 − 0.3 = 0.1 | 1 | −1 | w₁ = 0.4 − 0.2 = 0.2; w₂ = 0.2; b = −0.3 − 0.2 = −0.5 |
| 1 | (1,1) | 1 | 0.2 + 0.2 − 0.5 = −0.1 | 0 | +1 | w₁ = 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.3 | 0 | 0 | (0.4, 0.4), −0.3 |
| 2 | (0,1) | 0 | 0.4 − 0.3 = 0.1 | 1 | −1 | w₂ = 0.4 − 0.2 = 0.2; b = −0.3 − 0.2 = −0.5 → (0.4, 0.2), −0.5 |
| 2 | (1,0) | 0 | 0.4 − 0.5 = −0.1 | 0 | 0 | (0.4, 0.2), −0.5 |
| 2 | (1,1) | 1 | 0.4 + 0.2 − 0.5 = 0.1 | 1 | 0 | (0.4, 0.2), −0.5 |
| 3 | (0,0) | 0 | −0.5 | 0 | 0 | — |
| 3 | (0,1) | 0 | 0.2 − 0.5 = −0.3 | 0 | 0 | — |
| 3 | (1,0) | 0 | 0.4 − 0.5 = −0.1 | 0 | 0 | — |
| 3 | (1,1) | 1 | 0.4 + 0.2 − 0.5 = 0.1 | 1 | 0 | — |
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.
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).
torch.dot(a, b): do 1-D tensors ka dot product (scalar tensor).zip(X, y): X ki har row aur y ki matching value ko saath-saath deta hai.self.b.item(): ek-element tensor ko normal Python number banata hai.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 aayegaOutput
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.
Exam Radar
Galti mat karna
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 regression · matrix form · "linear" ka matlab · logistic regression · linear layers collapse proof · nn.Linear · Lab 1 & Lab 5 code
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.
Chalo numbers se samjhein
Sawaal: w1 = 4, w2 = 15, b = 10. Aaj 50 students guzre (x1 = 5) aur baarish hai (x2 = 1). Kitne cup?
Jawab: ŷ = 45 cups.
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.
Chalo numbers se samjhein
| 5 | 1 |
| 3 | 0 |
| 8 | 1 |
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.
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 + 5 | Haan (affine) | sirf weighted sum + constant |
| x2, x1x2, sin x, |x| | Nahi | power, product, curve, kink |
| σ(x), tanh(x), ReLU(x) = max(0, x) | Nahi | ye activation functions isi liye hain: non-linearity laane ke liye |
| step(x) | Nahi | ek jump hai |
nn.Linear me bias hota hai). Exam me "linear layer" = Wx + b.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.
Chalo numbers se samjhein
Sawaal: z = 0.8x1 − 0.4x2 − 0.2. Customer A: (2, 1). Customer B: (0.5, 2).
Jawab: A → 0.7311 (class 1), B → 0.3543 (class 0).
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
Logistic regression
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)
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.
Chalo numbers se samjhein
| 1 | 2 |
| 0 | 1 |
| 2 | −1 |
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.
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.
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)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 mebias=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 biaskaiming_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>)
TrueOutput haath se check
Jawab: [[−1.5, 3], [1.5, 0]] — PyTorch se match.
p.numel() = tensor me kitne numbers ("number of elements"). sum(p.numel() for p in model.parameters()) = total learnable parameters — exam favourite line.grad_fn=<AddmmBackward0> ka matlab: PyTorch ne yaad rakha ki ye tensor "add + matrix multiply" se bana, taaki baad me gradient nikal sake (Chapter 13). Abhi ignore kar sakte ho.print(layer) layer ka summary dikhata hai: Linear(in_features=3, out_features=2, bias=True).nn.Linear seLab 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]model.weight.data = ...: parameter ki value seedhe replace (gradient tracking ko bypass karke). Shape (1, 2) hi deni hai, kyunki (out, in).(z >= 0) boolean tensor deta hai; .int() use 0/1 integers me badalta hai..squeeze() size-1 dimensions hata deta hai: (4, 1) → (4,).y = [[0],[1],[1],[1]] aur bias = -0.5 → z = [−0.5, 0.5, 0.5, 1.5] → Predicted [0, 1, 1, 1]. Lab ka plot line: x2 = 0.5 − x1.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 + 1Output
MySimpleNN( (linear): Linear(in_features=30, out_features=1, bias=True) (sigmoid): Sigmoid() ) params: 31
class MySimpleNN(nn.Module): apni model class, nn.Module ki saari powers ke saath.super().__init__(): parent (nn.Module) ka constructor. Bhoole to error: layers assign karte hi "cannot assign module before Module.__init__() call".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.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.forward: data ka raasta: linear → sigmoid. Isko forward pass kehte hain (Chapter 8).y_pred = model(X) — model.forward(X) mat likho; model(X) andar se forward() + hooks dono chalata hai.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
y_pred (N, 1) hai, target (N,). .view(-1, 1) target ko (N, 1) banata hai (-1 = "khud calculate kar lo"). Lab notebook warn karta hai ki bhoolne pe (N,) aur (N, 1) broadcast hokar (N, N) ban jaate hain aur loss bekaar. Asal behaviour: naye PyTorch me nn.BCELoss seedha ValueError: Using a target size (torch.Size([N])) that is different to the input size (torch.Size([N, 1]))... deta hai, jabki nn.MSELoss sirf warning deke chupchaap (N, N) broadcast kar deta hai (silent bug). Fix dono me same: shapes match karo..astype(np.float32).zero_grad() → backward() → step(). Gradients by default accumulate hote hain, isliye zero_grad zaroori (backward se pehle kahin bhi).nn.Sigmoid() + nn.BCELoss() ki jagah raw output pe nn.BCEWithLogitsLoss() zyada numerically stable hai (Chapter 11).DiabetesClassifier = nn.Linear(8, 1) + Sigmoid → accuracy ≈ 0.72. Linear model ki seema: har data linearly separable nahi hota.Exam Radar
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).super().__init__() first; layers in __init__; computation in forward; call model(x); model.parameters() → optimizer..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.nn.BCELoss ko probabilities (sigmoid ke baad) chahiye; logits pe BCEWithLogitsLoss.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
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
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.
| x1 | x2 | OR | AND | XOR |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 1 | 0 | 1 |
| 1 | 1 | 1 | 1 | 0 |
Ek line me: XOR = 1 jab inputs different hon. (Isko "x1 ≠ x2" bhi bol sakte ho; binary me XOR = (x1 + x2) mod 2.)
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."
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
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.
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.
| Saal | Ghatna |
|---|---|
| 1943 | McCulloch–Pitts neuron |
| 1957/58 | Rosenblatt ka Perceptron (First Golden Age) |
| 1969 | Minsky & Papert, "Perceptrons": XOR impossible for single layer → First AI winter |
| 1986 | Backprop (Rumelhart, Hinton, Williams) → hidden layers trainable → revival |
| 1989 | Universal Approximation Theorem (Chapter 10) |
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!
Worksheet wali table ("Discover the trick yourself")
| x1 | x2 | h1 = OR | h2 = NAND | y = h1 AND h2 | XOR |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 1 | 0 | 0 ✔ |
| 0 | 1 | 1 | 1 | 1 | 1 ✔ |
| 1 | 0 | 1 | 1 | 1 | 1 ✔ |
| 1 | 1 | 1 | 0 | 0 | 0 ✔ |
Jawab: last column bilkul XOR. Teen perceptrons (do hidden + ek output) ne wo kar diya jo ek nahi kar paaya.
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."
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 |
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.
2-2-1 matlab: 2 inputs, 2 hidden neurons, 1 output. Lab 1 ke weights:
Chaaron inputs pe poora verification
| x | zh1 = x1 + x2 − 0.5 | h1 | zh2 = −x1 − x2 + 1.5 | h2 | zout = h1 + h2 − 1.5 | ŷ |
|---|---|---|---|---|---|---|
| (0,0) | −0.5 | 0 | 1.5 | 1 | 0 + 1 − 1.5 = −0.5 | 0 ✔ |
| (0,1) | 0.5 | 1 | 0.5 | 1 | 1 + 1 − 1.5 = 0.5 | 1 ✔ |
| (1,0) | 0.5 | 1 | 0.5 | 1 | 0.5 | 1 ✔ |
| (1,1) | 1.5 | 1 | −0.5 | 0 | 1 + 0 − 1.5 = −0.5 | 0 ✔ |
Jawab: ŷ = [0, 1, 1, 0] = XOR. Network ne solve kar diya.
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):
| x1 | x2 | a1 (OR) | a2 (AND) | ŷ = step(a1 − a2 − 0.5) |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | step(0 − 0 − 0.5) = 0 |
| 0 | 1 | 1 | 0 | step(1 − 0 − 0.5) = 1 |
| 1 | 0 | 1 | 0 | step(1 − 0 − 0.5) = 1 |
| 1 | 1 | 1 | 1 | step(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."
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
| x | zOR = 20x1 + 20x2 − 10 | hOR | zNAND = −20x1 − 20x2 + 30 | hNAND | zout = 20hOR + 20hNAND − 30 | σ(zout) |
|---|---|---|---|---|---|---|
| (0,0) | −10 | 0.0000454 | 30 | ≈ 1.0000 | ≈ −9.9991 | 0.0000454 ≈ 0 |
| (0,1) | 10 | 0.99995 | 10 | 0.99995 | ≈ 9.9982 | 0.99995 ≈ 1 |
| (1,0) | 10 | 0.99995 | 10 | 0.99995 | ≈ 9.9982 | 0.99995 ≈ 1 |
| (1,1) | 30 | ≈ 1.0000 | −10 | 0.0000454 | ≈ −9.9991 | 0.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]
x[:, 0:1] vs x[:, 0]: slice 0:1 shape (4, 1) rakhta hai (column), jabki x[:, 0] (4,) de deta. Lab (4, 1) chahta hai taaki torch.cat(..., dim=1) se columns jud sakein.torch.cat([a, b], dim=1): tensors ko column-wise jodta hai (dim=0 hota to rows ke neeche).def step(x): return (x >= 0).float() aur weights OR (1, 1, −0.5), NAND (−1, −1, 1.5), AND (1, 1, −1.5) → hidden [[0 1] [1 1] [1 1] [1 0]], predictions [0, 1, 1, 0].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).
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
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
Galti mat karna
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
Definition · terminology · Prince (UDL) notation · ReLU joints · piecewise-linear functions · activation pattern · multiple inputs/outputs · parameter counting · capacity · nn.Module & nn.Sequential
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.
| Term | Matlab |
|---|---|
| Input layer | Raw 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 layer | Beech 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 layer | Final prediction. Size problem pe depend: binary → 1 neuron (sigmoid), K classes → K neurons (softmax), regression → 1 neuron (linear, koi activation nahi). |
| Neuron / unit / node | Ek circle: weighted sum + bias + activation. "Hidden unit" = hidden layer ka neuron. |
| Fully connected / dense | Ek layer ka har neuron agli layer ke har neuron se juda. PyTorch me nn.Linear. |
| Pre-activation z | Activation 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. |
| Width | Ek layer me kitne neurons (hidden units), Prince isko D likhte hain. Ye hyperparameter hai (hum chunte hain, training nahi). |
| Depth | Kitni hidden layers (Prince: K). Shallow = 1. |
| Parameters | Saare 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
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:
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.
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.
| Region | Activation pattern (on units) | Slope = Σ φdθd1 (active) | Check |
|---|---|---|---|
| x < 0.5 | h2 only | 0.5 × (−1) = −0.5 | 0.6 → 0.35 over 0.5 ✔ |
| 0.5 < x < 1 | h1, h2 | 1×1 + 0.5×(−1) = +0.5 | 0.35 → 0.6 ✔ |
| 1 < x < 1.5 | h1 only | 1 × 1 = +1.0 | 0.6 → 1.1 ✔ |
| x > 1.5 | h1, h3 | 1×1 + (−2)×1 = −1.0 | 1.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.
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.
Chalo numbers se samjhein
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).
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.
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 menn.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
self.hidden)super().__init__() zaroorinn.Sequential
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 = 10Output
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>)
10Haath wali table se ek-ek number match. Hidden activations ki har row me dekho kaunse units on hain — wahi activation pattern hai.
Exam Radar
nn.Linear(in, out).weight = (out, in).sum(p.numel() for p in model.parameters()); model.named_parameters() gives names + tensors.Galti mat karna
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
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
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.)
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).
Address padhna (Worksheet 04 "worked reading")
Worksheet 04 ka 2-2-1 MLP, har neuron explicitly:
Har neuron alag-alag (Worksheet 04)
| w11 | w12 |
| w21 | w22 |
| x1 |
| x2 |
| b1 |
| b2 |
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.
Sabse chhota network: 1 input, 1 hidden neuron, 1 output. Sigmoid activation dono neurons pe, squared-error loss.
Chalo numbers se samjhein — x = 2, y = 1, w₁ = 0.5, b₁ = 0, w₂ = 0.5, b₂ = 0
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.
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."
Step-by-step (activity ke 7 steps)
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.
| 0.1 | 0.2 |
| 0.3 | 0.4 |
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)
Row convention (PyTorch, NumPy code)
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
| 1 | 2 |
| 0 | 1 |
| 2 | 0 |
| 0.1 | 0.2 |
| 0.3 | 0.4 |
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.
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:
| Step | Operation | Weight / bias shape | Output shape | Params |
|---|---|---|---|---|
| Input | X | — | (64, 784) | 0 |
| Layer 1 | Z1 = XW1T + b1 | W₁ (256, 784), b₁ (256,) | (64, 256) | 784·256 + 256 = 200,960 |
| A1 = ReLU(Z1) | — | (64, 256) | 0 | |
| Layer 2 | Z2 = A1W2T + b2 | W₂ (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 |
| Total | 235,146 |
out_features = agli layer ka in_features. Galat diya to error: RuntimeError: mat1 and mat2 shapes cannot be multiplied (64x256 and 512x128).Deep network me kuch naya nahi: har layer pe "linear step + activation" repeat karo. Algorithm:
Forward pass algorithm (pseudo-code)
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.clone(): tensor ki alag copy (taaki model ke weights aur hamare W1 ek hi memory share na karein).X @ W.T + b kiya — isliye manual row-convention result se allclose 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
W[1][0] (0-indexing).Galti mat karna
W @ X likhna jab X ki rows samples hon — shape error; sahi X @ W.T (ya bas layer(X)).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
Step · Sigmoid · Tanh · ReLU · Leaky ReLU · PReLU · ELU · Softmax
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).
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).
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.
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:
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.
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:
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
Galti mat karna
Dimaag me aise baitha lo
Ruler pe ruler = ruler. Mod chahiye? Activation lagao.
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.
| Property | Matlab kya | Kyun chahiye |
|---|---|---|
| Non-linear | Poora function ek seedhi line na ho | Warna layers collapse ho jaati hain (9.1) |
| Differentiable (kam se kam almost everywhere) | Har point (ya lagbhag har point) par slope defined ho | Gradient descent ko f′(z) chahiye. ReLU sirf z = 0 par fail hota hai — chalta hai. |
| Non-zero, non-saturating gradient | Bade |z| par bhi slope 0 ke paas na pahunche | Saturation = vanishing gradient = deep layers seekhna band |
| Zero-centered | Outputs positive bhi, negative bhi; average lagbhag 0 | Agli layer ke weights ke gradients ek hi sign ke na hon (zig-zag se bachav, 9.4) |
| Cheap (computationally) | Calculate karna sasta | Crores of neurons, har step par; exp() mehenga, max() sasta |
| Sahi output range (output layer ke liye) | Jaisa answer chahiye waisa range | Probability 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.
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.
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.
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
(z >= 0).float() ka requires_grad False aur grad_fn None hota hai.Galti mat karna
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.
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.
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 | −2 | 0 | 2 | 4 | 6 |
|---|---|---|---|---|---|---|---|
| σ(z) | 0.0025 | 0.0180 | 0.1192 | 0.5 | 0.8808 | 0.9820 | 0.9975 |
| σ′(z) | 0.0025 | 0.0177 | 0.1050 | 0.25 | 0.1050 | 0.0177 | 0.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.
Step-by-step derivation
σ(z) = (1 + e−z)−1 likho. Ab chain rule (bahar ka power, andar ka derivative):
σ′(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.
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.
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])
torch.sigmoid(z) — function; seedha tensor par lagao. (F.sigmoid bhi bilkul same kaam karta hai — sirf bahut purane PyTorch versions me uspe deprecation warning aata tha, ab nahi; standard choice torch.sigmoid hi hai.)nn.Sigmoid() — ek layer (module) object; koi argument nahi, koi learnable parameter nahi. Pehle object banao, phir call karo: nn.Sigmoid()(z). nn.Sequential ke andar isi ko daalte hain.z.grad me autograd ka derivative aaya aur σ(1 − σ) formula se exactly match hua. .sum().backward() isliye kyunki backward() ko scalar chahiye; sum ka derivative har element ke liye 1 hai, to har z.grad[i] = σ′(zi).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.
Chalo numbers se samjhein — worksheet 03, Part 2
Sawaal: x = 1, w = 6, b = 0, y = 0, α = 1. (Useful: e−6 = 0.00247875)
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.)
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:
Chalo numbers se samjhein
a = (0.8, 0.3) (sigmoid outputs, dono positive).
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.
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).
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)
Nuksaan (cons)
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
torch.sigmoid(x), nn.Sigmoid() (no parameters).Galti mat karna
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).
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.
Step-by-step derivation
u = ez − e−z aur v = ez + e−z, to tanh = u/v.
tanh′(z) = 1 − tanh2(z). At z = 0: 1 − 02 = 1 = maximum. Sigmoid ke 0.25 se 4 guna bada peak.
Proof, line by line
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 ✓.
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.
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.
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
Nuksaan
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
torch.tanh(x), nn.Tanh().Galti mat karna
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.
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.
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).
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.
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 haiOutput
relu : tensor([0.0000, 0.0000, 0.0000, 0.5000, 3.0000]) same? : True grad : tensor([0., 0., 0., 1., 1.])
torch.relu(x) aur F.relu(x) same. nn.ReLU() layer hai; iska ek optional argument inplace=False (default) hai — True karne pe input tensor ko hi overwrite karta hai (memory bachata hai).torch.clamp(z, min=0) = 0 se neeche sab 0. Scratch implementation ke liye sabse simple.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):
Chalo numbers se samjhein — worksheet 03, Part 5
Sawaal: x = 1, w = −3, b = 0, y = 1, α = 1.
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])
torch.linspace(-2, 2, 100) = −2 se 2 tak 100 barabar doori wale numbers, shape (100,). .unsqueeze(1) → (100, 1). .repeat(1, 3) → (100, 3): har sample me teeno features same value t.a_hidden[:, i] = neuron i ka poora column. torch.all(... == 0) = kya saare 100 samples pe output 0 hai? Haan → completely dead (is data ke liye).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
torch.relu, F.relu, nn.ReLU(inplace=False), torch.clamp(z, min=0).Galti mat karna
Dimaag me aise baitha lo
ReLU = one-way valve: ulta pressure → 0, seedha → jitna aaya utna. Valve jam = dead neuron.
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.
negative_slope, default 0.01). Hyperparameter — haath se choose.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.
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.
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])
F.leaky_relu(x, negative_slope=0.01) functional; nn.LeakyReLU(negative_slope=0.01) layer — pehla positional argument hi slope hai, isliye nn.LeakyReLU(0.01).torch.where(cond, a, b): jahan cond True wahan a, warna b — piecewise functions likhne ka standard tareeka.nn.Parameter hai (prelu.weight), isliye parameters() me count hota hai: 1. Output: −3 × 0.25 = −0.75.Exam Radar
negative_slope=0.01 default.nn.PReLU() default init 0.25, 1 parameter. Con: extra parameters.Galti mat karna
Dimaag me aise baitha lo
Leaky = valve me 1% leak. PReLU = leak kitna ho, wo pipe khud seekhe.
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.
alpha=1.0 default.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."
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)
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/∂w | w: −3 → | Verdict |
|---|---|---|---|---|---|
| ReLU | 0 | 0 | 0 | −3 | Dead |
| Identity | −3 | 1 | −4 | 1 | Alive, par network ek seedhi line |
| Leaky ReLU | −0.03 | 0.01 | −0.0103 | −2.9897 | Alive, crawling |
| ELU | −0.950213 | 0.049787 | −0.097095 | −2.902905 | Alive, 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
Nuksaan
Use: hidden layers jahan bahut saare ReLU units mar rahe hon, ya jahan faster convergence chahiye.
Exam Radar
Galti mat karna
Dimaag me aise baitha lo
ELU = soft-close drawer: positive pe ReLU, negative pe smooth landing at −α.
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.
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.
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.
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.
Proof
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.
Chalo numbers se samjhein — overflow
Sawaal: logits [1000, 1001, 1002].
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.
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.
| Problem | Output activation | Output units | Example |
|---|---|---|---|
| Binary (2 class, ek sawaal) | Sigmoid | 1 | Spam ya nahi |
| Multi-class, single-label (exactly ek class sahi) | Softmax | K (har class ka ek) | Digit 0–9 → 10 units |
| Multi-label (kai classes ek saath sahi ho sakti) | Sigmoid har unit pe (independent) | K | Photo me cat bhi, dog bhi |
Proof: 2-class softmax = sigmoid of the difference
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 ✓.
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? TrueJacobian 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).
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.
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
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
dim=0 lagana: error nahi aayega, answer galat aayega.nn.CrossEntropyLoss se pehle softmax lagana = double softmax.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.
| Function | Formula | Range | f(0) | Derivative | Max f′ | Zero-centered? | Saturates? | Cost | Main problem | Typical use |
|---|---|---|---|---|---|---|---|---|---|---|
| Step / threshold | 1 if z ≥ 0, else 0 | {0, 1} | 1 | 0 (undefined at 0) | 0 | No | Flat everywhere | Cheap | Train hi nahi hota | Historical (1958 perceptron) |
| Linear / identity | z | (−∞, ∞) | 0 | 1 | 1 | Yes | No | Cheapest | Network linear ho jaata hai | Regression output layer |
| Sigmoid | 1/(1 + e−z) | (0, 1) | 0.5 | σ(1 − σ) | 0.25 | No | Yes, both ends | exp | Vanishing gradient, zig-zag | Binary output, multi-label, LSTM gates |
| Tanh | (ez − e−z)/(ez + e−z) | (−1, 1) | 0 | 1 − tanh2 | 1 | Yes | Yes, both ends | exp | Vanishing gradient | RNN/LSTM states, old nets |
| ReLU | max(0, z) | [0, ∞) | 0 | 0 or 1 (PyTorch: 0 at z = 0) | 1 | No | No (z > 0) | Very cheap | Dying ReLU | Default hidden layer (CNN, MLP) |
| Leaky ReLU | max(αz, z), α = 0.01 | (−∞, ∞) | 0 | α or 1 | 1 | ~ (slide: ~Yes) | No | Very cheap | α hand-tuned, tiny negatives | Hidden layers, GANs |
| PReLU | max(αz, z), α learned | (−∞, ∞) | 0 | α or 1 | 1 (if α ≤ 1) | ~ | No | Cheap + params | Extra parameters | Deep CNNs |
| ELU | z; α(ez − 1) for z ≤ 0 | (−α, ∞) | 0 | 1 or αez (= ELU + α) | 1 (α = 1) | ~ (mean near 0) | Negative side → −α | exp (z < 0) | Slower than ReLU | Hidden layers, faster convergence |
| Softmax | ezi/Σezj | (0, 1), sum = 1 | 1/K if all z = 0 | si(δij − sj) | N/A | N/A | N/A | exp + sum | Overflow (use max trick) | Multi-class output only |
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.
| Jagah / Task | Activation | Output units | Loss (PyTorch) |
|---|---|---|---|
| Hidden layers (default) | ReLU | any | — |
| Hidden, units mar rahe hon | Leaky ReLU / ELU / PReLU | any | — |
| Hidden in RNN/LSTM, old nets | tanh (states), sigmoid (gates) | — | — |
| Regression (number) | None (linear); ReLU agar negative ho hi nahi sakta | 1 (per target) | nn.MSELoss / nn.L1Loss |
| Binary classification | Sigmoid | 1 | nn.BCELoss (after sigmoid) ya nn.BCEWithLogitsLoss (raw logit) |
| Multi-class (single label) | Softmax | K | nn.CrossEntropyLoss — raw logits do, softmax nahi |
| Multi-label | Sigmoid per class | K | nn.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.])
nn.Sequential me nn.Linear + nn.ReLU() pairs. Activation layers ke koi parameters nahi hote, sirf Linear ke.dim=1 softmax, har row ka sum 1.forward raw logits return karta hai aur nn.CrossEntropyLoss / nn.BCEWithLogitsLoss andar stable tareeke se softmax/sigmoid sambhal lete hain. Probabilities sirf prediction/report ke time chahiye. (Binary values ~0.5 isliye kyunki model untrained hai.)Exam Radar
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
Network capacity · UAT (Cybenko 1989, Hornik 1991) · bumps & ReLU pieces · linear regions · folding · depth vs width trade-off
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?
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.
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
Galti mat karna
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.
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.
Jawab: andar ≈ 0.99, bahar ≈ 0 — ek saaf bump. k aur bada karo to aur bhi rectangle jaisa.
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.
| Bumps N | Hidden units (2N) | Max error (ideal steps), sin on [0, π] |
|---|---|---|
| 4 | 8 | 0.3827 |
| 8 | 16 | 0.1951 |
| 16 | 32 | 0.0980 |
| 32 | 64 | 0.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.
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).
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.
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.
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
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
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.
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
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.
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.
Exam Radar
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."
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].
Depth ke saath regions exponentially (2k) badhte hain; width ke saath sirf linearly (D + 1).
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.
Chalo numbers se samjhein — same budget, shallow vs deep
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.)
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.
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.
Go Deeper (modern trend)
Pros
Cons
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
Cons
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".
| Aspect | Deep (zyada layers) | Wide (zyada units/layer) |
|---|---|---|
| Regions (1-D ReLU) | up to (D + 1)K — exponential in K | D + 1 — linear in D |
| Parameter efficiency | High | Low (params ~ D2) |
| Features | Hierarchical, reusable (abstraction) | Flat, ek hi level (memory) |
| Optimization | Mushkil (vanishing/exploding) | Aasaan (smoother) |
| Parallelism / latency | Sequential, zyada latency | Parallel, GPU friendly |
| Overfitting | Generalization aksar better | Risk zyada |
| Best for | Images, speech, text — compositional data | Simple / small tabular problems |
| Fixes needed | ReLU, BN, He init, ResNet skips | Regularization (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).
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
model.parameters() saare learnable tensors (weights + biases) deta hai; p.numel() = us tensor me kitne numbers. Sum = total params. Exam me "count params" sawaal isi formula se: in × out + out per Linear layer. ReLU layers ke 0 params.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
Galti mat karna
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 vs Cost · Loss landscape · Maximum Likelihood · NLL · MSE · MAE · Huber · Binary & Categorical Cross-Entropy · Hinge · PyTorch loss classes
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.
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.
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.
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)
loss.backward() sirf ek single number par chalta hai.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
Galti mat karna
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.
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.
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
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.
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
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.
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:
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?
Jawab: p = 0.7 likelihood ko maximum karta hai. Yahi MLE hai.
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
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
Galti mat karna
Dimaag me aise baitha lo
Data ne jo dikhaya, use sabse believable banane wala parameter hi MLE. 7 heads in 10 → 0.7.
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?
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 numberOutput
torch.float32 tensor(0.) tensor(-115.1293)
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.
torch.log bhi natural log haimaximize L(θ) ⟺ maximize ln L(θ) ⟺ minimize −ln L(θ) = NLL
Coin wapas
NLL(p) = −[7 ln p + 3 ln(1 − p)]:
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
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.
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)
| Task | y kaisa hai | Distribution | Network kya predict karta hai | Output activation (kyun) | NLL banta hai |
|---|---|---|---|---|---|
| Regression | koi bhi real number | Normal / Gaussian 𝒩(μ, σ²) | mean μ = ŷ | koi nahi / linear (μ koi bhi real ho sakta hai) | MSE |
| Robust regression | real number, outliers wala | Laplace | centre μ = ŷ | koi nahi / linear | MAE |
| Binary classification | 0 ya 1 | Bernoulli(p) | p = P(y = 1) = ŷ | sigmoid (p ko (0, 1) me rakhna hai) | Binary Cross-Entropy |
| Multi-class | K classes me se ek | Categorical(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
Dimaag me aise baitha lo
"Distribution chuno, network se uska parameter bolwao, NLL ghatao." Teen line, chaar loss functions.
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.
Gaussian NLL → 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.
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
Dimaag me aise baitha lo
Bell curve ke exp ke andar (y − μ)² baitha hai. Log lo, exp gaya, square bacha — MSE.
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.
Derivation step by step
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.
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.
Chalo numbers se samjhein
Sahi class = dog, y = [0, 1, 0]. Model ka softmax output ŷ = [0.2, 0.7, 0.1].
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 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
Dimaag me aise baitha lo
Bell (Gaussian) → square → MSE. Nukeela tent (Laplace) → absolute → MAE. Sikka → BCE. Dice → CE.
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.
Chalo numbers se samjhein (example 1)
Sawaal: y = [3, −0.5, 2, 7], ŷ = [2.5, 0.0, 2, 8]. MSE aur RMSE nikaalo.
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)
| i | True y | Predicted ŷ | Residual (y − ŷ) | (y − ŷ)² | total ka hissa |
|---|---|---|---|---|---|
| 1 | 3.0 | 2.8 | 0.2 | 0.04 | 0.45% |
| 2 | 5.0 | 4.5 | 0.5 | 0.25 | 2.83% |
| 3 | 2.5 | 4.0 | −1.5 | 2.25 | 25.48% |
| 4 | 8.0 | 8.2 | −0.2 | 0.04 | 0.45% |
| 5 | 6.0 | 3.5 | 2.5 | 6.25 | 70.78% |
| Sum → | 8.83 | 100% | |||
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."
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 demo
Example 1 me aakhri target galti se 7 ki jagah 27 likha gaya (data entry mistake). Prediction wahi [2.5, 0, 2, 8].
Ek outlier ne MSE ko 0.375 se 90.375 kar diya. Training ab sirf us ek point ko khush karne me lag jaayegi.
Socho model bilkul buddhu hai — har input ke liye ek hi number c bolta hai. MSE ke hisaab se best c kya?
Derivation
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
nn.MSELoss(), default reduction='mean'; options 'sum', 'none'. a.k.a. L2 loss.Galti mat karna
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.
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.
Chalo numbers se samjhein
Same example: y = [3, −0.5, 2, 7], ŷ = [2.5, 0.0, 2, 8].
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
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/nOutput
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).
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".
Derivation
MAE ka best constant = median → MAE-trained model conditional median predict karta hai.
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.
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
nn.L1Loss() (naam me "MAE" nahi hai!).Galti mat karna
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.
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.
Chalo numbers se samjhein (δ = 1)
Huber outlier par MAE ki tarah shaant (4.6875), aur chhoti galtiyon par MSE ki tarah smooth.
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)
nn.HuberLoss(delta=1.0) — upar wala formula, default δ = 1.0.nn.SmoothL1Loss(beta=1.0) — formula: 0.5r²/β agar |r| < β, warna |r| − 0.5β. Default β = 1.0. Rishta: HuberLoss(δ) = δ × SmoothL1Loss(β = δ). δ = β = 1 par dono bilkul same (isliye upar teeno numbers same aaye). δ = 2 par residuals [1, 3] ke liye Huber = 2.25, SmoothL1 = 1.125. Smooth L1 object detection (Fast R-CNN ke bounding-box regression) se famous hua.Exam Radar
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.
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
| Metric | Clean data | Outlier data | Change |
|---|---|---|---|
| MSE | 4.4830 | 6.9770 | +2.4940 (+55.6%) |
| MAE | 1.8151 | 1.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 lossPlot 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."
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.
Teacher ke numbers (slide 23)
0.99 → 0.01 se 0.01 → 4.6: probability 99 guna giri, surprise 458 guna badha. Confident galti ki saza sabse bhaari.
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.
Dimaag me aise baitha lo
Cross-entropy = sahi answer dekh kar model ka surprise. Confident aur sahi → 0. Confident aur galat → aasmaan.
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.
Chalo numbers se samjhein — single cases (y = 1)
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?
BCE = 0.3375.
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 lossOutput
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
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.
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.
Teacher ka example (slide 26)
Prediction: Cat 0.7, Dog 0.2, Bird 0.1. True class: Cat → y = [1, 0, 0].
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?
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
Dimaag me aise baitha lo
CE = −log(sahi class ki probability). Gradient = predicted − true. Bas do line.
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.
Chain rule
Chain rule
Log-sum-exp form se
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.
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/∂z | BCE: ∂L/∂z | BCE / MSE |
|---|---|---|---|---|---|
| 0 | 0.5 | 0.25 | 0.125 | 0.5 | 4 |
| 2 | 0.880797 | 0.104994 | 0.092478 | 0.880797 | 9.5 |
| 4 | 0.982014 | 0.017663 | 0.017345 | 0.982014 | 56.6 |
| 6 | 0.997527 | 0.002467 | 0.002460 | 0.997527 | 405 |
| 8 | 0.999665 | 0.000335 | 0.000335 | 0.999665 | 2983 |
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
Exam Radar
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.
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.
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.
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 ~20Output
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.
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
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.Dimaag me aise baitha lo
Probability banao hi mat — logits seedha loss ko do. "WithLogits" aur "CrossEntropy" andar se safe kaam kar lenge.
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 · 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.
.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.
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)
nn.MSELoss / nn.L1Loss: sirf UserWarning, phir (3, 3) broadcasting → 0.3144 (sahi 0.0700). Training chalegi, par galat cheez seekhegi — sabse khatarnaak, kyunki crash nahi hota.nn.BCELoss / nn.BCEWithLogitsLoss: seedha ValueError ("Using a target size … that is different to the input size …" / "Target size … must be the same as input size …"). Kuch purane versions sirf warning dete the — isliye Lab 5 isko "silent broadcasting bug" kehta hai..view(-1, 1) (ya .unsqueeze(1)) se (N, 1) karo, ya prediction ko .squeeze(1) se (N,) karo (Lab 4 style). Accuracy nikaalte waqt bhi same dhyaan (Lab 5 eval me y_test_tensor.view(-1, 1)).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 step | Loss | Nateeja |
|---|---|---|
| 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
nn.Linear(features, 1) → output (N, 1) → target .view(-1, 1).torch.sigmoid(logits), class = (prob > 0.5).float() (logit > 0 ka bhi yahi matlab).nn.BCEWithLogitsLoss(pos_weight=torch.tensor([k])).Galti mat karna
nn.Sigmoid() + nn.BCEWithLogitsLoss() — double sigmoid, koi error nahi.RuntimeError: Found dtype Long but expected Float (BCEWithLogitsLoss: "result type Float can't be cast to the desired output type Long"). .float() karo.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).
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() — 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.
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 gayaOutput
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.
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).
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
target.long(). Lab 4 me isi liye digits.target.astype(np.int64) kiya tha.target.squeeze(1) ya target.view(-1). (Dhyaan: BCE me (N, 1) chahiye tha, CE me (N,) — ulta!)target.argmax(dim=1) se indices banao.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 liyeOutput
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.reduction='mean', ignore_index=-100, label_smoothing=0.0, weight=None.logits.argmax(dim=1); probabilities = torch.softmax(logits, dim=1).nn.Linear(f, 2) + CrossEntropyLoss + long targets.Galti mat karna
nn.Softmax + nn.CrossEntropyLoss = double softmax, silent bug, loss floor (3 classes: 0.5514).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.
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.
Chalo numbers se samjhein
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)
torch.clamp(1 - y * s, min=0).mean() likho. Multi-class SVM hinge = nn.MultiMarginLoss(). nn.HingeEmbeddingLoss naam se milta-julta hai par alag cheez hai (similarity/embeddings ke liye) — confuse mat hona.Exam Radar
Dimaag me aise baitha lo
Hinge = "40+ laao to chain". Margin ≥ 1 → loss 0. Labels ±1, output raw score.
| Loss | Formula | Task | Output activation | PyTorch class | Input expects | Target expects |
|---|---|---|---|---|---|---|
| MSE (L2) | (1/n)Σ(y − ŷ)² | Regression | none (linear) | nn.MSELoss() | float predictions, koi bhi real | float, same shape |
| MAE (L1) | (1/n)Σ|y − ŷ| | Regression with outliers | none (linear) | nn.L1Loss() | float predictions | float, same shape |
| Huber / Smooth L1 | ½r² if |r| ≤ δ, else δ(|r| − ½δ) | Robust + smooth regression | none (linear) | nn.HuberLoss(delta=1.0), nn.SmoothL1Loss(beta=1.0) | float predictions | float, 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 logits | same, sigmoid andar | Binary (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 ŷcorrect | Multi-class (ek label) | none (softmax loss ke andar) | nn.CrossEntropyLoss() | raw logits (N, C) | class indices (N,), long, 0 … C−1 |
| NLL | −log pcorrect | Multi-class | LogSoftmax (model me) | nn.NLLLoss() | log-probabilities (N, C) | class indices (N,), long |
| Hinge | max(0, 1 − y·ŷ) | Binary, margin (SVM) | none (raw score) | manual torch.clamp(1 - y*s, min=0).mean(); multi-class nn.MultiMarginLoss() | raw scores | labels −1/+1 |
| Dimension | MSE | MAE | Cross-Entropy | Hinge |
|---|---|---|---|---|
| Formula | (1/n)Σ(y − ŷ)² | (1/n)Σ|y − ŷ| | −Σ y log ŷ | max(0, 1 − yŷ) |
| Task type | Regression | Regression | Classification | Classification |
| Output layer | Linear | Linear | Sigmoid / Softmax | Raw score |
| Label format | Continuous y | Continuous y | Probability / 1-hot | y ∈ {−1, +1} |
| Outlier sensitivity | High | Low | Medium | Medium |
| Differentiable? | Yes (smooth) | No at r = 0 | Yes (smooth) | No at margin y·ŷ = 1 |
| Gradient at error | Linear in residual | Constant ±1 | ŷ − y (probabilistic) | −y if y·ŷ < 1, else 0 |
| Optimal prediction | Conditional mean | Conditional median | Probability of class | Maximum margin |
| Penalises confident wrongs? | Quadratically | Linearly | "Exponentially" (−log p → ∞) | Linearly (margin) |
| Classic use case | Linear regression, CNNs | Robust regression | Neural nets, LLMs | SVMs, ranking |
nn.BCEWithLogitsLoss (logits) ya sigmoid + nn.BCELoss. Labels 0/1 float.nn.CrossEntropyLoss (logits + long indices).nn.BCEWithLogitsLoss with (N, C) float multi-hot targets.Exam Radar — poore chapter ke "pakke" facts
nn.CrossEntropyLoss = LogSoftmax + NLLLoss; logits (N, C) + long (N,). nn.BCEWithLogitsLoss = Sigmoid + BCELoss (stable). nn.BCELoss log ko −100 par clamp.reduction='mean' sab losses me. nn.L1Loss = MAE. HuberLoss default δ = 1.0, SmoothL1Loss default β = 1.0.Galti mat karna — master list
.view(-1, 1) / .squeeze(1) (Lab 5).log_softmax.reduction='none' par seedha backward() → "grad can be implicitly created only for scalar outputs".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
Gradient kyun · Derivative refresher · Computational graph · Local × Upstream · Chain rule (single + multivariable) · Gate patterns · Sigmoid neuron graph · Forward vs Backward pass · PyTorch autograd
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:
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:
| Sawaal | Gradient se jawab | Example: ∂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 |
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.
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.
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
Galti mat karna
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.)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.
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:
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?
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
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:
| Operation | Forward | Local derivative |
|---|---|---|
| Add | z = x + y | ∂z/∂x = 1, ∂z/∂y = 1 |
| Multiply | z = x · y | ∂z/∂x = y, ∂z/∂y = x |
| Add constant | z = x + c | 1 |
| Scale by constant | z = ax | a |
| Square | z = x2 | 2x |
| Exponential | z = ex | ex |
| Reciprocal | z = 1/x | −1/x2 |
| Log | z = ln x | 1/x |
| Sigmoid | z = σ(x) | σ(x)(1 − σ(x)), max 0.25 at x = 0 |
| Tanh | z = tanh(x) | 1 − tanh2(x), max 1 at x = 0 |
| ReLU | z = max(0, x) | 1 if x > 0, else 0 |
| Max | z = max(x, y) | 1 for the bigger one, 0 for the other |
| Half squared error | L = ½(ŷ − y)2 | ∂L/∂ŷ = ŷ − y |
Exam Radar
Dimaag me aise baitha lo
Derivative = "kitna hilaya" ke upar "kitna hila". Partial derivative = baaki sabko pause karke sirf ek ko hilao.
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).
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
if/for model ke andar chal jata hai. Purana TensorFlow 1.x static graph ("define-and-run") use karta tha.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.
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):
Backward pass (right se left, gradients nikalo):
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!
Galti mat karna
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:
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.
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:
Backward (upstream × local, har node pe):
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:
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
Dimaag me aise baitha lo
"Aaya hua × apna = aage bhejo." Upstream × local = downstream. Bas yahi ek line poora backprop hai.
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.
Chalo numbers se samjhein
Sawaal: y = (3x + 1)2. x = 2 pe dy/dx nikalo — chain rule se.
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
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.
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.
Chalo numbers se samjhein
Sawaal: x = 2. Do branches: u = x2, v = 3x, aur f = u · v. df/dx nikalo.
Jawab: df/dx = 36. Agar sirf ek raasta lete to 24 ya 12 aata — dono galat.
Galti mat karna
Dimaag me aise baitha lo
"Raaste ke andar guna, raaston ke beech jod." (Along a path multiply, across paths add.)
Kahani
Factory me chaar type ke worker hain, aur complaint (gradient) aane par har type ka reaction fix hai:
| Gate | Forward | Backward (upstream = g) | Yaad rakhne ka naam |
|---|---|---|---|
| Add | z = x + y | ∂L/∂x = g, ∂L/∂y = g | Distributor — same gradient sabko |
| Multiply | z = x · y | ∂L/∂x = g · y, ∂L/∂y = g · x | Swapper — doosre input se scale |
| Max | z = max(x, y) | bade input ko g, chhote ko 0 | Router — sirf winner ko |
| Copy / branch | x do jagah use | ∂L/∂x = g1 + g2 | Adder — gradients jodo |
| ReLU | max(0, x) | g if x > 0 else 0 | Max 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:
Backward:
Jawab: ∂f/∂x = −8, ∂f/∂y = 6, ∂f/∂z = 2, ∂f/∂w = 0.
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 = 2aOutput
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
.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.
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):
Output f ≈ 0.73.
Backward pass (upstream × local, right se left):
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].
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
Galti mat karna
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.
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
Backward pass
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 & store | 2nd 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)2 | Parameters: ∂ℓ/∂β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
torch.no_grad()) ye storage nahi hota → kam memory.Dimaag me aise baitha lo
Jaate waqt nishaan lagao (forward + cache), aate waqt nishaan padho (backward). Nishaan nahi to wapsi mehngi.
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.
| Cheez | Matlab | Note |
|---|---|---|
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 graph | Graph forward pass chalte-chalte banta hai (define-by-run), har iteration me naya | Backward ke baad graph free ho jaata hai (memory bachane ke liye) |
grad_fn | Kis operation ne ye tensor banaya — uska "backward function" node | AddBackward0, MulBackward0, SigmoidBackward0...; leaf tensor ka grad_fn = None |
| leaf tensor | Jo 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() / .grad | backward() graph ulta chala ke gradients nikalta hai; result leaf ke .grad me add hota hai | backward() 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.
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.
x.grad.zero_(): x.grad = 0PyTorch · 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 6Output
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.
with torch.no_grad(): — is block ke andar koi graph nahi banta. Use: (1) evaluation/inference (memory + time bachta hai), (2) manual weight update w -= lr * w.grad (update ko graph me record nahi karna).t.detach() — same data wala naya tensor jo graph se kata hua hai (requires_grad=False). Use: value ko numpy me badalna (t.detach().numpy()), plotting, ya kisi value ko "constant" treat karna.loss.item() — scalar tensor se plain Python number; logging ke liye (graph pakad ke nahi rakhta).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)
.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>.optimizer.zero_grad() (ya tensor.grad.zero_()) har step pe.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.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 (-=)..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
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
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):
Teacher ki slide ke do "Intuitive Observations" yaad rakho — poora algorithm inhi do baaton pe khada hai:
Exam Radar
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.
Initial values: x = 2, y = 1, w1 = 0.5, b1 = 0, w2 = 0.5, b2 = 0, learning rate η = 0.1.
Step 1 — Forward propagation
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.
Step 3 — Gradients (chain = local derivatives ka product)
w₂ — path: w2 → z2 → ŷ → L
b₂ — same path, last link 1:
w₁ — lamba path: w1 → z1 → h → z2 → ŷ → L
b₁ — same lamba path, last link 1:
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.
Step 4 — Update (η = 0.1) aur verification
Rule: new parameter = old parameter − η × gradient
Naye parameters se dobara forward pass:
Loss 0.083895 → 0.082351. Ghata! Gradients ne parameters ko sahi direction me hilaya. Training = yahi step hazaaron baar.
| Parameter | Gradient | Initial | Updated |
|---|---|---|---|
| w1 | −0.01948 | 0.50000 | 0.50195 |
| b1 | −0.009738 | 0.00000 | 0.000974 |
| w2 | −0.07242 | 0.50000 | 0.50724 |
| b2 | −0.09906 | 0.00000 | 0.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).
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
Galti mat karna
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).
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.
Part 2 — Forward propagation
Checkpoint: prediction target se kam hai → expect karo ki chhe weights mostly badhenge.
Part 3 — Output weights w₅, w₆
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₂
Checkpoint: x₂ = 2x₁, isliye ∂L/∂w₂ = 2 × ∂L/∂w₁ (−0.016234 = 2 × −0.008117).
Part 5 — h₂ branch: w₃, w₄
Checkpoint: phir se x₂ = 2x₁ ⇒ ∂L/∂w₄ = 2 × ∂L/∂w₃.
| Weight | Old value | Full chain | Gradient | Updated value |
|---|---|---|---|---|
| w1 | 0.100000 | δ3 · w5 · h1(1−h1) · x1 | −0.008117 | 0.100812 |
| w2 | 0.200000 | δ3 · w5 · h1(1−h1) · x2 | −0.016234 | 0.201623 |
| w3 | 0.300000 | δ3 · w6 · h2(1−h2) · x1 | −0.007766 | 0.300777 |
| w4 | 0.400000 | δ3 · w6 · h2(1−h2) · x2 | −0.015533 | 0.401553 |
| w5 | 0.500000 | δ3 · h1 | −0.043000 | 0.504300 |
| w6 | 0.600000 | δ3 · h2 | −0.051829 | 0.605183 |
Part 6 — Verification after one update
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
Galti mat karna
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.
nn.Linear(in, out).weight ka shape bhi (out, in) hai.)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.
| 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 (½) | ŷ − y | g′ = 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.
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.
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.2 | 0.1 |
| 0.3 | 0.2 |
| −0.4 | −0.6 |
b[1] = (0.1, 0.3, 0.1)
Layer 2 (into outputs): rows = output neuron (destination)
| 0.5 | −0.3 | 0.8 |
| −0.6 | 0.4 | 0.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)
Output 1 bahut kam hai (chahiye 1), output 2 bahut zyada hai (chahiye 0).
Output layer — BP1, BP3
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")
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.
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: TrueFull 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 (⊙).
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:
| Cheez | Shape | Formula (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.
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]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 | δ form | Matlab |
|---|---|---|
| ∂ℓ/∂fk | δ[k+1] | pre-activation ka gradient |
| ∂ℓ/∂βk = ∂ℓ/∂fk | ∂L/∂b = δ | bias gradient = δ |
| ∂ℓ/∂Ωk = (∂ℓ/∂fk) hkT | ∂L/∂W = δ aT | error × 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) |
Exam Radar
Galti mat karna
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 = δ."
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:
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?
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.
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.
| Layers n (sigmoid) | Best case 0.25n | Matlab |
|---|---|---|
| 1 | 0.25 | chauthai bacha |
| 2 | 0.0625 | |
| 3 | 0.015625 | |
| 5 | 9.77 × 10−4 | hazaar guna chhota |
| 10 | 9.54 × 10−7 | ~ ek million guna chhota |
| 20 | 9.09 × 10−13 | practically 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.
| Fix | Kaise madad karta hai | Chapter |
|---|---|---|
| ReLU family (ReLU, Leaky ReLU, ELU) | Positive side pe derivative exactly 1 → factor shrink nahi hota | Ch 9 |
| Xavier / He initialization | Weights ka variance aisa ki per-layer signal/gradient factor ≈ 1 (Xavier: tanh/sigmoid, He: ReLU) | Ch 21 |
| Batch Normalization | Har layer ke z ko normalize → saturation zone se bahar, healthy gradients | Ch 20 |
| Residual / skip connections | y = x + F(x) → ∂y/∂x = 1 + F′ — add gate ka "1" gradient highway | Ch 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 hai | Ch 11 |
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-02Padho isko (layer 1 = input ke paas, layer 11 = output layer):
nn.Linear init (uniform ±1/√fan_in) ReLU ke liye thoda chhota hai.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
Galti mat karna
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.
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.
| Layers n | 1.5n |
|---|---|
| 1 | 1.5 |
| 5 | 7.59 |
| 10 | 57.67 |
| 20 | 3325.3 ≈ 3.3 × 103 |
| 30 | 1.92 × 105 |
| 50 | 6.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.
| Fix | Kaise | Chapter |
|---|---|---|
| Gradient clipping (by norm / by value) | Backward ke baad, step se pehle, gradient ka size cap kar do | yahin, neeche |
| Lower learning rate / LR schedule | Chhote steps, feedback loop todo | Ch 15 |
| Proper init (Xavier/He) | Per-layer factor ≈ 1 rakho | Ch 21 |
| Batch Normalization | Activations ka scale control | Ch 20 |
| Weight decay (L2) | Weights ko bada hone se rokta hai | Ch 19 |
| Input scaling | StandardScaler: features ~ mean 0, std 1 | 13.8 |
max_norm ya clip_value), jaise 1.0Chalo 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.
Jawab: by norm → (0.6, 0.8); by value → (0.5, 0.5).
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.
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.
| Vanishing | Exploding | |
|---|---|---|
| Per-layer factor | < 1 (e.g. σ′ ≤ 0.25) | > 1 (e.g. 1.5) |
| Gradient at early layers | ≈ 0 | huge / inf |
| Loss behaviour | plateau, bahut slow | wild jumps, NaN/inf |
| Common cause | sigmoid/tanh saturation, small weights, depth | large weights, RNNs, high LR |
| Main fixes | ReLU, He/Xavier, BN, residual, LSTM/GRU | clipping, lower LR, init, BN, weight decay |
| Clipping helps? | Nahi (chhote gradient ko bada nahi karta) | Haan |
Exam Radar
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) — between loss.backward() and optimizer.step().Galti mat karna
clip_grad_norm_ ka order: zero_grad → backward → clip → step. Clip ko step ke baad likhna bekaar hai.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).
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.
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.
| Line | Kya karti hai | Hata do to? |
|---|---|---|
torch.manual_seed(42) | Random init fix → har run same result | Har run me alag loss/accuracy |
df.drop(columns=[...]) | Non-feature columns (id, khaali column) hatao | id 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 karo | Unscaled features (area ~2000 vs smoothness ~0.1) → bade-chhote gradients, slow/unstable training |
scaler.transform(X_test) | Test pe train wala mean/std lagao | Test pe fit kiya to data leakage (test ki info training me ghus gayi) |
LabelEncoder | 'B'/'M' strings → 0/1 numbers | Strings tensor nahi ban sakti |
.astype(np.float32) | numpy float64 → float32 | RuntimeError: Double vs Float |
super().__init__() | nn.Module ki machinery (parameter registry) chalu | Layers assign karte hi error |
self.linear = nn.Linear(30, 1) | Learnable w (1×30) + b; auto-registered | Koi 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 targets | Loss hi nahi → backward kis cheez ka? |
optim.SGD(model.parameters(), lr=0.1) | Optimizer ko parameters ki list aur step size | step() 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 jaisa | BCELoss 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 banao | Memory + 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 | — |
y_pred = model(X): z = XWT + b, ŷ = σ(z). Autograd graph banata hai aur X, ŷ jaisi values cache karta hai.loss = loss_function(...): ek scalar; graph ka last node.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.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.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.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.
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.
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
nn.BCELoss mismatch pe seedha ValueError deta hai (achha hai, bug pakda gaya). Dono case me ilaaj same: y.view(-1, 1) (ya .reshape(-1, 1), ya .unsqueeze(1)).-1 in view(-1, 1) = "is dimension ka size khud calculate karo".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
loss.backward() gradients compute karta hai; optimizer.step() parameters update karta hai; optimizer.zero_grad() gradients reset karta hai.fit_transform on train, transform on test. LabelEncoder alphabetical: B → 0, M → 1.nn.Linear(30, 1): input (N, 30) → output (N, 1); weight shape (1, 30), bias (1,). Labels ko .view(-1, 1).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).fit_transform = leakage. Test pe sirf transform.train_losses.append(loss) (tensor) ke bajaye loss.item() append karo.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
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.
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 me | Deep 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 uchaai | Loss L(θ) — ek number |
| Pairon se dhalaan mehsoos karna | Gradient ∇L(θ) nikalna (backprop / loss.backward()) |
| Kadam ki lambai | Learning rate η (eta), code me lr |
| Ek kadam lena | Ek 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:
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.
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).
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.
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.
Exam Radar
Galti mat karna
Dimaag me aise baitha lo
"Gradient bole upar, hum chalein neeche. Slope bada to kadam bada, slope chhota to kadam chhota." θ ← θ − η∇L.
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.
Chalo numbers se samjhein
Sawaal: L(w) = w², w₀ = 5, η = 0.1. Pehle 6 updates karo.
| t | wt | L = wt² | gradient 2wt | kadam η·g | wt+1 |
|---|---|---|---|---|---|
| 0 | 5 | 25 | 10 | 1 | 4 |
| 1 | 4 | 16 | 8 | 0.8 | 3.2 |
| 2 | 3.2 | 10.24 | 6.4 | 0.64 | 2.56 |
| 3 | 2.56 | 6.5536 | 5.12 | 0.512 | 2.048 |
| 4 | 2.048 | 4.1943 | 4.096 | 0.4096 | 1.6384 |
| 5 | 1.6384 | 2.6844 | 3.2768 | 0.3277 | 1.3107 |
| 6 | 1.3107 | 1.7180 | 2.6214 | 0.2621 | 1.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.
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:
Chalo numbers se samjhein — same start w₀ = 5, alag η
| η | factor (1 − 2η) | w₀ | w₁ | w₂ | w₃ | w₄ | Kya hua |
|---|---|---|---|---|---|---|---|
| 0.01 | 0.98 | 5 | 4.9 | 4.802 | 4.706 | 4.612 | bahut dheema (100 steps baad bhi ≈ 0.663) |
| 0.1 | 0.8 | 5 | 4 | 3.2 | 2.56 | 2.048 | smooth, aaram se converge |
| 0.5 | 0 | 5 | 0 | 0 | 0 | 0 | ek hi step me seedha minimum! |
| 0.75 | −0.5 | 5 | −2.5 | 1.25 | −0.625 | 0.3125 | zig-zag, par converge |
| 1.0 | −1 | 5 | −5 | 5 | −5 | 5 | hamesha oscillate, loss hamesha 25 |
| 1.1 | −1.2 | 5 | −6 | 7.2 | −8.64 | 10.368 | diverge: loss 25 → 36 → 51.84 → 74.65 → 107.5 |
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.
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.
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)
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.
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 kaOutput
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
Galti mat karna
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.
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.
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.
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.
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.)
torch.no_grad() kyun? Update khud ek math operation hai. Agar autograd use record kare, to agle backward me gadbad hogi, aur leaf tensor par in-place change error dega.w.grad.zero_() kyun? .backward() purane gradient me naya add karta hai (accumulate). Saaf nahi kiya to har step pichhle sab steps ka gradient bhi jud jayega.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
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).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.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.
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).
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.
| step | x | y | L = x² − y² | gradient ka size |
|---|---|---|---|---|
| 0 | 1 | 0.001 | 0.999999 | 2.0 |
| 5 | 0.3277 | 0.0025 | 0.1074 | 0.6554 |
| 10 | 0.1074 | 0.0062 | 0.0115 | 0.2151 |
| 20 | 0.0115 | 0.0383 | −0.0013 | 0.0801 |
| 30 | 0.0012 | 0.2374 | −0.0563 | 0.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 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.
| Jagah | Gradient | Aas-paas ki shakal | Plain GD ka kya hota hai |
|---|---|---|---|
| Global minimum | 0 | har taraf upar | ruk jata hai — yahi chahiye |
| Local minimum | 0 | har taraf upar (sirf locally) | ruk jata hai — par better jagah kahin aur hai |
| Saddle point | 0 | kuch disha upar, kuch neeche | bahut dheema ho jata hai, atak sakta hai |
| Plateau | ≈ 0 | sab taraf lagbhag flat | rengta hai, loss curve flat |
| Local maximum | 0 | har taraf neeche | GD 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
Galti mat karna
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.
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:
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.
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.
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 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)
Nuksaan (Disadvantages)
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.
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.
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.
Do alag sawaal hain, Lab 6 ne dono alag kiye:
SGD ke fayde
SGD ke nuksaan
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.
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.
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.
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
Tune karne wali cheezein
optim.SGD bhi isi wajah se ye naam rakhta hai.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?
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 updatesJawab: 32 iterations/epoch (aakhri batch 8 ka), 320 updates in 10 epochs; drop_last=True → 31/epoch, 310 total.
| Situation | N | B | Updates / epoch |
|---|---|---|---|
| Worksheet MCQ | 1,000 | 50 | 1000/50 = 20 |
| Batch GD (Worksheet) | 50,000 | 50,000 | 1 (har update me 50,000 examples) |
| Lab 6 (Social Network Ads, train rows) | 320 | 320 / 1 / 32 | 1 / 320 / 10 |
| Lecture timing table | 500,000 | 32 / 128 / 1,024 / 8,192 | 15,625 / 3,907 / 489 / 62 |
| MNIST-size data | 60,000 | 64 | ⌈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.
| Property | Batch (Full / Vanilla) | Stochastic (SGD) | Mini-batch |
|---|---|---|---|
| Data per update | Saare N | 1 example | B examples (e.g. 32) |
| Updates per epoch | 1 | N | ⌈N / B⌉ |
| Gradient | Exact (true average) | Bahut noisy estimate | Thoda noisy estimate |
| Convergence path | Smooth, seedha | Bahut zig-zag / jittery | Halka wiggle (mildly noisy) |
| Cost per update | Bahut mehenga | Bahut sasta | Moderate |
| Memory | High (saara data + activations) | Bahut kam | Moderate |
| Wall-clock per epoch | Sabse kam (agar memory me fit ho) | Sabse zyada (overhead) | Kam (full batch ke kareeb) |
| Loss drop per epoch | Sabse kam (1 update) | Sabse zyada | Beech me, kaafi achha |
| Determinism | Deterministic (same path har run) | Random (shuffle par depend) | Random |
| Escapes local minima / saddles | Nahi | Haan | Haan |
| Minimum par settle | Haan (sahi η par) | Nahi, aas-paas ghoomta; LR decay chahiye | Lagbhag; LR decay se behtar |
| GPU efficiency | Achha | Kharab | Excellent |
| Kab use karein | Chhota dataset jo memory me fit ho | Online/streaming data; akela rarely | Deep learning ka default |
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.
TensorDataset(X, y): tensors ko jod deta hai. dataset[i] → (X[i], y[i]). len(dataset) → N. Sab tensors ka pehla dimension (N) same hona chahiye.DataLoader(dataset, batch_size=1, shuffle=False, drop_last=False, num_workers=0) — ye defaults hain:
batch_size: ek batch me kitne samples. Default 1 (dhyaan! bhool gaye to SGD chalega).shuffle: har epoch ke shuru me order random karo. Default False. Training ke liye True, validation/test ke liye False.drop_last: aakhri adhoora batch phenk do. Default False.num_workers: kitne background processes data load karein (0 = main process).len(loader) = batches per epoch = updates per epoch = ⌈N/B⌉ (ya drop_last=True par ⌊N/B⌋).for xb, yb in loader: ek epoch ke saare batches deta hai; har baar loop shuru hone par (shuffle=True ho to) naya order.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])
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 / epochOutput
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.
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/epochrange(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 loss | Epoch 10 loss | Total time |
|---|---|---|---|
| Batch GD (batch_size = 320) | 0.6661 | 0.6269 | 0.01 s |
| SGD (batch_size = 1) | 0.3740 | 0.2473 | 2.03 s |
StandardScaler sirf train par fit karo (split ke baad), BCELoss ke liye labels shape [N, 1] aur float — .reshape(-1, 1), warna [N] vs [N, 1] broadcasting se [N, N] ban ke bakwaas loss.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
optim.SGD teeno ke liye; type DataLoader(batch_size=...) decide karta hai. DataLoader defaults: batch_size=1, shuffle=False, drop_last=False.__len__ aur __getitem__ zaroori; __getitem__ ek sample deta hai, batching DataLoader karta hai.Galti mat karna
optim.SGD ka matlab ye nahi ki batch size 1 hai. Naam historical hai; asal batch size DataLoader me hai.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.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
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.
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.
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 w | 12 steps baad w | loss (start 49) | Diagnosis |
|---|---|---|---|---|---|
| 0.03 | 0.94 | −4, −3.58, −3.185, −2.814 | −0.331 | 11.0985 | Too small: sahi direction, par rengta hai (stagnation) |
| 0.15 | 0.70 | −4, −1.9, −0.43, 0.599 | 2.903 | 0.0094 | Just right: tez aur stable |
| 0.65 | −0.30 | −4, 5.1, 2.37, 3.189 | 3.000 | ≈ 0 | Bada: 3 ko cross karke oscillate karta hai, phir bhi converge |
| 1.05 | −1.10 | −4, 10.7, −5.47, 12.317 | −18.969 | 482.6369 | Too large: har bounce pehle se door — divergence |
Jawab: η = 0.03 slow, 0.15 achha, 0.65 oscillate-but-converge, 1.05 diverge (is function ke liye boundary η = 1).
| lr | 60 steps baad w | Kya hua |
|---|---|---|
| 0.0201 | 2.8571 | abhi tak nahi pahuncha (too small) |
| 1.0553 | 1049.9766 | gaya! (divergence) |
| 1.0050 | 6.0000 | boundary: 6.000 ↔ −0.880 ke beech hamesha jhoola, loss har step bilkul same (28.1487) |
| 0.3015 | 2.5600 | pahunch 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.
| Observation | Diagnosis | η kaisa hai | Ilaaj |
|---|---|---|---|
| Loss bahut epochs tak bahut dheere girta hai (train aur val dono) | Stagnation | Too low | η badhao (e.g. 10×), ya schedule/warmup ke saath bada start |
| Loss overall girta hai par minimum ke paas upar-neeche uchhalta hai | Oscillation | Thoda zyada (slightly too high) | η ghatao ya decay schedule lagao |
| Loss tezi se badhta hai, values NaN/inf ho jaati hain | Divergence | Bahut zyada (much too high) | Sabse pehle η kam karo (lecture: NaN aaye to pehla kaam LR down); data/gradients check |
Galti mat karna
suggest_float('lr', 1e-4, 1e-1, log=True) (Chapter 22).lr=0.1), SGD + momentum ~0.01–0.1, Adam ka default lr = 0.001 (1e-3) (Chapter 17). Recent PyTorch versions me optim.SGD ka default lr bhi 1e-3 hai, par hamesha khud likho.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.
lr_find() isi ka tool hai.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
Fixed LR ke nuksaan
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.
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).
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.
Chalo numbers se samjhein
Sawaal: η0 = 0.1, γ = 0.1, step_size = 10. Epoch 0, 9, 10, 15, 20, 29, 30 par LR?
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
StepLR(optimizer, step_size, gamma=0.1) — step_size required, gamma ka default 0.1.MultiStepLR(optimizer, milestones, gamma=0.1) — milestones = increasing epoch numbers ki list.StepLR(opt, step_size=40, gamma=0.5), start lr 0.994975 → har 40 steps aadha: 0.994975 → 0.497488 → 0.248744 → 0.124372 → 0.062186 → 0.031093. Har drop par loss graph me ek "kink" — bouncing ruk ke seedha andar.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%.
Chalo numbers se samjhein
Sawaal: η0 = 0.1, γ = 0.95. Epoch 0–5, 10, 20, 50 par LR?
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.95Output
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
ExponentialLR(opt, gamma=0.97) har step, 0.994975 se shuru → 200 steps baad 0.994975 × 0.97200 ≈ 0.002250.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?
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)).
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.
eta_min.T_max). t = current epoch (0 se T).Chalo numbers se samjhein
Sawaal: ηmax = 0.1, ηmin = 0, T = 10. Har epoch ka LR?
| t | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| ηt | 0.1 | 0.0976 | 0.0905 | 0.0794 | 0.0655 | 0.05 | 0.0345 | 0.0206 | 0.0095 | 0.0024 | 0 |
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
T_max ko aam taur par total epochs ke barabar rakho. Agar Tmax ke baad bhi step karte raho to PyTorch ka cosine wapas upar chadhne lagta hai (cosine periodic hai).CosineAnnealingWarmRestarts(optimizer, T_0, T_mult=1, eta_min=0) — cosine se neeche aao, phir achanak LR wapas ηmax (restart), phir neeche. Har restart model ko nayi ghaati dhoondhne ka mauka deta hai. T_mult = 2 ho to har cycle pichhle se dugni lambi.CosineAnnealingLR(opt, T_max=200) ne 49 steps me minimum ke 0.01 ke andar pahunchaya aur end par lr = 0.000000.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?
Chalo numbers se samjhein
Sawaal: ηmax = 0.1, W = 5, phir epochs 5–19 me cosine se 0 tak (total 20 epochs).
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).
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
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.
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.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?
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.
Ab tak sab schedules LR ko sirf ghatate the. Leslie Smith ka idea: LR ko upar-neeche bhi le jao.
base_lr aur max_lr ke beech triangle wave ki tarah upar-neeche ghoomta hai (step_size_up iterations upar, phir utne hi neeche). Bada LR wala hissa saddle points / sharp minima se nikalne me madad karta hai, chhota wala settle karne me. PyTorch: CyclicLR(optimizer, base_lr, max_lr, step_size_up=2000, mode='triangular').max_lr tak chadho (default pehle 30% steps, pct_start=0.3), phir baaki training me bahut chhote LR tak utro (default cosine). Start LR = max_lr/25 (div_factor=25), end LR = start/104 (final_div_factor=1e4). Momentum ulta chalta hai (LR upar to momentum neeche, 0.95 → 0.85 → 0.95). Fast training ("super-convergence") ke liye famous. PyTorch: OneCycleLR(optimizer, max_lr=0.1, epochs=10, steps_per_epoch=len(train_loader)).CyclicLR aur OneCycleLR ka scheduler.step() har batch ke baad call hota hai (ye per-iteration schedulers hain), epoch ke baad nahi.param_groups[i]['lr'] badalta hai. Isliye scheduler banate waqt optimizer pass karte ho: StepLR(optimizer, ...).scheduler.step() har epoch ke end me ek baar, batch loop ke bahar. Batch loop ke andar rakh diya to har batch par decay → LR bahut jaldi zero (galti!).optimizer.step() ke baad.scheduler.step(val_loss). Baaki sab ko kuch nahi: 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.)
optimizer.param_groups[0]['lr'] — optimizer ke andar abhi jo LR set hai (har optimizer, har scheduler ke saath chalta hai). param_groups ek list hai; aam taur par sirf ek group, isliye [0].scheduler.get_last_lr() — scheduler ka last computed LR, list me (har param group ka ek): scheduler.get_last_lr()[0].scheduler.step() ke pehle padha → is epoch me jo LR use hua. Baad me padha → agle epoch ka LR. Isliye print kahan hai, uspe output depend karta hai.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.
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.
| Schedule | Formula | Shape / pehchaan | Total epochs pata hone chahiye? | Val loss dekhta hai? | PyTorch | Kab use karein |
|---|---|---|---|---|---|---|
| Fixed | ηt = η | seedhi flat line | Nahi | Nahi | sirf optimizer ka lr | Simple baseline; Adam ke saath aksar theek |
| Step decay | η₀ γ⌊e/s⌋ | staircase, achanak drops | Roughly (milestones chunne ke liye) | Nahi | StepLR(opt, step_size, gamma=0.1), MultiStepLR(opt, milestones, gamma=0.1) | Classic CNN training (SGD + momentum) |
| Exponential | η₀ γt = η₀ e−kt | smooth percentage-style fall | Nahi | Nahi | ExponentialLR(opt, gamma) | Smooth steady decay chahiye |
| Time-based 1/t | η₀ / (1 + k t) | shuru me tez, lambi poonchh | Nahi | Nahi | LambdaLR(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 landing | Haan (T_max) | Nahi | CosineAnnealingLR(opt, T_max, eta_min=0) | Known budget; modern default (aksar warmup ke saath) |
| Linear warmup | ηmax(t+1)/W, t < W | near-zero se upar chadhna | Sirf W | Nahi | LambdaLR, LinearLR | Adam / Transformers / large batch; unstable early updates |
| ReduceLROnPlateau | bad > patience ⇒ η × factor | tab drop jab val loss atke | Nahi | Haan | ReduceLROnPlateau(opt, mode='min', factor=0.1, patience=10) + step(val_loss) | Total epochs pata nahi; feedback-based |
| Cyclical / One-cycle | base ↔ max (triangle) / ek baar upar-neeche | upar-neeche wave / ek pahad | One-cycle: haan | Nahi | CyclicLR, OneCycleLR (per batch step) | Fast training, super-convergence |
Exam Radar
scheduler.step(val_loss); patience = p → (p+1)-th lagataar bad epoch par cut.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].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.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.T_max total epochs se chhota rakha to baad me LR wapas badhne lagega.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
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.
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:
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.
Ek hi learning rate dono directions ke liye theek nahi: bada α → steep wall pe uchhalna/diverge; chhota α → flat floor pe rengna.
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:
| sample | x (km) | y (min) | ŷ = 2x | error ŷ − y | gradient x(ŷ − y) | ye sample kya kehta hai |
|---|---|---|---|---|---|---|
| 1 | 2 | 2 | 4 | +2 | +4 | w ghatao |
| 2 | 4 | 7 | 8 | +1 | +4 | w ghatao |
| 3 | 1 | 3 | 2 | −1 | −1 | w badhao |
| 4 | 3 | 5 | 6 | +1 | +3 | w ghatao |
| 5 | 2 | 5 | 4 | −1 | −2 | w badhao |
| 6 | 5 | 9 | 10 | +1 | +5 | w 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.
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.
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.
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
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".)
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.
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.
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).
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.
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 turnOutput
[1.0, 1.0] [1.6, 1.76] [1.0, 1.4, 1.32] [1.2, 2.16, 1.328]
Rule me st−1 = βst−2 + (1 − β)xt−1 daalo, phir st−2 ko bhi kholo, aur aise hi peeche chalte jao:
| kitna purana | weight, β = 0.8 | weight, β = 0.9 |
|---|---|---|
| newest (aaj) | 0.2000 | 0.1000 |
| 1 step purana | 0.1600 | 0.0900 |
| 2 steps purana | 0.1280 | 0.0810 |
| 3 steps purana | 0.1024 | 0.0729 |
| 4 steps purana | 0.0819 | 0.0656 |
| 5 steps purana | 0.0655 | 0.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.
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.
| β | newest weight (1 − β) | effective memory 1/(1 − β) | kaisa behave karta hai |
|---|---|---|---|
| 0.5 | 0.500 | ~2 steps | bahut jumpy, turant react |
| 0.8 | 0.200 | ~5 steps | beech ka |
| 0.9 | 0.100 | ~10 steps | typical choice (pichhle ~10 din ka temperature) |
| 0.98 | 0.020 | ~50 steps | bahut smooth, par der se react (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:
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.
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.
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).
Chalo numbers se samjhein — constant signal test (Worksheet 08)
Har step observation = 1 (hamesha). Imaandaar average 1 hona chahiye. β = 0.9:
Average galat nahi tha, adhoora tha: 3 steps baad 0.729 weight abhi bhi m0 = 0 pe baitha tha.
| t steps baad | 1 − βt (β = 0.9) | 1 − βt (β = 0.999) | matlab |
|---|---|---|---|
| 1 | 0.1000 | 0.0010 | 0.9 wala 90% chhota, 0.999 wala ~100% chhota |
| 2 | 0.1900 | 0.0020 | |
| 3 | 0.2710 | 0.0030 | |
| 10 | 0.6513 | 0.0100 | 0.9 wala abhi 35% chhota |
| 50 | 0.9948 | 0.0488 | 0.9 lagbhag theek; 0.999 abhi 95% chhota |
| 300 | 1.0000 | 0.2593 | |
| 3000 | 1.0000 | 0.9503 | 0.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
Galti mat karna
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.
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.
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?
| step 1 | step 2 | step 3 | w* = 1.7797 se doori | |
|---|---|---|---|---|
| plain SGD | 1.6000 | 1.2000 | 1.3000 | 0.4797 (1 reversal) |
| Momentum | 1.9600 | 1.8840 | 1.8256 | 0.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.
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.
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:
| step | plain GD (α = 0.05) | Momentum (α = 0.25, β = 0.8) | momentum ki velocity v |
|---|---|---|---|
| 0 | 2.0000 | 2.0000 | 0 |
| 1 | 1.8000 | 1.8000 | 0.8000 |
| 2 | 1.6400 | 1.4800 | 1.2800 |
| 3 | 1.5120 | 1.1280 | 1.4080 |
| 4 | 1.4096 | 0.8208 (paar!) | 1.2288 |
MCQ me ya PyTorch docs me tumhe momentum teen tarah likha milega. Ghabrana nahi — ye learning rate ki rescaling tak ek hi algorithm hain.
| form | velocity update | weight update | kahan milega |
|---|---|---|---|
| (c) EWMA form | vt = βvt−1 + (1 − β)gt | w ← w − αvt | hamara course (worksheets, Prince UDL), Andrew Ng |
| (b) PyTorch form | vt = βvt−1 + gt | w ← w − ηvt | PyTorch optim.SGD, Lab 7, Keras-style "framework note" |
| (a) classic form | vt = βvt−1 + ηgt | w ← w − vt | Sutskever et al. 2013, cs231n notes |
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 |
|---|---|---|---|
| 1 | 0.4, 1.96 | 4.0, 1.96 | 0.04, 1.96 |
| 2 | 0.744, 1.8856 | 7.44, 1.8856 | 0.0744, 1.8856 |
| 3 | 1.02384, 1.783216 | 10.2384, 1.783216 | 0.102384, 1.783216 |
| 4 | 1.234742, 1.659742 | 12.347424, 1.659742 | 0.123474, 1.659742 |
Weights har form me identical; velocity sirf scale (1 : 10 : 0.1) me alag. (Python se verify.)
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
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.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.
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.
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):
| β | 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.
Exam Radar
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)params — kaunse tensors update karne hain (aksar model.parameters()).lr — learning rate η. (Purane PyTorch versions me lr dena compulsory tha; naye versions me default 1e-3 hai — exam me hamesha khud likho.)momentum — β. Default 0 = plain SGD. 0.9 sabse common.dampening — velocity me naye gradient pe (1 − dampening) factor. Default 0.weight_decay — L2 penalty, gradient me λw jodta hai (Chapter 19).nesterov — True karne pe NAG (16.10).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).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.
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.
Momentum ki dono lines ko jodo (velocity ko weight wali line me substitute karo):
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.
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.
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.)
θ: 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.
NAG: 1.8000 → 1.5120 → 1.2253 → 0.9967 (minimum se sirf 0.0033 paar). Momentum: → 0.8208 (0.1792 paar).
| step | plain GD (α = 0.05) | Momentum | NAG |
|---|---|---|---|
| 1 | 1.8000 | 1.8000 | 1.8000 |
| 2 | 1.6400 | 1.4800 | 1.5120 (NAG early me peeche) |
| 3 | 1.5120 | 1.1280 | 1.2253 |
| 4 | 1.4096 | 0.8208 | 0.9967 |
| lowest w reached | — (kabhi paar nahi) | 0.5208 | 0.7877 |
| overshoot past w = 1 | 0 | 0.4792 | 0.2123 (56% chhota) |
| w = 1 ke 0.01 ke andar kab se | — | step 42 | step 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.
| step | w_look = w − 0.2v | g = 4(w_look − 3) | v = 0.8v + 0.2g | w = w − 0.25v (NAG) | Momentum ka w |
|---|---|---|---|---|---|
| 1 | 5.0000 | 8.0000 | 1.6000 | 4.6000 | 4.6000 |
| 2 | 4.6000 − 0.3200 = 4.2800 | 5.1200 | 1.2800 + 1.0240 = 2.3040 | 4.0240 | 3.9600 |
| 3 | 4.0240 − 0.4608 = 3.5632 | 2.2528 | 1.8432 + 0.4506 = 2.2938 | 3.4505 | 3.2560 |
| 4 | 3.4505 − 0.4588 = 2.9917 | −0.0332 | 1.8350 − 0.0066 = 1.8284 | 2.9934 | 2.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
nesterov=True + manual implementationAPI
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)nesterov=True ke liye momentum > 0 aur dampening = 0 zaroori. Warna PyTorch error deta hai: ValueError: Nesterov momentum requires a momentum and zero dampening (real run se check kiya).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θ.
| step | GD | Momentum (PyTorch) | classic NAG θ | PyTorch nesterov=True print |
|---|---|---|---|---|
| 1 | 0.8000 | 0.8000 | 0.8000 | 0.6200 |
| 2 | 0.6400 | 0.4600 | 0.4960 | 0.2224 |
| 3 | 0.5120 | 0.0620 | 0.1779 | −0.1084 |
| 4 | 0.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).| Plain SGD | SGD + Momentum | NAG | |
|---|---|---|---|
| yaad kya rakhta hai | kuch nahi | velocity = gradients ka EWMA | velocity + look-ahead measurement |
| gradient kahan naapta hai | current w | current w | w − αβv (look-ahead) |
| extra memory / parameter | 0 | 1 (velocity) | 1 (velocity) |
| hyperparameters | α | α, β (0.9) | α, β (0.9) |
| problem jo fix kiya | — | zig-zag, noise, slow flat directions | momentum ka overshoot |
| bachi hui problem | noise, zig-zag | overshoot; same α sabke liye | same α sabke liye |
| PyTorch | optim.SGD(p, lr) | optim.SGD(p, lr, momentum=0.9) | optim.SGD(p, lr, momentum=0.9, nesterov=True) |
Exam Radar
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.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
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).
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:
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 steps | flat direction ke steps | flat, momentum β = 0.9 ke saath |
|---|---|---|---|
| 0.02 | 21 | 2301 | 2268 |
| 0.08 | 3 | 574 | 539 |
| 0.19 | 44 | 241 | 200 |
| 0.22 | diverges | 165 | — |
α = 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.
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).
√ 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.
| G₁ | step 1 | G₂ | step 2 | w after | |
|---|---|---|---|---|---|
| w₁ | 9.0000 | 0.1000 | 25.0000 | 0.0800 | 0.8200 |
| w₂ | 0.0900 | 0.1000 | 0.2500 | 0.0800 | 0.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.
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.
Gradient same (2) raha, phir bhi step 0.1 → 0.0707 → 0.0577 → 0.05. Constant gradient pe step = α/√t.
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 baad | Gt | effective step α/√Gt |
|---|---|---|
| 1 | g² | 1.0000 α/g |
| 4 | 4g² | 0.5000 α/g |
| 25 | 25g² | 0.2000 α/g |
| 100 | 100g² | 0.1000 α/g |
| 400 | 400g² | 0.0500 α/g |
| 2500 | 2500g² | 0.0200 α/g |
| 10000 | 10000g² | 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.
optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0, initial_accumulator_value=0, eps=1e-10) — default lr = 0.01.initial_accumulator_value = G0 (default 0). lr_decay = lr ko aur ghatane ka option (default 0).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
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.
alpha, Keras rho kehte hain. Sab ek hi cheez.alpha=0.99. ~1/(1 − β) recent gradients "recent" maane jaate hain.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.)
| v₁ | step 1 | v₂ | step 2 | w after | |
|---|---|---|---|---|---|
| w₁ | 0.9000 | 0.3162 | 2.4100 | 0.2577 | 0.4261 |
| w₂ | 0.0090 | 0.3162 | 0.0241 | 0.2577 | 0.4261 |
Sum ki jagah exponentially weighted average aaya, aur step decay hona band hua (AdaGrad: 0.1, 0.08; RMSProp: 0.3162, 0.2577).
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:
| β₂ | 1 − β₂ | RMSProp ka pehla step (α ki units me) |
|---|---|---|
| 0.5 | 0.500 | 1.41 × |
| 0.9 | 0.100 | 3.16 × |
| 0.99 (PyTorch default) | 0.010 | 10.00 × |
| 0.999 | 0.001 | 31.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.
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 t | AdaGrad G | AdaGrad step | RMSProp v | RMSProp step |
|---|---|---|---|---|
| 20 (g = 10) | 2000.0 | 0.0224 | 87.84 | 0.1067 |
| 21 (g = 1) | 2001.0 | 0.0022 | 79.16 | 0.0112 |
| 30 | 2010.0 | 0.0022 | 31.28 | 0.0179 |
| 40 | 2020.0 | 0.0022 | 11.56 | 0.0294 |
| 60 | 2040.0 | 0.0022 | 2.284 | 0.0662 |
| 80 | 2060.0 | 0.0022 | 1.156 | 0.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.
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]
optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False) — defaults yaad karo: lr 0.01, alpha 0.99.alpha = smoothing/decay constant of the squared-gradient EWMA (course ka β / ρ). Keras me isi ka naam rho (classroom notebook: tf.keras.optimizers.RMSprop(learning_rate=0.01, rho=0.9)).momentum > 0 diya to: b = μb + g/(√v + ε), w −= lr·b (RMSProp + momentum). centered=True → gradient ka variance estimate (mean² ghata ke) se normalize.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
alpha default 0.99, lr default 0.01.Galti mat karna
alpha learning rate nahi hai (learning rate lr hai). Lab 7 me {'lr': 0.15, 'alpha': 0.9}.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.
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:
exp_avg.exp_avg_sq. (Dhyaan: Adam ka v squared gradients hai, momentum wala velocity nahi!)step).| line | kya hai | kahan mile the |
|---|---|---|
| 1 | gradients ka average | Momentum (Chapter 16) |
| 2 | squared gradients ka average | RMSProp (17.3) |
| 3 | har average ko uske applied weight se divide | bias correction (16.4) |
| 4 | m̂ ki direction me, √v̂ se scaled step | AdaGrad 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.
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.
| step 1 | step 2 | w after | |
|---|---|---|---|
| w₁ (Adam) | 0.1000 | 0.0990 | 0.8010 |
| w₂ (Adam, gradients 0.3, 0.4) | 0.1000 | 0.0990 | 0.8010 |
| w₁, correction off | 0.0316 | 0.0432 | 0.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.
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.
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 1 | sabse zyada galat | 5% ke andar kab se |
|---|---|---|---|
| 0.9 | 0.32 × | 0.32 × (step 1) | step 23 |
| 0.99 | 1.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 → 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).
optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)betas ek tuple hai: (β₁, β₂). weight_decay Adam me gradient me λw jodta hai (L2 style — 17.5 me iski problem). amsgrad=True → AMSGrad variant (v yaani exp_avg_sq ka running max use karta hai, taaki denominator kabhi chhota na ho).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
Galti mat karna
betas=0.9 galat; betas=(0.9, 0.999) sahi (tuple).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!
optim.Adam(..., weight_decay=λ)): gradient ban jaata hai g + λw, aur ye poora m, v me jaata hai → decay wala hissa bhi √v̂ se divide ho jaata hai. Jis weight ke gradients bade the (bada √v̂), uska decay bahut kam; jiske chhote the, uska zyada. Regularisation worksheet (L8 Part 2) ka jawab: "Adam rescales the penalty along with the gradient, so equal weights shrink unequally."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.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.
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.989Adam(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!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 haiAdamW 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
weight_decay = L2 inside gradient (rescaled by √v̂); AdamW's = true weight decay.| kadam | problem jo fix hui | kaise | nayi problem jo bachi |
|---|---|---|---|
| SGD → Momentum | ravine me zig-zag, noisy gradients, flat direction me slow | gradients ka EWMA (velocity) | overshoot; ek α sabke liye |
| Momentum → NAG | overshoot | gradient look-ahead point pe naapo | ek α sabke liye |
| SGD → AdaGrad | ek hi α steep aur flat / rare aur frequent parameters ke liye | Σg² se divide: per-parameter LR | G hamesha badhta → LR → 0 |
| AdaGrad → RMSProp | learning rate ka mar jaana | Σ ki jagah EWMA of g² (bhoolo) | zero-start bias (bada first step), raw-gradient direction |
| Momentum + RMSProp → Adam | direction aur scale dono ek saath; start-up bias | m̂/√v̂ + bias correction | L2 penalty bhi rescale; 2× memory |
| Adam → AdamW | weight decay ka adaptive scaling me ulajhna | decay ko update se decouple | — (aaj ka standard) |
| Optimizer | Update rule (short) | extra memory / parameter | key hyperparameters (PyTorch defaults) | Pros | Cons | kab use karein |
|---|---|---|---|---|---|---|
| SGD | w ← w − α g | 0 | lr (naye PyTorch me default 1e-3; hamesha khud do) | simple, sasta; tuned ho to acchi generalization | noisy, zig-zag, plateau/saddle pe slow, ek α | baseline; schedule ke saath |
| SGD + Momentum | v ← βv + g; w ← w − η v | 1 (velocity) | momentum=0.9 (default 0), dampening=0 | ravine me tez, oscillation cancel, bumps paar | overshoot, extra β | CNNs (e.g. ResNet: SGD 0.9 + LR schedule) |
| NAG | gradient at look-ahead w − βv (classic form a; course form me w − αβv) | 1 | nesterov=True (momentum > 0, dampening = 0) | kam overshoot, jaldi settle | ek α sabke liye | jab bhi momentum use karo |
| AdaGrad | G ← G + g²; w ← w − α g/(√G + ε) | 1 (G) | lr=0.01, eps=1e-10, initial_accumulator_value=0 | per-param LR, sparse features, kam tuning | LR → 0, learning ruk jaati | sparse data (NLP, recsys), convex problems |
| RMSProp | v ← βv + (1−β)g²; w ← w − α g/(√v + ε) | 1 (+1 agar momentum) | lr=0.01, alpha=0.99, eps=1e-8, momentum=0, centered=False | AdaGrad ka decay fix; non-stationary problems | no bias correction, no direction memory | RNNs, RL; Adam se pehle ka favourite |
| Adam | m, v EWMAs; w ← w − α m̂/(√v̂ + ε) | 2 (m, v) | lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0 | tez, robust, kam tuning, noisy/sparse/saddle pe accha | 2× memory; kabhi worse generalization; L2 coupling | default first choice (most DL) |
| AdamW | Adam + (1 − αλ)θ decay | 2 | lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0.01 | sahi weight decay, better generalization | ek 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.
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).
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).
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
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.Worksheet 06 ka decision map: pehle poocho "is training me mushkil kya hai?"
| diagnosis | starting choice | kyun |
|---|---|---|
| noisy direction (gradients same direction me par noisy) | Momentum | direction ko smooth karta hai |
| overshoot risk | NAG | gradient prediction point pe naapta hai |
| uneven coordinates (alag-alag scales) | AdaGrad ya RMSProp | per-coordinate scale |
| adaptive baseline chahiye | Adam | dono memories (m + v) |
| adaptive + explicit weight decay | AdamW | decay 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
Galti mat karna
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
Generalization · Train / Validation / Test · Underfitting (high bias) · Overfitting (high variance) · Bias² + Variance + σ² · U-curve · Model capacity · Learning curves · Polynomial degree · Diagnosis · Double descent
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.
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.
| Set | Kaam | Analogy | Kab use? |
|---|---|---|---|
| Training set | Optimizer isi pe gradient nikal ke weights (θ) update karta hai | Roz ki padhai, practice questions | Har epoch, baar-baar |
| Validation set | Kabhi train nahi hota; model / hyperparameter choose karne ke liye (4 vs 16 vs 64 neurons? λ kitna? kab rukna hai?) | Mock test | Development ke dauraan, compare karne ke liye |
| Test set | Saare faisle ho jaane ke baad ek baar final, unbiased report | Board exam | Sirf 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.
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 points | Test points | |||||
|---|---|---|---|---|---|---|
| Actual | 5 | 7 | 6 | 8 | 6 | 9 |
| Model A (smooth) | 6 | 6 | 7 | 7 | 7 | 8 |
| Model B (wiggly) | 5 | 7 | 6 | 8 | 9 | 6 |
MSE = squared errors ka average, yaani (actual − predicted)² ka mean.
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
Galti mat karna
Dimaag me aise baitha lo
Past paper pe 100 aana kamaal nahi; naye paper pe achha aana kamaal hai. Wahi generalization hai.
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 neurons | Train MSE | Validation MSE | Diagnosis |
|---|---|---|---|
| 1 (tiny) | 0.184 | 0.201 | Dono high → Underfitting |
| 4 (balanced) | 0.036 | 0.044 | Dono low, paas-paas → Good fit |
| 32 (large) | 0.004 | 0.138 | Train 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.
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
Galti mat karna
Dimaag me aise baitha lo
Underfit = Pappu ne sirf titles padhe: practice me bhi fail, exam me bhi fail.
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."
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.
Jawab: har training point ke liye ~69 "knobs" — ratta maarne ke liye bharpoor freedom.
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):
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.
Exam Radar
Galti mat karna
Dimaag me aise baitha lo
Overfit = Chintu ne answer key ki typing mistakes tak ratt li: mock me 100, board me dhadaam.
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:
| Underfit | Overfit | Good Fit | |
|---|---|---|---|
| Training dataset | Poor | Very Good | Good |
| Evaluation dataset | Very Poor | Poor | Good |
| Bias | High | Low | Low |
| Variance | Low | High | Low (acceptable) |
| Curve shape | Seedhi line, wave miss | Har point se guzarti wiggly line | Smooth curve, asli pattern ke paas |
Lab ka "final checkpoint" — chaar cases, tum diagnose karo:
| Case | Evidence | Diagnosis |
|---|---|---|
| A | train .210, val .238 — dono plateau pe high | Underfit |
| B | train .003, val .146 — bada gap | Overfit |
| C | train .035, val .041 — dono low aur paas | Good fit |
| D | train .000, test peak ke baad gir raha, capacity interpolation se kaafi aage | Second-descent regime (double descent, 18.12) |
Dimaag me aise baitha lo
High–High = underfit. Low–High = overfit. Low–Low (chhota gap) = good fit.
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).
| Combination | Dartboard pe | Model example |
|---|---|---|
| Low bias, low variance | Bullseye pe tight gucchha | Sahi capacity, achha data — ideal (good fit) |
| Low bias, high variance | Bullseye ke charon taraf bikhra | Bahut bada model, kam data — overfitting |
| High bias, low variance | Door, lekin tight gucchha | Bahut simple model (line for a wave) — underfitting |
| High bias, high variance | Door bhi, bikhra bhi | Galat 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
Galti mat karna
Dimaag me aise baitha lo
Bias = nishana hi tedha. Variance = haath kaanp raha hai.
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.
Shortcut notation: f = f(x), f̂ = f̂(x), m = E[f̂] (average prediction).
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.
Jawab: Expected error 7 = Bias² 4 + Variance 2 + Noise 1. Yahan bias sabse bada hissa hai, to model ko zyada flexible banana chahiye.
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
Galti mat karna
Dimaag me aise baitha lo
Galti = nishane ka tedhapan² + haath ka kaampna + hawa ka jhonka (jo kisi ke control me nahi).
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."
Chalo numbers se samjhein (lab ke formulas)
Bias²(t) = 0.08 + 0.72(1 − t)², Variance(t) = 0.08 + 0.72t², Noise = 0.08.
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 units | Bias² | Variance | Noise | Expected test MSE | Training 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
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.
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.
17 params → underfit, 129 → theek, 198,401 params for 150 points (≈ 1323 params per point!) → overfit.
Exam Radar
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
Saare degrees 0 se 19 tak chalao (same data) to poora U dikhta hai:
| Degree | 0 | 1 | 3 | 5 | 9 | 12 | 15 | 17 | 19 |
|---|---|---|---|---|---|---|---|---|---|
| Train MSE | 0.4214 | 0.2694 | 0.0787 | 0.0560 | 0.0373 | 0.0130 | 0.0066 | 0.0018 | 0.0000 |
| Val MSE | 0.5608 | 0.3210 | 0.1002 | 0.1230 | 0.1517 | 0.3116 | 0.3907 | 1037.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
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.
| Curves me kya dikha | Diagnosis | Kya karein |
|---|---|---|
| Dono high, paas-paas, flat ho gaye | Underfitting (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 gap | Healthy / good fit | Tuning band karo, test ek baar check karo |
| Dono abhi bhi gir rahe hain | Abhi training adhoori | Aur epochs |
Chalo numbers se samjhein (Model Capacity Lab ka training timeline)
| Epoch | Train MSE | Validation MSE | Gap (val − train) |
|---|---|---|---|
| 1 | 0.214 | 0.226 | 0.012 |
| 20 | 0.082 | 0.101 | 0.019 |
| 80 | 0.026 | 0.041 | 0.015 |
| 300 | 0.004 | 0.137 | 0.133 |
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.
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:
nn.BCEWithLogitsLoss() — binary classification ka loss; andar khud sigmoid lagata hai, isliye model ka last layer raw logit deta hai (sigmoid mat lagao). Target float hona chahiye, shape (N, 1).p.numel() — ek parameter tensor me kitne numbers hain; sabka sum = total parameters.(model(X) > 0) — logit > 0 matlab sigmoid > 0.5 matlab class 1.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):
| Epoch | 1 | 10 | 20 | 30 | 50 | 100 | 200 | 300 | 400 |
|---|---|---|---|---|---|---|---|---|---|
| Train loss | 0.6935 | 0.3699 | 0.2430 | 0.1878 | 0.1255 | 0.0802 | 0.0669 | 0.0136 | 0.0023 |
| Val loss | 0.6447 | 0.2861 | 0.2764 | 0.2562 | 0.3611 | 0.7400 | 0.6302 | 1.3501 | 1.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
BCEWithLogitsLoss = sigmoid + BCE; model outputs raw logits.Galti mat karna
Dimaag me aise baitha lo
Do lines: training hamesha neeche jaana chahti hai. Validation jis din upar mude, samjho ratta shuru.
Doctor pehle report padhta hai, phir dawai deta hai. Hum bhi pehle (train error, val error) ka jodaa padhte hain:
| Symptoms | Problem | Bias / Variance | Remedies |
|---|---|---|---|
| Train error high, val error high (dono paas) | Underfitting | High bias, low variance | Bigger model (layers/neurons), better features, train longer, less regularization (λ ↓, dropout ↓), better LR/optimizer |
| Train error very low, val error much higher (gap bada) | Overfitting | Low bias, high variance | More data, data augmentation, L2/L1, dropout, early stopping, simpler model, ensembles |
| Dono low, gap chhota | Good fit | Low bias, low variance | Ruk jao; test set pe ek baar final score |
| Train error high, val error aur bhi bahut zyada | Dono problem | High bias + high variance | Pehle bias theek karo (fit training), phir variance |
| Train ≈ 0, test peak ke baad gir raha, capacity bahut badi | Over-parameterized regime | Second descent | Double 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).
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).
| Params | Params / data | Regime | Train MSE | Test MSE |
|---|---|---|---|---|
| 20 | 0.1× | under-parameterized | 0.250 | 0.300 |
| 60 | 0.3× | under-parameterized | 0.170 | 0.220 |
| 120 | 0.6× | under-parameterized | 0.100 | 0.170 (classical sweet spot) |
| 180 | 0.9× | near interpolation | 0.025 | 0.290 |
| 200 | 1× | interpolation threshold | 0 | 0.600 (peak) |
| 300 | 1.5× | over-parameterized | 0 | 0.400 |
| 500 | 2.5× | over-parameterized | 0 | 0.250 |
| 800 | 4× | over-parameterized | 0 | 0.180 |
| 1600 | 8× | highly over-parameterized | 0 | 0.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."
| Type | Kya badalte hain | Kya dikhta hai |
|---|---|---|
| Model-wise | Model size (width/params), training fixed | Test error: girta → threshold pe peak → girta (upar wala curve) |
| Epoch-wise | Bada model fixed, training epochs badhaate jao | Test error: girta → badhta → phir girta. Lambi training effective capacity badhati hai, to model time ke saath threshold cross karta hai |
| Sample-wise | Model fixed, training data ki quantity badhao | Threshold 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.
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.
Exam Radar
Galti mat karna
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
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
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."
Chalo numbers se samjhein (regularisation worksheet)
E = 0.40, λ = 0.10, Ω = 2.0. Ẽ = ?
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.
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.
| Technique | Ek line me | Kahan padhein |
|---|---|---|
| L2 (Ridge / weight decay) | Bade weights pe fine; sab weights proportionally chhote | 19.2 |
| L1 (Lasso) | Har weight pe fine; faltu weights exactly zero | 19.3 |
| Early stopping | Ratta shuru hone se pehle ruk jao ("delete training time") | 19.7 |
| Dropout | Har step random neurons band ("delete activations for one step") | Chapter 20 |
| Batch norm | Activations ko minibatch stats se re-scale; mild regularizing side effect | Chapter 20 |
| Data augmentation / more data | Naye training examples banao | 19.8 |
| Ensembles (bagging) | Kai models ka average → variance kam | 19.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
Galti mat karna
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.
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."
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.
(b) Kai weights: W₁ = 2, W₂ = −3, W₃ = 1, λ = 0.1, Ldata = 5.
(c) λ/2 form, 10 guna bade weights (worksheet 09): λ = 0.1 → λ/2 = 0.05.
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:
GD + L2:
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".
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 − αλ).
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.
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.
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.
weight_decaySabse 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)weight_decay default: 0 for SGD, Adam, RMSprop (yaani bina likhe koi L2 nahi). AdamW ka default 0.01.model.parameters() me biases bhi hain, to weight_decay biases ko bhi decay karta hai (practically chhoti baat; chaho to param groups se alag kar sakte ho).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.
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
weight_decay=λ adds λw to the gradient (≡ (λ/2)Σw² in loss for SGD). Default 0 (SGD/Adam/RMSprop), AdamW default 0.01.Galti mat karna
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.
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."
torch.sign(0) = 0 leta 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.
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.
Push ka size (worksheet 09, λ = 0.5, λ/2-form L2 push = λw)
| w | 4 | 2 | 1 | 0.5 | 0.1 |
|---|---|---|---|---|---|
| L2 push λw | 2 | 1 | 0.5 | 0.25 | 0.05 |
| L1 push λ | 0.5 | 0.5 | 0.5 | 0.5 | 0.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.
| Step | 0 | 1 | 2 | 3 | 4 | 8 |
|---|---|---|---|---|---|---|
| L2 | 1 | 0.75 | 0.5625 | 0.4219 | 0.3164 | 0.1001 |
| L1 | 1 | 0.75 | 0.50 | 0.25 | 0 | 0 |
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.
Ek weight ke liye problem: minimize ½(w − z)² + λ|w| (z = bina penalty wala best weight). Iska exact minimizer soft-thresholding hai:
Chalo numbers se samjhein (regularisation worksheet)
Ridge (L2) ka equivalent z/(1 + λ) hota — kabhi zero nahi. Lasso threshold se neeche sab kuch zero kar deta hai.
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].
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 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)p.abs() = elementwise |w|; .sum() = tensor ka total. Bahar wala sum(... for p in model.parameters()) saari layers jodta hai → ek scalar tensor.loss.backward() uska gradient (L1: λ·sign(w), L2: 2λw) apne aap jod deta hai.model.named_parameters() → (naam, tensor) pairs, jaise '0.weight', '0.bias'; isse biases chhod sakte ho.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
sum(p.abs().sum() for p in model.parameters()).Galti mat karna
Dimaag me aise baitha lo
L2 divide karta hai (sab chhote, koi zero nahi). L1 subtract karta hai (kamzor seedha zero).
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.
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.
| Feature | L1 (Lasso) | L2 (Ridge / weight decay) |
|---|---|---|
| Penalty | λΣ|w| | λΣw² (ya (λ/2)Σw²) |
| Uses | Absolute weight | Squared weight |
| Gradient of penalty | λ·sign(w) — constant size | 2λw (ya λw) — proportional to w |
| Update ka asar | Fixed amount subtract | Fixed fraction multiply (decay) |
| Exact zeros | Haan, common (sparse) | Lagbhag kabhi nahi |
| Feature selection | Haan (automatic) | Nahi |
| Weights ka pattern | Kuch bade, baaki 0 (0.8, 0, 1, 0) | Sab chhote, spread (0.3, 0.1, 0.3, 0.2) |
| Constraint shape | Diamond | Circle / sphere |
| Differentiable at 0? | Nahi (kona) | Haan (smooth) |
| Closed form | Nahi (soft-thresholding / iterative) | Haan: (XᵀX + λI)⁻¹Xᵀy |
| Outlier robustness (slide) | Zyada robust | Kam robust |
| Correlated features | Ek ko chunta, baaki zero | Credit baant deta hai |
| Tuning | "Cliff" — thoda λ badla, bahut weights achanak zero | Gradual, aasaan |
| PyTorch | Loss me manual: p.abs().sum() | weight_decay= (ya manual p.pow(2).sum()) |
| Deep learning me | Specific jobs: first-layer feature selection, compression | Default |
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).
Dimaag me aise baitha lo
L1 = "kaun zaroori hai?" chunta hai. L2 = "sab thoda-thoda" rakhta hai. Elastic Net = dono ka mix.
λ 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 MSE | Val MSE | Largest |w| | Diagnosis |
|---|---|---|---|---|
| 0 | 0.0220 | 0.2157 | 28171.95 | Overfit (bade weights!) |
| 1e-6 | 0.0561 | 0.1353 | 247.31 | Kam overfit |
| 1e-4 | 0.1099 | 0.1144 | 15.39 | Achha |
| 1e-3 | 0.1229 | 0.1136 | 9.57 | Best (sweet spot) |
| 1e-2 | 0.1802 | 0.1521 | 3.44 | Thoda underfit |
| 1e-1 | 0.2144 | 0.2012 | 1.10 | Underfit |
| 1 | 0.2929 | 0.3323 | 0.45 | Underfit |
| 10 | 0.4002 | 0.4878 | 0.09 | Bahut 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
Dimaag me aise baitha lo
λ = namak. Zero: pheeka overfit. Mutthi bhar: kadwa underfit. Chutki bhar: perfect.
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).
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.
| Epoch | Val loss | Best so far | Best epoch | Patience counter | Action |
|---|---|---|---|---|---|
| 1 | 0.90 | 0.90 | 1 | 0 | Naya best → save |
| 2 | 0.72 | 0.72 | 2 | 0 | Naya best → save |
| 3 | 0.61 | 0.61 | 3 | 0 | Naya best → save |
| 4 | 0.55 | 0.55 | 4 | 0 | Naya best → save |
| 5 | 0.52 | 0.52 | 5 | 0 | Naya best → save |
| 6 | 0.53 | 0.52 | 5 | 1 | Sudhaar nahi (0.53 ≥ 0.52) |
| 7 | 0.50 | 0.50 | 7 | 0 | Naya best → save, counter reset |
| 8 | 0.51 | 0.50 | 7 | 1 | Sudhaar nahi |
| 9 | 0.52 | 0.50 | 7 | 2 | Sudhaar nahi |
| 10 | 0.54 | 0.50 | 7 | 3 | Counter = patience → STOP |
| 11, 12 | — | Kabhi train hi nahi hue | |||
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.
Ek aur (regularisation worksheet ka check)
Patience 2, validation losses: 0.80, 0.60, 0.62, 0.59, 0.61, 0.63.
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.
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
model.state_dict() = ek ordered dictionary: har layer ka naam → uska weight/bias tensor (aur BatchNorm ke running stats bhi).copy.deepcopy(...) zaroori hai: state_dict() live tensors ke references deta hai; bina deepcopy ke "saved" copy bhi aage ki training ke saath badalti rahegi aur aakhir me best ki jagah last weights milenge.model.load_state_dict(best_state) = saved weights wapas model me copy.fit() yahi karta hai, bas validation accuracy track karke (if val_acc > best_accuracy), aur end me model.load_state_dict(best_state). Loss track karo to "<" (kam behtar), accuracy track karo to ">" (zyada behtar).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
restore_best_weights default = False. PyTorch: manual with copy.deepcopy(model.state_dict()) + load_state_dict.Galti mat karna
best_state = model.state_dict() (bina deepcopy) — classic bug.Dimaag me aise baitha lo
Chai chakhte raho; 3 baar lagatar behtar na ho to gas band, aur best wali chai ki photocopy paroso.
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
transforms.Compose([...]) — transforms ki list ko order me ek ke baad ek lagata hai.RandomHorizontalFlip(p=0.5) — probability p se image left-right mirror. Default p = 0.5.RandomRotation(degrees=10) — [−10°, +10°] me se random angle.RandomAffine(degrees, translate=(a, b), scale=(lo, hi)) — rotation + shift (width ka a fraction, height ka b fraction tak) + zoom ek saath. Lab 14 KMNIST: degrees=8, translate=(0.08, 0.08), scale=(0.95, 1.05); SVHN: degrees=6, translate=(0.06, 0.06).ToTensor() — PIL/numpy (H, W, C) uint8 0–255 → float tensor (C, H, W) 0–1. Normalize(mean, std) — (x − mean)/std per channel; (0.5, 0.5) se [0,1] → [−1,1]. RGB ke liye 3-value tuples: Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)).datasets.FashionMNIST(root="data", train=True, download=True, transform=train_transform) (CNN chapter me detail — Chapter 23).Galti mat karna
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.
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
model.train() / model.eval() yahan zaroori hain kyunki Dropout aur BatchNorm train/eval me alag behave karte hain.torch.max(outputs, 1) → (max values, indices); index = predicted class. outputs.argmax(dim=1) same kaam.| L2 | L1 | Early stopping | Dropout | |
|---|---|---|---|---|
| Kya karta hai | Saare weights proportionally shrink | Fixed amount subtract; kuch weights 0 | Noise seekhne se pehle ruk jaata hai | Random neurons band |
| Boundary | Smooth | Smooth, bekaar inputs ignore | Pehle wala, simpler epoch | Smooth, robust |
| Dial | λ (weight_decay) | λ | patience | rate p |
| Dhyaan rakho | AdamW use karo; features scale karo | Hidden layers pe kam madad; cliff | Best weights restore karo | Zyada epochs; chhote network ko hurt |
| PyTorch | weight_decay=1e-4 | loss + lam*sum(p.abs().sum() ...) | deepcopy(state_dict()) + load_state_dict | nn.Dropout(0.3) |
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
nn.BatchNorm1d after Linear, nn.Dropout(0.3) after ReLU, optim.SGD(..., lr=0.1, weight_decay=1e-4).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
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
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.
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:
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.
nn.Dropout(p=0.5) wala p), default 0.5Galti mat karna: p ka matlab har jagah alag hai!
nn.Dropout(p): p = probability of an element to be zeroed (drop). p=0.3 matlab 30% band, 70% chalu.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.
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.
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.
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.
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 nahiOutput
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 kamaalOutput
zero fraction : 0.30008599162101746 survivor value: 1.4285714626312256 mean of output: 0.9998772740364075
Original paper me scaling test time pe hoti thi, training pe nahi. Isko "standard" (non-inverted) dropout kehte hain:
Same example, purane tareeke se
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 | |
|---|---|---|
| Training | mask, phir ÷ (1 − p) (survivors bade) | sirf mask |
| Test | kuch nahi, plain forward pass | outputs/weights × (1 − p) |
| Expected value match? | Haan | Haan |
| Fayda | Deployment code ko dropout ke baare me kuch pata hone ki zaroorat nahi | Paper ka original formulation |
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).
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.
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:
| Mask | Calculation | Output |
|---|---|---|
| dono on [1, 1] | 2 × (1 + 3) | 8 |
| sirf pehla [1, 0] | 2 × 1 | 2 |
| sirf doosra [0, 1] | 2 × 3 | 6 |
| dono off [0, 0] | 0 | 0 |
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.
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.)
Linear → ReLU → Dropout. Lab 9 aur Lab 12/13 dono yahi pattern use karte hain.Flatten → Dropout → Linear.nn.Dropout2d(p): ye single pixels nahi, poora channel (feature map) zero karta hai, kyunki ek channel ke neighbouring pixels highly correlated hote hain; ek-ek pixel drop karne se kuch khaas farak nahi padta.| Course lab | Dropout kahan | Value |
|---|---|---|
| Lab 9 (Fashion-MNIST MLP) | har ReLU ke baad | 0.3 |
| Lab 12 (Fashion-MNIST CNN), Lab 13 (CIFAR-10) | classifier me har ReLU ke baad | 0.4 (Lab 13 Optuna range 0.20–0.55, best ≈ 0.27) |
| Lab 14 KMNIST CNN | Flatten ke baad, final Linear se pehle | 0.30 default, Optuna 0.1–0.5 |
| Lab 14 SVHN ResNet | Flatten ke baad | 0.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.
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.
model.train(): training = True (model aur uske saare sub-modules me). Dropout ON (mask + scale), BatchNorm batch ke stats use karta hai aur running stats update karta hai.model.eval(): training = False. Dropout OFF (identity), BatchNorm saved running stats use karta hai.model.eval() gradients band nahi karta! Uske liye alag se with torch.no_grad(): lagao (memory aur speed ke liye). Dono alag kaam hain.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.
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
nn.Dropout(p=0.5): p = drop probability, default 0.5; training me survivors × 1/(1 − p) (inverted dropout); eval me identity.F.dropout ka training default True; use training=self.training.Galti mat karna
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.
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.
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.
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.
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):
bn.weight, init 1)bn.bias, init 0)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.
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.
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
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.
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.
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.
nn.BatchNorm1d(num_features): MLP (Linear layers) ke liye. Input shape (N, C) (ya (N, C, L) sequences ke liye). Har feature/neuron ka μ aur σ² batch ke N samples ke upar. num_features = C = previous Linear ka out_features.nn.BatchNorm2d(num_features): CNN ke liye. Input (N, C, H, W). Har channel ka μ aur σ² (N, H, W) teeno ke upar: us channel ki saari images ke saare pixels. num_features = C = previous Conv2d ka out_channels. Kyun per-channel? Ek channel = ek filter ka output, jo image me har jagah same kaam karta hai; isliye uske saare positions ko ek hi scale do (Chapter 23 ke CNN aur Chapter 25 ke ResNet me har conv ke baad yahi BatchNorm2d aayega).weight, β = bias) + 2 buffers (running_mean, running_var) + ek counter num_batches_tracked. Buffers training se update hote hain lekin gradient se nahi, isliye "parameters" me count nahi hote (par state_dict me save hote 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?
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.)
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.
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?
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])
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 hainOutput
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.)
Linear/Conv → BN → ReLU. Yaani activation se pehle, pre-activation z = Wx + b ko normalize karo. Lab 9 (Linear → BatchNorm1d → ReLU → Dropout), Lab 14 (Conv2d(bias=False) → BatchNorm2d → ReLU) aur ResNet blocks (Chapter 25) isi order me hain.Conv → ReLU → BatchNorm2d → MaxPool (activation ke baad). Ye bhi practice me chalta hai aur kaam karta hai. Exam me "standard/original placement" pooche to: before the activation.Linear → BN → ReLU → Dropout. Output layer pe na BN na Dropout (raw logits chahiye, CrossEntropyLoss khud softmax lagata hai).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 SAMEOutput
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.
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 bhiOutput
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.
Fayde (lecture slide "Batch Norm Recap" + paper)
Nuksaan
eval() bhoole to bugTeacher 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."
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.)
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 γ, β.
| BatchNorm | LayerNorm | |
|---|---|---|
| μ, σ² kiske upar | ek 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 test | Alag (batch stats vs running stats) | Same calculation |
| Running stats | Haan (running_mean, running_var) | Nahi |
| γ, β | per feature/channel | per feature (normalized_shape) |
| Kahan use | CNNs (ResNet, VGG-BN), MLPs | Transformers, RNN/LSTM |
| Paper | Ioffe & Szegedy 2015 | Ba, Kiros & Hinton 2016 |
Exam Radar · Batch Normalization
Galti mat karna
BatchNorm2d(C) ka argument channels ki sankhya hai, H ya W nahi.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 | Batch Normalization | |
|---|---|---|
| Main kaam | Regularization (overfitting kam) | Training fast/stable (side me mild regularization) |
| Kya karta hai | Random activations ko 0, survivors × 1/(1−p) | Har feature ko batch stats se normalize, phir γx̂ + β |
| Learnable params | 0 | 2 per feature/channel (γ, β) |
| Train mode | Random mask + scaling | Batch μ, σ²; running stats update |
| Eval mode | Identity (kuch nahi) | Running μ, σ² |
| Typical jagah | FC layers me activation ke baad; CNN classifier | Linear/Conv ke baad, activation se pehle |
| PyTorch | nn.Dropout(p), nn.Dropout2d(p), F.dropout | nn.BatchNorm1d(C), nn.BatchNorm2d(C) |
| Saal | 2014 (Srivastava, Hinton et al.) | 2015 (Ioffe, Szegedy) |
Chapter 21
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
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:
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).
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.
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)
Step 1 (backward)
Step 2
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.
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]])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.
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).
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.
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.
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%.
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".
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.
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).
Ab wo formula derive karte hain jisse Xavier aur He dono nikle. Ek neuron (bias = 0):
Step-by-step derivation
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.
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.
Derivation
a = √(6/(nin + nout)). "6 = 3 × 2": 3 uniform ke variance (a²/3) se, 2 Xavier ke numerator se.
Chalo numbers se samjhein
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.
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).
Factor 2 kahan se? (step by step)
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
nn.Conv2d(3, 64, 3): fan_in = 3×3×3 = 27, fan_out = 64×3×3 = 576, He std = √(2/27) ≈ 0.2722.| Init | Formula (Var(w)) | Normal std / Uniform bound | Best with | Problem it solves / problem it has |
|---|---|---|---|---|
| Zero | 0 | sab 0 | sirf biases ke liye | Symmetry: sab neurons identical; ReLU ke saath gradients bhi 0 |
| Constant (c) | 0 (sab same) | sab c | kahin nahi (hidden weights) | Symmetry wahi; bada c → saturation/explosion |
| Naive random small | σ² (jaise 0.01²) | N(0, 0.01²) | controlled experiments | Symmetry tooti, lekin signal shrink → vanishing |
| Naive random large | σ² (jaise 1) | N(0, 1) | kahin nahi | tanh/sigmoid saturate (vanishing), ReLU explode |
| LeCun | 1/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, linear | Forward + 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 networks | Kaam 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.
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?
kaiming_uniform_ ka default nonlinearity 'leaky_relu' hai, gain = √(2/(1 + a²)) = √(2/(1 + 5)) = √(1/3) ≈ 0.5774nn.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.
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).
nn.init.zeros_(t), nn.init.ones_(t), nn.init.constant_(t, val): fixed valuesnn.init.normal_(t, mean=0.0, std=1.0): N(mean, std²). Dhyan: argument std hai, variance nahinn.init.uniform_(t, a=0.0, b=1.0): U(a, b). Default range 0 se 1 (symmetric nahi!), isliye uniform_(w, -a, a) likhonn.init.xavier_uniform_(t, gain=1.0): bound = gain × √(6/(fan_in + fan_out))nn.init.xavier_normal_(t, gain=1.0): std = gain × √(2/(fan_in + fan_out))nn.init.kaiming_uniform_(t, a=0, mode='fan_in', nonlinearity='leaky_relu'): bound = gain × √(3/fan_mode); ReLU ke liye nonlinearity='relu' → gain √2 → bound √(6/fan_in)nn.init.kaiming_normal_(t, a=0, mode='fan_in', nonlinearity='leaky_relu'): std = gain/√fan_mode; ReLU → √(2/fan_in). a = leaky ReLU ka negative slope; mode = 'fan_in' (forward preserve) ya 'fan_out' (backward preserve)nn.init.calculate_gain(name): 'linear'/'sigmoid' → 1, 'tanh' → 5/3, 'relu' → √2, 'leaky_relu' → √(2/(1 + slope²))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.
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)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 + Init | L1 | L2 | L3 | L5 | L7 | L10 | mean L10 | ∂L/∂z std: L10 → L1 | Verdict |
|---|---|---|---|---|---|---|---|---|---|
| tanh + zero | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 → 0 | Kuch nahi bacha |
| tanh + N(0, 0.01²) | 0.157 | 0.0251 | 0.00401 | 0.000102 | 2.59e−6 | 1.04e−8 | ≈ 0 | 1 → 6.6e−8 | Har layer ×0.16: vanishing |
| tanh + N(0, 1) | 0.975 | 0.974 | 0.974 | 0.974 | 0.974 | 0.974 | 0.003 | 0.186 → 3130 | ±1 pe saturated; gradient unstable |
| tanh + Xavier | 0.628 | 0.486 | 0.408 | 0.32 | 0.269 | 0.224 | −0.001 | 0.952 → 0.267 | Dheere ghatta, zinda |
| tanh + He | 0.721 | 0.633 | 0.595 | 0.566 | 0.557 | 0.553 | −0.003 | 0.745 → 0.779 | Stable |
| ReLU + zero | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 → 0 | Dead network |
| ReLU + N(0, 0.01²) | 0.0939 | 0.0101 | 0.00113 | 1.48e−5 | 1.67e−7 | 2.2e−10 | 1.4e−10 | 0.681 → 1.95e−9 | Vanishing |
| ReLU + N(0, 1) | 9.39 | 101 | 1130 | 1.48e5 | 1.67e7 | 2.2e10 | 1.4e10 | 0.681 → 1.95e9 | Exploding |
| ReLU + Xavier | 0.587 | 0.396 | 0.275 | 0.141 | 0.0623 | 0.0201 | 0.0127 | 0.681 → 0.0283 | Har layer ~×0.7: dheere vanishing |
| ReLU + He | 0.83 | 0.793 | 0.777 | 0.80 | 0.705 | 0.642 | 0.407 | 0.681 → 0.641 | Stable (best match) |
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| Initialization | Weights kaise | Final train loss | Test accuracy | Kyun |
|---|---|---|---|---|
| Zero | zeros_ | 0.693147 | 50.00% | ReLU(0) aur ReLU′(0) = 0: kuch nahi seekha; loss = ln 2 |
| Constant 0.5 | constant_(w, 0.5) | 0.693151 | 50.00% | Symmetry: dono logits barabar, 50-50 |
| Naive Random | normal_(std=0.005) | 0.693147 | 50.20% | Signal 4 layers me hi mar gaya (vanishing) |
| Xavier | xavier_normal_ | 0.493034 | 82.60% | Theek scale, par ReLU ke saath thoda chhota |
| He | kaiming_normal_(relu) | 0.274678 | 87.80% | ReLU ke liye bana: best |
| (PyTorch default) | kuch nahi kiya | 0.578856 | 81.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
kaiming_uniform_(a=√5) = U(−1/√fan_in, 1/√fan_in); bias bhi same range.Galti mat karna
normal_(std=...) me square root daalo. 2/768 = 0.0026 variance hai, std 0.051.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].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
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
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."
| Parameters | Hyperparameters | |
|---|---|---|
| Kaun set karta hai | Training (gradient descent / backprop) | Hum (ya Optuna), training se pehle |
| Examples | Weights W, biases b, BatchNorm ke γ, β | lr, batch size, #layers, #neurons, dropout, optimizer, weight decay, epochs, activation, conv channels |
| Kahan dikhte | model.parameters() | Constructor arguments, optimizer arguments, DataLoader, loop range |
| Kaise milte | Loss ka gradient | Search: manual / grid / random / Bayesian (Optuna) |
| Kis data pe judge | Training set pe fit | Validation set pe score |
| Hyperparameter | Code me kahan | Optuna call (Lab 10 ANN) | Range |
|---|---|---|---|
| Hidden layers | MyNN loop | suggest_int | 1 – 5 |
| Neurons / layer | nn.Linear(in, n) | suggest_int(step=8) | 8 – 128 |
| Epochs | for epoch in range(epochs) | suggest_int(step=10) | 10 – 50 |
| Learning rate | optim.X(..., lr=) | suggest_float(log=True) | 1e-5 – 1e-1 |
| Dropout | nn.Dropout(p) | suggest_float(step=0.1) | 0.1 – 0.5 |
| Batch size | DataLoader(batch_size=) | suggest_categorical | 16, 32, 64, 128 |
| Optimizer | Adam / SGD / RMSprop | suggest_categorical | Adam, SGD, RMSprop |
| Weight decay (L2) | optim.X(..., weight_decay=) | suggest_float(log=True) | 1e-5 – 1e-3 |
Exam Radar
Galti mat karna
Dimaag me aise baitha lo
Khiladi jo practice me khud seekhe = parameter. Coach jo practice se pehle tay kare = hyperparameter.
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."
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.
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.
Chalo numbers se samjhein — Lab 10 ka search space grid me
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.
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.
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.
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.
~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
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.
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 = Tree-structured Parzen Estimator (Bergstra et al., 2011). Optuna me optuna.samplers.TPESampler — default. Kaam aise karta hai:
n_startup_trials (default 10) trials random (exploration; abhi seekhne ke liye data nahi).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.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.
| Manual | Grid search | Random search | Bayesian / TPE (Optuna) | |
|---|---|---|---|---|
| Kaise chunta hai | Insaan ka andaaza | Saare combinations | Random samples | Pichhle trials se seekh ke |
| Pichhle results use? | Haan (dimaag se) | Nahi | Nahi | Haan |
| Cost scaling | Insaan ka time | Exponential (kd) | Budget fixed (n_trials) | Budget fixed (n_trials) |
| Continuous ranges | Haan | Sirf grid points | Haan | Haan (log scale bhi) |
| Parallel | Mushkil | Bahut aasaan | Bahut aasaan | Thoda kam natural (sequential learning) |
| Early stop of bad trials | Haan | Nahi (by default) | Nahi (by default) | Haan — pruners |
| Kab use | Pehli intuition | 1–3 hyperparameters, chhota space | Bada space, simple baseline | Mehenge trials, bada space — default choice |
Exam Radar
TPESampler(seed=42) → reproducible; n_startup_trials=10 random trials first (default).RandomSampler, GridSampler, CMA-ES (CmaEsSampler).Dimaag me aise baitha lo
Grid = andha, Random = kismat, TPE = yaaddasht.
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.
| Term | Kya hai | Code me kaise milta |
|---|---|---|
| Study | Poori search (container): sampler ka maalik, har trial chalata hai, best yaad rakhta hai | optuna.create_study() |
| Trial | Ek attempt = hyperparameters ka ek specific combination, exactly ek baar try | objective(trial) ka argument (Optuna khud pass karta hai) |
| Objective function | Tumhara function: trial se values maango, model banao, train karo, score return karo | def objective(trial): ... return accuracy |
| Sampler | Strategy jo decide kare agla kya try karna hai; har pichhle trial se seekhta hai | study.sampler — default TPESampler |
| Pruner | Beech ke (intermediate) scores dekh ke bekaar trials ko jaldi band karta hai | optuna.pruners.MedianPruner() — default |
| direction | Score bada achha ('maximize', accuracy) ya chhota achha ('minimize', loss) | create_study(direction='maximize') — default minimize |
| n_trials | Objective 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 hai | trial.suggest_float / _int / _categorical |
| best_trial | Ab tak ka sabse achha trial | study.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 karegaLab 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.
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)study.best_params me isi key se aayega. Ek trial me har naam ek baar (same naam dobara = same value).suggest_float me step aur log ek saath allowed nahi.* ka matlab: step/log keyword-only hain, naam se likhne padenge (step=32, log=True).| Call | Kya values aa sakti hain | Kab use |
|---|---|---|
trial.suggest_float('lr', 1e-5, 1e-1, log=True) | 0.00001 se 0.1 tak koi bhi float, log-uniform | lr, 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.5 | Dropout (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, 3 | Depth |
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 string | Optimizer, activation |
trial.suggest_categorical('batch_size', [16, 32, 64, 128]) | Char me se ek int | Batch size (powers of 2) |
"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
units hamesha 32 ka multiple; n_layers 1–3.0.30000000000000004 = floating-point ka chhota sa artifact (0.1 × 3); matlab 0.3 hi. Lab 10 ke logs me bhi 'dropout_rate': 0.30000000000000004 dikhta hai — ghabrao mat.trial.number = trial ka index (0 se).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."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.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)
)
)nn.Sequential(*layers): * list ko alag-alag arguments me "khol" deta hai — nn.Sequential(l0, l1, l2, ...) ke barabar. nn.Sequential(layers) (bina *) error deta hai: TypeError: list is not a Module subclass.input_dim = neurons_per_layer wali line bhool gaye to doosri Linear ka input size galat → shape mismatch error.nn.Linear(neurons_per_layer, output_dim) pe koi activation/softmax nahi — nn.CrossEntropyLoss raw logits chahta hai.self.layers = [..]) to PyTorch unke parameters register nahi karega — isliye Sequential (ya nn.ModuleList).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.)
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.
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 tablecreate_study(direction=...): 'maximize' (accuracy) ya 'minimize' (loss). Default minimize — accuracy return karte waqt direction='maximize' bhool gaye to Optuna sabse kharab model dhoondhega!sampler=: default TPESampler() (bina seed). TPESampler(seed=42) se suggestions reproducible.pruner=: default MedianPruner(); lekin pruning tabhi hogi jab objective trial.report + should_prune kare (22.10).study.optimize(func, n_trials=None, timeout=None, n_jobs=1, gc_after_trial=False, show_progress_bar=False): n_trials = kitne trials; timeout = seconds ki limit; n_jobs = parallel threads; gc_after_trial=True = har trial ke baad memory saaf (Lab 14). Dobara optimize call karo to study wahin se aage badhta hai.study.best_trial = FrozenTrial: .number, .value, .params, .state (COMPLETE / PRUNED / FAIL).study.trials_dataframe() = saare trials ki table (Lab 13/14 isse sort karke dekhte hain).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).return karna bhool gaye → value None → trial FAIL.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_acctrial.report(value, step): intermediate value (yahan val accuracy) aur step (epoch number) record karta hai.trial.should_prune(): pruner se poochhta hai; True/False.raise optuna.TrialPruned(): exception; Optuna use pakad ke trial ko PRUNED state deta hai (error nahi maanta).optuna.pruners.MedianPruner(n_startup_trials=5, n_warmup_steps=0, interval_steps=1)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.
n_startup_trials=3 — abhi 3 complete trials nahi the, isliye koi pruning nahi; teeno complete.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.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
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.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.
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()plot_optimization_history(study): x = trial number, y = objective value; dots = har trial, line = best so far. "A rising curve means Optuna is learning where the good regions are." Line flat ho gayi → aur trials se khaas fayda nahi.plot_param_importances(study): har hyperparameter ka importance bar (default evaluator: Optuna 5 me PED-ANOVA; purane versions me fANOVA). "Tall bars = high importance; use this to narrow your search space next time." (Aksar lr sabse upar hota hai.)plot_slice (har param vs score), plot_contour (do params ka interaction), plot_parallel_coordinate, plot_intermediate_values (pruning curves). Matplotlib versions optuna.visualization.matplotlib me..cummax() ("best so far").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 COMPLETEbest_trial pehla wala (11) deta hai.optuna.trial.FixedTrial(best_params) ek nakli trial hai jo har suggest_* pe dict se fixed value lauta deta hai. Isse wahi build_model(trial) function dobara use karke best architecture ban jaata hai. (Lab 14 bhi FixedTrial se objective ko test karta hai.)Exam Radar
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.FixedTrial(params) = fixed values wala trial (testing / rebuild).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.
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
Subset(dataset, indices) (from torch.utils.data) — dataset ka sirf diya hua hissa. Tuning sasti rakhne ke liye: chhota data + kam epochs (2). Accuracy 0.5457 kam dikhti hai kyunki sirf 2 epochs × 12k images — ranking ke liye kaafi hai, final number ke liye nahi.torch.Generator().manual_seed(42 + trial.number) — har trial ka alag lekin reproducible shuffle.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).)
[16, 24, 32, 40]), aur architecture usse scale ho (C, 2C, 4C). Isse ek hi knob poore CNN ki capacity control karta hai.base_channels [16, 24, 32], blocks_per_stage = trial.suggest_int("blocks_per_stage", 1, 2) (depth!), dropout 0.05–0.4, lr 2e-4–2e-2 log, wd 1e-6–2e-3 log, batch [64, 128, 256], optimizer [AdamW, SGD] (SGD with momentum=0.9, nesterov=True).max over epochs) — per-trial early stopping jaisa.TPESampler(seed=SEED, n_startup_trials=2): quick mode me sirf 5 trials hain, to 10 random startup ki jagah 2 — "A short startup phase lets TPE use evidence even in quick mode."best_params = dict(study.best_params) → fresh model → full training split → fit() jo best-validation checkpoint restore karta hai (copy.deepcopy(model.state_dict()), 19.7) → test once → checkpoint dict save (model_state_dict, best_params, class_names).train_transform me (RandomAffine), eval_transform deterministic; SVHN me horizontal flip mana (19.8).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 TODO | Solution |
|---|---|
| MyNN: loop me 4 layers append | nn.Linear(input_dim, neurons_per_layer), nn.BatchNorm1d(neurons_per_layer), nn.ReLU(), nn.Dropout(dropout_rate); phir input_dim = neurons_per_layer |
| Final layer | layers.append(nn.Linear(neurons_per_layer, output_dim)) |
self.model = None | self.model = nn.Sequential(*layers) |
forward return None | return self.model(x) |
8 suggest_* calls | suggest_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 |
| Study | study = 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).
| TODO | Solution |
|---|---|
| Split | train_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] |
| MyNN | Same as Lab 10 (Linear → BatchNorm1d → ReLU → Dropout loop + final Linear + Sequential(*layers)) |
| TODO 1: suggest | layers 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: DataLoaders | DataLoader(train_dataset, batch_size=batch_size, shuffle=True, pin_memory=pin); test loader shuffle=False; pin = (device.type == "cuda") |
| TODO 3: model | input_dim = X_train.shape[1] (784), output_dim = len(classes) (10), MyNN(...).to(device) |
| TODO 4: loss + optimizer | nn.CrossEntropyLoss(); if/elif/else Adam / SGD / RMSprop with lr + weight_decay |
| TODO 5: training loop | model.train(); epochs × batches: forward → loss → zero_grad → backward → step; har epoch ke baad trial.report(...) + should_prune → raise optuna.TrialPruned() |
| Study | optuna.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.)
| TODO | Kya likhna hai |
|---|---|
| 1 — transforms | train_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 — split | train_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_loaders | Train loader shuffle=True (RandomSampler), val/test shuffle=False (SequentialSampler) |
| 4A/4B — CNN | KMNISTCNN upar wala (≥ 5 convs, BN, 2 maxpools, AdaptiveAvgPool2d, Dropout, Linear(4C, 10)); forward raw logits |
| 5A/5B — run_epoch, fit | run_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 — baseline | batch 128, base 32, dropout 0.30, Adam(lr=1e-3, weight_decay=1e-5) |
| 7A/7B/7C — Optuna | build_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 — retrain | Fresh model from study.best_params, full loaders, fit |
| 9A/9B/9C — evaluate + save | torch.no_grad() predictions + confidence (softmax max), metrics, confusion pairs, checkpoint dict save |
Exam Radar
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).study.best_params (dict), study.best_value (float), study.best_trial (FrozenTrial).log=True for lr / weight decay; needs low > 0; no step with log.plot_optimization_history, plot_param_importances (plotly).Galti mat karna
direction bhoolna (default minimize) jab accuracy return kar rahe ho.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
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
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.
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.
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.
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.
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?
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).
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.
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.)
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"):
| Idea | Matlab (simple bhasha) | Kaunsi problem solve hui |
|---|---|---|
| Local connectivity (sparse interactions) | Har output neuron sirf ek chhote patch (jaise 3×3) ko dekhta hai | Kam weights + padosi pixels ka structure use hota hai |
| Weight sharing (parameter sharing) | Wahi chhota detector (filter) poori photo par har jagah use hota hai | Aur bhi kam weights + pattern kahin bhi ho, pakda jaata hai (equivariance) |
| Pooling | Chhote area ka summary (max/average) le kar map chhota karo | Kam compute, thoda khisakne se farak nahi (local invariance) |
| Hierarchy (layers ki seedhi) | Edges → textures → parts → objects | Badi cheezein chhoti cheezon se banti hain |
Exam Radar
Galti mat karna
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.
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.
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).
X_train / 255.0 yahi karta hai)Chalo numbers se samjhein
Jawab: 0–255 → 0–1 → −1 se +1.
Ab bahut important baat, coding me roz kaam aayegi. PyTorch ki conv layers input ko is order me chahti hain:
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 jodoOutput
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
permute(2, 0, 1): HWC → CHW. unsqueeze(0): ek photo me batch dimension jodna → (1, C, H, W).Galti mat karna
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..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.
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.
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.
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
| 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
| 1 | 0 | 1 |
| 0 | 1 | 0 |
| 1 | 0 | 1 |
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.
| Output cell | Patch (rows, cols) | 4 kone | Centre | Jod |
|---|---|---|---|---|
| (0, 0) | rows 0–2, cols 0–2 | 1, 1, 0, 1 | 1 | 4 |
| (0, 1) | rows 0–2, cols 1–3 | 1, 0, 0, 1 | 1 | 3 |
| (0, 2) | rows 0–2, cols 2–4 | 1, 0, 1, 1 | 1 | 4 |
| (1, 0) | rows 1–3, cols 0–2 | 0, 1, 0, 1 | 0 | 2 |
| (1, 1) | rows 1–3, cols 1–3 | 1, 1, 0, 1 | 1 | 4 |
| (1, 2) | rows 1–3, cols 2–4 | 1, 0, 1, 0 | 1 | 3 |
| (2, 0) | rows 2–4, cols 0–2 | 0, 1, 0, 1 | 0 | 2 |
| (2, 1) | rows 2–4, cols 1–3 | 0, 1, 1, 0 | 1 | 3 |
| (2, 2) | rows 2–4, cols 2–4 | 1, 1, 1, 0 | 1 | 4 |
(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.
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.)
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).
| Type | Kernel kitni directions me slide karta hai | Data example | PyTorch |
|---|---|---|---|
| 1D convolution | 1 (sirf time/length ke along) | audio, ECG, stock prices, text sequences | nn.Conv1d, input (N, C, L) |
| 2D convolution | 2 (height aur width) | photos (grayscale/RGB) | nn.Conv2d, input (N, C, H, W) |
| 3D convolution | 3 (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
Galti mat karna
Dimaag me aise baitha lo
"Rakho, guna karo, jodo, likho, khisko." — Torch rakho, matching khaane guna, sab jod, output me likho, aage khisko.
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)."
| Kernel | Values | Kya karta hai | Weights ka jod | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Identity |
| Centre pixel copy kar deta hai (photo jaisi thi waisi) | 1 | |||||||||
| Vertical edge (left − right) |
| Khadi (vertical) edges: left roshan, right andhera → positive | 0 | |||||||||
| Horizontal edge (top − bottom) |
| Leti (horizontal) edges: upar roshan, neeche andhera → positive | 0 | |||||||||
| Sobel-x (right − left) |
| Vertical edges; beech wali row ko double weight (2) — thoda noise-robust. Right side roshan → positive | 0 | |||||||||
| Sobel-y |
| Horizontal edges (neeche − upar) | 0 | |||||||||
| Box blur | (1/9) ×
| 9 pixels ka average → photo dhundhli/smooth; noise kam | 1 | |||||||||
| Sharpen |
| Centre ko padosiyon se zyada ubhaarta hai → details tez | 1 |
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.
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).
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.
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].
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).
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 kernelOutput
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.]]]])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
Galti mat karna
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.
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.
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.
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.
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.
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).
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.
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 exampleOutput
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
conv.bias.shape = (out_channels,).out_channels.Galti mat karna
Dimaag me aise baitha lo
Jitne judge, utne naksha. Jitne filters, utne channels.
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."
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:
nn.Conv2d(32, 64, 3) → weight (64, 32, 3, 3), bias (64)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
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]) 112Exam Radar
Galti mat karna
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."
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.
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.
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.
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 -> 16Output
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=(2, 1) = height me 2, width me 1.Galti mat karna
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:
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.
Chalo numbers se samjhein
Sawaal 1 (worksheet): input [[1, 2], [3, 4]], P = 1.
Sawaal 2: hamara 5×5 input, kernel [1 0 1; 0 1 0; 1 0 1], P = 1, S = 1.
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 convolutionspadding 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
nn.Conv2d default padding=0; padding='same' sirf stride 1 ke saath (warna ValueError).Galti mat karna
Dimaag me aise baitha lo
Stride = kitna koodo. Padding = kitna frame lagao. 3×3 ke saath "1 ka frame, 1 ka kadam" = size same.
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."
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.
Chalo numbers se samjhein — teen important cases
Case A (AlexNet ki pehli layer): W = 227, K = 11, S = 4, P = 0, 96 filters.
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.
Case C (non-integer, floor ka kamaal): W = 6, K = 3, P = 0, S = 2.
Jawab: 55, 112, 2. Teeno PyTorch ne bhi confirm kiye (neeche code).
| Input W | K | P | S | Calculation | Output | Note |
|---|---|---|---|---|---|---|
| 32 | 5 | 0 | 1 | (32 − 5 + 0)/1 + 1 | 28 | valid conv; "32 − 5 + 1" (worksheet MCQ) |
| 28 | 3 | 1 | 1 | (28 − 3 + 2)/1 + 1 | 28 | same padding (Lab 12) |
| 227 | 11 | 0 | 4 | 216/4 + 1 | 55 | AlexNet conv1 |
| 224 | 11 | 0 | 4 | ⌊213/4⌋ + 1 = 53 + 1 | 54 | floor case |
| 7 | 3 | 0 | 2 | 4/2 + 1 | 3 | worksheet |
| 224 | 7 | 3 | 2 | ⌊223/2⌋ + 1 = 111 + 1 | 112 | halving |
| 6 | 3 | 0 | 2 | ⌊3/2⌋ + 1 = 1 + 1 | 2 | non-integer → floor |
| 32 | 3 | 1 | 2 | ⌊31/2⌋ + 1 = 15 + 1 | 16 | teacher solved: 32×32×3, 8 filters → 16×16×8 |
| 28 | 5 | 1 | 2 | ⌊25/2⌋ + 1 = 12 + 1 | 13 | teacher quiz: 6 filters → 13×13×6 |
| 7 | 3 | 0 | 3 | ⌊4/3⌋ + 1 = 1 + 1 | 2 | worksheet |
| 8 | 3 | 0 | 1 | 5/1 + 1 | 6 | worksheet 8×8 digit → 6×6 |
| 28 | 2 | 0 | 2 | 26/2 + 1 | 14 | 2×2 max pool (pooling bhi same formula) |
| 15 | 3 | 1 | 2 | ⌊14/2⌋ + 1 = 7 + 1 | 8 | teacher pooling quiz |
| 55 | 3 | 0 | 2 | 52/2 + 1 | 27 | AlexNet overlapping pool 3×3, s2 |
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
ceil_mode=True karke ceil le sakte ho).Galti mat karna
Dimaag me aise baitha lo
"Wall − Kernel + 2Padding, Stride se bhaag, floor, plus one." Poster wali deewar yaad karo.
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)."
Chalo numbers se samjhein
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.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.
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
Conv2d(1, 32, 3) → (9×1 + 1) × 32 = 320; Conv2d(32, 64, 3) → (9×32 + 1) × 64 = 289 × 64 = 18,496.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:
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.
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())) # 0Output
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
Galti mat karna
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.
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.
Exam Radar
Galti mat karna
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.
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.
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.
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.
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.
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.
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 tareekaOutput
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/4Output
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
nn.MaxPool2d(kernel_size=2) → stride default = kernel_size = 2. Conv2d stride default = 1.nn.AdaptiveAvgPool2d((1,1)).Galti mat karna
nn.MaxPool2d(2, 1) likh diya to stride 1 → size sirf 1 kam hoga, aadha nahi.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.
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.
Kaun kya deta hai?
| Operation | Translation ke saath behaviour | Kyun |
|---|---|---|
| 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) invariance | Window 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 pixels | Neither | Har position ke apne weights |
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.
Ab 1D max pool (window 2, stride 2) ki "local" invariance:
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: invariantOutput
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.0Image 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
Galti mat karna
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").
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."
Chalo numbers se samjhein
Case A: teen 3×3 conv, sab stride 1.
Case B (worksheet): teen 3×3, sab stride 2.
Case C (pooling ke saath): conv 3×3 (s1) → max pool 2×2 (s2) → conv 3×3 (s1).
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
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
Galti mat karna
Dimaag me aise baitha lo
Sipahi 3, havaldar 5, captain 7 — har level "+2", aur jahan stride/pool aaya wahan se "+2 × jump".
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:
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
Dimaag me aise baitha lo
Akshar → shabd → vaakya → kahani = edges → textures → parts → object.
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.
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.
| Layer | Output shape (N=1) | Shape kaise aaya | Params | Params calculation |
|---|---|---|---|---|
| Input | (1, 1, 28, 28) | grayscale 28×28 | 0 | — |
| Conv2d(1, 32, 3, padding='same') | (1, 32, 28, 28) | (28 − 3 + 2×1)/1 + 1 = 28; 32 filters | 320 | (3×3×1 + 1) × 32 |
| ReLU | (1, 32, 28, 28) | shape same | 0 | — |
| BatchNorm2d(32) | (1, 32, 28, 28) | shape same | 64 | γ, β per channel: 2 × 32 |
| MaxPool2d(2, 2) | (1, 32, 14, 14) | (28 − 2)/2 + 1 = 14 | 0 | — |
| Conv2d(32, 64, 3, padding='same') | (1, 64, 14, 14) | same padding; 64 filters | 18,496 | (3×3×32 + 1) × 64 = 289 × 64 |
| ReLU | (1, 64, 14, 14) | — | 0 | — |
| BatchNorm2d(64) | (1, 64, 14, 14) | — | 128 | 2 × 64 |
| MaxPool2d(2, 2) | (1, 64, 7, 7) | (14 − 2)/2 + 1 = 7 | 0 | — |
| Flatten | (1, 3136) | 64 × 7 × 7 = 3136 | 0 | — |
| Linear(3136, 128) | (1, 128) | — | 401,536 | 3136 × 128 + 128 |
| ReLU, Dropout(0.4) | (1, 128) | — | 0 | — |
| Linear(128, 64) | (1, 64) | — | 8,256 | 128 × 64 + 64 |
| ReLU, Dropout(0.4) | (1, 64) | — | 0 | — |
| Linear(64, 10) | (1, 10) | 10 logits | 650 | 64 × 10 + 10 |
| Total | 429,450 | conv 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
nn.CrossEntropyLoss.Galti mat karna
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.
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.
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')in_channels: input ke channels (grayscale 1, RGB 3, ya pichhli conv ke out_channels). Input tensor ke C se match karna zaroori.out_channels: filters ki ginti = output channels.kernel_size: int (3 → 3×3) ya tuple (3, 5).stride=1, padding=0 (int, tuple, 'valid', ya 'same' — 'same' sirf stride 1 ke saath), dilation=1, bias=True..weight shape (Cout, Cin, kH, kW), .bias shape (Cout).nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False) — stride=None ⇒ stride = kernel_size. (Positional order dhyan: 5th argument return_indices hai, isliye ceil_mode=True naam se likho.)nn.AvgPool2d(kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True).nn.AdaptiveAvgPool2d(output_size) / nn.AdaptiveMaxPool2d(output_size) — tum output size do, window PyTorch khud nikalta hai. (1, 1) = global pooling.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).
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).
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.
| Layer | Learnable params | Channels badle? | H, W badle? | Default / trap |
|---|---|---|---|---|
| nn.Conv2d | (K·K·Cin + 1)·Cout | Haan (→ out_channels) | Formula se | stride=1, padding=0 |
| nn.ReLU | 0 | Nahi | Nahi | — |
| nn.BatchNorm2d(C) | 2C | Nahi | Nahi | arg = channels |
| nn.MaxPool2d / AvgPool2d | 0 | Nahi | Haan (formula) | stride default = kernel_size |
| nn.AdaptiveAvgPool2d((1,1)) | 0 | Nahi | → 1×1 | output size deta hai |
| nn.Flatten | 0 | (C,H,W) → C·H·W | — | start_dim=1 |
| nn.Dropout | 0 | Nahi | Nahi | p=0.5; eval me off |
| nn.Linear | (in + 1)·out | — | — | in_features exact chahiye |
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
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.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)
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 = 400Jawab: 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
torch.zeros(1, 1, 28, 28) (grayscale) ya torch.zeros(1, 3, 32, 32) (CIFAR) — torch.no_grad() ke andar.Dimaag me aise baitha lo
Naap nahi pata? Putla chadha do. torch.zeros(1, C, H, W) → features → .numel().
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.
(0.5,), RGB (0.5, 0.5, 0.5)(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]].
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]]])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 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
datasets.FashionMNIST(root, train, download, transform): train=True → 60k, False → 10k.shuffle=True, test shuffle=False; batch shape (B, 1, 28, 28), labels (B,).__init__, __len__, __getitem__.Galti mat karna
Normalize((0.5), (0.5)) — bina comma ke ye tuple nahi, float hai. Habit: (0.5,).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".
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
evaluate eval mode me poore trained model se hai.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
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.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.
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.
| Part | Fashion-MNIST (Lab 12) | CIFAR-10 (Lab 13) |
|---|---|---|
| Image | 28×28, 1 channel (grayscale) | 32×32, 3 channels (RGB) |
| Input to CNN | (batch, 1, 28, 28) | (batch, 3, 32, 32) |
| First conv | Conv2d(1, 32, 3, padding='same') | Conv2d(3, 32, 3, padding='same') |
| Spatial sizes | 28 → 14 → 7 | 32 → 16 → 8 |
| Flatten | 64 × 7 × 7 = 3136 | 64 × 8 × 8 = 4096 |
| Data prep | reshape(-1, 1, 28, 28), /255 | permute(2, 0, 1), /255 |
| Hyperparameters | manually (SGD, lr 0.01) | Optuna se tuned (val set par) — Chapter 22 |
| Result | test ≈ 0.92 | train 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).
transforms.RandomHorizontalFlip(p=0.5): 50% chance se photo ko left-right mirror. Billi/car ke liye theek; digits/text ke liye nahi (mirror "2" ya "7" label badal deta hai) — Lab 14 (SVHN) ka note: "Do not use horizontal flips; mirrored digits are not label-preserving."transforms.RandomCrop(32, padding=4): pehle har side 4 pixel zero padding (32 → 40×40), phir random jagah se 32×32 kaat lo → photo ±4 pixel tak random khiskti hai (translation).transforms.RandomAffine(degrees=8, translate=(0.08, 0.08), scale=(0.95, 1.05)): chhota rotation, shift, zoom (Lab 14 KMNIST).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 copyOutput (augmentation pipeline ko ek 32×32 RGB image par chala kar check kiya)
torch.Size([3, 32, 32])
Exam Radar
Galti mat karna
Dimaag me aise baitha lo
Augmentation = ek hi billi ki alag-alag selfie. Label wahi, roop naya.
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.
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).
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]) 2080Kaam 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
Dimaag me aise baitha lo
1×1 conv = har glass ke andar ka mixer: padosi nahi dekhta, sirf flavours (channels) badalta hai.
Chapter 24
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
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.
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?
Jawab: top-1 error 20%, top-5 error 0%. Isliye leaderboards par top-5 number hamesha chhota dikhta hai.
| Saal | Team / Model | Top-5 error | Kya khaas tha |
|---|---|---|---|
| 2010 | NEC-UIUC (Lin et al.) | ≈ 28.2% | Hand-crafted features (SIFT, LBP) + linear SVM. No deep learning. |
| 2011 | XRCE (Perronnin et al.) | ≈ 25.8% | Fisher vectors + SVM. Abhi bhi shallow. |
| 2012 | AlexNet (SuperVision: Krizhevsky, Sutskever, Hinton) | 15.3% (runner-up ≈ 26.2%) | Pehla bada deep CNN, ReLU, dropout, 2 GPUs. ~11 percentage points ki chhalaang. |
| 2013 | ZFNet / 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. |
| 2014 | GoogLeNet (Inception v1, Szegedy et al.) — winner | 6.67% | Inception module, 1×1 bottleneck, 22 layers, sirf ~5–7M params. |
| 2014 | VGG (Simonyan & Zisserman) — 2nd | 7.3% (competition entry; baad me 6.8%) | Sirf 3×3 conv, 16–19 layers. Localization task me 1st. |
| 2015 | ResNet (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". |
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
Galti mat karna
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.
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.
Ye teeno Chapter 23 me derive ho chuke hain; yahan sirf use karenge. Do aur zaroori baatein:
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.
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.
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).
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.
| Layer | Kya karta hai | Output shape | Parameters (calculation) |
|---|---|---|---|
| Input | grayscale digit | 32×32×1 | 0 |
| C1 | 6 filters, 5×5, stride 1, no padding | 28×28×6 (32−5+1 = 28) | (5·5·1 + 1)·6 = 156 |
| S2 | 2×2 subsampling, stride 2 (har map ka 1 trainable coefficient + 1 bias) | 14×14×6 | 2·6 = 12 (modern avg/max pool me 0) |
| C3 | 16 filters, 5×5, partial connections to S2 maps | 10×10×16 (14−5+1 = 10) | 1,516 original | (5·5·6 + 1)·16 = 2,416 agar full connected |
| S4 | 2×2 subsampling, stride 2 | 5×5×16 | 2·16 = 32 (modern me 0) |
| C5 | 120 filters, 5×5×16 (filter = poora input!) | 1×1×120 (5−5+1 = 1) | (5·5·16 + 1)·120 = 48,120 |
| F6 | fully connected, 84 neurons, tanh | 84 | (120 + 1)·84 = 10,164 |
| Output | 10 units (original: Euclidean RBF; modern: Linear + softmax) | 10 | modern: (84 + 1)·10 = 850 (original RBF ke 84·10 = 840 fixed values) |
Chalo numbers se samjhein — total ≈ 60k kaise?
Jawab: original ≈ 60,000 params; modern PyTorch/Keras version = 61,706 params, jisme ~95.8% fully connected hisse me.
Flatten + nn.Linear(400, 120) likhte hain — dono ke params same: 48,120.| Stage | Input | C1 | S2 | C3 | S4 | C5 |
|---|---|---|---|---|---|---|
| Original image me receptive field | 1×1 | 5×5 | 6×6 | 14×14 | 16×16 | 32×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.
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.
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
Galti mat karna
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.
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.
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.)
| Layer | Settings | Output shape | Size calculation | Parameters |
|---|---|---|---|---|
| Input | RGB | 227×227×3 | — | 0 |
| Conv1 | 96 filters 11×11, stride 4, pad 0, ReLU (+LRN) | 55×55×96 | (227−11)/4+1 = 55 | (11·11·3+1)·96 = 34,944 |
| MaxPool1 | 3×3, stride 2 (overlapping) | 27×27×96 | ⌊(55−3)/2⌋+1 = 27 | 0 |
| Conv2 | 256 filters 5×5, pad 2, ReLU (+LRN) | 27×27×256 | (27−5+4)/1+1 = 27 | (5·5·96+1)·256 = 614,656 |
| MaxPool2 | 3×3, stride 2 | 13×13×256 | ⌊(27−3)/2⌋+1 = 13 | 0 |
| Conv3 | 384 filters 3×3, pad 1, ReLU | 13×13×384 | (13−3+2)+1 = 13 | (3·3·256+1)·384 = 885,120 |
| Conv4 | 384 filters 3×3, pad 1, ReLU | 13×13×384 | 13 | (3·3·384+1)·384 = 1,327,488 |
| Conv5 | 256 filters 3×3, pad 1, ReLU | 13×13×256 | 13 | (3·3·384+1)·256 = 884,992 |
| MaxPool3 | 3×3, stride 2 | 6×6×256 | ⌊(13−3)/2⌋+1 = 6 | 0 |
| Flatten | — | 9,216 | 6·6·256 | 0 |
| FC6 | 4096, ReLU, dropout 0.5 | 4096 | — | (9216+1)·4096 = 37,752,832 |
| FC7 | 4096, ReLU, dropout 0.5 | 4096 | — | (4096+1)·4096 = 16,781,312 |
| FC8 | 1000, softmax | 1000 | — | (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?
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)".
| # | Idea | Kya kiya / kyun |
|---|---|---|
| 1 | ReLU activation | tanh/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) |
| 2 | Dropout 0.5 | FC6 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) |
| 3 | Data 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. |
| 4 | LRN (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. |
| 5 | Overlapping max pooling | Window 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. |
| 6 | Multi-GPU training | Network 2 × GTX 580 (3GB) pe split; GPUs sirf kuch layers pe baat karte the. ~5–6 din training. |
| 7 | Training recipe | SGD + 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."
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)
torchvision.models.alexnet() thoda alag "one weird trick" version hai (channels 64-192-384-256-256, beech me AdaptiveAvgPool2d((6,6))): 61,100,840 params. Number dekh ke ghabrana mat.Exam Radar
Galti mat karna
Dimaag me aise baitha lo
"11-4-96" (pehli layer), "R-D-A-L-O-G": ReLU, Dropout, Augmentation, LRN, Overlapping pool, GPUs.
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.
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.)
| Block | Layer | Output shape | Parameters = (3·3·Cin + 1)·Cout |
|---|---|---|---|
| 1 | Conv1_1 (3→64) | 224×224×64 | (27 + 1)·64 = 1,792 |
| Conv1_2 (64→64) | 224×224×64 | (576 + 1)·64 = 36,928 | |
| MaxPool | 112×112×64 | 0 | |
| 2 | Conv2_1 (64→128) | 112×112×128 | 73,856 |
| Conv2_2 (128→128) | 112×112×128 | 147,584 | |
| MaxPool | 56×56×128 | 0 | |
| 3 | Conv3_1 (128→256) | 56×56×256 | 295,168 |
| Conv3_2 (256→256) | 56×56×256 | 590,080 | |
| Conv3_3 (256→256) | 56×56×256 | 590,080 | |
| MaxPool | 28×28×256 | 0 | |
| 4 | Conv4_1 (256→512) | 28×28×512 | 1,180,160 |
| Conv4_2 (512→512) | 28×28×512 | 2,359,808 | |
| Conv4_3 (512→512) | 28×28×512 | 2,359,808 | |
| MaxPool | 14×14×512 | 0 | |
| 5 | Conv5_1 (512→512) | 14×14×512 | 2,359,808 |
| Conv5_2 (512→512) | 14×14×512 | 2,359,808 | |
| Conv5_3 (512→512) | 14×14×512 | 2,359,808 | |
| MaxPool | 7×7×512 | 0 | |
| Classifier | Flatten | 7·7·512 = 25,088 | 0 |
| FC1 (25,088→4096) | 4096 | (25,088 + 1)·4096 = 102,764,544 | |
| FC2 (4096→4096) | 4096 | 16,781,312 | |
| FC3 (4096→1000) | 1000 | 4,097,000 | |
| Total (13 conv = 14,714,688; 3 FC = 123,642,856) | 138,357,544 ≈ 138.4M | ||
Chalo numbers se samjhein
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.
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:
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.
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.
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.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
Galti mat karna
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".
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.
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):
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.
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?
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.
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).
Jawab: output = 2.5. 1×1 conv ne 4 numbers ko 1 me "mix" kar diya — padosi pixels ko nahi dekha.
Teacher ka L12 example (parameters ginna, 64 channels)
Input 28×28×64, chahiye 28×28×32 via 5×5.
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).
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).
| Stage | Kya | Output |
|---|---|---|
| Input | RGB | 224×224×3 |
| Stem | 7×7 conv 64, stride 2 → 3×3 max-pool s2 → 1×1 conv 64 → 3×3 conv 192 → 3×3 max-pool s2 | 112×112×64 → 56×56×64 → 56×56×192 → 28×28×192 |
| Inception 3a, 3b | 2 modules | 28×28×256 → 28×28×480 |
| Max-pool s2 | — | 14×14×480 |
| Inception 4a–4e | 5 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, 5b | 2 modules | 7×7×832 → 7×7×1024 |
| Global average pool | har 7×7 map ka average | 1×1×1024 |
| Dropout (40%) → Linear → softmax | ek hi FC | 1000 |
Module widths (notebook ka table; ratta zaroori nahi, pattern samjho):
| Module | 1×1 | 3×3 reduce | 3×3 | 5×5 reduce | 5×5 | pool proj | Output |
|---|---|---|---|---|---|---|---|
| 3a | 64 | 96 | 128 | 16 | 32 | 32 | 256 |
| 3b | 128 | 128 | 192 | 32 | 96 | 64 | 480 |
| 4a | 192 | 96 | 208 | 16 | 48 | 64 | 512 |
| 4b | 160 | 112 | 224 | 24 | 64 | 64 | 512 |
| 4c | 128 | 128 | 256 | 24 | 64 | 64 | 512 |
| 4d | 112 | 144 | 288 | 32 | 64 | 64 | 528 |
| 4e | 256 | 160 | 320 | 32 | 128 | 128 | 832 |
| 5a | 256 | 160 | 320 | 32 | 128 | 128 | 832 |
| 5b | 384 | 192 | 384 | 48 | 128 | 128 | 1024 |
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).
Chalo numbers se samjhein
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.)
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:
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 logitsOutput
GoogLeNetOutputs torch.Size([2, 1000]) torch.Size([2, 1000]) torch.Size([2, 1000]) Tensor
Exam Radar
Galti mat karna
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.
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.
Chalo numbers se samjhein (worksheet ke numbers)
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
| LeNet-5 | AlexNet | VGG-16 | GoogLeNet (Inception v1) | ResNet (Ch 25) | |
|---|---|---|---|---|---|
| Saal | 1998 | 2012 | 2014 | 2014 | 2015 |
| Authors | LeCun, Bottou, Bengio, Haffner | Krizhevsky, Sutskever, Hinton | Simonyan, Zisserman (Oxford VGG) | Szegedy et al. (Google) | He, Zhang, Ren, Sun (Microsoft) |
| Depth (weight layers) | 5 (2 conv + 3 FC*) — paper: 7 layers | 8 (5 conv + 3 FC) | 16 (13 conv + 3 FC); VGG-19: 19 | 22 (9 inception modules) | 18 / 34 / 50 / 101 / 152 |
| Params | ≈ 60k (61,706 modern) | ≈ 60–62M | ≈ 138M (VGG-19 ≈ 144M) | ≈ 5–7M | 11.7M (18) … 25.6M (50) … 60.2M (152) |
| Input | 32×32×1 | 227×227×3 (paper: 224) | 224×224×3 | 224×224×3 | 224×224×3 |
| Kernels | 5×5 | 11×11, 5×5, 3×3 | sirf 3×3 | 1×1, 3×3, 5×5 parallel | 3×3 (+1×1 in bottleneck), 7×7 stem |
| Key idea | conv + pooling + weight sharing, end-to-end | scale up: ReLU, dropout, augmentation, GPUs, LRN | depth via stacked small 3×3 filters | multi-scale parallel branches, 1×1 bottleneck, GAP, aux classifiers | residual / skip connections, BN, bottleneck |
| Activation | tanh / sigmoid | ReLU | ReLU | ReLU | ReLU |
| Pooling | average (subsampling) | max 3×3/2 (overlapping) | max 2×2/2 | max + global average | max (stem) + global average |
| Classifier head | FC 120-84-10 | FC 4096-4096-1000 | FC 4096-4096-1000 | GAP + 1 FC | GAP + 1 FC |
| Normalization / reg. | — | LRN, dropout 0.5 | dropout 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":
| Model | Total params | FC params | Train acc | Test acc |
|---|---|---|---|---|
| Baseline CNN (2 conv + big FC) | 1.97M | 1.96M (99%) | 97.97% | 72.23% |
| AlexNet (CIFAR version) | 35.9M | 33.6M (94%) | 88.87% | 77.34% |
| VGG-style + BN | 9.2M | ≈0.005M | 96.68% | 81.76% |
| ResNet-18 (CIFAR version) | 11.2M | ≈0.005M | 93.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
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
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.
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.
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."
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)
| Model | Parameters | Final training loss |
|---|---|---|
| Shallow plain · 4 layers (stem + 1 block of two 3×3 convs + linear) | 16,938 | 0.150 |
| Deep plain · 26 layers (12 blocks) | 29,786 | 2.300 |
| Deep residual · 26 layers (same 12 blocks + shortcuts) | 29,786 | 0.0068 (test acc 95.3%) |
Jawab: zyada capacity (29,786 > 16,938) ≠ behtar training. Problem optimization ki hai, aur residual connection usko theek karta hai.
Exam Radar
Galti mat karna
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.
Chalo numbers se samjhein (worksheet ka example)
Gradient samples A = [1, −1, 1, −1] aur B = [1, 1, −1, −1].
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").
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.
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
Galti mat karna
Dimaag me aise baitha lo
Vanishing = dheemi awaaz. Shattered = tez static. Dono ka ilaaj ek shortcut wire.
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.
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.
Chalo numbers se samjhein
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.
Ab backward pass dekho (chain rule, Chapter 12). y = F(x) + x ko x se differentiate karo:
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
Chalo numbers se samjhein
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
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+01Exam Radar
Galti mat karna
x + F(x) hai, x * F(x) ya concat nahi.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!
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
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.
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.
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 != 1 or in_channels != out_channels.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):
| Option | Kaise | Params | Note |
|---|---|---|---|
| A — zero-padding identity | Shortcut me stride 2 se subsample (har doosra pixel), aur extra channels zeros se bhar do (64 asli + 64 zero) | 0 | Parameter-free; CIFAR ResNets me use hua. |
| B — projection jahan zaroori | Sirf dimension badalne wale shortcuts me 1×1 conv, stride 2 (+ BN); baaki sab identity | thode | Standard choice (torchvision me downsample). |
| C — sab projection | Har shortcut 1×1 conv | zyada | B se marginally behtar, par extra params/memory — faayda nahi maana gaya. |
Chalo numbers se samjhein
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.
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).
Chalo numbers se samjhein (weights only, bias/BN chhod ke)
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.
| Stage (output) | ResNet-18 | ResNet-34 | ResNet-50 | ResNet-101 | ResNet-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 |
| head | global average pool → FC 1000 → softmax | ||||
| Block type | basic | basic | bottleneck | bottleneck | bottleneck |
| Blocks | [2,2,2,2] | [3,4,6,3] | [3,4,6,3] | [3,4,23,3] | [3,8,36,3] |
| Layer count | 1 + 8·2 + 1 = 18 | 1 + 16·2 + 1 = 34 | 1 + 16·3 + 1 = 50 | 1 + 33·3 + 1 = 101 | 1 + 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
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 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)
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).model.eval() zaroor — warna BN batch statistics use karega aur dropout chalta rahega.Lecture 11 aur "Advanced CNN Architectures and applications" decks me classification ke aage ki duniya bhi thi. MCQ ke liye zaroori baatein:
Classification me hum size ghatate hain (pool, stride). Segmentation (har pixel ka label) me wapas original size chahiye. Slides ke tareeke:
nn.ConvTranspose2d, dilation 1)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.)
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).
Exam Radar
| VGG | Inception (GoogLeNet) | ResNet | |
|---|---|---|---|
| Ek line | deeper sequential stack | wider: parallel multi-scale branches | shortcut connections |
| Merge operation | — (seedha chain) | concatenate (channels jodo) | add (element-wise) |
| Shape rule | — | H×W same, channels alag chal sakte | poora shape same (warna projection) |
| 1×1 conv ka role | (config C me thoda) | bottleneck before 3×3/5×5, pool projection | bottleneck (50+), projection shortcut |
| Head | 3 bade FC | GAP + 1 FC | GAP + 1 FC |
| Max depth (ImageNet) | 19 | 22 | 152 (CIFAR: 1202) |
Exam Radar
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!