codesearch / scripts /cache_query_vectors.py
CamQuestByte
M2 concept checkpoint: HNSW ef_search sweep
086a913
Raw
History Blame Contribute Delete
2.72 kB
"""
Pre-compute query embeddings and cache them to disk.
The ef_sweep script (and any future queries-only experiment) reads from this
cache to skip the embedding step. The cache key includes the embedding model
name, so changing EMBEDDING_MODEL invalidates this cache automatically and
forces a recompute.
Usage:
uv run python scripts/cache_query_vectors.py
uv run python scripts/cache_query_vectors.py --recompute
"""
from __future__ import annotations
import argparse
import os
import pickle
import sys
import time
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src"))
import numpy as np
from sentence_transformers import SentenceTransformer
from codesearch.config import EMBEDDING_MODEL
from codesearch.data import load_codesearch
CACHE_DIR = ".cache"
def cache_path(model_name: str) -> str:
safe = model_name.replace("/", "_")
return os.path.join(CACHE_DIR, f"query_vectors_{safe}.pkl")
def main() -> None:
parser = argparse.ArgumentParser(
description="Cache query embeddings so the ef sweep can skip the encode step."
)
parser.add_argument(
"--recompute",
action="store_true",
help="Re-embed even if the cache already exists.",
)
args = parser.parse_args()
path = cache_path(EMBEDDING_MODEL)
if os.path.exists(path) and not args.recompute:
size_mb = os.path.getsize(path) / 1024 / 1024
print(f"[skip] Cache already exists at {path} ({size_mb:.1f} MB).")
print(" Pass --recompute to rebuild.")
return
os.makedirs(CACHE_DIR, exist_ok=True)
print("[1/3] Loading eval queries (test split only)...")
_, queries = load_codesearch(n=-1, queries_only=True)
print(f"[2/3] Loading embedding model: {EMBEDDING_MODEL}")
model = SentenceTransformer(EMBEDDING_MODEL)
print(f"[3/3] Embedding {len(queries):,} queries...")
t0 = time.perf_counter()
vectors = model.encode(
[q["query"] for q in queries],
normalize_embeddings=True,
show_progress_bar=True,
batch_size=256,
)
elapsed = time.perf_counter() - t0
print(f" Done in {elapsed:.1f}s ({elapsed / len(queries) * 1000:.2f} ms/query)")
print(f"Writing cache to {path}...")
with open(path, "wb") as f:
pickle.dump(
{
"model": EMBEDDING_MODEL,
"queries": queries,
"vectors": np.asarray(vectors, dtype=np.float32),
},
f,
protocol=pickle.HIGHEST_PROTOCOL,
)
size_mb = os.path.getsize(path) / 1024 / 1024
print(f"Cached {len(queries):,} query vectors ({size_mb:.1f} MB).")
if __name__ == "__main__":
main()