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 einer Sequenz anhand von Daten, die in den Zeitschritten 1 bis erfasst wurden, vorhersagen. Um Vorhersagen für Zeitschritt zu treffen, warten Sie, bis Sie den wahren Wert für 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 bis der Sequenz vorhersagen und hierfür nur die in Zeitschritt 1 bis verwendeten Daten verwenden. Um Vorhersagen für Zeitschritt zu treffen, verwenden Sie als Eingang den für Zeitschritt 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.
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 einer Sequenz anhand von Daten, die in den Zeitschritten 1 bis erfasst wurden, vorhersagen. Um Vorhersagen für Zeitschritt zu treffen, warten Sie, bis Sie den wahren Wert für 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 bis der Sequenz vorhersagen und hierfür nur die in Zeitschritt 1 bis verwendeten Daten verwenden. Um Vorhersagen für Zeitschritt zu treffen, verwenden Sie als Eingang den für Zeitschritt 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
trainnet
| trainingOptions
| dlnetwork
| lstmLayer
| sequenceInputLayer