ValueError
custom loss function
focal loss
model loading error
machine learning error

ValueError Unknown loss functionfocal_loss_fixed when loading model with my custom loss function

Master System Design with Codemia

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

Introduction

This error appears because Keras can save a model reference to a custom loss, but it cannot recreate that Python function by name unless you provide it again at load time. If your model was compiled with a custom focal loss called focal_loss_fixed, loading will fail until Keras knows how to resolve that symbol.

Why The Error Happens

When you save a compiled model, Keras stores the training configuration, including the loss name. Built-in losses such as "binary_crossentropy" can be restored automatically, but user-defined functions are not part of the standard registry unless you register them or pass them explicitly.

A very common focal-loss pattern looks like this:

python
1import tensorflow as tf
2
3
4def focal_loss(gamma=2.0, alpha=0.25):
5    def focal_loss_fixed(y_true, y_pred):
6        y_true = tf.cast(y_true, tf.float32)
7        y_pred = tf.clip_by_value(y_pred, 1e-7, 1.0 - 1e-7)
8
9        pt = tf.where(tf.equal(y_true, 1), y_pred, 1 - y_pred)
10        weight = alpha * tf.pow(1 - pt, gamma)
11        loss = -weight * tf.math.log(pt)
12        return tf.reduce_mean(loss)
13
14    return focal_loss_fixed

That inner function is perfectly valid during training, but on load Keras only sees the saved name and does not know how to rebuild the closure on its own.

Fix It With custom_objects

The fastest fix is to pass the same loss object when calling load_model.

python
1from tensorflow import keras
2
3loss_fn = focal_loss(gamma=2.0, alpha=0.25)
4
5model = keras.models.load_model(
6    "model.h5",
7    custom_objects={"focal_loss_fixed": loss_fn}
8)

The important detail is that the dictionary value must be the actual callable that matches the saved loss, not the outer factory function by itself.

If you saved the model with different gamma or alpha values, recreate the loss with the same parameters before loading.

A More Durable Fix: Register The Loss

If you save and load the model often, registering the loss is cleaner than passing custom_objects everywhere.

python
1import tensorflow as tf
2from tensorflow import keras
3
4
5@keras.utils.register_keras_serializable(package="Custom")
6def focal_loss_fixed(y_true, y_pred):
7    y_true = tf.cast(y_true, tf.float32)
8    y_pred = tf.clip_by_value(y_pred, 1e-7, 1.0 - 1e-7)
9    pt = tf.where(tf.equal(y_true, 1), y_pred, 1 - y_pred)
10    alpha = 0.25
11    gamma = 2.0
12    weight = alpha * tf.pow(1 - pt, gamma)
13    return tf.reduce_mean(-weight * tf.math.log(pt))

Once registered, Keras can serialize and deserialize the function by name more reliably.

This approach is especially useful if the loss does not need to be parameterized dynamically at runtime.

Best Option For Parameterized Losses

If you want the loss configuration to travel with the model cleanly, subclass keras.losses.Loss and implement get_config.

python
1import tensorflow as tf
2from tensorflow import keras
3
4
5@keras.utils.register_keras_serializable(package="Custom")
6class FocalLoss(keras.losses.Loss):
7    def __init__(self, gamma=2.0, alpha=0.25, name="focal_loss"):
8        super().__init__(name=name)
9        self.gamma = gamma
10        self.alpha = alpha
11
12    def call(self, y_true, y_pred):
13        y_true = tf.cast(y_true, tf.float32)
14        y_pred = tf.clip_by_value(y_pred, 1e-7, 1.0 - 1e-7)
15        pt = tf.where(tf.equal(y_true, 1), y_pred, 1 - y_pred)
16        weight = self.alpha * tf.pow(1 - pt, self.gamma)
17        return tf.reduce_mean(-weight * tf.math.log(pt))
18
19    def get_config(self):
20        return {"gamma": self.gamma, "alpha": self.alpha, "name": self.name}

Then compile with loss=FocalLoss(...), save the model, and load it without rebuilding a fragile closure manually.

This is the most maintainable route for production code because the hyperparameters are part of the serialized config.

Save Weights Only If You Do Not Need The Compile State

Sometimes you only care about inference. In that case, you can save weights and rebuild the model architecture in code, avoiding loss deserialization entirely.

That approach is useful when:

  • The model architecture is already defined in source control.
  • You do not need to resume training from the saved file.
  • You want to avoid serializing custom training objects.

It is not always the best answer, but it can remove unnecessary loading problems for inference-only deployments.

Common Pitfalls

The most common mistake is passing the outer loss factory into custom_objects instead of the returned callable. If the saved model expects focal_loss_fixed, then Keras needs the inner function object that actually computes the loss.

Another mistake is recreating the loss with different hyperparameters from the ones used during training. The model may load, but resumed training or evaluation will no longer match the original setup.

People also forget that custom metrics, activations, and layers need the same treatment. If one custom object is missing, loading can still fail even after the loss issue is fixed.

Finally, if you are only serving predictions, loading with compile-related state may be unnecessary overhead. Inference-only workflows can often avoid the problem by rebuilding the model and loading weights.

Summary

  • Keras cannot restore a custom loss by name unless you register it or pass it explicitly.
  • 'custom_objects fixes the error quickly, but the callable must match the saved loss name.'
  • Registering the function improves repeatable loading.
  • Subclassing keras.losses.Loss with get_config is the cleanest solution for parameterized focal loss.
  • For inference-only use cases, loading weights without compile state can avoid custom loss deserialization entirely.

Course illustration
Course illustration

All Rights Reserved.