LSTM(Long Short-Term Memory) 即长短期记忆,适合于处理和预测时间序列中间隔和延迟非常长的重要事件。其中的内部机制就是通过四个门调节信息流,了解序列中哪些数据需要保留或丢弃。
假设你在网上查看淘宝评论,以确定你是否想购买生活物品。你将首先阅读评论,然后确定是否有人认为它是好的或是否是坏的。
当你阅读评论时,你的大脑下意识地只会记住重要的关键词。你会选择“惊人”和“完美均衡的早餐”这样的词汇。你不太关心“this”,“give”,“all”,“should”等。如果你的朋友第二天问你评论说什么,你不可能一字不漏地记住它。但你可能还记得主要观点,比如“肯定会再次购买”。其他的话就会从记忆中逐渐消失。
这基本上就是LSTM或GRU的作用。它可以学习只保留相关信息来进行预测,并忘记不相关的数据。在这种情况下,你记住的词让你判断它是好的。
LSTM 的核心概念是细胞状态,三个门和两个激活函数。细胞状态充当高速公路,在序列链中传递相关信息。门是不同的神经网络,决定在细胞状态上允许那些信息。有些门可以了解在训练期间保持或忘记那些信息。
用于调节流经神经网络的值,限制在-1和1之间,防止梯度爆炸
与激活函数 Tanh不同,他是在0和1之间取值。这有助于更新或忘记数据,因为任何数字乘以0都是0,这会导致值小时或被”遗忘”。而任何数字乘1都是相同的值。网络可以通过这种方法了解那些数据不重要或那些数据重要。
遗忘门决定应该丢弃或保留那些信息。来自先前隐藏状态的信息和来自当前输入的信息通过sigmoid函数传递。值接近0和1之间,越接近0意味着忘记,越接近1意味着要保持。
输入门可以更新细胞状态,将先前的隐藏状态和当前输入分别传递sigmoid函数和tanh函数。然后将两个函数的输出相乘。
细胞状态逐点乘以遗忘向量(遗忘门操作得到),然后与输入门获得的输出进行逐点相加,将神经网络发现的新值更新为细胞状态。
输出门可以决定下一个隐藏状态应该是什么,并且可用于预测。首先将先前的隐藏状态和当前的输入传给sigmoid函数,然后将新修改的细胞状态传递给tanh函数,最后就结果相乘。输出的是隐藏状态,然后将新的细胞状态和新的隐藏状态移动到下一个时间序列中。
从上述图解操作,我们可以轻松的理解LSTM的数学描述。
from keras.models import Sequential
from keras.layers.core import Dense, Activation, Dropout
from keras.layers.recurrent import LSTM
models
是 Keras 神经网络的核心。这个对象代表这个我们所定义的神经网络:它有层、激活函数等等属性和功能。我们进行训练和测试也是基于这个models
。 Sequetial
表示我们将使用层堆叠起来的网络,这是Keras中的基本网络结构。Dense, Activation, Dropout
这些是神经网络里面的核心层,用于构建整个神经网络。Dense
实际上就是 Fully-connected 层;Activation
是激活函数,它会通过Relu, Softmax 等函数对上一层产生的结果进行修改;当神经元过多的时候,可能效果并不好,因为容易导致过拟合的现象,Dropout
是将上一层神经元进行随机丢弃,有助于解决过拟合的问题。LSTM
是经典的RNN神经网络层。因为 LSTM 是预测时间序列,即比如通过前19个数据去预测第20个数据。所有每次喂给LSTM的数据也必须是一个滑动窗口。
而这其中的19个数据就是我们训练集X的一个样本,第20个为训练集Y样本。也就是说,我们用前19个值,去预测第20个值,然后对比预测至与第20个的真实值。通过这样的误差不断的优化我们模型。
for index in range(len(data) - sequence_length):
result.append(data[index: index + sequence_length])
result = np.array(result)
当然这是一维样本的情况,如果我们的数据如下。
x1 | x2 | x3 | x4 | y |
---|---|---|---|---|
0.363986149 | 0.333333333 | 0.713178295 | 0.724661696 | 191 |
0.411312043 | 0.333333333 | 0.666666667 | 0.731013532 | 190 |
0.357445171 | 0.166666667 | 0.627906977 | 0.621375311 | 189 |
0.166602539 | 0.333333333 | 0.573643411 | 0.662386081 | 188 |
0.402077722 | 0.416666667 | 0.589147287 | 0.704501519 | 187 |
0.330511735 | 0.25 | 0.651162791 | 0.652720243 | 186 |
那么滑动窗口将是一个矩阵形式。假设滑动窗口为3行,则训练集X的第一个样本是
x1 | x2 | x3 | x4 |
---|---|---|---|
0.363986149 | 0.333333333 | 0.713178295 | 0.724661696 |
0.411312043 | 0.333333333 | 0.666666667 | 0.731013532 |
0.357445171 | 0.166666667 | 0.627906977 | 0.621375311 |
去预测的是第4行的y。然后对比预测至与第4行y的真实值。通过这样的误差不断的优化我们模型。
model = Sequential()
model.add(LSTM(units=100, input_shape=(30, 40), return_sequences=True))
model.add(Dropout(0.2))
model.add(LSTM(units=50, return_sequences=False))
model.add(Dropout(0.2))
model.add(Dense(units=1))
model.add(Activation("linear"))
model.compile(loss="mse", optimizer="adam", metrics=['mae', 'mape'])
实例化模型后我们添加了两层隐含层(LSTM层)和一个输出期望(Dense层),激活函数设置为线性(linear),其中每完成一层计算丢弃20%的数据(Dropout层)防止过拟合。最后我们使用 MSE 进行误差计算,优化函数选择 adam,评价指标为 mae 和 mape
return_sequences=True/False
return_sequence=True
为多对多的关系(上图第5种情况),return_sequence=False
为多对一的关系(上图第3种情况)
我们这里建立了两层LSTM,第一层因为我们希望把每一次的输出信息都输入到下一层LSTM作为训练数据(箭头向上),也同时也将信息传给下一个自己作为输入数据(箭头向右)。而第二层连接Dense层,只期望一个输出。所以第一层为多对多的关系,第二层为多对一的关系。
input_shape
LSTM 的输入是一个三维数组,尽管他的input_shape
为二维,但我们的输入必须也是(批次大小, 时间步长, 单元数)即每批次输入LSTM的样本数,时间步长,训练集的列数。
input_shape=(time_steps, input_dim)
只接受时间步长和单元数是因为可以自动切分批次大小,如果需要固定批次大小,可以通过batch_input_shape=(batch_size,time_steps,input_dim)
参数实现。
units
指设置的细胞单元数量,也可当做输出维度(因为在不考虑细胞状态输出的情况下,每一个细胞单元只有一个隐藏关系的输出)。比如,我们这里设置的输入为input_shape=(30, 40)
,而我们设置的单元数为100,那么我们最终输出就是(100,4)了。
Dense
Dense
层接受上一层传递过来的输出数据,然后与激活函数结合真实值进行loss计算和优化等操作,设置的单元数units
同上也可当做输出维度。
fit_result = model.fit(trainX, trainY, epochs=100, batch_size=200)
跟机器学习算法一样,也是输入训练集X、Y,epochs
为循环训练次数,batch_size
为单次训练的样本数。
predicted = model.predict(testX)
与训练模型时喂数据一致,输入一个testX数组,testX[0]为一个滑动窗口所有的样本,例如一维数组前19个,预测的结果是第20个。那么predicted[0]就为预测的第20个结果。
同理,predicted就返回了一个与testY一样大的数组。
Illustrated Guide to LSTM’s and GRU’s: A step by step explanation 一文了解LSTM和GRU背后的秘密(绝对没有公式) 人人都能看懂的LSTM 使用Keras中的RNN模型进行时间序列预测 用「动图」和「举例子」讲讲 RNN Understanding Input and Output shapes in LSTM | Keras