Saltar al contenido

Implementación del decodificador de transformador desde cero en TensorFlow y Keras

15 de octubre de 2022


Existen muchas similitudes entre el codificador y el decodificador de Transformer, como la implementación de la atención de varios cabezales, la normalización de capas y una red de retroalimentación totalmente conectada como su subcapa final. Habiendo implementado el codificador de Transformer, ahora procederemos a aplicar nuestro conocimiento en la implementación del decodificador de Transformer, como un paso más hacia la implementación del modelo de Transformer completo. Nuestro objetivo final sigue siendo la aplicación del modelo completo al procesamiento del lenguaje natural (NLP).

En este tutorial, descubrirás cómo implementar el decodificador Transformer desde cero en TensorFlow y Keras.

Después de completar este tutorial, sabrás:

  • Las capas que forman parte del decodificador Transformer.
  • Cómo implementar el decodificador Transformer desde cero.

Empecemos.

Implementación del decodificador de transformador desde cero en TensorFlow y Keras
Foto de François Kaiser, algunos derechos reservados.

Descripción general del tutorial

Este tutorial se divide en tres partes; están:

  • Resumen de la arquitectura del transformador
    • El decodificador del transformador
  • Implementando el decodificador de transformador desde cero
    • La capa del decodificador
    • El decodificador del transformador
  • Probando el código

requisitos previos

Para este tutorial, asumimos que ya está familiarizado con:

  • El modelo del transformador
  • La atención del producto punto escalado
  • La atención de múltiples cabezas
  • La codificación posicional del transformador
  • El codificador 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.

Habíamos visto que la parte del decodificador del Transformador comparte muchas similitudes en su arquitectura con el codificador. Este tutorial explorará estas similitudes.

El decodificador del transformador

Al igual que el codificador de Transformer, el decodificador de Transformer también consta de una pila de $N$ capas idénticas. El decodificador Transformer, sin embargo, implementa un bloque de atención de varios cabezales adicional, para un total de tres subcapas principales:

  • La primera subcapa comprende un mecanismo de atención de múltiples cabezas que recibe las consultas, claves y valores como entradas.
  • La segunda subcapa comprende un segundo mecanismo de atención multicabezal.
  • La tercera subcapa comprende una red feed-forward totalmente conectada.

El bloque decodificador de la arquitectura del transformador
Tomado de “La atención es todo lo que necesitas“

A cada una de estas tres subcapas le sigue también la normalización de capa, donde la entrada al paso de normalización de capa es su correspondiente entrada de subcapa (a través de una conexión residual) y salida.

En el lado del decodificador, las consultas, claves y valores que se introducen en el primer bloque de atención de varios cabezales también representan la misma secuencia de entrada. Sin embargo, esta vez, es el objetivo secuencia que se incrusta y aumenta con información posicional antes de suministrarse al decodificador. El segundo bloque de atención multicabezal, por otro lado, recibe la salida del codificador en forma de claves y valores, y la salida normalizada del primer bloque de atención del decodificador como consultas. En ambos casos, la dimensionalidad de las consultas y claves permanece igual a $d_k$, mientras que la dimensionalidad de los valores permanece igual a $d_v$.

Recomendado:  Búsqueda local iterada desde cero en Python

Vaswani et al. introduzca la regularización en el modelo en el lado del decodificador también, aplicando abandono a la salida de cada subcapa (antes del paso de normalización de la capa), así como a las codificaciones posicionales antes de que se introduzcan en el decodificador.

Veamos ahora cómo implementar el decodificador Transformer desde cero en TensorFlow y Keras.

Implementando el decodificador de transformador desde cero

La capa del decodificador

Dado que ya implementamos las subcapas requeridas cuando cubrimos la implementación del codificador Transformer, crearemos una clase para la capa del decodificador que haga uso de estas subcapas de inmediato:

from multihead_attention import MultiHeadAttention
from encoder import AddNormalization, FeedForward

class DecoderLayer(Layer):
def __init__(self, h, d_k, d_v, d_model, d_ff, rate, **kwargs):
super(DecoderLayer, self).__init__(**kwargs)
self.multihead_attention1 = MultiHeadAttention(h, d_k, d_v, d_model)
self.dropout1 = Dropout(rate)
self.add_norm1 = AddNormalization()
self.multihead_attention2 = MultiHeadAttention(h, d_k, d_v, d_model)
self.dropout2 = Dropout(rate)
self.add_norm2 = AddNormalization()
self.feed_forward = FeedForward(d_ff, d_model)
self.dropout3 = Dropout(rate)
self.add_norm3 = AddNormalization()

Observe aquí que, dado que mi código para las diferentes subcapas se había guardado en varios scripts de Python (es decir, multihead_atencion.py y codificador.py), era necesario importarlos para poder utilizar las clases requeridas.

Como habíamos hecho con el codificador de Transformer, procederemos a crear el método de clase, call()que implementa todas las subcapas del decodificador:


def call(self, x, encoder_output, lookahead_mask, padding_mask, training):
# Multi-head attention layer
multihead_output1 = self.multihead_attention1(x, x, x, lookahead_mask)
# Expected output shape = (batch_size, sequence_length, d_model)

# Add in a dropout layer
multihead_output1 = self.dropout1(multihead_output1, training=training)

# Followed by an Add & Norm layer
addnorm_output1 = self.add_norm1(x, multihead_output1)
# Expected output shape = (batch_size, sequence_length, d_model)

# Followed by another multi-head attention layer
multihead_output2 = self.multihead_attention2(addnorm_output1, encoder_output, encoder_output, padding_mask)

# Add in another dropout layer
multihead_output2 = self.dropout2(multihead_output2, training=training)

# Followed by another Add & Norm layer
addnorm_output2 = self.add_norm1(addnorm_output1, multihead_output2)

# Followed by a fully connected layer
feedforward_output = self.feed_forward(addnorm_output2)
# Expected output shape = (batch_size, sequence_length, d_model)

# Add in another dropout layer
feedforward_output = self.dropout3(feedforward_output, training=training)

# Followed by another Add & Norm layer
return self.add_norm3(addnorm_output2, feedforward_output)

Las subcapas de atención de varios cabezales también pueden recibir una máscara de relleno o una máscara de anticipación. Como un breve recordatorio de lo que habíamos dicho en un tutorial anterior, el relleno La máscara es necesaria para evitar que el relleno de ceros en la secuencia de entrada se procese junto con los valores de entrada reales. los mirar hacia el futuro mask evita 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.

Lo mismo call() El método de clase también puede recibir un training para aplicar solo las capas de abandono durante el entrenamiento, cuando el valor de este indicador se establece en True.

El decodificador del transformador

El decodificador Transformer toma la capa del decodificador que acabamos de implementar y la replica idénticamente $N$ veces.

Recomendado:  Las 7 principales tendencias de marketing de contenidos para triunfar en 2023

Estaremos creando lo siguiente Decoder() class para implementar el decodificador Transformer:

from positional_encoding import PositionEmbeddingFixedWeights class Decoder(Layer): def __init__(self, vocab_size, secuencia_length, h, d_k, d_v, d_model, d_ff, n, rate, **kwargs): super(Decoder, self).__init__(**kwargs ) self.pos_encoding = PositionEmbeddingFixedWeights(sequence_length, vocab_size, d_model) self.dropout = Dropout(tasa) self.decoder_layer = [DecoderLayer(h, d_k, d_v, d_model, d_ff, rate) for _ in range(n)

As in the Transformer encoder, the input to the first multi-head attention block on the decoder side receives the input sequence after this would have undergone a process of word embedding and positional encoding. For this purpose, an instance of the PositionEmbeddingFixedWeights class (covered in this tutorial) is initialized and its output assigned to the pos_encoding variable.

The final step is to create a class method, call(), that applies word embedding and positional encoding to the input sequence and feeds the result, together with the encoder output, to $N$ decoder layers:


def call(self, output_target, encoder_output, lookahead_mask, padding_mask, training):
# Generate the positional encoding
pos_encoding_output = self.pos_encoding(output_target)
# Expected output shape = (number of sentences, sequence_length, d_model)

# Add in a dropout layer
x = self.dropout(pos_encoding_output, training=training)

# Pass on the positional encoded values to each encoder layer
for i, layer in enumerate(self.decoder_layer):
x = layer(x, encoder_output, lookahead_mask, padding_mask, training)

return x

The code listing for the full Transformer decoder is the following:

from tensorflow.keras.layers import Layer, Dropout
from multihead_attention import MultiHeadAttention
from positional_encoding import PositionEmbeddingFixedWeights
from encoder import AddNormalization, FeedForward

# Implementing the Decoder Layer
class DecoderLayer(Layer):
def __init__(self, h, d_k, d_v, d_model, d_ff, rate, **kwargs):
super(DecoderLayer, self).__init__(**kwargs)
self.multihead_attention1 = MultiHeadAttention(h, d_k, d_v, d_model)
self.dropout1 = Dropout(rate)
self.add_norm1 = AddNormalization()
self.multihead_attention2 = MultiHeadAttention(h, d_k, d_v, d_model)
self.dropout2 = Dropout(rate)
self.add_norm2 = AddNormalization()
self.feed_forward = FeedForward(d_ff, d_model)
self.dropout3 = Dropout(rate)
self.add_norm3 = AddNormalization()

def call(self, x, encoder_output, lookahead_mask, padding_mask, training):
# Multi-head attention layer
multihead_output1 = self.multihead_attention1(x, x, x, lookahead_mask)
# Expected output shape = (batch_size, sequence_length, d_model)

# Add in a dropout layer
multihead_output1 = self.dropout1(multihead_output1, training=training)

# Followed by an Add & Norm layer
addnorm_output1 = self.add_norm1(x, multihead_output1)
# Expected output shape = (batch_size, sequence_length, d_model)

# Followed by another multi-head attention layer
multihead_output2 = self.multihead_attention2(addnorm_output1, encoder_output, encoder_output, padding_mask)

# Add in another dropout layer
multihead_output2 = self.dropout2(multihead_output2, training=training)

# Followed by another Add & Norm layer
addnorm_output2 = self.add_norm1(addnorm_output1, multihead_output2)

# Followed by a fully connected layer
feedforward_output = self.feed_forward(addnorm_output2)
# Expected output shape = (batch_size, sequence_length, d_model)

# Add in another dropout layer
feedforward_output = self.dropout3(feedforward_output, training=training)

# Followed by another Add & Norm layer
return self.add_norm3(addnorm_output2, feedforward_output)

# Implementing the Decoder
class Decoder(Layer):
def __init__(self, vocab_size, sequence_length, h, d_k, d_v, d_model, d_ff, n, rate, **kwargs):
super(Decoder, self).__init__(**kwargs)
self.pos_encoding = PositionEmbeddingFixedWeights(sequence_length, vocab_size, d_model)
self.dropout = Dropout(rate)
self.decoder_layer = [DecoderLayer(h, d_k, d_v, d_model, d_ff, rate) for _ in range(n)]

def call(self, output_target, encoder_output, lookahead_mask, padding_mask, training): # Generar la codificación posicional pos_encoding_output = self.pos_encoding(output_target) # Forma de salida esperada = (número de oraciones, secuencia_longitud, d_modelo) # Agregar una capa de abandono x = self.dropout(pos_encoding_output, training=training) # Pasar los valores posicionales codificados a cada capa de codificador para i, capa en enumerate(self.decoder_layer): x = capa(x, codificador_salida, lookahead_mask, padding_mask, training) return x

Probando el código

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

Recomendado:  Graduado con honores combina el amor por las matemáticas con la economía

batch_size = 64 # Batch size from the training process
dropout_rate = 0.1 # Frequency of dropping the input units in the dropout layers

En cuanto a la secuencia de entrada, por el momento trabajaremos con datos ficticios hasta que lleguemos a la etapa de entrenamiento del modelo completo de Transformer en un tutorial separado, momento en el cual usaremos oraciones reales:


dec_vocab_size = 20 # Vocabulary size for the decoder
input_seq_length = 5 # Maximum length of the input sequence

input_seq = random.random((batch_size, input_seq_length))
enc_output = random.random((batch_size, input_seq_length, d_model))

A continuación, crearemos una nueva instancia del Decoder clase, asignando su salida a la decoder variable y, posteriormente, pasar los argumentos de entrada e imprimir el resultado. Estableceremos las máscaras de relleno y de anticipación para None por el momento, pero volveremos a ellos cuando implementemos el modelo completo de Transformer:


decoder = Decoder(dec_vocab_size, input_seq_length, h, d_k, d_v, d_model, d_ff, n, dropout_rate)
print(decoder(input_seq, enc_output, None, True)

Unir todo produce la siguiente lista de códigos:

from numpy import random

dec_vocab_size = 20 # Vocabulary size for the decoder
input_seq_length = 5 # Maximum length of the input 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 decoder stack

batch_size = 64 # Batch size from the training process
dropout_rate = 0.1 # Frequency of dropping the input units in the dropout layers

input_seq = random.random((batch_size, input_seq_length))
enc_output = random.random((batch_size, input_seq_length, d_model))

decoder = Decoder(dec_vocab_size, input_seq_length, h, d_k, d_v, d_model, d_ff, n, dropout_rate)
print(decoder(input_seq, enc_output, None, True))

Ejecutar este código produce una salida de forma, (tamaño del lote, longitud de la secuencia, dimensionalidad del modelo). Tenga en cuenta que probablemente verá una salida diferente debido a la inicialización aleatoria de la secuencia de entrada y los valores de los parámetros de las capas densas.

tf.Tensor(
[[[-0.04132953 -1.7236308 0.5391184 … -0.76394725 1.4969798
0.37682498] [ 0.05501875 -1.7523409 0.58404493 … -0.70776534 1.4498456
0.32555297] [ 0.04983566 -1.8431275 0.55850077 … -0.68202156 1.4222856
0.32104644] [-0.05684051 -1.8862512 0.4771412 … -0.7101341 1.431343
0.39346313] [-0.15625843 -1.7992781 0.40803364 … -0.75190556 1.4602519
0.53546077]] …

[[-0.58847624 -1.646842 0.5973466 … -0.47778523 1.2060764
0.34091905] [-0.48688865 -1.6809179 0.6493542 … -0.41274604 1.188649
0.27100053] [-0.49568555 -1.8002801 0.61536175 … -0.38540334 1.2023914
0.24383534] [-0.59913146 -1.8598882 0.5098136 … -0.3984461 1.2115746
0.3186561 ] [-0.71045107 -1.7778647 0.43008155 … -0.42037937 1.2255307
0.47380894]]], shape=(64, 5, 512), dtype=float32)

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 decodificador Transformer desde cero en TensorFlow y Keras.

Específicamente, aprendiste:

  • Las capas que forman parte del decodificador Transformer.
  • Cómo implementar el decodificador Transformer desde cero.

¿Tiene usted alguna pregunta?
Haga sus preguntas en los comentarios a continuación y haré todo lo posible para responder.

 



La publicación Implementing the Transformer Decoder From Scratch in TensorFlow and Keras apareció primero en Machine Learning Mastery.