RNN循环神经网络:CNN只能单独的取处理一个个的输入,前一个输入和后一个输入是完全没有关系的。但是,某些任务需要能够更好的处理序列的信息,即前面的输入和后面的输入是有关系的。
如果把上面有W的那个带箭头的圈去掉,它就变成了最普通的全连接神经网络。循环神经网络的隐藏层的值s不仅仅取决于当前这次的输入x,还取决于上一次隐藏层的值s。权重矩阵 W就是隐藏层上一次的值作为这一次的输入的权重。
这个网络在t时刻接收到输入
x
t
x_t
xt之后,隐藏层的值是
s
t
s_t
st,输出值是
o
t
o_t
ot 。关键一点是,
s
t
s_t
st的值不仅仅取决于
x
t
x_t
xt ,还取决于
s
t
−
1
s_{t-1}
st−1 。我们可以用下面的公式来表示循环神经网络的计算方法(t=n时):
O
n
=
g
(
V
S
n
)
S
n
=
f
(
W
S
n
−
1
+
U
X
t
)
O_n=g(VS_n) \\ S_n=f(WS_{n-1}+UX_{t})
On=g(VSn)Sn=f(WSn−1+UXt)
f
(
x
)
f(x)
f(x)和
g
(
x
)
g(x)
g(x)都表示激活函数。
权值参数W、U、V每个时刻都相等(权值共享)
航空公司流量预测
数据的格式:
先是读取数据,显示出原本的流量变化图
import csv
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
filename = 'international-airline-passengers.csv'
'''
所以为啥计算用的数据要去给他标准化?
'''
# 返回标准化数据(原值-平均值)/标准差,计算用
def load_series(filename, series_idx=1):
try:
with open(filename) as csvfile:
csvreader = csv.reader(csvfile)
data = [float(row[series_idx]) for row in csvreader if len(row) > 0] # 得到了第二列的数据,列表形式的
normalized_data = (data - np.mean(data)) / np.std(data)
return normalized_data
except IOError:
return None
# 返回原始数据,绘图用
def load_data(filename, series_idx=1):
try:
with open(filename) as csvfile:
csvreader = csv.reader(csvfile)
data = [float(row[series_idx]) for row in csvreader if len(row) > 0]
return data
except IOError:
return None
# 划分训练集和测试集
# data标准化之后的客流人数, rate训练集和测试集的占比
'''
enumerate(data)的返回形式是
[(0,112),(1, 118)……(索引,当日客流量)]
'''
def split_data(data, rate):
num_rows = len(data)
train_data, test_data = [], []
for idx, row in enumerate(data):
if idx < num_rows * rate:
train_data.append(row)
else:
test_data.append(row)
return train_data, test_data
def print_title_dataloader():
print('******************************************************')
print(' RNN在时间序列预测中的应用 ')
print(' 航空公司旅客数量预测实验 ')
print(' data_loader.py ')
print('******************************************************')
def draw_pic(data):
plt.figure(num='RNN在时间序列预测中的应用')
plt.title(u'航空公司时间序列-旅客数量图')
plt.xlabel(u'时间序列/天数')
plt.ylabel(u'旅客人数/人')
plt.plot(data)
plt.show()
# 原本数据里的值们
if __name__ == '__main__':
print_title_dataloader()
print('数据正在载入,请稍等......')
time_series = load_series(filename) # 标准化数据
print('数据载入成功')
data = load_data(filename)
draw_pic(data)
然后是设计的一连串代码,去预测,很多可以调节的地方
import tensorflow as tf
from tensorflow.contrib import rnn
import data_loader
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
class SeriesPredictor:
def __init__(self, input_dim, seq_size, hidden_dim):
# 设置超参数
self.input_dim = input_dim # 由于此次实验的输入是一维数据(旅客人数数据),张量维度(input_dim)只能为1
self.seq_size = seq_size
self.hidden_dim = hidden_dim # 隐藏层神经元个数
# 设置权重变量和占位符
self.W_out = tf.Variable(tf.random_normal([hidden_dim, 1]),
name='W_out')
self.b_out = tf.Variable(tf.random_normal([1]), name='b_out')
self.x = tf.placeholder(tf.float32, [None, seq_size, input_dim])
'''
Tensorflow的设计理念称之为计算流图,在编写程序时,首先构筑整个系统的graph,代码并不会直接生效,这一点和python的其他数值计算库
(如Numpy等)不同,graph为静态的,类似于docker中的镜像。然后,在实际的运行时,启动一个session,程序才会真正的运行。这样做的好处就是
:避免反复地切换底层程序实际运行的上下文,tensorflow帮你优化整个系统的代码。我们知道,很多python程序的底层为C语言或者其他语言,
执行一行脚本,就要切换一次,是有成本的,tensorflow通过计算流图的方式,帮你优化整个session需要执行的代码,还是很有优势的。
所以placeholder()函数是在神经网络构建graph的时候在模型中的占位,此时并没有把要输入的数据传入模型,它只会分配必要的内存。
等建立session,在会话中,运行模型的时候通过feed_dict()函数向占位符喂入数据。
'''
self.y = tf.placeholder(tf.float32, [None, seq_size])
# 定义均方误差损失、使用AdamOptimizer优化器
self.cost = tf.reduce_mean(tf.square(self.model() - self.y))
self.train_op = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(self.cost)
# 保存模型
self.saver = tf.train.Saver()
# 开始搭建模型
def model(self):
cell = rnn.BasicRNNCell(self.hidden_dim)
'''
cell它是TensorFlow中实现RNN的基本单元,每个cell都有一个call方法,
每调用一次cell的call方法,就相当于在时间上“推进了一步”,这就是cell的基本功能。
对于单个的cell,我们使用它的call函数进行运算时,只是在序列时间上前进了一步,
TensorFlow提供了一个tf.nn.dynamic_rnn函数,使用该函数就相当于调用了n次call函数。即通过{h0,x1, x2, …., xn}直接得{h1,h2…,hn}。
'''
outputs, states = tf.nn.dynamic_rnn(cell, self.x, dtype=tf.float32)
'''
为啥要在这里把权重这样搞?难道就是CNN的全连接和softmax之类的吗?最后几步都这套路?
'''
num_examples = tf.shape(self.x)[0]
# tile() 平铺之意,用于在同一维度上的复制(输入,同一维度上复制的次数)
W_repeated = tf.tile(tf.expand_dims(self.W_out, 0), [num_examples, 1, 1])
out = tf.matmul(outputs, W_repeated) + self.b_out
out = tf.squeeze(out) # 把张量维度压缩为1
return out
# 方案一 到达一定精度停止迭代
def train_1(self, train_x, train_y, test_x, test_y, precision):
with tf.Session() as sess:
tf.get_variable_scope().reuse_variables()
sess.run(tf.global_variables_initializer())
test_err = 1
step = 0
while precision <= test_err:
_, train_err = sess.run([self.train_op, self.cost],
feed_dict={
self.x: train_x,
self.y: train_y
})
if step % 100 == 0:
test_err = sess.run(self.cost,
feed_dict={
self.x: test_x,
self.y: test_y
})
print('step:{}\t\ttrain err:{}\t\ttest err:{}'.format(
step, train_err, test_err))
step += 1
save_path = self.saver.save(sess, './model/')
print('模型已保存到./model/目录下')
# 方案二 测试err浮动达到一定次数停止迭代
def train_2(self, train_x, train_y, test_x, test_y, max_patience):
with tf.Session() as sess:
tf.get_variable_scope().reuse_variables()
sess.run(tf.global_variables_initializer())
patience = max_patience
min_test_err = float('inf')
step = 0
while patience > 0:
_, train_err = sess.run([self.train_op, self.cost],
feed_dict={
self.x: train_x,
self.y: train_y
})
if step % 100 == 0:
test_err = sess.run(self.cost,
feed_dict={
self.x: test_x,
self.y: test_y
})
print('step:{}\t\ttrain err:{}\t\ttest err:{}'.format(
step, train_err, test_err))
if test_err < min_test_err:
min_test_err = test_err
patience = max_patience
else:
patience -= 1
step += 1
save_path = self.saver.save(sess, './model/')
print('模型已保存到./model/目录下')
# 方案三 迭代次数达到一定次数停止迭代
def train_3(self, train_x, train_y, test_x, test_y, turn):
with tf.Session() as sess:
tf.get_variable_scope().reuse_variables()
sess.run(tf.global_variables_initializer())
step = 0
while turn >= 0:
_, train_err = sess.run([self.train_op, self.cost],
feed_dict={
self.x: train_x,
self.y: train_y
})
if step % 100 == 0:
test_err = sess.run(self.cost,
feed_dict={
self.x: test_x,
self.y: test_y
})
print('step:{}\t\ttrain err:{}\t\ttest err:{}'.format(
step, train_err, test_err))
step += 1
turn -= 1
save_path = self.saver.save(sess, './model/')
print('模型已保存到./model/目录下')
def test(self, sess, test_x):
# 保存权重?
tf.get_variable_scope().reuse_variables()
self.saver.restore(sess, './model/')
output = sess.run(self.model(), feed_dict={self.x: test_x})
return output
def plot_results(train_x, predictions, actual, filename):
plt.figure()
plt.title(u'航空公司时间序列-旅客数量预测结果图')
plt.xlabel(u'时间序列/天数')
plt.ylabel(u'旅客人数/人')
num_train = len(train_x)
plt.plot(list(range(num_train)), train_x, color='b', label='训练集数据')
plt.plot(list(range(num_train, num_train + len(predictions))),
predictions,
color='r',
label='预测结果')
plt.plot(list(range(num_train, num_train + len(actual))),
actual,
color='g',
label='测试集数据')
plt.legend()
if filename is not None:
plt.savefig(filename)
else:
plt.show()
def print_title_rnnts():
print('*******************************************************')
print('* RNN在时间序列预测中的应用 *')
print('* 航空公司旅客数量预测实验 *')
print('* rnn_ts.py *')
print('*******************************************************')
if __name__ == '__main__':
print_title_rnnts()
input_dim = 1
print(
'参数说明:\n由于此次实验的输入是一维数据(旅客人数数据),张量维度(input_dim)只能为1\n序列长度建议输入:2-26\n隐藏层神经元个数建议输入:100-300\n学习率建议输入:0.001-0.01'
)
print('====================================================')
seq_size = int(input('请输入序列的长度(seq_size):')) # 喂入的次数
hidden_dim = int(input('请输入隐藏层神经元个数(hidden_dim):'))
learning_rate = float(input('请输入学习率(learning_rate):')) # 输入学习率
predictor = SeriesPredictor(input_dim, seq_size, hidden_dim)
data = data_loader.load_series('international-airline-passengers.csv') # 标准化数据划分,用于计算
rate = float(input('请输入划分的训练集占总数据的比例:'))
train_data, actual_vals = data_loader.split_data(data, rate)
train_x, train_y = [], [] # 训练数据和训练标签
for i in range(len(train_data) - seq_size - 1):
train_x.append(
np.expand_dims(train_data[i:i + seq_size], axis=1).tolist())
train_y.append(train_data[i + 1:i + seq_size + 1])
test_x, test_y = [], [] # 测试数据和测试标签
for i in range(len(actual_vals) - seq_size - 1):
test_x.append(
np.expand_dims(actual_vals[i:i + seq_size], axis=1).tolist())
test_y.append(actual_vals[i + 1:i + seq_size + 1])
choice = int(
input(
'\n1.达到一定精度停止迭代\n2.测试err浮动达到一定次数停止迭代\n3.迭代次数达到一定次数停止迭代\n请选择停止迭代的方式:'
))
if choice == 1:
precision = float(input('请输入停止迭代的精度:'))
predictor.train_1(train_x, train_y, test_x, test_y, precision)
elif choice == 2:
patience = int(input('请输入停止迭代的err浮动次数:'))
predictor.train_2(train_x, train_y, test_x, test_y, patience)
elif choice == 3:
turn = int(input('请输入停止迭代的次数:'))
predictor.train_3(train_x, train_y, test_x, test_y, turn)
else:
print('输入错误!')
with tf.Session() as sess:
prev_seq = train_x[-1]
predicted_vals = []
for i in range(20):
next_seq = predictor.test(sess, [prev_seq])
predicted_vals.append(next_seq[-1])
prev_seq = np.vstack((prev_seq[1:], next_seq[-1]))
plot_results(train_data, predicted_vals, actual_vals,
'基于训练集的连续预测结果.png')