Keras中的带有掩码支持的平均池化或最大池化

8
...
print('Build model...')
model = Sequential()
model.add(Embedding(max_features, 128))
model.add(LSTM(size, return_sequences=True, dropout_W=0.2 dropout_U=0.2)) 
model.add(GlobalAveragePooling1D())
model.add(Dense(1))
model.add(Activation('sigmoid'))
....

我需要能够在LSTM层后,对样本中所有时间步长的向量进行平均值或最大值,并将此平均或最大向量传递给Keras中的密集层。我认为timedistributedmerge可以做到这一点,但它已被弃用。使用return_sequences=True,我可以获得LSTM层后样本中所有时间步长的向量。然而,GlobalAveragePooling1D()不兼容掩码,并且它考虑所有时间步长,而我仅需要非掩码时间步长。我看到有人推荐Lambda层,但这些也不考虑掩码。任何帮助将不胜感激。
4个回答

4

Jacoxu的回答是正确的。但是,如果你正在使用keras的tensorflow后端,Tensor类型不支持dimshuffle函数,请改用以下方式。

def call(self, x, mask=None):
    if mask is not None:
        # mask (batch, time)
        mask = K.cast(mask, K.floatx())
        # mask (batch, x_dim, time)
        mask = K.repeat(mask, x.shape[-1])
        # mask (batch, time, x_dim)
        mask = tf.transpose(mask, [0,2,1])
        x = x * mask
    return K.sum(x, axis=1) / K.sum(mask, axis=1)

3
这是我在Keras 2上的操作方法(借鉴了所有答案并修复了维度问题):
class MeanPool(Layer):
  def __init__(self, **kwargs):
      self.supports_masking = True
      super(MeanPool, self).__init__(**kwargs)

  def compute_mask(self, input, input_mask=None):
      # do not pass the mask to the next layers
      return None

  def call(self, x, mask=None):
      if mask is not None:
          # mask (batch, time)
          mask = K.cast(mask, K.floatx())
          # mask (batch, x_dim, time)
          mask = K.repeat(mask, x.shape[-1])
          # mask (batch, time, x_dim)
          mask = tf.transpose(mask, [0,2,1])
          x = x * mask
      return K.sum(x, axis=1) / K.sum(mask, axis=1)

  def compute_output_shape(self, input_shape):
      # remove temporal dimension
      return (input_shape[0], input_shape[2])

3

由于平均池化只对一个轴进行平均,因此您只需要校正平均值中的元素数量,因为损失屏蔽是在最后处理的,而不是在这里。您可以使用以下类似代码完成此操作:

class GlobalAveragePooling1DMasked(GlobalAveragePooling1D):
    def call(self, x, mask=None):
        if mask != None:
            return K.sum(x, axis=1) / K.sum(mask, axis=1)
        else:
            return super().call(x)

2
请注意,在Keras中,你不能确定x中的掩码值是否等于零!因此,这个实现将会得出错误的结果。 - pir
口罩本身由“Masking”和“Embedding”层引入,是二进制的。当然,您始终可以拥有实现“compute_mask”的不同图层,但就我所见,在keras本身中并没有发生这种情况。 - nemo
是的,掩码是二进制的。但这不是我要说的 :) - pir
1
但这是我在上面的例子中所做的唯一假设。抱歉,我不明白你的意思。编辑:算了,是矩阵x的求和操作假设掩码值为零。你是对的。这可能可以通过使用mask索引x来修复,但我必须先测试一下。 - nemo

3
为了使x中的掩码值为零,您可以执行以下操作:
class MeanPool(Layer):
def __init__(self, **kwargs):
    self.supports_masking = True
    super(MeanPool, self).__init__(**kwargs)

def compute_mask(self, input, input_mask=None):
    # do not pass the mask to the next layers
    return None

def call(self, x, mask=None):
    if mask is not None:
        # mask (batch, time)
        mask = K.cast(mask, K.floatx())
        # mask (batch, time, 'x')
        mask = mask.dimshuffle(0, 1, 'x')
        # to make the masked values in x be equal to zero
        x = x * mask
    return K.sum(x, axis=1) / K.sum(mask, axis=1)

def get_output_shape_for(self, input_shape):
    # remove temporal dimension
    return input_shape[0], input_shape[2]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接