EasyLoss Functions

Implement Huber Loss

Loss Functions

Easy

Problem

Compute the mean Huber loss between targets and predictions. For error e = y_{true}-y_{pred}:

L_{\delta}(e) = \begin{cases} \frac{1}{2}e^2, & |e| \le \delta \\ \delta\left(|e|-\frac{1}{2}\delta\right), & |e| > \delta \end{cases}

Here, \delta is delta. Apply the piecewise loss elementwise and return its mean as a Python float.

Theory

Mean Squared Error (MSE) is the most common loss function for regression:

\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2

MSE works well when errors are normally distributed, but it has a critical weakness: sensitivity to outliers.

Because errors are squared, large errors dominate the loss:

A single outlier with a large error can completely dominate the loss and pull the model away from fitting the majority of the data well.


Mean Absolute Error: The Other Extreme

Mean Absolute Error (MAE) handles outliers better:

\text{MAE} = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i|

Large errors grow linearly, not quadratically:

MAE is much more robust to outliers. But it has its own problem: the gradient is constant everywhere (either +1 or -1), which can make optimization unstable near the minimum where we want gentle, precise adjustments.


Huber Loss: The Best of Both Worlds

Huber loss combines MSE and MAE. It behaves like MSE for small errors and like MAE for large errors:

L_\delta(y, \hat{y}) = \begin{cases} \frac{1}{2}(y - \hat{y})^2 & \text{if } |y - \hat{y}| \leq \delta \\ \delta \cdot |y - \hat{y}| - \frac{1}{2}\delta^2 & \text{if } |y - \hat{y}| > \delta \end{cases}

where \delta (delta) is a threshold parameter that controls when to switch from quadratic to linear behavior.

The key insight:


Understanding the Formula

Let e = y - \hat{y} be the error. The Huber loss can be written as:

L_\delta(e) = \begin{cases} \frac{1}{2}e^2 & \text{if } |e| \leq \delta \\ \delta |e| - \frac{1}{2}\delta^2 & \text{if } |e| > \delta \end{cases}

Why the specific formula for large errors?

The term \delta |e| - \frac{1}{2}\delta^2 is carefully chosen so that:

  1. The function is continuous at |e| = \delta
  2. The function is differentiable at |e| = \delta (smooth transition)

At the boundary |e| = \delta:

Both pieces give the same value, ensuring continuity.


Concrete Examples

Let \delta = 1.0. Here are some loss values:

Small errors (quadratic region):

Large errors (linear region):

Comparison with MSE for the same errors:

The Huber loss grows much more slowly for large errors, reducing the influence of outliers.


The Gradient of Huber Loss

The gradient (derivative) of Huber loss with respect to the prediction:

\frac{\partial L_\delta}{\partial \hat{y}} = \begin{cases} -(y - \hat{y}) = \hat{y} - y & \text{if } |y - \hat{y}| \leq \delta \\ -\delta \cdot \text{sign}(y - \hat{y}) & \text{if } |y - \hat{y}| > \delta \end{cases}

Key properties:

This bounded gradient is why Huber loss is more stable during training when outliers are present.


Choosing Delta

The threshold \delta is a hyperparameter you must choose:

Small \delta (e.g., 0.1):

Large \delta (e.g., 10.0):

Common default: \delta = 1.0

How to choose:


Huber Loss for a Batch

For a batch of n predictions, the mean Huber loss is:

L = \frac{1}{n} \sum_{i=1}^{n} L_\delta(y_i, \hat{y}_i)

Each sample's loss is computed independently using the piecewise formula, then averaged.


When to Use Huber Loss

Good use cases:

When MSE might be better:

When MAE might be better:


Smooth L1 Loss

In some frameworks (especially object detection), you will see Smooth L1 Loss, which is Huber loss with \delta = 1:

\text{Smooth L1}(e) = \begin{cases} 0.5 e^2 & \text{if } |e| < 1 \\ |e| - 0.5 & \text{otherwise} \end{cases}

This is exactly Huber loss. The different name comes from the computer vision community, where it was popularized by the Fast R-CNN paper for bounding box regression.

Examples

Example 1

Input
y_true = [1, 2, 3], y_pred = [1.5, 1.7, 2.5], delta = 1.0
Output
0.098333
Explanation
Every absolute error is at most 1, so all three terms use the quadratic branch before averaging.

Example 2

Input
y_true = [0, 5], y_pred = [2, 8], delta = 1.0
Output
2.0

Example 3

Input
y_true = [1, 2], y_pred = [1, 2], delta = 1.0
Output
0.0

Hints

  1. Compute absolute_error = np.abs(y_true - y_pred).
  2. Use np.where to select the two loss branches before taking the mean.

Requirements

Constraints

Starter Code

import numpy as np

def huber_loss(y_true: list, y_pred: list, delta: float = 1.0) -> float:
    """
    Returns the loss as a float.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Basicpublic
L1 region 2-elementExample 2public
Perfect 2-elementExample 3public