Motivando la Autoatención

Motivating self-attention.

¿Por qué necesitamos consultas, claves y valores?

¿...auto-atención?

El objetivo de este artículo es ofrecer una explicación no sobre cómo funciona el mecanismo de auto-atención en los transformadores, sino sobre por qué se diseñó de esa manera.

Comenzaremos con una discusión sobre los tipos de habilidades que nos gustaría que tuviera un modelo de comprensión del lenguaje, seguido de una construcción interactiva del mecanismo de auto-atención. En el proceso, descubriremos por qué necesitamos consultas, claves y valores para modelar las relaciones entre las palabras de manera natural, y que la atención QKV es una de las formas más simples de hacerlo.

Este artículo será más esclarecedor para los lectores que hayan encontrado transformadores y auto-atención antes, pero debería ser accesible para cualquier persona familiarizada con algunos conceptos básicos de álgebra lineal. Para aquellos que buscan comprender mejor los transformadores, los remitiré con gusto a esta publicación del blog.

Todas las imágenes son del autor.

Los transformadores se presentan con frecuencia en el contexto de tareas de modelado de secuencia a secuencia, como la traducción de idiomas o, más relevante, la finalización de oraciones. Sin embargo, creo que es más fácil comenzar pensando en el problema del modelado de secuencias y, específicamente, la comprensión del lenguaje.

Entonces, aquí hay una oración que queremos entender:

Pensemos un poco en cómo entendemos esta oración.

  • El perro de Evan, Riley… A partir de esto, sabemos que Riley es el nombre del perro y que Evan es el dueño de Riley.
  • …es tan hiperactivo… Sencillo, “hiperactivo” se refiere al perro Riley, influyendo en nuestra impresión de Riley.
  • …nunca deja de moverse. Esto es interesante. “ella” se refiere a Riley, ya que el perro es el sujeto de la primera frase. Esto nos dice que el perro de Evan, Riley, es en realidad una hembra, lo cual era ambiguo debido al nombre de perro unisex comúnmente utilizado, “Riley”. “nunca deja de moverse” es un conjunto de palabras ligeramente más complejo que elabora sobre “hiperactivo”.

La idea clave aquí es que para construir nuestra comprensión de la oración, estamos constantemente considerando cómo las palabras se relacionan entre sí para mejorar su significado.

En la comunidad de aprendizaje automático, el proceso de mejorar el significado de una palabra a por la presencia de otra palabra b se conoce coloquialmente como ” a atiende a b “, como en la palabra a presta atención a la palabra b.

Una flecha a => b indica que ” a atiende a b ”

Entonces, si queremos que un modelo de aprendizaje automático comprenda el lenguaje, lógicamente querríamos que el modelo tenga la capacidad de que una palabra atienda a otra y actualice de alguna manera su significado.

Esta es precisamente la habilidad que esperamos emular a medida que construimos el mecanismo de atención (propia) en 3 partes.

A continuación, plantearé una serie de preguntas en cursiva. Aliento encarecidamente al lector a detenerse y considerar la pregunta durante un minuto antes de continuar.

Parte 1

Por ahora, centrémonos en la relación entre las palabras “perro” y “Riley”. La palabra “perro” influye fuertemente en el significado de la palabra “Riley”, por lo que queremos que “Riley” atienda a “perro”, y el objetivo aquí es actualizar de alguna manera el significado de la palabra “Riley” en consecuencia.

Para hacer este ejemplo más concreto, supongamos que comenzamos con representaciones vectoriales de cada palabra, cada una de longitud n, basadas en una comprensión libre de contexto de la palabra. Supondremos que este espacio vectorial está bastante bien organizado, lo que significa que las palabras que son más similares en significado están asociadas con vectores que están más cerca en el espacio.

Entonces, tenemos dos vectores, v_dog y v_Riley, que capturan el significado de las dos palabras.

¿Cómo podemos actualizar el valor de v_Riley utilizando v_dog para obtener un nuevo valor para la palabra “Riley” que incorpore el significado de “perro”?

No queremos reemplazar completamente el valor de v_Riley con v_dog, así que digamos que tomamos una combinación lineal de v_Riley y v_dog como el nuevo valor para v_Riley:

v_Riley = get_value('Riley')v_dog = get_value('dog')ratio = .75v_Riley = (ratio * v_Riley) + ((1-ratio) * v_dog)

Esto parece funcionar bastante bien, hemos incorporado un poco del significado de la palabra “perro” en la palabra “Riley”.

Ahora nos gustaría intentar aplicar esta forma de atención a toda la oración actualizando las representaciones vectoriales de cada palabra por las representaciones vectoriales de cada otra palabra.

¿Qué sale mal aquí?

El problema principal es que no sabemos qué palabras deberían asumir los significados de otras palabras. También nos gustaría alguna medida de cuánto debería contribuir el valor de cada palabra a cada otra palabra.

Parte 2

Bien. Así que necesitamos saber cuánto se relacionan dos palabras.

Es hora de intento número 2.

He rediseñado nuestra base de datos vectorial para que cada palabra tenga realmente dos vectores asociados. El primero es el mismo vector de valor que teníamos antes, todavía denotado por v. Además, ahora tenemos vectores unitarios denotados por k que almacenan alguna noción de relaciones entre palabras. Específicamente, si dos vectores k están cerca uno del otro, significa que es probable que los valores asociados con estas palabras influyan en los significados de cada uno.

Con nuestros nuevos vectores k y v, ¿cómo podemos modificar nuestro esquema anterior para actualizar el valor de v_Riley con v_dog de una manera que respete cuánto se relacionan dos palabras?

Continuemos con el mismo negocio de combinación lineal que antes, pero solo si los vectores k de ambos están cerca en el espacio de incrustación. Aún mejor, podemos usar el producto punto de los dos vectores k (que van de 0 a 1 ya que son vectores unitarios) para decirnos cuánto deberíamos actualizar v_Riley con v_dog.

v_Riley, v_dog = get_value('Riley'), get_value('dog')k_Riley, k_dog = get_key('Riley'), get_key('dog')relevance = k_Riley · k_dog # producto puntov_Riley = (relevance) * v_Riley + (1 - relevance) * v_dog

Esto es un poco extraño ya que si la relevancia es 1, v_Riley se reemplaza completamente con v_dog, pero ignoremos eso por un minuto.

Quiero en cambio pensar en lo que sucede cuando aplicamos este tipo de idea a toda la secuencia. La palabra “Riley” tendrá un valor de relevancia con cada otra palabra a través del producto punto de los vectores k. Entonces, tal vez podamos actualizar el valor de cada palabra proporcionalmente al valor del producto punto. Para simplificar, también incluyamos su producto punto consigo mismo como una forma de preservar su propio valor.

oración = "El perro de Evan, Riley, es tan hiperactivo, nunca deja de moverse"palabras = oración.split()# obtenga una lista de valoresvalores = get_values(palabras)# ah sí, eso es lo que significa k por ciertokeys = get_keys(palabras)# obtenga la clave de relevancia de Riley con cada otra palabrariley_index = palabras.index('Riley')riley_key = keys[riley_index]# genere la relevancia de "Riley" con cada otra palabrarelevances = [riley_key · key for key in keys] #aún pretendiendo que python tiene ·# normalice las relevancias para que sumen 1relevances /= sum(relevances)# toma una combinación lineal de valores, ponderada por las relevanciasv_Riley = relevances · valores

Está bien, eso es suficientemente bueno por ahora.

Pero una vez más, afirmo que hay algo mal en este enfoque. No es que ninguna de nuestras ideas haya sido implementada incorrectamente, sino que hay algo fundamentalmente diferente entre este enfoque y cómo realmente pensamos en las relaciones entre las palabras.

Si hay algún punto en este artículo en el que realmente creo que debería detenerse a pensar, es aquí. Incluso aquellos de ustedes que piensan que entienden completamente la atención. ¿Qué hay de malo en nuestro enfoque?

Una pista

¡Las relaciones entre las palabras son inherentemente asimétricas! La forma en que “Riley” presta atención a “perro” es diferente de la forma en que “perro” presta atención a “Riley”. Es mucho más importante que “Riley” se refiera a un perro, no a un humano, que el nombre del perro.

En contraste, el producto punto es una operación simétrica, lo que significa que en nuestra configuración actual, si “a” presta atención a “b”, ¡entonces “b” presta atención igualmente fuerte a “a”! En realidad, esto es algo falso porque estamos normalizando las puntuaciones de relevancia, pero el punto es que las palabras deberían tener la opción de prestar atención de manera asimétrica, incluso si los otros tokens se mantienen constantes.

Parte 3

¡Casi estamos allí! Finalmente, la pregunta es:

¿Cómo podemos extender de manera más natural nuestra configuración actual para permitir relaciones asimétricas?

Bueno, ¿qué podemos hacer con un tipo más de vector? Todavía tenemos nuestros vectores de valor v y nuestro vector de relación k. Ahora tenemos otro vector q para cada token.

¿Cómo podemos modificar nuestra configuración y usar q para lograr la relación asimétrica que queremos?

Aquellos de ustedes que estén familiarizados con cómo funciona la autoatención estarán sonriendo en este punto.

En lugar de calcular la relevancia k_perro · k_Riley cuando “perro” presta atención a “Riley”, en su lugar podemos consultar q_Riley contra la clave k_perro tomando su producto punto. Cuando se calcula en la dirección opuesta, tendremos q_perro · k_Riley en su lugar, ¡relevancia asimétrica!

¡Aquí está todo junto, calculando la actualización para cada valor a la vez!

frase = "El perro de Evan, Riley, es tan hiperactivo que nunca deja de moverse"palabras = frase.split()seq_len = len(palabras)# obtener matrices de consultas, claves y valores, cada una de forma (seq_len, n)Q = array(get_queries(palabras))K = array(get_keys(palabras))V = array(get_values(palabras))relevancias = Q @ K.Tnormalized_relevances = relevancias / relevancias.sum(axis=1)new_V = normalized_relevances @ V

¡Y eso es básicamente la autoatención!

Hay algunos detalles que dejé fuera, pero todas las ideas importantes están ahí.

Para resumir, comenzamos con vectores de valor (v) para representar el significado de cada palabra, pero rápidamente descubrimos que necesitamos vectores clave (k) para tener en cuenta cómo se relacionan las palabras entre sí. Finalmente, para modelar adecuadamente la naturaleza asimétrica de las relaciones entre palabras, presentamos vectores de consulta (q). Casi parece que si todo lo que se nos permite son productos puntos y similares, 3 es el número mínimo de vectores por palabra necesarios para modelar adecuadamente las relaciones entre palabras.

El propósito de este artículo es desentrañar el mecanismo de autoatención de una manera menos abrumadora que el enfoque tradicional de algoritmos en primer lugar. Espero que desde esta perspectiva más motivada por el lenguaje, la elegancia y simplicidad del diseño de consulta-clave-valor puedan mostrar.

Algunos detalles que dejé fuera:

  • En lugar de almacenar 3 vectores para cada token, en su lugar almacenamos un solo vector de incrustación del cual podemos extraer nuestros vectores qkv. El proceso de extracción es solo una proyección lineal.
  • Técnicamente, en toda esta configuración, cada palabra no tiene idea de dónde están las otras palabras en la oración. La autoatención es realmente una operación de conjunto. Entonces, necesitamos incrustar el conocimiento posicional, que normalmente se hace agregando un vector de posición al vector de incrustación. Esto no es completamente trivial ya que los transformadores deberían permitir secuencias de longitud arbitraria. Cómo funciona esto en la práctica está fuera del alcance de este artículo.
  • Una sola capa de autoatención solo nos permite representar relaciones de dos palabras. Pero al componer capas de autoatención, podemos modelar relaciones de nivel superior entre palabras. Dado que la salida de una capa de autoatención es de la misma longitud de secuencia que la secuencia original, esto significa que podemos componerlas. De hecho, los bloques de transformadores son solo capas de autoatención seguidas de bloques de alimentación hacia adelante en la posición. ¡Apila unos cientos de estos, paga unos pocos millones de dólares, y tendrás tu propio LLM! 🙂
OpenAI demostró que se necesitan 512 bloques transformadores para entender esa segunda mitad.

We will continue to update Zepes; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

Inteligencia Artificial

Dominando la Interpretabilidad del Modelo Un Análisis Integral de los Gráficos de Dependencia Parcial

Saber cómo interpretar tu modelo es esencial para entender si no está haciendo cosas extrañas. Cuanto más conozcas tu...

Inteligencia Artificial

Herramienta LLM encuentra y remedia vulnerabilidades de software

La empresa de software Vicarius presentó vuln_GPT, una herramienta de inteligencia artificial generativa que identifi...

Inteligencia Artificial

Píldoras de la impresora 3D

Los científicos del Instituto Max Planck de Informática de Alemania y la Universidad de California, Davis, imprimiero...

Inteligencia Artificial

Destilando lo que sabemos

Los investigadores buscan reducir el tamaño de los modelos GPT grandes.

Inteligencia Artificial

Top 40+ Herramientas Generativas de IA (Diciembre 2023)

ChatGPT – GPT-4 GPT-4 es el último LLM de OpenAI, que es más inventivo, preciso y seguro que sus predecesores. Tambié...