from joblib import parallel_backend
parallel_backend("loky", n_jobs=-1)<joblib.parallel.parallel_backend at 0x11e2d6e70>import sys
sys.path.append("./../src/")
from get_dataset import dataset_loaders
dataset = list(dataset_loaders.keys())[6]
dataset'bankmarketing'# Parameters
dataset = "abalone20"
from get_dataset import load_dataset
X, y = load_dataset(dataset)Data presentation¶
*Unexecuted inline expression for: dataset* dataset contains n = Unexecuted inline expression for: X.shape[0] samples and p = Unexecuted inline expression for: X.shape[1] features.
The target variable is binary and Unexecuted inline expression for: y.mean() * 100:.2f% of the samples are positive.
from sklearn.model_selection import train_test_split
# Split the dataset into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
from sklearn.preprocessing import StandardScaler
# Normalize data using only the training set
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)Prepare model results storage¶
MODELS = dict()
def store_results(name, grid):
MODELS[name] = {
"best_params": grid.best_params_,
"X_test": X_test,
"y_true": y_test,
"y_pred": grid.predict(X_test),
"y_proba": grid.predict_proba(X_test)
}
passfrom sklearn.model_selection import GridSearchCV
from sklearn.experimental import enable_halving_search_cv
from sklearn.model_selection import HalvingRandomSearchCV
def get_grid(model, params):
# grid = GridSearchCV(model, params, n_jobs=-1, cv=5)
grid = HalvingRandomSearchCV(model, params, n_jobs=-1, cv=5, verbose=1, scoring="accuracy", refit=True)
return gridEntraînement des classifieurs¶
Classifieurs non paramétriques¶
K-Nearest Neighbors¶
from sklearn.neighbors import KNeighborsClassifier
model = KNeighborsClassifier(weights='uniform', algorithm='auto')
param_grid = {
'n_neighbors': [3, 5, 7, 9],
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('KNN', grid_search)n_iterations: 2
n_required_iterations: 2
n_possible_iterations: 5
min_resources_: 20
max_resources_: 2923
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 4
n_resources: 20
Fitting 5 folds for each of 4 candidates, totalling 20 fits
/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:317: UserWarning: The total space of parameters 4 is smaller than n_iter=146. Running 4 iterations. For exhaustive searches, use GridSearchCV.
warnings.warn(
----------
iter: 1
n_candidates: 2
n_resources: 60
Fitting 5 folds for each of 2 candidates, totalling 10 fits
Distance-Weighted KNN¶
model = KNeighborsClassifier(weights='distance', algorithm='auto')
param_grid = {
'n_neighbors': [3, 5, 7, 9],
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('KNN Distance Weighted', grid_search)n_iterations: 2
n_required_iterations: 2
n_possible_iterations: 5
min_resources_: 20
max_resources_: 2923
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 4
n_resources: 20
Fitting 5 folds for each of 4 candidates, totalling 20 fits
----------
iter: 1
n_candidates: 2
n_resources: 60
Fitting 5 folds for each of 2 candidates, totalling 10 fits
/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:317: UserWarning: The total space of parameters 4 is smaller than n_iter=146. Running 4 iterations. For exhaustive searches, use GridSearchCV.
warnings.warn(
Condensed Nearest Neighbor¶
from imblearn.under_sampling import CondensedNearestNeighbour
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.utils import check_X_y
from sklearn.utils.validation import validate_data
# Wrap CondensedNearestNeighbour into an sklearn compatible transformer for use in pipelines
class CondensedNearestNeighbourTransformer(BaseEstimator, TransformerMixin):
def __init__(self, sampling_strategy = "auto", random_state = 42, n_neighbors = None, n_seeds_S = 1):
self.sampling_strategy = sampling_strategy
self.random_state = random_state
self.n_neighbors = n_neighbors
self.n_seeds_S = n_seeds_S
def fit(self, X, y=None):
# validate_data(X, y, accept_sparse=True, reset=True)
self.n_features_in_ = X.shape[1]
return self
def transform(self, X, y=None):
# check_X_y(X, y)
if y is None:
return X
else:
return CondensedNearestNeighbour(
sampling_strategy = self.sampling_strategy,
random_state = self.random_state,
n_neighbors = self.n_neighbors,
n_seeds_S = self.n_seeds_S
).fit_resample(X, y)
from sklearn.utils.estimator_checks import check_estimator
# check_estimator(CondensedNearestNeighbourTransformer())from sklearn.pipeline import Pipeline
model = Pipeline([
('cnn', CondensedNearestNeighbourTransformer(sampling_strategy='auto', n_neighbors=3, n_seeds_S=1)),
('knn', KNeighborsClassifier(weights='uniform', algorithm='auto'))
])
param_grid = {
'cnn__n_neighbors': [3, 5, 7, 9],
'knn__n_neighbors': [3, 5, 7, 9],
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('KNN Condensed Nearest Neighbor', grid_search)n_iterations: 3
n_required_iterations: 3
n_possible_iterations: 5
min_resources_: 20
max_resources_: 2923
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 16
n_resources: 20
Fitting 5 folds for each of 16 candidates, totalling 80 fits
/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:317: UserWarning: The total space of parameters 16 is smaller than n_iter=146. Running 16 iterations. For exhaustive searches, use GridSearchCV.
warnings.warn(
----------
iter: 1
n_candidates: 6
n_resources: 60
Fitting 5 folds for each of 6 candidates, totalling 30 fits
----------
iter: 2
n_candidates: 2
n_resources: 180
Fitting 5 folds for each of 2 candidates, totalling 10 fits
Locally Adaptive KNN¶
class LocallyAdaptiveKNN(KNeighborsClassifier):
def predict(self, X):
distances, indices = self.kneighbors(X)
predictions = []
for i, neighbors in enumerate(indices):
local_k = int(len(neighbors) / 2) # Example of adapting k locally
local_knn = KNeighborsClassifier(n_neighbors=local_k)
local_knn.fit(self._fit_X[neighbors], self._y[neighbors])
predictions.append(local_knn.predict([X[i]])[0])
return predictions
model = LocallyAdaptiveKNN(weights='uniform', algorithm='auto')
param_grid = {
'n_neighbors': [3, 5, 7, 9],
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('KNN Locally Adaptive', grid_search)n_iterations: 2
n_required_iterations: 2
n_possible_iterations: 5
min_resources_: 20
max_resources_: 2923
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 4
n_resources: 20
Fitting 5 folds for each of 4 candidates, totalling 20 fits
/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:317: UserWarning: The total space of parameters 4 is smaller than n_iter=146. Running 4 iterations. For exhaustive searches, use GridSearchCV.
warnings.warn(
----------
iter: 1
n_candidates: 2
n_resources: 60
Fitting 5 folds for each of 2 candidates, totalling 10 fits
Classifieurs binaires non linéaires¶
Arbre de décision (Decision Tree)¶
from sklearn.tree import DecisionTreeClassifier
model = DecisionTreeClassifier(random_state=42)
param_grid = {
'max_depth': [3, 5, 7, 9],
'min_samples_split': [2, 5, 10]
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('Decision Tree', grid_search)n_iterations: 3
n_required_iterations: 3
n_possible_iterations: 5
min_resources_: 20
max_resources_: 2923
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 12
n_resources: 20
Fitting 5 folds for each of 12 candidates, totalling 60 fits
/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:317: UserWarning: The total space of parameters 12 is smaller than n_iter=146. Running 12 iterations. For exhaustive searches, use GridSearchCV.
warnings.warn(
----------
iter: 1
n_candidates: 4
n_resources: 60
Fitting 5 folds for each of 4 candidates, totalling 20 fits
----------
iter: 2
n_candidates: 2
n_resources: 180
Fitting 5 folds for each of 2 candidates, totalling 10 fits
Forêt aléatoire (RandomForest)¶
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(random_state=42, class_weight=None)
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [3, 5, 7, 9],
'min_samples_split': [2, 5, 10],
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('Random Forest', grid_search)n_iterations: 4
n_required_iterations: 4
n_possible_iterations: 5
min_resources_: 20
max_resources_: 2923
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 36
n_resources: 20
Fitting 5 folds for each of 36 candidates, totalling 180 fits
/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:317: UserWarning: The total space of parameters 36 is smaller than n_iter=146. Running 36 iterations. For exhaustive searches, use GridSearchCV.
warnings.warn(
----------
iter: 1
n_candidates: 12
n_resources: 60
Fitting 5 folds for each of 12 candidates, totalling 60 fits
----------
iter: 2
n_candidates: 4
n_resources: 180
Fitting 5 folds for each of 4 candidates, totalling 20 fits
----------
iter: 3
n_candidates: 2
n_resources: 540
Fitting 5 folds for each of 2 candidates, totalling 10 fits
Forêt aléatoire avec cost-sensitive learning¶
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(random_state=42, class_weight='balanced')
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [3, 5, 7, 9],
'min_samples_split': [2, 5, 10],
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('Random Forest - cost-sensitive learning', grid_search)n_iterations: 4
n_required_iterations: 4
n_possible_iterations: 5
min_resources_: 20
max_resources_: 2923
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 36
n_resources: 20
Fitting 5 folds for each of 36 candidates, totalling 180 fits
/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:317: UserWarning: The total space of parameters 36 is smaller than n_iter=146. Running 36 iterations. For exhaustive searches, use GridSearchCV.
warnings.warn(
----------
iter: 1
n_candidates: 12
n_resources: 60
Fitting 5 folds for each of 12 candidates, totalling 60 fits
----------
iter: 2
n_candidates: 4
n_resources: 180
Fitting 5 folds for each of 4 candidates, totalling 20 fits
----------
iter: 3
n_candidates: 2
n_resources: 540
Fitting 5 folds for each of 2 candidates, totalling 10 fits
AdaBoost¶
from sklearn.ensemble import AdaBoostClassifier
model = AdaBoostClassifier(random_state=42)
param_grid = {
'n_estimators': [50, 100, 200],
'learning_rate': [0.01, 0.1, 1.0]
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('AdaBoost', grid_search)n_iterations: 3
n_required_iterations: 3
n_possible_iterations: 5
min_resources_: 20
max_resources_: 2923
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 9
n_resources: 20
Fitting 5 folds for each of 9 candidates, totalling 45 fits
----------
iter: 1
n_candidates: 3
n_resources: 60
Fitting 5 folds for each of 3 candidates, totalling 15 fits
/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:317: UserWarning: The total space of parameters 9 is smaller than n_iter=146. Running 9 iterations. For exhaustive searches, use GridSearchCV.
warnings.warn(
----------
iter: 2
n_candidates: 1
n_resources: 180
Fitting 5 folds for each of 1 candidates, totalling 5 fits
Gradient Boosting¶
from sklearn.ensemble import GradientBoostingClassifier
model = GradientBoostingClassifier(random_state=42)
param_grid = {
'loss': ['log_loss', 'exponential'],
'n_estimators': [50, 100, 200],
'learning_rate': [0.01, 0.1, 1.0],
'max_depth': [3, 5, 7, 9]
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('Gradient Boosting', grid_search)n_iterations: 4
n_required_iterations: 4
n_possible_iterations: 5
min_resources_: 20
max_resources_: 2923
aggressive_elimination: False
factor: 3
----------
iter: 0
n_candidates: 72
n_resources: 20
Fitting 5 folds for each of 72 candidates, totalling 360 fits
/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:317: UserWarning: The total space of parameters 72 is smaller than n_iter=146. Running 72 iterations. For exhaustive searches, use GridSearchCV.
warnings.warn(
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[17], line 14
5 param_grid = {
6 'loss': ['log_loss', 'exponential'],
7 'n_estimators': [50, 100, 200],
8 'learning_rate': [0.01, 0.1, 1.0],
9 'max_depth': [3, 5, 7, 9]
10 }
12 grid_search = get_grid(model, param_grid)
---> 14 grid_search.fit(X_train, y_train)
15 store_results('Gradient Boosting', grid_search)
File ~/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/base.py:1389, in _fit_context.<locals>.decorator.<locals>.wrapper(estimator, *args, **kwargs)
1382 estimator._validate_params()
1384 with config_context(
1385 skip_parameter_validation=(
1386 prefer_skip_nested_validation or global_skip_validation
1387 )
1388 ):
-> 1389 return fit_method(estimator, *args, **kwargs)
File ~/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search_successive_halving.py:253, in BaseSuccessiveHalving.fit(self, X, y, **params)
247 self._check_input_parameters(
248 X=X, y=y, split_params=routed_params.splitter.split
249 )
251 self._n_samples_orig = _num_samples(X)
--> 253 super().fit(X, y=y, **params)
255 # Set best_score_: BaseSearchCV does not set it, as refit is a callable
256 self.best_score_ = self.cv_results_["mean_test_score"][self.best_index_]
File ~/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/base.py:1389, in _fit_context.<locals>.decorator.<locals>.wrapper(estimator, *args, **kwargs)
1382 estimator._validate_params()
1384 with config_context(
1385 skip_parameter_validation=(
1386 prefer_skip_nested_validation or global_skip_validation
1387 )
1388 ):
-> 1389 return fit_method(estimator, *args, **kwargs)
File ~/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:1024, in BaseSearchCV.fit(self, X, y, **params)
1018 results = self._format_results(
1019 all_candidate_params, n_splits, all_out, all_more_results
1020 )
1022 return results
-> 1024 self._run_search(evaluate_candidates)
1026 # multimetric is determined here because in the case of a callable
1027 # self.scoring the return type is only known after calling
1028 first_test_score = all_out[0]["test_scores"]
File ~/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search_successive_halving.py:357, in BaseSuccessiveHalving._run_search(self, evaluate_candidates)
350 cv = self._checked_cv_orig
352 more_results = {
353 "iter": [itr] * n_candidates,
354 "n_resources": [n_resources] * n_candidates,
355 }
--> 357 results = evaluate_candidates(
358 candidate_params, cv, more_results=more_results
359 )
361 n_candidates_to_keep = ceil(n_candidates / self.factor)
362 candidate_params = _top_k(results, n_candidates_to_keep, itr)
File ~/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_search.py:1001, in BaseSearchCV.fit.<locals>.evaluate_candidates(candidate_params, cv, more_results)
994 elif len(out) != n_candidates * n_splits:
995 raise ValueError(
996 "cv.split and cv.get_n_splits returned "
997 "inconsistent results. Expected {} "
998 "splits, got {}".format(n_splits, len(out) // n_candidates)
999 )
-> 1001 _warn_or_raise_about_fit_failures(out, self.error_score)
1003 # For callable self.scoring, the return type is only know after
1004 # calling. If the return type is a dictionary, the error scores
1005 # can now be inserted with the correct key. The type checking
1006 # of out will be done in `_insert_error_scores`.
1007 if callable(self.scoring):
File ~/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_validation.py:517, in _warn_or_raise_about_fit_failures(results, error_score)
510 if num_failed_fits == num_fits:
511 all_fits_failed_message = (
512 f"\nAll the {num_fits} fits failed.\n"
513 "It is very likely that your model is misconfigured.\n"
514 "You can try to debug the error by setting error_score='raise'.\n\n"
515 f"Below are more details about the failures:\n{fit_errors_summary}"
516 )
--> 517 raise ValueError(all_fits_failed_message)
519 else:
520 some_fits_failed_message = (
521 f"\n{num_failed_fits} fits failed out of a total of {num_fits}.\n"
522 "The score on these train-test partitions for these parameters"
(...)
526 f"Below are more details about the failures:\n{fit_errors_summary}"
527 )
ValueError:
All the 360 fits failed.
It is very likely that your model is misconfigured.
You can try to debug the error by setting error_score='raise'.
Below are more details about the failures:
--------------------------------------------------------------------------------
360 fits failed with the following error:
Traceback (most recent call last):
File "/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/model_selection/_validation.py", line 866, in _fit_and_score
estimator.fit(X_train, y_train, **fit_params)
File "/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/base.py", line 1389, in wrapper
return fit_method(estimator, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/ensemble/_gb.py", line 669, in fit
y = self._encode_y(y=y, sample_weight=None)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/mathisderenne/Documents/02 - Scolaire/M1 MIASHS/02 - Guillaume Mezler/Projet/.venv/lib/python3.12/site-packages/sklearn/ensemble/_gb.py", line 1532, in _encode_y
raise ValueError(
ValueError: y contains 1 class after sample_weight trimmed classes with zero weights, while a minimum of 2 classes are required.
Classifieurs binaires paramétriques¶
SVM Linéaire¶
from sklearn.svm import SVC
model = SVC(
kernel='linear',
random_state=42, probability=True)
param_grid = {
'C': [0.1, 0.5, 1],
'degree': [2, 3, 4]
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('SVM', grid_search)SVM non linéaire¶
from sklearn.svm import SVC
model = SVC(random_state=42, probability=True)
param_grid = {
'kernel': ['poly', 'rbf', 'sigmoid'],
'C': [0.1, 0.5, 1],
'gamma': ['scale', 'auto']
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('SVM non linéaire', grid_search)SVM non linéaire avec sur-échantillonnage¶
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
from sklearn.preprocessing import StandardScaler
# Normalize data using only the training set
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)from imblearn.pipeline import Pipeline as ImbPipeline
from imblearn.over_sampling import SMOTE
from sklearn.svm import SVC
model = ImbPipeline([
('smote', SMOTE(sampling_strategy='auto', k_neighbors=1, random_state=42)),
('svm', SVC(random_state=42, probability=True))
])
param_grid = {
'svm__kernel': ['poly', 'rbf', 'sigmoid'],
'svm__C': [0.1, 0.5, 1],
'svm__gamma': ['scale', 'auto']
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('SVM non linéaire avec SMOTE', grid_search)SVM avec cost-sensitive learning (ajustement pénalité C)¶
from sklearn.svm import SVC
model = SVC(random_state=42, probability=True, class_weight='balanced')
param_grid = {
'kernel': ['poly', 'rbf', 'sigmoid'],
'C': [0.1, 0.5, 1],
'gamma': ['scale', 'auto']
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('SVM cost-sensitive learning', grid_search)Régression logistique¶
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(random_state=42, solver='liblinear', dual=False)
param_grid = {
'C': [0.1, 0.5, 1], # Inverse de la force de régularisation
'penalty': ['l1', 'l2'], # Type de régularisation
'class_weight': [None, 'balanced'] # Poids des classes
}
grid_search = get_grid(model, param_grid)
grid_search.fit(X_train, y_train)
store_results('Logistic Regression', grid_search)Sauvegarde des prédictions et paramètres des modèles¶
from pathlib import Path
from joblib import dump
# Save models results
dump(MODELS, f"./../results/{dataset}.joblib")Performance des modèles sur les données de test¶
from utils import plot_roc, plot_precision_recall, table_report
for model_name, model in MODELS.items():
print(f"Model: {model_name}")
table_report(model['y_true'], model['y_pred'])
plot_roc(model['y_true'], model['y_proba'][:, 1])
plot_precision_recall(model['y_true'], model['y_proba'][:, 1])