TensorFlow实战:Chapter-7下(TFTS库与时间序列预测)
- 本节简介
- TensorFlow更新
- 注意
- 下载最新源码编译源码更新
- 首先卸载当前的tensorflow
- 参考官方的从源码安装tensorflow
- 直接使用pip工具更新
- 更新后可能会遇到的异常
- 自定义LSTM循环神经网络进行时间序列预测
- 工程实现
- 需要用到的模块
- 生成训练数据与测试数据
- 注解
- 定义网络模型
- 创建模型并训练
- Estimator工具
- 绘图
- 利用TFTS进行时间序列预测
- 载入数据部分
- 从CSV文件中读入时间序列数据
- 使用AR模型预测时间序列
- 代码实现
- 产生数据
- 创建ar模型
- 训练评估模型并预测
- 使用LSTM预测单变量时间序列
- 产生训练数据
- 定义训练模型并预测
- 绘制预测数据图
- 使用LSTM预测多变量时间序列
- 获取训练数据
- 定义训练模型并预测
- 绘制预测图
- 总结
本节简介
本节关于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模型对时间序列进行预测(包含单变量和多变量)
先看效果图,
- 使用自定义LSTM循环网络对单变量进行时间序列预测(没使用TFTS,代码比较繁琐):
- 使用TFTS下的AR模型预测效果如下,蓝色为训练数据,绿色为模型拟合数据,红色为预测数据:
- 使用TFTS下的LSTM对单变量进行时间序列预测:
- 使用TFTS下的LSTM对多变量进行时间序列预测:
这里涉及到的代码保存在github-hzy46.网速不好的同学可以到或者在CSDN上下载。
TensorFlow更新
注意
后面使用的LSTM模型的例子须使用TensorFlow最新的开发版的源码。具体来说,要保证下面这句话可以成功执行。
from tensorflow.contrib.timeseries.python.timeseries.estimators import TimeSeriesRegressor
- 1
- 2
如果执行不成功,则需要下面的更新操作。
下载最新源码(编译源码更新)
1. 首先卸载当前的tensorflow
pip uninstall tensorflow #gpu版 就是tensorflow-gpu
- 1
- 2
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。
- 下载适配CUDA版本和系统版本的cudnn-v6.0
- 解压下载好的cudnn
- $ : sudo tar -zxvf cudnn-8.0-linux-x64-v6.0.tgz
- …
- 将解压文件拷贝并修改权限
- $ :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工具是用来提供一个自定义模型的框架,我们照着定义好的格式配置好输入即可。
- 创建一个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
- 训练模型
- 将我们的训练数据塞给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
- 预测
- 将需要预测的数据塞给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
- 注意
- 前面我说了如果在输入的时候设置x,y,batch_size就不要设置input_fn,需要注意的是:,如果使用x,y而不是input_fn来传参数的形式,需要用Estimator里一个叫SKCompat的类包装一下.
- estimator_instance = SKCompat(Estimator(model_fn=..., model_dir=...))
- 1
- 2
- 到这里,我们对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.005∗x+noise
y=sin(0.01∗π∗x)+0.005∗x+noise
y的周期分量的frequency为2∗π∗(1/f)
2∗π∗(1/f),故f=200,所以periodicities=200
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.py和train_lstm_multivariate.py。前者是在LSTM中进行单变量的时间序列预测,后者是使用LSTM进行多变量时间序列预测。为了使用LSTM模型,我们需要先使用TFTS库对其进行定义,定义模型的代码来源于TFTS的示例源码,在train_lstm.py和train_lstm_multivariate.py中分别拷贝了一份。
产生训练数据
这里我们产生的数据公式修改一下
y=sin(π∗0.02∗x)+cos(π∗0.02∗x)+sin(π∗0.04∗x)+noise
y=sin(π∗0.02∗x)+cos(π∗0.02∗x)+sin(π∗0.04∗x)+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模型。