Hello Model Services
← Glossary

Glossary

What is the F1 score?

The F1 score combines precision and recall into one number between 0 and 1. It’s high only when both are high.

How it’s calculated

F1 is the harmonic mean of precision and recall: F1 = 2 × precision × recall ÷ (precision + recall). With 80% precision and 60% recall, F1 is 2 × 0.8 × 0.6 ÷ 1.4 ≈ 0.69.

Why not a simple average? The average of 80% and 60% is 70%, close to F1. But take a model with 100% precision and 2% recall: it flags one case, correctly, and misses everything else. Its simple average is 51%, which sounds acceptable. Its F1 is about 0.04, which correctly says it’s useless. The harmonic mean is pulled down by the weaker of the two numbers, so you can’t hide a terrible recall behind a perfect precision.

When to use it

  • The positive class is rare, such as fraud, defects or spam, and accuracy would look good no matter what.
  • Both kinds of mistake matter and you don’t have clear costs for each yet.
  • You want one number to compare models, for example while trying different features.

When not to use it

If you know what each mistake costs, use those costs instead: pick the threshold with the lowest total cost, or set a minimum precision and maximize recall. F1 treats precision and recall as equally important, which is rarely exactly true. It also ignores true negatives entirely, which is fine for rare-event problems but less useful when both classes matter equally.

Macro F1 for several classes

With more than two classes, such as positive, neutral and negative reviews, compute F1 for each class and average them. That’s macro F1. Each class counts equally, so a model that ignores a small class gets a low score, even if it’s right about the big one. A model that always predicts the most common class has a macro F1 close to zero for every other class, which makes it a good way to expose a lazy model.

CodeF1 and macro F1 with scikit-learn
pythonRun in Colab
from sklearn.metrics import f1_score

y_true = ["pos", "pos", "neg", "neu", "pos", "neg"]
y_pred = ["pos", "pos", "neg", "pos", "pos", "pos"]
print("Macro F1:", round(f1_score(y_true, y_pred, average="macro"), 3))

always_pos = ["pos"] * len(y_true)
print("Always 'pos':", round(f1_score(y_true, always_pos, average="macro", zero_division=0), 3))
Tip Report F1 together with precision and recall, not on its own. An F1 of 0.7 could mean balanced performance, or excellent precision and mediocre recall, and those lead to different decisions.

Planning a project? Describe your idea and get a step-by-step plan that covers this.

Or browse the whole glossary.