Module 2 — Le neurone récurrent et son état caché
Le module 1 a expliqué pourquoi une séquence exige autre chose qu'un dense. Voici l'objet qui répond : une petite cellule qui lit un pas à la fois, garde une trace de ce qu'elle a vu, et réutilise les mêmes poids à chaque pas.
L'équation de récurrence
Un neurone récurrent maintient un vecteur appelé état caché. À chaque pas, il combine l'entrée du pas et l'état précédent :
Le vecteur résume tout le passé de la séquence à la date , dans une taille fixe choisie à l'avance. C'est le point clé : quelle que soit la longueur de la séquence, la mémoire tient toujours dans de dimension .
Trois matrices de paramètres :
- de forme mélange les variables d'entrée
- de forme propage l'état d'un pas au suivant
- de forme est le biais
Le nombre total de paramètres est . Pour la consommation électrique avec variable d'entrée et unités cachées, cela fait paramètres. Ce compte est indépendant de la longueur .
Poids partagés : la propriété fondamentale
Les mêmes matrices sont utilisées à tous les pas de temps. C'est ce qui rend le modèle capable de généraliser à des séquences plus longues que celles vues à l'entraînement, et c'est ce qui borne le nombre de paramètres.
Comparez avec un dense qui traiterait entrées et unités : paramètres, tous spécifiques à leur position. Un motif appris à la position 12 n'aide en rien à la position 24. Un récurrent, lui, apprend une transformation qui s'applique partout.
Le contraste tient dans une image simple : le dense apprend un modèle par heure, le récurrent apprend une règle de mise à jour.
Dépliage dans le temps
Pour le calcul, la cellule est dépliée : on écrit copies chaînées, où la sortie de l'une devient l'entrée de la suivante. C'est un graphe plus long, mais qui utilise les mêmes poids partout.
x_1 --> [cellule] --> h_1 --+
|
x_2 --> [cellule] --> h_2 --+
|
x_3 --> [cellule] --> h_3 --+
...
x_T --> [cellule] --> h_T
Le graphe déplié sert autant à la propagation avant qu'à la propagation arrière — c'est de là que vient le nom BPTT du module 3.
Les formes de tenseurs
Toute erreur de forme à ce stade vient d'une confusion sur l'ordre des dimensions. Keras et PyTorch conviennent : par défaut, la forme est où est la taille du lot, la longueur en pas, le nombre de variables par pas.
| Objet | Forme | Signification |
|---|---|---|
| Entrée d'une couche récurrente | séquences, pas, variables | |
Sortie avec return_sequences=False | uniquement le dernier état | |
Sortie avec return_sequences=True | tous les | |
| État caché | une valeur par exemple et par unité |
Le drapeau return_sequences est le paramètre le plus source d'erreurs.
Une couche récurrente qui suit une autre couche récurrente doit exposer tous
ses , donc return_sequences=True. Une couche récurrente suivie d'une
couche Dense de classification doit ne rendre que le dernier, donc
return_sequences=False.
Un premier récurrent en Keras
Sur le fil rouge de la consommation électrique, on prévoit une seule valeur (la consommation à l'heure suivante) à partir des 168 dernières.
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
modele = keras.Sequential([
layers.Input(shape=(168, 1)), # T = 168, F = 1
layers.SimpleRNN(64), # return_sequences=False par defaut
layers.Dense(1), # une valeur predite
])
modele.compile(optimizer="adam", loss="mse", metrics=["mae"])
modele.summary()
La ligne SimpleRNN(64) fabrique une cellule de 64 unités cachées et la
déplie automatiquement sur 168 pas. Il n'y a rien à écrire pour la boucle
temporelle ; le cadriciel s'en charge.
Pour prédire 24 heures d'un coup, deux voies existent. La plus simple est un dense final à 24 sorties : la cellule résume les 168 heures en un , puis un dense projette ce vecteur sur 24 valeurs.
modele = keras.Sequential([
layers.Input(shape=(168, 1)),
layers.SimpleRNN(64),
layers.Dense(24), # 24 heures d'un coup
])
L'autre voie, avec return_sequences=True et une cellule à la sortie, produit
les 24 valeurs pas à pas. Elle est plus riche mais plus coûteuse ; on la garde
pour l'encodeur-décodeur du module 8.
Ce que l'état caché n'est pas
Trois erreurs de représentation valent d'être nommées.
L'état caché n'est pas la sortie du modèle. Il est interne à la cellule.
La sortie visible dépend d'une couche Dense (ou d'une autre cellule) qui
consomme .
Il n'est pas une mémoire infinie. Sa capacité est fixée par la dimension ; au-delà d'une certaine longueur, il faut réécrire ce qui est utile plutôt que d'ajouter. C'est exactement ce que fait la porte d'oubli du LSTM au module 5.
Il n'est pas remis à zéro entre exemples d'un lot indépendant, sauf si
vous l'exigez. La plupart du temps, chaque exemple d'un lot part avec
; l'option stateful=True change ce comportement pour enchaîner
des lots consécutifs, utile uniquement quand on ne peut pas mettre toute la
séquence dans une seule fenêtre.
Une série univariée mal préparée arrive en forme au lieu de
. Keras lève alors une erreur claire. Une série multivariée dont
on aurait mis les variables en tête donne et fait converger le
modèle vers une soupe : il apprend à voir les variables comme du temps.
Vérifier X.shape avant fit prend cinq secondes et sauve une soirée.
Puisque les poids sont partagés, remplacer par à
l'inférence ne demande aucun nouvel entraînement, à condition d'utiliser
Input(shape=(None, 1)). C'est utile pour tester si allonger l'historique
améliore les prévisions sans réécrire l'architecture.
En résumé
- Un neurone récurrent maintient un état caché mis à jour par une combinaison de et de via des matrices et et un biais .
- Les poids sont partagés entre tous les pas : le nombre de paramètres est indépendant de la longueur , ce qui permet de généraliser à des séquences plus longues.
- Les formes clés sont en entrée, en sortie avec
return_sequences=False, avecTrue— c'est ce drapeau qui décide si l'on empile une autre récurrente ou uneDensede sortie. - L'état caché n'est ni la sortie, ni une mémoire infinie : sa capacité est bornée par , ce qui motive les portes d'oubli du LSTM.
Module suivant : comment ces poids apprennent, en dépliant le graphe pour y faire circuler un gradient qui, on va le voir, ne circule pas si bien.