Spaces:
Paused
Paused
File size: 2,880 Bytes
9efa0c5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | import json
import torch
import numpy as np
from pathlib import Path
from tqdm import tqdm
from search import model, tokenizer
# 1. Load filenames.json and image_embeddings.npy
# build a lookup: filename → row number (0..999)
EMB_DIR = Path("embeddings")
image_embeddings = np.load(EMB_DIR / "image_embeddings.npy") # (1000, 512)
filenames = json.loads((EMB_DIR / "filenames.json").read_text()) # 1000 names
row_of = {name: i for i, name in enumerate(filenames)}
# 2. Read Flickr8k.token.txt
# for each line: split on "\t", strip the "#n" from the filename
# keep it only if the filename is in the lookup
# → captions: list of 5,000 strings
# → true_rows: list of 5,000 row numbers (which image each caption belongs to)
CAPTIONS_FILE = Path("data/Flickr8k.token.txt")
captions = [] # the caption texts
true_rows = [] # for each caption, the row of the image it describes
for line in CAPTIONS_FILE.read_text().splitlines():
if not line.strip():
continue
image_ref, caption = line.split("\t", 1) # "abc.jpg#0", "A dog runs ..."
filename = image_ref.split("#")[0] # "abc.jpg"
if filename in row_of: # only the 1,000 indexed images
captions.append(caption.strip())
true_rows.append(row_of[filename])
true_rows = np.array(true_rows)
# 3. Embed the 5,000 captions with CLIP's text encoder
# in batches (e.g. 256), normalize, like build_index.py did for images
# → text_embeddings: (5000, 512)
BATCH_SIZE = 256
text_vectors = []
for start in tqdm(range(0, len(captions), BATCH_SIZE)):
batch = captions[start:start + BATCH_SIZE]
tokens = tokenizer(batch)
with torch.no_grad():
vectors = model.encode_text(tokens)
vectors = vectors / vectors.norm(dim=-1, keepdim=True)
text_vectors.append(vectors.cpu().numpy())
text_embeddings = np.concatenate(text_vectors)
# 4. Similarity matrix
# sims = text_embeddings @ image_embeddings.T → (5000, 1000)
sims = text_embeddings @ image_embeddings.T # (5000, 1000)
# 5. Rank of the true image for each caption
# true_scores = sims[i, true_rows[i]] for each i
# rank = how many images scored HIGHER than the true one
# (0 = it came first, 4 = it came fifth, …)
true_scores = sims[np.arange(len(captions)), true_rows] # (5000,)
ranks = (sims > true_scores[:, None]).sum(axis=1) # (5000,) 0 = first place
# 6. Recall@K = fraction of captions with rank < K, for K = 1, 5, 10
# print a small table
print()
print("Text → image retrieval on Flickr8k test (1,000 images, 5,000 captions)")
print("Model: CLIP ViT-B/32 (laion2b_s34b_b79k), zero-shot")
print("-" * 40)
for k in (1, 5, 10):
recall = (ranks < k).mean()
print(f"Recall@{k:<2} {recall:6.1%}")
print("-" * 40)
print(f"Median rank: {int(np.median(ranks)) + 1}") |