RNN初识

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} st1 。我们可以用下面的公式来表示循环神经网络的计算方法(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(WSn1+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')

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值