Refactor
- renamed many variables - results better displayed - fixed log(0) error with 1e-15
This commit is contained in:
@@ -1,25 +1,26 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from plot import Plot
|
||||
from tqdm import tqdm
|
||||
from learning.data import Dataset, Data
|
||||
from learning.data import ConfusionMatrix, Dataset, Data, TargetType
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class MLAlgorithm(ABC):
|
||||
""" Classe generica per gli algoritmi di Machine Learning """
|
||||
|
||||
learnset: Data
|
||||
validset: Data
|
||||
testset: Data
|
||||
_target_type: TargetType
|
||||
_learnset: Data
|
||||
_validset: Data
|
||||
_testset: Data
|
||||
_learn_loss: list[float]
|
||||
_valid_loss: list[float]
|
||||
|
||||
def __init__(self, dataset:Dataset) -> None:
|
||||
learn, test, valid = dataset.get_dataset(0.2, 0.2)
|
||||
self.learnset = learn
|
||||
self.validset = valid
|
||||
self.testset = test
|
||||
learn, test, valid = dataset.get_dataset()
|
||||
self._target_type = dataset.target_type
|
||||
self._learnset = learn
|
||||
self._validset = valid
|
||||
self._testset = test
|
||||
|
||||
def learn(self, epochs:int, early_stop:float=0.0000001, max_patience:int=10, verbose:bool=False) -> tuple[int, list, list]:
|
||||
learn = []
|
||||
@@ -33,15 +34,15 @@ class MLAlgorithm(ABC):
|
||||
for _ in trange:
|
||||
if count > 1 and valid[-2] - valid[-1] < early_stop:
|
||||
if patience >= max_patience:
|
||||
self.set_parameters(backup)
|
||||
self._set_parameters(backup)
|
||||
break
|
||||
patience += 1
|
||||
else:
|
||||
backup = self.get_parameters()
|
||||
backup = self._get_parameters()
|
||||
patience = 0
|
||||
|
||||
count += 1
|
||||
learn.append(self.learning_step())
|
||||
learn.append(self._learning_step())
|
||||
valid.append(self.validation_loss())
|
||||
|
||||
if verbose: trange.set_postfix({"learn": f"{learn[-1]:2.5f}", "validation": f"{valid[-1]:2.5f}"})
|
||||
@@ -53,13 +54,13 @@ class MLAlgorithm(ABC):
|
||||
return (count, learn, valid)
|
||||
|
||||
def learning_loss(self) -> float:
|
||||
return self.predict_loss(self.learnset)
|
||||
return self._predict_loss(self._learnset)
|
||||
|
||||
def validation_loss(self) -> float:
|
||||
return self.predict_loss(self.validset)
|
||||
return self._predict_loss(self._validset)
|
||||
|
||||
def test_loss(self) -> float:
|
||||
return self.predict_loss(self.testset)
|
||||
return self._predict_loss(self._testset)
|
||||
|
||||
def plot(self, skip:int=1000) -> None:
|
||||
skip = skip if len(self._learn_loss) > skip else 0
|
||||
@@ -68,29 +69,46 @@ class MLAlgorithm(ABC):
|
||||
plot.line("validation", "red", data=self._valid_loss[skip:])
|
||||
plot.wait()
|
||||
|
||||
def confusion_matrix(self, dataset:Data) -> np.ndarray:
|
||||
h0 = np.where(self._h0(dataset.x) > 0.5, 1, 0)
|
||||
def display_results(self) -> None:
|
||||
print("======== RESULT ========")
|
||||
print(f"Loss learn : {self.learning_loss():0.5f}")
|
||||
print(f"Loss valid : {self.validation_loss():0.5f}")
|
||||
print(f"Loss test : {self.test_loss():0.5f}")
|
||||
if self._target_type == TargetType.Regression:
|
||||
print(f"R^2 : {self.test_r_squared():0.5f}")
|
||||
else:
|
||||
conf = self.test_confusion_matrix()
|
||||
print(f"Accuracy : {conf.accuracy_per_class()}")
|
||||
print(f"Precision : {conf.precision_per_class()}")
|
||||
print(f"Recall : {conf.recall_per_class()}")
|
||||
print(f"F1 score : {conf.f1_score_per_class()}")
|
||||
print(f"Specificity: {conf.specificity_per_class()}")
|
||||
|
||||
classes = len(np.unique(dataset.y))
|
||||
conf_matrix = np.zeros((classes, classes), dtype=int)
|
||||
def test_confusion_matrix(self) -> ConfusionMatrix:
|
||||
if self._target_type != TargetType.Classification\
|
||||
and self._target_type != TargetType.MultiClassification:
|
||||
return None
|
||||
|
||||
for actual, prediction in zip(dataset.y, h0):
|
||||
conf_matrix[int(actual), int(prediction)] += 1
|
||||
return conf_matrix
|
||||
h0 = np.where(self._h0(self._testset.x) > 0.5, 1, 0)
|
||||
return ConfusionMatrix(self._testset.y, h0)
|
||||
|
||||
def accuracy(self, dataset:Data) -> float:
|
||||
conf = self.confusion_matrix(dataset)
|
||||
correct = np.sum(np.diagonal(conf))
|
||||
total = np.sum(conf)
|
||||
return correct / total
|
||||
def test_r_squared(self) -> float:
|
||||
if self._target_type != TargetType.Regression:
|
||||
return 0
|
||||
|
||||
h0 = self._h0(self._testset.x)
|
||||
y_mean = np.mean(self._testset.y)
|
||||
ss_total = np.sum((self._testset.y - y_mean) ** 2)
|
||||
ss_resid = np.sum((self._testset.y - h0) ** 2)
|
||||
return 1 - (ss_resid / ss_total)
|
||||
|
||||
@abstractmethod
|
||||
def _h0(self, x:np.ndarray) -> np.ndarray: pass
|
||||
@abstractmethod
|
||||
def learning_step(self) -> float: pass
|
||||
def _learning_step(self) -> float: pass
|
||||
@abstractmethod
|
||||
def predict_loss(self, dataset:Data) -> float: pass
|
||||
def _predict_loss(self, dataset:Data) -> float: pass
|
||||
@abstractmethod
|
||||
def get_parameters(self): pass
|
||||
def _get_parameters(self): pass
|
||||
@abstractmethod
|
||||
def set_parameters(self, parameters): pass
|
||||
def _set_parameters(self, parameters): pass
|
||||
|
||||
Reference in New Issue
Block a user