Aller au contenu principal

Module 8 — Approches profondes : LSTM et Transformers temporels

Les modules 5, 6 et 7 ont couvert les méthodes statistiques et les modèles à base de régression. Ce module explore la troisième famille — les réseaux de neurones — dont l'apport réel est plus nuancé que ce que la littérature marketing laisse entendre. Comprendre où le profond gagne vraiment est plus important qu'apprendre l'architecture d'un LSTM.

Le fenêtrage : la seule vraie mise en forme

Un réseau ne traite pas une série directement, il traite des fenêtres : chaque exemple d'entraînement est une paire (fenêtre historique, valeur future à prédire). C'est ce fenêtrage qui rend l'approche compatible avec les optimiseurs stochastiques du cours 07.

import numpy as np
import torch

def fenetrer(serie, taille_fenetre, horizon):
X, y = [], []
for i in range(len(serie) - taille_fenetre - horizon + 1):
X.append(serie[i : i + taille_fenetre])
y.append(serie[i + taille_fenetre : i + taille_fenetre + horizon])
return np.array(X), np.array(y)

# Sur notre fil rouge : 90 jours en entree, 28 jours de prevision multi-pas.
train_log = np.log(ventes.loc[:"2024-12-31", "quantite"].values).astype(np.float32)
X_train, y_train = fenetrer(train_log, taille_fenetre=90, horizon=28)
print(X_train.shape, y_train.shape)

Deux règles à ne jamais enfreindre. Le mélange des fenêtres pendant l'entraînement est autorisé — c'est même souhaitable pour le SGD — parce que chaque fenêtre reste ordonnée en interne ; l'ordre à préserver est celui à l'intérieur de la fenêtre, pas entre fenêtres. En revanche, la frontière train/test reste chronologique : aucune fenêtre du test ne doit chevaucher une fenêtre du train, même partiellement, sous peine de fuite.

Le LSTM, brièvement

Le LSTM (cours 11) est une variante de réseau récurrent conçue pour préserver l'information sur de longues séquences grâce à ses portes (oubli, entrée, sortie). Appliqué à la prévision, il produit à chaque pas de temps un état caché qu'on transforme en prédiction.

Deux stratégies coexistent pour prévoir un horizon multi-pas. La prévision récursive : le modèle prédit un pas, la prédiction est réinjectée en entrée, et on recommence. Simple, mais les erreurs s'accumulent. La prévision directe multi-pas (« seq2seq ») : la sortie du modèle est un vecteur de 28 valeurs, une par jour. Coût mémoire plus élevé, mais nettement plus stable sur des horizons longs.

import torch.nn as nn

class LSTMPrev(nn.Module):
def __init__(self, taille_cachee=128, horizon=28, couches=2, abandon=0.2):
super().__init__()
self.lstm = nn.LSTM(
input_size=1, hidden_size=taille_cachee, num_layers=couches,
batch_first=True, dropout=abandon,
)
self.tete = nn.Linear(taille_cachee, horizon)

def forward(self, x):
# x : (lot, temps, variables). Ici une seule variable, la vente.
sortie, (h, c) = self.lstm(x)
# On prend l'etat cache du dernier pas de temps pour predire les 28 futurs.
return self.tete(sortie[:, -1, :])

Sur notre fil rouge (une seule série de 4 ans), ce LSTM n'a pratiquement aucune raison de battre le gradient boosting du module 7. Le nombre d'exemples d'entraînement — environ 1 350 fenêtres — reste trop faible pour tirer parti des dizaines de milliers de paramètres du réseau. C'est un constat qu'il faut anticiper : sortir un LSTM sur une série univariée courte donne un modèle brillant en démo et médiocre en production.

Où le profond gagne réellement

L'ordre de grandeur qui change tout est le nombre de séries en parallèle. Une chaîne de pharmacies possède plusieurs centaines de points de vente, chacun avec sa propre série. Un LSTM (ou un Transformer) entraîné en modèle global sur toutes ces séries simultanément — chaque exemple étant une fenêtre appartenant à une pharmacie identifiée — apprend une représentation partagée qui transfère entre pharmacies. C'est exactement l'idée qui a fait gagner à N-BEATS puis N-HiTS le concours M4, et qui domine aujourd'hui la prévision retail.

class ModeleGlobal(nn.Module):
def __init__(self, n_pharmacies, dim_embed=16, taille_cachee=128, horizon=28):
super().__init__()
self.embed = nn.Embedding(n_pharmacies, dim_embed)
self.lstm = nn.LSTM(input_size=1 + dim_embed, hidden_size=taille_cachee, batch_first=True)
self.tete = nn.Linear(taille_cachee, horizon)

def forward(self, x, id_pharmacie):
# id_pharmacie : (lot,) entier.
e = self.embed(id_pharmacie).unsqueeze(1).expand(-1, x.size(1), -1)
entree = torch.cat([x, e], dim=-1)
sortie, _ = self.lstm(entree)
return self.tete(sortie[:, -1, :])

Cette architecture apprend un vecteur par pharmacie (l'embedding) qui la distingue des autres, tout en partageant la structure hebdomadaire et annuelle apprise sur l'ensemble. Sur des jeux de plusieurs milliers de séries, elle bat de 5 à 15 % un boosting bien réglé.

Les Transformers temporels

Les Transformers ont pris le relais des LSTM dans plusieurs benchmarks récents. Trois architectures méritent d'être citées.

Informer (2021) réduit la complexité O(L2)O(L^2) classique de l'attention à O(LlogL)O(L \log L), ce qui rend possibles des horizons très longs. Temporal Fusion Transformer (TFT, 2021) combine attention et LSTM avec un mécanisme d'interprétabilité par variable ; c'est un choix courant en industrie. PatchTST (2023) découpe la série en patches courts qu'il traite comme des jetons — proche de l'approche vision — et bat régulièrement les autres sur des benchmarks publics.

Un article majeur, publié en 2022 par des chercheurs de Meta et intitulé « Are Transformers Effective for Time Series Forecasting? », a montré qu'un simple modèle linéaire DLinear battait la plupart des Transformers publiés sur les benchmarks classiques. Ce résultat a provoqué une remise en cause salutaire : sur beaucoup de séries à saisonnalité stable, un modèle linéaire à retards suffit, et les Transformers ne montrent leur avantage que sur des jeux massifs avec des dépendances complexes.

Les modèles de fondation : promesse récente

Depuis 2023, plusieurs modèles pré-entraînés sur des collections gigantesques de séries proposent une prévision zero-shot : donnez une nouvelle série, obtenez une prévision sans réentraîner. Les plus connus sont TimeGPT, Chronos, MOIRAI, TimesFM et Lag-Llama. Ils fonctionnent comme les grands modèles de langage : un unique modèle générique, appliqué à toute nouvelle série via son historique.

Leur intérêt réel dépend du cas. Sur des séries nouvelles dont on n'a que quelques semaines d'historique, ils battent souvent SARIMA et rivalisent avec des modèles ajustés. Sur des séries longues et bien connues, un modèle spécifique reste meilleur. Le point à connaître pour l'examen : ces modèles ne sont pas magiques, ils transfèrent une connaissance apprise ailleurs, et la qualité dépend de la ressemblance entre votre série et celles vues à l'entraînement.

Ce qu'il ne faut pas faire

Trois erreurs récurrentes dans les projets qui tentent le profond.

Ne pas normaliser par série : sur des séries de niveaux très différents (10 unités par jour pour une petite pharmacie, 500 pour une grosse), un modèle global apprend surtout le niveau, pas la structure. La standardisation par série (moyenne et écart-type calculés sur chaque série train) résout cela.

Ne pas surveiller les gradients : la rétropropagation à travers de longues séquences amplifie les problèmes du cours 07 (gradients qui explosent ou disparaissent). Un clip_grad_norm_ à 1,0 est presque toujours nécessaire.

Comparer une seule fois : un LSTM entraîné une seule fois avec une graine donnée peut battre ou perdre contre le baseline par pur hasard. Comparer sur plusieurs graines et prendre la médiane des scores, ou évaluer sur plusieurs origines glissantes (module 9), est la seule évaluation honnête.

Le profond ne remplace pas les modules 5 à 7

Sur une série univariée d'une décennie, un SARIMA bien ajusté (ou un gradient boosting du module 7) est presque toujours suffisant et souvent supérieur. Le profond exige un contexte spécifique : beaucoup de séries partageant une structure, ou une richesse multivariée que les autres approches gèrent mal. Choisir du profond parce que « c'est ce qu'on fait aujourd'hui » est le meilleur moyen de payer une infrastructure coûteuse pour un score inférieur.

La décision à quatre questions

Avant d'entraîner un réseau, poser : (1) combien de séries similaires puis-je regrouper ? (2) combien d'observations par série ? (3) mon horizon dépasse-t-il ce que gèrent bien SARIMA et Prophet ? (4) ai-je des régresseurs multivariés riches à intégrer ? Si les réponses ne sont pas franchement « beaucoup, beaucoup, oui, oui », rester sur les méthodes précédentes est presque toujours le meilleur choix.

En résumé

  • Le fenêtrage transforme la série en paires (fenêtre historique, cible future) ; la frontière train/test reste chronologique.
  • Un LSTM sur une seule série courte n'a presque jamais l'avantage face au boosting ; le profond gagne sur beaucoup de séries entraînées en modèle global.
  • Les Transformers temporels (Informer, TFT, PatchTST) apportent surtout sur les horizons longs et les dépendances complexes ; un simple linéaire (DLinear) reste souvent compétitif.
  • Les modèles de fondation proposent une prévision zero-shot utile sur séries nouvelles ; un modèle ajusté reste supérieur sur séries connues.

Module suivant : la validation glissante et les métriques qui permettent une comparaison honnête de tous ces modèles.