在 Keras 自定义层中连接多个形状为 (None, m) 的 LSTM 输出

Posted

技术标签:

【中文标题】在 Keras 自定义层中连接多个形状为 (None, m) 的 LSTM 输出【英文标题】:Concatenate multiple LSTM outputs of shape (None, m) in Keras custom layer 【发布时间】:2019-12-03 01:48:53 【问题描述】:

我正在尝试使用 Keras 自定义图层模板创建自定义损失函数。我无法执行类似于以下论文中提到的计算(第 4 页,等式 5):https://web.stanford.edu/class/archive/cs/cs224n/cs224n.1174/reports/2748045.pdf

class CustomLoss(Layer):
    def __init__(self, **kwargs):
        self.result = None
        super(CustomLoss, self).__init__(**kwargs)

    def build(self, input_shape):
#         shape_ = 
        self.weight = self.add_weight(name='trainable_weight', shape=(input_shape[1], 4), initializer='glorot_uniform',
                                      trainable=True)
        self.bias = self.add_weight(name='trainable_bias', shape=(4, ), initializer='zeros', trainable=True)
        super(CustomLoss, self).build(input_shape)

    def call(self, input_vec, **kwargs):
        v1 = input_vec[0] # This is of shape (None, m)
        v2 = input_vec[1] # This is of shape (None, m)
        v3 = K.square(input_vec[0] - input_vec[1])
        v4 = K.dot(input_vec[0], input_vec[1])
        vec = concatenate([v1, v2, v3, v4])
        self.result = keras.layer.Softmax(keras.layers.ReLU(vec) * self.weight + self.bias)
        return self.result

    def compute_output_shape(self, input_shape):
        return K.int_shape(self.result)

我收到以下错误:

TypeError: '>' not supported between instances of 'tuple' and 'float'

编辑1

num = 75
EMB_DIM = 300
SEN_LEN = 20

def base_network(_input):
    embd = embedding_layer(V_SIZE, EMB_DIM, SEN_LEN, embedding_matrix(_input)
    x = Bidirectional(lstm(num), merge_mode='concat')(embd)
    x = Bidirectional(lstm(num), merge_mode='concat')(x)
    x = Dropout(0.2)(x)
    y = TimeDistributed(Dense(1, activation='tanh'))(x)
    y = Flatten()(y)
    y = Activation('softmax')(y)
    y = RepeatVector(num * 2)(y)
    y = Permute([2, 1]) (y)
    z = multiply([x, y])
    z = Lambda(lambda xin: K.sum(xin, axis=1))(z)
    return z
inp1 = Input(shape=(SEN_LEN,), dtype='float32')
inp2 = Input(shape=(SEN_LEN,), dtype='float32')

s1 = base_network(inp1)
s2 = base_network(inp1)

sim_score = CustomLoss()([s1, s2])
output = concatenate([s1, s2 , sim_score])
d1 = Dense(2)(output)
sim = Dense(1, activation='sigmoid')(d1)
model = Model(inputs=[inp1, inp2], outputs=[sim])
model.compile(loss='mean_squared_error', optimizer=RMSprop())

编辑 2

TypeError                                 Traceback (most recent call last)
<ipython-input-97-fa4c18ab9e4e> in <module>
      7 s1 = base_network(input_2)
      8 
----> 9 sim_score = CustomLoss()([s1, s2])
     10 output = concatenate([s1, s2 , sim_score])
     11 d1 = Dense(2)(output)

~\.julia\conda\3\lib\site-packages\keras\engine\base_layer.py in __call__(self, inputs, **kwargs)
    429                                          'You can build it manually via: '
    430                                          '`layer.build(batch_input_shape)`')
--> 431                 self.build(unpack_singleton(input_shapes))
    432                 self.built = True
    433 

<ipython-input-96-2f2fb52e16d0> in build(self, input_shape)
    117 #         shape_ =
    118         self.weight = self.add_weight(name='trainable_weight', shape=(input_shape[1], 4, ), initializer='glorot_uniform',
--> 119                                       trainable=True)
    120         self.bias = self.add_weight(name='trainable_bias', shape=(4, ), initializer='zeros', trainable=True)
    121         super(CustomLoss, self).build(input_shape)

~\.julia\conda\3\lib\site-packages\keras\legacy\interfaces.py in wrapper(*args, **kwargs)
     89                 warnings.warn('Update your `' + object_name + '` call to the ' +
     90                               'Keras 2 API: ' + signature, stacklevel=2)
---> 91             return func(*args, **kwargs)
     92         wrapper._original_function = func
     93         return wrapper

~\.julia\conda\3\lib\site-packages\keras\engine\base_layer.py in add_weight(self, name, shape, dtype, initializer, regularizer, trainable, constraint)
    247         if dtype is None:
    248             dtype = K.floatx()
--> 249         weight = K.variable(initializer(shape),
    250                             dtype=dtype,
    251                             name=name,

~\.julia\conda\3\lib\site-packages\keras\initializers.py in __call__(self, shape, dtype)
    203         scale = self.scale
    204         if self.mode == 'fan_in':
--> 205             scale /= max(1., fan_in)
    206         elif self.mode == 'fan_out':
    207             scale /= max(1., fan_out)

TypeError: '>' not supported between instances of 'tuple' and 'float'

编辑 3 新错误:

---------------------------------------------------------------------------
InvalidArgumentError                      Traceback (most recent call last)
~\.julia\conda\3\lib\site-packages\tensorflow\python\framework\ops.py in _create_c_op(graph, node_def, inputs, control_inputs)
   1627   try:
-> 1628     c_op = c_api.TF_FinishOperation(op_desc)
   1629   except errors.InvalidArgumentError as e:

InvalidArgumentError: Dimensions must be equal, but are 2000 and 500 for 'custom_loss_1/MatMul' (op: 'MatMul') with input shapes: [?,2000], [500,4].

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-99-fa4c18ab9e4e> in <module>
      7 s1 = base_network(input_2)
      8 
----> 9 sim_score = CustomLoss()([s1, s2])
     10 output = concatenate([s1, s2 , sim_score])
     11 d1 = Dense(2)(output)

~\.julia\conda\3\lib\site-packages\keras\engine\base_layer.py in __call__(self, inputs, **kwargs)
    455             # Actually call the layer,
    456             # collecting output(s), mask(s), and shape(s).
--> 457             output = self.call(inputs, **kwargs)
    458             output_mask = self.compute_mask(inputs, previous_mask)
    459 

<ipython-input-98-23434db31b00> in call(self, x, **kwargs)
    127         vec = concatenate([v1, v2, v3, v4])
    128 #         vec = K.Flatten(vec)
--> 129         self.result = keras.layer.Softmax(keras.layers.ReLU(vec) * self.weight + self.bias)
    130         return self.result
    131 

~\.julia\conda\3\lib\site-packages\keras\backend\tensorflow_backend.py in dot(x, y)
   1083         out = tf.sparse_tensor_dense_matmul(x, y)
   1084     else:
-> 1085         out = tf.matmul(x, y)
   1086     return out
   1087 

~\.julia\conda\3\lib\site-packages\tensorflow\python\ops\math_ops.py in matmul(a, b, transpose_a, transpose_b, adjoint_a, adjoint_b, a_is_sparse, b_is_sparse, name)
   2055     else:
   2056       return gen_math_ops.mat_mul(
-> 2057           a, b, transpose_a=transpose_a, transpose_b=transpose_b, name=name)
   2058 
   2059 

~\.julia\conda\3\lib\site-packages\tensorflow\python\ops\gen_math_ops.py in mat_mul(a, b, transpose_a, transpose_b, name)
   4855     _, _, _op = _op_def_lib._apply_op_helper(
   4856         "MatMul", a=a, b=b, transpose_a=transpose_a, transpose_b=transpose_b,
-> 4857         name=name)
   4858     _result = _op.outputs[:]
   4859     _inputs_flat = _op.inputs

~\.julia\conda\3\lib\site-packages\tensorflow\python\framework\op_def_library.py in _apply_op_helper(self, op_type_name, name, **keywords)
    785         op = g.create_op(op_type_name, inputs, output_types, name=scope,
    786                          input_types=input_types, attrs=attr_protos,
--> 787                          op_def=op_def)
    788       return output_structure, op_def.is_stateful, op
    789 

~\.julia\conda\3\lib\site-packages\tensorflow\python\util\deprecation.py in new_func(*args, **kwargs)
    486                 'in a future version' if date is None else ('after %s' % date),
    487                 instructions)
--> 488       return func(*args, **kwargs)
    489     return tf_decorator.make_decorator(func, new_func, 'deprecated',
    490                                        _add_deprecated_arg_notice_to_docstring(

~\.julia\conda\3\lib\site-packages\tensorflow\python\framework\ops.py in create_op(***failed resolving arguments***)
   3272           input_types=input_types,
   3273           original_op=self._default_original_op,
-> 3274           op_def=op_def)
   3275       self._create_op_helper(ret, compute_device=compute_device)
   3276     return ret

~\.julia\conda\3\lib\site-packages\tensorflow\python\framework\ops.py in __init__(self, node_def, g, inputs, output_types, control_inputs, input_types, original_op, op_def)
   1790           op_def, inputs, node_def.attr)
   1791       self._c_op = _create_c_op(self._graph, node_def, grouped_inputs,
-> 1792                                 control_input_ops)
   1793 
   1794     # Initialize self._outputs.

~\.julia\conda\3\lib\site-packages\tensorflow\python\framework\ops.py in _create_c_op(graph, node_def, inputs, control_inputs)
   1629   except errors.InvalidArgumentError as e:
   1630     # Convert to ValueError for backwards compatibility.
-> 1631     raise ValueError(str(e))
   1632 
   1633   return c_op

ValueError: Dimensions must be equal, but are 2000 and 500 for 'custom_loss_1/MatMul' (op: 'MatMul') with input shapes: [?,2000], [500,4].

【问题讨论】:

您确定在调用compute_output_shape 之前有结果吗?我几乎可以肯定这就是问题所在。 @DanielMöller,我的意图不是改变输出张量的形状。我使用类似的类来计算曼哈顿距离和欧几里得距离。我没有改变那里的形状。我在这里可能错了。 def call(self, x, **kwargs): self.result = K.sum(K.abs(x[0] - x[1]), axis=1, keepdims=True) return self.result def compute_output_shape(self, input_shape): return K.int_shape(self.result) 【参考方案1】:

这一行:

self.result = keras.layer.Softmax(keras.layers.ReLU(vec) * self.weight + self.bias)

您不能使用这样的图层。而是使用等效的后端函数:

self.result = K.softmax(K.bias_add(K.dot(K.relu(vec), self.weight), self.bias, data_format='channels_last'))

注意:我没有阅读论文,所以还要确保你想在连接结果上应用relu,而不是在带有权重和添加偏差的点积之后。

更新:还有另一个问题是由您创建的权重形状引起的。由于您的层有两个输入张量,因此 build 方法的 input_shape 参数将是两个元组的列表,即对应于每个输入张量的一个输入形状。因此,在创建权重时,不要写shape=(input_shape[1], 4),而是需要写shape=(input_shape[0][1], 4)

更新 2vec 的形状为 (?, 2000),但 weight 的形状为 (500, 4),因此它们不能相乘。您可能想相应地调整weight 的形状:改用shape=(input_shape[0][1] * 4, 4)

【讨论】:

我尝试了您的解决方案,但没有奏效。我收到与以前相同的错误。我相信,我在concatenate 操作期间正在做一些工作 @Lufy 您能否编辑您的问题并展示您如何在模型中使用该层? @Lufy 还有一件事:完整的堆栈跟踪。 添加了完整的堆栈跟踪 @Lufy 我刚刚更新了我的答案。请看一看。

以上是关于在 Keras 自定义层中连接多个形状为 (None, m) 的 LSTM 输出的主要内容,如果未能解决你的问题,请参考以下文章

如何在Tensorflow 2.x Keras自定义层中使用多个输入?

如何为层中的每个节点为 Keras relu 函数分配自定义 alpha?

如何在 Keras 中的预训练 InceptionResNetV2 模型的不同层中找到激活的形状 - Tensorflow 2.0

如何定义具有动态形状的新张量以支持自定义层中的批处理

Keras 的 LSTM 层中的 4D 输入

来自自定义 keras 层的 tflite 转换器