1 puntos por GN⁺ 2024-02-25 | 1 comentarios | Compartir por WhatsApp
  • Se trasladó el flujo de inferencia de GPT-2 a SQL usando solo PostgreSQL y pgvector, reproduciendo desde la tokenización hasta los bloques Transformer y la generación del siguiente token
  • Un LLM generativo se parece a una función determinista que, para la misma entrada, produce las mismas probabilidades de tokens candidatos; el punto donde las respuestas cambian es la etapa de selección probabilística de un token candidato
  • La implementación expresa el tokenizador BPE de GPT-2, 50,257 tokens, embeddings de 768 dimensiones, contexto de 1,024 tokens, 12 bloques, 12 attention heads y feedforward basado en GELU mediante consultas y tablas SQL
  • Fueron necesarios atajos prácticos adaptados al entorno de base de datos, como la falta de soporte de propiedades Unicode en las expresiones regulares de PostgreSQL y las limitaciones de EXP al manejar valores pequeños
  • El ejemplo generó 10 tokens a partir de "Happy New Year! I wish you" y produjo "Happy New Year! I wish you all the best in your new year!"; en el entorno del autor tardó 2 minutos y 44 segundos

Crear un pipeline de inferencia de GPT-2 con SQL

  • ChatGPT respondió que SQL no es adecuado para implementar modelos de lenguaje de gran escala, pero aun así se implementó un pipeline de inferencia de GPT-2 en SQL de PostgreSQL
  • La explicación de implementación tomada como referencia es GPT in 60 Lines of NumPy de Jay Mody, trasladando los mismos componentes a tablas y consultas de base de datos
  • Un LLM generativo puede verse como una función con la forma llm(prompt: str) -> list[tuple[str, float]]
    • La entrada es un prompt de texto
    • La salida es un arreglo de cadenas candidatas para lo que sigue y sus probabilidades
    • Si la matemática interna y los parámetros son los mismos, devuelve el mismo resultado para la misma entrada
  • La razón por la que productos como ChatGPT pueden dar respuestas distintas a la misma pregunta está en la selección probabilística de la etapa de selección del siguiente token, más que en el modelo en sí

Bucle de generación de texto

  • El proceso de generación consiste en convertir el prompt en un arreglo de tokens, llamar repetidamente al modelo, elegir el siguiente token y anexarlo al prompt
  • El flujo básico se compone de estos pasos
    • tokenize(prompt) convierte la cadena en un arreglo de IDs de token
    • gpt2(tokens) calcula probabilidades para 50,257 tokens
    • select_next_token(candidates) selecciona el siguiente token
    • Agrega el token elegido al arreglo
    • Se detiene bajo condiciones como una cantidad definida de tokens, timeout o una stopword
    • detokenize(tokens) restaura el arreglo de tokens como cadena
  • La secuencia acumulada de tokens puede convertirse en texto en lenguaje natural con características que parecen gramática, sintaxis, significado y razonamiento

Implementar el tokenizador BPE en SQL

  • Antes de entrar a la red neuronal, el texto debe convertirse en una lista de números, pero usar directamente puntos de código Unicode hace que el espacio de tokens y la longitud sean ineficientes
  • GPT-2 usa una variante de Byte pair encoding
    • El vocabulario de tokens usa 50,257 puntos de código
    • Incluye secuencias de bytes UTF-8 y el token “end of text”
    • Inicialmente parte de 256 tokens de bytes y agrega como nuevo token los pares de tokens adyacentes que aparecen con frecuencia
    • Repite esta fusión 50,000 veces para crear 50,256 tokens, y al final agrega el token end-of-text
  • El tokenizador de GPT-2 tiene una capa adicional que mapea bytes a caracteres de cadena, y este mapeo está definido en encoder.py de OpenAI GPT-2
  • En la implementación SQL, el vocabulario de tokens descargado de OpenAI se colocó en la tabla tokenizer, y el mapeo byte-caracter se almacenó en la tabla encoder
  • El ejemplo "Mississippilessly" empieza con bytes individuales mediante un CTE recursivo y fusiona repetidamente el mejor par adyacente que pueda fusionarse
    • En el ejemplo, la cantidad de tokens se reduce de 17 a 5
    • Usa el espacio de unos 50k tokens de GPT-2 en lugar del espacio de unos 150k puntos de código Unicode
  • Al procesar varias palabras, GPT-2 divide el texto con una expresión regular y fusiona dentro de cada palabra
    • PostgreSQL no soporta propiedades de caracteres Unicode en expresiones regulares, por lo que se modificó parcialmente la expresión regular original de GPT-2
    • Es posible que esta modificación haya dañado el soporte Unicode adecuado
  • "PostgreSQL is great" se convierte en [6307, 47701, 318, 1049] con el tokenizador SQL
    • Los clústeres de tokens son Post, greSQL, Ġis, Ġgreat
    • Ġ representa un espacio

Embeddings y ventana de contexto

  • Los IDs de token no se usan directamente en el cálculo del modelo, sino que se convierten en vectores de embedding
  • GPT-2 usa embeddings por separado para tokens y posiciones
    • WTE es word token embedding y es una matriz de 50257×768
    • WPE es word position embedding y es una matriz de 1024×768
  • En cada posición de token, se suman el vector WTE y el vector WPE para crear el vector de entrada de la siguiente etapa
  • Como WPE solo tiene 1,024 posiciones, la cantidad máxima de tokens que puede usarse en un prompt de GPT-2 es 1,024
    • Ese número corresponde a la ventana de contexto del LLM
    • Es un hiperparámetro definido al diseñar el modelo y no cambia con el entrenamiento
  • La implementación SQL usa pgvector
    • También sería posible hacerlo en SQL puro definiendo operaciones vectoriales directamente sobre arreglos, pero el rendimiento sería bajo
    • La versión inicial funcionaba con funciones SQL puras, pero era lenta

Desplegar self-attention como consultas SQL

  • El núcleo del Transformer es el mecanismo de self-attention, basado en el paper de 2017 Attention is all you need
  • Attention hace que los vectores de tokens influyan entre sí, permitiendo que la información del inicio del prompt se transmita al último vector
  • La implementación de GPT-2 usa 12 conjuntos de matrices Q, K, V
    • Cada conjunto es un attention head
    • Cada head tiene 64 dimensiones
    • c_attn es una transformación lineal de 768×2304, y el resultado es un vector de 2,304 dimensiones donde Q, K y V están apilados horizontalmente
    • Los pesos y bias se almacenan en las tablas c_attn_w, c_attn_b
  • Antes del cálculo de attention se aplica layer normalization
    • Los parámetros de scale y shift se almacenan en las tablas ln_1_g, ln_1_b
  • En causal self-attention se aplica una causal mask para impedir que los tokens posteriores influyan en los anteriores
    • Los candidatos para el siguiente token del modelo se determinan finalmente a partir del último embedding
    • El flujo de información debe dirigirse hacia el último vector, y los valores intermedios del último vector no deben influir en los vectores anteriores
  • La implementación SQL, para evitar el problema de que EXP de PostgreSQL falle con números muy pequeños en el cálculo de softmax, trata como 0 las entradas menores que -745.13
  • Gracias a la causal mask, aunque se agregue un nuevo token al prompt, los resultados de cálculo para los tokens anteriores no cambian
    • La implementación original de GPT-2 aprovecha esta propiedad
    • La implementación SQL no reutiliza esos resultados para mantener la simplicidad

Multi-head attention y conexiones residuales

  • Los resultados de attention de los 12 heads son de 64 dimensiones cada uno, y se apilan horizontalmente para volver a formar 768 dimensiones
  • Luego, la salida de attention se proyecta mediante la transformación lineal entrenada almacenada en c_proj_w, c_proj_b
  • Al resultado de multi-headed attention se le vuelve a sumar la entrada original
    • Esta conexión residual es una técnica incluida en el paper original de Transformer
    • Se presentó como un diseño para aliviar los problemas de vanishing gradient y exploding gradient durante el entrenamiento

Etapa feedforward y bloques Transformer

  • Después de attention sigue una red neuronal feedforward
  • La etapa feedforward de GPT-2 está compuesta por un perceptrón multicapa de 3 capas
    • Las dimensiones son 768 → 3072 → 768
    • Usa GELU como función de activación
  • Los parámetros de transformación lineal se almacenan en las siguientes tablas
    • mlp_c_fc_w, mlp_c_fc_b
    • mlp_c_proj_w, mlp_c_proj_b
  • La entrada de feedforward también se normaliza primero con los parámetros ln_2
    • ln_2_g, ln_2_b almacenan scale y shift
  • A la salida de feedforward también se le aplica una conexión residual que vuelve a sumar la entrada
  • Esta combinación de attention + feedforward es un bloque, y GPT-2 conecta 12 bloques como un pipeline
    • Cada bloque tiene su propio conjunto de parámetros entrenados
    • En SQL, los bloques se conectan con un CTE recursivo
  • La salida del último bloque se normaliza de nuevo con los parámetros ln_f

Volver al siguiente token

  • El vector de 768 dimensiones de la última posición en la salida final es el embedding del siguiente token
  • Para convertir este vector nuevamente en un token, se reutiliza la matriz WTE empleada inicialmente para los embeddings de tokens
  • La transformación inversa exacta normalmente no es posible
    • El embedding predicho puede no coincidir exactamente con una fila específica de WTE
    • Por lo tanto, se calcula el dot product con cada embedding de token para encontrar tokens cercanos
  • El resultado del dot product entre WTE y el embedding predicho son 50,257 puntajes, es decir, logits
  • Estos puntajes pasan por softmax y se convierten en probabilidades
    • La cantidad de candidatos superiores es top_n
    • El valor que ajusta la distribución de probabilidad es temperature
    • Cuanto más alta es la temperature, mayor es la posibilidad de elegir un token que no esté en primer lugar, y la inferencia se vuelve menos predecible
  • En el ejemplo "PostgreSQL is great", los 5 principales candidatos para el siguiente token son los siguientes
    • Ġfor
    • ,
    • .
    • Ġat
    • Ġto
  • Cuando la temperature cambia a 0.5, 1 y 2, cambia la distribución de probabilidades softmax de los mismos candidatos

Resultado real de inferencia y código

  • El SQL final repite el proceso de seleccionar tokens según sus probabilidades y anexarlos al prompt
  • El modelo en sí es determinista, y el único elemento no determinista son los números aleatorios incluidos en la selección de tokens
  • La configuración del ejemplo es la siguiente
    • Prompt: "Happy New Year! I wish you"
    • Cantidad de tokens generados: 10
    • temperature: 2
    • top_n: 1
    • Uso de SETSEED(0.20231231)
  • En el entorno del autor, la consulta se ejecutó durante 2 minutos y 44 segundos
  • El resultado de salida fue "Happy New Year! I wish you all the best in your new year!"
  • La consulta y el código de instalación están en el repositorio de GitHub quassnoi/explain-extended-2024

1 comentarios

 
GN⁺ 2024-02-25
Comentarios en Hacker News
  • Es hermoso. Yo también estaba metiéndome en una madriguera similar con SQLite, pero todavía no había llegado al punto de incorporar redes neuronales
    Me inspiré en la serie de clases de makemore[0], y como después de aproximadamente 1 hora pasa del enfoque de conteo a las redes neuronales, más o menos hasta ahí fue a donde llegué
    Descomponer esto en el modelo relacional es de verdad un muy buen ejercicio
    [0] https://www.youtube.com/watch?v=PaCmpygFfXo

    • Si sigues viéndolo, la red neuronal termina produciendo exactamente la misma tabla que el enfoque de conteo, y al generar también produce exactamente el mismo resultado
  • Es una buena demo, pero la explicación del enmascaramiento causal en el artículo parece mezclar entrenamiento e inferencia
    El enmascaramiento causal sirve para evitar que durante el entrenamiento se “espíen” tokens futuros, y en arquitecturas tipo GPT también para imponer la autorregresión durante la inferencia
    En la inferencia, de todos modos solo se usa el último token, así que ese token presta atención a toda la secuencia de entrada y, por lo tanto, el siguiente token no se determina solo por el embedding del último token

  • Me pregunto si esto representa con precisión el bucle controlador de GPT: tokenizar el prompt, obtener con gpt2(tokens) las probabilidades de 50257 tokens, elegir el siguiente token, adjuntarlo a la lista de tokens, revisar la condición de parada y al final destokenizar
    Pero esto hace que la máquina de estados parezca estar implementando el algoritmo de Shlemiel the painter, así que me hace cuestionar el costo computacional inherente de la generación

    • Entiendo que lo que la gente llama la ventana de contexto en los modelos grandes de lenguaje significa que hay un máximo de tokens que se mantienen y que los más antiguos se descartan
      Esa ventana es una ventana deslizante
    • Sí, ese es el bucle, y toda la magia está dentro de la función gpt2
    • Esta es una parte muy pequeña del algoritmo
      Se parece más a mostrar únicamente la forma de juntar en una oración los tokens generados
  • Material relacionado: A GPT in 60 Lines of NumPy - https://news.ycombinator.com/item?id=34726115 - febrero de 2023, 146 comentarios

    • Ese material ya aparece al comienzo del artículo
  • En una línea parecida, alguien implementó GPT completo usando funciones de hoja de cálculo e incluso hizo un tutorial en video para verlo junto con ello
    https://spreadsheets-are-all-you-need.ai/

    • El primer video es excelente
      Aunque los LLM me parecen bastante geniales, nunca había necesitado aprender profesionalmente cómo funcionan en realidad, y ese video de 10 minutos me enseñó más que años de leer comentarios crípticos en HN y artículos superficiales de medios generalistas
      Ver esa enorme cantidad de números de punto flotante apilados esperando ser calculados también me hizo entender de forma mucho más intuitiva por qué esta tecnología consume tantas GPU
    • Las hojas de cálculo son una forma natural de explicar los LLM
      Parece que también se podría explicar bien el proceso de entrenamiento si se calcula la derivada de cada parámetro en cada ejemplo de entrenamiento y se muestra explícitamente cómo eso se mapea a cada parámetro
  • Está bien. Algo que hace apenas 1 año parecía una especie de magia ahora está siendo explicado tan bien, casi hasta el punto de que un niño podría seguirlo

    • Esa magia no empezó hace 1 año
      El modelo que explica el artículo es GPT-2, publicado a inicios de 2019
    • No diría que “un niño podría seguirlo”
      Para entender bien este artículo hace falta una base sólida de ciencias de la computación, y el propio título ya es poco accesible para el 99% de la humanidad
  • He evitado por completo GPT y los LLM, pero este enfoque no parece capaz de hacer mucho más que producir texto con cierta fluidez; no parece tener la capacidad de interpretar preguntas y responderlas
    Me pregunto si hay algún post o curso sencillo que explique cómo funciona realmente o que muestre un motor de juguete en Python o algo parecido
    El material educativo que he visto hasta ahora se enfoca más en cómo usar plataformas y no tanto en el funcionamiento interno

  • Curiosamente, el aprendizaje automático moderno no requiere completitud de Turing
    Y aun así estamos considerando la posibilidad de AGI, así que sería bastante interesante si la conclusión fuera que la completitud de Turing no es necesaria

    • Parece que sí hace falta completitud de Turing
      Por una razón simple: yo puedo seguir mentalmente la ejecución de código Turing-completo
    • La inferencia de tokens por sí sola no es Turing-completa, pero si la salida puede producir efectos secundarios, por ejemplo modificar el prompt de la siguiente iteración, entonces ya es una historia completamente distinta
  • El artículo fue excelente, y la explicación de cada componente fue clara y bastante completa, así que fue una buena lectura
    Pero por accidente le di a “+ expand source” y, después de ver ese monstruo impresionante, terminé entendiendo por qué ChatGPT dice que “SQL no es adecuado para implementar modelos grandes de lenguaje”

    • Yo también le di clic y no pude encontrar cómo volver a cerrarlo
  • La frase “el Unicode normal no encaja bien con las redes neuronales” no es cierta. Basta ver ByT5
    Lo que el artículo llama “alfabeto” normalmente se llama vocabulario, y si usas bytes UTF-8 como vocabulario, no son 149186 tokens sino 256
    Eso es exactamente lo que hace ByT5

    • El punto no es que no funcione en absoluto, sino que no funciona tan bien como otros enfoques que tenemos
      El hecho de que todos los modelos con mejor rendimiento del mercado usen tokenización es prueba de ello
      No es ningún secreto que la tokenización, en el fondo, se parece más a un hack, y que idealmente en algún momento se querría eliminar de una forma u otra (https://twitter.com/karpathy/status/1657949234535211009)
      En principio, los defectos de la tokenización a nivel de bytes pueden compensarse con modelos más grandes y contextos más amplios, pero en la práctica se necesitan muchos más recursos para entrenar un modelo con el mismo nivel de inteligencia
      Por supuesto, también hay tareas específicas en las que la tokenización incluso perjudica la inteligencia, como contar las letras de una palabra