Skip to content

22. ResNet, Residual Connections and TorchVision

Understand why very deep neural networks become difficult to optimize, how Residual Networks (ResNet) solve this problem using skip connections, how residual blocks work, and how pretrained ResNet models are implemented and adapted using TorchVision and PyTorch.


๐ŸŽฏ Learning Objectives

After completing this chapter, you will be able to:

  • Understand the limitations of simply making CNNs deeper
  • Explain the degradation problem in very deep networks
  • Understand residual learning
  • Explain skip connections
  • Understand the mathematical formulation of a residual block
  • Understand identity mappings
  • Understand projection shortcuts
  • Explain BasicBlock and Bottleneck architectures
  • Understand ResNet-18, ResNet-34, ResNet-50, ResNet-101, and ResNet-152
  • Compare ResNet architectures
  • Understand why ResNet enabled much deeper CNNs
  • Use pretrained ResNet models with TorchVision
  • Replace the ResNet classification head
  • Freeze and unfreeze ResNet layers
  • Fine-tune ResNet for custom Computer Vision tasks
  • Understand ResNet tensor shapes
  • Understand Batch Normalization inside ResNet
  • Understand the role of ReLU in residual blocks
  • Build a custom residual block using PyTorch
  • Use ResNet for feature extraction
  • Use ResNet as a Transfer Learning backbone
  • Understand ResNet training and optimization
  • Analyze common ResNet mistakes
  • Evaluate ResNet models for production workloads
  • Understand the relationship between ResNet and modern CNN architectures

๐Ÿ“– Overview

As CNNs became deeper, researchers expected additional layers to provide greater representational power.

However, simply adding more layers did not always improve performance.

A deeper network could suffer from:

Optimization Difficulty
+
Vanishing / Exploding Gradients
+
Degradation
+
Training Instability

Residual Networks introduced a simple but powerful idea:

Instead of forcing a layer stack to learn an entire transformation, allow it to learn a residual function relative to its input.

This is implemented using:

Skip Connection

The resulting architecture is known as:

Residual Network โ€” ResNet


๐Ÿง  Why Do We Need ResNet?

Consider:

Shallow CNN
    โ†“
Good Performance

Adding layers should theoretically allow:

Deeper CNN
    โ†“
Equal or Better Performance

But in practice, very deep plain networks can become harder to optimize.

This creates the:

Degradation Problem

โš  The Degradation Problem

The degradation problem does not simply mean overfitting.

A deeper plain network may have:

Higher Training Error

even though it has:

More Parameters

This indicates an optimization problem.


๐Ÿง  Plain CNN vs ResNet

Plain CNN

Input
  โ†“
Conv
  โ†“
ReLU
  โ†“
Conv
  โ†“
ReLU
  โ†“
Conv
  โ†“
ReLU
  โ†“
Output

Every layer must learn the transformation required to produce the next representation.


ResNet

             โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
             โ”‚                     โ”‚
Input โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ–บ Addition โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ–บ Output
 โ”‚            โ”‚          โ–ฒ         โ”‚
 โ”‚            โ”‚          โ”‚         โ”‚
 โ””โ”€โ”€โ–บ Conv โ”€โ–บ ReLU โ”€โ–บ Conv โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The original input can bypass the convolutional layers.


๐Ÿง  Residual Learning

Instead of directly learning:

[ H(x) ]

the residual block learns:

[ F(x)=H(x)-x ]

Therefore:

[ H(x)=F(x)+x ]

where:

H(x) = Desired Mapping
F(x) = Residual Mapping
x    = Input

The network learns:

Residual Transformation
+
Original Input

๐Ÿง  Core ResNet Idea

Input x
   โ”‚
   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚                        โ”‚
   โ–ผ                        โ”‚
Residual Function F(x)      โ”‚
   โ”‚                        โ”‚
   โ–ผ                        โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Add โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ–ผ
               H(x)

This simple addition is the foundation of ResNet.


๐Ÿง  Residual Block

A basic residual block can be represented as:

Input
  โ”‚
  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚                     โ”‚
  โ–ผ                     โ”‚
Conv                    โ”‚
  โ†“                     โ”‚
BatchNorm               โ”‚
  โ†“                     โ”‚
ReLU                    โ”‚
  โ†“                     โ”‚
Conv                    โ”‚
  โ†“                     โ”‚
BatchNorm               โ”‚
  โ”‚                     โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Add โ—„โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
                 โ–ผ
                ReLU
                 โ”‚
                 โ–ผ
              Output

๐Ÿง  Residual Block Architecture

flowchart TD

    INPUT["Input x"]

    CONV1["3ร—3 Convolution"]

    BN1["Batch Normalization"]

    RELU1["ReLU"]

    CONV2["3ร—3 Convolution"]

    BN2["Batch Normalization"]

    ADD["Addition"]

    RELU2["ReLU"]

    OUTPUT["Output"]

    INPUT --> CONV1
    CONV1 --> BN1
    BN1 --> RELU1
    RELU1 --> CONV2
    CONV2 --> BN2
    BN2 --> ADD
    INPUT --> ADD
    ADD --> RELU2
    RELU2 --> OUTPUT

๐Ÿง  Skip Connection

The skip connection is the path that bypasses the residual transformation.

Input
  โ”‚
  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚                       โ”‚
  โ–ผ                       โ”‚
Conv โ†’ BN โ†’ ReLU โ†’ Conv โ†’ BN
                          โ”‚
                          โ–ผ
                       Addition
                          โ–ฒ
                          โ”‚
                    Skip Connection

The skip connection is sometimes called:

Shortcut Connection
Identity Connection
Residual Connection

๐Ÿง  Why Does the Skip Connection Help?

The shortcut provides a direct path for information and gradients.

Without a shortcut:

Input
 โ†“
Layer
 โ†“
Layer
 โ†“
Layer
 โ†“
Layer
 โ†“
Output

With a shortcut:

Input
 โ”‚
 โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ”‚                          โ”‚
 โ–ผ                          โ–ผ
Layers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Add
                              โ”‚
                              โ–ผ
                           Output

This makes optimization easier for very deep networks.


๐Ÿง  Identity Mapping

When input and output dimensions are identical, the shortcut can simply be:

[ S(x)=x ]

Then:

[ y=F(x)+x ]

This is called an:

Identity Shortcut

๐Ÿง  Projection Shortcut

Sometimes the residual branch changes:

Height
Width
Channels

The original input can no longer be added directly.

For example:

Input:
56 ร— 56 ร— 64

Residual Output:
28 ร— 28 ร— 128

The dimensions do not match.

A projection shortcut can solve this.

Input
  โ”‚
  โ–ผ
1 ร— 1 Convolution
  โ”‚
  โ–ผ
28 ร— 28 ร— 128

๐Ÿง  Projection Shortcut

The shortcut can be represented as:

[ S(x)=W_s*x ]

Then:

[ y=F(x)+S(x) ]


๐Ÿง  Identity vs Projection Shortcut

Identity Shortcut Projection Shortcut
S(x) = x S(x) = W_s * x
No learned parameters Learnable parameters
Dimensions already match Dimensions need transformation
Very efficient Adds computation
Common inside same-resolution blocks Used when changing dimensions

๐Ÿง  Residual Block with Projection

flowchart TD

    INPUT["Input"]

    MAIN["Residual Transform"]

    PROJ["1ร—1 Projection"]

    ADD["Addition"]

    OUTPUT["Output"]

    INPUT --> MAIN
    INPUT --> PROJ

    MAIN --> ADD
    PROJ --> ADD

    ADD --> OUTPUT

๐Ÿง  Downsampling in ResNet

ResNet stages commonly reduce spatial resolution.

For example:

56 ร— 56
    โ†“
28 ร— 28
    โ†“
14 ร— 14
    โ†“
7 ร— 7

At the same time, channels usually increase:

64
 โ†“
128
 โ†“
256
 โ†“
512

When this happens, the shortcut path must also transform the dimensions.


๐Ÿง  ResNet Stage Pattern

flowchart LR

    INPUT["Input"]

    STEM["Initial Conv"]

    S1["Stage 1<br>64 Channels"]

    S2["Stage 2<br>128 Channels"]

    S3["Stage 3<br>256 Channels"]

    S4["Stage 4<br>512 Channels"]

    GAP["Global Average Pooling"]

    FC["Classifier"]

    INPUT --> STEM
    STEM --> S1
    S1 --> S2
    S2 --> S3
    S3 --> S4
    S4 --> GAP
    GAP --> FC

๐Ÿง  ResNet Architecture

A simplified ResNet architecture:

Input
  โ†“
7 ร— 7 Conv
  โ†“
BatchNorm
  โ†“
ReLU
  โ†“
MaxPool
  โ†“
Residual Stage 1
  โ†“
Residual Stage 2
  โ†“
Residual Stage 3
  โ†“
Residual Stage 4
  โ†“
Global Average Pooling
  โ†“
Fully Connected Layer
  โ†“
Output

๐Ÿง  ResNet Architecture Landscape

flowchart TD

    RESNET["ResNet"]

    BASIC["BasicBlock"]

    BOTTLENECK["Bottleneck"]

    R18["ResNet-18"]

    R34["ResNet-34"]

    R50["ResNet-50"]

    R101["ResNet-101"]

    R152["ResNet-152"]

    RESNET --> BASIC
    RESNET --> BOTTLENECK

    BASIC --> R18
    BASIC --> R34

    BOTTLENECK --> R50
    BOTTLENECK --> R101
    BOTTLENECK --> R152

๐Ÿง  ResNet-18

ResNet-18 uses the simpler:

BasicBlock

architecture.

A simplified stage configuration is:

64
 โ†“
128
 โ†“
256
 โ†“
512

with residual blocks distributed across these stages.

ResNet-18 is relatively lightweight and is often useful when inference cost matters.


๐Ÿง  ResNet-34

ResNet-34 also uses:

BasicBlock

but contains more residual blocks than ResNet-18.

Conceptually:

ResNet-18
 โ†“
More Residual Blocks
 โ†“
ResNet-34

This increases representational capacity and computation.


๐Ÿง  BasicBlock

A simplified BasicBlock contains:

3 ร— 3 Conv
 โ†“
BatchNorm
 โ†“
ReLU
 โ†“
3 ร— 3 Conv
 โ†“
BatchNorm
 โ†“
Addition
 โ†“
ReLU

๐Ÿง  BasicBlock Diagram

flowchart LR

    INPUT["Input"]

    C1["3ร—3 Conv"]

    BN1["BN"]

    R1["ReLU"]

    C2["3ร—3 Conv"]

    BN2["BN"]

    ADD["Add"]

    R2["ReLU"]

    OUTPUT["Output"]

    INPUT --> C1
    C1 --> BN1
    BN1 --> R1
    R1 --> C2
    C2 --> BN2
    BN2 --> ADD
    INPUT --> ADD
    ADD --> R2
    R2 --> OUTPUT

๐Ÿง  Bottleneck Block

Deeper ResNet variants such as:

ResNet-50
ResNet-101
ResNet-152

use:

Bottleneck Blocks

A bottleneck block uses:

1 ร— 1 Conv
 โ†“
3 ร— 3 Conv
 โ†“
1 ร— 1 Conv

๐Ÿง  Bottleneck Architecture

Input
  โ”‚
  โ–ผ
1 ร— 1 Conv
  โ”‚
  โ–ผ
3 ร— 3 Conv
  โ”‚
  โ–ผ
1 ร— 1 Conv
  โ”‚
  โ–ผ
Addition
  โ–ฒ
  โ”‚
Shortcut

The first 1 ร— 1 convolution reduces or transforms channel dimensions, while the final 1 ร— 1 convolution expands them.


๐Ÿง  Bottleneck Block Diagram

flowchart TD

    INPUT["Input"]

    C1["1ร—1 Conv"]

    C2["3ร—3 Conv"]

    C3["1ร—1 Conv"]

    SHORT["Shortcut"]

    ADD["Addition"]

    OUTPUT["Output"]

    INPUT --> C1
    C1 --> C2
    C2 --> C3
    C3 --> ADD

    INPUT --> SHORT
    SHORT --> ADD

    ADD --> OUTPUT

๐Ÿง  Why Use Bottleneck Blocks?

A bottleneck block allows deeper networks to increase depth without making every convolution operate at the highest channel dimension.

Conceptually:

High-Dimensional Input
        โ†“
1ร—1 Reduction
        โ†“
3ร—3 Processing
        โ†“
1ร—1 Expansion
        โ†“
Output

This improves computational efficiency compared with simply stacking wide 3 ร— 3 convolutions.


๐Ÿง  BasicBlock vs Bottleneck

BasicBlock Bottleneck
3ร—3 โ†’ 3ร—3 1ร—1 โ†’ 3ร—3 โ†’ 1ร—1
Used by ResNet-18 Used by ResNet-50
Used by ResNet-34 Used by ResNet-101
Simpler More compute-efficient for deeper models
Suitable for shallower ResNets Suitable for deeper ResNets

๐Ÿง  ResNet Model Comparison

Model Main Block Relative Depth Relative Compute
ResNet-18 BasicBlock Low Low
ResNet-34 BasicBlock Medium Medium
ResNet-50 Bottleneck Higher Higher
ResNet-101 Bottleneck Very High High
ResNet-152 Bottleneck Extremely High Very High

The exact parameter counts and FLOPs depend on implementation and input resolution.


๐Ÿง  ResNet-18 vs ResNet-50

ResNet-18

BasicBlock
3ร—3
 โ†“
3ร—3

versus:

ResNet-50

Bottleneck
1ร—1
 โ†“
3ร—3
 โ†“
1ร—1

The architecture is not simply:

"ResNet-50 = ResNet-18 but deeper"

The block design also changes.


๐Ÿง  Why Residual Learning Helps

Suppose the desired mapping is:

[ H(x) ]

A plain network must directly approximate:

[ H(x) ]

A residual network learns:

[ F(x)=H(x)-x ]

and then:

[ H(x)=F(x)+x ]

If the desired transformation is close to identity:

H(x) โ‰ˆ x

then:

F(x) โ‰ˆ 0

The residual branch only needs to learn a small modification.

This can make optimization easier.


๐Ÿง  Gradient Flow

During backpropagation, the shortcut provides a direct computational path.

Conceptually:

Forward:

Input
 โ”‚
 โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Shortcut โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ”‚                                 โ”‚
 โ–ผ                                 โ–ผ
Residual Layers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Add
                                   โ”‚
                                   โ–ผ
                                 Output

and:

Backward:

Gradient
   โ”‚
   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Shortcut
   โ”‚
   โ–ผ
Residual Layers

This helps gradients propagate through deep networks.


๐Ÿง  ResNet and Vanishing Gradients

Very deep plain networks can suffer from gradient degradation.

Residual connections provide a direct path:

Deep Network

Layer
 โ†“
Layer
 โ†“
Layer
 โ†“
Layer
 โ†“
Layer

versus:

Residual Network

Layer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ†“                  โ”‚
Layer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
 โ†“                  โ”‚
Layer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
 โ†“                  โ”‚
Add โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

This architectural shortcut contributes to more stable optimization of deep networks.


๐Ÿง  ResNet as a Feature Extractor

A pretrained ResNet can be used without its classifier:

Image
 โ†“
ResNet Backbone
 โ†“
Feature Representation

This representation can then feed:

Classification
Detection
Segmentation
Similarity Search
Retrieval
Clustering

๐Ÿง  ResNet Transfer Learning

flowchart LR

    IMAGE["Input Image"]

    RESNET["Pretrained ResNet Backbone"]

    FEATURES["Feature Vector"]

    HEAD["Custom Task Head"]

    OUTPUT["Target Prediction"]

    IMAGE --> RESNET
    RESNET --> FEATURES
    FEATURES --> HEAD
    HEAD --> OUTPUT

๐Ÿ Part I โ€” TorchVision

TorchVision provides pretrained Computer Vision models and utilities for PyTorch.

A typical workflow is:

TorchVision
   โ†“
Pretrained Model
   โ†“
Modify Classifier
   โ†“
Freeze / Fine-Tune
   โ†“
Train

๐Ÿงช Load Pretrained ResNet-18

import torch
import torch.nn as nn

from torchvision import models


model = models.resnet18(
    weights=models.ResNet18_Weights.DEFAULT
)

The exact weight enum depends on the TorchVision version.


๐Ÿง  Inspect the Model

print(model)

You will see major components such as:

conv1
bn1
relu
maxpool
layer1
layer2
layer3
layer4
avgpool
fc

These correspond to the major stages of ResNet.


๐Ÿง  ResNet Components

flowchart LR

    INPUT["Input"]

    STEM["conv1 + bn1 + relu + maxpool"]

    L1["layer1"]

    L2["layer2"]

    L3["layer3"]

    L4["layer4"]

    AVG["avgpool"]

    FC["fc"]

    INPUT --> STEM
    STEM --> L1
    L1 --> L2
    L2 --> L3
    L3 --> L4
    L4 --> AVG
    AVG --> FC

๐Ÿงช Replace the Classification Head

Suppose the target task contains:

5 Classes

Replace:

num_features = model.fc.in_features


model.fc = nn.Linear(
    num_features,
    5
)

๐Ÿง  Freeze the Backbone

for param in model.parameters():

    param.requires_grad = False

Then:

for param in model.fc.parameters():

    param.requires_grad = True

Now:

ResNet Backbone
      โ†“
Frozen

Classification Head
      โ†“
Trainable

๐Ÿงช Optimizer

optimizer = torch.optim.AdamW(

    model.fc.parameters(),

    lr=1e-3,

    weight_decay=1e-4
)

This ensures the optimizer updates the classification head only.


๐Ÿง  Fine-Tune ResNet Layer 4

After the classification head converges:

for param in model.layer4.parameters():

    param.requires_grad = True

Now the optimizer can include the trainable parameters:

optimizer = torch.optim.AdamW(

    filter(
        lambda p: p.requires_grad,
        model.parameters()
    ),

    lr=1e-5,

    weight_decay=1e-4
)

๐Ÿง  ResNet Fine-Tuning Strategy

Stage 1

Backbone
 โ†“
Frozen

Head
 โ†“
Trainable


Stage 2

layer4
 โ†“
Trainable

Head
 โ†“
Trainable


Stage 3

More Backbone Layers
 โ†“
Optional Fine-Tuning

๐Ÿง  Why Fine-Tune Layer 4 First?

Later ResNet layers generally contain more task-specific representations than early layers.

Therefore:

Early Layers
 โ†“
General Features

while:

Later Layers
 โ†“
More Task-Specific Features

Unfreezing later layers first provides a controlled way to adapt the model.


๐Ÿงช TorchVision Preprocessing

TorchVision pretrained weights often provide an associated preprocessing configuration.

For example:

weights = models.ResNet50_Weights.DEFAULT

preprocess = weights.transforms()

Then:

image_tensor = preprocess(
    image
)

This helps ensure that the input follows the preprocessing expectations of the pretrained weights.


๐Ÿง  Why Preprocessing Matters

A pretrained model expects a particular input distribution.

Incorrect:

Resize
Normalization
Channel Order
Scaling

can reduce model performance.

Therefore:

The preprocessing pipeline is part of the model contract.


๐Ÿง  ResNet Input Pipeline

flowchart LR

    IMAGE["Raw Image"]

    TRANSFORM["TorchVision Transform"]

    TENSOR["Tensor"]

    RESNET["ResNet"]

    PRED["Prediction"]

    IMAGE --> TRANSFORM
    TRANSFORM --> TENSOR
    TENSOR --> RESNET
    RESNET --> PRED

๐Ÿง  ResNet Tensor Shapes

For a typical ResNet with input:

224 ร— 224 ร— 3

the internal representation approximately follows:

224 ร— 224
     โ†“
112 ร— 112
     โ†“
56 ร— 56
     โ†“
28 ร— 28
     โ†“
14 ร— 14
     โ†“
7 ร— 7
     โ†“
1 ร— 1

while channels generally increase:

64
 โ†“
64
 โ†“
128
 โ†“
256
 โ†“
512

๐Ÿง  ResNet Shape Flow

flowchart TD

    A["224 ร— 224 ร— 3"]

    B["112 ร— 112 ร— 64"]

    C["56 ร— 56 ร— 64"]

    D["28 ร— 28 ร— 128"]

    E["14 ร— 14 ร— 256"]

    F["7 ร— 7 ร— 512"]

    G["Global Average Pooling"]

    H["512 Features"]

    A --> B
    B --> C
    C --> D
    D --> E
    E --> F
    F --> G
    G --> H

The exact tensor shapes can vary with architecture and implementation details.


๐Ÿง  Global Average Pooling in ResNet

Before the final classifier, ResNet uses global average pooling.

Conceptually:

7 ร— 7 ร— 512
      โ†“
1 ร— 1 ร— 512
      โ†“
512

This creates one feature value per channel.


๐Ÿง  ResNet Classification

Feature Maps
      โ†“
Global Average Pooling
      โ†“
Feature Vector
      โ†“
Fully Connected Layer
      โ†“
Class Logits

๐Ÿง  Logits vs Probabilities

ResNet's final fc layer produces logits.

For example:

[-1.2, 2.7, 0.4, ...]

During training with:

nn.CrossEntropyLoss()

you normally provide the raw logits.

Do not apply softmax before CrossEntropyLoss.


๐Ÿงช PyTorch Classification Setup

criterion = nn.CrossEntropyLoss()


optimizer = torch.optim.AdamW(

    filter(
        lambda p: p.requires_grad,
        model.parameters()
    ),

    lr=1e-3,

    weight_decay=1e-4
)

๐Ÿง  ResNet Training Loop

for epoch in range(
    epochs
):

    model.train()

    for images, labels in train_loader:

        images = images.to(
            device
        )

        labels = labels.to(
            device
        )

        optimizer.zero_grad()

        logits = model(
            images
        )

        loss = criterion(
            logits,
            labels
        )

        loss.backward()

        optimizer.step()

๐Ÿง  ResNet Validation

model.eval()

correct = 0
total = 0

with torch.no_grad():

    for images, labels in val_loader:

        images = images.to(
            device
        )

        labels = labels.to(
            device
        )

        logits = model(
            images
        )

        predictions = logits.argmax(
            dim=1
        )

        correct += (
            predictions == labels
        ).sum().item()

        total += labels.size(0)

accuracy = correct / total

๐Ÿง  Custom Residual Block

Understanding the residual block is more important than simply knowing how to call resnet50().

class ResidualBlock(
    nn.Module
):

    def __init__(
        self,
        in_channels,
        out_channels,
        stride=1
    ):

        super().__init__()

        self.conv1 = nn.Conv2d(
            in_channels,
            out_channels,
            kernel_size=3,
            stride=stride,
            padding=1,
            bias=False
        )

        self.bn1 = nn.BatchNorm2d(
            out_channels
        )

        self.relu = nn.ReLU(
            inplace=True
        )

        self.conv2 = nn.Conv2d(
            out_channels,
            out_channels,
            kernel_size=3,
            padding=1,
            bias=False
        )

        self.bn2 = nn.BatchNorm2d(
            out_channels
        )

        if (
            stride != 1
            or in_channels != out_channels
        ):

            self.shortcut = nn.Sequential(

                nn.Conv2d(
                    in_channels,
                    out_channels,
                    kernel_size=1,
                    stride=stride,
                    bias=False
                ),

                nn.BatchNorm2d(
                    out_channels
                )
            )

        else:

            self.shortcut = nn.Identity()

    def forward(
        self,
        x
    ):

        identity = self.shortcut(
            x
        )

        out = self.conv1(
            x
        )

        out = self.bn1(
            out
        )

        out = self.relu(
            out
        )

        out = self.conv2(
            out
        )

        out = self.bn2(
            out
        )

        out += identity

        out = self.relu(
            out
        )

        return out

๐Ÿง  Custom Residual Block Flow

flowchart TD

    INPUT["Input"]

    MAIN1["Conv 3ร—3"]

    BN1["BatchNorm"]

    RELU["ReLU"]

    MAIN2["Conv 3ร—3"]

    BN2["BatchNorm"]

    SHORT["Identity / Projection"]

    ADD["Add"]

    OUTPUT["ReLU Output"]

    INPUT --> MAIN1
    MAIN1 --> BN1
    BN1 --> RELU
    RELU --> MAIN2
    MAIN2 --> BN2
    BN2 --> ADD

    INPUT --> SHORT
    SHORT --> ADD

    ADD --> OUTPUT

๐Ÿง  Identity Shortcut in Code

When dimensions match:

self.shortcut = nn.Identity()

This means:

Shortcut(x) = x

No parameters are introduced.


๐Ÿง  Projection Shortcut in Code

When dimensions differ:

nn.Conv2d(
    in_channels,
    out_channels,
    kernel_size=1,
    stride=stride
)

This performs:

Channel Transformation
+
Optional Downsampling

๐Ÿง  ResNet Block Invariants

For residual addition:

Main Branch Shape
=
Shortcut Shape

must hold.

For example:

Main:
28 ร— 28 ร— 128

Shortcut:
28 ร— 28 ร— 128

Addition:
Valid

But:

Main:
28 ร— 28 ร— 128

Shortcut:
56 ร— 56 ร— 64

Addition:
Invalid

This is one of the most important implementation rules for residual networks.


๐Ÿง  Residual Addition

The addition operation is element-wise:

[ y_i=F(x)_i+x_i ]

Both tensors must have compatible shapes.


๐Ÿง  ResNet and Transfer Learning

ResNet became one of the most widely used pretrained CNN backbones.

Typical workflow:

Pretrained ResNet
       โ†“
Remove Original Classifier
       โ†“
Add Custom Head
       โ†“
Freeze Backbone
       โ†“
Train Head
       โ†“
Unfreeze Selected Layers
       โ†“
Fine-Tune

๐Ÿง  ResNet Transfer Learning Lifecycle

flowchart LR

    BASE["Pretrained ResNet"]

    HEAD["Replace FC"]

    FREEZE["Freeze Backbone"]

    TRAIN["Train Head"]

    UNFREEZE["Unfreeze Layer 4"]

    FINETUNE["Fine-Tune"]

    EVAL["Evaluate"]

    DEPLOY["Deploy"]

    BASE --> HEAD
    HEAD --> FREEZE
    FREEZE --> TRAIN
    TRAIN --> UNFREEZE
    UNFREEZE --> FINETUNE
    FINETUNE --> EVAL
    EVAL --> DEPLOY

๐Ÿง  ResNet vs Plain CNN

Plain CNN ResNet
Sequential transformations Residual transformations
No shortcut by default Skip connections
Deep networks harder to optimize Deep networks easier to optimize
More susceptible to degradation Residual learning addresses degradation
Simple architecture More sophisticated architecture

๐Ÿง  ResNet Advantages

ResNet provides:

  • Better optimization of deep networks
  • Effective gradient flow
  • Strong feature representations
  • Reusable pretrained models
  • Strong Computer Vision baseline
  • Good Transfer Learning performance
  • Multiple depth variants
  • Mature ecosystem support

โš  ResNet Limitations

ResNet is not always the optimal architecture.

Potential limitations include:

  • Higher computation than lightweight CNNs
  • Larger memory requirements
  • Higher inference latency
  • More expensive deployment
  • Possible overkill for simple tasks
  • Newer architectures may provide better efficiency
  • CNN inductive biases may be limiting for some global-context tasks

For constrained environments, architectures such as:

MobileNet
EfficientNet
EfficientNet-Lite

may be more appropriate.


๐Ÿง  ResNet vs Lightweight Models

ResNet Lightweight CNN
Strong general-purpose backbone Optimized for constrained environments
More computation Lower computation
Larger model Smaller model
Strong accuracy baseline Strong efficiency
Useful for server-side inference Useful for edge/mobile

The correct choice depends on:

Accuracy
Latency
Memory
Hardware
Cost

๐Ÿง  ResNet and Vision Transformers

ResNet represents a major CNN milestone.

The Computer Vision architecture landscape later expanded toward:

CNN
 โ†“
ResNet
 โ†“
Efficient CNNs
 โ†“
Vision Transformers
 โ†“
Hybrid CNN + ViT

Vision Transformers are covered in:

23. Vision Transformers and CNN-ViT Hybrids.


๐Ÿง  ResNet Architecture Landscape

flowchart LR

    CNN["Classic CNN"]

    RESNET["ResNet"]

    EFFICIENT["Efficient CNNs"]

    VIT["Vision Transformers"]

    HYBRID["CNN + ViT"]

    CNN --> RESNET
    RESNET --> EFFICIENT
    RESNET --> VIT
    VIT --> HYBRID
    EFFICIENT --> HYBRID

๐Ÿง  ResNet as a General Vision Backbone

ResNet can provide feature representations for:

Image Classification
Object Detection
Semantic Segmentation
Instance Segmentation
Image Retrieval
Visual Similarity
Metric Learning
Anomaly Detection

๐Ÿง  ResNet in Object Detection

A simplified detection architecture may use:

Image
 โ†“
ResNet Backbone
 โ†“
Feature Maps
 โ†“
Detection Head
 โ†“
Bounding Boxes
+
Class Predictions

The backbone learns reusable visual representations.


๐Ÿง  ResNet in Segmentation

For segmentation:

Image
 โ†“
CNN Backbone
 โ†“
Feature Maps
 โ†“
Decoder
 โ†“
Pixel-Level Predictions

ResNet can act as the encoder/backbone.


๐Ÿง  ResNet Feature Extraction

A pretrained ResNet can generate embeddings:

Image
 โ†“
ResNet
 โ†“
Global Average Pooling
 โ†“
Feature Vector

These vectors can be used for:

Similarity Search
Image Retrieval
Clustering
Duplicate Detection
Recommendation

๐Ÿงช Extract Features with PyTorch

import torch


model.eval()


with torch.no_grad():

    features = model.avgpool(
        model.layer4(
            model.layer3(
                model.layer2(
                    model.layer1(
                        model.maxpool(
                            model.relu(
                                model.bn1(
                                    model.conv1(
                                        images
                                    )
                                )
                            )
                        )
                    )
                )
            )
        )
    )


features = torch.flatten(
    features,
    1
)

In production, prefer a dedicated feature-extractor interface rather than depending on deeply nested internal calls that may vary between architectures.


๐Ÿง  Better Feature Extractor Design

A cleaner approach is to define a model that explicitly exposes the representation:

class ResNetFeatureExtractor(
    nn.Module
):

    def __init__(
        self,
        backbone
    ):

        super().__init__()

        self.backbone = backbone

        self.backbone.fc = nn.Identity()

    def forward(
        self,
        x
    ):

        return self.backbone(
            x
        )

Now:

features = extractor(
    images
)

This is easier to integrate into production systems.


๐Ÿง  Production ResNet Architecture

A production system might look like:

Client
  โ†“
API Gateway
  โ†“
Inference Service
  โ†“
Image Preprocessing
  โ†“
ResNet Model
  โ†“
Prediction
  โ†“
Business Service

๐Ÿข Enterprise ResNet Architecture

flowchart TD

    CLIENT["Client / Application"]

    API["API Gateway"]

    SERVICE["Vision Inference Service"]

    PREP["Image Preprocessing"]

    MODEL["ResNet Model"]

    PRED["Prediction"]

    BUSINESS["Business Decision"]

    MONITOR["Observability"]

    CLIENT --> API
    API --> SERVICE
    SERVICE --> PREP
    PREP --> MODEL
    MODEL --> PRED
    PRED --> BUSINESS

    SERVICE --> MONITOR
    MODEL --> MONITOR

๐Ÿข Production ResNet Considerations

Important concerns include:

Model

Accuracy
Model Size
Architecture
Input Resolution

Inference

Latency
Throughput
Batch Size
Concurrency

Infrastructure

CPU
GPU
Memory
Autoscaling

Operations

Logging
Metrics
Tracing
Model Versioning
Monitoring
Rollback

๐Ÿง  ResNet Model Optimization

Possible techniques include:

Input Resolution Reduction
Quantization
Pruning
Knowledge Distillation
Batching
GPU Acceleration
Mixed Precision
Model Compilation

These techniques should be evaluated against the required accuracy.


๐Ÿง  Accuracy vs Latency Trade-Off

Higher Accuracy
      โ†‘
      โ”‚
      โ”‚       Large ResNet
      โ”‚
      โ”‚
      โ”‚   Medium ResNet
      โ”‚
      โ”‚ Lightweight Model
      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ†’
              Latency

The optimal model depends on the application.

For:

Offline Batch Processing

higher compute may be acceptable.

For:

Real-Time Edge Inference

latency and memory may dominate.


๐Ÿง  ResNet Monitoring

A production ResNet system should monitor:

Inference Latency
Throughput
Error Rate
Prediction Distribution
Input Quality
Data Drift
Model Accuracy
Class Distribution
GPU Utilization
Memory Usage

๐Ÿง  Model Drift

A deployed ResNet can degrade if production images differ from training data.

Examples:

New Camera
Different Lighting
Different Product
New Background
Seasonal Variation
Resolution Change
New Customer Segment

This creates:

Training Distribution
        โ‰ 
Production Distribution

๐Ÿง  ResNet Retraining

flowchart TD

    PRODUCTION["Production Images"]

    MONITOR["Monitor Predictions"]

    DRIFT["Detect Drift"]

    LABEL["Collect / Label Data"]

    TRAIN["Fine-Tune ResNet"]

    VALIDATE["Validate"]

    DEPLOY["Deploy New Version"]

    PRODUCTION --> MONITOR
    MONITOR --> DRIFT
    DRIFT --> LABEL
    LABEL --> TRAIN
    TRAIN --> VALIDATE
    VALIDATE --> DEPLOY
    DEPLOY --> PRODUCTION

๐Ÿงช Practical Exercise 1 โ€” Load ResNet-18

Use TorchVision:

model = models.resnet18(
    weights=models.ResNet18_Weights.DEFAULT
)

Inspect:

Architecture
Parameter Count
Input Shape
Output Shape

๐Ÿงช Practical Exercise 2 โ€” Replace the Classifier

Adapt ResNet-18 for:

5 Classes

Replace:

model.fc

and verify:

Output Shape = 5

๐Ÿงช Practical Exercise 3 โ€” Feature Extraction

Freeze:

Entire Backbone

Train:

Classification Head

Compare training time and validation performance.


๐Ÿงช Practical Exercise 4 โ€” Fine-Tune Layer 4

Unfreeze:

layer4

Use a lower learning rate.

Compare:

Feature Extraction
vs
Fine-Tuning

๐Ÿงช Practical Exercise 5 โ€” Compare ResNet Variants

Train:

ResNet-18
ResNet-34
ResNet-50

Compare:

Accuracy
Parameters
Training Time
Inference Latency
Memory

๐Ÿงช Practical Exercise 6 โ€” Residual Block

Implement:

Basic Residual Block

from scratch using:

Conv2d
BatchNorm2d
ReLU
Identity

Verify that tensor shapes are compatible before addition.


๐Ÿงช Practical Exercise 7 โ€” Projection Shortcut

Create a residual block where:

Input Channels = 64
Output Channels = 128
Stride = 2

Implement the projection shortcut.

Verify:

Main Branch Shape
=
Shortcut Shape

๐Ÿงช Practical Exercise 8 โ€” Feature Extraction

Use a pretrained ResNet to generate embeddings.

Then perform:

Cosine Similarity

between image embeddings.

Explore:

Similar Images
Different Images

๐Ÿงช Practical Exercise 9 โ€” Transfer Learning

Build:

Pretrained ResNet
      โ†“
Frozen Backbone
      โ†“
Custom Head

Then progressively fine-tune:

layer4

and compare performance.


๐Ÿงช Practical Exercise 10 โ€” Production Benchmark

Benchmark:

ResNet-18
ResNet-50

on your target hardware.

Measure:

Model Size
Inference Latency
Throughput
Memory Usage
Accuracy

Determine which architecture provides the best production trade-off.


๐Ÿง  Interview Questions

Beginner

1. What is ResNet?

ResNet is a family of Deep CNN architectures that use residual connections to make very deep networks easier to optimize.

2. What is a residual connection?

A residual connection provides a shortcut path that adds the input to the output of a learned transformation.

3. What is the basic residual equation?

[ y=F(x)+x ]

4. What problem does ResNet address?

It primarily addresses the optimization degradation encountered when making plain CNNs increasingly deep.

5. What is a BasicBlock?

A residual block typically containing two 3 ร— 3 convolutions with normalization and activation.

6. What is a Bottleneck block?

A residual block typically using:

1ร—1
 โ†“
3ร—3
 โ†“
1ร—1

to make deeper networks more computationally practical.


Intermediate

7. What is a skip connection?

A shortcut that bypasses one or more layers and is combined with the residual branch.

8. When is an identity shortcut possible?

When the residual branch and input have compatible dimensions.

9. When is a projection shortcut required?

When spatial dimensions or channel dimensions need to change.

10. Why is a 1 ร— 1 convolution used in projection shortcuts?

It can transform channel dimensions and, with stride, perform spatial downsampling.

11. Why are ResNet-50 and deeper models based on bottleneck blocks?

Bottleneck blocks provide a computationally efficient way to build much deeper networks.

12. What is the difference between ResNet-18 and ResNet-50?

ResNet-18 uses BasicBlocks, while ResNet-50 uses Bottleneck blocks and is substantially deeper and more computationally expensive.


Advanced

13. Why does residual learning make optimization easier?

It provides shortcut paths for information and gradients and allows the residual branch to learn modifications relative to the input rather than necessarily learning the complete mapping directly.

14. What happens if the residual branch learns zero?

Then:

[ F(x)=0 ]

and:

[ y=x ]

The block can therefore represent an identity mapping.

15. Why must tensor shapes match before residual addition?

Element-wise addition requires compatible tensor dimensions.

16. Why can ResNet still overfit?

Residual connections improve optimization, but they do not eliminate the fundamental risk of excessive model capacity relative to the target dataset.

17. Why is fine-tuning ResNet usually done with a small learning rate?

Because the pretrained backbone already contains useful representations and large updates may destroy them.

18. Why might ResNet-18 be preferable to ResNet-152?

When:

Latency
Memory
Compute Cost

are more important than the additional representational capacity of a much deeper model.

19. What is the purpose of Global Average Pooling?

It converts spatial feature maps into one representative value per channel, reducing the need for a large fully connected classification head.

20. How would you use ResNet for image similarity?

Remove or bypass the classification head, extract feature embeddings, and compare embeddings using an appropriate similarity metric such as cosine similarity.

21. How would you optimize ResNet for production inference?

Consider:

Input Resolution
Batching
Quantization
Mixed Precision
Model Compilation
Hardware
Model Variant
Memory
Latency
Throughput

22. How would you detect whether a deployed ResNet is becoming unreliable?

Monitor:

Input Distribution
Prediction Distribution
Data Drift
Model Performance
Latency
Error Rate
Class Distribution

๐Ÿข Enterprise Perspective

ResNet is important not only because of its architecture but because it became a highly reusable Computer Vision backbone.

A single pretrained ResNet can support many enterprise applications:

Image Classification
        โ†“
Object Detection
        โ†“
Image Retrieval
        โ†“
Similarity Search
        โ†“
Visual Inspection
        โ†“
Anomaly Detection

This makes ResNet a useful bridge between:

Deep Learning Research
        โ†“
Reusable AI Components
        โ†“
Production AI Systems

๐Ÿข Enterprise ResNet Platform

A reusable enterprise vision platform can expose the model behind a capability interface:

VisionProvider
      โ”‚
      โ”œโ”€โ”€ classify()
      โ”œโ”€โ”€ embed()
      โ””โ”€โ”€ predict()

The ResNet implementation becomes one model adapter behind the capability.

Conceptually:

flowchart LR

    APP["Enterprise Application"]

    API["Vision Capability"]

    PROVIDER["Vision Model Provider"]

    RESNET["ResNet Adapter"]

    OTHER["Other Vision Model"]

    APP --> API
    API --> PROVIDER
    PROVIDER --> RESNET
    PROVIDER --> OTHER

This separates:

Business Capability

from:

Specific Model Implementation

and makes model replacement easier.


๐Ÿข Model Versioning

A production ResNet deployment should track:

Model Architecture
Model Version
Pretrained Weight Version
Fine-Tuning Dataset
Dataset Version
Training Configuration
Code Version
Evaluation Metrics
Deployment Version

For example:

Vision Model
    โ†“
resnet50-v3
    โ†“
Dataset v12
    โ†“
Fine-Tuned 2026-08
    โ†“
Production

๐Ÿข Production ResNet Lifecycle

flowchart TD

    REQUIREMENTS["Business Requirements"]

    DATA["Enterprise Image Data"]

    BASE["Select Pretrained ResNet"]

    TRAIN["Feature Extraction / Fine-Tuning"]

    VALIDATE["Offline Validation"]

    BENCHMARK["Latency / Cost Benchmark"]

    REGISTRY["Model Registry"]

    DEPLOY["Production Deployment"]

    MONITOR["Production Monitoring"]

    DRIFT["Drift Detection"]

    RETRAIN["Retraining"]

    REQUIREMENTS --> DATA
    DATA --> BASE
    BASE --> TRAIN
    TRAIN --> VALIDATE
    VALIDATE --> BENCHMARK
    BENCHMARK --> REGISTRY
    REGISTRY --> DEPLOY
    DEPLOY --> MONITOR
    MONITOR --> DRIFT
    DRIFT --> RETRAIN
    RETRAIN --> TRAIN

Production Insight

ResNet is more than a CNN architecture. It is a reusable representation-learning backbone.

In production, the right question is not:

"Which ResNet is most accurate?"

but:

```text Which model provides the required

Accuracy +

Latency + Throughput + Memory Efficiency + Cost + Reliability ```

for the actual production workload?

ResNet-18 may be the right choice for a latency-sensitive application, while ResNet-50 may provide a better accuracy/compute balance for server-side inference.

๐Ÿ“Œ Key Takeaways

  • ResNet introduced residual learning to make very deep CNNs easier to optimize.
  • Residual blocks learn a transformation relative to the input.
  • The core formulation is y = F(x) + x.
  • Skip connections provide direct information and gradient paths.
  • Identity shortcuts are used when tensor dimensions already match.
  • Projection shortcuts use learnable transformations when dimensions need to change.
  • 1 ร— 1 convolutions are commonly used for projection and channel transformation.
  • ResNet commonly reduces spatial resolution while increasing channel depth.
  • ResNet-18 and ResNet-34 use BasicBlocks.
  • ResNet-50, ResNet-101, and ResNet-152 use Bottleneck blocks.
  • Bottleneck blocks use 1 ร— 1 โ†’ 3 ร— 3 โ†’ 1 ร— 1 convolutions.
  • Global Average Pooling reduces feature maps to compact representations.
  • ResNet models are widely useful for Transfer Learning.
  • TorchVision provides pretrained ResNet implementations for PyTorch.
  • The original classification head can be replaced for custom tasks.
  • Feature extraction can freeze the ResNet backbone.
  • Fine-tuning can progressively unfreeze later layers.
  • Fine-tuning generally requires smaller learning rates than head training.
  • Correct preprocessing is part of the pretrained model contract.
  • ResNet can serve as a backbone for classification, detection, segmentation, and embedding applications.
  • Production ResNet systems should be evaluated for accuracy, latency, throughput, memory, and cost.
  • Model monitoring should include input drift and prediction behavior.
  • ResNet provides an important foundation for understanding modern Computer Vision architectures.

๐Ÿ“š Further Reading

Continue with:

The next chapter explores Vision Transformers (ViT) and CNN-ViT hybrid architectures, introducing the transition from convolution-based visual representation learning toward attention-based Computer Vision models.


โžก๏ธ Next Chapter

23. Vision Transformers and CNN-ViT Hybrids


Enterprise AI Engineering Handbook
Building Production-Grade Enterprise AI Systems โ€” One Chapter at a Time.