Source code for spacr.cli_leakage
"""``spacr-leakage`` — audit a classifier dataset without training a model."""
from __future__ import annotations
import argparse
import json
import sys
from typing import Optional, Sequence
from .classifier_evaluation import (
audit_dataset_splits,
write_leakage_audit,
)
[docs]
def build_parser() -> argparse.ArgumentParser:
"""Return the configured ``spacr-leakage`` argument parser."""
parser = argparse.ArgumentParser(
prog="spacr-leakage",
description=(
"Verify that related crops do not cross a classifier train/test "
"boundary."
),
)
parser.add_argument("dataset", help="folder containing train/ and test/")
parser.add_argument(
"--group-by", choices=("field", "well", "plate", "none"),
default="well",
)
parser.add_argument(
"--no-content-hash", action="store_true",
help="skip SHA-256 detection of renamed byte-identical crops",
)
parser.add_argument(
"--allow-unverifiable", action="store_true",
help="warn instead of failing when identity or content cannot be verified",
)
parser.add_argument("--output", help="also write the JSON report to this path")
return parser
[docs]
def main(argv: Optional[Sequence[str]] = None) -> int:
"""Audit a requested classifier dataset without training a model.
:param argv: command arguments without the executable name, or ``None`` to
read :data:`sys.argv`.
:returns: zero for a clean audit, one for leakage, or two when the dataset
cannot be audited.
"""
args = build_parser().parse_args(argv)
try:
report = audit_dataset_splits(
args.dataset,
group_by=args.group_by,
hash_content=not args.no_content_hash,
require_identity=not args.allow_unverifiable,
raise_on_leakage=False,
)
except (OSError, ValueError) as exc:
print(f"spacr-leakage: {exc}", file=sys.stderr)
return 2
payload = report.to_dict()
print(json.dumps(payload, indent=2, sort_keys=True))
if args.output:
write_leakage_audit(args.output, report)
return 0 if report.passed else 1
if __name__ == "__main__":
raise SystemExit(main())