我正在处理最大长度为50的填充序列。我有两种类型的序列数据:
1) 一个整数序列seq1,范围在1到100之间,对应于事件类型(例如[3,6,3,1,45,45….3])
2) 一个整数序列seq2,表示从seq1中的最后一个事件开始的时间,单位为分钟。因此,根据定义,最后一个元素为零。例如[100, 96, 96, 45, 44, 12,… 0]。seq1和seq2的长度相同,都是50。
我试图主要在事件/seq1数据上运行LSTM,但让时间/seq2强烈影响LSTM内部的遗忘门。这样做的原因是我希望LSTM倾向于对较旧的事件进行更严厉的惩罚,并且更有可能忘记它们。我考虑将遗忘权重乘以时间/seq2序列当前值的倒数。或者可能是(1/seq2_element + 1),以处理零分钟的情况。
我在Keras代码(LSTMCell类)中看到了需要更改的地方:
f = self.recurrent_activation(x_f + K.dot(h_tm1_f,self.recurrent_kernel_f))
所以我需要修改Keras的LSTM代码以接受多个输入。作为初步测试,在LSTMCell类中,我将call函数更改为如下形式:
def call(self, inputs, states, training=None): time_input = inputs[1] inputs = inputs[0]
这样它就可以处理以列表形式给出的两个输入。
当我尝试使用函数式API运行模型时:
# 输入1:事件类型序列# 将事件整数序列通过嵌入层转换为浮点向量,然后通过LSTMMain_input = Input(shape =(max_seq_length,), dtype = 'int32', name = 'main_input')x = Embedding(output_dim = embedding_length, input_dim = num_unique_event_symbols, input_length = max_seq_length, mask_zero=True)(main_input)## 输入2:时间向量 auxiliary_input = Input(shape=(max_seq_length,1), dtype='float32', name='aux_input')m = Masking(mask_value = 99999999.0)(auxiliary_input)lstm_out = LSTM(32)(x, time_vector = m)# 来自第一个输入的辅助损失auxiliary_output = Dense(1, activation='sigmoid', name='aux_output')(lstm_out)# 此处可以有任意数量的密集隐藏层x = Dense(64, activation='relu')(lstm_out)# 主输出节点main_output = Dense(1, activation='sigmoid', name='main_output')(x)## 编译并拟合模型model = Model(inputs=[main_input, auxiliary_input], outputs=[main_output, auxiliary_output])model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'], loss_weights=[1., 0.2])print(model.summary())np.random.seed(21)model.fit([train_X1, train_X2], [train_Y, train_Y], epochs=1, batch_size=200)
然而,我得到了以下错误:
An `initial_state` was passed that is not compatible with `cell.state_size`. Received `state_spec`=[InputSpec(shape=(None, 50, 1), ndim=3)]; however `cell.state_size` is (32, 32)
有什么建议吗?
回答:
你不能将输入列表传递给Keras中的默认循环层。输入规范是固定的,并且循环代码的实现也是基于单一张量输入的,这一点在文档中也有说明,即它不会神奇地迭代两个相同时间步的输入并将其传递给单元。这部分是因为迭代是如何优化的,以及如果网络是展开的等所做的假设。
如果你喜欢两个输入,你可以将constants
(文档)传递给单元,这样张量将保持不变。这主要是为了将来实现注意力模型。所以一个输入将迭代时间步,而另一个不会。如果你真的希望两个输入像Python中的zip()
一样被迭代,你将不得不实现一个自定义层。