Prévision de Séries Temporelles
“Quand l'ordre des observations compte — apprendre du passé pour prédire l'avenir”
Décomposition tendance-saisonnalité-résidu, features de lag, statistiques glissantes, TimeSeriesSplit, ARIMA et gradient boosting pour la prévision tabulaire.
Prérequis
Concepts Couverts
∑Formules Clés
Décomposition
Additif : tendance + saisonnalité + résidus. Multiplicatif : T × S × R quand les amplitudes s'adaptent
Modèle AR(p)
Autorégressif : la valeur actuelle est une combinaison linéaire de p valeurs passées
Fonction d'Autocorrélation
FCA — à quel point la série est-elle corrélée avec son décalage de k pas ?
MAPE
Erreur Absolue Moyenne en Pourcentage — métrique de prévision sans échelle
▶Simulation Interactive
Les Séries Temporelles Sont Partout
Cours boursiers, demande d'électricité, charge CPU des serveurs, trafic web, cas de COVID, météo, ventes — toutes sont des séries temporelles. La différence fondamentale avec le ML standard : les observations sont ordonnées et corrélées. Utiliser les données de demain pour prédire hier viole la causalité. Utiliser une division train/test standard (mélange aléatoire) contamine votre évaluation car les données de test apparaissent dans la période d'entraînement. Les séries temporelles nécessitent une validation croisée temporelle et un ingénierie de caractéristiques temporelles.
Prophet (Meta) et ARIMA sont les standards industriels pour la prévision. Mais le gradient boosting avec des caractéristiques de décalage soigneuses et la validation croisée TimeSeriesSplit surpasse souvent les deux sur les séries temporelles tabulaires.
Décomposition : Séparer le Signal du Bruit
La plupart des séries temporelles du monde réel ont trois composantes : Tendance (la direction à long terme — les ventes augmentant sur des années), Saisonnalité (les motifs répétitifs — ventes plus élevées en décembre), et Résidus (bruit aléatoire après la suppression de la tendance et de la saisonnalité). La décomposition additive fonctionne quand l'amplitude saisonnière est constante ; multiplicative quand elle croît avec la tendance. STL (décomposition Saisonnalité-Tendance utilisant LOESS) est l'approche moderne robuste — gère plusieurs périodes de saisonnalité et les anomalies.
Créer des Caractéristiques à partir du Temps
Les séries temporelles peuvent être traitées comme du ML supervisé en créant des caractéristiques de décalage et des statistiques glissantes. Caractéristiques de décalage : y_{t-1}, y_{t-2}, ..., y_{t-p} capturent l'autocorrélation. Statistiques glissantes : rolling_mean(window=7), rolling_std, rolling_max capturent la tendance et la volatilité récentes. Caractéristiques calendaires : heure_du_jour, jour_semaine, mois, est_vacances capturent la saisonnalité. Caractéristiques de Fourier : sin(2πt/période), cos(2πt/période) encodent les motifs saisonniers doux.
Créer les décalages : df['lag_1'] = df['y'].shift(1)
Statistiques glissantes : df['roll_mean_7'] = df['y'].rolling(7).mean()
Caractéristiques calendaires : df['jour_semaine'] = df.index.dayofweek
Saisonnalité de Fourier : paires sin/cos pour chaque période saisonnière
Toujours utiliser TimeSeriesSplit — ne jamais mélanger les séries temporelles pour la VC
Écart entre train/validation : ajouter gap= pour éviter la fuite par autocorrélation
TimeSeriesSplit : Validation Croisée Correcte
Pli 1 : Train=[t₁…t₃₀₀], Val=[t₃₀₁…t₄₀₀]
Pli 2 : Train=[t₁…t₄₀₀], Val=[t₄₀₁…t₅₀₀]
Pli 3 : Train=[t₁…t₅₀₀], Val=[t₅₀₁…t₆₀₀]
La fenêtre d'entraînement se termine toujours avant la validation — pas de fuite du futur
Option : gap=k entre la fin du train et le début de la val (évite la fuite par autocorrélation)
Option : max_train_size=N pour une fenêtre glissante (seulement les N derniers points en train)
Prévision avec sklearn + LightGBM
import pandas as pd import numpy as np import lightgbm as lgb from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_absolute_error class="tok-comment"># ── Série temporelle journalière dclass="tok-str">'exemple ──────────────────────────── dates = pd.date_range('class="tok-num">2022-class="tok-num">01-class="tok-num">01class="tok-str">', periods=class="tok-num">365, freq='Dclass="tok-str">') np.random.seed(class="tok-num">42) tendance = np.linspace(class="tok-num">100, class="tok-num">200, class="tok-num">365) saisonnalite = class="tok-num">20 * np.sin(class="tok-num">2 * np.pi * np.arange(class="tok-num">365) / class="tok-num">7) bruit = np.random.randn(class="tok-num">365) * class="tok-num">5 df = pd.DataFrame({'ventesclass="tok-str">': tendance + saisonnalite + bruit}, index=dates) def creer_caracteristiques(df, col_cible, decalages, fenetres): """Créer des caractéristiques de décalage et glissantes pour la prévision supervisée.""" df = df.copy() for lag in decalages: df[f'lag_{lag}class="tok-str">'] = df[col_cible].shift(lag) for f in fenetres: df[f'roll_mean_{f}class="tok-str">'] = df[col_cible].shift(class="tok-num">1).rolling(f).mean() df[f'roll_std_{f}class="tok-str">'] = df[col_cible].shift(class="tok-num">1).rolling(f).std() class="tok-comment"># Caractéristiques calendaires df['jour_semaineclass="tok-str">'] = df.index.dayofweek df['moisclass="tok-str">'] = df.index.month df['est_weekendclass="tok-str">'] = df['jour_semaineclass="tok-str">'] >= class="tok-num">5 class="tok-comment"># Saisonnalité de Fourier (hebdomadaire=class="tok-num">7, annuelle=class="tok-num">365) for k in range(class="tok-num">1, class="tok-num">3): df[f'sin_sem_{k}class="tok-str">'] = np.sin(class="tok-num">2*np.pi*k * df.index.dayofyear / class="tok-num">7) df[f'cos_sem_{k}class="tok-str">'] = np.cos(class="tok-num">2*np.pi*k * df.index.dayofyear / class="tok-num">7) return df.dropna() df_feat = creer_caracteristiques(df, 'ventesclass="tok-str">', decalages=[class="tok-num">1,class="tok-num">2,class="tok-num">3,class="tok-num">7,class="tok-num">14,class="tok-num">28], fenetres=[class="tok-num">7,class="tok-num">14,class="tok-num">28]) X = df_feat.drop('ventesclass="tok-str">', axis=class="tok-num">1) y = df_feat['ventes'] class="tok-comment"># ── Validation croisée TimeSeriesSplit ──────────────────────────── tscv = TimeSeriesSplit(n_splits=class="tok-num">5, gap=class="tok-num">7) class="tok-comment"># gap de class="tok-num">7 jours eam_scores = [] for idx_train, idx_val in tscv.split(X): X_tr, X_val = X.iloc[idx_train], X.iloc[idx_val] y_tr, y_val = y.iloc[idx_train], y.iloc[idx_val] modele = lgb.LGBMRegressor(n_estimators=class="tok-num">500, learning_rate=class="tok-num">0.05, num_leaves=class="tok-num">31, min_child_samples=class="tok-num">20) modele.fit(X_tr, y_tr, eval_set=[(X_val, y_val)], callbacks=[lgb.early_stopping(class="tok-num">50, verbose=False)]) eam_scores.append(mean_absolute_error(y_val, modele.predict(X_val))) print(fclass="tok-str">"CV EAM : {np.mean(eam_scores):.2f} ± {np.std(eam_scores):.2f}")
Pièges des Séries Temporelles
Utiliser une division train/test aléatoire sur les données de séries temporelles est l'erreur n°1 — votre modèle s'entraîne sur les données futures, résultant en une évaluation follement optimiste. Toujours utiliser TimeSeriesSplit ou une division temporelle unique. Deuxième : ne pas ajouter d'écart entre les fenêtres train et validation — l'autocorrélation rend le dernier point d'entraînement et le premier point de validation hautement corrélés. Troisième : fuite de caractéristiques — utiliser une moyenne glissante de y sans décalage approprié signifie que les valeurs futures contaminent les caractéristiques actuelles. Toujours shift(1) avant le rolling.
Pour la prévision en production, ré-entraîner votre modèle au fur et à mesure de l'arrivée de nouvelles données. Les modèles précis il y a 6 mois peuvent avoir dérivé avec l'évolution de la distribution de la série temporelle.
?Vérification des Connaissances
La progression est sauvegardée dans votre navigateur — aucun compte requis.
Besoin d'un ingénieur IA ou data scientist ?
Je conçois des modèles ML sur mesure, des agents IA, de la vision par ordinateur et de l'automatisation — de l'idée à la production.