www.machinelearningmastery.ru

Машинное обучение, нейронные сети, искусственный интеллект
Header decor

Home

Как разработать модели сверточных нейронных сетей для прогнозирования временных рядов

Дата публикации 2018-11-12

Сверточные модели нейронной сети, или сокращенно CNN, можно применять для прогнозирования временных рядов.

Существует много типов моделей CNN, которые можно использовать для каждой конкретной задачи прогнозирования временных рядов.

В этом руководстве вы узнаете, как разработать набор моделей CNN для ряда стандартных задач прогнозирования временных рядов.

Цель этого руководства - предоставить отдельные примеры каждой модели для каждого типа проблемы временных рядов в качестве шаблона, который вы можете скопировать и адаптировать для конкретной задачи прогнозирования временных рядов.

После завершения этого урока вы узнаете:

  • Как разработать модели CNN для одномерного прогнозирования временных рядов.
  • Как разработать модели CNN для прогнозирования многомерных временных рядов.
  • Как разработать модели CNN для многоэтапного прогнозирования временных рядов.

Это большой и важный пост; Вы можете добавить его в закладки для дальнейшего использования.

Давайте начнем.

Обзор учебника

В этом руководстве мы рассмотрим, как разработать набор различных типов моделей CNN для прогнозирования временных рядов.

Модели демонстрируются на задачах с небольшими надуманными временными рядами, предназначенных для того, чтобы дать представление о типе решаемой проблемы временных рядов. Выбранная конфигурация моделей является произвольной и не оптимизирована для каждой задачи; это не было целью.

Этот урок разделен на четыре части; они есть:

  1. Одномерные модели CNN
  2. Многомерные модели CNN
  3. Многошаговые модели CNN
  4. Многомерные многоступенчатые модели CNN

Одномерные модели CNN

Традиционно разработанные для данных двумерного изображения, CNN могут использоваться для моделирования одномерных задач прогнозирования временных рядов.

Одномерный временной ряд - это наборы данных, состоящие из одной серии наблюдений с временным упорядочением, и требуется модель, чтобы извлечь уроки из серии прошлых наблюдений, чтобы предсказать следующее значение в последовательности.

Этот раздел разделен на две части; они есть:

  1. Подготовка данных
  2. Модель CNN

Подготовка данных

Прежде чем можно смоделировать одномерный ряд, он должен быть подготовлен.

Модель CNN изучит функцию, которая отображает последовательность прошлых наблюдений в качестве входных данных для выходного наблюдения. Таким образом, последовательность наблюдений должна быть преобразована в несколько примеров, из которых модель может извлечь уроки.

Рассмотрим данную одномерную последовательность:

[10, 20, 30, 40, 50, 60, 70, 80, 90]

Мы можем разделить последовательность на несколько шаблонов ввода / вывода, называемых выборками, где три временных шага используются в качестве входных данных, а один временной шаг используется в качестве выходных данных для изучаемого одношагового предсказания.

X,				y
10, 20, 30		40
20, 30, 40		50
30, 40, 50		60
...

split_sequence ()Приведенная ниже функция реализует это поведение и будет разбивать заданную одномерную последовательность на несколько выборок, где каждая выборка имеет определенное количество временных шагов, а вывод - один временной шаг.

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Мы можем продемонстрировать эту функцию на нашем небольшом надуманном наборе данных выше.

Полный пример приведен ниже.

# univariate data preparation
from numpy import array

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 3
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

Выполнение примера разделяет одномерный ряд на шесть выборок, где каждая выборка имеет три входных временных шага и один выходной временной шаг.

[10 20 30] 40
[20 30 40] 50
[30 40 50] 60
[40 50 60] 70
[50 60 70] 80
[60 70 80] 90

Теперь, когда мы знаем, как подготовить одномерный ряд для моделирования, давайте посмотрим на разработку модели CNN, которая может научиться отображать входы в выходы.

Модель CNN

Одномерный CNN - это модель CNN, которая имеет сверточный скрытый слой, который работает над одномерной последовательностью. За этим следует, возможно, второй сверточный слой в некоторых случаях, например, очень длинные входные последовательности, и затем объединяющий слой, задача которого состоит в том, чтобы перевести вывод сверточного слоя в наиболее заметные элементы.

За сверточным и объединяющим слоями следует плотный полностью связанный слой, который интерпретирует особенности, извлекаемые сверточной частью модели. Сглаженный слой используется между сверточными слоями и плотным слоем, чтобы свести карты объектов к одному одномерному вектору.

Мы можем определить 1D модель CNN для одномерного прогнозирования временных рядов следующим образом.

# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')

Ключевым в определении является форма ввода; это то, что модель ожидает в качестве входных данных для каждой выборки с точки зрения количества временных шагов и количества функций.

Мы работаем с одномерным рядом, поэтому число функций равно одной для одной переменной.

Число временных шагов в качестве входных данных - это число, которое мы выбрали при подготовке набора данных в качестве аргумента дляsplit_sequence ()функция.

Форма ввода для каждого образца указана вinput_shapeАргумент об определении первого скрытого слоя.

У нас почти всегда есть несколько выборок, поэтому модель будет ожидать, что входной компонент обучающих данных будет иметь размеры или форму:

[samples, timesteps, features]

нашsplit_sequence ()Функция в предыдущем разделе выводит X с формой [образцы, временные шаги], поэтому мы можем легко изменить его, чтобы иметь дополнительное измерение для одного объекта

# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))

CNN фактически не рассматривает данные как имеющие временные шаги, вместо этого он обрабатывается как последовательность, над которой могут выполняться сверточные операции чтения, как одномерное изображение.

В этом примере мы определяем сверточный слой с 64 картами фильтров и размером ядра 2. Затем следует слой максимального пула и плотный слой для интерпретации входного объекта. Указан выходной слой, который предсказывает одно числовое значение.

Модель подходит с использованием эффективногоАдам версия стохастического градиентного спускаи оптимизирован с использованием среднеквадратичной ошибки, или ‘MSELoss, функция потери.

Как только модель определена, мы можем разместить ее в наборе обучающих данных.

# fit model
model.fit(X, y, epochs=1000, verbose=0)

После того, как модель подходит, мы можем использовать ее для прогнозирования.

Мы можем предсказать следующее значение в последовательности, предоставив вход:

[70, 80, 90]

И ожидая, что модель будет предсказывать что-то вроде:

[100]

Модель ожидает, что входная форма будет трехмерной с [образцы, временные шаги, особенности], поэтому мы должны изменить форму единственной входной выборки, прежде чем делать прогноз.

# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)

Мы можем связать все это вместе и продемонстрировать, как разработать одномерную модель CNN для одномерного прогнозирования временных рядов и сделать единый прогноз.

# univariate cnn example
from numpy import array
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the sequence
		if end_ix > len(sequence)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps = 3
# split into samples
X, y = split_sequence(raw_seq, n_steps)
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=1000, verbose=0)
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Выполнение примера подготавливает данные, соответствует модели и делает прогноз.

Ваши результаты могут отличаться в зависимости от стохастической природы алгоритма; попробуйте запустить пример несколько раз.

Мы можем видеть, что модель предсказывает следующее значение в последовательности.

[[101.67965]]

Многомерные модели CNN

Данные многомерного временного ряда означают данные, в которых имеется более одного наблюдения для каждого временного шага.

Есть две основные модели, которые нам могут потребоваться с многомерными данными временных рядов; они есть:

  1. Несколько входов серии.
  2. Несколько параллельных серий.

Давайте посмотрим на каждого по очереди.

Несколько входов

Проблема может иметь два или более параллельных входных временных рядов и выходной временной ряд, который зависит от входного временного ряда.

Входные временные ряды параллельны, потому что у каждой серии есть наблюдения с одинаковыми временными шагами.

Мы можем продемонстрировать это на простом примере двух параллельных входных временных рядов, где выходные ряды являются простым добавлением входных рядов.

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])

Мы можем преобразовать эти три массива данных в один набор данных, где каждая строка представляет собой шаг по времени, а каждый столбец представляет собой отдельный временной ряд.

Это стандартный способ хранения параллельных временных рядов в файле CSV.

# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))

Полный пример приведен ниже.

# multivariate data preparation
from numpy import array
from numpy import hstack
# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
print(dataset)

При выполнении примера печатается набор данных с одной строкой на шаг по времени и одним столбцом для каждого из двух входных и одного выходного параллельных временных рядов.

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

Как и в случае одномерного временного ряда, мы должны структурировать эти данные в выборки с входными и выходными выборками.

Модель 1D CNN нуждается в достаточном контексте, чтобы узнать соответствие входной последовательности выходному значению. CNN могут поддерживать параллельные входные временные ряды в виде отдельных каналов, таких как красный, зеленый и синий компоненты изображения. Поэтому нам нужно разбить данные на выборки, сохраняя порядок наблюдений по двум входным последовательностям.

Если мы выберем три шага ввода времени, то первый пример будет выглядеть следующим образом:

Входные данные:

10, 15
20, 25
30, 35

Выход:

65

То есть первые три временных шага каждой параллельной серии предоставляются в качестве входных данных для модели, и модель связывает это со значением в выходной серии на третьем временном шаге, в данном случае 65.

Мы можем видеть, что при преобразовании временных рядов во входные / выходные выборки для обучения модели нам придется отбрасывать некоторые значения из выходных временных рядов, где у нас нет значений во входных временных рядах на предыдущих временных шагах. В свою очередь, выбор размера количества входных временных шагов будет иметь важное влияние на то, сколько данных тренировки используется.

Мы можем определить функцию с именемsplit_sequences ()это будет набор данных, как мы его определили, с строками для временных шагов и столбцами для параллельных рядов и возвращаемыми выборками ввода / вывода.

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Мы можем протестировать эту функцию в нашем наборе данных, используя три временных шага для каждого входного временного ряда в качестве входных данных.

Полный пример приведен ниже.

# multivariate data preparation
from numpy import array
from numpy import hstack

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

При запуске примера сначала печатается формаИкса такжеYкомпоненты.

Мы видим, чтоИксКомпонент имеет трехмерную структуру.

Первое измерение - это число выборок, в данном случае 7. Второе измерение - это количество временных шагов на выборку, в данном случае 3 - значение, указанное для функции. Наконец, последнее измерение указывает количество параллельных временных рядов или количество переменных, в данном случае 2 для двух параллельных рядов.

Это точная трехмерная структура, ожидаемая 1D CNN в качестве входных данных. Данные готовы к использованию без дальнейшего изменения.

Затем мы можем увидеть, что входные и выходные данные для каждого образца напечатаны, показывая три временных шага для каждого из двух входных рядов и связанные выходные данные для каждого образца.

(7, 3, 2) (7,)

[[10 15]
 [20 25]
 [30 35]] 65
[[20 25]
 [30 35]
 [40 45]] 85
[[30 35]
 [40 45]
 [50 55]] 105
[[40 45]
 [50 55]
 [60 65]] 125
[[50 55]
 [60 65]
 [70 75]] 145
[[60 65]
 [70 75]
 [80 85]] 165
[[70 75]
 [80 85]
 [90 95]] 185

Теперь мы готовы приспособить модель 1D CNN к этим данным, указав ожидаемое количество временных шагов и функций, ожидаемых для каждой входной выборки, в данном случае три и два соответственно.

# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')

При прогнозировании модель ожидает три временных шага для двух входных временных рядов.

Мы можем предсказать следующее значение в выходной серии, предоставив входные значения:

80,	 85
90,	 95
100, 105

Форма одного образца с тремя временными шагами и двумя переменными должна быть [1, 3, 2].

Мы ожидаем, что следующее значение в последовательности будет 100 + 105 или 205.

# demonstrate prediction
x_input = array([[80, 85], [90, 95], [100, 105]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)

Полный пример приведен ниже.

# multivariate cnn example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=1000, verbose=0)
# demonstrate prediction
x_input = array([[80, 85], [90, 95], [100, 105]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Выполнение примера подготавливает данные, соответствует модели и делает прогноз.

[[206.0161]]

Существует другой, более сложный способ моделирования проблемы.

Каждый входной ряд может быть обработан отдельным CNN, и выходные данные каждой из этих подмоделей могут быть объединены до того, как будет сделан прогноз для выходной последовательности.

Мы можем ссылаться на это как на многоголовую модель CNN. Это может предложить большую гибкость или лучшую производительность в зависимости от специфики моделируемой проблемы. Например, он позволяет настраивать каждую подмодель по-разному для каждой входной серии, например, количество карт фильтров и размер ядра.

Этот тип модели может быть определен в Keras с помощьюKeras функциональный API,

Во-первых, мы можем определить первую входную модель как 1D CNN с входным слоем, который ожидает векторы сn_stepsи 1 особенность.

# first input model
visible1 = Input(shape=(n_steps, n_features))
cnn1 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible1)
cnn1 = MaxPooling1D(pool_size=2)(cnn1)
cnn1 = Flatten()(cnn1)

Мы можем определить вторую входную подмодель таким же образом.

# second input model
visible2 = Input(shape=(n_steps, n_features))
cnn2 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible2)
cnn2 = MaxPooling1D(pool_size=2)(cnn2)
cnn2 = Flatten()(cnn2)

Теперь, когда обе входные подмодели определены, мы можем объединить выходные данные каждой модели в один длинный вектор, который можно интерпретировать, прежде чем делать прогноз для выходной последовательности.

# merge input models
merge = concatenate([cnn1, cnn2])
dense = Dense(50, activation='relu')(merge)
output = Dense(1)(dense)

Затем мы можем связать входы и выходы вместе.

model = Model(inputs=[visible1, visible2], outputs=output)

На изображении ниже представлена ​​схема того, как эта модель выглядит, включая форму входов и выходов каждого слоя.

Эта модель требует, чтобы входные данные были представлены в виде списка из двух элементов, где каждый элемент в списке содержит данные для одной из подмоделей.

Чтобы достичь этого, мы можем разделить входные данные 3D на два отдельных массива входных данных; то есть от одного массива с формой [7, 3, 2] до двух трехмерных массивов с [7, 3, 1]

# one time series per head
n_features = 1
# separate input data
X1 = X[:, :, 0].reshape(X.shape[0], X.shape[1], n_features)
X2 = X[:, :, 1].reshape(X.shape[0], X.shape[1], n_features)

Эти данные могут быть предоставлены для того, чтобы соответствовать модели.

# fit model
model.fit([X1, X2], y, epochs=1000, verbose=0)

Точно так же мы должны подготовить данные для одной выборки в виде двух отдельных двумерных массивов при одноэтапном прогнозировании.

x_input = array([[80, 85], [90, 95], [100, 105]])
x1 = x_input[:, 0].reshape((1, n_steps, n_features))
x2 = x_input[:, 1].reshape((1, n_steps, n_features))

Мы можем связать все это вместе; полный пример приведен ниже.

# multivariate multi-headed 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
from keras.layers.merge import concatenate

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# one time series per head
n_features = 1
# separate input data
X1 = X[:, :, 0].reshape(X.shape[0], X.shape[1], n_features)
X2 = X[:, :, 1].reshape(X.shape[0], X.shape[1], n_features)
# first input model
visible1 = Input(shape=(n_steps, n_features))
cnn1 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible1)
cnn1 = MaxPooling1D(pool_size=2)(cnn1)
cnn1 = Flatten()(cnn1)
# second input model
visible2 = Input(shape=(n_steps, n_features))
cnn2 = Conv1D(filters=64, kernel_size=2, activation='relu')(visible2)
cnn2 = MaxPooling1D(pool_size=2)(cnn2)
cnn2 = Flatten()(cnn2)
# merge input models
merge = concatenate([cnn1, cnn2])
dense = Dense(50, activation='relu')(merge)
output = Dense(1)(dense)
model = Model(inputs=[visible1, visible2], outputs=output)
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit([X1, X2], y, epochs=1000, verbose=0)
# demonstrate prediction
x_input = array([[80, 85], [90, 95], [100, 105]])
x1 = x_input[:, 0].reshape((1, n_steps, n_features))
x2 = x_input[:, 1].reshape((1, n_steps, n_features))
yhat = model.predict([x1, x2], verbose=0)
print(yhat)

Выполнение примера подготавливает данные, соответствует модели и делает прогноз.

[[205.871]]

Несколько параллельных серий

Альтернативной проблемой временных рядов является случай, когда существует несколько параллельных временных рядов, и для каждого должно быть предсказано значение.

Например, учитывая данные из предыдущего раздела:

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

Мы можем захотеть предсказать значение для каждого из трех временных рядов для следующего временного шага.

Это можно назвать многомерным прогнозированием.

Опять же, данные должны быть разделены на выборки ввода / вывода для обучения модели.

Первый образец этого набора данных будет:

Входные данные:

10, 15, 25
20, 25, 45
30, 35, 65

Выход:

40, 45, 85

split_sequences ()Приведенная ниже функция будет разбивать несколько параллельных временных рядов со строками для временных шагов и по одному ряду на столбец в требуемой форме ввода / вывода.

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Мы можем продемонстрировать это на надуманной проблеме; полный пример приведен ниже.

# multivariate output data prep
from numpy import array
from numpy import hstack

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

При запуске примера сначала печатается форма подготовленных компонентов X и y.

Форма X является трехмерной, включая количество выборок (6), количество временных шагов, выбранных для выборки (3), и количество параллельных временных рядов или объектов (3).

Форма y является двумерной, как можно ожидать для числа выборок (6) и количества временных переменных на выборку, которые будут предсказаны (3).

Данные готовы к использованию в одномерной модели CNN, которая ожидает трехмерные входные и двухмерные формы вывода для компонентов X и y каждого образца.

Затем печатается каждый из образцов с указанием входных и выходных компонентов каждого образца.

(6, 3, 3) (6, 3)

[[10 15 25]
 [20 25 45]
 [30 35 65]] [40 45 85]
[[20 25 45]
 [30 35 65]
 [40 45 85]] [ 50  55 105]
[[ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]] [ 60  65 125]
[[ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]] [ 70  75 145]
[[ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]] [ 80  85 165]
[[ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]] [ 90  95 185]

Теперь мы готовы установить модель 1D CNN на эти данные.

В этой модели число временных шагов и параллельных рядов (объектов) указывается для входного слоя черезinput_shapeаргумент.

Число параллельных рядов также используется в спецификации количества значений, которые прогнозируются моделью в выходном слое; опять же это три.

# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_features))
model.compile(optimizer='adam', loss='mse')

Мы можем предсказать следующее значение в каждом из трех параллельных рядов, введя три временных шага для каждого ряда.

70, 75, 145
80, 85, 165
90, 95, 185

Форма ввода для создания одного прогноза должна быть 1 выборка, 3 временных шага и 3 функции или [1, 3, 3].

# demonstrate prediction
x_input = array([[70,75,145], [80,85,165], [90,95,185]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)

Мы ожидаем, что выходной вектор будет:

[100, 105, 205]

Мы можем связать все это вместе и продемонстрировать 1D CNN для многомерного прогнозирования временных рядов выхода ниже.

# multivariate output 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_features))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=3000, verbose=0)
# demonstrate prediction
x_input = array([[70,75,145], [80,85,165], [90,95,185]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Выполнение примера подготавливает данные, соответствует модели и делает прогноз

[[100.11272 105.32213 205.53436]]

Как и в случае множественных входных рядов, существует еще один более сложный способ моделирования проблемы.

Каждая выходная серия может обрабатываться отдельной выходной моделью CNN.

Мы можем сослаться на это как на модель с несколькими выходами CNN. Это может предложить большую гибкость или лучшую производительность в зависимости от специфики моделируемой проблемы.

Этот тип модели может быть определен в Keras с помощьюKeras функциональный API,

Во-первых, мы можем определить первую входную модель как модель 1D CNN.

# define model
visible = Input(shape=(n_steps, n_features))
cnn = Conv1D(filters=64, kernel_size=2, activation='relu')(visible)
cnn = MaxPooling1D(pool_size=2)(cnn)
cnn = Flatten()(cnn)
cnn = Dense(50, activation='relu')(cnn)

Затем мы можем определить один выходной слой для каждой из трех серий, которые мы хотим прогнозировать, где каждая выходная подмодель будет прогнозировать один временной шаг.

# define output 1
output1 = Dense(1)(cnn)
# define output 2
output2 = Dense(1)(cnn)
# define output 3
output3 = Dense(1)(cnn)

Затем мы можем связать входной и выходной слои вместе в одну модель.

# tie together
model = Model(inputs=visible, outputs=[output1, output2, output3])
model.compile(optimizer='adam', loss='mse')

Чтобы прояснить архитектуру модели, на схеме ниже четко показаны три отдельных выходных слоя модели и формы ввода и вывода каждого слоя.

При обучении модели потребуется три отдельных выходных массива на выборку. Мы можем достичь этого путем преобразования выходных обучающих данных, имеющих форму [7, 3], в три массива с формой [7, 1].

# separate output
y1 = y[:, 0].reshape((y.shape[0], 1))
y2 = y[:, 1].reshape((y.shape[0], 1))
y3 = y[:, 2].reshape((y.shape[0], 1))

Эти массивы могут быть предоставлены модели во время обучения.

# fit model
model.fit(X, [y1,y2,y3], epochs=2000, verbose=0)

Связав все это вместе, полный пример приведен ниже.

# multivariate output 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences)-1:
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps = 3
# convert into input/output
X, y = split_sequences(dataset, n_steps)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# separate output
y1 = y[:, 0].reshape((y.shape[0], 1))
y2 = y[:, 1].reshape((y.shape[0], 1))
y3 = y[:, 2].reshape((y.shape[0], 1))
# define model
visible = Input(shape=(n_steps, n_features))
cnn = Conv1D(filters=64, kernel_size=2, activation='relu')(visible)
cnn = MaxPooling1D(pool_size=2)(cnn)
cnn = Flatten()(cnn)
cnn = Dense(50, activation='relu')(cnn)
# define output 1
output1 = Dense(1)(cnn)
# define output 2
output2 = Dense(1)(cnn)
# define output 3
output3 = Dense(1)(cnn)
# tie together
model = Model(inputs=visible, outputs=[output1, output2, output3])
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, [y1,y2,y3], epochs=2000, verbose=0)
# demonstrate prediction
x_input = array([[70,75,145], [80,85,165], [90,95,185]])
x_input = x_input.reshape((1, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Выполнение примера подготавливает данные, соответствует модели и делает прогноз.

[array([[100.96118]], dtype=float32),
 array([[105.502686]], dtype=float32),
 array([[205.98045]], dtype=float32)]

Многошаговые модели CNN

На практике существует небольшая разница с моделью 1D CNN в прогнозировании векторного вывода, который представляет различные выходные переменные (как в предыдущем примере), или векторного вывода, который представляет несколько временных шагов одной переменной.

Тем не менее, существуют тонкие и важные различия в том, как готовятся данные обучения. В этом разделе мы продемонстрируем случай разработки многошаговой модели прогноза с использованием векторной модели.

Прежде чем мы рассмотрим особенности модели, давайте сначала рассмотрим подготовку данных для многоэтапного прогнозирования.

Подготовка данных

Как и в случае одношагового прогнозирования, временной ряд, используемый для многоэтапного прогнозирования временных рядов, должен быть разбит на выборки с компонентами ввода и вывода.

Оба компонента ввода и вывода будут состоять из нескольких временных шагов и могут иметь или не иметь одинаковое количество шагов.

Например, учитывая одномерный временной ряд:

[10, 20, 30, 40, 50, 60, 70, 80, 90]

Мы могли бы использовать последние три временных шага в качестве входных данных и прогнозировать следующие два временных шага.

Первый образец будет выглядеть следующим образом:

Входные данные:

[10, 20, 30]

Выход:

[40, 50]

split_sequence ()Функция ниже реализует это поведение и будет разбивать заданный одномерный временной ряд на выборки с указанным количеством входных и выходных временных шагов.

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Мы можем продемонстрировать эту функцию на небольшом вымышленном наборе данных.

Полный пример приведен ниже.

# multi-step data preparation
from numpy import array

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# split into samples
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

Выполнение примера разбивает одномерный ряд на временные шаги ввода и вывода и печатает компоненты ввода и вывода каждого.

[10 20 30] [40 50]
[20 30 40] [50 60]
[30 40 50] [60 70]
[40 50 60] [70 80]
[50 60 70] [80 90]

Теперь, когда мы знаем, как подготовить данные для многоэтапного прогнозирования, давайте посмотрим на одномерную модель CNN, которая может изучить это отображение.

Модель векторного вывода

1D CNN может напрямую выводить вектор, который можно интерпретировать как многошаговый прогноз.

Этот подход был замечен в предыдущем разделе, где один временной шаг каждого выходного временного ряда прогнозировался как вектор.

Как и в одномерных моделях CNN для одномерных данных в предыдущем разделе, подготовленные образцы должны быть сначала изменены. CNN ожидает, что данные будут иметь трехмерную структуру [образцы, временные шаги, особенности], и в этом случае у нас есть только одна особенность, поэтому изменение формы является простым.

# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))

С количеством шагов ввода и вывода, указанных вn_steps_inа такжеn_steps_outпеременные, мы можем определить многоступенчатую модель прогнозирования временных рядов.

# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')

Модель может сделать прогноз для одной выборки. Мы можем предсказать следующие два шага за концом набора данных, предоставив входные данные:

[70, 80, 90]

Мы ожидаем, что прогнозируемый результат будет:

[100, 110]

Как и ожидалось моделью, форма одиночной выборки входных данных при прогнозировании должна быть [1, 3, 1] для 1 выборки, 3 временных шагов ввода и одиночной функции.

# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)

Связывая все это вместе, 1D CNN для многоэтапного прогнозирования с одномерным временным рядом перечислено ниже.

# univariate multi-step vector-output 1d cnn example
from numpy import array
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a univariate sequence into samples
def split_sequence(sequence, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequence)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the sequence
		if out_end_ix > len(sequence):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequence[i:end_ix], sequence[end_ix:out_end_ix]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# split into samples
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
# reshape from [samples, timesteps] into [samples, timesteps, features]
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=2000, verbose=0)
# demonstrate prediction
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Выполнение примера прогнозирует и печатает следующие два временных шага в последовательности.

[[102.86651 115.08979]]

Многомерные многоступенчатые модели CNN

В предыдущих разделах мы рассматривали одномерные, многомерные и многошаговые прогнозы временных рядов.

Можно смешивать и сочетать различные типы моделей 1D CNN, представленные до настоящего времени, для различных задач. Это также относится к задачам прогнозирования временных рядов, которые включают многомерное и многошаговое прогнозирование, но это может быть немного более сложным.

В этом разделе мы рассмотрим короткие примеры подготовки и моделирования данных для многомерного многоступенчатого прогнозирования временных рядов в качестве шаблона для облегчения этой задачи, а именно:

  1. Многошаговый многошаговый выход
  2. Множественный параллельный вход и многошаговый выход.

Возможно, самым большим камнем преткновения является подготовка данных, и именно на этом мы сосредоточим наше внимание.

Многошаговый многошаговый выход

Существуют проблемы прогнозирования многомерного временного ряда, когда выходной ряд является отдельным, но зависит от входного временного ряда, и для выходного ряда требуется несколько временных шагов.

Например, рассмотрим наш многомерный временной ряд из предыдущего раздела:

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

Мы можем использовать три предыдущих временных шага каждого из двух входных временных рядов, чтобы предсказать два временных шага выходных временных рядов.

Входные данные:

10, 15
20, 25
30, 35

Выход:

65
85

split_sequences ()Функция ниже реализует это поведение.

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out-1
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1:out_end_ix, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Мы можем продемонстрировать это на нашем надуманном наборе данных. Полный пример приведен ниже.

# multivariate multi-step data preparation
from numpy import array
from numpy import hstack

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out-1
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1:out_end_ix, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# convert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

При запуске примера сначала печатается форма подготовленных данных тренировки.

Мы можем видеть, что форма входной части выборок является трехмерной, состоящей из шести выборок, с тремя временными шагами и двумя переменными для двух входных временных рядов.

Выходная часть выборок является двумерной для шести выборок и двух временных шагов для каждой выборки, которая должна быть предсказана.

Подготовленные образцы затем печатают, чтобы подтвердить, что данные были подготовлены, как мы указали.

(6, 3, 2) (6, 2)

[[10 15]
 [20 25]
 [30 35]] [65 85]
[[20 25]
 [30 35]
 [40 45]] [ 85 105]
[[30 35]
 [40 45]
 [50 55]] [105 125]
[[40 45]
 [50 55]
 [60 65]] [125 145]
[[50 55]
 [60 65]
 [70 75]] [145 165]
[[60 65]
 [70 75]
 [80 85]] [165 185]

Теперь мы можем разработать 1D модель CNN для многоэтапного прогнозирования.

В этом случае мы продемонстрируем модель векторного вывода. Полный пример приведен ниже.

# multivariate multi-step 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out-1
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1:out_end_ix, -1]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# convert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=2000, verbose=0)
# demonstrate prediction
x_input = array([[70, 75], [80, 85], [90, 95]])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Выполнение примера соответствует модели и прогнозирует следующие два временных шага выходной последовательности за пределами набора данных.

Мы ожидаем, что следующие два шага будут [185, 205].

Это сложное решение проблемы с очень небольшим объемом данных, и произвольно сконфигурированная версия модели приближается.

[[185.57011 207.77893]]

Множественный параллельный вход и многошаговый выход

Проблема с параллельными временными рядами может потребовать предсказания нескольких временных шагов каждого временного ряда.

Например, рассмотрим наш многомерный временной ряд из предыдущего раздела:

[[ 10  15  25]
 [ 20  25  45]
 [ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]
 [ 80  85 165]
 [ 90  95 185]]

Мы можем использовать последние три временных шага из каждого из трех временных рядов в качестве входных данных для модели и прогнозировать следующие временные шаги каждого из трех временных рядов в качестве выходных данных.

Первый образец в обучающем наборе данных будет следующим.

Входные данные:

10, 15, 25
20, 25, 45
30, 35, 65

Выход:

40, 45, 85
50, 55, 105

split_sequences ()Функция ниже реализует это поведение.

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix:out_end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

Мы можем продемонстрировать эту функцию на небольшом вымышленном наборе данных.

Полный пример приведен ниже.

# multivariate multi-step data preparation
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import RepeatVector
from keras.layers import TimeDistributed

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix:out_end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# convert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
print(X.shape, y.shape)
# summarize the data
for i in range(len(X)):
	print(X[i], y[i])

При запуске примера сначала распечатывается форма подготовленного набора обучающих данных.

Мы можем видеть, что оба входа (Икс) и вывод (Y) элементы набора данных являются трехмерными для количества выборок, временных шагов и переменных или параллельных временных рядов соответственно.

Затем элементы ввода и вывода каждой серии печатаются рядом, чтобы мы могли подтвердить, что данные были подготовлены так, как мы ожидали.

(5, 3, 3) (5, 2, 3)

[[10 15 25]
 [20 25 45]
 [30 35 65]] [[ 40  45  85]
 [ 50  55 105]]
[[20 25 45]
 [30 35 65]
 [40 45 85]] [[ 50  55 105]
 [ 60  65 125]]
[[ 30  35  65]
 [ 40  45  85]
 [ 50  55 105]] [[ 60  65 125]
 [ 70  75 145]]
[[ 40  45  85]
 [ 50  55 105]
 [ 60  65 125]] [[ 70  75 145]
 [ 80  85 165]]
[[ 50  55 105]
 [ 60  65 125]
 [ 70  75 145]] [[ 80  85 165]
 [ 90  95 185]]

Теперь мы можем разработать 1D модель CNN для этого набора данных.

В этом случае мы будем использовать модель векторного вывода. Таким образом, мы должны сгладить трехмерную структуру выходной части каждого образца, чтобы обучить модель. Это означает, что вместо прогнозирования двух шагов для каждой серии модель обучается и ожидается, что она будет предсказывать вектор из шести чисел напрямую.

# flatten output
n_output = y.shape[1] * y.shape[2]
y = y.reshape((y.shape[0], n_output))

Полный пример приведен ниже.

# multivariate output multi-step 1d cnn example
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps_in, n_steps_out):
	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps_in
		out_end_ix = end_ix + n_steps_out
		# check if we are beyond the dataset
		if out_end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix:out_end_ix, :]
		X.append(seq_x)
		y.append(seq_y)
	return array(X), array(y)

# define input sequence
in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90])
in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95])
out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
# convert to [rows, columns] structure
in_seq1 = in_seq1.reshape((len(in_seq1), 1))
in_seq2 = in_seq2.reshape((len(in_seq2), 1))
out_seq = out_seq.reshape((len(out_seq), 1))
# horizontally stack columns
dataset = hstack((in_seq1, in_seq2, out_seq))
# choose a number of time steps
n_steps_in, n_steps_out = 3, 2
# convert into input/output
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
# flatten output
n_output = y.shape[1] * y.shape[2]
y = y.reshape((y.shape[0], n_output))
# the dataset knows the number of features, e.g. 2
n_features = X.shape[2]
# define model
model = Sequential()
model.add(Conv1D(filters=64, kernel_size=2, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(50, activation='relu'))
model.add(Dense(n_output))
model.compile(optimizer='adam', loss='mse')
# fit model
model.fit(X, y, epochs=7000, verbose=0)
# demonstrate prediction
x_input = array([[60, 65, 125], [70, 75, 145], [80, 85, 165]])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)

Выполнение примера соответствует модели и прогнозирует значения для каждого из трех временных шагов для следующих двух временных шагов после конца набора данных.

Мы ожидаем, что значения для этих рядов и временных шагов будут следующими:

90, 95, 185
100, 105, 205

Мы видим, что прогноз модели достаточно близок к ожидаемым значениям.

[[ 90.47855 95.621284 186.02629 100.48118 105.80815 206.52821 ]]

Резюме

В этом руководстве вы узнали, как разработать набор моделей CNN для ряда стандартных задач прогнозирования временных рядов.

В частности, вы узнали:

  • Как разработать модели CNN для одномерного прогнозирования временных рядов.
  • Как разработать модели CNN для прогнозирования многомерных временных рядов.
  • Как разработать модели CNN для многоэтапного прогнозирования временных рядов.

У вас есть вопросы?
Задайте свои вопросы в комментариях ниже, и я сделаю все возможное, чтобы ответить.

Оригинальная статья

Footer decor

© www.machinelearningmastery.ru | Ссылки на оригиналы и авторов сохранены. | map