import numpy as np
[docs]
class LossBase:
def __init__(self, name) -> None:
self.name = name
[docs]
def loss_func(self, y_pred, y_true):
raise NotImplementedError(f"{self.name} must implement the loss_func method.")
[docs]
def loss_gradient(self, y_pred, y_true):
raise NotImplementedError(
f"{self.name} must implement the loss_gradient method."
)
[docs]
class MeanSquaredError(LossBase):
def __init__(self) -> None:
super().__init__("MeanSquaredError")
[docs]
def loss_func(self, y_pred, y_true):
return np.mean((y_pred - y_true) ** 2)
[docs]
def loss_gradient(self, y_pred, y_true):
m = len(y_true)
return 2 * (y_pred - y_true) / m
[docs]
class CategoricalCrossEntropy(LossBase):
def __init__(self) -> None:
super().__init__("CategoricalCrossEntropy")
[docs]
def loss_func(self, y_pred, y_true):
N = len(y_true)
correct = y_pred[np.arange(N), y_true]
return -np.mean(np.log(correct + 1e-8))
[docs]
def loss_gradient(self, y_pred, y_true):
# Fused Softmax + CCE gradient: (y_pred - one_hot(y_true)) / N
N = len(y_true)
grad = y_pred.copy()
grad[np.arange(N), y_true] -= 1.0
return grad / N
[docs]
class BinaryCrossEntropy(LossBase):
def __init__(self) -> None:
super().__init__("BinaryCrossEntropy")
[docs]
def loss_func(self, y_pred, y_true):
return -np.mean(
y_true * np.log(y_pred + 1e-8) + (1 - y_true) * np.log(1 - y_pred + 1e-8)
)
[docs]
def loss_gradient(self, y_pred, y_true):
return (y_pred - y_true) / (y_pred * (1 - y_pred) + 1e-8)
[docs]
class Hinge(LossBase):
def __init__(self) -> None:
super().__init__("Hinge")
[docs]
def loss_func(self, y_pred, y_true):
return np.mean(np.maximum(0, 1 - y_pred * y_true))
[docs]
def loss_gradient(self, y_pred, y_true):
yz = y_pred * y_true
return np.where(yz < 1, -y_true, 0)
[docs]
class Huber(LossBase):
def __init__(self, delta=1.0):
super().__init__("Huber")
self.delta = delta
[docs]
def loss_func(self, y_pred, y_true):
error = y_true - y_pred
L1 = error ** 2 / 2
L2 = self.delta * (np.abs(error) - (self.delta / 2))
return np.mean(np.where(np.abs(error) > self.delta, L1, L2))
[docs]
def loss_gradient(self, y_pred, y_true):
error = y_pred - y_true
return np.where(np.abs(error) > self.delta, self.delta * np.sign(error), error)