-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathErrorFunctions.py
More file actions
165 lines (126 loc) · 4.49 KB
/
ErrorFunctions.py
File metadata and controls
165 lines (126 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import numpy as np
from ErrorLayer import LossFunction
class MSE(LossFunction):
"""
Mean Squared Error (MSE) loss function.
Formula
-------
.. math::
L = \\frac{1}{n} \\sum_i (y_i - \\hat{y}_i)^2
Attributes
----------
None
"""
def forward(self, y_true: np.ndarray, y_pred: np.ndarray) -> float:
"""
Compute the mean squared error.
Parameters
----------
y_true : np.ndarray
True target values.
y_pred : np.ndarray
Predicted values.
Returns
-------
float
The mean squared error.
"""
return np.mean((y_true - y_pred) ** 2)
def backward(self, y_true: np.ndarray, y_pred: np.ndarray) -> np.ndarray:
"""
Compute the gradient of MSE with respect to predictions.
Returns
-------
np.ndarray
Gradient of the loss.
"""
return 2.0 * (y_pred - y_true) / y_true.size
class MAE(LossFunction):
"""
Mean Absolute Error (MAE) loss function.
Formula
-------
.. math::
L = \\frac{1}{n} \\sum_i |y_i - \\hat{y}_i|
"""
def forward(self, y_true: np.ndarray, y_pred: np.ndarray) -> float:
return np.mean(np.abs(y_true - y_pred))
def backward(self, y_true: np.ndarray, y_pred: np.ndarray) -> np.ndarray:
return np.sign(y_pred - y_true) / y_true.size
class Huber(LossFunction):
"""
Huber loss (Smooth L1 Loss).
Provides a balance between MSE and MAE, being less sensitive to outliers.
Parameters
----------
delta : float, optional
Threshold for switching between quadratic and linear loss.
Defaults to 1.0.
Formula
-------
.. math::
L = \\begin{cases}
0.5 (y - \\hat{y})^2, & |y - \\hat{y}| \\le \\delta \\\\
\\delta (|y - \\hat{y}| - 0.5 \\delta), & \\text{otherwise}
\\end{cases}
"""
def __init__(self, delta: float = 1.0) -> None:
self.delta = delta
def forward(self, y_true: np.ndarray, y_pred: np.ndarray) -> float:
error = y_true - y_pred
abs_error = np.abs(error)
is_small = abs_error <= self.delta
squared_loss = 0.5 * error**2
linear_loss = self.delta * (abs_error - 0.5 * self.delta)
return np.mean(np.where(is_small, squared_loss, linear_loss))
def backward(self, y_true: np.ndarray, y_pred: np.ndarray) -> np.ndarray:
error = y_pred - y_true
abs_error = np.abs(error)
is_small = abs_error <= self.delta
grad = np.where(is_small, error, self.delta * np.sign(error))
return grad / y_true.size
class BinaryCrossEntropy(LossFunction):
"""
Binary Cross-Entropy (BCE) loss with numerical stability.
Suitable for binary classification tasks.
Parameters
----------
epsilon : float, optional
Small constant for numerical stability.
Defaults to 1e-15.
Formula
-------
.. math::
L = -\\frac{1}{n} \\sum_i [y_i \\log(\\hat{y}_i) + (1 - y_i) \\log(1 - \\hat{y}_i)]
"""
def __init__(self, epsilon: float = 1e-15) -> None:
self.epsilon = epsilon
def forward(self, y_true: np.ndarray, y_pred: np.ndarray) -> float:
y_pred = np.clip(y_pred, self.epsilon, 1.0 - self.epsilon)
return -np.mean(y_true * np.log(y_pred) + (1.0 - y_true) * np.log(1.0 - y_pred))
def backward(self, y_true: np.ndarray, y_pred: np.ndarray) -> np.ndarray:
y_pred = np.clip(y_pred, self.epsilon, 1.0 - self.epsilon)
grad = ((1.0 - y_true) / (1.0 - y_pred) - y_true / y_pred) / y_true.size
return grad
class CategoricalCrossEntropy(LossFunction):
"""
Categorical Cross-Entropy (CCE) loss for multi-class classification.
Assumes one-hot encoded `y_true`.
Parameters
----------
epsilon : float, optional
Small constant for numerical stability.
Defaults to 1e-15.
Formula
-------
.. math::
L = -\\frac{1}{n} \\sum_i \\sum_j y_{ij} \\log(\\hat{y}_{ij})
"""
def __init__(self, epsilon: float = 1e-15) -> None:
self.epsilon = epsilon
def forward(self, y_true: np.ndarray, y_pred: np.ndarray) -> float:
y_pred = np.clip(y_pred, self.epsilon, 1.0 - self.epsilon)
return -np.mean(np.sum(y_true * np.log(y_pred), axis=0))
def backward(self, y_true: np.ndarray, y_pred: np.ndarray) -> np.ndarray:
y_pred = np.clip(y_pred, self.epsilon, 1.0 - self.epsilon)
return -y_true / (y_pred * y_true.size)