Computer Vision
This guide covers Convolutional Neural Networks (CNNs) and image processing.
CNN and modern vision curriculum map (this guide)
CNN track (RNN/Transformer for text → NLP guide; PyTorch CNN patterns → Deep learning frameworks).
- CNN foundation (conv, pool, channels) → Convolution and edge detection, Convolutional layers
- Activation functions (Sigmoid, tanh, ReLU, Leaky ReLU, ELU, SELU) → Activation functions for CNNs
- CNN architectures and math (receptive field, parameter count) → CNN architectures
- CNN in PyTorch (
nn.Conv2d, training loops) → Deep learning frameworks. PyTorch CV - Data augmentation and pre-trained CNNs → Data augmentation, Transfer learning
- Transformers in vision (ViT etc.) → Computer vision advanced topics
Table of Contents
- CNN and modern vision curriculum map (this guide)
- Introduction to Computer Vision
- Activation functions for CNNs
- What Are Images and Pixels?
- Convolution and Edge Detection Techniques
- Padding, Strides, and Spatial Arrangement
- Working with Convolution on RGB Images
- Understanding and Building Convolutional Layers
- Pooling Mechanisms: Max and Average Pooling
- Optimizing CNN Training: Techniques and Practices
- CNN Architectures
- ImageNet and Large-Scale Recognition
- Transfer Learning
- Data Augmentation
- Practice Exercises
Introduction to Computer Vision
Overview of Computer Vision and Its Applications
Computer Vision is a field of artificial intelligence that enables machines to interpret and understand visual information from the world, similar to how humans use their eyes and brain.
Key Applications:
- Image Classification: Categorize images (e.g., cat vs. dog, object recognition)
- Object Detection: Locate and identify multiple objects in images
- Face Recognition: Identify individuals from facial features
- Medical Imaging: Analyze X-rays, MRIs, CT scans for diagnosis
- Autonomous Vehicles: Detect pedestrians, traffic signs, lanes
- Augmented Reality: Overlay digital information on real-world scenes
- Quality Control: Inspect products in manufacturing
- Retail: Visual search, inventory management
- Security: Surveillance and monitoring systems
Understanding the Human Visual System
The human visual system provides inspiration for computer vision algorithms:
How Humans See:
- Retina: Captures light and converts to neural signals
- Visual Cortex: Processes visual information hierarchically
- V1 (Primary Visual Cortex): Detects edges, lines, orientations
- V2-V4: Recognizes shapes, patterns, textures
- IT (Inferior Temporal Cortex): Recognizes objects, faces
- Hierarchical Processing: Simple features → Complex patterns → Objects
Connection to CNNs:
- Early Layers: Detect edges, gradients (like V1)
- Middle Layers: Detect shapes, textures (like V2-V4)
- Deep Layers: Recognize objects, complex patterns (like IT)
# Visualizing how CNNs process images (conceptual)
import matplotlib.pyplot as plt
import numpy as np
# Simulate hierarchical feature detection
def visualize_cnn_layers():
"""
Conceptual visualization of how CNNs process images
similar to human visual cortex
"""
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# Original image
original = np.random.rand(224, 224, 3)
axes[0, 0].imshow(original)
axes[0, 0].set_title('Input Image\n(Retina)', fontweight='bold')
axes[0, 0].axis('off')
# Layer 1: Edge detection (V1-like)
edges = np.random.rand(224, 224, 32) # 32 edge filters
axes[0, 1].imshow(edges[:, :, 0], cmap='gray')
axes[0, 1].set_title('Layer 1: Edges\n(V1 - Primary Visual Cortex)', fontweight='bold')
axes[0, 1].axis('off')
# Layer 2: Shapes (V2-like)
shapes = np.random.rand(112, 112, 64) # After pooling
axes[0, 2].imshow(shapes[:, :, 0], cmap='gray')
axes[0, 2].set_title('Layer 2: Shapes\n(V2-V4 - Pattern Recognition)', fontweight='bold')
axes[0, 2].axis('off')
# Layer 3: Textures
textures = np.random.rand(56, 56, 128)
axes[1, 0].imshow(textures[:, :, 0], cmap='gray')
axes[1, 0].set_title('Layer 3: Textures\n(Complex Patterns)', fontweight='bold')
axes[1, 0].axis('off')
# Layer 4: Objects
objects = np.random.rand(28, 28, 256)
axes[1, 1].imshow(objects[:, :, 0], cmap='gray')
axes[1, 1].set_title('Layer 4: Objects\n(IT - Object Recognition)', fontweight='bold')
axes[1, 1].axis('off')
# Final classification
axes[1, 2].text(0.5, 0.5, 'Classification:\nCat (95%)\nDog (3%)\nBird (2%)',
ha='center', va='center', fontsize=14, fontweight='bold')
axes[1, 2].set_title('Output: Classification\n(Decision)', fontweight='bold')
axes[1, 2].axis('off')
plt.suptitle('CNN Processing: From Pixels to Understanding\n(Mimicking Human Visual Cortex)',
fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# Note: This is a conceptual visualization
# In practice, you'd visualize actual CNN feature maps
Key Historical Milestones in Computer Vision
1960s-1970s: Early Foundations
- 1966: MIT's "Summer Vision Project" - first attempt at computer vision
- 1970s: Edge detection algorithms (Roberts, Sobel operators)
1980s-1990s: Feature-Based Methods
- 1980s: Scale-Invariant Feature Transform (SIFT) developed
- 1998: LeNet-5 - First successful CNN for digit recognition (Yann LeCun)
2000s: Machine Learning Era
- 2001: Viola-Jones face detection algorithm
- 2006: Deep learning renaissance (Hinton's work on deep belief networks)
2010s: Deep Learning Revolution
- 2012: AlexNet wins ImageNet (breakthrough moment for CNNs)
- 2014: VGGNet - Very deep networks with small filters
- 2015: ResNet - Residual connections enable very deep networks (152 layers)
- 2015: YOLO - Real-time object detection
- 2017: Transformer architecture adapted for vision (Vision Transformer)
2020s: Modern Era
- 2020: Vision Transformers (ViT) - Transformers for images
- 2021: CLIP - Contrastive Language-Image Pre-training
- 2022: DALL-E 2, Stable Diffusion - Text-to-image generation
Activation functions for CNNs
CNNs stack linear convolutions + nonlinear activations. Same families appear in MLPs and RNNs; pick based on depth, saturation, and speed.
| Activation | Output range | Typical use in CNNs |
|---|---|---|
| Sigmoid | (0, 1) | Binary heads; avoid many stacked sigmoids (vanishing gradient) |
| tanh | (-1, 1) | Older nets; zero-centered but can saturate |
| ReLU | [0, ∞) | Default hidden activation (sparse, fast) |
| Leaky ReLU | (-∞, ∞) | Mitigates “dying ReLU” with small negative slope |
| ELU | (-α, ∞) | Smooth negatives; can improve generalization |
| SELU | self-normalizing | With LeCun normal init and proper architecture, can stabilize training |
import torch
import torch.nn as nn
# PyTorch modules (use inside nn.Sequential or custom forward)
acts = nn.ModuleDict({
"sigmoid": nn.Sigmoid(),
"tanh": nn.Tanh(),
"relu": nn.ReLU(),
"leaky_relu": nn.LeakyReLU(0.01),
"elu": nn.ELU(alpha=1.0),
"selu": nn.SELU(), # use with nn.init.xavier_normal_ / LeCun and eval drop behavior per docs
})
x = torch.randn(4, 16, 32, 32) # NCHW
for name, f in acts.items():
print(name, f(x).min().item(), f(x).max().item())
What Are Images and Pixels?
Fundamentals of Digital Images
A digital image is a 2D array of numbers representing light intensity at each point.
Key Concepts:
- Pixel: Picture element - smallest unit of a digital image
- Resolution: Number of pixels (width × height)
- Color Depth: Number of bits per pixel (determines color range)
- Channels: Color components (RGB = 3 channels, Grayscale = 1 channel)
Understanding Pixels and Color Models
Grayscale Images:
- Single channel (intensity values)
- Values typically range from 0 (black) to 255 (white)
- Shape:
(height, width)or(height, width, 1)
RGB Images:
- Three channels: Red, Green, Blue
- Each pixel has 3 values (R, G, B)
- Values range from 0 to 255 for each channel
- Shape:
(height, width, 3)
Other Color Models:
- HSV: Hue, Saturation, Value (useful for color-based segmentation)
- LAB: Perceptually uniform color space
- CMYK: Cyan, Magenta, Yellow, Black (for printing)
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# Understanding pixels and color models
def explore_image_structure():
"""Demonstrate image structure and color models"""
# Create a simple grayscale image
gray_image = np.array([[0, 50, 100, 150, 200, 255],
[0, 50, 100, 150, 200, 255],
[0, 50, 100, 150, 200, 255]])
# Create an RGB image
rgb_image = np.zeros((100, 100, 3), dtype=np.uint8)
rgb_image[:, :, 0] = 255 # Red channel
rgb_image[25:75, 25:75, 1] = 255 # Green square
rgb_image[50:, :, 2] = 255 # Blue in bottom half
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# Grayscale image
axes[0, 0].imshow(gray_image, cmap='gray')
axes[0, 0].set_title('Grayscale Image\n(1 channel, 0-255)', fontweight='bold')
axes[0, 0].axis('off')
# Grayscale pixel values
axes[0, 1].text(0.5, 0.5, f'Shape: {gray_image.shape}\nValues: {gray_image[0]}',
ha='center', va='center', fontsize=12,
bbox=dict(boxstyle='round', facecolor='wheat'))
axes[0, 1].set_title('Grayscale Structure', fontweight='bold')
axes[0, 1].axis('off')
# RGB image
axes[0, 2].imshow(rgb_image)
axes[0, 2].set_title('RGB Image\n(3 channels: R, G, B)', fontweight='bold')
axes[0, 2].axis('off')
# RGB channels separately
axes[1, 0].imshow(rgb_image[:, :, 0], cmap='Reds')
axes[1, 0].set_title('Red Channel', fontweight='bold')
axes[1, 0].axis('off')
axes[1, 1].imshow(rgb_image[:, :, 1], cmap='Greens')
axes[1, 1].set_title('Green Channel', fontweight='bold')
axes[1, 1].axis('off')
axes[1, 2].imshow(rgb_image[:, :, 2], cmap='Blues')
axes[1, 2].set_title('Blue Channel', fontweight='bold')
axes[1, 2].axis('off')
plt.suptitle('Understanding Images: Pixels and Color Models',
fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# Print image properties
print(f"Grayscale image shape: {gray_image.shape}")
print(f"RGB image shape: {rgb_image.shape}")
print(f"RGB image dtype: {rgb_image.dtype}")
print(f"Pixel value range: {rgb_image.min()} to {rgb_image.max()}")
# explore_image_structure()
Image Types and Formats Explained
Common Image Formats:
- JPEG/JPG: Lossy compression, good for photos (smaller file size)
- PNG: Lossless compression, supports transparency
- GIF: Supports animation, limited colors (256)
- BMP: Uncompressed, large file sizes
- TIFF: High quality, used in professional photography
For Machine Learning:
- NumPy Arrays:
(height, width, channels)- most common - PIL/Pillow Images: Python Imaging Library format
- TensorFlow Tensors:
(batch, height, width, channels) - PyTorch Tensors:
(batch, channels, height, width)- note channel order!
# Loading and converting between formats
from PIL import Image
import numpy as np
# Load image
img = Image.open('image.jpg')
# Convert to numpy array
img_array = np.array(img)
print(f"Image shape: {img_array.shape}")
print(f"Image dtype: {img_array.dtype}")
# Convert grayscale
gray_img = img.convert('L') # 'L' = grayscale
gray_array = np.array(gray_img)
print(f"Grayscale shape: {gray_array.shape}")
# Normalize to 0-1 range (common in ML)
normalized = img_array.astype('float32') / 255.0
print(f"Normalized range: {normalized.min():.2f} to {normalized.max():.2f}")
# Resize image
resized = img.resize((224, 224)) # Common size for CNNs
resized_array = np.array(resized)
print(f"Resized shape: {resized_array.shape}")
Introduction to CNNs
Why CNNs for Images?
Convolutional Neural Networks are specifically designed for image data and excel at detecting spatial patterns.
Key Advantages:
- Translation Invariance: Detect features anywhere in the image
- Parameter Sharing: Same filters used across image (fewer parameters)
- Local Patterns: Detect edges, shapes, textures, objects
- Hierarchical Features: Low-level (edges) → High-level (objects)
Why Not Fully Connected?
- Too many parameters (e.g., 28x28 image = 784 inputs → millions of parameters)
- Doesn't leverage spatial structure
- Not translation invariant
CNN Architecture
Input Image → Conv Layers → Pooling → Conv Layers → Pooling → Fully Connected → Output
↓ ↓ ↓ ↓ ↓ ↓ ↓
(H×W×C) Feature Maps Downsample Feature Maps Downsample Features Predictions
Key Components:
- Convolutional Layers: Detect features using filters
- Pooling Layers: Reduce spatial dimensions
- Fully Connected Layers: Final classification
Convolution and Edge Detection Techniques
Introduction to Convolution Operations
Convolution is a mathematical operation that combines two functions to produce a third function. In image processing, it's used to apply filters (kernels) to images to detect features like edges, blurs, and sharpening.
How Convolution Works:
- Place a small filter (kernel) over a region of the image
- Multiply corresponding values
- Sum all products
- Place result in output image
- Slide filter across entire image
Mathematical Definition:
Output[i, j] = Σ Σ Input[i+m, j+n] × Filter[m, n]
m n
Edge Detection Filters
Edges are boundaries between different regions in an image. Edge detection is fundamental in computer vision.
Sobel Operator
Detects edges by computing gradient approximations.
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
import cv2
# Sobel filters for edge detection
sobel_x = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
sobel_y = np.array([[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]])
# Create test image with edges
image = np.zeros((100, 100))
image[30:70, 30:70] = 1 # White square on black background
# Apply Sobel filters
edges_x = ndimage.convolve(image, sobel_x)
edges_y = ndimage.convolve(image, sobel_y)
edges_magnitude = np.sqrt(edges_x**2 + edges_y**2)
# Visualize
fig, axes = plt.subplots(2, 2, figsize=(10, 10))
axes[0, 0].imshow(image, cmap='gray')
axes[0, 0].set_title('Original Image', fontweight='bold')
axes[0, 0].axis('off')
axes[0, 1].imshow(edges_x, cmap='gray')
axes[0, 1].set_title('Sobel X (Vertical Edges)', fontweight='bold')
axes[0, 1].axis('off')
axes[1, 0].imshow(edges_y, cmap='gray')
axes[1, 0].set_title('Sobel Y (Horizontal Edges)', fontweight='bold')
axes[1, 0].axis('off')
axes[1, 1].imshow(edges_magnitude, cmap='gray')
axes[1, 1].set_title('Edge Magnitude', fontweight='bold')
axes[1, 1].axis('off')
plt.suptitle('Sobel Edge Detection', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
Canny Edge Detector
Multi-stage algorithm for optimal edge detection.
import cv2
import matplotlib.pyplot as plt
# Load image
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# Canny edge detection
edges = cv2.Canny(image, threshold1=50, threshold2=150)
# Visualize
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].imshow(image, cmap='gray')
axes[0].set_title('Original Image', fontweight='bold')
axes[0].axis('off')
axes[1].imshow(edges, cmap='gray')
axes[1].set_title('Canny Edges', fontweight='bold')
axes[1].axis('off')
plt.tight_layout()
plt.show()
# Canny parameters explained:
# threshold1: Lower threshold for edge linking
# threshold2: Upper threshold for edge detection
# Edges with gradient > threshold2 are strong edges
# Edges with gradient between threshold1 and threshold2 are weak edges
# Weak edges are kept only if connected to strong edges
Prewitt Operator
Similar to Sobel but with different weights.
# Prewitt filters
prewitt_x = np.array([[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]])
prewitt_y = np.array([[-1, -1, -1],
[0, 0, 0],
[1, 1, 1]])
# Apply Prewitt
edges_x_prewitt = ndimage.convolve(image, prewitt_x)
edges_y_prewitt = ndimage.convolve(image, prewitt_y)
edges_prewitt = np.sqrt(edges_x_prewitt**2 + edges_y_prewitt**2)
Practical Examples in Python using OpenCV
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Load image
img = cv2.imread('image.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 1. Sobel edge detection
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
sobel_combined = np.sqrt(sobelx**2 + sobely**2)
# 2. Laplacian edge detection
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
# 3. Canny edge detection
canny = cv2.Canny(gray, 50, 150)
# Visualize all methods
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes[0, 0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
axes[0, 0].set_title('Original', fontweight='bold')
axes[0, 0].axis('off')
axes[0, 1].imshow(sobelx, cmap='gray')
axes[0, 1].set_title('Sobel X', fontweight='bold')
axes[0, 1].axis('off')
axes[0, 2].imshow(sobely, cmap='gray')
axes[0, 2].set_title('Sobel Y', fontweight='bold')
axes[0, 2].axis('off')
axes[1, 0].imshow(sobel_combined, cmap='gray')
axes[1, 0].set_title('Sobel Combined', fontweight='bold')
axes[1, 0].axis('off')
axes[1, 1].imshow(laplacian, cmap='gray')
axes[1, 1].set_title('Laplacian', fontweight='bold')
axes[1, 1].axis('off')
axes[1, 2].imshow(canny, cmap='gray')
axes[1, 2].set_title('Canny', fontweight='bold')
axes[1, 2].axis('off')
plt.suptitle('Edge Detection Comparison', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
How Convolution Works (Detailed)
import numpy as np
import matplotlib.pyplot as plt
def manual_convolution(image, kernel):
"""Manual convolution implementation for understanding"""
img_height, img_width = image.shape
kernel_height, kernel_width = kernel.shape
# Output size
output_height = img_height - kernel_height + 1
output_width = img_width - kernel_width + 1
output = np.zeros((output_height, output_width))
# Slide kernel over image
for i in range(output_height):
for j in range(output_width):
# Extract region
region = image[i:i+kernel_height, j:j+kernel_width]
# Element-wise multiplication and sum
output[i, j] = np.sum(region * kernel)
return output
# Example
image = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]])
kernel = np.array([[1, 0, -1],
[1, 0, -1],
[1, 0, -1]])
result = manual_convolution(image, kernel)
print("Original Image:")
print(image)
print("\nKernel:")
print(kernel)
print("\nConvolved Output:")
print(result)
Padding, Strides, and Spatial Arrangement
Definitions and Purposes of Padding and Strides
Padding adds extra pixels around the input image before convolution.
Why Padding?
- Preserve Size: Keep output same size as input (useful in deep networks)
- Prevent Information Loss: Avoid losing edge information
- Control Output Size: Fine-tune spatial dimensions
Types of Padding:
- Valid Padding (no padding): Output size decreases
- Same Padding: Output size equals input size (adds zeros to maintain size)
Stride determines how many pixels the filter moves each step.
Why Stride?
- Downsampling: Reduce spatial dimensions (alternative to pooling)
- Efficiency: Fewer computations with larger strides
- Receptive Field: Larger stride increases receptive field faster
How Convolution Changes Spatial Dimensions
Output Size Formula:
Output Size = (Input Size - Filter Size + 2×Padding) / Stride + 1
Examples:
import numpy as np
import matplotlib.pyplot as plt
def calculate_output_size(input_size, filter_size, padding, stride):
"""Calculate convolution output size"""
return (input_size - filter_size + 2 * padding) // stride + 1
# Example 1: No padding, stride 1
input_size = 32
filter_size = 3
padding = 0
stride = 1
output_size = calculate_output_size(input_size, filter_size, padding, stride)
print(f"Input: {input_size}x{input_size}, Filter: {filter_size}x{filter_size}")
print(f"Padding: {padding}, Stride: {stride}")
print(f"Output: {output_size}x{output_size}")
# Example 2: Same padding, stride 1
padding = 1 # For 3x3 filter, padding=1 gives same size
output_size = calculate_output_size(input_size, filter_size, padding, stride)
print(f"\nWith padding={padding}: Output: {output_size}x{output_size}")
# Example 3: Stride 2 (downsampling)
stride = 2
output_size = calculate_output_size(input_size, filter_size, padding, stride)
print(f"\nWith stride={stride}: Output: {output_size}x{output_size}")
# Visualize the effect
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# Original image
original = np.ones((32, 32))
axes[0].imshow(original, cmap='gray')
axes[0].set_title(f'Input: {original.shape[0]}x{original.shape[1]}', fontweight='bold')
axes[0].axis('off')
# After convolution (no padding, stride 1)
no_pad = np.ones((30, 30)) # 32-3+1 = 30
axes[1].imshow(no_pad, cmap='gray')
axes[1].set_title(f'No Padding: {no_pad.shape[0]}x{no_pad.shape[1]}', fontweight='bold')
axes[1].axis('off')
# After convolution (same padding, stride 1)
same_pad = np.ones((32, 32)) # Same size
axes[2].imshow(same_pad, cmap='gray')
axes[2].set_title(f'Same Padding: {same_pad.shape[0]}x{same_pad.shape[1]}', fontweight='bold')
axes[2].axis('off')
plt.suptitle('Effect of Padding on Output Size', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
Implementation Tips in Deep Learning Frameworks
Keras/TensorFlow:
from tensorflow.keras import layers
# Valid padding (no padding)
c>32, (3, 3), padding='valid', strides=1)
# Same padding (preserve size)
c>32, (3, 3), padding='same', strides=1)
# Stride 2 for downsampling
c>64, (3, 3), padding='same', strides=2)
# Custom padding
c>32, (3, 3), padding='same', strides=1) # Keras: 'valid' or 'same' (not an int)
PyTorch:
import torch.nn as nn
# Valid padding (no padding)
c>1, 32, kernel_size=3, padding=0, stride=1)
# Same padding (for 3x3 kernel, padding=1 gives same size)
c>1, 32, kernel_size=3, padding=1, stride=1)
# Stride 2 for downsampling
c>32, 64, kernel_size=3, padding=1, stride=2)
Key Rules:
- For 3x3 kernel:
padding=1gives same size output (with stride=1) - For 5x5 kernel:
padding=2gives same size output (with stride=1) - General rule:
padding = (kernel_size - 1) / 2for same size
Working with Convolution on RGB Images
Handling Multiple Channels in Convolutions
RGB images have 3 channels (Red, Green, Blue). Convolution on RGB images requires filters with matching depth.
Key Concepts:
- Input:
(height, width, 3)- 3 channels - Filter:
(filter_height, filter_width, 3)- Must match input channels - Output:
(output_height, output_width, num_filters)- Each filter produces one feature map
Challenges and Considerations for RGB Data
Channel Order: Different frameworks use different orders
- TensorFlow/Keras:
(height, width, channels)- channels last - PyTorch:
(channels, height, width)- channels first
- TensorFlow/Keras:
Normalization: RGB values typically 0-255, need normalization (0-1 or standardized)
Color Information: All channels contribute to feature detection
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
# Create RGB image (simple example)
rgb_image = np.zeros((100, 100, 3))
rgb_image[25:75, 25:75, 0] = 1 # Red square
rgb_image[40:60, 40:60, 1] = 1 # Green square inside
# Create 3-channel filter (edge detector for each channel)
edge_filter = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
# Apply convolution to each channel separately
red_edges = ndimage.convolve(rgb_image[:, :, 0], edge_filter)
green_edges = ndimage.convolve(rgb_image[:, :, 1], edge_filter)
blue_edges = ndimage.convolve(rgb_image[:, :, 2], edge_filter)
# Combine channels (or use separately)
combined_edges = np.stack([red_edges, green_edges, blue_edges], axis=2)
# Visualize
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes[0, 0].imshow(rgb_image)
axes[0, 0].set_title('Original RGB Image', fontweight='bold')
axes[0, 0].axis('off')
axes[0, 1].imshow(rgb_image[:, :, 0], cmap='Reds')
axes[0, 1].set_title('Red Channel', fontweight='bold')
axes[0, 1].axis('off')
axes[0, 2].imshow(rgb_image[:, :, 1], cmap='Greens')
axes[0, 2].set_title('Green Channel', fontweight='bold')
axes[0, 2].axis('off')
axes[1, 0].imshow(red_edges, cmap='gray')
axes[1, 0].set_title('Red Channel Edges', fontweight='bold')
axes[1, 0].axis('off')
axes[1, 1].imshow(green_edges, cmap='gray')
axes[1, 1].set_title('Green Channel Edges', fontweight='bold')
axes[1, 1].axis('off')
axes[1, 2].imshow(np.mean(combined_edges, axis=2), cmap='gray')
axes[1, 2].set_title('Combined Edges', fontweight='bold')
axes[1, 2].axis('off')
plt.suptitle('Convolution on RGB Images', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
Case Studies of RGB Image Processing
In CNNs, RGB convolution works like this:
from tensorflow.keras import layers
import numpy as np
# Example: First convolutional layer on RGB image
# Input: (224, 224, 3) - RGB image
# Filter: (3, 3, 3) - 3x3 filter operating on 3 channels
# Output: (224, 224, 32) - 32 feature maps
model = layers.Conv2D(
filters=32, # Number of output feature maps
kernel_size=(3, 3), # 3x3 filter
input_shape=(224, 224, 3), # RGB input
padding='same'
)
# The filter actually has shape (3, 3, 3, 32):
# - 3x3: spatial dimensions
# - 3: input channels (RGB)
# - 32: output filters
print(f"Filter shape: {model.kernel.shape}") # (3, 3, 3, 32)
print(f"Input shape: (224, 224, 3)")
print(f"Output shape: (224, 224, 32)")
Understanding Multi-Channel Convolution:
# Conceptual explanation
def rgb_convolution_explanation():
"""
How convolution works on RGB images:
1. Filter has depth = input channels (3 for RGB)
2. Filter slides over all 3 channels simultaneously
3. Element-wise multiplication across all channels
4. Sum all channel results to get single output value
5. Repeat for each filter to get multiple feature maps
"""
# Example: 3x3x3 filter on RGB image
input_rgb = np.random.rand(5, 5, 3) # 5x5 RGB image
filter_3d = np.random.rand(3, 3, 3) # 3x3 filter for 3 channels
# For each position, compute:
# output[i,j] = sum over all channels of (input_region * filter)
output = np.zeros((3, 3)) # Output after 3x3 convolution
for i in range(3):
for j in range(3):
# Extract 3x3 region from all channels
region = input_rgb[i:i+3, j:j+3, :] # Shape: (3, 3, 3)
# Element-wise multiplication and sum across all dimensions
output[i, j] = np.sum(region * filter_3d)
return output
# rgb_convolution_explanation()
Understanding and Building Convolutional Layers
Anatomy of Convolutional Layers
A convolutional layer consists of:
- Filters/Kernels: Learnable weights that detect features
- Bias Terms: Learnable offsets
- Activation Function: Non-linearity (ReLU, sigmoid, etc.)
- Parameters: All learnable weights and biases
Layer Structure:
Input → Convolution → Batch Normalization → Activation → Output
Designing Convolutional Layers in Deep Learning Models
Best Practices:
- Start Small: Begin with few filters, increase depth gradually
- Small Kernels: Use 3x3 filters (more efficient than 5x5)
- Progressive Depth: Increase filters as you go deeper (32 → 64 → 128)
- Regularization: Add dropout, batch normalization
- Pooling: Reduce spatial dimensions periodically
from tensorflow.keras import layers, Model
import tensorflow as tf
def build_conv_block(filters, kernel_size=3, use_bn=True, use_dropout=False):
"""Build a convolutional block with best practices"""
block = []
# Convolution
block.append(layers.Conv2D(
filters,
kernel_size,
padding='same',
use_bias=not use_bn # No bias if using batch norm
))
# Batch Normalization
if use_bn:
block.append(layers.BatchNormalization())
# Activation
block.append(layers.ReLU())
# Dropout (optional)
if use_dropout:
block.append(layers.Dropout(0.25))
return block
# Build a CNN using blocks
def build_cnn(input_shape, num_classes):
inputs = layers.Input(shape=input_shape)
x = inputs
# Block 1: 32 filters
for layer in build_conv_block(32, use_dropout=False):
x = layer(x)
x = layers.MaxPooling2D(2)(x)
# Block 2: 64 filters
for layer in build_conv_block(64, use_dropout=False):
x = layer(x)
x = layers.MaxPooling2D(2)(x)
# Block 3: 128 filters
for layer in build_conv_block(128, use_dropout=True):
x = layer(x)
x = layers.MaxPooling2D(2)(x)
# Classifier
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(128, activation='relu')(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(num_classes, activation='softmax')(x)
model = Model(inputs, outputs)
return model
# Create model
model = build_cnn(input_shape=(224, 224, 3), num_classes=10)
model.summary()
Hands-on Coding Session with TensorFlow/Keras
from tensorflow.keras import layers, Model
import tensorflow as tf
# Method 1: Sequential API (Simple)
model_sequential = tf.keras.Sequential([
layers.Conv2D(32, 3, activation='relu', input_shape=(28, 28, 1)),
layers.MaxPooling2D(2),
layers.Conv2D(64, 3, activation='relu'),
layers.MaxPooling2D(2),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(10, activation='softmax')
])
# Method 2: Functional API (Flexible)
def create_cnn_functional():
inputs = layers.Input(shape=(28, 28, 1))
# Conv Block 1
x = layers.Conv2D(32, 3, padding='same')(inputs)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.MaxPooling2D(2)(x)
# Conv Block 2
x = layers.Conv2D(64, 3, padding='same')(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.MaxPooling2D(2)(x)
# Classifier
x = layers.Flatten()(x)
x = layers.Dense(128, activation='relu')(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(10, activation='softmax')(x)
model = Model(inputs, outputs)
return model
model_functi>
# Compile and train
model_functional.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# model_functional.summary()
Building CNNs
Understanding Convolutional Layers
Convolution Operation:
- Filter/Kernel: Small matrix that slides over image
- Stride: How many pixels filter moves each step
- Padding: Add zeros around image (same/valid)
- Output Size:
(input_size - filter_size + 2*padding) / stride + 1
Key Parameters:
- Filters: Number of feature maps to learn
- Kernel Size: Size of filter (e.g., 3x3, 5x5)
- Stride: Step size (default: 1)
- Padding: 'same' (preserve size) or 'valid' (no padding)
Simple CNN with Keras
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
# CNN for image classification
model = keras.Sequential([
# First convolutional block
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1), name='conv1'),
layers.MaxPooling2D((2, 2), name='pool1'),
layers.BatchNormalization(name='bn1'),
# Second convolutional block
layers.Conv2D(64, (3, 3), activation='relu', name='conv2'),
layers.MaxPooling2D((2, 2), name='pool2'),
layers.BatchNormalization(name='bn2'),
# Third convolutional block
layers.Conv2D(64, (3, 3), activation='relu', name='conv3'),
layers.Dropout(0.25, name='dropout1'),
# Flatten and classify
layers.Flatten(name='flatten'),
layers.Dense(64, activation='relu', name='fc1'),
layers.Dropout(0.5, name='dropout2'),
layers.Dense(10, activation='softmax', name='output')
], name='CNN_Classifier')
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# Model summary
model.summary()
# Visualize architecture
keras.utils.plot_model(model, to_file='cnn_architecture.png', show_shapes=True)
# Calculate parameters
total_params = model.count_params()
print(f"\nTotal parameters: {total_params:,}")
CNN with PyTorch
import torch
import torch.nn as nn
import torch.nn.functional as F
class CNN(nn.Module):
def __init__(self, num_classes=10):
super().__init__()
# Convolutional layers
self.c>1, 32, kernel_size=3, padding=1)
self.bn1 = nn.BatchNorm2d(32)
self.pool1 = nn.MaxPool2d(2, 2)
self.c>32, 64, kernel_size=3, padding=1)
self.bn2 = nn.BatchNorm2d(64)
self.pool2 = nn.MaxPool2d(2, 2)
self.c>64, 64, kernel_size=3, padding=1)
self.dropout1 = nn.Dropout(0.25)
# Fully connected layers
self.flatten = nn.Flatten()
self.fc1 = nn.Linear(64 * 7 * 7, 64)
self.dropout2 = nn.Dropout(0.5)
self.fc2 = nn.Linear(64, num_classes)
def forward(self, x):
# Convolutional blocks
x = self.pool1(F.relu(self.bn1(self.conv1(x))))
x = self.pool2(F.relu(self.bn2(self.conv2(x))))
x = self.dropout1(F.relu(self.conv3(x)))
# Fully connected
x = self.flatten(x)
x = F.relu(self.fc1(x))
x = self.dropout2(x)
x = self.fc2(x)
return x
model = CNN(num_classes=10)
print(model)
# Count parameters
total_params = sum(p.numel() for p in model.parameters())
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"\nTotal parameters: {total_params:,}")
print(f"Trainable parameters: {trainable_params:,}")
Pooling Mechanisms: Max and Average Pooling
The Role of Pooling in Feature Reduction
Pooling (also called subsampling or downsampling) reduces the spatial dimensions of feature maps while retaining important information.
Why Pooling?
- Reduce Parameters: Smaller feature maps = fewer parameters in subsequent layers
- Translation Invariance: Makes model robust to small translations
- Receptive Field: Increases receptive field (area of input each neuron sees)
- Computational Efficiency: Less computation in deeper layers
- Prevent Overfitting: Reduces model capacity
Common Pooling Types:
- Max Pooling: Takes maximum value in each window
- Average Pooling: Takes average value in each window
- Global Pooling: Pools entire feature map to single value
Comparative Analysis of Max Pooling vs. Average Pooling
Max Pooling:
- Advantages:
- Preserves strongest features
- Better for detecting edges, textures
- More commonly used
- Disadvantages:
- Loses information about other values
- Can be sensitive to noise
Average Pooling:
- Advantages:
- Smooths features
- Better for background regions
- Less sensitive to noise
- Disadvantages:
- May blur important features
- Less commonly used
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
# Example feature map (simulating edge detection output)
feature_map = np.array([[1, 2, 3, 4, 5, 6],
[5, 6, 7, 8, 9, 10],
[9, 10, 11, 12, 13, 14],
[13, 14, 15, 16, 17, 18],
[17, 18, 19, 20, 21, 22],
[21, 22, 23, 24, 25, 26]])
def max_pooling_2d(input_map, pool_size=2):
"""Manual max pooling implementation"""
h, w = input_map.shape
pool_h, pool_w = pool_size, pool_size
out_h = h // pool_h
out_w = w // pool_w
output = np.zeros((out_h, out_w))
for i in range(out_h):
for j in range(out_w):
region = input_map[i*pool_h:(i+1)*pool_h, j*pool_w:(j+1)*pool_w]
output[i, j] = np.max(region)
return output
def avg_pooling_2d(input_map, pool_size=2):
"""Manual average pooling implementation"""
h, w = input_map.shape
pool_h, pool_w = pool_size, pool_size
out_h = h // pool_h
out_w = w // pool_w
output = np.zeros((out_h, out_w))
for i in range(out_h):
for j in range(out_w):
region = input_map[i*pool_h:(i+1)*pool_h, j*pool_w:(j+1)*pool_w]
output[i, j] = np.mean(region)
return output
# Apply pooling
max_pooled = max_pooling_2d(feature_map, pool_size=2)
avg_pooled = avg_pooling_2d(feature_map, pool_size=2)
# Visualize
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
axes[0].imshow(feature_map, cmap='viridis')
axes[0].set_title(f'Original Feature Map\n{feature_map.shape}', fontweight='bold')
axes[0].axis('off')
for i in range(feature_map.shape[0]):
for j in range(feature_map.shape[1]):
axes[0].text(j, i, int(feature_map[i, j]), ha='center', va='center', color='white', fontsize=8)
axes[1].imshow(max_pooled, cmap='viridis')
axes[1].set_title(f'Max Pooling (2x2)\n{max_pooled.shape}', fontweight='bold')
axes[1].axis('off')
for i in range(max_pooled.shape[0]):
for j in range(max_pooled.shape[1]):
axes[1].text(j, i, int(max_pooled[i, j]), ha='center', va='center', color='white', fontsize=10)
axes[2].imshow(avg_pooled, cmap='viridis')
axes[2].set_title(f'Average Pooling (2x2)\n{avg_pooled.shape}', fontweight='bold')
axes[2].axis('off')
for i in range(avg_pooled.shape[0]):
for j in range(avg_pooled.shape[1]):
axes[2].text(j, i, f'{avg_pooled[i, j]:.1f}', ha='center', va='center', color='white', fontsize=10)
plt.suptitle('Pooling Comparison: Max vs Average', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
print("Original shape:", feature_map.shape)
print("After 2x2 pooling:", max_pooled.shape)
print("Reduction factor:", feature_map.size / max_pooled.size)
Implementing Pooling Layers in Neural Networks
Keras/TensorFlow:
from tensorflow.keras import layers
# Max Pooling
max_pool = layers.MaxPooling2D(pool_size=(2, 2), strides=2, padding='valid')
# Average Pooling
avg_pool = layers.AveragePooling2D(pool_size=(2, 2), strides=2, padding='valid')
# Global Pooling (reduces to single value per channel)
global_max_pool = layers.GlobalMaxPooling2D()
global_avg_pool = layers.GlobalAveragePooling2D()
# Example in a model
model = tf.keras.Sequential([
layers.Conv2D(32, 3, activation='relu', input_shape=(28, 28, 1)),
layers.MaxPooling2D(2), # Reduces 28x28 to 14x14
layers.Conv2D(64, 3, activation='relu'),
layers.MaxPooling2D(2), # Reduces 14x14 to 7x7
layers.GlobalAveragePooling2D(), # Reduces 7x7x64 to 64
layers.Dense(10, activation='softmax')
])
PyTorch:
import torch.nn as nn
# Max Pooling
max_pool = nn.MaxPool2d(kernel_size=2, stride=2)
# Average Pooling
avg_pool = nn.AvgPool2d(kernel_size=2, stride=2)
# Adaptive Pooling (outputs fixed size regardless of input)
adaptive_pool = nn.AdaptiveAvgPool2d((1, 1)) # Always outputs 1x1
When to Use Each:
- Max Pooling: Default choice, use in most cases
- Average Pooling: Use when you want smoother features
- Global Pooling: Use before final classifier to reduce dimensions
Optimizing CNN Training: Techniques and Practices
Effective Strategies to Improve CNN Training
Training CNNs effectively requires several optimization techniques:
- Batch Normalization: Normalize activations to stabilize training
- Dropout: Randomly disable neurons to prevent overfitting
- Learning Rate Scheduling: Adjust learning rate during training
- Early Stopping: Stop training when validation performance plateaus
- Data Augmentation: Increase dataset diversity
- Weight Initialization: Start with good initial weights
- Gradient Clipping: Prevent exploding gradients
Batch Normalization and Dropout Techniques
Batch Normalization:
- Normalizes layer inputs to have zero mean and unit variance
- Allows higher learning rates
- Reduces internal covariate shift
- Acts as regularization
from tensorflow.keras import layers, Model
def conv_block_with_bn(filters, kernel_size=3):
"""Convolutional block with batch normalization"""
block = [
layers.Conv2D(filters, kernel_size, padding='same', use_bias=False),
layers.BatchNormalization(),
layers.ReLU()
]
return block
# Model with batch normalization
inputs = layers.Input(shape=(224, 224, 3))
x = inputs
for filters in [32, 64, 128]:
for layer in conv_block_with_bn(filters):
x = layer(x)
x = layers.MaxPooling2D(2)(x)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(128, activation='relu')(x)
outputs = layers.Dense(10, activation='softmax')(x)
model = Model(inputs, outputs)
Dropout:
- Randomly sets fraction of input units to 0 during training
- Prevents overfitting
- Forces network to learn redundant representations
# Model with dropout
model = tf.keras.Sequential([
layers.Conv2D(32, 3, activation='relu', input_shape=(224, 224, 3)),
layers.BatchNormalization(),
layers.MaxPooling2D(2),
layers.Dropout(0.25), # 25% dropout
layers.Conv2D(64, 3, activation='relu'),
layers.BatchNormalization(),
layers.MaxPooling2D(2),
layers.Dropout(0.25),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5), # Higher dropout in FC layers
layers.Dense(10, activation='softmax')
])
Using Callback Functions and Checkpoints
Callbacks are functions called during training to monitor and modify behavior.
from tensorflow.keras.callbacks import (
EarlyStopping,
ReduceLROnPlateau,
ModelCheckpoint,
TensorBoard,
CSVLogger
)
# Comprehensive callback setup
callbacks = [
# Early stopping: stop if no improvement
EarlyStopping(
monitor='val_loss',
patience=10, # Wait 10 epochs
restore_best_weights=True,
verbose=1
),
# Reduce learning rate when plateau
ReduceLROnPlateau(
monitor='val_loss',
factor=0.5, # Reduce LR by half
patience=5,
min_lr=1e-7,
verbose=1
),
# Save best model
ModelCheckpoint(
'best_model.h5',
monitor='val_accuracy',
save_best_only=True,
verbose=1
),
# TensorBoard logging
TensorBoard(
log_dir='./logs',
histogram_freq=1,
write_graph=True
),
# CSV logger
CSVLogger('training.log')
]
# Train with callbacks
history = model.fit(
x_train, y_train,
validation_data=(x_val, y_val),
epochs=100,
batch_size=32,
callbacks=callbacks,
verbose=1
)
Best Practices for Training:
# Complete training setup
def train_cnn_optimized(model, x_train, y_train, x_val, y_val):
"""Optimized CNN training with all best practices"""
# Compile with appropriate optimizer
model.compile(
optimizer=tf.keras.optimizers.Adam(
learning_rate=0.001,
beta_1=0.9,
beta_2=0.999
),
loss='sparse_categorical_crossentropy',
metrics=['accuracy', 'top_k_categorical_accuracy']
)
# Callbacks
callbacks = [
EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True),
ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5),
ModelCheckpoint('best_model.h5', monitor='val_accuracy', save_best_only=True)
]
# Train
history = model.fit(
x_train, y_train,
validation_data=(x_val, y_val),
epochs=100,
batch_size=32,
callbacks=callbacks,
verbose=1
)
return history, model
# Usage
# history, trained_model = train_cnn_optimized(model, x_train, y_train, x_val, y_val)
CNN Architectures
Case Study: LeNet Architecture (1998)
LeNet-5 (1998) by Yann LeCun was one of the first successful CNNs, designed for handwritten digit recognition.
Historical Significance:
- First practical CNN for real-world applications
- Used for reading zip codes and checks
- Established CNN architecture patterns still used today
Architecture Design:
- Convolutional Layers: Feature extraction
- Subsampling (Pooling): Dimension reduction
- Fully Connected Layers: Classification
Key Innovations:
- Convolutional layers for translation invariance
- Subsampling for dimension reduction
- End-to-end training
In-Depth Review of LeNet's Design:
from tensorflow.keras import layers, Model
import tensorflow as tf
def create_lenet5_original():
"""
Original LeNet-5 architecture (1998)
Input: 32x32 grayscale images
Output: 10 classes (digits 0-9)
"""
model = tf.keras.Sequential([
# Layer 1: Convolutional
layers.Conv2D(6, (5, 5), activation='tanh', input_shape=(32, 32, 1), name='C1'),
# Layer 2: Average Pooling (Subsampling)
layers.AveragePooling2D((2, 2), name='S2'),
# Layer 3: Convolutional
layers.Conv2D(16, (5, 5), activation='tanh', name='C3'),
# Layer 4: Average Pooling
layers.AveragePooling2D((2, 2), name='S4'),
# Flatten
layers.Flatten(name='Flatten'),
# Layer 5: Fully Connected
layers.Dense(120, activation='tanh', name='F5'),
# Layer 6: Fully Connected
layers.Dense(84, activation='tanh', name='F6'),
# Output Layer
layers.Dense(10, activation='softmax', name='Output')
], name='LeNet-5')
return model
# Create and inspect model
lenet5 = create_lenet5_original()
lenet5.summary()
# Visualize architecture
tf.keras.utils.plot_model(lenet5, to_file='lenet5.png', show_shapes=True)
Step-by-Step Walkthrough:
# Understanding each layer's output
def analyze_lenet5():
"""Analyze LeNet-5 layer by layer"""
# Input: 32x32x1
input_shape = (32, 32, 1)
print(f"Input: {input_shape}")
# C1: Conv2D(6, 5x5) on 32x32 → 28x28x6
# Formula: (32 - 5 + 0) / 1 + 1 = 28
print(f"C1 (Conv): 32x32x1 → 28x28x6")
# S2: AvgPool(2x2) on 28x28 → 14x14x6
print(f"S2 (Pool): 28x28x6 → 14x14x6")
# C3: Conv2D(16, 5x5) on 14x14 → 10x10x16
print(f"C3 (Conv): 14x14x6 → 10x10x16")
# S4: AvgPool(2x2) on 10x10 → 5x5x16
print(f"S4 (Pool): 10x10x16 → 5x5x16")
# Flatten: 5x5x16 → 400
print(f"Flatten: 5x5x16 → 400")
# F5: Dense(120) → 120
print(f"F5 (FC): 400 → 120")
# F6: Dense(84) → 84
print(f"F6 (FC): 120 → 84")
# Output: Dense(10) → 10
print(f"Output: 84 → 10")
analyze_lenet5()
Modern LeNet Implementation (with ReLU and improvements):
def create_lenet5_modern():
"""Modern version of LeNet-5 with ReLU and batch normalization"""
model = tf.keras.Sequential([
layers.Conv2D(6, (5, 5), activation='relu', input_shape=(32, 32, 1)),
layers.BatchNormalization(),
layers.AveragePooling2D((2, 2)),
layers.Conv2D(16, (5, 5), activation='relu'),
layers.BatchNormalization(),
layers.AveragePooling2D((2, 2)),
layers.Flatten(),
layers.Dense(120, activation='relu'),
layers.Dropout(0.5),
layers.Dense(84, activation='relu'),
layers.Dropout(0.5),
layers.Dense(10, activation='softmax')
])
return model
Impact of LeNet:
- Proved CNNs work for real-world problems
- Established architecture patterns
- Inspired future architectures (AlexNet, VGG, ResNet)
ImageNet and Large-Scale Recognition
Diving into Large Scale Image Recognition with ImageNet
ImageNet is a large-scale image database that revolutionized computer vision.
ImageNet Dataset:
- Size: Over 14 million images
- Classes: 20,000+ categories
- ImageNet Challenge: Annual competition (2010-2017)
- Impact: Drove CNN development
Challenges and Solutions:
Scale: Millions of images
- Solution: Efficient data loading, distributed training
Diversity: Thousands of categories
- Solution: Transfer learning, hierarchical classification
Computational Resources: Training deep networks
- Solution: GPUs, distributed training, model optimization
Key Achievements from ImageNet Competitions:
| Year | Model | Top-5 Error | Innovation |
|---|---|---|---|
| 2010 | - | 28.2% | Baseline |
| 2012 | AlexNet | 15.3% | Deep CNN breakthrough |
| 2013 | ZFNet | 11.7% | Visualization techniques |
| 2014 | VGG | 7.3% | Very deep networks |
| 2014 | GoogLeNet | 6.7% | Inception modules |
| 2015 | ResNet | 3.6% | Residual connections |
| 2016 | - | 2.99% | Ensemble methods |
| 2017 | - | 2.25% | Final competition |
Lessons from ImageNet:
- Depth Matters: Deeper networks learn better features
- Data Matters: Large datasets enable better models
- Architecture Matters: Good design improves performance
- Transfer Learning: Pre-trained models useful for other tasks
Impact on Deep Learning Advancement:
- Proved deep learning works for complex vision tasks
- Established benchmarks for model evaluation
- Enabled transfer learning to other domains
- Drove GPU development and adoption
# Loading ImageNet pre-trained models
from tensorflow.keras.applications import (
VGG16, VGG19, ResNet50, ResNet101, ResNet152,
InceptionV3, Xception, MobileNetV2, EfficientNetB0
)
# All models trained on ImageNet
models = {
'VGG16': VGG16,
'VGG19': VGG19,
'ResNet50': ResNet50,
'ResNet101': ResNet101,
'InceptionV3': InceptionV3,
'Xception': Xception,
'MobileNetV2': MobileNetV2,
'EfficientNetB0': EfficientNetB0
}
# Compare model sizes and ImageNet performance
for name, model_class in models.items():
model = model_class(weights='imagenet', include_top=False)
params = model.count_params()
print(f"{name:15s}: {params:>12,} parameters")
Breakthrough with AlexNet: Architecture and Innovations
AlexNet (2012) by Alex Krizhevsky et al. won ImageNet 2012 and sparked the deep learning revolution.
Key Innovations:
- Deep Architecture: 8 layers (5 conv + 3 FC)
- ReLU Activation: Replaced tanh/sigmoid
- Dropout: Regularization technique
- Data Augmentation: Increased dataset diversity
- GPU Training: Used GPUs for faster training
Architecture Analysis:
def create_alexnet():
"""
AlexNet architecture (2012)
Input: 224x224x3 RGB images
Output: 1000 classes (ImageNet)
"""
model = tf.keras.Sequential([
# Conv Block 1
layers.Conv2D(96, (11, 11), strides=4, activation='relu', input_shape=(224, 224, 3)),
layers.MaxPooling2D((3, 3), strides=2),
layers.BatchNormalization(), # Added for stability (not in original)
# Conv Block 2
layers.Conv2D(256, (5, 5), padding='same', activation='relu'),
layers.MaxPooling2D((3, 3), strides=2),
layers.BatchNormalization(),
# Conv Block 3
layers.Conv2D(384, (3, 3), padding='same', activation='relu'),
# Conv Block 4
layers.Conv2D(384, (3, 3), padding='same', activation='relu'),
# Conv Block 5
layers.Conv2D(256, (3, 3), padding='same', activation='relu'),
layers.MaxPooling2D((3, 3), strides=2),
# Flatten
layers.Flatten(),
# FC Layers
layers.Dense(4096, activation='relu'),
layers.Dropout(0.5),
layers.Dense(4096, activation='relu'),
layers.Dropout(0.5),
layers.Dense(1000, activation='softmax') # ImageNet classes
], name='AlexNet')
return model
alexnet = create_alexnet()
alexnet.summary()
Key Innovations Introduced:
ReLU Activation Function:
- Faster training than tanh/sigmoid
- Addresses vanishing gradient problem
- Now standard in CNNs
Dropout Regularization:
- Prevents overfitting
- Randomly disables neurons during training
- Still widely used
Data Augmentation:
- Random crops, flips, color jittering
- Increases effective dataset size
- Essential for good performance
GPU Training:
- Made deep learning practical
- Enabled training on large datasets
- Accelerated research
AlexNet vs. Previous Models:
| Aspect | Previous | AlexNet | Improvement |
|---|---|---|---|
| Depth | 1-2 layers | 8 layers | Much deeper |
| Top-5 Error | 28.2% | 15.3% | ~46% reduction |
| Training Time | Weeks | Days | Much faster |
| Activation | Tanh/Sigmoid | ReLU | Better gradients |
Comparative Study:
# Compare AlexNet with simpler model
def simple_cnn():
"""Simple CNN for comparison"""
return tf.keras.Sequential([
layers.Conv2D(32, 3, activation='tanh', input_shape=(224, 224, 3)),
layers.MaxPooling2D(2),
layers.Flatten(),
layers.Dense(1000, activation='softmax')
])
simple = simple_cnn()
alexnet = create_alexnet()
print(f"Simple CNN parameters: {simple.count_params():,}")
print(f"AlexNet parameters: {alexnet.count_params():,}")
print(f"AlexNet is {alexnet.count_params() / simple.count_params():.1f}x larger")
Deep Dive into VGGNet Architecture
VGGNet (2014) by Simonyan & Zisserman introduced very deep networks with small filters.
Key Design Principles:
- Small Filters: Use 3x3 filters instead of larger ones
- Deep Networks: 16-19 layers
- Uniform Architecture: Consistent block structure
- Proven Effectiveness: Strong ImageNet performance
Why 3x3 Filters?
- Two 3x3 c 5x5 conv (same receptive field)
- More non-linearities (2 ReLUs vs 1)
- Fewer parameters: 2×(3²) = 18 vs 5² = 25
- Better feature learning
VGG Architecture Variants:
def vgg_block(filters, num_layers, x):
"""VGG block: multiple 3x3 convs followed by max pooling"""
for _ in range(num_layers):
x = layers.Conv2D(filters, (3, 3), padding='same', activation='relu')(x)
x = layers.MaxPooling2D((2, 2), strides=2)(x)
return x
def create_vgg16():
"""
VGG-16 architecture
16 layers: 13 conv + 3 FC
"""
inputs = layers.Input(shape=(224, 224, 3))
x = inputs
# Block 1: 2 convs, 64 filters
x = vgg_block(64, 2, x)
# Block 2: 2 convs, 128 filters
x = vgg_block(128, 2, x)
# Block 3: 3 convs, 256 filters
x = vgg_block(256, 3, x)
# Block 4: 3 convs, 512 filters
x = vgg_block(512, 3, x)
# Block 5: 3 convs, 512 filters
x = vgg_block(512, 3, x)
# Classifier
x = layers.Flatten()(x)
x = layers.Dense(4096, activation='relu')(x)
x = layers.Dropout(0.5)(x)
x = layers.Dense(4096, activation='relu')(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(1000, activation='softmax')(x)
model = Model(inputs, outputs, name='VGG16')
return model
def create_vgg19():
"""
VGG-19 architecture
19 layers: 16 conv + 3 FC
"""
inputs = layers.Input(shape=(224, 224, 3))
x = inputs
# Block 1: 2 convs
x = vgg_block(64, 2, x)
# Block 2: 2 convs
x = vgg_block(128, 2, x)
# Block 3: 4 convs (VGG19 has 4 here vs 3 in VGG16)
x = vgg_block(256, 4, x)
# Block 4: 4 convs
x = vgg_block(512, 4, x)
# Block 5: 4 convs
x = vgg_block(512, 4, x)
# Classifier (same as VGG16)
x = layers.Flatten()(x)
x = layers.Dense(4096, activation='relu')(x)
x = layers.Dropout(0.5)(x)
x = layers.Dense(4096, activation='relu')(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(1000, activation='softmax')(x)
model = Model(inputs, outputs, name='VGG19')
return model
# Create models
vgg16 = create_vgg16()
vgg19 = create_vgg19()
print(f"VGG16 parameters: {vgg16.count_params():,}")
print(f"VGG19 parameters: {vgg19.count_params():,}")
Hands-on Session: Building VGGNet from Scratch
# Simplified VGG for CIFAR-10 (smaller input)
def create_vgg_cifar10():
"""VGG-style network for CIFAR-10 (32x32 images)"""
model = tf.keras.Sequential([
# Block 1
layers.Conv2D(64, 3, padding='same', activation='relu', input_shape=(32, 32, 3)),
layers.Conv2D(64, 3, padding='same', activation='relu'),
layers.MaxPooling2D(2),
layers.BatchNormalization(),
# Block 2
layers.Conv2D(128, 3, padding='same', activation='relu'),
layers.Conv2D(128, 3, padding='same', activation='relu'),
layers.MaxPooling2D(2),
layers.BatchNormalization(),
# Block 3
layers.Conv2D(256, 3, padding='same', activation='relu'),
layers.Conv2D(256, 3, padding='same', activation='relu'),
layers.MaxPooling2D(2),
layers.BatchNormalization(),
# Classifier
layers.Flatten(),
layers.Dense(512, activation='relu'),
layers.Dropout(0.5),
layers.Dense(10, activation='softmax')
], name='VGG-CIFAR10')
return model
vgg_cifar = create_vgg_cifar10()
vgg_cifar.summary()
Scalability and Adaptability of VGGNet:
- Scalable: Easy to add more layers
- Adaptable: Works well for transfer learning
- Interpretable: Clear, uniform structure
- Proven: Strong baseline for many tasks
Discussion Points:
- VGG's uniform structure makes it easy to understand
- Small filters enable deeper networks
- High parameter count (138M) but good performance
- Still used as feature extractor in many applications
Introduction to Residual Networks (ResNet)
ResNet (2015) by He et al. introduced residual connections, enabling training of very deep networks (100+ layers).
The Problem ResNet Solved:
- Vanishing Gradients: Gradients become too small in deep networks
- Degradation Problem: Deeper networks perform worse than shallower ones
- Training Difficulty: Very deep networks were hard to train
The Solution: Residual Learning
Instead of learning H(x), learn the residual F(x) = H(x) - x, then H(x) = F(x) + x.
Why This Works:
- If identity mapping is optimal, network learns
F(x) = 0 - Easier to learn residual than full mapping
- Skip connections allow gradients to flow directly
ResNet Architecture and Variants:
def residual_block(x, filters, kernel_size=3, stride=1, use_1x1=False):
"""
ResNet residual block
"""
shortcut = x
# Main path
x = layers.Conv2D(filters, kernel_size, strides=stride, padding='same', use_bias=False)(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.Conv2D(filters, kernel_size, padding='same', use_bias=False)(x)
x = layers.BatchNormalization()(x)
# Skip connection (1x1 conv if dimensions change)
if use_1x1 or stride > 1:
shortcut = layers.Conv2D(filters, 1, strides=stride, use_bias=False)(shortcut)
shortcut = layers.BatchNormalization()(shortcut)
# Add skip connection
x = layers.Add()([x, shortcut])
x = layers.ReLU()(x)
return x
def create_resnet18():
"""ResNet-18 architecture"""
inputs = layers.Input(shape=(224, 224, 3))
x = layers.Conv2D(64, 7, strides=2, padding='same', use_bias=False)(inputs)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
x = layers.MaxPooling2D(3, strides=2, padding='same')(x)
# Layer 1: 2 blocks, 64 filters
for _ in range(2):
x = residual_block(x, 64)
# Layer 2: 2 blocks, 128 filters
x = residual_block(x, 128, stride=2, use_1x1=True)
for _ in range(1):
x = residual_block(x, 128)
# Layer 3: 2 blocks, 256 filters
x = residual_block(x, 256, stride=2, use_1x1=True)
for _ in range(1):
x = residual_block(x, 256)
# Layer 4: 2 blocks, 512 filters
x = residual_block(x, 512, stride=2, use_1x1=True)
for _ in range(1):
x = residual_block(x, 512)
# Classifier
x = layers.GlobalAveragePooling2D()(x)
outputs = layers.Dense(1000, activation='softmax')(x)
model = Model(inputs, outputs, name='ResNet18')
return model
resnet18 = create_resnet18()
resnet18.summary()
ResNet Variants:
- ResNet-18: 18 layers
- ResNet-34: 34 layers
- ResNet-50: 50 layers (uses bottleneck blocks)
- ResNet-101: 101 layers
- ResNet-152: 152 layers
Practical Applications and Performance Analysis:
- ImageNet: 3.6% top-5 error (2015 winner)
- Transfer Learning: Excellent feature extractor
- Depth: Successfully trains 100+ layer networks
- Efficiency: Better accuracy with fewer parameters than VGG
Key Takeaways:
- Residual connections enable very deep networks
- Residual/skip connections ease deep-net optimization (degradation); vanishing gradients are a related but separate story
- ResNet is foundation for many modern architectures
- Still widely used in production systems
Transfer Learning
Why Transfer Learning?
- Limited Data: Pre-trained models learned from millions of images
- Faster Training: Start from good weights
- Better Performance: Often outperforms training from scratch
- Time Saving: Don't need to train from scratch
Using Pre-trained Models
from tensorflow.keras.applications import VGG16, ResNet50, MobileNetV2
# Method 1: Feature Extraction (Freeze base)
base_model = VGG16(
weights='imagenet',
include_top=False,
input_shape=(224, 224, 3)
)
base_model.trainable = False # Freeze all layers
# Add custom classifier
model = keras.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(10, activation='softmax')
])
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# Method 2: Fine-tuning (Unfreeze some layers)
base_model.trainable = True
# Freeze early layers, fine-tune later layers
for layer in base_model.layers[:-4]:
layer.trainable = False
# Use lower learning rate for fine-tuning
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=1e-5),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
Available Pre-trained Models
| Model | Parameters | Top-1 Accuracy | Use Case |
|---|---|---|---|
| VGG16 | 138M | 71.3% | Good baseline |
| ResNet50 | 25M | 76.0% | Deep, accurate |
| MobileNetV2 | 3.4M | 71.3% | Mobile/edge devices |
| EfficientNet | 5.3M | 77.1% | Strong accuracy/size trade-off in the original paper (example numbers) |
| InceptionV3 | 23M | 78.0% | High accuracy |
# Compare different architectures
models_to_try = {
'VGG16': VGG16,
'ResNet50': ResNet50,
'MobileNetV2': MobileNetV2
}
for name, model_class in models_to_try.items():
base_model = model_class(
weights='imagenet',
include_top=False,
input_shape=(224, 224, 3)
)
base_model.trainable = False
model = keras.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(10, activation='softmax')
])
print(f"{name}: {model.count_params():,} parameters")
Data Augmentation
Why Data Augmentation?
- More Training Data: Artificially increase dataset size
- Reduce Overfitting: Model sees more variations
- Better Generalization: Model learns robust features
- Handle Imbalance: Augment minority classes more
Image Augmentation Techniques
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
import numpy as np
# Comprehensive augmentation
datagen = ImageDataGenerator(
rotation_range=20, # Rotate images up to 20 degrees
width_shift_range=0.2, # Shift horizontally by 20%
height_shift_range=0.2, # Shift vertically by 20%
shear_range=0.2, # Apply shearing transformation
zoom_range=0.2, # Zoom in/out by 20%
horizontal_flip=True, # Flip horizontally
vertical_flip=False, # Don't flip vertically (for most images)
fill_mode='nearest', # Fill pixels outside boundaries
brightness_range=[0.8, 1.2], # Adjust brightness
rescale=1./255 # Normalize pixel values
)
# Visualize augmented images
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
axes = axes.flatten()
# Original image
original = x_train[0]
axes[0].imshow(original, cmap='gray')
axes[0].set_title('Original', fontsize=10, fontweight='bold')
axes[0].axis('off')
# Generate augmented images
augmented = datagen.flow(np.expand_dims(original, 0), batch_size=1)
for i in range(1, 10):
aug_img = next(augmented)[0].squeeze()
axes[i].imshow(aug_img, cmap='gray')
axes[i].set_title(f'Augmented {i}', fontsize=10, fontweight='bold')
axes[i].axis('off')
plt.suptitle('Data Augmentation Examples', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
Using Augmentation in Training
# Method 1: Using ImageDataGenerator
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
horizontal_flip=True,
validation_split=0.2
)
train_generator = train_datagen.flow(
x_train, y_train,
batch_size=32,
subset='training'
)
val_generator = train_datagen.flow(
x_train, y_train,
batch_size=32,
subset='validation'
)
# Train with generator
history = model.fit(
train_generator,
epochs=10,
validation_data=val_generator
)
# Method 2: Using layers (Keras 3+)
augmentati>
layers.RandomRotation(0.1),
layers.RandomTranslation(0.1, 0.1),
layers.RandomFlip('horizontal'),
layers.RandomZoom(0.1)
])
# Add to model
model = keras.Sequential([
layers.Input(shape=(28, 28, 1)),
augmentation_layers,
# ... rest of model
])
Advanced Augmentation with Albumentations
try:
import albumentations as A
from albumentations.pytorch import ToTensorV2
# More advanced augmentations
transform = A.Compose([
A.Rotate(limit=20, p=0.5),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.2),
A.GaussNoise(var_limit=(10.0, 50.0), p=0.3),
A.CoarseDropout(max_holes=8, max_height=8, max_width=8, p=0.3),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
])
# Apply to image
augmented = transform(image=image)['image']
except ImportError:
print("Install albumentations: pip install albumentations")
Practice Exercises
Exercise 1: CIFAR-10 Classification
Task: Build CNN to classify CIFAR-10 images with data augmentation.
Solution:
from tensorflow.keras.datasets import cifar10
# Load data
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
# Build improved CNN
model = keras.Sequential([
# Data augmentation
layers.RandomRotation(0.1, input_shape=(32, 32, 3)),
layers.RandomTranslation(0.1, 0.1),
layers.RandomFlip('horizontal'),
# Convolutional blocks
layers.Conv2D(32, (3, 3), activation='relu', padding='same'),
layers.BatchNormalization(),
layers.Conv2D(32, (3, 3), activation='relu', padding='same'),
layers.MaxPooling2D((2, 2)),
layers.Dropout(0.25),
layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
layers.BatchNormalization(),
layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
layers.MaxPooling2D((2, 2)),
layers.Dropout(0.25),
layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
layers.BatchNormalization(),
layers.Dropout(0.25),
# Classifier
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(10, activation='softmax')
])
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# Train with callbacks
callbacks = [
keras.callbacks.EarlyStopping(patience=5, restore_best_weights=True),
keras.callbacks.ReduceLROnPlateau(patience=3, factor=0.5)
]
history = model.fit(
x_train, y_train,
epochs=50,
validation_split=0.2,
batch_size=128,
callbacks=callbacks
)
# Evaluate
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f"Test accuracy: {test_acc:.4f}")
Exercise 2: Transfer Learning Project
Task: Use pre-trained model for custom image classification.
Solution:
# Load pre-trained ResNet50
base_model = ResNet50(
weights='imagenet',
include_top=False,
input_shape=(224, 224, 3)
)
# Freeze base
base_model.trainable = False
# Add classifier
model = keras.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(256, activation='relu'),
layers.Dropout(0.5),
layers.Dense(num_classes, activation='softmax')
])
# Train feature extraction phase
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_data, epochs=10)
# Fine-tuning phase
base_model.trainable = True
for layer in base_model.layers[:-10]:
layer.trainable = False
model.compile(optimizer=keras.optimizers.Adam(learning_rate=1e-5),
loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_data, epochs=5)
Key Takeaways
- CNNs: Best for image data - leverage spatial structure
- Architecture Matters: Deeper networks learn more complex features
- Transfer Learning: Use pre-trained models for better performance
- Data Augmentation: Essential for preventing overfitting
- Batch Normalization: Stabilizes training in deep networks
- Pooling: Reduces spatial dimensions and parameters
- Practice: Work with real image datasets (MNIST, CIFAR-10, ImageNet)
Best Practices
CNN Design
- Start with simple architecture
- Use small filters (3x3) in multiple layers
- Add batch normalization after conv layers
- Use dropout to prevent overfitting
- Use data augmentation
Training
- Use appropriate learning rate (start with 0.001)
- Use learning rate scheduling
- Monitor training and validation metrics
- Use early stopping
- Save best model checkpoints
Transfer Learning
- Start with feature extraction (freeze base)
- Fine-tune with lower learning rate
- Unfreeze only top layers initially
- Use appropriate pre-trained model for your task
Resources and Further Learning
Books
"Deep Learning" - Ian Goodfellow, Yoshua Bengio & Aaron Courville
- Free Online
- Chapter 9: Convolutional Networks
"Computer Vision: Algorithms and Applications" - Richard Szeliski
- Free Online
- Comprehensive computer vision textbook
"Programming Computer Vision with Python" - Jan Erik Solem
- Practical guide with code examples
Important Papers
- "ImageNet Classification with Deep Convolutional Neural Networks" - Krizhevsky et al., 2012 (AlexNet)
- "Very Deep Convolutional Networks for Large-Scale Image Recognition" - Simonyan & Zisserman, 2014 (VGG)
- "Deep Residual Learning for Image Recognition" - He et al., 2015 (ResNet)
- "Going Deeper with Convolutions" - Szegedy et al., 2014 (GoogLeNet/Inception)
- "EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks" - Tan & Le, 2019
- "Rich feature hierarchies for accurate object detection" - Girshick et al., 2013 (R-CNN)
- "You Only Look Once: Unified, Real-Time Object Detection" - Redmon et al., 2015 (YOLO)
- "Fully Convolutional Networks for Semantic Segmentation" - Long et al., 2014
Online Courses
CS231n: Convolutional Neural Networks for Visual Recognition - Stanford
- Course Website
- Comprehensive deep learning for computer vision
Deep Learning Specialization - Coursera (DeepLearning.AI)
- Course Link
- Course 4: Convolutional Neural Networks
Fast.ai: Practical Deep Learning for Coders
- Course Website
- Practical computer vision with deep learning
Datasets
Image Classification:
Object Detection:
Semantic Segmentation:
Face Recognition:
Tools and Libraries
OpenCV: Computer vision library
- Documentation
- Image processing, feature detection
TensorFlow/Keras: Deep learning framework
- Documentation
- Pre-trained models, transfer learning
PyTorch: Deep learning framework
- Documentation
- torchvision for computer vision
Pillow (PIL): Image processing
- Documentation
- Image manipulation
albumentations: Image augmentation
- Documentation
- Advanced data augmentation
ultralytics (YOLO): Object detection
- Documentation
- YOLO implementation
detectron2: Object detection and segmentation
- Documentation
- Facebook's computer vision library
Next Steps
- Practice with image datasets (MNIST, CIFAR-10, custom)
- Experiment with different architectures
- Try transfer learning on your own dataset
- Learn about advanced architectures (ResNet, EfficientNet)
- Explore object detection and segmentation
- Move to 12-natural-language-processing
Try next: Train a small CNN on CIFAR-10 or Fashion-MNIST. Plot train vs val loss before you reach for transfer learning.
Recall ::
Why are convolutions a natural fit for images?
They share local filters across space, capturing translation-tolerant patterns with far fewer parameters than dense layers.