Download scripts/bench.py from Arafath10/textclsy: direct link, hf CLI and curl.
- Browser
- Download file 5.26 kB
-
https://huggingface.co/spaces/Arafath10/textclsy/resolve/main/scripts/bench.py
- Command line
-
hf download hf://spaces/Arafath10/textclsy/scripts/bench.py
-
curl -L -o bench.py https://huggingface.co/spaces/Arafath10/textclsy/resolve/main/scripts/bench.py
5.26 kB
| """Benchmark the zero-shot classifier against a small labelled set. | |
| Run from the project root: | |
| python scripts/bench.py # current MODEL_NAME | |
| python scripts/bench.py MoritzLaurer/deberta-v3-xsmall-zeroshot-v1.1-all-33 | |
| Prints accuracy for a sweep of LEXICAL_WEIGHT values and hypothesis templates, | |
| so config changes are made on evidence rather than vibes. The cases are the | |
| English text as it would arrive *after* translation, since that is what the | |
| model actually sees. | |
| The weight sweep runs the model ONCE per case and re-blends the cached scores: | |
| LEXICAL_WEIGHT only affects the blend, never the model, so re-running inference | |
| per weight would just burn time for identical numbers. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| import time | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) | |
| os.environ.setdefault("HF_HUB_DISABLE_XET", "1") | |
| from app import classifier # noqa: E402 | |
| from app.config import DEFAULT_LABELS # noqa: E402 | |
| LABELS = [{"name": n, "description": d} for n, d in DEFAULT_LABELS] | |
| CASES: list[tuple[str, str]] = [ | |
| ("There is no water in our village for three days", "Water Supply"), | |
| ("The water coming from the tap is muddy and smells bad", "Water Supply"), | |
| ("A main pipe has burst and water is flooding the street", "Water Supply"), | |
| ("Our area has been without electricity for three days", "Electricity"), | |
| ("The transformer near our house is damaged and sparking", "Electricity"), | |
| ("Voltage keeps fluctuating and our appliances are getting damaged", "Electricity"), | |
| ("There is a huge pothole on the main road", "Road & Transport"), | |
| ("The traffic signal at the junction is not working", "Road & Transport"), | |
| ("The pavement is broken and people cannot walk safely", "Road & Transport"), | |
| ("Garbage has not been collected in our locality for a week", "Waste & Sanitation"), | |
| ("The drain is blocked and sewage is overflowing on the street", "Waste & Sanitation"), | |
| ("The public bin is overflowing and smells terrible", "Waste & Sanitation"), | |
| ("Mosquitoes are breeding in the stagnant water near the school", "Public Health"), | |
| ("Stray dogs are attacking children in our neighbourhood", "Public Health"), | |
| ("Construction noise starts at 5am every day and nobody can sleep", "Noise & Pollution"), | |
| ("A factory is releasing thick black smoke into the air", "Noise & Pollution"), | |
| ("Someone is putting up an illegal building without permission", "Building & Property"), | |
| ("The old building next door is cracked and looks unsafe", "Building & Property"), | |
| ] | |
| CANDIDATES = [l["name"] for l in LABELS] | |
| def model_scores(template: str) -> tuple[list[list[tuple[str, float]]], int]: | |
| """Raw model scores for every case under one hypothesis template.""" | |
| out, latencies = [], [] | |
| for text, _ in CASES: | |
| started = time.perf_counter() | |
| r = classifier._pipe( | |
| text, candidate_labels=CANDIDATES, | |
| hypothesis_template=template, multi_label=False, | |
| ) | |
| latencies.append((time.perf_counter() - started) * 1000) | |
| out.append(list(zip(r["labels"], [float(s) for s in r["scores"]]))) | |
| return out, int(sum(latencies) / len(latencies)) | |
| def score(cached, weight, avg_ms, title) -> tuple[int, list[str]]: | |
| hits, misses = 0, [] | |
| for (text, expected), raw in zip(CASES, cached): | |
| blended = classifier._blend(list(raw), text, LABELS, weight) | |
| blended.sort(key=lambda p: p[1], reverse=True) | |
| got, conf = blended[0] | |
| if got == expected: | |
| hits += 1 | |
| else: | |
| misses.append(f" {expected:<20} -> {got:<20} ({conf:.2f}) {text[:46]}") | |
| print(f" {title:<44} {hits:>2}/{len(CASES)} ({100 * hits / len(CASES):3.0f}%) ~{avg_ms}ms") | |
| return hits, misses | |
| def main() -> int: | |
| if len(sys.argv) > 1: | |
| classifier.MODEL_NAME = sys.argv[1] | |
| classifier._state["model"] = sys.argv[1] | |
| started = time.time() | |
| state = classifier.load_model(force=True) | |
| if state["status"] != "ready": | |
| print("MODEL FAILED TO LOAD:\n", state["error"][:500]) | |
| return 1 | |
| print(f"\nmodel: {classifier._state['model']} (loaded in {time.time() - started:.0f}s)\n") | |
| default_template = "This complaint is about {}." | |
| cached, avg_ms = model_scores(default_template) | |
| print("LEXICAL_WEIGHT sweep (model run once, blend re-applied)") | |
| best = (-1, 0.0, []) | |
| for weight in (0.0, 0.15, 0.25, 0.3, 0.4, 0.5, 0.6): | |
| hits, misses = score(cached, weight, avg_ms, f"lexical_weight = {weight}") | |
| if hits > best[0]: | |
| best = (hits, weight, misses) | |
| print(f"\n best: lexical_weight={best[1]} -> {best[0]}/{len(CASES)}") | |
| if best[2]: | |
| print(" remaining misses:") | |
| print("\n".join(best[2])) | |
| print("\nhypothesis template sweep") | |
| score(cached, best[1], avg_ms, f"{default_template!r}") | |
| for template in ( | |
| "This is a complaint about {}.", | |
| "The problem is {}.", | |
| "This text is about {}.", | |
| ): | |
| c, ms = model_scores(template) | |
| score(c, best[1], ms, f"{template!r}") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |