最近在做一个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曲线完全重合,效果不错。
主要强调以下几点:
- tf.nn.rnn_cell与tf.contrib.rnn以及BasicLSTMCell与LSTMCell,api的变化,使不使用tf.unpack
[1][2] - 注意数据的输入输出格式,tensor形状的变化,跟踪rnn数据处理方式
- zeros_state的使用,数据的输入方式,采不采用第三方高层库
- 当前是做的什么任务(rnn的几种输入输出形式),采用endcoder-decoder架构的attention或者transformer,seq to seq或者CTC,端到端还是gmm+hmm,kws还是asr等等
- 使用了什么优化器,检查点文件,定义的操作流或者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的形状或者格式是什么样的。