欢迎来到theFlyer的博客—希望你有不一样的感悟
前言:这次内容是LSTM,主要讲解了LSTM的前向传播和一个飞机流量的代码,若无基础建议先看下循环神经网络RNN。
目录
一. LSTM提出原因
RNN存在的问题
记忆最大的问题在于遗忘性,我们能更加清楚记得最近发生的事情而遗忘很久之前的事情。循环神经网络同样有这样的问题。循环神经网络可以解决短时依赖问题。相关的信息和预测的词位置之间的间隔是非常小的,RNN 可以学会使用先前的信息.
若记忆信息和预测位置之间跨度太大,循环神经网络就越来越难学到这些信息。
问题存在原因
在RNN中,采用了Sigmoid函数,它的导数值域范围是(0,1/4]内,每次反向传播都会以1/4的速度递减,时间越远梯度减小越明显。 例:若当前时刻为t,在(t-3)时刻时,梯度将减少至少(0.25*0.25*0.25=1/64),这就是原始RNN无法处理长距离依赖的原因。LSTM则克服了梯度消失的缺点,对于捕获长时间的记忆信息效果非常显著。
梯度消失:上图的t-3时刻开始,梯度已经几乎减少到0。从这个时刻开始再往之前走,得到的梯度(几乎为零)就不会对最终的梯度值有任何贡献,这就相当于无论t-3时刻之前的网络状态h是什么,在训练中都不会对权重数组W的更新产生影响,也就是网络事实上已经忽略了t-3时刻之前的状态。
二. LSTM模型
1RNN与LSTM
1.1RNN
在标准的 RNN 中,这个重复的模块只有一个非常简单的结构
1.2LSTM
在下面的图例中,每一条黑线传输着一整个向量,从一个节点的输出到其他节点的输入。粉色的圈代表 pointwise 的操作,诸如向量的和,而黄色的矩阵就是学习到的神经网络层。合在一起的线表示向量的连接,分开的线表示内容被复制,然后分发到不同的位置。
1.3LSTM与RNN
长短时记忆网络的思路比较简单。原始RNN的隐藏层只有一个状态,即h,它对于短期的输入非常敏感。那么,假如我们再增加一个状态,即c,让它来保存长期的状态。新增加的状态c,称为单元状态(cell state)。
2存储长期信息
2.1引入simple RNN
为介绍如何存储长期信息,引入simple RNN。
2.2如何存储新的信息
把新信息放到长期状态C,有两种操作,乘法操作,加法操作。
乘法操作更多的是作为一种对信息进行某种控制的操作(比如任意数与0相乘后直接消失,相当于关闭操作;任意数与大于1的数相乘后会被放大规模等),而加法操作则是新信息叠加旧信息的操作。
2.3乘法操作
通过以上模型的构建,可以发现直接将历史记忆乘进长时记忆单元只会让情况更糟糕,这也说明了乘性更新并不是简单的信息叠加,而是控制。
2.4加法操作
直接将历史记忆乘进长时记忆单元只会让情况更糟糕,这也说明了乘性更新并不是简单的信息叠加,而是控制。加法操作则是新信息叠加旧信息的操作。
3LSTM结构
3.1门(gate)
门的使用,就是用门的输出向量按元素乘以我们需要控制的那个向量。因为门的输出是0到1之间的实数向量,那么,当门输出为0时,任何向量与之相乘都会得到0向量,这就相当于什么都不能通过;输出为1时,任何向量与之相乘都不会有任何改变,这就相当于什么都可以通过。而这里sigmoid函数的值域是(0,1) ,所以门的状态都是半开半闭的。
3.2遗忘门
通过将上一隐藏层的输出信息与当前的输入进行线性组合后,利用激活函数,将函数值压缩,得到一个大小在0和1之间的阈值,当函数值越接近1时,表示记忆体保留的信息越多。当函数值越接近0时,表示记忆体丢弃的信息就越多。
3.3输入门
输入门决定了当前时刻的输入信息,有多少信息将添加到记忆信息流中,与遗忘门计算公式几乎一致,输入门同样通过一个激活函数来实现。
3.4候选门
可以把候选门的输出理解为近期信息,有当前的输入xt,和上一时刻隐藏层的状态ht-1。
新信息是否被记忆是一个控制操作,应该使用乘法操作。因此在新信息前加一个控制阀门。
遗忘门是用来控制记忆消失程度的,因此也要用乘性运算。
然后在采用之前提到为把把新信息放到长期状态C中的加法操作。
3.5输出门
输出门,它控制着有多少记忆信息将被用于下一阶段的更新中。
3.6LSTM整体模型
输出门,它控制着有多少记忆信息将被用于下一阶段的更新中。
三. LSTM用于时间序列分析
1.1目的
用前几个月的飞机流量来预测当月的流量。
读入数据是2010年的飞机月流量
1.2模型
数据预处理,将数据中 na 的数据去掉,然后将数据标准化到 0 ~ 1 之间。
# 数据预处理
data_csv = data_csv.dropna()
dataset = data_csv.values
dataset = dataset.astype('float32')
max_value = np.max(dataset)
min_value = np.min(dataset)
scalar = max_value - min_value
dataset = list(map(lambda x: x / scalar, dataset))
进行数据集的创建,可以把前两个月流量数据作为输入,当月流量作为标签。为了进一步测试可以把数据划分为训练集、测试集。这里主要看下LSTM模型。
"""pytorch"""
# 定义模型
class lstm_reg(nn.Module):
def __init__(self, input_size, hidden_size, output_size=1, num_layers=2):
super(lstm_reg, self).__init__()
self.rnn = nn.LSTM(input_size, hidden_size, num_layers) # rnn
self.reg = nn.Linear(hidden_size, output_size) # 回归
def forward(self, x):
x, _ = self.rnn(x) # (seq, batch, hidden)
s, b, h = x.shape
x = x.view(s*b, h) # 转换成线性层的输入格式
x = self.reg(x)
x = x.view(s, b, -1)
return x
net = lstm_reg(2, 4)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(net.parameters(), lr=1e-2)
定义好网络结构,输入的维度是 2,因为使用两个月的流量作为输入,隐藏层的维度可以任意指定,这里指定为 4
预测结果如下
这里蓝色的是真实的数据集,红色的是预测的结果,可以看到,使用 lstm 能够得到比较相近的结果,预测的趋势也与真实的数据集是相同的.
代码及数据下载
参考
注1: 内容参考了博文LSTM
注2: 代码参考了书籍
后记:孔曰成仁,孟曰取义,惟其义尽,所以仁至。读圣贤书,所学何事,而今而后,庶几无愧。
欢迎关注个人公众号