@@ -671,6 +671,46 @@ def kullback_leibler_divergence(y_true: np.ndarray, y_pred: np.ndarray) -> float
671671 return np .sum (kl_loss )
672672
673673
674+ def symmetric_mean_absolute_percentage_error (
675+ y_true : np .ndarray , y_pred : np .ndarray , epsilon : float = 1e-15
676+ ) -> float :
677+ """
678+ Calculate the Symmetric Mean Absolute Percentage Error (SMAPE) between y_true and
679+ y_pred.
680+
681+ SMAPE is an accuracy measure based on percentage (or relative) errors. It is
682+ symmetric and treats over- and under- predictions equally.
683+
684+ SMAPE = (1/n) * Σ( |y_true - y_pred| / ((|y_true| + |y_pred|) / 2) )
685+
686+ Reference: https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error
687+
688+ Parameters:
689+ - y_true: The true values (ground truth)
690+ - y_pred: The predicted values
691+ - epsilon: Small constant to avoid division by zero
692+
693+ >>> true_values = np.array([100, 200, 300, 400])
694+ >>> predicted_values = np.array([110, 190, 310, 420])
695+ >>> float(symmetric_mean_absolute_percentage_error(true_values, predicted_values))
696+ 0.05702187989273155
697+ >>> true_labels = np.array([100, 200, 300])
698+ >>> predicted_probs = np.array([110, 190, 310, 420])
699+ >>> symmetric_mean_absolute_percentage_error(true_labels, predicted_probs)
700+ Traceback (most recent call last):
701+ ...
702+ ValueError: Input arrays must have the same length.
703+ """
704+ if len (y_true ) != len (y_pred ):
705+ raise ValueError ("Input arrays must have the same length." )
706+
707+ denominator = (np .abs (y_true ) + np .abs (y_pred )) / 2.0
708+ denominator = np .where (denominator == 0 , epsilon , denominator )
709+
710+ smape_loss = np .abs (y_true - y_pred ) / denominator
711+ return np .mean (smape_loss )
712+
713+
674714if __name__ == "__main__" :
675715 import doctest
676716
0 commit comments