Skip to content
Back to skills

Differentiable Soft F1 Loss

ASecurity

Use a soft macro-F1 loss `1 − mean(2·tp / (2·tp + fp + fn))` computed from raw sigmoid probabilities (no thresholding) as a direct training objective for multi-label classification, optionally combined with BCE — closes the gap between training surrogate and the F1 metric the leaderboard scores

  • 61 stars
  • 0 votes
  • 0 copies
  • 6 views
  • Added September 12, 2026
developmentpythonbackend

Security analysis

A100/100

Scanned September 12, 2026

npx -y skills add wenmin-wu/ds-skills --skill differentiable-soft-f1-loss --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Differentiable Soft F1 Loss?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Differentiable Soft F1 Loss
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wenmin-wu-differentiable-soft-f1-loss/badge)](https://www.skillsdirectory.com/skills/wenmin-wu-differentiable-soft-f1-loss)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cv-differentiable-soft-f1-loss
description: Use a soft macro-F1 loss `1 − mean(2·tp / (2·tp + fp + fn))` computed from raw sigmoid probabilities (no thresholding) as a direct training objective for multi-label classification, optionally combined with BCE — closes the gap between training surrogate and the F1 metric the leaderboard scores
---

## Overview

When the eval metric is macro-F1, BCE is a poor proxy: it weights every label equally regardless of base rate, and it doesn't push the model to actually pass a threshold. The fix is a *differentiable* macro-F1 surrogate computed directly from sigmoid probabilities — no thresholding, no rounding. Sum probability-weighted TP/FP/FN over the batch, compute per-class F1, average, return `1 − mean_F1`. The loss is well-behaved as long as you mask NaNs from empty classes. In practice it is most effective as a co-loss (`α·BCE + (1−α)·F1_soft`) — pure F1 loss has weak gradients early when no class is being predicted at all; BCE bootstraps confidence and F1 sharpens it.

## Quick Start

```python
import keras.backend as K
import tensorflow as tf

def f1_loss(y_true, y_pred):
    tp = K.sum(y_true * y_pred, axis=0)
    fp = K.sum((1 - y_true) * y_pred, axis=0)
    fn = K.sum(y_true * (1 - y_pred), axis=0)
    p = tp / (tp + fp + K.epsilon())
    r = tp / (tp + fn + K.epsilon())
    f1 = 2 * p * r / (p + r + K.epsilon())
    f1 = tf.where(tf.is_nan(f1), tf.zeros_like(f1), f1)
    return 1 - K.mean(f1)

def combo_loss(alpha=0.5):
    bce = tf.keras.losses.BinaryCrossentropy()
    return lambda y, p: alpha * bce(y, p) + (1 - alpha) * f1_loss(y, p)

model.compile(optimizer='adam', loss=combo_loss(0.5))
```

## Workflow

1. Implement the soft-F1 reduction over the batch axis (axis=0), per class
2. Mask NaN / Inf from classes with zero positives in the batch — `tf.where(tf.is_nan(f1), 0, f1)`
3. Train with combo loss for the first ~10 epochs (BCE-weighted), then anneal `alpha` to 0 to fully optimize F1
4. Pair with per-class threshold tuning at inference for the final lift
5. Watch out for very small batches — soft-F1 estimate becomes noisy when batch < 32

## Key Decisions

- **No thresholding inside the loss**: rounding kills gradients; use raw probabilities and let the loss push them apart.
- **Macro vs. micro**: macro-F1 (averaged across classes) is the right pairing for class-imbalanced multi-label; micro-F1 is dominated by frequent classes and barely differs from BCE.
- **Combo with BCE is almost always better than pure F1**: BCE provides stable early gradients that pure F1 lacks when predictions are still around 0.5.
- **Larger batches help**: 64+ samples per batch give enough positives per rare class for the soft-F1 estimate to be reliable.
- **Don't average across batch examples**: average across classes, sum across the batch — that's the macro definition.
- **NaN guard is mandatory**: classes with no positives produce 0/0; without the mask the model collapses.

## References

- [Best loss function for F1-score metric](https://www.kaggle.com/code/rejpalcz/best-loss-function-for-f1-score-metric)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.