分散在我网站上的传感器(相同类型)正在不定期地手动向我的后端报告。在报告之间,传感器聚合事件并将它们作为批处理报告。
以下数据集是序列事件数据的集合,批量收集。例如传感器 1 报告了 2 次。第一批 2 个事件和第二批 3 个事件,而传感器 2 报告 1 次,有 3 个事件。
我想将此数据用作我的火车数据X
sensor_id | batch_id | 时间戳 | 功能_1 | feature_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,是根据传感器收集的所有事件计算得出的分数:
即socre_sensor_1 = f([[batch1...],[batch2...]])
sensor_id | final_score |
---|---|
1 | 0.8 |
2 | 0.6 |
我想在每次收集批次时预测 y,即具有 2 个报告的传感器的 2 个预测。
LSTM 模型:
我从 LSTM 模型开始,因为我试图预测事件的时间序列。
我的第一个想法是选择一个固定大小的输入,并在收集到的事件数量小于输入大小时对输入进行零填充。然后屏蔽填充值:
model.add(Masking(mask_value=0., input_shape=(num_samples, num_features)))
例如:
sensor_id | batch_id | 时间戳 | 功能_1 | feature_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,但我不确定它是否适合我的用例。如何解决这样的问题?
答案 0 :(得分:1)
我没有您的模型的具体信息,但 LSTM 的 TF 实现通常期望 (batch, seq, features)
作为输入。
现在不要假设这是您的 batch_id 之一:
data = np.zeros((15,5))
array([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
您可以使用 (1, 15, 5
) 对其进行整形并将其提供给模型,但是只要您的 batch_id
长度发生变化,您的序列长度也会发生变化,您的模型需要一个固定序列。
相反,您可以在训练前重塑数据,以便将 batch_id 长度作为批量大小传递:
data = data[:,np.newaxis,:]
array([[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]],
[[0., 0., 0., 0., 0.]]])
相同的数据,形状为 (15, 1, 5),但您的模型现在将查看固定长度 1
,并且样本数量会有所不同。
确保也重塑您的 label
。
据我所知,RNN 和 LSTM 被应用于每个时间步和状态在 bacthe 之间重置,这不会影响模型行为。
答案 1 :(得分:1)
粗糙的张量是要走的路:
<块引用>不规则张量是嵌套可变长度列表的 TensorFlow 等价物。它们可以轻松存储和处理具有非均匀形状的数据
您可以通过多种方式创建参差不齐的张量,一种来自嵌套列表。
import tensorflow as tf
# your first sensor data from your example above
data = [[[0.54 , 0.33],[0.23 , 0.14]],[[0.51,0.13],[0.23,0.24],[0.33, 0.44]]]
X = tf.ragged.constant(data)
<tf.RaggedTensor [[[0.5400000214576721, 0.33000001311302185], [0.23000000417232513, 0.14000000059604645]], [[0.5099999904632568, 0.12999999523162842], [0.23000000417232513, 0.23999999463558197], [0.33000001311302185, 0.4399999976158142]]]>
然后在您的模型中,您的第一层应该是带有 ragged=True
且形状为 [None , number_of_features]
的输入:
model = Sequential()
model.add(Input(shape=[None,2], dtype=tf.float32, ragged=True))
model.add(LSTM(16, activation='tanh'))
...
答案 2 :(得分:0)
使用可变大小的输入序列非常简单。虽然每个批次中具有相同大小的序列有限制,但存在 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
这是将可变大小序列作为输入的方式。只有属于同一批次的序列才需要填充/屏蔽。
现在,您可以为您的输入数据创建一个生成器,该生成器一次生成一个事件序列作为模型的输入,在这种情况下,您显式 do not need to specify batch_size
,因为您正在生成一个已经一次排序。
如果您的数据采用数据集、生成器或 keras.utils.Sequence 实例的形式(因为它们会生成批次),请不要指定 batch_size。
或者您可以使用您提到的 ragged tensors 并为每个序列提供一个 batch_size
为 1。就我个人而言,我更喜欢使用生成器来训练数据,因为它也为您提供了更大的预处理灵活性。
有趣的是,您可以进一步 optimize
此代码,但将相同长度序列的批次捆绑在一个批次中,然后传递一个 variable batch size
。如果您有大量数据并且无法为每次梯度更新运行 batch_size 为 1,这将有所帮助!
另外一个警告!如果您的序列非常长,那么我建议您使用 Truncated Backpropagation through time (TBPTT)
(查找详细信息 here)。
希望这能解决您的问题。
答案 3 :(得分:0)
无需将所有内容都提供给同一个 LSTM,并且当“我的训练数据中每个传感器报告的事件数量差异很大”时,子网方法应该更有效。
如果您有 N 个采样间隔变化较大的传感器,则使 N Input
s 和 LSTM
s(并行),然后在稍后阶段连接特征。为了避免制作许多 LSTM,请按预期的样本长度分组传感器,例如100-150、900-1100 等,以及每组内的 pad 以达到各自的最大长度。
避免填充过多,因为(典型的右填充)具有缩小“学习信号”的严重缺点(BPTT展开right-to-left,因此如果“正确”大部分为零,则大多数“学习”忽略它们而不是特征提取)。您的 batch_size
与对上述差异进行分组的能力一样宽松;因此,这是为您的数据找到合适的平衡点(只是不要使用 batch_size<32
进行批量规范,而是更喜欢批量重新归一化或其他小批量替代)。
最后,对于如此稀疏的数据,我会推荐一种注意力机制(可用的各种实现)。
多分支 LSTM 示例:
from tensorflow.keras.layers import Input, LSTM, Dense, concatenate
from tensorflow.keras.models import Model
ipt1 = Input(shape=(None, 2)) # 2 sensors grouped, variable input length
x1 = LSTM(4)(ipt1)
ipt2 = Input(shape=(None, 3)) # 3 sens, var
x2 = LSTM(6)(ipt2)
xc = concatenate([x1, x2])
out = Dense(1, activation='sigmoid')(xc)
model = Model([ipt1, ipt2], out)
model.compile('adam', 'binary_crossentropy')
x1 = np.random.randn(2, 4, 2)
x2 = np.random.randn(2, 5, 3)
y = np.random.randint(0, 2, 2)
model.fit([x1, x2], y)