LSTM模型基础学习
因为项目需要针对时间序列预测数据,故临时学习。本篇适合没有人工智能基础但需要进行时间序列数据预测开发的读者,供您挑选出适合自身项目开发的模型模板。
LSTM模型基础学习
因为项目需要针对时间序列预测数据,故临时学习。本篇适合没有人工智能基础但需要进行时间序列数据预测开发的读者,供您挑选出适合自身项目开发的模型模板。
1. 概述
循环神经网络RNN
循环神经网络(RNN)是一种神经网络结构,它能够处理序列数据,例如时间序列、语音信号、自然语言等。RNN通过引入“循环”的结构,在处理序列数据时能够保留之前的信息,从而更好地理解当前的输入。但是,RNN也存在着“梯度消失”或“梯度爆炸”等问题,导致在处理较长的序列数据时效果不佳。
LSTM(Long Short-Term Memory)是一种RNN的变种,与传统的RNN相比,它引入了门控机制(Gate),能够更好地处理长期依赖关系,避免了梯度消失或爆炸的问题,提高了模型的训练效果和预测准确率。
在神经网络中,我们通常使用反向传播算法来更新网络中的权重参数。反向传播算法通过计算损失函数对权重参数的偏导数来更新权重参数。这个偏导数就是梯度,它可以告诉我们应该如何调整权重参数才能使损失函数最小化。
但是,在传统的RNN中,当序列长度较长时,反向传播算法中的梯度可能会变得非常小,甚至趋近于0,这就是梯度消失的问题。当梯度消失时,权重参数几乎不会被更新,导致模型无法学习到长期依赖关系,从而影响了模型的训练效果。与梯度消失相反的是梯度爆炸的问题,这时梯度会变得非常大,甚至趋近于无穷大,导致权重参数的值变得异常大,无法进行有效的更新,最终导致模型失效。
可以将梯度消失和梯度爆炸类比为水流的问题。当水流通过一条管道时,如果管道很长,水流就会逐渐减小,最终流量几乎为0,这就类似于梯度消失的问题。而当水流通过一条管道时,如果管道很窄,水流就会逐渐加速,最终造成管道爆炸,这就类似于梯度爆炸的问题。
在机器学习中,我们通常通过最小化损失函数来训练模型,使得模型能够更好地适应训练数据,并在未见过的数据上取得更好的预测效果。因此,控制损失函数是实现模型训练的重要步骤。
损失函数通常用来衡量模型预测结果与真实结果之间的差距。在训练过程中,我们通过反向传播算法计算损失函数对模型参数的梯度,然后使用优化算法(如随机梯度下降)来更新模型参数,使得模型预测结果能够更接近真实结果,从而降低损失函数的值。
如果不控制损失函数,模型的训练过程可能会出现以下几种情况:
- 模型过拟合:损失函数在训练集上表现很好,但是在测试集上表现很差,说明模型过于复杂,过度拟合了训练数据,无法泛化到新的数据上。
- 模型欠拟合:损失函数在训练集和测试集上都表现很差,说明模型过于简单,无法适应数据的复杂性,需要更复杂的模型结构。
- 梯度消失或梯度爆炸:损失函数在训练过程中出现了梯度消失或梯度爆炸的问题,导致模型无法收敛或者收敛速度非常慢。
LSTM通过引入门控机制,能够更好地控制梯度的流动,从而避免了梯度消失和梯度爆炸的问题。因此,LSTM在处理长期依赖关系的序列数据时表现出色。
在LSTM中,每个单元包含了三个门(输入门、遗忘门和输出门),它们能够控制哪些信息可以通过,哪些信息需要被忽略。LSTM通过这些门控制信息的流动,从而能够更好地处理长期依赖关系,避免了RNN中的梯度消失或爆炸的问题。因此,LSTM被广泛应用于序列数据的建模和分析中。可以将LSTM比喻为一个有记忆功能的人,他可以根据之前的经验和现在的情况来做决策。而传统的RNN则类似于一个只能记住当前状态的人,无法很好地处理长期依赖关系。
LSTM
LSTM(Long Short-Term Memory)是一种递归神经网络(RNN)的变种,专门用于处理时间序列数据。
时间序列数据是按照时间顺序排列的数据集合,其中每个数据点都与一个特定的时间戳相关联。时间序列数据通常用于描述随时间变化的现象,例如股票价格、气温、交通流量等等。
LSTM通过学习时间序列数据中的长期依赖关系,可以用来预测未来的数值、分类时间序列数据、生成新的时间序列数据等等。相比于传统的RNN,LSTM能够更好地处理长期依赖关系,避免了梯度消失或爆炸的问题,提高了模型的训练效果和预测准确率。
LSTM的应用场景非常广泛,包括但不限于以下几个方面:
- 时间序列数据预测:如股票价格预测、气温预测、交通流量预测等。
- 时间序列数据分类:如语音识别、手写体识别、股票涨跌分类等。
- 时间序列数据生成:如音乐生成、文本生成、图像生成等。
- 其他:如序列标注、机器翻译、视频分析等。
2. 数据预处理
在构造LSTM模型前,需要进行数据预处理,生成用于训练LSTM模型的输入和输出数据。
例如:
from numpy import array
def split_sequence(sequence, n_steps):
x, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence) - 1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
x.append(seq_x)
y.append(seq_y)
return array(x), array(y)
# 样本数据
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# 选择时间步长
n_steps = 3
x, y = split_sequence(raw_seq, n_steps)
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
其中,左侧数据为输入序列,右侧数据为输出序列。输出序列将作为训练模型过程中的预测目标。
这段代码定义了一个名为split_sequence
的函数,它接受两个参数:原始时间序列数据sequence
和时间步长n_steps
。
时间步长是时间序列数据中相邻两个数据点的时间间隔。例如,如果一个时间序列数据集中的数据点每隔一小时采样一次,那么时间步长就是1小时。
函数返回两个数组x
和y
,分别表示输入和输出数据。其中,x
是一个二维数组,每一行代表一个时间步的输入数据,共有len(sequence) - n_steps
行;y
是一个一维数组,每个元素代表对应时间步的输出数据,共有len(sequence) - n_steps
个元素。
具体实现方面,函数通过一个循环遍历原始时间序列数据,每次取出n_steps
个连续的数据作为一个输入序列,同时取出该序列的下一个数据作为对应的输出。这样就可以生成一组输入和输出数据。随后,将所有的输入和输出数据分别保存到列表x
和y
中,并返回这两个列表。
3. 单变量LSTM模型
单变量LSTM模型是指只使用一个时间序列数据来训练和预测LSTM模型,即只有一个特征输入。这种模型通常用于处理单一变量的时间序列数据,例如股票价格或气温。
香草模型
基于 python 3.6 的环境,安装如下依赖包:
pip install tensorflow keras
首先,定义一个名为split_sequence
的函数,该函数的作用是将原始的时间序列数据按照给定的时间步长进行切割,生成用于训练LSTM模型的输入和输出数据:
def split_sequence(sequence, n_steps):
x, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence) - 1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
x.append(seq_x)
y.append(seq_y)
return array(x), array(y)
然后,定义一个原始时间序列数据raw_seq
和一个时间步长n_steps
。利用split_sequence
函数生成用于训练LSTM模型的输入和输出数据X
和y
。接着定义变量n_features
,其值为1,表示每个时间步的输入数据只有一个特征(此处的特征就是纯数字,若为气象数据则包含温度、湿度等特征,特征值则相应改变)。然后,将输入数据X
的形状从(batch_size, n_steps)
变为(batch_size, n_steps, n_features)
,以适应LSTM模型的输入要求:
输入数据
X
的形状从(batch_size, n_steps)
变为(batch_size, n_steps, n_features)
,其中batch_size
表示每个batch中包含的样本数,n_steps
表示时间步数,n_features
表示每个时间步的输入数据中包含的特征数量。
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
n_steps = 3
X, y = split_sequence(raw_seq, n_steps)
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
接下来便可以构建模型。当我们利用Keras定义一个LSTM模型时,我们需要指定模型的架构和训练过程中需要使用的优化器和损失函数。
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=200, verbose=0)
该模型应该包含一个LSTM层和一个全连接层。
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
LSTM层是一种循环神经网络层(基于RNN),它可以处理序列数据,包括文本、时间序列等等。全连接层是一种普通的神经网络层,它可以将输入数据的所有特征都连接到输出层,用于输出预测结果。
神经网络(Neural Network)是一种模拟人脑神经元工作方式的数学模型,用于解决各种机器学习和深度学习问题。神经网络由多个神经元(Neuron)组成,每个神经元接收多个输入,经过一系列数学运算后,输出一个结果。
循环神经网络(Recurrent Neural Network,简称RNN)是一种特殊的神经网络,它可以处理序列数据,例如文本、时间序列等等。与普通神经网络不同的是,RNN中的神经元可以接收来自上一个时间步的输出作为输入。这意味着RNN可以保留过去的信息,从而更好地处理序列数据。
在循环神经网络中,每个时间步都有一个神经元,它接收来自当前时间步的输入和上一个时间步的输出,并计算当前时间步的输出。这个过程可以看作是一个时间循环,从而得名循环神经网络。在LSTM这种特殊的RNN中,还加入了门控机制,可以更好地处理长序列数据。
在这个LSTM模型中,LSTM层包含50个神经元,这意味着LSTM层将生成50个输出,每个输出对应一个神经元。在LSTM层中,使用了ReLU激活函数。激活函数是一种非线性函数,它可以将神经元的输出转换为非线性值,从而增加模型的表达能力。
当神经元接收到输入数据后,它会计算一些权重和偏置,并将结果传递给激活函数进行处理。激活函数是一种非线性函数,它可以将神经元的输出转换为非线性值,从而增加模型的表达能力。
在LSTM层中,我们使用了ReLU激活函数。ReLU的全称是Rectified Linear Unit,它是一种常用的非线性激活函数。ReLU函数将所有负数输入值转换为零,而将所有正数输入值保持不变。这个函数的公式可以表示为:f(x) = max(0, x)。
使用ReLU激活函数可以使得LSTM模型更容易学习非线性关系,从而提高模型的表达能力。如果我们希望LSTM模型能够学习到输入数据中的某些模式,例如时间序列中的周期性变化,那么使用ReLU激活函数可以使得模型更容易学习到这些模式,并提高模型的准确性。
全连接层只有一个神经元,这意味着模型的输出只有一个值,用于输出预测结果。在这个模型中,输出层没有使用激活函数,因为我们希望输出的是一个连续值,而不是一个分类结果。
model.compile(optimizer='adam', loss='mse')
在编译模型时,我们需要指定优化器和损失函数。在这个模型中,我们使用了Adam优化器和均方误差(MSE)损失函数。Adam优化器是一种基于梯度下降的优化算法,用于调整模型的权重以最小化损失函数。
训练模型的过程通常是通过不断地调整模型的权重来最小化损失函数来完成的。损失函数可以用来评估模型的预测值和真实值之间的差异,我们希望这个差异越小越好。
Adam优化器是一种基于梯度下降的优化算法,它可以自适应地调整每个权重的学习率,以便更快地收敛到最优解。在模型训练过程中,我们计算损失函数的梯度,然后使用Adam优化器来更新模型的权重,从而使得模型能够更好地拟合数据。
如果不使用Adam优化器,那么可能会导致模型训练过程中收敛速度变慢,需要更长时间才能达到最优解。
均方误差(MSE)是一种常见的损失函数,用于评估模型的预测结果与实际结果之间的差距。我们的目标是最小化损失函数,从而提高模型的准确性和预测能力。
model.fit(X, y, epochs=200, verbose=0)
使用fit函数对LSTM模型进行训练,训练过程持续200个epochs,以预测输出序列y为目标。
fit函数是Keras中常用的函数之一,它可以用于训练模型。在训练模型时,我们需要提供输入数据和目标输出数据,以便模型能够学习如何将输入数据映射到输出数据。
在本例中,我们使用LSTM模型来预测输出序列y。因此,在训练模型时,我们将输入数据提供给模型,并指定目标输出序列y为训练目标。在训练过程中,模型将通过反向传播算法来更新模型的权重,以便更好地拟合输入和目标输出之间的关系。训练过程中,我们将模型迭代训练200次,每次训练称为一个epoch。在每个epoch中,模型将使用不同的训练数据来进行训练,直到模型的性能达到最优。最终,我们希望模型能够准确地预测输出序列y,从而实现我们的预测目标。
verbose是用于控制训练过程中输出信息的参数。当verbose设置为0时,Keras将不输出任何训练过程中的信息,例如每个epoch的训练损失和准确率等等。这通常用于在训练过程中不想看到大量输出信息的情况下,以加快训练速度。
相反,当verbose设置为1时,Keras将输出每个epoch的训练进度和性能信息,例如训练损失、准确率等等。当verbose设置为2时,Keras将只输出每个epoch的训练进度信息,而不会输出性能信息。
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)
最后,利用训练好的LSTM模型对一个新的输入序列进行预测。输入序列为[70, 80, 90]
,形状为(1, n_steps, n_features)
,其中1表示这是单个输入序列,与batch_size有关,这里只有一个输入序列。代码使用predict
函数得到模型的预测结果yhat
,并将其打印出来。
完整代码:
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
def split_sequence(sequence, n_steps):
x, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence) - 1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
x.append(seq_x)
y.append(seq_y)
return array(x), array(y)
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
n_steps = 3
X, y = split_sequence(raw_seq, n_steps)
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=200, verbose=0)
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.66596]]
堆叠LSTM模型
单变量的堆叠LSTM模型是指使用多个LSTM层来处理单变量时间序列预测问题的模型。在这个模型中,我们将单变量时间序列数据作为模型的输入,并使用多个LSTM层来学习序列中的模式,从而预测未来的值。堆叠LSTM模型通常可以更好地处理序列中的长期依赖关系,从而提高预测的准确性。
单变量LSTM模型通常只包含一个LSTM层,它可以对单变量时间序列进行建模和预测。虽然单层LSTM能够学习序列中的模式,但是它可能无法捕捉到更复杂的序列模式,这可能会导致预测结果不够准确。因此,单层LSTM模型可能无法处理更复杂的时间序列预测问题。
相反,单变量的堆叠LSTM模型包含多个LSTM层,它们可以一层一层地学习序列中的模式,从而更好地捕捉序列中的长期依赖关系。由于堆叠LSTM模型能够处理更复杂的序列模式,因此它通常能够提供更准确的预测结果,特别是对于需要预测较长时间步的时间序列问题。
另外,堆叠LSTM模型还可以通过调整层数和每层的神经元数量来提高模型的灵活性和适应性,这使得它能够更好地适应不同的时间序列预测问题。
相对于单变量LSTM模型,我们在单变量堆叠LSTM模型中定义了两个LSTM层,每个层包含50个神经元。这两个LSTM层都采用了ReLU激活函数,并且第一个LSTM层的输出被传递给第二个LSTM层作为输入。
第一个LSTM层可以学习并提取输入序列中的某些特征,然后将这些特征传递给第二个LSTM层,让第二个LSTM层能够进一步学习序列中的更高层次的模式。这种方式可以帮助模型更好地捕捉时间序列中的长期依赖关系,从而提高预测准确性。
需要注意的是,第二个LSTM层的输入不仅包括第一个LSTM层的输出序列,还包括一些其他信息,例如序列中的时间步信息等。这些信息都将被整合到第二个LSTM层的输入中,以帮助模型更好地学习时间序列中的模式。
最后,我们添加了一个全连接层,用于输
出预测结果。
model = Sequential()
model.add(LSTM(50, activation='relu', return_sequences=True, input_shape=(n_steps, n_features)))
model.add(LSTM(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=200, verbose=0)
完整代码:
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
def split_sequence(sequence, n_steps):
x, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence) - 1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
x.append(seq_x)
y.append(seq_y)
return array(x), array(y)
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
n_steps = 3
X, y = split_sequence(raw_seq, n_steps)
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
model = Sequential()
model.add(LSTM(50, activation='relu', return_sequences=True, input_shape=(n_steps, n_features)))
model.add(LSTM(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=200, verbose=0)
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.47101]]
双向LSTM模型
双向LSTM模型是一种能够利用序列中所有信息的LSTM模型,它通过从前向后和从后向前两个方向学习序列中的模式,进一步提高了模型的预测准确性。
在双向LSTM中,"双向"指的是LSTM模型从两个方向学习序列中的模式。具体来说,LSTM模型从正向和反向两个方向分别学习序列中的模式,然后将这些模式结合起来,产生一个更全面的序列表示。
在单向LSTM中,模型只能从前向后学习序列中的模式。这意味着,模型在处理序列数据时,只能考虑当前时间步之前的信息,无法利用当前时间步之后的信息,这可能会影响模型的预测准确性。
相反,在双向LSTM中,模型不仅可以从前向后学习序列中的模式,还可以从后向前学习序列中的模式。这样做的好处是,模型可以利用当前时间步之前和之后的信息,从两个方向学习序列中的模式,从而更好地捕捉序列中的长期依赖关系和复杂模式。例如,对于一个语音识别任务,双向LSTM可以同时考虑前面和后面的声音信号,从而更好地识别语音中的单词和音节。
总之,双向LSTM中的"双向"指的是模型从正向和反向两个方向分别学习序列中的模式,从而更好地利用序列中的信息,提高模型的预测准确性。
model = Sequential()
model.add(Bidirectional(LSTM(50, activation='relu'), input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=500, verbose=0)
完整代码:
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import Bidirectional
def split_sequence(sequence, n_steps):
x, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence) - 1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
x.append(seq_x)
y.append(seq_y)
return array(x), array(y)
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
n_steps = 3
X, y = split_sequence(raw_seq, n_steps)
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
model = Sequential()
model.add(Bidirectional(LSTM(50, activation='relu'), input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=500, verbose=0)
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)
输出结果:
[[100.44518]]
卷积神经网络LSTM模型
该模型使用了一个TimeDistributed卷积层来提取序列特征,然后将这些特征输入到LSTM层中进行时间序列建模。
卷积层是一种可以从输入数据中提取局部特征的神经网络层,它可以通过滤波器和卷积操作来实现对输入数据的高效处理和表征学习,从而提高模型的预测性能。
因此,它融合了CNN和LSTM的特点,可以同时利用卷积层的局部特征提取和LSTM的长期依赖学习能力,从而更好地处理序列数据。TimeDistributed LSTM常用于处理序列数据的深度学习模型中,如语音识别、自然语言处理、视频分析等领域。
TimeDistributed是一种用于将一个层应用到输入的每个时间步的技术,即对每个时间步都使用相同的层结构进行处理。在这个模型中,我们将输入序列分成了两个子序列,每个子序列都由两个时间步组成,然后使用TimeDistributed来将卷积层应用到每个时间步上。
n_steps = 4
X, y = split_sequence(raw_seq, n_steps)
n_features = 1
n_seq = 2
n_steps = 2
X = X.reshape((X.shape[0], n_seq, n_steps, n_features))
定义一个TimeDistributed卷积层,一个TimeDistributed池化层和一个TimeDistributed展平层。这些层将卷积、池化和展平操作应用到每个时间步上,从而提取出序列中的特征表示。
卷积层可以将输入序列中的每个时间步看作一个二维图片,然后利用一系列可训练的卷积核来对每个时间步进行卷积操作。这个卷积操作可以类比于图片处理中的滤波器操作,可以提取出输入序列中的各种局部特征,如边缘、纹理等。然后,我们使用TimeDistributed技术,将卷积层应用到每个时间步上,从而得到一个新的序列,其中每个时间步都表示输入序列中对应时间步的局部特征。
利用池化层来对卷积层的输出进行下采样。池化层可以将输入序列中的每个时间步看作一个二维图片,然后在每个时间步中对某个区域的数值进行池化操作,如最大池化、平均池化等。这个池化操作可以类比于图片处理中的降采样操作,可以减少序列数据的维度,同时保留重要的特征信息。然后,我们再次使用TimeDistributed技术,将池化层应用到每个时间步上,从而得到一个新的序列,其中每个时间步都表示输入序列中对应时间步的池化特征。
使用展平层将池化层的输出展平为一个一维向量,以便将其输入到LSTM层中进行时间序列建模。展平层可以看作是将序列数据中的每个时间步展开为一个独立的特征,从而方便进行后续处理。
最后,我们使用一个LSTM层和一个全连接层来预测序列的下一个时间步的值,并使用Adam优化器和均方误差(MSE)损失函数来编译模型:
model = Sequential()
model.add(TimeDistributed(Conv1D(filters=64, kernel_size=1, activation='relu'), input_shape=(None, n_steps, n_features)))
model.add(TimeDistributed(MaxPooling1D(pool_size=2)))
model.add(TimeDistributed(Flatten()))
model.add(LSTM(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=1000, verbose=0)
其中,Conv1D表示一维卷积层,filters=64表示使用64个卷积核,kernel_size=1表示卷积核的大小为1,activation='relu’表示使用ReLU激活函数。input_shape=(None, n_steps, n_features)表示输入数据的形状,其中None表示输入序列的长度可以是任意值,n_steps表示每个输入序列的时间步数,n_features表示每个时间步上的特征数
model.add(TimeDistributed(Conv1D(filters=64, kernel_size=1, activation='relu'), input_shape=(None, n_steps, n_features)))
卷积层是深度学习中常用的一种层,用于提取输入数据中的特征。在卷积层中,通过在输入数据上滑动一个卷积核(也称为过滤器),来提取出局部特征。一维卷积层和二维卷积层的卷积核的形状不同,但都是通过滑动卷积核来提取输入数据的局部特征。
filters参数表示卷积层中使用的卷积核的数量,即卷积操作时使用的过滤器的数量。使用多个卷积核可以提取出多种不同的特征,从而提高模型的性能。
kernel_size参数表示卷积核的大小,即卷积核在输入数据上滑动的区域大小。卷积核的大小越大,可以提取的特征越广泛,但也会增加模型的计算复杂度。卷积核的大小需要根据具体任务进行调整。
对于一维卷积层而言,卷积核大小为1表示每次只对当前时间步的特征进行卷积操作,而不考虑与其他时间步的特征之间的关系。这种操作方式适用于一些简单的时间序列任务,如预测下一个时间步的数据。
对于二维卷积层而言,卷积核大小为1也类似地表示每次只对当前像素点的特征进行卷积操作,而不考虑与其他像素点之间的关系。这种操作方式适用于一些简单的图像处理任务,如图像分类。
32个卷积核和64个卷积核是常见的设置。使用更多的卷积核可以提取更多的特征,但也会增加计算复杂度和模型大小。具体使用多少个卷积核需要根据具体任务和数据集进行调整。
完整代码:
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import TimeDistributed
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
def split_sequence(sequence, n_steps):
x, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence) - 1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
x.append(seq_x)
y.append(seq_y)
return array(x), array(y)
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
n_steps = 4
X, y = split_sequence(raw_seq, n_steps)
n_features = 1
n_seq = 2
n_steps = 2
X = X.reshape((X.shape[0], n_seq, n_steps, n_features))
model = Sequential()
model.add(TimeDistributed(Conv1D(filters=64, kernel_size=1, activation='relu'), input_shape=(None, n_steps, n_features)))
model.add(TimeDistributed(MaxPooling1D(pool_size=2)))
model.add(TimeDistributed(Flatten()))
model.add(LSTM(50, activation='relu'))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=1000, verbose=0)
x_input = array([60, 70, 80, 90])
x_input = x_input.reshape((1, n_seq, n_steps, n_features))
yhat = model.predict(x_input, verbose=0)
print(yhat)
输出结果:
[[100.353325]]
卷积二维层LSTM模型
这是一个使用ConvLSTM2D层的LSTM模型,而ConvLSTM2D是一种特殊的LSTM层,它可以处理具有空间结构的输入数据,例如图像或视频。相对于传统的LSTM模型,ConvLSTM2D可以处理更加复杂的输入数据,它可以在时间维度和空间维度上同时进行计算。这种模型通常被用于视频分析、天气预测等领域。这些领域的数据往往具有空间和时间上的结构。
n_features = 1
n_seq = 2
n_steps = 2
X = X.reshape((X.shape[0], n_seq, 1, n_steps, n_features))
ConvLSTM2D层需要五维形状,因此需要将预处理的数据重塑成ConvLSTM2D层接受的形状。我们将输入数据重塑成了一个5维的数组,其中第一维表示样本数量,第二维表示序列数量,第三维表示通道数量,第四维表示时间步数,第五维表示特征数量。具体地,我们将输入序列重塑成了2个子序列,并将每个子序列重塑成了一个2维的矩阵,其中行数为1,列数为2,表示每个子序列包含2个时间步。由于每个时间步只有一个特征,因此特征数量为1。
通道数量通常指的是卷积神经网络(CNN)或者其他一些具有卷积操作的神经网络中的卷积核数量。在ConvLSTM2D中,通道数量也是指卷积核的数量。在这个示例中,我们将通道数量设置为1,因为每个时间步只有一个特征。如果您将通道数量设置为100,那么模型会使用100个卷积核来提取特征,但这可能会导致模型过度拟合或者需要更多的训练数据来减少过拟合的影响。在设置通道数量时需要根据具体的问题和数据集进行调整,以平衡模型的性能和泛化能力。
model = Sequential()
model.add(ConvLSTM2D(filters=64, kernel_size=(1,2), activation='relu', input_shape=(n_seq, 1, n_steps, n_features)))
model.add(Flatten())
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=1000, verbose=0)
添加了一个ConvLSTM2D层,其中包含了64个过滤器,每个过滤器的大小为1行2列(二维)。这个层的激活函数是ReLU函数。接着,我们向模型中添加了一个Flatten层,将多维的输出数据展平成一维。最后,我们向模型中添加了一个全连接层(Dense),其中只有一个节点,用于输出预测值。模型的损失函数使用均方误差(MSE),优化器使用Adam算法。
输出结果:
[[103.23106]]
4. 多变量LSTM模型
多变量LSTM模型则是指使用多个时间序列数据来训练和预测LSTM模型,即有多个特征输入。这种模型通常用于处理包含多个变量的时间序列数据,例如同时考虑股票价格、交易量和新闻情感等多个因素对股价的影响。
多个并行输入时间序列
具有两个或多个并行输入时间序列和一个依赖于输入时间序列的输出时间序列。例如:输出序列是输入序列的简单相加。
from numpy import array
from numpy import hstack
def split_sequences(sequences, n_steps):
X, y = list(), list()
for i in range(len(sequences)):
end_ix = i + n_steps
if end_ix > len(sequences):
break
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)
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))])
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))
dataset = hstack((in_seq1, in_seq2, out_seq))
n_steps = 3
X, y = split_sequences(dataset, n_steps)
print(X.shape, y.shape)
for i in range(len(X)):
print(X[i], y[i])
输出结果:
(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
然后,我们可以使用上一节中的任何单变量LSTM模型变体,例如香草、堆叠、双向、CNN 或 ConvLSTM 模型拟合此人为数据集。
使用香草模型:
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
进行预测时,模型需要两个输入时间序列的三个时间步长。
我们可以预测输出序列中的下一个值,提供以下输入值:
80, 85
90, 95
100, 105
具有三个时间步长和两个变量的一个样本的形状必须是 [1, 3, 2]。
我们期望序列中的下一个值为 100 + 105,即 205。
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)
以下为完整代码:
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
def split_sequences(sequences, n_steps):
X, y = list(), list()
for i in range(len(sequences)):
end_ix = i + n_steps
if end_ix > len(sequences):
break
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)
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))])
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))
dataset = hstack((in_seq1, in_seq2, out_seq))
n_steps = 3
X, y = split_sequences(dataset, n_steps)
n_features = X.shape[2]
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=1000, verbose=0)
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)
输出结果为:
[[205.72319]]
多个并行时间序列
存在多个并行时间序列并且必须为每个时间序列预测一个值。
例如,针对以下数据集:
[[ 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
可以使用以下代码:
from numpy import array
from numpy import hstack
def split_sequences(sequences, n_steps):
X, y = list(), list()
for i in range(len(sequences)):
end_ix = i + n_steps
if end_ix > len(sequences) - 1:
break
seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
X.append(seq_x)
y.append(seq_y)
return array(X), array(y)
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))])
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))
dataset = hstack((in_seq1, in_seq2, out_seq))
n_steps = 3
X, y = split_sequences(dataset, n_steps)
print(X.shape, y.shape)
for i in range(len(X)):
print(X[i], y[i])
输出结果为:
(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]
然后,我们可以使用上一节中的任何单变量LSTM模型变体,例如香草、堆叠、双向、CNN 或 ConvLSTM 模型拟合此人为数据集。
使用堆叠模型:
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_features))
model.compile(optimizer='adam', loss='mse')
具有以下输入形状:
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]
完整代码:
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
def split_sequences(sequences, n_steps):
X, y = list(), list()
for i in range(len(sequences)):
end_ix = i + n_steps
if end_ix > len(sequences) - 1:
break
seq_x, seq_y = sequences[i:end_ix, :], sequences[end_ix, :]
X.append(seq_x)
y.append(seq_y)
return array(X), array(y)
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))])
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))
dataset = hstack((in_seq1, in_seq2, out_seq))
n_steps = 3
X, y = split_sequences(dataset, n_steps)
n_features = X.shape[2]
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_features))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=1000, verbose=0)
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.508255 105.650116 205.78018 ]]
5. 多步LSTM模型
需要预测未来多个时间步长的时间序列预测问题可称为多步时间序列预测。
矢量输出模型
我们希望给定单变量时间序列:
[10, 20, 30, 40, 50, 60, 70, 80, 90]
然后使用三个时间步长以预测两个时间步长,例如,输入序列为:
[10, 20, 30]
输出序列应为:
[40, 50]
可以使用以下代码生成训练数据:
from numpy import array
def split_sequence(sequence, n_steps_in, n_steps_out):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps_in
out_end_ix = end_ix + n_steps_out
if out_end_ix > len(sequence):
break
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)
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
n_steps_in, n_steps_out = 3, 2
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
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]
然后我们可以重塑形状:
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
LSTM 期望数据具有 [样本、时间步长、特征] 的三维结构,在这种情况下,我们只有一个特征。
然后,我们可以使用上一节中的任何单变量LSTM模型变体,例如香草、堆叠、双向、CNN 或 ConvLSTM 模型拟合此人为数据集。
此处定义一个用于多步骤预测的堆叠 LSTM:
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps_in, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')
例如,我们希望输入:
[70, 80, 90]
预测结果为:
[100, 110]
代码表示为:
x_input = array([70, 80, 90])
x_input = x_input.reshape((1, n_steps_in, n_features))
yhat = model.predict(x_input, verbose=0)
完整代码:
from numpy import array
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
def split_sequence(sequence, n_steps_in, n_steps_out):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps_in
out_end_ix = end_ix + n_steps_out
if out_end_ix > len(sequence):
break
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)
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
n_steps_in, n_steps_out = 3, 2
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps_in, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=5000, verbose=0)
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)
输出结果:
[[101.4844 111.92061]]
编码器-解码器模型
专门为预测可变长度输出序列而开发的模型称为编码器-解码器 LSTM。
该模型专为同时存在输入和输出序列的预测问题而设计,即所谓的序列到序列问题,例如将文本从一种语言翻译成另一种语言。
此模型可用于多步骤时间序列预测。编码器是负责读取和解释输入序列的模型。编码器的输出是一个固定长度的向量,表示模型对序列的解释。编码器传统上是 Vanilla LSTM (香草)模型,也可以使用其他编码器模型,例如堆叠、双向和 CNN 模型。
例如,此处使用香草模型作为编码器:
model.add(LSTM(100, activation='relu', input_shape=(n_steps_in, n_features)))
那么,解码器使用编码器的输出作为输入。在那之前需要先将输出序列重复一次:
model.add(RepeatVector(n_steps_out))
假设你正在写一个机器翻译模型,将英文翻译成法语。你的模型的输入是一个英文句子,输出是一个对应的法语句子。为了实现这个模型,你使用了编码器-解码器模型。
在编码器-解码器模型中,编码器将输入序列编码成一个向量,然后解码器使用这个向量来生成输出序列。但是,解码器需要在每个时间步骤上都使用这个向量来生成相应的输出,因为每个时间步骤的输出都可能不同。
假设你的输出序列是 5 个词,因此
n_steps_out
是 5。如果你的编码器输出一个长度为 100 的向量,那么解码器需要在每个时间步骤上都使用这个向量来生成相应的输出。为了实现这一点,你需要将编码器的输出向量重复 5 次,这样解码器就可以在每个时间步骤上都使用这个向量来生成相应的输出。
然后将此序列提供给 LSTM 解码器模型:
model.add(LSTM(100, activation='relu', return_sequences=True))
在解码器的最后一层,我们需要使用一个全连接层来将 LSTM 层的输出映射成一个标量值。因为我们需要在每个时间步骤上预测一个输出值,所以我们需要使用 TimeDistributed
层来将全连接层应用到序列的每个时间步骤上:
model.add(TimeDistributed(Dense(1)))
与其他 LSTM 模型一样,输入数据必须重塑为预期的三维形状 [样本、时间步长、特征]:
X = X.reshape((X.shape[0], X.shape[1], n_features))
对于编码器-解码器模型,训练数据集的输出或 y 部分也必须具有此形状。这是因为模型将预测每个输入样本具有给定特征数的给定时间步长:
y = y.reshape((y.shape[0], y.shape[1], n_features))
完整代码:
from numpy import array
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
def split_sequence(sequence, n_steps_in, n_steps_out):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps_in
out_end_ix = end_ix + n_steps_out
if out_end_ix > len(sequence):
break
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)
raw_seq = [10, 20, 30, 40, 50, 60, 70, 80, 90]
n_steps_in, n_steps_out = 3, 2
X, y = split_sequence(raw_seq, n_steps_in, n_steps_out)
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
y = y.reshape((y.shape[0], y.shape[1], n_features))
model = Sequential()
model.add(LSTM(100, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(RepeatVector(n_steps_out))
model.add(LSTM(100, activation='relu', return_sequences=True))
model.add(TimeDistributed(Dense(1)))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=100, verbose=0)
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.016335]
[114.85793 ]]]
6. 多变量多步LSTM模型
多输入多步输出
存在一些多变量时间序列预测问题,其中输出序列是独立的,但依赖于输入时间序列,并且输出序列需要多个时间步长。
例如,有此多变量时间序列:
[[ 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
可以使用此代码生成训练数据集:
from numpy import array
from numpy import hstack
def split_sequences(sequences, n_steps_in, n_steps_out):
X, y = list(), list()
for i in range(len(sequences)):
end_ix = i + n_steps_in
out_end_ix = end_ix + n_steps_out-1
if out_end_ix > len(sequences):
break
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)
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))])
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))
dataset = hstack((in_seq1, in_seq2, out_seq))
n_steps_in, n_steps_out = 3, 2
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
print(X.shape, y.shape)
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]
可以看到样本的输入部分的形状是三维的,由六个样本组成,具有三个时间步长,以及 2 个输入时间序列的两个变量。样本的输出部分对于六个样本是二维的,对于要预测的每个样本,样本的两个时间步长是二维的。
可以使用矢量输出或编码器-解码器模型。此处定义堆叠 LSTM 模型,完整代码如下:
from numpy import array
from numpy import hstack
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
def split_sequences(sequences, n_steps_in, n_steps_out):
X, y = list(), list()
for i in range(len(sequences)):
end_ix = i + n_steps_in
out_end_ix = end_ix + n_steps_out-1
if out_end_ix > len(sequences):
break
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)
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))])
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))
dataset = hstack((in_seq1, in_seq2, out_seq))
n_steps_in, n_steps_out = 3, 2
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
n_features = X.shape[2]
model = Sequential()
model.add(LSTM(100, activation='relu', return_sequences=True, input_shape=(n_steps_in, n_features)))
model.add(LSTM(100, activation='relu'))
model.add(Dense(n_steps_out))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=200, verbose=0)
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]
实际输出结果为:
[[183.40097 205.77441]]
多路并行输入和多步输出
仍然是该多变量时间序列:
[[ 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
可以使用以下代码生成训练数据:
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)):
end_ix = i + n_steps_in
out_end_ix = end_ix + n_steps_out
if out_end_ix > len(sequences):
break
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)
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))])
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))
dataset = hstack((in_seq1, in_seq2, out_seq))
n_steps_in, n_steps_out = 3, 2
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
print(X.shape, y.shape)
for i in range(len(X)):
print(X[i], y[i])
输出结果为:
(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]]
然后,使用编码器-解码器模型,以下为完整代码:
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)):
end_ix = i + n_steps_in
out_end_ix = end_ix + n_steps_out
if out_end_ix > len(sequences):
break
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)
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))])
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))
dataset = hstack((in_seq1, in_seq2, out_seq))
n_steps_in, n_steps_out = 3, 2
X, y = split_sequences(dataset, n_steps_in, n_steps_out)
n_features = X.shape[2]
model = Sequential()
model.add(LSTM(200, activation='relu', input_shape=(n_steps_in, n_features)))
model.add(RepeatVector(n_steps_out))
model.add(LSTM(200, activation='relu', return_sequences=True))
model.add(TimeDistributed(Dense(n_features)))
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=300, verbose=0)
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.59101 95.89372 185.98097]
[100.69182 105.41041 206.77353]]]
参考
How to Develop LSTM Models for Time Series Forecasting - MachineLearningMastery.com
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)