Существуют разные подходы, в зависимости от ваших точных настроек и желаемого результата.
Версия A
Если вы хотите иметь модель LSTM, которая берет кусок данных и предсказывает следующий шаг Вот отдельный пример.
Синтетические данные c только в меру похожи на те, что показаны на вашем рисунке, но я надеюсь, что они по-прежнему полезны для иллюстрации.
Прогнозы на верхних панелях показан случай, когда известны все фрагменты временных рядов, и для каждого из них предсказан следующий шаг.
На нижних панелях показан более реалистичный c случай, когда начало временного ряда этот вопрос известен, а остальное предсказывается итеративно, по одному шагу за раз. Очевидно, что ошибка предсказания может накапливаться и расти со временем.
# import modules
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import keras
import keras.models
import keras.layers
import sklearn
import sklearn.metrics
# please load auxiliary functions defined below!
# (omitted here for better readability)
# set seed
np.random.seed(42)
# number of time series
n_samples = 5
# number of steps used for prediction
n_steps = 50
# number of epochs for LSTM training
epochs = 100
# create synthetic data
# (see bottom left panel below, very roughly resembling your data)
tab = create_data(n_samples)
# train model without first column
x_train, y_train = prepare_data(tab.iloc[:, 1:], n_steps=n_steps)
model, history = train_model(x_train, y_train, n_steps=n_steps, epochs=epochs)
# predict first column for testing
# (all chunks are known and only on time step is predicted for each)
veo = tab[0].copy().values
y_test, y_pred = predict_all(veo, model)
# predict iteratively
# (first chunk is known and new values are predicted iteratively)
vec = veo.copy()
y_iter = predict_iterative(vec, n_steps, model)
# plot results
plot_single(y_test, [y_pred, y_iter], n_steps)
Версия B
Если общая длина вашего временные ряды известны и фиксированы, и вы хотите «автоматически завершить» неполные временные ряды (пунктирной зеленой линией на рисунке), может быть проще и надежнее прогнозировать множество значений одновременно.
Однако, поскольку для В каждом временном ряду вы берете только стартовый блок в качестве обучающих данных (и прогнозируете остальную его часть), для этого, вероятно, требуются более известные временные ряды.
Тем не менее, поскольку каждый временной ряд используется только один раз во время обучения (и не разбивается на множество последовательных фрагментов), обучение проходит быстрее и результаты выглядят хорошо.
# please load auxiliary functions defined below!
# (omitted here for better readability)
# number of time series
n_samples = 10
# create synthetic data
# (see bottom left panel below, very roughly resembling your data)
tab = create_data(n_samples)
# prepare training data
x_train = tab.iloc[:n_steps, 1:].values.T
x_train = x_train.reshape(*x_train.shape, 1)
y_train = tab.iloc[n_steps:, 1:].values.T
print(x_train.shape) # (9, 50, 1) = old shape, 1D time series
# create additional dummy features to demonstrate usage of nD time series input data
# (feature_i = factor_i * score_i, with sum_i factor_i = 1)
feature_factors = [0.3, 0.2, 0.5]
x_train = np.dstack([x_train] + [factor*x_train for factor in feature_factors])
print(x_train.shape) # (9, 50, 4) = new shape, original 1 + 3 new features
# create LSTM which predicts everything beyond n_steps
n_steps_out = len(tab) - n_steps
model, history = train_model(x_train, y_train, n_steps=n_steps, epochs=epochs,
n_steps_out=n_steps_out)
# prepare test data
x_test = tab.iloc[:n_steps, :1].values.T
x_test = x_test.reshape(*x_test.shape, 1)
x_test = np.dstack([x_test] + [factor*x_test for factor in feature_factors])
y_test = tab.iloc[n_steps:, :1].values.T[0]
y_pred = model.predict(x_test)[0]
# plot results
plot_multi(history, tab, y_pred, n_steps)
Обновление
Привет, Шломи Спасибо за ваше обновление. Если я правильно понимаю, вместо 1D временных рядов у вас есть больше возможностей, то есть nD временных рядов. Действительно, это уже включено в модель (с частично неопределенной переменной n_features, теперь исправленной). Я добавил раздел «Создание дополнительных фиктивных объектов» в версии B, где фиктивные объекты создаются путем разделения исходного 1D временного ряда (но также с сохранением исходных данных, соответствующих вашему f (...) = счету, который звучит как спроектированный особенность, которая должна пригодиться). Затем я только добавил n_features = x_train.shape[2]
в функцию настройки сети LSTM. Просто убедитесь, что ваши индивидуальные функции масштабированы правильно (например, [0-1]), прежде чем вводить их в сеть. Конечно, качество прогноза сильно зависит от фактических данных.
Вспомогательные функции
def create_data(n_samples):
# window width for rolling average
window = 10
# position of change in trend
thres = 200
# time period of interest
dates = pd.date_range(start='2020-02-16', end='2020-03-15', freq='H')
# create data frame
tab = pd.DataFrame(index=dates)
lend = len(tab)
lin = np.arange(lend)
# create synthetic time series
for ids in range(n_samples):
trend = 4 * lin - 3 * (lin-thres) * (lin > thres)
# scale to [0, 1] interval (approximately) for easier handling by network
trend = 0.9 * trend / max(trend)
noise = 0.1 * (0.1 + trend) * np.random.randn(lend)
vec = trend + noise
tab[ids] = vec
# compute rolling average to get smoother variation
tab = tab.rolling(window=window).mean().iloc[window:]
return tab
def split_sequence(vec, n_steps=20):
# split sequence into chunks of given size
x_trues, y_trues = [], []
steps = len(vec) - n_steps
for step in range(steps):
ilo = step
iup = step + n_steps
x_true, y_true = vec[ilo:iup], vec[iup]
x_trues.append(x_true)
y_trues.append(y_true)
x_true = np.array(x_trues)
y_true = np.array(y_trues)
return x_true, y_true
def prepare_data(tab, n_steps=20):
# convert data frame with multiple columns into chucks
x_trues, y_trues = [], []
if tab.ndim == 2:
arr = np.atleast_2d(tab).T
else:
arr = np.atleast_2d(tab)
for col in arr:
x_true, y_true = split_sequence(col, n_steps=n_steps)
x_trues.append(x_true)
y_trues.append(y_true)
x_true = np.vstack(x_trues)
x_true = x_true.reshape(*x_true.shape, 1)
y_true = np.hstack(y_trues)
return x_true, y_true
def train_model(x_train, y_train, n_units=50, n_steps=20, epochs=200,
n_steps_out=1):
# get number of features from input data
n_features = x_train.shape[2]
# setup network
# (feel free to use other combination of layers and parameters here)
model = keras.models.Sequential()
model.add(keras.layers.LSTM(n_units, activation='relu',
return_sequences=True,
input_shape=(n_steps, n_features)))
model.add(keras.layers.LSTM(n_units, activation='relu'))
model.add(keras.layers.Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse', metrics=['mse'])
# train network
history = model.fit(x_train, y_train, epochs=epochs,
validation_split=0.1, verbose=1)
return model, history
def predict_all(vec, model):
# split data
x_test, y_test = prepare_data(vec, n_steps=n_steps)
# use trained model to predict all data points from preceeding chunk
y_pred = model.predict(x_test, verbose=1)
y_pred = np.hstack(y_pred)
return y_test, y_pred
def predict_iterative(vec, n_steps, model):
# use last chunk to predict next value, iterate until end is reached
y_iter = vec.copy()
lent = len(y_iter)
steps = lent - n_steps - 1
for step in range(steps):
print(step, steps)
ilo = step
iup = step + n_steps + 1
x_test, y_test = prepare_data(y_iter[ilo:iup], n_steps=n_steps)
y_pred = model.predict(x_test, verbose=0)
y_iter[iup] = y_pred
return y_iter[n_steps:]
def plot_single(y_test, y_plots, n_steps, nrows=2):
# prepare variables for plotting
metric = 'mse'
mima = [min(y_test), max(y_test)]
titles = ['all', 'iterative']
lin = np.arange(-n_steps, len(y_test))
# create figure
fig, axis = plt.subplots(figsize=(16, 9),
nrows=2, ncols=3)
# plot time series
axia = axis[1, 0]
axia.set_title('original data')
tab.plot(ax=axia)
axia.set_xlabel('time')
axia.set_ylabel('value')
# plot network training history
axia = axis[0, 0]
axia.set_title('training history')
axia.plot(history.history[metric], label='train')
axia.plot(history.history['val_'+metric], label='test')
axia.set_xlabel('epoch')
axia.set_ylabel(metric)
axia.set_yscale('log')
plt.legend()
# plot result for "all" and "iterative" prediction
for idy, y_plot in enumerate(y_plots):
# plot true/predicted time series
axia = axis[idy, 1]
axia.set_title(titles[idy])
axia.plot(lin, veo, label='full')
axia.plot(y_test, label='true')
axia.plot(y_plot, label='predicted')
plt.legend()
axia.set_xlabel('time')
axia.set_ylabel('value')
axia.set_ylim(0, 1)
# plot scatter plot of true/predicted data
axia = axis[idy, 2]
r2 = sklearn.metrics.r2_score(y_test, y_plot)
axia.set_title('R2 = %.2f' % r2)
axia.scatter(y_test, y_plot)
axia.plot(mima, mima, color='black')
axia.set_xlabel('true')
axia.set_ylabel('predicted')
plt.tight_layout()
return None
def plot_multi(history, tab, y_pred, n_steps):
# prepare variables for plotting
metric = 'mse'
# create figure
fig, axis = plt.subplots(figsize=(16, 9),
nrows=1, ncols=2, squeeze=False)
# plot network training history
axia = axis[0, 0]
axia.set_title('training history')
axia.plot(history.history[metric], label='train')
axia.plot(history.history['val_'+metric], label='test')
axia.set_xlabel('epoch')
axia.set_ylabel(metric)
axia.set_yscale('log')
plt.legend()
# plot true/predicted time series
axia = axis[0, 1]
axia.plot(tab[0].values, label='true')
axia.plot(range(n_steps, len(tab)), y_pred, label='predicted')
plt.legend()
axia.set_xlabel('time')
axia.set_ylabel('value')
axia.set_ylim(0, 1)
plt.tight_layout()
return None