vlm-apmatch / backend /scripts /precompute_embeddings.py
razaell97's picture
Deploy VLM-APMatch demo (redacted public scan set, single-service Docker)
87d5c49 verified
Raw
History Blame Contribute Delete
1.92 kB
#!/usr/bin/env python3
"""
precompute_embeddings.py — build demo_data/embeddings.npz.
Collects every item-name string the matcher could feed to the semantic step
(applying the matcher's own _norm, plus the "UNKNOWN" blank-fallback) from every
served demo document across all five models, encodes them once with
all-MiniLM-L6-v2 (normalize_embeddings=True), and saves an .npz the runtime
loads as a torch-free cache. Run offline with system python3 (needs
sentence-transformers in the user site-packages).
"""
import glob
import json
import os
import sys
import numpy as np
HERE = os.path.dirname(os.path.abspath(__file__))
BACKEND = os.path.dirname(HERE)
TX_DIR = os.path.join(BACKEND, "demo_data", "transactions")
OUT = os.path.join(BACKEND, "demo_data", "embeddings.npz")
# Use the vendored matcher's exact normaliser so cached keys match runtime lookups.
sys.path.insert(0, BACKEND)
from vendor import matcher as M # noqa: E402
def collect_names():
names = set()
for f in glob.glob(os.path.join(TX_DIR, "**", "*.json"), recursive=True):
doc = json.load(open(f, encoding="utf-8"))
for it in (doc.get("line_items") or []):
norm = M._norm(it.get("item_name") or "")
names.add(norm if norm else "UNKNOWN")
names.add("UNKNOWN")
return sorted(n for n in names if n)
def main():
names = collect_names()
if not names:
print("No item names found — did you run build_demo_data.py first?")
sys.exit(1)
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("all-MiniLM-L6-v2")
vectors = model.encode(names, normalize_embeddings=True).astype("float32")
np.savez_compressed(OUT, names=np.array(names, dtype=object), vectors=vectors)
print(f"Wrote {len(names)} embeddings (dim={vectors.shape[1]}) to {OUT}")
print("Sample names:", names[:5])
if __name__ == "__main__":
main()