Spaces:
Running
Running
Fix condition comparison (mean-center embeddings), heatmap show-all toggle, CLI for run_embeddings, README local-embedding guide
1bdf54e | """ | |
| run_embeddings.py — compute and save ONLY the embeddings. | |
| Unlike run_pipeline.py (which also fits BERTopic and calls the LLM), this script | |
| does just the slow part: preprocess the CSV and embed it. It needs no HF_TOKEN and | |
| does not touch the BERTopic config. | |
| It writes the exact two files the Streamlit app looks for: | |
| precomputed_<...>_docs.json | |
| precomputed_<...>_<model>_..._embeddings.npy | |
| so you can upload them to the Hugging Face Space (same cache/ path) and the app | |
| will skip embedding entirely. | |
| Usage: | |
| python run_embeddings.py # local (set DEVICE below) | |
| sbatch run_embeddings.sh # on a SLURM cluster (e.g. Artemis) | |
| """ | |
| import argparse | |
| import json | |
| from pathlib import Path | |
| import numpy as np | |
| from mosaic_core.core_functions import ( | |
| preprocess_and_embed, | |
| get_cache_dir, | |
| get_precomputed_filenames, | |
| ) | |
| # ── Defaults (override any of these on the command line, see --help) ─────────── | |
| PROJECT_ROOT = Path(__file__).parent | |
| DATASET_NAME = "MOSAIC" # must match the app sidebar "Project/Dataset name" | |
| CSV_PATH = "data/preprocessed/MOSAIC/your_file.csv" # path to your CSV (← change this) | |
| EMBEDDING_MODEL = "Qwen/Qwen3-Embedding-4B" # must match the model picked in the app sidebar | |
| TEXT_COL = None # None = auto-detect, or e.g. "reflection_answer_english" | |
| SPLIT_SENTENCES = True | |
| MIN_WORDS = 3 | |
| DEVICE = "cuda" # "cuda" on Artemis, "mps" on a Mac, "cpu" otherwise | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| _p = argparse.ArgumentParser(description="Compute and save ONLY the embeddings for a CSV.") | |
| _p.add_argument("--csv", default=CSV_PATH, help="Path to the input CSV") | |
| _p.add_argument("--dataset", default=DATASET_NAME, help="Dataset name (must match the app sidebar)") | |
| _p.add_argument("--model", default=EMBEDDING_MODEL, help="SentenceTransformer model name") | |
| _p.add_argument("--text-col", default=TEXT_COL, help="Text column (omit to auto-detect)") | |
| _p.add_argument("--min-words", type=int, default=MIN_WORDS, help="Drop sentences shorter than this") | |
| _p.add_argument("--device", default=DEVICE, help='"cuda", "mps" or "cpu"') | |
| _p.add_argument("--no-split", dest="split", action="store_false", help="Do NOT split into sentences") | |
| _p.set_defaults(split=SPLIT_SENTENCES) | |
| _args = _p.parse_args() | |
| DATASET_NAME = _args.dataset | |
| CSV_PATH = _args.csv | |
| EMBEDDING_MODEL = _args.model | |
| TEXT_COL = _args.text_col | |
| SPLIT_SENTENCES = _args.split | |
| MIN_WORDS = _args.min_words | |
| DEVICE = _args.device | |
| CACHE_DIR = get_cache_dir(PROJECT_ROOT, DATASET_NAME) | |
| CACHE_DIR.mkdir(parents=True, exist_ok=True) | |
| DOCS_FILE, EMBEDDINGS_FILE = get_precomputed_filenames( | |
| CACHE_DIR, CSV_PATH, EMBEDDING_MODEL, SPLIT_SENTENCES, TEXT_COL, MIN_WORDS | |
| ) | |
| print(f"Dataset : {DATASET_NAME}") | |
| print(f"CSV : {CSV_PATH}") | |
| print(f"Model : {EMBEDDING_MODEL} (device={DEVICE})") | |
| print(f"Docs -> {DOCS_FILE}") | |
| print(f"Embeds -> {EMBEDDINGS_FILE}") | |
| docs, embeddings = preprocess_and_embed( | |
| CSV_PATH, | |
| model_name=EMBEDDING_MODEL, | |
| text_col=TEXT_COL, | |
| split_sentences=SPLIT_SENTENCES, | |
| min_words=MIN_WORDS, | |
| device=DEVICE, | |
| ) | |
| embeddings = np.asarray(embeddings, dtype=np.float32) | |
| np.save(EMBEDDINGS_FILE, embeddings) | |
| with open(DOCS_FILE, "w", encoding="utf-8") as f: | |
| json.dump(docs, f, ensure_ascii=False) | |
| print(f"\nDone. {len(docs)} docs -> embeddings shape {embeddings.shape}") | |
| print("Upload these two files to the Space's cache/ folder to skip embedding there.") | |