- renamed many variables
- results better displayed
- fixed log(0) error with 1e-15
This commit is contained in:
2024-06-11 16:30:59 +02:00
parent 17a7ea9435
commit f800fd1a60
7 changed files with 164 additions and 71 deletions

View File

@@ -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