transfer learning
MNIST dataset
machine learning
deep learning
neural networks

How to do transfer learning for MNIST dataset?

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 means starting from a model that has already learned useful visual features and adapting it to a new task. MNIST is small enough that training from scratch is often fine, but it is still a good dataset for learning the transfer-learning workflow in TensorFlow and Keras.

Why MNIST Needs Preprocessing First

Most pretrained image models expect larger RGB images, while MNIST images are 28 x 28 grayscale digits. Before reuse is possible, the images need to be resized and converted from one channel to three channels.

That means a transfer-learning pipeline for MNIST usually includes:

  • resize from 28 x 28 to the base model input size
  • duplicate the grayscale channel into RGB
  • scale pixel values to the range expected by the pretrained model

Build A Preprocessing Pipeline

The example below uses MobileNetV2, which is a lightweight ImageNet model that works well for demonstrations.

python
1import tensorflow as tf
2from tensorflow import keras
3from tensorflow.keras import layers
4
5(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
6
7x_train = x_train.astype("float32") / 255.0
8x_test = x_test.astype("float32") / 255.0
9
10x_train = tf.expand_dims(x_train, axis=-1)
11x_test = tf.expand_dims(x_test, axis=-1)
12
13x_train = tf.image.resize(x_train, (96, 96))
14x_test = tf.image.resize(x_test, (96, 96))
15
16x_train = tf.image.grayscale_to_rgb(x_train)
17x_test = tf.image.grayscale_to_rgb(x_test)

The choice of 96 x 96 keeps the model small enough to train quickly while still matching a practical input size for pretrained vision backbones.

Freeze The Base Model

In the first stage, reuse the pretrained convolution layers as a fixed feature extractor.

python
1base_model = keras.applications.MobileNetV2(
2    input_shape=(96, 96, 3),
3    include_top=False,
4    weights="imagenet"
5)
6base_model.trainable = False
7
8inputs = keras.Input(shape=(96, 96, 3))
9x = keras.applications.mobilenet_v2.preprocess_input(inputs)
10x = base_model(x, training=False)
11x = layers.GlobalAveragePooling2D()(x)
12x = layers.Dropout(0.2)(x)
13outputs = layers.Dense(10, activation="softmax")(x)
14model = keras.Model(inputs, outputs)
15
16model.compile(
17    optimizer="adam",
18    loss="sparse_categorical_crossentropy",
19    metrics=["accuracy"]
20)

The classifier head is new, but the convolution stack starts from ImageNet features. Even though handwritten digits are very different from natural photographs, low-level edge and shape features can still transfer.

Train The New Classification Head

Now train only the newly added layers.

python
1history = model.fit(
2    x_train,
3    y_train,
4    validation_split=0.1,
5    epochs=3,
6    batch_size=64
7)
8
9test_loss, test_accuracy = model.evaluate(x_test, y_test)
10print("test accuracy:", test_accuracy)

On MNIST, this stage is often enough to reach strong accuracy quickly because the task is relatively simple.

Fine-Tune If Needed

If the frozen model plateaus and you want a small improvement, unfreeze part of the base model and continue training with a lower learning rate.

python
1base_model.trainable = True
2
3for layer in base_model.layers[:-20]:
4    layer.trainable = False
5
6model.compile(
7    optimizer=keras.optimizers.Adam(1e-5),
8    loss="sparse_categorical_crossentropy",
9    metrics=["accuracy"]
10)
11
12model.fit(x_train, y_train, validation_split=0.1, epochs=2, batch_size=64)

Fine-tuning too aggressively can destroy the pretrained weights, so smaller learning rates are important.

When Transfer Learning Is Overkill

MNIST is so small and well-behaved that a compact CNN trained from scratch can perform extremely well. The transfer-learning value here is mostly educational: you learn how to adapt image size, channels, freezing, and fine-tuning.

For harder image tasks with smaller labeled datasets, the same workflow becomes much more valuable.

Common Pitfalls

A common mistake is passing raw 28 x 28 x 1 MNIST images directly into an ImageNet backbone. Pretrained models expect a specific input size and channel count.

Another mistake is forgetting the model-specific preprocessing function. For MobileNetV2, preprocess_input matters because the weights were trained with a specific input normalization scheme.

It is also easy to unfreeze the whole backbone too early and train with a high learning rate. That usually harms performance instead of helping.

Summary

  • Resize MNIST images and convert them from grayscale to RGB before using pretrained vision backbones.
  • Freeze the base model first and train only the new classification head.
  • Use the backbone's matching preprocessing function.
  • Fine-tune later with a low learning rate if the frozen model plateaus.
  • On MNIST, transfer learning is mainly a workflow exercise because simpler models also work very well.

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.