每个目标具有多个输入序列的 RNN

RNN with multiple input sequences for each target

标准的 RNN 计算图如下所示(在我的例子中,回归到单个标量值 y

我想构建一个接受输入 m 序列 X_1...X_m(其中 m 和序列长度都不同)的网络,在每个序列上运行 RNN X_i 以获得表示向量 R_i,对表示进行平均,然后运行全连接网络来计算输出 y_hat。计算图应如下所示:

问题

这可以(最好)在 Keras 中实现吗?否则在 TensorFlow 中?如果有人能指出这个或类似东西的有效实现,我将不胜感激。

没有直接的 Keras 实现,因为 Keras 强制输入和输出层的批处理轴(样本维度,维度 0)固定(但 不是 所有层in-between) - 而你试图通过平均来折叠它。但是,有一个解决方法 - 见下文:

import tensorflow.keras.backend as K
from tensorflow.keras.layers import Input, Dense, GRU, Lambda
from tensorflow.keras.layers import Reshape, GlobalAveragePooling1D
from tensorflow.keras.models import Model
from tensorflow.keras.utils  import plot_model
import numpy as np

def make_model(batch_shape):
    ipt  = Input(batch_shape=batch_shape)
    x    = Lambda(lambda x: K.squeeze(x, 0))(ipt)
    x, s = GRU(4, return_state=True)(x) # s == last returned state 
    x    = Lambda(lambda x: K.expand_dims(x, 0))(s)
    x    = GlobalAveragePooling1D()(x)  # averages along axis1 (original axis2)
    x    = Dense(32, activation='relu')(x)
    out  = Dense(1,  activation='sigmoid')(x)

    model = Model(ipt, out)
    model.compile('adam', 'binary_crossentropy')
    return model

def make_data(batch_shape):
    return (np.random.randn(*batch_shape),
            np.random.randint(0, 2, (batch_shape[0], 1)))

m, timesteps = 16, 100
batch_shape = (1, m, timesteps, 1)

model = make_model(batch_shape)
model.summary()  # see model structure
plot_model(model, show_shapes=True)

x, y = make_data(batch_shape)
model.train_on_batch(x, y)

以上假定任务是二元分类,但您可以轻松地将其适应任何其他任务 - 主要任务通过将 m 个样本作为 1 来欺骗 Keras,其余层可以自由选择取 m 代替,因为 Keras 不强制执行 1 那里。

但是请注意,我不能保证它会按以下预期工作:

  1. Keras 将批处理轴上的所有条目视为 独立,而您的样本声称为 依赖
  2. 根据 (1),主要关注点是 反向传播:我不太确定梯度将如何随着所有维数改组的进行而流动。
  3. (1) 对于 有状态 RNNs 也是必然的,因为 Keras 构造了 batch_size 个独立状态,它们仍然可能像预期的那样表现是保持记忆,但仍然值得充分理解 - 参见

(2) 是 "elephant in the room",但除此之外,该模型符合您的确切描述。很有可能,如果你已经计划了前向传播并且所有暗淡都同意代码,它会按预期工作 - 否则,为了完整性检查,我建议打开另一个问题来验证你想要的梯度流他们按照上面的代码。


model.summary():

Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(1, 32, 100, 1)]         0         
_________________________________________________________________
lambda (Lambda)              (32, 100, 1)              0         
_________________________________________________________________
gru (GRU)                    [(32, 16), (32, 16)]      864       
_________________________________________________________________
lambda_1 (Lambda)            (1, 32, 16)               0         
_________________________________________________________________
global_average_pooling1d (Gl (1, 16)                   0         
_________________________________________________________________
dense (Dense)                (1, 8)                    136       
_________________________________________________________________
dense_1 (Dense)              (1, 1)                    9     

关于 LSTM:将 return 两个 最后状态,一个用于细胞状态,一个用于隐藏状态 - 见 source code;如果你要使用它,你应该明白这到底意味着什么。如果这样做,您将需要 concatenate:

from tensorflow.keras.layers import concatenate
# ...
x, s1, s2 = LSTM(return_state=True)(x)
x = concatenate([s1, s2], axis=-1)
# ...