Main Content

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.

  • Previsione a loop aperto: prevedere 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.

  • Previsione a loop chiuso: prevedere 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 numTimeSteps per -numChannels, dove numTimeSteps è il numero di fasi temporali della sequenza e numChannels è il numero di canali della sequenza.

load WaveformData

Visualizzare le dimensioni delle prime sequenze.

data(1:4)
ans=4×1 cell array
    {103×3 double}
    {136×3 double}
    {140×3 double}
    {124×3 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},2)
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

Figure contains objects of type stackedplot.

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. Non includere la fase temporale finale nelle sequenze di addestramento. 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.

numObservationsTrain = numel(dataTrain);
XTrain = cell(numObservationsTrain,1);
TTrain = cell(numObservationsTrain,1);
for n = 1:numObservationsTrain
    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 i canali abbiano una media pari a zero e una varianza unitaria. Quando si effettuano le previsioni, è necessario normalizzare anche i dati di test utilizzando le stesse statistiche dei dati di addestramento.

Calcolare i valori della media e della deviazione standard per canale delle sequenze. Per calcolare facilmente la media e la deviazione standard dei dati di addestramento, creare array numerici che contengano le sequenze concatenate utilizzando la funzione cell2mat.

muX = mean(cell2mat(XTrain));
sigmaX = std(cell2mat(XTrain),0);

muT = mean(cell2mat(TTrain));
sigmaT = std(cell2mat(TTrain),0);

Normalizzare le sequenze utilizzando i valori della media e della deviazione standard calcolati.

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.

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

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=false);

Addestramento di una rete neurale ricorrente

Addestrare la rete neurale LSTM utilizzando la funzione trainnet. Per la regressione, utilizzare la perdita dell'errore quadratico medio. Per impostazione predefinita, la funzione trainnet utilizza una GPU, se disponibile. L'utilizzo di una GPU richiede una licenza Parallel Computing Toolbox™ e un dispositivo GPU supportato. Per informazioni sui dispositivi supportati, vedere GPU Computing Requirements (Parallel Computing Toolbox). In caso contrario, la funzione utilizza la CPU. Per specificare l'ambiente di esecuzione, utilizzare l'opzione di addestramento ExecutionEnvironment.

net = trainnet(XTrain,TTrain,layers,"mse",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.

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

Eseguire le previsioni utilizzando la funzione minibatchpredict. Per impostazione predefinita, la funzione minibatchpredict utilizza una GPU, se disponibile. Riempire le sequenze utilizzando le stesse opzioni di riempimento utilizzate per l'addestramento. Per le attività sequenza-sequenza con sequenze di lunghezza variabile, restituire le previsioni come array di celle impostando l'opzione UniformOutput su false.

YTest = minibatchpredict(net,XTest, ...
    SequencePaddingDirection="left", ...
    UniformOutput=false);

Per ciascuna sequenza di test, calcolare l'errore quadratico medio (RMSE) tra le previsioni e i target. Ignorare eventuali valori di riempimento nelle sequenze previste utilizzando come riferimento le lunghezze delle sequenze target.

for n = 1:numObservationsTest
    T = TTest{n};

    sequenceLength = size(T,1);    

    Y = YTest{n}(end-sequenceLength+1:end,:);

    err(n) = rmse(Y,T,"all");
end

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

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

Figure contains an axes object. The axes object with xlabel RMSE, ylabel Frequency contains an object of type histogram.

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

mean(err,"all")
ans = single
    0.5099

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 predict 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)

Figure contains an object of type stackedplot. The chart of type stackedplot has title Test Observation 2.

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;
[Z,state] = predict(net,X(1:offset,:));
net.State = state;

Per prevedere ulteriori previsioni, eseguire un loop sulle fasi temporali e fare le previsioni utilizzando la funzione predict. Dopo ciascuna previsione, aggiornare lo stato della RNN. 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. L'ultima fase temporale della previsione iniziale è la prima fase temporale prevista.

numTimeSteps = size(X,1);
numPredictionTimeSteps = numTimeSteps - offset;
Y = zeros(numPredictionTimeSteps,numChannels);
Y(1,:) = Z(end,1);

for t = 1:numPredictionTimeSteps-1
    Xt = X(offset+t,:);
    [Y(t+1,:),state] = predict(net,Xt);
    net.State = state;
end

Confrontare le previsioni con i valori di input.

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

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

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

Figure contains 3 axes objects. Axes object 1 with ylabel Channel 1 contains 2 objects of type line. These objects represent Input, Forecasted. Axes object 2 with ylabel Channel 2 contains 2 objects of type line. Axes object 3 with xlabel Time Step, ylabel Channel 3 contains 2 objects of type line.

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,1);
[Z,state] = predict(net,X(1:offset,:));
net.State = state;

Per prevedere ulteriori previsioni, eseguire un loop sulle fasi temporali e fare le previsioni utilizzando la funzione predict. Dopo ciascuna previsione, aggiornare lo stato della RNN. 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. L'ultima fase temporale della previsione iniziale è la prima fase temporale prevista.

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

for t = 2:numPredictionTimeSteps
    [Y(t,:),state] = predict(net,Y(t-1,:));
    net.State = state;
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(X(1:offset,i))
    hold on
    plot(offset:numTimeSteps,[X(offset,i) Y(:,i)'],"--")
    ylabel("Channel " + i)
end

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

Figure contains 3 axes objects. Axes object 1 with ylabel Channel 1 contains 2 objects of type line. These objects represent Input, Forecasted. Axes object 2 with ylabel Channel 2 contains 2 objects of type line. Axes object 3 with xlabel Time Step, ylabel Channel 3 contains 2 objects of type line.

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