本文简单介绍了LSTM网络原理,并展示了LSTM网络的TensorFlow实现,进行了海表温度及股价预测的实验。源代码见https://github.com/Su-Lemon/lstm-master
一 引言
LSTM 是一种RNN特殊的类型, 可以避免常规RNN的梯度消失问题,用来处理长序列的数据。LSTM 通过刻意的设计来避免长期依赖问题。
二 LSTM模型结构
2.1 整体结构
LSTM模型结构示意如下图所示。
每一条黑线传输着一整个向量,从一个节点的输出到其他节点的输入。方框中的圆圈代表运算操作( 如向量的和) ,而中间的方框就是学习到的神经网络层。合在一起的线表示向量的连接,分开的线表示内容被复制,然后分发到不同的位置。
这种结构的核心思想是引入了一个叫做细胞状态的连接,这个细胞状态用来存放想要记忆的东西。
- 忘记门:决定什么时候需要把以前的状态忘记;
- 输入门:决定什么时候加入新的状态;
- 输出门:决定什么时候需要把状态和输入放在一起输出。
从字面意思可以看出,简单RNN 只是把上一次的状态当成本次的输入一起输出。而LSTM 在状态的更新和状态是否参与输入都做了灵活的选择, 具体选什么,则一起交给神经网络的训练机制来训练。
2.2 忘记门
忘记门,是控制是否遗忘的,在LSTM中即以一定的概率控制是否遗忘上一层的隐藏细胞状态。
图中输入的有上一序列的隐藏状态 和本序列数据 ,通过一个激活函数,一般是sigmoid,得到忘记门的输出 。由于sigmoid的输出 在 之间,因此这里的输出 代表了忘记上一层隐藏细胞状态的概率。用数学表达式即为上图右侧所示。其中 , , 为线性关系的系数和偏置, 为sigmoid激活函数。
2.3 输入门
输入门,负责处理当前序列位置的输入。
从图中可以看到输入门由两部分组成,第一部分使用了sigmoid激活函数,输出为 第二部分使用了tanh激活函数,输出为 ,两者的结果后面会相乘再去更新细胞状态。用数学表达式即为上图右侧所示。
2.4 输出门
从图中可以看出,隐藏状态
的更新由两部分组成,第一部分是
,它由上一序列的隐藏状态
和本序列数据
,以及激活函数sigmoid得到,第二部分由隐藏状态
和tanh激活函数组成, 用数学表达式即为上图右侧所示。
三 TensorFlow搭建LSTM网络
下面展示了利用TensorFlow创建Encoder-Decoder 框架的LSTM网络的过程。完整的实验源码请前往https://github.com/Su-Lemon/lstm-master下载。
import tensorflow as tf
import numpy as np
import pandas as pd
from ..configs import config as cfg
pd.options.mode.chained_assignment = None # default='warn'
class Net(object):
def __init__(self):
self.encoder_input = []
self.expected_output = []
self.decode_input = []
self.losses = {}
self.tcells = []
self.Mcell = []
self.reshaped_outputs = []
def createNet(self, sess):
for i in range(cfg.FLAGS.seq_len):
self.encoder_input.append(
tf.placeholder(tf.float32, shape=(None, cfg.FLAGS.input_dim)))
self.expected_output.append(
tf.placeholder(tf.float32, shape=(None, cfg.FLAGS.output_dim)))
self.decode_input.append(
tf.placeholder(tf.float32, shape=(None, cfg.FLAGS.input_dim)))
# Create LSTM(GRU)
for i in range(cfg.FLAGS.layers_num):
self.tcells.append(tf.contrib.rnn.GRUCell(cfg.FLAGS.hidden_dim))
self.Mcell = tf.contrib.rnn.MultiRNNCell(self.tcells)
# Connected by Encoder-Decoder
dec_outputs, dec_memory = tf.contrib.legacy_seq2seq.basic_rnn_seq2seq(
self.encoder_input, self.decode_input, self.Mcell)
# Create output leyer
for ii in dec_outputs:
self.reshaped_outputs.append(
tf.contrib.layers.fully_connected(ii, cfg.FLAGS.output_dim,
activation_fn=None))
# L2 loss
output_loss = 0
for _y, _Y in zip(self.reshaped_outputs, self.expected_output):
output_loss += tf.reduce_mean(tf.pow(_y - _Y, 2))
self.losses['output_loss'] = output_loss
# generalization capacity
reg_loss = 0
for tf_var in tf.trainable_variables():
if not ("fully_connected" in tf_var.name):
# print(tf_var.name)
reg_loss += tf.reduce_mean(tf.nn.l2_loss(tf_var))
self.losses['reg_loss'] = reg_loss
loss = output_loss + cfg.FLAGS.lambda_l2_reg * reg_loss
self.losses['loss'] = loss
return self.losses
def trainer(self, sess, dataset, train_op, isTrain):
X, Y = dataset.generateData(isTrain=True)
feed_dict = {self.encoder_input[t]: X[t] for t in range(len(self.encoder_input))}
feed_dict.update({self.expected_output[t]: Y[t] for t in range(len(self.expected_output))})
c = np.concatenate(([np.zeros_like(Y[0])], Y[:-1]), axis=0)
feed_dict.update({self.decode_input[t]: c[t] for t in range(len(c))})
if isTrain:
_, loss_t = sess.run([train_op, self.losses['loss']], feed_dict)
return loss_t
else:
output_lossv, reg_lossv, loss_t = sess.run(
[self.losses['output_loss'], self.losses['reg_loss'],
self.losses['loss']],
feed_dict)
print("-----------------")
print(output_lossv, reg_lossv)
return loss_t
def demo(self, sess, dataset):
X, Y = dataset.generateData(isTrain=False)
feed_dict = {self.encoder_input[t]: X[t] for t in range(cfg.FLAGS.seq_len)}
c = np.concatenate(
([np.zeros_like(Y[0])], Y[0:cfg.FLAGS.seq_len - 1]), axis=0)
feed_dict.update(
{self.decode_input[t]: c[t] for t in range(len(c))})
outputs = np.array(sess.run([self.reshaped_outputs], feed_dict)[0])
return outputs
部分地区海表温度预测结果:
2018全球海表温度热力图:
转载:https://blog.csdn.net/BreakingDawn0/article/details/104462178