Source code for numpynet.losses.functions

import numpy as np


[docs] class Loss:
[docs] def forward(self, y_pred, y_true): raise NotImplementedError
[docs] def backward(self, y_pred, y_true): raise NotImplementedError
def __call__(self, y_pred, y_true): return self.forward(y_pred, y_true)
[docs] class MeanSquaredError(Loss):
[docs] def forward(self, y_pred, y_true): return np.mean((y_pred - y_true) ** 2)
[docs] def backward(self, y_pred, y_true): n = y_pred.shape[0] return 2.0 * (y_pred - y_true) / n
[docs] class MeanAbsoluteError(Loss):
[docs] def forward(self, y_pred, y_true): return np.mean(np.abs(y_pred - y_true))
[docs] def backward(self, y_pred, y_true): n = y_pred.shape[0] return np.sign(y_pred - y_true) / n
[docs] class HuberLoss(Loss): def __init__(self, delta=1.0): self.delta = delta
[docs] def forward(self, y_pred, y_true): diff = np.abs(y_pred - y_true) quadratic = np.minimum(diff, self.delta) linear = diff - quadratic return np.mean(0.5 * quadratic ** 2 + self.delta * linear)
[docs] def backward(self, y_pred, y_true): n = y_pred.shape[0] diff = y_pred - y_true abs_diff = np.abs(diff) return np.where(abs_diff <= self.delta, diff, self.delta * np.sign(diff)) / n
[docs] class BinaryCrossentropy(Loss): def __init__(self, from_logits=False, eps=1e-7): self.from_logits = from_logits self.eps = eps
[docs] def forward(self, y_pred, y_true): if self.from_logits: y_pred = 1.0 / (1.0 + np.exp(-np.clip(y_pred, -500, 500))) y_pred = np.clip(y_pred, self.eps, 1.0 - self.eps) return -np.mean(y_true * np.log(y_pred) + (1.0 - y_true) * np.log(1.0 - y_pred))
[docs] def backward(self, y_pred, y_true): n = y_pred.shape[0] if self.from_logits: y_pred_sig = 1.0 / (1.0 + np.exp(-np.clip(y_pred, -500, 500))) return (y_pred_sig - y_true) / n y_pred = np.clip(y_pred, self.eps, 1.0 - self.eps) return (-(y_true / y_pred) + (1.0 - y_true) / (1.0 - y_pred)) / n
[docs] class CategoricalCrossentropy(Loss): def __init__(self, from_logits=False, eps=1e-7): self.from_logits = from_logits self.eps = eps def _softmax(self, x): shifted = x - np.max(x, axis=-1, keepdims=True) exp_x = np.exp(shifted) return exp_x / np.sum(exp_x, axis=-1, keepdims=True)
[docs] def forward(self, y_pred, y_true): if self.from_logits: y_pred = self._softmax(y_pred) y_pred = np.clip(y_pred, self.eps, 1.0 - self.eps) return -np.mean(np.sum(y_true * np.log(y_pred), axis=-1))
[docs] def backward(self, y_pred, y_true): n = y_pred.shape[0] if self.from_logits: probs = self._softmax(y_pred) return (probs - y_true) / n y_pred = np.clip(y_pred, self.eps, 1.0 - self.eps) return -(y_true / y_pred) / n
[docs] class SparseCategoricalCrossentropy(Loss): def __init__(self, from_logits=False, eps=1e-7): self.from_logits = from_logits self.eps = eps def _softmax(self, x): shifted = x - np.max(x, axis=-1, keepdims=True) exp_x = np.exp(shifted) return exp_x / np.sum(exp_x, axis=-1, keepdims=True) def _to_one_hot(self, y_true, n_classes): n = y_true.shape[0] one_hot = np.zeros((n, n_classes)) one_hot[np.arange(n), y_true.astype(int)] = 1.0 return one_hot
[docs] def forward(self, y_pred, y_true): if self.from_logits: y_pred = self._softmax(y_pred) y_pred = np.clip(y_pred, self.eps, 1.0 - self.eps) n = y_pred.shape[0] idx = y_true.astype(int).flatten() return -np.mean(np.log(y_pred[np.arange(n), idx]))
[docs] def backward(self, y_pred, y_true): n = y_pred.shape[0] if self.from_logits: probs = self._softmax(y_pred) one_hot = self._to_one_hot(y_true.flatten(), y_pred.shape[-1]) return (probs - one_hot) / n y_pred = np.clip(y_pred, self.eps, 1.0 - self.eps) one_hot = self._to_one_hot(y_true.flatten(), y_pred.shape[-1]) return -(one_hot / y_pred) / n
[docs] class KLDivergence(Loss): def __init__(self, eps=1e-7): self.eps = eps
[docs] def forward(self, y_pred, y_true): y_pred = np.clip(y_pred, self.eps, 1.0) y_true = np.clip(y_true, self.eps, 1.0) return np.mean(np.sum(y_true * np.log(y_true / y_pred), axis=-1))
[docs] def backward(self, y_pred, y_true): n = y_pred.shape[0] y_pred = np.clip(y_pred, self.eps, 1.0) y_true = np.clip(y_true, self.eps, 1.0) return -(y_true / y_pred) / n
_REGISTRY = { "mse": MeanSquaredError, "mean_squared_error": MeanSquaredError, "mae": MeanAbsoluteError, "mean_absolute_error": MeanAbsoluteError, "huber": HuberLoss, "binary_crossentropy": BinaryCrossentropy, "categorical_crossentropy": CategoricalCrossentropy, "sparse_categorical_crossentropy": SparseCategoricalCrossentropy, "kl_divergence": KLDivergence, }
[docs] def get(identifier): if isinstance(identifier, Loss): return identifier if isinstance(identifier, str): key = identifier.lower() if key in _REGISTRY: return _REGISTRY[key]() raise ValueError(f"Unknown loss: '{identifier}'. Available: {list(_REGISTRY)}") raise TypeError(f"Could not interpret loss: {identifier}")