¿Por qué los transformers no pueden simplemente leer más?

Todo transformer tiene un límite fundamental: su ventana de contexto , el número máximo de tokens que puede procesar en un solo pase hacia adelante. GPT-2 (2019) tenía una ventana de contexto de 1,024 tokens — aproximadamente 750 palabras, apenas suficiente para una entrada de blog corta. GPT-4 (2023) la amplió a 128,000 tokens. Gemini 1.5 Pro (2024) afirma 10 millones. Cada generación empuja el límite más lejos, pero ¿por qué era tan pequeño en primer lugar? ¿Y qué hace que extenderlo sea tan difícil?

La respuesta está en la operación central de todo transformer: la auto-atención . En la atención multi-cabeza estándar, cada token en la secuencia calcula una puntuación de atención contra cada otro token. Si la secuencia tiene $n$ tokens, eso significa $n \times n$ puntuaciones de atención por cabeza por capa. El costo es cuadrático en la longitud de la secuencia — $O(n^2)$ — y este escalado cuadrático es el mayor obstáculo para los transformers de contexto largo.

Para tener una idea de lo rápido que esto crece, consideremos tres longitudes de contexto. Para $n = 1{,}024$ (GPT-2), cada cabeza de atención calcula $1{,}024 \times 1{,}024 \approx 1$ millón de puntuaciones por capa. Manejable. Para $n = 128{,}000$ (GPT-4 Turbo), eso salta a $128{,}000 \times 128{,}000 \approx 16.4$ mil millones de puntuaciones por capa por cabeza. Para $n = 1{,}000{,}000$ (aproximadamente el rango de Gemini), alcanza $10^{12}$ — un billón de puntuaciones. Pasar de 1K a 1M tokens multiplicó el costo de atención por un factor de aproximadamente un millón.

Este escalado cuadrático afecta dos recursos de hardware separados. Primero, cómputo (FLOPs) : la multiplicación de matrices $QK^T$ requiere $O(n^2 \cdot d)$ operaciones de punto flotante por cabeza por capa, donde $d$ es la dimensión de la cabeza. Segundo, memoria : la propia matriz de puntuaciones de atención es $n \times n$ y debe almacenarse (al menos temporalmente) durante el pase hacia adelante. Además, la caché KV utilizada durante la generación autoregresiva crece linealmente con $n$, pero el factor constante es lo suficientemente grande como para dominar la memoria de la GPU en longitudes de secuencia largas. Así que extender la ventana de contexto no es solo un desafío algorítmico — es un problema de presupuesto de hardware.

El costo cuadrático de la atención

Pongamos números al costo. La atención estándar de producto punto escalado calcula:

$$\text{Attention}(Q,K,V) = \text{softmax}\!\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$

donde $Q$, $K$ y $V$ son las matrices de consulta, clave y valor de forma $n \times d_k$, y $n$ es la longitud de la secuencia (para un repaso de cómo se construyen estas matrices, consulta el artículo sobre puntuaciones de atención ). La softmax normaliza cada fila de puntuaciones en una distribución de probabilidad. El paso crítico es el producto matricial $QK^T$: multiplica una matriz $n \times d_k$ por una matriz $d_k \times n$, produciendo una matriz de puntuaciones de atención $n \times n$. Esa matriz $n \times n$ es donde reside el costo cuadrático.

Desglosemos cada componente de costo con precisión. Para una sola cabeza de atención en una sola capa:

  • Cómputo para $QK^T$: multiplicar $n \times d_k$ por $d_k \times n$ cuesta $O(n^2 \cdot d_k)$ FLOPs. Cuando $n$ se duplica, esto se cuadruplica.
  • Cómputo para $\text{scores} \times V$: multiplicar la matriz de puntuaciones $n \times n$ por $V$ (forma $n \times d_k$) cuesta otros $O(n^2 \cdot d_k)$ FLOPs. También cuadrático en $n$.
  • Memoria para la matriz de atención: la matriz completa $n \times n$ debe almacenarse — son $O(n^2)$ elementos por cabeza. En la práctica, FlashAttention (Dao et al., 2022) evita materializar toda esta matriz calculando la atención en bloques, reduciendo la memoria pico de $O(n^2)$ a $O(n)$. Pero los FLOPs siguen siendo $O(n^2 \cdot d_k)$ — FlashAttention es una optimización de IO, no una algorítmica.
  • Memoria de la caché KV: durante la generación autoregresiva, el modelo almacena todos los vectores de clave y valor previamente calculados. Para $L$ capas, $H$ cabezas y dimensión de cabeza $d_k$, esto cuesta $O(n \cdot d_k \cdot H \cdot L)$ — lineal en $n$, pero la constante es sustancial. Para un modelo de 70B parámetros con 80 capas y 64 cabezas, almacenar la caché KV para 128K tokens en FP16 requiere aproximadamente 40 GB (ver el artículo sobre caché KV) .

La consecuencia práctica es contundente: duplicar la longitud de contexto cuadruplica el cómputo de atención y duplica la memoria de la caché KV . La tabla a continuación hace esto concreto mostrando cómo ambos escalan a través de las longitudes de contexto que importan en la práctica.

import json, js

# Attention compute and KV cache scaling across context lengths
# Using d_k=128, H=32 heads, L=32 layers as a representative config (roughly a 7B model)
d_k = 128
H = 32
L = 32

seq_lengths = [1024, 4096, 16384, 65536, 131072, 1048576]
labels = ["1K", "4K", "16K", "64K", "128K", "1M"]

rows = []
for n, label in zip(seq_lengths, labels):
    # Attention FLOPs per layer (all heads): 2 * H * n^2 * d_k (QK^T + scores*V)
    attn_flops = 2 * H * (n ** 2) * d_k
    # KV cache memory in bytes (FP16 = 2 bytes): 2 (K+V) * L * H * d_k * n * 2 bytes
    kv_bytes = 2 * L * H * d_k * n * 2

    # Format FLOPs
    if attn_flops >= 1e18:
        flops_str = f"{attn_flops / 1e18:.1f} ExaFLOPs"
    elif attn_flops >= 1e15:
        flops_str = f"{attn_flops / 1e15:.1f} PetaFLOPs"
    elif attn_flops >= 1e12:
        flops_str = f"{attn_flops / 1e12:.1f} TeraFLOPs"
    elif attn_flops >= 1e9:
        flops_str = f"{attn_flops / 1e9:.1f} GigaFLOPs"
    else:
        flops_str = f"{attn_flops / 1e6:.1f} MegaFLOPs"

    # Format KV cache
    if kv_bytes >= 1e9:
        kv_str = f"{kv_bytes / 1e9:.1f} GB"
    elif kv_bytes >= 1e6:
        kv_str = f"{kv_bytes / 1e6:.1f} MB"
    else:
        kv_str = f"{kv_bytes / 1e3:.1f} KB"

    # n^2 attention scores
    scores = n * n
    if scores >= 1e12:
        scores_str = f"{scores / 1e12:.1f}T"
    elif scores >= 1e9:
        scores_str = f"{scores / 1e9:.1f}B"
    elif scores >= 1e6:
        scores_str = f"{scores / 1e6:.1f}M"
    else:
        scores_str = f"{scores / 1e3:.1f}K"

    rows.append([label, f"{n:,}", scores_str, flops_str, kv_str])

js.window.py_table_data = json.dumps({
    "headers": ["Contexto", "Tokens (n)", "Punt. Atención (n²)", "FLOPs Atención/capa", "Caché KV (FP16)"],
    "rows": rows
})

print("Config: d_k=128, H=32 cabezas, L=32 capas (aprox. modelo 7B)")
print()
print("Observaciones clave:")
print("  1K -> 4K   (4x tokens): FLOPs de atención crecen 16x, caché KV crece 4x")
print("  1K -> 128K (128x tokens): FLOPs de atención crecen 16,384x, caché KV crece 128x")
print("  1K -> 1M   (1024x tokens): FLOPs de atención crecen ~1,000,000x, caché KV crece 1,024x")
💡 La tabla anterior muestra FLOPs para una sola capa. Un modelo de 32 capas multiplica estos números por 32, y un modelo con contexto de 128K realizaría aproximadamente 268 TeraFLOPs de cómputo de atención solo por pase hacia adelante — antes de contar las capas feed-forward, que añaden otro bloque comparable de cómputo.

Verifiquemos la relación de escalado con una comprobación de límites rápida. Si pasamos de $n$ a $2n$, las puntuaciones de atención pasan de $n^2$ a $(2n)^2 = 4n^2$ — un aumento de $4\times$. La caché KV pasa de $n \cdot c$ a $2n \cdot c$ donde $c = d_k \cdot H \cdot L$ es constante — un aumento de $2\times$. Así que el cómputo de atención es cuadrático en $n$ (duplicar $n$ cuesta $4\times$), mientras que la caché KV es lineal (duplicar $n$ cuesta $2\times$). En longitudes de contexto moderadas, la caché KV domina la memoria porque FlashAttention evita materializar la matriz de atención $n^2$. Pero en contextos muy largos (1M+), incluso el término lineal de la caché KV se vuelve enorme — 512 GB para 1M tokens en la configuración anterior.

Tres estrategias para contexto largo

Si la atención cuadrática es el muro, ¿qué herramientas tenemos para superarlo? A lo largo del resto de este track, exploraremos tres estrategias fundamentalmente diferentes, cada una haciendo un compromiso distinto.

La primera estrategia son mejores codificaciones de posición (artículo 2). Los transformers estándar aprenden un conjunto fijo de embeddings de posición durante el entrenamiento — uno por posición hasta la longitud máxima de contexto. Un modelo entrenado con 4,096 posiciones nunca ha visto la posición 4,097 y no tiene idea de qué hacer con ella. Técnicas como RoPE (Rotary Position Embedding), ALiBi (Attention with Linear Biases), y YaRN (Yet another RoPE extensioN) reemplazan o modifican la codificación de posición para que el modelo pueda generalizar a posiciones en las que nunca fue entrenado. El compromiso es la calidad de extrapolación : el modelo podría manejar secuencias más largas, pero la calidad de la atención puede degradarse en posiciones muy alejadas de la distribución de entrenamiento.

La segunda estrategia son patrones de atención eficientes (artículo 3). En lugar de que cada token atienda a todos los demás ($O(n^2)$), restringimos la atención a un subconjunto: una ventana deslizante de $w$ tokens cercanos, un conjunto fijo de tokens globales, o un patrón disperso determinado por hashing o enrutamiento aprendido. Esto reduce el costo de $O(n^2)$ a $O(n \cdot w)$ donde $w \ll n$ — a menudo $O(n \log n)$ o incluso $O(n)$. Ejemplos incluyen Longformer , BigBird , y la atención de ventana deslizante de Mistral. El compromiso es el acceso completo a la atención : los tokens solo pueden atender a su vecindario local, por lo que la información distante debe propagarse a través de múltiples capas en lugar de ser directamente accesible en un solo paso de atención.

La tercera estrategia es la memoria externa (artículo 4). En lugar de comprimir todo en la ventana de atención, se le da al modelo un banco de memoria externo — una representación comprimida de tamaño fijo del contexto pasado desde la cual puede leer y escribir. Este es el enfoque adoptado por Titans (Google, 2025), Infini-Attention (Google, 2024), y Memorizing Transformers (Google, 2022). El compromiso es recuperación exacta por almacenamiento comprimido : el modelo almacena un resumen comprimido en lugar de los tokens sin procesar, por lo que puede no recuperar detalles de grano fino con tanta precisión como lo haría la atención completa.

Más allá de estas tres estrategias centrales, el artículo 5 cubre trucos de escalado — técnicas de ingeniería como entrenamiento progresivo, calentamiento de longitud de contexto y pre-entrenamiento continuado que permiten a los modelos existentes adaptarse a contextos más largos sin entrenar desde cero. El artículo 6 examina cómo los modelos de producción combinan estos enfoques : los sistemas modernos de contexto largo típicamente apilan múltiples estrategias (por ejemplo, escalado de RoPE + atención de ventana deslizante + FlashAttention) en lugar de depender de una sola técnica.

Para resumir el panorama:

  • Codificaciones de posición — extienden dónde puede mirar el modelo (comprometen calidad de extrapolación)
  • Atención dispersa — reducen cuánto calcula el modelo (comprometen acceso completo a la atención)
  • Memoria externa — expanden qué puede recordar el modelo (comprometen recuperación exacta por compresión)

¿Qué ocurre cuando el contexto se alarga?

Supongamos que resolvemos los problemas de cómputo y memoria — construimos un modelo que puede caber un millón de tokens en su ventana de contexto sin quedarse sin memoria de GPU ni tardar horas por pase hacia adelante. ¿Realmente usa todo ese contexto de manera efectiva? La respuesta, sorprendentemente, es: no siempre.

En 2023, Liu et al. publicaron un artículo titulado "Lost in the Middle: How Language Models Use Long Contexts" que expuso un patrón sorprendente. Le dieron a los modelos una tarea de respuesta a preguntas con múltiples documentos donde el documento relevante se colocó en diferentes posiciones dentro de un contexto largo. Los modelos funcionaban bien cuando la información relevante aparecía al principio o al final del contexto, pero el rendimiento caía significativamente cuando estaba enterrada en el medio. Esta curva de rendimiento en forma de U se mantuvo en múltiples modelos y longitudes de contexto.

¿Por qué ocurre esto? La explicación probable involucra cómo la atención se distribuye entre posiciones. En los transformers autoregresivos, los primeros tokens tienden a recibir pesos de atención desproporcionadamente altos (a veces llamados "sumideros de atención"), y los tokens más recientes son naturalmente prominentes porque están más cerca en la ventana de atención causal. Los tokens del medio quedan apretados: están lejos del inicio (así que han perdido su ventaja de primacía) y lejos del final (así que carecen de recencia). La normalización softmax en la atención significa que la atención es de suma cero — dar más peso a las posiciones de los extremos necesariamente quita peso a las posiciones del medio.

Este hallazgo llevó al paradigma de evaluación needle-in-a-haystack (aguja en un pajar), que se ha convertido en la prueba de estrés estándar para modelos de contexto largo. La configuración es simple: insertar un dato específico (la "aguja") en una posición controlada dentro de un pasaje largo de texto irrelevante (el "pajar"), y luego pedirle al modelo que recupere ese dato. Variando tanto la longitud total del contexto como la posición de la aguja, obtenemos un mapa de calor 2D de precisión de recuperación. Un modelo de contexto largo perfecto mostraría verde uniforme en todo el mapa de calor — recupera la aguja independientemente de dónde esté colocada o cuán largo sea el contexto.

En la práctica, la mayoría de los modelos muestran recuperación degradada en las posiciones del medio, especialmente en longitudes de contexto más largas. Algunos modelos más nuevos (GPT-4o, Claude 3.5, Gemini 1.5 Pro) han mejorado sustancialmente en este benchmark mediante intervenciones durante el entrenamiento, pero la lección general permanece: el desafío de calidad es ortogonal al desafío de cómputo . Resolver el problema de escalado $O(n^2)$ nos da un modelo que puede técnicamente procesar un millón de tokens. Pero si el modelo realmente atiende y recupera información de manera uniforme a través de todos esos tokens es un problema separado que requiere correcciones durante el entrenamiento (como fine-tuning de contexto largo con posiciones de aguja diversas) e innovaciones arquitectónicas (como los mecanismos de atención eficiente y memoria que cubriremos en los artículos 3 y 4).

Esta distinción — entre poder procesar contextos largos y poder usarlos — es lo que hace que el problema de contexto largo sea tan multifacético. Extender la ventana de contexto requiere resolver al menos tres sub-problemas simultáneamente: el muro de cómputo cuadrático, el muro de memoria lineal-pero-grande, y la degradación de calidad que proviene de la dificultad de la atención para distribuir el foco uniformemente sobre secuencias muy largas. El resto de este track aborda cada uno a su turno.

Quiz

Pon a prueba tu comprensión del problema de longitud de contexto y sus implicaciones.

Si la longitud de contexto de un modelo aumenta de 4,096 a 16,384 tokens (un aumento de 4$\times$), ¿por qué factor aumenta el cómputo de atención ($QK^T$)?

FlashAttention reduce el uso máximo de memoria de la atención de $O(n^2)$ a $O(n)$. ¿Qué NO reduce?

En el hallazgo 'Lost in the Middle' (Liu et al., 2023), ¿dónde en un contexto largo los modelos rinden peor al recuperar información relevante?

¿Qué compromiso está asociado con la estrategia de atención dispersa para contexto largo?