CoolFace
Apppublic

FlorianSC/agritech-interface

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
notebook_modelisation.ipynb1747 linesDownload Raw Back to notebooks
1{2 "cells": [3  {4   "cell_type": "markdown",5   "id": "e049226f",6   "metadata": {},7   "source": [8    "# Système de recommandation agricole - Modélisation"9   ]10  },11  {12   "cell_type": "markdown",13   "id": "d11e1ec2",14   "metadata": {},15   "source": [16    "## Librairies nécessaires"17   ]18  },19  {20   "cell_type": "code",21   "execution_count": 1,22   "id": "19e6e556",23   "metadata": {},24   "outputs": [25    {26     "name": "stderr",27     "output_type": "stream",28     "text": [29      "/Users/florianschorer/Library/Caches/pypoetry/virtualenvs/systeme-recommandation-agricole-5e-JMIdX-py3.12/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",30      "  from .autonotebook import tqdm as notebook_tqdm\n"31     ]32    }33   ],34   "source": [35    "# Import de base\n",36    "import pandas as pd\n",37    "import numpy as np\n",38    "import sys,os, joblib, shap\n",39    "import matplotlib.pyplot as plt\n",40    "import logging\n",41    "sys.path.append(os.path.abspath(\"..\"))\n",42    "\n",43    "# Import scikit learn\n",44    "from sklearn.model_selection import (train_test_split, KFold, cross_validate, GridSearchCV)\n",45    "from sklearn.compose import ColumnTransformer\n",46    "from sklearn.pipeline import Pipeline\n",47    "from sklearn.preprocessing import StandardScaler\n",48    "from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, mean_absolute_percentage_error\n",49    "from sklearn.linear_model import LinearRegression\n",50    "from sklearn.ensemble import RandomForestRegressor\n",51    "from sklearn.dummy import DummyRegressor\n",52    "\n",53    "# Import des autres modèles testés\n",54    "import xgboost as xgb\n",55    "import lightgbm as lgb\n",56    "import warnings\n",57    "warnings.filterwarnings(\"ignore\", message=\"X does not have valid feature names\")\n",58    "warnings.filterwarnings(\"ignore\", message=\"Hint: Inferred schema contains integer\")\n",59    "warnings.filterwarnings(\"ignore\", message=\"Saving scikit-learn models\")\n",60    "\n",61    "# Import mlflow\n",62    "import mlflow\n",63    "mlflow.set_tracking_uri(\"file://\" + os.path.abspath(\"../mlruns\"))\n",64    "from mlflow.tracking import MlflowClient\n",65    "from mlflow.models.signature import infer_signature\n",66    "# Warning pickle/sklearn de MLflow\n",67    "logging.getLogger(\"mlflow.sklearn\").setLevel(logging.ERROR)\n",68    "# Import du projet\n",69    "from scripts.preprocessing_pipeline import (\n",70    "    separation_X_y,\n",71    "    preparation_pipeline,\n",72    "    cross_validation,\n",73    "    train_predict\n",74    ")\n",75    "from scripts.config import (csv_yield_conso, csv_yield_enriched)"76   ]77  },78  {79   "cell_type": "markdown",80   "id": "d6134633",81   "metadata": {},82   "source": [83    "# Comparaison modèle sans optimisation sur le fichier consolidé"84   ]85  },86  {87   "cell_type": "code",88   "execution_count": 20,89   "id": "5b8cc3b8",90   "metadata": {},91   "outputs": [],92   "source": [93    "def mlflow_tracking_model(model, model_name, tags, projet_description):\n",94    "    # =====================\n",95    "    # Configuration MLflow\n",96    "    mlflow.set_tracking_uri(\"http://127.0.0.1:5000\")\n",97    "    mlflow.set_experiment(\"Agritech_Answers\")\n",98    "\n",99    "    reg_name = \"Yield_Forecaster_Global\"\n",100    "    mlflow.sklearn.autolog(log_models=False, log_datasets=False, silent=True)\n",101    "\n",102    "    # =======================\n",103    "    # Chargement des données\n",104    "    # Dataset préparé mais non encodé\n",105    "    df = pd.read_csv(csv_yield_conso)\n",106    "\n",107    "    with mlflow.start_run(run_name=model_name, tags={\n",108    "        \"Training Info\": tags,\n",109    "        \"Algorithm\": model.__class__.__name__,\n",110    "        \"mlflow.note.content\": projet_description\n",111    "    }) as run:\n",112    "\n",113    "        X_train, X_test, y_train, y_test, categorical_cols, numeric_cols = separation_X_y(df)\n",114    "\n",115    "        # Pipeline & Cross-Validation\n",116    "        pipeline = preparation_pipeline(\n",117    "            numeric_cols=numeric_cols,\n",118    "            categorical_cols=categorical_cols,\n",119    "            model=model\n",120    "        )\n",121    "        cv_results = cross_validation(pipeline=pipeline, X_train=X_train, y_train=y_train)\n",122    "\n",123    "        # On divise par 10 pour avoir les informations en kg (actuellement en hg)\n",124    "        cv_metrics = {\n",125    "            \"cv_rmse_mean_kg\": np.sqrt(-cv_results[\"test_mse\"]).mean() / 10,\n",126    "            \"cv_rmse_std_kg\": np.sqrt(-cv_results[\"test_mse\"]).std() / 10,\n",127    "            \"cv_mae_mean_kg\": (-cv_results[\"test_mae\"]).mean() / 10,\n",128    "            \"cv_mae_std_kg\": (-cv_results[\"test_mae\"]).std() / 10,\n",129    "            \"cv_mape_mean\": (-cv_results[\"test_mape\"]).mean(),\n",130    "            \"cv_mape_std\": (-cv_results[\"test_mape\"]).std(),\n",131    "            \"cv_r2_mean\": cv_results[\"test_r2\"].mean(),\n",132    "            \"cv_r2_std\": cv_results[\"test_r2\"].std()\n",133    "            }\n",134    "        mlflow.log_metrics(cv_metrics)\n",135    "\n",136    "        # Entraînement final\n",137    "        pipeline.fit(X_train, y_train)\n",138    "\n",139    "        # Prédictions test\n",140    "        y_pred = pipeline.predict(X_test)\n",141    "\n",142    "        # Prix moyen au global tiré d'un fichier officiel - FAO\n",143    "        prices_fao = {\n",144    "            \"cassava\": 270, \"maize\": 260, \"plantains_and_others\": 480, \"potatoes\": 330,\n",145    "            \"rice\": 360, \"sorghum\": 230, \"soybean\": 400, \"sweet_potatoes\": 420,\n",146    "            \"wheat\": 200, \"yams\": 890, \"barley\": 220\n",147    "        }\n",148    "        # Construction d'un dataframe pour y extraire ligne à ligne par la suite\n",149    "        results_df = pd.DataFrame({\n",150    "            \"actual\": y_test,\n",151    "            \"pred\": y_pred,\n",152    "            \"abs_error\": np.abs(y_test - y_pred),\n",153    "        }, index=X_test.index)\n",154    "\n",155    "        # On récupère directement la culture brute\n",156    "        results_df[\"crop\"] = (\n",157    "            X_test.loc[results_df.index, \"item\"]\n",158    "            .astype(str)\n",159    "            .str.strip()\n",160    "            .str.lower()\n",161    "        )\n",162    "        # Calcul du R2 par item\n",163    "        r2_by_item = results_df.groupby(\"crop\").apply(\n",164    "            lambda g: r2_score(g[\"actual\"], g[\"pred\"]) if len(g) > 1 else np.nan, include_groups=False\n",165    "            ).dropna()\n",166    "        \n",167    "        # Calcul du MAPE par item\n",168    "        mape_by_item = results_df.groupby(\"crop\").apply(\n",169    "            lambda g: mean_absolute_percentage_error(g[\"actual\"], g[\"pred\"]) if len(g) > 1 else np.nan, include_groups=False\n",170    "            ).dropna()        \n",171    "        # Enregistrement des métriques\n",172    "        mlflow.log_metrics({f\"test_r2_{crop}\": float(r2)for crop, r2 in r2_by_item.items()})\n",173    "        mlflow.log_metrics({f\"test_mape_{crop}\": float(mape)for crop, mape in mape_by_item.items()})\n",174    "\n",175    "        # Calcul du coût économique\n",176    "        def calculate_monetary_error(row):\n",177    "            price = prices_fao.get(row[\"crop\"], 250)\n",178    "            return (row[\"abs_error\"] / 10000) * price\n",179    "\n",180    "        results_df[\"error_cost_usd_ha\"] = results_df.apply(calculate_monetary_error, axis=1)\n",181    "        # Par crop\n",182    "        economic_error_by_crop = (results_df.groupby(\"crop\")[\"error_cost_usd_ha\"].mean().sort_values())\n",183    "\n",184    "        mlflow.log_metrics({f\"test_economic_error_usd_ha_{crop}\": float(cost)\n",185    "                            for crop, cost in economic_error_by_crop.items()\n",186    "                            })\n",187    "        # Au total\n",188    "        mean_economic_error = results_df[\"error_cost_usd_ha\"].mean()\n",189    "\n",190    "        rmse_hg = np.sqrt(mean_squared_error(y_test, y_pred))\n",191    "        mae_hg = mean_absolute_error(y_test, y_pred)\n",192    "        test_metrics = {\n",193    "            \"test_rmse_kg\": rmse_hg / 10,\n",194    "            \"test_mae_kg\": mae_hg / 10,\n",195    "            \"test_mape\": mean_absolute_percentage_error(y_test, y_pred),\n",196    "            \"test_r2\": r2_score(y_test, y_pred),\n",197    "            \"economic_error_usd_ha\": mean_economic_error\n",198    "        }\n",199    "        mlflow.log_metrics(test_metrics)\n",200    "\n",201    "        signature = infer_signature(X_test, y_pred)\n",202    "        model_info = mlflow.sklearn.log_model(\n",203    "            sk_model=pipeline,\n",204    "            name=\"model\",\n",205    "            signature=signature,\n",206    "            registered_model_name=reg_name\n",207    "        )\n",208    "\n",209    "        client = MlflowClient()\n",210    "        mv = model_info.registered_model_version\n",211    "\n",212    "        full_description = (\n",213    "            f\"**Modèle :** {model_name}\\n\"\n",214    "            f\"**Note :** {projet_description}\\n\\n\"\n",215    "            f\"**Scores CV (Moyenne ± Écart-type) :**\\n\"\n",216    "            f\"- CV R2: {cv_metrics['cv_r2_mean']:.4f} (± {cv_metrics['cv_r2_std']:.4f})\\n\"\n",217    "            f\"- CV RMSE: {cv_metrics['cv_rmse_mean_kg']:.2f} (± {cv_metrics['cv_rmse_std_kg']:.2f})\\n\\n\"\n",218    "            f\"**Scores Test :**\\n\"\n",219    "            f\"- Test R2: {test_metrics['test_r2']:.4f}\\n\"\n",220    "            f\"- Test economic_error_usd_ha: {test_metrics['economic_error_usd_ha']:.4f}\\n\"\n",221    "        )\n",222    "\n",223    "        client.update_model_version(name=reg_name, version=mv, description=full_description)\n",224    "        client.set_model_version_tag(reg_name, mv, \"Algo\", model.__class__.__name__)\n",225    "        client.set_model_version_tag(reg_name, mv, \"CV_R2\", round(cv_metrics[\"cv_r2_mean\"], 4))\n",226    "        client.set_model_version_tag(reg_name, mv, \"CV_Std\", round(cv_metrics[\"cv_r2_std\"], 4))\n",227    "        \n",228    "\n",229    "        print(f\"\\nVersion {mv} enregistrée.\")\n",230    "        print(\"\\n=== Résultats métriques ===\")\n",231    "        print(f\"CV RMSE : {cv_metrics['cv_rmse_mean_kg']:.4f} (± {cv_metrics['cv_rmse_std_kg']:.2f})\")\n",232    "        print(f\"CV MAPE  : {cv_metrics['cv_mape_mean']:.4f} (± {cv_metrics['cv_mape_std']:.4f})\")\n",233    "        print(f\"CV R2   : {cv_metrics['cv_r2_mean']:.4f} (± {cv_metrics['cv_r2_std']:.4f})\")\n",234    "        print(f\"Test RMSE : {test_metrics['test_rmse_kg']:.4f}\")\n",235    "        print(f\"Test R2   : {test_metrics['test_r2']:.4f}\")\n",236    "        print(f\"Test MAPE  : {test_metrics['test_mape']:.4f}\")\n",237    "        print(f\"Test economic_error_usd_ha : {test_metrics['economic_error_usd_ha']:.4f}\\n\")"238   ]239  },240  {241   "cell_type": "code",242   "execution_count": 13,243   "id": "0705928c",244   "metadata": {},245   "outputs": [246    {247     "name": "stderr",248     "output_type": "stream",249     "text": [250      "2026-04-12 08:49:59,181 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",251      "2026-04-12 08:49:59,182 - INFO - Colonnes catégorielles : ['region', 'item', 'is_drought']\n",252      "Successfully registered model 'Yield_Forecaster_Global'.\n",253      "2026/04/12 08:50:03 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 1\n"254     ]255    },256    {257     "name": "stdout",258     "output_type": "stream",259     "text": [260      "\n",261      "Version 1 enregistrée.\n",262      "\n",263      "=== Résultats métriques ===\n",264      "CV RMSE : 7604.3730 (± 187.80)\n",265      "CV MAPE  : 2.6730 (± 0.1885)\n",266      "CV R2   : -0.0002 (± 0.0002)\n",267      "Test RMSE : 7216.6343\n",268      "Test R2   : -0.0013\n",269      "Test MAPE  : 2.4448\n",270      "Test economic_error_usd_ha : 1680.7060\n",271      "\n",272      "🏃 View run DummyRegressor - Baseline at: http://127.0.0.1:5000/#/experiments/5/runs/9c590a69132549ad912658af83cb30c4\n",273      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"274     ]275    },276    {277     "name": "stderr",278     "output_type": "stream",279     "text": [280      "Created version '1' of model 'Yield_Forecaster_Global'.\n"281     ]282    }283   ],284   "source": [285    "# DummyRegressor\n",286    "model = DummyRegressor()\n",287    "model_name = \"DummyRegressor - Baseline\"\n",288    "tags = \"DummyRegressor - Baseline\"\n",289    "projet_description = \"Test d'un modèle DummyRegressor pour avoir une base sur laquelle comparer\"\n",290    "mlflow_tracking_model(model, model_name, tags, projet_description)"291   ]292  },293  {294   "cell_type": "code",295   "execution_count": 14,296   "id": "ebe96f7f",297   "metadata": {},298   "outputs": [299    {300     "name": "stderr",301     "output_type": "stream",302     "text": [303      "2026-04-12 08:50:39,180 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",304      "2026-04-12 08:50:39,181 - INFO - Colonnes catégorielles : ['region', 'item', 'is_drought']\n",305      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",306      "2026/04/12 08:50:44 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 2\n"307     ]308    },309    {310     "name": "stdout",311     "output_type": "stream",312     "text": [313      "\n",314      "Version 2 enregistrée.\n",315      "\n",316      "=== Résultats métriques ===\n",317      "CV RMSE : 4840.4290 (± 55.16)\n",318      "CV MAPE  : 0.9546 (± 0.0388)\n",319      "CV R2   : 0.5943 (± 0.0143)\n",320      "Test RMSE : 4805.9227\n",321      "Test R2   : 0.5559\n",322      "Test MAPE  : 0.9859\n",323      "Test economic_error_usd_ha : 1033.3629\n",324      "\n",325      "🏃 View run LinearRegression - Baseline at: http://127.0.0.1:5000/#/experiments/5/runs/871688843f8347a08019ae537790c430\n",326      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"327     ]328    },329    {330     "name": "stderr",331     "output_type": "stream",332     "text": [333      "Created version '2' of model 'Yield_Forecaster_Global'.\n"334     ]335    }336   ],337   "source": [338    "# Modèle de régression linéaire\n",339    "model = LinearRegression()\n",340    "model_name = \"LinearRegression - Baseline\"\n",341    "tags = \"LinearRegression - Baseline\"\n",342    "projet_description = \"Test d'un modèle LinearRegression sans optimisation\"\n",343    "mlflow_tracking_model(model, model_name, tags, projet_description)\n"344   ]345  },346  {347   "cell_type": "code",348   "execution_count": 15,349   "id": "6f7dd30f",350   "metadata": {},351   "outputs": [352    {353     "name": "stderr",354     "output_type": "stream",355     "text": [356      "2026-04-12 08:50:53,242 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",357      "2026-04-12 08:50:53,242 - INFO - Colonnes catégorielles : ['region', 'item', 'is_drought']\n",358      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",359      "2026/04/12 08:53:12 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 3\n",360      "Created version '3' of model 'Yield_Forecaster_Global'.\n"361     ]362    },363    {364     "name": "stdout",365     "output_type": "stream",366     "text": [367      "\n",368      "Version 3 enregistrée.\n",369      "\n",370      "=== Résultats métriques ===\n",371      "CV RMSE : 1893.0226 (± 45.34)\n",372      "CV MAPE  : 0.2091 (± 0.0098)\n",373      "CV R2   : 0.9378 (± 0.0051)\n",374      "Test RMSE : 1729.6763\n",375      "Test R2   : 0.9425\n",376      "Test MAPE  : 0.1739\n",377      "Test economic_error_usd_ha : 273.6834\n",378      "\n",379      "🏃 View run RandomForest - Baseline at: http://127.0.0.1:5000/#/experiments/5/runs/1c3f33cbdb7a486995cbb441297fd81e\n",380      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"381     ]382    }383   ],384   "source": [385    "# Modèle de RandomForest\n",386    "model = RandomForestRegressor(random_state=42)\n",387    "model_name = \"RandomForest - Baseline\"\n",388    "tags = \"RandomForest - Baseline\"\n",389    "projet_description = \"Test d'un modèle Random Forest sans optimisation\"\n",390    "mlflow_tracking_model(model, model_name, tags, projet_description)\n"391   ]392  },393  {394   "cell_type": "code",395   "execution_count": 16,396   "id": "c486a9a3",397   "metadata": {},398   "outputs": [399    {400     "name": "stderr",401     "output_type": "stream",402     "text": [403      "2026-04-12 08:53:58,395 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",404      "2026-04-12 08:53:58,396 - INFO - Colonnes catégorielles : ['region', 'item', 'is_drought']\n",405      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",406      "2026/04/12 08:54:04 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 4\n"407     ]408    },409    {410     "name": "stdout",411     "output_type": "stream",412     "text": [413      "\n",414      "Version 4 enregistrée.\n",415      "\n",416      "=== Résultats métriques ===\n",417      "CV RMSE : 2152.0037 (± 71.11)\n",418      "CV MAPE  : 0.3938 (± 0.0186)\n",419      "CV R2   : 0.9195 (± 0.0082)\n",420      "Test RMSE : 2040.8158\n",421      "Test R2   : 0.9199\n",422      "Test MAPE  : 0.3603\n",423      "Test economic_error_usd_ha : 406.6547\n",424      "\n",425      "🏃 View run XGBRegressor - Baseline at: http://127.0.0.1:5000/#/experiments/5/runs/4d8e7f0cc79d4d7cbbe9685a5a1ccd3a\n",426      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"427     ]428    },429    {430     "name": "stderr",431     "output_type": "stream",432     "text": [433      "Created version '4' of model 'Yield_Forecaster_Global'.\n"434     ]435    }436   ],437   "source": [438    "# Modèle de XGBoost\n",439    "model = xgb.XGBRegressor(random_state=42)\n",440    "model_name = \"XGBRegressor - Baseline\"\n",441    "tags = \"XGBRegressor - Baseline\"\n",442    "projet_description = \"Test d'un modèle XGBRegressor sans optimisation\"\n",443    "mlflow_tracking_model(model, model_name, tags, projet_description)\n"444   ]445  },446  {447   "cell_type": "code",448   "execution_count": 17,449   "id": "26fbf12c",450   "metadata": {},451   "outputs": [452    {453     "name": "stderr",454     "output_type": "stream",455     "text": [456      "2026-04-12 08:54:10,602 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",457      "2026-04-12 08:54:10,602 - INFO - Colonnes catégorielles : ['region', 'item', 'is_drought']\n",458      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",459      "2026/04/12 08:54:19 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 5\n"460     ]461    },462    {463     "name": "stdout",464     "output_type": "stream",465     "text": [466      "\n",467      "Version 5 enregistrée.\n",468      "\n",469      "=== Résultats métriques ===\n",470      "CV RMSE : 2689.7368 (± 50.00)\n",471      "CV MAPE  : 0.5493 (± 0.0383)\n",472      "CV R2   : 0.8745 (± 0.0095)\n",473      "Test RMSE : 2679.5389\n",474      "Test R2   : 0.8620\n",475      "Test MAPE  : 0.5107\n",476      "Test economic_error_usd_ha : 551.5749\n",477      "\n",478      "🏃 View run LGBMRegressor - Baseline at: http://127.0.0.1:5000/#/experiments/5/runs/b29dcd3b524e4853a8ae6db900a8327e\n",479      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"480     ]481    },482    {483     "name": "stderr",484     "output_type": "stream",485     "text": [486      "Created version '5' of model 'Yield_Forecaster_Global'.\n"487     ]488    }489   ],490   "source": [491    "# Modèle de LightGBM\n",492    "model = lgb.LGBMRegressor(random_state=42, verbose=-1)\n",493    "model_name = \"LGBMRegressor - Baseline\"\n",494    "tags = \"LGBMRegressor - Baseline\"\n",495    "projet_description = \"Test d'un modèle LGBMRegressor sans optimisation\"\n",496    "mlflow_tracking_model(model, model_name, tags, projet_description)\n"497   ]498  },499  {500   "cell_type": "markdown",501   "id": "9180d9a8",502   "metadata": {},503   "source": [504    "- Sur ces premiers tests, le modèle qui s'en sort le mieux sur l'ensemble des métriques (performance et métier) c'est celui de Random Forest.\n",505    "- Les résultats de XGBoost et LightGBM sont exploitables pour essayer d'améliorer leur performance.\n",506    "- Par contre le modèle de régression linéaire est trop loin en terme de performance. Au vu de la distribution de notre varibale cible, cela n'est pas étonnant."507   ]508  },509  {510   "cell_type": "markdown",511   "id": "fd6c92b7",512   "metadata": {},513   "source": [514    "# Comparaison modèle sans optimisation sur le fichier enrichi"515   ]516  },517  {518   "cell_type": "code",519   "execution_count": 2,520   "id": "3304ea12",521   "metadata": {},522   "outputs": [],523   "source": [524    "def mlflow_tracking_model(model, model_name, tags, projet_description):\n",525    "    # =====================\n",526    "    # Configuration MLflow\n",527    "    mlflow.set_tracking_uri(\"http://127.0.0.1:5000\")\n",528    "    mlflow.set_experiment(\"Agritech_Answers\")\n",529    "\n",530    "    reg_name = \"Yield_Forecaster_Global\"\n",531    "    mlflow.sklearn.autolog(log_models=False, log_datasets=False, silent=True)\n",532    "\n",533    "    # =======================\n",534    "    # Chargement des données\n",535    "    # Dataset préparé mais non encodé\n",536    "    df = pd.read_csv(csv_yield_enriched)\n",537    "\n",538    "    with mlflow.start_run(run_name=model_name, tags={\n",539    "        \"Training Info\": tags,\n",540    "        \"Algorithm\": model.__class__.__name__,\n",541    "        \"mlflow.note.content\": projet_description\n",542    "    }) as run:\n",543    "\n",544    "        X_train, X_test, y_train, y_test, categorical_cols, numeric_cols = separation_X_y(df)\n",545    "\n",546    "        # Pipeline & Cross-Validation\n",547    "        pipeline = preparation_pipeline(\n",548    "            numeric_cols=numeric_cols,\n",549    "            categorical_cols=categorical_cols,\n",550    "            model=model\n",551    "        )\n",552    "        cv_results = cross_validation(pipeline=pipeline, X_train=X_train, y_train=y_train)\n",553    "\n",554    "        # On divise par 10 pour avoir les informations en kg (actuellement en hg)\n",555    "        cv_metrics = {\n",556    "            \"cv_rmse_mean_kg\": np.sqrt(-cv_results[\"test_mse\"]).mean() / 10,\n",557    "            \"cv_rmse_std_kg\": np.sqrt(-cv_results[\"test_mse\"]).std() / 10,\n",558    "            \"cv_mae_mean_kg\": (-cv_results[\"test_mae\"]).mean() / 10,\n",559    "            \"cv_mae_std_kg\": (-cv_results[\"test_mae\"]).std() / 10,\n",560    "            \"cv_mape_mean\": (-cv_results[\"test_mape\"]).mean(),\n",561    "            \"cv_mape_std\": (-cv_results[\"test_mape\"]).std(),\n",562    "            \"cv_r2_mean\": cv_results[\"test_r2\"].mean(),\n",563    "            \"cv_r2_std\": cv_results[\"test_r2\"].std()\n",564    "            }\n",565    "        mlflow.log_metrics(cv_metrics)\n",566    "\n",567    "        # Entraînement final\n",568    "        pipeline.fit(X_train, y_train)\n",569    "\n",570    "        # Prédictions test\n",571    "        y_pred = pipeline.predict(X_test)\n",572    "\n",573    "        # Prix moyen au global tiré d'un fichier officiel - FAO\n",574    "        prices_fao = {\n",575    "            \"cassava\": 270, \"maize\": 260, \"plantains_and_others\": 480, \"potatoes\": 330,\n",576    "            \"rice\": 360, \"sorghum\": 230, \"soybean\": 400, \"sweet_potatoes\": 420,\n",577    "            \"wheat\": 200, \"yams\": 890, \"barley\": 220\n",578    "        }\n",579    "        # Construction d'un dataframe pour y extraire ligne à ligne par la suite\n",580    "        results_df = pd.DataFrame({\n",581    "            \"actual\": y_test,\n",582    "            \"pred\": y_pred,\n",583    "            \"abs_error\": np.abs(y_test - y_pred),\n",584    "        }, index=X_test.index)\n",585    "\n",586    "        # On récupère directement la culture brute\n",587    "        results_df[\"crop\"] = (\n",588    "            X_test.loc[results_df.index, \"item\"]\n",589    "            .astype(str)\n",590    "            .str.strip()\n",591    "            .str.lower()\n",592    "        )\n",593    "        # Calcul du R2 par item\n",594    "        r2_by_item = results_df.groupby(\"crop\").apply(\n",595    "            lambda g: r2_score(g[\"actual\"], g[\"pred\"]) if len(g) > 1 else np.nan, include_groups=False\n",596    "            ).dropna()\n",597    "        \n",598    "        # Calcul du MAPE par item\n",599    "        mape_by_item = results_df.groupby(\"crop\").apply(\n",600    "            lambda g: mean_absolute_percentage_error(g[\"actual\"], g[\"pred\"]) if len(g) > 1 else np.nan, include_groups=False\n",601    "            ).dropna()        \n",602    "        # Enregistrement des métriques\n",603    "        mlflow.log_metrics({f\"test_r2_{crop}\": float(r2)for crop, r2 in r2_by_item.items()})\n",604    "        mlflow.log_metrics({f\"test_mape_{crop}\": float(mape)for crop, mape in mape_by_item.items()})\n",605    "\n",606    "        # Calcul du coût économique\n",607    "        def calculate_monetary_error(row):\n",608    "            price = prices_fao.get(row[\"crop\"], 250)\n",609    "            return (row[\"abs_error\"] / 10000) * price\n",610    "\n",611    "        results_df[\"error_cost_usd_ha\"] = results_df.apply(calculate_monetary_error, axis=1)\n",612    "        # Par crop\n",613    "        economic_error_by_crop = (results_df.groupby(\"crop\")[\"error_cost_usd_ha\"].mean().sort_values())\n",614    "\n",615    "        mlflow.log_metrics({f\"test_economic_error_usd_ha_{crop}\": float(cost)\n",616    "                            for crop, cost in economic_error_by_crop.items()\n",617    "                            })\n",618    "        # Au total\n",619    "        mean_economic_error = results_df[\"error_cost_usd_ha\"].mean()\n",620    "\n",621    "        rmse_hg = np.sqrt(mean_squared_error(y_test, y_pred))\n",622    "        mae_hg = mean_absolute_error(y_test, y_pred)\n",623    "        test_metrics = {\n",624    "            \"test_rmse_kg\": rmse_hg / 10,\n",625    "            \"test_mae_kg\": mae_hg / 10,\n",626    "            \"test_mape\": mean_absolute_percentage_error(y_test, y_pred),\n",627    "            \"test_r2\": r2_score(y_test, y_pred),\n",628    "            \"economic_error_usd_ha\": mean_economic_error\n",629    "        }\n",630    "        mlflow.log_metrics(test_metrics)\n",631    "\n",632    "        signature = infer_signature(X_test, y_pred)\n",633    "        model_info = mlflow.sklearn.log_model(\n",634    "            sk_model=pipeline,\n",635    "            name=\"model\",\n",636    "            signature=signature,\n",637    "            registered_model_name=reg_name\n",638    "        )\n",639    "\n",640    "        client = MlflowClient()\n",641    "        mv = model_info.registered_model_version\n",642    "\n",643    "        full_description = (\n",644    "            f\"**Modèle :** {model_name}\\n\"\n",645    "            f\"**Note :** {projet_description}\\n\\n\"\n",646    "            f\"**Scores CV (Moyenne ± Écart-type) :**\\n\"\n",647    "            f\"- CV R2: {cv_metrics['cv_r2_mean']:.4f} (± {cv_metrics['cv_r2_std']:.4f})\\n\"\n",648    "            f\"- CV RMSE: {cv_metrics['cv_rmse_mean_kg']:.2f} (± {cv_metrics['cv_rmse_std_kg']:.2f})\\n\\n\"\n",649    "            f\"**Scores Test :**\\n\"\n",650    "            f\"- Test R2: {test_metrics['test_r2']:.4f}\\n\"\n",651    "            f\"- Test economic_error_usd_ha: {test_metrics['economic_error_usd_ha']:.4f}\\n\"\n",652    "        )\n",653    "\n",654    "        client.update_model_version(name=reg_name, version=mv, description=full_description)\n",655    "        client.set_model_version_tag(reg_name, mv, \"Algo\", model.__class__.__name__)\n",656    "        client.set_model_version_tag(reg_name, mv, \"CV_R2\", round(cv_metrics[\"cv_r2_mean\"], 4))\n",657    "        client.set_model_version_tag(reg_name, mv, \"CV_Std\", round(cv_metrics[\"cv_r2_std\"], 4))\n",658    "        \n",659    "\n",660    "        print(f\"\\nVersion {mv} enregistrée.\")\n",661    "        print(\"\\n=== Résultats métriques ===\")\n",662    "        print(f\"CV RMSE : {cv_metrics['cv_rmse_mean_kg']:.4f} (± {cv_metrics['cv_rmse_std_kg']:.2f})\")\n",663    "        print(f\"CV MAPE  : {cv_metrics['cv_mape_mean']:.4f} (± {cv_metrics['cv_mape_std']:.4f})\")\n",664    "        print(f\"CV R2   : {cv_metrics['cv_r2_mean']:.4f} (± {cv_metrics['cv_r2_std']:.4f})\")\n",665    "        print(f\"Test RMSE : {test_metrics['test_rmse_kg']:.4f}\")\n",666    "        print(f\"Test R2   : {test_metrics['test_r2']:.4f}\")\n",667    "        print(f\"Test MAPE  : {test_metrics['test_mape']:.4f}\")\n",668    "        print(f\"Test economic_error_usd_ha : {test_metrics['economic_error_usd_ha']:.4f}\\n\")"669   ]670  },671  {672   "cell_type": "code",673   "execution_count": 3,674   "id": "007ff704",675   "metadata": {},676   "outputs": [677    {678     "name": "stderr",679     "output_type": "stream",680     "text": [681      "2026-04-12 09:01:09,645 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",682      "2026-04-12 09:01:09,646 - INFO - Colonnes catégorielles : ['region', 'item', 'fertilizer_used', 'irrigation_used', 'weather_condition', 'soil_type', 'is_drought']\n",683      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",684      "2026/04/12 09:01:16 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 6\n"685     ]686    },687    {688     "name": "stdout",689     "output_type": "stream",690     "text": [691      "\n",692      "Version 6 enregistrée.\n",693      "\n",694      "=== Résultats métriques ===\n",695      "CV RMSE : 7604.3730 (± 187.80)\n",696      "CV MAPE  : 2.6730 (± 0.1885)\n",697      "CV R2   : -0.0002 (± 0.0002)\n",698      "Test RMSE : 7216.6343\n",699      "Test R2   : -0.0013\n",700      "Test MAPE  : 2.4448\n",701      "Test economic_error_usd_ha : 1680.7060\n",702      "\n",703      "🏃 View run DummyRegressor - Baseline - fichier enrichi at: http://127.0.0.1:5000/#/experiments/5/runs/f15a2b7071754947abb27685d1282e0e\n",704      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"705     ]706    },707    {708     "name": "stderr",709     "output_type": "stream",710     "text": [711      "Created version '6' of model 'Yield_Forecaster_Global'.\n"712     ]713    }714   ],715   "source": [716    "# DummyRegressor\n",717    "model = DummyRegressor()\n",718    "model_name = \"DummyRegressor - Baseline - fichier enrichi\"\n",719    "tags = \"DummyRegressor - Baseline - fichier enrichi\"\n",720    "projet_description = \"Test d'un modèle DummyRegressor pour avoir une base sur laquelle comparer\"\n",721    "mlflow_tracking_model(model, model_name, tags, projet_description)"722   ]723  },724  {725   "cell_type": "code",726   "execution_count": 4,727   "id": "f5b2bbd0",728   "metadata": {},729   "outputs": [730    {731     "name": "stderr",732     "output_type": "stream",733     "text": [734      "2026-04-12 09:01:24,808 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",735      "2026-04-12 09:01:24,808 - INFO - Colonnes catégorielles : ['region', 'item', 'fertilizer_used', 'irrigation_used', 'weather_condition', 'soil_type', 'is_drought']\n",736      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",737      "2026/04/12 09:01:30 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 7\n"738     ]739    },740    {741     "name": "stdout",742     "output_type": "stream",743     "text": [744      "\n",745      "Version 7 enregistrée.\n",746      "\n",747      "=== Résultats métriques ===\n",748      "CV RMSE : 4841.7110 (± 54.37)\n",749      "CV MAPE  : 0.9555 (± 0.0386)\n",750      "CV R2   : 0.5941 (± 0.0143)\n",751      "Test RMSE : 4807.8225\n",752      "Test R2   : 0.5556\n",753      "Test MAPE  : 0.9849\n",754      "Test economic_error_usd_ha : 1034.2202\n",755      "\n",756      "🏃 View run LinearRegression - Baseline - fichier enrichi at: http://127.0.0.1:5000/#/experiments/5/runs/5704e47afdd842e58333e141750f025d\n",757      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"758     ]759    },760    {761     "name": "stderr",762     "output_type": "stream",763     "text": [764      "Created version '7' of model 'Yield_Forecaster_Global'.\n"765     ]766    }767   ],768   "source": [769    "# Modèle de régression linéaire\n",770    "model = LinearRegression()\n",771    "model_name = \"LinearRegression - Baseline - fichier enrichi\"\n",772    "tags = \"LinearRegression - Baseline - fichier enrichi\"\n",773    "projet_description = \"Test d'un modèle LinearRegression sans optimisation\"\n",774    "mlflow_tracking_model(model, model_name, tags, projet_description)\n"775   ]776  },777  {778   "cell_type": "code",779   "execution_count": 5,780   "id": "5f4160e5",781   "metadata": {},782   "outputs": [783    {784     "name": "stderr",785     "output_type": "stream",786     "text": [787      "2026-04-12 09:01:37,118 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",788      "2026-04-12 09:01:37,119 - INFO - Colonnes catégorielles : ['region', 'item', 'fertilizer_used', 'irrigation_used', 'weather_condition', 'soil_type', 'is_drought']\n",789      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",790      "2026/04/12 09:05:12 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 8\n"791     ]792    },793    {794     "name": "stdout",795     "output_type": "stream",796     "text": [797      "\n",798      "Version 8 enregistrée.\n",799      "\n",800      "=== Résultats métriques ===\n",801      "CV RMSE : 1926.1649 (± 66.57)\n",802      "CV MAPE  : 0.2276 (± 0.0102)\n",803      "CV R2   : 0.9355 (± 0.0065)\n",804      "Test RMSE : 1748.9721\n",805      "Test R2   : 0.9412\n",806      "Test MAPE  : 0.1982\n",807      "Test economic_error_usd_ha : 291.8239\n",808      "\n",809      "🏃 View run RandomForest - Baseline - fichier enrichi at: http://127.0.0.1:5000/#/experiments/5/runs/a071b5bdea9345c9b04d4ad003464fbc\n",810      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"811     ]812    },813    {814     "name": "stderr",815     "output_type": "stream",816     "text": [817      "Created version '8' of model 'Yield_Forecaster_Global'.\n"818     ]819    }820   ],821   "source": [822    "# Modèle de RandomForest\n",823    "model = RandomForestRegressor(random_state=42)\n",824    "model_name = \"RandomForest - Baseline - fichier enrichi\"\n",825    "tags = \"RandomForest - Baseline\"\n",826    "projet_description = \"Test d'un modèle Random Forest sans optimisation\"\n",827    "mlflow_tracking_model(model, model_name, tags, projet_description)\n"828   ]829  },830  {831   "cell_type": "code",832   "execution_count": 6,833   "id": "493e55f7",834   "metadata": {},835   "outputs": [836    {837     "name": "stderr",838     "output_type": "stream",839     "text": [840      "2026-04-12 09:06:35,255 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",841      "2026-04-12 09:06:35,255 - INFO - Colonnes catégorielles : ['region', 'item', 'fertilizer_used', 'irrigation_used', 'weather_condition', 'soil_type', 'is_drought']\n",842      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",843      "2026/04/12 09:06:39 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 9\n"844     ]845    },846    {847     "name": "stdout",848     "output_type": "stream",849     "text": [850      "\n",851      "Version 9 enregistrée.\n",852      "\n",853      "=== Résultats métriques ===\n",854      "CV RMSE : 2179.0641 (± 44.71)\n",855      "CV MAPE  : 0.3995 (± 0.0220)\n",856      "CV R2   : 0.9175 (± 0.0072)\n",857      "Test RMSE : 2017.5085\n",858      "Test R2   : 0.9217\n",859      "Test MAPE  : 0.3757\n",860      "Test economic_error_usd_ha : 413.5231\n",861      "\n",862      "🏃 View run XGBRegressor - Baseline - fichier enrichi at: http://127.0.0.1:5000/#/experiments/5/runs/d89fdce99f9b43ae87ad023c2c8ba077\n",863      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"864     ]865    },866    {867     "name": "stderr",868     "output_type": "stream",869     "text": [870      "Created version '9' of model 'Yield_Forecaster_Global'.\n"871     ]872    }873   ],874   "source": [875    "# Modèle de XGBoost\n",876    "model = xgb.XGBRegressor(random_state=42)\n",877    "model_name = \"XGBRegressor - Baseline - fichier enrichi\"\n",878    "tags = \"XGBRegressor - Baseline - fichier enrichi\"\n",879    "projet_description = \"Test d'un modèle XGBRegressor sans optimisation\"\n",880    "mlflow_tracking_model(model, model_name, tags, projet_description)"881   ]882  },883  {884   "cell_type": "code",885   "execution_count": 7,886   "id": "0c5606eb",887   "metadata": {},888   "outputs": [889    {890     "name": "stderr",891     "output_type": "stream",892     "text": [893      "2026-04-12 09:06:43,463 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",894      "2026-04-12 09:06:43,464 - INFO - Colonnes catégorielles : ['region', 'item', 'fertilizer_used', 'irrigation_used', 'weather_condition', 'soil_type', 'is_drought']\n",895      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",896      "2026/04/12 09:06:52 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 10\n"897     ]898    },899    {900     "name": "stdout",901     "output_type": "stream",902     "text": [903      "\n",904      "Version 10 enregistrée.\n",905      "\n",906      "=== Résultats métriques ===\n",907      "CV RMSE : 2709.1046 (± 52.11)\n",908      "CV MAPE  : 0.5443 (± 0.0350)\n",909      "CV R2   : 0.8726 (± 0.0101)\n",910      "Test RMSE : 2677.7113\n",911      "Test R2   : 0.8621\n",912      "Test MAPE  : 0.5128\n",913      "Test economic_error_usd_ha : 553.9541\n",914      "\n",915      "🏃 View run LGBMRegressor - Baseline - fichier enrichi at: http://127.0.0.1:5000/#/experiments/5/runs/a6066be51e0c4f49b5d7a6478e5b9514\n",916      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n"917     ]918    },919    {920     "name": "stderr",921     "output_type": "stream",922     "text": [923      "Created version '10' of model 'Yield_Forecaster_Global'.\n"924     ]925    }926   ],927   "source": [928    "# Modèle de LightGBM\n",929    "model = lgb.LGBMRegressor(random_state=42, verbose=-1)\n",930    "model_name = \"LGBMRegressor - Baseline - fichier enrichi\"\n",931    "tags = \"LGBMRegressor - Baselzine - fichier enrichi\"\n",932    "projet_description = \"Test d'un modèle LGBMRegressor sans optimisation\"\n",933    "mlflow_tracking_model(model, model_name, tags, projet_description)"934   ]935  },936  {937   "cell_type": "markdown",938   "id": "5b1bfb2d",939   "metadata": {},940   "source": [941    "# Optimisation des hyperparamètres sur RandomForest"942   ]943  },944  {945   "cell_type": "markdown",946   "id": "62c70c95",947   "metadata": {},948   "source": [949    "A l'aide de gridsearchCV, on va essayer ici de trouver les meilleurs hyperparamètres pour chacun des modèles"950   ]951  },952  {953   "cell_type": "code",954   "execution_count": 2,955   "id": "f49d391c",956   "metadata": {},957   "outputs": [],958   "source": [959    "def mlflow_tracking_gridsearch(model, model_name, param_grid, tags, projet_description):\n",960    "    # =====================\n",961    "    # Configuration MLflow\n",962    "    mlflow.set_tracking_uri(\"http://127.0.0.1:5000\")\n",963    "    mlflow.set_experiment(\"Agritech_Answers\")\n",964    "\n",965    "    reg_name = \"Yield_Forecaster_Global\"\n",966    "    mlflow.sklearn.autolog(log_models=False, log_datasets=False, silent=True, max_tuning_runs=0)\n",967    "\n",968    "    # =======================\n",969    "    # Chargement des données\n",970    "    df = pd.read_csv(csv_yield_conso)\n",971    "\n",972    "    with mlflow.start_run(run_name=model_name, tags={\n",973    "        \"Training Info\": tags,\n",974    "        \"Algorithm\": model.__class__.__name__,\n",975    "        \"mlflow.note.content\": projet_description\n",976    "    }) as run:\n",977    "\n",978    "        mlflow.set_tag(\"target_raw_unit\", \"hg/ha\")\n",979    "        mlflow.set_tag(\"logged_metric_unit\", \"kg/ha\")\n",980    "        mlflow.set_tag(\"economic_metric_unit\", \"usd/ha\")\n",981    "\n",982    "        X_train, X_test, y_train, y_test, categorical_cols, numeric_cols = separation_X_y(df)\n",983    "\n",984    "        # Pipeline & GridSearch\n",985    "        pipeline = preparation_pipeline(\n",986    "            numeric_cols=numeric_cols,\n",987    "            categorical_cols=categorical_cols,\n",988    "            model=model\n",989    "        )\n",990    "\n",991    "        cv = KFold(n_splits=5, shuffle=True, random_state=42)\n",992    "\n",993    "        grid_search = GridSearchCV(\n",994    "            estimator=pipeline,\n",995    "            param_grid=param_grid,\n",996    "            scoring=\"r2\",\n",997    "            n_jobs=-1,\n",998    "            cv=cv,\n",999    "            refit=True,\n",1000    "            error_score=\"raise\"\n",1001    "        )\n",1002    "\n",1003    "        grid_search.fit(X_train, y_train)\n",1004    "\n",1005    "        best_index = grid_search.best_index_\n",1006    "\n",1007    "        cv_metrics = {\n",1008    "            \"best_cv_r2_mean\": grid_search.best_score_,\n",1009    "            \"best_cv_r2_std\": grid_search.cv_results_[\"std_test_score\"][best_index]\n",1010    "        }\n",1011    "\n",1012    "        mlflow.log_params(grid_search.best_params_)\n",1013    "        mlflow.log_metrics(cv_metrics)\n",1014    "\n",1015    "        # Entraînement final déjà fait par refit=True\n",1016    "        best_pipeline = grid_search.best_estimator_\n",1017    "\n",1018    "        # Prédictions test\n",1019    "        y_pred = best_pipeline.predict(X_test)\n",1020    "\n",1021    "               # Prix moyen au global tiré d'un fichier officiel - FAO\n",1022    "        prices_fao = {\n",1023    "            \"cassava\": 270, \"maize\": 260, \"plantains_and_others\": 480, \"potatoes\": 330,\n",1024    "            \"rice\": 360, \"sorghum\": 230, \"soybean\": 400, \"sweet_potatoes\": 420,\n",1025    "            \"wheat\": 200, \"yams\": 890, \"barley\": 220\n",1026    "        }\n",1027    "        # Construction d'un dataframe pour y extraire ligne à ligne par la suite\n",1028    "        results_df = pd.DataFrame({\n",1029    "            \"actual\": y_test,\n",1030    "            \"pred\": y_pred,\n",1031    "            \"abs_error\": np.abs(y_test - y_pred),\n",1032    "        }, index=X_test.index)\n",1033    "\n",1034    "        # On récupère directement la culture brute\n",1035    "        results_df[\"crop\"] = (\n",1036    "            X_test.loc[results_df.index, \"item\"]\n",1037    "            .astype(str)\n",1038    "            .str.strip()\n",1039    "            .str.lower()\n",1040    "        )\n",1041    "        # Calcul du R2 par item\n",1042    "        r2_by_item = results_df.groupby(\"crop\").apply(\n",1043    "            lambda g: r2_score(g[\"actual\"], g[\"pred\"]) if len(g) > 1 else np.nan, include_groups=False\n",1044    "            ).dropna()\n",1045    "        \n",1046    "        # Calcul du MAPE par item\n",1047    "        mape_by_item = results_df.groupby(\"crop\").apply(\n",1048    "            lambda g: mean_absolute_percentage_error(g[\"actual\"], g[\"pred\"]) if len(g) > 1 else np.nan, include_groups=False\n",1049    "            ).dropna()        \n",1050    "        # Enregistrement des métriques\n",1051    "        mlflow.log_metrics({f\"test_r2_{crop}\": float(r2)for crop, r2 in r2_by_item.items()})\n",1052    "        mlflow.log_metrics({f\"test_mape_{crop}\": float(mape)for crop, mape in mape_by_item.items()})\n",1053    "\n",1054    "        # Calcul du coût économique\n",1055    "        def calculate_monetary_error(row):\n",1056    "            price = prices_fao.get(row[\"crop\"], 250)\n",1057    "            return (row[\"abs_error\"] / 10000) * price\n",1058    "\n",1059    "        results_df[\"error_cost_usd_ha\"] = results_df.apply(calculate_monetary_error, axis=1)\n",1060    "        # Par crop\n",1061    "        economic_error_by_crop = (results_df.groupby(\"crop\")[\"error_cost_usd_ha\"].mean().sort_values())\n",1062    "\n",1063    "        mlflow.log_metrics({f\"test_economic_error_usd_ha_{crop}\": float(cost)\n",1064    "                            for crop, cost in economic_error_by_crop.items()\n",1065    "                            })\n",1066    "        # Au total\n",1067    "        mean_economic_error = results_df[\"error_cost_usd_ha\"].mean()\n",1068    "\n",1069    "        rmse_hg = np.sqrt(mean_squared_error(y_test, y_pred))\n",1070    "        mae_hg = mean_absolute_error(y_test, y_pred)\n",1071    "        test_metrics = {\n",1072    "            \"test_rmse_kg\": rmse_hg / 10,\n",1073    "            \"test_mae_kg\": mae_hg / 10,\n",1074    "            \"test_mape\": mean_absolute_percentage_error(y_test, y_pred),\n",1075    "            \"test_r2\": r2_score(y_test, y_pred),\n",1076    "            \"economic_error_usd_ha\": mean_economic_error\n",1077    "        }\n",1078    "        mlflow.log_metrics(test_metrics)\n",1079    "\n",1080    "        signature = infer_signature(X_test, y_pred)\n",1081    "\n",1082    "        model_info = mlflow.sklearn.log_model(\n",1083    "            sk_model=best_pipeline,\n",1084    "            name=\"model\",\n",1085    "            signature=signature,\n",1086    "            registered_model_name=reg_name\n",1087    "        )\n",1088    "\n",1089    "        client = MlflowClient()\n",1090    "        mv = model_info.registered_model_version\n",1091    "\n",1092    "        full_description = (\n",1093    "            f\"**Modèle Optimisé :** {model_name}\\n\"\n",1094    "            f\"**Note :** {projet_description}\\n\\n\"\n",1095    "            f\"**Scores CV :**\\n\"\n",1096    "            f\"- Best CV R2: {cv_metrics['best_cv_r2_mean']:.4f} \"\n",1097    "            f\"(± {cv_metrics['best_cv_r2_std']:.4f})\\n\\n\"\n",1098    "            f\"**Scores Test :**\\n\"\n",1099    "            f\"- Test R2: {test_metrics['test_r2']:.4f}\\n\"\n",1100    "            f\"- Test RMSE (kg/ha): {test_metrics['test_rmse_kg']:.2f}\\n\"\n",1101    "            f\"- Test MAE (kg/ha): {test_metrics['test_mae_kg']:.2f}\\n\"\n",1102    "            f\"- Test MAPE: {test_metrics['test_mape']:.2%}\\n\"\n",1103    "            f\"- Test economic_error_usd_ha: {test_metrics['economic_error_usd_ha']:.4f}\\n\"\n",1104    "        )\n",1105    "\n",1106    "        client.update_model_version(name=reg_name, version=mv, description=full_description)\n",1107    "        client.set_model_version_tag(reg_name, mv, \"Algo\", model.__class__.__name__)\n",1108    "        client.set_model_version_tag(reg_name, mv, \"Best_CV_R2\", round(cv_metrics[\"best_cv_r2_mean\"], 4))\n",1109    "        client.set_model_version_tag(reg_name, mv, \"Best_CV_Std\", round(cv_metrics[\"best_cv_r2_std\"], 4))\n",1110    "        client.set_model_version_tag(reg_name, mv, \"Test_RMSE_kg_ha\", round(test_metrics[\"test_rmse_kg\"], 2))\n",1111    "        client.set_model_version_tag(reg_name, mv, \"Test_MAPE\", round(test_metrics[\"test_mape\"],2))\n",1112    "\n",1113    "        print(f\"\\nVersion {mv} enregistrée.\")\n",1114    "        print(\"\\n=== Résultats métriques ===\")\n",1115    "        print(f\"Best CV R2         : {cv_metrics['best_cv_r2_mean']:.4f} (± {cv_metrics['best_cv_r2_std']:.4f})\")\n",1116    "        print(f\"Test RMSE (kg/ha)  : {test_metrics['test_rmse_kg']:.4f}\")\n",1117    "        print(f\"Test R2            : {test_metrics['test_r2']:.4f}\")\n",1118    "        print(f\"Test MAPE          : {test_metrics['test_mape']:.2%}\")\n",1119    "        print(f\"Test economic_error_usd_ha : {test_metrics['economic_error_usd_ha']:.4f}\\n\")\n",1120    "\n",1121    "        return {\n",1122    "            \"best_pipeline\": best_pipeline,\n",1123    "            \"best_params\": grid_search.best_params_,\n",1124    "            \"test_metrics\": test_metrics,\n",1125    "            \"X_test\": X_test,\n",1126    "            \"y_test\": y_test\n",1127    "        }"1128   ]1129  },1130  {1131   "cell_type": "code",1132   "execution_count": null,1133   "id": "596e1c31",1134   "metadata": {},1135   "outputs": [1136    {1137     "name": "stderr",1138     "output_type": "stream",1139     "text": [1140      "2026-04-12 09:54:25,072 - INFO - Colonnes numériques : ['avg_temp', 'rainfall_mm', 'pesticides_tonnes', 'input_imbalance', 'thermal_stress', 'years_from_now']\n",1141      "2026-04-12 09:54:25,073 - INFO - Colonnes catégorielles : ['region', 'item', 'is_drought']\n",1142      "Registered model 'Yield_Forecaster_Global' already exists. Creating a new version of this model...\n",1143      "2026/04/12 09:57:30 INFO mlflow.store.model_registry.abstract_store: Waiting up to 300 seconds for model version to finish creation. Model name: Yield_Forecaster_Global, version 11\n",1144      "Created version '11' of model 'Yield_Forecaster_Global'.\n"1145     ]1146    },1147    {1148     "name": "stdout",1149     "output_type": "stream",1150     "text": [1151      "\n",1152      "Version 11 enregistrée.\n",1153      "\n",1154      "=== Résultats métriques ===\n",1155      "Best CV R2         : 0.9397 (± 0.0055)\n",1156      "Test RMSE (kg/ha)  : 1712.4748\n",1157      "Test R2            : 0.9436\n",1158      "Test MAPE          : 17.75%\n",1159      "Test economic_error_usd_ha : 274.4312\n",1160      "\n",1161      "🏃 View run RandomForest - GridSearchCV at: http://127.0.0.1:5000/#/experiments/5/runs/5d7e084d2b0b43bba6d7d2a9802d8a11\n",1162      "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/5\n",1163      "Pipeline sauvegardé : ../model/randomforest_best_pipeline.joblib\n",1164      "Best params : {'model__max_depth': 25, 'model__max_features': 0.7, 'model__min_samples_split': 2, 'model__n_estimators': 200}\n"1165     ]1166    }1167   ],1168   "source": [1169    " # Modèle de RandomForest\n",1170    "model = RandomForestRegressor(random_state=42, n_jobs=1)\n",1171    "model_name = \"RandomForest - GridSearchCV\"\n",1172    "param_grid = {\n",1173    "    'model__n_estimators': [200],\n",1174    "    'model__max_depth': [25],\n",1175    "    'model__min_samples_split': [2],\n",1176    "    'model__max_features': [0.7]\n",1177    "}\n",1178    "tags = \"RandomForest - GridSearchCV\"\n",1179    "projet_description = \"L'objectif est de trouver les meilleurs paramètres possibles pour ce modèle\"\n",1180    "results = mlflow_tracking_gridsearch(model, model_name,param_grid, tags, projet_description)\n",1181    "\n",1182    "best_pipeline = results[\"best_pipeline\"]\n",1183    "\n",1184    "# Sauvegarde explicite du meilleur pipeline pour SHAP plus tard\n",1185    "joblib.dump(best_pipeline, \"../model/randomforest_best_pipeline.joblib\")\n",1186    "\n",1187    "print(\"Pipeline sauvegardé : ../model/randomforest_best_pipeline.joblib\")\n",1188    "print(\"Best params :\", results[\"best_params\"])"1189   ]1190  },1191  {1192   "cell_type": "code",1193   "execution_count": 6,1194   "id": "a789374d",1195   "metadata": {},1196   "outputs": [1197    {1198     "name": "stderr",1199     "output_type": "stream",1200     "text": [

Showing the first 1,200 of 1747 lines. Download the file for the rest.