首页 > 解决方案 > 对可变长度序列进行训练和预测

问题描述

分散在我网站上的传感器(相同类型)正在不定期地手动向我的后端报告。在报告之间,传感器聚合事件并将它们作为一个批次报告。

以下数据集是序列事件数据的集合,批量收集。例如传感器 1 报告了 2 次。在第一批 2 个事件和第二批 3 个事件上,而传感器 2 报告了 1 次和 3 个事件。

我想将此数据用作我的火车数据X

sensor_id batch_id 时间戳 特征_1 特征_n
1 1 2020-12-21T00:00:00+00:00 0.54 0.33
1 1 2020-12-21T01:00:00+00:00 0.23 0.14
1 2 2020-12-21T03:00:00+00:00 0.51 0.13
1 2 2020-12-21T04:00:00+00:00 0.23 0.24
1 2 2020-12-21T05:00:00+00:00 0.33 0.44
2 1 2020-12-21T00:00:00+00:00 0.54 0.33
2 1 2020-12-21T01:00:00+00:00 0.23 0.14
2 1 2020-12-21T03:00:00+00:00 0.51 0.13

我的目标y是根据传感器收集的所有事件计算得出的分数:
IEsocre_sensor_1 = f([[batch1...],[batch2...]])

sensor_id final_score
1 0.8
2 0.6

我想在每次收集批次时预测y ,IE 2 预测具有 2 个报告的传感器。


LSTM 模型:
我从 LSTM 模型开始,因为我试图预测事件的时间序列。我的第一个想法是选择一个固定大小的输入,并在收集的事件数小于输入大小时对输入进行零填充。然后屏蔽填充值:

model.add(Masking(mask_value=0., input_shape=(num_samples, num_features)))

例如:

sensor_id batch_id 时间戳 特征_1 特征_n
1 1 2020-12-21T00:00:00+00:00 0.54 0.33
1 1 2020-12-21T01:00:00+00:00 0.23 0.14

如果选择的长度为 5,将产生以下输入:

[
 [0.54, 0.33],
 [0.23, 0.14],
 [0,0],
 [0,0],
 [0,0]
]

但是,我的火车数据中每个传感器报告的事件数量差异很大,一份报告可以收集 1000 个事件,而另一份可以收集 10 个。因此,如果我选择平均大小(比如说 200),一些输入会有很多填充,而其他会被截断并且数据会丢失。

我听说过ragged tensors,但我不确定它是否适合我的用例。如何解决这样的问题?

标签: pythontensorflowkeraslstm

解决方案


使用可变大小的输入序列非常简单。虽然每个批次中具有相同大小的序列是有限制的,但存在NO RESTRICTION of having variable-sized sequences between the batches. 利用这一点,您可以简单地将 LSTM 的输入序列设置为(None, features)batch_size用作 1。

让我们创建一个生成器,该生成器生成 2 个特征的可变长度序列和一个随机浮点分数,您根据这些序列寻找一个随机浮点分数,类似于传感器的输入数据。

#Infinitely creates batches of dummy data
def generator():
    while True:
        length = np.random.randint(2, 10) #Variable length sequences
        x_train = np.random.random((1, length, 2)) #batch, seq, features
        y_train = np.random.random((1,1)) #batch, score
        yield x_train, y_train

next(generator())
#x.shape = (1,4,2), y.shape = (1,1)
(array([[[0.63841991, 0.91141833],
         [0.73131801, 0.92771373],
         [0.61298585, 0.6455549 ],
         [0.25893925, 0.40202978]]]),
 array([[0.05934613]]))

上面是由生成器创建的 4 长度序列的示例,而下一个是 9 长度序列。

next(generator())
#x.shape = (1,9,2), y.shape = (1,1)
(array([[[0.76006158, 0.27457503],
         [0.57739596, 0.75416962],
         [0.03029365, 0.29339812],
         [0.93866829, 0.79137367],
         [0.52739961, 0.11475738],
         [0.85832651, 0.19247399],
         [0.37098216, 0.48703114],
         [0.95846681, 0.15507787],
         [0.86945015, 0.70949593]]]),
 array([[0.02560889]]))

现在,让我们创建一个基于 LSTM 的神经网络,它可以处理每个批次的这些可变大小的序列。

from tensorflow.keras import layers, Model, utils

inp = layers.Input((None, 2))
x = layers.LSTM(10, return_sequences=True)(inp)
x = layers.LSTM(10)(x)
out = layers.Dense(1)(x)

model = Model(inp, out)
utils.plot_model(model, show_layer_names=False, show_shapes=True)

在此处输入图像描述

以 1 的批大小训练这些 -

model.compile(loss='binary_crossentropy', optimizer='adam')
model.fit(generator(), steps_per_epoch=100, epochs=10, batch_size=1)
#Steps_per_epoch is to stop the generator from generating infinite batches of data per epoch.
Epoch 1/10
100/100 [==============================] - 1s 5ms/step - loss: 1.5145
Epoch 2/10
100/100 [==============================] - 0s 5ms/step - loss: 0.7435
Epoch 3/10
100/100 [==============================] - 0s 4ms/step - loss: 0.7885
Epoch 4/10
100/100 [==============================] - 0s 4ms/step - loss: 0.7384
Epoch 5/10
100/100 [==============================] - 0s 4ms/step - loss: 0.7139
Epoch 6/10
100/100 [==============================] - 0s 5ms/step - loss: 0.7462
Epoch 7/10
100/100 [==============================] - 0s 4ms/step - loss: 0.7173
Epoch 8/10
100/100 [==============================] - 0s 4ms/step - loss: 0.7116
Epoch 9/10
100/100 [==============================] - 0s 4ms/step - loss: 0.6875
Epoch 10/10
100/100 [==============================] - 0s 4ms/step - loss: 0.7153

这就是您可以使用可变大小的序列作为输入的方式。只有属于同一批次的序列才需要填充/屏蔽。

现在,您可以为输入数据创建一个生成器,该生成器一次生成一个事件序列作为模型的输入,在这种情况下,您不需要显式指定batch_size因为您已经一次生成一个序列。

如果您的数据采用数据集、生成器或 keras.utils.Sequence 实例的形式(因为它们会生成批次),请不要指定 batch_size。

或者您可以使用您提到的参差不齐的张量batch_size并为每个序列提供 1。就个人而言,我更喜欢使用生成器来训练数据,因为它也为您提供了更大的预处理灵活性。

有趣的是,您可以optimize进一步编写此代码,但将相同长度序列的批次捆绑在一起,然后将variable batch size. 如果您有大量数据并且无法为每次梯度更新运行 1 的 batch_size ,这将有所帮助!

另一个警告!如果您的序列非常长,那么我建议使用(在此处Truncated Backpropagation through time (TBPTT)查找详细信息)。

希望这可以解决您正在寻找的问题。


推荐阅读