TensorFlow学习之LSTM --- 预测sin函数

一、理论知识

LSTM是一种拥有三个“门”结构的特殊网络结构,这个结构让信息有选择性地影响循环神经网络中每个时刻的状态。“门”结构就是一个使用 sigmoid 神经网络和一个按位做乘法的操作,这两个操作合在一起就是一个“门”结构。
LSTM单元结构示意图
遗忘门:让循环神经网络“忘记”之前没有用的信息。取决于当前的输入 x t x_t xt,上一时刻的输出 h t − 1 h_{t-1} ht1和上一时刻的状态 c t − 1 c_{t-1} ct1
输入门:“忘记”了部分之前的状态后,需要从当前的输入补充最新的记忆。取决于当前的输入 x t x_t xt和上一时刻的输出 h t − 1 h_{t-1} ht1
输出门:LSTM结构在计算得到新的状态 c t c_t ct后需要产生当前时刻的输出。取决于当前的状态 c t c_t ct,上一时刻的输出 h t − 1 h_{t-1} ht1和当前的输入 x t x_t xt
具体 LSTM 每个“门”的公式定义如下:

输入值: z = t a n h ( W z [ h t − 1 , x t ] ) z = tanh(W_z[h_{t-1}, x_{t}]) z=tanh(Wz[ht1,xt])
输入门: i = s i g m o i d ( W i [ h t − 1 , x t ] ) i = sigmoid(W_i[h_{t-1}, x_{t}]) i=sigmoid(Wi[ht1,xt])
遗忘门: f = s i g m o i d ( W f [ h t − 1 , x t ] ) f = sigmoid(W_f[h_{t-1}, x_{t}]) f=sigmoid(Wf[ht1,xt])
输出门: o = s i g m o i d ( W o [ h t − 1 , x t ] ) o = sigmoid(W_o[h_{t-1}, x_{t}]) o=sigmoid(Wo[ht1,xt])
新状态: c t = f ⋅ c t − 1 + i ⋅ z c_t = f · c_{t-1} + i · z ct=fct1+iz
输出: h t = o ⋅ t a n h c t h_t = o · tanh c_t ht=otanhct
其中 W z W_z Wz W i W_i Wi W f W_f Wf W o W_o Wo是4个维度为 [ 2 n , n ] [2n, n] [2n,n] 的参数矩阵。

LSTM单元细节图

二、代码实现

  1. 定义一个LSTM结构。在TensorFLow中,LSTM结构可以很简单地实现,只需一个命令。
lstm = tf.nn.rnn_cell.LSTMCell(lstm_hidden_size)
  1. 将LSTM中的状态初始化为全0数组。LSTMCell类提供了 zero_state 函数来生成全零的初始状态。state是一个包含两个张量的 LSTMStateTuple 类,其中 state.c 和 state.h 分别是此时刻的新状态和此时刻的输出。
state = lstm.zero_state(batch_size, tf.float32)
  1. 深层循环神经网络。TensorFlow中提供了 MultiRNNCell 类来实现深层循环神经网络的前向传播过程,只需要在 LSTMCell 的基础上再封装一层 MultiRNNCell 就可以了非常容易地实现深层循环神经网络。
# 定义一个基本的 LSTM 结构作为循环体的基础结构。深层循环网络也支持使用其他的循环体结构。
lstm_cell = tf.nn.rnn_cell.LSTMCell
# 通过 MultiRNNCell 类实现深层循环神经网络中每一个时刻的前向传播过程。其中 number_of_layers表示有多少层
stacked_lstm = tf.nn.rnn_cell.MultiRNNCell(
	[lstm_cell(lstm_size) for _ in range(number_of_layers)])
# 和经典的循环神经网络一样,可以通过 zero_state 函数来获取初始状态
state = stacked_lstm.zero_state(batch_size, tf.float32)
  1. 实现dropout。在TensorFlow中,使用 tf.nn.rnn_cell.DropoutWrapper 类可以很容易实现 dropout 功能。通过两个参数来控制 dropout 的概率,一个参数为 input_keep_prob,它可以用来控制输入的 dropout 概率;另一个 output_keep_prob ,它可以用来控制输出的 dropout 概率。
# 定义LSTM结构
lstm_cell = tf.nn.rnn_cell.LSTMCell
stacked_lstm = tf.nn.rnn_cell.MultiRNNCell(
	[tf.nn.rnn_cell.DropoutWrapper(lstm_cell(lstm_size)) 
	for _ in range(number_of_layers)])
  1. tf.nn.dynamic_rnn 实现的功能就是可以让不同迭代传入的batch可以是长度不同数据,但同一次迭代一个batch内容的所有数据长度仍然是固定的(后边的文章再进行详细介绍)。
  2. 介绍了一些主要的网络实现之后,接下来给出一个具体的TensorFlow程序来实现这些循环神经网络的前向传播过程。以时序预测为例,利用循环神经网络实现对函数 sinx 取值的预测。
import numpy as np
import tensorflow as tf
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt

HIDDEN_SIZE = 30            # LSTM中隐藏节点的个数
NUM_LAYERS = 2              # LSTM的层数
TIMESTEPS = 10              # 循环神经网络的训练序列长度
TRAINING_STEPS = 10000      # 训练轮数
BATCH_SIZE = 32             # batch大小
TRAINING_EXAMPLES = 10000   # 训练数据个数
TESTING_EXAMPLES = 1000     # 测试数据个数
SAMPLE_GAP = 0.01           # 采样间隔


def generate_data(seq):
    X = []
    y = []
    # 序列的第i项和后面的TIMESTEPS-1项合在一起作为输入,在i+TIMESTEPS项作为输出
    # 即用sin函数前面的TIMESTEPS个点的信息,预测i+TIMESTEPS个点的函数值
    for i in range(len(seq) - TIMESTEPS):
        X.append([seq[i: i + TIMESTEPS]])
        y.append([seq[i + TIMESTEPS]])
    return np.array(X, dtype=np.float32), np.array(y, dtype=np.float32)


def lstm_model(X, y, is_training):
    # 使用多层的LSTM结构
    cell = tf.nn.rnn_cell.MultiRNNCell([tf.nn.rnn_cell.LSTMCell(HIDDEN_SIZE) for _ in range(NUM_LAYERS)])
    # 使用tensorflow接口将多层的LSTM结构连接成RNN网络并计算其前向传播结果
    outputs, _ = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
    # outputs是顶层LSTM在每一步的输出结果,它的维度是 [batch_size, time, HIDDEN_SIZE]
    output = outputs[:, -1, :]
    # 对LSTM网络的输出再做加一层全连接层并计算损失,注意这里默认的损失为平均平方差损失函数
    predictions = tf.contrib.layers.fully_connected(output, 1, activation_fn=None)
    # 只在训练时计算损失函数和优化步骤,测试时直接返回预测结果
    if not is_training:
        return predictions, None, None
    # 计算损失函数
    loss = tf.losses.mean_squared_error(labels=y, predictions=predictions)
    # 创建模型优化器并得到优化步骤
    train_op = tf.contrib.layers.optimize_loss(loss, tf.train.get_global_step(),
                                               optimizer='Adagrad', learning_rate=0.1)
    return predictions, loss, train_op


def train(sess, train_X, train_Y):
    # 将训练数据以数据集的方式提供给计算图
    ds = tf.data.Dataset.from_tensor_slices((train_X, train_Y))
    ds = ds.repeat().shuffle(1000).batch(BATCH_SIZE)
    X, y = ds.make_one_shot_iterator().get_next()

    # 调用模型,得到预测结果、损失函数和训练操作
    with tf.variable_scope('model'):
        predictions, loss, train_op = lstm_model(X, y, True)
    # 初始化变量
    sess.run(tf.global_variables_initializer())
    for i in range(TRAINING_STEPS):
        _, l = sess.run([train_op, loss])
        if i % 100 == 0:
            print('train step: ' + str(i) + ', loss: ' + str(l))


def run_eval(sess, test_X, test_y):
    # 将测试数据以数据集的方式提供给计算图
    ds = tf.data.Dataset.from_tensor_slices((test_X, test_y))
    ds = ds.batch(1)
    X, y = ds.make_one_shot_iterator().get_next()
    # 调用模型得到计算结果,这里不需要输入真实的y值
    with tf.variable_scope('model', reuse=True):
        prediction, _, _ = lstm_model(X, [0, 0], False)
        # 将预测结果存入一个数组
    predictions = []
    labels = []
    for i in range(TESTING_EXAMPLES):
        p, l = sess.run([prediction, y])
        predictions.append(p)
        labels.append(l)
    # 计算rmse作为评价指标
    predictions = np.array(predictions).squeeze()
    labels = np.array(labels).squeeze()
    rmse = np.sqrt(((predictions - labels) ** 2).mean(axis=0))
    print('Mean Square Error is: %f' % rmse)

    # 对预测的sin函数曲线进行绘图
    plt.figure()
    plt.plot(predictions, alpha=0.5, linewidth=3, label='predictions')
    plt.plot(labels, linewidth=1, label='real_sin')
    plt.legend()
    plt.show()
    plt.savefig('./sin.png')


test_start = (TRAINING_EXAMPLES + TIMESTEPS) * SAMPLE_GAP
test_end = test_start + (TESTING_EXAMPLES + TIMESTEPS) * SAMPLE_GAP
train_X, train_y = generate_data(np.sin(np.linspace(
    0, test_start, TRAINING_EXAMPLES + TIMESTEPS, dtype=np.float32)))
test_X, test_y = generate_data(np.sin(np.linspace(
    test_start, test_end, TESTING_EXAMPLES + TIMESTEPS, dtype=np.float32)))
with tf.Session() as sess:
    train(sess, train_X, train_y)
    run_eval(sess, test_X, test_y)

上边的代码涉及到了数据的处理(tf.data.Dataset),这个在后边的文章中进行讲解。
下图为最终的预测结果,可以看到:预测得到的结果和真实的sin函数几乎是重合的,也就是说通过循环神经网络可以非常好地预测sin函数的取值。
在这里插入图片描述
这段代码已经放到GitHub上,如需要可进行下载代码

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CharlesWu123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值