Ricerca nel sito web

Come sviluppare modelli di rete neurale convoluzionale per la previsione delle serie temporali


I modelli di rete neurale convoluzionale, o CNN in breve, possono essere applicati alla previsione delle serie temporali.

Esistono molti tipi di modelli CNN che possono essere utilizzati per ogni tipo specifico di problema di previsione delle serie temporali.

In questo tutorial scoprirai come sviluppare una suite di modelli CNN per una serie di problemi di previsione di serie temporali standard.

L'obiettivo di questo tutorial è fornire esempi autonomi di ciascun modello su ciascun tipo di problema di serie temporali come modello che è possibile copiare e adattare per il problema specifico di previsione delle serie temporali.

Dopo aver completato questo tutorial, saprai:

  • Come sviluppare modelli CNN per la previsione di serie temporali univariate.
  • Come sviluppare modelli CNN per la previsione di serie temporali multivariate.
  • Come sviluppare modelli CNN per la previsione di serie temporali a più fasi.

Questo è un post ampio e importante; potresti volerlo aggiungere ai segnalibri per riferimento futuro.

Avvia il tuo progetto con il mio nuovo libro Deep Learning for Time Series Forecasting, che include tutorial passo passo e i file codice sorgente Python per tutti gli esempi.

Cominciamo.

Panoramica dell'esercitazione

In questo tutorial esploreremo come sviluppare una suite di diversi tipi di modelli CNN per la previsione delle serie temporali.

I modelli vengono dimostrati su piccoli problemi di serie temporali inventati per dare l'idea del tipo di problema di serie temporali da affrontare. La configurazione scelta dei modelli è arbitraria e non ottimizzata per ciascun problema; non era quello l'obiettivo.

Questo tutorial è diviso in quattro parti; sono:

  1. Modelli CNN univariati
  2. Modelli CNN multivariati
  3. Modelli CNN a più fasi
  4. Modelli CNN multivariati e multifase

Modelli CNN univariati

Sebbene tradizionalmente sviluppate per dati di immagini bidimensionali, le CNN possono essere utilizzate per modellare problemi di previsione di serie temporali univariate.

Le serie temporali univariate sono set di dati costituiti da una singola serie di osservazioni con un ordinamento temporale ed è necessario un modello per apprendere dalle serie di osservazioni passate per prevedere il valore successivo nella sequenza.

Questa sezione è divisa in due parti; sono:

  1. Preparazione dei dati
  2. Modello CNN

Preparazione dei dati

Prima di poter modellare una serie univariata, è necessario prepararla.

Il modello CNN apprenderà una funzione che mappa una sequenza di osservazioni passate come input per un'osservazione di output. Pertanto, la sequenza di osservazioni deve essere trasformata in molteplici esempi da cui il modello può imparare.

Consideriamo una data sequenza univariata:

[10, 20, 30, 40, 50, 60, 70, 80, 90]

Possiamo dividere la sequenza in più modelli di input/output chiamati campioni, in cui tre passaggi temporali vengono utilizzati come input e un passaggio temporale viene utilizzato come output per la previsione a un passaggio che viene appresa.

X,				y
10, 20, 30		40
20, 30, 40		50
30, 40, 50		60
...

La funzione split_sequence() riportata di seguito implementa questo comportamento e dividerà una determinata sequenza univariata in più campioni in cui ciascun campione ha un numero specificato di passaggi temporali e l'output è un singolo passaggio temporale.

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Possiamo dimostrare questa funzione sul nostro piccolo set di dati artificioso sopra.

L'esempio completo è elencato di seguito.

# univariate data preparation
from numpy import array

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 3
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

L'esecuzione dell'esempio divide la serie univariata in sei campioni in cui ciascun campione ha tre fasi temporali di input e una fase temporale di output.

[10 20 30] 40
[20 30 40] 50
[30 40 50] 60
[40 50 60] 70
[50 60 70] 80
[60 70 80] 90

Ora che sappiamo come preparare una serie univariata per la modellazione, proviamo a sviluppare un modello CNN in grado di apprendere la mappatura degli input in output.

Modello CNN

Una CNN unidimensionale è un modello CNN che ha uno strato convoluzionale nascosto che opera su una sequenza 1D. Questo è seguito forse da un secondo strato convoluzionale in alcuni casi, come sequenze di input molto lunghe, e quindi da uno strato di pooling il cui compito è quello di distillare l'output dello strato convoluzionale negli elementi più salienti.

Gli strati convoluzionali e di pooling sono seguiti da uno strato denso completamente connesso che interpreta le caratteristiche estratte dalla parte convoluzionale del modello. Uno strato appiattito viene utilizzato tra gli strati convoluzionali e lo strato denso per ridurre le mappe delle caratteristiche a un singolo vettore unidimensionale.

Possiamo definire un modello CNN 1D per la previsione di serie temporali univariate come segue.

# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')

La chiave nella definizione è la forma dell'input; questo è ciò che il modello si aspetta come input per ciascun campione in termini di numero di passaggi temporali e numero di funzionalità.

Stiamo lavorando con una serie univariata, quindi il numero di caratteristiche è uno, per una variabile.

Il numero di passaggi temporali come input è il numero che abbiamo scelto durante la preparazione del nostro set di dati come argomento per la funzione split_sequence().

La forma di input per ciascun campione è specificata nell'argomento input_shape nella definizione del primo livello nascosto.

Abbiamo quasi sempre più campioni, pertanto il modello si aspetta che il componente di input dei dati di addestramento abbia le dimensioni o la forma:

[samples, timesteps, features]

La nostra funzione split_sequence() nella sezione precedente restituisce la X con la forma [samples, timessteps], quindi possiamo facilmente rimodellarla per avere una dimensione aggiuntiva per quella caratteristica .

# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))

La CNN in realtà non vede i dati come aventi fasi temporali, ma li tratta come una sequenza su cui è possibile eseguire operazioni di lettura convoluzionale, come un'immagine unidimensionale.

In questo esempio, definiamo uno strato convoluzionale con 64 mappe di filtro e una dimensione del kernel pari a 2. Questo è seguito da uno strato di pooling massimo e da uno strato denso per interpretare la funzionalità di input. Viene specificato un livello di output che prevede un singolo valore numerico.

Il modello è adattato utilizzando l’efficiente versione Adam della discesa stocastica del gradiente e ottimizzato utilizzando la funzione di perdita dell’errore quadratico medio, o ‘mse‘.

Una volta definito il modello, possiamo adattarlo al set di dati di training.

# fit model
model.fit(X, y, epochs=1000, verbose=0)

Dopo che il modello è stato adattato, possiamo usarlo per fare una previsione.

Possiamo prevedere il valore successivo nella sequenza fornendo l'input:

[70, 80, 90]

E aspettarsi che il modello preveda qualcosa del tipo:

[100]

Il modello prevede che la forma dell'input sia tridimensionale con [campioni, passaggi temporali, caratteristiche], pertanto dobbiamo rimodellare il singolo campione di input prima di effettuare la previsione.

# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)

Possiamo collegare tutto questo insieme e dimostrare come sviluppare un modello CNN 1D per la previsione di serie temporali univariate e fare un'unica previsione.

# univariate cnn example
from numpy import array
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 3
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=1000, verbose=0)
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

L'esecuzione dell'esempio prepara i dati, adatta il modello e fa una previsione.

Nota: i risultati possono variare a causa della natura stocastica dell'algoritmo o della procedura di valutazione o delle differenze nella precisione numerica. Considera l'idea di eseguire l'esempio alcune volte e confrontare il risultato medio.

Possiamo vedere che il modello prevede il valore successivo nella sequenza.

[[101.67965]]

Modelli CNN multivariati

Per dati di serie temporali multivariati si intendono i dati in cui è presente più di un'osservazione per ciascuna fase temporale.

Esistono due modelli principali che potremmo richiedere con dati di serie temporali multivariate; sono:

  1. Serie di ingressi multipli.
  2. Serie parallele multiple.

Diamo un'occhiata a ciascuno di essi a turno.

Serie di ingressi multipli

Un problema può avere due o più serie temporali di input parallele e una serie temporale di output che dipende dalla serie temporale di input.

Le serie temporali di input sono parallele perché ciascuna serie presenta osservazioni negli stessi passaggi temporali.

Possiamo dimostrarlo con un semplice esempio di due serie temporali di input parallele in cui la serie di output è la semplice addizione delle serie di input.

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])

Possiamo rimodellare queste tre matrici di dati come un singolo set di dati in cui ogni riga è un passaggio temporale e ogni colonna è una serie temporale separata.

Questo è un modo standard per archiviare serie temporali parallele in un file CSV.

# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))

L'esempio completo è elencato di seguito.

# multivariate data preparation
from numpy import array
from numpy import hstack
# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
print(dataset)

L'esecuzione dell'esempio stampa il set di dati con una riga per fase temporale e una colonna per ciascuna delle due serie temporali parallele di input e una di output.

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

Come con le serie temporali univariate, dobbiamo strutturare questi dati in campioni con campioni di input e output.

Un modello CNN 1D necessita di un contesto sufficiente per apprendere una mappatura da una sequenza di input a un valore di output. Le CNN possono supportare serie temporali di input parallele come canali separati, come i componenti rosso, verde e blu di un'immagine. Pertanto, dobbiamo suddividere i dati in campioni mantenendo l'ordine delle osservazioni nelle due sequenze di input.

Se scegliessimo tre fasi temporali di input, il primo campione apparirebbe come segue:

Ingresso:

10, 15
20, 25
30, 35

Produzione :

65

Cioè, i primi tre passaggi temporali di ciascuna serie parallela vengono forniti come input al modello e il modello li associa al valore nella serie di output al terzo passaggio temporale, in questo caso, 65.

Possiamo vedere che, nel trasformare le serie temporali in campioni di input/output per addestrare il modello, dovremo scartare alcuni valori dalle serie temporali di output laddove non abbiamo valori nelle serie temporali di input nelle fasi temporali precedenti. A sua volta, la scelta della dimensione del numero di fasi temporali di input avrà un effetto importante sulla quantità di dati di training utilizzati.

Possiamo definire una funzione denominata split_sequences() che prenderà un set di dati come lo abbiamo definito con righe per passaggi temporali e colonne per serie parallele e restituirà campioni di input/output.

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Possiamo testare questa funzione sul nostro set di dati utilizzando tre passaggi temporali per ciascuna serie temporale di input come input.

L'esempio completo è elencato di seguito.

# multivariate data preparation
from numpy import array
from numpy import hstack

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

Eseguendo l'esempio viene prima stampata la forma dei componenti X e y.

Possiamo vedere che il componente X ha una struttura tridimensionale.

La prima dimensione è il numero di campioni, in questo caso 7. La seconda dimensione è il numero di passaggi temporali per campione, in questo caso 3, il valore specificato nella funzione. Infine, l'ultima dimensione specifica il numero di serie temporali parallele o il numero di variabili, in questo caso 2 per le due serie parallele.

Questa è l'esatta struttura tridimensionale prevista da una CNN 1D come input. I dati sono pronti per l'uso senza ulteriore rimodellamento.

Possiamo quindi vedere che vengono stampati l'input e l'output per ciascun campione, mostrando i tre passaggi temporali per ciascuna delle due serie di input e l'output associato per ciascun campione.

(7, 3, 2) (7,)

[[10 15]
 [20 25]
 [30 35]] 65
[[20 25]
 [30 35]
 [40 45]] 85
[[30 35]
 [40 45]
 [50 55]] 105
[[40 45]
 [50 55]
 [60 65]] 125
[[50 55]
 [60 65]
 [70 75]] 145
[[60 65]
 [70 75]
 [80 85]] 165
[[70 75]
 [80 85]
 [90 95]] 185

Siamo ora pronti per adattare un modello CNN 1D su questi dati, specificando il numero previsto di passaggi temporali e di caratteristiche previste per ciascun campione di input, in questo caso rispettivamente tre e due.

# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')

Quando si effettua una previsione, il modello prevede tre fasi temporali per due serie temporali di input.

Possiamo prevedere il valore successivo nella serie di output fornendo i valori di input di:

80,	 85
90,	 95
100, 105

La forma di un campione con tre fasi temporali e due variabili deve essere [1, 3, 2].

Ci aspetteremmo che il valore successivo nella sequenza sia 100 + 105 o 205.

# demonstrate prediction
x_input = array([[80, 85], [90, 95], [100, 105]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)

L'esempio completo è elencato di seguito.

# multivariate cnn example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=1000, verbose=0)
# demonstrate prediction
x_input = array([[80, 85], [90, 95], [100, 105]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Nota: i risultati possono variare a causa della natura stocastica dell'algoritmo o della procedura di valutazione o delle differenze nella precisione numerica. Considera l'idea di eseguire l'esempio alcune volte e confrontare il risultato medio.

L'esecuzione dell'esempio prepara i dati, adatta il modello e fa una previsione.

[[206.0161]]

Esiste un altro modo più elaborato per modellare il problema.

Ciascuna serie di input può essere gestita da una CNN separata e l'output di ciascuno di questi sottomodelli può essere combinato prima che venga fatta una previsione per la sequenza di output.

Possiamo riferirci a questo come un modello CNN multi-testa. Può offrire maggiore flessibilità o prestazioni migliori a seconda delle specificità del problema che viene modellato. Ad esempio, consente di configurare ciascun sottomodello in modo diverso per ciascuna serie di input, come il numero di mappe di filtro e la dimensione del kernel.

Questo tipo di modello può essere definito in Keras utilizzando l'API funzionale Keras.

Innanzitutto, possiamo definire il primo modello di input come una CNN 1D con un livello di input che prevede vettori con n_steps e 1 feature.

# first input model
visible1 = Input(shape=(n_steps, n_features))
cnn1 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible1)
cnn1 = MaxPooling1D(pool_size=2)(cnn1)
cnn1 = Flatten()(cnn1)

Possiamo definire il secondo sottomodello di input allo stesso modo.

# second input model
visible2 = Input(shape=(n_steps, n_features))
cnn2 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible2)
cnn2 = MaxPooling1D(pool_size=2)(cnn2)
cnn2 = Flatten()(cnn2)

Ora che entrambi i sottomodelli di input sono stati definiti, possiamo unire l'output di ciascun modello in un lungo vettore che può essere interpretato prima di fare una previsione per la sequenza di output.

# merge input models
merge = concatenate([cnn1, cnn2])
dense = Dense(50, activation='relu')(merge)
output = Dense(1)(dense)

Possiamo quindi collegare insieme gli input e gli output.

model = Model(inputs=[visible1, visible2], outputs=output)

L'immagine seguente fornisce uno schema dell'aspetto di questo modello, inclusa la forma degli input e degli output di ciascun livello.

Questo modello richiede che l'input venga fornito come elenco di due elementi in cui ciascun elemento nell'elenco contiene dati per uno dei sottomodelli.

Per raggiungere questo obiettivo, possiamo dividere i dati di input 3D in due array separati di dati di input; ovvero da un array con la forma [7, 3, 2] a due array 3D con [7, 3, 1]

# one time series per head
n_features = 1
# separate input data
X1 = X[:, :, 0].reshape(X.shape[0], X.shape[1], n_features)
X2 = X[:, :, 1].reshape(X.shape[0], X.shape[1], n_features)

Questi dati possono quindi essere forniti per adattare il modello.

# fit model
model.fit([X1, X2], y, epochs=1000, verbose=0)

Allo stesso modo, dobbiamo preparare i dati per un singolo campione come due array bidimensionali separati quando si effettua una singola previsione in un unico passaggio.

x_input = array([[80, 85], [90, 95], [100, 105]])
x1 = x_input[:, 0].reshape((1, n_steps, n_features))
x2 = x_input[:, 1].reshape((1, n_steps, n_features))

Possiamo collegare tutto questo insieme; l'esempio completo è elencato di seguito.

# multivariate multi-headed 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from keras.layers.merge import concatenate

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# one time series per head
n_features = 1
# separate input data
X1 = X[:, :, 0].reshape(X.shape[0], X.shape[1], n_features)
X2 = X[:, :, 1].reshape(X.shape[0], X.shape[1], n_features)
# first input model
visible1 = Input(shape=(n_steps, n_features))
cnn1 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible1)
cnn1 = MaxPooling1D(pool_size=2)(cnn1)
cnn1 = Flatten()(cnn1)
# second input model
visible2 = Input(shape=(n_steps, n_features))
cnn2 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible2)
cnn2 = MaxPooling1D(pool_size=2)(cnn2)
cnn2 = Flatten()(cnn2)
# merge input models
merge = concatenate([cnn1, cnn2])
dense = Dense(50, activation='relu')(merge)
output = Dense(1)(dense)
model = Model(inputs=[visible1, visible2], outputs=output)
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit([X1, X2], y, epochs=1000, verbose=0)
# demonstrate prediction
x_input = array([[80, 85], [90, 95], [100, 105]])
x1 = x_input[:, 0].reshape((1, n_steps, n_features))
x2 = x_input[:, 1].reshape((1, n_steps, n_features))
yhat = model.predict([x1, x2], verbose=0)
print(yhat)

Nota: i risultati possono variare a causa della natura stocastica dell'algoritmo o della procedura di valutazione o delle differenze nella precisione numerica. Considera l'idea di eseguire l'esempio alcune volte e confrontare il risultato medio.

L'esecuzione dell'esempio prepara i dati, adatta il modello e fa una previsione.

[[205.871]]

Serie parallele multiple

Un problema di serie temporali alternative si verifica nel caso in cui siano presenti più serie temporali parallele e per ciascuna sia necessario prevedere un valore.

Ad esempio, dati i dati della sezione precedente:

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

Potremmo voler prevedere il valore di ciascuna delle tre serie temporali per il passaggio temporale successivo.

Questa potrebbe essere definita previsione multivariata.

Ancora una volta, i dati devono essere suddivisi in campioni di input/output per addestrare un modello.

Il primo campione di questo set di dati sarebbe:

Ingresso:

10, 15, 25
20, 25, 45
30, 35, 65

Produzione :

40, 45, 85

La funzione split_sequences() riportata di seguito dividerà più serie temporali parallele con righe per passaggi temporali e una serie per colonna nella forma di input/output richiesta.

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Possiamo dimostrarlo con il problema inventato; l'esempio completo è elencato di seguito.

# multivariate output data prep
from numpy import array
from numpy import hstack

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

L'esecuzione dell'esempio stampa innanzitutto la forma dei componenti X e y preparati.

La forma di X è tridimensionale, compreso il numero di campioni (6), il numero di passaggi temporali scelti per campione (3) e il numero di serie temporali o caratteristiche parallele (3).

La forma di y è bidimensionale come potremmo aspettarci per il numero di campioni (6) e il numero di variabili temporali per campione da prevedere (3).

I dati sono pronti per l'uso in un modello CNN 1D che prevede forme di input tridimensionali e di output bidimensionali per i componenti X e y di ciascun campione.

Quindi, ciascuno dei campioni viene stampato mostrando i componenti di input e output di ciascun campione.

(6, 3, 3) (6, 3)

[[10 15 25]
 [20 25 45]
 [30 35 65]] [40 45 85]
[[20 25 45]
 [30 35 65]
 [40 45 85]] [ 50  55 105]
[[ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]] [ 60  65 125]
[[ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]] [ 70  75 145]
[[ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]] [ 80  85 165]
[[ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]] [ 90  95 185]

Ora siamo pronti per adattare un modello CNN 1D a questi dati.

In questo modello, il numero di passaggi temporali e le serie parallele (caratteristiche) vengono specificati per il livello di input tramite l'argomento input_shape.

Il numero di serie parallele viene utilizzato anche nella specifica del numero di valori da prevedere dal modello nello strato di output; ancora una volta, questo è tre.

# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_features))
model.compile(optimizer='adam', loss='mse')

Possiamo prevedere il valore successivo in ciascuna delle tre serie parallele fornendo un input di tre fasi temporali per ciascuna serie.

70, 75, 145
80, 85, 165
90, 95, 185

La forma dell'input per effettuare una singola previsione deve essere 1 campione, 3 fasi temporali e 3 caratteristiche oppure [1, 3, 3].

# demonstrate prediction
x_input = array([[70,75,145], [80,85,165], [90,95,185]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)

Ci aspetteremmo che l'output del vettore sia:

[100, 105, 205]

Possiamo collegare tutto questo insieme e dimostrare una CNN 1D per la previsione di serie temporali di output multivariate di seguito.

# multivariate output 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_features))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=3000, verbose=0)
# demonstrate prediction
x_input = array([[70,75,145], [80,85,165], [90,95,185]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Nota: i risultati possono variare a causa della natura stocastica dell'algoritmo o della procedura di valutazione o delle differenze nella precisione numerica. Considera l'idea di eseguire l'esempio alcune volte e confrontare il risultato medio.

L'esecuzione dell'esempio prepara i dati, adatta il modello e fa una previsione.

[[100.11272 105.32213 205.53436]]

Come per le serie di input multipli, esiste un altro modo più elaborato per modellare il problema.

Ciascuna serie di output può essere gestita da un modello CNN di output separato.

Possiamo riferirci a questo come un modello CNN multi-uscita. Può offrire maggiore flessibilità o prestazioni migliori a seconda delle specificità del problema che viene modellato.

Questo tipo di modello può essere definito in Keras utilizzando l'API funzionale Keras.

Innanzitutto, possiamo definire il primo modello di input come un modello CNN 1D.

# define model
visible = Input(shape=(n_steps, n_features))
cnn = Conv1D(filters=64, kernel_size=2, activation='relu')(visible)
cnn = MaxPooling1D(pool_size=2)(cnn)
cnn = Flatten()(cnn)
cnn = Dense(50, activation='relu')(cnn)

Possiamo quindi definire un livello di output per ciascuna delle tre serie che desideriamo prevedere, dove ciascun sottomodello di output prevederà un singolo passaggio temporale.

# define output 1
output1 = Dense(1)(cnn)
# define output 2
output2 = Dense(1)(cnn)
# define output 3
output3 = Dense(1)(cnn)

Possiamo quindi collegare insieme i livelli di input e output in un unico modello.

# tie together
model = Model(inputs=visible, outputs=[output1, output2, output3])
model.compile(optimizer='adam', loss='mse')

Per rendere chiara l'architettura del modello, lo schema seguente mostra chiaramente i tre livelli di output separati del modello e le forme di input e output di ciascun livello.

Durante l'addestramento del modello, saranno necessari tre array di output separati per campione. Possiamo ottenere questo risultato convertendo i dati di training in output che hanno la forma [7, 3] in tre array con la forma [7, 1].

# separate output
y1 = y[:, 0].reshape((y.shape[0], 1))
y2 = y[:, 1].reshape((y.shape[0], 1))
y3 = y[:, 2].reshape((y.shape[0], 1))

Questi array possono essere forniti al modello durante l'addestramento.

# fit model
model.fit(X, [y1,y2,y3], epochs=2000, verbose=0)

Mettendo insieme tutto questo, l'esempio completo è elencato di seguito.

# multivariate output 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# separate output
y1 = y[:, 0].reshape((y.shape[0], 1))
y2 = y[:, 1].reshape((y.shape[0], 1))
y3 = y[:, 2].reshape((y.shape[0], 1))
# define model
visible = Input(shape=(n_steps, n_features))
cnn = Conv1D(filters=64, kernel_size=2, activation='relu')(visible)
cnn = MaxPooling1D(pool_size=2)(cnn)
cnn = Flatten()(cnn)
cnn = Dense(50, activation='relu')(cnn)
# define output 1
output1 = Dense(1)(cnn)
# define output 2
output2 = Dense(1)(cnn)
# define output 3
output3 = Dense(1)(cnn)
# tie together
model = Model(inputs=visible, outputs=[output1, output2, output3])
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, [y1,y2,y3], epochs=2000, verbose=0)
# demonstrate prediction
x_input = array([[70,75,145], [80,85,165], [90,95,185]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Nota: i risultati possono variare a causa della natura stocastica dell'algoritmo o della procedura di valutazione o delle differenze nella precisione numerica. Considera l'idea di eseguire l'esempio alcune volte e confrontare il risultato medio.

L'esecuzione dell'esempio prepara i dati, adatta il modello e fa una previsione.

[array([[100.96118]], dtype=float32),
 array([[105.502686]], dtype=float32),
 array([[205.98045]], dtype=float32)]

Modelli CNN a più fasi

In pratica, c'è poca differenza rispetto al modello CNN 1D nel prevedere un output vettoriale che rappresenta diverse variabili di output (come nell'esempio precedente) o un output vettoriale che rappresenta più fasi temporali di una variabile.

Tuttavia, esistono differenze sottili e importanti nel modo in cui vengono preparati i dati di addestramento. In questa sezione, dimostreremo il caso di sviluppo di un modello di previsione a più fasi utilizzando un modello vettoriale.

Prima di esaminare le specifiche del modello, esaminiamo innanzitutto la preparazione dei dati per la previsione in più fasi.

Preparazione dei dati

Come per la previsione in una fase, una serie temporale utilizzata per la previsione di serie temporali in più fasi deve essere suddivisa in campioni con componenti di input e output.

Sia i componenti di input che quelli di output saranno costituiti da più fasi temporali e potrebbero o meno avere lo stesso numero di fasi.

Ad esempio, data la serie temporale univariata:

[10, 20, 30, 40, 50, 60, 70, 80, 90]

Potremmo utilizzare gli ultimi tre passaggi temporali come input e prevedere i successivi due passaggi temporali.

Il primo campione sarebbe il seguente:

Ingresso:

[10, 20, 30]

Produzione :

[40, 50]

La funzione split_sequence() riportata di seguito implementa questo comportamento e dividerà una determinata serie temporale univariata in campioni con un numero specificato di passaggi temporali di input e output.

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Possiamo dimostrare questa funzione sul piccolo set di dati artificioso.

L'esempio completo è elencato di seguito.

# multi-step data preparation
from numpy import array

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# split into samples
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

L'esecuzione dell'esempio divide la serie univariata in passaggi temporali di input e output e stampa i componenti di input e output di ciascuno.

[10 20 30] [40 50]
[20 30 40] [50 60]
[30 40 50] [60 70]
[40 50 60] [70 80]
[50 60 70] [80 90]

Ora che sappiamo come preparare i dati per la previsione in più fasi, diamo un'occhiata a un modello CNN 1D in grado di apprendere questa mappatura.

Modello di output vettoriale

La CNN 1D può emettere direttamente un vettore che può essere interpretato come una previsione a più fasi.

Questo approccio è stato visto nella sezione precedente in cui un passo temporale di ciascuna serie temporale di output è stato previsto come vettore.

Come con i modelli CNN 1D per dati univariati nella sezione precedente, i campioni preparati devono prima essere rimodellati. La CNN si aspetta che i dati abbiano una struttura tridimensionale di [campioni, fasi temporali, caratteristiche] e, in questo caso, abbiamo solo una caratteristica, quindi la rimodellazione è semplice.

# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))

Con il numero di passaggi di input e output specificati nelle variabili n_steps_in e n_steps_out, possiamo definire un modello di previsione di serie temporali a più passaggi.

# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')

Il modello può fare una previsione per un singolo campione. Possiamo prevedere i due passaggi successivi oltre la fine del set di dati fornendo l'input:

[70, 80, 90]

Ci aspetteremmo che il risultato previsto sia:

[100, 110]

Come previsto dal modello, la forma del singolo campione di dati di input quando si effettua la previsione deve essere [1, 3, 1] per 1 campione, 3 fasi temporali dell'input e la singola caratteristica.

# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)

Mettendo insieme tutto questo, di seguito è elencata la CNN 1D per la previsione a più fasi con una serie temporale univariata.

# univariate multi-step vector-output 1d cnn example
from numpy import array
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# split into samples
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=2000, verbose=0)
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Nota: i risultati possono variare a causa della natura stocastica dell'algoritmo o della procedura di valutazione o delle differenze nella precisione numerica. Considera l'idea di eseguire l'esempio alcune volte e confrontare il risultato medio.

L'esecuzione dell'esempio prevede e stampa i due passaggi temporali successivi nella sequenza.

[[102.86651 115.08979]]

Modelli CNN multivariati e multifase

Nelle sezioni precedenti abbiamo esaminato la previsione di serie temporali univariata, multivariata e multifase.

È possibile combinare e abbinare i diversi tipi di modelli CNN 1D presentati finora per i diversi problemi. Questo vale anche per i problemi di previsione delle serie temporali che coinvolgono previsioni multivariate e multifase, ma potrebbe essere un po’ più impegnativo.

In questa sezione, esploreremo brevi esempi di preparazione e modellazione dei dati per la previsione multivariata di serie temporali a più fasi come modello per facilitare questa sfida, in particolare:

  1. Ingresso multiplo Uscita multifase.
  2. Ingresso parallelo multiplo e uscita multi-step.

Forse l’ostacolo più grande è nella preparazione dei dati, quindi è qui che concentreremo la nostra attenzione.

Ingresso multiplo Uscita multifase

Esistono problemi di previsione di serie temporali multivariate in cui la serie di output è separata ma dipendente dalla serie temporale di input e per la serie di output sono necessari più passaggi temporali.

Ad esempio, considera le nostre serie temporali multivariate da una sezione precedente:

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

Possiamo utilizzare tre fasi temporali precedenti di ciascuna delle due serie temporali di input per prevedere due fasi temporali della serie temporale di output.

Ingresso:

10, 15
20, 25
30, 35

Produzione :

65
85

La funzione split_sequences() riportata di seguito implementa questo comportamento.

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out-1
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1:out_end_ix, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Possiamo dimostrarlo sul nostro set di dati artificioso. L'esempio completo è elencato di seguito.

# multivariate multi-step data preparation
from numpy import array
from numpy import hstack

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out-1
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1:out_end_ix, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# convert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

L'esecuzione dell'esempio stampa innanzitutto la forma dei dati di training preparati.

Possiamo vedere che la forma della porzione di input dei campioni è tridimensionale, composta da sei campioni, con tre fasi temporali e due variabili per le due serie temporali di input.

La porzione di output dei campioni è bidimensionale per i sei campioni e i due passaggi temporali per ciascun campione da prevedere.

I campioni preparati vengono quindi stampati per confermare che i dati sono stati preparati come specificato.

(6, 3, 2) (6, 2)

[[10 15]
 [20 25]
 [30 35]] [65 85]
[[20 25]
 [30 35]
 [40 45]] [ 85 105]
[[30 35]
 [40 45]
 [50 55]] [105 125]
[[40 45]
 [50 55]
 [60 65]] [125 145]
[[50 55]
 [60 65]
 [70 75]] [145 165]
[[60 65]
 [70 75]
 [80 85]] [165 185]

Ora possiamo sviluppare un modello CNN 1D per previsioni multi-step.

In questo caso, dimostreremo un modello di output vettoriale. L'esempio completo è elencato di seguito.

# multivariate multi-step 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out-1
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1:out_end_ix, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# convert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=2000, verbose=0)
# demonstrate prediction
x_input = array([[70, 75], [80, 85], [90, 95]])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

L'esecuzione dell'esempio si adatta al modello e prevede i due passaggi temporali successivi della sequenza di output oltre il set di dati.

Ci aspetteremmo che i prossimi due passaggi siano [185, 205].

Nota: i risultati possono variare a causa della natura stocastica dell'algoritmo o della procedura di valutazione o delle differenze nella precisione numerica. Considera l'idea di eseguire l'esempio alcune volte e confrontare il risultato medio.

Si tratta di un’inquadratura impegnativa del problema con pochissimi dati e la versione del modello configurata arbitrariamente si avvicina molto.

[[185.57011 207.77893]]

Ingresso parallelo multiplo e uscita multi-step

Un problema con serie temporali parallele può richiedere la previsione di più passaggi temporali di ciascuna serie temporale.

Ad esempio, considera le nostre serie temporali multivariate da una sezione precedente:

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

Possiamo utilizzare gli ultimi tre passaggi temporali di ciascuna delle tre serie temporali come input per il modello e prevedere i successivi passaggi temporali di ciascuna delle tre serie temporali come output.

Il primo campione nel set di dati di addestramento sarebbe il seguente.

Ingresso:

10, 15, 25
20, 25, 45
30, 35, 65

Produzione :

40, 45, 85
50, 55, 105

La funzione split_sequences() riportata di seguito implementa questo comportamento.

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix:out_end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Possiamo dimostrare questa funzione sul piccolo set di dati artificioso.

L'esempio completo è elencato di seguito.

# multivariate multi-step data preparation
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import RepeatVector
from keras.layers import TimeDistributed

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix:out_end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# convert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

L'esecuzione dell'esempio stampa innanzitutto la forma del set di dati di addestramento preparato.

Possiamo vedere che sia gli elementi di input (X) che quelli di output (Y) del set di dati sono tridimensionali per il numero di campioni, fasi temporali e variabili o tempo parallelo serie rispettivamente.

Gli elementi di input e di output di ciascuna serie vengono quindi stampati fianco a fianco in modo da poter confermare che i dati sono stati preparati come previsto.

(5, 3, 3) (5, 2, 3)

[[10 15 25]
 [20 25 45]
 [30 35 65]] [[ 40  45  85]
 [ 50  55 105]]
[[20 25 45]
 [30 35 65]
 [40 45 85]] [[ 50  55 105]
 [ 60  65 125]]
[[ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]] [[ 60  65 125]
 [ 70  75 145]]
[[ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]] [[ 70  75 145]
 [ 80  85 165]]
[[ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]] [[ 80  85 165]
 [ 90  95 185]]

Ora possiamo sviluppare un modello CNN 1D per questo set di dati.

In questo caso utilizzeremo un modello di output vettoriale. Pertanto, dobbiamo appiattire la struttura tridimensionale della porzione di output di ciascun campione per addestrare il modello. Ciò significa che, invece di prevedere due passaggi per ciascuna serie, il modello viene addestrato e prevede di prevedere direttamente un vettore di sei numeri.

# flatten output
n_output = y.shape[1] * y.shape[2]
y = y.reshape((y.shape[0], n_output))

L'esempio completo è elencato di seguito.

# multivariate output multi-step 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix:out_end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# convert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
# flatten output
n_output = y.shape[1] * y.shape[2]
y = y.reshape((y.shape[0], n_output))
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_output))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=7000, verbose=0)
# demonstrate prediction
x_input = array([[60, 65, 125], [70, 75, 145], [80, 85, 165]])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

L'esecuzione dell'esempio si adatta al modello e prevede i valori per ciascuno dei tre passaggi temporali per i due passaggi temporali successivi oltre la fine del set di dati.

Ci aspetteremmo che i valori per queste serie e passaggi temporali siano i seguenti:

90, 95, 185
100, 105, 205

Nota: i risultati possono variare a causa della natura stocastica dell'algoritmo o della procedura di valutazione o delle differenze nella precisione numerica. Considera l'idea di eseguire l'esempio alcune volte e confrontare il risultato medio.

Possiamo vedere che la previsione del modello si avvicina ragionevolmente ai valori attesi.

[[ 90.47855 95.621284 186.02629 100.48118 105.80815 206.52821 ]]

Riepilogo

In questo tutorial hai scoperto come sviluppare una suite di modelli CNN per una serie di problemi di previsione di serie temporali standard.

Nello specifico, hai imparato:

  • Come sviluppare modelli CNN per la previsione di serie temporali univariate.
  • Come sviluppare modelli CNN per la previsione di serie temporali multivariate.
  • Come sviluppare modelli CNN per la previsione di serie temporali a più fasi.

Hai qualche domanda?
Poni le tue domande nei commenti qui sotto e farò del mio meglio per rispondere.

Articoli correlati