KV Cache está consumiendo tu VRAM. Así es como Google lo solucionó con TurboQuant.

En cualquier momento con Transformers, ya sabes que la atención es el cerebro de toda la operación. Es lo que permite al modelo determinar qué tokens se comunican entre sí y que un mecanismo es responsable de casi todo lo que hacen los LLM impresionantes.

La atención funciona con tres componentes: consulta (Q), clave (K) y valor (V).[1]. El producto escalar entre Q y K es lo que le dice al modelo cuánto debe centrarse cada token en los demás, y eso es esencialmente el núcleo de lo que hace la atención.

Ahora bien, llamar la atención sobre el “cerebro” también significa que tiene un costo. Durante la inferencia, cada vez que se predice un nuevo token, las matrices K y V también se recalculan para todos los tokens anteriores. Entonces, si ya hay 90 tokens y el modelo predice el 91, regresa y vuelve a calcular KV para los 90. ¿No es esta repetitividad un desperdicio?

El caché KV cambió esto. La idea es sencilla: en lugar de volver a calcular, simplemente almacenar las matrices K y V en VRAM y reutilizarlas durante la inferencia. Suena simple, ¿verdad? Probablemente esa sea la razón por la que todos los principales LLM lo han adoptado; es difícil discutir la caída en la latencia.

Aunque la caché de KV fue un lado positivo para los LLM, planteó más desafíos. Introdujo una sobrecarga de memoria adicional. Puede que esto no sea un gran problema para los SLM, pero los mega-LLM con miles de millones de parámetros ahora se volvieron más difíciles de cargar en las máquinas. Aproximadamente entre un 20 y un 30 % de VRAM adicional se consume únicamente con la caché KV. La mayor limitación es que esta sobrecarga no es estática, sino que sigue creciendo. Esto puede crecer hasta el tamaño del modelo con contextos largos o más usuarios simultáneos, ya que cada usuario obtiene su propia caché KV. Para resolver esto, muchos investigadores introdujeron diferentes enfoques como la atención de consultas agrupadas (GQA).[2], Atención paginada (VLLM)[3], Cuantización (a 4 bits u 8 bits). Sin embargo, todo esto ayudó con el problema de la sobrecarga de memoria, pero la precisión tuvo que verse comprometida por eso. No había ninguna solución para comprimirlos y conservar la precisión original. Luego vino TurboQuant de Google, que sorprendentemente logra hacer ambas cosas. Los autores también demuestran que esta solución se sitúa en el óptimo teórico, lo mejor posible para este tipo de problema.

TurboQuant viene con dos etapas: PolarQuant y Corrección Residual[4].

PolarQuant (Etapa 1): Comprime las matrices K y V.

Corrección Residual (Etapa 2): Corrige el error de cuantificación que queda después de PolarQuant, recuperando la información perdida.

La aplicación de ambos de forma secuencial es lo que la diferencia de la cuantificación tradicional. Aquí hay un desglose visual:

La cuantificación tradicional reconstruye el vector. TurboQuant reconstruye qué atención realmente se necesita. Imagen del autor.

Esto debería darle una idea clara del proceso de TurboQuant y en qué se diferencia de la cuantificación tradicional de la que hablamos. Antes de sumergirnos en cada etapa, descubramos otra cosa importante: dado que estamos hablando de reducir la sobrecarga de memoria, ¿qué almacena exactamente TurboQuant en la caché? ¿Y cuánta menos memoria ocupa realmente? Veámoslo visualmente a continuación:

La imagen muestra la diferencia intuitiva entre el modelo original, la cuantificación tradicional y TurboQuant en términos del almacenamiento que llevan y un ejemplo que muestra la compresión y precisión que ofrecen.
Eficiencia del almacenamiento frente a precisión: comparación de las diferencias arquitectónicas entre la compresión INT8/INT4 estándar y el canal de almacenamiento basado en residuos de TurboQuant. Imagen del autor.

Es posible que todavía no entiendas completamente lo que significan Idx, QJL y ε, pero quedarán claros a medida que analicemos este proceso paso a paso. Por ahora, la tabla anterior le da la idea esencial: muestra exactamente lo que almacena TurboQuant en comparación con la cuantificación tradicional.

¿La conclusión clave? Aunque ambas técnicas logran tasas de compresión idénticas (el escalar ε adicional es insignificante una vez que se distribuye entre las dimensiones del vector), TurboQuant mantiene la precisión a la par con el modelo original de precisión total. De hecho, el documento oficial informa que TurboQuant ofrece una compresión de caché de más de 4,5 a 5 veces KV, lo que equivale a 3,5 a 2,5 bits efectivos por canal, con una pérdida de precisión casi nula en la práctica. Eso es bastante fenomenal.

Ahora veamos el flujo real paso a paso de TurboQuant, la secuencia exacta que vimos anteriormente en el diagrama.

Etapa 1 (PolarQuant):

Esto implica dos operaciones principales, Rotación y LLoyd Max Quantization.

Pero, en primer lugar, ¿por qué la rotación? El principal defecto de la cuantificación tradicional es lo mal que maneja los valores atípicos. Para concretar esto, supongamos que tenemos un vector clave de 4 dimensiones para un token: [0.125, 0.103, 0.220, 6.030] (valores atípicos como este son bastante comunes en las claves de atención). Ahora bien, si los cuantificamos tradicionalmente, el cuantificador tiene que estirar sus niveles limitados para cubrir ese enorme pico de 6,030. ¿El resultado? Algo así como [0, 0, 0, 1], casi toda la información se pierde.

Girar el vector resuelve este problema. Este "giro" del vector en un espacio de alta dimensión (y = R*x, donde R es una matriz de rotación ortogonal aleatoria) elimina el pico y sumerge su energía en las otras coordenadas, lo que hace que la distribución del vector sea suave (isotrópica). Los valores cambian pero la magnitud general sigue siendo la misma. Después de la rotación, el mismo vector de ejemplo podría verse algo más equilibrado como [1,42, -0,85, 2,31, 0,97].

Dos gráficos de coordenadas 3D que muestran la transformación de datos. El gráfico de la izquierda, denominado 'Spiky (antes)', muestra puntos de datos agrupados con una enorme dimensión atípica roja. Una flecha denominada
De picos a esferas: la rotación aleatoria elimina las dimensiones atípicas "puntiagudas", distribuyendo la energía atípica entre las dimensiones para lograr una distribución isotrópica (uniforme). Imagen del autor.

Esta distribución suavizada para vectores de alta dimensión nos acerca a la distribución gaussiana (en la práctica, el vector rotado se distribuye uniformemente en la esfera unitaria, como se espera del teorema del límite central). Como resultado, cada coordenada sigue una distribución similar a la beta sobre la energía presente en el vector.

xi2∑j=1dxj2∼Beta(12,d−12)frac{x_i^2}{sum_{j=1}^d x_j^2} sim text{Beta}left(frac{1}{2}, frac{d-1}{2}right)

donde d es la dimensión de la cabeza
Consejo (omita si no está familiarizado con los detalles matemáticos): esto está vinculado a una propiedad fundamental en las estadísticas multivariadas, donde si X1, X2,…. Xd ~ N(0,1) son independientes y están distribuidas idénticamente (iid), luego Xi2 ~ distribución Chi-Cuadrado y existe un teorema que establece que:

Si U∼χ2(ν1) y V∼χ2(ν2), entonces:UU+V∼Beta(ν12,ν22)text{Si } U sim chi^2(nu_1) text{ y } V sim chi^2(nu_2), text{ entonces:} \ frac{U}{U+V} sim text{Beta}left( frac{nu_1}{2}, frac{nu_2}{2} right)

Ahora la rotación nos ha llevado al punto en que sabemos cómo es la distribución de coordenadas. Ahora viene la segunda operación importante en la etapa 1: Lloyd Max Quantization:

La idea detrás de Lloyd-Max [5,6] es colocar los niveles de cuantificación (centroides) exactamente en los lugares correctos para minimizar el error cuadrático medio. Básicamente se trata de una agrupación inteligente de datos 1D. Simplifiquémoslo con un ejemplo. Tomando el mismo vector rotado que el anterior: [1,42, -0,85, 2,31, 0,97]. Supongamos que aquí estamos haciendo una cuantificación de 1 bit.

El número de centroides o niveles aquí es 2 bits = 21 = 2. Tomemos niveles aleatorios iniciales como [0.5, 1.5], su punto medio o límite es (0.5 + 1.5)/2 ~ 1, por lo que los valores cuantificados ahora se convierten en [1.5, 0.5, 1.5, 0.5] (Todos los valores por debajo de 1 pertenecen a 0.5 y por encima de 1 pertenecen a 1.5). Esa es la idea de la cuantización, ¿verdad? pero lo que notamos es que hay mucho error aquí, es decir, el MSE es muy alto. Por lo tanto, tenemos que encontrar niveles óptimos tales que el MSE sea mínimo y los valores estén mejor representados alrededor de ellos. Esto se hace mediante la cuantificación de Llyod Max: desde ahora los nuevos valores son [1.5, 0.5, 1.5, 0.5], asignando dos grupos:
-0,85, 0,97 –> grupo de nivel 0,5,
1,42, 2,31 –> grupo de nivel 1,5.
Tomando su media, la media del grupo de 0,5 niveles ~ 0,06 y la media del grupo de 1,5 ~ 1,86.
Entonces, ahora los nuevos niveles han cambiado de [0.5, 1.5] a [0.06, 1.86], y nuestro nuevo límite ahora es (0.06+1.86)/2 ~ 0.96, ahora los valores inferiores a 0.96 pertenecen al nivel 0.06 y los valores superiores a 0.96 pertenecen al nivel 1.86. Esto se repite hasta que llegamos a un punto en el que MSE no mejora.

Consejo: Hay una razón estadística fundamental por la que esto funciona: el valor que minimiza el error al cuadrado para cualquier grupo de puntos es simplemente su media.

Pero espera, ejecutar este proceso repetitivo en cada nuevo vector durante la inferencia sería demasiado lento, ¿verdad? Aquí es donde la rotación vuelve a dar sus frutos. Debido a que ahora cada coordenada sigue la misma distribución conocida (la Beta que vimos antes), no tenemos que calcular un libro de códigos Lloyd-Max nuevo para cada nuevo dato. En cambio, el libro de códigos óptimo depende sólo de dos parámetros fijos: la dimensión del cabezal (d) y el número de bits (b). Lo calculamos una vez, sin conexión y lo reutilizamos para siempre. A continuación se muestra un fragmento de este libro de códigos:

Libros de códigos Lloyd-Max precalculados para diferentes anchos de broca y dimensiones de cabezal. La distribución de cada coordenada es siempre Beta(1/2, (d−1)/2). Imagen del autor.

Los valores cuantificados no se almacenan en flotante, sino en forma de índices (idx) de niveles. Ejemplo: si los niveles fueran 8, entonces su forma indexada (idx) es 0, 1, 2, 3, 4, 5, 6, 7. Por lo tanto, se necesitan 3 bits para almacenar cada valor.

Nota: En la etapa 1 de TurboQuant (PolarQuant), el índice almacenado real (idx) utiliza b-1 bits por dimensión (tamaño del libro de códigos = 2{b-1}), no b bits. El bit adicional por dimensión proviene de la corrección residual QJL en la Etapa 2 (lo mismo se mencionó en el diagrama de comparación de almacenamiento de este artículo anterior, espero que ahora quede claro) La tabla anterior muestra la configuración general de Lloyd-Max; TurboQuant divide inteligentemente el presupuesto para dejar espacio para esa corrección.

Estos índices se almacenan en caché hasta que se desaloja el token. La descuantificación ocurre sobre la marcha siempre que se necesita la K de ese token para llamar la atención, se busca idx en el libro de códigos para recuperar los valores flotantes para cada índice, y esta matriz luego se multiplica con la transpuesta de la matriz de rotación original para recuperar K̂ en el espacio original. Esto completa la primera etapa.

Por tanto, finalmente podemos extraer residuos:

ε = Matriz K original – Matriz K̂ [descuantificada]

Etapa 2 (Corrección Residual):

Ahora que tenemos los residuos, sigue la parte más intrigante de TurboQuant.

La cuantificación tradicional ni siquiera analizaba los residuos. Sin embargo, TurboQuant no descarta este residuo. En cambio, plantea una pregunta inteligente: cualquier información que se haya perdido durante la compresión de la Etapa 1, ¿podemos extraer sus características esenciales en lugar de almacenarla por completo? Piense en ello como si se hicieran preguntas simples de sí o no sobre el residual: ¿esta dimensión se inclina hacia lo positivo o lo negativo? Las respuestas a estas preguntas de sí/no son las que preserva la Etapa 2.

Para ello, se multiplica una matriz de proyección aleatoria S de forma (d, d) por el vector residual. Los signos de los valores resultantes, ya sea +1 o -1, son los que realmente se almacenan.

Signo(ε(seq_length, d) * S(d, d))

Estas proyecciones de signos se conocen como transformada cuantificada de Johnson-Lindenstrauss (QJL).[7].

Nota: La aleatoriedad de S no es arbitraria; el lema de Johnson-Lindenstrauss garantiza que las proyecciones aleatorias preserven la estructura interna del producto con alta probabilidad.

Pero los signos por sí solos sólo captan la dirección, no la magnitud. Entonces, junto con QJL, la norma L2 del residual (‖ε‖₂) también se almacena como un único escalar por vector. Este escalar es el que restaura la magnitud durante la reconstrucción.

Durante la descuantización, estos bits de signo almacenados se multiplican nuevamente con S transpuesto, luego se escalan por (√π/2)/d y la norma almacenada ‖ε‖₂. Los autores muestran que sin este factor de escala, la estimación basada en signos del producto interno está sesgada; esta corrección es lo que la hace insesgada. La fórmula exacta se muestra a continuación:

𝐊~QJL=π/2d×‖ϵ‖×𝐒⊤×QJLtilde{mathbf{K}}_{text{QJL}} equal frac{sqrt{pi/2}}{d} times |epsilon| times mathbf{S}^top times text{QJL}

Finalmente se suman las dos partes de ambas etapas para obtener:

K̃ = K̂ + K̃QJL

Algunas de las observaciones de última hora:

Resumido del proceso completo de TurboQuant: la etapa 1 maneja la compresión masiva, la etapa 2 busca lo que se perdió y lo vuelve a agregar. Entonces, lo que realmente se almacena en caché para cada token son tres cosas: Idx, los bits de signo QJL y la norma escalar ‖ε‖₂. Esa es la representación comprimida completa. Los autores demuestran formalmente que este diseño de dos etapas alcanza el óptimo teórico, lo que significa que ningún método que funcione con el mismo presupuesto de bits puede funcionar mejor a la hora de preservar los productos punto de atención.

Conclusión:

Al fin y al cabo, TurboQuant funciona porque deja de obsesionarse con la reconstrucción perfecta de vectores y se centra inteligentemente en lo que el mecanismo de atención realmente necesita ver. En lugar de luchar contra el “impuesto” de VRAM con una calibración más compleja, simplemente utiliza un proceso matemático más limpio para realizar el trabajo.

A medida que seguimos presionando para lograr ventanas de contexto más largas, el cuello de botella de la caché KV no va a desaparecer. Pero como muestra este marco, no necesariamente necesitamos más hardware, sólo debemos ser más intencionales en la forma en que manejamos los datos que ya tenemos.

Con la introducción de TurboQuant, ¿finalmente se cierra el capítulo de gestión de memoria caché KV? ¿O es esto sólo la base de algo aún más poderoso?

Nota: Este desglose representa mi comprensión actual del proceso TurboQuant. Cualquier error de interpretación es enteramente mío y animo a los lectores a consultar la investigación original para obtener las pruebas matemáticas completas.

Referencias:

[1]Vaswani, A., et al. (2017). Atención es todo lo que necesita. Avances en los sistemas de procesamiento de información neuronal (NeurIPS 2017).

[2]Ainslie, J., et al. (2023). GQA: Entrenamiento de modelos de transformadores de consultas múltiples generalizados a partir de puntos de control de múltiples cabezales. EMNLP 2023.

[3]Kwon, W., et al. (2023). Gestión eficiente de la memoria para modelos de lenguaje grandes que sirven con PagedAttention. SOSP 2023.

[4]Zandieh, A., et al. (2025). TurboQuant: Cuantización vectorial en línea con una tasa de distorsión casi óptima. arXiv:2504.19874.

[5]Lloyd, SP (1982). Cuantización de mínimos cuadrados en PCM. Transacciones IEEE sobre teoría de la información, 28 (2), 129-137.

[6]Max, J. (1960). Cuantización para una distorsión mínima. Transacciones IRE sobre teoría de la información, 6 (1), 7-12.

[7]Zandieh, A., et al. (2024). QJL: Transformación JL cuantificada de 1 bit para cuantificación de caché KV sin sobrecarga. AAAI 2025.