Aprende cómo funciona un Transformer, la arquitectura de modelos de aprendizaje automático, que se ha impuesto en tareas de procesamiento de lenguaje natural (NLP). Traducción a español del interesantísimo artículo de Jay Alammar titulado en inglés «El Transformer ilustrado»
An original text written by Jay Alammar, originally published in
https://jalammar.github.io/illustrated-transformer/
***
En la publicación anterior, analizamos la «Atención«, un mecanismo omnipresente en los modelos modernos de aprendizaje profundo. La atención es un concepto que ayudó a mejorar el rendimiento de las aplicaciones de traducción automática neuronal. En esta publicación, analizaremos «el Transformer«, una arquitectura que utiliza la atención para aumentar la velocidad con la que se pueden entrenar estos modelos de Machine Learning. El Transformer supera al modelo de traducción automática neuronal de Google en tareas específicas. Sin embargo, el mayor beneficio proviene de cómo el Transformer usa la paralelización. De hecho, Google Cloud recomienda usar el Transformer como modelo de referencia para su oferta de Cloud TPU. Intentemos descomponer el modelo y veamos cómo funciona.
DISCLAIMER: Traducción gratuita a Español realizada por la empresa de traducción Ibidem Group. Si necesitas traducción de vídeos y subtítulos, o traducciones de documentos para tu empresa, te aconsejamos buscar una agencia de traducción que trabaje con traductores profesionales humanos, nativos. Si necesitas traducciones en Madrid o traductores en Barcelona, contacta con nosotros!
…
El Transformer fue presentado en el artículo Attention is All You Need. Una implementación de TensorFlow está disponible como parte del paquete Tensor2Tensor. El grupo de NLP de Harvard creó una guía que anota el documento con la implementación de PyTorch. En esta publicación, intentaremos simplificar un poco las cosas e introducir los conceptos uno por uno para que sea más fácil de entender para las personas sin un conocimiento profundo del tema.
Actualización de 2020: he creado un video titulado «the Narrated Transformer» (Introducción al Transformer), con un enfoque más light del tema.
Comencemos mirando el modelo como si fuera una caja negra. Si se tratara de una aplicación de traducción automática, tomaría una oración en un idioma y generaría una traducción a otro idioma.
Si miramos dentro de Optimus Prime, veremos un encoder, un decoder y conexiones entre ellos.
El encoder es una pila de codificadores (el paper apila seis uno encima del otro; no hay nada mágico en el número seis, definitivamente se puede experimentar con otros esquemas). El decoder es una pila de decodificadores, en este caso también 6.
Los encoders son todos idénticos en estructura (pero no comparten pesos). Cada uno se divide en dos subcapas:
Las entradas del codificador fluyen primero a través de una capa de autoatención (self-attention), una capa que ayuda al codificador a ver otras palabras en la oración de entrada mientras codifica una palabra específica. Veremos más de cerca la autoatención más adelante en la publicación.
Las salidas de la capa de autoatención se envían a una red neuronal de avance (feed-forward). La misma red de feed-forward se aplica de forma independiente a cada posición.
El decodificador tiene ambas capas, pero entre ellas hay una capa de atención que ayuda al decodificador a enfocarse en partes relevantes de la oración de entrada (similar a lo que hace la atención en los modelos seq2seq).
Ahora que hemos visto los componentes principales del modelo, comencemos a ver los diversos vectores/tensores y cómo fluyen entre estos componentes para convertir la entrada de un modelo entrenado en una salida.
Como es el caso en las aplicaciones NLP en general, comenzamos convirtiendo cada palabra de entrada en un vector utilizando un algoritmo de incrustación.
La incrustación solo ocurre en el codificador de más abajo. La abstracción que es común a todos los codificadores es que reciben una lista de vectores, cada uno de tamaño 512: en el codificador inferior, sería la palabra incrustaciones, pero en otros codificadores, sería la salida del codificador que está directamente debajo. . El tamaño de esta lista es un hiperparámetro que podemos configurar; básicamente, sería la longitud de la oración más larga en nuestro conjunto de datos de entrenamiento.
Después de incrustar las palabras en nuestra secuencia de entrada, cada una de ellas fluye a través de cada una de las dos capas del codificador.
Aquí comenzamos a ver una propiedad clave del Transformer, que es que la palabra en cada posición fluye a través de su propio camino en el codificador. Existen dependencias entre estas rutas en la capa de autoatención. Sin embargo, la capa de avance no tiene esas dependencias y, por lo tanto, las diversas rutas se pueden ejecutar en paralelo mientras fluye a través de la capa de avance.
A continuación, cambiaremos el ejemplo a una oración más corta y veremos qué sucede en cada subcapa del codificador.
Como ya mencionamos, un codificador recibe una lista de vectores como entrada. Procesa esta lista al pasar estos vectores a una capa de ‘autoatención’, luego a una red neuronal de avance, luego envía la salida hacia arriba al siguiente codificador.
No se deje engañar por la palabra «autoatención» como si fuera un concepto con el que todos deberían estar familiarizados. Personalmente, nunca me había topado con el concepto hasta que leí el documento Attention is All You Need. Vamos a destilar cómo funciona.
Digamos que la siguiente oración es una oración de entrada que queremos traducir:
” The animal didn't cross the street because it was too tired
”
¿A qué se refiere “eso” en esta oración? ¿Se refiere a la calle o al animal? Es una pregunta simple para un ser humano, pero no tan simple para un algoritmo.
Cuando el modelo está procesando la palabra “eso”, la autoatención le permite asociar “eso” con “animal”.
A medida que el modelo procesa cada palabra (cada posición en la secuencia de entrada), la atención propia le permite observar otras posiciones en la secuencia de entrada en busca de pistas que puedan ayudar a codificar mejor esta palabra.
Si está familiarizado con los RNN, piense en cómo mantener un estado oculto permite que un RNN incorpore su representación de palabras/vectores anteriores que ha procesado con el actual que está procesando. La autoatención es el método que usa el Transformer para incorporar la «comprensión» de otras palabras relevantes a la que estamos procesando actualmente.
Asegúrese de revisar el cuaderno Tensor2Tensor donde puede cargar un modelo de Transformer y examinarlo usando esta visualización interactiva.
Primero veamos cómo calcular la autoatención usando vectores, luego procedamos a ver cómo se implementa realmente, usando matrices.
El primer paso para calcular la autoatención es crear tres vectores a partir de cada uno de los vectores de entrada del codificador (en este caso, la incrustación de cada palabra). Entonces, para cada palabra, creamos un vector de consulta, un vector clave y un vector de valor. Estos vectores se crean multiplicando la incrustación por tres matrices que entrenamos durante el proceso de entrenamiento.
Tenga en cuenta que estos nuevos vectores son más pequeños en dimensión que el vector incrustado. Su dimensionalidad es 64, mientras que los vectores de entrada/salida de incrustación y codificador tienen una dimensionalidad de 512. No TIENEN QUE ser más pequeños, esta es una elección de arquitectura para hacer que el cálculo de la atención de múltiples cabezas (en su mayoría) sea constante.
¿Qué son los vectores de «consulta», «clave» y «valor»?
Son abstracciones que son útiles para calcular y pensar en la atención. Una vez que continúe leyendo cómo se calcula la atención a continuación, sabrá prácticamente todo lo que necesita saber sobre el papel que juega cada uno de estos vectores.
El segundo paso para calcular la autoatención es calcular una puntuación. Digamos que estamos calculando la autoatención de la primera palabra en este ejemplo, «Pensando». Necesitamos puntuar cada palabra de la oración de entrada contra esta palabra. La puntuación determina cuánto enfoque colocar en otras partes de la oración de entrada a medida que codificamos una palabra en una posición determinada.
La puntuación se calcula tomando el producto escalar del vector de consulta con el vector clave de la palabra respectiva que estamos puntuando. Entonces, si estamos procesando la autoatención de la palabra en la posición #1 , la primera puntuación sería el producto escalar de q1 y k1 . La segunda puntuación sería el producto escalar de q1 y k2 .
Los pasos tercero y cuarto consisten en dividir las puntuaciones por 8 (la raíz cuadrada de la dimensión de los vectores clave utilizados en el documento: 64). Esto lleva a tener gradientes más estables. Podría haber otros valores posibles aquí, pero este es el predeterminado), luego pase el resultado a través de una operación softmax. Softmax normaliza las puntuaciones para que todas sean positivas y sumen 1.
Esta puntuación softmax determina cuánto se expresará cada palabra en esta posición. Claramente, la palabra en esta posición tendrá la puntuación de softmax más alta, pero a veces es útil prestar atención a otra palabra que sea relevante para la palabra actual.
El quinto paso es multiplicar cada vector de valor por la puntuación softmax (en preparación para resumirlos). La intuición aquí es mantener intactos los valores de las palabras en las que queremos centrarnos y ahogar las palabras irrelevantes (multiplicándolas por números pequeños como 0,001, por ejemplo).
El sexto paso es sumar los vectores de valores ponderados. Esto produce la salida de la capa de autoatención en esta posición (para la primera palabra).
Eso concluye el cálculo de la autoatención. El vector resultante es uno que podemos enviar a la red neuronal de avance. Sin embargo, en la implementación real, este cálculo se realiza en forma de matriz para un procesamiento más rápido. Así que veamos eso ahora que hemos visto la intuición del cálculo a nivel de palabra.
El primer paso es calcular las matrices de consulta, clave y valor. Lo hacemos empaquetando nuestras incrustaciones en una matriz X y multiplicándola por las matrices de peso que hemos entrenado ( WQ , WK , WV ).
Finalmente , dado que estamos tratando con matrices, podemos condensar los pasos dos a seis en una fórmula para calcular los resultados de la capa de autoatención.
El documento refinó aún más la capa de autoatención al agregar un mecanismo llamado atención de «múltiples cabezas». Esto mejora el rendimiento de la capa de atención de dos maneras:
Si hacemos el mismo cálculo de autoatención que describimos anteriormente, solo ocho veces diferentes con diferentes matrices de peso, terminamos con ocho matrices Z diferentes
Esto nos deja con un pequeño desafío. La capa de avance no espera ocho matrices, espera una sola matriz (un vector para cada palabra). Así que necesitamos una forma de condensar estos ocho en una sola matriz.
¿Como hacemos eso? Concatenamos las matrices y luego las multiplicamos por una matriz de pesos adicional WO.
Eso es prácticamente todo lo que hay para la autoatención de múltiples cabezas. Es un buen puñado de matrices, me doy cuenta. Permítanme tratar de ponerlos todos en una imagen para que podamos verlos en un solo lugar.
Ahora que hemos tocado las cabezas de atención, revisemos nuestro ejemplo anterior para ver dónde se enfocan las diferentes cabezas de atención a medida que codificamos la palabra «eso» en nuestra oración de ejemplo:
Sin embargo, si agregamos todas las cabezas de atención a la imagen, las cosas pueden ser más difíciles de interpretar:
Una cosa que falta en el modelo tal como lo hemos descrito hasta ahora es una forma de explicar el orden de las palabras en la secuencia de entrada.
Para abordar esto, el Transformer agrega un vector a cada incorporación de entrada. Estos vectores siguen un patrón específico que aprende el modelo, lo que le ayuda a determinar la posición de cada palabra o la distancia entre diferentes palabras en la secuencia. La intuición aquí es que agregar estos valores a las incrustaciones proporciona distancias significativas entre los vectores de incrustaciones una vez que se proyectan en vectores Q/K/V y durante la atención del producto escalar.
Si asumiéramos que la incrustación tiene una dimensionalidad de 4, las codificaciones posicionales reales se verían así:
¿Cómo podría ser este patrón?
En la siguiente figura, cada fila corresponde a una codificación posicional de un vector. Entonces, la primera fila sería el vector que agregaríamos a la incrustación de la primera palabra en una secuencia de entrada. Cada fila contiene 512 valores, cada uno con un valor entre 1 y -1. Los hemos codificado por colores para que el patrón sea visible.
La fórmula para la codificación posicional se describe en el artículo (sección 3.5). Puede ver el código para generar codificaciones posicionales en get_timing_signal_1d()
. Este no es el único método posible para la codificación posicional. Sin embargo, ofrece la ventaja de poder escalar a longitudes de secuencias no vistas (por ejemplo, si se le pide a nuestro modelo entrenado que traduzca una oración más larga que cualquiera de las de nuestro conjunto de entrenamiento).
Actualización de julio de 2020: La codificación posicional que se muestra arriba es de la implementación de Transformerr2Transformer. El método que se muestra en el documento es ligeramente diferente en el sentido de que no concatena directamente, sino que entrelaza las dos señales. La siguiente figura muestra cómo se ve. Aquí está el código para generarlo :
Un detalle de la arquitectura del codificador que debemos mencionar antes de continuar es que cada subcapa (autoatención, ffnn) en cada codificador tiene una conexión residual a su alrededor, y va seguida de un paso de normalización de capa .
Si vamos a visualizar los vectores y la operación de norma de capa asociada con la atención propia, se vería así:
Esto también se aplica a las subcapas del decodificador. Si tuviéramos que pensar en un Transformer de 2 codificadores y decodificadores apilados, se vería así:
Ahora que hemos cubierto la mayoría de los conceptos en el lado del codificador, básicamente también sabemos cómo funcionan los componentes de los decodificadores. Pero echemos un vistazo a cómo funcionan juntos.
El codificador comienza procesando la secuencia de entrada. La salida del codificador superior se transforma luego en un conjunto de vectores de atención K y V. Estos deben ser utilizados por cada decodificador en su capa de «atención codificador-decodificador» que ayuda al decodificador a enfocarse en los lugares apropiados en la secuencia de entrada:
Los siguientes pasos repiten el proceso hasta que un especialse alcanza el símbolo que indica que el decodificador del Transformer ha completado su salida. La salida de cada paso se alimenta al decodificador inferior en el siguiente paso de tiempo, y los decodificadores aumentan sus resultados de decodificación al igual que lo hicieron los codificadores. Y tal como hicimos con las entradas del codificador, incrustamos y agregamos codificación posicional a esas entradas del decodificador para indicar la posición de cada palabra.
Las capas de autoatención en el decodificador funcionan de una manera ligeramente diferente a la del codificador:
En el decodificador, la capa de autoatención solo puede atender posiciones anteriores en la secuencia de salida. Esto se hace enmascarando posiciones futuras (configurándolas en -inf
) antes del paso softmax en el cálculo de autoatención.
La capa de «Atención de codificador-descodificador» funciona como la autoatención de varios encabezados, excepto que crea su matriz de consultas a partir de la capa debajo de ella y toma la matriz de claves y valores de la salida de la pila del codificador.
La pila del decodificador genera un vector de flotadores. ¿Cómo convertimos eso en una palabra? Ese es el trabajo de la capa lineal final, seguida de una capa Softmax.
La capa lineal es una red neuronal simple totalmente conectada que proyecta el vector producido por la pila de decodificadores en un vector mucho, mucho más grande llamado vector logits.
Supongamos que nuestro modelo conoce 10 000 palabras únicas en inglés (el «vocabulario de salida» de nuestro modelo) que ha aprendido de su conjunto de datos de entrenamiento. Esto haría que el vector logits tuviera 10 000 celdas de ancho, cada celda correspondiente a la puntuación de una palabra única. Así es como interpretamos la salida del modelo seguido de la capa Lineal.
La capa softmax luego convierte esos puntajes en probabilidades (todos positivos, todos suman 1.0). Se elige la celda con la probabilidad más alta y la palabra asociada a ella se produce como salida para este paso de tiempo.
Ahora que hemos cubierto todo el proceso de pase hacia adelante a través de un Transformer entrenado, sería útil echar un vistazo a la intuición de entrenar el modelo.
Durante el entrenamiento, un modelo no entrenado pasaría exactamente por el mismo pase hacia adelante. Pero dado que lo estamos entrenando en un conjunto de datos de entrenamiento etiquetado, podemos comparar su salida con la salida correcta real.
Para visualizar esto, supongamos que nuestro vocabulario de salida solo contiene seis palabras («a», «soy», «i», «gracias», «estudiante» y «<eos>» (abreviatura de ‘fin de oración’)) .
Una vez que definimos nuestro vocabulario de salida, podemos usar un vector del mismo ancho para indicar cada palabra en nuestro vocabulario. Esto también se conoce como codificación one-hot. Entonces, por ejemplo, podemos indicar la palabra «am» usando el siguiente vector:
Después de este resumen, analicemos la función de pérdida del modelo: la métrica que estamos optimizando durante la fase de entrenamiento para conducir a un modelo entrenado y, con suerte, sorprendentemente preciso.
Digamos que estamos entrenando a nuestro modelo. Digamos que es nuestro primer paso en la fase de entrenamiento, y lo estamos entrenando en un ejemplo simple: traducir «merci» en «gracias».
Lo que esto significa es que queremos que el resultado sea una distribución de probabilidad que indique la palabra «gracias». Pero dado que este modelo aún no está capacitado, es poco probable que eso suceda todavía.
¿Cómo se comparan dos distribuciones de probabilidad? Simplemente restamos uno del otro. Para obtener más detalles, observe la entropía cruzada y la divergencia de Kullback-Leibler .
Pero tenga en cuenta que este es un ejemplo demasiado simplificado. De manera más realista, usaremos una oración de más de una palabra. Por ejemplo, entrada: «je suis étudiant» y salida esperada: «soy un estudiante». Lo que esto realmente significa es que queremos que nuestro modelo genere sucesivamente distribuciones de probabilidad donde:
<end of sentence>
símbolo ‘ ‘, que también tiene asociada una celda del vocabulario de 10.000 elementos.Después de entrenar el modelo durante suficiente tiempo en un conjunto de datos lo suficientemente grande, esperamos que las distribuciones de probabilidad producidas se vean así:
Ahora, debido a que el modelo produce los resultados uno a la vez, podemos suponer que el modelo selecciona la palabra con la probabilidad más alta de esa distribución de probabilidad y desecha el resto. Esa es una forma de hacerlo (llamada decodificación codiciosa). Otra forma de hacerlo sería mantener, por ejemplo, las dos palabras principales (por ejemplo, ‘I’ y ‘a’), luego, en el siguiente paso, ejecutar el modelo dos veces: una vez suponiendo que la primera posición de salida era la palabra ‘I’, y otra vez asumiendo que la primera posición de salida fue la palabra ‘a’, y se mantiene la versión que produjo menos error considerando ambas posiciones #1 y #2. Repetimos esto para las posiciones #2 y #3…etc. Este método se llama “búsqueda de haz”, donde en nuestro ejemplo, beam_size era dos (lo que significa que en todo momento, se mantienen en la memoria dos hipótesis parciales (traducciones inconclusas), y top_beams también es dos (lo que significa que devolveremos dos traducciones). Ambos son hiperparámetros con los que puede experimentar.
Espero que hayas encontrado este lugar útil para comenzar a romper el hielo con los principales conceptos del Transformer. Si quieres profundizar más, te sugiero los siguientes pasos:
Trabajos de seguimiento:
Ver: Conferencia sobre el estado del arte del aprendizaje profundo
del MIT que hace referencia a esta publicación
Gracias a Illia Polosukhin , Jakob Uszkoreit , Llion Jones , Lukasz Kaiser , Niki Parmar y Noam Shazeer por brindar sus comentarios sobre las versiones anteriores de esta publicación.
Por favor, contáctame en Twitter para cualquier corrección o comentario.Escrito el 27 de junio de 2018
Debates: Hacker News (65 puntos, 4 comentarios) , Reddit r/MachineLearning (29 puntos, 3 comentarios)
Dirección de correo electrónico
Este trabajo tiene una licencia internacional Creative Commons Attribution-NonCommercial-ShareAlike 4.0 .
Ejemplo de atribución:
Alammar, J (2018). El Transformer ilustrado [Entrada de blog]. Obtenido de https://jalammar.github.io/illustrated-transformer/
Articulos relacionados
Descubre qué es y cómo funciona la nueva Inteligencia Artificial Explicable (Explainable AI). Traducción al español de un interesante artículo de Jay Alammar, uno de los principales divulgadores sobre Inteligencia Artificial y Machine Learning.
Aprende cómo funciona Stable Diffusion y cómo consigue crear imágenes de alta calidad. Traducción al español de un interesante artículo de Jay Alammar, uno de los nombres más relevantes en el sector de modelos linguísticos y aprendizaje automático.
Aprende cómo funciona realmente Chat GPT-3, el famosísimo y gigantesco modelo de lenguaje o inteligencia artifical generativa, de la mano de Jay Alammar, un reputado científico de datos y escritor técnico conocido por sus contribuciones en el campo de la inteligencia artificial y el...