Transfer Learning with Pre-trained Models
Keras Basics
2 min read
Published Nov 17 2025, updated Aug 17 2026
Guide Sections
Guide Comments
Transfer learning is one of the most powerful techniques in modern deep learning.
Instead of training a model from scratch on your dataset, you:
- Take a model pre-trained on a huge dataset (ImageNet)
- Freeze its convolutional base
- Add your own classifier layer(s)
- Train for a short time
- (Optional) fine-tune deeper layers
Benefits:
- Much higher accuracy
- Works with small datasets
- Faster training
- Lower compute cost
In this section we apply transfer learning to Cats vs Dogs using TensorFlow Datasets
Load the Cats vs Dogs Dataset
TensorFlow Datasets (TFDS) provides cats_vs_dogs fully prepared for use.
Install TFDS if needed:
pip install tensorflow-datasetsLoad dataset:
import tensorflow_datasets as tfdsimport tensorflow as tf(ds_train, ds_val), ds_info = tfds.load( "cats_vs_dogs", split=["train[:80%]", "train[80%:]"], shuffle_files=True, as_supervised=True, with_info=True)print(ds_info)Labels:
- 0 = cat
- 1 = dog
Build Input Pipeline
Transfer learning models expect consistent image size, so resize all images.
img_size = (160, 160)batch_size = 32AUTOTUNE = tf.data.AUTOTUNEdef format_example(image, label): image = tf.image.resize(image, img_size) # normalize to 0–1 image = image / 255.0 return image, labeltrain_ds = ( ds_train .map(format_example, num_parallel_calls=AUTOTUNE) .cache() .shuffle(1000) .batch(batch_size) .prefetch(AUTOTUNE))val_ds = ( ds_val .map(format_example, num_parallel_calls=AUTOTUNE) .batch(batch_size) .prefetch(AUTOTUNE))This pipeline:
- Resizes images
- Normalises pixels
- Batches data
- Prefetches for speed
Load a Pre-trained Base Model (MobileNetV2)
We use MobileNetV2 (fast, accurate, lightweight):
from tensorflow.keras import applicationsbase_model = applications.MobileNetV2( input_shape=img_size + (3,), include_top=False, # remove ImageNet classifier weights="imagenet")Freeze it:
base_model.trainable = FalseThis makes it a fixed feature extractor.
Add Your Own Classification Head
Typical transfer-learning head:
from tensorflow.keras import layers, Sequentialmodel = Sequential([ base_model, layers.GlobalAveragePooling2D(), layers.Dropout(0.2), # binary classification layers.Dense(1, activation="sigmoid") ])Why this design?
- GlobalAveragePooling2D → reduces feature map dimensions without dense layers
- Dropout → reduces overfitting
- Sigmoid → output probability of "dog"
Compile the Model
Use a small learning rate:
model.compile( optimizer=tf.keras.optimizers.Adam(1e-4), loss="binary_crossentropy", metrics=["accuracy"])Train the Frozen Model (Feature Extraction Phase)
history = model.fit( train_ds, validation_data=val_ds, epochs=5)Typical accuracy: 93–96% after just a few epochs, this is the power of transfer learning.
Fine-Tuning for Extra Accuracy
Once the head is trained, unfreeze part of the base model:
base_model.trainable = TrueUnfreeze only the newest convolutional layers (recommended):
# number of layers to leave frozenfine_tune_at = 100 for layer in base_model.layers[:fine_tune_at]: layer.trainable = FalseRecompile with a very low learning rate:
model.compile( optimizer=tf.keras.optimizers.Adam(1e-5), loss="binary_crossentropy", metrics=["accuracy"])Train again:
history_fine = model.fit( train_ds, validation_data=val_ds, epochs=5)Fine-tuning often boosts accuracy by 1–3%.
Data Augmentation (Highly Recommended)
Adding augmentation improves generalisation:
data_augmentation = Sequential([ layers.RandomFlip("horizontal"), layers.RandomRotation(0.1), layers.RandomZoom(0.1),])Use it at the start of your model:
model = Sequential([ data_augmentation, base_model, layers.GlobalAveragePooling2D(), layers.Dropout(0.2), layers.Dense(1, activation='sigmoid')])Using Other Pre-trained Keras Models
You can replace MobileNetV2 with any Keras Application model:
applications.ResNet50()applications.EfficientNetB0()applications.EfficientNetB3()applications.DenseNet121()applications.InceptionV3()applications.Xception()All follow the same pattern:
include_top=Falseweights="imagenet"- Freeze
- Add classifier head
- Train
- Optionally fine-tune
Full Working Transfer Learning Script
import tensorflow as tfimport tensorflow_datasets as tfdsfrom tensorflow.keras import layers, Sequential, applications# Load dataset(ds_train, ds_val), ds_info = tfds.load( "cats_vs_dogs", split=["train[:80%]", "train[80%:]"], shuffle_files=True, as_supervised=True, with_info=True)# Preprocessingimg_size = (160, 160)batch_size = 32AUTOTUNE = tf.data.AUTOTUNEdef format_example(image, label): image = tf.image.resize(image, img_size) image = image / 255.0 return image, labeltrain_ds = ( ds_train .map(format_example, num_parallel_calls=AUTOTUNE) .cache() .shuffle(1000) .batch(batch_size) .prefetch(AUTOTUNE))val_ds = ( ds_val .map(format_example, num_parallel_calls=AUTOTUNE) .batch(batch_size) .prefetch(AUTOTUNE))# Load base modelbase_model = applications.MobileNetV2( input_shape=img_size + (3,), include_top=False, weights="imagenet")base_model.trainable = False# Build modelmodel = Sequential([ base_model, layers.GlobalAveragePooling2D(), layers.Dropout(0.2), layers.Dense(1, activation="sigmoid")])# Compilemodel.compile( optimizer=tf.keras.optimizers.Adam(1e-4), loss="binary_crossentropy", metrics=["accuracy"])# Trainhistory = model.fit( train_ds, validation_data=val_ds, epochs=5)