MAALOUFimad02/Machine_Learning_Training
0
1{2 "cells": [3 {4 "cell_type": "markdown",5 "metadata": {},6 "source": [7 "# ⏱️ TP-4 : Prédiction de Séries Temporelles avec LSTM\n",8 "\n",9 "**Objectif** : Prédire la consommation électrique avec des réseaux LSTM.\n",10 "\n",11 "**Compétences** :\n",12 "- Préparation de données temporelles\n",13 "- Fenêtres glissantes (windowing)\n",14 "- Architecture LSTM avec Keras\n",15 "- Early stopping et régularisation"16 ]17 },18 {19 "cell_type": "code",20 "execution_count": null,21 "metadata": {},22 "outputs": [],23 "source": [24 "import numpy as np\n",25 "import pandas as pd\n",26 "import matplotlib.pyplot as plt\n",27 "from sklearn.preprocessing import MinMaxScaler\n",28 "from sklearn.metrics import mean_squared_error, mean_absolute_error\n",29 "\n",30 "import tensorflow as tf\n",31 "from tensorflow.keras.models import Sequential\n",32 "from tensorflow.keras.layers import LSTM, Dense, Dropout\n",33 "from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n",34 "\n",35 "# Reproductibilité\n",36 "np.random.seed(42)\n",37 "tf.random.set_seed(42)\n",38 "\n",39 "print(f\"✅ TensorFlow version : {tf.__version__}\")"40 ]41 },42 {43 "cell_type": "code",44 "execution_count": null,45 "metadata": {},46 "outputs": [],47 "source": [48 "# Génération de données synthétiques (consommation électrique)\n",49 "# En pratique, remplacez par vos données réelles\n",50 "\n",51 "def generate_energy_data(n_days=365*2):\n",52 " \"\"\"Génère des données de consommation électrique simulées\"\"\"\n",53 " hours = np.arange(n_days * 24)\n",54 " \n",55 " # Tendance\n",56 " trend = 0.001 * hours\n",57 " \n",58 " # Saisonnalité journalière\n",59 " daily = 10 * np.sin(2 * np.pi * hours / 24)\n",60 " \n",61 " # Saisonnalité hebdomadaire\n",62 " weekly = 5 * np.sin(2 * np.pi * hours / (24 * 7))\n",63 " \n",64 " # Saisonnalité annuelle\n",65 " yearly = 15 * np.sin(2 * np.pi * hours / (24 * 365))\n",66 " \n",67 " # Bruit\n",68 " noise = np.random.normal(0, 3, len(hours))\n",69 " \n",70 " # Consommation totale\n",71 " consumption = 50 + trend + daily + weekly + yearly + noise\n",72 " consumption = np.maximum(consumption, 0) # Pas de valeurs négatives\n",73 " \n",74 " return consumption\n",75 "\n",76 "# Génération des données\n",77 "data = generate_energy_data(n_days=730) # 2 ans de données\n",78 "\n",79 "# Création du DataFrame\n",80 "dates = pd.date_range(start='2022-01-01', periods=len(data), freq='H')\n",81 "df = pd.DataFrame({'consumption': data}, index=dates)\n",82 "\n",83 "print(f\"📊 Période : {df.index[0]} à {df.index[-1]}\")\n",84 "print(f\"📊 Total : {len(df)} heures de données\")\n",85 "df.head()"86 ]87 },88 {89 "cell_type": "code",90 "execution_count": null,91 "metadata": {},92 "outputs": [],93 "source": [94 "# Visualisation des données\n",95 "fig, axes = plt.subplots(3, 1, figsize=(15, 10))\n",96 "\n",97 "# Vue complète\n",98 "axes[0].plot(df.index, df['consumption'], alpha=0.7)\n",99 "axes[0].set_title('Consommation électrique - Vue complète (2 ans)')\n",100 "axes[0].set_ylabel('kWh')\n",101 "\n",102 "# Vue d'une semaine\n",103 "one_week = df.iloc[:24*7]\n",104 "axes[1].plot(one_week.index, one_week['consumption'], marker='o')\n",105 "axes[1].set_title('Consommation - Vue hebdomadaire')\n",106 "axes[1].set_ylabel('kWh')\n",107 "\n",108 "# Vue d'une journée\n",109 "one_day = df.iloc[:24]\n",110 "axes[2].plot(one_day.index.hour, one_day['consumption'], marker='o')\n",111 "axes[2].set_title('Consommation - Vue journalière')\n",112 "axes[2].set_xlabel('Heure')\n",113 "axes[2].set_ylabel('kWh')\n",114 "\n",115 "plt.tight_layout()\n",116 "plt.show()"117 ]118 },119 {120 "cell_type": "code",121 "execution_count": null,122 "metadata": {},123 "outputs": [],124 "source": [125 "# Feature Engineering temporel\n",126 "df['hour'] = df.index.hour\n",127 "df['day_of_week'] = df.index.dayofweek\n",128 "df['month'] = df.index.month\n",129 "df['is_weekend'] = (df['day_of_week'] >= 5).astype(int)\n",130 "\n",131 "# Lags (valeurs précédentes)\n",132 "for lag in [1, 2, 3, 24, 48]:\n",133 " df[f'lag_{lag}'] = df['consumption'].shift(lag)\n",134 "\n",135 "# Rolling statistics\n",136 "df['rolling_mean_24'] = df['consumption'].rolling(window=24).mean()\n",137 "df['rolling_std_24'] = df['consumption'].rolling(window=24).std()\n",138 "\n",139 "# Suppression des NaN\n",140 "df = df.dropna()\n",141 "\n",142 "print(f\"✅ Features créées : {df.shape[1]} colonnes\")\n",143 "df.head()"144 ]145 },146 {147 "cell_type": "code",148 "execution_count": null,149 "metadata": {},150 "outputs": [],151 "source": [152 "# Préparation des séquences pour LSTM\n",153 "def create_sequences(data, target_col, sequence_length=24):\n",154 " \"\"\"\n",155 " Crée des séquences pour LSTM\n",156 " data : DataFrame avec features\n",157 " target_col : nom de la colonne cible\n",158 " sequence_length : longueur de la séquence (ex: 24 heures)\n",159 " \"\"\"\n",160 " X, y = [], []\n",161 " values = data.values\n",162 " target_idx = data.columns.get_loc(target_col)\n",163 " \n",164 " for i in range(sequence_length, len(values)):\n",165 " X.append(values[i-sequence_length:i])\n",166 " y.append(values[i, target_idx])\n",167 " \n",168 " return np.array(X), np.array(y)\n",169 "\n",170 "# Séparation train/test\n",171 "train_size = int(len(df) * 0.8)\n",172 "train_df = df.iloc[:train_size]\n",173 "test_df = df.iloc[train_size:]\n",174 "\n",175 "# Normalisation\n",176 "scaler = MinMaxScaler()\n",177 "train_scaled = scaler.fit_transform(train_df)\n",178 "test_scaled = scaler.transform(test_df)\n",179 "\n",180 "# Conversion en DataFrame pour garder les noms de colonnes\n",181 "train_scaled = pd.DataFrame(train_scaled, columns=df.columns, index=train_df.index)\n",182 "test_scaled = pd.DataFrame(test_scaled, columns=df.columns, index=test_df.index)\n",183 "\n",184 "# Création des séquences\n",185 "SEQUENCE_LENGTH = 24 # 24 heures d'historique\n",186 "\n",187 "X_train, y_train = create_sequences(train_scaled, 'consumption', SEQUENCE_LENGTH)\n",188 "X_test, y_test = create_sequences(test_scaled, 'consumption', SEQUENCE_LENGTH)\n",189 "\n",190 "print(f\"📊 X_train shape : {X_train.shape}\")\n",191 "print(f\"📊 y_train shape : {y_train.shape}\")\n",192 "print(f\"📊 X_test shape : {X_test.shape}\")\n",193 "print(f\"📊 y_test shape : {y_test.shape}\")"194 ]195 },196 {197 "cell_type": "code",198 "execution_count": null,199 "metadata": {},200 "outputs": [],201 "source": [202 "# Construction du modèle LSTM\n",203 "model = Sequential([\n",204 " LSTM(64, return_sequences=True, input_shape=(SEQUENCE_LENGTH, X_train.shape[2])),\n",205 " Dropout(0.2),\n",206 " LSTM(32, return_sequences=False),\n",207 " Dropout(0.2),\n",208 " Dense(16, activation='relu'),\n",209 " Dense(1)\n",210 "])\n",211 "\n",212 "model.compile(\n",213 " optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),\n",214 " loss='mse',\n",215 " metrics=['mae']\n",216 ")\n",217 "\n",218 "model.summary()"219 ]220 },221 {222 "cell_type": "code",223 "execution_count": null,224 "metadata": {},225 "outputs": [],226 "source": [227 "# Callbacks\n",228 "callbacks = [\n",229 " EarlyStopping(\n",230 " monitor='val_loss',\n",231 " patience=10,\n",232 " restore_best_weights=True\n",233 " ),\n",234 " ReduceLROnPlateau(\n",235 " monitor='val_loss',\n",236 " factor=0.5,\n",237 " patience=5,\n",238 " min_lr=1e-6\n",239 " )\n",240 "]\n",241 "\n",242 "# Entraînement\n",243 "history = model.fit(\n",244 " X_train, y_train,\n",245 " epochs=100,\n",246 " batch_size=32,\n",247 " validation_split=0.2,\n",248 " callbacks=callbacks,\n",249 " verbose=1\n",250 ")"251 ]252 },253 {254 "cell_type": "code",255 "execution_count": null,256 "metadata": {},257 "outputs": [],258 "source": [259 "# Visualisation de l'entraînement\n",260 "fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",261 "\n",262 "# Loss\n",263 "axes[0].plot(history.history['loss'], label='Train')\n",264 "axes[0].plot(history.history['val_loss'], label='Validation')\n",265 "axes[0].set_title('Loss (MSE)')\n",266 "axes[0].set_xlabel('Epoch')\n",267 "axes[0].set_ylabel('Loss')\n",268 "axes[0].legend()\n",269 "\n",270 "# MAE\n",271 "axes[1].plot(history.history['mae'], label='Train')\n",272 "axes[1].plot(history.history['val_mae'], label='Validation')\n",273 "axes[1].set_title('MAE')\n",274 "axes[1].set_xlabel('Epoch')\n",275 "axes[1].set_ylabel('MAE')\n",276 "axes[1].legend()\n",277 "\n",278 "plt.tight_layout()\n",279 "plt.show()"280 ]281 },282 {283 "cell_type": "code",284 "execution_count": null,285 "metadata": {},286 "outputs": [],287 "source": [288 "# Prédictions\n",289 "y_pred = model.predict(X_test)\n",290 "\n",291 "# Métriques (sur données normalisées)\n",292 "mse = mean_squared_error(y_test, y_pred)\n",293 "mae = mean_absolute_error(y_test, y_pred)\n",294 "rmse = np.sqrt(mse)\n",295 "\n",296 "print(f\"📊 MSE : {mse:.6f}\")\n",297 "print(f\"📊 MAE : {mae:.6f}\")\n",298 "print(f\"📊 RMSE : {rmse:.6f}\")"299 ]300 },301 {302 "cell_type": "code",303 "execution_count": null,304 "metadata": {},305 "outputs": [],306 "source": [307 "# Visualisation des prédictions\n",308 "plt.figure(figsize=(15, 6))\n",309 "\n",310 "# Plot des 500 premières prédictions\n",311 "n_plot = 500\n",312 "plt.plot(y_test[:n_plot], label='Réel', alpha=0.8)\n",313 "plt.plot(y_pred[:n_plot], label='Prédit', alpha=0.8)\n",314 "plt.title(f'Prédictions LSTM - {n_plot} premiers points de test')\n",315 "plt.xlabel('Temps')\n",316 "plt.ylabel('Consommation (normalisée)')\n",317 "plt.legend()\n",318 "plt.show()"319 ]320 },321 {322 "cell_type": "code",323 "execution_count": null,324 "metadata": {},325 "outputs": [],326 "source": [327 "# Scatter plot : Réel vs Prédit\n",328 "plt.figure(figsize=(8, 8))\n",329 "plt.scatter(y_test, y_pred, alpha=0.5)\n",330 "plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)\n",331 "plt.xlabel('Valeurs réelles')\n",332 "plt.ylabel('Valeurs prédites')\n",333 "plt.title('Réel vs Prédit')\n",334 "plt.show()"335 ]336 },337 {338 "cell_type": "markdown",339 "metadata": {},340 "source": [341 "## 🎓 Conclusion\n",342 "\n",343 "Dans ce TP, nous avons :\n",344 "\n",345 "1. ✅ **Généré** des données de consommation électrique avec patterns temporels\n",346 "2. ✅ **Créé** des features temporelles (heure, jour, mois, lags, rolling stats)\n",347 "3. ✅ **Préparé** les séquences pour LSTM avec windowing\n",348 "4. ✅ **Construit** un modèle LSTM avec Dropout et Early Stopping\n",349 "5. ✅ **Évalué** les performances sur l'ensemble de test\n",350 "\n",351 "**Améliorations possibles** :\n",352 "- Utiliser des données météo comme features externes\n",353 "- Tester des architectures plus complexes (Bidirectional LSTM, GRU)\n",354 "- Faire du multi-step forecasting (prédire plusieurs heures en avance)"355 ]356 }357 ],358 "metadata": {359 "kernelspec": {360 "display_name": "Python 3",361 "language": "python",362 "name": "python3"363 },364 "language_info": {365 "name": "python",366 "version": "3.8.0"367 }368 },369 "nbformat": 4,370 "nbformat_minor": 4371}372 