web-dev-qa-db-fra.com

Construire un LSTM multi-tâches multi-tâches avec Keras

Préambule

Je travaille actuellement sur un problème de Machine Learning où nous sommes chargés d'utiliser les données passées sur les ventes de produits afin de prédire les volumes de ventes à l'avenir (afin que les magasins puissent mieux planifier leurs stocks). Nous avons essentiellement des données de séries chronologiques, où pour chaque produit, nous savons combien d'unités ont été vendues quels jours. Nous avons également des informations comme le temps qu'il faisait, s'il y avait un jour férié, si l'un des produits était en vente, etc.

Nous avons réussi à modéliser cela avec un certain succès en utilisant un MLP avec des couches denses, et en utilisant simplement une approche de fenêtre coulissante pour inclure les volumes de ventes des jours environnants. Cependant, nous pensons que nous serons en mesure d'obtenir de bien meilleurs résultats avec une approche chronologique telle qu'un LSTM.

Données

Les données dont nous disposons sont essentiellement les suivantes:

enter image description here

( EDIT: pour plus de clarté, la colonne "Time" dans l'image ci-dessus n'est pas correcte. Nous avons des entrées une fois par jour, pas une fois par mois. Mais sinon la structure est la même!)

Les données X sont donc de forme:

(numProducts, numTimesteps, numFeatures) = (50 products, 1096 days, 90 features)

Et les données Y sont de forme:

(numProducts, numTimesteps, numTargets) =  (50 products, 1096 days, 3 binary targets)

enter image description here

Nous avons donc des données sur trois ans (2014, 2015, 2016) et nous voulons nous entraîner à cela pour faire des prédictions pour 2017. (Ce n'est bien sûr pas vrai à 100%, car nous avons en fait des données jusqu'en octobre 2017, mais disons simplement ignorer cela pour l'instant)

Problème

Je voudrais construire un LSTM à Keras qui me permette de faire ces prédictions. Il y a quelques endroits où je suis coincé. J'ai donc six questions concrètes (je sais que l'on est censé essayer de limiter un post Stackoverflow à une seule question, mais elles sont toutes liées).

Tout d'abord, comment pourrais-je découper mes données pour les lots ? Étant donné que j'ai trois années complètes, est-il judicieux de simplement passer par trois lots, chaque fois de la taille d'un an? Ou est-il plus judicieux de faire des lots plus petits (disons 30 jours) et également d'utiliser des fenêtres coulissantes? C'est à dire. au lieu de 36 lots de 30 jours chacun, j'utilise 36 * 6 lots de 30 jours chacun, glissant à chaque fois avec 5 jours? Ou n'est-ce pas vraiment la façon dont les LSTM devraient être utilisés? (Notez qu'il y a un peu de saisonnalité dans les données, je dois également attraper ce genre de tendance à long terme).

Deuxièmement, est-il judicieux d'utiliser return_sequences=True ici? En d'autres termes, je conserve mes données Y telles quelles (50, 1096, 3) pour qu'il y ait (à ma connaissance) une prédiction à chaque pas de temps pour laquelle une perte peut être calculée par rapport aux données cibles? Ou serais-je mieux avec return_sequences=False, de sorte que seule la valeur finale de chaque lot soit utilisée pour évaluer la perte (par exemple, si vous utilisez des lots annuels, puis en 2016 pour le produit 1, nous évaluons par rapport à la valeur de décembre 2016 de (1,1,1)).

Troisièmement comment dois-je traiter les 50 produits différents? Ils sont différents, mais toujours fortement corrélés et nous l'avons vu avec d'autres approches (par exemple un MLP avec des fenêtres temporelles simples) que les résultats sont meilleurs lorsque tous les produits sont considérés dans le même modèle. Voici quelques idées qui sont actuellement sur la table:

  • changer la variable cible pour qu'elle ne soit plus seulement 3 variables, mais 3 * 50 = 150; c'est-à-dire que pour chaque produit, il existe trois cibles, qui sont toutes entraînées simultanément.
  • répartissez les résultats après la couche LSTM en 50 réseaux denses, qui prennent en entrée les sorties du LSTM, ainsi que certaines fonctionnalités spécifiques à chaque produit - c'est-à-dire que nous obtenons un réseau multitâche avec 50 fonctions de perte, que nous optimisons ensuite ensemble. Serait-ce fou?
  • considérer un produit comme une seule observation et inclure des fonctionnalités spécifiques au produit déjà au niveau de la couche LSTM. Utilisez uniquement cette couche suivie d'une couche de sortie de taille 3 (pour les trois cibles). Faites passer chaque produit dans un lot distinct.

Quatrièmement, comment gérer les données de validation ? Normalement, je garderais simplement un échantillon sélectionné au hasard pour valider, mais ici, nous devons garder l'ordre du temps en place. Donc je suppose que le mieux est de garder quelques mois de côté?

Cinquièmement, et c'est la partie qui est probablement la plus floue pour moi - comment puis-je utiliser les résultats réels pour effectuer des prédictions ? Disons que j'ai utilisé return_sequences=False et je me suis entraîné sur les trois années en trois lots (à chaque fois jusqu'en novembre) dans le but de former le modèle pour prédire la prochaine valeur (déc. 2014, déc. 2015, déc. 2016). Si je veux utiliser ces résultats en 2017, comment cela fonctionne-t-il réellement? Si je l'ai bien compris, la seule chose que je puisse faire dans ce cas est de nourrir le modèle de tous les points de données de janvier à novembre 2017 et cela me donnera une prédiction pour décembre 2017. Est-ce correct? Cependant, si je devais utiliser return_sequences=True, puis formé sur toutes les données jusqu'en décembre 2016, pourrais-je alors obtenir une prédiction pour janvier 2017 simplement en donnant au modèle les fonctionnalités observées en janvier 2017? Ou dois-je également lui donner les 12 mois avant janvier 2017? Qu'en est-il de février 2017, dois-je en plus donner la valeur pour 2017, plus 11 mois avant cela? (Si on dirait que je suis confus, c'est parce que je le suis!)

Enfin, selon la structure que je devrais utiliser, comment puis-je faire cela dans Keras ? Ce que je pense en ce moment, c'est quelque chose du genre: (bien que ce ne soit que pour un seul produit, cela ne résout pas le fait d'avoir tous les produits dans le même modèle):

Code Keras

trainX = trainingDataReshaped #Data for Product 1, Jan 2014 to Dec 2016
trainY = trainingTargetReshaped
validX = validDataReshaped #Data for Product 1, for ??? Maybe for a few months?
validY = validTargetReshaped    

numSequences = trainX.shape[0]
numTimeSteps = trainX.shape[1]
numFeatures = trainX.shape[2]

numTargets = trainY.shape[2]

model = Sequential()
model.add(LSTM(100, input_shape=(None, numFeatures), return_sequences=True)) 
model.add(Dense(numTargets, activation="softmax"))    

model.compile(loss=stackEntry.params["loss"],
      optimizer="adam",
      metrics=['accuracy'])

history = model.fit(trainX, trainY,
            batch_size=30,
            epochs=20,
            verbose=1,
            validation_data=(validX, validY))               

predictX  = predictionDataReshaped #Data for Product 1, Jan 2017 to Dec 2017

prediction=model.predict(predictX)
24
Karl

Alors:

Premièrement, comment pourrais-je découper mes données pour les lots? Étant donné que j'ai trois années complètes, est-il judicieux de simplement passer par trois lots, chaque fois de la taille d'un an? Ou est-il plus judicieux de faire des lots plus petits (disons 30 jours) et également d'utiliser des fenêtres coulissantes? C'est à dire. au lieu de 36 lots de 30 jours chacun, j'utilise 36 * 6 lots de 30 jours chacun, glissant à chaque fois avec 5 jours? Ou n'est-ce pas vraiment la façon dont les LSTM devraient être utilisés? (Notez qu'il y a un peu de saisonnalité dans les données, je dois également attraper ce genre de tendance à long terme).

Honnêtement, la modélisation de telles données est quelque chose de vraiment difficile. Tout d'abord - je ne vous conseillerais pas d'utiliser LSTMs car ils sont plutôt conçus pour capturer un type de données un peu différent (par exemple PNL ou discours où il est vraiment important de modéliser les dépendances à long terme - pas la saisonnalité ) et ils ont besoin de beaucoup de données pour être apprises. Je vous conseille plutôt d'utiliser GRU ou SimpleRNN qui sont beaucoup plus faciles à apprendre et devraient être meilleurs pour votre tâche.

En ce qui concerne le traitement par lots - je vous conseillerais certainement d'utiliser une technique de fenêtre fixe car elle finira par produire beaucoup plus de points de données que d'alimenter une année entière ou un mois entier. Essayez de définir un nombre de jours comme méta-paramètre qui sera également optimisé en utilisant différentes valeurs dans la formation et en choisissant la plus appropriée.

En ce qui concerne la saisonnalité - bien sûr, c'est un cas, mais:

  • Vous avez peut-être trop peu de points de données et d'années collectées pour fournir une bonne estimation des tendances de la saison,
  • L'utilisation de tout type de réseau de neurones récurrent pour capturer de telles saisonnalités est une très mauvaise idée .

Je vous conseille plutôt de faire:

  • essayez d'ajouter des caractéristiques saisonnières (par exemple, la variable du mois, la variable du jour, une variable qui est définie sur true s'il y a un jour férié ce jour-là ou combien de jours il y a jusqu'au prochain jour férié important - c'est une pièce où vous pourriez être vraiment Créatif)
  • Utilisez une donnée agrégée de l'année dernière comme fonctionnalité - vous pouvez, par exemple, alimenter les résultats de l'année dernière ou des agrégations comme la moyenne mobile des résultats de l'année dernière, maximum, minimum - etc.

Deuxièmement, est-il judicieux d'utiliser return_sequences = True ici? En d'autres termes, je conserve mes données Y telles quelles (50, 1096, 3) de sorte que (pour autant que je l'ai compris), il y a une prédiction à chaque pas de temps pour laquelle une perte peut être calculée par rapport aux données cibles? Ou serais-je mieux avec return_sequences = False, de sorte que seule la valeur finale de chaque lot soit utilisée pour évaluer la perte (c'est-à-dire si vous utilisez des lots annuels, puis en 2016 pour le produit 1, nous évaluons par rapport à la valeur de décembre 2016 de (1 , 1,1)).

En utilisant return_sequences=True peut être utile, mais uniquement dans les cas suivants:

  1. Quand un LSTM donné (ou une autre couche récurrente) sera suivi d'une autre couche récurrente.
  2. Dans un scénario - lorsque vous alimentez une série originale décalée en sortie par ce que vous apprenez simultanément un modèle dans différentes fenêtres temporelles, etc.

La manière décrite dans un deuxième point peut être une approche intéressante mais gardez à l'esprit que cela peut être un peu difficile à mettre en œuvre car vous devrez réécrire votre modèle afin d'obtenir un résultat de production. Ce qui pourrait également être plus difficile, c'est que vous devrez tester votre modèle contre de nombreux types d'instabilités temporelles - et une telle approche pourrait rendre cela totalement irréalisable.

Troisièmement, comment dois-je gérer les 50 produits différents? Ils sont différents, mais toujours fortement corrélés et nous avons vu avec d'autres approches (par exemple un MLP avec de simples fenêtres temporelles) que les résultats sont meilleurs lorsque tous les produits sont considérés dans le même modèle. Voici quelques idées qui sont actuellement sur la table:

  • changer la variable cible pour qu'elle ne soit plus seulement 3 variables, mais 3 * 50 = 150; c'est-à-dire que pour chaque produit, il existe trois cibles, qui sont toutes entraînées simultanément.
  • répartissez les résultats après la couche LSTM en 50 réseaux denses, qui prennent en entrée les sorties du LSTM, ainsi que certaines fonctionnalités spécifiques à chaque produit - c'est-à-dire que nous obtenons un réseau multitâche avec 50 fonctions de perte, que nous optimisons ensuite ensemble. Serait-ce fou?
  • considérer un produit comme une observation unique et inclure des fonctionnalités spécifiques au produit déjà au niveau de la couche LSTM. Utilisez uniquement cette couche suivie d'une couche de sortie de taille 3 (pour les trois cibles). Faites passer chaque produit dans un lot distinct.

J'irais certainement pour un premier choix mais avant de fournir une explication détaillée, je discuterai des inconvénients des 2e et 3e:

  • Dans la deuxième approche: ce ne serait pas fou mais vous perdrez beaucoup de corrélations entre les cibles de produits,
  • Dans la troisième approche: vous perdrez beaucoup de modèles intéressants apparaissant dans les dépendances entre différentes séries chronologiques.

Avant d'arriver à mon choix - discutons encore un autre problème - les redondances dans votre ensemble de données. Je suppose que vous avez 3 types de fonctionnalités:

  • des produits spécifiques (disons qu'il y en a 'm')
  • caractéristiques générales - disons qu'il y en a "n".

Vous avez maintenant un tableau de taille (timesteps, m * n, products). Je le transformerais en table de forme (timesteps, products * m + n) car les caractéristiques générales sont les mêmes pour tous les produits. Cela vous fera économiser beaucoup de mémoire et rendra également possible l'alimentation du réseau récurrent (gardez à l'esprit que les couches récurrentes dans keras n'ont qu'une seule dimension de fonctionnalité - alors que vous en aviez deux - product et feature ceux).

Alors pourquoi la première approche est la meilleure à mon avis? Parce qu'il tire parti de nombreuses dépendances intéressantes des données. Bien sûr - cela pourrait nuire au processus de formation - mais il existe une astuce simple pour surmonter cela: réduction de la dimensionnalité . Vous pourriez par exemple entraînez PCA sur votre vecteur 150 dimensions et réduisez sa taille à un plus petit - grâce à ce que vos dépendances sont modélisées par PCA et votre sortie a une taille beaucoup plus réalisable.

Quatrièmement, comment gérer les données de validation? Normalement, je garderais simplement un échantillon sélectionné au hasard pour valider, mais ici, nous devons garder l'ordre du temps en place. Donc je suppose que le mieux est de garder quelques mois de côté?

C'est une question vraiment importante. D'après mon expérience, vous devez tester votre solution contre de nombreux types d'instabilités afin de vous assurer qu'elle fonctionne correctement. Voici donc quelques règles que vous devez garder à l'esprit:

  • Il ne devrait y avoir aucun chevauchement entre vos séquences d'entraînement et les séquences de test. S'il y en avait - vous aurez des valeurs valides à partir d'un ensemble de test alimenté à un modèle pendant l'entraînement,
  • Vous devez tester la stabilité temporelle du modèle par rapport à de nombreux types de dépendances temporelles.

Le dernier point peut être un peu vague - donc pour vous donner quelques exemples:

  • stabilité de l'année - validez votre modèle en l'entraînant à l'aide de chaque combinaison possible de deux ans et testez-le sur une année de maintien (par exemple 2015, 2016 contre 2017, 2015, 2017 contre 2016, etc.) - cela vous montrera comment les changements d'année affectent votre modèle,
  • stabilité future des prédictions - entraînez votre modèle sur un sous-ensemble de semaines/mois/années et testez-le en utilisant un résultat de semaine/mois/année suivant (par exemple, entraînez-le en janvier 2015, janvier 2016 et janvier 2017 et testez-le en utilisant les données de février 2015, février 2016, février 2017, etc.)
  • stabilité du mois - modèle de train en gardant un certain mois dans un ensemble de test.

Bien sûr, vous pouvez essayer un autre blocage.

Cinquièmement, et c'est la partie qui est probablement la plus floue pour moi - comment puis-je utiliser les résultats réels pour effectuer des prédictions? Supposons que j'utilise return_sequences = False et que je m'entraîne sur les trois années en trois lots (à chaque fois jusqu'en novembre) dans le but de former le modèle pour prédire la valeur suivante (décembre 2014, décembre 2015, décembre 2016). Si je veux utiliser ces résultats en 2017, comment cela fonctionne-t-il réellement? Si je l'ai bien compris, la seule chose que je puisse faire dans ce cas est de nourrir le modèle de tous les points de données de janvier à novembre 2017 et cela me donnera une prédiction pour décembre 2017. Est-ce correct? Cependant, si je devais utiliser return_sequences = True, puis formé sur toutes les données jusqu'en décembre 2016, serais-je alors en mesure d'obtenir une prédiction pour janvier 2017 simplement en donnant au modèle les fonctionnalités observées en janvier 2017? Ou dois-je également lui donner les 12 mois avant janvier 2017? Qu'en est-il de février 2017, dois-je en plus donner la valeur pour 2017, plus 11 mois avant cela? (Si on dirait que je suis confus, c'est parce que je le suis!)

Cela dépend de la façon dont vous avez construit votre modèle:

  • si vous avez utilisé return_sequences=True vous devez le réécrire pour avoir return_sequence=False ou simplement prendre la sortie et ne considérer que la dernière étape du résultat,
  • si vous avez utilisé une fenêtre fixe - il vous suffit d'alimenter une fenêtre avant la prédiction pour modéliser,
  • si vous avez utilisé une longueur variable - vous pouvez alimenter tous les pas de temps procédant à la période de prédiction que vous souhaitez (mais je vous conseille de nourrir au moins 7 jours de procédure).

    Enfin, selon la structure que je devrais utiliser, comment dois-je procéder dans Keras? Ce que je pense en ce moment, c'est quelque chose du genre: (bien que ce ne soit que pour un seul produit, cela ne résout pas le fait d'avoir tous les produits dans le même modèle)

Ici - plus d'informations sur le type de modèle que vous avez choisi est nécessaire.

13
Marcin Możejko

Question 1

Il existe plusieurs approches pour ce problème. Celui que vous proposez semble être une fenêtre coulissante.

Mais en fait, vous n'avez pas besoin de découper la dimension temporelle, vous pouvez saisir les 3 années à la fois. Vous pouvez trancher la dimension des produits, au cas où votre lot deviendrait trop gros pour la mémoire et la vitesse.

Vous pouvez travailler avec un seul tableau de forme (products, time, features)

Question 2

Oui, il est logique d'utiliser return_sequences=True.

Si j'ai bien compris votre question, vous avez y prévisions pour chaque jour, non?

Question 3

C'est vraiment une question ouverte. Toutes les approches ont leurs avantages.

Mais si vous envisagez de rassembler toutes les fonctionnalités du produit, ces caractéristiques étant de nature différente, vous devriez probablement étendre toutes les fonctionnalités possibles comme s'il y avait un gros vecteur unique tenant compte de toutes les fonctionnalités de tous les produits.

Si chaque produit a des fonctionnalités indépendantes qui ne s'appliquent qu'à lui-même, l'idée de créer des modèles individuels pour chaque produit ne me semble pas insensée.

Vous pourriez également penser à faire de l'identifiant du produit une entrée vectorielle unique et à utiliser un seul modèle.

Question 4

Selon l'approche que vous choisissez, vous pouvez:

  • Fractionner certains produits en tant que données de validation
  • Laisser la dernière partie des pas de temps comme données de validation
  • Essayez une méthode de validation croisée laissant différentes longueurs pour la formation et le test (plus les données de test sont longues, plus l'erreur est importante, cependant, vous voudrez peut-être recadrer ces données de test pour avoir une longueur fixe)

Question 5

Il peut également y avoir de nombreuses approches.

Il existe des approches où vous utilisez des fenêtres coulissantes. Vous entraînez votre modèle pour des durées fixes.

Et il existe des approches où vous entraînez les couches LSTM avec toute la longueur. Dans ce cas, vous devez d'abord prédire la totalité de la partie connue, puis commencer à prédire la partie inconnue.

Ma question: les données X sont-elles connues pour la période où vous devez prédire Y? De X est également inconnu pendant cette période, vous devez donc également prévoir X?

Question 6

Je vous recommande de jeter un coup d'œil à cette question et à sa réponse: Comment gérer les prévisions chronologiques en plusieurs étapes en LSTM multivarié en keras

Voir aussi ce cahier qui parvient à démontrer l'idée: https://github.com/danmoller/TestRepo/blob/master/TestBookLSTM.ipynb

Dans ce cahier, cependant, j'ai utilisé une approche qui met X et Y en entrée. Et nous prédisons le futur X et Y.

Vous pouvez essayer de créer un modèle (si c'est le cas) uniquement pour prédire X. Puis un deuxième modèle pour prédire Y à partir de X.

Dans un autre cas (si vous avez déjà toutes les données X, pas besoin de prédire X), vous pouvez créer un modèle qui ne prédit que Y à partir de X. (Vous suivriez toujours une partie de la méthode dans le cahier, où vous prédisez d'abord le Y déjà connu juste pour que votre modèle soit ajusté à l'endroit où il se trouve, alors vous prédisez le Y inconnu) - Cela peut être fait en une seule entrée X pleine longueur (qui contient le X de formation au début et le test X à la fin).

Réponse bonus

Savoir quelle approche et quel type de modèle choisir est probablement la réponse exacte pour gagner le concours ... donc, il n'y a pas de meilleure réponse à cette question, chaque concurrent essaie de trouver cette réponse.

4
Daniel Möller

Suite aux deux réponses déjà fournies, je pense que vous devriez consulter cet article d'Amazon Research sur les prévisions de ventes à l'aide de LSTM pour voir comment elles gèrent les problèmes que vous avez mentionnés:

https://arxiv.org/abs/1704.0411

En outre, je dois également souligner qu'une régularisation appropriée est extrêmement importante lors de l'utilisation de réseaux récurrents, car leur capacité à sur-équiper peut être vraiment spectaculaire. Vous voudrez peut-être jeter un oeil à "décrochage récurrent variationnel" comme décrit dans cet article

https://arxiv.org/abs/1512.05287

Remarque: cela a déjà été implémenté dans Tensorflow!

0
Martin Keller