ResNet — Skip Connection
এই পাঠে যা শিখবেন
- Degradation problem — কেন deep stack ভেঙে পড়ে
- Residual learning — $F(x) + x$-এর intuition ও math
- Bottleneck block — ResNet-50/101/152
- Skip connection-এ gradient flow analysis
- PyTorch-এ ResNet-18 from scratch
- Modern descendants — DenseNet, ResNeXt, Wide ResNet
১ · Degradation problem — paradox
২০১৫-এর আগে CNN community-এর ধারণা ছিল — "deeper = better"। VGG ১৯ layer থেকে ৩০ layer-এ গেলে accuracy বাড়ার কথা। বাস্তবে — ৩০-৫৬ layer-এর plain stack-এর train accuracy পড়ে যায়।
- Overfitting না — train accuracy-ও কমে।
- Vanishing gradient-ও না — BN দিয়ে fix করা যাচ্ছিল।
- "Optimization difficulty" — He et al.-এর identification।
Deep network-এর optimizer identity function-ও শিখতে কষ্ট পায়। শুধু extra layer-গুলো $H(x) = x$ output করলেই baseline accuracy match হত — কিন্তু solver সেটা পারে না।
২ · Residual learning — He-এর insight
যদি network-এর কাজ identity শেখা হয় — সেটা সরাসরি না শিখিয়ে, বলি "input-এর সাথে কী যোগ করতে হবে"।
Plain block: $y = H(x)$ — full mapping শিখতে হয়।
Residual block: $y = F(x) + x$ — শুধু "residue" $F(x)$ শিখতে হয়।
যদি optimal $H(x) = x$ হয় — $F(x) = 0$ শেখা trivially সহজ (সব weight $\to 0$)। Plain network-এ identity শেখা সম্পূর্ণ ভিন্ন কাজ।
৩ · Residual block — basic ও bottleneck
Basic block (ResNet-18, 34):
- Conv $3 \times 3$ → BN → ReLU
- Conv $3 \times 3$ → BN
- + skip connection
- ReLU
Bottleneck block (ResNet-50, 101, 152):
- Conv $1 \times 1$ → BN → ReLU (channel reduce)
- Conv $3 \times 3$ → BN → ReLU (spatial)
- Conv $1 \times 1$ → BN (channel expand)
- + skip connection
- ReLU
৪ · Skip connection — শুধু copy বা projection
Input ($x$) এবং $F(x)$-এর shape একই হলে — skip directly $x$।
Spatial বা channel mismatch হলে — $1 \times 1$ projection conv:
$$y = F(x) + W_s \cdot x$$
(stride-2 দিয়ে spatial কমান এবং $1 \times 1$ দিয়ে channel adjust)।
৫ · Gradient flow — magical math
Backward pass-এ:
$$\frac{\partial L}{\partial x_l} = \frac{\partial L}{\partial x_L} \cdot \left( 1 + \frac{\partial}{\partial x_l} \sum_{i=l}^{L-1} F(x_i) \right)$$
- $1$-এর term — sum-এ direct gradient flow। Vanishing impossible।
- Gradient deep layer থেকে shallow layer-এ unaltered পৌঁছে।
- This single insight — DL-কে ১০০০ layer-এ যেতে দিল (২০১৬-র "Deep Residual Learning")।
৬ · ResNet-18 — PyTorch from scratch
import torch
import torch.nn as nn
import torch.nn.functional as F
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, in_ch, out_ch, stride=1):
super().__init__()
self.conv1 = nn.Conv2d(in_ch, out_ch, 3, stride, 1, bias=False)
self.bn1 = nn.BatchNorm2d(out_ch)
self.conv2 = nn.Conv2d(out_ch, out_ch, 3, 1, 1, bias=False)
self.bn2 = nn.BatchNorm2d(out_ch)
if stride != 1 or in_ch != out_ch:
self.shortcut = nn.Sequential(
nn.Conv2d(in_ch, out_ch, 1, stride, bias=False),
nn.BatchNorm2d(out_ch),
)
else:
self.shortcut = nn.Identity()
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)), inplace=True)
out = self.bn2(self.conv2(out))
out = out + self.shortcut(x) # skip!
return F.relu(out, inplace=True)
class ResNet18(nn.Module):
def __init__(self, num_classes=1000):
super().__init__()
self.stem = nn.Sequential(
nn.Conv2d(3, 64, 7, 2, 3, bias=False),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
nn.MaxPool2d(3, 2, 1),
)
self.layer1 = self._make(64, 64, 2, 1)
self.layer2 = self._make(64, 128, 2, 2)
self.layer3 = self._make(128, 256, 2, 2)
self.layer4 = self._make(256, 512, 2, 2)
self.gap = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Linear(512, num_classes)
def _make(self, in_ch, out_ch, n_block, stride):
layers = [BasicBlock(in_ch, out_ch, stride)]
for _ in range(n_block - 1):
layers.append(BasicBlock(out_ch, out_ch))
return nn.Sequential(*layers)
def forward(self, x):
x = self.stem(x)
x = self.layer1(x); x = self.layer2(x)
x = self.layer3(x); x = self.layer4(x)
x = self.gap(x).flatten(1)
return self.fc(x)
model = ResNet18()
print("Params:", sum(p.numel() for p in model.parameters()))
# ~11.7M
৭ · ResNet variants
- ResNet-18: 2-2-2-2 BasicBlock। 11.7M params।
- ResNet-34: 3-4-6-3 BasicBlock। 21M params।
- ResNet-50: 3-4-6-3 Bottleneck। 25M params।
- ResNet-101: 3-4-23-3 Bottleneck। 44M params।
- ResNet-152: 3-8-36-3 Bottleneck। 60M params।
৮ · ResNet-এর descendants
- DenseNet (২০১৬): "all-to-all" skip — every layer-এ আগের সব layer concatenate।
- ResNeXt (২০১৬): ResNet + Inception cardinality — parallel branch।
- Wide ResNet (২০১৬): shallow but wide।
- Highway Networks (২০১৫): ResNet-এর gated cousin।
- Pre-activation ResNet: BN-ReLU আগে, conv পরে।
- Transformer: "x + Attention(x)" — direct residual idea।
৯ · কেন এত successful
- Optimization easier: identity sub-network always available।
- Gradient flow: direct path shallow → deep।
- Implicit ensemble: exponentially many sub-paths (Veit et al. ২০১৬)।
- BN compatibility: mean stable, not exploding।
- Generalization: empirically strong across task।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ "ResNet — implicit ensemble of shallower networks" (Veit, Wilber, Belongie ২০১৬) — এই interpretation কী? Skip connection drop করলে কী ঘটে?
Veit et al.-এর "Residual Networks Behave Like Ensembles of Relatively Shallow Networks" — ResNet-এর alternative theoretical understanding।
Ensemble interpretation:
- $n$ residual block — $2^n$ possible path।
- Each block — skip বা F(x) "binary choice"।
- প্রতিটি path — different network।
- Output — সব path-এর effective sum।
Mathematical expansion:
- $y_3 = y_2 + F_3(y_2) = (y_1 + F_2(y_1)) + F_3(y_1 + F_2(y_1))$।
- Recursive expand — $2^n$ sub-network-এর sum।
- Direct path (all skip) — input passthrough।
- Long path (all F) — full computation।
Empirical evidence:
- Test time block drop — small accuracy drop।
- Plain network — single block drop দেদার damage।
- Multiple block drop — gradual degradation।
Path length distribution:
- Most paths — relatively short (5-17 blocks)।
- Long paths — exist but contribute less।
- Effective depth small।
Stochastic Depth (Huang ২০১৬):
- Train-এ randomly block drop।
- Test-এ full network।
- Implicit shorter ensemble।
- 1200-layer ResNet train possible।
Implementation:
class StochasticBlock(nn.Module):
def __init__(self, in_ch, out_ch, drop_p=0.0):
super().__init__()
self.block = BasicBlock(in_ch, out_ch)
self.drop_p = drop_p
def forward(self, x):
if self.training and torch.rand(1) < self.drop_p:
return x # skip block
return self.block(x)
Implications:
- "Deep" misnomer — effectively shallow ensemble।
- Path diversity learning-এ key।
- Architecture-এ "hidden" parallel computation।
- Training dynamics — multi-scale interaction।
Theoretical implications:
- Generalization bound — depth less important।
- Width vs depth trade-off।
- Wide ResNet (shallower wider) competitive।
- Architecture exploration insight।
Modern relevance:
- EfficientNet — scaling balance based on this insight।
- Transformer — similar residual ensemble structure।
- NAS — block-wise search natural।
Counter-argument:
- "Pure ensemble" overstates।
- Path interaction matters।
- Joint training — pure ensemble different।
- Empirical limit।
Practical implications:
- ResNet robust — block fail tolerable।
- Transfer learning effective।
- Layer pruning practical।
- Stochastic depth regularization।
Bangladesh edge deployment:
- Block pruning model size reduce।
- Adaptive inference compute save।
- Mobile-friendly variants।
- Quality-cost trade-off।
মূল উপলব্ধি: ResNet — single deep network deceptive। Actually $2^n$ shallow path ensemble। Skip connection block-level ensemble enable। Stochastic depth regularization extension। Modern architecture — ensemble interpretation pervasive। Practical pruning, robustness, transfer learning insight। DL theory-র elegant alternative perspective।
প্র ০২ Pre-activation ResNet (He et al. ২০১৬) — original ResNet-এর successor। কী পরিবর্তন, কেন এটা better gradient flow?
He et al.-এর follow-up paper "Identity Mappings in Deep Residual Networks" — ResNet design refinement।
Original ResNet block:
- Conv → BN → ReLU → Conv → BN → ADD → ReLU
- BN-ReLU after conv (post-activation)।
- Final ReLU after sum।
Pre-activation version:
- BN → ReLU → Conv → BN → ReLU → Conv → ADD
- BN-ReLU before conv (pre-activation)।
- No ReLU after sum।
- Identity path completely clean।
Why "identity matters":
- Original — sum-এর পরে ReLU।
- ReLU non-linear — identity path এ "interferes"।
- Pre-activation — sum after conv, no ReLU।
- Pure additive identity flow।
Mathematical advantage:
- $y = x + F(x)$ exactly।
- Gradient: $\frac{\partial L}{\partial x} = \frac{\partial L}{\partial y} \cdot (1 + \frac{\partial F}{\partial x})$।
- The "1" — pure identity path, never blocked।
- Original-এ ReLU sometimes derivative 0।
Empirical results:
- ResNet-1001 — pre-activation যথেষ্ট train।
- ResNet-1001 (post-activation) — diverge।
- 200-layer-এ ~1% accuracy gain।
- Deeper network easier।
Implementation:
class PreActBlock(nn.Module):
def __init__(self, in_ch, out_ch, stride=1):
super().__init__()
self.bn1 = nn.BatchNorm2d(in_ch)
self.conv1 = nn.Conv2d(in_ch, out_ch, 3,
stride, 1, bias=False)
self.bn2 = nn.BatchNorm2d(out_ch)
self.conv2 = nn.Conv2d(out_ch, out_ch, 3,
1, 1, bias=False)
self.shortcut = (
nn.Conv2d(in_ch, out_ch, 1, stride, bias=False)
if (stride != 1 or in_ch != out_ch)
else nn.Identity()
)
def forward(self, x):
out = F.relu(self.bn1(x), inplace=True)
shortcut = self.shortcut(out if not isinstance(self.shortcut, nn.Identity) else x)
out = self.conv1(out)
out = F.relu(self.bn2(out), inplace=True)
out = self.conv2(out)
return out + shortcut
Comparison summary:
- Post-activation (original): ReLU(x + F(x))
- Pre-activation: x + F(x), F has BN-ReLU first
- Cleaner identity path
- Better very-deep training
Why post-activation worked too:
- 50-152 layer — ReLU effect minor।
- Deeper — accumulate problem।
- Most production ResNet still post-activation।
Modern adoption:
- 1000-layer experiments — pre-activation।
- Standard 50-152 — post-activation।
- Both pre-trained available।
- Choice depends on depth।
Transformer parallel:
- Pre-LN Transformer (newer)।
- Post-LN Transformer (original)।
- Same gradient flow consideration।
- Pre-LN training stability।
Practical recommendations:
- Standard depth — original sufficient।
- Very deep experimentation — pre-activation।
- NAS — both options consider।
- Domain-specific tune।
Bangladesh deployment:
- Standard ResNet sufficient most cases।
- Custom training scenarios — pre-activation।
- Pre-trained both available।
- Architecture detail rarely matters production।
মূল উপলব্ধি: Pre-activation ResNet — refinement focused on gradient flow। Identity path কোনো interference নেই। Very deep network train enable। Production-এ rare necessity but theoretical important। Transformer-এ similar pattern (Pre-LN/Post-LN)। Architecture detail matters scaling extreme। Standard usage — original sufficient।
প্র ০৩ "Skip connection = বিপ্লব" — কেন? এই simple addition কেন এত গুরুত্বপূর্ণ ছিল CNN evolution-এ?
Skip connection — ML history-র সবচেয়ে impactful single idea-গুলোর একটি। Simple কিন্তু transformative।
Pre-skip era সমস্যা:
- Vanishing gradient — sigmoid/tanh deep network।
- Optimization difficulty — even 30 layer struggle।
- BN আশাকাল করল — কিন্তু 100+ layer impossible।
- Architecture barrier।
Skip connection ফল:
- 1000+ layer network train possible।
- ImageNet error 3% (human-level)।
- Detection, segmentation — সব advance।
- Foundation model era enable।
Cross-architecture impact:
- Transformer: "x + Attention(x), x + FFN(x)" — fundamental।
- U-Net: encoder-decoder skip — segmentation revolution।
- DenseNet: all-to-all extreme version।
- Wide ResNet, ResNeXt, EfficientNet: সব residual।
Why "small" addition transformative:
- Optimization landscape smooth।
- Identity mapping easy learn।
- Gradient direct flow।
- Training dynamics stable।
The minimal change argument:
- Pre-ResNet block: $y = F(x)$।
- Post-ResNet block: $y = F(x) + x$।
- Single addition operation।
- No new learnable parameter।
- Compute negligible increase।
Theoretical perspective:
- Optimization: identity easy reach।
- Generalization: implicit regularization।
- Ensemble: exponential path।
- Information flow: bypass available।
Beyond DL:
- Highway networks (Schmidhuber, ২০১৫) — gated version।
- LSTM cell state — temporal skip।
- Recurrent skip — sequence model।
- Idea pervasive across architecture।
Industrial transformation:
- Pre-ResNet — DL niche application।
- Post-ResNet — every CV task।
- Foundation model basis।
- Modern AI infrastructure।
Hinton's quote:
- "The most important idea in deep learning"।
- (Across many talks)।
- Not exaggeration।
- Skip connection central role।
Modern variations:
- Dense: all-to-all।
- Multi-scale: different resolution।
- Attention residual: Transformer।
- Gated: learnable weights।
Why was it "missed" so long:
- Field's depth focus — kept adding layers।
- Hindsight obvious — পেতে কঠিন।
- Empirical observation pre-ResNet।
- Theoretical understanding post-hoc।
Lesson for researchers:
- Simple ideas powerful।
- Question fundamental assumption।
- Architecture detail matters।
- Theory follow practice।
Bangladesh implication:
- Modern model adopt — skip standard।
- Training stability default।
- Transfer learning effective।
- Local task — modern architecture।
মূল উপলব্ধি: Skip connection — DL-এর foundational breakthrough। Single addition operation, transformative impact। Optimization, generalization, gradient flow — সব address। Modern architecture-এ universal। Bangladesh — modern model standard usage, skip implicit। ML innovation — sometimes simplest idea most powerful। ResNet legacy — eternal।
প্র ০৪ Bangladesh medical imaging-এ ResNet-50 fine-tune করছেন chest X-ray-তে। Limited data (~5000 image)। কী strategy অনুসরণ করবেন?
Medical imaging + small data + ResNet-50 — practical Bangladesh scenario। Strategy carefully balance।
Pre-trained model selection:
- ImageNet pretrained — natural image, transferable।
- RadImageNet — medical pretrained — better।
- CheXNet — DenseNet121, chest X-ray specific।
- Best: domain-pretrained + ImageNet।
Transfer learning options:
- (1) Feature extraction: freeze all + new head।
- (2) Fine-tune top: freeze early + train last layers।
- (3) Full fine-tune: all weights train, low lr।
- (4) Discriminative lr: different lr per layer।
5000 sample - approach 2 ভাল:
import torchvision.models as m
model = m.resnet50(weights='IMAGENET1K_V2')
# Replace head
model.fc = nn.Sequential(
nn.Dropout(0.5),
nn.Linear(2048, 14), # 14 disease class
)
# Freeze early layers
for p in model.layer1.parameters():
p.requires_grad = False
for p in model.layer2.parameters():
p.requires_grad = False
# Train layer3, layer4, fc
trainable = [p for p in model.parameters() if p.requires_grad]
optim = torch.optim.AdamW(trainable, lr=1e-4, weight_decay=0.01)
Discriminative learning rates:
params = [
{'params': model.layer3.parameters(), 'lr': 1e-5},
{'params': model.layer4.parameters(), 'lr': 1e-4},
{'params': model.fc.parameters(), 'lr': 1e-3},
]
optim = torch.optim.AdamW(params, weight_decay=0.01)
Augmentation strategy:
train_transform = A.Compose([
A.Resize(256, 256),
A.RandomCrop(224, 224),
A.HorizontalFlip(p=0.5), # X-ray দু'দিকে symmetric
A.RandomRotation(10),
A.ColorJitter(0.1, 0.1, 0.1),
A.GaussianBlur(blur_limit=3, p=0.3),
A.GaussNoise(p=0.3),
A.Normalize(),
ToTensorV2(),
])
# Test transform: শুধু resize + center crop + normalize
Class imbalance handling:
- TB rare (~5-10%)।
- Weighted CrossEntropyLoss।
- Focal loss alternative।
- SMOTE for minority class।
Training schedule:
- Stage 1: head only train (5 epoch)।
- Stage 2: top blocks unfreeze (15 epoch)।
- Stage 3: full unfreeze low lr (10 epoch)।
- Cosine annealing lr schedule।
Regularization:
- Dropout 0.5 in head।
- Weight decay 0.01।
- Mixup augmentation।
- Label smoothing 0.1।
Validation strategy:
- 5-fold cross-validation।
- Stratified by disease label।
- Hold-out test from different hospital।
- Bangladesh-specific test set।
Bangladesh-specific challenges:
- X-ray equipment variation (Siemens, GE, locally manufactured)।
- Image quality difference।
- Patient population — different baseline।
- Radiologist labeling consistency।
Domain adaptation:
- Synthetic data — local hospital simulation।
- Test-time augmentation।
- Adversarial training।
- Self-supervised pretrain on unlabeled BD X-rays।
Ensemble strategy:
- 5 models from k-fold।
- Test-time augmentation।
- Model averaging।
- ~2-3% accuracy gain।
Interpretability:
- Grad-CAM visualization।
- Attention map।
- Doctor verification।
- Explainable predictions।
Calibration:
- Probability calibrate (temperature scaling)।
- Uncertainty quantification (MC Dropout)।
- Confidence threshold।
- Doctor-in-loop borderline।
Deployment:
- ONNX export।
- Quantization (INT8)।
- Mobile deployment (rural clinic)।
- Bangla report generation।
Active learning:
- Uncertain samples — expert label।
- Iterate dataset growth।
- Cost-effective improvement।
Validation metrics:
- AUC-ROC per class।
- F1 score।
- Sensitivity-specificity।
- NPV/PPV (clinical context)।
Realistic expectations:
- 5000 sample — 80-85% accuracy।
- Comparable to junior radiologist।
- Senior radiologist ground truth।
- Continuous improvement।
Regulatory:
- BMRC (Bangladesh Medical Research Council) approval।
- Doctor-in-loop mandatory।
- Liability framework।
- Patient consent।
মূল উপলব্ধি: Medical imaging fine-tune — careful strategy। Pretrained foundation + augmentation + class balance + validation। ResNet-50 strong baseline। Discriminative lr + staged unfreezing optimal। Bangladesh — domain adaptation + Bangla integration + regulatory compliance। 5000 sample challenging but achievable। Iterative improvement via active learning। Doctor-AI partnership clinical safety।
অনুশীলন
-
Math: একটি BasicBlock-এ input ($x$) shape $(8, 64, 56, 56)$, stride=1, in=out=64। Output shape কত?
$F(x)$: padding 1, stride 1 → $(8, 64, 56, 56)$. Skip identity → same। Sum → $(8, 64, 56, 56)$।
-
Bottleneck params: Bottleneck block, in=256, mid=64, out=256, kernel-গুলো 1-3-1, stride=1। Conv params (bias=False) কত?
Conv1 ($1 \times 1$): $256 \times 64 = 16{,}384$।
Conv2 ($3 \times 3$): $64 \times 64 \times 9 = 36{,}864$।
Conv3 ($1 \times 1$): $64 \times 256 = 16{,}384$।
Total: $69{,}632$ + BN params.
-
Pre-trained ResNet-18: CIFAR-10-এর জন্য torchvision ResNet-18 fine-tune।
import torchvision.models as m model = m.resnet18(weights='IMAGENET1K_V1') # CIFAR-10: 10 classes, replace fc model.fc = nn.Linear(512, 10) # Note: CIFAR 32x32 — stem 7x7/2 + maxpool aggressive # Better: replace stem with 3x3 stride 1, no maxpool model.conv1 = nn.Conv2d(3, 64, 3, 1, 1, bias=False) model.maxpool = nn.Identity()
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ২৩ · Transfer learning পরবর্তী পাঠ ResNet pretrained — কীভাবে নতুন task-এ ব্যবহার।
- পাঠ ২১ · VGG ও Inception আগের পাঠ ResNet-এর ancestor।
- পাঠ ৩৩ · Transformer M5 Skip connection-এর পরবর্তী evolution।
- সব AI Courses দেখুন ABCL TECH Python, ML, DL, NLP, CV, GenAI, RL, MLOps — সব AI কোর্স একসাথে।