Building and Training CNNs for EO Image Classification
EU-Philippines CoPhil Programme
Duration: 2.5 hours
Type: Intensive hands‑on lab
Goal: Turn CNN theory into a working model
You will: - Prepare the EuroSAT dataset - Build a CNN from scratch in TensorFlow/Keras - Train with GPU acceleration in Colab - Evaluate with confusion matrix and F1 - Apply transfer learning (ResNet50)
Prerequisites: - Session 3 complete (CNN basics) - Colab account with GPU enabled - Python & NumPy fundamentals
Steps: 1. Runtime → Change runtime type → Hardware accelerator → GPU
2. Verify with !nvidia-smi
3. pip install tensorflow (if needed)
4. Set seeds for reproducibility
Preprocessing: Normalize to [0,1], one‑hot labels, split 70/15/15
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Dropout, Flatten
model = Sequential([
Conv2D(32, (3,3), activation='relu', input_shape=(64,64,3)),
MaxPooling2D(),
Conv2D(64, (3,3), activation='relu'),
MaxPooling2D(),
Conv2D(128,(3,3), activation='relu'),
MaxPooling2D(),
Flatten(), Dropout(0.5),
Dense(128, activation='relu'), Dropout(0.5),
Dense(10, activation='softmax')
])model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy','top_k_categorical_accuracy'])
cb = [
tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True),
tf.keras.callbacks.ModelCheckpoint('best_model.h5', save_best_only=True),
tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3)
]
history = model.fit(ds_train.batch(64).prefetch(2),
validation_data=ds_val.batch(64).prefetch(2),
epochs=30, callbacks=cb)Healthy curves: train↓, val↓ then plateau; small gap
Overfitting: large gap → add dropout/augmentation
import numpy as np, matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, classification_report
y_true, y_pred = [], []
for x, y in ds_test.batch(64):
p = model.predict(x, verbose=0).argmax(axis=1)
y_pred.extend(p); y_true.extend(y.numpy())
cm = confusion_matrix(y_true, y_pred)
print(classification_report(y_true, y_pred))Look for: - Which pairs are confused? (e.g., AnnualCrop vs Herbaceous)
- Per‑class precision/recall balance
from tensorflow.keras.applications import ResNet50
from tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout, Input
from tensorflow.keras.models import Model
base = ResNet50(include_top=False, weights='imagenet', input_shape=(64,64,3))
for l in base.layers[:int(0.8*len(base.layers))]:
l.trainable = False
x = GlobalAveragePooling2D()(base.output)
x = Dense(256, activation='relu')(x)
x = Dropout(0.5)(x)
out = Dense(10, activation='softmax')(x)
model = Model(base.input, out)Strategy: - Start with frozen base → quick convergence
- Unfreeze top blocks for small accuracy gains
| Approach | Train Time | Accuracy |
|---|---|---|
| From scratch | 15–25 min | 92–95% |
| Feature extraction | 5–10 min | 94–96% |
| Partial fine‑tune | 10–20 min | 95–97% |
Tip: Use the fastest path during live sessions, fine‑tune offline later
Next steps: move to Day 3 (U‑Net segmentation, flood mapping)
Notebook:
session4_cnn_classification_STUDENT.ipynb
Render slides (local):
DAY 2 - Session 4 | CNN Hands‑on Lab | 20-23 October 2025