Pour les RNN (par exemple, LSTM et GRU), l'entrée de couche est une liste de pas de temps, et chaque pas de temps est un tenseur de caractéristiques. Cela signifie que vous pourriez avoir un tenseur d’entrée comme celui-ci (en notation Pythonic):
# Input tensor to RNN
[
# Timestep 1
[ temperature_in_paris, value_of_nasdaq, unemployment_rate ],
# Timestep 2
[ temperature_in_paris, value_of_nasdaq, unemployment_rate ],
# Timestep 3
[ temperature_in_paris, value_of_nasdaq, unemployment_rate ],
...
]
Donc, absolument, vous pouvez avoir plusieurs fonctionnalités à chaque fois. Dans mon esprit, la météo est une caractéristique de la série chronologique: là où je vis, il s’agit d’une fonction du temps. Il serait donc tout à fait raisonnable de coder les informations météo comme une de vos caractéristiques dans chaque pas de temps (avec un codage approprié, comme nuageux = 0, ensoleillé = 1, etc.).
Si vous avez des données non chronologiques, il n’a pas vraiment de sens de les transmettre par le biais du LSTM. Peut-être que le LSTM fonctionnera de toute façon, mais même s'il le fait, cela entraînera probablement un coût plus élevé en pertes / précision par temps d’entraînement.
Vous pouvez également introduire ce type d'informations "supplémentaires" dans votre modèle en dehors de LSTM au moyen de couches supplémentaires. Vous pourriez avoir un flux de données comme ceci:
TIME_SERIES_INPUT ------> LSTM -------\
*---> MERGE ---> [more processing]
AUXILIARY_INPUTS --> [do something] --/
Ainsi, vous fusionneriez vos entrées auxiliaires dans les sorties LSTM et poursuivriez votre réseau à partir de là. Maintenant, votre modèle est simplement multi-entrées.
Par exemple, supposons que dans votre application particulière, vous ne gardiez que la dernière sortie de la séquence de sortie LSTM. Disons que c'est un vecteur de longueur 10. Votre entrée auxiliaire pourrait être votre météo encodée (donc un scalaire). Votre couche de fusion pourrait simplement ajouter les informations météorologiques auxiliaires à la fin du vecteur de sortie LSTM pour produire un seul vecteur de longueur 11. Toutefois, il n’est pas nécessaire de conserver le dernier timestep de sortie LSTM: si le LSTM a généré 100 timesteps, chacun avec un vecteur de 10 caractéristiques, vous pouvez toujours ajouter vos informations météorologiques auxiliaires, ce qui donne 100 pas de temps, chacun consistant en un vecteur de 11 points de données.
La documentation de Keras sur son API fonctionnelle en donne une bonne vue d'ensemble.
Dans d'autres cas, comme @horaceT le fait remarquer, vous souhaiterez peut-être conditionner le LSTM sur des données non temporelles. Par exemple, prédisez le temps demain, à un endroit donné. Dans ce cas, voici trois suggestions, chacune avec des points positifs / négatifs:
Demandez au premier pas de temps de contenir vos données de conditionnement, car cela "définira" efficacement l'état interne / caché de votre RNN. Franchement, je ne le ferais pas , pour diverses raisons: vos données de conditionnement doivent avoir la même forme que le reste de vos fonctionnalités, ce qui rend plus difficile la création de RNN avec état (en termes de soin de suivre la façon dont vous alimentez les données). réseau), le réseau peut "oublier" les données de conditionnement suffisamment longtemps (par exemple, de longues séquences d’entraînement ou de longues séquences de prédiction), etc.
Inclure les données dans les données temporelles elles-mêmes. Ainsi, chaque vecteur de caractéristiques à une heure donnée inclut "principalement" des données de série temporelle, mais les données de conditionnement sont ensuite ajoutées à la fin de chaque vecteur de caractéristiques. Le réseau va-t-il apprendre à reconnaître cela? Probablement, mais même dans ce cas, vous créez une tâche d'apprentissage plus dure en polluant les données de séquence avec des informations non séquentielles. Donc, je déconseillerais aussi cela.
La meilleure approche serait probablement d’affecter directement l’état caché du RNN à l’heure zéro. C'est l'approche adoptée par Karpathy et Fei-Fei et par Vinyals et al . Voilà comment cela fonctionne:
- X⃗
- v⃗ = W x⃗ + b⃗ Wb⃗
- v⃗
Cette approche est la plus "théoriquement" correcte, car elle conditionne correctement le RNN sur vos entrées non temporelles, résout naturellement le problème de forme et évite également de polluer vos horodatages avec des informations supplémentaires non temporelles. L'inconvénient est que cette approche nécessite souvent un contrôle au niveau graphique de votre architecture. Par conséquent, si vous utilisez une abstraction de niveau supérieur telle que Keras, vous aurez du mal à l'implémenter à moins d'ajouter votre propre type de couche.