Media o máxima agrupación con soporte de enmascaramiento en Keras

... 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')) .... 

Necesito poder tomar la media o la máxima de los vectores para todos los pasos de tiempo en una muestra después de la capa LSTM antes de dar esta media o máxima a la capa densa en Keras.

Creo que timedistributedmerge fue capaz de hacer esto pero estaba en desuso. Usando return_sequences=True , puedo obtener los vectores para todos los pasos de tiempo en una muestra después de la capa LSTM. Sin embargo, GlobalAveragePooling1D() no es compatible con el enmascaramiento y considera todos los pasos de tiempo, mientras que solo necesito los pasos de tiempo no enmascarados.

Vi publicaciones que recomiendan la capa Lambda , pero tampoco tienen en cuenta el enmascaramiento. Cualquier ayuda sería apreciada.

Para hacer que los valores enmascarados en x sean iguales a cero, puede hacer esto:

 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] 

La respuesta de Jacoxu es correcta. Pero si está utilizando un backend tensorflow para keras, el tipo Tensor no admite la función dimshuffle, intente esto en su lugar.

 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) 

Dado que la agrupación promedio solo hace una media en un eje, solo necesita corregir el número de elementos en la media, ya que el enmascaramiento de pérdida se maneja al final, no aquí. Probablemente puedes hacer esto con algo como esto:

 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) 

Así es como lo hice en Keras 2 (tomando prestado de todas las respuestas y arreglando las dimensiones):

 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])