RNN简单应用----预测正弦函数

最近在做一个RNN的实验,之前其实学习过RNN的一些知识,但由于长时间不用,加上很多API的更新,有些东西也记得不太清了,真的很想吐槽TF这种静态图,看个shape都费劲,现在也不想升级到2.0或者使用PyTorch,只能将就着用吧。
这个正弦预测应该算是入门基本实验了,网上很多资料都是一些小修小改,但是却很多都是错的,而错的人却还一直转载,我也是服了。建议还是去看看官方书籍或者自己调试一下吧,下面我也会提到这一点。

先来个正确的吧,也是比较接近官方书籍的(tensorflow实战google深度学习底框架数据已经有第二版了,两版的实现在api上略有不同,但是思路是一致的):

参考链接:https://blog.csdn.net/hesongzefairy/article/details/105014004

利用RNN实现对函数sinx的取值预测,因为RNN模型预测的是离散时刻的取值,所以代码中需要将sin函数的曲线离散化,每隔一个时间段对sinx进行采样,采样得到的序列就是离散化的结果。

import numpy as np
import tensorflow as tf
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.BasicLSTMCell(HIDDEN_SIZE)
        for _ in range(NUM_LAYERS)])
 
    # 使用TensorFlow接口将多层的LSTM结构连接成RNN网络并计算其前向传播结果。
    outputs, _ = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
    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_EXAMPLES):
        _, 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("Root Mean Square Error is: %f" % rmse)
 
    # 对预测的sin函数曲线进行绘图。
    plt.figure()
    plt.plot(predictions, label='predictions')
    plt.plot(labels, label='real_sin')
    plt.legend()
    plt.show()
 
# 用正弦函数生成训练和测试数据集合。
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)          

训练输出:

预测结果:

从曲线可以看出,预测得到的曲线和真实的sinx曲线完全重合,效果不错。

主要强调以下几点:

  1. tf.nn.rnn_cell与tf.contrib.rnn以及BasicLSTMCell与LSTMCell,api的变化,使不使用tf.unpack
    [1][2]
  2. 注意数据的输入输出格式,tensor形状的变化,跟踪rnn数据处理方式
  3. zeros_state的使用,数据的输入方式,采不采用第三方高层库
  4. 当前是做的什么任务(rnn的几种输入输出形式),采用endcoder-decoder架构的attention或者transformer,seq to seq或者CTC,端到端还是gmm+hmm,kws还是asr等等
  5. 使用了什么优化器,检查点文件,定义的操作流或者api,使用的软件版本,操作系统与gpu,是否匹配。好好debug,查文献或者做实验,寻找规律,总结经验,然后打印输出,设置断点。

给一份我的的代码,你好好品。

"""
Created on Wed Dec  5 09:27:45 2018
tensorflow rnn 预测正弦函数
@author: lingtwave wang
"""
 
import numpy as np
import tensorflow as tf
import matplotlib as mpl
from matplotlib import pyplot as plt
from tensorflow.contrib.learn.python.learn.estimators.estimator import SKCompat
 
# TensorFlow的高层封装TFLearn
learn = tf.contrib.learn
 
# 神经网络参数
HIDDEN_SIZE = 30  # LSTM隐藏节点个数
NUM_LAYERS = 2    # LSTM层数
TIMESTEPS = 10    # 循环神经网络截断长度
BATCH_SIZE = 32   # batch大小
 
# 数据参数
TRAINING_STEPS = 3000      # 训练轮数
TRAINING_EXAMPLES = 10000  # 训练数据个数
TESTING_EXAMPLES = 1000    # 测试数据个数
SAMPLE_GAP = 0.01          # 采样间隔
 
 
def generate_data(seq):
    # 序列的第i项和后面的TIMESTEPS-1项合在一起作为输入,第i+TIMESTEPS项作为输出
    X = []
    y = []
    for i in range(len(seq) - TIMESTEPS - 1): #0-9989  #0-989
        X.append([seq[i:i + TIMESTEPS]])
        y.append([seq[i + TIMESTEPS]])
    return np.array(X, dtype=np.float32), np.array(y, dtype=np.float32)
    
 
# LSTM基本结构单元
def LstmCell():
    lstm_cell = tf.contrib.rnn.BasicLSTMCell(HIDDEN_SIZE)
    return lstm_cell
 
def lstm_model(X, y):
    # 使用多层LSTM,不能用lstm_cell*NUM_LAYERS的方法,会导致LSTM的tensor名字都一样
    cell = tf.contrib.rnn.MultiRNNCell([LstmCell() for i in range(NUM_LAYERS)])  
    # 将多层LSTM结构连接成RNN网络并计算前向传播结果
    #X = tf.placeholder(tf.float32, [10, 10, 30])
    #输入 cell,inputs,dtype
    #输出形如 [batch_size,max_time,cell.output.size]
    output, _ = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
    #形如[? *30] -1表示自适应
    output = tf.reshape(output[:,-1,:], [-1, HIDDEN_SIZE])
    # 通过无激活函数的全联接层计算线性回归,并将数据压缩成一维数组的结构
    # 输入,输出,激活函数
    predictions = tf.contrib.layers.fully_connected(output, 1, None) 
    # 将predictions和labels调整为统一的shape
    y = tf.reshape(y, [-1])
    predictions = tf.reshape(predictions, [-1])
    # 计算损失值
    loss = tf.losses.mean_squared_error(predictions, y)
    # 创建模型优化器并得到优化步骤
    train_op = tf.contrib.layers.optimize_loss(
        loss,
        tf.train.get_global_step(),
        optimizer='Adagrad',
        learning_rate=0.1
    )
    return predictions, loss, train_op
 
 
#用正弦函数生成训练和测试数据集合
#numpu.linspace函数可以创建一个等差序列的数组,它常用的参数有三个参数,第一个参数
#表示起始值,第二个参数表示终止值,第三个参数表示数列的长度。例如,linespace(1,10,10)
#产生的数组是array([1,2,3,4,5,6,7,8,9,10])
test_start = TRAINING_EXAMPLES * SAMPLE_GAP
test_end = (TRAINING_EXAMPLES + TESTING_EXAMPLES) * SAMPLE_GAP
train_X, train_y = generate_data(np.sin(np.linspace(0, test_start, TRAINING_EXAMPLES, dtype=np.float32)))
test_X, test_y = generate_data(np.sin(np.linspace(test_start, test_end, TESTING_EXAMPLES, dtype=np.float32)))
train_X = np.transpose(train_X, [0,2,1])
test_X = np.transpose(test_X, [0,2,1])

# 建立深层循环网络模型
regressor = SKCompat(learn.Estimator(model_fn=lstm_model, model_dir='model/'))
 
# 调用fit函数训练模型
regressor.fit(train_X, train_y, batch_size=BATCH_SIZE, steps=TRAINING_STEPS)
 
# 使用训练好的模型对测试集进行预测
predicted = [[pred] for pred in regressor.predict(test_X)]
# 计算rmse作为评价指标
rmse = np.sqrt(((predicted - test_y)**2).mean(axis=0))
print('Mean Square Error is: %f' % (rmse[0]))
 
# 对预测曲线绘图,并存储到sin.jpg
fig = plt.figure()
plot_test, = plt.plot(test_y, label='real_sin',c='blue')
plot_predicted, = plt.plot(predicted,label='predicted',c='red')
 
plt.legend([plot_predicted, plot_test], ['predicted', 'real_sin'])
plt.show()
fig.savefig('sin.png')

好的,下面再来看看错的实例,能跑不一定代表是对的:
其实它们主要的错误在于逻辑错误,没有注意shape的变化以及输入输出的格式。
[1]
[2]
[3]
[4]
[5]
[6]

其实真正把seq to seq用得好是下面这几个例子:
[1]
[2]
[3]
[4]
[5]
你可以看下他是怎么处理输入数据以及target与input的形状或者格式是什么样的。

更多详细解析可以参考:
[1]
[2]
[3]
[4]
[5]
[6]
[7]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值