Keras
ResNet50
fine-tuning
machine learning
troubleshooting

Not able to load weights for fine tuning in Keras with ResNet50

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When ResNet50 weights fail to load in Keras, the root cause is usually one of three things: the model architecture does not match the saved weights, the file format does not match the loading API, or the fine-tuning workflow is trying to reuse weights after changing the classifier head. The fix is to separate backbone loading from custom head construction and verify that each layer shape matches the checkpoint.

Start with the Correct ResNet50 Base

If you want the standard pretrained backbone, build the model exactly as Keras expects before loading any custom weights. For transfer learning, that usually means include_top=False so the ImageNet classifier head is excluded.

python
1import tensorflow as tf
2from tensorflow.keras.applications import ResNet50
3from tensorflow.keras import layers, models
4
5base_model = ResNet50(weights="imagenet", include_top=False, input_shape=(224, 224, 3))
6base_model.trainable = False
7
8model = models.Sequential([
9    base_model,
10    layers.GlobalAveragePooling2D(),
11    layers.Dense(1, activation="sigmoid")
12])
13
14model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"])

This works because the ResNet50 backbone matches the built-in pretrained weight file exactly. You are only training the new classifier head on top.

Why Weight Loading Breaks

The most common failure happens when a checkpoint was created from a different architecture. Even small differences matter:

  • Different input channel count
  • Different number of output classes
  • Added or removed pooling layers
  • Changed layer names in subclassed models

If you saved weights from a model with a 10-class Dense output and later rebuild the model with a 2-class output, loading all weights will fail on the final layer because the tensor shapes differ.

Fine-Tuning in Two Phases

A reliable fine-tuning workflow has two phases.

Phase one trains a new head while the backbone is frozen. Phase two unfreezes some deeper ResNet layers and continues with a lower learning rate.

python
1import tensorflow as tf
2from tensorflow.keras.applications import ResNet50
3from tensorflow.keras import layers, Model
4
5inputs = tf.keras.Input(shape=(224, 224, 3))
6base_model = ResNet50(weights="imagenet", include_top=False, input_tensor=inputs)
7base_model.trainable = False
8
9x = layers.GlobalAveragePooling2D()(base_model.output)
10outputs = layers.Dense(3, activation="softmax")(x)
11model = Model(inputs, outputs)
12
13model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
14# model.fit(...)
15model.save_weights("phase1.weights.h5")
16
17base_model.trainable = True
18for layer in base_model.layers[:-30]:
19    layer.trainable = False
20
21model.compile(
22    optimizer=tf.keras.optimizers.Adam(1e-5),
23    loss="sparse_categorical_crossentropy",
24    metrics=["accuracy"]
25)
26model.load_weights("phase1.weights.h5")

The important detail is that the model architecture is identical between save_weights and load_weights. Only the trainable flags and optimizer change.

Use the Right Loading Strategy

If you want only the ResNet backbone weights, let Keras download them through weights="imagenet". If you want to restore your whole fine-tuned model state, rebuild the exact same model and call load_weights() on it.

If you intentionally changed the top layers, load only the matching base-model weights:

python
new_base = ResNet50(weights=None, include_top=False, input_shape=(224, 224, 3))
new_base.load_weights("resnet_backbone_only.weights.h5")

This works only if the saved file contains weights for that backbone structure.

Checkpoint Formats and Layer Names

Keras can save full models and weights-only files. Those are not interchangeable in every loading path. model.save() stores architecture plus weights and optimizer metadata. model.save_weights() stores weights only.

Layer naming can also matter, especially in subclassed models or when using by_name=True in older loading workflows. If layer names drift, partial loads become harder to reason about.

A good debugging tactic is to call model.summary() for both the saving and loading models and compare the output carefully.

Common Pitfalls

A common mistake is attaching a new Dense head and then trying to load a checkpoint that included the old head. The output layer shape mismatch makes the restore fail.

Another issue is mixing include_top=True and include_top=False. Those are different architectures, so their weights are not drop-in replacements.

Developers also sometimes unfreeze the backbone, recompile, and assume recompilation changes the saved weights structure. It does not. Recompiling changes training behavior, not tensor shapes.

Summary

  • Build the same architecture before calling load_weights().
  • Use include_top=False when you want the ResNet50 backbone for transfer learning.
  • Save and load weights across matching model definitions.
  • If you change the classifier head, do not expect old head weights to fit.
  • Separate backbone loading, head training, and later fine-tuning into clear phases.

Course illustration
Course illustration

All Rights Reserved.