Per gli RNN (ad esempio, LSTM e GRU), l'input di layer è un elenco di timestep e ogni timestep è un tensore di funzionalità. Ciò significa che potresti avere un tensore di input come questo (in notazione 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 ],
...
]
Quindi assolutamente, puoi avere più funzionalità ad ogni timestep. Nella mia mente, il tempo è una caratteristica delle serie temporali: dove vivo, sembra essere una funzione del tempo. Quindi sarebbe abbastanza ragionevole codificare le informazioni meteorologiche come una delle tue caratteristiche in ogni timestep (con una codifica appropriata, come nuvoloso = 0, soleggiato = 1, ecc.).
Se disponi di dati non relativi a serie temporali, non ha davvero senso passarli attraverso LSTM. Forse l'LSTM funzionerà comunque, ma anche se lo fa, probabilmente arriverà al costo di una maggiore perdita / minore precisione per tempo di allenamento.
In alternativa, è possibile introdurre questo tipo di informazioni "extra" nel modello al di fuori di LSTM mediante livelli aggiuntivi. Potresti avere un flusso di dati come questo:
TIME_SERIES_INPUT ------> LSTM -------\
*---> MERGE ---> [more processing]
AUXILIARY_INPUTS --> [do something] --/
Quindi uniresti i tuoi ingressi ausiliari nelle uscite LSTM e proseguirai la tua rete da lì. Ora il tuo modello è semplicemente multi-input.
Ad esempio, supponiamo che nella tua particolare applicazione, mantieni solo l'ultimo output della sequenza di output LSTM. Diciamo che è un vettore di lunghezza 10. L'ingresso ausiliario potrebbe essere il tuo tempo codificato (quindi uno scalare). Il livello di unione potrebbe semplicemente aggiungere le informazioni meteorologiche ausiliarie alla fine del vettore di output LSTM per produrre un singolo vettore di lunghezza 11. Ma non è necessario solo mantenere l'ultimo timestep di output LSTM: se l'LSTM ha prodotto 100 timestep, ciascuno con un vettore di 10 funzioni, è ancora possibile puntare sulle informazioni meteorologiche ausiliarie, ottenendo 100 timestep, ciascuno costituito da un vettore di 11 punti dati.
La documentazione di Keras sulla sua API funzionale ha una buona panoramica di ciò.
In altri casi, come sottolinea @horaceT, potresti voler condizionare l'LSTM su dati non temporali. Ad esempio, prevedere il tempo domani, data la posizione. In questo caso, ecco tre suggerimenti, ciascuno con aspetti positivi / negativi:
Chiedi al primo timestep di contenere i tuoi dati di condizionamento, dal momento che "imposterà" effettivamente lo stato interno / nascosto del tuo RNN. Francamente, non lo farei, per una serie di motivi: i tuoi dati di condizionamento devono avere la stessa forma del resto delle tue funzionalità, rende più difficile creare RNN con stato (in termini di essere davvero attenti a tenere traccia del modo in cui alimenti i dati nella rete), la rete potrebbe "dimenticare" i dati di condizionamento con tempo sufficiente (ad es. sequenze di allenamento lunghe o sequenze di previsione lunghe), ecc.
Includi i dati come parte dei dati temporali stessi. Pertanto, ogni vettore di funzione in un determinato timestep include dati "principalmente" di serie temporali, ma i dati di condizionamento vengono quindi aggiunti alla fine di ciascun vettore di funzionalità. La rete imparerà a riconoscerlo? Probabilmente, ma anche in questo caso, stai creando un'attività di apprendimento più difficile inquinando i dati della sequenza con informazioni non sequenziali. Quindi scoraggerei anche questo.
Probabilmente l' approccio migliore sarebbe influenzare direttamente lo stato nascosto dell'RNN al tempo zero. Questo è l'approccio adottato da Karpathy e Fei-Fei e da Vinyals et al . È così che funziona:
- X⃗
- v⃗ = W x⃗ + b⃗ WB⃗
- v⃗
Questo approccio è il più "teoricamente" corretto, poiché condiziona correttamente l'RNN sugli input non temporali, risolve naturalmente il problema di forma ed evita inoltre di inquinare i timestep degli input con informazioni aggiuntive non temporali. Il rovescio della medaglia è che questo approccio richiede spesso un controllo a livello di grafico della tua architettura, quindi se stai usando un'astrazione di livello superiore come Keras, ti sarà difficile implementare se non aggiungi il tuo tipo di livello.