Main Content

Pronóstico de series de tiempo mediante deep learning

Este ejemplo muestra cómo pronosticar datos de series de tiempo mediante una red de memoria de corto-largo plazo (LSTM).

Una red de LSTM es una red neuronal recurrente (RNN) que procesa datos de entrada formando un lazo con las unidades de tiempo y actualizando el estado de la red. El estado de la red contiene información recordada durante todas las unidades de tiempo anteriores. Puede utilizar una red de LSTM para pronosticar valores posteriores de una serie de tiempo o secuencia utilizando las unidades de tiempo anteriores como entrada. Para entrenar una red de LSTM para el pronóstico de series de tiempo, entrene una red de regresión de LSTM con salida secuencial, donde las respuestas (objetivos) son las secuencias de entrenamiento con valores desplazados una unidad de tiempo. En otras palabras, en cada unidad de tiempo de la secuencia de entrada, la red de LSTM aprende a predecir el valor de la siguiente unidad de tiempo.

Existen dos métodos para pronosticar: pronóstico de lazo abierto y de lazo cerrado.

  • El pronóstico de lazo abierto predice la siguiente unidad de tiempo en una secuencia utilizando solo datos de entrada. Cuando se hacen predicciones para unidades de tiempo posteriores, se recopilan valores reales de la fuente de datos y se utilizan como entrada. Por ejemplo, imagine que desea predecir el valor para la unidad de tiempo t de una secuencia con datos recopilados en las unidades de tiempo desde 1 hasta t-1. Para hacer predicciones para la unidad de tiempo t+1, espere hasta que registre el valor real de la unidad de tiempo t y utilícelo como entrada para hacer la siguiente predicción. Utilice el pronóstico de lazo abierto cuando tenga valores reales que proporcionar a la red antes de hacer la siguiente predicción.

  • El pronóstico de lazo cerrado predice unidades de tiempo posteriores en una secuencia utilizando las predicciones previas como entrada. En este caso, el modelo no requiere los valores reales para realizar la predicción. Por ejemplo, imagine que desea predecir los valores para las unidades de tiempo desdet hasta t+k de la secuencia con datos recopilados en las unidades de tiempo desde 1 hasta t-1 únicamente. Para hacer predicciones para la unidad de tiempo i, utilice el valor predicho para la unidad de tiempo i-1 como entrada. Utilice el pronóstico de lazo cerrado para pronosticar varias unidades de tiempo posteriores o cuando no tenga valores reales que proporcionar a la red antes de hacer la siguiente predicción.

Esta figura muestra un ejemplo de secuencia con valores pronosticados mediante la predicción de lazo cerrado.

closedloop.png

Este ejemplo utiliza el conjunto de datos con forma de onda, que contiene 2000 formas de onda generadas de forma sintética de diferentes longitudes con tres canales. En el ejemplo se entrena una red de LSTM para pronosticar valores futuros de las formas de onda, dados los valores de unidades de tiempo anteriores, mediante pronósticos tanto de lazo cerrado como de lazo abierto.

Cargar datos

Cargue los datos de ejemplo de WaveformData.mat. Los datos son un arreglo de celdas de numObservations por 1 de secuencias, donde numObservations es el número de secuencias. Cada secuencia es un arreglo numérico de numChannels por -numTimeSteps, donde numChannels es el número de canales de la secuencia y numTimeSteps es el número de unidades de tiempo de la secuencia.

load WaveformData

Visualice los tamaños de las primeras secuencias.

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

Visualice el número de canales. Para entrenar la red, cada secuencia debe tener el mismo número de canales.

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

Visualice las primeras secuencias en una gráfica.

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

    xlabel("Time Step")
end

Divida los datos en conjuntos de prueba y de entrenamiento. Utilice el 90% de las observaciones para el entrenamiento y el resto para la prueba.

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

Preparar datos para el entrenamiento

Para pronosticar los valores de unidades de tiempo futuras de una secuencia, especifique los objetivos como las secuencias de entrenamiento con valores desplazados una unidad de tiempo. En otras palabras, en cada unidad de tiempo de la secuencia de entrada, la red de LSTM aprende a predecir el valor de la siguiente unidad de tiempo. Los predictores son las secuencias de entrenamiento sin la unidad de tiempo final.

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

Para un mejor ajuste y para evitar que el entrenamiento diverja, normalice los predictores y los objetivos para que tengan media cero y varianza unitaria. Cuando haga predicciones, también deberá normalizar los datos de prueba con las mismas estadísticas que los datos de entrenamiento. Para calcular la media y la desviación estándar de todas las secuencias con facilidad, concatene las secuencias en la dimensión de tiempo.

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

Definir la arquitectura de red de LSTM

Cree una red de regresión de LSTM.

  • Utilice una capa de entrada de secuencias con un tamaño de entrada que coincida con el número de canales de los datos de entrada.

  • Utilice una capa de LSTM con 128 unidades ocultas. El número de unidades ocultas determina cuánta información aprende la capa. Utilizar más unidades ocultas puede producir resultados más precisos, pero es más probable que se produzca un sobreajuste de los datos de entrenamiento.

  • Para generar secuencias con el mismo número de canales que los datos de entrada, incluya una capa totalmente conectada con un tamaño de salida que coincida con el número de canales de los datos de entrada.

  • Por último, incluya una capa de regresión.

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

Especificar las opciones de entrenamiento

Especifique las opciones de entrenamiento.

  • Entrene utilizando la optimización de Adam.

  • Entrene durante 200 épocas. Para conjuntos de datos más grandes, puede que no sea necesario entrenar durante tantas épocas para conseguir un buen ajuste.

  • En cada minilote, rellene a la izquierda las secuencias para que tengan la misma longitud. Rellenar a la izquierda evita que la red prediga valores de relleno al final de las secuencias.

  • Cambie el orden de los datos en cada época.

  • Muestre el progreso del entrenamiento en una gráfica.

  • Deshabilite la salida detallada.

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

Entrenar redes neuronales

Entrene la red de LSTM con las opciones de entrenamiento especificadas mediante la función trainNetwork.

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

Probar la red

Prepare los datos de prueba para la predicción utilizando los mismos pasos que para los datos de entrenamiento.

Normalice los datos de prueba con las estadísticas calculadas a partir de los datos de entrenamiento. Especifique los objetivos como secuencias de prueba con valores desplazados una unidad de tiempo y los predictores como secuencias de prueba sin la unidad de tiempo final.

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

Realice predicciones con los datos de prueba. Especifique las mismas opciones de relleno que para el entrenamiento.

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

Para evaluar la precisión, en cada secuencia de prueba, calcule el error cuadrático medio raíz (RMSE) entre las predicciones y el objetivo.

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

Visualice los errores en un histograma. Cuanto más bajos sean los valores, mayor será la precisión.

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

Calcule el RMSE medio en todas las observaciones de prueba.

mean(rmse)
ans = single
    0.5080

Pronosticar unidades de tiempo futuras

Dada una serie de tiempo o una secuencia de entrada, para pronosticar los valores de varias unidades de tiempo futuras, utilice la función predictAndUpdateState para predecir unidades de tiempo una por una y actualizar el estado de la red en cada predicción. Para cada predicción, utilice la predicción anterior como la entrada para la función.

Visualice una de las secuencias de prueba en una gráfica.

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

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

Pronóstico de lazo abierto

El pronóstico de lazo abierto predice la siguiente unidad de tiempo en una secuencia utilizando solo datos de entrada. Cuando se hacen predicciones para unidades de tiempo posteriores, se recopilan valores reales de la fuente de datos y se utilizan como entrada. Por ejemplo, imagine que desea predecir el valor para la unidad de tiempo t de una secuencia con datos recopilados en las unidades de tiempo desde 1 hasta t-1. Para hacer predicciones para la unidad de tiempo t+1, espere hasta que registre el valor real de la unidad de tiempo t y utilícelo como entrada para hacer la siguiente predicción. Utilice el pronóstico de lazo abierto cuando tenga valores reales proporcionar a la red antes de hacer la siguiente predicción.

Inicialice el estado de la red restableciendo primero el estado mediante la función resetState. A continuación, realice una predicción inicial con las primeras unidades de tiempo de los datos de entrada. Actualice el estado de la red con las primeras 75 unidades de tiempo de los datos de entrada.

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

Para pronosticar más predicciones, forme un lazo con las unidades de tiempo y actualice el estado de la red mediante la función predictAndUpdateState. Pronostique los valores para las unidades de tiempo restantes de la observación de prueba formando un lazo de las unidades de tiempo de los datos de entrada y utilícelos como entrada para la red. La primera predicción es el valor correspondiente a la unidad de tiempo 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

Compare las predicciones con los valores objetivo.

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

Pronóstico de lazo cerrado

El pronóstico de lazo cerrado predice unidades de tiempo posteriores en una secuencia utilizando las predicciones previas como entrada. En este caso, el modelo no requiere los valores reales para realizar la predicción. Por ejemplo, imagine que desea predecir el valor para las unidades de tiempo desde t hasta t+k de la secuencia con datos recopilados en las unidades de tiempo desde 1 hasta t-1 únicamente. Para hacer predicciones para la unidad de tiempo i, utilice el valor predicho para la unidad de tiempo i-1 como entrada. Utilice el pronóstico de lazo cerrado para pronosticar varias unidades de tiempo posteriores o cuando no tenga valores reales que proporcionar a la red antes de hacer la siguiente predicción.

Inicialice el estado de la red restableciendo primero el estado mediante la función resetState. A continuación, realice una predicción inicial Z con las primeras unidades de tiempo de los datos de entrada. Actualice el estado de la red con las primeras 75 unidades de tiempo de los datos de entrada.

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

Para pronosticar más predicciones, forme un lazo con las unidades de tiempo y actualice el estado de la red mediante la función predictAndUpdateState. Pronostique las próximas 200 unidades de tiempo pasando iterativamente el valor predicho anteriormente a la red. Dado que la red no requiere los datos de entrada para realizar más predicciones, puede especificar cualquier número de unidades de tiempo que desea pronosticar.

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

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

Visualice los valores pronosticados en una gráfica.

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

El pronóstico de lazo cerrado permite pronosticar un número arbitrario de unidades de tiempo, pero puede resultar menos preciso en comparación con el pronóstico de lazo abierto, ya que la red no tiene acceso a los valores reales durante el proceso de pronóstico.

Consulte también

| | |

Temas relacionados