v9.1.1
Menu

CLIP Contrastive (InfoNCE) Loss

mediumFREEContrastive Objectives: CLIP and SigLIPVision-Language ModelsVLM 1: Vision-Language Models

Problem Statement

Given a batch of image embeddings and their paired text embeddings, compute CLIP's symmetric InfoNCE loss.

Background

CLIP trains two encoders so that the i-th image matches the i-th caption and nothing else in the batch. Both embeddings are first L2-normalised, so their dot product is a cosine similarity in [-1, 1], then scaled by a learned logit_scale (the inverse temperature, exp(t), roughly 100 at convergence):

logits=s⋅I^T^⊤,I^i=Ii∥Ii∥2\text{logits} = s \cdot \hat{I}\hat{T}^\top, \qquad \hat{I}_i = \frac{I_i}{\lVert I_i \rVert_2}

The diagonal holds the n positive pairs; every off-diagonal entry is a negative. The loss is cross-entropy against the labels 0, 1, ..., n-1 computed in both directions and averaged:

L=12(1n∑i−log⁡es I^i⋅T^i∑jes I^i⋅T^j⏟image→text+1n∑j−log⁡es I^j⋅T^j∑ies I^i⋅T^j⏟text→image)\mathcal{L} = \tfrac{1}{2}\Big( \underbrace{\tfrac{1}{n}\sum_i -\log \tfrac{e^{s\,\hat I_i\cdot\hat T_i}}{\sum_j e^{s\,\hat I_i\cdot\hat T_j}}}_{\text{image}\to\text{text}} + \underbrace{\tfrac{1}{n}\sum_j -\log \tfrac{e^{s\,\hat I_j\cdot\hat T_j}}{\sum_i e^{s\,\hat I_i\cdot\hat T_j}}}_{\text{text}\to\text{image}} \Big)

The two directions differ: the first normalises over each row of the logit matrix, the second over each column. Averaging them is what makes CLIP usable for both image-to-text and text-to-image retrieval.

Because logit_scale is large, exp overflows on raw logits. Use the log-sum-exp trick: subtract each row's max before exponentiating.

Your Task

Implement:

def clip_loss(image_emb, text_emb, logit_scale):

image_emb and text_emb are n x d nested lists, row i of each being a positive pair. Return the scalar loss rounded to 4 decimals.

Input Format

  • image_emb, text_emb - lists of n lists of d floats, NOT pre-normalised
  • logit_scale - a positive float

Output Format

A single float rounded to 4 decimals.

Sample

img = [[1.0, 0.0], [0.0, 1.0]]
txt = [[0.9, 0.1], [0.2, 1.0]]
print(clip_loss(img, txt, 10.0))

Output:

0.0003

Example

Input
img = [[1.0, 0.0], [0.0, 1.0]]
txt = [[0.9, 0.1], [0.2, 1.0]]
print(clip_loss(img, txt, 10.0))
Output
0.0003
Reasoning
  • Inputs: img holds two image vectors and txt two text vectors in R2\mathbb{R}^2; row ii of each pairs up. logit_scale is s=10s = 10.
  • Normalise: the image rows already have length 1. The text rows have lengths 0.82≈0.9055\sqrt{0.82} \approx 0.9055 and 1.04≈1.0198\sqrt{1.04} \approx 1.0198, giving T^1=(0.9939,0.1104)\hat T_1 = (0.9939, 0.1104) and T^2=(0.1961,0.9806)\hat T_2 = (0.1961, 0.9806).
  • Logits: entry (i,j)(i, j) is s I^i⋅T^js\,\hat I_i \cdot \hat T_j, so the 2×22 \times 2 matrix is (9.93881.96121.10439.8058)\begin{pmatrix} 9.9388 & 1.9612 \\ 1.1043 & 9.8058 \end{pmatrix}. Positives sit on the diagonal.
  • Image to text (rows): row 1 costs log⁡[1+e1.9612−9.9388]≈0.000343\log\big[1 + e^{1.9612 - 9.9388}\big] \approx 0.000343, row 2 costs log⁡[1+e1.1043−9.8058]≈0.000166\log\big[1 + e^{1.1043 - 9.8058}\big] \approx 0.000166; mean ≈0.000255\approx 0.000255.
  • Text to image (columns): column 1 compares 9.93889.9388 with 1.10431.1043 (≈0.000146\approx 0.000146), column 2 compares 9.80589.8058 with 1.96121.9612 (≈0.000392\approx 0.000392); mean ≈0.000269\approx 0.000269.
  • Output: 12(0.000255+0.000269)≈0.000262\tfrac12(0.000255 + 0.000269) \approx 0.000262, which rounds to 0.0003. Notice the directions differ because the off-diagonals are not symmetric, and the loss is tiny because each positive beats its rival by about 8 logits.

Constraints

  • 1 <= n <= 64, 1 <= d <= 64; no row is the zero vector
  • Embeddings arrive unnormalised - you must L2-normalise the rows yourself
  • The loss is the AVERAGE of the image-to-text and text-to-image cross-entropies
  • Use a numerically stable log-sum-exp; logit_scale can be 100
  • Round the returned loss to 4 decimals

Learn this in

More from VLM 1: Vision-Language Models

solution.py

Test results

Sign in to run your code against the test cases.