Keras
precision
recall
F1 score
machine learning

Getting precision, recall and F1 score per class in Keras

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

If you want precision, recall, and F1 score per class in Keras, the most practical approach is usually to compute predictions first and then evaluate them outside the training loop. Keras provides built-in metrics, but per-class metrics for multiclass classification are much easier and more reliable to obtain with a post-processing step such as classification_report.

Core Sections

Why built-in Keras metrics are not the whole answer

Keras has metrics such as Precision and Recall, but those are not automatically "per class" for a multiclass softmax model in the way many people expect. They are usually threshold-oriented or aggregate over predictions, which makes them less convenient when you want a class-by-class table.

For per-class reporting, the normal workflow is:

  1. run model.predict(...)
  2. convert probabilities to predicted class IDs
  3. compare predictions against the true labels
  4. calculate per-class metrics using a reporting library

That keeps evaluation explicit and avoids confusion about what the training-time metric actually means.

Post-training evaluation with classification_report

For multiclass problems, scikit-learn is the easiest way to get per-class precision, recall, and F1.

python
1import numpy as np
2from sklearn.metrics import classification_report
3
4y_true = np.array([0, 1, 2, 1, 0, 2])
5y_pred_proba = np.array([
6    [0.9, 0.1, 0.0],
7    [0.1, 0.8, 0.1],
8    [0.2, 0.1, 0.7],
9    [0.2, 0.6, 0.2],
10    [0.7, 0.2, 0.1],
11    [0.1, 0.3, 0.6],
12])
13
14y_pred = np.argmax(y_pred_proba, axis=1)
15
16print(classification_report(y_true, y_pred, digits=4))

In a real Keras workflow, y_pred_proba comes from model.predict(x_val). This produces a table with one row per class, which is usually what people mean by "precision, recall and F1 per class."

Full Keras example

python
1import numpy as np
2import tensorflow as tf
3from sklearn.metrics import classification_report
4
5x_train = np.random.randn(100, 8).astype("float32")
6y_train = np.random.randint(0, 3, size=(100,))
7x_val = np.random.randn(20, 8).astype("float32")
8y_val = np.random.randint(0, 3, size=(20,))
9
10model = tf.keras.Sequential([
11    tf.keras.layers.Dense(16, activation="relu"),
12    tf.keras.layers.Dense(3, activation="softmax"),
13])
14
15model.compile(
16    optimizer="adam",
17    loss="sparse_categorical_crossentropy",
18    metrics=["accuracy"],
19)
20
21model.fit(x_train, y_train, epochs=3, verbose=0)
22
23predictions = model.predict(x_val, verbose=0)
24y_pred = np.argmax(predictions, axis=1)
25
26print(classification_report(y_val, y_pred, digits=4))

That gives you per-class metrics after training or validation without needing fragile custom metric code inside the model compile step.

If you want metrics after every epoch

Sometimes you want class-level metrics on the validation set after each epoch. A callback is a good place for that.

python
1import numpy as np
2from sklearn.metrics import classification_report
3import tensorflow as tf
4
5class PerClassMetricsCallback(tf.keras.callbacks.Callback):
6    def __init__(self, x_val, y_val):
7        super().__init__()
8        self.x_val = x_val
9        self.y_val = y_val
10
11    def on_epoch_end(self, epoch, logs=None):
12        predictions = self.model.predict(self.x_val, verbose=0)
13        y_pred = np.argmax(predictions, axis=1)
14        print(f"\nEpoch {epoch + 1} report:")
15        print(classification_report(self.y_val, y_pred, digits=4))

This is heavier than scalar metrics, but it is useful when you care about minority-class behavior during training.

Binary versus multiclass details

Be careful with label shape and prediction conversion:

  • binary sigmoid output usually needs thresholding, such as pred > 0.5
  • multiclass softmax output usually needs argmax
  • one-hot labels may need argmax before comparison

If the shapes do not match, the metric report can look wrong even though the model itself is fine.

When custom Keras metrics still make sense

If you only need macro-averaged precision or recall during training, a custom Keras metric can be reasonable. But true per-class reporting is usually easier outside the compiled metric system because it naturally produces a table instead of one scalar.

That separation also makes evaluation reproducible across frameworks and easier to compare with other models.

Common Pitfalls

  • Expecting built-in Keras Precision and Recall metrics to automatically give per-class multiclass reports.
  • Forgetting to convert softmax probabilities to class IDs with argmax before computing the report.
  • Comparing one-hot encoded labels directly to integer class predictions without reshaping or decoding first.
  • Reporting metrics on the training set only and assuming they reflect real validation behavior.
  • Printing per-class reports every epoch on huge validation sets without considering the extra evaluation cost.

Summary

  • Per-class precision, recall, and F1 are usually easiest to compute after model.predict(...).
  • For multiclass models, convert predicted probabilities to class IDs with argmax.
  • 'classification_report from scikit-learn is the most practical tool for class-by-class evaluation.'
  • Use a callback only if you need those reports during training rather than after it.
  • Keep training-time scalar metrics and detailed evaluation reports as separate concerns.

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