Hauptinhalt

Zeitreihen-Vorhersagen mit Deep Learning

Dieses Beispiel veranschaulicht, wie Sie Zeitreihendaten mithilfe eines LSTM-Netzes (Long-Short-Term-Memory, langes Kurzzeitgedächtnis) vorhersagen können.

Ein LSTM-Netz ist ein rekurrentes neuronales Netz (RNN-Netz), das Eingangsdaten verarbeitet, indem es über Zeitschritte hinweg wiederholt wird und den RNN-Zustand aktualisiert. Der RNN-Zustand enthält Informationen, die über alle vorherigen Zeitschritte hinweg beibehalten wurden. Sie können ein neuronales LSTM-Netz verwenden, um anhand der vorherigen Zeitschritte als Eingang die darauffolgenden Werte einer Zeitreihe oder Sequenz vorherzusagen. Um ein neuronales LSTM-Netz für Zeitreihen-Vorhersagen zu trainieren, trainieren Sie ein neuronales LSTM-Netz mit Sequenzausgaben, wobei die Antworten (Ziele) die um einen Zeitschritt verschobenen Trainingssequenzen sind. Anders gesagt: Zu jedem Zeitschritt der Eingangssequenz lernt das neuronale LSTM-Netz, den Wert des nächsten Zeitschritts vorherzusagen.

Es gibt zwei Vorhersagemethoden: Open-Loop- und Closed-Loop-Vorhersagen.

  • Open-Loop-Vorhersagen – Vorhersagen des nächsten Zeitschritts in einer Sequenz nur mithilfe der Eingangsdaten. Werden Vorhersagen für die folgenden Zeitschritte getroffen, erfassen Sie die wahren Werte aus Ihrer Datenquelle und verwenden diese als Eingang. Ein Beispiel: Sie möchten den Wert von Zeitschritt t einer Sequenz anhand von Daten, die in den Zeitschritten 1 bis t-1 erfasst wurden, vorhersagen. Um Vorhersagen für Zeitschritt t+1 zu treffen, warten Sie, bis Sie den wahren Wert für t erfasst haben und verwenden Sie diesen als Eingang für die nächste Vorhersage. Verwenden Sie Open-Loop-Vorhersagen, wenn Sie dem RNN wahre Werte zur Verfügung stellen können, bevor Sie die nächste Vorhersage treffen.

  • Closed-Loop-Vorhersagen – Vorhersagen der folgenden Zeitschritte einer Sequenz, indem die vorherigen Vorhersagen als Eingang verwendet werden. In diesem Fall benötigt das Modell keine wahren Werte zum Treffen einer Vorhersage. Ein Beispiel: Sie möchten die Werte für die Zeitschritte t bis t+k der Sequenz vorhersagen und hierfür nur die in Zeitschritt 1 bis t-1 verwendeten Daten verwenden. Um Vorhersagen für Zeitschritt i zu treffen, verwenden Sie als Eingang den für Zeitschritt i-1 vorhergesagten Wert. Verwenden Sie Closed-Loop-Vorhersagen, um mehrere folgende Zeitschritte vorherzusagen oder wenn dem RNN vor der nächsten Vorhersage keine wahren Werte zur Verfügung gestellt werden können.

Diese Abbildung zeigt eine Beispielsequenz mit Werten, die mittels Closed-Loop-Vorhersagen vorhergesagt wurden.

closedloop.png

In diesem Beispiel wird der Waveform-Datensatz verwendet, der 1000 synthetisch erzeugte Wellenformen verschiedener Längen mit drei Kanälen umfasst. In diesem Beispiel wird ein neuronales LSTM-Netz darauf trainiert, die zukünftigen Werte der Wellenformen ausgehend von den Werten der vorherigen Zeitschritte sowohl mithilfe von Closed-Loop- als auch Open-Loop-Vorhersagen vorherzusagen.

Laden der Daten

Laden Sie die Beispieldaten aus WaveformData.mat. Die Daten bestehen aus einem numObservations-mal-1-Zellen-Array aus Sequenzen, wobei numObservations der Anzahl Sequenzen entspricht. Jede Sequenz ist ein numerisches numTimeSteps-mal--numChannels-Array, bei dem numTimeSteps der Anzahl Zeitschritte der Sequenz und numChannels der Anzahl Kanäle der Sequenz entspricht.

load WaveformData

Sehen Sie sich die Größen der ersten Sequenzen an.

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

Sehen Sie sich die Anzahl der Kanäle an. Um das neuronale LSTM-Netz zu trainieren, muss jede Sequenz dieselbe Anzahl Kanäle aufweisen.

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

Visualisieren Sie die ersten Sequenzen in einem Diagramm.

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

    xlabel("Time Step")
end

Unterteilen Sie die Daten in Trainings- und Testdatensätze. Verwenden Sie 90 % der Beobachtungen für das Training, den Rest für das Testen.

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

Vorbereiten von Daten für das Training

Um die Werte zukünftiger Zeitschritte einer Sequenz vorherzusagen, legen Sie als Ziel die Trainingssequenzen mit um einen Zeitschritt verschobenen Werten fest. Nehmen Sie den letzten Zeitschritt der Trainingssequenzen nicht auf. Anders gesagt: Zu jedem Zeitschritt der Eingangssequenz lernt das neuronale LSTM-Netz, den Wert des nächsten Zeitschritts vorherzusagen. Die Prädiktoren sind die Trainingssequenzen ohne den letzten Zeitschritt.

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

Für eine bessere Anpassung und um eine Divergenz des Trainings zu verhindern können Sie die Prädiktoren und Ziele normalisieren, sodass die Kanäle einen Mittelwert von Null und Varianz von 1 aufweisen. Wenn Sie Vorhersagen treffen, müssen Sie zudem die Testdaten mithilfe derselben Statistiken wie die Trainingsdaten normalisieren.

Berechnen Sie die Mittelwerte pro Kanal und die Standardabweichungswerte der Sequenzen. Um die Mittelwerte und Standardabweichung für die Trainingsdaten einfach zu berechnen, erstellen Sie mithilfe der cell2mat-Funktion numerische Arrays, die die verketteten Sequenzen enthalten.

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

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

Normalisieren Sie die Sequenzen anhand der berechneten Mittelwerte und Standardabweichungen.

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

Definieren von neuronalen LSTM-Netzarchitekturen

Erstellen Sie ein neuronales LSTM-Regressionsnetz.

  • Verwenden Sie eine Sequenz-Eingangsschicht mit einer Eingangsgröße, die der Anzahl Kanäle der Eingangsdaten entspricht.

  • Verwenden Sie eine LSTM-Schicht mit 128 verborgenen Einheiten. Die Anzahl verborgener Schichten legt fest, wie viel Informationen von der Schicht erlernt werden. Die Verwendung weiterer verborgener Einheiten kann genauere Ergebnisse ergeben, führt aber mit höherer Wahrscheinlichkeit zu einer Überanpassung an die Trainingsdaten.

  • Um Sequenzen mit derselben Anzahl Kanäle wie die Eingangsdaten auszugeben, verwenden Sie eine vollständig verbundene Schicht mit einer Ausgangsgröße, die der Anzahl Kanäle der Eingangsdaten entspricht.

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

Festlegen von Trainingsoptionen

Legen Sie die Trainingsoptionen fest.

  • Trainieren Sie mithilfe von Adam-Optimierung.

  • Trainieren Sie über 200 Epochen hinweg. Bei größeren Datensätzen kann eine gute Anpassung unter Umständen auch mit weniger Epochen erzielt werden.

  • Füllen Sie die Sequenzen in jedem Mini-Batch von links auf dieselbe Länge auf. Durch ein Auffüllen von Links wird verhindert, dass das RNN aufgefüllte Werte am Ende der Sequenzen vorhersagt.

  • Mischen Sie die Daten jede Epoche.

  • Zeigen Sie den Trainingsfortschritt in einem Diagramm an.

  • Deaktivieren Sie die ausführliche Ausgabe.

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

Trainieren eines rekurrenten neuronalen Netzes

Trainieren Sie das neuronale LSTM-Netz mit der Funktion trainnet. Verwenden Sie für die Regression den mittleren quadratischen Abweichungsverlust. Standardmäßig verwendet die trainnet-Funktion eine GPU, sofern vorhanden. Die Verwendung einer GPU erfordert eine Parallel Computing Toolbox™-Lizenz und ein unterstütztes GPU-Gerät. Informationen zu unterstützten Geräten finden Sie unter GPU Computing Requirements (Parallel Computing Toolbox). Andernfalls verwendet die Funktion die CPU. Um die Ausführungsumgebung festzulegen, verwenden Sie die Trainingsoption ExecutionEnvironment.

net = trainnet(XTrain,TTrain,layers,"mse",options);

Testen eines rekurrenten neuronalen Netzes

Bereiten Sie die Testdaten mit denselben Schritten wie die Trainingsdaten auf die Vorhersage vor.

Normalisieren Sie die Testdaten mithilfe der aus den Trainingsdaten berechneten Statistiken. Legen Sie als Ziele die Testsequenzen mit um einen Zeitschritt verschobene Werte fest; legen Sie als Prädikatoren die Testsequenzen ohne den letzten Zeitschritt fest.

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

Treffen Sie Vorhersagen mit der Funktion minibatchpredict. Standardmäßig verwendet die minibatchpredict-Funktion eine GPU, sofern vorhanden. Füllen Sie die Sequenzen mit denselben Auffüllungs-Optionen wie beim Training auf. Geben Sie bei Sequenz-Sequenz-Aufgaben mit Sequenzen variabler Länge die Vorhersagen als Zellen-Array aus, indem Sie die Option UniformOutput auf false setzen.

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

Berechnen Sie für jede Testsequenz die Wurzel der mittleren quadratischen Abweichung (RMSE) zwischen Vorhersagen und Zielen. Ignorieren Sie aufgefüllte Werte in den vorhergesagten Sequenzen; ziehen Sie hierfür die Längen der Zielsequenzen als Referenz heran.

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

Visualisieren Sie die Abweichungen in einem Histogramm. Niedrigere Werte stehen hier für höhere Genauigkeit.

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

Berechnen Sie die mittlere RMSE über alle Testbeobachtungen hinweg.

mean(err,"all")
ans = single

0.5096

Vorhersagen zukünftiger Zeitschritte

Verwenden Sie ausgehend von einer Eingangs-Zeitreihe oder -Sequenz die predict-Funktion, um die Werte mehrerer zukünftiger Zeitschritte vorherzusagen; hierbei wird jeweils ein Zeitschritt vorhergesagt und der RNN-Zustand daraufhin aktualisiert. Verwenden Sie bei jeder Vorhersage die vorherige Vorhersage als Eingang für die Funktion.

Visualisieren Sie eine der Testsequenzen in einem Diagramm.

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

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

Open-Loop-Vorhersagen

Bei Open-Loop-Vorhersagen wird der nächste Zeitschritt einer Sequenz nur anhand der Eingangsdaten vorhergesagt. Werden Vorhersagen für die folgenden Zeitschritte getroffen, erfassen Sie die wahren Werte aus Ihrer Datenquelle und verwenden diese als Eingang. Ein Beispiel: Sie möchten den Wert von Zeitschritt t einer Sequenz anhand von Daten, die in den Zeitschritten 1 bis t-1 erfasst wurden, vorhersagen. Um Vorhersagen für Zeitschritt t+1 zu treffen, warten Sie, bis Sie den wahren Wert für t erfasst haben und verwenden Sie diesen als Eingang für die nächste Vorhersage. Verwenden Sie Open-Loop-Vorhersagen, wenn Sie dem RNN wahre Werte zur Verfügung stellen können, bevor Sie die nächste Vorhersage treffen.

Initialisieren Sie den RNN-Zustand, indem Sie den Zustand zunächst mithilfe der resetState-Funktion zurücksetzen; erzeugen Sie daraufhin mit den ersten Zeitschritten der Eingangsdaten eine anfängliche Vorhersage. Aktualisieren Sie den RNN-Zustand mithilfe der ersten 75 Zeitschritte der Eingangsdaten.

net = resetState(net);
offset = 75;
[Z,state] = predict(net,X(1:offset,:));
net.State = state;

Um weitere Vorhersagen zu treffen, gehen Sie über die Zeitschritte und treffen Sie Vorhersagen mit der predict-Funktion. Aktualisieren Sie den RNN-Zustand nach jeder Vorhersage. Sagen Sie Werte für die restlichen Zeitschritte der Testbeobachtung hervor, indem Sie über die Zeitschritte der Eingangsdaten gehen und diese als Eingang für das RNN verwenden. Der letzte Zeitschritt der anfänglichen Vorhersage ist der erste vorhergesagte Zeitschritt.

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

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

Vergleichen Sie die Vorhersagen mit den Eingangswerten.

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"])

Closed-Loop-Vorhersagen

Bei Closed-Loop-Vorhersagen werden die folgenden Zeitschritte einer Sequenz vorhergesagt, indem die vorherigen Vorhersagen als Eingang verwendet werden. In diesem Fall benötigt das Modell keine wahren Werte zum Treffen einer Vorhersage. Ein Beispiel: Sie möchten den Wert für die Zeitschritte t bis t+k der Sequenz vorhersagen und hierfür nur die in Zeitschritt 1 bis t-1 verwendeten Daten verwenden. Um Vorhersagen für Zeitschritt i zu treffen, verwenden Sie als Eingang den für Zeitschritt i-1 vorhergesagten Wert. Verwenden Sie Closed-Loop-Vorhersagen, um mehrere folgende Zeitschritte vorherzusagen oder wenn dem RNN vor der nächsten Vorhersage keine wahren Werte zur Verfügung gestellt werden können.

Initialisieren Sie den RNN-Zustand, indem Sie den Zustand zunächst mithilfe der resetState-Funktion zurücksetzen; erzeugen Sie daraufhin mit den ersten Zeitschritten der Eingangsdaten eine anfängliche Vorhersage Z. Aktualisieren Sie den RNN-Zustand mithilfe aller Zeitschritte der Eingangsdaten.

net = resetState(net);
offset = size(X,1);
[Z,state] = predict(net,X(1:offset,:));
net.State = state;

Um weitere Vorhersagen zu treffen, gehen Sie über die Zeitschritte und treffen Sie Vorhersagen mit der predict-Funktion. Aktualisieren Sie den RNN-Zustand nach jeder Vorhersage. Sagen Sie die nächsten 200 Zeitschritte vorher, indem Sie den letzten vorhergesagten Wert iterativ an das RNN übergeben. Da das RNN keine Eingangsdaten benötigt, um weitere Vorhersagen zu treffen, können Sie eine beliebige Anzahl vorherzusagender Zeitschritte festlegen. Der letzte Zeitschritt der anfänglichen Vorhersage ist der erste vorhergesagte Zeitschritt.

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

Visualisieren Sie die vorhergesagten Werte in einem Diagramm.

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"])

Mit Closed-Loop-Vorhersagen können Sie eine beliebige Anzahl an Zeitschritten vorhersagen; dies kann jedoch im Vergleich zu Open-Loop-Vorhersagen weniger genau sein, da das RNN während des Vorhersageprozesses keinen Zugriff auf die wahren Werte hat.

Siehe auch

| | | |

Themen