更多深度文章,请关注:https://yq.aliyun.com/cloud
看深度学习框架排名第一的TensorFlow如何进行时序预测——第一篇
时间序列分析是一种动态数据处理的统计方法。根据对系统进行观测得到的时间序列数据,用曲线拟合的方法对系统进行客观的描述。
如今,时间序列数据出现在金融,信号处理,语音识别和医学等诸多领域。解决时间序列问题的标准方法通常需要手动提炼数据特征,然后才能将其输入到机器学习算法中。这通常还要求开发设计人员掌握数据所属学科领域的知识特征,以便在算法中加入特征过滤。例如,如果处理信号(即EEG信号的分类),则需要掌握的知识特征涉及各种频带的功率谱及Hjorth参数。对于认真钻研本领域的程序猿来说,这简直就是噩梦。
那么是不是不掌握这些学科领域的知识特征,我们就没有办法进行模型设计了呢?
其实答案不然,在图像分类领域也出现了类似的情况。但是,随着深度学习的出现,卷积神经网络(CNN)的性能已经可以胜过这种人工提取特征的方法。CNN不需要任何手动设置任何的图像特征。在训练过程中,随着层次越来越深,CNN越来越复杂,进而它自己会学习得到许多“过滤器”,并在最终的分类器中使用它们。
在这篇博客文章中,我将讨论使用深度学习的方法对时间序列数据进行分类,而无需手动设计特征。我在本文中将使用到的例子是UCI存储库中经典的人类活动识别(HAR)数据集。该数据集包含原始时间序列数据,以及具有561个预处理数据的工程特征。在博客中我会比较使用工程特征与深度学习这两种方法(卷积和复现神经网络),并表明深度学习可以超越前者的性能。
在本文中我将使用Tensorflow来实现和训练博客中所用到的模型。在下面的讨论中,提供了代码片段来解释实现过程。有关完整的代码,请参阅我的Github资源库。
卷积神经网络(CNN)
第一步是将数据投射到具有一定形状的numpy数组中:(batch_size, seq_len, n_channels),其中batch_size是训练期间批次中的示例数,seq_len是时间序列的长度(在我们的情况下n_channels为128),并且是进行测量的通道的数量。在本文的小例子中,有9个通道,每3个坐标轴包括3个不同的加速度测量。每次观察有6类活动LAYING, STANDING, SITTING, WALKING_DOWNSTAIRS, WALKING_UPSTAIRS, WALKING。
首先,我们为输入到计算图的数据创建占位符:
graph = tf.Graph()
with graph.as_default():
inputs_ = tf.placeholder(tf.float32, [None, seq_len, n_channels],name = 'inputs')
labels_ = tf.placeholder(tf.float32, [None, n_classes], name = 'labels')
keep_prob_ = tf.placeholder(tf.float32, name = 'keep')
learning_rate_ = tf.placeholder(tf.float32, name = 'learning_rate')
inputs是将
输入的张量馈送到计算图,并将其数组第一个位置设置为None
,以便
允许可变的批量大小。labels_
是要预测的一个热编码的标签,keep_prob
的作用
是在退出正则化中保持概率来防止过度拟合,并且learning_rate_
是Adam优化器中使用的学习率。
我们将通过使用移动序列的一维内核构造卷积层(与使用2d卷积的图像不同)来构造卷积层,这些内核作为在训练过程中的过滤器。像许多CNN架构一样,层越深,过滤器数越多。每个卷积之后是汇集层,以此减少序列长度。下面是可能可以使用的CNN架构的简单图片:
上面描述的卷积层如下实现:
with graph.as_default():
# (batch, 128, 9) -> (batch, 32, 18)
conv1 = tf.layers.conv1d(inputs=inputs_, filters=18, kernel_size=2, strides=1,padding='same', activation = tf.nn.relu)
max_pool_1 = tf.layers.max_pooling1d(inputs=conv1, pool_size=4, strides=4, padding='same')
# (batch, 32, 18) -> (batch, 8, 36)
conv2 = tf.layers.conv1d(inputs=max_pool_1, filters=36, kernel_size=2, strides=1,padding='same', activation = tf.nn.relu)
max_pool_2 = tf.layers.max_pooling1d(inputs=conv2, pool_size=4, strides=4, padding='same')
# (batch, 8, 36) -> (batch, 2, 72)
conv3 = tf.layers.conv1d(inputs=max_pool_2, filters=72, kernel_size=2, strides=1,padding='same', activation = tf.nn.relu)
max_pool_3 = tf.layers.max_pooling1d(inputs=conv3, pool_size=4, strides=4, padding='same')
一旦达到最后一层,我们需要张量平坦化并将其输送到具有正确数量的神经元的分类器中(上图中的144个)。模型功能:
1. 计算softmax交叉熵,这是多类问题中使用的标准损失度量。
2. 从最大概率以及精度预测类标签。
功能实现代码如下:
with graph.as_default():
# Flatten and add dropout
flat = tf.reshape(max_pool_3, (-1, 2*72))
flat = tf.nn.dropout(flat, keep_prob=keep_prob_)
# Predictions
logits = tf.layers.dense(flat, n_classes)
# Cost function and optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=labels_))
optimizer = tf.train.AdamOptimizer(learning_rate_).minimize(cost)
# Accuracy
correct_pred = tf.equal(tf.argmax(logits, 1), tf.argmax(labels_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32), name='accuracy')
其余的实施部分涉及向图表馈送批次的训练数据并评估验证集的性能。最后,对测试集进行评估。采用上述架构
batch_size=
600,learning_rate
=0.001(默认值),keep_prob=
0.5,500训练次数,我们得到98%的测试精度。下面的图表显示了训练/验证精度如何通过训练次数演变:长短期记忆网络(LSTM)
LSTM在处理基于文本的数据方面非常受欢迎,在情感分析,语言翻译和文本生成方面也相当成功。今天我们就用LSTM来解决我们今天的问题。
以下是可以在我们的问题中使用的示例架构:
为了将数据传送到网络中,我们需要将数组分成128个,每个的形状我们定义为:(batch_size, n_channels)。然后,单层神经元将把这些输入转换成LSTM细胞,每一个都具有维度lstm_size。该参数的大小选择要大于通道数。这是一种类似于在文本应用程序中嵌入图层的方式。为了实现,占位符与上述相同。以下代码段实现了LSTM层:
with graph.as_default():
# Construct the LSTM inputs and LSTM cells
lstm_in = tf.transpose(inputs_, [1,0,2]) # reshape into (seq_len, N, channels)
lstm_in = tf.reshape(lstm_in, [-1, n_channels]) # Now (seq_len*N, n_channels)
# To cells
lstm_in = tf.layers.dense(lstm_in, lstm_size, activation=None)
# Open up the tensor into a list of seq_len pieces
lstm_in = tf.split(lstm_in, seq_len, 0)
# Add LSTM layers
lstm = tf.contrib.rnn.BasicLSTMCell(lstm_size)
drop = tf.contrib.rnn.DropoutWrapper(lstm, output_keep_prob=keep_prob_)
cell = tf.contrib.rnn.MultiRNNCell([drop] * lstm_layers)
initial_state = cell.zero_state(batch_size, tf.float32)
上面的代码段中有一个重要的技术细节。我将阵列重新整形(batch_size, seq_len, n_channels)到(seq_len, batch_size, n_channels),这样tf.split就可以在每个步骤中将数据(由第零个索引)正确地分割成数组列表。其余的是LSTM实现的标准,包括构建层(包括正则化的退出),然后是定义初始状态。
下一步是通过网络实现前向传递和成本函数。一个重要的技术方面利用梯度剪辑,因为它通过防止反向传播期间的爆炸梯度来改善训练。
with graph.as_default():
outputs, final_state = tf.contrib.rnn.static_rnn(cell, lstm_in, dtype=tf.float32,initial_state = initial_state)
# We only need the last output tensor to pass into a classifier
logits = tf.layers.dense(outputs[-1], n_classes, name='logits')
# Cost function and optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=labels_))
# Grad clipping
train_op = tf.train.AdamOptimizer(learning_rate_)
gradients = train_op.compute_gradients(cost)
capped_gradients = [(tf.clip_by_value(grad, -1., 1.), var) for grad,var in gradients]
optimizer = train_op.apply_gradients(capped_gradients)
# Accuracy
correct_pred = tf.equal(tf.argmax(logits, 1), tf.argmax(labels_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32), name='accuracy')
请注意,仅使用LSTM输出顶部序列的最后一个成员,因为我们正在尝试每个序列预测一个数字。其余的类似CNN,我们只需要将数据提供给图表进行训练。lstm_size=27
,lstm_layers=2
,batch_size=600
,learning_rate=0.0005
,和keep_prob=0.5
,我获得95%的准确度的测试集。这比CNN的结果还差,但还是不错的。这些超参数的更好选择会改进的结果。
与工程特征进行比较
在此之前,我已经使用561个预先设计的特征测试了一些关于这个问题的机器学习方法。性能最好的模型之一是梯度提升树(gradient booster)(树形或线性),其结果是96%的精确度(您可以从这本笔记本中了解更多信息)。CNN架构优于梯度提升树,但LSTM的性能相较于梯度提升树(gradient booster)就稍差一些。
总结:
在这篇博客文章中,我已经说明了如何使用CNN和LSTM进行时间序列分类,并证明深层架构可以胜过预先设计的功能特征训练的模型。除了达到更好的准确性外,深度学习模式还“培养”了自己的功能。这是非常可取的,因为人们不需要具有来自数据来源的领域专长,能够训练准确的模型。
我们在这篇文章中使用的序列相当小(128步)。人们可能会想,如果步骤数量很多,那么今天我讨论的这些架构的可训练性是否还有?如果有?会发生什么。我认为一种可能的架构将涉及LSTM和CNN的组合,其对于较大的序列(即> 1000,对于LSTM是有问题的)可以更好地工作。因为在这种情况下,具有汇集作用的几个卷积就可以有效地减少前几个层中的步数,并且得到的较短的序列可以被反馈送到LSTM层。这种结构的一个例子最近被用于从移动设备记录的心房颤动检测。如果你有兴趣了解这种长序列的方法可以去研究它。
本文由北邮@爱可可-爱生活老师推荐,阿里云云栖社区组织翻译。
文章原标题《time-series-classification-with-tensorflow》,
作者:burakhimmetoglu 作者博客:https://burakhimmetoglu.com/
译者:袁虎 审阅:主题曲
文章为简译,更为详细的内容,请查看原文