如何将多个值传递给keras联接模型

时间:2020-05-16 06:14:24

标签: python python-3.x tensorflow keras deep-learning

我有两个预先训练的模型。假设它们是model_A和model_B。在此,model_A是CNN + LSTM网络,而model_B是GCN。

Model_A:

enter image description here

Model_A需要1个输入,如下所示。 X_in = Input(shape=(None, 150), name='X_in', dtype=int32)

Model_B:

enter image description here

Model_B需要3个输入,如下所示。 X_in = Input(shape=(X_train_B[0].shape[-1], ), name='X_in', dtype=tf.float64) A_in = Input(shape=(None,), sparse=True, dtype=tf.float64) I_in = Input(shape=(), name='segment_ids_in', dtype=tf.int32)

我需要通过获取最后一层之前的输出来合并这两个模型。下面是我定义新模型的方式。

model_A = load_model('model_A.h5')
model_A = Model(inputs=model_A.inputs, outputs=model_A.layers[-2].output)

model_B = load_model('model_B.h5', custom_objects={'GraphConvSkip': GraphConvSkip, 'MinCutPool': MinCutPool,
                                                'GlobalAvgPool': GlobalAvgPool})
model_B = Model(inputs=model_B.inputs, outputs=model_B.layers[-2].output) 

def final_model():  
    X_in = Input(shape=(X_train_B[0].shape[-1] ), name='X_in', dtype=tf.float64)
    A_in = Input(shape=(None,), sparse=True, dtype=tf.float64)
    I_in = Input(shape=(), name='segment_ids_in', dtype=tf.int32)
    merged = Concatenate(axis=1)([X_in, A_in, I_in])

    concat = concatenate([model_A.output, model_B.output], axis=-1)
    concat = Dense(1, activation='sigmoid')(concat)

    model = Model(inputs=[model_A.input, merged], outputs=concat)
    adam = tf.keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0)
    model.compile(optimizer=adam, loss='binary_crossentropy', metrics=['acc'])
    return model

model = final_model()
model.fit([X_train_A,[X_train_B, A_train_B, I_]], [y_train_A], verbose=1)

执行此代码后,出现以下错误。

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-54-8c7e818acc11> in <module>
----> 1 model = final_model()
      2 
      3 print('Fitting model')
      4 batches = batch_iterator([A_train_B, X_train_B, y_train_B, X_train_A, y_train_A], batch_size=1, epochs=2)
      5 for b in batches:

<ipython-input-53-5c9278e5efb8> in final_model()
      3     A_in = Input(shape=(None,), sparse=True, dtype=tf.float64)
      4     I_in = Input(shape=(), name='segment_ids_in', dtype=tf.int32)
----> 5     merged = Concatenate(axis=1)([X_in, A_in, I_in])
      6 
      7     concat = concatenate([model_A.output, model_B.output], axis=-1)

~/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/base_layer.py in __call__(self, inputs, *args, **kwargs)
    746           # Build layer if applicable (if the `build` method has been
    747           # overridden).
--> 748           self._maybe_build(inputs)
    749           cast_inputs = self._maybe_cast_inputs(inputs)
    750 

~/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/base_layer.py in _maybe_build(self, inputs)
   2114         # operations.
   2115         with tf_utils.maybe_init_scope(self):
-> 2116           self.build(input_shapes)
   2117       # We must set self.built since user defined build functions are not
   2118       # constrained to set self.built.

~/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/utils/tf_utils.py in wrapper(instance, input_shape)
    304     if input_shape is not None:
    305       input_shape = convert_shapes(input_shape, to_tuples=True)
--> 306     output_shape = fn(instance, input_shape)
    307     # Return shapes from `fn` as TensorShapes.
    308     if output_shape is not None:

~/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/layers/merge.py in build(self, input_shape)
    380     shape_set = set()
    381     for i in range(len(reduced_inputs_shapes)):
--> 382       del reduced_inputs_shapes[i][self.axis]
    383       shape_set.add(tuple(reduced_inputs_shapes[i]))
    384 

IndexError: list assignment index out of range


有人可以帮助我解决如何将这些输入值传递给两个不同的模型吗?

----编辑01 ----

根据Andrea的建议修改代码后,出现以下错误。我也尝试使用np.array()传递输入,但是仍然遇到相同的错误。您能检查我在哪里弄错了吗?

此外,这是我要传递给模型B的值。由于它是图形,所以X_是N*d特征向量,A_是N*N邻接矩阵,I_是segment_ids通过致电get('XAI')

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-71-22f819a7a9cd> in <module>
     18     return model
     19 
---> 20 model = final_model()
     21 print('Fitting model')
     22 batches = batch_iterator([A_train_B, X_train_B, y_train_B, X_train_A, y_train_A], batch_size=1, epochs=2)

<ipython-input-71-22f819a7a9cd> in final_model()
      7 
      8 def final_model():
----> 9     concat = Concatenate(-1)([model_A.outputs, model_B.outputs]) # merge outputs
     10     concat = Dense(1, activation='sigmoid')(concat)
     11 

~/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/base_layer.py in __call__(self, inputs, *args, **kwargs)
    771                     not base_layer_utils.is_in_eager_or_tf_function()):
    772                   with auto_control_deps.AutomaticControlDependencies() as acd:
--> 773                     outputs = call_fn(cast_inputs, *args, **kwargs)
    774                     # Wrap Tensors in `outputs` in `tf.identity` to avoid
    775                     # circular dependencies.

~/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/layers/merge.py in call(self, inputs)
    179         return y
    180     else:
--> 181       return self._merge_function(inputs)
    182 
    183   @tf_utils.shape_type_conversion

~/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/layers/merge.py in _merge_function(self, inputs)
    402 
    403   def _merge_function(self, inputs):
--> 404     return K.concatenate(inputs, axis=self.axis)
    405 
    406   @tf_utils.shape_type_conversion

~/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/backend.py in concatenate(tensors, axis)
   2671   """
   2672   if axis < 0:
-> 2673     rank = ndim(tensors[0])
   2674     if rank:
   2675       axis %= rank

~/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/backend.py in ndim(x)
   1200 
   1201   """
-> 1202   dims = x.shape._dims
   1203   if dims is not None:
   1204     return len(dims)

AttributeError: 'list' object has no attribute 'shape'


2 个答案:

答案 0 :(得分:0)

问题出在您的I_in = Input(shape=(), name='segment_ids_in', dtype=tf.int32)上,此输入层的形状为(None,)(等级为1),而其他输入的等级为2,则转换为(None, 2)之类的形状。您可以在模型摘要中看到这一点。您正在尝试通过第二个维度(axis=1)进行连接,但此操作无法完成,因为I_in没有第二个维度,只有批量维度。

尝试将I_in扩展为第二个维度,例如:

def final_model():  
    X_in = Input(shape=(X_train_B[0].shape[-1] ), name='X_in', dtype=tf.float64)
    A_in = Input(shape=(None,), sparse=True, dtype=tf.float64)
    I_in = Input(shape=(), name='segment_ids_in', dtype=tf.int32)
    I_in = Lambda(lambda x: tf.expand_dims(x, -1))(I_in) 
    merged = Concatenate(axis=1)([X_in, A_in, I_in])

    concat = concatenate([model_A.output, model_B.output], axis=-1)
    concat = Dense(1, activation='sigmoid')(concat)

    model = Model(inputs=[model_A.input, merged], outputs=concat)
    adam = tf.keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0)
    model.compile(optimizer=adam, loss='binary_crossentropy', metrics=['acc'])
    return model

经OP澄清后进行编辑

您没有在代码中通过模型传递输入。如果我理解正确,您有4个输入,想要将第一个输入通过model_A,其余输入通过model_B。最后,您要输出连接的第二层到最后一层的输出。为此,您不需要连接model_B的输入。 由于我没有完整的代码,因此无法尝试,但是我认为这应该可行:

model_A = load_model('model_A.h5')

model_B = load_model('model_B.h5', custom_objects={'GraphConvSkip': GraphConvSkip, 'MinCutPool': MinCutPool,
                                                'GlobalAvgPool': GlobalAvgPool})

def final_model():
    concat = Concatenate(-1)([model_A.get_layer(-2).output, model_B.get_layer(-2).output]) # merge outputs
    concat = Dense(1, activation='sigmoid')(concat)

    model = Model(inputs=[model_A.inputs, model_B.inputs], # the inputs do not change!
                  outputs=concat)
    adam = tf.keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, 
                                    epsilon=1e-08, decay=0.0)
    model.compile(optimizer=adam, loss='binary_crossentropy', metrics= 
                 ['acc'])
    return model

model = final_model()
model.fit([X_train_A, X_train_B, A_train_B, I_], # notice how the inner list disappeared!
          [y_train_A], verbose=1) 

请确保您的y_train_A的形状(BATCH_NUM,64),因为您要通过最后一个轴(第二到最后一层输出)连接两个(BATCH_NUM,32)张量。

答案 1 :(得分:0)

我找到了以下针对我问题的解决方案。

# Constructing the model
concat = Concatenate(-1)([model_A.output, model_B.output]) # merge outputs
concat = Dense(2, activation='sigmoid', name='output')(concat)

model = Model(inputs=[model_A.inputs, model_B.inputs], outputs=concat)
model.compile(optimizer='adam',  
              loss='binary_crossentropy')

# Training setup
opt = tf.keras.optimizers.Adam(learning_rate=learning_rate)
loss_fn = model.loss_functions[0]
acc_fn = lambda x, y: K.mean(tf.keras.metrics.categorical_accuracy(x, y))

# Training function
@tf.function(experimental_relax_shapes=True)
def train_step(inputs, targets):
    with tf.GradientTape() as tape:
        predictions = model(inputs, training=True)
        loss = loss_fn(targets, predictions)
    gradients = tape.gradient(loss, model.trainable_variables)
    opt.apply_gradients(zip(gradients, model.trainable_variables))
    return loss, acc_fn(targets, predictions)

# Training the model
batches = batch_iterator([A_train_B, X_train_B, y_train_B, X_train_A], batch_size=batch_size, epochs=epochs)
for b in batches:
    X_, A_, I_ = Batch(b[0], b[1]).get('XAI')
    A_ = sp_matrix_to_sp_tensor(A_)
    y_ = b[2]
    X_A_ = b[3]
    outs = train_step([X_A_, X_, A_, I_], y_)