3D数组训练/测试TensorFlow RNN LSTM

8

(我正在测试我的能力,写出短而有效的问题,让我知道我在这里做得如何)

我正在尝试用TensorFlow循环神经网络,特别是LSTM,对以下ndarray格式的时间序列数据进行一些实验性的训练/测试:

[[[time_step_trial_0, feature, feature, ...]
  [time_step_trial_0, feature, feature, ...]]                  
 [[time_step_trial_1, feature, feature, ...]
  [time_step_trial_1, feature, feature, ...]]
 [[time_step_trial_2, feature, feature, ...]
  [time_step_trial_2, feature, feature, ...]]]

这个3D数组的1d部分包含了一个时间步骤和在该时间步骤观察到的所有特征值。2d块包含了在一个试验中观察到的所有1d数组(时间步骤)。3d块包含了记录时间序列数据的所有2d块(试验)。对于每个试验,时间步骤频率是恒定的,窗口间隔在所有试验中都相同(0至50秒,0至50秒等)。
例如,我获得了一些F1赛车的数据,如扭矩、速度、加速度、旋转速度等。在记录每个0.5秒的时间步骤的一定时间间隔内,我使用每个时间步骤与在该时间步骤记录的特征相对应的1d数组。然后,我围绕所有时间步骤形成一个2D数组,对应于一个F1赛车在赛道上的运行。我创建了一个最终的3D数组,包含所有F1赛车和它们的时间序列数据。我想要训练和测试一个模型,以便检测新车在F1常规轨迹上的异常情况。
我目前知道TensorFlow模型支持用于训练和测试的2D数组。我想知道如果要在这个3D数组中训练和测试所有独立试验(2D),我需要经过哪些步骤。此外,我将来还会添加更多的试验。那么,为了不断更新我的模型并加强我的LSTM,我需要遵循什么样的适当程序?
这是我最初尝试复制的模型,用于除人类活动识别之外的其他目的:https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition。另一个更可行的模型是我更愿意看到的,用于时间序列数据中的异常检测:https://arxiv.org/abs/1607.00148。我想构建一个异常检测模型,给定非异常时间序列训练数据集,我们可以检测出测试数据集中随着时间推移的某些数据部分被定义为“不正常”。

@E_net4 已更新。 - Julian Rachman
@vijaym,我只是想知道,你是在回答问题还是只是评论? - Julian Rachman
我也可以获取第二篇论文中提到的数据。如果您只需要一个示例seq2seq模型来应用,您可以查看我的答案:https://dev59.com/alcP5IYBdhLWcg3w6-Op - Vijay Mariappan
@vijaym 好的,获取那些数据并展示一下你能用第二篇论文和/或自编码器来分类异常的能力。我知道重构误差可以作为异常分类的指标。 - Julian Rachman
让我们在聊天中继续这个讨论。点击此处进入聊天室 - Vijay Mariappan
显示剩余12条评论
2个回答

4
我认为对于大多数LSTM,您需要以这种方式考虑数据(因为它易于用作网络的输入)。
您将有3个尺寸:
feature_size =不同特征的数量(扭矩、速度等)
number_of_time_steps =收集单个汽车的时间步数
number_of_cars =汽车数量
最简单的方法是将数据读入一组矩阵中,其中每个矩阵对应一个完整样本(单个汽车的所有时间步长)。
您可以排列这些矩阵,使得每行都是一个观测值,每列都是不同的参数(或相反,您可能需要转置矩阵,查看网络输入的格式)。
因此,每个矩阵的大小为:number_of_time_steps x feature_size(#rows x #columns)。您将拥有number_of_cars个不同的矩阵。每个矩阵都是一个样本。
要将数组转换为此格式,可以使用此代码块(请注意,您已经可以使用A [n]访问数组中的单个样本,但是这样做可以使访问元素的形状符合您的预期):
import numpy as np

A = [[['car1', 'timefeatures1'],['car1', 'timefeatures2']],
     [['car2', 'timefeatures1'],['car2', 'timefeatures2']], 
     [['car3', 'timefeatures1'],['car3', 'timefeatures2']]
    ]

easy_format = np.array(A)

现在你可以使用 easy_format[n] 获取一个单独的样本,其中 n 是您想要的样本编号。
easy_format[1] prints

array([['car2', 'timefeatures1'],
       ['car2', 'timefeatures2']],
      dtype='|S12')

easy_format[1].shape = (2,2)

现在您可以按照需要对其进行格式化,以适应您正在使用的网络(必要时转置行和列,一次呈现单个样本或所有样本等)。
如果我正确地理解了第二篇论文中所要做的事情,您可能需要一个序列到序列的lstm或rnn。您的原始序列是给定试验的时间序列,并且您正在生成一组中间权重(嵌入),该权重可以用低误差重新创建原始序列。您正在为所有试验执行此操作。您将在一系列相当正常的试验上训练此lstm,并使其表现良好(准确重构序列)。然后,您可以使用相同的嵌入集尝试重建新序列,如果它具有高重建误差,则可以认为它是异常的。
请查看此存储库,了解所需内容的示例以及如何使用它和代码执行的说明。它仅将整数序列映射到另一个整数序列,但很容易扩展为将向量序列映射到另一个向量序列:https://github.com/ichuang/tflearn_seq2seq。您定义的模式只是原始序列。您也可以查看自编码器来解决此问题。
最终编辑:请查看此存储库:https://github.com/beld/Tensorflow-seq2seq-autoencoder/blob/master/simple_seq2seq_autoencoder.py 我在其中稍微修改了代码,使其适用于最新版本的TensorFlow,并使一些变量名称更清晰。您应该能够修改它以运行您的数据集。现在我只让它自动编码一个随机生成的1和0数组。您可以对大量数据进行这样的操作,然后查看其他数据是否重构得准确(比平均误差高得多可能意味着存在异常)。
import numpy as np
import tensorflow as tf


learning_rate = 0.001
training_epochs = 30000
display_step = 100

hidden_state_size = 100
samples = 10
time_steps = 20
step_dims = 5
test_data = np.random.choice([ 0, 1], size=(time_steps, samples, step_dims))

initializer = tf.random_uniform_initializer(-1, 1)

seq_input = tf.placeholder(tf.float32, [time_steps, samples, step_dims])

encoder_inputs = [tf.reshape(seq_input, [-1, step_dims])]


decoder_inputs = ([tf.zeros_like(encoder_inputs[0], name="GO")]
                  + encoder_inputs[:-1])
targets = encoder_inputs
weights = [tf.ones_like(targets_t, dtype=tf.float32) for targets_t in targets]

cell = tf.contrib.rnn.BasicLSTMCell(hidden_state_size)
_, enc_state = tf.contrib.rnn.static_rnn(cell, encoder_inputs, dtype=tf.float32)
cell = tf.contrib.rnn.OutputProjectionWrapper(cell, step_dims)
dec_outputs, dec_state = tf.contrib.legacy_seq2seq.rnn_decoder(decoder_inputs, enc_state, cell)

y_true = [tf.reshape(encoder_input, [-1]) for encoder_input in encoder_inputs]
y_pred = [tf.reshape(dec_output, [-1]) for dec_output in dec_outputs]

loss = 0
for i in range(len(y_true)):
    loss += tf.reduce_sum(tf.square(tf.subtract(y_pred[i], y_true[i])))
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss)

init = tf.initialize_all_variables()

with tf.Session() as sess:
    sess.run(init)
    x = test_data
    for epoch in range(training_epochs):
        #x = np.arange(time_steps * samples * step_dims)
        #x = x.reshape((time_steps, samples, step_dims))
        feed = {seq_input: x}
        _, cost_value = sess.run([optimizer, loss], feed_dict=feed)
        if epoch % display_step == 0:
            print "logits"
            a = sess.run(y_pred, feed_dict=feed)
            print a
            print "labels"
            b = sess.run(y_true, feed_dict=feed)
            print b

            print("Epoch:", '%04d' % (epoch+1), "cost=", "{:.9f}".format(cost_value))

print("Optimization Finished!")

那么,我需要对代码进行哪些修改才能将其推向异常检测模型?由于这是编码器-解码器,我们有一个重构。但是,我该如何计算和理解用于异常检测的重构呢? - Julian Rachman
将x设为训练数据,包含您认为相对正常的试验。然后验证当您尝试重建其他“正常”的时间试验数据时,您得到的重建误差相当低。然后展示在“异常”数据上,您得到的重建误差相当高。抱歉,我无法更具体地说明,因为这之后需要对网络进行相当多的调整,以确保您没有过拟合/它具有泛化性,还需要做出一些判断。我会仔细阅读引擎异常检测的论文,以了解他们究竟做了什么,尽管我只是粗略地浏览了一下。 - Saedeas
你可以尝试调整隐藏状态的大小和学习率。这可能需要一些微调才能在你的数据集上表现良好。此外,十次试验并不是很多。无论如何,你应该能够获得比那更少的误差。 - Saedeas
隐藏状态变量所做的就是改变BasicLSTMCell中的单元数量?我不确定它如何影响其他任何事情。明确一下,您有: 样本数=10, 时间步长=5399, 步骤维度=32? - Saedeas
是的。这真的很奇怪。 - Julian Rachman
显示剩余13条评论

1
您输入的形状和相应的模型取决于您想要检测的异常类型。您可以考虑:
1. 仅特征异常: 在这种情况下,您考虑单个特征,并决定它们中是否有任何异常值,而不考虑其测量时间。例如,在您的示例中,如果一个或多个特征[扭矩、速度、加速度等]与其他特征相比是异常值,则该特征是异常值。在这种情况下,您的输入应为[批次,特征]的形式。
2. 时间-特征异常: 在这种情况下,您的输入取决于您测量特征的时间。您当前的特征可能取决于过去一段时间内测量的先前特征。例如,如果某个特征在时间0出现时其值是异常值,但如果它在未来的时间出现则不是异常值。在这种情况下,您将每个试验分成重叠的时间窗口,并形成[批次,时间窗口,特征]的特征集。
使用自动编码器非常简单,您可以从(1)开始训练自动编码器,通过输入输出之间的误差,您可以选择一个阈值,例如均值的2个标准偏差,以确定其是否是异常值。

对于(2),你可以使用seq2seq模型遵循你提到的第二篇论文,其中你的解码器误差将确定哪些特征是异常值。你可以在this上检查这种模型的实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接