zoukankan      html  css  js  c++  java
  • (数据科学学习手札40)tensorflow实现LSTM时间序列预测

    一、简介

      上一篇中我们较为详细地铺垫了关于RNN及其变种LSTM的一些基本知识,也提到了LSTM在时间序列预测上优越的性能,本篇就将对如何利用tensorflow,在实际时间序列预测任务中搭建模型来完成任务,若你对RNN及LSTM不甚了解,请移步上一篇数据科学学习手札39;

    二、数据说明及预处理

    2.1 数据说明

      我们本文使用到的第一个数据来自R中自带的数据集AirPassengers,这个数据集记录了Box & Jenkins航空公司1949-1960年共144个观测值(对应每个月的国际航线乘客数),是一个经典的时间序列数据集,你可以从R中导出或去uci的网站下载;

    2.2 数据预处理

      我们都知道,RNN最终经由tanh激活后输出的值位于[-1,1]内,若为分类任务则可以经由softmax进行处理,但我们这里要做的是对连续数值的预测,因此需要的输出即为tanh的输出,因此需要将原始数据进行尺度放缩,而尺度放缩的方法主要有两种,一种是极差规格化,即将原数据通过下面的公式无损地映射到[0,1]之间:

      另一种是标准化,将原数据通过下面的公式转换为均值为0,标准差为1的服从正态分布的随机变量:

    我们这里选择标准化(选极差规格化也可以,读者们可以自己尝试,我懒得写了。。。);

    三、模型建立及训练

    数据预处理部分:

      这一部分,我们完成原始数据的导入和预处理,为了配合之后的采样过程,这里选择列表作为预处理后原始数据的储存对象:

    import numpy as np
    import tensorflow as tf
    from tensorflow.contrib import rnn
    import matplotlib.pyplot as plt
    from tensorflow.contrib.learn.python.learn.estimators.estimator import SKCompat
    from matplotlib import style
    import pandas as pd
    
    '''读入原始数据并转为list'''
    path = 'C:\Users\windows\Desktop\'
    
    data = pd.read_csv(path+'AirPassenger.csv')
    
    data = data.iloc[:,0].tolist()
    
    '''自定义数据尺度缩放函数'''
    def data_processing(raw_data,scale=True):
        if scale == True:
            return (raw_data-np.mean(raw_data))/np.std(raw_data)#标准化
        else:
            return (raw_data-np.min(raw_data))/(np.max(raw_data)-np.min(raw_data))#极差规格化

    数据观察部分:

      这一部分,我们需要初步观察到原数据的一些基本特性,以便确定之后的一些参数,如LSTM单元内一个时间步内的递归次数:

    '''观察数据'''
    
    '''设置绘图风格'''
    style.use('ggplot')
    
    plt.plot(data)

    可以看出,我们的数据集具有很明显的周期性与上升趋势,下面就基于此,对LSTM的一些基本参数进行设置;

    LSTM基本参数设置:

      这里我们需要设置的参数有隐层层数,因为数据集比较简单,我们设置为1;隐层神经元个数,这里我随意设置为40个;时间步中递归次数,这里根据上面观察的结论,设置为12;训练轮数,这里也是随意设置的不宜过少,2000;训练批尺寸,这里随意设置为20,表示每一轮从训练集中抽出20组序列样本进行训练:

    '''设置隐层神经元个数'''
    HIDDEN_SIZE = 40
    '''设置隐层层数'''
    NUM_LAYERS = 1
    '''设置一个时间步中折叠的递归步数'''
    TIMESTEPS = 12
    '''设置训练轮数'''
    TRAINING_STEPS = 2000
    '''设置训练批尺寸'''
    BATCH_SIZE = 20

    生成训练集数据:

      这里为了将原始的单变量时序数据处理成LSTM可以接受的数据类型(有X输入,有真实标签Y),我们通过自编函数,将原数据(144个)从第一个开始,依次采样长度为12的连续序列作为一个时间步内部的输入序列X,并采样其之后一期的数据作为一个Y,具体过程如下:

    '''样本数据生成函数'''
    def generate_data(seq):
        X = []#初始化输入序列X
        Y= []#初始化输出序列Y
        '''生成连贯的时间序列类型样本集,每一个X内的一行对应指定步长的输入序列,Y内的每一行对应比X滞后一期的目标数值'''
        for i in range(len(seq) - TIMESTEPS - 1):
            X.append([seq[i:i + TIMESTEPS]])#从输入序列第一期出发,等步长连续不间断采样
            Y.append([seq[i + TIMESTEPS]])#对应每个X序列的滞后一期序列值
        return np.array(X, dtype=np.float32), np.array(Y, dtype=np.float32)

    构造LSTM模型主体

    '''定义LSTM cell组件,该组件将在训练过程中被不断更新参数'''
    def LstmCell():
        lstm_cell = rnn.BasicLSTMCell(HIDDEN_SIZE, state_is_tuple=True)#
        return lstm_cell
    
    '''定义LSTM模型'''
    def lstm_model(X, y):
        '''以前面定义的LSTM cell为基础定义多层堆叠的LSTM,我们这里只有1层'''
        cell = rnn.MultiRNNCell([LstmCell() for _ in range(NUM_LAYERS)])
    
        '''将已经堆叠起的LSTM单元转化成动态的可在训练过程中更新的LSTM单元'''
        output, _ = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
    
        '''根据预定义的每层神经元个数来生成隐层每个单元'''
        output = tf.reshape(output, [-1, HIDDEN_SIZE])
    
        '''通过无激活函数的全连接层计算线性回归,并将数据压缩成一维数组结构'''
        predictions = tf.contrib.layers.fully_connected(output, 1, None)
    
        '''统一预测值与真实值的形状'''
        labels = tf.reshape(y, [-1])
        predictions = tf.reshape(predictions, [-1])
    
        '''定义损失函数,这里为正常的均方误差'''
        loss = tf.losses.mean_squared_error(predictions, labels)
    
        '''定义优化器各参数'''
        train_op = tf.contrib.layers.optimize_loss(loss,
                                                   tf.contrib.framework.get_global_step(),
                                                   optimizer='Adagrad',
                                                   learning_rate=0.6)
        '''返回预测值、损失函数及优化器'''
        return predictions, loss, train_op
    
    '''载入tf中仿sklearn训练方式的模块'''
    learn = tf.contrib.learn
    
    '''初始化我们的LSTM模型,并保存到工作目录下以方便进行增量学习'''
    regressor = SKCompat(learn.Estimator(model_fn=lstm_model, model_dir='Models/model_2'))

    训练部分:

    '''对原数据进行尺度缩放'''
    data = data_processing(data)
    
    '''将所有样本来作为训练样本'''
    train_X, train_y = generate_data(data)
    
    '''将所有样本作为测试样本'''
    test_X, test_y = generate_data(data)
    
    '''以仿sklearn的形式训练模型,这里指定了训练批尺寸和训练轮数'''
    regressor.fit(train_X, train_y, batch_size=BATCH_SIZE, steps=TRAINING_STEPS)

    评价部分:

      这里我们将原数据(尺度缩放之后的)feed进我们已经训练好的模型中,得到对应的预测值:

    '''利用已训练好的LSTM模型,来生成对应测试集的所有预测值'''
    predicted = np.array([pred for pred in regressor.predict(test_X)])
    
    '''绘制反标准化之前的真实值与预测值对比图'''
    plt.plot(predicted, label='预测值')
    plt.plot(test_y, label='真实值')
    plt.title('反标准化之前')
    plt.legend()
    plt.show()

    可以看到,预测值与真实值非常的吻合,但这并不是我们需要的形式,我们需要的是反标准化后的真实数值,下面进行相关操作;

    '''自定义反标准化函数'''
    def scale_inv(raw_data,scale=True):
        '''读入原始数据并转为list'''
        path = 'C:\Users\windows\Desktop\'
    
        data = pd.read_csv(path + 'AirPassenger.csv')
    
        data = data.iloc[:, 0].tolist()
    
        if scale == True:
            return raw_data*np.std(data)+np.mean(data)
        else:
            return raw_data*(np.max(data)-np.min(data))+np.min(data)
    
    
    '''绘制反标准化之前的真实值与预测值对比图'''
    plt.figure()
    plt.plot(scale_inv(predicted), label='预测值')
    plt.plot(scale_inv(test_y), label='真实值')
    plt.title('反标准化之后')
    plt.legend()
    plt.show()

    实际使用中,若想利用已训练好的LSTM模型来预测未出现的下一期,则直接输入最后12步(这里是12步)即可得到未来的一步预测值,若想要获得更远更多期的预测值,则可以逐步将预测值积累起来,相当于用预测值当作真实发生的值进行预测,这样的坏处是越往后可能越不准,以上这个过程的完整代码如下:

    import numpy as np
    import tensorflow as tf
    from tensorflow.contrib import rnn
    import matplotlib.pyplot as plt
    from tensorflow.contrib.learn.python.learn.estimators.estimator import SKCompat
    from matplotlib import style
    import pandas as pd
    
    '''读入原始数据并转为list'''
    path = 'C:\Users\windows\Desktop\'
    
    data = pd.read_csv(path+'AirPassenger.csv')
    
    data = data.iloc[:,0].tolist()
    
    '''自定义数据尺度缩放函数'''
    def data_processing(raw_data,scale=True):
        if scale == True:
            return (raw_data-np.mean(raw_data))/np.std(raw_data)#标准化
        else:
            return (raw_data-np.min(raw_data))/(np.max(raw_data)-np.min(raw_data))#极差规格化
    
    '''观察数据'''
    
    '''设置绘图风格'''
    style.use('ggplot')
    
    plt.plot(data)
    
    '''设置隐层神经元个数'''
    HIDDEN_SIZE = 40
    '''设置隐层层数'''
    NUM_LAYERS = 1
    '''设置一个时间步中折叠的递归步数'''
    TIMESTEPS = 12
    '''设置训练轮数'''
    TRAINING_STEPS = 10000
    '''设置训练批尺寸'''
    BATCH_SIZE = 20
    
    
    
    
    '''样本数据生成函数'''
    def generate_data(seq):
        X = []#初始化输入序列X
        Y= []#初始化输出序列Y
        '''生成连贯的时间序列类型样本集,每一个X内的一行对应指定步长的输入序列,Y内的每一行对应比X滞后一期的目标数值'''
        for i in range(len(seq) - TIMESTEPS - 1):
            X.append([seq[i:i + TIMESTEPS]])#从输入序列第一期出发,等步长连续不间断采样
            Y.append([seq[i + TIMESTEPS]])#对应每个X序列的滞后一期序列值
        return np.array(X, dtype=np.float32), np.array(Y, dtype=np.float32)
    
    
    '''定义LSTM cell组件,该组件将在训练过程中被不断更新参数'''
    def LstmCell():
        lstm_cell = rnn.BasicLSTMCell(HIDDEN_SIZE, state_is_tuple=True)#
        return lstm_cell
    
    '''定义LSTM模型'''
    def lstm_model(X, y):
        '''以前面定义的LSTM cell为基础定义多层堆叠的LSTM,我们这里只有1层'''
        cell = rnn.MultiRNNCell([LstmCell() for _ in range(NUM_LAYERS)])
    
        '''将已经堆叠起的LSTM单元转化成动态的可在训练过程中更新的LSTM单元'''
        output, _ = tf.nn.dynamic_rnn(cell, X, dtype=tf.float32)
    
        '''根据预定义的每层神经元个数来生成隐层每个单元'''
        output = tf.reshape(output, [-1, HIDDEN_SIZE])
    
        '''通过无激活函数的全连接层计算线性回归,并将数据压缩成一维数组结构'''
        predictions = tf.contrib.layers.fully_connected(output, 1, None)
    
        '''统一预测值与真实值的形状'''
        labels = tf.reshape(y, [-1])
        predictions = tf.reshape(predictions, [-1])
    
        '''定义损失函数,这里为正常的均方误差'''
        loss = tf.losses.mean_squared_error(predictions, labels)
    
        '''定义优化器各参数'''
        train_op = tf.contrib.layers.optimize_loss(loss,
                                                   tf.contrib.framework.get_global_step(),
                                                   optimizer='Adagrad',
                                                   learning_rate=0.6)
        '''返回预测值、损失函数及优化器'''
        return predictions, loss, train_op
    
    '''载入tf中仿sklearn训练方式的模块'''
    learn = tf.contrib.learn
    
    '''初始化我们的LSTM模型,并保存到工作目录下以方便进行增量学习'''
    regressor = SKCompat(learn.Estimator(model_fn=lstm_model, model_dir='Models/model_2'))
    
    '''对原数据进行尺度缩放'''
    data = data_processing(data)
    
    '''将所有样本来作为训练样本'''
    train_X, train_y = generate_data(data)
    
    '''将所有样本作为测试样本'''
    test_X, test_y = generate_data(data)
    
    '''以仿sklearn的形式训练模型,这里指定了训练批尺寸和训练轮数'''
    regressor.fit(train_X, train_y, batch_size=BATCH_SIZE, steps=TRAINING_STEPS)
    
    '''利用已训练好的LSTM模型,来生成对应测试集的所有预测值'''
    predicted = np.array([pred for pred in regressor.predict(test_X)])
    
    '''绘制反标准化之前的真实值与预测值对比图'''
    plt.figure()
    plt.plot(predicted, label='预测值')
    plt.plot(test_y, label='真实值')
    plt.title('反标准化之前')
    plt.legend()
    plt.show()
    
    
    '''自定义反标准化函数'''
    def scale_inv(raw_data,scale=True):
        '''读入原始数据并转为list'''
        path = 'C:\Users\windows\Desktop\'
    
        data = pd.read_csv(path + 'AirPassenger.csv')
    
        data = data.iloc[:, 0].tolist()
    
        if scale == True:
            return raw_data*np.std(data)+np.mean(data)
        else:
            return raw_data*(np.max(data)-np.min(data))+np.min(data)
    
    
    '''绘制反标准化之前的真实值与预测值对比图'''
    plt.figure()
    plt.plot(scale_inv(predicted), label='预测值')
    plt.plot(scale_inv(test_y), label='真实值')
    plt.title('反标准化之后')
    plt.legend()
    plt.show()

      以上就是本篇文章的全部内容,如有笔误或混淆不清之处,望指出。

  • 相关阅读:
    OneSQL安装
    Dropbox可伸缩性设计最佳实践分享
    软件开发实践的24条军规
    最精彩的英语学习经验总结:俺的英语之路
    Facebook和Google如何激发工程师的创造力
    十种更好的表达“你的代码写的很烂”的方法
    一次java程序的重构
    漂亮代码
    一段代码引发的思考
    最难忘的Bug调试经历
  • 原文地址:https://www.cnblogs.com/feffery/p/9130728.html
Copyright © 2011-2022 走看看