Phase 12 - Lesson 06
Visión en Cualquier Resolución: Patch-n'-Pack y NaFlex
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
Las imágenes reales no son cuadrados de 224x224. Un recibo es 9:16, un gráfico es 16:9, un escaneo médico podría ser 4096x4096, una captura de pantalla de móvil es 9:19.5. La respuesta de los VLM previos a 2024 — redimensionar todo a un cuadrado fijo — descartaba la señal que hace que el OCR, la comprensión de documentos y el análisis de escenas de alta resolución funcionen. NaViT (Google, 2023) demostró que se podían empaquetar parches (patches) de resolución variable en un único lote de transformer con enmascaramiento de bloque diagonal. M-RoPE de Qwen2-VL (2024) eliminó por completo las tablas de posición absoluta. AnyRes de LLaVA-NeXT dividió las imágenes de alta resolución en una base + subimágenes. La variante NaFlex de SigLIP 2 (2025) es ahora el codificador predeterminado para los VLM abiertos que desean un único checkpoint para servir cualquier relación de aspecto. Esta lección implementa patch-n'-pack de extremo a extremo.
Tipo: Build Idiomas: Python (biblioteca estándar, patch packer + máscara de bloque diagonal) Prerrequisitos: Phase 12 · 01 (parches de ViT), Phase 12 · 05 (LLaVA) Tiempo: ~120 minutos
Objetivos de Aprendizaje
- Empaquetar parches de un lote de imágenes de resolución variable en una sola secuencia y construir la máscara de atención de bloque diagonal.
- Elegir entre AnyRes tiling (LLaVA-NeXT), NaFlex (SigLIP 2) y M-RoPE (Qwen2-VL) para una tarea determinada.
- Calcular presupuestos de tokens para OCR, gráficos y fotografía sin redimensionar.
- Nombrar los tres modos de fallo del redimensionamiento cuadrado: texto aplastado, contenido recortado y tokens desperdiçados en relleno (padding).
El Problema
Los transformers esperan una secuencia. Un lote (batch) es una pila de secuencias de la misma longitud. Si tus imágenes son de 224x224, obtienes 196 tokens de parche cada vez, no se requiere relleno (padding), trabajo terminado. Entrena en 224, infiere en 224, nunca vuelvas a pensar en la resolución.
El mundo no coopera. Los documentos son en formato retrato (8.5x11 pulgadas, aprox. 2:3). Las capturas de pantalla de gráficos son en formato apaisado (16:9). Los recibos son altos y delgados (1:3). Las imágenes médicas se entregan en 2048x2048 o más. Las capturas de pantalla de dispositivos móviles son de 1170x2532 (0.46:1).
Tres opciones previas a 2024 y por qué falla cada una:
- Redimensionar a un cuadrado fijo (224x224 o 336x336). El aplastamiento distorsiona el texto y las caras. La reducción de escala destruye las etiquetas de los gráficos y el contenido de OCR. Práctica estándar hasta LLaVA-1.5.
- Recortar a una relación de aspecto fija. Descartas la mayor parte de la imagen, y elegir la ubicación del recorte es su propio problema de visión.
- Rellenar (padding) hasta el lado más largo. Corrige la distorción pero desperdicia más del 50% de los tokens en relleno para imágenes en formato retrato. Costo de atención cuadrático en todos esos tokens de relleno.
La respuesta para 2024-2025: permitir que el transformer procese parches en la resolución nativa de la imagen y descubrir cómo empaquetar un lote heterogéneo en una sola secuencia sin desperdiciar computación.
El Concepto
NaViT y patch-n'-pack
NaViT (Dehghani et al., 2023) fue el artículo que demostró que esto funciona a escala. La idea es mecánica:
- Para cada imagen en el lote, calcula su cuadrícula de parches nativa con un tamaño de parche elegido (por ejemplo, 14).
- Aplana (flatten) los parches de cada imagen en su propia secuencia de longitud variable.
- Concatena los parches de todas las imágenes en una sola secuencia larga para el lote.
- Construye una máscara de atención de bloque diagonal para que los parches de la imagen A solo atiendan dentro de la propia imagen A.
- Lleva información de posición por parche (2D RoPE o embeddings de posición fraccionarios).
Un lote de tres imágenes de 336x336 (576 tokens), 224x224 (256 tokens) y 448x336 (768 tokens) se convierte en una sola secuencia de 1600 tokens con una máscara de bloque diagonal de 1600x1600. Sin relleno. Sin desperdicio de computación. El transformer maneja relaciones de aspecto arbitrarias.
NaViT también introdujo el descarte fraccionario de parches (fractional patch dropping) durante el entrenamiento — descartando el 50% de los parches al azar en todo el lote —, lo que regulariza y acelera el entrenamiento. SigLIP 2 heredó esto.
AnyRes (LLaVA-NeXT)
AnyRes de LLaVA-NeXT es la alternativa pragmática. Dada una imagen de alta resolución y un codificador fijo (CLIP o SigLIP en 336), divide la imagen en cuadrículas (tiles):
- Elige un diseño de cuadrícula de un conjunto predefinido — (1x1), (1x2), (2x1), (1x3), (3x1), (2x2), etc. — que mejor se adapte a la relación de aspecto de la imagen.
- Divide la imagen completa en la cuadrícula; cada cuadro se convierte en un recorte de 336x336.
- Produce también una miniatura (thumbnail): la imagen completa redimensionada a 336x336 como un token de contexto global.
- Codifica cada cuadro a través del codificador 336 congelado. Concatena los tokens de los cuadros + los tokens de la miniatura.
Para una imagen de 672x672 en una cuadrícula de 2x2 más miniatura: 4 * 576 + 576 = 2880 tokens visuales. Costoso pero efectivo: el LLM ve tanto el detalle local como el contexto global.
AnyRes es la ruta preferida cuando tu codificador está congelado y solo admite una resolución. Explota el recuento de tokens para imágenes grandes (una imagen de 1344x1344 en una cuadrícula de 4x4 es 9216 + 576 ≈ 9800 tokens, lo que llena la mayor parte del contexto de un LLM de 8k).
M-RoPE (Qwen2-VL)
Qwen2-VL introdujo Multimodal Rotary Position Embedding (Embedding de Posición Rotativo Multimodal). En lugar de las posiciones fraccionarias de NaViT o el esquema de cuadro-y-miniatura de AnyRes, cada parche lleva una posición 3D (temporal, altura, anchura). Las rotaciones de query/key manejan H, W y longitud temporal arbitrarias.
M-RoPE ofrece resolución dinámica nativa sin reentrenamiento. En la inferencia, alimentas cualquier imagen HxW, el incorporador de parches (patch embedder) produce H/14 x W/14 tokens, cada token obtiene su posición (t=0, r=fila, c=columna), RoPE rota la atención con las frecuencias correctas, y listo. Qwen2.5-VL y Qwen3-VL continúan con esto. V2PE de InternVL3 es la misma idea con codificación variable por modalidad.
A diferencia de AnyRes, M-RoPE es de O(H x W / P^2) tokens en resolución nativa — sin el costo multiplicativo de los cuadros. A diferencia de NaViT, sigue esperando una sola imagen por paso hacia adelante (forward). El procesamiento por lotes (batching) entre resolucciones todavía requiere usar patch-n'-pack adicionalmente.
NaFlex (SigLIP 2)
NaFlex es el modo nativo-flexible del checkpoint de SigLIP 2. Un único modelo sirve múltiples longitudes de secuencia (256, 729, 1024 tokens) en la inferencia. Internamente, utiliza patch-n'-pack al estilo NaViT durante el entrenamiento y posiciones fraccionarias absolutas por parche. El punto de venta: un único checkpoint, elige tu presupuesto de tokens en la inferencia según la tarea.
Para una tarea semántica (clasificación, recuperación), 256 tokens. Para OCR o comprensión de gráficos, 1024 tokens. Sin reentrenamiento.
La máscara de empaquetamiento
La máscara de bloque diagonal es donde tropiezan la mayoría de las implementaciones. Para una secuencia empaquetada de longitud N_total que cubre imágenes i=0..B-1 con longitudes n_i, la máscara M de forma (N_total, N_total) es 1 si ambos índices caen en el bloque de la misma imagen, de lo contrario 0. Puedes construirla a partir de una lista de longitudes acumuladas:
offsets = [0, n_0, n_0+n_1, ..., N_total]
M[i, j] = 1 iff there exists b where offsets[b] <= i < offsets[b+1] and offsets[b] <= j < offsets[b+1]
Esto es una línea en PyTorch con torch.block_diag o un gather explícito. La ruta de longitud variable de FlashAttention (cu_seqlens) omite la máscara por completo y atiende dentro de las secuencias utilizando el tensor de longitud acumulada directamente — aproximadamente 10 veces más rápido que una máscara densa para lotes típicos.
Presupuestos de tokens
Elige tu estrategia por tarea:
- OCR / documentos: 1024-4096 tokens. SigLIP 2 NaFlex a 1024, o AnyRes 3x3 + miniatura.
- Gráficos e interfaz de usuario (UI): 729-1024 tokens a 384-448 nativos. Resolución dinámica Qwen2.5-VL con límite máximo de píxeles (max pixels cap).
- Fotos naturales: 256-576 tokens es suficiente. El LLM downstream ve lo necesario. Paga por tokens donde la densidad de contenido sea alta.
- Video: 64-128 tokens por fotograma después del agrupamiento (pooling) espacial, 2-8 FPS. La lección 12.17 cubre esto.
La regla de producción de 2026: elige un límite máximo de píxeles por tarea, codifica a la relación de aspecto nativa hasta ese límite, empaqueta el lote y omite el relleno (padding). Qwen2.5-VL expone min_pixels y max_pixels exactamente para este control.
Úsalo
code/main.py implementa patch-n'-pack para un lote heterogéneo de imágenes con coordenadas de píxeles enteras. Este:
- Toma una lista de tamaños de imagen (H, W).
- Calcula la longitud de la secuencia de parches de cada imagen con un tamaño de parche de 14.
- Los empaqueta en una secuencia de longitud total
sum(n_i). - Construye la máscara de atención de bloque diagonal (densa, para mayor claridad).
- Compara el costo empaquetado frente al redimensionamiento cuadrado y el AnyRes tiling.
- Imprime una tabla de presupuesto de tokens para un lote mixto (recibo, gráfico, captura de pantalla, foto).
Ejecútalo. Los números que resultan son la razón por la cual todos los VLM abiertos de 2026 utilizan patch-n'-pack.
Impleméntalo
Esta lección produce outputs/skill-resolution-budget-planner.md. Dado un volumen de trabajo con proporciones de aspecto mixtas (OCR, gráficos, fotos, fotogramas de video) y un presupuesto total de tokens, elige la estrategia correcta (NaFlex, AnyRes, M-RoPE o fixed-square) y emite una configuración por solicitud. Utiliza esta habilidad cuando estés dimensionando un VLM para un producto — evita la explosión silenciosa de tokens de 10x que destruye los presupuestos de latencia.
Ejercicios
Un recibo es de 600x1500 (1:2.5). Con un tamaño de parche de 14, ¿cuántos tokens de resolución nativa tiene? ¿Cuántos después de redimensionar a un cuadrado de 336? ¿Cuál pierde más precisión de OCR en la práctica?
Construye la máscara de bloque diagonal para un lote de cuatro imágenes con longitudes 256, 576, 729, 1024. Verifica que la matriz de atención sea de 2585x2585 y tenga exactamente
256^2 + 576^2 + 729^2 + 1024^2entradas distintas de cero.Para una imagen de 1792x896 en el parche 14, compara: (a) redimensionar a un cuadrado de 336 y luego codificar, (b) AnyRes 2x1 + miniatura, (c) M-RoPE en nativo. ¿Cuál usa menos tokens? ¿Cuál preserva más detalles?
Implementa el descarte fraccionario de parches (fractional patch dropping): dada una secuencia empaquetada, descarta el 50% de los tokens de manera uniforme al azar, y actualiza la máscara de bloque diagonal correspondientemente. Mide el cambio de dispersión (sparsity) de la máscara.
Lee la Sección 3.2 del artículo de Qwen2-VL (arXiv:2409.12191). Describe en dos oraciones qué controlan
min_pixelsymax_pixelsy por qué ambos límites son importantes.
Términos Clave
| Término | Lo que la gente dice | Lo que realmente significa |
|---|---|---|
| Patch-n'-pack | "Empaquetamiento al estilo NaViT" | Concatena secuencias de parches de longitud variable de diferentes imágenes en una sola dimensión de lote |
| Block-diagonal mask | "Máscara de empaquetamiento" | Máscara de atención que confina los parches de cada imagen para atender solo a sí mismos, no a los vecinos en el paquete |
| AnyRes | "Tiling de LLaVA-NeXT" | Divide una imagen de alta resolución en una cuadrícula de cuadros de tamaño fijo más una miniatura global; codifica cada cuadro con un codificador fijo |
| NaFlex | "SigLIP 2 native-flex" | Checkpoint único de SigLIP 2 que sirve presupuestos de tokens de 256/729/1024 en la inferencia sin reentrenamiento |
| M-RoPE | "RoPE Multimodal" | Codificación de posición rotativa 3D (tiempo, fila, columna) que maneja H, W, T arbitrarios sin tablas de posición |
| cu_seqlens | "Empaquetamiento FlashAttention" | Tensor de longitud acumulada que utiliza la ruta varlen de FlashAttention en lugar de una máscara de bloque diagonal densa |
| min_pixels / max_pixels | "Límites de resolución" | Controles por solicitud de Qwen2.5-VL que limitan el recuento de tokens en entradas muy pequeñas o muy grandes |
| Visual token budget | "Cuántos tokens por imagen" | Recuento aproximado de tokens de parche emitidos por imagen; establece el presupuesto de prompt del LLM y el costo de atención |
Lecturas Adicionales
- Dehghani et al. — Patch n' Pack: NaViT (arXiv:2307.06304)
- Wang et al. — Qwen2-VL (arXiv:2409.12191)
- Laurençon et al. — What matters when building vision-language models? (Idefics2, arXiv:2405.02246)
- Tschannen et al. — SigLIP 2 (arXiv:2502.14786)
- Qwen Team — Qwen2.5-VL Technical Report (arXiv:2502.13923)