我正在使用用 Keras 构建的 LSTM nn。我需要将事件历史作为一个单一因素传递,我正在考虑将其作为一个列表来做。但是我从来没有以这种方式传递过一个列表——这对 Keras 来说是可能的吗?还是我需要为此进行某种分类过程?
您将如何使用一系列事件作为 NN 的一个因素?
您的输入矩阵应该有维度(# of instances, length of events of events, 1)。例如,我有顺序的文本数据,我会将其切割成顺序相关的序列。矩阵中的每一行都是序列,事件的历史。我对这些中的每一个的标签是下一个连续的字母,或者在你的情况下是事件。
输入具有以下形状
print(X.shape)
(163717, 100, 1)
Keras 中的 LSTM 模型。
# define the LSTM model
model = Sequential()
model.add(LSTM(256, input_shape=(X.shape[1], X.shape[2])))
model.add(Dropout(0.2))
model.add(Dense(y.shape[1], activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')
model.fit(X, y, epochs=20, batch_size=128, callbacks=callbacks_list)
是我的目标。可以是任何一种分类。
如果您有 1000 个具有 50 个历史值的事件序列实例,那么您的矩阵应该具有形状(1000, 50, 1)
如果你想添加结构化数据,你总是可以在 LSTM 层到密集连接层之后执行此操作。您可以将两个模型的输出连接起来然后相加。使用 Keras 的功能 API 更容易做到这一点,因为它更加灵活,并且允许您更直接地与张量进行交互。例如
from keras.models import Sequential, Model
from keras.layers import Concatenate, Dense, LSTM, Input, concatenate
from keras.optimizers import Adagrad
seq_input = Input(shape=(X.shape[1], X.shape[2]))
seq_model = LSTM(256)(seq_input)
seq_model = Dropout(0.2)(seq_model)
seq_model = Dense(100, activation = 'softmax')(seq_model)
metadata_input = Input(shape=(8, ))
metamodel = Dense(10, )(metadata_input)
merged = concatenate([seq_model, metamodel])
merged = Dense(64, activation = 'softmax')(merged)
merged = Dense(y.shape[1], activation = 'softmax')(merged)
model = Model(inputs=[seq_input, metadata_input], outputs=merged)
model.compile(loss='categorical_crossentropy', optimizer='adam')
这个例子如上。但是,我在 LSTM 层之后向模型添加了大小为 8 的元数据输入。这将有助于输出,因为它将来自这些特征的信息嵌入模型的最后几层。
如果我理解正确,您想知道是否可以输入一个元素是列表或矩阵的输入。它不是。
首先,这需要完全重写 Keras 中的所有激活和决策功能。以线性激活为例。Keras 期望它得到一堆数字并且可以计算加权和. 如果你向它传递几个列表,它应该怎么做?
编辑1:让我对这一点更清楚一点。神经网络的每个组件都建立在输入张量包含实数值的假设之上。数学上:输入必须是形式 相反,您想要的是包含任意长度的所有实向量的集合元素,: 你想输入 这需要你重新思考和重新定义一切:激活、损失函数、前向传播、反向传播……编辑结束 1。
第二,没有必要。假设您的输入包含一个列表和两个标量输入和. 然后你应该简单地通过 Keras标量输入. 请参阅 JahKnows 的答案以获得很好的实现。
如果您的输入列表具有不同的长度,事情会变得更加棘手......所以您的第一个观察包含一个包含五个项目的列表,您的第二个观察包含一个包含七个项目的列表,等等。在这种情况下,您需要使用padding 和 masking。
编辑 2:您在评论中说您对填充不满意,因为您的某些序列很长。让我指出一些替代方案:
- 使用可变长度的批处理填充。这对于将句子作为输入的网络很常见。测试集按句子长度排序并分成批次。每批都用尽可能少的垫子单独填充。
- 通过删除最旧的条目(对于时间序列)、最小的、最大的、最极端的或最不极端的条目或任何对您的数据有意义的条目来切断序列。
- 为您的网络提供序列的派生属性,而不是序列本身。我想得越多,这似乎就越是解决您的问题的最佳方法。分布完全由它的矩来描述。通过将足够多的时刻(和序列长度)输入你的网络,你可以保留几乎所有关于它的信息。还要注意,对于序列的排列,矩保持不变。由于您说您的序列不是时间序列,因此这可能是一个非常重要的属性(并且填充没有!)