7. Implementación de los modelos en PyTorch#
La implementación en forma de código de los diferentes modelos es un enfoque complementario a su estudio desde un punto de vista matemático. En esta página se presentan implementaciones en PyTorch de cada uno de los modelos estudiados. La idea es que abordes el estudio de estas implementaciones después de haber estudiado conceptualmente el modelo en cuestión.
Nota
Esta página es parte de la serie "Un recorrido peso a peso por el transformer", donde se presenta una guía para aprender cómo funcionan las redes neuronales que procesan textos y cómo se programan. Es también posible que hayas llegado a ella desde otra fuente (por ejemplo, una asignatura específica) que plantee otra forma diferente de utilizar estos contenidos. En ese caso, usa las recomendaciones y la planificación que te ofrezca esa fuente.
7.1 Código para un regresor logístico y uno multinomial#
Estas son dos implementaciones en PyTorch de los regresores que se estudian en esta página en unas pocas decenas de líneas de código. Asegúrate de que terminas entendiendo el código lo suficiente como para sentirte con ánimo de poder modificarlo para adaptarlo a otras necesidades.
Repasa antes de abordar el código cómo puedes depurar programas escritos en Python. Repasa también cómo funciona el mecanismo de broadcasting en PyTorch.
Los dos programas de este apartado son:
- Un regresor logístico que clasifica muestras bidimensionales sintéticas en dos clases. Se usan solo los elementos más básicos de PyTorch para poder tener una implementación lo más detallada posible. Como ejercicio, puedes hacer una traza y analizar qué tamaños tienen los tensores. Puedes jugar también con el número de pasos de entrenamiento y la tasa de aprendizaje para ver cómo evoluciona el entrenamiento. Explora diversas posiciones de los centros de las clases y de la dispersión de los datos alrededor de estos y observa cómo cambia la frontera de decisión. Elimina el sesgo (bias) de las ecuaciones y observa cómo se restringe la forma de la frontera de decisión al obligar a esta a pasar por el origen de coordenadas.
- Un regresor softmax para clasificar textos según su temática. Como ejercicio, puedes probar a entrenar con un solo embedding en cada paso de entrenamiento en lugar de con un batch de todos ellos y ver cómo se comporta el error. Puedes también adaptar el código del regresor logístico anterior para que use las funciones de PyTorch que se ven en este programa.
Si no lo has hecho ya, puedes empezar a aprender Python y PyTorch siguiendo el capítulo correspondiente de esta serie.
7.2 Código para skip-grams#
Esta es una implementación del algoritmo skip-gram para la obtención de embeddings incontextuales que se estudia en esta página de la guía y que sigue las pautas marcadas en el libro de Jurafsky y Martin.
7.3 Código para un modelo de lengua con redes feedforward#
Esta es la implementación de un modelo de lengua con redes feedforward que se estudia en esta página y que sigue las ecuaciones del libro de Jurafsky y Martin.
7.4 Código para el transformer#
El transformer (estudiado en esta parte de la guía) se presenta en tres cuadernos diferentes:
- Uno que contiene la arquitectura base y las implementaciones tanto de un modelo basado en codificador como de uno basado en descodificador.
- Otro que aplica el descodificador a un modelo de lengua que predice el siguiente token de una secuencia.
- Y otro que se basa en el codificador para instrumentar un sistema de reconocimiento de entidades nombradas.
7.5 Código para un transformer del proyecto minGPT#
Una buena implementación en PyTorch de un modelo de lengua basado en transformer es la de minGPT de Andrej Karpathy. El código permite entrenar y usar modelos de lengua, además de permitir la carga de los pesos del modelo GPT-2. El código de nuestro transformer está basado en el de minGPT, por lo que el modelo en sí no te debería resultar difícil de entender.
El repositorio de esta guía tiene una copia del código de minGPT con algunas pequeñas modificaciones. A continuación, se comenta qué ficheros son relevantes para nuestros intereses. Los ficheros de los que no se diga nada no tienes que mirarlos. Para usar el código y poder modificarlo, puedes instalarlo con:
Debido a cambios en elementos externos, el código actual no funciona tal cual. Para que funcione, tienes que cambiar la línea 200 del fichero mingpt/model.py
de:
a:
7.5.1 Fichero mingpt/bpe.py#
Este fichero contiene la implementación necesaria para usar el modelo de subpalabras de tipo BPE usado por GPT-2. De su función se habla más adelante. El código principal del fichero muestra un ejemplo de tokenización paso a paso de una cadena de entrada que puedes ver haciendo python bpe.py
. La primera vez que se llama a los métodos encode
o decode
se descargan los ficheros encoder.json
y vocab.bpe
, que contienen el vocabulario y las reglas de unión de subpalabras, respectivamente, usados por GPT-2. Estos ficheros se almacenan en el directorio ~/.cache/mingpt
.
No es necesario que estudies el código de este fichero. Basta con que sepas que nos permite obtener la lista de índices de los tokens de un texto de entrada, así como obtener el texto asociado a una lista de índices de tokens obtenida a la salida del modelo:
bpe = BPETokenizer()
tokens = bpe("A relaxing cup of café con leche in Plaza Mayor") # encode
# tokens is a tensor of shape (1, 9)
print(bpe.decode(tokens[0]))
# "A relaxing cup of café con leche in Plaza Mayor"
print(tokens[0].tolist())
# [32, 28175, 6508, 286, 40304, 369, 443, 2395, 287, 23280, 10106]
for token in tokens[0]:
print(bpe.decode(torch.tensor([token])), end='/')
# A/ relaxing/ cup/ of/ café/ con/ le/che/ in/ Plaza/ Mayor/
7.5.2 Fichero mingpt/utils.py#
No es necesario que estudies con detalle el código de este fichero. Simplemente, ábrelo y observa que define dos funciones auxiliares (set_seed
y setup_logging
) y una clase (CfgNode
) para gestionar los parámetros de configuración del modelo.
7.5.3 Fichero mingpt/trainer.py#
Estudia este fichero, que contiene el código general que se encarga de entrenar un modelo. El código no tiene nada específico de la arquitectura transformer, por lo que podría aplicarse con pocas modificaciones a cualquier otro modelo.
7.5.4 Fichero mingpt/model.py#
El fichero más importante para nuestros propósitos. Puedes saltarte, no obstante, el método from_pretrained
de la clase GPT
(incorpora los pesos de GPT-2 descargados de huggingface/transformers a nuestro modelo) y, especialmente, el método configure_optimizers
(devuelve un optimizador de tipo Adam que trabaja de forma diferente según el tipo de parámetro sobre el que actúa), ya que contienen código muy específico para el sistema GPT-2.
Estudia con detalle las clases CausalSelfAttention
y Block
, así como los métodos forward
, generate
, __init__
, _init_weights
y get_default_config
de la clase GPT
.
7.5.5 Fichero generate.ipynb#
Estudia este código que usa el modelo para generar texto. Es un cuaderno de Python, pero lo puedes ejecutar desde la línea de órdenes conviertiéndolo antes a un programa de Python con:
Puedes cambiar la variable model-type
para que use diferentes modelos preentrenados de GPT-2. De mayor a menor tamaño, los modelos disponibles son gpt2-xl
, gpt2-large
, gpt2-medium
y gpt2
. Si quieres poder ejecutar el código sobre CPU, cambia el valor de device
a:
7.5.6 Fichero projects/chargpt/charpgt.py#
Este código realiza el entrenamiento de un modelo de lengua a nivel de caracteres a partir del contenido del fichero input.txt
. Puedes usar para el fichero de entrada textos como el Quijote o parte de la obra de Shakespeare.
Puedes cambiar la variable C.model.model_type
para que use modelos de diferentes tamaños (de mayor a menor, gpt2-xl
, gpt2-large
, gpt2-medium
, gpt2
, gpt-mini
, gpt-micro
y gpt-nano
). Puedes ver el número de capas, el número de cabezales y el tamaño de los embeddings de cada modelo en el constructor de la clase GPT
del fichero mingpt/model.py
.
Lanza el programa y déjalo un tiempo entrenando con python charpgt.py
. El modelo se va guardando en la carpeta out
.
7.6 Implementaciones adicionales#
El proyecto MinT incluye diferentes tutoriales con implementaciones desde cero de modelos tipo BERT, GPT, BART o T5. El código es ligeramente más extenso que el que hemos estudiado, pero puede servir para afianzar conocimientos en una fase avanzada. El proyecto x-transformers sigue un enfoque similar.
Existe cierto pique entre los desarrolladores por conseguir una implementación del transformer lo más compacta posible. Algunas de ellas son minGPT, nanoGPT y picoGPT. Un aspecto destacable de estas es que son capaces de cargar los pesos de GPT-2 y realizar inferencia con ellos. Andrej Karpathy, el desarrollador de minGPT y nanoGPT tiene un vídeo muy pedagógico en el que explica el funcionamiento de su implementación.