tensorflow
inceptionV3
retraining
classification
machine learning

Edit tensorflow inceptionV3 retraining-example.py for multiple classificiations

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

Transfer learning with TensorFlow's InceptionV3 lets you adapt a powerful image classifier to your own categories without training from scratch. The standard retrain.py example script handles single-label multi-class classification out of the box, where each image belongs to exactly one category. This article explains how to modify that script for both multi-class (many categories, one label per image) and multi-label (many categories, multiple labels per image) classification.

Prerequisites

Before modifying the retraining script, make sure you have the following in place.

bash
pip install tensorflow>=2.10 tensorflow-hub numpy Pillow

You also need a labeled dataset. For multi-class classification, organize images into subdirectories where each directory name is the class label.

 
1dataset/
2  cats/
3    img001.jpg
4    img002.jpg
5  dogs/
6    img003.jpg
7    img004.jpg
8  birds/
9    img005.jpg
10    img006.jpg

Loading InceptionV3 as a Feature Extractor

The core idea of transfer learning is to freeze the convolutional layers of a pretrained model and replace only the final classification head.

python
1import tensorflow as tf
2import tensorflow_hub as hub
3
4# Load InceptionV3 feature vector module (outputs 2048-dim vector)
5feature_extractor_url = (
6    "https://tfhub.dev/google/imagenet/inception_v3/feature_vector/5"
7)
8
9feature_extractor = hub.KerasLayer(
10    feature_extractor_url,
11    input_shape=(299, 299, 3),
12    trainable=False  # Freeze pretrained weights
13)

The feature extractor outputs a 2048-dimensional vector for each 299x299 input image. This vector captures high-level visual features learned from ImageNet.

Building a Multi-Class Classification Head

For standard multi-class classification (one label per image), add a dense layer with softmax activation.

python
1num_classes = 3  # cats, dogs, birds
2
3model = tf.keras.Sequential([
4    feature_extractor,
5    tf.keras.layers.Dropout(0.3),
6    tf.keras.layers.Dense(num_classes, activation="softmax")
7])
8
9model.compile(
10    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
11    loss="categorical_crossentropy",
12    metrics=["accuracy"]
13)
14
15model.summary()

Softmax ensures the output probabilities sum to 1.0, so the model always picks exactly one class. Use categorical_crossentropy when labels are one-hot encoded, or sparse_categorical_crossentropy when labels are integers.

Preparing the Data Pipeline

TensorFlow's image_dataset_from_directory automates loading and label assignment.

python
1IMG_SIZE = (299, 299)
2BATCH_SIZE = 32
3
4train_ds = tf.keras.utils.image_dataset_from_directory(
5    "dataset/",
6    label_mode="categorical",  # One-hot encoded
7    image_size=IMG_SIZE,
8    batch_size=BATCH_SIZE,
9    validation_split=0.2,
10    subset="training",
11    seed=42
12)
13
14val_ds = tf.keras.utils.image_dataset_from_directory(
15    "dataset/",
16    label_mode="categorical",
17    image_size=IMG_SIZE,
18    batch_size=BATCH_SIZE,
19    validation_split=0.2,
20    subset="validation",
21    seed=42
22)
23
24# Normalize pixel values to [0, 1]
25normalization = tf.keras.layers.Rescaling(1.0 / 255)
26train_ds = train_ds.map(lambda x, y: (normalization(x), y))
27val_ds = val_ds.map(lambda x, y: (normalization(x), y))

Prefetching and caching improve training throughput.

python
AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

Training the Multi-Class Model

With the data pipeline ready, training is straightforward.

python
1history = model.fit(
2    train_ds,
3    validation_data=val_ds,
4    epochs=10,
5    callbacks=[
6        tf.keras.callbacks.EarlyStopping(
7            patience=3, restore_best_weights=True
8        )
9    ]
10)

Early stopping prevents overfitting by reverting to the best weights if validation accuracy stalls for three consecutive epochs.

Modifying for Multi-Label Classification

The key modification for multi-label classification is changing the final activation from softmax to sigmoid and the loss from categorical crossentropy to binary crossentropy. Sigmoid treats each output neuron independently, so multiple classes can be active simultaneously.

python
1num_labels = 5  # e.g., scene, outdoor, animal, vehicle, person
2
3model_multilabel = tf.keras.Sequential([
4    feature_extractor,
5    tf.keras.layers.Dropout(0.3),
6    tf.keras.layers.Dense(num_labels, activation="sigmoid")
7])
8
9model_multilabel.compile(
10    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
11    loss="binary_crossentropy",
12    metrics=["binary_accuracy"]
13)

For multi-label data, you cannot use image_dataset_from_directory because a single image may belong to multiple categories. Instead, load labels from a CSV file.

python
1import numpy as np
2
3# labels.csv: filename, label1, label2, label3, label4, label5
4# img001.jpg, 1, 0, 1, 0, 0
5labels_df = tf.data.experimental.make_csv_dataset(
6    "labels.csv", batch_size=BATCH_SIZE
7)
8
9# Or load manually
10def load_image_and_labels(image_path, label_vector):
11    img = tf.io.read_file(image_path)
12    img = tf.image.decode_jpeg(img, channels=3)
13    img = tf.image.resize(img, IMG_SIZE)
14    img = img / 255.0
15    return img, label_vector

Running Inference

After training, run predictions on new images.

python
1from PIL import Image
2import numpy as np
3
4def predict(model, image_path, class_names):
5    img = Image.open(image_path).resize((299, 299))
6    img_array = np.array(img) / 255.0
7    img_array = np.expand_dims(img_array, axis=0)
8
9    predictions = model.predict(img_array)[0]
10
11    # Multi-class: pick the top class
12    top_class = class_names[np.argmax(predictions)]
13    confidence = np.max(predictions)
14    print(f"Predicted: {top_class} ({confidence:.2%})")
15
16    return top_class, confidence
17
18class_names = ["cats", "dogs", "birds"]
19predict(model, "test_image.jpg", class_names)

For multi-label inference, apply a threshold to each sigmoid output.

python
1def predict_multilabel(model, image_path, label_names, threshold=0.5):
2    img = Image.open(image_path).resize((299, 299))
3    img_array = np.expand_dims(np.array(img) / 255.0, axis=0)
4
5    predictions = model.predict(img_array)[0]
6    active_labels = [
7        label_names[i]
8        for i, prob in enumerate(predictions)
9        if prob >= threshold
10    ]
11    print(f"Active labels: {active_labels}")
12    return active_labels

Fine-Tuning for Better Accuracy

After the classification head converges, you can unfreeze some InceptionV3 layers for fine-tuning.

python
1feature_extractor.trainable = True
2
3# Freeze all layers except the last 30
4for layer in model.layers[0].layers[:-30]:
5    layer.trainable = False
6
7model.compile(
8    optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),
9    loss="categorical_crossentropy",
10    metrics=["accuracy"]
11)
12
13model.fit(train_ds, validation_data=val_ds, epochs=5)

Use a much lower learning rate during fine-tuning (1e-5 instead of 1e-3) to avoid destroying the pretrained features. Fine-tuning typically improves accuracy by 2-5 percentage points.

Common Pitfalls

  • Using softmax for multi-label tasks: Softmax forces outputs to sum to 1.0, which prevents multiple labels from being active. Always use sigmoid activation with binary crossentropy for multi-label classification.
  • Forgetting to normalize input images: InceptionV3 expects pixel values in the [0, 1] range. Feeding raw [0, 255] values produces poor accuracy and unstable training.
  • Setting learning rate too high during fine-tuning: A high learning rate destroys the pretrained weights. Use 1e-5 or lower when unfreezing convolutional layers.
  • Insufficient training data per class: Transfer learning reduces data requirements but each class still needs at least 100-200 images for reasonable accuracy. Classes with fewer than 50 images often overfit.
  • Not using data augmentation: For small datasets, add random flips, rotations, and brightness adjustments to reduce overfitting and improve generalization.

Summary

  • Transfer learning with InceptionV3 replaces only the final classification head while keeping pretrained convolutional features frozen.
  • For multi-class (one label per image), use softmax activation with categorical crossentropy loss.
  • For multi-label (multiple labels per image), switch to sigmoid activation with binary crossentropy loss.
  • Normalize images to [0, 1] and resize to 299x299 to match InceptionV3's expected input format.
  • Fine-tune the last few convolutional layers with a low learning rate after the head converges for an additional accuracy boost.
  • Use early stopping and data augmentation to prevent overfitting on small datasets.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.