PIXELBANKv9.1.0
Menu

Problem Statement

Implement SigLIP's sigmoid contrastive loss over a batch of image and text embeddings.

Background

SigLIP replaces CLIP's softmax with an independent binary classification per pair. Every one of the n x n image-text pairs gets a label

zij={+1i=j−1i≠jz_{ij} = \begin{cases} +1 & i = j \\ -1 & i \neq j \end{cases}

and a logit built from the scaled cosine similarity plus a learned bias b (initialised strongly negative, around -10, because the batch is overwhelmingly negatives):

ℓij=s⋅I^i⋅T^j+b\ell_{ij} = s \cdot \hat I_i \cdot \hat T_j + b

The loss is the mean over the batch of the summed pairwise logistic losses:

L=1n∑i=1n∑j=1nlog⁡ ⁣(1+e−zijℓij)\mathcal{L} = \frac{1}{n}\sum_{i=1}^{n}\sum_{j=1}^{n} \log\!\left(1 + e^{-z_{ij}\ell_{ij}}\right)

Note the normalisation: the double sum is divided by n, not by n^2.

The point of the reformulation is that no term needs any other pair's logit. CLIP's denominator sums the whole row, forcing an all-gather across every device in the batch; SigLIP's terms are independent, so it trains at small batch sizes and shards trivially. That independence is why -log sigmoid must be computed pairwise here.

log(1 + exp(-z*l)) overflows for large negative z*l. Use numpy.logaddexp(0.0, -z*l), which is the stable softplus.

Your Task

Implement:

def siglip_loss(image_emb, text_emb, logit_scale, bias):

Return the scalar loss rounded to 4 decimals.

Input Format

  • image_emb, text_emb - n x d nested lists, unnormalised, row i a positive pair
  • logit_scale - positive float
  • bias - float, typically negative

Output Format

A single float rounded to 4 decimals.

Sample

img = [[1.0, 0.0], [0.0, 1.0]]
txt = [[1.0, 0.0], [0.0, 1.0]]
print(siglip_loss(img, txt, 10.0, -5.0))

Output:

0.0134

Example:

Input:
img = [[1.0, 0.0], [0.0, 1.0]]
txt = [[1.0, 0.0], [0.0, 1.0]]
print(siglip_loss(img, txt, 10.0, -5.0))
Output:
0.0134
Reasoning:

The cosine matrix is the identity. Diagonal logits are 101 - 5 = 5, giving softplus(-5) = 0.0067 each. Off-diagonal logits are 100 - 5 = -5 with label -1, so z*l = +5 and softplus(-5) = 0.0067 each. Four terms of 0.006715 summed and divided by n = 2 gives 0.0134.

Constraints:

  • 1 <= n <= 64, 1 <= d <= 64; no row is the zero vector
  • L2-normalise the embedding rows before taking dot products
  • Labels are +1 on the diagonal and -1 everywhere else
  • The double sum is divided by n (NOT by n * n)
  • Use np.logaddexp (stable softplus) - naive log(1 + exp(-x)) overflows
  • Round the returned loss to 4 decimals
🔒

Editor locked

The code editor is locked for Pro problems. It is only available for free problems. Please upgrade to gain access to the code editor for all problems.

solution.py

Test Results

0/0
Run code to see test results.
SigLIP Pairwise Sigmoid Loss - Medium | PixelBank