Main Content

La traduzione di questa pagina non è aggiornata. Fai clic qui per vedere l'ultima versione in inglese.

Previsione delle serie temporali tramite il Deep Learning

Questo esempio mostra come prevedere i dati delle serie temporali utilizzando una rete con memoria a breve e lungo termine (LSTM).

Una rete LSTM è una rete neurale ricorrente (RNN) che elabora i dati di input eseguendo un loop sulle fasi temporali e aggiornando lo stato RNN. Lo stato RNN contiene le informazioni ricordate in tutte le fasi temporali precedenti. È possibile utilizzare una rete neurale LSTM per prevedere i valori successivi di una serie o di una sequenza temporale utilizzando come input le fase temporali precedenti. Per addestrare una rete neurale LSTM per la previsione di serie temporali, è necessario addestrare una rete neurale LSTM di regressione con una sequenza di output, dove le risposte (target) sono le sequenze di addestramento con valori spostati di una fase temporale. In altre parole, in ciascuna fase temporale della sequenza di input, la rete neurale LSTM impara a prevedere il valore della fase temporale successiva.

Esistono due metodi di previsione: la previsione a loop aperto e la previsione a loop chiuso.

  • La previsione a loop aperto prevede la fase temporale successiva di una sequenza utilizzando solo i dati di input. Quando si effettuano le previsioni per le fasi temporali successive, si raccolgono i valori reali dalla sorgente dei dati e li si utilizzano come input. Ad esempio, si supponga di voler prevedere il valore per la fase temporale t di una sequenza utilizzando i dati raccolti nelle fasi temporali da 1 a t-1. Per fare previsioni per la fase temporale t+1, è necessario attendere che il valore reale per la fase temporale t sia registrato e quindi utilizzarlo come input per eseguire la previsione successiva. Utilizzare la previsione a loop aperto quando si dispone di valori reali da fornire alla RNN prima di effettuare la previsione successiva.

  • La previsione a loop chiuso prevede le fasi temporali successive di una sequenza utilizzando le previsioni precedenti come input. In questo caso, il modello non necessita dei valori reali per effettuare la previsione. Ad esempio, si supponga di voler prevedere i valori per le fasi temporali da t a t+k della sequenza utilizzando solo i dati raccolti nelle fasi temporali da 1 a t-1. Per fare previsioni per la fase temporale i, utilizzare il valore previsto per la fase temporale i-1 come input. Utilizzare la previsione a loop chiuso per prevedere più fasi temporali successive o quando non si dispone dei valori reali da fornire alla RNN prima di effettuare la previsione successiva.

Questa figura mostra un esempio di sequenza con valori previsti utilizzando la previsione a loop chiuso.

closedloop.png

Questo esempio utilizza l’insieme di dati waveform, che contiene 2000 forme d'onda generate sinteticamente di varia lunghezza con tre canali. L'esempio addestra una rete neurale LSTM a prevedere i valori futuri delle forme d'onda in base ai valori dati dalle fasi temporali precedenti, utilizzando sia la previsione a loop chiuso sia la previsione a loop aperto.

Caricamento dei dati

Caricare i dati di esempio da WaveformData.mat. I dati sono un array di sequenze di celle numObservations per 1, dove numObservations è il numero delle sequenze. Ciascuna sequenza è un array numerico numChannels per -numTimeSteps, dove numChannels è il numero di canali della sequenza e numTimeSteps è il numero di fasi temporali della sequenza.

load WaveformData

Visualizzare le dimensioni delle prime sequenze.

data(1:5)
ans=5×1 cell array
    {3×103 double}
    {3×136 double}
    {3×140 double}
    {3×124 double}
    {3×127 double}

Visualizzare il numero di canali. Per addestrare la rete neurale LSTM, ciascuna sequenza deve avere lo stesso numero di canali.

numChannels = size(data{1},1)
numChannels = 3

Visualizzare le prime sequenze in un grafico.

figure
tiledlayout(2,2)
for i = 1:4
    nexttile
    stackedplot(data{i}')

    xlabel("Time Step")
end

Suddividere i dati in insiemi di addestramento e di test. Utilizzare il 90% delle osservazioni per l'addestramento e il resto per il test.

numObservations = numel(data);
idxTrain = 1:floor(0.9*numObservations);
idxTest = floor(0.9*numObservations)+1:numObservations;
dataTrain = data(idxTrain);
dataTest = data(idxTest);

Preparazione dei dati per l’addestramento

Per prevedere i valori delle fasi temporali future di una sequenza, specificare i target come sequenze di addestramento e con valori spostati di una fase temporale. In altre parole, in ciascuna fase temporale della sequenza di input, la rete neurale LSTM impara a prevedere il valore della fase temporale successiva. I predittori sono le sequenze di addestramento senza la fase temporale finale.

for n = 1:numel(dataTrain)
    X = dataTrain{n};
    XTrain{n} = X(:,1:end-1);
    TTrain{n} = X(:,2:end);
end

Per un migliore adattamento e per evitare che l'addestramento diverga, normalizzare i predittori e i target in modo che abbiano media pari a zero e varianza unitaria. Quando si effettuano le previsioni, è necessario normalizzare anche i dati di test utilizzando le stesse statistiche dei dati di addestramento. Per calcolare facilmente la media e la deviazione standard su tutte le sequenze, concatenare le sequenze nella dimensione temporale.

muX = mean(cat(2,XTrain{:}),2);
sigmaX = std(cat(2,XTrain{:}),0,2);

muT = mean(cat(2,TTrain{:}),2);
sigmaT = std(cat(2,TTrain{:}),0,2);

for n = 1:numel(XTrain)
    XTrain{n} = (XTrain{n} - muX) ./ sigmaX;
    TTrain{n} = (TTrain{n} - muT) ./ sigmaT;
end

Definizione dell'architettura della rete neurale LSTM

Creare una rete neurale LSTM di regressione.

  • Utilizzare un livello di input in sequenza con una dimensione di input che corrisponda al numero di canali dei dati di input.

  • Utilizzare un livello LSTM con 128 unità nascoste. Il numero di unità nascoste determina quante informazioni sono apprese dal livello. L’utilizzo di un numero maggiore di unità nascoste può dare risultati più accurati, ma è più probabile che porti a un sovraadattamento dei dati di addestramento.

  • Per ottenere sequenze di output con lo stesso numero di canali dei dati di input, includere un livello completamente connesso con una dimensione di output che corrisponda al numero di canali dei dati di input.

  • Infine, includere un livello di regressione.

layers = [
    sequenceInputLayer(numChannels)
    lstmLayer(128)
    fullyConnectedLayer(numChannels)
    regressionLayer];

Specificazione delle opzioni di addestramento

Specificare le opzioni di addestramento.

  • Addestrare utilizzando l'ottimizzazione Adam.

  • Addestrare per 200 epoche. Per insiemi di dati più grandi, potrebbe non essere necessario l’addestramento per un numero così elevato di epoche per ottenere un buon adattamento.

  • In ciascun mini-batch, eseguire il riempimento a sinistra delle sequenze in modo che abbiano la stessa lunghezza. Il riempimento a sinistra impedisce alla RNN di prevedere i valori di riempimento alle estremità delle sequenze.

  • Mescolare i dati a ciascuna epoca.

  • Visualizzare l'andamento dell'addestramento in un grafico.

  • Disattivare l’output verboso.

options = trainingOptions("adam", ...
    MaxEpochs=200, ...
    SequencePaddingDirection="left", ...
    Shuffle="every-epoch", ...
    Plots="training-progress", ...
    Verbose=0);

Addestramento di una rete neurale ricorrente

Addestrare la rete neurale LSTM con le opzioni di addestramento specificate utilizzando la funzione trainNetwork.

net = trainNetwork(XTrain,TTrain,layers,options);

Test della rete neurale ricorrente

Preparare i dati di test per la previsione utilizzando gli stessi passaggi utilizzati per i dati di addestramento.

Normalizzare i dati di test utilizzando le statistiche calcolate dai dati di addestramento. Specificare i target come sequenze di test con valori spostati di una fase temporale e i predittori come sequenze di test senza la fase temporale finale.

for n = 1:size(dataTest,1)
    X = dataTest{n};
    XTest{n} = (X(:,1:end-1) - muX) ./ sigmaX;
    TTest{n} = (X(:,2:end) - muT) ./ sigmaT;
end

Eseguire le previsioni utilizzando i dati del test. Specificare le stesse opzioni di riempimento dell'addestramento.

YTest = predict(net,XTest,SequencePaddingDirection="left");

Per valutare la precisione per ogni sequenza di test, calcolare l'errore quadratico medio (RMSE) tra le previsioni e il target.

for i = 1:size(YTest,1)
    rmse(i) = sqrt(mean((YTest{i} - TTest{i}).^2,"all"));
end

Visualizzare gli errori in un istogramma. Valori più bassi indicano una maggiore precisione.

figure
histogram(rmse)
xlabel("RMSE")
ylabel("Frequency")

Calcolare l'RMSE medio su tutte le osservazioni del test.

mean(rmse)
ans = single
    0.5080

Previsione delle fasi temporali future

Data una serie o una sequenza temporale di input, per prevedere i valori di più fasi temporali future, utilizzare la funzione predictAndUpdateState per prevedere le fasi temporali una alla volta e aggiornare lo stato della RNN a ogni previsione. Per ogni previsione, utilizzare la previsione precedente come input della funzione.

Visualizzare una delle sequenze di test in un grafico.

idx = 2;
X = XTest{idx};
T = TTest{idx};

figure
stackedplot(X',DisplayLabels="Channel " + (1:numChannels))
xlabel("Time Step")
title("Test Observation " + idx)

Previsione a loop aperto

La previsione a loop aperto prevede la fase temporale successiva di una sequenza utilizzando solo i dati di input. Quando si effettuano le previsioni per le fasi temporali successive, si raccolgono i valori reali dalla sorgente dei dati e li si utilizzano come input. Ad esempio, si supponga di voler prevedere il valore per la fase temporale t di una sequenza utilizzando i dati raccolti nelle fasi temporali da 1 a t-1. Per fare previsioni per la fase temporale t+1, è necessario attendere che il valore reale per la fase temporale t sia registrato e quindi utilizzarlo come input per eseguire la previsione successiva. Utilizzare la previsione a loop aperto quando si dispone di valori reali da fornire alla RNN prima di effettuare la previsione successiva.

Inizializzare lo stato della RNN resettando prima lo stato utilizzando la funzione resetState, quindi fare una previsione iniziale utilizzando le prime poche fasi temporali dei dati di input. Aggiornare lo stato della RNN utilizzando le prime 75 fasi temporali dei dati di input.

net = resetState(net);
offset = 75;
[net,~] = predictAndUpdateState(net,X(:,1:offset));

Per prevedere ulteriori previsioni, eseguire un loop sulle fasi temporali e aggiornare lo stato della RNN utilizzando la funzione predictAndUpdateState. Prevedere i valori per le restanti fasi temporali dell'osservazione di test eseguendo un loop sulle fasi temporali dei dati di input e utilizzandoli come input della RNN. La prima previsione è il valore corrispondente alla fase temporale offset + 1.

numTimeSteps = size(X,2);
numPredictionTimeSteps = numTimeSteps - offset;
Y = zeros(numChannels,numPredictionTimeSteps);

for t = 1:numPredictionTimeSteps
    Xt = X(:,offset+t);
    [net,Y(:,t)] = predictAndUpdateState(net,Xt);
end

Confrontare le previsioni con i valori target.

figure
t = tiledlayout(numChannels,1);
title(t,"Open Loop Forecasting")

for i = 1:numChannels
    nexttile
    plot(T(i,:))
    hold on
    plot(offset:numTimeSteps,[T(i,offset) Y(i,:)],'--')
    ylabel("Channel " + i)
end

xlabel("Time Step")
nexttile(1)
legend(["Input" "Forecasted"])

Previsione a loop chiuso

La previsione a loop chiuso prevede le fasi temporali successive di una sequenza utilizzando le previsioni precedenti come input. In questo caso, il modello non necessita dei valori reali per effettuare la previsione. Ad esempio, si supponga di voler prevedere il valore per le fasi temporali da t a t+k della sequenza utilizzando solo i dati raccolti nelle fasi temporali da 1 a t-1. Per fare previsioni per la fase temporale i, utilizzare il valore previsto per la fase temporale i-1 come input. Utilizzare la previsione a loop chiuso per prevedere più fasi temporali successive o quando non si dispone di valori reali da fornire alla RNN prima di effettuare la previsione successiva.

Inizializzare lo stato della RNN resettando prima lo stato utilizzando la funzione resetState, quindi fare una previsione iniziale Z utilizzando le prime poche fasi temporali dei dati di input. Aggiornare lo stato della RNN utilizzando tutte le fasi temporali dei dati di input.

net = resetState(net);
offset = size(X,2);
[net,Z] = predictAndUpdateState(net,X);

Per prevedere ulteriori previsioni, eseguire un loop sulle fasi temporali e aggiornare lo stato della RNN utilizzando la funzione predictAndUpdateState. Prevedere le successive 200 fasi temporali passando iterativamente il precedente valore previsto alla RNN. Poiché la RNN non ha bisogno dei dati di input per fare ulteriori previsioni, è possibile specificare un numero qualsiasi di fasi temporali da prevedere.

numPredictionTimeSteps = 200;
Xt = Z(:,end);
Y = zeros(numChannels,numPredictionTimeSteps);

for t = 1:numPredictionTimeSteps
    [net,Y(:,t)] = predictAndUpdateState(net,Xt);
    Xt = Y(:,t);
end

Visualizzare i valori previsti in un grafico.

numTimeSteps = offset + numPredictionTimeSteps;

figure
t = tiledlayout(numChannels,1);
title(t,"Closed Loop Forecasting")

for i = 1:numChannels
    nexttile
    plot(T(i,1:offset))
    hold on
    plot(offset:numTimeSteps,[T(i,offset) Y(i,:)],'--')
    ylabel("Channel " + i)
end

xlabel("Time Step")
nexttile(1)
legend(["Input" "Forecasted"])

La previsione a loop chiuso consente di prevedere un numero arbitrario di fasi temporali, ma può essere meno accurata rispetto a quella a loop aperto in quanto la RNN non ha accesso ai valori reali durante il processo di previsione.

Vedi anche

| | |

Argomenti complementari