LSTM的时序应用

RNN与LSTM

在上一讲中,我们简单介绍了RNN的思想。RNN是一种使用类似链表的形式、具有一定记忆能力的网络模型。对于具有序列性的样本数据,记住过去的信息对预测当前状态是非常必要的。相比于一般的神经网络的组成,RNN会额外增加一个T-1时刻隐含层到T时刻隐含层的传播矩阵。

RNN中的隐含层可以想象为我们的记忆,在当前作决定的时候我们会考虑记忆中过去的情况,这就是所谓的利用经验判断;而老的记忆会随时间的流失会被不断遗忘,因此在做当前判断时不会用到时间间隔很长的记忆。

然而不幸的是,当记忆的间隔时间需要很长的时候,训练 RNN 变得非常困难,其根本原因在于训练的时候会出现梯度消失或者梯度爆炸的现象,关于这一点此处不展开说明。LSTM(Long Short-Term Memory)是一种具有长记忆特征的RNN,它有效地解决了RNN在训练时出现的梯度问题。

LSTM的独特之处在于它有一个专门进行记忆的存储单元,这个存储单位由一些门神经元保护。这些门神经元与一般的神经元不同在于它们有开和关两个状态,当处于开的状态时,连接权值为1;反正则权重为0。最简单的LSTM包含以下3种不同功能的门神经元:

  • keep(保存门/遗忘门):保存门开,存储单位记忆的内容不清除;保存们关,清除以前记忆的内容。

  • read(读取们/输出门):读取门开,其他神经元可读取存储单元保存的内容;读取门关,则其他神经元无法读取存储单元。

  • write(写入门/输入门):写入门开,存储单元可以写入其他神经元的内容作为记忆的一部分,反之则无法写入。


与RNN相比,LSTM在T-1时刻隐含层到T时刻隐含层之间增加了这样的一个结构,解决了训练时出现的梯度问题,具有长期记忆的功能。注意到每个门神经元都有自己的权值需要模型进行训练。


时间序列的特征工程

在数据挖掘中,我们的首要任务是选取有用的特征,选取一些对我们预测变量有较大关联和影响的特征有利于预测的准确性。在时间序列的挖掘模型中,常常用到的特征有以下几类:

  • 时间自身的特征:即时间自身的属性,一般有如下一些特征:星期数、月份数、季节、季度数、是否是工作日、是否是周末、是否是节假日。

  • 滞后项的特征:之前N期的时间序列值。

  • 窗口特征:窗口特征分为两种。一种是以窗口滚动计算的指标,比如在量化投资中经常用到的moving average;另一种是利用过去所有值计算的指标,如迄今为止的最大值、最小值、平均值等。


LSTM的时序预测

我们采用keras包进行LSTM模型的训练和预测。keras是一个简单易学的深度学习框架,它做了非常高层的封装因此模型搭建十分快速。它的后端可以选用theano或者tensorflow,此外你可以在keras使用theano或者tensorflow的语句满足扩展性的算法需要。

下面我们列举一个使用LSTM进行时序预测的例子,数据集大家可以在pan.baidu.com/s/1kUY25q下载(提取码bv97),字段val是时序值,也就是我们需要预测的,val后面的其他字段是我已经整理好的特征,我们希望训练出一个模型可以在T时刻预测T+1时刻的值。

首先是将样本拆分为训练集和测试集,我们用sample_split函数实现这一功能。

以上的代码有一些点需要重点解释的:

1)将样本按照时间顺序的前2/3部分作为训练集,后1/3部分作为测试集;

2)is_scale这个参数控制是否对样本做0-1的归一化处理,在LSTM的模型训练中一般是需要进行归一化处理的。这里可以采用sklearn中的sklearn.preprocessing.MinMaxScaler来处理。

在拆分样本后,我们就需要正式采用LSTM进行测试和预测了。

这段代码比较长,我们来分别解析代码的含义。

train_x = np.reshape(train_x, (train_x.shape[0], 1, train_x.shape[1]))

test_x = np.reshape(test_x, (test_x.shape[0], 1, test_x.shape[1]))

这一步是对矩阵x做变形,使得输入满足LSTM的要求[samples, time steps, features]。

model = Sequential()

model.add(LSTM(hidden_number, input_dim=df.values.shape[1]-2))

model.add(Dense(1))

model.compile(loss='mean_squared_error', optimizer='adam')

这里是网络结果的构建和算法的选择,此处我构建一个简单的3层LSTM网络,输入层神经元个数为特征数,隐含层神经元个数可以作为参数指定,输出层神经元个数为1(只预测未来1期),损失函数是均方误差,优化方法为adam。

model.fit(train_x, train_y, nb_epoch=1750, batch_size=int(train_x.shape[0]/3), verbose=2)

train_predict, test_predict = model.predict(train_x), model.predict(test_x)

这一步是模型的训练和预测,关于epoch和batch相关的知识后续再详细说明。

scaler = MinMaxScaler(feature_range=(0, 1))

scaler_y = scaler.fit_transform(df.values[:,1])

train_y, test_y= scaler.inverse_transform([train_y]), scaler.inverse_transform([test_y])

train_predict, test_predict = scaler.inverse_transform(train_predict), scaler.inverse_transform(test_predict)

为了计算最终测试的均方误差,将模型预测得到的y进行反归一化,还原成本身的值。

test_score = math.sqrt(mean_squared_error(test_y[0], test_predict[:,0]))

print('Test Score: %.2f RMSE' % (test_score))

计算并打印测试样本预测结果的RMSE。

以上函数可以在main中调用:

if __name__ == "__main__" :

np.random.seed(7)

df = pd.read_csv("your_path")

lstm_predict(df, hidden_number = 6)

发布于 2017-05-22

文章被以下专栏收录