custom F1 loss
weighted average
Keras
loss function
machine learning

How to write a custom f1 loss function with weighted average for keras?

Master System Design with Codemia

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

Introduction

If you want an F1-like loss in Keras, you cannot use the ordinary hard F1 formula directly because thresholding makes it non-differentiable. The usual solution is a soft approximation that computes differentiable precision and recall from probabilities, then combines per-class soft F1 values with weights.

Why Hard F1 Fails as a Loss

Classic F1 uses true positives, false positives, and false negatives from discrete predictions. That is fine for reporting metrics, but not for gradient-based training. A hard threshold destroys useful gradients.

So the training version usually replaces hard counts with soft ones computed from probabilities. The loss then becomes 1 - weighted_soft_f1.

A Weighted Soft F1 Loss

python
1import tensorflow as tf
2
3
4def weighted_soft_f1_loss(class_weights):
5    class_weights = tf.constant(class_weights, dtype=tf.float32)
6
7    def loss(y_true, y_pred):
8        y_true = tf.cast(y_true, tf.float32)
9        y_pred = tf.clip_by_value(y_pred, 1e-7, 1.0 - 1e-7)
10
11        tp = tf.reduce_sum(y_true * y_pred, axis=0)
12        fp = tf.reduce_sum((1.0 - y_true) * y_pred, axis=0)
13        fn = tf.reduce_sum(y_true * (1.0 - y_pred), axis=0)
14
15        soft_f1 = (2.0 * tp) / (2.0 * tp + fp + fn + 1e-7)
16        weighted_f1 = tf.reduce_sum(class_weights * soft_f1) / tf.reduce_sum(class_weights)
17        return 1.0 - weighted_f1
18
19    return loss

This assumes one-hot labels and class probabilities from a softmax output.

Example Usage

python
1import numpy as np
2import tensorflow as tf
3
4X = np.random.randn(200, 4).astype("float32")
5y = np.random.randint(0, 3, size=(200,))
6y = tf.keras.utils.to_categorical(y, num_classes=3)
7
8model = tf.keras.Sequential([
9    tf.keras.layers.Input(shape=(4,)),
10    tf.keras.layers.Dense(16, activation="relu"),
11    tf.keras.layers.Dense(3, activation="softmax"),
12])
13
14model.compile(
15    optimizer="adam",
16    loss=weighted_soft_f1_loss([1.0, 2.0, 3.0])
17)
18
19model.fit(X, y, epochs=3, batch_size=32, verbose=0)

The weight vector lets you emphasize some classes more than others.

Choosing the Weights

"Weighted average" can mean two different things:

  • support-weighted averaging, where larger classes count more
  • business-weighted averaging, where important classes count more regardless of frequency

Be explicit about which one you want. They optimize different goals.

This matters most on imbalanced datasets. Support-weighted loss can still let majority classes dominate training, while business-weighted loss can deliberately prioritize rare classes whose mistakes are more expensive.

When to Combine with Cross-Entropy

Soft F1 can help on imbalanced data, but it is often less stable than cross-entropy. A common compromise is to combine them so the model learns both class separation and F1-oriented behavior.

python
1def combined_loss(class_weights):
2    f1_loss = weighted_soft_f1_loss(class_weights)
3
4    def loss(y_true, y_pred):
5        ce = tf.keras.losses.categorical_crossentropy(y_true, y_pred)
6        return ce + f1_loss(y_true, y_pred)
7
8    return loss

That hybrid setup is often easier to tune because cross-entropy gives the optimizer a smoother signal early in training, while the F1 term nudges the model toward the precision and recall balance you actually care about at evaluation time.

It is also easier to compare against a strong baseline that way. If the combined loss improves validation F1 without making optimization unstable, you have a clearer justification for keeping the custom objective.

That makes ablation testing far more honest.

Common Pitfalls

  • Using hard thresholds inside the loss.
  • Forgetting to clip probabilities for numerical stability.
  • Mixing sparse labels with one-hot labels incorrectly.
  • Assuming support-weighted and business-weighted F1 mean the same thing.
  • Expecting pure F1 loss to produce well-calibrated probabilities.

Summary

  • Ordinary F1 is not suitable as a direct training loss.
  • Use a differentiable soft approximation instead.
  • Weighted soft F1 can be written cleanly as 1 - weighted_f1.
  • Be clear about what the class weights are supposed to represent.
  • Cross-entropy plus soft F1 is often more stable than pure F1 loss alone.

Course illustration
Course illustration

All Rights Reserved.