Session 4: CNN Hands-on Lab

Building and Training CNNs for EO Image Classification

Instructor

CoPhil Advanced Training Program

Date

November 17, 2025

Session 4: CNN Hands-on Lab

Building and Training CNNs for EO Image Classification

From theory to implementation with TensorFlow and PyTorch

Session Overview

Duration: 2.5 hours | Type: Intensive Hands-On Lab | Difficulty: Intermediate-Advanced


This session transforms CNN theory from Session 3 into working code. You’ll build, train, and evaluate real deep learning models for Earth Observation, achieving significantly higher accuracy than the Random Forest models from Sessions 1-2.

Presentation Slides


ImportantPrerequisites

Technical Requirements: - ✓ Complete Session 3 (CNN theory and concepts) - ✓ Understand convolution, pooling, activation functions - ✓ Python programming proficiency (NumPy, pandas basics) - ✓ Google Colab account with GPU access - ✓ Stable internet connection (for dataset downloads)

Conceptual Understanding: - ✓ Know difference between traditional ML and deep learning - ✓ Understand supervised learning workflow - ✓ Familiar with classification metrics (accuracy, confusion matrix) - ✓ Basic understanding of gradient descent optimization


What You’ll Accomplish

TipLearning Outcomes

After completing this session, you will be able to:

1. Implement CNNs in TensorFlow/Keras

  • Set up GPU-accelerated deep learning environment
  • Load and preprocess Earth observation datasets
  • Build CNN architectures layer-by-layer from scratch
  • Compile models with appropriate loss functions and optimizers
  • Train models with advanced callbacks (early stopping, checkpointing)

2. Work with EuroSAT Benchmark Dataset

  • Download and prepare standardized EO dataset
  • Understand 10-class land use classification task
  • Create efficient data pipelines with tf.data.Dataset
  • Apply data augmentation strategies for satellite imagery
  • Handle train/validation/test splits properly

3. Evaluate Deep Learning Models

  • Generate and interpret confusion matrices
  • Calculate per-class precision, recall, and F1-scores
  • Visualize training and validation curves
  • Analyze misclassifications and model errors
  • Compare CNN performance with Random Forest baseline

4. Apply Transfer Learning

  • Load pre-trained models (ResNet50, VGG16, EfficientNet)
  • Understand when and why to use pre-trained weights
  • Freeze and fine-tune network layers strategically
  • Adapt ImageNet models for Earth observation tasks
  • Compare from-scratch vs. transfer learning performance

5. Optimize and Debug Models

  • Prevent overfitting with dropout and regularization
  • Tune hyperparameters (learning rate, batch size, architecture)
  • Diagnose training issues (vanishing gradients, exploding loss)
  • Use callbacks for adaptive learning
  • Implement best practices for reproducibility

Why This Matters for Philippine EO

🎯 Accuracy Improvement

CNNs consistently outperform Random Forest: - EuroSAT: 92-98% vs 87-90% - Palawan: Estimated +5-10% accuracy - Critical for: DRR applications where errors cost lives

🌍 Spatial Context

CNNs understand image context: - RF: Treats pixels independently - CNN: Captures spatial patterns - Benefit: Better forest boundary detection, fewer misclassifications

⚡ Scalability

Once trained, CNNs scale efficiently: - Deployment: Fast inference on new imagery - Automation: Process entire Philippines nightly - Operations: PhilSA operational monitoring

🔄 Transfer Learning

Pre-trained models accelerate development: - Small Data: Works with limited labeled samples - Time Savings: Days vs weeks of training - Philippine Context: Adapt global models locally


Session Structure

Part A: Environment Setup & Data Preparation (30 minutes)

Setup Google Colab Environment - Configure GPU runtime for acceleration - Install required libraries (TensorFlow, Keras, auxiliary packages) - Verify GPU detection and availability - Set random seeds for reproducibility

Download EuroSAT Dataset - Understand the benchmark dataset (27,000 Sentinel-2 patches) - Automated download and extraction - Verify data integrity with checksums - Explore directory structure and file formats

Data Loading and Exploration - Load images and labels efficiently - Visualize sample images from each class - Analyze class distribution and balance - Calculate dataset statistics (mean, std)

Create Data Pipeline - Split data (70% train, 15% validation, 15% test) - Build tf.data.Dataset for efficient loading - Apply normalization and preprocessing - Implement data augmentation for training set - Configure batching, shuffling, and prefetching


Part B: Building CNN from Scratch (40 minutes)

Design CNN Architecture - Start with simple 3-block architecture - Understand layer choices and progression - Calculate output dimensions at each layer - Visualize network architecture diagram

Implementation Details:

# Example architecture (you'll implement in notebook)
Model: "eurosat_cnn"
_________________________________________________________________
Layer (type)                Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)          (None, 62, 62, 32)        896       
activation_1 (ReLU)        (None, 62, 62, 32)        0         
max_pooling2d_1            (None, 31, 31, 32)        0         
conv2d_2 (Conv2D)          (None, 29, 29, 64)        18,496    
activation_2 (ReLU)        (None, 29, 29, 64)        0         
max_pooling2d_2            (None, 14, 14, 64)        0         
conv2d_3 (Conv2D)          (None, 12, 12, 128)       73,856    
activation_3 (ReLU)        (None, 12, 12, 128)       0         
max_pooling2d_3            (None, 6, 6, 128)         0         
flatten (Flatten)          (None, 4608)              0         
dropout (Dropout)          (None, 4608)              0         
dense_1 (Dense)            (None, 128)               589,952   
activation_4 (ReLU)        (None, 128)               0         
dropout_2 (Dropout)        (None, 128)               0         
dense_2 (Dense)            (None, 10)                1,290     
activation_5 (Softmax)     (None, 10)                0         
=================================================================
Total params: 684,490
Trainable params: 684,490
Non-trainable params: 0

Key Architectural Decisions: - Filter progression (32→64→128): Capture features at multiple scales - 3×3 convolutions: Standard choice balancing receptive field and parameters - MaxPooling: Spatial dimension reduction and translation invariance - Dropout (0.3-0.5): Regularization to prevent overfitting - Dense layers: Final classification from learned features - Softmax output: Probability distribution over 10 classes

Model Compilation: - Loss function: Categorical cross-entropy (multi-class classification) - Optimizer: Adam (adaptive learning rate, generally robust) - Metrics: Accuracy, top-3 accuracy - Learning rate: Start with 0.001 (default), tune if needed


Part C: U-Net for Semantic Segmentation (60 minutes)

Configure Training Callbacks

EarlyStopping

Stop training when validation loss stops improving:

EarlyStopping(
    monitor='val_loss',
    patience=5,
    restore_best_weights=True
)

Prevents overfitting, saves time

ModelCheckpoint

Save best model during training:

ModelCheckpoint(
    'best_model.h5',
    monitor='val_accuracy',
    save_best_only=True
)

Preserves optimal weights

ReduceLROnPlateau

Lower learning rate when stuck:

ReduceLROnPlateau(
    monitor='val_loss',
    factor=0.5,
    patience=3
)

Helps escape local minima

TensorBoard

Real-time training visualization:

TensorBoard(
    log_dir='./logs'
)

Monitor metrics live

Execute Training - Train for 20-30 epochs (likely stops early) - Monitor training/validation metrics in real-time - Observe learning curves for overfitting signs - Track GPU utilization and training speed

Interpret Learning Curves

Healthy Training: - Train loss decreases steadily - Validation loss decreases, plateaus - Small train-val gap (<5-10%) - Validation accuracy plateaus at high value

Overfitting Signs: - Train accuracy → 100%, val accuracy plateaus low - Large train-val gap (>15-20%) - Validation loss increases while train loss decreases - Solution: More dropout, stronger regularization, more data

Underfitting Signs: - Both train and val accuracy low - Loss plateaus at high value - No improvement with more epochs - Solution: More complex model, lower regularization, train longer


Part D: Comprehensive Evaluation (30 minutes)

Test Set Performance - Load best saved model - Evaluate on held-out test set - Calculate final accuracy and loss - Compare with validation performance

Confusion Matrix Analysis

Generate and visualize 10×10 confusion matrix:

True ↓ / Pred → AnnualCrop Forest Herbaceous Highway
AnnualCrop 450 12 8 2
Forest 5 492 3 0
Herbaceous 18 7 441 1

Insights from Confusion: - Diagonal values: Correct classifications (darker = better) - Off-diagonal: Common confusions - Typical EO confusions: - AnnualCrop ↔︎ Herbaceous (similar vegetation) - Industrial ↔︎ Highway (both gray infrastructure) - Forest ↔︎ PermanentCrop (tree canopies)

Per-Class Metrics

              precision    recall  f1-score   support

  AnnualCrop       0.93      0.94      0.93       478
      Forest       0.96      0.97      0.97       507
  Herbaceous       0.91      0.92      0.92       481
     Highway       0.94      0.92      0.93       453
  Industrial       0.89      0.87      0.88       461
     Pasture       0.90      0.91      0.91       489
PermanentCrop      0.92      0.91      0.92       471
 Residential       0.95      0.96      0.95       498
       River       0.98      0.97      0.98       502
     SeaLake       0.97      0.98      0.98       510

    accuracy                           0.94      4850
   macro avg       0.94      0.94      0.94      4850
weighted avg       0.94      0.94      0.94      4850

Error Analysis - Identify most confused class pairs - Visualize misclassified examples - Understand model failure modes - Suggest improvements


Part E: Transfer Learning (20 minutes)

Why Transfer Learning for EO?

NoteTransfer Learning Benefits

ImageNet Pre-training Advantages: - Low-level features transfer: Edges, textures, colors are universal - Reduced training time: 80-90% faster convergence - Better with limited data: Works with 100s of samples vs 1000s - Higher accuracy: +2-5% typical improvement - Regularization effect: Pre-trained weights prevent overfitting

When to Use: - Limited labeled training data (<5000 samples) - Similar task to ImageNet (object recognition) - Time/compute constraints - Need strong baseline quickly

When NOT to Use: - Very different from natural images (SAR, hyperspectral) - Abundant labeled data (>50K samples) - Highly specialized task

Load Pre-trained Model

from tensorflow.keras.applications import ResNet50

# Load ResNet50 with ImageNet weights
base_model = ResNet50(
    include_top=False,  # Exclude classification head
    weights='imagenet',  # Use pre-trained weights
    input_shape=(64, 64, 3),
    pooling='avg'  # Global average pooling
)

Fine-tuning Strategy

Option 1: Freeze All Layers (Feature Extraction)

# Freeze all base model layers
base_model.trainable = False

# Add custom classification head
model = Sequential([
    base_model,
    Dense(256, activation='relu'),
    Dropout(0.5),
    Dense(10, activation='softmax')
])

Fast training, use when data is very limited

Option 2: Freeze Early, Train Late (Partial Fine-tuning)

# Freeze first 80% of layers
for layer in base_model.layers[:int(0.8 * len(base_model.layers))]:
    layer.trainable = False

# Fine-tune top layers + custom head

Balanced approach, best for moderate data

Option 3: Full Fine-tuning

# All layers trainable
base_model.trainable = True

# Use lower learning rate (0.0001 instead of 0.001)
optimizer = Adam(learning_rate=1e-4)

Slowest, use when data is abundant

Compare Results:

Approach Train Time Test Accuracy Notes
From Scratch 25 min 93.2% Baseline
Feature Extraction 8 min 94.1% Fast, good boost
Partial Fine-tuning 15 min 95.3% Best balance
Full Fine-tuning 30 min 95.8% Marginal gain

Part F: Philippine EO Applications (10 minutes)

Adapting CNNs for Philippine Contexts

Multi-spectral Considerations

Challenge: Sentinel-2 has 13 bands, ResNet expects 3

Solutions: 1. Band selection: Use only RGB (B4, B3, B2) 2. Band combinations: NIR-Red-Green false color 3. PCA: Reduce 13→3 dimensions 4. Architecture modification: Change input layer to accept 13 channels

Recommendation for Palawan: - Start with RGB for transfer learning - Train custom CNN with all bands for production

Scaling to Operational Use

PhilSA Production Pipeline: 1. Training: Palawan pilot (this session) 2. Validation: Other provinces 3. Deployment: Nationwide monitoring 4. Updates: Retrain quarterly

Computational Needs: - Training: Cloud GPU (Colab, AWS, Azure) - Inference: Can run on CPU for deployment - Storage: Model weights ~50-200 MB

CNN for Disaster Response

Typhoon Damage Assessment: - Input: Pre/post imagery pairs - Task: Binary (damaged/not damaged) - Architecture: Siamese network or stacked CNN - Speed: Process 1000 km² in hours

Flood Extent Mapping: - Advance to Day 3: U-Net for pixel-level segmentation - Real-time: CNN classification during event - Integration: Feed into NOAH or Project DOST systems

Accuracy Requirements

Application-Specific Needs: - Land cover monitoring: 85-90% sufficient - Forest law enforcement: 95%+ required - Disaster response: Speed > perfect accuracy - REDD+ MRV: High precision needed

Session 4 Achievement: 93-96% on EuroSAT


Hands-On Notebook

Tip📓 Interactive Jupyter Notebook

The complete hands-on lab is available as an executable Jupyter notebook:

Student Version (with exercises and TODOs):
session4_cnn_classification_STUDENT.ipynb

Google Colab Direct Link:
Open In Colab

What’s Included: - Complete environment setup (TensorFlow, GPU config) - EuroSAT download and preparation scripts - CNN architecture implementation (from-scratch and transfer learning) - Training loops with callbacks - Comprehensive evaluation code - Visualization functions - Interactive exercises

Estimated Execution Time: - Setup: 5 minutes - Data download: 3-5 minutes (90 MB) - Training from scratch: 15-20 minutes (GPU) - Transfer learning: 5-10 minutes (GPU) - Evaluation: 5 minutes - Total: ~30-40 minutes with GPU


Expected Outcomes

By the end of this session, you will have:

Completed Projects: - Working EuroSAT CNN classifier (93-96% accuracy) - Transfer learning model with ResNet50 (94-97% accuracy) - Comprehensive evaluation report with confusion matrix - Trained model weights saved for deployment

Technical Skills: - TensorFlow/Keras proficiency for CNNs - Data pipeline creation with tf.data - Training with advanced callbacks - Model evaluation and debugging - Transfer learning implementation

Practical Deliverables: - Executable Jupyter notebook (student version completed) - Trained models (.h5 or SavedModel format) - Learning curve plots - Confusion matrix visualizations - Classification report (precision, recall, F1)

Understanding: - When CNNs outperform traditional ML - How to choose architecture for EO tasks - Transfer learning for small datasets - Hyperparameter tuning strategies - Deployment considerations


Troubleshooting Guide

Common Issues & Solutions

WarningGPU Not Detected

Symptoms:

Physical devices: []
WARNING: No GPU available, using CPU

Solutions: 1. Colab: Runtime → Change runtime type → Hardware accelerator → GPU 2. Verify: Run tf.config.list_physical_devices('GPU') 3. Restart runtime: Runtime → Restart runtime 4. Check quota: Colab has usage limits (reconnect after 12 hours) 5. Alternative: Use Kaggle Notebooks (30 hrs/week GPU free)

WarningOut of Memory Error

Symptoms:

ResourceExhaustedError: OOM when allocating tensor

Solutions: 1. Reduce batch size: 32 → 16 → 8 2. Smaller model: Fewer filters (128→64), fewer layers 3. Mixed precision: tf.keras.mixed_precision.set_global_policy('mixed_float16') 4. Clear memory: tf.keras.backend.clear_session() 5. Restart runtime: Fresh start clears memory

WarningTraining Not Improving

Symptoms: - Accuracy stuck at ~10% (random guessing) - Loss = NaN or infinity - Very slow convergence

Solutions: 1. Learning rate too high: Reduce to 0.0001 2. Check data normalization: Images should be 0-1 or standardized 3. Verify labels: One-hot encoded correctly (shape: [batch, 10]) 4. Gradient clipping: optimizer = Adam(clipnorm=1.0) 5. Simpler model: Start with 2 conv blocks instead of 3 6. Check for bugs: Print shapes, verify data loading

WarningOverfitting Severely

Symptoms: - Train accuracy: 99%, Val accuracy: 75% - Validation loss increases while train loss decreases

Solutions: 1. More dropout: Increase from 0.3 to 0.5 2. Data augmentation: Add rotation, flip, zoom 3. L2 regularization: Conv2D(..., kernel_regularizer=l2(0.001)) 4. Reduce model capacity: Fewer filters, fewer layers 5. Early stopping: Patience=3-5 epochs 6. More training data: Use full EuroSAT (27K images)

WarningDataset Download Fails

Symptoms:

HTTPError: 404 Not Found
Connection timeout

Solutions: 1. Use mirror site: TensorFlow Datasets, Kaggle 2. Manual download: Provide local copy 3. Check internet: Colab connectivity issues 4. Retry: wget with retries 5. Pre-downloaded: Load from Google Drive

WarningAugmentation Visualization Error

Symptoms:

TypeError: only integer scalar arrays can be converted to a scalar index
# OR
IndexError: invalid index to scalar variable

When: In the “Visualize Augmentation” cell when trying to display augmented images

Root Cause: The code uses class_names[sample_label.numpy()] but sample_label.numpy() returns a numpy scalar (e.g., numpy.int64(3)) which some Python versions don’t accept as a list index.

Solutions:

Quick Fix (Add one line):

# After: sample_image, sample_label = next(iter(ds_train))
# Add this line:
label_idx = int(sample_label.numpy())

# Then change plt.suptitle line to use label_idx:
plt.suptitle(f'Data Augmentation Examples\nClass: {class_names[label_idx]}',
             fontsize=14, fontweight='bold')

Complete Fixed Cell:

# Show original vs augmented
sample_image, sample_label = next(iter(ds_train))

# Convert label to integer for indexing
label_idx = int(sample_label.numpy())  # <-- ADD THIS LINE

fig, axes = plt.subplots(2, 4, figsize=(14, 7))

# Original
axes[0, 0].imshow(sample_image.numpy())
axes[0, 0].set_title('Original', fontweight='bold')
axes[0, 0].axis('off')

# Augmented versions
for idx in range(1, 8):
    row = idx // 4
    col = idx % 4
    
    augmented = data_augmentation(tf.expand_dims(sample_image, 0), training=True)[0]
    axes[row, col].imshow(augmented.numpy())
    axes[row, col].set_title(f'Augmented {idx}', fontweight='bold')
    axes[row, col].axis('off')

# Use label_idx instead of sample_label.numpy()
plt.suptitle(f'Data Augmentation Examples\nClass: {class_names[label_idx]}',  # <-- CHANGED
             fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()

print("\n✓ Augmentation creates realistic variations")

Why This Works: - int() explicitly converts numpy scalar to Python integer - Python lists accept native integers as indices reliably - Prevents type mismatch between numpy and Python types

Note: A fixed version of the notebook is available: session4_cnn_classification_STUDENT_FIXED.ipynb


Key Concepts Recap

Convolutional Layers

What they do: - Apply learnable filters to extract features - Preserve spatial relationships - Translation invariant (feature detected anywhere in image)

Parameters: - filters: Number of feature maps (32, 64, 128, …) - kernel_size: Filter dimensions (3×3, 5×5, …) - strides: Step size (usually 1) - padding: ‘valid’ (shrinks) or ‘same’ (maintains size) - activation: Usually ReLU

Pooling Layers

Purpose: - Reduce spatial dimensions - Add translation invariance - Reduce parameters and computation

MaxPooling vs AveragePooling: - MaxPooling: Keeps strongest activation (most common) - AveragePooling: Smooths response - Typically 2×2 with stride 2 (halves dimensions)

Activation Functions

ReLU (Rectified Linear Unit): - f(x) = max(0, x) - Most common in hidden layers - Addresses vanishing gradient problem - Fast to compute

Softmax: - Converts logits to probabilities - Sum to 1.0 - Used in output layer for multi-class classification

Loss Functions

Categorical Cross-Entropy: - For multi-class classification (>2 classes) - Requires one-hot encoded labels - Formula: -Σ y_true * log(y_pred) - Penalizes confident wrong predictions heavily

Optimizers

Adam (Adaptive Moment Estimation): - Adaptive learning rate per parameter - Combines momentum + RMSprop - Generally robust, good default choice - Learning rate: 0.001 (default) to 0.0001 (fine-tuning)


Resources

Documentation & Tutorials

Pre-trained Models

Philippine EO Resources

Course Materials


Next Steps

NoteWhat Comes After Session 4?

Immediate: - Complete all exercises in the notebook - Experiment with different architectures - Try your own hyperparameter combinations - Apply to Philippine imagery (optional challenge)

Day 3 Preview: - U-Net for Semantic Segmentation: Pixel-level land cover classification - Flood Mapping Case Study: Central Luzon with Sentinel-1 SAR - Object Detection: Metro Manila building/settlement detection - Advanced Architectures: Deeper networks, attention mechanisms

Preparation for Day 3: - Ensure you understand CNN fundamentals - Be comfortable with TensorFlow/Keras syntax - Know how to debug training issues - Understand when to use different architectures

Preview Day 3 →


Assessment & Exercises

Formative Assessment (In-Notebook)

Challenge Exercises

Exercise 1: Architecture Design - Modify the CNN to have 4 convolutional blocks instead of 3 - Add batch normalization after each convolution - Compare training speed and final accuracy

Exercise 2: Hyperparameter Tuning - Test learning rates: [0.001, 0.0001, 0.00001] - Test batch sizes: [16, 32, 64] - Find optimal combination for fastest convergence

Exercise 3: Advanced Augmentation - Add RandomBrightness and RandomContrast - Implement mixup augmentation - Measure impact on validation accuracy

Exercise 4: Multi-spectral CNN (Advanced) - Load EuroSAT 13-band version - Modify input layer to accept all bands - Compare RGB vs multi-spectral performance

Exercise 5: Philippine Application (Capstone) - Apply trained model to Palawan Sentinel-2 patches - Compare predictions with Session 2 Random Forest - Identify areas where CNN performs better/worse


Timing Management: - Part A (Setup & Data): Can take 35-40 min if students unfamiliar with Colab GPU setup - Part B (Build CNN): Usually 40-45 min, architecture design is tricky for beginners - Part C (Training): 30-40 min including watching training live - Part D (Evaluation): 25-30 min, confusion matrix analysis takes time - Part E (Transfer Learning): 15-20 min, can be shortened if running behind - Part F (Philippine Context): 10-15 min discussion

Common Student Questions: - “Why is my GPU not detected?” → Check runtime type, may need reconnect - “Training is very slow on CPU” → Emphasize GPU requirement, show how to enable - “What batch size should I use?” → Start with 32, reduce if OOM errors - “Why transfer learning?” → Explain data efficiency, show time savings - “Can I use PyTorch instead?” → Yes, but maintain focus; TensorFlow for consistency

Teaching Tips: - Show failures: Demonstrate overfitting, then fix it - Visualize: Use TensorBoard or matplotlib for learning curves in real-time - Pause for training: Use 5-10 min training time for Q&A or breaks - Compare with RF: Reinforce why we learned both approaches

Technical Preparation: - Pre-download EuroSAT to Google Drive as backup - Test notebook 24 hours before session - Have pre-trained models ready in case training fails - Prepare troubleshooting guide printout - Test on both GPU and CPU for comparison

Extension Activities for Fast Finishers: - Implement ensemble of multiple CNNs - Try different pre-trained models (EfficientNet, MobileNet) - Explore GradCAM for visualization - Start Day 3 preview material


This session is part of the CoPhil 4-Day Advanced Training on AI/ML for Earth Observation, funded by the European Union under the Global Gateway initiative and delivered in partnership with PhilSA and DOST.