Intervalo de previsão em torno da previsão de séries temporais LSTM

13

Existe um método para calcular o intervalo de previsão (distribuição de probabilidade) em torno de uma série temporal prevista a partir de uma rede neural LSTM (ou outra rede recorrente)?

Digamos, por exemplo, que estou prevendo 10 amostras no futuro (t + 1 a t + 10), com base nas últimas 10 amostras observadas (t-9 a t), eu esperaria que a previsão em t + 1 fosse mais preciso do que a previsão em t + 10. Normalmente, pode-se desenhar barras de erro ao redor da previsão para mostrar o intervalo. Com um modelo ARIMA (sob a suposição de erros normalmente distribuídos), posso calcular um intervalo de previsão (por exemplo, 95%) em torno de cada valor previsto. Posso calcular o mesmo (ou algo relacionado ao intervalo de previsão) de um modelo LSTM?

Estou trabalhando com LSTMs em Keras / Python, seguindo muitos exemplos de machinelearningmastery.com , nos quais meu código de exemplo (abaixo) se baseia. Estou pensando em reformular o problema como classificação em compartimentos discretos, pois isso gera confiança por classe, mas isso parece uma solução ruim.

Existem alguns tópicos semelhantes (como o abaixo), mas nada parece abordar diretamente a questão dos intervalos de previsão das redes neurais LSTM (ou mesmo outras):

/stats/25055/how-to-calculate-the-confidence-interval-for-time-series-prediction

Previsão de séries temporais usando ARIMA vs LSTM

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sin
from matplotlib import pyplot
import numpy as np

# Build an LSTM network and train
def fit_lstm(X, y, batch_size, nb_epoch, neurons):
    X = X.reshape(X.shape[0], 1, X.shape[1]) # add in another dimension to the X data
    y = y.reshape(y.shape[0], y.shape[1])      # but don't add it to the y, as Dense has to be 1d?
    model = Sequential()
    model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
    model.add(Dense(y.shape[1]))
    model.compile(loss='mean_squared_error', optimizer='adam')
    for i in range(nb_epoch):
        model.fit(X, y, epochs=1, batch_size=batch_size, verbose=1, shuffle=False)
        model.reset_states()
    return model

# Configuration
n = 5000    # total size of dataset
SLIDING_WINDOW_LENGTH = 30
SLIDING_WINDOW_STEP_SIZE = 1
batch_size = 10
test_size = 0.1 # fraction of dataset to hold back for testing
nb_epochs = 100 # for training
neurons = 8 # LSTM layer complexity

# create dataset
#raw_values = [sin(i/2) for i in range(n)]  # simple sine wave
raw_values = [sin(i/2)+sin(i/6)+sin(i/36)+np.random.uniform(-1,1) for i in range(n)]  # double sine with noise
#raw_values = [(i%4) for i in range(n)] # saw tooth

all_data = np.array(raw_values).reshape(-1,1) # make into array, add anothe dimension for sci-kit compatibility

# data is segmented using a sliding window mechanism
all_data_windowed = [np.transpose(all_data[idx:idx+SLIDING_WINDOW_LENGTH]) for idx in np.arange(0,len(all_data)-SLIDING_WINDOW_LENGTH, SLIDING_WINDOW_STEP_SIZE)]
all_data_windowed = np.concatenate(all_data_windowed, axis=0).astype(np.float32)

# split data into train and test-sets
# round datasets down to a multiple of the batch size
test_length = int(round((len(all_data_windowed) * test_size) / batch_size) * batch_size)
train, test = all_data_windowed[:-test_length,:], all_data_windowed[-test_length:,:]
train_length = int(np.floor(train.shape[0] / batch_size)*batch_size) 
train = train[:train_length,...]

half_size = int(SLIDING_WINDOW_LENGTH/2) # split the examples half-half, to forecast the second half
X_train, y_train = train[:,:half_size], train[:,half_size:]
X_test, y_test = test[:,:half_size], test[:,half_size:]

# fit the model
lstm_model = fit_lstm(X_train, y_train, batch_size=batch_size, nb_epoch=nb_epochs, neurons=neurons)

# forecast the entire training dataset to build up state for forecasting
X_train_reshaped = X_train.reshape(X_train.shape[0], 1, X_train.shape[1])
lstm_model.predict(X_train_reshaped, batch_size=batch_size)

# predict from test dataset
X_test_reshaped = X_test.reshape(X_test.shape[0], 1, X_test.shape[1])
yhat = lstm_model.predict(X_test_reshaped, batch_size=batch_size)

#%% Plot prediction vs actual

x_axis_input = range(half_size)
x_axis_output = [x_axis_input[-1]] + list(half_size+np.array(range(half_size)))

fig = pyplot.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x_axis_input,np.zeros_like(x_axis_input), 'r-')
line2, = ax.plot(x_axis_output,np.zeros_like(x_axis_output), 'o-')
line3, = ax.plot(x_axis_output,np.zeros_like(x_axis_output), 'g-')
ax.set_xlim(np.min(x_axis_input),np.max(x_axis_output))
ax.set_ylim(-4,4)
pyplot.legend(('Input','Actual','Predicted'),loc='upper left')
pyplot.show()

# update plot in a loop
for idx in range(y_test.shape[0]):

    sample_input = X_test[idx]
    sample_truth = [sample_input[-1]] + list(y_test[idx]) # join lists
    sample_predicted = [sample_input[-1]] + list(yhat[idx])

    line1.set_ydata(sample_input)
    line2.set_ydata(sample_truth)
    line3.set_ydata(sample_predicted)
    fig.canvas.draw()
    fig.canvas.flush_events()

    pyplot.pause(.25)
4Oh4
fonte

Respostas:

9

Diretamente, isso não é possível. No entanto, se você o modelar de uma maneira diferente, poderá obter intervalos de confiança. Você poderia, em vez de uma regressão normal, abordá-la como estimar uma distribuição de probabilidade contínua. Ao fazer isso em cada etapa, você pode traçar sua distribuição. Maneiras de fazer isso são as Redes de Mistura de Kernel ( https://janvdvegt.github.io/2017/06/07/Kernel-Mixture-Networks.html , divulgação, meu blog) ou Density Mixture Networks ( http: //www.cedar .buffalo.edu / ~ srihari / CSE574 / Chap5 / Chap5.7-MixDensityNetworks.pdf ), o primeiro usa kernels como base e estima uma mistura sobre esses kernels e o segundo estima uma mistura de distribuições, incluindo os parâmetros de cada um dos as distribuições. Você usa a probabilidade do log para treinar o modelo.

Outra opção para modelar a incerteza é usar a desistência durante o treinamento e também durante a inferência. Você faz isso várias vezes e toda vez que obtém uma amostra do seu posterior. Você não recebe distribuições, apenas amostras, mas é a mais fácil de implementar e funciona muito bem.

No seu caso, você deve pensar na maneira como gera t + 2 até t + 10. Dependendo da sua configuração atual, pode ser necessário fazer uma amostra da etapa anterior e alimentá-la para a próxima. Isso não funciona muito bem com a primeira abordagem, nem com a segunda. Se você tiver 10 saídas por etapa de tempo (t + 1 até t + 10), todas essas abordagens serão mais limpas, mas um pouco menos intuitivas.

Jan van der Vegt
fonte
1
Usar redes de mistura é interessante, vou tentar implementar isso. Existe alguma pesquisa sólida sobre o uso do abandono aqui: arxiv.org/abs/1709.01907 e arxiv.org/abs/1506.02142
4Oh4 /
Uma nota para o abandono, você pode realmente calcular a variância da previsão de Monte Carlo abandono, e usar isso como quantificação de incerteza
Charles Chow
Isso é verdade @CharlesChow, mas é uma maneira ruim de construir um intervalo de confiança nesse contexto. Seria melhor ordenar os valores e usar quantis devido à distribuição potencialmente muito distorcida.
Jan van der Vegt
Concordo @JanvanderVegt, mas você ainda pode estimar as estatísticas de MC abandono, sem a assunção de distribuição de saída, quero dizer que você também pode usar percentil ou bootstrapping para construir o CI de MC dropout
Charles Chow
2

Previsão conforme como uma palavra da moda pode ser interessante para você, porque funciona sob muitas condições - em particular, não precisa de erro distribuído normal e funciona para quase qualquer modelo de aprendizado de máquina.

Duas boas apresentações são dadas por Scott Locklin e Henrik Linusson .

Boris W
fonte
1

Vou divergir um pouco e argumentar que, na prática, o intervalo de confiança do cálculo geralmente não é uma coisa valiosa a ser feita. A razão é que há sempre um monte de suposições que você precisa fazer. Mesmo para a regressão linear mais simples, você precisa ter

  • Relação linear.
  • Normalidade multivariada.
  • Nenhuma ou pouca multicolinearidade.
  • Sem correlação automática.
  • Homocedasticidade.

Uma abordagem muito mais pragmática é fazer uma simulação de Monte Carlo. Se você já sabe ou deseja assumir a distribuição de suas variáveis ​​de entrada, pegue um monte de amostras e forneça LSTM para você, agora você pode calcular empiricamente o seu "intervalo de confiança".

Louis T
fonte
1

Sim você pode. A única coisa que você precisa alterar é a função de perda. Implemente a função de perda usada na regressão quantílica e integre-a. Além disso, você deseja dar uma olhada em como você avalia esses intervalos. Para isso, eu usaria métricas de ICP, MIL e RMIL.

Inigo
fonte