Hemos llegado a un punto en el que hemos implementado y probado el codificador y decodificador de Transformer por separado, y ahora podemos unir los dos en un modelo completo. También veremos cómo crear máscaras de relleno y de anticipación mediante las cuales suprimiremos los valores de entrada que no consideraremos en los cálculos del codificador o del decodificador. Nuestro objetivo final sigue siendo la aplicación del modelo completo al procesamiento del lenguaje natural (NLP).
En este tutorial, descubrirá cómo implementar el modelo completo de Transformer y crear máscaras de relleno y de anticipación.
Después de completar este tutorial, sabrás:
- Cómo crear una máscara de relleno para el codificador y decodificador.
- Cómo crear una máscara de anticipación para el decodificador.
- Cómo unir el codificador y decodificador Transformer en un solo modelo.
- Cómo imprimir un resumen de las capas de codificador y decodificador.
Empecemos.
Unión del codificador y decodificador del transformador, y enmascaramiento
Foto de John O’Nolan, algunos derechos reservados.
Descripción general del tutorial
Este tutorial se divide en cuatro partes; están:
- Resumen de la arquitectura del transformador
- Enmascaramiento
- Crear una máscara de relleno
- Creación de una máscara de anticipación
- Unión del codificador y decodificador del transformador
- Creación de una instancia del modelo de transformador
- Impresión de un resumen de las capas de codificador y decodificador
requisitos previos
Para este tutorial, asumimos que ya está familiarizado con:
- El modelo del transformador
- El codificador del transformador
- El decodificador del transformador
Resumen de la arquitectura del transformador
Recuerde haber visto que la arquitectura de Transformer sigue una estructura de codificador-decodificador: el codificador, en el lado izquierdo, tiene la tarea de mapear una secuencia de entrada a una secuencia de representaciones continuas; el decodificador, en el lado derecho, recibe la salida del codificador junto con la salida del decodificador en el paso de tiempo anterior, para generar una secuencia de salida.
La estructura del codificador-decodificador de la arquitectura del transformador
Tomado de “La atención es todo lo que necesitas“
Al generar una secuencia de salida, el Transformador no se basa en recurrencias ni circunvoluciones.
Hemos visto cómo implementar el codificador y decodificador de Transformer por separado. En este tutorial, uniremos los dos en un modelo de Transformador completo y aplicaremos relleno y enmascaramiento de anticipación a los valores de entrada.
Comencemos primero por descubrir cómo aplicar el enmascaramiento.
Enmascaramiento
Crear una máscara de relleno
Ya nos hemos familiarizado con la importancia de enmascarar los valores de entrada antes de introducirlos en el codificador y decodificador.
Como veremos cuando procedamos a entrenar el modelo de Transformador, las secuencias de entrada que se alimentarán al codificador y decodificador primero se rellenarán con ceros hasta una longitud de secuencia específica. La importancia de tener una máscara de relleno es asegurarse de que el codificador y el decodificador no procesen estos valores cero junto con los valores de entrada reales.
Vamos a crear la siguiente función para generar una máscara de relleno tanto para el codificador como para el decodificador:
from tensorflow import math, cast, float32
def padding_mask(input):
# Create mask which marks the zero padding values in the input by a 1
mask = math.equal(input, 0)
mask = cast(mask, float32)
return mask
Al recibir una entrada, esta función generará un tensor que marca por un valor de una siempre que la entrada contenga un valor de cero.
Por lo tanto, si ingresamos la siguiente matriz:
from numpy import array
input = array([1, 2, 3, 4, 0, 0, 0])
print(padding_mask(input))
Entonces la salida del padding_mask
la funcion seria la siguiente:
tf.Tensor([0. 0. 0. 0. 1. 1. 1.], shape=(7,), dtype=float32)
Creación de una máscara de anticipación
Se requiere una máscara de anticipación para evitar que el decodificador preste atención a las palabras sucesivas, de modo que la predicción de una palabra en particular solo puede depender de las salidas conocidas de las palabras que le preceden.
Para este propósito, vamos a crear la siguiente función para generar una máscara de anticipación para el decodificador:
from tensorflow import linalg, ones
def lookahead_mask(shape):
# Mask out future entries by marking them with a 1.0
mask = 1 – linalg.band_part(ones((shape, shape)), -1, 0)
return mask
Le pasaremos la longitud de la entrada del decodificador. Tomemos esta longitud igual a 5, como ejemplo:
print(lookahead_mask(5))
Entonces la salida que el lookahead_mask
la función que devuelve es la siguiente:
tf.Tensor(
[[0. 1. 1. 1. 1.]
[0. 0. 1. 1. 1.]
[0. 0. 0. 1. 1.]
[0. 0. 0. 0. 1.]
[0. 0. 0. 0. 0.]], shape=(5, 5), dtype=float32)
Nuevamente, el una Los valores enmascaran las entradas que no deben usarse. De esta manera, la predicción de cada palabra sólo depende de las que le preceden.
Unión del codificador y decodificador del transformador
Comencemos por crear la clase, TransformerModel
que hereda del Model
clase base en Keras:
class TransformerModel(Model):
def __init__(self, enc_vocab_size, dec_vocab_size, enc_seq_length, dec_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate, **kwargs):
super(TransformerModel, self).__init__(**kwargs)
# Set up the encoder
self.encoder = Encoder(enc_vocab_size, enc_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate)
# Set up the decoder
self.decoder = Decoder(dec_vocab_size, dec_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate)
# Define the final dense layer
self.model_last_layer = Dense(dec_vocab_size)
…
Nuestro primer paso en la creación de la TransformerModel
clase es inicializar instancias de la Encoder
y Decoder
clases que habíamos implementado anteriormente, y asignando sus salidas a las variables, encoder
y decoder
, respectivamente. Si ha guardado estas clases en secuencias de comandos de Python separadas, no olvide importarlas. Había guardado mi código en los scripts de Python, codificador.py y decodificador.pyy por lo tanto necesito importarlos en consecuencia.
También incluimos una capa densa final que produce el resultado final, como en la arquitectura Transformer de Vaswani et al (2017).
A continuación, crearemos el método de clase, call()
para alimentar las entradas relevantes en el codificador y decodificador.
Primero se genera una máscara de relleno para enmascarar la entrada del codificador, así como la salida del codificador cuando se alimenta al segundo bloque de autoatención del decodificador:
…
def call(self, encoder_input, decoder_input, training):
# Create padding mask to mask the encoder inputs and the encoder outputs in the decoder
enc_padding_mask = self.padding_mask(encoder_input)
…
A continuación, se generan una máscara de relleno así como una máscara de anticipación para enmascarar la entrada del decodificador. Estos se combinan entre sí a través de un elemento-sabio maximum
operación:
…
# Create and combine padding and look-ahead masks to be fed into the decoder
dec_in_padding_mask = self.padding_mask(decoder_input)
dec_in_lookahead_mask = self.lookahead_mask(decoder_input.shape[1])
dec_in_lookahead_mask = maximum(dec_in_padding_mask, dec_in_lookahead_mask)
…
A continuación, las entradas relevantes se introducen en el codificador y el decodificador, y la salida del modelo de transformador se genera alimentando la salida del decodificador en una capa densa final:
…
# Feed the input into the encoder
encoder_output = self.encoder(encoder_input, enc_padding_mask, training)
# Feed the encoder output into the decoder
decoder_output = self.decoder(decoder_input, encoder_output, dec_in_lookahead_mask, enc_padding_mask, training)
# Pass the decoder output through a final dense layer
model_output = self.model_last_layer(decoder_output)
return model_output
Combinando todos los pasos juntos, nos da la siguiente lista completa de códigos:
from encoder import Encoder
from decoder import Decoder
from tensorflow import math, cast, float32, linalg, ones, maximum, newaxis
from tensorflow.keras import Model
from tensorflow.keras.layers import Dense
class TransformerModel(Model):
def __init__(self, enc_vocab_size, dec_vocab_size, enc_seq_length, dec_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate, **kwargs):
super(TransformerModel, self).__init__(**kwargs)
# Set up the encoder
self.encoder = Encoder(enc_vocab_size, enc_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate)
# Set up the decoder
self.decoder = Decoder(dec_vocab_size, dec_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate)
# Define the final dense layer
self.model_last_layer = Dense(dec_vocab_size)
def padding_mask(self, input):
# Create mask which marks the zero padding values in the input by a 1.0
mask = math.equal(input, 0)
mask = cast(mask, float32)
# The shape of the mask should be broadcastable to the shape
# of the attention weights that it will be masking later on
return mask[:, newaxis, newaxis, :]
def lookahead_mask(self, shape):
# Mask out future entries by marking them with a 1.0
mask = 1 – linalg.band_part(ones((shape, shape)), -1, 0)
return mask
def call(self, encoder_input, decoder_input, training):
# Create padding mask to mask the encoder inputs and the encoder outputs in the decoder
enc_padding_mask = self.padding_mask(encoder_input)
# Create and combine padding and look-ahead masks to be fed into the decoder
dec_in_padding_mask = self.padding_mask(decoder_input)
dec_in_lookahead_mask = self.lookahead_mask(decoder_input.shape[1])
dec_in_lookahead_mask = maximum(dec_in_padding_mask, dec_in_lookahead_mask)
# Feed the input into the encoder
encoder_output = self.encoder(encoder_input, enc_padding_mask, training)
# Feed the encoder output into the decoder
decoder_output = self.decoder(decoder_input, encoder_output, dec_in_lookahead_mask, enc_padding_mask, training)
# Pass the decoder output through a final dense layer
model_output = self.model_last_layer(decoder_output)
return model_output
Tenga en cuenta que hemos realizado un pequeño cambio en la salida que devuelve el padding_mask
función, de modo que su forma se hace extensible a la forma del tensor de peso de atención que estará enmascarando cuando entrenemos el modelo de Transformador.
Creación de una instancia del modelo de transformador
Trabajaremos con los valores de los parámetros especificados en el artículo Attention Is All You Need, de Vaswani et al. (2017):
h = 8 # Number of self-attention heads
d_k = 64 # Dimensionality of the linearly projected queries and keys
d_v = 64 # Dimensionality of the linearly projected values
d_ff = 2048 # Dimensionality of the inner fully connected layer
d_model = 512 # Dimensionality of the model sub-layers’ outputs
n = 6 # Number of layers in the encoder stack
dropout_rate = 0.1 # Frequency of dropping the input units in the dropout layers
…
En cuanto a los parámetros relacionados con la entrada, trabajaremos con valores ficticios por el momento hasta que lleguemos a la etapa de entrenamiento del modelo completo de Transformer, momento en el que usaremos oraciones reales:
…
enc_vocab_size = 20 # Vocabulary size for the encoder
dec_vocab_size = 20 # Vocabulary size for the decoder
enc_seq_length = 5 # Maximum length of the input sequence
dec_seq_length = 5 # Maximum length of the target sequence
…
Podemos proceder a crear una instancia del TransformerModel
clase de la siguiente manera:
from model import TransformerModel
# Create model
training_model = TransformerModel(enc_vocab_size, dec_vocab_size, enc_seq_length, dec_seq_length, h, d_k, d_v, d_model, d_ff, n, dropout_rate)
El listado completo de códigos es el siguiente:
enc_vocab_size = 20 # Vocabulary size for the encoder
dec_vocab_size = 20 # Vocabulary size for the decoder
enc_seq_length = 5 # Maximum length of the input sequence
dec_seq_length = 5 # Maximum length of the target sequence
h = 8 # Number of self-attention heads
d_k = 64 # Dimensionality of the linearly projected queries and keys
d_v = 64 # Dimensionality of the linearly projected values
d_ff = 2048 # Dimensionality of the inner fully connected layer
d_model = 512 # Dimensionality of the model sub-layers’ outputs
n = 6 # Number of layers in the encoder stack
dropout_rate = 0.1 # Frequency of dropping the input units in the dropout layers
# Create model
training_model = TransformerModel(enc_vocab_size, dec_vocab_size, enc_seq_length, dec_seq_length, h, d_k, d_v, d_model, d_ff, n, dropout_rate)
Impresión de un resumen de las capas de codificador y decodificador
También podemos imprimir un resumen de los bloques codificadores y decodificadores del modelo Transformer. La opción de imprimirlos por separado es poder ver los detalles de sus subcapas individuales. Para hacerlo, agregaremos la siguiente línea de código al __init__()
método de ambos EncoderLayer
y DecoderLayer
clases:
self.build(input_shape=[None, sequence_length, d_model])
Entonces necesitamos agregar el siguiente método para EncoderLayer
clase:
def build_graph(self):
input_layer = Input(shape=(self.sequence_length, self.d_model))
return Model(inputs=[input_layer], outputs=self.call(input_layer, None, True))
Y el siguiente método para el DecoderLayer
clase:
def build_graph(self):
input_layer = Input(shape=(self.sequence_length, self.d_model))
return Model(inputs=[input_layer], outputs=self.call(input_layer, input_layer, None, None, True))
Esto resulta en el EncoderLayer
clase se modifica de la siguiente manera (los tres puntos debajo de la call()
método significa que este sigue siendo el mismo que habíamos implementado aquí):
from tensorflow.keras.layers import Input
from tensorflow.keras import Model
class EncoderLayer(Layer):
def __init__(self, sequence_length, h, d_k, d_v, d_model, d_ff, rate, **kwargs):
super(EncoderLayer, self).__init__(**kwargs)
self.build(input_shape=[None, sequence_length, d_model])
self.d_model = d_model
self.sequence_length = sequence_length
self.multihead_attention = MultiHeadAttention(h, d_k, d_v, d_model)
self.dropout1 = Dropout(rate)
self.add_norm1 = AddNormalization()
self.feed_forward = FeedForward(d_ff, d_model)
self.dropout2 = Dropout(rate)
self.add_norm2 = AddNormalization()
def build_graph(self):
input_layer = Input(shape=(self.sequence_length, self.d_model))
return Model(inputs=[input_layer], outputs=self.call(input_layer, None, True))
def call(self, x, padding_mask, training):
…
Se pueden hacer cambios similares en el DecoderLayer
clase también.
Una vez que tengamos los cambios necesarios en su lugar, podemos proceder a las instancias creadas del EncoderLayer
y DecoderLayer
clases, e imprimir sus resúmenes de la siguiente manera:
from encoder import EncoderLayer
from decoder import DecoderLayer
encoder = EncoderLayer(enc_seq_length, h, d_k, d_v, d_model, d_ff, dropout_rate)
encoder.build_graph().summary()
decoder = DecoderLayer(dec_seq_length, h, d_k, d_v, d_model, d_ff, dropout_rate)
decoder.build_graph().summary()
El resumen resultante para el codificador es el siguiente:
Model: «model»
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) [(None, 5, 512)] 0 []
multi_head_attention_18 (Multi (None, 5, 512) 131776 [‘input_1[0][0]’,
HeadAttention) ‘input_1[0][0]’,
‘input_1[0][0]’]
dropout_32 (Dropout) (None, 5, 512) 0 [‘multi_head_attention_18[0][0]’]
add_normalization_30 (AddNorma (None, 5, 512) 1024 [‘input_1[0][0]’,
lization) ‘dropout_32[0][0]’]
feed_forward_12 (FeedForward) (None, 5, 512) 2099712 [‘add_normalization_30[0][0]’]
dropout_33 (Dropout) (None, 5, 512) 0 [‘feed_forward_12[0][0]’]
add_normalization_31 (AddNorma (None, 5, 512) 1024 [‘add_normalization_30[0][0]’,
lization) ‘dropout_33[0][0]’]
==================================================================================================
Total params: 2,233,536
Trainable params: 2,233,536
Non-trainable params: 0
__________________________________________________________________________________________________
Mientras que el resumen resultante para el decodificador es el siguiente:
Model: «model_1»
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_2 (InputLayer) [(None, 5, 512)] 0 []
multi_head_attention_19 (Multi (None, 5, 512) 131776 [‘input_2[0][0]’,
HeadAttention) ‘input_2[0][0]’,
‘input_2[0][0]’]
dropout_34 (Dropout) (None, 5, 512) 0 [‘multi_head_attention_19[0][0]’]
add_normalization_32 (AddNorma (None, 5, 512) 1024 [‘input_2[0][0]’,
lization) ‘dropout_34[0][0]’,
‘add_normalization_32[0][0]’,
‘dropout_35[0][0]’]
multi_head_attention_20 (Multi (None, 5, 512) 131776 [‘add_normalization_32[0][0]’,
HeadAttention) ‘input_2[0][0]’,
‘input_2[0][0]’]
dropout_35 (Dropout) (None, 5, 512) 0 [‘multi_head_attention_20[0][0]’]
feed_forward_13 (FeedForward) (None, 5, 512) 2099712 [‘add_normalization_32[1][0]’]
dropout_36 (Dropout) (None, 5, 512) 0 [‘feed_forward_13[0][0]’]
add_normalization_34 (AddNorma (None, 5, 512) 1024 [‘add_normalization_32[1][0]’,
lization) ‘dropout_36[0][0]’]
==================================================================================================
Total params: 2,365,312
Trainable params: 2,365,312
Non-trainable params: 0
__________________________________________________________________________________________________
Otras lecturas
Esta sección proporciona más recursos sobre el tema si desea profundizar más.
Libros
- Aprendizaje profundo avanzado con Python, 2019.
- Transformadores para el procesamiento del lenguaje natural, 2021.
Documentos
- La atención es todo lo que necesitas, 2017.
Resumen
En este tutorial, descubrió cómo implementar el modelo completo de Transformer y crear máscaras de relleno y de anticipación.
Específicamente, aprendiste:
- Cómo crear una máscara de relleno para el codificador y decodificador.
- Cómo crear una máscara de anticipación para el decodificador.
- Cómo unir el codificador y decodificador Transformer en un solo modelo.
- Cómo imprimir un resumen de las capas de codificador y decodificador.
¿Tiene usted alguna pregunta?
Haga sus preguntas en los comentarios a continuación y haré todo lo posible para responder.
La publicación Unión del codificador y decodificador de transformador y enmascaramiento apareció primero en Machine Learning Mastery.