La Arquitectura de Codificador Dual

CLIP (Radford et al., 2021) utiliza dos codificadores separados que nunca ven directamente las entradas del otro. Cada codificador procesa su propia modalidad de forma aislada, y el único punto de contacto entre ellos es un espacio de embeddings compartido donde se comparan sus salidas.

  • Codificador de imagen (ya sea un ResNet o un Vision Transformer): mapea una imagen a un vector de tamaño fijo $\mathbf{v}_I \in \mathbb{R}^d$. La variante ViT divide la imagen en parches, proyecta cada parche en un embedding y procesa la secuencia a través de capas de transformer , usando la salida del token [CLS] como representación de la imagen.
  • Codificador de texto (un Transformer): mapea un título o descripción a un vector $\mathbf{v}_T \in \mathbb{R}^d$ de la misma dimensionalidad. Tokeniza el texto de entrada, lo procesa a través de capas de transformer y toma el embedding en la posición del token [EOS] como representación del texto.

Ambos vectores se normalizan con L2 para que vivan en la hiperesfera unitaria ($\|\mathbf{v}_I\| = \|\mathbf{v}_T\| = 1$), y la similitud se mide con el coseno, que para vectores unitarios se reduce al producto punto:

$$\text{sim}(I, T) = \frac{\mathbf{v}_I \cdot \mathbf{v}_T}{\|\mathbf{v}_I\| \, \|\mathbf{v}_T\|} = \mathbf{v}_I \cdot \mathbf{v}_T$$

La similitud coseno va desde $-1$ (vectores apuntando en direcciones opuestas) pasando por $0$ (vectores ortogonales, completamente no relacionados) hasta $+1$ (vectores apuntando en la misma dirección, coincidencia perfecta). Al normalizar ambos vectores a longitud unitaria, eliminamos cualquier efecto de magnitud — una imagen más brillante o un título más largo no obtiene una puntuación de similitud más alta solo porque su vector bruto resultó ser más grande. Lo único que importa es el ángulo entre los dos vectores, que captura cuán alineados semánticamente están la imagen y el texto.

💡 El diseño de codificador dual es lo que hace a CLIP eficiente a escala. Como los codificadores de imagen y texto son independientes, podemos precomputar todos los embeddings de imágenes una sola vez y buscar entre ellos en tiempo de consulta — la misma división offline/online que vimos en las pipelines RAG. Indexa un millón de imágenes offline, y luego en tiempo de consulta codifica un único prompt de texto y encuentra los vectores de imagen más cercanos con una búsqueda por producto punto.

La Pérdida Contrastiva: InfoNCE

El corazón del entrenamiento de CLIP es una pérdida contrastiva llamada InfoNCE (Estimación Contrastiva de Ruido con maximización de Información). La configuración es directa: un lote de entrenamiento contiene $N$ pares imagen-texto $(I_1, T_1), \ldots, (I_N, T_N)$. El par $(I_i, T_i)$ es una coincidencia genuina — el texto describe la imagen. Todas las demás combinaciones $(I_i, T_j)$ donde $i \neq j$ se tratan como no coincidencias. El modelo debe aprender a acercar los pares coincidentes en el espacio de embeddings compartido mientras aleja los pares no coincidentes.

La pérdida para una sola imagen $I_i$ encontrando su texto coincidente entre todos los $N$ textos del lote es:

$$\mathcal{L}_i^{I \to T} = -\log \frac{\exp\!\bigl(\text{sim}(I_i, T_i) / \tau\bigr)}{\sum_{j=1}^{N} \exp\!\bigl(\text{sim}(I_i, T_j) / \tau\bigr)}$$

Cada parte de esta fórmula tiene un trabajo específico. Recorrámoslas una por una.

El numerador $\exp(\text{sim}(I_i, T_i) / \tau)$: es la similitud del par correcto, exponenciada y escalada por un parámetro de temperatura $\tau$. La exponencial asegura que el valor sea siempre positivo (necesario para una distribución de probabilidad válida), y el escalado por temperatura controla cuánto se amplifica una pequeña diferencia en las puntuaciones de similitud. Queremos que este numerador sea lo más grande posible — el modelo debería asignar la mayor similitud al par imagen-texto correcto.

El denominador $\sum_{j=1}^{N} \exp(\text{sim}(I_i, T_j) / \tau)$: la suma recorre todos los $N$ textos del lote — la coincidencia correcta $T_i$ más los $N-1$ distractores. Juntos, el numerador dividido por el denominador forma un softmax sobre las similitudes, convirtiéndolas en una distribución de probabilidad que suma 1. La pérdida pregunta: ¿qué fracción de la masa total de similitud exponenciada cae sobre el texto correcto? Si el modelo asigna toda la masa al texto correcto, esta fracción es 1. Si el modelo no puede distinguir el texto correcto de los distractores, la masa se distribuye uniformemente y la fracción cae a $1/N$.

El $-\log$ : convierte la probabilidad en un valor de pérdida. Si el modelo asigna probabilidad 1.0 al texto correcto, $-\log(1) = 0$ — perfecto, sin pérdida. Si asigna probabilidad $1/N$ (azar, el peor caso para una distribución uniforme), $-\log(1/N) = \log N$ — pérdida máxima para adivinación uniforme. Así que la pérdida va desde $0$ (discriminación perfecta) hasta $\log N$ (adivinación aleatoria), dándonos una escala natural donde el límite superior crece logarítmicamente con el tamaño del lote. Para el tamaño de lote de CLIP de $N = 32{,}768$, la pérdida por azar sería $\log(32768) \approx 10.4$.

La temperatura $\tau$ : un escalar aprendido (inicializado alrededor de 0.07 en CLIP). Controla la nitidez de la distribución softmax. Para entender esto, consideremos dos extremos:

  • Cuando $\tau \to 0^+$ , el softmax se aproxima a un argmax duro — el modelo se vuelve extremadamente confiado, asignando casi toda la probabilidad al texto más similar. Dividir similitudes por un $\tau$ diminuto amplifica pequeñas diferencias en enormes, así que incluso una ligera ventaja en similitud hace que un par domine completamente. Los gradientes se vuelven muy puntiagudos (grandes para el ganador, cercanos a cero para todo lo demás), lo que puede hacer inestable el entrenamiento.
  • Cuando $\tau \to \infty$ , el softmax se aplana hacia una distribución uniforme. Dividir similitudes por un $\tau$ enorme reduce todas las diferencias a casi cero, así que $\exp(s_i / \tau) \approx 1$ para cada par. Todos los textos parecen igualmente similares sin importar las puntuaciones reales, y el modelo no recibe gradiente útil.
  • El punto óptimo está en el medio: lo suficientemente nítido para discriminar bien entre coincidencias correctas e incorrectas, pero lo suficientemente suave para proporcionar señal de gradiente a todos los negativos para que el modelo pueda aprender a alejarlos.
💡 $\tau$ se aprende conjuntamente con los codificadores, no se ajusta manualmente. La temperatura final aprendida de CLIP típicamente se estabiliza alrededor de $\tau \approx 0.01$, lo que significa que el modelo aprende a ser bastante nítido — distingue con confianza las coincidencias de las no coincidencias. El hecho de que $\tau$ disminuya desde su valor inicial de 0.07 durante el entrenamiento nos dice que el modelo se vuelve más confiado a medida que los codificadores mejoran.

Simetría. La pérdida anterior pregunta: "dada la imagen $I_i$, ¿puede el modelo encontrar el texto correcto $T_i$?" Pero también necesitamos la dirección inversa: "dado el texto $T_i$, ¿puede el modelo encontrar la imagen correcta $I_i$?" CLIP calcula la pérdida en ambas direcciones. La pérdida de texto a imagen es:

$$\mathcal{L}_i^{T \to I} = -\log \frac{\exp\!\bigl(\text{sim}(T_i, I_i) / \tau\bigr)}{\sum_{j=1}^{N} \exp\!\bigl(\text{sim}(T_i, I_j) / \tau\bigr)}$$

La pérdida total promedia ambas direcciones a través de todos los $N$ pares del lote:

$$\mathcal{L} = \frac{1}{2N} \sum_{i=1}^{N} \bigl(\mathcal{L}_i^{I \to T} + \mathcal{L}_i^{T \to I}\bigr)$$

¿Por qué importa la simetría? Sin la dirección texto-a-imagen, el modelo podría aprender una solución degenerada: mapear todas las imágenes a un solo punto en el espacio de embeddings que resulte estar cerca de todos los textos. La pérdida imagen-a-texto se satisfaría (cada imagen encuentra algún texto cercano), pero las representaciones de imagen serían inútiles porque son todas idénticas. La pérdida bidireccional previene este colapso porque la dirección texto-a-imagen requiere que cada texto encuentre su imagen única , lo cual solo es posible si los embeddings de imagen están dispersos y son distintos. Simétricamente, la dirección imagen-a-texto previene que todos los embeddings de texto colapsen. Juntas, ambas direcciones fuerzan al modelo a aprender un espacio de embeddings bien estructurado y discriminativo.

Por Qué Importa el Tamaño del Lote

Cada lote de $N$ pares crea una señal de entrenamiento rica. La matriz de similitud $N \times N$ contiene $N$ pares positivos a lo largo de la diagonal y $N^2 - N$ comparaciones negativas fuera de la diagonal. Lotes más grandes significan más negativos por positivo, lo que significa una tarea de discriminación más difícil que obliga al modelo a aprender representaciones más finas.

Para ponerlo en números: con un lote de $N = 256$, cada imagen se compara contra 255 textos incorrectos. El modelo podría salirse con distinciones gruesas ("esto es un animal" vs. "esto es un edificio"). Con $N = 32{,}768$ (el tamaño de lote real de CLIP), cada imagen se compara contra 32,767 textos incorrectos. Ahora el lote casi con certeza contiene muchos distractores semánticamente similares — múltiples imágenes de perros con diferentes descripciones, múltiples escenas al aire libre — así que el modelo debe aprender a distinguir "un golden retriever jugando a buscar en la playa" de "un labrador nadando en un lago".

Esto tiene un costo. Todos los $N$ embeddings de imagen y todos los $N$ embeddings de texto deben caber simultáneamente en la memoria de la GPU para calcular la matriz de similitud completa de $N \times N$. Con $N = 32{,}768$ y embeddings de 512 dimensiones en float32, eso es aproximadamente $32{,}768 \times 512 \times 4 \text{ bytes} \times 2 \text{ (imagen + texto)} \approx 128$ MB solo para los embeddings, más la propia matriz de similitud de $32{,}768 \times 32{,}768$ (~4 GB en float32). CLIP distribuye esto entre muchas GPUs, con cada GPU calculando un lote local y luego reuniendo embeddings de todas las demás GPUs para formar la matriz de similitud completa.

💡 Trabajos posteriores han encontrado formas de obtener aprendizaje contrastivo fuerte sin lotes tan enormes. SigLIP (cubierto en el artículo 4 de este track) reemplaza la pérdida InfoNCE basada en softmax con una pérdida sigmoide que se descompone en decisiones independientes por par, eliminando la necesidad de normalización de todos los pares y permitiendo entrenamiento eficiente con tamaños de lote efectivos más pequeños.

Clasificación Zero-Shot

Una vez entrenado, CLIP puede clasificar imágenes en categorías para las que nunca ha visto ejemplos etiquetados — una capacidad llamada clasificación zero-shot . El truco es tratar la clasificación como un problema de recuperación en el espacio de embeddings compartido:

  • Paso 1: Para cada clase $c$, crear un prompt de texto: "a photo of a {nombre de clase}" (p. ej., "a photo of a golden retriever", "a photo of a sports car", "a photo of a pizza").
  • Paso 2: Codificar todos los prompts a través del codificador de texto para obtener embeddings de texto $\{\mathbf{v}_{T_c}\}$. Estos pueden precomputarse una sola vez.
  • Paso 3: Codificar la imagen de prueba a través del codificador de imagen para obtener $\mathbf{v}_I$.
  • Paso 4: Elegir la clase cuyo embedding de texto tiene la mayor similitud coseno con la imagen:
$$\hat{c} = \arg\max_c \; \text{sim}(\mathbf{v}_I, \mathbf{v}_{T_c})$$

Esto es notablemente efectivo. En ImageNet (1,000 clases), el mejor modelo de CLIP (ViT-L/14) logra aproximadamente 76% de precisión top-1 sin ver una sola imagen de entrenamiento de ImageNet (Radford et al., 2021) . Para contexto, un ResNet-50 entrenado en el conjunto completo de entrenamiento de ImageNet (1.2 millones de imágenes etiquetadas, supervisado explícitamente para estas 1,000 clases) también logra alrededor del 76%. CLIP iguala ese rendimiento usando solo la comprensión del lenguaje natural que adquirió de 400 millones de pares imagen-texto recopilados de la web — ninguno de los cuales eran etiquetas de ImageNet.

La plantilla del prompt importa. "a photo of a {clase}" tiende a funcionar mejor que solo "{clase}" porque añade contexto que ancla el embedding de texto más cerca de lo que parecen los títulos reales de imágenes en los datos de entrenamiento de CLIP. OpenAI descubrió que combinar múltiples plantillas de prompt ("a painting of a {clase}", "a drawing of a {clase}", etc.) y promediar los embeddings de texto resultantes puede aumentar la precisión en algunos puntos porcentuales.

El código a continuación demuestra esta idea con un ejemplo simplificado. Simulamos 4 embeddings de imagen y 4 embeddings de prompts de texto en 3 dimensiones, calculamos la matriz completa de similitud coseno y vemos qué texto coincide mejor con cada imagen:

import json
import js
import math

# Embeddings simulados (en la práctica vienen de los codificadores de CLIP)
# 4 imágenes, 4 descripciones de texto, dim de embedding = 3
images = [
    [0.9, 0.1, 0.0],   # imagen "perro"
    [0.0, 0.8, 0.3],   # imagen "gato"
    [0.1, 0.0, 0.95],  # imagen "auto"
    [0.7, 0.3, 0.1],   # imagen "cachorro"
]

texts = [
    [0.85, 0.15, 0.05],  # "a photo of a dog"
    [0.05, 0.75, 0.35],  # "a photo of a cat"
    [0.15, 0.05, 0.9],   # "a photo of a car"
    [0.6, 0.4, 0.2],     # "a photo of a puppy"
]

def norm(v):
    mag = math.sqrt(sum(x**2 for x in v))
    return [x / mag for x in v]

def dot(a, b):
    return sum(x * y for x, y in zip(a, b))

# Normalizar
images_n = [norm(v) for v in images]
texts_n  = [norm(v) for v in texts]

# Calcular matriz de similitud
labels_i = ["perro img", "gato img", "auto img", "cachor img"]
labels_t = ["perro txt", "gato txt", "auto txt", "cachor txt"]

table_rows = []
for i, iv in enumerate(images_n):
    row = [dot(iv, tv) for tv in texts_n]
    best = row.index(max(row))
    table_rows.append([labels_i[i]] + [f"{s:.3f}" for s in row] + [labels_t[best]])

js.window.py_table_data = json.dumps({
    "headers": ["Imagen", *labels_t, "Mejor Coincidencia"],
    "rows": table_rows
})

print("La diagonal debería ser la más alta si el modelo aprendió buena alineación.")
print("Nota: 'cachorro img' también coincide bien con 'perro txt' — CLIP captura similitud semántica.")

Observa cómo la matriz de similitud revela estructura más allá de la diagonal. La "imagen cachorro" tiene alta similitud tanto con "texto perro" como con "texto cachorro" porque los cachorros y los perros están semánticamente cerca. En un modelo CLIP real con 512 o 768 dimensiones, estas relaciones semánticas se capturan con mucha mayor granularidad, pero el principio es el mismo: el espacio de embeddings compartido organiza conceptos por significado, no por píxeles o caracteres superficiales.

Quiz

Pon a prueba tu comprensión de la arquitectura, entrenamiento y capacidades zero-shot de CLIP.

En la pérdida contrastiva de CLIP, ¿qué papel juega el denominador (suma sobre todos los textos del lote)?

¿Qué sucede con la distribución softmax cuando la temperatura $\tau \to 0^+$?

¿Por qué CLIP usa ambas direcciones de pérdida, imagen-a-texto y texto-a-imagen?

¿Cómo realiza CLIP la clasificación zero-shot en un dataset en el que nunca fue entrenado?