Category 2: CNNs
The CNN category tests your ability to build convolutional neural networks for image classification. You must handle image preprocessing, build CNN architectures, apply transfer learning with pre-trained models, and use data augmentation to improve accuracy.
What the Exam Tests
You will receive image datasets (often loaded via ImageDataGenerator or tf.keras.utils.image_dataset_from_directory) and must build models that classify images above a target accuracy. Tasks range from simple binary classification (cats vs dogs) to multi-class problems with many categories.
Practice Model 1: CNN from Scratch
Build a convolutional neural network from scratch for image classification. This is the foundational pattern for all CNN exam tasks.
import tensorflow as tf
# ---- Load a dataset (Fashion MNIST as exam-like example) ----
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()
# ---- Preprocess ----
# Normalize pixel values to [0, 1]
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
# Add channel dimension: (28, 28) -> (28, 28, 1)
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]
print(f"Training shape: {x_train.shape}") # (60000, 28, 28, 1)
print(f"Classes: {len(set(y_train))}") # 10
# ---- Build CNN ----
model = tf.keras.Sequential([
# Block 1
tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
tf.keras.layers.MaxPooling2D((2, 2)),
# Block 2
tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
tf.keras.layers.MaxPooling2D((2, 2)),
# Block 3
tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
# Classification head
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(10, activation='softmax')
])
model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# ---- Train ----
history = model.fit(
x_train, y_train,
validation_data=(x_test, y_test),
epochs=20,
batch_size=64,
callbacks=[
tf.keras.callbacks.EarlyStopping(
monitor='val_accuracy', patience=3,
restore_best_weights=True
)
]
)
model.save('fashion_cnn.h5')
Practice Model 2: Transfer Learning
Transfer learning is the most powerful technique for the exam. Use a pre-trained model as a feature extractor and train only the classification head.
import tensorflow as tf
# ---- Transfer Learning with MobileNetV2 ----
# This pattern works for ANY image classification exam task
IMG_SIZE = 160 # MobileNetV2 expects at least 96x96
BATCH_SIZE = 32
NUM_CLASSES = 5 # Adjust based on exam task
# ---- Data augmentation (critical for small datasets) ----
data_augmentation = tf.keras.Sequential([
tf.keras.layers.RandomFlip("horizontal"),
tf.keras.layers.RandomRotation(0.2),
tf.keras.layers.RandomZoom(0.2),
])
# ---- Load pre-trained base model ----
base_model = tf.keras.applications.MobileNetV2(
input_shape=(IMG_SIZE, IMG_SIZE, 3),
include_top=False, # Remove classification head
weights='imagenet' # Pre-trained on ImageNet
)
# FREEZE the base model - do not train these weights
base_model.trainable = False
# ---- Build the complete model ----
model = tf.keras.Sequential([
# Preprocessing
tf.keras.layers.Resizing(IMG_SIZE, IMG_SIZE),
tf.keras.layers.Rescaling(1./255),
data_augmentation,
# Pre-trained feature extractor
base_model,
# Custom classification head
tf.keras.layers.GlobalAveragePooling2D(),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(NUM_CLASSES, activation='softmax')
])
model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# ---- Train (only the classification head trains) ----
# Replace x_train/y_train with your exam dataset
# history = model.fit(
# train_dataset,
# validation_data=val_dataset,
# epochs=10,
# callbacks=[
# tf.keras.callbacks.EarlyStopping(
# monitor='val_accuracy', patience=3,
# restore_best_weights=True
# )
# ]
# )
# ---- Optional: Fine-tune top layers of base model ----
# If accuracy is still too low, unfreeze the last 20 layers
base_model.trainable = True
for layer in base_model.layers[:-20]:
layer.trainable = False
# Re-compile with lower learning rate for fine-tuning
model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# Continue training (fine-tune)
# history_fine = model.fit(
# train_dataset,
# validation_data=val_dataset,
# epochs=10
# )
model.save('transfer_learning_cnn.h5')
Practice Model 3: ImageDataGenerator Pattern
The exam often provides data in directory format. This pattern loads images from directories with augmentation.
import tensorflow as tf
# ---- Loading images from directories ----
# Exam datasets are often structured as:
# data/
# train/
# class_a/ (images)
# class_b/ (images)
# validation/
# class_a/ (images)
# class_b/ (images)
IMG_SIZE = (150, 150)
BATCH_SIZE = 32
# Method 1: ImageDataGenerator (older but still valid for exam)
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1./255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
val_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1./255 # Only rescale for validation - NO augmentation
)
# train_generator = train_datagen.flow_from_directory(
# 'data/train',
# target_size=IMG_SIZE,
# batch_size=BATCH_SIZE,
# class_mode='categorical' # or 'binary' for 2 classes
# )
# val_generator = val_datagen.flow_from_directory(
# 'data/validation',
# target_size=IMG_SIZE,
# batch_size=BATCH_SIZE,
# class_mode='categorical'
# )
# Method 2: tf.keras.utils (newer, preferred)
# train_ds = tf.keras.utils.image_dataset_from_directory(
# 'data/train',
# image_size=IMG_SIZE,
# batch_size=BATCH_SIZE
# )
# ---- CNN for directory-loaded data ----
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 3)),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Conv2D(128, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Conv2D(128, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Flatten(),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dense(5, activation='softmax') # Adjust num classes
])
model.compile(
optimizer='adam',
loss='categorical_crossentropy', # categorical for one-hot from generator
metrics=['accuracy']
)
# history = model.fit(
# train_generator,
# validation_data=val_generator,
# epochs=25,
# callbacks=[tf.keras.callbacks.EarlyStopping(patience=3)]
# )
model.save('image_dir_cnn.h5')
Key Takeaways
- Always normalize pixel values to [0, 1] by dividing by 255 — raw pixel values will not train well
- Use Conv2D → MaxPooling2D blocks, then Flatten → Dense for the classification head
- Apply data augmentation only to training data, never to validation or test data
- Transfer learning with MobileNetV2 is the fastest path to high accuracy on exam tasks
- Freeze the base model first, train the head, then optionally fine-tune top layers with a low learning rate
- Match
class_modein ImageDataGenerator to your loss function:'binary'withbinary_crossentropy,'categorical'withcategorical_crossentropy
Lilly Tech Systems