当前位置:   article > 正文

TensorFlow实战:Chapter-7下(TFTS库与时间序列预测)_tensorflow time series实战

tensorflow time series实战

本节简介

本节关于TFTS模块的使用参考知乎-何之源-如何优雅地用TensorFlow预测时间序列:TFTS库详细教程

如何在TensorFlow上使用LSTM来做时间序列预测是一个很老的话题,然而一直没有比较好的解决方案。在刚刚发布的TensorFlow1.3版本中,在tf.contrib包下引入了一个Time Series模块(TensorFlow Time Series,TFTS)。其源码地址在github timeseries。TFTS提供了一套基础的时间序列模型API。目前提供AR、Anomaly Mixture AR、LSTM三种预测模型。

这里因为是刚刚发布的库,文档匮乏,我们着重于介绍TFTS的以下几个功能:

  • 读入时间序列数据(分为从numpy数组和csv文件两种方式)
  • 用AR模型对时间序列进行预测
  • 用LSTM模型对时间序列进行预测(包含单变量和多变量)

先看效果图,

  1. 使用自定义LSTM循环网络对单变量进行时间序列预测(没使用TFTS,代码比较繁琐):

    这里写图片描述

  2. 使用TFTS下的AR模型预测效果如下,蓝色为训练数据,绿色为模型拟合数据,红色为预测数据:
    这里写图片描述

  3. 使用TFTS下的LSTM对单变量进行时间序列预测:
    这里写图片描述

  4. 使用TFTS下的LSTM对多变量进行时间序列预测:
    这里写图片描述

这里涉及到的代码保存在github-hzy46.网速不好的同学可以到或者在CSDN上下载



TensorFlow更新

注意

后面使用的LSTM模型的例子须使用TensorFlow最新的开发版的源码。具体来说,要保证下面这句话可以成功执行。

from tensorflow.contrib.timeseries.python.timeseries.estimators import TimeSeriesRegressor
  • 1

如果执行不成功,则需要下面的更新操作。

下载最新源码(编译源码更新)

1. 首先卸载当前的tensorflow
pip uninstall tensorflow  #gpu版 就是tensorflow-gpu
  • 1
2. 参考官方的从源码安装tensorflow

参考TensorFlow官方安装教程。我的开发环境是Ubuntu16.04+1080显卡,需要安装gpu版本。

  • 保证显卡驱动,CUDA8.0,cudnn6.0安装成功
  • 安装bazel bazel安装教程
  • 安装Python依赖包

        sudo apt-get install python-numpy python-dev python-pip python-wheel  # 对py2
        sudo apt-get install python3-numpy python3-dev python3-pip python3-wheel #对py3
    • 1
    • 2
  • 从github上下载最新的源码

    git clone https://github.com/tensorflow/tensorflow 
    • 1
  • 配置tensorflow安装

    
        cd tensorflow  # cd to the top-level directory created
        $ ./configure
        '''
        除了CUDA项选择y,其他都是n或者默认项.
        Configuration finished
        '''
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 编译成pip包

    '''
    编译
    '''
    bazel build --config=opt --config=cuda //tensorflow/tools/pip_package:build_pip_package

    '''
    挺长的一段编译时间
    生产pip包
    '''
    bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
    '''
    安装
    '''
    sudo pip install /tmp/tensorflow_pkg/tensorflow-1.3.0-py2-none-any.whl
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

如果出啥问题,建议直接看官方教程来整,有啥小问题,上stackoverflow上找找原因。(从源码编译tensorflow前前后后花费了我2天的时间,现在整出来了,踩了不少的坑,感觉下次会快很多了~)

现在9月初tensorflow的pip安装版本还不支持这个TimeSeriesRegressor类,等到后面版本稳定更新了,应该可以用下面pip工具更新。

直接使用pip工具更新

因为本次用到的库需要运行在TensorFlow1.3版本,而我的环境是Ubuntu下的1.0.1版本的TensorFlow。如果你不知道自己的TensorFlow是啥版本,有一个简单的方法:

激活python编程环境,键入以下代码运行即可。

    import tensorflow as tf
    print(tf.__version__)   # 查看tensorflow版本
    print(tf.__path__)      # 查看tensorflow安装位置

    '''
    输出:
    1.0.1
    ['/root/anaconda2/lib/python2.7/site-packages/tensorflow']
    '''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

更新方法也很简单,如果你的TensorFlow是普通的安装,直接在命令行键入以下命令:

$: sudo pip install --upgrade tensorflow-gpu  # 我安装的是gpu版
  • 1

等待更新完成即可。

    import tensorflow as tf
    print(tf.__version__)   # 查看tensorflow版本
    print(tf.__path__)      # 查看tensorflow安装位置

    '''
    输出:
    1.3.0
    ['/root/anaconda2/lib/python2.7/site-packages/tensorflow']
    '''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
更新后可能会遇到的异常
  • 问题描述:

    libcudnn.so.6:cannot open sharedobject file: No such file or directory
    • 1
  • 问题分析:
    我在开始安装1.0.1版本的tensorflow时,配置的是CUDA8.0+cudnn-v5.1。这里我把tensorflow升级到1.3版本,新的Tensorflow适配的是cudnn-v6.0。故需要升级cudnn。

  • 解决办法:
    升级cudnn的方法参考安装CUDNN

    1. 下载适配CUDA版本和系统版本的cudnn-v6.0

    1. 解压下载好的cudnn

      $ : sudo tar -zxvf cudnn-8.0-linux-x64-v6.0.tgz

    2. 将解压文件拷贝并修改权限

      $ :sudo cp cuda/include/cudnn.h /usr/local/cuda/include/    # copy file
      $ :sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lin64/
      $ :sudo chmod a+r /usr/local/cuda/include/cudnn.h       # 修改权限
      $ :sudo chmod a+r /usr/local/cuda/libcudnn*
      • 1
      • 2
      • 3
      • 4

到这里,算是大功告成了~



自定义LSTM循环神经网络进行时间序列预测

在使用TFTS库前,我们先利用自定义循环神经网络预测正弦函数。初步学习一下如何使用LSTM循环神经网络进行时间序列预测,这里我们会使用TensorFlow的一个高级封装工具-TFLearn(集成在tf.contrib.learn).

工程实现

1. 需要用到的模块

  # coding:utf8
  import numpy as np
  import tensorflow as tf
  from tensorflow.contrib.learn.python.learn.estimators.estimator import SKCompat
  import matplotlib.pyplot as plt

  learn = tf.contrib.learn  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2. 生成训练数据与测试数据

因为是要预测正弦函数,这里我们使用np模块下的np.sin函数生成训练数据和测试数据。

  TRAINING_EXAMPLES = 10000 # 训练数据大小
  TESTING_EXAMPLES = 1000 # 测试数据大小
  SAMPLE_GAP = 0.01   #采样间隔

  TIMESTEPS = 10  # 循环神经网络截断长度


  def generate_data(seq):
      '''
          定义生成正弦函数数据函数
      :param seq:
      :return: X为训练数据序列,y为预测数据
      '''
      X = []
      y = []

      for i in range(len(seq) - TIMESTEPS - 1):
          X.append([seq[i: i + TIMESTEPS]])  # 截取以i下标开始的以TIMESTEPS为batch的数据
          y.append([seq[i + TIMESTEPS]])      # 预测i+TIMESTEPS的数据
      return np.array(X, dtype=np.float32), np.array(y, dtype=np.float32)

  # 生成数据
  # TRAINING_EXAMPLES训练数据个数  SAMPLE_GAP采样间隔
  test_start = TRAINING_EXAMPLES * SAMPLE_GAP
  # 训练数据和测试数据个数
  test_end = (TRAINING_EXAMPLES + TESTING_EXAMPLES) * SAMPLE_GAP


  # np.linspace生成等差数列 即采样横轴数据
  # 从0到test_start,生成TRAINING_EXAMPLES个数据(即采样间隔为SAMPLE_GAP)
  train_X, train_y = generate_data(np.sin(np.linspace(
      0, test_start, TRAINING_EXAMPLES, dtype=np.float32)))

  # np.linspace生成等差数列
  #
  test_X, test_y = generate_data(np.sin(np.linspace(
      test_start, test_end, TESTING_EXAMPLES, dtype=np.float32)))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

注解:

训练数据TRAINING_EXAMPLES加上测试数据TESTING_EXAMPLES一共需要11000组。

这里我们设置是采样间隔SAMPLE_GAP是0.01。故我们整个采样距离是11000*0.01=110.也就是在sin函数上,x轴为[0,110]这段距离上均分为11000份。

训练数据是以网络的截断长度为分割间距。这里循环神经网络的截断长度TIMESTEPS为10。故我们的数据也是10个采样点和对应的sin值为一组,预测第11个点。(训练时候就是回归第11个点的值).

  • 先使用np.linspace函数取出等差间隔的采样点
  • 再使用np.sin函数获得对应的sin值。
  • 在生成数据generate_data时,以TIMESTEPS截断数据到X内,将TIMESTEPS+1个数据放到对应的标签y内。

下面是np.linspace和np.sin的用法示例:

这里写图片描述


3. 定义网络模型

我们使用BasicLSTMCell和MultiRNNCell构成一个hidden_size为30的2层的LSTM循环神经网络。需要注意的是不同版本下在创建LSTMCells的方法是不一样的。

  HIDDEN_SIZE = 30 # 隐藏单元个数
  NUM_LAYERS = 2  #LSTM层数

  TRAINING_STEPS = 3000 # 训练数据轮数
  BATCH_SIZE = 32 # batch大小

  def lstm_model(X, y):
      '''
          定义LSTM模型
      :param X: 训练数据
      :param y: 预测标签
      :return:
      '''
      # 1.2版本后,tensorflow对使用BasicLSTMCell等 RNNCells生成cells有不同的处理方法,这里多层的RNN建议采用这种创建cell方法
      stacked_rnn = []
      for iiLyr in range(NUM_LAYERS):
          stacked_rnn.append(tf.nn.rnn_cell.BasicLSTMCell(HIDDEN_SIZE, state_is_tuple=True))
      cell = tf.nn.rnn_cell.MultiRNNCell(cells=stacked_rnn, state_is_tuple=True)
      #lstm_cell = tf.contrib.rnn.BasicLSTMCell(HIDDEN_SIZE, state_is_tuple=True)     #1.2版本前
      #cell = tf.contrib.rnn.MultiRNNCell([lstm_cell] * NUM_LAYERS)

      # 将多层LSTM结构连接成RNN网络并计算其前向传播结果
      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.1)

      return predictions, loss, train_op
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

4. 创建模型并训练

这里我们使用了tf.contrib.learn下的一个封装模型工具Estimator。使用Estimator封装好一个预测模型后((已训练),我们对测试数据进行了预测,再计算了下均方误差,大体上的评估了一下模型的预测性能。

    # 封装之前定义的lstm
    # 如果你的tensorflow1.2版本前已经训练好了这样的的一个模型,在tensorflow更新后,重新生成模型。
    # 因为在新版本的Tensorflow里,LSTM单元的文件改变了,这里我们简单的把以前的model_dir修改了,保证创建了新的模型
    regressor = SKCompat(learn.Estimator(model_fn=lstm_model, model_dir="Models/model_3")) 
    # 拟合数据
    regressor.fit(train_X, train_y, batch_size=BATCH_SIZE, steps=TRAINING_STEPS)
    # 计算预测值
    predicted = [[pred] for pred in regressor.predict(test_X)]
    # 计算MSE
    rmse = np.sqrt(((predicted - test_y) ** 2).mean(axis=0))
    print ("Mean Square Error is: %f" % rmse[0])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Estimator工具

这里我们简单的介绍以下Estimator工具。
参考TensorFlow 0.12 Estimators Models Layers学习笔记。Estimators的作用是:

  • tf.estimator framework用于快速构建和训练机器学习模型,同时Estimator提供了一些常见的模型(常见的回归和分类模型,例如:线性分类,线性回归等)
  • tf.estimator为monitors,checkpointing提供了初始化配置,同时提供了构建和评估自定义模型的大部分逻辑。依照着tutorial可以很方便的创建一个estimator.TensorFlow关于Estimator介绍

总的来说,我们可以认为tf.estimator工具是用来提供一个自定义模型的框架,我们照着定义好的格式配置好输入即可。

  1. 创建一个Estimator

    • 先看构造器定义:

       init(model_fn=None, model_dir=None, config=None, params=None, feature_engineering_fn=None)
       '''
       Args:
              model_fn: 模型定义,定义了train, eval, predict的实现      
              model_dir: log文件和训练参数的保存目录
              config: Configuration object
              params: dict of hyper parameters that will be passed into model_fn. Keys are names of parameters, values are basic python types.
              feature_engineering_fn: Feature engineering function. Takes features and labels which are the output of input_fn and returns features and labels which will be fed into model_fn. Please check model_fn for a definition of features and labels.
       '''
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • tf.estimator常接收2个参数:model_fn和model_dir.注意到我们使用的.

      learn.Estimator(model_fn=lstm_model, model_dir="Models/model_3") # model_fn和model_dir参数
      • 1
    • 这里需要注意model_fn:

         model_fn(features, labels, mode, params)
         '''
         features: Tensor or dict of Tensor's. 即样本数据x.
         labels: Tensor or dict of Tensor's. 样本数据y.(支持无标签训练,调整对应的mode即可)
         mode: 指定model_fn功能.
         params: params is a dict of hyperparameters
         '''
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  2. 训练模型

    将我们的训练数据塞给fit,训练完成后,就会按照前面指定的model_dir存放训练好的模型.

        fit(x=None, y=None, input_fn=None, steps=None, batch_size=None, monitors=None, max_steps=None)
        '''
        x: 训练数据x. 格式为[n_samples,n_features...],如果设置此参数,input_fn需为None.
        y: 训练数据y。x对应的标签。
        steps: 每次训练ops.
        batch_size: minibatch size.
        moitors: Used for callbacks inside the training loop.
        max_steps: Number of total steps for which to train model.
        input_fn:  说白了就是把x,y,batch_size包装一下。 用这个就不用设置x,y,batch_size了.
        '''
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  3. 预测

    将需要预测的数据塞给predict,返回的就是预测值.

        predict(x=None, input_fn=None, batch_size=None, outputs=None, as_iterable=True)
        '''
        x : 为需要预测的数据。
    
        batch_size: minibatch size.
        input_fn: 对x和batch_size的包装
        outputs:  list of str, name of the output to predict. If None, returns all
        as_iterable: If True, return an iterable which keeps yielding predictions for each example until inputs are exhausted. Note: The inputs must terminate if you want the iterable to terminate (e.g. be sure to pass num_epochs=1 if you are using something like read_batch_features).
        '''
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  4. 注意

    前面我说了如果在输入的时候设置x,y,batch_size就不要设置input_fn,需要注意的是:,如果使用x,y而不是input_fn来传参数的形式,需要用Estimator里一个叫SKCompat的类包装一下.

        estimator_instance = SKCompat(Estimator(model_fn=..., model_dir=...))
    
    • 1

    到这里,我们对TensorFlow内的Estimator工具介绍就算结束了~


5. 绘图

使用plt将预测数据和测试数据绘制出来,有一个直观上的认识。

    fig = plt.figure()
    plot_predicted, = plt.plot(predicted, label='predicted')
    plot_test, = plt.plot(test_y[0:399], label='real_sin')
    plt.legend([plot_predicted, plot_test],['predicted', 'real_sin'])
    plt.show()
    fig.savefig('pre_sin.png')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里写图片描述



利用TFTS进行时间序列预测

到这里算是切入主题了,下面介绍如何使用TFTS模块进行时间序列预测。

载入数据部分

对于时间序列预测问题,我们可以把数据抽象成:{观察点:观察值}。例如某年一月的价格为120元,二月的价格为130元,三月的价格为135元,四月的价格为132元。那么观察的时间点可以看做是1,2,3,4,而在各时间点上观察到的数据的值为120,130,135,132。

TFTS库提供了两个数据读取器NumpyReader和CSVReader.

NumpyReader用于从Numpy数组中读入数据,下面举一个demo:

    import numpy as np
    import matplotlib.pyplot as plt

    x = np.array(range(1000))
    noise = np.random.uniform(-0.2, 0.2, 1000)  # 随机生成-0.2~0.2之间的数据
    y = np.sin(np.pi * x * 0.01) + x * 0.005 + noise  # y=sin(0.01*pi*x) + 0.005*x + noise
    p = plt.plot(x, y)
    plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里写图片描述

横轴即’采样点x’,纵轴为’采样值y’.

TFTS提供的读入x和y的接口非常简单,使用demo如下:

  data = {
      tf.contrib.timeseries.TrainEvalFeatures.TIMES: x,
      tf.contrib.timeseries.TrainEvalFeatures.VALUES: y,
  }
  reader = NumpyReader(data)
  • 1
  • 2
  • 3
  • 4
  • 5

我们首先把x和y转为Python中的dict.我们来分析以下上面data的写法。tf.contrib.timeseries.TrainEvalFeatures.TIMES就是一个字符串’times’,而对应tf.contrib.timeseries.TrainEvalFeatures.VALUES也是一个字符串’values’.上面的data定义也可以写成:

  data = {
      'times' : x,
      'values': y,
  }
  • 1
  • 2
  • 3
  • 4

至于为什么写成上面的那个形式,也是为了配合规范化。

NumpyReader返回的对象有一个read_full()方法,该方法用于从Reader中读取所有的数据,但需要注意的是:read_full()会产生读取队列(这样的处理训练数据的方法和TensorFlow开源的AlexNet网络上对输入数据做增强操作使用的方法类似),这要求我们在使用该方法前,需要先调用tf.train.start_queue_runners启动队列,然后才能读取数据。使用的demo如下:

 with tf.Session() as sess:
      full_data = reader.read_full()
      coord = tf.train.Coordinator()  # 创建一个线程协调器
      threads = tf.train.start_queue_runners(sess=sess, coord=coord) # 启动线程队列
      print('times shape:', full_data['times'])
      print('values shape:', full_data['values'])
      print(sess.run(full_data)['times'][0:10]) 
      print(sess.run(full_data)['values'][0:10])  
      coord.request_stop()

      '''
      输出:

      times shape: Tensor("Squeeze_1:0", shape=(1000,), dtype=int64)
      values shape: Tensor("Squeeze:0", shape=(1000, 1), dtype=float64)

      [0 1 2 3 4 5 6 7 8 9]

      [[-0.09581681]
       [ 0.01284531]
       [ 0.1107236 ]
       [ 0.08856841]
       [ 0.19104294]
       [ 0.32795446]
       [ 0.17780316]
       [ 0.35017529]
       [ 0.10477021]
       [ 0.16101822]]
      '''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

通常在训练模型时,我们采需要的是minibatch形式的训练数据,TFTS库提供了tf.contrib.timeseries.RandomWindowInputFn方法用于在reader中随机选取window_size大小的数据组成一组序列数据。demo如下:

train_input_fn = tf.contrib.timeseries.RandomWindowInputFn(
  reader, batch_size=2, window_size=10)

  with tf.Session() as sess:
      batch_data = train_input_fn.create_batch()
      coord = tf.train.Coordinator()
      threads = tf.train.start_queue_runners(sess=sess, coord=coord)
      one_batch = sess.run(batch_data[0])
      coord.request_stop()

  print('one_batch_data:', one_batch)
  '''
      即一个batch为2组序列数据,每组序列数据有10条数据。  

  输出:
      one_batch_data: {
      'values': array([[[ 1.21827106],
                  [ 1.37975747],
                  [ 1.15419451],
                  [ 1.07579377],
                  [ 1.19008057],
                  [ 1.32173953],
                  [ 1.2152622 ],
                  [ 1.31092923],
                  [ 1.26184174],
                  [ 1.25915473]],

                 [[ 0.08465949],
                  [-0.0859257 ],
                  [-0.02987006],
                  [ 0.17472125],
                  [ 0.23542243],
                  [ 0.2032668 ],
                  [ 0.07650485],
                  [ 0.20822309],
                  [ 0.30753332],
                  [ 0.16054565]]]), 
      'times': array([[61, 62, 63, 64, 65, 66, 67, 68, 69, 70],
                      [ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10]])}

  '''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

TFTS从Numpy中读取数据的流程大概操作就是这样了~


从CSV文件中读入时间序列数据

TFTS还提供了CSVReader用于读取CSV文件。
项目中提供了一个input_input_csv.py文件用于处理csv文件,这里处理的文件是’./data/period_trend.csv’.

这里CSV的文件形式如下(截取):

   1,-0.6656603714
   2,-0.1164380359
   3,0.7398626488
   4,0.7368633029
   5,0.2289480898
   6,2.257073255
   7,3.023457405
   8,2.481161007
   9,3.773638612
   10,5.059257738
   11,3.553186083
   12,4.554486452
   13,3.655475698
   14,3.419647598
   15,4.303376245
   16,4.830153934
   17,7.253057441
   18,5.064802335
   19,5.448082106
   20,6.251301517
   ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

CSV的第一列数据为时间点,第二列数据为对应的观察值。
CSVReader的操作步骤除了读取文件的部分不同,后面的操作和前面的NumpyReader是一样的。操作的demo如下:

 from __future__ import print_function
 import tensorflow as tf

 csv_file_name = './data/period_trend.csv'
 reader = tf.contrib.timeseries.CSVReader(csv_file_name)

 with tf.Session() as sess:
     data = reader.read_full()
     coord = tf.train.Coordinator()
     threads = tf.train.start_queue_runners(sess=sess, coord=coord)
     print(sess.run(data))
     coord.request_stop()

 train_input_fn = tf.contrib.timeseries.RandomWindowInputFn(reader, batch_size=4, window_size=16)
 with tf.Session() as sess:
     data = train_input_fn.create_batch()
     coord = tf.train.Coordinator()
     threads = tf.train.start_queue_runners(sess=sess, coord=coord)
     batch1 = sess.run(data[0])
     batch2 = sess.run(data[0])
     coord.request_stop()

 print('batch1:', batch1)
 print('batch2:', batch2)

 '''
 输出:
 {'values': array([[ -0.66566038],
    [ -0.11643804],
    [  0.73986262],
    [  0.73686332],
    [  0.22894809],
    [  2.25707316],
    [  3.02345729],
    ...
    dtype=float32), 'times': array([  1,   2,   3,   4,   5,   6,  ...,500])}

 batch1: {'values': array([[[  9.75562382],
     [  9.1494894 ],
     [  8.94796562],
     [  9.1767683 ],dtype=float32), 'times': array([[ 98,  99,...,129]])}

 batch2: {'values': array([[[  4.97288084],
         [  5.21278238],...,dtype=float32), 'times': array([[ 82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,
          95,  96,  97],...,226]])}

 '''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

到这里为止,载入数据部分到这里就算结束了。


使用AR模型预测时间序列

AR模型是一种线性预测,即已知N个数据,可由模型推出第N点前面或后面的数据(设推出P点),所以其本质类似于插值,其目的都是为了增加有效数据,只是AR模型是由N点递推,而插值是由两点(或少数几点)去推导多点,所以AR模型要比插值方法效果更好。

代码实现

产生数据

产生数据的方法就是上面介绍的方法。

    x = np.array(range(1000))
    noise = np.random.uniform(-0.2, 0.2, 1000)
    y = np.sin(np.pi * x / 100) + x / 200. + noise
    plt.plot(x, y)
    plt.savefig('timeseries_y.jpg')

    data = {
        tf.contrib.timeseries.TrainEvalFeatures.TIMES: x,
        tf.contrib.timeseries.TrainEvalFeatures.VALUES: y,
    }

    reader = NumpyReader(data)

    train_input_fn = tf.contrib.timeseries.RandomWindowInputFn(
        reader, batch_size=16, window_size=40)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
创建ar模型

创建ar模型,我们需要使用tf.contrib.timeseries.ARRegressor类


先看下ARRegressor类

    class ARRegressor(_TimeSeriesRegressor):
    """
    ARRegressor是基于滑窗模型的。这要求输入窗口大小要固定为'input_window_size',输出窗口大小固定为'output_window_size'. 同时这两个参数的和必须等于window_size(满足训练或评估时使用的input_fn)。建议使用'RandomWindowInputFn'(就是上面讲的随机产生batch数据的函数)产生训练或者评估。

      """
      def __init__(self, 
            periodicities, input_window_size,
            output_window_size, num_features, num_time_buckets=10, 
            loss=ar_model.ARModel.NORMAL_LIKELIHOOD_LOSS, 
            hidden_layer_sizes=None, anomaly_prior_probability=None, 
            anomaly_distribution=None, optimizer=None, 
            model_dir=None, config=None):
        """
        参数:
            periodicities:  value or a list of values.  输入信号的周期。
            input_window_size: 回归时给定的输入数据时间步数.
            output_window_size: 预测时间步数,建议设置>1.
            num_features: 时间序列的维度.(数据的观察值)
            loss: SQUARED_LOSS 或者 NORMAL_LIKELIHOOD_LOSS.
            hidden_layer_sizes: 默认即可.
            anomaly_distribution;anomaly_distribution: 默认即可,指定即构成混合模型
            optimizer: defaults to Adagrad with step size 0.1.
            model_dir:模型存储地址.(上面Estimator有讲)
            config: See `Estimator`.


    """
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

总的来说,需要填的参数有periodicities,input_window_size,output_window_size,num_features,loss。其它的参数默认即可。


这里需要注意的有input_window_size + output_window_size = window_size.(RandomWindowInputFn生成数据里面的window_size).我们在上面的生成数据使用的window_size=40.下面使用的是input_window_size=30, output_window_size=10.就是输入序列30个,预测10个。
num_features即时间序列的维度。就是在一个时间点上观察到的数据维度。我们这里每一步都是一个单独的值,所以num_features=1。
periodicities是信号的周期分量的周期,我们信号的表达式为:

y=sin(0.01πx)+0.005x+noise

y的周期分量的frequency为 2π(1/f),故 f=200,periodicities=200
loss的取值现在支持NORMAL_LIKELIHOOD_LOSS和SQUARED_LOSS。

   ar = tf.contrib.timeseries.ARRegressor(
        periodicities=200, input_window_size=30, output_window_size=10,
        num_features=1,
        loss=tf.contrib.timeseries.ARModel.NORMAL_LIKELIHOOD_LOSS)
        '''
        (input_window_size=30) + (output_window_size=10) = (window_size=40)
        '''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
训练评估模型并预测

使用train函数传入创建好的数据train_input_fn训练模型即可.

ar.train(input_fn=train_input_fn, steps=6000)
  • 1

接下来就是对模型进行评估,首先我们使用AR模型提供的evaluate函数,这里evaluation的处理是使用训练好的模型在原先的训练集上进行计算,由此我们可以观察到模型的拟合效果.

    evaluation_input_fn = tf.contrib.timeseries.WholeDatasetInputFn(reader)
    evaluation = ar.evaluate(input_fn=evaluation_input_fn, steps=1)
    # keys of evaluation: ['covariance', 'loss', 'mean', 'observed', 'start_tuple', 'times', 'global_step']
    # evaluation['covariance']代表协方差  evaluation['loss']代表损失 etc..
  • 1
  • 2
  • 3
  • 4

如果要理解这里evaluate函数的逻辑,这里AR模型:每次都接收长度为30的输入观测序列,并输出长度为10的预测序列。以此为规则,每次移动步长为1,以此类推,整个训练集长度为1000的序列,最终我们得到970个预测值。
这970个预测值记录在evaluation[‘mean’]中;evaluation还有其他几个键值:evaluation[‘loss’]表示总的损失,evaluation[‘times’]表示evaluation[‘mean’]对应的时间点等等.

评估完模型后,下面该是使用模型了,这里我们会用到predict函数来预测,传入参数为evaluation[‘start_tuple’]会被用于之后的预测中,它相当于最后30步的输出值和对应的时间点。以此为起点(也就是给定观察数据),我们可以对1000步以后的值进行预测,对应的代码为:

   (predictions,) = tuple(ar.predict(
        input_fn=tf.contrib.timeseries.predict_continuation_input_fn(
            evaluation, steps=250)))
  • 1
  • 2
  • 3

这里的代码在1000步之后又像后预测了250个时间点。对应的值就保存在predictions[‘mean’]中。我们可以把观测到的值、模型拟合的值、预测值用下面的代码画出来:

    plt.figure(figsize=(15, 5))
    plt.plot(data['times'].reshape(-1), data['values'].reshape(-1), label='origin')
    plt.plot(evaluation['times'].reshape(-1), evaluation['mean'].reshape(-1), label='evaluation')
    plt.plot(predictions['times'].reshape(-1), predictions['mean'].reshape(-1), label='prediction')
    plt.xlabel('time_step')
    plt.ylabel('values')
    plt.legend(loc=4)
    plt.savefig('predict_result.jpg')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里写图片描述


使用LSTM预测单变量时间序列

注意:以下LSTM模型的例子必须使用TensorFlow最新的开发版的源码。具体来说,要保证

from tensorflow.contrib.timeseries.python.timeseries.estimators import TimeSeriesRegressor
  • 1

可以成功执行。(就是前面说的需要安装最新版的tensorflow)

给出两个用LSTM预测时间序列模型的例子,分别是train_lstm.pytrain_lstm_multivariate.py。前者是在LSTM中进行单变量的时间序列预测,后者是使用LSTM进行多变量时间序列预测。为了使用LSTM模型,我们需要先使用TFTS库对其进行定义,定义模型的代码来源于TFTS的示例源码,在train_lstm.py和train_lstm_multivariate.py中分别拷贝了一份。

产生训练数据

这里我们产生的数据公式修改一下

y=sin(π0.02x)+cos(π0.02x)+sin(π0.04x)+noise

同样用函数加噪声的方法生成一个模拟的时间序列数据:

    x = np.array(range(1000))
    noise = np.random.uniform(-0.2, 0.2, 1000)
    y = np.sin(np.pi * x / 50 ) + np.cos(np.pi * x / 50) + np.sin(np.pi * x / 25) + noise

    data = {
        tf.contrib.timeseries.TrainEvalFeatures.TIMES: x,
        tf.contrib.timeseries.TrainEvalFeatures.VALUES: y,
    }

    '''
    plt.plot(x, y)
    plt.savefig('train_data.jpg')
    '''
    reader = NumpyReader(data)

    train_input_fn = tf.contrib.timeseries.RandomWindowInputFn(
    reader, batch_size=4, window_size=100) # batch_size为4 序列长度为100
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

产生数据如下:

这里写图片描述

定义训练模型并预测

这里使用TFTS包内提供的TimeSeriesRegressor模型。其中_LSTMModel来自TFTS包的官方例子



_LSTMModel接受两个参数:

  • num_units: 模型中使用的LSTM单元个数
  • num_features: 时间序列能观察到的维度.(即每个时间步能观察到的数据特征量)

这里我们使用的模型参数为num_features = 1表示单变量时间序列,即每个时间点上观察到的量只是一个单独的数值。num_units=128表示使用隐层为128大小的LSTM模型。后续的训练,评估,预测和前面讲的代码类似。在以后的1000组数据上,我们向后预测了200组数据。


代码如下:

    estimator = ts_estimators.TimeSeriesRegressor(
        model=_LSTMModel(num_features=1, num_units=128),
        optimizer=tf.train.AdamOptimizer(0.001))

    estimator.train(input_fn=train_input_fn, steps=2000)
    evaluation_input_fn = tf.contrib.timeseries.WholeDatasetInputFn(reader)
    evaluation = estimator.evaluate(input_fn=evaluation_input_fn, steps=1)
    # Predict starting after the evaluation 预测后续的200组数据
    (predictions,) = tuple(estimator.predict(
        input_fn=tf.contrib.timeseries.predict_continuation_input_fn(
            evaluation, steps=200)))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

绘制预测数据图

    observed_times = evaluation["times"][0]
    observed = evaluation["observed"][0, :, :]
    evaluated_times = evaluation["times"][0]
    evaluated = evaluation["mean"][0]
    predicted_times = predictions['times']
    predicted = predictions["mean"]

    plt.figure(figsize=(15, 5))
    plt.axvline(999, linestyle="dotted", linewidth=4, color='r')
    observed_lines = plt.plot(observed_times, observed, label="observation", color="k")
    evaluated_lines = plt.plot(evaluated_times, evaluated, label="evaluation", color="g")
    predicted_lines = plt.plot(predicted_times, predicted, label="prediction", color="r")
    plt.legend(handles=[observed_lines[0], evaluated_lines[0], predicted_lines[0]],
               loc="upper left")
    plt.savefig('predict_result.jpg')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里写图片描述


使用LSTM预测多变量时间序列

与上面的使用LSTM预测单变量时间序列不同的地方在于数据的读取和模型的预测量。使用原理实质上是一致的。

获取训练数据

所谓多变量时间序列,就是指在每个时间点上的观测量有多个值。在data/multivariate_periods.csv文件中,保存了一个多变量时间序列的数据:(下面是截取)

    0,0.926906299771,1.99107237682,2.56546245685,3.07914768197,4.04839057867
    1,0.108010001864,1.41645361423,2.1686839775,2.94963962176,4.1263503303
    2,-0.800567600028,1.0172132907,1.96434754116,2.99885333086,4.04300485864
    3,0.0607042871898,0.719540073421,1.9765012584,2.89265588817,4.0951014426
    4,0.933712200629,0.28052120776,1.41018552514,2.69232603996,4.06481164223
    5,-0.171730652974,0.260054421028,1.48770816369,2.62199129293,4.44572807842
    6,-1.00180162933,0.333045158863,1.50006392277,2.88888309683,4.24755865606
    7,0.0580061875336,0.688929398826,1.56543458772,2.99840358953,4.52726873347
    8,0.764139447412,1.24704875327,1.77649279698,3.13578593851,4.63238922951
    9,-0.230331874785,1.47903998963,2.03547545751,3.20624030377,4.77980005228
    10,-1.03846045211,2.01133000781,2.31977503972,3.67951536251,5.09716775897
    11,0.188643592253,2.23285349038,2.68338482249,3.49817168611,5.24928239634
    12,0.91207302309,2.24244446841,2.71362604985,3.96332587625,5.37802271594
    13,-0.296588665881,2.02594634141,3.07733910479,3.99698324956,5.56365901394
    14,-0.959961476551,1.45078629833,3.18996420137,4.3763059609,5.65356015609
    15,0.46313530679,1.01141441548,3.4980215948,4.20224896882,5.88842247449
    16,0.929354125798,0.626635305936,3.70508262244,4.51791573544,5.73945973251
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

即每个时间步上能观察到多个数据变量(这里有5组数据)。举个简单的例子:如果现在我在跑步,每个时间步上,可以获取到我的心跳,血压,体温。这就是在每个时间步上能观察到3个数据变量。

下面依旧是使用CSVReader处理数据,区别在column_names参数,该参数告诉CSVReader那些变量是对应的时间步和变量。

    csv_file_name = path.join("./data/multivariate_periods.csv")
     reader = tf.contrib.timeseries.CSVReader(
         csv_file_name,
         column_names=((tf.contrib.timeseries.TrainEvalFeatures.TIMES,)
                       + (tf.contrib.timeseries.TrainEvalFeatures.VALUES,) * 5))
     train_input_fn = tf.contrib.timeseries.RandomWindowInputFn(
         reader, batch_size=4, window_size=32)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

定义训练模型并预测

这里与上面的程序不同的地方在于_LSTMModel中参数num_features=5.即能观察的数据量维度为5.本次向后预测的数据为100组。(训练数据100组)

     estimator = ts_estimators.TimeSeriesRegressor(
         model=_LSTMModel(num_features=5, num_units=128),
         optimizer=tf.train.AdamOptimizer(0.001))

     estimator.train(input_fn=train_input_fn, steps=200)
     evaluation_input_fn = tf.contrib.timeseries.WholeDatasetInputFn(reader)
     evaluation = estimator.evaluate(input_fn=evaluation_input_fn, steps=1)
     # Predict starting after the evaluation
     (predictions,) = tuple(estimator.predict(
         input_fn=tf.contrib.timeseries.predict_continuation_input_fn(
             evaluation, steps=100)))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

绘制预测图

     observed_times = evaluation["times"][0]
     observed = evaluation["observed"][0, :, :]
     evaluated_times = evaluation["times"][0]
     evaluated = evaluation["mean"][0]
     predicted_times = predictions['times']
     predicted = predictions["mean"]

     plt.figure(figsize=(15, 5))
     plt.axvline(99, linestyle="dotted", linewidth=4, color='r')
     observed_lines = plt.plot(observed_times, observed, label="observation", color="k")
     evaluated_lines = plt.plot(evaluated_times, evaluated, label="evaluation", color="g")
     predicted_lines = plt.plot(predicted_times, predicted, label="prediction", color="r")
     plt.legend(handles=[observed_lines[0], evaluated_lines[0], predicted_lines[0]],
                loc="upper left")
     plt.savefig('predict_result.jpg')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里写图片描述



总结

TFTS是Tensorflow官方提供的基于LSTM模型的时序预测工具,可以用于常见的时序模型上(替代HMM)。这里讲了如果使用TFTS模型读取数据并产生训练数据,同时讲了如果使用TFTS模块自带的AR和LSTM模型。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号