Home Ciencia y Tecnología Implementación de enmascaramiento de entrada y relleno en modelos TensorFlow Keras

Implementación de enmascaramiento de entrada y relleno en modelos TensorFlow Keras

35
0

Descripción basic del contenido

  • Configuración
  • Introducción
  • Datos de secuencia de relleno
  • Enmascaramiento
  • Capas generadoras de máscaras: incrustación y enmascaramiento
  • Propagación de máscara en la API funcional y la API secuencial
  • Pasando los tensores de máscara directamente a las capas
  • Soporte de enmascaramiento en sus capas personalizadas
  • Optando para enmascarar la propagación en capas compatibles
  • Escribir capas que necesitan información de máscaras
  • Resumen

Configuración

import numpy as np
import tensorflow as tf
import keras
from keras import layers

Introducción

Enmascaramiento es una forma de indicar capas de procesamiento de secuencia que faltan ciertos instances en una entrada y, por lo tanto, deben omitirse al procesar los datos.

Relleno es una forma especial de enmascaramiento donde los pasos enmascarados están al comienzo o al closing de una secuencia. El relleno proviene de la necesidad de codificar los datos de secuencia en lotes contiguos: para hacer que todas las secuencias en un lote se ajusten a una longitud estándar dada, es necesario rellenar o truncar algunas secuencias.

Echemos un vistazo de cerca.

Datos de secuencia de relleno

Al procesar datos de secuencia, es muy común que las muestras individuales tengan diferentes longitudes. Considere el siguiente ejemplo (texto tokenizado como palabras):

[
  ["Hello", "world", "!"],
  ["How", "are", "you", "doing", "today"],
  ["The", "weather", "will", "be", "nice", "tomorrow"],
]

Después de la búsqueda de vocabulario, los datos pueden ser vectorizados como enteros, por ejemplo:

[
  [71, 1331, 4231]
  [73, 8, 3215, 55, 927],
  [83, 91, 1, 645, 1253, 927],
]

Los datos son una lista anidada donde las muestras individuales tienen la longitud 3, 5 y 6, respectivamente. Dado que los datos de entrada para un modelo de aprendizaje profundo deben ser un tensor único (de forma, por ejemplo, (batch_size, 6, vocab_size) En este caso), las muestras que son más cortas que el elemento más largo deben acolcharse con algún valor del marcador de posición (alternativamente, una también podría truncar muestras largas antes de acolchar muestras cortas).

Keras proporciona una función de utilidad para truncar y rellenar las listas de Python a una longitud común: tf.keras.utils.pad_sequences.

raw_inputs = [
    [711, 632, 71],
    [73, 8, 3215, 55, 927],
    [83, 91, 1, 645, 1253, 927],
]

# By default, it will pad utilizing 0s; it's configurable through the
# "worth" parameter.
# Notice that you possibly can use "pre" padding (firstly) or
# "submit" padding (on the finish).
# We advocate utilizing "submit" padding when working with RNN layers
# (so as to have the ability to use the
# CuDNN implementation of the layers).
padded_inputs = tf.keras.utils.pad_sequences(raw_inputs, padding="submit")
print(padded_inputs)
[[ 711  632   71    0    0    0]
 [  73    8 3215   55  927    0]
 [  83   91    1  645 1253  927]]

Enmascaramiento

Ahora que todas las muestras tienen una longitud uniforme, el modelo debe estar informado de que alguna parte de los datos en realidad está rellenada y debe ignorarse. Ese mecanismo es enmascaramiento.

Hay tres formas de introducir máscaras de entrada en los modelos Keras:

  • Agregar un keras.layers.Masking capa.
  • Configurar un keras.layers.Embedding cubrir con mask_zero=True.
  • Pasar masks argumento manualmente al llamar a capas que respaldan este argumento (por ejemplo, capas RNN).

Capas generadoras de máscaras: Embedding y Masking

Debajo del capó, estas capas crearán un tensor de máscara (tensor second con forma (batch, sequence_length)), y adjuntarlo a la salida tensor devuelta por el Masking o Embedding capa.

embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
masked_output = embedding(padded_inputs)

print(masked_output._keras_mask)

masking_layer = layers.Masking()
# Simulate the embedding lookup by increasing the 2D enter to 3D,
# with embedding dimension of 10.
unmasked_embedding = tf.forged(
    tf.tile(tf.expand_dims(padded_inputs, axis=-1), [1, 1, 10]), tf.float32
)

masked_embedding = masking_layer(unmasked_embedding)
print(masked_embedding._keras_mask)
tf.Tensor(
[[ True  True  True False False False]
 [ True  True  True  True  True False]
 [ True  True  True  True  True  True]], form=(3, 6), dtype=bool)
tf.Tensor(
[[ True  True  True False False False]
 [ True  True  True  True  True False]
 [ True  True  True  True  True  True]], form=(3, 6), dtype=bool)

Como puede ver en el resultado impreso, la máscara es un tensor booleano 2D con forma (batch_size, sequence_length)donde cada individuo False La entrada indica que el paso de tiempo correspondiente debe ignorarse durante el procesamiento.

Propagación de máscara en la API funcional y la API secuencial

Cuando se usa la API funcional o la API secuencial, una máscara generada por un Embedding o Masking La capa se propagará a través de la pink para cualquier capa que sea capaz de usarlas (por ejemplo, capas RNN). Keras obtendrá automáticamente la máscara correspondiente a una entrada y la pasará a cualquier capa que sepa cómo usarla.

Por ejemplo, en el siguiente modelo secuencial, el LSTM La capa recibirá automáticamente una máscara, lo que significa que ignorará los valores acolchados:

mannequin = keras.Sequential(
    [
        layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True),
        layers.LSTM(32),
    ]
)

Este es también el caso del siguiente modelo de API funcional:

inputs = keras.Enter(form=(None,), dtype="int32")
x = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)(inputs)
outputs = layers.LSTM(32)(x)

mannequin = keras.Mannequin(inputs, outputs)

Pasando los tensores de máscara directamente a las capas

Capas que pueden manejar máscaras (como el LSTM capa) tener un masks argumento en su __call__ método.

Mientras tanto, capas que producen una máscara (por ejemplo Embedding) Exponer un compute_mask(enter, previous_mask) método que puedes llamar.

Por lo tanto, puede pasar la salida del compute_mask() método de una capa productora de máscara para el __call__ Método de una capa que devour máscaras, como esta:

class MyLayer(layers.Layer):
    def __init__(self, **kwargs):
        tremendous().__init__(**kwargs)
        self.embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
        self.lstm = layers.LSTM(32)

    def name(self, inputs):
        x = self.embedding(inputs)
        # Notice that you possibly can additionally put together a `masks` tensor manually.
        # It solely must be a boolean tensor
        # with the best form, i.e. (batch_size, timesteps).
        masks = self.embedding.compute_mask(inputs)
        output = self.lstm(x, masks=masks)  # The layer will ignore the masked values
        return output


layer = MyLayer()
x = np.random.random((32, 10)) * 100
x = x.astype("int32")
layer(x)

Supporting masking in your custom layers

Sometimes, you may need to write layers that generate a mask (like Embedding), or layers that need to modify the current mask.

For instance, any layer that produces a tensor with a different time dimension than its input, such as a Concatenate layer that concatenates on the time dimension, will need to modify the current mask so that downstream layers will be able to properly take masked timesteps into account.

To do this, your layer should implement the layer.compute_mask() method, which produces a new mask given the input and the current mask.

Here is an example of a TemporalSplit layer that needs to modify the current mask.

class TemporalSplit(keras.layers.Layer):
    """Split the input tensor into 2 tensors along the time dimension."""

    def call(self, inputs):
        # Expect the input to be 3D and mask to be 2D, split the input tensor into 2
        # subtensors along the time axis (axis 1).
        return tf.split(inputs, 2, axis=1)

    def compute_mask(self, inputs, mask=None):
        # Also split the mask into 2 if it presents.
        if mask is None:
            return None
        return tf.split(mask, 2, axis=1)


first_half, second_half = TemporalSplit()(masked_embedding)
print(first_half._keras_mask)
print(second_half._keras_mask)
tf.Tensor(
[[ True  True  True]
 [ True  True  True]
 [ True  True  True]]forma = (3, 3), dtype = bool) tf.tensor (
[[False False False]
 [ True  True False]
 [ True  True  True]]forma = (3, 3), dtype = bool)

Aquí hay otro ejemplo de un CustomEmbedding capa que es capaz de generar una máscara a partir de valores de entrada:

class CustomEmbedding(keras.layers.Layer):
    def __init__(self, input_dim, output_dim, mask_zero=False, **kwargs):
        tremendous().__init__(**kwargs)
        self.input_dim = input_dim
        self.output_dim = output_dim
        self.mask_zero = mask_zero

    def construct(self, input_shape):
        self.embeddings = self.add_weight(
            form=(self.input_dim, self.output_dim),
            initializer="random_normal",
            dtype="float32",
        )

    def name(self, inputs):
        return tf.nn.embedding_lookup(self.embeddings, inputs)

    def compute_mask(self, inputs, masks=None):
        if not self.mask_zero:
            return None
        return tf.not_equal(inputs, 0)


layer = CustomEmbedding(10, 32, mask_zero=True)
x = np.random.random((3, 10)) * 9
x = x.astype("int32")

y = layer(x)
masks = layer.compute_mask(x)

print(masks)
tf.Tensor(
[[ True  True  True  True  True  True  True  True  True  True]
 [ True  True  True  True False  True False  True  True  True]
 [ True False  True False  True  True  True  True  True  True]], form=(3, 10), dtype=bool)

Nota: Para obtener más detalles sobre las limitaciones de formato relacionadas con el enmascaramiento, consulte el guía de serialización.

Optando para enmascarar la propagación en capas compatibles

La mayoría de las capas no modifican la dimensión de tiempo, por lo que no necesita modificar la máscara precise. Sin embargo, es posible que aún quieran poder propagar la máscara precise, sin cambios, a la siguiente capa. Este es un comportamiento de suscripción. Por defecto, una capa personalizada destruirá la máscara precise (ya que el marco no tiene forma de saber si propagación de la máscara es segura).

Si tiene una capa personalizada que no modifica la dimensión de tiempo, y si desea que pueda propagar la máscara de entrada precise, debe establecer self.supports_masking = True En el constructor de la capa. En este caso, el comportamiento predeterminado de compute_mask() es simplemente pasar la máscara precise.

Aquí hay un ejemplo de una capa que está en blanco para la propagación de máscara:

@keras.saving.register_keras_serializable()
class MyActivation(keras.layers.Layer):
    def __init__(self, **kwargs):
        tremendous().__init__(**kwargs)
        # Sign that the layer is secure for masks propagation
        self.supports_masking = True

    def name(self, inputs):
        return tf.nn.relu(inputs)

Ahora puede usar esta capa personalizada entre una capa de generación de máscara (como Embedding) y una capa que devour máscara (como LSTM), y pasará la máscara para que alcance la capa que devour máscara.

inputs = keras.Enter(form=(None,), dtype="int32")
x = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)(inputs)
x = MyActivation()(x)  # Will cross the masks alongside
print("Masks discovered:", x._keras_mask)
outputs = layers.LSTM(32)(x)  # Will obtain the masks

mannequin = keras.Mannequin(inputs, outputs)
Masks discovered: KerasTensor(type_spec=TensorSpec(form=(None, None), dtype=tf.bool, identify=None), identify='Placeholder_1:0')

Escribir capas que necesitan información de máscaras

Algunas capas son máscara consumidores: aceptan un masks argumentar name y úselo para determinar si se omitir ciertos pasos de tiempo.

Para escribir tal capa, simplemente puede agregar un masks=None argumento en tu name firma. La máscara asociada con las entradas se pasará a su capa cuando esté disponible.

Aquí hay un ejemplo easy a continuación: una capa que calcula un Softmax sobre la dimensión de tiempo (Eje 1) de una secuencia de entrada, mientras que descarta los instances enmascarados.

@keras.saving.register_keras_serializable()
class TemporalSoftmax(keras.layers.Layer):
    def name(self, inputs, masks=None):
        broadcast_float_mask = tf.expand_dims(tf.forged(masks, "float32"), -1)
        inputs_exp = tf.exp(inputs) * broadcast_float_mask
        inputs_sum = tf.reduce_sum(
            inputs_exp * broadcast_float_mask, axis=-1, keepdims=True
        )
        return inputs_exp / inputs_sum


inputs = keras.Enter(form=(None,), dtype="int32")
x = layers.Embedding(input_dim=10, output_dim=32, mask_zero=True)(inputs)
x = layers.Dense(1)(x)
outputs = TemporalSoftmax()(x)

mannequin = keras.Mannequin(inputs, outputs)
y = mannequin(np.random.randint(0, 10, dimension=(32, 100)), np.random.random((32, 100, 1)))

Resumen

Eso es todo lo que necesita saber sobre el relleno y el enmascaramiento en Keras. Para recapitular:

  • El “enmascaramiento” es cómo las capas pueden saber cuándo omitir / ignorar ciertos instances en las entradas de secuencia.
  • Algunas capas son generadores de máscaras: Embedding puede generar una máscara a partir de valores de entrada (si mask_zero=True), y también el Masking capa.
  • Algunas capas son consumo de máscaras: exponen un masks argumento en su __call__ método. Este es el caso de las capas RNN.
  • En la API funcional y la API secuencial, la información de máscara se propaga automáticamente.
  • Al usar capas de manera independiente, puede pasar el masks Argumentos a capas manualmente.
  • Puede escribir fácilmente capas que modifiquen la máscara precise, que generen una nueva máscara o que consumen la máscara asociada con las entradas.

Publicado originalmente en el Flujo tensor Sitio net, este artículo aparece aquí en un nuevo titular y tiene licencia bajo CC por 4.0. Muestras de código compartidas bajo la licencia Apache 2.0.

fuente

LEAVE A REPLY

Please enter your comment!
Please enter your name here