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 conmask_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
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 (simask_zero=True
), y también elMasking
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