Download scripts/ann_recall.py from CamiloS1111/ragsolver: direct link, hf CLI and curl.
- Browser
- Download file 3.85 kB
-
https://huggingface.co/CamiloS1111/ragsolver/resolve/main/scripts/ann_recall.py
- Command line
-
hf download hf://CamiloS1111/ragsolver/scripts/ann_recall.py
-
curl -L -o ann_recall.py https://huggingface.co/CamiloS1111/ragsolver/resolve/main/scripts/ann_recall.py
3.85 kB
| """Índice ANN de `mathlib_full`: construye IVF_SQ (si hace falta) y mide cuánto se aparta de la | |
| búsqueda exacta (recall@k del ANN respecto del top-k exacto), latencia p50/p95 de ambas y RSS. | |
| Consultas sintéticas sin cargar el embedder: normalize(e_i + e_j) para pares al azar de | |
| declaraciones (caen entre dos documentos, no encima de uno). Uso: | |
| python scripts/ann_recall.py [--build] [--n 200] [--out data/eval/results/ann_mathlib_full.json] | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import resource | |
| import statistics | |
| import time | |
| import lancedb | |
| import numpy as np | |
| from ragsolver.rag.index import ANN_NPROBES, ANN_REFINE, build_ann_index, sql_quote | |
| def main() -> None: | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--db-path", default="data/index/lancedb") | |
| ap.add_argument("--table", default="mathlib_full") | |
| ap.add_argument("--build", action="store_true") | |
| ap.add_argument("--n", type=int, default=200) | |
| ap.add_argument("--k", type=int, default=50) | |
| ap.add_argument("--out", default="data/eval/results/ann_mathlib_full.json") | |
| args = ap.parse_args() | |
| t = lancedb.connect(args.db_path).open_table(args.table) | |
| n_rows = t.count_rows() | |
| if args.build: | |
| t0 = time.time() | |
| build_ann_index(t) | |
| print(f"índice construido en {time.time() - t0:.0f} s", flush=True) | |
| has_index = any("IVF" in str(i.index_type).upper() for i in t.list_indices()) | |
| rng = np.random.default_rng(13) | |
| all_ids = t.search().select(["chunk_id"]).limit(n_rows).to_arrow().column("chunk_id").to_pylist() | |
| pick = [all_ids[i] for i in rng.choice(n_rows, size=2 * args.n, replace=False)] | |
| rows = t.search().where("chunk_id IN (" + ", ".join(sql_quote(c) for c in pick) + ")").select(["chunk_id", "embedding"]).limit(len(pick)).to_list() | |
| by_id = {r["chunk_id"]: r["embedding"] for r in rows} | |
| emb = np.array([by_id[c] for c in pick], dtype="float32") | |
| queries = emb[0::2] + emb[1::2] | |
| queries /= np.linalg.norm(queries, axis=1, keepdims=True) | |
| rec10, rec50, lat_exact, lat_ann = [], [], [], [] | |
| for i, q in enumerate(queries): | |
| t0 = time.time() | |
| exact = [r["chunk_id"] for r in t.search(q, vector_column_name="embedding").bypass_vector_index().limit(args.k).select(["chunk_id"]).to_list()] | |
| lat_exact.append(time.time() - t0) | |
| t0 = time.time() | |
| ann = [r["chunk_id"] for r in t.search(q, vector_column_name="embedding").nprobes(ANN_NPROBES).refine_factor(ANN_REFINE).limit(args.k).select(["chunk_id"]).to_list()] | |
| lat_ann.append(time.time() - t0) | |
| rec10.append(len(set(exact[:10]) & set(ann[:10])) / 10) | |
| rec50.append(len(set(exact) & set(ann)) / len(exact)) | |
| if (i + 1) % 50 == 0: | |
| print(f"{i + 1}/{len(queries)} recall@10 {statistics.mean(rec10):.4f} recall@50 {statistics.mean(rec50):.4f}", flush=True) | |
| p = lambda xs, q: round(sorted(xs)[int(q * (len(xs) - 1))], 3) | |
| out = { | |
| "table": args.table, "rows": n_rows, "ann_index": has_index, "index_type": "IVF_SQ", "num_partitions": 256, | |
| "nprobes": ANN_NPROBES, "refine_factor": ANN_REFINE, "n_queries": len(queries), "k": args.k, | |
| "ann_recall@10_vs_exact": round(statistics.mean(rec10), 4), "ann_recall@50_vs_exact": round(statistics.mean(rec50), 4), | |
| "latency_exact_s": {"p50": p(lat_exact, 0.5), "p95": p(lat_exact, 0.95)}, "latency_ann_s": {"p50": p(lat_ann, 0.5), "p95": p(lat_ann, 0.95)}, | |
| "maxrss_mb": resource.getrusage(resource.RUSAGE_SELF).ru_maxrss // 1024, | |
| "note": "consultas sintéticas normalize(e_i + e_j); latencias medidas con la corrida eval_v1 en paralelo (CPU compartida)", | |
| } | |
| open(args.out, "w", encoding="utf-8").write(json.dumps(out, indent=2, ensure_ascii=False)) | |
| print(json.dumps(out, ensure_ascii=False)) | |
| if __name__ == "__main__": | |
| main() | |