虽然我对机器学习并不陌生,但我对神经网络,尤其是如何实现它们(在Keras/Python中)仍然相对新手。前馈和卷积架构相当简单明了,但我对RNN遇到了麻烦。
我的X
数据由长度可变的序列组成,每个序列中的数据点有26个特征。我的y
数据虽然长度可变,但每对X
和y
的长度相同,例如:
X_train[0].shape: (226,26)y_train[0].shape: (226,)X_train[1].shape: (314,26)y_train[1].shape: (314,)X_train[2].shape: (189,26)y_train[2].shape: (189,)
我的目标是将序列中的每个项目分类到39个类别中的一个。
从阅读示例代码中,我目前能理解的是,我们会做类似以下的事情:
encoder_inputs = Input(shape=(None, 26))encoder = GRU(256, return_state=True)encoder_outputs, state_h = encoder(encoder_inputs)decoder_inputs = Input(shape=(None, 39))decoder_gru= GRU(256, return_sequences=True)decoder_outputs, _ = decoder_gru(decoder_inputs, initial_state=state_h)decoder_dense = Dense(39, activation='softmax')decoder_outputs = decoder_dense(decoder_outputs)model = Model([encoder_inputs, decoder_inputs], decoder_outputs)model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adadelta(), metrics=['accuracy'])
这对我来说是有道理的,因为每个序列的长度都不同。因此,通过一个循环遍历所有序列,我们在第一个GRU层的输入形状中使用None
,因为我们不确定序列的长度会是什么,然后返回编码器的隐藏状态state_h
。第二个GRU层返回序列,并且初始状态是编码器返回的状态,然后我们将输出传递给最终的softmax激活层。
显然这里有些问题,因为我得到了以下错误:
decoder_outputs, _ = decoder_gru(decoder_inputs, initial_state=state_h)File "/usr/local/lib/python3.6/dist- packages/tensorflow/python/framework/ops.py", line 458, in __iter__"Tensor objects are only iterable when eager execution is "TypeError: Tensor objects are only iterable when eager execution is enabled. To iterate over this tensor use tf.map_fn.
这个链接指向了一个提议的解决方案,但我不知道为什么你会为网络中你拥有的层数添加编码器状态到一个元组中。
我真的希望能得到帮助,成功编写这个RNN来完成这个任务,同时也希望能理解。我对RNN非常感兴趣,希望能更深入地了解它们,以便将它们应用到其他问题上。
作为一个额外的说明,每个序列的形状为(sequence_length, 26)
,但我将维度扩展为X
的(1, sequence_length, 26)
和y
的(1, sequence_length)
,然后在循环中传递它们进行拟合,decoder_target_data
比当前输入提前一步:
for idx in range(X_train.shape[0]): X_train_s = np.expand_dims(X_train[idx], axis=0) y_train_s = np.expand_dims(y_train[idx], axis=0) y_train_s1 = np.expand_dims(y_train[idx+1], axis=0) encoder_input_data = X_train_s decoder_input_data = y_train_s decoder_target_data = y_train_s1 model.fit([encoder_input_data, decoder_input_data], decoder_target_data, epochs=50, validation_split=0.2)
对于我编写的其他网络(前馈和CNN),我通过在Keras的Sequential类上添加层来指定模型。由于RNN的固有复杂性,我认为像上面那样使用Keras的Input类并检索隐藏状态(以及LSTM的单元状态)等是合乎逻辑的,但我还看到它们是使用Keras的Sequential类构建的。虽然这些是多对一类型的任务,但我对你如何以那种方式编写也感兴趣。
回答:
问题在于decoder_gru
层不返回其状态,因此你不应该使用_
作为状态的返回值(即只需删除, _
):
decoder_outputs = decoder_gru(decoder_inputs, initial_state=state_h)
由于输入和输出的长度相同,并且输入和输出的元素之间有一对一的映射,你可以选择以这种方式构建模型:
inputs = Input(shape=(None, 26))gru = GRU(64, return_sequences=True)(inputs)outputs = Dense(39, activation='softmax')(gru)model = Model(inputs, outputs)
现在你可以通过在彼此之上堆叠多个GRU层来使这个模型更加复杂(即增加其容量):
inputs = Input(shape=(None, 26))gru = GRU(256, return_sequences=True)(inputs)gru = GRU(128, return_sequences=True)(gru)gru = GRU(64, return_sequences=True)(gru)outputs = Dense(39, activation='softmax')(gru)model = Model(inputs, outputs)
此外,你可以使用LSTM层代替GRU层,LSTM层具有更高的表示能力(当然,这可能会增加计算成本)。并且不要忘记,当你增加模型的容量时,你也增加了过拟合的风险。因此,你必须牢记这一点,并考虑防止过拟合的解决方案(例如,添加正则化)。
附注:如果你有可用的GPU,那么你可以使用CuDNNGRU
(或CuDNNLSTM
)层,它已针对GPU进行了优化,因此与GRU
相比运行速度要快得多。