← Master Index
Vol. 06 Module 6.1 Lecture

Residual Networks

Neural Network Foundations

How This Lesson Fits the Module

Module 6.1 built the training stack: forward pass, backprop, optimizers, batch norm, and dropout. Stacking plain layers still made very deep networks hard to train—gradients vanished and accuracy saturated.

Residual Networks (ResNets) introduced skip connections: learn residual mappings F(x) so the block outputs x + F(x). This capstone shows how architecture and training mechanics combine to enable 50+ layer models that actually work.

Learning Objectives

By the end of this lesson, students should be able to:

  • Explain skip connections and the residual formulation y = x + F(x).
  • Implement a ResidualBlock in PyTorch with dimension matching.
  • Relate ResNets to gradient highways through backpropagation.
  • Place BN and ReLU in a pre-activation residual block.
  • Recognize ResNet as the backbone pattern for vision before transformers dominated some tasks.

The Vanishing Depth Problem

Plain deep CNNs: add layers, validation error gets worse. Gradients shrink through long chains of nonlinearities. ReLU and BatchNorm help but do not fully solve identity learning—a deep layer should be able to pass its input forward unchanged if that is optimal.

Definition — Residual Connection

A residual (skip) connection adds the block input directly to the block output: y = x + F(x). The subnetwork F learns the residual correction rather than the full mapping H(x). If the optimal H is identity, F ≈ 0 is easy to learn.

DesignPlain Deep NetResNet Block
OutputH(x)x + F(x)
Gradient pathThrough all layersDirect + through F
Identity learningHard (weights → identity)Easy (F → 0)
Typical depth< 20 layers (era-dependent)18–152+ layers common

PyTorch Residual Block

import torch import torch.nn as nn class ResidualBlock(nn.Module): def __init__(self, channels): super().__init__() self.conv1 = nn.Conv2d(channels, channels, 3, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(channels) self.conv2 = nn.Conv2d(channels, channels, 3, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(channels) self.relu = nn.ReLU(inplace=True) def forward(self, x): identity = x out = self.relu(self.bn1(self.conv1(x))) out = self.bn2(self.conv2(out)) out = out + identity # skip connection return self.relu(out) class TinyResNet(nn.Module): def __init__(self, n_classes=10): super().__init__() self.stem = nn.Sequential( nn.Conv2d(1, 32, 3, padding=1, bias=False), nn.BatchNorm2d(32), nn.ReLU(inplace=True), ) self.layer1 = nn.Sequential( ResidualBlock(32), ResidualBlock(32), ) self.head = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(32, n_classes), ) def forward(self, x): x = self.stem(x) x = self.layer1(x) return self.head(x)

Projection Shortcuts

When spatial size or channels change, use a 1×1 conv on the skip path so x and F(x) shapes match before addition.

class ResBlockDown(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv1 = nn.Conv2d(in_ch, out_ch, 3, stride=2, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(out_ch) self.conv2 = nn.Conv2d(out_ch, out_ch, 3, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(out_ch) self.relu = nn.ReLU(inplace=True) self.downsample = nn.Sequential( nn.Conv2d(in_ch, out_ch, 1, stride=2, bias=False), nn.BatchNorm2d(out_ch), ) def forward(self, x): identity = self.downsample(x) out = self.relu(self.bn1(self.conv1(x))) out = self.bn2(self.conv2(out)) return self.relu(out + identity)
Module 6.1 Synthesis Train TinyResNet with SGD+momentum or AdamW, tune learning rate, track epochs, and watch for overfitting on small data.
Common Misconception: “Skip connections skip gradients entirely.”

Reality: Gradients flow through both the addition (identity path) and F(x). The identity path preserves signal magnitude—it does not bypass learning in F when F is needed.

Common Misconception: “ResNets are only for ImageNet-scale data.”

Reality: Residual ideas appear in audio, video, and transformer blocks (residual around attention/FFN). The pattern is universal: stable depth.

Critical Mistake — Shape Mismatch on Add

out + identity requires identical shapes. Forgetting downsample on the skip when stride=2 causes runtime errors—or silent bugs if shapes accidentally broadcast wrong in sloppy code.

Knowledge Check

  1. Short Answer: ResNet block output formula? Answer: y = x + F(x).
  2. True/False: Residual connections help gradient flow. Answer: True.
  3. Multiple Choice: When channels change, skip path needs: (a) dropout, (b) projection conv, (c) softmax, (d) larger lr only. Answer: (b).
  4. Short Answer: What does F learn if optimal mapping is identity? Answer: Near-zero residual.
  5. True/False: ResNets were introduced primarily for NLP transformers. Answer: False—vision (2015).
  6. Multiple Choice: BN in example block sits: (a) after skip add only, (b) on conv outputs before add, (c) nowhere, (d) on loss. Answer: (b).
  7. Short Answer: Why 1×1 conv on downsample path? Answer: Match channels and spatial size for addition.
  8. True/False: Deeper ResNets always need more dropout. Answer: False—task and data dependent.
  9. Multiple Choice: Next module topic: (a) Training Loop, (b) Perceptron, (c) Dataset, (d) Sigmoid. Answer: (a).
  10. Short Answer: One benefit of skip connections for optimization? Answer: Easier identity mapping / improved gradient flow.

Key Takeaways

  • ResNets learn residuals: y = x + F(x).
  • Skip connections enable identity paths and healthier gradients.
  • Use projection shortcuts when shapes change.
  • Combine with BN, ReLU, and tuned optimizers from this module.
  • Next module: 6.2 Training Loop — end-to-end training engineering.
Trainer’s Guide

Capstone project: Train plain CNN vs TinyResNet (same param budget) on CIFAR-10; compare depth achievable before val accuracy drops.

Whiteboard: Draw backward paths through x+F(x) with chain rule—highlight +1 term on identity branch.

Module 6.1 Complete You have the foundations for deep learning training. Continue to Module 6.2 — Training Loop.