Given a query embedding and a gallery of candidate embeddings from a CLIP-style dual encoder, return the indices of the k most similar candidates and their cosine similarities.
A dual encoder makes retrieval a nearest-neighbour lookup in a shared space. Similarity is cosine, not raw dot product, because the contrastive objective only ever constrained directions - vector magnitude carries no meaning and an unnormalised dot product would just rank long vectors first:
sim(q,gi)=∥q∥2∥gi∥2q⋅gi
Normalise both sides and the cosine is a plain dot product, which is why production retrieval stores pre-normalised vectors and uses an inner-product index.
Rank descending and take the first k. Ties must break toward the lower index - a stable sort of the negated scores does this for free.
Implement:
def retrieve_topk(query, gallery, k):
Return [indices, sims] where indices is a list of k ints (best first) and sims is the matching list of cosine similarities, each rounded to 4 decimals.
[[int, ...], [float, ...]], both lists of length k.
q = [1.0, 0.0]
gal = [[0.0, 2.0], [3.0, 0.0], [1.0, 1.0]]
print(retrieve_topk(q, gal, 2))
Output:
[[1, 2], [1.0, 0.7071]]
q = [1.0, 0.0] gal = [[0.0, 2.0], [3.0, 0.0], [1.0, 1.0]] print(retrieve_topk(q, gal, 2))
[[1, 2], [1.0, 0.7071]]
Cosines against [1,0] are 0.0, 1.0 and 0.7071. Gallery item 1 points the same way as the query despite being three times longer - magnitude is normalised away - so it ranks first, then item 2 at 0.7071.
1 <= m <= 200, 1 <= d <= 64; no vector is the zero vector-0.0