Training a CNN from Scratch on a Custom Dataset: A Comprehensive Guide

Convolutional neural networks (CNNs) have revolutionized computer vision, enabling superhuman performance on tasks like image classification, object detection, and semantic segmentation. The most common approach to applying CNNs is to use a pre-trained model and fine-tune it on your specific dataset. While effective, this limits you to the architecture, learned features, and dataset biases of the original model.

Training a CNN from scratch on a custom dataset unlocks the full flexibility and potential of deep learning. You have complete control over the network design, can tailor it to the unique characteristics of your data, and the model learns features specific to your application. The downside is that CNNs typically require large annotated datasets to train effectively from scratch.

In this guide, we‘ll walk through the complete process of training a CNN from the ground up on a custom dataset. The principles apply whether using PyTorch, TensorFlow, or any other deep learning framework. We‘ll cover dataset preparation, model design, configuring the training pipeline, evaluating results, and tips for maximizing performance when working with limited training data. Finally, we‘ll compare CNNs trained from scratch to fine-tuned models and discuss real-world use cases.

Preparing a Custom Dataset

The first step is assembling a well-constructed dataset. For CNNs, this means a collection of labeled images, ideally with a uniform size and aspect ratio. The images should capture the full diversity you expect the model to handle in the real world.

How many images do you need? While more is always better, a general rule of thumb is at least 1000 samples per class for reasonable performance. With techniques like data augmentation and transfer learning, it‘s possible to train with fewer though.

Organize the images in a logical directory structure, typically with a folder per class. Creating separate subfolders for the training, validation, and test splits is also recommended. The training set is used to optimize the model parameters, the validation set for tuning hyperparameters and preventing overfitting, and the test set gives an unbiased estimate of real-world performance.

An example directory structure:

dataset/
    train/
        class1/
            img1.jpg
            img2.jpg
            ...
        class2/
            img1.jpg
            ...
    val/
        class1/
            img1.jpg
            ...
        class2/
            img1.jpg
            ...
    test/
        class1/
            img1.jpg
        class2/
            img1.jpg
            ...

With the dataset prepared, the next step is loading it in a format suitable for training a CNN. The typical approach is to use an image data generator that reads images from disk, applies random transformations for data augmentation, and yields batches for training.

Most deep learning frameworks provide utilities for creating data generators from image directories. Here‘s an example using Keras:

from tensorflow.keras.preprocessing.image import ImageDataGenerator

# Create training generator 
train_datagen = 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)
train_generator = train_datagen.flow_from_directory(‘dataset/train‘,
                                                    target_size=(224, 224), 
                                                    batch_size=32,
                                                    class_mode=‘categorical‘)

# Create validation generator
val_datagen = ImageDataGenerator(rescale=1./255) 
val_generator = val_datagen.flow_from_directory(‘dataset/val‘,
                                                target_size=(224, 224),
                                                batch_size=32,
                                                class_mode=‘categorical‘)

This code creates a training generator that loads images from the dataset/train directory, resizes them to 224×224, applies random augmentations, and returns them in batches of 32. The validation generator is similar but doesn‘t apply data augmentation since we want to evaluate on unmodified images.

Designing a CNN Architecture

With the data prepared, the next step is designing the CNN architecture. While pre-trained networks are well-optimized, rolling your own architecture allows you to experiment and innovate.

The core building blocks of a CNN are:

  • Convolutional layers: Learn spatial hierarchies of features by applying sliding filters to the input
  • Pooling layers: Downsample features to increase receptive field and provide translation invariance
  • Activation functions: Add non-linearity to enable the network to learn complex mappings
  • Fully-connected layers: Output the final class scores or predictions

A simple CNN architecture in Keras looks like:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPool2D, Flatten, Dense

model = Sequential([
    Conv2D(32, 3, activation=‘relu‘, input_shape=(224, 224, 3)),
    MaxPool2D(2),
    Conv2D(64, 3, activation=‘relu‘),
    MaxPool2D(2),
    Conv2D(128, 3, activation=‘relu‘), 
    MaxPool2D(2),
    Flatten(),
    Dense(128, activation=‘relu‘),
    Dense(num_classes, activation=‘softmax‘)
])

This network has three conv-pool blocks that learn 32, 64 and 128 filters respectively. The spatial dimensions are halved after each block. The output of the final pooling layer is flattened and passed through two dense layers to generate the class probabilities.

Some tips for designing CNNs:

  • Increase the number of filters as you go deeper in the network
  • Use small filters (3×3) with stride 1 and same padding to preserve spatial dimensions
  • Follow 2-3 convolution layers by a max pooling layer to reduce dimensions
  • Batch normalization and dropout can improve convergence and regularize the model
  • Global average pooling is a good alternative to fully-connected layers at the end of the network

Experiment with different architectures to find what works best for your data. Plotting the model graph can help visualize the flow of data and spot potential issues.

Configuring the Training Pipeline

With the model and data loaders defined, we can now set up the training pipeline. The key components are:

  • Loss function: Measures how closely the model‘s predictions match the true labels. Cross-entropy is commonly used for classification.
  • Optimizer: Updates the model parameters based on the loss gradients. Adam, SGD with momentum, and RMSprop are popular choices.
  • Learning rate schedule: Controls how drastically weights are updated throughout training. Strategies include reducing the learning rate on plateaus, cosine annealing, and 1-cycle.
  • Batch size: Number of examples used in each iteration. Higher batch sizes provide more stable gradients but require more memory.
  • Epochs: Number of complete passes through the dataset during training.

In Keras, compiling the model with these configurations looks like:

from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ReduceLROnPlateau

model.compile(optimizer=Adam(lr=1e-3),
              loss=‘categorical_crossentropy‘, 
              metrics=[‘accuracy‘])

reduce_lr = ReduceLROnPlateau(monitor=‘val_loss‘, 
                              factor=0.1,
                              patience=5,
                              min_lr=1e-6)

callbacks = [reduce_lr]

batch_size = 32
epochs = 50

Here we‘re using the Adam optimizer with an initial learning rate of 0.001 and categorical cross-entropy loss since we‘re doing multi-class classification. We‘re also using the ReduceLROnPlateau callback to reduce the learning rate by 10x if the validation loss doesn‘t improve for 5 epochs.

Keras provides many other useful callbacks like ModelCheckpoint for saving the best model during training and EarlyStopping for halting training if performance plateaus.

Training the Model

We‘re now ready to train our CNN on the custom dataset. In Keras, this is done by calling fit() on the model and passing the data generators:

history = model.fit(train_generator,
                    steps_per_epoch=len(train_generator),
                    validation_data=val_generator,
                    validation_steps=len(val_generator),
                    epochs=epochs,
                    callbacks=callbacks)

During training, you should monitor the loss and accuracy on the training and validation sets. If training loss decreases but validation loss increases, the model is overfitting. Common strategies to combat overfitting are collecting more data, applying stronger data augmentation, using architectures with fewer parameters, and adding regularization like weight decay and dropout.

Evaluating Performance

Once the model is trained, you can evaluate its final performance on the test set. Use the same metrics as during training, like accuracy, precision, recall, and F1 score for classification.

In Keras:

test_loss, test_acc = model.evaluate(test_generator)
print(‘Test accuracy:‘, test_acc)

To get a more granular view, plot a confusion matrix to see which classes are misclassified most often. Visualizing the learned convolutional filters and activation maps can also give insights into what features the model is learning and where it‘s looking.

Improving Performance on Small Datasets

Training CNNs from scratch on small datasets is challenging, but several techniques can help:

  1. Data augmentation: Generate additional training samples by applying random transformations like flipping, rotation, scaling, and color jittering. This reduces overfitting and helps the model generalize.

  2. Transfer learning: Use a pre-trained CNN as a feature extractor and replace the final classification layer. The pre-trained model provides a strong starting point, allowing the model to learn quickly with less data. You can optionally fine-tune the entire model end-to-end.

  3. Semi-supervised learning: Leverage unlabeled data by training the model to produce consistent predictions for different augmented versions of the same image. Pseudo-labeling is another technique that uses a model‘s predictions on unlabeled data as targets for training.

  4. Squeeze-and-Excitation blocks: Add architectural components that adaptively recalibrate feature maps to emphasize informative channels and suppress less useful ones. This improves parameter efficiency and performance.

  5. Regularization: Combat overfitting with techniques like L1/L2 regularization, dropout, weight averaging, and early stopping.

Real-world Applications

Training CNNs from scratch has numerous real-world applications, including:

  • Medical imaging: Detect tumors, lesions, and abnormalities in X-rays, MRIs, and CT scans
  • Defect detection: Identify manufacturing defects and anomalies on assembly lines
  • Facial recognition: Verify identities and analyze facial expressions for emotion detection
  • Agriculture: Detect crop diseases, pests, and nutrient deficiencies from aerial imagery
  • Autonomous driving: Perceive lane markings, traffic signs, pedestrians, and other vehicles

In many specialized domains, pre-trained models are unavailable or insufficient. Training on a custom dataset allows building tailored solutions for niche applications.

Custom vs Pre-trained CNNs

To summarize, the main advantages of training a CNN from scratch compared to fine-tuning a pre-trained model are:

  • Architecture flexibility to design a network tailored to your problem and compute constraints
  • Learning domain-specific features instead of generic ImageNet features
  • No bias towards the original training data and potentially better performance on your data
  • Smaller model size if using a compact architecture

The disadvantages are:

  • Requires more training data to achieve good performance
  • Longer training times and higher computational cost
  • Need more hyperparameter tuning and architecture design
  • Risk of overfitting small datasets

In practice, the best approach depends on the dataset size, similarity to ImageNet, and computational budget. If you have a large, diverse dataset and sufficient compute, training from scratch maximizes performance and customizability. For smaller datasets or quick prototyping, transfer learning is typically more efficient.

Conclusion

Training a CNN from scratch is a powerful skill to master for applying deep learning to new domains. PyTorch and TensorFlow make the process accessible by providing high-level APIs for defining models and loading data. The keys to success are configuring an effective training pipeline, choosing an appropriate architecture, and employing techniques like data augmentation and regularization to combat overfitting.

I hope this guide gives you the knowledge and confidence to train a CNN from the ground up on your own custom dataset. The flexibility and control you gain open up a world of possibilities for solving computer vision problems. Remember to experiment, iterate, and have fun!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Similar Posts