{ "cells": [ { "cell_type": "markdown", "id": "2936bcdd", "metadata": {}, "source": [ "## MLFLOW & Modélisation" ] }, { "cell_type": "markdown", "id": "d07f903e", "metadata": {}, "source": [ "### Import des modules" ] }, { "cell_type": "code", "execution_count": 1, "id": "675bc3c9", "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "import joblib\n", "import warnings\n", "warnings.filterwarnings(\"ignore\")\n", "import mlflow\n", "import mlflow.sklearn \n", "from sklearn.pipeline import Pipeline\n", "from sklearn.compose import ColumnTransformer\n", "from sklearn.preprocessing import StandardScaler, OneHotEncoder, TargetEncoder\n", "from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score, GridSearchCV, RandomizedSearchCV\n", "from sklearn.dummy import DummyClassifier\n", "from sklearn.svm import SVC\n", "from sklearn.linear_model import LogisticRegression\n", "from sklearn.ensemble import RandomForestClassifier\n", "from xgboost import XGBClassifier\n", "from sklearn.metrics import (accuracy_score, roc_auc_score, f1_score, precision_score, recall_score, roc_curve, precision_recall_curve, classification_report, confusion_matrix)\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "74fa8866", "metadata": {}, "outputs": [], "source": [ "app_test_clean = joblib.load(\"data/app_test_clean_v2.joblib\")" ] }, { "cell_type": "code", "execution_count": null, "id": "4bb1da50", "metadata": {}, "outputs": [], "source": [ "# Importation des données \n", "app_train_clean = joblib.load(\"data/app_train_clean_v2.joblib\")\n", "app_test_clean = joblib.load(\"data/app_test_clean_v2.joblib\")" ] }, { "cell_type": "code", "execution_count": null, "id": "64195ccb", "metadata": {}, "outputs": [], "source": [ "# Fonction permettant de vérifier la différence de colonnes entre 2 tables\n", "def check_columns(df_ref, df_test, name_ref=\"train\", name_test=\"test\"):\n", " cols_ref = set(df_ref.columns)\n", " cols_test = set(df_test.columns)\n", "\n", " missing = cols_ref - cols_test\n", " extra = cols_test - cols_ref\n", "\n", " print(f\"--- Vérification colonnes : {name_test} vs {name_ref} ---\")\n", " print(f\"Colonnes attendues : {len(cols_ref)}\")\n", " print(f\"Colonnes trouvées : {len(cols_test)}\")\n", "\n", " if missing:\n", " print(\"\\n❌ Colonnes manquantes dans\", name_test, \":\")\n", " for c in sorted(missing):\n", " print(\" -\", c)\n", " else:\n", " print(\"\\n✔️ Aucune colonne manquante\")\n", "\n", " if extra:\n", " print(\"\\n⚠️ Colonnes supplémentaires dans\", name_test, \":\")\n", " for c in sorted(extra):\n", " print(\" -\", c)\n", " else:\n", " print(\"\\n✔️ Aucune colonne supplémentaire\")\n", "\n", " print(\"\\n--------------------------------------------\\n\")\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "b07074ac", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--- Vérification colonnes : test vs train ---\n", "Colonnes attendues : 328\n", "Colonnes trouvées : 327\n", "\n", "❌ Colonnes manquantes dans test :\n", " - TARGET\n", "\n", "✔️ Aucune colonne supplémentaire\n", "\n", "--------------------------------------------\n", "\n" ] } ], "source": [ "# Check des colonnes entre train et test\n", "check_columns(app_train_clean, app_test_clean, name_ref=\"train\", name_test=\"test\")" ] }, { "cell_type": "markdown", "id": "4a9db321", "metadata": {}, "source": [ "Mon objectif principal est de pouvoir tracer l'impact du preprocessing, des modèles et des hyperparamètres sur les métriques et le scoring.\n", "\n", "De ce fait il faudrait une structure du MLFlow permettant de:\n", "- comparer facilement les préprocessings entre eux\n", "- comparer facilement les modèles entre eux\n", "- comparer facilement les hyperparamètres entre eux\n", "- garder une hiérarchie lisible dans MLflow UI\n", "- ne pas exploser le nombre de runs" ] }, { "cell_type": "markdown", "id": "0ed123dc", "metadata": {}, "source": [ "### Fonctions du preprocessing\n", "\n", "3 fonctions de preprocessing permettant de gérer les variables manquantes:\n", "- Garder les NaN tel quel en les flaguant (je ne sais pas si en flaguant les numériques par '-1' çà n'a pas un impact sur les données?)\n", "- Imputation classique(**médiane** pour les numériques, **mode** pour les catégorielles)\n", "- Suppression des variables dont le ratio NaN est >= 70%\n", "\n", "1 fonction pour tester l'impact de la gestion des outliers sur les scores et résultats en plus des nettoyages précédents" ] }, { "cell_type": "code", "execution_count": 6, "id": "ba8ba74d", "metadata": {}, "outputs": [], "source": [ "# Flag \"Missing\"\n", "def preprocess_missing_flag(df):\n", " df = df.copy()\n", " df_num = df.select_dtypes(include=[\"int64\", \"float64\"])\n", " df_cat = df.select_dtypes(include=[\"object\", \"category\", \"bool\"])\n", "\n", " df_num = df_num.fillna(-1)\n", " df_cat = df_cat.fillna(\"MISSING\")\n", "\n", " return pd.concat([df_num, df_cat], axis=1)\n", "\n", "\n", "# Imputation classique\n", "from sklearn.impute import SimpleImputer\n", "\n", "def preprocess_imputation(df):\n", " df = df.copy()\n", " df_num = df.select_dtypes(include=[\"int64\", \"float64\"])\n", " df_cat = df.select_dtypes(include=[\"object\", \"category\", \"bool\"])\n", "\n", " df_num = pd.DataFrame(SimpleImputer(strategy=\"median\").fit_transform(df_num),\n", " columns=df_num.columns)\n", " df_cat = pd.DataFrame(SimpleImputer(strategy=\"most_frequent\").fit_transform(df_cat),\n", " columns=df_cat.columns)\n", "\n", " return pd.concat([df_num, df_cat], axis=1)\n", "\n", "\n", "# Suppression colonnes >= 70% NaN\n", "def preprocess_drop70(df, threshold=0.7):\n", " df = df.copy()\n", " missing_ratio = df.isna().mean()\n", " cols_to_drop = missing_ratio[missing_ratio >= threshold].index.tolist()\n", "\n", " print(\"Colonnes supprimées (>=70% NaN) :\", cols_to_drop)\n", "\n", " df = df.drop(columns=cols_to_drop)\n", "\n", " # puis imputation classique\n", " return preprocess_imputation(df)\n", "\n", "\n", "# Gestion des outliers\n", "def clip_outliers(df, lower_q=0.01, upper_q=0.99):\n", " df_out = df.copy()\n", " num_cols = df_out.select_dtypes(include=['int64', 'float64']).columns\n", " \n", " for col in num_cols:\n", " lower = df_out[col].quantile(lower_q)\n", " upper = df_out[col].quantile(upper_q)\n", " df_out[col] = df_out[col].clip(lower, upper)\n", " \n", " return df_out\n", "\n", "\n", "def preprocess_outliers_missing_flag(X):\n", " X2 = clip_outliers(X)\n", " return preprocess_missing_flag(X2)\n", "\n", "def preprocess_outliers_imputation(X):\n", " X2 = clip_outliers(X)\n", " return preprocess_imputation(X2)\n", "\n", "def preprocess_outliers_drop70(X):\n", " X2 = clip_outliers(X)\n", " return preprocess_drop70(X2)\n" ] }, { "cell_type": "markdown", "id": "a26b47a6", "metadata": {}, "source": [ "### Transformation des colonnes selon leur type: ColumnTransformer\n", "\n", "Après la séparation des données en X & y:\n", "- je crèe des listes de colonnes selon leur type (**numériques, catégorielle, booléennes**)\n", "- ensuite j'applique la stratégie de transformation selon le type de la variable\n", " - les variables catégorielles ont 2 stratégies distinctes dû au nombre élevé de catégories pour certaines variables(\n", " **OneHotEncoder** si un *maximum de 10 catégories*\n", " **TargetEncoder** si *plus de 10 catégories*)" ] }, { "cell_type": "code", "execution_count": 7, "id": "75bd1792", "metadata": {}, "outputs": [], "source": [ "# Séparation du jeu de données\n", "X = app_train_clean.drop(columns=[\"TARGET\"])\n", "y = app_train_clean[\"TARGET\"].astype(int)" ] }, { "cell_type": "code", "execution_count": null, "id": "d7856726", "metadata": {}, "outputs": [], "source": [ "# Fonction qui construit le process de transformation des colonnes\n", "def tranformation_process(X_prep):\n", " num_cols = X_prep.select_dtypes(include=[\"int64\", \"float64\"]).columns.tolist()\n", " cat_cols = X_prep.select_dtypes(include=[\"object\", \"category\"]).columns.tolist()\n", " bool_cols = X_prep.select_dtypes(include=[\"bool\"]).columns.tolist()\n", "\n", " nb_faible_cols = [c for c in cat_cols if X_prep[c].nunique() <= 10]\n", " nb_eleve_cols = [c for c in cat_cols if X_prep[c].nunique() > 10]\n", "\n", " preprocessor = ColumnTransformer(\n", " transformers=[\n", " (\"num\", StandardScaler(), num_cols),\n", " (\"card_faible\", OneHotEncoder(handle_unknown=\"ignore\"), nb_faible_cols),\n", " (\"card_eleve\", TargetEncoder(), nb_eleve_cols),\n", " (\"bool\", \"passthrough\", bool_cols)\n", " ]\n", " )\n", " return preprocessor\n" ] }, { "cell_type": "markdown", "id": "29569db1", "metadata": {}, "source": [ "### Score métier et optimisation\n", "\n", "L'objectif métier de la modélisation est de créer un **scoring métier - score qui permet de minimiser le coût d'erreur de prédiction des FN & FP**\n", "- FN = faux négatif = mauvais client prédit bon <> coût très élevé (perte financière)\n", "- FP = faux positif = bon client prédit mauvais <> coût plus faible (manque à gagner)\n", "\n", "Pour se faire, il faudra trouver le *seuil optimal* permettant de minimiser ce coût, car le seuil par défaut étant de 0.5 ne permet pas de capter toutes les erreurs. \n", "Ainsi, j'ai créé une fonction qui:\n", "- teste plusieurs seuils; et pour chaque seuil:\n", " - elle convertit les probabilités en classes\n", " - calcule FN, FP\n", " - calcule le coût métier\n", " - garde le seuil qui minimise ledit coût" ] }, { "cell_type": "code", "execution_count": 9, "id": "2bca6b48", "metadata": {}, "outputs": [], "source": [ "# Coût des classes\n", "cout_FN = 10.0\n", "cout_FP = 1.0\n", "\n", "# Fonction du calcul du coût métier\n", "def cout_metier(y_true, y_pred):\n", " cm = confusion_matrix(y_true, y_pred)\n", " TN, FP, FN, TP = cm.ravel() # transformer la matrice en un tableau 1D\n", " cout_total = FN * cout_FN + FP * cout_FP\n", " return cout_total, cm\n", "\n", "# Fonction d'optimisation du seuil\n", "def best_seuil(y_true, y_proba):\n", " seuils = np.linspace(0.01, 0.99, 99) # 99 seuils allant de 0.01 à 0.99 avec un espace de 0.01 (0.01, 0.02, 0.03, ..., 0.99)\n", " best_s = 0.5\n", " best_cost = np.inf\n", " best_cm = None\n", "\n", " for s in seuils:\n", " y_pred = (y_proba >= s).astype(int)\n", " cost, cm = cout_metier(y_true, y_pred)\n", " if cost < best_cost:\n", " best_cost = cost\n", " best_s = s\n", " best_cm = cm\n", "\n", " return best_s, best_cost, best_cm\n" ] }, { "cell_type": "markdown", "id": "43415548", "metadata": {}, "source": [ "### Déclaration des modèles à comparer - Optimisation des hyperparamètres" ] }, { "cell_type": "code", "execution_count": 10, "id": "39166bc8", "metadata": {}, "outputs": [], "source": [ "# Grilles d'hyperparamètres pour optimisation\n", "\n", "# LR\n", "param_LR = {\n", " \"model__C\": [0.01, 0.1, 1, 10], \n", " \"model__max_iter\": [200, 500]}\n", "\n", "# Random Forest\n", "param_RF = {\n", " # \"model__n_estimators\": [200, 400, 600],\n", " # \"model__max_depth\": [5, 10, 15, None],\n", " # \"model__min_samples_split\": [2, 5, 10],\n", " # \"model__min_samples_leaf\": [1, 2, 4]\n", " \"model__max_depth\": [3, 5],\n", " \"model__n_estimators\": [100, 150, 200],\n", " \"model__min_samples_leaf\": [1, 2, 4],\n", " \"model__max_features\": [\"sqrt\", \"log2\"]}\n", "\n", "# XGBoost\n", "param_XGB = {\n", " # \"model__n_estimators\": [400, 600, 700],\n", " # \"model__learning_rate\": [0.1, 0.2, 0.3],\n", " # \"model__max_depth\": [3, 5, 7],\n", " # \"model__subsample\": [0.8, 1.0],\n", " # \"model__colsample_bytree\": [0.8, 1.0],\n", " # \"model__min_child_weight\": [1, 3, 5],\n", " # \"model__gamma\": [0, 1, 5],\n", " # \"model__reg_lambda\": [1, 5, 10]\n", " \"model__learning_rate\": [0.05, 0.1], \n", " \"model__max_depth\": [3, 5], \n", " \"model__subsample\": [0.8, 1.0], \n", " \"model__colsample_bytree\": [0.8, 1.0]}" ] }, { "cell_type": "code", "execution_count": 11, "id": "cf336c4c", "metadata": {}, "outputs": [], "source": [ "# Rapport entre négatifs et positifs - pour calculer le ratio des classes négatives et positives afin de l'utiliser dans l'hyperparamètre\n", "scale_pos_weight = y.value_counts()[0] / y.value_counts()[1]\n", "\n", "# Modèles à comparer + hyperparamètres\n", "modeles = {\n", " \"Dummy\": (DummyClassifier(strategy = \"stratified\"), {}),\n", " \"LogisticRegression\": (LogisticRegression(max_iter = 2000, class_weight = \"balanced\", C = 0.1), param_LR),\n", "\n", " \"RandomForest\": (RandomForestClassifier(n_estimators=200, class_weight=\"balanced\", random_state = 42,max_depth = 3, min_samples_split = 2, min_samples_leaf = 2),\n", " param_RF),\n", "\n", " \"XGBoost\": (XGBClassifier(eval_metric=\"logloss\", n_estimators = 100, learning_rate = 0.01, max_depth = 3, subsample = 0.4, colsample_bytree = 0.4, min_child_weight = 3, gamma = 2, \n", " reg_lambda = 3, scale_pos_weight = scale_pos_weight, random_state = 42),\n", " param_XGB)}\n" ] }, { "cell_type": "code", "execution_count": null, "id": "a8a96bcc", "metadata": {}, "outputs": [], "source": [ "\n", "def optimisation_hyperparametres(model_name, model, param_grid, preprocessor, X_train, y_train):\n", "\n", " # 1. Réduire n_estimators uniquement pour l’optimisation\n", " if hasattr(model, \"n_estimators\"):\n", " # Valeurs recommandées pour accélérer\n", " if model_name.lower().startswith(\"randomforest\"):\n", " model.set_params(n_estimators=150)\n", " elif model_name.lower().startswith(\"xgb\"):\n", " model.set_params(n_estimators=200)\n", "\n", " # 2. Pipeline\n", " pipe = Pipeline([\n", " (\"preprocess\", preprocessor),\n", " (\"model\", model)\n", " ])\n", "\n", " # 3. RandomizedSearchCV plus léger\n", " search = RandomizedSearchCV(\n", " estimator=pipe,\n", " param_distributions=param_grid,\n", " n_iter=5, \n", " scoring=\"roc_auc\",\n", " cv=3, \n", " n_jobs=-1,\n", " random_state=42,\n", " verbose=1\n", " )\n", "\n", " search.fit(X_train, y_train)\n", "\n", " return search.best_estimator_, search.best_params_, search.best_score_\n", "\n", "\n" ] }, { "cell_type": "markdown", "id": "3a68a441", "metadata": {}, "source": [ "#### Fonction d'évaluation des modèles - Pipeline MLOps\n", "\n", "Fonction qui pour chaque modèle:\n", "- le combine avec le préprocesseur intégré (pipeline preprocessing), \n", "- l’entraîne, \n", "- calcule toutes les métriques utiles, \n", "- optimise le seuil métier, \n", "- génère les artefacts (ROC + matrice de confusion),\n", "- enregistre tout dans MLflow.\n", "\n", "Ceci est effectué en mettant en place un tracking permettant d'enregistrer, pour chaque modèle, les résultats obtenus dans le but de les comparer selon le besoin:\n", "- démarrer un **run MLFlow** en créant un sous-run par modèle *(with mlflow.start_run(run_name=model_name, nested=True))* afin d'enregistrer tout ce qui lui est demandé\n", " - enregistrer les hyperparamètres\n", " - entrainer le pipeline <> application du preprocessing dans le pipeline\n", " - calculer les prédictions (0, 1) & probabilités (entre 0 & 1)\n", " - calculer les métriques nécessaires, puis les enregister dans le MLFlow <> j'ai choisi de mesurer la qualité globale, la capacité à détecter les mauvais clients (*Recall*), l'équilibre précision/rappel (*F1*), et \n", " surtout la performance globale (*AUC - la contrainte est de s'assurer qu'il soit inférieur à 0.82, indicateur de surapprentissage*)\n", " - effectuer une validation croisée indépendamment du split train/test\n", " - calculer, pour 99 seuils distinct, le coût métier, puis choisir le seuil qui minimise le coût. Le modèle qui permettra d'obtenir le coût total le moins élevé sera celui choisi\n", " - puis tracer la matrice de confusion et la courbe ROC.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "644f673f", "metadata": {}, "outputs": [], "source": [ "# Fonction permettant de calculer les métriques et artefacts(matrice de confusion, ROC), coût métier, seuil optimal\n", "def evaluation_modele(model_name, model, X_train, X_test, y_train, y_test, preprocessor):\n", " \n", " pipe = Pipeline([\n", " (\"preprocess\", preprocessor),\n", " (\"model\", model)])\n", "\n", " # Log hyperparamètres\n", " for param_name, param_value in model.get_params().items():\n", " mlflow.log_param(param_name, param_value)\n", "\n", " # Fit\n", " pipe.fit(X_train, y_train)\n", "\n", " # Prédictions\n", " y_pred_train = pipe.predict(X_train)\n", " y_pred_test = pipe.predict(X_test)\n", "\n", " y_proba_train = pipe.predict_proba(X_train)[:, 1]\n", " y_proba_test = pipe.predict_proba(X_test)[:, 1]\n", "\n", " # compteur de step pour éviter les collisions MLflow \n", " step = 0\n", "\n", " # Métriques train/test\n", " metrics = {\n", " \"accuracy_train\": accuracy_score(y_train, y_pred_train),\n", " \"accuracy_test\": accuracy_score(y_test, y_pred_test),\n", " \"precision_train\": precision_score(y_train, y_pred_train),\n", " \"precision_test\": precision_score(y_test, y_pred_test),\n", " \"recall_train\": recall_score(y_train, y_pred_train),\n", " \"recall_test\": recall_score(y_test, y_pred_test),\n", " \"f1_train\": f1_score(y_train, y_pred_train),\n", " \"f1_test\": f1_score(y_test, y_pred_test),\n", " \"auc_train\": roc_auc_score(y_train, y_proba_train),\n", " \"auc_test\": roc_auc_score(y_test, y_proba_test)}\n", "\n", " for m, v in metrics.items():\n", " mlflow.log_metric(m, v, step=step)\n", "\n", " # Validation croisée\n", " cv = StratifiedKFold(n_splits = 5, shuffle = True, random_state = 42)\n", " auc_cv = cross_val_score(pipe, X_train, y_train, cv = cv, scoring = \"roc_auc\", n_jobs = -1 ).mean() \n", " mlflow.log_metric(\"auc_cv\", auc_cv, step=step)\n", " step += 1\n", " \n", "\n", " # Score métier\n", " best_s, best_cost, best_cm = best_seuil(y_test, y_proba_test)\n", " mlflow.log_metric(\"business_cost\", best_cost, step=step); step += 1\n", " mlflow.log_metric(\"best_threshold\", best_s, step=step); step += 1\n", "\n", " # Prédictions au seuil optimal\n", " y_pred_opt = (y_proba_test >= best_s).astype(int)\n", "\n", " # Recall des mauvais payeurs au seuil optimal\n", " recall_bad_payers = recall_score(y_test, y_pred_opt, pos_label=1)\n", " mlflow.log_metric(\"recall_bad_payers\", recall_bad_payers, step=step)\n", " step += 1\n", "\n", "\n", " # Matrice de confusion\n", " fig_cm, ax = plt.subplots(figsize=(4,4))\n", " sns.heatmap(best_cm, annot=True, fmt=\"d\", cmap=\"Blues\", ax=ax)\n", " plt.tight_layout()\n", " cm_path = f\"artefacts/cm_{model_name}.png\"\n", " fig_cm.savefig(cm_path)\n", " mlflow.log_artifact(cm_path)\n", " plt.close(fig_cm)\n", "\n", "\n", " # Courbe ROC\n", " fpr, tpr, _ = roc_curve(y_test, y_proba_test)\n", " fig_roc, ax = plt.subplots(figsize=(5,4))\n", " ax.plot(fpr, tpr, label=f\"AUC={metrics['auc_test']:.3f}\")\n", " ax.plot([0,1],[0,1],\"k--\")\n", " plt.tight_layout()\n", " roc_path = f\"artefacts/roc_{model_name}.png\"\n", " fig_roc.savefig(roc_path)\n", " mlflow.log_artifact(roc_path)\n", " plt.close(fig_roc)\n", "\n", " # Log du modèle\n", " mlflow.sklearn.log_model(pipe, model_name)\n", "\n", " return metrics, auc_cv, best_s, best_cost\n" ] }, { "cell_type": "markdown", "id": "42294c06", "metadata": {}, "source": [ "#### Exécution du MLFlow" ] }, { "cell_type": "code", "execution_count": null, "id": "cddc1074", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2026/03/07 23:38:53 INFO mlflow.tracking.fluent: Experiment with name 'missing_flag_base_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: Dummy\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/07 23:45:08 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: LogisticRegression\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/07 23:49:56 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: RandomForest\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/07 23:57:52 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: XGBoost\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 00:01:53 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 00:02:04 INFO mlflow.tracking.fluent: Experiment with name 'missing_flag_optimisé_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : Dummy\n", "Fitting 3 folds for each of 1 candidates, totalling 3 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 00:08:14 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 00:08:22 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'missing_flag_Dummy_optimized_v5_2-07-03'.\n", "Created version '1' of model 'missing_flag_Dummy_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : LogisticRegression\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 00:21:35 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 00:21:45 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'missing_flag_LogisticRegression_optimized_v5_2-07-03'.\n", "Created version '1' of model 'missing_flag_LogisticRegression_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : RandomForest\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 00:45:41 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 00:45:50 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'missing_flag_RandomForest_optimized_v5_2-07-03'.\n", "Created version '1' of model 'missing_flag_RandomForest_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : XGBoost\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 01:02:43 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 01:02:53 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'missing_flag_XGBoost_optimized_v5_2-07-03'.\n", "Created version '1' of model 'missing_flag_XGBoost_optimized_v5_2-07-03'.\n", "2026/03/08 01:03:02 INFO mlflow.tracking.fluent: Experiment with name 'imputation_base_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: Dummy\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 01:09:50 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: LogisticRegression\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 01:13:27 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: RandomForest\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 01:19:58 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: XGBoost\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 01:26:31 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 01:26:40 INFO mlflow.tracking.fluent: Experiment with name 'imputation_optimisé_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : Dummy\n", "Fitting 3 folds for each of 1 candidates, totalling 3 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 01:33:55 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 01:34:04 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'imputation_Dummy_optimized_v5_2-07-03'.\n", "Created version '1' of model 'imputation_Dummy_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : LogisticRegression\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 01:45:04 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 01:45:14 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'imputation_LogisticRegression_optimized_v5_2-07-03'.\n", "Created version '1' of model 'imputation_LogisticRegression_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : RandomForest\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 02:09:18 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 02:09:28 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'imputation_RandomForest_optimized_v5_2-07-03'.\n", "Created version '1' of model 'imputation_RandomForest_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : XGBoost\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 02:25:54 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 02:26:03 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'imputation_XGBoost_optimized_v5_2-07-03'.\n", "Created version '1' of model 'imputation_XGBoost_optimized_v5_2-07-03'.\n", "2026/03/08 02:26:13 INFO mlflow.tracking.fluent: Experiment with name 'drop70_base_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Colonnes supprimées (>=70% NaN) : ['CC_SK_DPD_DEF_max', 'CC_CNT_DRAWINGS_OTHER_CURRENT_mean', 'CC_CNT_DRAWINGS_ATM_CURRENT_sum', 'CC_AMT_DRAWINGS_CURRENT_min', 'CC_CNT_DRAWINGS_ATM_CURRENT_mean', 'PREV_RATE_INTEREST_PRIVILEGED_max', 'CC_AMT_DRAWINGS_CURRENT_mean', 'CC_NAME_CONTRACT_STATUS_Signed', 'CC_AMT_DRAWINGS_ATM_CURRENT_sum', 'BUREAU_AMT_ANNUITY_min', 'CC_UTILIZATION_mean', 'CC_AMT_BALANCE_mean', 'CC_NAME_CONTRACT_STATUS_Refused', 'CC_MONTHS_BALANCE_mean', 'CC_NAME_CONTRACT_STATUS_NUNIQUE', 'CC_UTILIZATION_max', 'CC_NAME_CONTRACT_STATUS_Sent proposal', 'CC_AMT_PAYMENT_TOTAL_CURRENT_max', 'CC_AMT_PAYMENT_CURRENT_min', 'CC_NAME_CONTRACT_STATUS_Approved', 'CC_CNT_DRAWINGS_ATM_CURRENT_min', 'CC_SK_DPD_max', 'CC_PAYMENT_RATIO_min', 'CC_CNT_DRAWINGS_OTHER_CURRENT_min', 'CC_AMT_INST_MIN_REGULARITY_min', 'BUREAU_AMT_ANNUITY_mean', 'CC_AMT_DRAWINGS_CURRENT_max', 'BUREAU_MONTHS_BALANCE_max', 'CC_MONTHS_BALANCE_max', 'CC_CNT_DRAWINGS_CURRENT_min', 'CC_AMT_DRAWINGS_ATM_CURRENT_mean', 'CC_AMT_CREDIT_LIMIT_ACTUAL_max', 'CC_AMT_CREDIT_LIMIT_ACTUAL_sum', 'CC_CNT_DRAWINGS_CURRENT_max', 'CC_AMT_DRAWINGS_POS_CURRENT_min', 'PREV_RATE_INTEREST_PRIMARY_max', 'CC_NAME_CONTRACT_STATUS_Completed', 'CC_UTILIZATION_min', 'CC_PAYMENT_RATIO_max']\n", "Modèle sans optimisation: Dummy\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 02:32:19 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: LogisticRegression\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 02:35:47 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: RandomForest\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 02:41:59 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: XGBoost\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 02:47:54 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 02:48:03 INFO mlflow.tracking.fluent: Experiment with name 'drop70_optimisé_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Colonnes supprimées (>=70% NaN) : ['CC_SK_DPD_DEF_max', 'CC_CNT_DRAWINGS_OTHER_CURRENT_mean', 'CC_CNT_DRAWINGS_ATM_CURRENT_sum', 'CC_AMT_DRAWINGS_CURRENT_min', 'CC_CNT_DRAWINGS_ATM_CURRENT_mean', 'PREV_RATE_INTEREST_PRIVILEGED_max', 'CC_AMT_DRAWINGS_CURRENT_mean', 'CC_NAME_CONTRACT_STATUS_Signed', 'CC_AMT_DRAWINGS_ATM_CURRENT_sum', 'BUREAU_AMT_ANNUITY_min', 'CC_UTILIZATION_mean', 'CC_AMT_BALANCE_mean', 'CC_NAME_CONTRACT_STATUS_Refused', 'CC_MONTHS_BALANCE_mean', 'CC_NAME_CONTRACT_STATUS_NUNIQUE', 'CC_UTILIZATION_max', 'CC_NAME_CONTRACT_STATUS_Sent proposal', 'CC_AMT_PAYMENT_TOTAL_CURRENT_max', 'CC_AMT_PAYMENT_CURRENT_min', 'CC_NAME_CONTRACT_STATUS_Approved', 'CC_CNT_DRAWINGS_ATM_CURRENT_min', 'CC_SK_DPD_max', 'CC_PAYMENT_RATIO_min', 'CC_CNT_DRAWINGS_OTHER_CURRENT_min', 'CC_AMT_INST_MIN_REGULARITY_min', 'BUREAU_AMT_ANNUITY_mean', 'CC_AMT_DRAWINGS_CURRENT_max', 'BUREAU_MONTHS_BALANCE_max', 'CC_MONTHS_BALANCE_max', 'CC_CNT_DRAWINGS_CURRENT_min', 'CC_AMT_DRAWINGS_ATM_CURRENT_mean', 'CC_AMT_CREDIT_LIMIT_ACTUAL_max', 'CC_AMT_CREDIT_LIMIT_ACTUAL_sum', 'CC_CNT_DRAWINGS_CURRENT_max', 'CC_AMT_DRAWINGS_POS_CURRENT_min', 'PREV_RATE_INTEREST_PRIMARY_max', 'CC_NAME_CONTRACT_STATUS_Completed', 'CC_UTILIZATION_min', 'CC_PAYMENT_RATIO_max']\n", "Optimisation du modèle : Dummy\n", "Fitting 3 folds for each of 1 candidates, totalling 3 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 02:54:32 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 02:54:41 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'drop70_Dummy_optimized_v5_2-07-03'.\n", "Created version '1' of model 'drop70_Dummy_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : LogisticRegression\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 03:04:55 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 03:05:04 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'drop70_LogisticRegression_optimized_v5_2-07-03'.\n", "Created version '1' of model 'drop70_LogisticRegression_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : RandomForest\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 03:29:02 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 03:29:14 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'drop70_RandomForest_optimized_v5_2-07-03'.\n", "Created version '1' of model 'drop70_RandomForest_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : XGBoost\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 03:44:32 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 03:44:43 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'drop70_XGBoost_optimized_v5_2-07-03'.\n", "Created version '1' of model 'drop70_XGBoost_optimized_v5_2-07-03'.\n", "2026/03/08 03:44:55 INFO mlflow.tracking.fluent: Experiment with name 'outliers_missing_flag_base_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: Dummy\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 03:51:06 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: LogisticRegression\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 03:56:07 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: RandomForest\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 04:02:15 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: XGBoost\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 04:08:17 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 04:08:26 INFO mlflow.tracking.fluent: Experiment with name 'outliers_missing_flag_optimisé_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : Dummy\n", "Fitting 3 folds for each of 1 candidates, totalling 3 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 04:14:41 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 04:14:50 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_missing_flag_Dummy_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_missing_flag_Dummy_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : LogisticRegression\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 04:28:15 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 04:28:24 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_missing_flag_LogisticRegression_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_missing_flag_LogisticRegression_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : RandomForest\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 04:52:02 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 04:52:12 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_missing_flag_RandomForest_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_missing_flag_RandomForest_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : XGBoost\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 05:08:39 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 05:08:48 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_missing_flag_XGBoost_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_missing_flag_XGBoost_optimized_v5_2-07-03'.\n", "2026/03/08 05:08:58 INFO mlflow.tracking.fluent: Experiment with name 'outliers_imputation_base_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: Dummy\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 05:15:59 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: LogisticRegression\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 05:19:41 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: RandomForest\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 05:25:57 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: XGBoost\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 05:32:21 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 05:32:30 INFO mlflow.tracking.fluent: Experiment with name 'outliers_imputation_optimisé_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : Dummy\n", "Fitting 3 folds for each of 1 candidates, totalling 3 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 05:39:47 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 05:39:55 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_imputation_Dummy_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_imputation_Dummy_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : LogisticRegression\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 05:50:54 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 05:51:04 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_imputation_LogisticRegression_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_imputation_LogisticRegression_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : RandomForest\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 06:14:40 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 06:14:51 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_imputation_RandomForest_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_imputation_RandomForest_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : XGBoost\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 06:30:49 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 06:30:58 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_imputation_XGBoost_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_imputation_XGBoost_optimized_v5_2-07-03'.\n", "2026/03/08 06:31:08 INFO mlflow.tracking.fluent: Experiment with name 'outliers_drop70_base_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Colonnes supprimées (>=70% NaN) : ['CC_SK_DPD_DEF_max', 'CC_CNT_DRAWINGS_OTHER_CURRENT_mean', 'CC_CNT_DRAWINGS_ATM_CURRENT_sum', 'CC_AMT_DRAWINGS_CURRENT_min', 'CC_CNT_DRAWINGS_ATM_CURRENT_mean', 'PREV_RATE_INTEREST_PRIVILEGED_max', 'CC_AMT_DRAWINGS_CURRENT_mean', 'CC_NAME_CONTRACT_STATUS_Signed', 'CC_AMT_DRAWINGS_ATM_CURRENT_sum', 'BUREAU_AMT_ANNUITY_min', 'CC_UTILIZATION_mean', 'CC_AMT_BALANCE_mean', 'CC_NAME_CONTRACT_STATUS_Refused', 'CC_MONTHS_BALANCE_mean', 'CC_NAME_CONTRACT_STATUS_NUNIQUE', 'CC_UTILIZATION_max', 'CC_NAME_CONTRACT_STATUS_Sent proposal', 'CC_AMT_PAYMENT_TOTAL_CURRENT_max', 'CC_AMT_PAYMENT_CURRENT_min', 'CC_NAME_CONTRACT_STATUS_Approved', 'CC_CNT_DRAWINGS_ATM_CURRENT_min', 'CC_SK_DPD_max', 'CC_PAYMENT_RATIO_min', 'CC_CNT_DRAWINGS_OTHER_CURRENT_min', 'CC_AMT_INST_MIN_REGULARITY_min', 'BUREAU_AMT_ANNUITY_mean', 'CC_AMT_DRAWINGS_CURRENT_max', 'BUREAU_MONTHS_BALANCE_max', 'CC_MONTHS_BALANCE_max', 'CC_CNT_DRAWINGS_CURRENT_min', 'CC_AMT_DRAWINGS_ATM_CURRENT_mean', 'CC_AMT_CREDIT_LIMIT_ACTUAL_max', 'CC_AMT_CREDIT_LIMIT_ACTUAL_sum', 'CC_CNT_DRAWINGS_CURRENT_max', 'CC_AMT_DRAWINGS_POS_CURRENT_min', 'PREV_RATE_INTEREST_PRIMARY_max', 'CC_NAME_CONTRACT_STATUS_Completed', 'CC_UTILIZATION_min', 'CC_PAYMENT_RATIO_max']\n", "Modèle sans optimisation: Dummy\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 06:37:18 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: LogisticRegression\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 06:40:39 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: RandomForest\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 06:46:42 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Modèle sans optimisation: XGBoost\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 06:52:30 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 06:52:39 INFO mlflow.tracking.fluent: Experiment with name 'outliers_drop70_optimisé_v5_2-07-03' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Colonnes supprimées (>=70% NaN) : ['CC_SK_DPD_DEF_max', 'CC_CNT_DRAWINGS_OTHER_CURRENT_mean', 'CC_CNT_DRAWINGS_ATM_CURRENT_sum', 'CC_AMT_DRAWINGS_CURRENT_min', 'CC_CNT_DRAWINGS_ATM_CURRENT_mean', 'PREV_RATE_INTEREST_PRIVILEGED_max', 'CC_AMT_DRAWINGS_CURRENT_mean', 'CC_NAME_CONTRACT_STATUS_Signed', 'CC_AMT_DRAWINGS_ATM_CURRENT_sum', 'BUREAU_AMT_ANNUITY_min', 'CC_UTILIZATION_mean', 'CC_AMT_BALANCE_mean', 'CC_NAME_CONTRACT_STATUS_Refused', 'CC_MONTHS_BALANCE_mean', 'CC_NAME_CONTRACT_STATUS_NUNIQUE', 'CC_UTILIZATION_max', 'CC_NAME_CONTRACT_STATUS_Sent proposal', 'CC_AMT_PAYMENT_TOTAL_CURRENT_max', 'CC_AMT_PAYMENT_CURRENT_min', 'CC_NAME_CONTRACT_STATUS_Approved', 'CC_CNT_DRAWINGS_ATM_CURRENT_min', 'CC_SK_DPD_max', 'CC_PAYMENT_RATIO_min', 'CC_CNT_DRAWINGS_OTHER_CURRENT_min', 'CC_AMT_INST_MIN_REGULARITY_min', 'BUREAU_AMT_ANNUITY_mean', 'CC_AMT_DRAWINGS_CURRENT_max', 'BUREAU_MONTHS_BALANCE_max', 'CC_MONTHS_BALANCE_max', 'CC_CNT_DRAWINGS_CURRENT_min', 'CC_AMT_DRAWINGS_ATM_CURRENT_mean', 'CC_AMT_CREDIT_LIMIT_ACTUAL_max', 'CC_AMT_CREDIT_LIMIT_ACTUAL_sum', 'CC_CNT_DRAWINGS_CURRENT_max', 'CC_AMT_DRAWINGS_POS_CURRENT_min', 'PREV_RATE_INTEREST_PRIMARY_max', 'CC_NAME_CONTRACT_STATUS_Completed', 'CC_UTILIZATION_min', 'CC_PAYMENT_RATIO_max']\n", "Optimisation du modèle : Dummy\n", "Fitting 3 folds for each of 1 candidates, totalling 3 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 06:59:11 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 06:59:19 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_drop70_Dummy_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_drop70_Dummy_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : LogisticRegression\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 07:09:42 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 07:09:51 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_drop70_LogisticRegression_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_drop70_LogisticRegression_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : RandomForest\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 07:32:29 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 07:32:38 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_drop70_RandomForest_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_drop70_RandomForest_optimized_v5_2-07-03'.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Optimisation du modèle : XGBoost\n", "Fitting 3 folds for each of 5 candidates, totalling 15 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026/03/08 07:46:39 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "2026/03/08 07:46:47 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n", "Successfully registered model 'outliers_drop70_XGBoost_optimized_v5_2-07-03'.\n", "Created version '1' of model 'outliers_drop70_XGBoost_optimized_v5_2-07-03'.\n" ] } ], "source": [ "# Forcer MLflow à utiliser le dossier mlruns pour sauvegarder les runs sans passer par mlflow db\n", "mlflow.set_tracking_uri(\"file:///C:/Users/Lenovo/Documents/WCS/GitHub/Classification-Risque-Credit-Pipeline-MLOps-/mlruns\")\n", "\n", "\n", "preprocessings = {\n", " \"missing_flag\": preprocess_missing_flag,\n", " \"imputation\": preprocess_imputation,\n", " \"drop70\": preprocess_drop70,\n", " \"outliers_missing_flag\": preprocess_outliers_missing_flag, \n", " \"outliers_imputation\": preprocess_outliers_imputation, \n", " \"outliers_drop70\": preprocess_outliers_drop70}\n", "\n", "version = \"v5_2-07-03\"\n", "\n", "for prep_name, prep_fn in preprocessings.items():\n", "\n", " # Sans optimisation\n", " mlflow.set_experiment(f\"{prep_name}_base_{version}\")\n", "\n", " with mlflow.start_run(run_name = f\"{prep_name}_base_{version}\"):\n", "\n", " X_prep = prep_fn(X)\n", "\n", " X_train, X_test, y_train, y_test = train_test_split(X_prep, y, test_size = 0.2, stratify = y, random_state = 4)\n", "\n", " # Sauvegarde pour SHAP\n", " X_test.to_csv(f\"data/X_test_prep_{prep_name}.csv\", index=False)\n", " y_test.to_csv(f\"data/y_test_prep_{prep_name}.csv\", index=False)\n", "\n", " preprocessor = tranformation_process(X_prep)\n", " \n", "\n", " # Log du dataset sans optimisation\n", " mlflow.log_input(mlflow.data.from_pandas(X_train, source=f\"{prep_name}_train_{version}\"))\n", "\n", " for model_name, (model, _) in modeles.items():\n", " print(f\"Modèle sans optimisation: {model_name}\")\n", "\n", " with mlflow.start_run(run_name = f\"{model_name}_{version}\", nested = True):\n", " evaluation_modele(model_name=model_name, model=model, X_train=X_train, X_test=X_test, y_train=y_train, y_test=y_test, preprocessor=preprocessor)\n", "\n", "\n", " # Avec optimisation des hyperparamètres\n", " mlflow.set_experiment(f\"{prep_name}_optimisé_{version}\")\n", "\n", " with mlflow.start_run(run_name = f\"{prep_name}_optimisé_{version}\"):\n", "\n", " X_prep = prep_fn(X)\n", "\n", " X_train, X_test, y_train, y_test = train_test_split(X_prep, y, test_size = 0.2, stratify = y, random_state = 4)\n", "\n", " # Sauvegarde pour SHAP\n", " X_test.to_csv(f\"data/X_test_prep_{prep_name}.csv\", index=False)\n", " y_test.to_csv(f\"data/y_test_prep_{prep_name}.csv\", index=False)\n", "\n", " preprocessor = tranformation_process(X_prep)\n", " \n", "\n", " # Log du dataset \n", " mlflow.log_input(mlflow.data.from_pandas(X_train, source=f\"{prep_name}_train_{version}\"))\n", "\n", " for model_name, (model, param_grid) in modeles.items():\n", " print(f\"Optimisation du modèle : {model_name}\")\n", "\n", " meilleur_modele, meilleur_params, meilleur_score = optimisation_hyperparametres( model_name, model, param_grid, preprocessor, X_train, y_train )\n", "\n", " with mlflow.start_run(run_name=f\"{model_name}_optimized_{version}\", nested=True):\n", "\n", " mlflow.log_params(meilleur_params) \n", " mlflow.log_metric(\"auc_cv_meilleur\", meilleur_score)\n", "\n", " evaluation_modele(model_name=f\"{model_name}_optimized\", model=meilleur_modele.named_steps[\"model\"], X_train=X_train, X_test=X_test, \n", " y_train=y_train, y_test=y_test, preprocessor=preprocessor)\n", "\n", " # Enregistrement dans le Model Registry \n", " mlflow.sklearn.log_model( meilleur_modele, artifact_path=\"model\", registered_model_name=f\"{prep_name}_{model_name}_optimized_{version}\" )\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "a2f38554", "metadata": {}, "outputs": [], "source": [ "import os\n", "os.listdir(\"C:/Users/Lenovo/Documents/WCS/GitHub/Classification-Risque-Credit-Pipeline-MLOps/mlruns\")" ] }, { "cell_type": "code", "execution_count": 15, "id": "e19683d4", "metadata": {}, "outputs": [], "source": [ "mlflow.end_run()" ] }, { "cell_type": "code", "execution_count": 67, "id": "93f3f190", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
| \n", " | experiment | \n", "run_id | \n", "run_name | \n", "preprocessing | \n", "mode | \n", "model | \n", "auc | \n", "seuil_optimal | \n", "cout_minimal | \n", "précision | \n", "recall_bad_payers | \n", "params | \n", "
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 21 | \n", "outliers_missing_flag_optimisé_v5_2-07-03 | \n", "79a042a4457d43c5a89a121722be0a30 | \n", "XGBoost_optimized_v5_2-07-03 | \n", "outliers | \n", "optimisé | \n", "XGBoost_v5_2-07-03 | \n", "0.78 | \n", "0.50 | \n", "30629.0 | \n", "0.188498 | \n", "0.672709 | \n", "{'base_score': 'None', 'booster': 'None', 'cal... | \n", "
| 112 | \n", "missing_flag_optimisé_v5-07-03 | \n", "cd71d3352dad456f88a07b1a963d3829 | \n", "XGBoost_optimized_v5-07-03 | \n", "missing | \n", "optimisé | \n", "XGBoost_v5-07-03 | \n", "0.78 | \n", "0.47 | \n", "30653.0 | \n", "0.187219 | \n", "0.715408 | \n", "{'base_score': 'None', 'booster': 'None', 'cal... | \n", "
| 82 | \n", "outliers_missing_flag_optimisé_v5-07-03 | \n", "3cbe62ccc9344fbaae6cbfee303d3690 | \n", "XGBoost_optimized_v5-07-03 | \n", "outliers | \n", "optimisé | \n", "XGBoost_v5-07-03 | \n", "0.78 | \n", "0.47 | \n", "30726.0 | \n", "0.187479 | \n", "0.710574 | \n", "{'base_score': 'None', 'booster': 'None', 'cal... | \n", "
| 51 | \n", "missing_flag_optimisé_v5_2-07-03 | \n", "bd607866a59f4ee6a2885ce926a59439 | \n", "XGBoost_optimized_v5_2-07-03 | \n", "missing | \n", "optimisé | \n", "XGBoost_v5_2-07-03 | \n", "0.78 | \n", "0.49 | \n", "30771.0 | \n", "0.186758 | \n", "0.684189 | \n", "{'base_score': 'None', 'booster': 'None', 'cal... | \n", "
| 72 | \n", "outliers_imputation_optimisé_v5-07-03 | \n", "fe9a86b14f514878be5df6bf2692e663 | \n", "XGBoost_optimized_v5-07-03 | \n", "outliers | \n", "optimisé | \n", "XGBoost_v5-07-03 | \n", "0.78 | \n", "0.51 | \n", "30771.0 | \n", "0.187865 | \n", "0.657200 | \n", "{'base_score': 'None', 'booster': 'None', 'cal... | \n", "
| ... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "
| 133 | \n", "imputation_optimisé_v4-07-03 | \n", "98555b84ce5244eb9543e5abd4ff61e6 | \n", "imputation_optimisé_v4-07-03 | \n", "imputation | \n", "optimisé | \n", "imputation_optimisé_v4-07-03 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "{} | \n", "
| 138 | \n", "imputation_base_v4-07-03 | \n", "3d975f8448a544fd945a2820eaf6b6a4 | \n", "imputation_base_v4-07-03 | \n", "imputation | \n", "base | \n", "imputation_base_v4-07-03 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "{} | \n", "
| 143 | \n", "missing_flag_optimisé_v4-07-03 | \n", "1c85cdcc54d94edba7e757ebab6520b2 | \n", "missing_flag_optimisé_v4-07-03 | \n", "missing | \n", "optimisé | \n", "missing_flag_optimisé_v4-07-03 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "{} | \n", "
| 144 | \n", "missing_flag_base_v4-07-03 | \n", "09350d3ffa5a490c82cd576116cf480a | \n", "missing_flag_base_v4-07-03 | \n", "missing | \n", "base | \n", "missing_flag_base_v4-07-03 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "{} | \n", "
| 149 | \n", "missing_flag_base_v4-07-03 | \n", "d5922ac0b502421dae3d09c6c7fb4596 | \n", "missing_flag_base_v4-07-03 | \n", "missing | \n", "base | \n", "missing_flag_base_v4-07-03 | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "NaN | \n", "{} | \n", "
150 rows × 12 columns
\n", "| \n", " | SK_ID_CURR | \n", "TARGET | \n", "DECISION | \n", "
|---|---|---|---|
| 0 | \n", "100001 | \n", "0.144826 | \n", "REFUSÉ | \n", "
| 1 | \n", "100005 | \n", "0.523025 | \n", "ACCORDÉ | \n", "
| 2 | \n", "100013 | \n", "0.084557 | \n", "REFUSÉ | \n", "
| 3 | \n", "100028 | \n", "0.239340 | \n", "REFUSÉ | \n", "
| 4 | \n", "100038 | \n", "0.266300 | \n", "REFUSÉ | \n", "
| 5 | \n", "100042 | \n", "0.099464 | \n", "REFUSÉ | \n", "
| 6 | \n", "100057 | \n", "0.058770 | \n", "REFUSÉ | \n", "
| 7 | \n", "100065 | \n", "0.106076 | \n", "REFUSÉ | \n", "
| 8 | \n", "100066 | \n", "0.172418 | \n", "REFUSÉ | \n", "
| 9 | \n", "100067 | \n", "0.567248 | \n", "ACCORDÉ | \n", "
| 10 | \n", "100074 | \n", "0.123175 | \n", "REFUSÉ | \n", "
| 11 | \n", "100090 | \n", "0.344088 | \n", "REFUSÉ | \n", "
| 12 | \n", "100091 | \n", "0.679527 | \n", "ACCORDÉ | \n", "
| 13 | \n", "100092 | \n", "0.419423 | \n", "REFUSÉ | \n", "
| 14 | \n", "100106 | \n", "0.166479 | \n", "REFUSÉ | \n", "