Hello everyone! How are you all doing?
Today, I want to share a mini story about my experience completing a deep learning assignment—part of my DeepTechReady Week 2 Challenge, where I built and compared two models: a Custom CNN and a MobileNetV2 for multiclass boat image classification.
Let’s dive in!
Background: My ML Journey
First things first—yes, I’m learning machine learning!
In February 2025, I joined a data science and machine learning training program called DeepTechReady, organized by Data Science Nigeria and 3MTT NITDA. Classes kicked off in March with foundational topics, and by April, we were neck-deep in specialization. I chose the Data Science/Machine Learning track.
During Week 2 of the Deep Learning module, we were given this assignment:
Objective:
Build and compare two CNN models—one custom and one pretrained (like MobileNetV2)—to classify a dataset of boat images into multiple classes. Evaluate and determine which performs better.
Challenge accepted.
Step 1: Understanding and Preparing the Dataset
I used Google Colab for this task and mounted my Google Drive:
from google.colab import drive
drive.mount('/content/drive', force_remount=True)
I added force_remount=True because Colab would yell at me if I ran the cell twice.
Here’s what I noticed about the dataset:
- Oddly, the train folder had 4000+ images, while the test folder had 24 class folders.
- In the test folder, some subfolders had hundreds of images, while others had just 5. That imbalance would become a big deal later.
Initially, I didn’t realize this was a multiclass classification problem, but the structure of the test folder made that clear quickly.
To make things work, I used the 24-class test folder and split it into training and validation sets:
train_ds = tf.keras.utils.image_dataset_from_directory(
data_path,
validation_split=0.2,
subset="training",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size
)
val_ds = tf.keras.utils.image_dataset_from_directory(
data_path,
validation_split=0.2,
subset="validation",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size
)
class_names = train_ds.class_names
num_classes = len(class_names)
Output:
Found 4774 files belonging to 24 classes.
Using 3820 files for training.
Using 954 files for validation.
Step 2: Building a Custom CNN from Scratch
Next, I created my own Convolutional Neural Network.
custom_model = models.Sequential([
layers.Conv2D(32, (3,3), padding='same', activation='relu', input_shape=(224, 224, 3)),
layers.Conv2D(64, (3,3), padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Dropout(0.25),
layers.Conv2D(64, (3,3), padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Dropout(0.25),
layers.Conv2D(128, (3,3), padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Dropout(0.25),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(num_classes, activation='softmax')
])
I compiled and trained the model:
custom_model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
custom_model.fit(train_ds, validation_data=val_ds, epochs=5)
This lasted for more than one hour sadly and I found out from my colleagues that it was because I used CPU instead of GPU. I learned something valuable there
Model Summary: Custom CNN
Here’s what the architecture looked like:
Total params: 38,935,370 (148.53 MB)
Trainable params: 12,978,456 (49.51 MB)
Non-trainable params: 0 (0.00 B)
Optimizer params: 25,959,914 (99.02 MB)
My custom CNN was decently deep and had about 38 million parameters in total when flattened out. It handled the task okay but definitely had some overfitting issues, especially with the imbalanced classes.
Step 3: Transfer Learning with MobileNetV2
Next, I turned to MobileNetV2, a lightweight pretrained model.
base_model = MobileNetV2(
input_shape=(224, 224, 3),
include_top=False,
weights='imagenet'
)
base_model.trainable = False
Then I added my custom classification head:
mobilenet_model = models.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(num_classes, activation='softmax')
])
mobilenet_model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
mobilenet_model.fit(train_ds, validation_data=val_ds, epochs=5)
Initially, I used 180x180 for the custom model and 224x224 for MobileNetV2. This caused issues when comparing performance, so I retrained the custom CNN using 224x224 for consistency.
Results: Comparing Custom CNN vs MobileNetV2
I evaluated both models using classification reports.
Here’s a summary of what I observed:
Custom CNN:
Performed better on high-frequency classes.
Struggled heavily on minority classes (many zero precision/recall).
Had more parameters and took longer to train.
MobileNetV2:
Generalized better across classes—even minority ones.
Faster to train.
Thanks to ImageNet pretraining, it handled even subtle boat differences better.
Verdict: Transfer learning wins here. MobileNetV2 outperformed the custom CNN in both accuracy and generalization—especially on imbalanced datasets.
Final Reflections
Here’s what I learned:
GPU matters—always check your runtime!
Transfer learning is powerful for real-world datasets, especially when you have class imbalance or limited compute.
Building custom CNNs is still a great learning experience and gives you full control over architecture.
It’s okay to experiment, make mistakes, and iterate—because that's where deep learning actually clicks.
Here is my notebook link: https://github.com/ezinneanne/DeepTechReady-Work/blob/main/DSML%20Notebooks/Week2_Assignment.ipynb
Thanks for reading my deep learning adventure! If you’re starting out, don’t be afraid to break things and learn from them—that’s the real magic.
Got questions or suggestions? I’d love to hear your thoughts!
Top comments (0)