CharlesCNorton
Image-level person classification on EUPE-ViT-B features with a single free parameter
f5498f9 | """Score a classifier config over a named pool and write its eval artifact. | |
| python verify.py # baseline on VAL5000 | |
| python verify.py --classifier classifier_tight_fpr.json | |
| python verify.py --pool CALIB1000 | |
| Writes eval.json for the baseline config and eval_tight_fpr.json for the | |
| tight-FPR one, unless --out says otherwise. | |
| """ | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| import torch | |
| sys.path.insert(0, str(Path(__file__).resolve().parent)) # repo root, for `common` | |
| from common import BACKBONE, device, f1_at, load_pool, score_pool, write_artifact # noqa: E402 | |
| from common.models import load_backbone # noqa: E402 | |
| from common.pools import VAL5000, by_name # noqa: E402 | |
| HERE = Path(__file__).resolve().parent | |
| def out_path_for(classifier: Path) -> Path: | |
| """classifier.json -> eval.json; classifier_tight_fpr.json -> eval_tight_fpr.json.""" | |
| suffix = classifier.stem[len('classifier'):] | |
| return classifier.parent / f'eval{suffix}.json' | |
| def main(): | |
| ap = argparse.ArgumentParser(description=__doc__) | |
| ap.add_argument('--classifier', type=Path, default=HERE / 'classifier.json') | |
| ap.add_argument('--backbone', default=BACKBONE) | |
| ap.add_argument('--pool', default=VAL5000.name) | |
| ap.add_argument('--out', type=Path, default=None) | |
| args = ap.parse_args() | |
| dev = device() | |
| pool = by_name(args.pool) | |
| c = json.loads(args.classifier.read_text()) | |
| print(f'[init] loading {args.backbone}', flush=True) | |
| backbone = load_backbone(args.backbone).to(dev) | |
| print(f'[pool] {pool.name}', flush=True) | |
| loaded = load_pool(pool, dev) | |
| pos = torch.tensor(c['pos_dims'], dtype=torch.long, device=dev) | |
| neg = torch.tensor(c['neg_dims'], dtype=torch.long, device=dev) | |
| scores, _ = score_pool(backbone, loaded, pos, neg) | |
| m = f1_at(scores, loaded.labels, c['threshold']) | |
| print(f'[verify] F1={m.f1:.4f} P={m.precision:.4f} R={m.recall:.4f}', flush=True) | |
| path = args.out or out_path_for(args.classifier) | |
| write_artifact( | |
| path, {'metrics': {k: round(v, 4) if k != 'threshold' else v | |
| for k, v in m.asdict().items()}}, | |
| generator='verify.py', classifier=args.classifier, | |
| pool_info=loaded.provenance(), | |
| task='image-level person presence (binary)', | |
| protocol='live backbone forward at 768 px, no feature caching') | |
| print(f'[done] wrote {path}', flush=True) | |
| if __name__ == '__main__': | |
| main() | |