Loss Functions¶
fusionlab provides several custom loss functions tailored for
advanced time series forecasting tasks, particularly those involving
probabilistic (quantile) predictions and integrated anomaly
detection. These functions are designed to be compatible with the
Keras API (e.g., model.compile(loss=...)).
Understanding these losses is key to training models like
TemporalFusionTransformer and
XTFT effectively, especially when dealing
with uncertainty estimation or anomaly-aware training strategies.
Quantile Loss Functions¶
These functions are used when the goal is to predict specific quantiles of the target distribution, enabling probabilistic forecasts. They typically return a callable loss function suitable for Keras compile.
quantile_loss¶
- API Reference:
Purpose: Creates a Keras-compatible loss function that computes the quantile (pinball) loss for a single, specified quantile \(q\).
Functionality: Takes a single quantile \(q\) (between 0 and 1) as input and returns a function loss_fn(y_true, y_pred). This returned function calculates the pinball loss:
where \(error = y_{true} - y_{pred}\). The mean is typically taken across all dimensions except the last feature dimension.
Usage Context: Useful when you need to train a model to predict
only one specific quantile of the target distribution. Pass the
result of this function to model.compile. For example:
model.compile(loss=quantile_loss(q=0.75)) would train the model
to predict the 75th percentile.
Code Example:
1import tensorflow as tf
2import numpy as np
3from fusionlab.nn.losses import quantile_loss
4
5# Config
6batch_size = 4
7horizon = 6
8output_dim = 1
9quantile_to_predict = 0.75
10
11# Dummy true values and predictions for a SINGLE quantile/output
12y_true = tf.random.normal((batch_size, horizon, output_dim))
13y_pred = y_true + tf.random.normal(tf.shape(y_true), stddev=0.5)
14
15# Create the loss function for the specific quantile
16loss_fn_q75 = quantile_loss(q=quantile_to_predict)
17
18# Calculate the loss
19loss_value = loss_fn_q75(y_true, y_pred)
20
21print(f"y_true shape: {y_true.shape}")
22print(f"y_pred shape (single quantile): {y_pred.shape}")
23print(f"Calculated Loss for q={quantile_to_predict}: {loss_value.numpy():.4f}")
quantile_loss_multi¶
- API Reference:
Purpose: Creates a Keras-compatible loss function that computes the average quantile (pinball) loss across a list of specified quantiles.
Functionality:
Takes a list of quantiles (e.g., [0.1, 0.5, 0.9]) as input and
returns a function loss_fn(y_true, y_pred). The model’s prediction
y_pred is expected to have a final dimension matching the number of
quantiles. The returned function calculates the pinball loss \(L_q\)
(as defined in quantile_loss()) for each quantile \(q\)
and corresponding prediction slice, then computes the average of these
individual quantile losses.
where \(Q\) is the set of specified quantiles.
Usage Context: Intended for training models that output predictions for multiple quantiles simultaneously. The model’s output layer should typically have a final dimension whose size equals the number of quantiles. This function provides one way to achieve multi-quantile training. Ensure the model output shape is compatible.
Code Example:
1import tensorflow as tf
2import numpy as np
3from fusionlab.nn.losses import quantile_loss_multi
4
5# Config
6batch_size = 4
7horizon = 6
8output_dim = 1 # Univariate target
9quantiles = [0.1, 0.5, 0.9]
10num_quantiles = len(quantiles)
11
12# Dummy true values (B, H, O=1)
13y_true = tf.random.normal((batch_size, horizon, output_dim))
14# Dummy predicted quantiles (B, H, Q) - Assuming O=1 is squeezed
15y_pred_multi_q = tf.random.normal((batch_size, horizon, num_quantiles))
16
17# Create the loss function
18loss_fn_multi = quantile_loss_multi(quantiles=quantiles)
19
20# Calculate the loss
21loss_value = loss_fn_multi(y_true, y_pred_multi_q)
22
23print(f"y_true shape: {y_true.shape}")
24print(f"y_pred shape (multi-quantile): {y_pred_multi_q.shape}")
25print(f"Calculated Multi-Quantile Loss: {loss_value.numpy():.4f}")
combined_quantile_loss¶
- API Reference:
Purpose: Creates a Keras-compatible loss function that
calculates the mean quantile loss (pinball loss) averaged across
multiple specified quantiles. This is the primary recommended loss
function for multi-quantile forecasting in fusionlab.
Functionality: This function takes a list of target quantiles (e.g., [0.1, 0.5, 0.9]) and returns another function loss_fn(y_true, y_pred) suitable for Keras. The returned loss_fn performs the following calculation:
Calculates the prediction error: \(error = y_{true} - y_{pred}\). Note that \(y_{true}\) (shape \((B, H, O)\)) is typically expanded and broadcasted internally to match the shape of \(y_{pred}\) which includes the quantile dimension (e.g., shape \((B, H, Q)\) or \((B, H, Q, O)\)).
For each specified quantile \(q\) in the quantiles list, it computes the pinball loss:
\[\text{Loss}_q(error) = \max(q \cdot error, (q - 1) \cdot error)\]It averages the loss across all dimensions (batch B, horizon H, quantiles Q, output O if present).
Usage Context: This is the standard loss function to use with
model.compile when training a model (like TFT or XTFT) that is
configured to output predictions for multiple quantiles. The use of
@register_keras_serializable within the factory ensures models
compiled with this loss can often be saved and loaded correctly.
Code Example:
1import tensorflow as tf
2import numpy as np
3from fusionlab.nn.losses import combined_quantile_loss
4
5# Config
6batch_size = 4
7horizon = 6
8output_dim = 1 # Univariate target
9quantiles = [0.1, 0.5, 0.9]
10num_quantiles = len(quantiles)
11
12# Dummy true values (B, H, O=1)
13y_true = tf.random.normal((batch_size, horizon, output_dim))
14# Dummy predicted quantiles (B, H, Q) - Assuming O=1 is squeezed
15y_pred_multi_q = tf.random.normal((batch_size, horizon, num_quantiles))
16
17# Create the loss function using the factory
18loss_fn_combined = combined_quantile_loss(quantiles=quantiles)
19
20# Calculate the loss
21loss_value = loss_fn_combined(y_true, y_pred_multi_q)
22
23print(f"y_true shape: {y_true.shape}")
24print(f"y_pred shape (multi-quantile): {y_pred_multi_q.shape}")
25print(f"Calculated Combined Quantile Loss: {loss_value.numpy():.4f}")
26
27# Typical compilation:
28# model.compile(optimizer='adam', loss=loss_fn_combined)
Anomaly & Combined Loss Functions¶
These functions integrate anomaly detection signals into the training objective, often combining them with a primary forecasting loss like the quantile loss. They typically return callable functions suitable for Keras compile.
anomaly_loss¶
- API Reference:
Purpose: Creates a Keras-compatible loss function based on fixed, pre-provided anomaly scores. This allows incorporating an anomaly penalty into the total loss where the anomaly scores themselves are static inputs captured when the loss function is created.
Functionality: Takes a tensor of anomaly_scores and an anomaly_loss_weight during initialization. It returns a Keras loss function \(loss\_fn(y_{true}, y_{pred})\). Crucially, this returned function ignores \(y_{true}\) and \(y_{pred}\) and computes the loss only based on the anomaly_scores provided when the loss function was created:
where \(w_{anomaly}\) is the anomaly_loss_weight.
Usage Context: This function differs significantly from the
AnomalyLoss layer (which processes
dynamic scores). This function captures scores at definition time.
It might be used in specific scenarios where anomaly scores are fixed
throughout training and treated purely as an additional static penalty
term. Its direct use might be less common than using the AnomalyLoss
layer within combined loss strategies like
combined_total_loss().
Code Example:
1import tensorflow as tf
2import numpy as np
3from fusionlab.nn.losses import anomaly_loss
4
5# Config
6batch_size = 4
7horizon = 6
8output_dim = 1
9anomaly_weight = 0.1
10
11# Dummy anomaly scores (fixed for the loss function)
12# Shape needs to be considered carefully based on how mean is taken
13dummy_scores = tf.constant(
14 np.random.rand(batch_size, horizon, output_dim) * 0.5,
15 dtype=tf.float32
16)
17
18# Create the loss function, capturing the scores
19loss_fn_anomaly = anomaly_loss(
20 anomaly_scores=dummy_scores,
21 anomaly_loss_weight=anomaly_weight
22)
23
24# Dummy y_true/y_pred (ignored by this specific loss function)
25y_true = tf.random.normal((batch_size, horizon, output_dim))
26y_pred = tf.random.normal((batch_size, horizon, output_dim))
27
28# Calculate the loss (depends only on captured scores and weight)
29loss_value = loss_fn_anomaly(y_true, y_pred)
30
31print(f"Captured anomaly scores shape: {dummy_scores.shape}")
32print(f"Calculated Anomaly Loss (fixed scores): {loss_value.numpy():.4f}")
prediction_based_loss¶
- API Reference:
Purpose: Creates a Keras-compatible loss function specifically
for the ‘prediction_based’ anomaly detection strategy used in
XTFT. This strategy defines anomalies based
on the magnitude of prediction errors.
Functionality: This function takes optional quantiles and an anomaly_loss_weight and returns a Keras loss function \(loss\_fn(y_{true}, y_{pred})\). The returned \(loss\_fn\) computes two components internally:
Prediction Loss (:math:`L_{pred}`):
If quantiles are provided: Standard quantile loss based on
fusionlab.nn.losses.combined_quantile_loss().If quantiles is None: Standard Mean Squared Error (MSE). \(L_{pred} = \text{mean}((y_{true} - y_{pred})^2)\).
Anomaly Loss (:math:`L_{anomaly}`):
Calculates prediction error \(|y_{true} - y_{pred}|\). If predicting quantiles, the error relative to the median (or average across quantiles) might be used.
Anomaly loss is the mean squared value of these errors: \(L_{anomaly} = \text{mean}(\text{error}^2)\).
Total Loss: Weighted sum:
\[L_{total} = L_{pred} + w_{anomaly} \cdot L_{anomaly}\]where \(w_{anomaly}\) is the anomaly_loss_weight.
Usage Context: This function should be used to create the loss
for model.compile only when using the ‘prediction_based’
anomaly detection strategy in XTFT. It allows
the model to simultaneously minimize forecasting error and penalize
large prediction errors (treating them as anomalies).
Code Example:
1import tensorflow as tf
2import numpy as np
3from fusionlab.nn.losses import prediction_based_loss
4
5# Config
6batch_size = 4
7horizon = 6
8output_dim = 1
9quantiles = [0.1, 0.5, 0.9] # Example for quantile mode
10num_quantiles = len(quantiles)
11anomaly_weight = 0.05
12
13# Create the loss function for quantile + prediction-based anomaly
14loss_fn_pred_based = prediction_based_loss(
15 quantiles=quantiles,
16 anomaly_loss_weight=anomaly_weight
17)
18
19# Dummy true values (B, H, O=1)
20y_true = tf.random.normal((batch_size, horizon, output_dim))
21# Dummy predicted quantiles (B, H, Q)
22y_pred_quantiles = tf.random.normal((batch_size, horizon, num_quantiles))
23
24# Calculate the combined loss
25loss_value = loss_fn_pred_based(y_true, y_pred_quantiles)
26
27print(f"y_true shape: {y_true.shape}")
28print(f"y_pred shape (multi-quantile): {y_pred_quantiles.shape}")
29print(f"Calculated Prediction-Based Loss: {loss_value.numpy():.4f}")
30
31# Example for point forecast mode
32loss_fn_point = prediction_based_loss(quantiles=None, anomaly_loss_weight=0.1)
33y_pred_point = tf.random.normal((batch_size, horizon, output_dim))
34loss_value_point = loss_fn_point(y_true, y_pred_point)
35print(f"\nCalculated Prediction-Based Loss (Point): {loss_value_point.numpy():.4f}")
combined_total_loss¶
- API Reference:
Purpose: Creates a Keras-compatible loss function that combines a standard quantile loss with an anomaly loss derived from pre-computed or externally provided anomaly scores captured at the time of loss creation. This is primarily used for the ‘from_config’ anomaly detection strategy.
Functionality:
This function takes the quantiles list, an instance of the
AnomalyLoss layer (anomaly_layer),
and a tensor of fixed anomaly_scores as input. It returns a Keras
loss function \(loss\_fn(y_{true}, y_{pred})\). The returned
\(loss\_fn\) computes:
Quantile Loss (:math:`L_{quantile}`): Calculated using the internal
combined_quantile_loss()logic based on quantiles, \(y_{true}\), and \(y_{pred}\).Anomaly Loss (:math:`L_{anomaly}`): Calculated by calling the provided anomaly_layer with the fixed anomaly_scores tensor that was passed during the creation of this loss function. Typically: \(L_{anomaly} = w \cdot \text{mean}(\text{anomaly\_{scores}}^2)\).
Total Loss: \(L_{total} = L_{quantile} + L_{anomaly}\)
Usage Context: Used to create the loss for model.compile when
using the ‘from_config’ anomaly detection strategy in
XTFT. Requires providing the anomaly_scores
tensor when creating the loss function. (Note: Aligning these
fixed scores with training batches within `model.fit` can be complex;
using `model.add_loss` in a custom `train_step` might be more robust
for `’from_config’`).
Code Example:
1import tensorflow as tf
2import numpy as np
3from fusionlab.nn.losses import combined_total_loss
4from fusionlab.nn.components import AnomalyLoss
5
6# Config
7batch_size = 4
8horizon = 6
9output_dim = 1
10quantiles = [0.1, 0.5, 0.9]
11num_quantiles = len(quantiles)
12anomaly_weight = 0.05
13
14# 1. Instantiate the anomaly loss layer component
15anomaly_loss_layer = AnomalyLoss(weight=anomaly_weight)
16
17# 2. Provide FIXED anomaly scores (e.g., for training data)
18# Shape needs careful handling based on loss implementation
19# Assuming (B, H, O) or compatible shape for demo
20dummy_scores_train = tf.constant(
21 np.random.rand(batch_size, horizon, output_dim) * 0.2,
22 dtype=tf.float32
23)
24
25# 3. Create the combined loss function, capturing scores
26loss_fn_total = combined_total_loss(
27 quantiles=quantiles,
28 anomaly_layer=anomaly_loss_layer,
29 anomaly_scores=dummy_scores_train # Pass fixed scores
30)
31
32# 4. Dummy data for calculation demo
33y_true = tf.random.normal((batch_size, horizon, output_dim))
34y_pred_quantiles = tf.random.normal((batch_size, horizon, num_quantiles))
35
36# 5. Calculate the loss
37# Uses y_true/y_pred for quantile part, uses captured scores for anomaly part
38loss_value = loss_fn_total(y_true, y_pred_quantiles)
39
40print(f"Captured anomaly scores shape: {dummy_scores_train.shape}")
41print(f"y_true shape: {y_true.shape}")
42print(f"y_pred shape: {y_pred_quantiles.shape}")
43print(f"Calculated Combined Total Loss: {loss_value.numpy():.4f}")
Loss Function Wrappers/Factories¶
These functions help in constructing or wrapping loss components for use with Keras.
objective_loss¶
- API Reference:
Purpose: To create a standard Keras-compatible loss function
signature \(loss(y_{true}, y_{pred})\) from a pre-configured
MultiObjectiveLoss layer instance,
potentially incorporating fixed anomaly_scores captured at creation time.
Functionality: This function acts as a bridge or factory. It takes an instantiated multi_obj_loss layer (which internally holds other loss layers like AdaptiveQuantileLoss and AnomalyLoss) and optional fixed anomaly_scores. It returns a standard Keras loss function _loss_fn(y_true, y_pred). When Keras calls _loss_fn, it internally invokes the multi_obj_loss layer’s call method, passing along y_true, y_pred, and the captured anomaly_scores in a way the MultiObjectiveLoss layer expects (requiring careful design of the MultiObjectiveLoss.call signature or data format).
Usage Context: Provides a way to package a configured
MultiObjectiveLoss layer and potentially
fixed anomaly scores into the standard loss(y_true, y_pred) format
expected by model.compile. This might be used to simplify compilation
when dealing with multi-task objectives managed by the
MultiObjectiveLoss layer, particularly for strategies like ‘from_config’
where scores are fixed.
Code Example (Instantiation):
1import tensorflow as tf
2from fusionlab.nn.losses import objective_loss
3from fusionlab.nn.components import (
4 MultiObjectiveLoss, AdaptiveQuantileLoss, AnomalyLoss
5)
6
7# Config
8quantiles = [0.1, 0.5, 0.9]
9anomaly_weight = 0.05
10batch_size, horizon, output_dim = 4, 6, 1
11
12# 1. Instantiate individual loss components
13quantile_loss_comp = AdaptiveQuantileLoss(quantiles=quantiles)
14anomaly_loss_comp = AnomalyLoss(weight=anomaly_weight)
15
16# 2. Instantiate the multi-objective loss layer
17multi_loss_layer = MultiObjectiveLoss(
18 quantile_loss_fn=quantile_loss_comp,
19 anomaly_loss_fn=anomaly_loss_comp
20)
21
22# 3. Provide FIXED anomaly scores (if needed by multi_loss_layer's logic)
23dummy_scores = tf.constant(
24 np.random.rand(batch_size, horizon, output_dim) * 0.2,
25 dtype=tf.float32
26)
27
28# 4. Create the Keras-compatible loss function using the factory
29keras_loss_fn = objective_loss(
30 multi_obj_loss=multi_loss_layer,
31 anomaly_scores=dummy_scores # Pass scores to be captured by the wrapper
32)
33
34print("Keras-compatible objective_loss function created.")
35# Now use this in compile:
36# model.compile(optimizer='adam', loss=keras_loss_fn)
37# Note: model.fit needs to provide y_true/y_pred in a format
38# that the internal MultiObjectiveLoss understands.