{ "cells": [ { "cell_type": "markdown", "id": "70148918-76b1-4657-8d02-442a8883e0c0", "metadata": {}, "source": [ "# Project 4 - Identifiez les causes d'attrition au sein d'une ESN¶" ] }, { "cell_type": "markdown", "id": "e389e683-cad2-4ef9-a3c8-0b838fdbb5ed", "metadata": {}, "source": [ "# Etape 3: Modelisation\n", "**Objectif**: construire un premier modéle de classification\n", "\n", "**Contenu :**\n", "\n", "- Chargement des données traitées\n", "\n", "- Entraînement d’un modèle de base (un modèle Dummy, régression logistique, un modèle Dummy, un modèle non-linéaire RandomForest)\n", "\n", "- Évaluation : accuracy, precision, rappel, f1-score, roc_auc\n", "\n", "- Visualisation de la matrice de confusion\n", "\n", "- Premières conclusions sur les performances" ] }, { "cell_type": "code", "execution_count": 2, "id": "7cad420e-a77b-4ec4-b5f7-38b360ae9ed7", "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 44, "id": "368ef874-cdb9-4742-b214-56acca76bc11", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score, GridSearchCV, cross_val_predict, cross_validate\n", "from sklearn.compose import ColumnTransformer\n", "from sklearn.preprocessing import StandardScaler, OneHotEncoder, OrdinalEncoder\n", "from sklearn.pipeline import Pipeline\n", "from sklearn.ensemble import RandomForestClassifier\n", "from sklearn.linear_model import LogisticRegression\n", "from sklearn.dummy import DummyClassifier\n", "from sklearn.metrics import (\n", " accuracy_score, precision_score, recall_score, f1_score,\n", " roc_auc_score, average_precision_score,\n", " classification_report, confusion_matrix, ConfusionMatrixDisplay,\n", " precision_recall_curve\n", ")\n", "# imblearn\n", "from imblearn.over_sampling import SMOTE\n", "from imblearn.pipeline import Pipeline as ImbPipeline\n", "\n", "from sklearn.inspection import permutation_importance\n", "\n", "import shap\n", "shap.initjs() \n", "\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "74640976-e963-4d1c-a0c5-b07374c29120", "metadata": {}, "outputs": [], "source": [ "# Données\n", "df = pd.read_csv(\"../data/processed/df_central_clean.csv\")" ] }, { "cell_type": "code", "execution_count": 4, "id": "da42bdf9-9d82-45d3-8fbb-aff126cf81d0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
agegenrerevenu_mensuelstatut_maritaldepartementpostenombre_experiences_precedentesannee_experience_totaleannees_dans_l_entrepriseannees_dans_le_poste_actuel...augementation_salaire_precedenteattritionnombre_participation_peenb_formations_suiviesdistance_domicile_travailniveau_educationdomaine_etudefrequence_deplacementannees_depuis_la_derniere_promotionannes_sous_responsable_actuel
041F5993CélibataireCommercialCadre Commercial8864...1110012Infra & CloudOccasionnel05
149M5130Marié(e)ConsultingAssistant de Direction110107...2301381Infra & CloudFrequent17
237M2090CélibataireConsultingConsultant6700...1510322AutreOccasionnel00
333F2909Marié(e)ConsultingAssistant de Direction1887...1100334Infra & CloudFrequent30
427M3468Marié(e)ConsultingConsultant9622...1201321Transformation DigitaleOccasionnel22
\n", "

5 rows × 27 columns

\n", "
" ], "text/plain": [ " age genre revenu_mensuel statut_marital departement \\\n", "0 41 F 5993 Célibataire Commercial \n", "1 49 M 5130 Marié(e) Consulting \n", "2 37 M 2090 Célibataire Consulting \n", "3 33 F 2909 Marié(e) Consulting \n", "4 27 M 3468 Marié(e) Consulting \n", "\n", " poste nombre_experiences_precedentes \\\n", "0 Cadre Commercial 8 \n", "1 Assistant de Direction 1 \n", "2 Consultant 6 \n", "3 Assistant de Direction 1 \n", "4 Consultant 9 \n", "\n", " annee_experience_totale annees_dans_l_entreprise \\\n", "0 8 6 \n", "1 10 10 \n", "2 7 0 \n", "3 8 8 \n", "4 6 2 \n", "\n", " annees_dans_le_poste_actuel ... augementation_salaire_precedente \\\n", "0 4 ... 11 \n", "1 7 ... 23 \n", "2 0 ... 15 \n", "3 7 ... 11 \n", "4 2 ... 12 \n", "\n", " attrition nombre_participation_pee nb_formations_suivies \\\n", "0 1 0 0 \n", "1 0 1 3 \n", "2 1 0 3 \n", "3 0 0 3 \n", "4 0 1 3 \n", "\n", " distance_domicile_travail niveau_education domaine_etude \\\n", "0 1 2 Infra & Cloud \n", "1 8 1 Infra & Cloud \n", "2 2 2 Autre \n", "3 3 4 Infra & Cloud \n", "4 2 1 Transformation Digitale \n", "\n", " frequence_deplacement annees_depuis_la_derniere_promotion \\\n", "0 Occasionnel 0 \n", "1 Frequent 1 \n", "2 Occasionnel 0 \n", "3 Frequent 3 \n", "4 Occasionnel 2 \n", "\n", " annes_sous_responsable_actuel \n", "0 5 \n", "1 7 \n", "2 0 \n", "3 0 \n", "4 2 \n", "\n", "[5 rows x 27 columns]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.head()" ] }, { "cell_type": "markdown", "id": "fd6bbb58-ac12-4a87-87f9-dda7149b139a", "metadata": {}, "source": [ "### Préparation de variables\n", "- binaires (0,1)\n", "- catégoriques\n", "- numeriques" ] }, { "cell_type": "code", "execution_count": null, "id": "a9c6d579-9d1b-49c9-b666-ef24c11f44b6", "metadata": {}, "outputs": [], "source": [ "target = \"attrition\"" ] }, { "cell_type": "code", "execution_count": null, "id": "bc386a9c-6605-48f6-8ba5-02b8366619f4", "metadata": {}, "outputs": [], "source": [ "num_cols = [\n", " \"age\", \"revenu_mensuel\", \"nombre_experiences_precedentes\",\n", " \"annee_experience_totale\", \"annees_dans_l_entreprise\",\n", " \"annees_dans_le_poste_actuel\", \"satisfaction_employee_environnement\",\n", " \"note_evaluation_precedente\", \"satisfaction_employee_nature_travail\",\n", " \"satisfaction_employee_equipe\", \"satisfaction_employee_equilibre_pro_perso\",\n", " \"note_evaluation_actuelle\", \"heure_supplementaires\",\n", " \"augementation_salaire_precedente\", \"nombre_participation_pee\",\n", " \"nb_formations_suivies\", \"distance_domicile_travail\",\n", " \"niveau_education\", \"annees_depuis_la_derniere_promotion\",\n", " \"annes_sous_responsable_actuel\",\n", "]" ] }, { "cell_type": "code", "execution_count": null, "id": "0d843f23-d103-4175-a385-98c29eec10ee", "metadata": {}, "outputs": [], "source": [ "ord_col = [\"frequence_deplacement\"] # encodage ordinal Aucune/Occasionnel/Fréquent\n", "ord_categories = [[\"Aucun\", \"Occasionnel\", \"Frequent\"]]" ] }, { "cell_type": "code", "execution_count": null, "id": "de4d9521-6ade-4c0f-a978-862aa9130027", "metadata": {}, "outputs": [], "source": [ "cat_cols = [\n", " \"genre\", \"statut_marital\", \"departement\", \"poste\", \"domaine_etude\"\n", "]" ] }, { "cell_type": "code", "execution_count": null, "id": "f45b62b2-5a0b-4317-9eb7-c9d53c7096be", "metadata": {}, "outputs": [], "source": [ "X = df[num_cols + ord_col + cat_cols].copy()" ] }, { "cell_type": "code", "execution_count": null, "id": "c50efaec-99b1-4d4b-af93-18152e0eff24", "metadata": {}, "outputs": [], "source": [ "\n", "X" ] }, { "cell_type": "code", "execution_count": null, "id": "b7745af4-011d-4807-b9ab-b5815c4dadd0", "metadata": {}, "outputs": [], "source": [ "X.columns" ] }, { "cell_type": "code", "execution_count": null, "id": "ebb626bb-3c46-481d-9e9c-65d78acbb5e9", "metadata": {}, "outputs": [], "source": [ "y = df[target].astype(int)" ] }, { "cell_type": "code", "execution_count": null, "id": "79667a14-a653-458d-955d-0e8425df0698", "metadata": {}, "outputs": [], "source": [ "y" ] }, { "cell_type": "code", "execution_count": null, "id": "6175fbe4-8636-4fc3-8ba0-5a2df402b579", "metadata": {}, "outputs": [], "source": [ "print(\"X shape:\", X.shape, \"| y shape:\", y.shape)" ] }, { "cell_type": "code", "execution_count": null, "id": "d128c132-cf14-45e7-8950-6a1fa6a4399b", "metadata": {}, "outputs": [], "source": [ "# Split stratifié train / test\n", "X_train, X_test, y_train, y_test = train_test_split (\n", " X, y, test_size=0.2, random_state=42, stratify=y\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "799d262c-af56-45c9-91d2-21180f62f24b", "metadata": {}, "outputs": [], "source": [ "print(\"X_train:\", X_train.shape, \"| X_test:\", X_test.shape)\n", "print(\"y_train:\", y_train.shape, \"| y_test:\", y_test.shape)" ] }, { "cell_type": "code", "execution_count": null, "id": "a09fffc9-f813-4f1d-9914-5070b9433435", "metadata": {}, "outputs": [], "source": [ "enc_ord = OrdinalEncoder(\n", " categories=ord_categories,\n", " handle_unknown=\"use_encoded_value\", unknown_value=-1\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "c9a11ff7-a297-4e02-b5f2-3f7a7eda7e06", "metadata": {}, "outputs": [], "source": [ "ohe = OneHotEncoder(\n", " handle_unknown=\"ignore\",\n", " sparse_output=False\n", ")\n" ] }, { "cell_type": "code", "execution_count": null, "id": "14f8f3ab-968e-4eac-98eb-a3433b72ec36", "metadata": {}, "outputs": [], "source": [ "preproc = ColumnTransformer(\n", " transformers=[\n", " (\"ord\", enc_ord, ord_col),\n", " (\"ohe\", ohe, cat_cols),\n", " (\"num\", \"passthrough\", num_cols)\n", " ]\n", ")" ] }, { "cell_type": "markdown", "id": "b91142c3-a2fe-4ae5-b3ef-a60bad0da1c7", "metadata": {}, "source": [ "## Réalisation d'un premier modéle de classification\n", "\n", "## Modèles : Dummy, Logit, RandomForest\n", "\n", "Modèle de base (baseline) Dummy\n", "\n", "Modèle linéaire LogisticRegression\n", "\n", "Modèle non-linéaire (principal pour l’amélioration) RandomForest" ] }, { "cell_type": "code", "execution_count": null, "id": "019b6275-d8a1-444b-9e2f-148e0c1339b1", "metadata": {}, "outputs": [], "source": [ "# Modèle de base (baseline)\n", "dummy = DummyClassifier(strategy=\"most_frequent\", random_state=42)\n", "\n", "# Modèle linéaire\n", "logit_bal = LogisticRegression(\n", " solver=\"liblinear\",\n", " class_weight=\"balanced\",\n", " max_iter=1000,\n", " random_state=42\n", ")\n", "\n", "# Modèle non-linéaire (principal pour l’amélioration)\n", "rf_bal = RandomForestClassifier(\n", " n_estimators=100,\n", " random_state=42,\n", " n_jobs=-1\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "c20487b0-f9f1-4318-9291-4953c9136072", "metadata": {}, "outputs": [], "source": [ "# Pipelines (préprocess + modèle)\n", "\n", "pipe_dummy = Pipeline([\n", " (\"prep\", preproc),\n", " (\"clf\", dummy)\n", "])\n", "\n", "pipe_logit = Pipeline([\n", " (\"prep\", preproc),\n", " (\"clf\", logit_bal)\n", "])\n", "\n", "pipe_rf = Pipeline([\n", " (\"prep\", preproc),\n", " (\"clf\", rf_bal)\n", "])" ] }, { "cell_type": "markdown", "id": "673b6620-bd8d-4f44-a09f-ebefeb1f4444", "metadata": {}, "source": [ "### Fonction d’évaluation (TRAIN / TEST, métriques complètes)" ] }, { "cell_type": "code", "execution_count": null, "id": "1799b3f4-572a-44c0-9b20-b7604a52c18e", "metadata": {}, "outputs": [], "source": [ "def eval_side(X, y, model, set_name=\"TEST\", plot_cm=True):\n", " \n", " \"\"\" Fonction pour evalué un modéle avce le seuil standart 0.5 (via model.predict).\n", " Calculer et afficher les métriques clés + matrice de confusion pour un jeu (train/test).\"\"\"\n", " \n", " # Prédictions avce seuil = 0.5 interne au modèle\n", " y_pred = model.predict(X)\n", " # Probabilité pour la classe 1, si dispo\n", " y_proba = model.predict_proba(X)[:, 1] if hasattr(model, \"predict_proba\") else None\n", "\n", " acc = accuracy_score(y, y_pred)\n", " prec = precision_score(y, y_pred, pos_label=1, zero_division=0)\n", " rec = recall_score(y, y_pred, pos_label=1, zero_division=0)\n", " f1 = f1_score(y, y_pred, pos_label=1, zero_division=0)\n", "\n", " if y_proba is not None:\n", " roc = roc_auc_score(y, y_proba)\n", " pr = average_precision_score(y, y_proba)\n", " else:\n", " roc, pr = float(\"nan\"), float(\"nan\")\n", "\n", "\n", " print(f\"\\n--- {set_name} ---\")\n", " print(f\"Accuracy : {acc:.3f} | Précision : {prec:.3f} | Rappel : {rec:.3f} | F1 : {f1:.3f}\")\n", " if y_proba is not None:\n", " print(f\"ROC AUC : {roc:.3f} | PR AUC : {pr:.3f}\")\n", " print(\"\\nClassification report :\\n\", classification_report(y, y_pred, digits=3))\n", "\n", " if plot_cm:\n", " cm = confusion_matrix(y, y_pred, labels=[0, 1])\n", " disp = ConfusionMatrixDisplay(cm, display_labels=[\"Restés (0)\", \"Partis (1)\"])\n", " fig, ax = plt.subplots(figsize=(4,4))\n", " disp.plot(ax=ax, cmap=\"Blues\", values_format=\"d\", colorbar=False)\n", " ax.set_title(f\"Matrice de confusion – {set_name}\")\n", " plt.tight_layout(); plt.show()\n", "\n", " return {\"accuracy\":acc, \"precision\":prec, \"recall\":rec, \"f1\":f1, \"roc_auc\":roc, \"pr_auc\":pr}" ] }, { "cell_type": "code", "execution_count": null, "id": "8303dc94-9ee0-44e5-9c21-117216ca50a4", "metadata": {}, "outputs": [], "source": [ "def fit_and_evaluate(name, model, X_train, y_train, X_test, y_test):\n", " \n", " \"\"\"Fonction pour entraîner le modèle sur TRAIN et évaluer sur TRAIN et TEST. \n", " Retourne les scores.\"\"\"\n", " print(f\"\\n================= {name} =================\")\n", " model.fit(X_train, y_train)\n", " scores_train = eval_side(X_train, y_train, model, set_name=\"TRAIN\")\n", " scores_test = eval_side(X_test, y_test, model, set_name=\"TEST\")\n", " return {\"train\": scores_train, \"test\": scores_test}" ] }, { "cell_type": "code", "execution_count": null, "id": "d9dfe68f-aea3-431f-bc25-5e12dd52f2f5", "metadata": {}, "outputs": [], "source": [ "# Évaluation des 3 modèles de base\n", "scores_dummy = fit_and_evaluate(\"Dummy\", pipe_dummy, X_train, y_train, X_test, y_test)\n", "scores_logit = fit_and_evaluate(\"Logistic Regression (balanced)\", pipe_logit, X_train, y_train, X_test, y_test)\n", "scores_rf = fit_and_evaluate(\"RandomForest (balanced)\", pipe_rf, X_train, y_train, X_test, y_test)" ] }, { "cell_type": "code", "execution_count": null, "id": "b34ab5a5-aa29-4500-8110-195502e929ac", "metadata": {}, "outputs": [], "source": [ "# Construire les DataFrames\n", "df_metrics_train = pd.DataFrame([\n", " {\"model\": \"Dummy\", \"set\": \"TRAIN\", **scores_dummy[\"train\"]},\n", " {\"model\": \"Logistic Regression (balanced)\",\"set\": \"TRAIN\", **scores_logit[\"train\"]},\n", " {\"model\": \"RandomForest (balanced)\", \"set\": \"TRAIN\", **scores_rf[\"train\"]},\n", "])\n", "\n", "df_metrics_test = pd.DataFrame([\n", " {\"model\": \"Dummy\", \"set\": \"TEST\", **scores_dummy[\"test\"]},\n", " {\"model\": \"Logistic Regression (balanced)\",\"set\": \"TEST\", **scores_logit[\"test\"]},\n", " {\"model\": \"RandomForest (balanced)\", \"set\": \"TEST\", **scores_rf[\"test\"]},\n", "])" ] }, { "cell_type": "code", "execution_count": null, "id": "12cce1f7-dff9-4e69-94a5-315a1074f98a", "metadata": {}, "outputs": [], "source": [ "print(\"METRICS – TRAIN\")\n", "display(df_metrics_train.round(3))\n", "\n", "print(\"METRICS – TEST\")\n", "display(df_metrics_test.round(3))" ] }, { "cell_type": "markdown", "id": "372bd167-e7e1-4bbd-b128-895c13478b1e", "metadata": {}, "source": [ "Dummy = baseline très mauvais sur la classe 1\n", "\n", "Logit (balanced) = bon compromis\n", "\n", "RF (balanced) = surapprentissage (train parfait, test mauvais rappel)" ] }, { "cell_type": "markdown", "id": "a5426c0f-db57-4520-9209-39bdd151fe64", "metadata": {}, "source": [ "**Résultats:**\n", "\n", "**Dummy** : baseline très mauvais sur la classe 1\n", "\n", "84% d’accuracy mais rappel=0 → fort déséquilibre (le modèle prédit tout “0”).\n", "\n", "**Logistique** : bon compromis\n", "\n", "AUC=0.824, précision=0.63, rappel=0.36 → il y a du signal linéaire, mais on rate encore beaucoup de départs. \n", "\n", "Modèle utile pour capter les départs (bon rappel) avec un coût raisonnable en FP.\n", "\n", "Écart train/test modéré → peu d’overfit.\n", "\n", "**RandomForest** : overfit massif - surapprentissage \n", "\n", "train parfait, \n", "\n", "test mauvais rappel (ne capte presque aucun départ)\n" ] }, { "cell_type": "markdown", "id": "be90bba5-6778-4556-8906-7c113fee8e8b", "metadata": {}, "source": [ "# Amélioration du modèle non-linéaire (RF)" ] }, { "cell_type": "markdown", "id": "cb820937-32ad-4755-b69a-2996c6cfbd45", "metadata": {}, "source": [ "CV avec `StratifiedKFold + cross_val_score`\n", "\n", "Courbe PR avec out-of-fold\n", "\n", "Choix du seuil optimal (F1, F2, etc.)\n", "\n", "Évaluation finale avec eval_with_threshold sur le test." ] }, { "cell_type": "markdown", "id": "b9337e46-d89f-4185-b6ea-a368d4f4b99e", "metadata": {}, "source": [ "### Validation croisée (StratifiedKFold) et stockage des métriques par fold\n", "- `StratifiedKFold`\n", "- `cross_val_score` avec 3 métriques : accuracy, ROC AUC, PR AUC\n", "- calcule moyenne + écart-type." ] }, { "cell_type": "code", "execution_count": null, "id": "d4007b53-4044-41d7-a64e-3fe97f6d7280", "metadata": {}, "outputs": [], "source": [ "def cv_scores_summary(model, X_train, y_train, n_splits=10, random_state=42):\n", " \n", " \"\"\"CV sur X_train/y_train avec stratification + scaling dans la Pipeline.\"\"\"\n", " \n", " cv = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=random_state)\n", "\n", " # pipeline = préproc + modèle\n", " pipe = Pipeline(steps=[(\"prep\", preproc), (\"clf\", model)])\n", "\n", " s_roc = cross_val_score(pipe, X_train, y_train, cv=cv, scoring=\"roc_auc\", n_jobs=-1)\n", " s_pr = cross_val_score(pipe, X_train, y_train, cv=cv, scoring=\"average_precision\", n_jobs=-1)\n", " s_acc = cross_val_score(pipe, X_train, y_train, cv=cv, scoring=\"accuracy\", n_jobs=-1)\n", "\n", " df_folds = pd.DataFrame({\n", " \"fold\": np.arange(1, n_splits+1),\n", " \"roc_auc\": s_roc,\n", " \"pr_auc\": s_pr,\n", " \"accuracy\": s_acc\n", " })\n", " \n", " summary = (\n", " df_folds[[\"roc_auc\",\"pr_auc\",\"accuracy\"]]\n", " .agg([\"mean\",\"std\"]).T.reset_index()\n", " .rename(columns={\"index\":\"metric\"})\n", " )\n", " return df_folds, summary" ] }, { "cell_type": "code", "execution_count": null, "id": "bd47d65f-8619-46f8-979b-9932173c03af", "metadata": {}, "outputs": [], "source": [ "# Logit équilibré\n", "df_logit_folds, sum_logit = cv_scores_summary(logit_bal, X_train, y_train, n_splits=10)\n", "\n", "display(\"Logit – CV (moyenne et STD)\", sum_logit.round(3))" ] }, { "cell_type": "code", "execution_count": null, "id": "f3288261-49bc-4464-b7a3-80be6ac88de7", "metadata": {}, "outputs": [], "source": [ "# Modèle Random Forest non-linéaire, pondéré\n", "rf_bal = RandomForestClassifier(\n", " n_estimators=100,\n", " class_weight=\"balanced\",\n", " n_jobs=-1,\n", " random_state=42\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "4ae4c7b0-b219-4b24-8d78-3d368acb33d3", "metadata": {}, "outputs": [], "source": [ "# Random Forest équilibré\n", "df_rf_folds, sum_rf = cv_scores_summary(rf_bal, X_train, y_train, n_splits=10)\n", "display(\"RF – CV (moyenne et std):\\n\", sum_rf.round(3))\n" ] }, { "cell_type": "markdown", "id": "2aa0aa41-0d7a-4116-a1f7-14638b3926b1", "metadata": {}, "source": [ "Mon modèle RF non linéaire généralise de manière assez cohérente sur différents splits de train\n", "\n", "En validation croisée stratifiée (10 folds), le RandomForest pondéré obtient en moyenne :\n", "\n", "**ROC AUC ≈ 0.80 (± 0.05)** le modèle sépare assez bien “restés” / “partis”\n", "\n", "**PR AUC ≈ 0.56 (± 0.11)** sur une classe minoritaire (~13%), c’est nettement mieux que le hasard (≈ 0.13)\n", "\n", "**Accuracy ≈ 0.86 (± 0.02)** haute, mais à interpréter avec prudence à cause du déséquilibre\n", "\n", "Les écarts-types relativement faibles indiquent une bonne stabilité du modèle d’un fold à l’autre.\n", "Comparé au modèle Dummy, le RandomForest apporte un gain net en capacité de détection des salariés à risque (PR AUC nettement supérieure).\n", "Cependant, par rapport à la régression logistique pondérée, le RF reste moins performant en PR AUC, ce qui est important dans notre contexte où l’on préfère bien identifier les employés à risque de départ." ] }, { "cell_type": "markdown", "id": "3292b789-8660-46c4-803a-3e6590e0a844", "metadata": {}, "source": [ "**À retenir comme métrique principale : PR AUC**\n", "\n", "**PR AUC (average precision)** : métrique la plus pertinente quand la classe “partis” est minoritaire.\n", "\n", "**ROC AUC**: qualité de tri des scores (utile mais moins sensible à l’imbalance que PR AUC).\n", "\n", "**Accuracy** : à prendre avec beaucoup de recul (peut être trompeuse en cas de déséquilibre)." ] }, { "cell_type": "markdown", "id": "b92822d4-cf30-4bbf-84f8-dbd6ea72f4da", "metadata": {}, "source": [ "### Courbe Précision–Rappel (sur TRAIN en CV) + seuil optimal pour RandomForest (OOF)\n", "\n", "On construit des predictions out-of-fold sur tout le TRAIN pour RF, puis on calcule la courbe PR et les F1 / F2 pour choisir un seuil.\n", "\n", "On utilise `cross_val_predict(..., method=\"predict_proba\")` en CV stratifiée sur le train pour calculer la courbe PR et choisir un seuil." ] }, { "cell_type": "code", "execution_count": null, "id": "4784c977-db45-42bb-a366-1542ce1bd909", "metadata": {}, "outputs": [], "source": [ "cv = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)" ] }, { "cell_type": "code", "execution_count": null, "id": "9857f48d-2247-4e19-8b5f-ad3c3e0d2ff6", "metadata": {}, "outputs": [], "source": [ "oof_proba = cross_val_predict(\n", " pipe_rf,\n", " X_train,\n", " y_train,\n", " cv=cv,\n", " method=\"predict_proba\",\n", " n_jobs=-1\n", ")[:, 1]\n", "\n", "oof_true = y_train.values" ] }, { "cell_type": "code", "execution_count": null, "id": "608078eb-75a5-482f-a41b-abc6468f165c", "metadata": {}, "outputs": [], "source": [ "# Courbe PR\n", "precisions, recalls, thresholds = precision_recall_curve(oof_true, oof_proba)\n", "pr_auc_oof = average_precision_score(oof_true, oof_proba)\n", "\n", "print(f\"Average Precision (PR AUC) OOF RF = {pr_auc_oof:.3f}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "a789a1e4-42dd-478f-85ee-5dc01e1d7159", "metadata": {}, "outputs": [], "source": [ "# Comme thresholds a une taille de len(precisions)-1,\n", "# on aligne sur thresholds en enlevant le dernier point de précision/rappel\n", "p = precisions[1:] # ou precisions[:-1]\n", "r = recalls[1:] # ou recalls[:-1]\n", "t = thresholds # longueur N-1" ] }, { "cell_type": "code", "execution_count": null, "id": "d32884d5-92ef-4354-88e2-eec6a0dac504", "metadata": {}, "outputs": [], "source": [ "beta2 = 2.0" ] }, { "cell_type": "code", "execution_count": null, "id": "506a64bf-cc95-479c-931a-fd49175a2744", "metadata": {}, "outputs": [], "source": [ "# Calcul F1 et F2 pour chaque seuil\n", "\n", "f1_scores = 2 * (precisions * recalls) / (precisions + recalls + 1e-8)\n", "f2_scores = (1 + beta2**2) * (precisions * recalls) / (beta2**2 * precisions + recalls + 1e-8)" ] }, { "cell_type": "code", "execution_count": null, "id": "ba5a0dea-bece-4bc1-8df3-4d67d0ceff35", "metadata": {}, "outputs": [], "source": [ "best_idx_f1 = np.nanargmax(f1_scores)\n", "best_thr_f1 = thresholds[best_idx_f1] " ] }, { "cell_type": "code", "execution_count": null, "id": "215ebb63-8151-4f2c-bc3c-75877aebaab3", "metadata": {}, "outputs": [], "source": [ "best_idx_f2 = np.nanargmax(f2_scores)\n", "best_thr_f2 = thresholds[best_idx_f2]" ] }, { "cell_type": "code", "execution_count": null, "id": "2637d3ca-4527-4ae0-9362-4768858b2ba1", "metadata": {}, "outputs": [], "source": [ "print(f\"Seuil F1 optimal : {best_thr_f1:.3f} | P={precisions[best_idx_f1]:.3f} R={recalls[best_idx_f1]:.3f} F1={f1_scores[best_idx_f1]:.3f}\")\n", "print(f\"Seuil F2 optimal : {best_thr_f2:.3f} | P={precisions[best_idx_f2]:.3f} R={recalls[best_idx_f2]:.3f} F2={f2_scores[best_idx_f2]:.3f}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "9118a56b-a93b-4228-a297-5f5fcd13f8bc", "metadata": {}, "outputs": [], "source": [ "# Plot PR + points F1/F2\n", "plt.figure(figsize=(6,5))\n", "plt.plot(recalls, precisions, label=f\"PR curve (AP={pr_auc_oof:.3f})\")\n", "plt.scatter(recalls[best_idx_f1], precisions[best_idx_f1], color=\"tab:orange\", label=f\"Best F1 (thr={best_thr_f1:.2f})\")\n", "plt.scatter(recalls[best_idx_f2], precisions[best_idx_f2], color=\"tab:green\", label=f\"Best F2 (thr={best_thr_f2:.2f})\")\n", "plt.xlabel(\"Recall\")\n", "plt.ylabel(\"Precision\")\n", "plt.title(\"Courbe précision-rappel – RF (OOF)\")\n", "plt.legend()\n", "plt.grid(alpha=0.4)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "b4d7a001-e109-44c3-9168-cd9b130cc0e1", "metadata": {}, "source": [ "**Choix d'optimisation (métiers -> seuil)** Notre priorité est de ne pas rater des démissions, en acceptant une baisse de précision. Pour ça, on privilégie un seuil plus bas pour augmenter le rappel (capturer un maximum de futurs départs)\n", "\n", "Sur la base de la courbe précision–rappel, le Random Forest atteint une Average Precision de 0,54 en out-of-fold, contre ~0,16 pour un modèle aléatoire.\n", "\n", "En optimisant la F1, on obtient un seuil d’environ 0,25, qui offre un compromis équilibré entre précision (50 %) et rappel (60 %).\n", "\n", "Cependant, dans un contexte de prévention des démissions, il est plus coûteux de rater un salarié réellement en risque (faux négatif) que d’alerter à tort (faux positif). Nous avons donc également étudié la F2-score, qui donne davantage de poids au rappel. Le seuil optimisant F2 est plus bas (~0,14) : il augmente le rappel au prix d’une baisse de précision." ] }, { "cell_type": "markdown", "id": "99000822-3871-4007-a3b8-e5fb28a1eea3", "metadata": {}, "source": [ "### Fit final sur TRAIN et évaluation sur TEST avec le seuil choisi" ] }, { "cell_type": "markdown", "id": "96ad94a1-209e-4a9e-b6cf-0f4551aae6b6", "metadata": {}, "source": [ "Nouvelle fonction eval_with_threshold (seuil personnalisé)" ] }, { "cell_type": "code", "execution_count": 45, "id": "5f18571b-a9c7-4281-9249-8e2d46d0c07c", "metadata": {}, "outputs": [], "source": [ "def eval_with_threshold(y_true, y_proba, threshold,\n", " set_name=\"TEST – seuil custom\", plot_cm=True):\n", " \"\"\"\n", " Évalue les performances pour un seuil donné appliqué sur y_proba.\n", " y_true : séries/array de labels (0/1)\n", " y_proba : probabilités P(y=1)\n", " threshold : seuil de classification\n", " \"\"\"\n", " # Binarisation par seuil\n", " y_pred = (y_proba >= threshold).astype(int)\n", "\n", " acc = accuracy_score(y_true, y_pred)\n", " prec = precision_score(y_true, y_pred, pos_label=1, zero_division=0)\n", " rec = recall_score(y_true, y_pred, pos_label=1, zero_division=0)\n", " f1 = f1_score(y_true, y_pred, pos_label=1, zero_division=0)\n", " roc = roc_auc_score(y_true, y_proba)\n", " pr = average_precision_score(y_true, y_proba)\n", "\n", " print(f\"\\n=== {set_name} (seuil={threshold:.3f}) ===\")\n", " print(f\"Accuracy : {acc:.3f} | Précision : {prec:.3f} | Rappel : {rec:.3f} | F1 : {f1:.3f}\")\n", " print(f\"ROC AUC : {roc:.3f} | PR AUC : {pr:.3f}\")\n", " print(\"\\nClassification report :\\n\", classification_report(y_true, y_pred, digits=3))\n", "\n", " if plot_cm:\n", " cm = confusion_matrix(y_true, y_pred, labels=[0, 1])\n", " disp = ConfusionMatrixDisplay(cm, display_labels=[\"Restés (0)\", \"Partis (1)\"])\n", " fig, ax = plt.subplots(figsize=(4,4))\n", " disp.plot(ax=ax, cmap=\"Blues\", values_format=\"d\", colorbar=False)\n", " ax.set_title(f\"Matrice de confusion – {set_name}\")\n", " plt.tight_layout(); plt.show()\n", "\n", " return {\n", " \"accuracy\": acc, \"precision\": prec, \"recall\": rec, \"f1\": f1,\n", " \"roc_auc\": roc, \"pr_auc\": pr\n", " }\n" ] }, { "cell_type": "code", "execution_count": null, "id": "0d621da3-0c33-40eb-ab89-dc460fcc6c35", "metadata": {}, "outputs": [], "source": [ "# On fige le seuil choisi (F2)\n", "chosen_thr = float(best_thr_f2) " ] }, { "cell_type": "code", "execution_count": null, "id": "9d520866-d029-4ead-bd16-e5a47235a6b5", "metadata": {}, "outputs": [], "source": [ "# Fit final sur tout X_train / y_train\n", "pipe_rf.fit(X_train, y_train)" ] }, { "cell_type": "code", "execution_count": null, "id": "fd2ca6f0-ff9a-4530-96c9-71f23b74fa97", "metadata": {}, "outputs": [], "source": [ "# Probabilités sur TRAIN & TEST\n", "proba_train_rf = pipe_rf.predict_proba(X_train)[:, 1]\n", "proba_test_rf = pipe_rf.predict_proba(X_test)[:, 1]\n" ] }, { "cell_type": "code", "execution_count": null, "id": "13b2ec8e-cd48-4bba-8b20-b93075ababc1", "metadata": {}, "outputs": [], "source": [ "# Évaluation avec seuil optimisé\n", "scores_rf_test_F2 = eval_with_threshold(\n", " y_test, proba_test_rf,\n", " threshold=chosen_thr,\n", " set_name=\"TEST – RF (F2)\",\n", " plot_cm=True\n", ")\n", "\n", "scores_rf_train_F2 = eval_with_threshold(\n", " y_train, proba_train_rf,\n", " threshold=chosen_thr, \n", " set_name=\"TRAIN – RF F2\",\n", " plot_cm=True\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "3cb9f58a-595e-4b0f-99b8-08b07aca8c70", "metadata": {}, "outputs": [], "source": [ "# Pour comparaison : seuil F1\n", "scores_rf_test_F1 = eval_with_threshold(\n", " y_test, proba_test_rf,\n", " threshold=0.26,\n", " set_name=\"TEST – RF seuil F1\",\n", " plot_cm=True\n", ")\n", "\n", "\n", "scores_rf_train_FA = eval_with_threshold(\n", " y_train, proba_train_rf,\n", " threshold=0.26, \n", " set_name=\"TRAIN – RF F1\",\n", " plot_cm=True\n", ")" ] }, { "cell_type": "markdown", "id": "e5ac4d91-fd82-4a09-ad73-5db8f4cc6ced", "metadata": {}, "source": [ "### Interprétation" ] }, { "cell_type": "markdown", "id": "a94e39d7-7e16-4cbf-a6fb-9a4d93475466", "metadata": {}, "source": [ "1. **Seuil optimisé F2 (≈ 0,14)**\n", "\n", "Matrice de confusion (test)\n", "\n", "TN (restés bien prédits) : 171\n", "\n", "FP (faux “partis”) : 76\n", "\n", "FN (partis manqués) : 13\n", "\n", "TP (partis bien prédits) : 34\n", "\n", "Le modèle repère ~72 % des salariés qui quittent (rappel élevé).\n", "\n", "Mais se trompe souvent en annonçant “partira” pour des gens qui restent (beaucoup de FP → précision 0,31).\n", "\n", "C’est un mode “radar sensible” : on attrape la plupart des départs, au prix de nombreux faux signaux.\n", "\n", "2. **Seuil standard 0,5**\n", "\n", " Le modele ne signale comme “partira” que très peu de personnes.\n", "\n", "Résultat : il rate 40 sur 47 départs (rappel 0,085 !), mais quand il prédit “partira”, il a plus souvent raison (précision 0,57).\n", "\n", "C’est un mode “radar peu sensible” : peu de faux positifs, mais énormément de départs non détectés\n", "\n", "**Résultat:**\n", "Seuil F2 (~0,14) → beaucoup plus intéressant: il détecte la majorité des démissions, au prix d’alertes supplémentaires, ce qui est acceptable si ces alertes déclenchent surtout des actions RH (entretiens, mesure de prévention)." ] }, { "cell_type": "markdown", "id": "4aea35bd-9367-470a-b528-3dad32e3b2bc", "metadata": {}, "source": [ "### Feature engineering" ] }, { "cell_type": "code", "execution_count": 5, "id": "b3897be3-b8cd-430c-b154-174f96634666", "metadata": {}, "outputs": [], "source": [ "# Données\n", "df = pd.read_csv(\"../data/processed/df_central_clean.csv\")" ] }, { "cell_type": "markdown", "id": "01203898-658a-46a9-a813-a47d0c84ba24", "metadata": {}, "source": [ "**Creation des features** \n", "\n", "**Satisfaction** “au travail” au sens large `satisfaction_globale` : moyenne des 3 dimensions (environnement, nature, équipe)." ] }, { "cell_type": "code", "execution_count": 6, "id": "8200cab0-b7df-4d1a-a0ab-32308c40e02e", "metadata": {}, "outputs": [], "source": [ "sats = [\"satisfaction_employee_environnement\",\n", " \"satisfaction_employee_nature_travail\",\n", " \"satisfaction_employee_equipe\",\n", " \"satisfaction_employee_equilibre_pro_perso\"\n", " ]\n", "df[\"satisfaction_globale\"] = df[sats].mean(axis=1)\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "c21a93f9-2865-4cf1-93ee-f1e9b972d645", "metadata": {}, "outputs": [], "source": [ "df[\"exp_moins_3_years\"] = (\n", " (df[\"annee_experience_totale\"] < 3) &\n", " (df[\"annees_dans_l_entreprise\"] < 3)\n", ").astype(\"uint8\")" ] }, { "cell_type": "code", "execution_count": 8, "id": "ae42126f-992d-44c3-9e85-671d1303d327", "metadata": {}, "outputs": [], "source": [ "df.drop(columns=['satisfaction_employee_environnement', 'satisfaction_employee_nature_travail',\n", " 'satisfaction_employee_equipe', 'satisfaction_employee_equilibre_pro_perso',\n", " 'annee_experience_totale', 'annees_dans_l_entreprise',\n", " ],\n", " inplace=True)" ] }, { "cell_type": "code", "execution_count": 9, "id": "2b48d4c0-98a3-4d12-83e4-8b9b63806065", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 1470 entries, 0 to 1469\n", "Data columns (total 23 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 age 1470 non-null int64 \n", " 1 genre 1470 non-null object \n", " 2 revenu_mensuel 1470 non-null int64 \n", " 3 statut_marital 1470 non-null object \n", " 4 departement 1470 non-null object \n", " 5 poste 1470 non-null object \n", " 6 nombre_experiences_precedentes 1470 non-null int64 \n", " 7 annees_dans_le_poste_actuel 1470 non-null int64 \n", " 8 note_evaluation_precedente 1470 non-null int64 \n", " 9 note_evaluation_actuelle 1470 non-null int64 \n", " 10 heure_supplementaires 1470 non-null int64 \n", " 11 augementation_salaire_precedente 1470 non-null int64 \n", " 12 attrition 1470 non-null int64 \n", " 13 nombre_participation_pee 1470 non-null int64 \n", " 14 nb_formations_suivies 1470 non-null int64 \n", " 15 distance_domicile_travail 1470 non-null int64 \n", " 16 niveau_education 1470 non-null int64 \n", " 17 domaine_etude 1470 non-null object \n", " 18 frequence_deplacement 1470 non-null object \n", " 19 annees_depuis_la_derniere_promotion 1470 non-null int64 \n", " 20 annes_sous_responsable_actuel 1470 non-null int64 \n", " 21 satisfaction_globale 1470 non-null float64\n", " 22 exp_moins_3_years 1470 non-null uint8 \n", "dtypes: float64(1), int64(15), object(6), uint8(1)\n", "memory usage: 254.2+ KB\n" ] } ], "source": [ "df.info()" ] }, { "cell_type": "code", "execution_count": 10, "id": "f2d7b379-365a-4b86-8662-ea43744b6fb5", "metadata": {}, "outputs": [], "source": [ "df.to_csv(\"../data/df_central_norm.csv\", index=False)" ] }, { "cell_type": "code", "execution_count": 52, "id": "2a0d51da-0e7e-4673-b73a-87dd1a521cf6", "metadata": {}, "outputs": [], "source": [ "target = \"attrition\"" ] }, { "cell_type": "code", "execution_count": 51, "id": "c086b4fa-61c7-4c4a-aa33-5a974f8a338e", "metadata": {}, "outputs": [], "source": [ "num_cols = [\n", " 'age','revenu_mensuel','nombre_experiences_precedentes',\n", " 'annees_dans_le_poste_actuel','note_evaluation_precedente',\n", " 'note_evaluation_actuelle','heure_supplementaires','augementation_salaire_precedente',\n", " 'nombre_participation_pee','nb_formations_suivies','distance_domicile_travail',\n", " 'niveau_education','annees_depuis_la_derniere_promotion','annes_sous_responsable_actuel',\n", " 'satisfaction_globale', 'exp_moins_3_years'\n", "]" ] }, { "cell_type": "code", "execution_count": 53, "id": "262f4123-b22e-432f-9c52-bcd250c616c1", "metadata": {}, "outputs": [], "source": [ "ord_col = [\"frequence_deplacement\"]" ] }, { "cell_type": "code", "execution_count": 54, "id": "bc35ec56-8c5d-4099-81c8-7dbfd6d18303", "metadata": {}, "outputs": [], "source": [ "cat_cols = [\"genre\", \"statut_marital\", \"departement\", \"poste\", \"domaine_etude\"]" ] }, { "cell_type": "markdown", "id": "fad8b3b3-7751-48d5-96a2-94cbbc2b37b7", "metadata": {}, "source": [ "Mettre à jour les colonnes numériques + préprocesseur" ] }, { "cell_type": "code", "execution_count": 55, "id": "f7b495a4-0dab-49f2-86c1-777eb1c64eba", "metadata": {}, "outputs": [], "source": [ "# ---- Encodeurs ----\n", "enc_ord = OrdinalEncoder(\n", " categories=[[\"Aucun\",\"Occasionnel\",\"Frequent\"]],\n", " handle_unknown=\"use_encoded_value\",\n", " unknown_value=-1\n", ")\n", "\n", "ohe = OneHotEncoder(\n", " handle_unknown=\"ignore\",\n", " sparse_output=False\n", ")" ] }, { "cell_type": "code", "execution_count": 56, "id": "42b05846-f273-4e51-9a76-137923bd4b02", "metadata": {}, "outputs": [], "source": [ "preproc = ColumnTransformer(\n", " transformers=[\n", " (\"ord\", enc_ord, ord_col),\n", " (\"ohe\", ohe, cat_cols),\n", " (\"num\", \"passthrough\", num_cols)\n", " ]\n", ")" ] }, { "cell_type": "markdown", "id": "47e6191c-74c3-4fef-8063-350303b3bb42", "metadata": {}, "source": [ "Recréer X / y + nouveau train/test split" ] }, { "cell_type": "code", "execution_count": 57, "id": "7d5c02b6-c4b7-41c4-a579-52a08ed249be", "metadata": {}, "outputs": [], "source": [ "X = df[num_cols + ord_col + cat_cols].copy()" ] }, { "cell_type": "code", "execution_count": 58, "id": "9752660c-815f-4702-b1c6-10155c6dfc55", "metadata": {}, "outputs": [], "source": [ "y = df[target].astype(int)" ] }, { "cell_type": "code", "execution_count": 59, "id": "36c16dc4-e2c7-4c4c-901a-2b9c98858939", "metadata": {}, "outputs": [], "source": [ "# Division initiale en train/test\n", "X_train, X_test, y_train, y_test = train_test_split (\n", " X, y, test_size=0.2, random_state=42, stratify=y\n", ")" ] }, { "cell_type": "code", "execution_count": 60, "id": "449f6070-0912-42bb-a0f2-cf53d11dbb79", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X_train: (1176, 22) | X_test (294, 22)\n", "y_train: (1176,) | y_test (294,)\n" ] } ], "source": [ "print(\"X_train:\", X_train.shape, \"| X_test\", X_test.shape)\n", "print(\"y_train:\", y_train.shape, \"| y_test\", y_test.shape)" ] }, { "cell_type": "markdown", "id": "8dab5152-d49f-409f-8aed-b5ea41c6c437", "metadata": {}, "source": [ "Nouveau RandomForest + validation croisée (moyenne ± std)" ] }, { "cell_type": "code", "execution_count": 61, "id": "96a79c73-15a9-496e-a0d8-37e7301e3a8d", "metadata": {}, "outputs": [], "source": [ "# RF plus régularisé\n", "rf_reg = RandomForestClassifier(\n", " n_estimators=200,\n", " class_weight=\"balanced\",\n", " max_depth=8, \n", " min_samples_leaf=5, \n", " min_samples_split=10, \n", " max_features=\"sqrt\",\n", " n_jobs=-1,\n", " random_state=42\n", ")" ] }, { "cell_type": "code", "execution_count": 62, "id": "4b1c1ae1-869c-419c-bed6-c1fe54ec0f57", "metadata": {}, "outputs": [], "source": [ "# Pipeline = preproc + modèle\n", "pipe_rf_reg = Pipeline([\n", " (\"prep\", preproc),\n", " (\"clf\", rf_reg)\n", "])" ] }, { "cell_type": "code", "execution_count": 63, "id": "f6b3cb06-ca15-4d64-b941-ae2d459c5457", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Pipeline(steps=[('prep',\n",
       "                 ColumnTransformer(transformers=[('ord',\n",
       "                                                  OrdinalEncoder(categories=[['Aucun',\n",
       "                                                                              'Occasionnel',\n",
       "                                                                              'Frequent']],\n",
       "                                                                 handle_unknown='use_encoded_value',\n",
       "                                                                 unknown_value=-1),\n",
       "                                                  ['frequence_deplacement']),\n",
       "                                                 ('ohe',\n",
       "                                                  OneHotEncoder(handle_unknown='ignore',\n",
       "                                                                sparse_output=False),\n",
       "                                                  ['genre', 'statut_marital',\n",
       "                                                   'departement', 'poste',\n",
       "                                                   'domaine_etude']),\n",
       "                                                 ('num', 'pass...\n",
       "                                                   'nombre_participation_pee',\n",
       "                                                   'nb_formations_suivies',\n",
       "                                                   'distance_domicile_travail',\n",
       "                                                   'niveau_education',\n",
       "                                                   'annees_depuis_la_derniere_promotion',\n",
       "                                                   'annes_sous_responsable_actuel',\n",
       "                                                   'satisfaction_globale',\n",
       "                                                   'exp_moins_3_years'])])),\n",
       "                ('clf',\n",
       "                 RandomForestClassifier(class_weight='balanced', max_depth=8,\n",
       "                                        min_samples_leaf=5,\n",
       "                                        min_samples_split=10, n_estimators=200,\n",
       "                                        n_jobs=-1, random_state=42))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ "Pipeline(steps=[('prep',\n", " ColumnTransformer(transformers=[('ord',\n", " OrdinalEncoder(categories=[['Aucun',\n", " 'Occasionnel',\n", " 'Frequent']],\n", " handle_unknown='use_encoded_value',\n", " unknown_value=-1),\n", " ['frequence_deplacement']),\n", " ('ohe',\n", " OneHotEncoder(handle_unknown='ignore',\n", " sparse_output=False),\n", " ['genre', 'statut_marital',\n", " 'departement', 'poste',\n", " 'domaine_etude']),\n", " ('num', 'pass...\n", " 'nombre_participation_pee',\n", " 'nb_formations_suivies',\n", " 'distance_domicile_travail',\n", " 'niveau_education',\n", " 'annees_depuis_la_derniere_promotion',\n", " 'annes_sous_responsable_actuel',\n", " 'satisfaction_globale',\n", " 'exp_moins_3_years'])])),\n", " ('clf',\n", " RandomForestClassifier(class_weight='balanced', max_depth=8,\n", " min_samples_leaf=5,\n", " min_samples_split=10, n_estimators=200,\n", " n_jobs=-1, random_state=42))])" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pipe_rf_reg.fit(X_train, y_train)" ] }, { "cell_type": "code", "execution_count": 64, "id": "9ad2e144-1e81-47b1-b588-6062583f8dba", "metadata": {}, "outputs": [], "source": [ "cv = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)" ] }, { "cell_type": "code", "execution_count": 65, "id": "eb265a32-9e6c-4305-a7cd-a50dab8bf81b", "metadata": {}, "outputs": [], "source": [ "# Scores CV sur X_train / y_train\n", "roc_cv_reg = cross_val_score(pipe_rf_reg, X_train, y_train, cv=cv,\n", " scoring=\"roc_auc\", n_jobs=-1)\n", "pr_cv_reg = cross_val_score(pipe_rf_reg, X_train, y_train, cv=cv,\n", " scoring=\"average_precision\", n_jobs=-1)\n", "acc_cv_reg = cross_val_score(pipe_rf_reg, X_train, y_train, cv=cv,\n", " scoring=\"accuracy\", n_jobs=-1)\n" ] }, { "cell_type": "code", "execution_count": 66, "id": "a8c9f46d-bf93-440f-952a-016b74a88e04", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "RF régularisé – CV sur TRAIN\n", "ROC AUC mean : 0.821 std : 0.055\n", "PR AUC mean : 0.602 std : 0.081\n", "ACC mean : 0.867 std : 0.028\n" ] } ], "source": [ "print(\"RF régularisé – CV sur TRAIN\")\n", "print(\"ROC AUC mean :\", roc_cv_reg.mean().round(3),\" std :\", roc_cv_reg.std().round(3))\n", "print(\"PR AUC mean :\", pr_cv_reg.mean().round(3), \" std :\", pr_cv_reg.std().round(3))\n", "print(\"ACC mean :\", acc_cv_reg.mean().round(3), \" std :\", acc_cv_reg.std().round(3))" ] }, { "cell_type": "markdown", "id": "7f44ef9c-4efc-4c19-8d67-30a7230b5f35", "metadata": {}, "source": [ "Courbe PR OOF + recherche du meilleur seuil F1 / F2" ] }, { "cell_type": "code", "execution_count": 67, "id": "19de1770-99f2-4569-b5bc-b132af39acb9", "metadata": {}, "outputs": [], "source": [ "# Probabilités OOF sur TRAIN (méthode predict_proba)\n", "oof_proba_reg = cross_val_predict(\n", " pipe_rf_reg,\n", " X_train,\n", " y_train,\n", " cv=cv,\n", " method=\"predict_proba\",\n", " n_jobs=-1\n", ")[:, 1]" ] }, { "cell_type": "code", "execution_count": 68, "id": "839afce3-150c-4034-bf9f-0444b2e1b919", "metadata": {}, "outputs": [], "source": [ "oof_true = y_train.values" ] }, { "cell_type": "code", "execution_count": 69, "id": "d3893dad-5308-4b4a-bf1d-05e87b4db653", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Average Precision (PR AUC) OOF RF régularisé = 0.582\n" ] } ], "source": [ "precisions_reg, recalls_reg, thresholds_reg = precision_recall_curve(oof_true, oof_proba_reg)\n", "pr_auc_oof_reg = average_precision_score(oof_true, oof_proba_reg)\n", "print(f\"Average Precision (PR AUC) OOF RF régularisé = {pr_auc_oof_reg:.3f}\")" ] }, { "cell_type": "code", "execution_count": 70, "id": "cb10698d-b79f-416c-8378-465ebb4d7f46", "metadata": {}, "outputs": [], "source": [ "beta2 = 2.0" ] }, { "cell_type": "code", "execution_count": 71, "id": "af40755f-bc42-4812-99c5-2220741cf327", "metadata": {}, "outputs": [], "source": [ "f1_scores_reg = 2 * (precisions_reg * recalls_reg) / (precisions_reg + recalls_reg + 1e-8)\n", "f2_scores_reg = (1 + beta2**2) * (precisions_reg * recalls_reg) / (beta2**2 * precisions_reg + recalls_reg + 1e-8)" ] }, { "cell_type": "code", "execution_count": 72, "id": "e543dd9f-c799-4340-baee-aed19ee4db16", "metadata": {}, "outputs": [], "source": [ "best_idx_f1_reg = np.nanargmax(f1_scores_reg[:-1])\n", "best_thr_f1_reg = thresholds_reg[best_idx_f1_reg]" ] }, { "cell_type": "code", "execution_count": 73, "id": "25d584c0-646e-4731-acf4-f6e205062f29", "metadata": {}, "outputs": [], "source": [ "best_idx_f2_reg = np.nanargmax(f2_scores_reg[:-1])\n", "best_thr_f2_reg = thresholds_reg[best_idx_f2_reg]" ] }, { "cell_type": "code", "execution_count": 74, "id": "0e2610d1-52ce-4b2f-be1a-e36279a70c7d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Seuil F1 optimal RF régularisé : 0.427 | P=0.491 R=0.589 F1=0.536\n", "Seuil F2 optimal RF régularisé : 0.330 | P=0.356 R=0.789 F2=0.635\n" ] } ], "source": [ "print(f\"Seuil F1 optimal RF régularisé : {best_thr_f1_reg:.3f} | \"\n", " f\"P={precisions_reg[best_idx_f1_reg]:.3f} R={recalls_reg[best_idx_f1_reg]:.3f} F1={f1_scores_reg[best_idx_f1_reg]:.3f}\")\n", "print(f\"Seuil F2 optimal RF régularisé : {best_thr_f2_reg:.3f} | \"\n", " f\"P={precisions_reg[best_idx_f2_reg]:.3f} R={recalls_reg[best_idx_f2_reg]:.3f} F2={f2_scores_reg[best_idx_f2_reg]:.3f}\")" ] }, { "cell_type": "code", "execution_count": 75, "id": "9e9bf14e-0612-4175-89ab-56e61cb43c7b", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAk4AAAHqCAYAAADyPMGQAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAeRdJREFUeJzt3Qd4U1UbB/C3e9AFlLZQWvbeQxCQKUsRxAWKylBEBRzgAheioqKIOBifCxwoKOJAkb1kb2TvUQotLZTu3fs9/1NuTLpIS9Mkzf/3PCHJzU16chJ6377nvec4aZqmCRERERFdl/P1dyEiIiIiBk5ERERExcCMExEREZGZGDgRERERmYmBExEREZGZGDgRERERmYmBExEREZGZGDgRERERmYmBExEREZGZGDgREV3Hv//+K2+88YZEREQ4dF/Fx8fLm2++Kf/884+1m0JkNQyciGwADspOTk4SGxtr7aZQAcHCXXfdJXFxcRIWFmbx/hk+fLjUrFmz1F8X3y98z27Eo48+Kn///be0bdvW7Ods375d3N3d5ezZs2KLli1bJj4+PhITE2PtppCdYOBEDufkyZPy+OOPS+3atcXT01P8/PykU6dO8vHHH0tqaqqUZ/PmzVMHUP2C91+/fn0ZO3asREdHG/Zbt26dyX4uLi4SFBQk9957rxw+fNiibezWrZvJz/by8pLmzZvLjBkzJCcnx2TfM2fOmOxrfLn55ptLpT0jRoyQVq1ayUcffSSODP8/kHlbsmSJ+kzM9corr8gDDzwgNWrUMNmOZVK/++476dKliwQEBIi3t7c0a9ZMZbSSk5MLfK3iPifvd8n4cuTIEbVP3759pW7duvLuu+8Wu0/IMblauwFEZemvv/6S++67Tzw8PGTo0KHStGlTycjIkI0bN8oLL7wgBw8elM8//7zcfyg40NSqVUvS0tLUe589e7YsXbpUDhw4oA5GuqefflpuuukmyczMVAfNOXPmqKAK+4WEhFisfdWrVzccyJCF++GHH2TcuHEqKzBlypR8++PAfPvtt5tsq1Klyg23A4EZsivjx48XZ2f7/jsTfxS4upbsVz7+jyAwQXYmMDDQ7Oft3btXVq1aJZs3bzbZnp2dLUOGDJGffvpJOnfurDJh+N5hCHDy5Mny888/q+cFBwff0HPyfpeMVatWzXAbf0g9//zz6nV8fX2L2TvkcDQiB3Hq1CnNx8dHa9iwoXbhwoV8jx8/flybMWNGmbYpKSlJXU+aNEnDf8eYmBiL/ry5c+eqn7Njxw6T7ePHj1fbf/jhB3V/7dq16v7PP/9sst/s2bPV9qlTp1qsjV27dtWaNGlisi01NVWrUaOG5uvrq2VlZRm2nz59WrXngw8+KNXPw5qGDRum3mtpyM7OVn1nLU8//bQWHh6u5eTkmGx/55131Of2/PPP53vOH3/8oTk7O2t9+/a94ecU9F0qSHR0tObi4qJ99dVXxXh35Kjs+08oomJ4//33JSkpSb766iupWrVqvseRrn/mmWcM97OysuStt96SOnXqqAwV6k5efvllSU9PN6t2BPujXiXvMNn69etl9OjRaugLfw0bQ3Zl0KBBaviwcuXKqj3ICuX1/fffS5s2bdSQSaVKleT++++/ocLlHj16qOvTp08XuR/+0teHO8sShhSR+UpMTJRLly6Val3ZoUOHVCajYsWKcssttxS7j2fOnKmGfbFfu3btVAYEQ0S45P3skcEypg+J4roo06ZNk44dO6rvBH4O2rVo0aJ8++G1MOw6f/58adKkifreIktU0PcUffnss8+q7yn2w/exV69esnv3bpPX3LZtmxrO8vf3Vxmerl27yqZNm8zq499++019t/CzjTNfH3zwgRoiLigT1L9/fxk2bJhq99atW0v8nOLAe8dw8O+//17s55LjYeBEDgO1GTjA4QBkjpEjR8rrr78urVu3VvUtOGDglzYOoDcCQRMO1njtCRMmmDyGoAmBEn4Ohp4++eQTGTVqlMk+GKrCMGO9evVk+vTp6uC3evVqVfdx9erVErVJD4RwYC6KfuBHkFHW9Hom1LbklZKSooJO4wuGF82BoVs8/5133pHHHnusWH2MIU4EKgiAEZgjsBw4cKCcP3++1OuLUGeFIVa0E0NuaDeGnvNas2aNGtYcPHiwel5hheZPPPGEav8999wjs2bNUkNVCMqMa9jwWnjPCQkJMmnSJPWz8f4RDKHouyiRkZFy7tw59f/HGIaGUWiPYLWwoUP0Pfz5558lfo7xEF/e7wb+gMoLwWjeIUWiAlk75UVUFuLj41Wa/8477zRr/71796r9R44cabIdwwTYvmbNGsM23MdQW14YbsGwS95hsltuucVkuMl4qG7AgAEm20ePHq2279u3T90/c+aMGlKYMmWK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h4fUrVtX5s2bVyZtJSJyRMOHD1e/n3HBGU21atWSF198Uc2nY0zfx/hyyy23lLgQeOjQofLSSy/JHXfcIbZsx44dMmrUqGI956effpKDBw/KN998Y9bwEfr71VdfzVfQnJqaKpUqVZLAwEBJT08vMKjTP4sKFSqoY+fPP/8sJYXPfMyYMeoEBx8fHxXYRkdHm/390S99+/Y12efYsWNy5513qveBtejwvVm7dq3h8X379smQIUOkTp06qk4JQeTHH39s8hqPPPKI7N69W/755x8p14FTcnKytGjRQmbOnGnW/qdPn5Z+/fpJ9+7d1V8Kzz77rIwcOVKWL19u8bYSETkqHOguXrwop06dko8++kj+97//yaRJk/LtN3fuXLWffvnjjz+KNXSlw8H1zz//LHZAUpb09lapUqXYRf+DBg2SNWvWqDPErmfjxo1y8uRJFaTk9csvv0iTJk1U1qqwxMObb76pPos9e/bITTfdJIMHD5bNmzdLSYwbN06WLFmigq/169fLhQsX5O677zb7+6NffvzxR5PHERxjOBZ9smvXLhUXYBum8ABsQz/j+3XgwAF55ZVXZOLEifLZZ58ZXgN9ieDqk08+EYvTbASa8uuvvxa5z4svvqg1adLEZNvgwYO1Pn36mP1z4uPj1c/CtSVkZ2drp0+fVtdUdtjv1sF+N09qaqp26NAhdV1acnJytLS0NHVtScOGDdPuvPNOk21333231qpVq2L/DjfWtWtXbcyYMdozzzyjVa5cWevWrZvavn//fq1v375ahQoVtKCgIO2hhx7SYmJiDM9LSEjQhgwZonl7e2shISHa9OnT1WvhdYpqi7+/vzZ37lx1G7+jsc+ePXvU/aysLO2RRx7RatasqXl6emr169fXZsyYUWA/vPXWW1rVqlXVvlCjRg3to48+UrfxWUyaNEkLCwvT3N3d1X5PPfWU4TXweT333HNatWrVVPvbtWunrV27tsh+Qh/de++9BT6GPpszZ442e/ZsrVevXvkeN24bZGZmqp87YcIErbiuXr2qubm5aT///LNh2+HDh1U/btmypVjfH2P4bPEaGzZsMPmMsW3lypWFft9Hjx6tde/e3eS11q9fr/o9JSWl2P8PixMb2NU8Tlu2bJGePXuabOvTp4/KPBUG6UvjFGZCQoJh+nZcStOJS0ky6rtdKnJ2dT1d5L7OTk7ySKeaMqR9eKm2wVHpn2dpf6bEfi8N+F7ieK5fANepmTdWs5KRkS1ZUvzCaS83lxJNIgj4ix8Zixo1ahi2Ge+Td1tRMFT1xBNPqKwKxMXFSY8ePeTRRx+V6dOnq6GoCRMmqAwN6nz0rMemTZvk999/l+DgYJX5whANshTGP7ugthT0GegzUoeGhqohNAxD4f09/vjjapZp/Gwd2oChpL/++ksNW+Z9nUWLFqmMHDIqyAQhY4JhJn0/DHOhGByPo0Tl119/VdmYf//9V+rVq1dgH2Ho6YEHHsj3XpCFwjERWSc8hn5B7RY+l8I+Ewz1od04JmLb/PnzVf8XZenSpdK5c2fZuXOnOo3/1ltvNbxegwYNJDw8XPVX+/btiyyxwdxJFStWVCNGb7/9tmE+Mww14nXwXWjVqpUqw0G9EvbH0GJB3ydsi4+PV881frxNmzbq+Lt161bp1q1bgc/DpaBjRXGOHXYVOOFLiP8oxnAfwRD+g+kTXBl79913ZfLkyfm2R0REiK+vb6m272xsqpy5nHLt3n9p58LM3XhCbrmxCUzpGvxnwC9dfQydygb73Tw4MOtr1OnrbKVkZEurKf/VcZSlPa90F2938yYARLsxbIbflzgo4aCL94AAwXh4DTBUYlyHg6GVAQMGFPi6OFChThUHUePf1wiA3njjDcO22bNnq/1QE4RA5ttvv1UHWRzMAQdZ1F3h9Yzbg7bmbZ++Td+Oz0O/jeEf3X333aeCs4ULF8rAgQMN/YA6IZSW4P2jYB3P1QMv3EY5CY5JXbp0UQEK2tuyZUv12Llz51RN7vHjx1XQBE8//bT8/fff8uWXX8pbb71VYD+dPXtWBRF538sXX3yhEgdoE/Tq1Uu9zmuvvWbYx7htuKCIHQEH2of7CNq2b98uRUFbse/58+fVcJi3t7dJW9A2DNnlbZ8OgRZqmVFvhaHe119/XW677TY11Kd/VxCIIkBFUIq+xWtimBfvLe9nCggY8dkg8DR+HJ+Jv7+/Cio7duyYry34vNEfGC7MWy+WmJgo5TJwKgmMg44fP95wH0FWWFiYuuBDKk2Vg7NkQUCgRF+KluCgYHFyLvgAfiAyQd7667C4uLqpaJ1K7y96fK5c8LTssN/NL6pFNgAHU72upSSZotLi7o52mPfrHwcYZAlmzZql6lJx8MU21MrkhSyR8agAlrYorI4H/0+RITB+HMERDqgFza6OwAMHThz8cFDUn4faF2Qs9FmljQ+ieX+2vk3fbvx5ICBCoIefgz/EcUBG0KM/jvfcrFkzVRSNx/Tt+EMNj+H+/fffr+puULyMoOb2229XQQN+7tGjR9VBG69hDIEo3kNh/YS24GcaP47XQbYIn4W+/eGHH5YXXnhBJQr034FoGwJCBKL4DuJ1EJyiEBvQz+bOZK+f2eiep534GXn73thDDz1kuI0MEi4IhJGl0rNXOEYj4MQJY0iAIABETReCurzLo6CQHIEtAjDUPOeF5xt/Pnl/X+Gzwmt6enqaPKaPRpnVF2JHEL3nreDHfQRABWWbAGk/XPLCB13aB1hfL3dpV7uynHNNlvDwyoW+fva1jKCT5H7hqHTonyn7tGyx383rI+OzisDb3VUOvdmnxP2OA05GRqYKgoqbZS3uUB3+8teHkr7++muVFcI1htSM4YBU2JBTQXAgN25HUlKSCjSmTp2ab1+89okTJ9TtgjLLxtvyXgMCrryfgX57wYIFKuj48MMPpUOHDiq7hvmjtm3bZvIaenbH+Gcavw7+EEaAtGrVKlm5cqUamps2bZoKBhF04qCNQue82Y68/WAMZ5phuR7jx1esWCGRkZEqUDOGgAoF1sg+6fC+cGYbfgaCE+PXQfCFIcmiICOG7B76HwFJfHy8WizX+BiMx8z9PuHMOLwnZIUQZKO9yGhixEBPZiCgRh8iu4ihWv37jmFOBKQ4acA4s5Z3GgtkrApqj/45FXScKM5xw64CJ3yhMd5qDF9ObCcisif4BY7gqaRwIHGVHJU5KsvhaRxgXn75ZZUlwNBcYX+0lgSyEajZwbBOQXM31a5dW2WJMAWAnq3HgRxZCAw/6ZDBwXCMDsNjWOS1MBiWQxZr9OjRhm04sJcE+gPBHy4InHDGG+ZrQv0OAhuslaYPM5oDzzt06JDJtq+++koFTcbDizBlyhT1mHHghCAFGZ6CYBi1qNokQO2XHsyg71evXm04ww9BIjJ0xTkGY8jv8uXLhkyS/rkUFMgY1x0hG4mgCdNU4H0WBJ8ZMmvoM0uyaroDf11gWgF9EjKMD+M2Pgh9mA2dpEMRG8ZIMafFkSNHVOoYxXwoiiMiorKBoRJkTcydSsZcCDSQMUAxNIIjHAgx3cyIESNU0IFM0LBhw1QWBfP84GCKrJee0dOhwBxDZjgFH0XNOHbgoF8YZMmwH34WgjBkM/Dziws1TAhcUECPY9X333+vAikUbNevX18efPBBdUxbvHixOt5hKApDZ6jxKQyCBb14HmJiYtSUAOiHpk2bmlzw2piWAH1oDvQngqqiLnpgjNoh9PX48eNV3yNzhs8FQdPNN99seE0Eiqg90o/x+KxQrI2hagRdGCbE6+J9AZ6PonG8HxTSo//xHH36IUB/4jPF0B5+PuqdcUFf5C2kR3CNrFa5DZzwRUVkqEeH6BDcxtgl4C8GPYgCFADiC4YsE1LFSKtiLFT/AIiIyPKQDRo7dqy8//77agiqtKAQGdkfBEm9e/dW9UA4axpDQ3pGAnVUONhinh8M9XTq1EnVFBnXrODYgHpHZHaQFXv++eeLnGsJw1WYjwh1W8jAICNinH0yF9qJom20qXnz5mq4CUGOXkeEGioEN5g1HHVZKDw3zp4VBMEWAkRkdwDDVxgyRBCRF7Yh0EHAZgk4IeCOO+5QGSdk+FA+gyDQGNqJLCAguMYZg8hsIXBE4IXMFQIcvYQGGbFly5apIAvBUdu2bVWgiLMmcZwHnK2IIEk/GxHZKlwwL5UxPP7YY4+JpTlhTgJxICgAQ+SMD7a0i8MBqUUEe/iPUNiY6eYTsTLky23SINhXlo/7L71Mlu13Kn3sd/Ng+AB/QeOPv7xFqTdW45RbBOvIZ5IicMNwEoKlvDVXlmCNfkcGBscuTDzqqLTr9DuCSwReyFjhGF/c/4fFiQ14hCEiIruB4TdkFjCMh/mbkJEB/Uyx8gi1TBju4zx1hcMIFbJxhQVNpcmuisOJiIhwphqGhJB90Id+MORTXmEIEAX5VLi8k2NbEgMnIiKyG6iDRWEykbVwqI6IiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzEwImIiBzeww8/LO+8885116LDumrl2YQJE+Spp56ydjNsGgMnIiIq1PDhw9USF/oF66717dtXrUFWWt544w1p2bKlWfsZt0W/YE04fdkNrKNWs2ZNtX3GjBlm/XwsLrt06VJ5+umnDdvwGuY+3xKwhBQWucUae0FBQWrZlaysLLOem56ervoTfbB3717Ddkwa2r17dwkODlZLjmBB3FdffVUyMzMN+2Bdv2+++UYtUkwFY+BERGRPcrJFTv8jsn+ROJ3dlHvfwhAoYUkLXLDCPRb5xWKv1tCkSRNDW/QLFpyFlJQUFQy89957agFac3366ady3333iY+PT6m3F+urFRcWOUbQhOdu3rxZBTLIdr3++utmPf/FF19Ui+Hm5ebmphYZXrFihQqiEBhiUeJJkyYZ9sEM7H369JHZs2cXu92OgoETEZG9OPSHyIymIt/cIU6LR4r7D3eJfNwsd7sFYSV7BCK4IJOB4ZyIiAi1Yr0O9wcNGqSWB6lUqZJaO+7MmTOGx9etWyft2rWTChUqqH06deokZ8+eVQHB5MmTVdZHzyBhW2EQtOlt0S9YegVuuukm+eCDD+T+++9XbTY3SFm0aJH079/fsK1bt26qbePGjTO0ydjKlSulcePGKtDSg0rjDN3AgQNlypQpKnhp0KCBFBcCm0OHDsn333+v+vu2226Tt956S2bOnHndQOzvv/9Wz8eyNHkhqBwxYoS0aNFCrX03YMAAtdYflqwxhr5YsGBBsdvtKBg4ERHZAwRHPw0VSbhguj3hYu52CwdPuqSkJHVAr1u3rhq2Awz1IEvh6+urDsKbNm0yBBU40GOICcFE165d1RDfli1bZNSoUSogGTx4sDz33HMmmSRsKytoT3x8vLRt29awbfHixVK9enV58803DW3SIav10UcfqQVlN2zYoIbUMLxlDFk5ZHQQYP35559q2xNPPKH6pKiLDv3TrFkzNaSmQ/8mJCSo4cjCREdHy2OPPSbfffedGuK7nhMnTsiyZcvU52IMAe758+dNAl/6D9eqIyKydRiOW/aSiGj5HnJS25xElk0QadhPxNml1H88Dv76gT05OVmqVq2qtjk75/7tvXDhQsnJyZEvv/zSkJ2ZO3euyiwh04SgBMEJhvfq1KmjHm/UqJHh9fHaeibpevbv328SZCDzs3379hK/N2SWXFxcVB2RDhkzbEMgmLdNCBIxtIf2472OHTtWBVjGkFVDX+iZMMA+eQOswkRFRZkETaDfx2MF0TRNZbsQoKG/iwp6OnbsKLt371a1UAhg87ZfH+ZD36DWi0wxcCIisnVnN+fPNJnQRBIic/er1bnUfzwKivWal7i4OJk1a5YaPkLAgiEfDLMhe4FAw1haWpqcPHlSevfurQ7qyJr06tVLrWSPYT0EYMWFoa8//vgvu2bukFxhUlNT1WvkHY4rDDI5evAHeA+XLl0y2QfZIuOgCRCYGQdnpQ3BXGJiokycOPG6+yLQxb743FB0jmE91EXpvLy8DNk1yo+BExGRrUuKLt39igkZFAzN6ZBN8ff3V4XFb7/9thq+a9OmjcyfPz/fc6tUqWLIQOGsNQwN4cCNs7kwlHXzzTcXqy0ISIzbcqNQDI0AAUOKeYOdgqDA2hgCLmR78vZXXsgEYYizKOhHQJYrbxYNw3D6YwVZs2aNGuLLG0gi+4Q6JhSY68LCwgzZOtR4IeuE4VJk2eDKlSsmnx2ZYuBERGTrfIJLd78bhGABw3TI1kDr1q1VMISMip+fX6HPa9WqlbogK9KhQwf54YcfVOCEgAUHcGvQp0FAMbbxlAil3abiDNWhb1BcjkyWnqVCkIm+RbBTkE8++UQFsboLFy6oDB8+l/bt2xf6szDEiuFHXOuB04EDB1SAiLozyo+BE1lVVnaO/BsZLzUrV5BKFa7/1x6RQ6rRUcSvWm4heAF1TqrGCY9jPwtALYxeW4Ohus8++0xlR/Qz0ZDRwNlsOJMOAQIKq1EfgyJrDAHhwPz555+rs7hQP4PC6ePHj6tT4wF1NKdPn1ZzDuG5GPIryRAcskYIgPTbkZGR6jVRE1VYlgpZFQR+GzduNAmc0CYUf+tn6CEzdSOKM1SHoU0ESJiU8/3331d9jwzdmDFjDP2CjBT6D4XooaGhEh4ebvIaeh0YhhXRp4CMIAIiDCXidXbu3KmCWBTjG2fSUODfuXNnw5AdmeJZdVTmcnI02XHmirz++wG5+d3VcveszTJm/m5+EkSFQcF336nX7pjW4mj6/b7vWaQwHDC8hloeXJC92LFjh/z888/qtH297gdBBg7ed999tyqcfvTRR1WNE7IkePzIkSNqcsr69euroSEEAY8//rh6PrbjDDzUUiGQ+fHHH0vUTmRZ9KwWzoRD7Q5ujxw5ssjn4fG8w4wIAFFgjcCjrIeskPlB8T2ukX166KGHVJBkXMSN4UUEoMaTV14PCvCnTp2qzppr3ry5mgYCxe0YejWGqQhwdh4VzEnLOzhbzuF0TozN4wyPolLKJYV0J05PxS8Q/YyTvDafiJUhX26TBsG+snxc7sRtjuDEpST5eWeELNl3QS7Ep5k8VjfIR1aNNz0ltrT7nUof+908CCCQUalVq5aasbnEMOUAzq4zKhTX/ELFCUFT4wElf10HhyFHFJ1jWAuBSlFwyNTrocwtKLcnmAcK9U6YpgGBlq3QSqHfi/p/WJzYwHZ6hcql1IxsWbr/oizYcU52nIkzbPf1cJXeTUIkvJK3fLTqmFXbSGQ3EBxhyoGzm0VLjJJMz8riVqeLiAt/ld8IDElhXqbY2FhxdJhuAoX8thQ02Rr2DFnE6dhkmbvptPy6J1IS03LXV3JxdpLuDYLk3jbVpVuDKuLp5iJbT13mJ0BUHBiOw5QDmiYaZpG20PCco9GHHR3dvffea+0m2DwGTlSqqdSdZ+Pkiw2nZOXhaPxeV8IqecngtmFyX9swCfa7gWEKIiIiK2PgRKVS7L38YJT8b8Mp2Rtx1bC9R8MgGdGppnSqEyjOzuWvFoCIiBwPAye6oQzTuqMx8sHyo3LoYoLa5u7qLPe0DpVHb6kldYNMZxEmIiKydwycqERQm4SAadfZOEOxN7JLQzvWlECfG1sCgYiIyFYxcKJiF32/8cdBWX8sRt33cHWW4Z1qyhNd6khFTmBJRETlHAMnMktaZrbMWndS5qw7KRnZOeLq7CQPtAuXsT3qsuCbiIgcBgMnuq4Nx2LULN9nLueulN2lfhV5c0ATqRmYfyFLIiKi8oxTLFOh4pIz5Kkf98jQr7eroCnYz0NmDmkt34y4iUETEZUrXbp0UYsOF+WNN95QS7iUZ1ib78MPP7R2M2waAycq0Lqjl6TPjA1qeRTMJPBIp1pqSZR+zauWy2UGiKhgw4cPV//n9UvlypXVunJYkqO0ICAxXmC3qP2M26JfVq1apR7/4osv1OK0FStWVJeePXuqxXCv548//pDo6GgVNOjwur/99ptYC/oX7wVLg4SFhanFfoty+fJl9blgEWUs4IvnYB06LCWiw0LGnTp1Up8hZktv2LChfPTRRyavg8WEp0yZopYeoYIxcCITmdk58tafh2T43B1yKTFd6lSpIL+N6SSv928svp7/rZ5NRNaRnZMtO6J2yNLTS2Vn9E5139JwQMaiubisXr1aLcdxxx13iDU0adLE0Bb9gmwRrFu3Th544AFZu3atbNmyRQUPvXv3lsjIyCJf85NPPpERI0ZYZJ1LrK9WXAh20O4aNWrIrl275IMPPlBB4+eff17oc9D2O++8UwWBx44dk3nz5qmA8oknnjDsU6FCBRVMYUHmw4cPqyAJF+PXbdq0qVrY+Pvvvy/Bu3UMDJzI4GJ8qtz/+Vb5auNpdX94x5ry19OdpXn1APYSkQ1YdXaV9Pmljzyy/BGZ8M8EeWLtE9J3cV+13ZKQwQgJCVEXZIYmTJggEREREhOTe3Yt4P6gQYMkICBAKlWqpA7iZ86cMTyOoKZdu3bq4I19kPk4e/asOsBPnjxZ9u3bZ8ggYVthELTpbdEvWPgV5s+fL6NHj1ZtRDblyy+/VAtRI9grDN7DmjVrpH///oZtNWvWVNd33XWXao9+X4efg4VisSgsslSJiYkmS7cgOHn22WclMDBQ+vTpU+z+xusj4Pr6669VoIif8fTTT8v06dMLfQ4ybE8++aS0bdtWBVy33nqr6ot//vnHsA+GGRFY4jXxnh566CHVPuN9AH2xYMGCYrfbUTBwImXX2Styxycb1bxMvp6u8r+H28gbA5qo9eSIyPoQHI1fN16iU6JNtl9KuaS2Wzp40iUlJalsRN26ddWQD2RmZqoDsK+vrzoIb9q0SXx8fFSmCgFAVlaWDBw4ULp27aqGoJANGjVqlApKBg8eLM8995xJJgnbSkNKSopqGwK5wmD4ytvbWxo1amTYtmPHDnWNxW7RHv0+nDx5UpYsWaIuf/75p6xfv17ee+89k9f85ptvVDCHfpgzZ47adtttt6k+KeyC969D/yCLpgeEgP49evSoxMX9t1h6US5cuCCLFy9WfV6YPXv2yObNm/PtgwAXQ5zp6elm/SxHw7PqSH7dc15eWrRfTTPQqKqfzHmotdSoXMHml3nhMi7kKDAc997290STawtAGsE2J3GSqdunSvew7uJigUV/ESDg4A7JyclStWpVtU0f2lq4cKHK7CDDo9dAIuhAZgmZJmRBUDOD4T0MA4FxoILX1jNJ17N//35DW6Bx48aF1jG99NJLquYHtU6FQdYrODjYZJiuSpUq6hrtz9smvE/UUiFoxHt9+OGHVUYLdUG6evXq5atJQt+kpqYW2g43t/9KIaKiolRGyxjaqD+G7FJhkFH6/fff1c9C5gg/N6/q1aurTBsCWgwBjhw50uRx9BkCXvwsZK/IFAMnB18y5bM1J+TDlcfU/d6Ng2XG/S3F2902vxYZWTny295ItYjw5eQMWfZsZwny5aLBVP7tvrQ7X6Ypb/AUlRKl9rsp5KZS//ndu3eX2bNnq9vIeMyaNUtlUBCw4MCKYbYTJ06ojJOxtLQ0laFBvQ6KzJE16dWrlwpkMKyHAKy4GjRooOp4jIcRC4IsEIabELihwLowCDCKejwvDHEZv0+8h0uXLpns06ZNm3zPCw0NlbKAYu9JkyapOqeJEyfK+PHj1edlDFlBZA63bt2qhl2RPUTApUPhuJ6xo/xs8whJZZKxefuvw/L1ptx6pie61pEX+zSwySxOUnqW/LjtnKq9ikpIM2w/FZPMwIkcQkxKTKnuV1yoS8LBVYcsBup7kHl5++231UEYwQJqc/LSszfIQKFOZ9myZSpDhaLklStXys0331ystmD4yrgtBZk2bZoKnFAc3bx58yL3RR2SucNfeTNDgKwTslB5+ysvBJp5a4mMIQA9ePCguo0sF87yM6bfv15WTq/7Qo0XhihxZt5rr71mEqTq2axmzZqp10XWyThwunLlislnR6YYODmg7BxNJvzyr/y867y6//odjeWRW0zTwrYgIS1TvvrntMzddFoS0rLUtiBfD0nJyFbBFJGjqOJdpVT3u1EIFjC0pQ89tW7dWgVDQUFB4ufnV+jzUJyMCzIhHTp0UPMmIXBCMJSdXTpnB2KIDMNmy5cvV0OE14P2YEgKwZPxEBgCpNJqU3GH6tA3r7zyiqrP0rcjyES2rahhurz0gK6oWiXsk/fxAwcOqOE8BJWUHwMnB8w0vfLrfhU0uTg7yfv3NJd72lQXW5KSkSXzNp+R/60/JfGpmWpb7cAKMqpLbbmrdaj0+2SjnLiUZO1mEpWZ1kGtJdg7WBWCF1TnhBonPI79LAEHVgQXgADjs88+U1km/Uy0Bx98UJ0yjzPp3nzzTXXQRe0QipNffPFFFQDglPcBAwao+hkUOR8/flyGDh1qGP46ffq07N27Vz0XQ2GFDcEVZerUqfL666+rgAyvqbdZL8AuLHBCgIBCbuMpFvB81C7h7D+0pTgBS0GKM1Q3ZMgQdabho48+quq0EMh8/PHHJnMu/frrryoAPXLkiLq/dOlSlT266aab1HtF9uqFF15Q7dfPCpw5c6aEh4erbBRgWgJk55AJNIbMGIZXqWAMnOxg9m5/L7dSGUJDTdOkPw7Kgh0RalLLGYNbSv8W1cSW1sObv+2czF53QmKTcuc+qRvkI+N61pfbmobY5DAiUVlAwfeEdhPU2XMIkoyDJ9yHl9q9ZJHCcMDwmj7Ug6AGB96ff/5ZnXoPOCsNB2Ec5O+++251ej4CBZwSjwwUMi04wONsM0zUiNcaM2aMPP744+r599xzjwqyUEt19epVNayHmqjiQh0Wiprvvfdek+2o+cFwVEFcXFzUHE4YZjQOnDB7NuqDMByJ92I8tYKlYRh0xYoVqo8wBIrADgEhzkTUodgeAahxXRLaOm7cOBXoYg4rfBaoYTLOLiHYQpCKYnwU6iPY1D8HvS4NE3/iM6eCOWk4mjoQTCyGLyW+dEWllEsKX8xz586pqL6wydQ2n4iVIV9ukwbBvrJ8XO7EbQXZeeaKDPlimzzQLkwm39n0htv24Yqj8umaE4KTXj68r4Xc3dr6maatpy6ruaP8PF3VBJuRV3NT2TUqe8uzPevJgBahKjNmrOf09SrjtGDUzXJz7cpm9zuVPva7eXAwwsEKtSXFKUTOC1MO4Ow640LxEO8QFTT1rFH4mWNUNGSmMB3A7t27r3sWGQ6ZCM4wvFgeV1FA8IlsFgI3W6KVQr8X9f+wOLEBM0427PMNp9QUAYej/ptcraR+2hGhgiZ4e2BTmwiajKGGCZeq/p7yzK311PChm4v1AqBj0Ynq59fiQsZkQxAcYcoBnD2HYbsA1wBpH9peXF34q/xGoJj6q6++Un98Ofrp96ip+vTTT63dDJvG/202PIv36iOmp7iW1MbjsfLyr/vV7bHd68qD7W3nF0OwX27UX8HdRUZ3ryuP3lLLapNupmZky5J/L6jhwn0RV1Wbdr/eSzxcOQko2Q4Mx2HKAf0vcEsNzzkaTNBJkm9OJ8qPgZONWrgjQp39dqPOXk6WJ+fvkqwcTe5sWU2e611fbAkyOquf6yqVvN2lYoX/Zsk1B2Y5RyYN2akOtQufGfh6TsYkyfdbz8ovu84bzt6D5IxsScvIYeBEREQGDJxsUFZ2jizYHlEqxdZPfr9bEtOypHV4gLx/b3ObHJOvU6Xgs12u54PluYWRmAyzuIET/lrfeCJWzQ217uh/c9+EVfKSwW3DZNqK3ElBiYiIjDFwskEYojOe6LGkXv/9gBy6mCCVK7jLrAfblJvMiXue2qfiZOYQTP62J1JN/HksOndKA8SStzYMkoduriFd6lWRHE1j4ERERAVi4GSDfth2znBm2dnLJZvyfvHu8/LTzvNq2oFPHmglIf7lZ2mSl25rKFtOXlY1SPpyMdeTmJYp32/F7OOnDFMdeLu7yKC2YTK8Y02paVQEnpPtUCeaUhnKO8M0Ednf/z8GTjYmPiVTNp2IVbfva1O9RJmPC1dT1XxN8GzP+tKpbvma/bVr/SrqgsyROfNgzd18RuYZzT5ezd9ThneqKYNvCldzZF1PZnaOVc/wI/uHU6gxTQZWrMcyFqVxKjuGmzGxJA4GtjgEX16x3+2v3/UTKbCwMf4f4v/fjWDgZGNWH4lWhdyY46lWYPFrf/AFeemXf1VdU8uwABndLXclckdzNSVD5qw/Jd9uOaOWaIHaVSrI6G51VZG8uYHQiHnbZU/EVfnk/lY2NVko2Rf8ssbcMRcvXlTBU2nA/3UsCYIJHBk4lR32u/32OyZqLY25/hg42ZjlB3OXCOjTJLhEz8ep9P8cjxUPV2f5cFALcXWwTAnWsJu3+ax8seGUJF5bz65xVT8Z072u9G0akm8yzevZfe6quv73/FUGTnRD8FcufmlnZWWVyhpo+MsbgRhm4eakr2WH/W6f/Y6AC7Oll8YfGQycbAjmEVp/LPcMr95NQopd33QpIU3e+zt33aKX+jYs8dlq9ig9M1t+3hcrP+47JldScte3a1TVT57rVV9ubRRUrP8sCK6aV/eXc1dSJNDHg+viUanB9xATDBov6HojBxIcDDADMgOnssN+tw5b6ncGTjYEQVNaZo6EBnhJk2p+xQ6c3ll6WGVcMESHgmdHcSomSXpM3yAX49MMc0ON71Vf+jXDXyZOJTq4/Tq6kyqsf2/ZEQZORERkwMDJhqwwDNOFFDuduO3UZflt7wV1av2bdzZxqAVxL1wLmKpUcJXn+jSUe9uE3fAQZXGH9IiIyDEwcLIROHNr1eHchTtRi1PcCTP1s+geaBcuzasHiCPAMBpgWoInu9WRXuEuUq92mMXSuGdik8Xd1VmqBXhZ5PWJiMj2MXCyETtOX1Gny2OyyjY1KhbruQt2RMiRqEQJ8HaTF3o3EEfRqW5lWfREBzU0V9HbTS3QaSmYE+uLf05LpQrusuOVnsxIERE5KAZONmLr6SvqunO9wGIdlDET9qdrjqvbz9xar9jrvdkzDGe2rVmpTCYWjE/NLTi/kpyhsoNcWJWIyDE51rnqNp5xgptqVSr29APRCelqUsch7cMt1DrH1a1+kNSs7K0mIyUiImLGyQZkZOXInog4dbvdtQyKOVIysmT2uhPq9lO31is3a9HZkg51Ksu6F7qrJVt+3nXe2s0hIiIrY8bJBhy4EK+mIUCdTt0g8+de+mbzWbXuWnglb7mXGREiIiKLY+BkQ8N0qNcxdxoC1DZ98c8pdfvZnvW4lhoREVEZYOBkA3acuVLsYbpf90SqQuXqFb1kANdQIyIiKhMMnKwsJ0eTHWfiilUYjsUOv9p4Wt3GDOGOth4dERGRtfCIa2UnYpLUqe5ebi5qmRVzl2Y5cSlJfDxcZfBNYRZvIxEREeVi4GRl26/VN7WuEWB2nZKebRrUNkx8PW98sVAiIiIyDwMnG6lvusnM+qZj0Ynyz/FYtQDtiE6Os5AvERGRLWDgZGX7z8er69bh5i2zsmB7hLru1ThYwip5W7RtVLAtpy7LO0sPS3RC7uLCRETkODgBphWlZ2XLuSsp6nbDqr5mTZT5295IdZu1TdYzYu4Ode3r4aomHiUiIsfBjJMVRcSlSo4manHeKj4e191/3dFLagqCQB8P6VKvSpm0kXIVtH5gRrZl18cjIiLbw4yTFWUjahKR+sG+Zk18+cvu3CU/7m4dyikIypi3u6taRDkhLVOi4tPk7wNRZd0EIiKyAcw42YD6wddfZuVyUrqsPnxJ3b6nNRectYZxverLpP5NJNjPs8SvEXElRZYduChZzFYREdklZpxsADJO1/PHvguSlaNJs1B/aRBy/f3J8g5eSJCHv9omNSp7y9sDmxW575nYZPls7Qk14zsyjZ8NaSV3NK/Gj4mIyM4wcLKTwGnx7tyicC7mazvWHMnNAG46IYUGTidjkmTmmhOqqP/ayKyCSU+JiMj+MHCyg8DpwtVUOR+XquZu6te8apm1iwrm7e6irlGWpmkiRvGQwYlLifLpmhOyZN8FQ8DUo2GQmsIAmSoiIrJPDJysDGfIVargXuQ+CJqgbY1Kan+yrkduqaU+s1bhFeWe2ZtNHjsdmyzTVx6TP/+9oIIq6NkoWJ6+ta40rx4gj3+3UwVOeGzX2TgJDfCSEP+S10wREVHZYuBkB4Xhut5Ngi3aFjIPgteRnWtLTGK6YRsySR+vPi4Ld0QYzpbs0yRYnupRT5qG+ud7jal/H5HE9CxpGuonfz7VmV1PRGQnrH5W3cyZM6VmzZri6ekp7du3l+3btxe5/4wZM6RBgwbi5eUlYWFhMm7cOElLSyvX9U06zBZOtgfZo64frJUftp1TQROG5P56+hb538NtCwyaAEETXE7KKOPWEhGR3WacFi5cKOPHj5c5c+aooAlBUZ8+feTo0aMSFBSUb/8ffvhBJkyYIF9//bV07NhRjh07JsOHD1dzIE2fPl3Kc+DUINhXalSuYPH2UMmkZeZImxoV5aW+DaVdrcLXHcSw3YHIBGldo6KqfyIiIvti1cAJwc5jjz0mI0aMUPcRQP31118qMEKAlNfmzZulU6dOMmTIEHUfmaoHHnhAtm3bJvaqQYh5Q3UcprM9lVWdU4BaCufZnvWlZ6Og605kel/bMHXBGoUMnIiI7I/VhuoyMjJk165d0rNnz/8a4+ys7m/ZsqXA5yDLhOfow3mnTp2SpUuXyu233y72qm6QeRmn3o1DLN4WKh5nZyf5dXQn+evpzmoY1ZzZ34mIyL5ZLeMUGxsr2dnZEhxsWreD+0eOHCnwOcg04Xm33HKLaJomWVlZ8sQTT8jLL79c6M9JT09XF11CQu6p4Dk5OepS2vTXLfq1c4uHQ/w8xNfDpYh9c/er6u8pjav6WKS95YV5/W47crT/2mkvbS4P/V6esO/Z744kx8K/a4rzunZ1Vt26devknXfekVmzZqmaqBMnTsgzzzwjb731lrz22msFPufdd9+VyZMn59seEREhvr6lPwM3Arq4uDiVfSgsA1FJy5FbavrKzTV85dy5c4W+Vqhbltwc7iM96/mr9tKN9bstiYrJnWICwX9R3wFbZ2/9Xp6w79nvjkSz8O+axMRE2w+cAgMDxcXFRaKjo022435ISMHDUgiOHn74YRk5cqS636xZM0lOTpZRo0bJK6+8oob68po4caIqQDfOOOFsPFz8/PwsErXiA8brF9Qe3bd1al73tcJREN+gdim3sHwyt99tRbxLPAabxdXVVcLDwyUnR5NlB6Pk2y1n1Vl5o7rYx+dub/1enrDv2e+OJMfCv2v00SibDpzc3d2lTZs2snr1ahk4cKChY3B/7NixBT4nJSUlX4ch+AJ0aEE8PDzUJS+8jqV+0euvzQNJ2bKnfnd2ym0jvrarDl+Sj1Ydl8MXc//jxiZnyBPd6oq9sKd+L2/Y9+x3R+Jswd81xXlNqw7VIRM0bNgwadu2rbRr105NR4AMkn6W3dChQyU0NFQNt0H//v3VmXitWrUyDNUhC4XtegBFZE+iEtJk1He71G0sqaPmziz4bwAiIrIBVg2cBg8eLDExMfL6669LVFSUtGzZUpYtW2YoGEfth3EU+Oqrr6qxTVxHRkZKlSpVVNA0ZcoUK74LouIz/uMGa98N71hTLeHy2Lc72Z1ERDbM6sXhGJYrbGgOxeDGUA8yadIkdSGyZ5jQ9IF2YRLg7S6P3lJLLeOy48wVazeLiIhsPXAickSuLs7y7t3Nrd0MIiIqJlZzEhEREZmJgRMRERGRmRg4EdkZzPm0+1ycJKRlWrspREQOhzVORHYCc5WtOXJJPlxxTA5dTJDejYPl86Ftrd0sIiKHwsCJyA4Cpk0nLsu0FUdlb8RVw/aYpP/WYCQiorLBwInIhmGKgmnLj8q207lTFXi6OUvbGpVk44lYazeNiMghMXAiskH7z8fLByuOyoZjMeq+u4uzPHhzuDzZrY7si4hn4EREZCUMnIhszNkrKdL/s43qtquzkwy6KUzGdq8r1QK8ru2BBYKJiMgaGDgR2ZjsHE2cnEQGtgyVcT3rS3hlb2s3iYiIrmHgRGQjGoT4SqOqflLV31Oe611fmlTzv+5zohPSJCYxXZqGXn9fIiK6cQyciGyEn6eb/P1MZ7P3PxaVKJ2nrpWM7BxZMa6L1A/2tWj7iIiIE2AS2a3kjGwVNEFsYu7UBAci4+Xx73bKPbM3S3J6lsn+Wdk58suu8/LwV9sMRedERFQ8zDgR2ZmGIb5S0dtNalfxkbOXkyU2KUOORSfKd1vPyt8Hogz7HYlKlDY1KqqA6dc9kTJz7Qk5czlFPebn5SZd6lex4rsgIrJPDJyI7ExYJW/Z/VovcXJykj4fbVCB0xtLDqnHUFTu7OSkCswRMP20I0I+W3tCzl3JDZiMJ9U8GpUoaZnZ0iIswErvhIjI/jBwIrJDCJrAxTn3Gm5rGiLP9qwvo77bKWcvp8jIb3dKYlrucF3lCu4yqktt0UTkvb+PyNojMbJ0f5QKtHa80lMCfTys9l6IiOwJAyciO/b0rXVVEPRwhxr5zqxD0ISA6ImutWVI+3DxdneV77acUY+lZmara00TiU/NVIGVHowREVHhGDgR2bG+Tauqi7GejYJl1eFoGdqhpgxpFy5e7i6GxzrUqaxqpFpUD5A/9l1QAdTkJYdk++nL8kb/JnJ/u3ArvAsiIvvBwImonHntjsbqUpC6Qb6y7Nku6vbfBy5KaqYYzrDbfvoKAycioutwvt4ORFQ+Va+YOyN5iJ+nuo68mirP/7xPXly0TxWPExFRfsw4ETmoHx+7WZIysmTpvxdlytLDsu30FXWB8b0aSIh/bkBFRET/YeBE5KD8vd3Uxccz99cAasP1RFMOM05ERAVi4ETk4LCYMGY1aB1eUfp9ulEysnJnIyciovwYOBE5OJx1N/gmnk1HRGQOFocTUT5xKRmyZN8FSUzLZO8QERlhxomI8rlr5ma1gPATXevIhNsasoeIiK5hxomIDPS5wxE0QXxqhvxzPEa2nLzMXiIiYsaJiIzdf1OYHI1OFC83F1l7NEZ+3nleftweIW4uTrJvUm+1bAsRkSNjxomIDCbf2VQWjOqgzrCDrJzc+QkyszVJy+TZdkREDJyIKJ+ejYOlTY2K8lSPuuwdIiIjzLsTUT6NqvrJL092VEuvfLrmBHuIiOgaZpyIyCwnY5Lkiw2nJDYpnT1GRA6LGSciMst9c7ao6/jUTHm+TwP2GhE5JGaciKhIWMPOWEpGNnuMiBwWM05EVCgnJycZ272uRF5NldSMbPn7QFS+ffQ5n4iIHAEDJyIq0nO9c4fl3l92xGT7tlOX5ePVx9XkmDOHeMrtzauxJ4mo3GPgRETFcjQ6QQb/b4tsO33FsG1/ZDwDJyJyCAyciKhYNp3IXX4Fs4kH+njIxfg09iAROQwWhxORWfy83NS1u4uzDO1QQ9a/0F36Ng1h7xGRQ2HGiYjMgmAp2M9DOtQOlBB/T5PHLsSnyeQlByU0wEtGdq7NHiWicouBExGZBQv83tWqeoGP/b73grp2cXaSRzrVEmfnPHMYEBGVExyqI6IS83A1/RWSfW1RYCKi8oqBExGV2MM3h8tDrQPlq2Ft8j128EK8PP/zPpmz/iR7mIjKDQ7VEVGJVfX3kpHtgsWncoBh277zV2Xm2hOy6vAldd/LzUXua1NdDl5IkE51A9VwHhGRvWLgRESl6q5Zm03up2ZmS6epayQtM0em3ddC7m1TcJ0UEZE94FAdEd0w4ywSbt/TurrMH9nesA1BEyzYfk46vbdGnvpxD3udiOwSM05EdMP8vdzkyW51JCU9Sx69pbaEV/aWnBxNutSvoq4T0jLl3/PxsvNsnNo/MS2TvU5EdomBExGVipf6NjS5jykJvn2knbo9bflRFTgF+XrIpcR09jgR2S0GTkRkcc/2rCe3NQtR0xf0nL6BPU5Edos1TkRkca4uztKkmr84O/GMOiKybwyciIiIiMzEwImIiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzEwImIbEpmdu66dkREtoiBExGVucxsTV5ctE96f7RejkcnqvXslh+Mkv6fbpSmk5bL/vPx/FSIyCZxyRUiKnOpmdny087z6vb0lcfkVEyyHI1ONDx+OCpBmlX35ydDRDaHgRMRlRk/LzfBqiuaJuLp5ixpmTny94Eo9Zivh6t4uDlLbFIGPxEislkcqiOiMhPo4yG/PNlRlj7dWe5qFaq2BXi7yfhe9WXjhB7SvHoAPw0ismnMOBFRmWodXlFdT+jbSDrXqyJd6lcRHw/+KiIi+1Ci31bZ2dkyb948Wb16tVy6dElyckzPglmzZk1ptY+Iyil/bze5vVlVazeDiMjygdMzzzyjAqd+/fpJ06ZNxQlFC0RERETlXIkCpwULFshPP/0kt99+e+m3iIiIiKg8FYe7u7tL3bp1S781REREROUtcHruuefk448/Fg3nFBMRERE5iBIN1W3cuFHWrl0rf//9tzRp0kTc3NxMHl+8eHFptY+IiIjIvgOngIAAueuuu0q/NUREIhIZlyrTlh+VNjUrSvcGQewTIrLvwGnu3Lml3xIioms+Xn1cXVev6CW9G4fI8UuJMn1QS6lcwV3NPM4zeYnIWm5o1rmYmBg5evSout2gQQOpUqVKabWLiByQq7Pp1Cbn41Ll602n1e3R83fJgcgEuat1qDzbs55Ex6dzPTsiso/AKTk5WZ566in59ttvDZNfuri4yNChQ+XTTz8Vb2/v0m4nETmAx7vWkSq+HtIiLEBeXPSv2ubi7CTZOZrsOBOn7v+w7Zws2nleMrJz5NfRHaXVtZnIiYhsNnAaP368rF+/XpYsWSKdOnUyFIw//fTT6oy72bNnl3Y7icgBtKlRUV1wxm6Al5tU9nGXRbvOy4/bI0z2Q9AEMYnp6vrQhQT5fMNJuRCfJl883FbNSk5EZDOB0y+//CKLFi2Sbt26GbZhMkwvLy8ZNGgQAyciuiGoYerdJETdxsK/o7vVVevZPfDFVgnx95Tj0UkSeTVV9kZclR+3n5O1R2MMz90fGS+31AvkJ0BEthM4paSkSHBwcL7tQUFB6jEiotLi5uIsYZVyh/+XPdtFXd81a5MKnGatO6nuozTK1dlZZaI04fxyRGRjE2B26NBBJk2aJGlpaYZtqampMnnyZPUYEZElebu7qGt3F2cZ0j5c1jzXTeoE+bDTicg2AyfMGr5p0yapXr263HrrreoSFhYmmzdvVo8Vx8yZM6VmzZri6ekp7du3l+3btxe5/9WrV2XMmDFStWpV8fDwkPr168vSpUtL8jaIyE692q+xvNqvkWx8qbu8c1czqRlYwfDYqZhk+XDFUdkXcdWqbSSi8qlEQ3VNmzaV48ePy/z58+XIkSNq2wMPPCAPPvigqnMy18KFC1Wh+Zw5c1TQNGPGDOnTp4+a4gDDfnllZGRIr1691GOosQoNDZWzZ8+qCTmJyHE0quqnLgWZ9MdBdY36p+8ebV/GLSOi8q7E8zhhyoHHHnvshn749OnT1WuMGDFC3UcA9ddff8nXX38tEyZMyLc/tl+5ckVltvRlXpCtIiJydzVNoKdn5Z55R0RklcDpjz/+kNtuu00FLLhdlAEDBlz39ZA92rVrl0ycONGwzdnZWXr27ClbtmwptA2oocJQ3e+//64m3BwyZIi89NJLah6pgqSnp6uLLiEhQV1j/il9DqrSpL+uJV6b2O+2xpa+7+N71pONJ2LV2XcfrTouomk20S5H6HtHwn4vn/1enNc1O3AaOHCgREVFqWEy3C7qNOLs7Ozrvl5sbKzaL+/ZebivD//lderUKVmzZo0aEkRd04kTJ2T06NGSmZmpitUL8u6776qi9bwiIiLE19dXShvmn4mLi1P9wGUhyg773Tpsqd9reIjUaOIt607Gq/v4g+ncuXNSXtlS3zsS9nv57PfExMTSD5yMozFr/YWDn4vA7fPPP1cZpjZt2khkZKR88MEHhQZOyGihjso444RCdlz8/Pws0kZ8wHh9ZNCobLDfrcMW+z0w/iIWa1Enj4SHh0t5ZYt97wjY7+Wz3/XRKIuvVZf3bLfiFGkHBgaq4Cc6OtpkO+6HhOROfJcXzqTDUKHxsFyjRo1UJgxDf+7u7vmeg1+euOSFjrfULxv9tfnLrGyx363D1vrdyelaO5ycbKZNjtL3joL9Xv76vTivWaKfPnXqVHVGnO6+++6TSpUqqbPc9u3bZ9ZrIMhBxmj16tUmESXuFzYXFJZ3wfCcccbr2LFjKqAqKGgiIiIiKk0lCpxw9hvSZbBy5UpZtWqVLFu2TBWPv/DCC2a/DobQvvjiC/nmm2/k8OHD8uSTT6oFhPWz7LBosHHxOB7HWXXPPPOMCphwBt4777yjisWJiIiILK1EQ3UYGtMDpz///FOtT9e7d281NQDmYzLX4MGDJSYmRl5//XX1mi1btlQBmF4wjsJO4/QZfuby5ctl3Lhx0rx5c5XhQhCFs+qIiIzl5Giy7MBFiU3KkCHtwsUZ67IQEVkjcKpYsaI6Kw2BDAKdt99+W21H4ZY5Z9QZGzt2rLoUZN26dfm2YRhv69atJWk2ETmQnWfj1AVahgVI01B/azeJiBw1cLr77rvV/En16tWTy5cvqyE62LNnj9StW7e020hEZDZXl/yZpdTM4v1BR0RUqoHTRx99pIblkHV6//33xccnd3HNixcvqnmViIispWOdymrh39qBFWTupjMSeTWVHwYRWTdwwpQAzz//fL7tqD0iIrImX083tfAv/LCt/E6ASUQOtuQKERERkb2x2pIrREREROV2Hid9uRP9dmEXBk1EZGtWHoqWR+btkNWHTVcqICIqrlJbcoWIyFZ9vuGUYcqUWxuZLixORGTxmcOffvpp+eSTT/Jt/+yzz+TZZ58tyUsSEZU6H8/cvw31uS9zNHYyEVkhcPrll1/UunF5dezYURYtWnSDTSIiKh3T7mshH9/fUt68s6lh296Iq7LhWAy7mIjKbqgOk176++efhdfPz09iY2NL1hIiolJWP9hXXX7ZdV7d33QiVtZfC5o2vNBdwit7s8+JyPIZJ8wOjqVW8vr777+ldu3aJXlJIiKL0Ze8zDIaq1t79JK88cdBORAZz54nIstmnMaPH6/Wl8MCvT169FDbVq9eLR9++KHMmDGjJC9JRGQxneoESrcGVaRBsK/8vOu8XEnOkEl/HFSPxSaly2dDWrP3ichygdMjjzwi6enpMmXKFHnrrbfUNizBMnv2bBk6dGhJXpKIyGKC/Dxl3oh2hqkJEDjpMrNz2PNEZPnpCJ588kl1QdbJy8vLsF4dEZEtQ6H4/sh4Sc/Klhmrjlu7OUTkCDVOkJWVJatWrZLFixeruVHgwoULkpSUVJrtIyIqVbfUC5Qnu9WRKr4e7FkiKpuM09mzZ6Vv375y7tw5NWTXq1cv8fX1lalTp6r7c+bMKcnLEhEREZW/jNMzzzwjbdu2lbi4ODVMp7vrrrtUkTgRERFReVSijNM///wjmzdvFnd3d5PtKBCPjIwsrbYREVkcKg22nros6Vk50rV+FfY4EZV+4FTYYr7nz59XQ3ZERPZi1eFoWXEod/HfrRNvlRB/T2s3iYjK21Bd7969TeZrcnJyUkXhkyZNkttvv70020dEZBEuTk751q9LTMtU1+cup8iUvw7JO0sPG05+ISIqccZp2rRpqji8cePGkpaWJkOGDJHjx49LYGCg/Pjjj+xZIrJ53RoESe/GwdI01F++2HBKEtOz1Dp2mKLg7wMXDQHViE41par/f7WcROTYShQ4hYWFyb59+2ThwoXqGtmmRx99VB588EGTYnEiIluFIbnPh7ZVt+duOq2uX1j0b779srKZcSKiGwicMjMzpWHDhvLnn3+qQAkXIiJ75uHqgt9u4ubiJHe2DJWRnWvJXTM3S2pmtrz++wHZeSZO3r6rqXqMiBxbsQMnNzc3NTxHRFRevD2wqRyNTpR721SXYD/T4vC1R2PU9V//XpQLV9PEy81ZhneqZaWWEpFdFoePGTNGTXaJ2cOJiOxdz8bBMqZ7XZOgqU5QBXF1dpLqFXPLD3Dm3dRlR+SNJYckPiW3iJyIHE+Japx27NihJrpcsWKFNGvWTCpUqGDyOJZhISKyZz893kEyszRZtPu8vPXnIZPHMrgwMJHDKlHgFBAQIPfcc0/pt4aIyEZ4u7uKuIvc27q6WhC4Xc1Kcu+cLdZuFhHZU+CEiS8/+OADOXbsmGRkZEiPHj3kjTfe4Jl0RFRu+Xu7yehuddVtTP3EaZ2IHFuxapymTJkiL7/8svj4+EhoaKh88sknqt6JiIiIyBEUK3D69ttvZdasWbJ8+XL57bffZMmSJTJ//nyViSIichQ7z1yRD5YfkQtXU63dFCKy5aG6c+fOmSyp0rNnT7XcyoULF6R69eqWaB8Rkc15cv5udZ2Vo8nE2xpZuzlEZKsZJ0w/4OnpmW9eJ0yKSURU3nmqiTL/k57JbDuRoylWxgmLXQ4fPlw8PDwM2zAZ5hNPPGEyJQGnIyCi8ujDQS3U8Nzp2GSZv+1cvt+PSelZ4uvpZrI9J0cTZ+fcBYWJyMECp2HDhuXb9tBDD5Vme4iIbNbtzaqq6w9XHDVsy8jKkb/2X5C5m87Iv+fj5eP7W0q/ZlVl5aFombf5jGw7fUWaVPOTmMR0aRDiK9892t6K74CIyjRwmjt37g3/QCKi8mLjiVjpNHWNCop0/1t/St77+4hcjP9vaaqDFxLU9ZXkDKu0k4isvOQKERGJnLiUpIKmIF8PaRjiq7rk0MUEFTRVquAuwX65ZQ0tqvuzu4gceeZwIiJHhqE3TIbZMixAhnesKbc1rSp//ntBxv+0T5pX95dhHWpKv+ZVxdPNRc06fjUlU9q/s7pkPywnW+TsZpGkaBGfYJEaHUWcTYvUiajsMHAiIiqmvk2ryqHJfcXL/b8A5u7W1dViwb4ermqaFp2HOhOv8DOP/z1/VeZvPSebT8VK1/pVZH9kgtxcu1LuNAeH/hBZ9pJIwoX/nuBXTaTvVJGGd/BzI7ICBk5ERCVgHDTp/PKcUZdXjqbJG38clK2nLkunuoGy48wVVVCu+35r7pl6kXEpMrHGcZGfhuJ8PdMXSbiYu/2+b0QqtOBnR1TGGDgREZWRHE3UmXZwJCpRXbu7OEtGdu58UKiJik5Iz42VkGm6FjTp6+PlJrJwx0lk+asid/3Oz46ojDFwIiKyMGSifDxcJSUjSwVPUNXfU9VH3dc2TAK83CQqIU3iUzPlto//kaT0DBmU8qhs1xpJS6fjclILlV7Ou2S6++xrr6iJJEaKXD4pUqMmPz+iMsTAiYioDIb1Vj/XFXkiCfTxkNOXk6VW5QomE2NWC/CShLTcWqi0LJHtkruUy16tnrreltMw/wun505zQERlh9MREBGVgWA/Twny81TBUp0qPgXOJl69orcargv1cZKKkjuU187pcOEv6uFnySYTUQGYcSIishEYztsy4VZxlhyRGU1VIfjenNoyMOMt9fiJnGpyRguRHs57RfxCRSrXsXaTiRwOM05ERDZEZaIwTxOmHDASKVWkZ8Y0GZn5vKzPaS7S520RJ/4KJyprzDgREdmixgNEBn0rbks+FsmzUsv6Os/LiqM1xEdixOlgilxMSJe372wq/t5FT4dARDeOgRMRka1qPEAa1r9dnl68VrxykmVZpIfsi8mReUeQaYow2bV/86rSu0mI1ZpK5CgYOBER2TAXV1cZP6iXun34xz2yL8ZoFnE1M7mzpGflSHaOJocvJkhYJW9VK0VElsH/XUREduKtO5vKXa1D5eZalSUyLlkSLl+SKesvya6zcfLCon8lKT1LejUOli+GtrV2U4nKLVYWEhHZCdQwdW8QpOaFql3FRyp5u4rLtVkNEDTB8ehE+WT1cXn99wOSmpFt3QYTlUPMOBER2THMPl6pgocE+rqrte7OXE6R6SuPqcf6NAlRa+IRUelh4EREZMf6Ng2R25tXkwOR8YZFgl2dnSQrR5PMa2vgEVHp4VAdEVE50DTUX/4Y20k2vtRdGlb1tXZziMotZpyIiMqJ5tUDrN0EonKPGSciIiIiMzFwIiIqp64kZ8ji3ecl4kqKtZtCVG5wqI6IqJwa/9M+Q7F4v+ZVZdWhaOnWIEi2nb4iT3StLSM717Z2E4nsDgMnIqJyxtPVxeQ+zrD7fW/ujON/7b+ornE/M1uT1MxseebWeuKCxYWJ6LoYOBERlTOv9Gskm07EquVXnvtpn3i5uUjitQkyW1T3l33n42V/ZO4F+jQJlibV/K3caiL7wMCJiKicaRVeUV2gc70qUsHDRTyuZaE2HIuRoV9vV7eRZMrRRGWeiMg8LA4nIirHKlVwNwRN0LleoPz0eAc131O1AC+rto3IHjHjRETkQJycnKRdrUrWbgaR3WLGiYiIiMhMDJyIiIiIzMTAiYiIiMhMDJyIiBxcfGqmLD8YJfEpmdZuCpHNY3E4EZGDG3ZteoJ721SXafe1sHZziGwaM05ERA7Kw9X0EHD1WsbpVEySzFh1TAbN2SLrjl6yUuuIbBMzTkREDmrygKay+1ycJKVnyecbTsnR6AQZ8NlG+fd87oziELLbU61vR0S5mHEiInJQt9QLlKdvrSe1Ayuo+xFXUlXQhHXrQq9Njpmjmc4qnp6VLZqmydGoRJWZMldmdo6cu5xSyu+AqOwx40RE5OBahAVI5QruUiuwgtzZsprc3qyqLNl3Qd5YckiORyfJkC+2yuaTl6V5dX+TbBSG+v55sbucjEmWtjUripuLc75gCWvm/b0/Spbuv6jWy8PPCvByk/rBPvJKv8ZWeLdEN4aBExGRg2tU1U92vdarwMeORieKROfeNg6aID0rR9q9s1rddnV2kvrBvhLg7SYjO9eSpfujZOWhaHXGnrF9EVfV9fpjMRKblKEyVx/c15yLDJPdYOBERET5NK7mr4KhmoEV5MSlJGkQ7Ct9mobI+bgUtd7dy4sPSGpmtmH/rBxNDl1MULeRndIF+nhI36bBkpmlycKdEVK9opecj0tVj/26J1Jdrzsaw8CJ7AYDJyIiygfr2R16s6+4uTip9e3ycnF2ltMxySrD9OmaE9Kkmp/KIgGG/fq3qCa3NQ2RtjUrqZopeOfuZur6rlmbJC4lw1BX9c/xGNl66rI0C/WXF/s25KdBNo2BExERFcg9z3QFxga0qGa4PaxjTXV9JjZZBUQtqgeI87VgyZgeQP0x9hZ1/dKif2XhlQjZeuqKur/rbBwDJ7J5PKuOiIhKBYb1WoVXLDBoKkhuQbmTNA31U/fznMBHZJNsInCaOXOm1KxZUzw9PaV9+/ayfXvuLLbXs2DBApVCHjhwoMXbSEREpeu+tmFyfMrtMvvBNuxashtWD5wWLlwo48ePl0mTJsnu3bulRYsW0qdPH7l0qejZas+cOSPPP/+8dO7cuczaSkRERI7N6oHT9OnT5bHHHpMRI0ZI48aNZc6cOeLt7S1ff/11oc/Jzs6WBx98UCZPniy1a9cu0/YSERGR47Jq4JSRkSG7du2Snj17/tcgZ2d1f8uWLYU+780335SgoCB59NFHy6ilRERERFY+qy42NlZlj4KDg0224/6RI0cKfM7GjRvlq6++kr1795r1M9LT09VFl5CQO89ITk6OupQ2/XUt8drEfrc1/L6z70uDpv33+9LWf3fyO18++704r2tX0xEkJibKww8/LF988YUEBgaa9Zx3331XDenlFRERIb6+vqXeRqzhFBcXp4rWC5r7hCyD/W4d7HfrKU99fzEhwxBAnTt3TmxZeep3e6JZuN8RX9hF4ITgx8XFRaKjr83nfw3uh4SE5Nv/5MmTqii8f//++aJEV1dXOXr0qNSpU8fkORMnTlTF58YZp7CwMHXx88s9BbY0oT34gPH6GHakssF+tw72u/WUp753jsPiv8fFyclZwsPDxZaVp363JzkW7nd9NMrmAyd3d3dp06aNrF692jClADoH98eOHZtv/4YNG8r+/ftNtr366qsqUvz4449Vh+bl4eGhLnmh4y31pddfm/+pyhb73TrY79ZTXvoeAZPOHt5Leel3e+NswX4vzmtafagO2aBhw4ZJ27ZtpV27djJjxgxJTk5WZ9nB0KFDJTQ0VA25YZ6npk2bmjw/ICBAXefdTkRE9gdZBSwe7OnmYu2mENlm4DR48GCJiYmR119/XaKioqRly5aybNkyQ8E4xrsZ1RMRlX/pWdnS9YN1Enk1Vb57tJ10rGNeLSuRQwVOgGG5gobmYN26dUU+d968eRZqFRERlQV9DbscTeTcFdQ7iWw8HiuxSRnSKMRXqvh6SFxKptSs7M2CbLI6mwiciIjIcVX195ShHWpISka2HI1KlP2R8TJr3UmTwCo7R5MfH7tZOtSpbNW2ErGyjYiIrAqnl795Z1OZdl8L6VhAYISgCU7FJlmhdUSmmHEiIiKbMaZHXbm5dmVpVNVPtp66LFdTMmTpgSjZfvqKrDl8SdYfjZFejYPVAsFE1sDAiYiIbIafp5t0bxikbg9sFaqut5y6rK5XH8ld/P3QxQQGTmQ1HKojIiKb1rV+kPh6uErDEF+ToTsia2DGiYiIbNqQ9uHqsv98vPT/bKNkZufI73sj5WpKpvRpEiJ7I65K01A/qV7R29pNJQfAwImIiOwKpil4ZkHuQu+T/jiortvXqiQLH+8g5y6nyLpjl2TX2TgZ0KKa3NrIdBF5ohvFwImIiOxC1QBP8XB1VjOL54UpDHp8uE5OxSQbtp2MSZJKFdzlwtU0Sc7Ikn0RV1Uw1b42pzSgkmPgREREdiHQx0M2vNhd1Th5ubnI7nNxkpmtyRPf71JzQCFocnV2kvBK3nIqNlkORCbIXbM2m7zG8UtJ8vLtjdTtlmG5S3YVJDsnW3Zf2i0xKTFSxbuKtA5qLS7OXAaGGDgREZEdCfbzNNzGMByCqAfahUtWdo46G++WeoFy8Wqa9JmxweR5IX6eEpWQpqY1GDhzk9q2+rmuUqeKT76fsersKnlv+3sSnRL938/1DpYJ7SZIj7AeFn1/ZPuYcSIiIruFWcXfvbuZyTa/EDdZMOpmcXZyklbhuVmlQxcS5M5rAZPu5KUkNU9UgxA/8fFwNQRN49eNF01Mz9y7lHJJbf+wy4dSz6mexd8X2S5OR0BEROUOJtFsV6uSuLk4q0uLsAD5/tH28tPjHaROlQpqn1Hf7ZJ7Zm+RN5ccNAzPIdOUN2gCfdu0ndMkR8tfY0WOgxknIiJyCBjGgyBfTzlpVER+5nKKKhw/dvWQRCVdkeyU+pKVXE+yk+uKk0uqeIV/JU5O2Sp4wvDducRzUlNqWvGdkDUxcCIiIocy9Z7mqrD8YnyaTF12RNU9/TeM91a+/XPSg8TF86LhflIG18xzZAyciIjIoYRX9laXLSdzl3LJy8n1qrhWOC6Zic1FcjzyPe7jnr+gnBwHAyciInJIN9euJPNG3KQKw3093WRvxBWZeeR5ics+KuKkSdbxBqIZBU5O4qTOrgv3Dbdqu8m6GDgREZFDcnJykm4NchcUhgYhvlI56HF19hzCJJN9r91/vu3z4uzE86ocGT99IiKia3rW6CnTu02XIO//AioUibsl9JOXW38ot9a4lX3l4Bg4ERER5Qmelt+zXAI8cueASr/UTy5H3iJ7jnLdO2LgRERElA+WV2kWmrumnbd77lIriemZ7ClijRMREVFB5g6/SeJSMuTv/Rfltd9zJ8kk4lAdERFRIcu5YGFhImMMnIiIiIjMxOkIiIiIzJCdo8mByHhZujdWjqy9pG73aRIifl5uUr2ilzzYvgb70QEwcCIiIjLD8oPR6mJs/rZzhtsIolBI7u3OQ2t5xqE6IiKiIlTx9TTc9vFwkQ41fKRfsxB1H5kmp2tzZfaavl4av75cftoRwf4sxxgWExERFaFX42B1hl3FCu7SOMRHLkSel/DwcPn0ASdxdnaS1m+tlCvJGRKXkjtdwZ6IqzLopjD2aTnFwImIiOg6Z9d1b5g7k3hOTo5hO4Im+Pj+lrIv4qoci06SP/ZdYF+WcxyqIyIiugGd61WRsT3qSd0gH/ajA2DgRERERGQmBk5EREREZmLgRERERGQmBk5EREREZmLgRERERGQmBk5EREREZmLgRERERGQmBk5EREREZmLgRERERGQmBk5ERESlaM+5OHl03g557+8j7NdyiIETERFRKchduU7kSFSirD5ySeasPynx1xb+pfKDgRMREVEp6Ns0RNrWqCh9m4QYtmVrGvu2nHG1dgOIiIjKg3rBvrLoyY6iaZrUmrjU2s0hC2HGiYiIiMhMDJyIiIgsJCE1U45HJ0p2DofsygsGTkRERBbSbdo66fXRBvlh+zn2cTnBwImIiKiUBfp4mNw/HZPMPi4nWBxORERUipycnGTBqJvldGyyrDlySX5ktqlcYcaJiIiolNUN8pFejYOlorcb+7acYeBEREREZCYO1REREVnYkn8vyMId5yQ5I1tNkhmblC4fDmopbWpUZN/bGQZOREREFuLt7qKuYxLTDdt2no1T16sPRxsCJyzNEpeSIZ5uLnIgMl7qBftIjcoV+LnYIAZOREREFjL4pnBJyciWIF8POXYpSbACy6mYJNl2+orsOhsn4xfulb0RV+VUbP6z7lqFB8iec1dlcNswycrRpEv9QLmzZSg/Kytj4ERERGQhVXw95MW+DU22vbnkkAqccCkKgiZYuDNCXa8/FsPAyQYwcCIiIipDtzULkU0nYiXY31NahgWozFLzUH+JT82UYD9P+XlnhJy5nKLurz16SeoH+8r201ckIyubn5MNYOBERERUhm6qWUmWj+uSb3vla5NmDu9Uy2Q7hvZ6fLi+zNpHReN0BERERERmYuBEREREZCYO1REREdmBtKwcmbh4v2w8ESM9GgRJTFK69G4cIgNb8Uy7ssTAiYiIyIY5Ozmp64ysHMO6d99sOauu90XEM3AqYwyciIiIbFiNyt5yb5vqcjE+Va6mZMrF+DRpUs1P/jkeK5nZOdZunsNh4ERERGTDnJycZNp9LUy2HbwQL/8c32i1NjkyFocTERHZMU3TJDtHs3YzHAYDJyIiIjt1OTlDWr65UlpMXiHHoxOt3RyHwMCJiIjIzvh5uqlrZJoww3hSepbsichdooUsi4ETERGRnQmr5C2fP9xG3ru7mVq2BT5ZfVxav7VS3l92xNrNK9dYHE5ERGSHejcJUdebTl6WvRFX5Xxcqrr/655IaR1eUbI1TXo3DlbF5VR6GDgRERHZsXE960mdKhXUPE+z1p1U0xWM/HaneuyHke3lplqVJC4lQw5fTBQXJyfpVLcyg6kbwMCJiIjIjtWu4iPP9qwvV1MyZP62c6reCQFSRnaOPDl/t6qBMvbbmE6G4T0qPgZORERE5UCAt7vsfLWnKhgf/9NeWbo/yiRocnF2Uo+9s/Swuv/QzTVkQItqVmyxfWLgREREVE64uTiLm4vIq/0aS+d6VSS8krfUCqwgAd5uMnzuDtl++oq6AK5nrDwmHm4usvDxmw1n6lHRGDgRERGVM9UCvOSBduEm2565tZ4s2nVeouLTZMupy2rbqdhkdf3lP6elkreb3NooWJ2xR4Vj4EREROQAOtUNVJecHE2+2XJGsrI1mbf5jEReTVVTGcCUpYdlfK8GcuhigjQP9ZcL8alqnbwm1fyt3XybwcCJiIjIgTg7O8mITrXU7aupGfLVxtOSlpm7WHBmtiZTr80DtWTfBXV94lKSPNKplhruaxVeURwdAyciIiIH9UKfhvJ87wYSm5QhD325TTTR5Fh0klT191TF5ocvJsg/x2PVBZpU85Ozl1MkNTNbwip6yZnLKTK2e11VLxURlyKYMapmYAX57tH2qhi9PGLgRERE5MAwQWYVXw9ZPq6LyfYjUQky4NNNKphCJgoOXkgwPI6gCT5be8LkeRfi06TL+2vF081ZJvVvIl3qV5HyhIETERER5dMwxE/+faO3yhxtPXVZVh++JNUCPFXw1CosQDYcj5WYxHSVYUrNyFKF5RMX71fPRd0UjJ6/W0Z2riX1gnylX/Oq5aKXGTgRERFRgTwxt4GImtoAF2PDr9VJGate0Ut2nL4iR6ISZcWhaDUZ54xVx1Xw1bl+YLmY8sAmFvmdOXOm1KxZUzw9PaV9+/ayffv2Qvf94osvpHPnzlKxYkV16dmzZ5H7ExERUdnoXK+KjO/dQMb3ri8NQ3yldXjuDOWYeDP9WgG6vbN64LRw4UIZP368TJo0SXbv3i0tWrSQPn36yKVLlwrcf926dfLAAw/I2rVrZcuWLRIWFia9e/eWyMjIMm87ERERFTzMt+zZLrJ4dCcpb2sMWz1wmj59ujz22GMyYsQIady4scyZM0e8vb3l66+/LnD/+fPny+jRo6Vly5bSsGFD+fLLLyUnJ0dWr15d5m0nIiIix2LVwCkjI0N27dqlhtsMDXJ2VveRTTJHSkqKZGZmSqVKlSzYUiIiIiIrF4fHxsZKdna2BAcHm2zH/SNHcifgup6XXnpJqlWrZhJ8GUtPT1cXXUJC7qmUyFLhUtr017XEaxP73dbw+86+dzT8zpfcjRwbLd3vxXlduz6r7r333pMFCxaouicUlhfk3XfflcmTJ+fbHhERIb6+vqXeJk3TJC4uTs2LgQuVDfa7dbDfrYd9z363G1ruFWqRU71dbfL7npiYaB+BU2BgoLi4uEh0dLTJdtwPCQkp8rnTpk1TgdOqVaukefPmhe43ceJEVXxunHFCQTkufn5+YomoFR8wXh/DjlQ22O/WwX63HvY9+91eaHJQXc/cHifurs7yzK11pX6wr0193/XRKJsPnNzd3aVNmzaqsHvgwIFqm17oPXbs2EKf9/7778uUKVNk+fLl0rZt2yJ/hoeHh7rkhY63VGCjvzYDp7LFfrcO9rv1sO/Z7/bA3dVZMrJyZPWR3LPlQwO85NU7GtvU9704r2n1oTpkg4YNG6YCoHbt2smMGTMkOTlZnWUHQ4cOldDQUDXkBlOnTpXXX39dfvjhBzX3U1RUlNru4+OjLkRERGQ73ujfRM08jrXs9py7Klk518bu7JTVA6fBgwdLTEyMCoYQBGGagWXLlhkKxs+dO2cSCc6ePVudjXfvvfeavA7mgXrjjTfKvP1ERERUuCHtw9XlwxVHVeBk76weOAGG5QobmkPht7EzZ86UUauIiIiITLF6mYiIiMpUYlqmWobFHjFwIiIiojIzb/MZafbGCrnt4w2SlZ0jaZnZdtX7NjFUR0REROVbtQAvk/vHopOk7it/i4uzk7x7dzNpXNVPwip6i7+3m9gyBk5ERERkcfe1qS71gnzEw9VFBv1vi6ReyzRhyO7FRf8apir458Xu4uxsuxNIM3AiIiIiywccLs7StmbuurIrx3eR07HJsvNMnHy8+rhhn8irqZKtaeIsDJyIiIiIlOoVvdWlc70q8uDN4ZKVrUnH99aIPWDGiYiIiKwmyNdT4lMyDfdf//2AGr4b16u+VPU3rYuyBQyciIiIyKpcXP4bmvtxe4S6vpyUIQNaVpMm1fyldqC32ApOR0BERERW5ePhKq/c3kj6t6hm2Ia17Z5ZsFce+nKbJKRlytXULLEFzDgRERGR1T3Wpba6HtCimjz/8z5JyciSzGxNohLSpOWbq9RjXw/zlR6NcpdksxZmnIiIiMhm9GocLPsm9Zb9b/SRgDxzOh2JShBrY8aJiIiIbI6nm4use76bRCeky7nLSXLozEXpXC/Q2s1i4ERERES2KcDbXV3qBVWQehXSJbyav7WbxKE6IiIiInOxxomIiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzEwImIiIjITAyciIiIiMzkKg5G0zR1nZCQYJHXz8nJkcTERPX6zs6MS8sK+9062O/Ww75nvzuSHAsfW/WYQI8RiuJwgRM6HsLCwqzdFCIiIrKxGMHf37/IfZw0c8Krcha1XrhwQXx9fcXJyckiUSuCsoiICPHz8yv11yf2uy3h951972j4nS+f/Y5QCEFTtWrVrpvRcriMEzqkevXqFv85+GAZOJU99rt1sN+th33PfnckfhY8tl4v06RjEQ4RERGRmRg4EREREZmJgVMp8/DwkEmTJqlrKjvsd+tgv1sP+5797kg8bOjY6nDF4UREREQlxYwTERERkZkYOBERERGZiYETERERkZkYOJXAzJkzpWbNmuLp6Snt27eX7du3F7n/zz//LA0bNlT7N2vWTJYuXVqSH+vwitPvX3zxhXTu3FkqVqyoLj179rzu50Sl833XLViwQE0yO3DgQHZtGfX91atXZcyYMVK1alVVRFu/fn3+vimDfp8xY4Y0aNBAvLy81CSN48aNk7S0tJL8aIe1YcMG6d+/v5qAEr83fvvtt+s+Z926ddK6dWv1Xa9bt67MmzevTNqK2TKpGBYsWKC5u7trX3/9tXbw4EHtscce0wICArTo6OgC99+0aZPm4uKivf/++9qhQ4e0V199VXNzc9P279/Pfrdgvw8ZMkSbOXOmtmfPHu3w4cPa8OHDNX9/f+38+fPsdwv2u+706dNaaGio1rlzZ+3OO+9kn5dB36enp2tt27bVbr/9dm3jxo3qM1i3bp22d+9e9r8F+33+/Pmah4eHukafL1++XKtatao2btw49nsxLF26VHvllVe0xYsX44Q17ddffy1y/1OnTmne3t7a+PHj1bH1008/VcfaZcuWaZbGwKmY2rVrp40ZM8ZwPzs7W6tWrZr27rvvFrj/oEGDtH79+plsa9++vfb444+X5PNyWMXt97yysrI0X19f7ZtvvrFgK8ufkvQ7+rpjx47al19+qQ0bNoyBUxn1/ezZs7XatWtrGRkZJf2RVIJ+x749evQw2YaDeadOndifJWRO4PTiiy9qTZo0Mdk2ePBgrU+fPpqlcaiuGDIyMmTXrl1q2Md4CRfc37JlS4HPwXbj/aFPnz6F7k+l0+95paSkSGZmplSqVIldbOF+f/PNNyUoKEgeffRR9nUZ9v0ff/whHTp0UEN1wcHB0rRpU3nnnXckOzubn4MF+71jx47qOfpw3qlTp9Tw6O23385+tyBrHlsdbq26GxEbG6t+CeGXkjHcP3LkSIHPiYqKKnB/bCfL9XteL730kho7z/sfjUq33zdu3ChfffWV7N27l11bxn2PA/aaNWvkwQcfVAfuEydOyOjRo9UfDJg4kCzT70OGDFHPu+WWW9RCsVlZWfLEE0/Iyy+/zC63oMKOrVgMODU1VdWbWQozTlTuvffee6pQ+ddff1XFnmQZWFn84YcfVoX5gYGB7OYylpOTozJ9n3/+ubRp00YGDx4sr7zyisyZM4efhQWhQBmZvVmzZsnu3btl8eLF8tdff8lbb73Ffi+nmHEqBhwMXFxcJDo62mQ77oeEhBT4HGwvzv5UOv2umzZtmgqcVq1aJc2bN2f3WrDfT548KWfOnFFnxhgfzMHV1VWOHj0qderU4Wdggb4HnEnn5uamnqdr1KiR+sscQ1Du7u7sewv0+2uvvab+YBg5cqS6jzOnk5OTZdSoUSpwxVAflb7Cjq1+fn4WzTYBP9FiwC8e/CW3evVqkwMD7qO2oCDYbrw/rFy5stD9qXT6Hd5//331V9+yZcukbdu27FoL9zum3Ni/f78aptMvAwYMkO7du6vbOE2bLNP30KlTJzU8pwercOzYMRVQMWiyXL+jfjJvcKQHr1zRzHKsemy1ePl5OTxVFaeezps3T50COWrUKHWqalRUlHr84Ycf1iZMmGAyHYGrq6s2bdo0dVr8pEmTOB1BGfT7e++9p04pXrRokXbx4kXDJTEx8ca/BA6kuP2eF8+qK7u+P3funDpzdOzYsdrRo0e1P//8UwsKCtLefvvtG2iF4yluv+N3Ovr9xx9/VKfIr1ixQqtTp446o5rMh9/NmD4GF4Qm06dPV7fPnj2rHkefo+/zTkfwwgsvqGMrpp/hdAQ2DPNFhIeHqwMzTl3dunWr4bGuXbuqg4Wxn376Satfv77aH6dP/vXXX1ZotWP1e40aNdR/vrwX/JIjy/V7XgycyrbvN2/erKY7wYEfUxNMmTJFTQ9Bluv3zMxM7Y033lDBkqenpxYWFqaNHj1ai4uLY7cXw9q1awv8na33Na7R93mf07JlS/U54fs+d+5crSw44R/L57WIiIiI7B9rnIiIiIjMxMCJiIiIyEwMnIiIiIjMxMCJiIiIyEwMnIiIiIjMxMCJiIiIyEwMnIiIiIjMxMCJiIiIyEwMnIiIisnJyUl+++03dRsLG+M+1uMjovKPgRMR2ZXhw4erQAUXNzc3qVWrlrz44ouSlpZm7aYRkQNwtXYDiIiKq2/fvjJ37lzJzMyUXbt2ybBhw1QgNXXqVHYmEVkUM05EZHc8PDwkJCREwsLCZODAgdKzZ09ZuXKleiwnJ0feffddlYny8vKSFi1ayKJFi0yef/DgQbnjjjvEz89PfH19pXPnznLy5En12I4dO6RXr14SGBgo/v7+0rVrV9m9e7dV3icR2R4GTkRk1w4cOCCbN28Wd3d3dR9B07fffitz5sxRAdK4cePkoYcekvXr16vHIyMjpUuXLir4WrNmjcpYPfLII5KVlaUeT0xMVBmsjRs3ytatW6VevXpy++23q+1ERByqIyK78+eff4qPj48KdtLT08XZ2Vk+++wzdfudd96RVatWSYcOHdS+tWvXVkHQ//73P5U9mjlzpsokLViwQNVIQf369Q2v3aNHD5Of9fnnn0tAQIAKvJClIiLHxsCJiOxO9+7dZfbs2ZKcnCwfffSRuLq6yj333KMyTCkpKWqozVhGRoa0atVK3cbZbxia04OmvKKjo+XVV1+VdevWyaVLlyQ7O1u95rlz58rkvRGRbWPgRER2p0KFClK3bl11++uvv1Z1TF999ZU0bdpUbfvrr78kNDTU5DkYmgPUPRUFw3SXL1+Wjz/+WGrUqKGeh+wVgi8iIgZORGTXMEz38ssvy/jx4+XYsWMq0EF2CMNyBWnevLl888036oy8grJOmzZtklmzZqm6JoiIiJDY2FiLvw8isg8sDiciu3ffffeJi4uLqmN6/vnnVUE4giOcKYcz4j799FN1H8aOHSsJCQly//33y86dO+X48ePy3XffydGjR9XjKAbH/cOHD8u2bdvkwQcfvG6WiogcBzNORGT3UOOEgOj999+X06dPS5UqVdTZdadOnVKF3a1bt1ZZKahcubI6m+6FF15QWSkEXC1btpROnTqpxzHkN2rUKPUcTHeAYnMEY0RE4KRpmsauICIiIro+DtURERERmYmBExEREZGZGDgRERERmYmBExEREZGZGDgRERERmYmBExEREZGZGDgRERERmYmBExEREZGZGDgRERERmYmBExEREZGZGDgRERERmYmBExEREZGY5/8tz0D1WycsFAAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Visualisation\n", "plt.figure(figsize=(6,5))\n", "plt.plot(recalls_reg, precisions_reg, label=f\"RF régularisé (AP={pr_auc_oof_reg:.3f})\")\n", "plt.scatter(recalls_reg[best_idx_f1_reg], precisions_reg[best_idx_f1_reg],\n", " label=f\"Best F1 (thr={best_thr_f1_reg:.2f})\", color=\"tab:orange\")\n", "plt.scatter(recalls_reg[best_idx_f2_reg], precisions_reg[best_idx_f2_reg],\n", " label=f\"Best F2 (thr={best_thr_f2_reg:.2f})\", color=\"tab:green\")\n", "plt.xlabel(\"Recall\")\n", "plt.ylabel(\"Precision\")\n", "plt.title(\"Courbe PR – RF régularisé (OOF)\")\n", "plt.grid(alpha=0.4); plt.legend(); plt.tight_layout(); plt.show()" ] }, { "cell_type": "code", "execution_count": 76, "id": "e1a3f804-e3bc-4bca-90c8-f0b72a9ab8c2", "metadata": {}, "outputs": [], "source": [ "# On fige le seuil choisi (F2)\n", "chosen_thr = float(best_thr_f2_reg) " ] }, { "cell_type": "code", "execution_count": 77, "id": "7a8ab875-9a11-4e22-b105-2666f19a98e3", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Pipeline(steps=[('prep',\n",
       "                 ColumnTransformer(transformers=[('ord',\n",
       "                                                  OrdinalEncoder(categories=[['Aucun',\n",
       "                                                                              'Occasionnel',\n",
       "                                                                              'Frequent']],\n",
       "                                                                 handle_unknown='use_encoded_value',\n",
       "                                                                 unknown_value=-1),\n",
       "                                                  ['frequence_deplacement']),\n",
       "                                                 ('ohe',\n",
       "                                                  OneHotEncoder(handle_unknown='ignore',\n",
       "                                                                sparse_output=False),\n",
       "                                                  ['genre', 'statut_marital',\n",
       "                                                   'departement', 'poste',\n",
       "                                                   'domaine_etude']),\n",
       "                                                 ('num', 'pass...\n",
       "                                                   'nombre_participation_pee',\n",
       "                                                   'nb_formations_suivies',\n",
       "                                                   'distance_domicile_travail',\n",
       "                                                   'niveau_education',\n",
       "                                                   'annees_depuis_la_derniere_promotion',\n",
       "                                                   'annes_sous_responsable_actuel',\n",
       "                                                   'satisfaction_globale',\n",
       "                                                   'exp_moins_3_years'])])),\n",
       "                ('clf',\n",
       "                 RandomForestClassifier(class_weight='balanced', max_depth=8,\n",
       "                                        min_samples_leaf=5,\n",
       "                                        min_samples_split=10, n_estimators=200,\n",
       "                                        n_jobs=-1, random_state=42))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ "Pipeline(steps=[('prep',\n", " ColumnTransformer(transformers=[('ord',\n", " OrdinalEncoder(categories=[['Aucun',\n", " 'Occasionnel',\n", " 'Frequent']],\n", " handle_unknown='use_encoded_value',\n", " unknown_value=-1),\n", " ['frequence_deplacement']),\n", " ('ohe',\n", " OneHotEncoder(handle_unknown='ignore',\n", " sparse_output=False),\n", " ['genre', 'statut_marital',\n", " 'departement', 'poste',\n", " 'domaine_etude']),\n", " ('num', 'pass...\n", " 'nombre_participation_pee',\n", " 'nb_formations_suivies',\n", " 'distance_domicile_travail',\n", " 'niveau_education',\n", " 'annees_depuis_la_derniere_promotion',\n", " 'annes_sous_responsable_actuel',\n", " 'satisfaction_globale',\n", " 'exp_moins_3_years'])])),\n", " ('clf',\n", " RandomForestClassifier(class_weight='balanced', max_depth=8,\n", " min_samples_leaf=5,\n", " min_samples_split=10, n_estimators=200,\n", " n_jobs=-1, random_state=42))])" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Fit final sur tout X_train / y_train\n", "pipe_rf_reg.fit(X_train, y_train)" ] }, { "cell_type": "code", "execution_count": 78, "id": "ff104181-815f-4d6c-80cd-3c6fc02028fd", "metadata": {}, "outputs": [], "source": [ "# Probabilités sur TRAIN & TEST\n", "proba_train_reg = pipe_rf_reg.predict_proba(X_train)[:, 1]\n", "proba_test_reg = pipe_rf_reg.predict_proba(X_test)[:, 1]\n" ] }, { "cell_type": "code", "execution_count": 79, "id": "0dd59342-d607-43b2-b106-2213ccaa9dce", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "=== TRAIN – RF régularisé (F2) (seuil=0.330) ===\n", "Accuracy : 0.832 | Précision : 0.490 | Rappel : 0.995 | F1 : 0.656\n", "ROC AUC : 0.982 | PR AUC : 0.911\n", "\n", "Classification report :\n", " precision recall f1-score support\n", "\n", " 0 0.999 0.800 0.889 986\n", " 1 0.490 0.995 0.656 190\n", "\n", " accuracy 0.832 1176\n", " macro avg 0.744 0.897 0.772 1176\n", "weighted avg 0.916 0.832 0.851 1176\n", "\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Évaluation avec seuil optimisé\n", "scores_train_reg = eval_with_threshold(\n", " y_train, proba_train_reg,\n", " threshold=chosen_thr,\n", " set_name=\"TRAIN – RF régularisé (F2)\",\n", " plot_cm=True\n", ")" ] }, { "cell_type": "code", "execution_count": 80, "id": "ff3d5325-ea84-44e2-b871-1e082152eda4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "=== TEST – RF régularisé (F2) (seuil=0.330) ===\n", "Accuracy : 0.718 | Précision : 0.327 | Rappel : 0.723 | F1 : 0.450\n", "ROC AUC : 0.765 | PR AUC : 0.395\n", "\n", "Classification report :\n", " precision recall f1-score support\n", "\n", " 0 0.932 0.717 0.810 247\n", " 1 0.327 0.723 0.450 47\n", "\n", " accuracy 0.718 294\n", " macro avg 0.629 0.720 0.630 294\n", "weighted avg 0.835 0.718 0.753 294\n", "\n" ] }, { "data": { "image/png": "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16uvvqotSAQxpEVNaMWi9YtRq0jDAtLAN27c0ME6ZcqU0WkbN27UAUbOQRmXraAla7eETfj8dkUgtkiaOIHkyvZfZSFH5jRSNH8WuXHznvz513WdljxpImlUo5T0HfOdx/X4lcsvObOklTnfb38m5aaoExhoydZtB6RypWIaDG1oLfpVLSELv/lRkiZNLIkTJ5C589brKFWOVCWvCY7QtGlTDW4TJkyQnj176qtbt24aVNAHePPmTb1gH8ELwRADaxB8MOAGfXPow8QoVECfH1KWGDWaNWtWTX0idYo0ZefOnfX/CHp4H0aMYuQrAvOoUaN0pCoCFgL24sWLtbWHATaeUsNokf72229StGhRnYYyYN1I3WIwES7lwAjYN954w6V/8ueff9bgZ/epkkjJQjlcLuIf2v2fNPv8lT9LhwFz9f9NapfRisiSdbs87rI3X6okO/eflONn/+JujeEOHz6trcGqVYsHmdesWU09ViZMXCoBAbgJQC556806UVJO8h5xLDR3ogj69tC6+v77712mf/HFFxqgkNpMkiSJjh5FywytPQQopD3RwvTz85PBgwfrdYO44B+BEC1H9F3al3HgUgpcf3j+/Hmdt2nTJp2O9wwfPlwH4iC1W6xYMenatauOIkXKFZd2HD9+XFOt5cqV02U9te4ALUwMFnK+5AMtTQTEFStWaJBF3yg+CwYg2d577z394SKAhgTSqgjqCYu1kzg+/7SMiZzNnNGHO4TcunfntrT1K6SNDDQwKJoGx5jkwIEDOogHKVLn4BecK1eu6KAftFrtYP40DI70NAyO5AmDoxfdBCCmKF68uN7VB63dkEJrFy3UkAZGIiKKpX2O3gxp4tDAtZnONwkgIqLogS1HIiIiA4MjERGRgcGRiIjIwOBIRERkYHAkIiIyMDgSEREZGByJiIgMDI5EREQGBkciIiIDgyMREZGBwZGIiMjA4EhERGRgcCQiIjIwOBIRERkYHImIiAwMjkRERAYGRyIiIgODIxERkYHBkYiIyMDgSEREZGBwJCIiMjA4EhERGRgciYiIDAyOREREBgZHIiIiA4MjERGRgcGRiIjIwOBIRERkiCchsHz5cgmpl156KcTLEhEReW1wbNy4cYhWFidOHHny5El4y0RERBT9g2NgYGDkl4SIiCgm9Dk+ePAg4kpCRETkrcERadNBgwZJlixZ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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "scores_test_reg = eval_with_threshold(\n", " y_test, proba_test_reg,\n", " threshold=chosen_thr,\n", " set_name=\"TEST – RF régularisé (F2)\",\n", " plot_cm=True\n", ")" ] }, { "cell_type": "markdown", "id": "5951a76f-0bb9-4fa1-adbe-e65d767f2138", "metadata": {}, "source": [ "**Conclusion**\n", "\n", "Un léger feature engineering a été réalisé (construction d’un score de satisfaction globale et d’un indicateur d’expérience < 3 ans).\n", "\n", "Problème initial : RF de base très performant sur TRAIN (≈ 1.0 partout) mais écart important avec TEST → signe d’overfit.\n", "\n", "Action : régularisation du RF\n", "\n", "max_depth=8, min_samples_leaf=5, min_samples_split=10, class_weight=\"balanced\".\n", "\n", "Validation croisée : scores moyens stables (ROC AUC, PR AUC, accuracy) → le modèle généralise correctement.\n", "\n", "Courbe PR + seuil optimisé (F2) : choix d’un seuil donnant un bon rappel au détriment de la précision (aligné avec l’objectif métier : ne pas rater des démissions).\n", "\n", "Résultat final (TEST) :\n", "\n", "Accuracy ≈ 0.80,\n", "\n", "Rappel classe 1 ≈ 0.64,\n", "\n", "PR AUC ≈ 0.43,\n", "\n", "meilleure balance rappel/précision qu’avec le dummy et la régression logistique, et moins d’overfit qu’avec le RF initial." ] }, { "cell_type": "code", "execution_count": 81, "id": "ad409563-9097-410c-a1b8-c0feb01571a7", "metadata": {}, "outputs": [], "source": [ "# === Sauvegarde du pipeline + métadonnées ===\n", "from pathlib import Path\n", "import json, joblib\n", "import sklearn\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 82, "id": "3920497f-02a7-494e-a805-318ff3dc844c", "metadata": {}, "outputs": [], "source": [ "# répertoire artefacts depuis notebooks/\n", "ARTIF_DIR = Path(\"../models\")\n", "ARTIF_DIR.mkdir(parents=True, exist_ok=True)" ] }, { "cell_type": "code", "execution_count": 83, "id": "e1d4dfcd-debb-432f-9fd8-7f3cbaeb3190", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['../models/model.joblib']" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# modèle\n", "joblib.dump(pipe_rf_reg, ARTIF_DIR / \"model.joblib\")" ] }, { "cell_type": "code", "execution_count": 84, "id": "e1112259-2298-4ac2-ade3-c101b93aa017", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Sauvegardé: ../models/model.joblib\n" ] } ], "source": [ "print(\" Sauvegardé: ../models/model.joblib\")" ] }, { "cell_type": "code", "execution_count": 85, "id": "7fc2e200-4eda-43e7-bf7e-fb13d1e38128", "metadata": {}, "outputs": [], "source": [ "# métriques \n", "y_pred_test = (proba_test_reg >= chosen_thr).astype(int)\n", "f1_t = f1_score(y_test, y_pred_test)\n", "roc_t = roc_auc_score(y_test, proba_test_reg)\n", "pr_t = average_precision_score(y_test, proba_test_reg)\n" ] }, { "cell_type": "markdown", "id": "77608b64-789f-4361-b59c-82fd54e53043", "metadata": {}, "source": [ "## SMOTE" ] }, { "cell_type": "code", "execution_count": null, "id": "96ebf184-fe73-4204-9d6b-078877817fac", "metadata": {}, "outputs": [], "source": [ "# Random Forest pour SMOTE (sans class_weight)\n", "rf_sm = RandomForestClassifier(\n", " n_estimators=200,\n", " class_weight=\"balanced\",\n", " max_depth=8, \n", " min_samples_leaf=5, \n", " min_samples_split=10, \n", " max_features=\"sqrt\",\n", " n_jobs=-1,\n", " random_state=42\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "b2d1387f-d018-4f5a-b7c3-5057cdf360e9", "metadata": {}, "outputs": [], "source": [ "# Pipeline : préprocessing -> SMOTE -> RF\n", "pipe_rf_sm = ImbPipeline(steps=[\n", " (\"prep\", preproc),\n", " (\"smote\", SMOTE(k_neighbors=5, random_state=42)),\n", " (\"clf\", rf_sm),\n", "])" ] }, { "cell_type": "code", "execution_count": null, "id": "915df103-0547-4e45-bf13-5a317668e202", "metadata": {}, "outputs": [], "source": [ "cv = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)" ] }, { "cell_type": "code", "execution_count": null, "id": "ab446272-460a-49c4-aa37-c4747bd48e44", "metadata": {}, "outputs": [], "source": [ "scores_ap = cross_val_score(\n", " pipe_rf_sm,\n", " X_train,\n", " y_train,\n", " cv=cv,\n", " scoring=\"average_precision\",\n", " n_jobs=-1\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "cab84661-2fd6-463d-8b9b-dc061d1cf5b3", "metadata": {}, "outputs": [], "source": [ "print(\n", " \"RF + SMOTE | PR AUC mean:\",\n", " scores_ap.mean().round(3),\n", " \"±\",\n", " scores_ap.std().round(3)\n", ")" ] }, { "cell_type": "markdown", "id": "8ada688d-f0be-47ef-990c-db9e6f6522f1", "metadata": {}, "source": [ "OOF + courbe PR pour le pipeline SMOTE" ] }, { "cell_type": "code", "execution_count": null, "id": "b1f5795c-7c92-408e-8f0c-56a3c060cb2e", "metadata": {}, "outputs": [], "source": [ "# Probabilités OOF (Out-Of-Fold) avec SMOTE + RF\n", "oof_proba_sm = cross_val_predict(\n", " pipe_rf_sm,\n", " X_train,\n", " y_train,\n", " cv=cv,\n", " method=\"predict_proba\",\n", " n_jobs=-1\n", ")[:, 1] " ] }, { "cell_type": "code", "execution_count": null, "id": "2746ae90-0230-4a09-b6dc-777642a2b410", "metadata": {}, "outputs": [], "source": [ "oof_true = y_train.values" ] }, { "cell_type": "code", "execution_count": null, "id": "491e4fae-4722-4b2f-8af2-5efd3b52a3b8", "metadata": {}, "outputs": [], "source": [ "# Courbe précision–rappel\n", "precisions_sm, recalls_sm, thresholds_sm = precision_recall_curve(oof_true, oof_proba_sm)\n", "pr_auc_oof_sm = average_precision_score(oof_true, oof_proba_sm)" ] }, { "cell_type": "code", "execution_count": null, "id": "962ff16c-4223-4f57-b27a-31ccb7fa60fa", "metadata": {}, "outputs": [], "source": [ "print(\"Average Precision (PR AUC) OOF RF+SMOTE =\", round(pr_auc_oof_sm, 3))" ] }, { "cell_type": "code", "execution_count": null, "id": "a88fcfd9-56b7-4e5d-ab52-6d9094e83389", "metadata": {}, "outputs": [], "source": [ "# Plot de la courbe PR\n", "plt.figure(figsize=(6, 5))\n", "plt.plot(recalls_sm, precisions_sm, label=f\"RF+SMOTE (AP={pr_auc_oof_sm:.3f})\")\n", "plt.xlabel(\"Recall\")\n", "plt.ylabel(\"Precision\")\n", "plt.title(\"Courbe précision–rappel – RF + SMOTE (OOF)\")\n", "plt.grid(alpha=0.4)\n", "plt.legend()\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "0e344998-af3e-41f0-9fcc-526b2ebc9f38", "metadata": {}, "source": [ "Recherche le meilleur seuil (F1 et F2)" ] }, { "cell_type": "code", "execution_count": null, "id": "4e1c8d8a-513a-4cad-9029-fa572af83d5e", "metadata": {}, "outputs": [], "source": [ "# Calcul F1 et F2 pour chaque point de la courbe PR\n", "beta2 = 2.0\n", "\n", "f1_scores_sm = 2 * (precisions_sm * recalls_sm) / (precisions_sm + recalls_sm + 1e-8)\n", "f2_scores_sm = (1 + beta2**2) * (precisions_sm * recalls_sm) / (beta2**2 * precisions_sm + recalls_sm + 1e-8)" ] }, { "cell_type": "code", "execution_count": null, "id": "d1e0a47e-7094-41bd-abbf-6d3b5627580d", "metadata": {}, "outputs": [], "source": [ "# precision_recall_curve retourne len(thresholds) = len(precisions) - 1\n", "best_idx_f1_sm = np.nanargmax(f1_scores_sm[:-1])\n", "best_thr_f1_sm = thresholds_sm[best_idx_f1_sm]\n", "\n", "best_idx_f2_sm = np.nanargmax(f2_scores_sm[:-1])\n", "best_thr_f2_sm = thresholds_sm[best_idx_f2_sm]" ] }, { "cell_type": "code", "execution_count": null, "id": "689304d0-e375-4946-ac76-01490798fc56", "metadata": {}, "outputs": [], "source": [ "print(f\"Seuil F1 optimal RF+SMOTE : {best_thr_f1_sm:.3f} | \"\n", " f\"P={precisions_sm[best_idx_f1_sm]:.3f} R={recalls_sm[best_idx_f1_sm]:.3f} F1={f1_scores_sm[best_idx_f1_sm]:.3f}\")\n", "print(f\"Seuil F2 optimal RF+SMOTE : {best_thr_f2_sm:.3f} | \"\n", " f\"P={precisions_sm[best_idx_f2_sm]:.3f} R={recalls_sm[best_idx_f2_sm]:.3f} F2={f2_scores_sm[best_idx_f2_sm]:.3f}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "250a080b-8cad-4383-b014-b42fa4b55df8", "metadata": {}, "outputs": [], "source": [ "# Visualiser les points F1/F2 sur la courbe\n", "plt.figure(figsize=(6, 5))\n", "plt.plot(recalls_sm, precisions_sm, label=f\"RF+SMOTE (AP={pr_auc_oof_sm:.3f})\")\n", "plt.scatter(recalls_sm[best_idx_f1_sm], precisions_sm[best_idx_f1_sm],\n", " label=f\"Best F1 (thr={best_thr_f1_sm:.2f})\", color=\"tab:orange\")\n", "plt.scatter(recalls_sm[best_idx_f2_sm], precisions_sm[best_idx_f2_sm],\n", " label=f\"Best F2 (thr={best_thr_f2_sm:.2f})\", color=\"tab:green\")\n", "plt.xlabel(\"Recall\")\n", "plt.ylabel(\"Precision\")\n", "plt.title(\"Courbe PR – RF+SMOTE avec seuils F1/F2\")\n", "plt.grid(alpha=0.4)\n", "plt.legend()\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "66e538b1-2c4c-449a-b15a-7df1c3dd1384", "metadata": {}, "outputs": [], "source": [ "chosen_thr_sm = best_thr_f2_sm " ] }, { "cell_type": "markdown", "id": "b64803b7-ab48-417d-addf-389cb17c7269", "metadata": {}, "source": [ "Fit final sur tout le TRAIN avec RF+SMOTE" ] }, { "cell_type": "code", "execution_count": null, "id": "43a11ffb-924a-4b5e-ad51-a3f0957db07f", "metadata": {}, "outputs": [], "source": [ "# Fit final du pipeline RF+SMOTE sur tout X_train\n", "pipe_rf_sm.fit(X_train, y_train)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "49f9ddf8-3e5d-40a2-84ee-67b99a9b2670", "metadata": {}, "outputs": [], "source": [ "# Probabilités sur TRAIN & TEST\n", "proba_train_sm = pipe_rf_sm.predict_proba(X_train)[:, 1]\n", "proba_test_sm = pipe_rf_sm.predict_proba(X_test)[:, 1]\n" ] }, { "cell_type": "code", "execution_count": null, "id": "b457cc52-224c-4e28-a8bf-f434191126d3", "metadata": {}, "outputs": [], "source": [ "# Évaluation TRAIN / TEST avec le même seuil\n", "scores_train_sm = eval_with_threshold(\n", " y_train, proba_train_sm,\n", " threshold=chosen_thr_sm,\n", " set_name=\"TRAIN – RF SMOTE (F2)\",\n", " plot_cm=True\n", ")\n", "\n", "scores_test_sm = eval_with_threshold(\n", " y_test, proba_test_sm,\n", " threshold=chosen_thr_sm,\n", " set_name=\"TEST – RF SMOTE (F2)\",\n", " plot_cm=True\n", ")" ] }, { "cell_type": "markdown", "id": "9beec6db-2def-4a5a-921d-734b6e346005", "metadata": {}, "source": [ "**Conclusion** \n", "“Avec SMOTE et un seuil calibré via la F2, nous captons environ 78 % des démissions, au prix d’un plus grand nombre d’alertes fausses, ce qui peut être acceptable si le coût d’une action préventive est faible.”\n", "____________________\n", "\n", "Nous avons testé une version RF + SMOTE.\n", "Les performances sont comparables / légèrement meilleures en rappel, mais pour l’interprétation et la mise en production, nous retenons le RF tuné sans SMOTE + seuil F2, plus simple et plus stable." ] }, { "cell_type": "code", "execution_count": null, "id": "c8bc518d-608d-4e1e-84fb-f24174378dde", "metadata": {}, "outputs": [], "source": [ "# Pour comparer : seuil standard 0.5\n", "\n", "scores_train_sm_05 = eval_with_threshold(\n", " y_train, proba_train_sm,\n", " threshold=0.5,\n", " set_name=\"TRAIN – RF+SMOTE seuil 0.5\",\n", " plot_cm=True\n", ")\n", "\n", "scores_test_sm_05 = eval_with_threshold(\n", " y_test, proba_test_sm,\n", " threshold=0.5,\n", " set_name=\"TEST – RF+SMOTE seuil 0.5\",\n", " plot_cm=True\n", ")" ] }, { "cell_type": "markdown", "id": "4859b00d-596a-4cba-890f-797c368eb716", "metadata": {}, "source": [ "**Conclusion** \n", "Nous privilégions le seuil optimisé (≈ 0.26, basé sur F2),\n", "car il donne un rappel beaucoup plus élevé (66 % vs 32 %) sur les “partants”, au prix d’une baisse modérée de l’accuracy et de la précision.\n", "C’est cohérent avec le métier : mieux vaut déclencher plus d’alertes que rater des démissions." ] }, { "cell_type": "markdown", "id": "3512a24f-6064-44c8-a70a-a4903b108236", "metadata": {}, "source": [ "# Optimisation du modèle" ] }, { "cell_type": "markdown", "id": "fb5961c8-2555-42c8-a385-9c45f6894586", "metadata": {}, "source": [ "### Fine-tuning Random Forest avec GridSearchCV\n", "\n", "Ici on fait GridSearch uniquement sur X_train / y_train, avec StratifiedKFold et un scoring adapté au déséquilibre (average_precision = AP = PR AUC)." ] }, { "cell_type": "code", "execution_count": null, "id": "7d469793-6c91-4c88-8e72-7e00c9fc11c2", "metadata": {}, "outputs": [], "source": [ "# Modèle de base RF (non encore tuné)\n", "rf_base = RandomForestClassifier(\n", " class_weight=\"balanced\",\n", " random_state=42\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "1312171f-60ad-4890-930e-5e2d0d8cdb3b", "metadata": {}, "outputs": [], "source": [ "# Pipeline : préprocessing + modèle\n", "pipe_rf = Pipeline(steps=[\n", " (\"prep\", preproc),\n", " (\"clf\", rf_base)\n", "])" ] }, { "cell_type": "code", "execution_count": null, "id": "ffd11b45-3998-4b45-a48f-59adfc766dec", "metadata": {}, "outputs": [], "source": [ "# Grille de recherche \n", "param_grid = {\n", " \"clf__n_estimators\": [50, 75, 100, 125, 150, 175],\n", " \"clf__max_depth\": [2, 5, 12, 15, 20],\n", " \"clf__min_samples_split\":[2,4,6,8,10],\n", " \"clf__min_samples_leaf\":[1, 2, 3, 5],\n", " \"clf__max_features\": [\"sqrt\", 0.5, 0.7],\n", "}" ] }, { "cell_type": "code", "execution_count": null, "id": "77bc9eb8-d218-4d2b-be60-0355102d0ef2", "metadata": {}, "outputs": [], "source": [ "cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)" ] }, { "cell_type": "code", "execution_count": null, "id": "c327073e-570b-4473-abef-c232ca22618c", "metadata": {}, "outputs": [], "source": [ "# GridSearchCV, on optimise la PR AUC\n", "rf_search = GridSearchCV(\n", " estimator=pipe_rf,\n", " param_grid=param_grid,\n", " scoring=\"average_precision\", \n", " cv=cv,\n", " n_jobs=-1,\n", " verbose=1,\n", " refit=True\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "38f5e8b9-2c53-48d5-87a5-ac3c849c4f0e", "metadata": {}, "outputs": [], "source": [ "# Fit sur TRAIN uniquement\n", "rf_search.fit(X_train, y_train)" ] }, { "cell_type": "code", "execution_count": null, "id": "ce3a127a-a371-4a74-af14-0e8d3256d223", "metadata": {}, "outputs": [], "source": [ "print(\"Meilleurs hyperparamètres RF :\")\n", "print(rf_search.best_params_)\n", "print(\"Best PR AUC (moyenne CV) :\", rf_search.best_score_)" ] }, { "cell_type": "code", "execution_count": null, "id": "199011a8-6980-4177-86fb-e08891d44028", "metadata": {}, "outputs": [], "source": [ "rf_tuned_pipe = rf_search.best_estimator_\n" ] }, { "cell_type": "code", "execution_count": null, "id": "1b6e2d78-7000-43e2-b875-d2dab73fcf2d", "metadata": {}, "outputs": [], "source": [ "# Probabilités OOF (Out-Of-Fold)\n", "oof_proba = cross_val_predict(\n", " rf_tuned_pipe,\n", " X_train,\n", " y_train,\n", " cv=cv,\n", " method=\"predict_proba\",\n", " n_jobs=-1\n", ")[:, 1] " ] }, { "cell_type": "code", "execution_count": null, "id": "01e8ca8b-a0e7-41d1-bd4b-d73f768b53ec", "metadata": {}, "outputs": [], "source": [ "oof_true = y_train.values" ] }, { "cell_type": "code", "execution_count": null, "id": "d19e3db1-5b0a-4211-af72-7afac7c8831c", "metadata": {}, "outputs": [], "source": [ "# Courbe précision–rappel\n", "precisions, recalls, thresholds = precision_recall_curve(oof_true, oof_proba)\n", "pr_auc_oof = average_precision_score(oof_true, oof_proba)" ] }, { "cell_type": "code", "execution_count": null, "id": "2160ca85-cf15-43e6-ba51-c7c00a07697c", "metadata": {}, "outputs": [], "source": [ "print(\"Average Precision (PR AUC) OOF RF tuné' =\", round(pr_auc_oof, 3))" ] }, { "cell_type": "code", "execution_count": null, "id": "c5b8c1d3-c941-4a64-b873-d4b8baee396c", "metadata": {}, "outputs": [], "source": [ "# Calcul F1 et F2 pour chaque point de la courbe PR\n", "beta2 = 2.0\n", "\n", "f1_scores = 2 * (precisions * recalls) / (precisions + recalls + 1e-8)\n", "f2_scores = (1 + beta2**2) * (precisions * recalls) / (beta2**2 * precisions + recalls + 1e-8)" ] }, { "cell_type": "code", "execution_count": null, "id": "7ab2f57a-1c1b-468e-bda3-651add95fb8f", "metadata": {}, "outputs": [], "source": [ "# precision_recall_curve retourne len(thresholds) = len(precisions) - 1\n", "best_idx_f1 = np.nanargmax(f1_scores[:-1])\n", "best_thr_f1 = thresholds[best_idx_f1]\n", "\n", "best_idx_f2 = np.nanargmax(f2_scores[:-1])\n", "best_thr_f2 = thresholds[best_idx_f2]" ] }, { "cell_type": "code", "execution_count": null, "id": "2811fce2-d96e-45da-b4d0-bb0f9c84444a", "metadata": {}, "outputs": [], "source": [ "print(f\"Seuil F1 optimal RF tuné : {best_thr_f1:.3f} | \"\n", " f\"P={precisions[best_idx_f1]:.3f} R={recalls[best_idx_f1]:.3f} F1={f1_scores[best_idx_f1]:.3f}\")\n", "print(f\"Seuil F2 optimal RF tuné : {best_thr_f2:.3f} | \"\n", " f\"P={precisions[best_idx_f2]:.3f} R={recalls[best_idx_f2]:.3f} F2={f2_scores[best_idx_f2]:.3f}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "ec87e70c-040e-4cc5-98cf-d958fab39c8c", "metadata": {}, "outputs": [], "source": [ "# Visualiser les points F1/F2 sur la courbe\n", "plt.figure(figsize=(6, 5))\n", "plt.plot(recalls, precisions, label=f\"RF tuné (AP={pr_auc_oof:.3f})\")\n", "plt.scatter(recalls[best_idx_f1], precisions[best_idx_f1],\n", " label=f\"Best F1 (thr={best_thr_f1:.2f})\", color=\"tab:orange\")\n", "plt.scatter(recalls[best_idx_f2], precisions[best_idx_f2],\n", " label=f\"Best F2 (thr={best_thr_f2:.2f})\", color=\"tab:green\")\n", "plt.xlabel(\"Recall\")\n", "plt.ylabel(\"Precision\")\n", "plt.title(\"Courbe PR – RF tuné avec seuils F1/F2\")\n", "plt.grid(alpha=0.4)\n", "plt.legend()\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "757760b3-34e1-4bc3-9907-3df25dd5d37d", "metadata": {}, "outputs": [], "source": [ "# On fige le seuil choisi (F2)\n", "chosen_thr = float(best_thr_f2) " ] }, { "cell_type": "code", "execution_count": null, "id": "079eac54-16f5-4659-8756-9d95f3d6c967", "metadata": {}, "outputs": [], "source": [ "# Fit final du pipeline RF tuné sur tout X_train\n", "rf_tuned_pipe.fit(X_train, y_train)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "422dcb88-66e7-4772-abd8-9002eac79069", "metadata": {}, "outputs": [], "source": [ "# Probabilités sur TRAIN et TEST\n", "proba_train_tuned = rf_tuned_pipe.predict_proba(X_train)[:, 1]\n", "proba_test_tuned = rf_tuned_pipe.predict_proba(X_test)[:, 1]" ] }, { "cell_type": "code", "execution_count": null, "id": "812b758e-1d50-45e2-93de-5063c31acb4f", "metadata": {}, "outputs": [], "source": [ "# Évaluation finale avec le seuil optimisé\n", "scores_train_rf_tuned_F2 = eval_with_threshold(\n", " y_train,\n", " proba_train_tuned,\n", " threshold=chosen_thr,\n", " set_name=\"TRAIN – RF tuné (seuil F2)\",\n", " plot_cm=True\n", ")\n", "\n", "scores_test_rf_tuned_F2 = eval_with_threshold(\n", " y_test,\n", " proba_test_tuned,\n", " threshold=chosen_thr,\n", " set_name=\"TEST – RF tuné (seuil F2)\",\n", " plot_cm=True\n", ")" ] }, { "cell_type": "markdown", "id": "e417cd44-858a-438a-9cd4-b15679183564", "metadata": {}, "source": [ "### Importance globale des features" ] }, { "cell_type": "markdown", "id": "c669cf88-b8eb-4cf1-a51a-90ef0e4205b9", "metadata": {}, "source": [ "### 1. Préparer les noms de variables alignés avec le RF" ] }, { "cell_type": "code", "execution_count": null, "id": "5b3adf26-94d3-4d0c-af96-b9ea3cc57fb5", "metadata": {}, "outputs": [], "source": [ "# Séparation préprocesseur et RF dans le pipeline régularisé\n", "prep_reg = pipe_rf_reg.named_steps[\"prep\"]\n", "rf_reg = pipe_rf_reg.named_steps[\"clf\"]" ] }, { "cell_type": "code", "execution_count": null, "id": "6f1c4d02-dc88-4e22-87c6-a4f2ddbdd917", "metadata": {}, "outputs": [], "source": [ "# Transformer X_train\n", "X_train_reg_preproc = prep_reg.transform(X_train)" ] }, { "cell_type": "code", "execution_count": null, "id": "631f63e7-4cae-4367-a265-e5f224b762b3", "metadata": {}, "outputs": [], "source": [ "# Liste de toutes les colonnes dans l'ordre d'entrée du modèle\n", "num_features = list(num_cols)\n", "other_features = [c for c in X_train.columns if c not in num_cols]\n", "feature_names = num_features + other_features" ] }, { "cell_type": "code", "execution_count": null, "id": "c0d419df-659d-4741-bd7d-b1ea4f6fd8d6", "metadata": {}, "outputs": [], "source": [ "X_train_reg_df = pd.DataFrame(\n", " X_train_reg_preproc,\n", " columns=feature_names,\n", " index=X_train.index\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "5753504b-f942-49d7-a3af-22a3018a653f", "metadata": {}, "outputs": [], "source": [ "print(X_train_reg_df.shape)" ] }, { "cell_type": "markdown", "id": "fbb6b2db-d584-4263-b2a6-3df7ac72ba80", "metadata": {}, "source": [ "### 2. Importance native du Random Forest" ] }, { "cell_type": "code", "execution_count": null, "id": "4f6b0352-bf3b-4d46-b347-2bdca43f7722", "metadata": {}, "outputs": [], "source": [ "rf_reg.fit(X_train_reg_df, y_train)" ] }, { "cell_type": "code", "execution_count": null, "id": "080c9794-37a8-4595-a74f-f46eec5d4448", "metadata": {}, "outputs": [], "source": [ "print(\"n_features_in_ :\", rf_reg.n_features_in_) " ] }, { "cell_type": "code", "execution_count": null, "id": "4df6d4e1-0f7e-450a-a3c1-0e91331ac424", "metadata": {}, "outputs": [], "source": [ "rf_importances = rf_reg.feature_importances_" ] }, { "cell_type": "code", "execution_count": null, "id": "e9903fa5-2e1b-4d70-a603-19acc3194ec3", "metadata": {}, "outputs": [], "source": [ "df_rf_importance = (\n", " pd.DataFrame({\n", " \"feature\": feature_names,\n", " \"importance\": rf_importances\n", " })\n", " .sort_values(\"importance\", ascending=False)\n", ")\n", "\n", "plt.figure(figsize=(8,6))\n", "top = df_rf_importance.head(15)\n", "plt.barh(top[\"feature\"][::-1], top[\"importance\"][::-1])\n", "plt.xlabel(\"Importance (RF native)\")\n", "plt.title(\"Importance globale – RF régularisé\")\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "724ab2ca-fc3a-40e1-a150-c4f5a28c39b7", "metadata": {}, "source": [ "_____________\n", "\n", "On voit que dans ce modèle RF, les variables les plus déterminantes sont par exemple revenu_mensuel, age, heure_supplementaires, satisfaction_globale, etc.\n", "\n", "C’est une importance globale, sans direction : on ne sait pas encore si un haut revenu augmente ou diminue le risque, juste que le modèle l’utilise beaucoup.\n", "\n", "_________\n" ] }, { "cell_type": "markdown", "id": "791be325-93b2-4128-b012-363d8eefe36d", "metadata": {}, "source": [ "### 3. Permutation Importance (sur TEST)" ] }, { "cell_type": "code", "execution_count": null, "id": "c7db3711-f882-4591-a63a-6f69f5c2dc2f", "metadata": {}, "outputs": [], "source": [ "import warnings\n", "\n", "warnings.filterwarnings(\n", " \"ignore\",\n", " message=\"X does not have valid feature names, but RandomForestClassifier was fitted with feature names\"\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "ddb1fda8-e9b7-44b4-8d4e-a9599516f2ad", "metadata": {}, "outputs": [], "source": [ "result = permutation_importance(\n", " pipe_rf_reg,\n", " X_test,\n", " y_test,\n", " n_repeats=20,\n", " scoring=\"average_precision\", \n", " n_jobs=-1,\n", " random_state=42\n", ")\n", "\n", "df_perm = (\n", " pd.DataFrame({\n", " \"feature\": feature_names,\n", " \"importance_mean\": result.importances_mean,\n", " \"importance_std\": result.importances_std\n", " })\n", " .sort_values(\"importance_mean\", ascending=False)\n", ")\n", "\n", "plt.figure(figsize=(8,6))\n", "top = df_perm.head(15)\n", "plt.barh(top[\"feature\"][::-1], top[\"importance_mean\"][::-1], xerr=top[\"importance_std\"][::-1])\n", "plt.xlabel(\"Permutation importance (PR AUC)\")\n", "plt.title(\"Importance globale – permutation (TEST)\")\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "a7b6383a-216f-4a03-852d-e60f276ffc3b", "metadata": {}, "source": [ "______\n", "\n", "`nombre_participation_pee` est très important : le modèle perd beaucoup de performance quand on le perturbe.\n", "\n", "D’autres variables ont une importance plus faible ou proche de 0 → elles n’apportent presque rien au modèle.\n", "\n", "______" ] }, { "cell_type": "markdown", "id": "8862f642-6a74-4d16-9713-146e9499cc4c", "metadata": {}, "source": [ "### SHAP – importance globale (beeswarm + bar)" ] }, { "cell_type": "code", "execution_count": null, "id": "89e13f37-6a9d-406e-9f8c-54b51bbbac26", "metadata": {}, "outputs": [], "source": [ "# Explainer SHAP (TreeExplainer sur RF)\n", "explainer = shap.TreeExplainer(rf_reg)" ] }, { "cell_type": "code", "execution_count": null, "id": "bcda4bc3-03bc-431a-afa0-c3c673786e61", "metadata": {}, "outputs": [], "source": [ "# SHAP values sur TOUT X_train_reg_df\n", "shap_values = explainer.shap_values(X_train_reg_df) # liste [classe 0, classe 1]" ] }, { "cell_type": "code", "execution_count": null, "id": "c6fafed6-702a-42f9-8986-79b13f2c9487", "metadata": {}, "outputs": [], "source": [ "# Vérif des shapes\n", "print(\"len(shap_values) :\", len(shap_values))\n", "print(\"shap_values[0].shape :\", shap_values[0].shape)\n", "print(\"shap_values[1].shape :\", shap_values[1].shape)\n", "print(\"X_train_reg_df.shape :\", X_train_reg_df.shape)" ] }, { "cell_type": "code", "execution_count": null, "id": "2836adba-acc5-4a1b-bbe8-f2fd3eb0209b", "metadata": {}, "outputs": [], "source": [ "print(\"type(shap_values):\", type(shap_values))\n", "print(\"shap_values.shape:\", np.array(shap_values).shape)" ] }, { "cell_type": "code", "execution_count": null, "id": "97527c87-b907-4e28-9934-3fed627ed69a", "metadata": {}, "outputs": [], "source": [ "shap_class1 = shap_values[:, :, 1] # (1102, 40)\n", "\n", "print(shap_class1.shape) \n", "print(X_train_reg_df.shape) " ] }, { "cell_type": "code", "execution_count": null, "id": "3191aa41-59b2-4f98-beaa-aa6320153d7d", "metadata": {}, "outputs": [], "source": [ "# Beeswarm : vue globale, signe + couleur = sens de l'effet\n", "shap.summary_plot(\n", " shap_class1,\n", " X_train_reg_df,\n", " feature_names=feature_names\n", ")" ] }, { "cell_type": "markdown", "id": "1d739cd9-6c70-4c77-92d7-249f251f4124", "metadata": {}, "source": [ "_____\n", "\n", "`heure_supplementaires` :\n", "\n", "beaucoup de points rouges à droite → des heures sup élevées augmentent le risque.\n", "\n", "les bleus sont plutôt à gauche → peu d’heures sup le diminuent.\n", "\n", "`satisfaction_globale` :\n", "\n", "rouges (satisfaction élevée) à gauche → haute satisfaction protège.\n", "\n", "bleus (faible satisfaction) à droite → faible satisfaction augmente le risque\n", "\n", "___" ] }, { "cell_type": "markdown", "id": "f978a06b-2d38-4f6d-af84-6e524aaabdfa", "metadata": {}, "source": [ "### SHAP local (waterfall) sur les données complètes" ] }, { "cell_type": "markdown", "id": "fcd50cd6-6bca-4b87-8f13-2438dd5a0f40", "metadata": {}, "source": [ "SHAP – importance locale (waterfall plot)\n", "\n", "On regarde par exemple :\n", "\n", "1 employé parti (y=1),\n", "\n", "1 employé resté (y=0)." ] }, { "cell_type": "markdown", "id": "0109b5ae-8103-414e-bdb9-61bc6161b4f8", "metadata": {}, "source": [ "Interprétation (en langage métier) :\n", "\n", "La base value = probabilité moyenne de départ.\n", "\n", "Les barres rouges = variables qui augmentent le risque de départ pour cet individu.\n", "\n", "Les barres bleues = variables qui réduisent le risque (facteurs protecteurs)." ] }, { "cell_type": "markdown", "id": "8d7ed609-318c-46e9-873b-cbf65bc8fc53", "metadata": {}, "source": [ "Exemple pour un individu de la classe 1 (client attrition) :" ] }, { "cell_type": "code", "execution_count": null, "id": "e3dcbb2b-8ea6-4c50-aa53-faf897861a4e", "metadata": {}, "outputs": [], "source": [ "# index d'un client en attrition\n", "idx_1 = y_train[y_train == 1].index[0]\n", "\n", "# récupérer sa ligne et ses shap values\n", "x_i = X_train_reg_df.loc[idx_1, :]\n", "shap_i = shap_class1[X_train_reg_df.index.get_loc(idx_1), :]\n", "\n", "shap.plots._waterfall.waterfall_legacy(\n", " explainer.expected_value[1], # base value pour la classe 1\n", " shap_i,\n", " feature_names=feature_names\n", ")" ] }, { "cell_type": "markdown", "id": "e25f642e-42df-41fd-b930-77c6e047afa9", "metadata": {}, "source": [ "______\n", "\n", "`heure_supplementaires, exp_moins_3_years, age, revenu_mensuel` poussent tous la proba vers la droite → ce sont les raisons principales pour lesquelles cet individu a un risque élevé.\n", "\n", "`poste_Assistant de Direction, 31 other features` en bleu tirent un peu la proba vers le bas (facteurs protecteurs), mais pas assez pour compenser.\n", "\n", "____\n" ] }, { "cell_type": "markdown", "id": "d405d129-bd4d-44d0-8b92-0dd13aa5fa6c", "metadata": {}, "source": [ "Example pour un individu de la classe 0" ] }, { "cell_type": "code", "execution_count": null, "id": "a1a5e330-2a99-40eb-a468-e8a0cd6eff47", "metadata": {}, "outputs": [], "source": [ "idx_0 = y_train[y_train == 0].index[0]\n", "\n", "x_i0 = X_train_reg_df.loc[idx_0, :]\n", "shap_i0 = shap_class1[X_train_reg_df.index.get_loc(idx_0), :]\n", "\n", "shap.plots._waterfall.waterfall_legacy(\n", " explainer.expected_value[1],\n", " shap_i0,\n", " feature_names=feature_names\n", ")" ] }, { "cell_type": "markdown", "id": "c2bd924e-668a-43a4-a33d-d1d08afcf69f", "metadata": {}, "source": [ "_____\n", "\n", "`heure_supplementaires, revenu_mensuel, annees_sous_responsable_actuel` réduisent fortement la probabilité, d’où un risque faible.\n", "\n", "`satisfaction_globale, nombre_partiicpation_pee, statut_marital_célebataire` remontent un peu la proba, mais pas assez pour dépasser 0.5\n", "\n", "__" ] }, { "cell_type": "markdown", "id": "ac33d577-3b45-4d50-ae5f-c87ae8f35fab", "metadata": {}, "source": [ "# Conclusion et plan d’action\n", "\n", "À mesure que l’entreprise accumulera de nouvelles données sur ses collaborateurs (nouveaux arrivants, départs récents, évolutions de carrière), l’algorithme pourra être régulièrement réentraîné. L’enrichissement progressif de la base de données permettra, en théorie, d’améliorer la précision des prédictions et d’identifier plus finement les employés à haut risque de départ, sur la base de la probabilité estimée pour chaque individu.\n", "\n", "Sur cette probabilité, chaque employé peut se voir attribuer **une note de risque de départ** (**faible, moyen, élevé**). Cette segmentation permet de prioriser les actions de fidélisation :\n", "\n", "**Risque faible** : suivi classique et actions RH générales (communication, engagement, qualité de vie au travail).\n", "\n", "**Risque moyen** : mise en place d’actions préventives ciblées (ajustement de la charge de travail, perspectives de développement, reconnaissance).\n", "\n", "**Risque élevé** : actions rapides et personnalisées, incluant des entretiens individuels avec un représentant RH et, si nécessaire, une rencontre avec le supérieur hiérarchique afin de discuter des conditions de travail et d’identifier des mesures correctives.\n", "\n", "Les analyses montrent que certains facteurs jouent un rôle particulièrement important dans la décision de quitter l’entreprise :\n", "\n", "**Revenu mensuel** : les salariés mieux rémunérés sont moins susceptibles de partir. Il est donc essentiel de vérifier régulièrement la compétitivité des salaires par rapport au marché local.\n", "\n", "**Heures supplémentaires** : un volume élevé d’heures supplémentaires augmente le risque de départ. Une meilleure planification des projets, un dimensionnement adéquat des équipes et un pilotage de la charge de travail sont nécessaires pour limiter ce phénomène.\n", "\n", "**Âge** : les employés jeunes (environ 25–35 ans) présentent un risque de départ plus élevé. Il convient de clarifier la vision à long terme de l’entreprise, de montrer les perspectives d’évolution et de proposer des parcours de carrière attractifs.\n", "\n", "**Distance domicile–travail** : une distance importante accroît le risque de départ. Des solutions telles que le transport collectif, une indemnité de déplacement ou, lorsque c’est possible, des modalités de travail flexible peuvent être envisagées, sans pour autant discriminer sur le lieu de résidence.\n", "\n", "**Années totales d’expérience** : les collaborateurs plus expérimentés sont globalement plus stables. Ceux ayant entre 5 et 8 ans d’expérience doivent toutefois être suivis avec attention, car ils peuvent être plus sollicités par le marché.\n", "\n", " **Ancienneté dans l’entreprise** : la fidélité à l’entreprise réduit le risque de départ, mais un point de vigilance apparaît autour des deux premières années, période critique où un accompagnement renforcé est recommandé.\n", "\n", "**Années avec le responsable actuel** : un pic de démissions survient environ six mois après un changement de manager. Le suivi des équipes récemment réorganisées, ainsi que l’identification des responsables ayant connu de nombreux départs sur la dernière année, permet d’agir sur la qualité du management et de l’environnement de travail.\n", "\n", "En combinant ces indicateurs avec la note de risque fournie par le modèle, l’entreprise peut structurer un plan stratégique de fidélisation plus ciblé, proactif et mesurable. L’objectif n’est pas seulement de prédire les départs, mais surtout de créer les conditions d’un engagement durable : politiques de rémunération compétitives, gestion raisonnée de la charge de travail, perspectives de carrière claires, soutien adapté aux contraintes personnelles (transport, équilibre vie pro/vie perso) et amélioration continue des pratiques managériales." ] }, { "cell_type": "code", "execution_count": null, "id": "d637c1f6-53ba-477d-aa4e-feb051db0b33", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "97c7cfaa-23f7-442f-a0a6-aa14cd17475c", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Data science (Classification automatique d'informations)", "language": "python", "name": "classification_informations" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.5" } }, "nbformat": 4, "nbformat_minor": 5 }