File size: 4,643 Bytes
b0e01a5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | #!/usr/bin/env python3
"""Sample a stratified subset from data/in1k_hybrid_1k/ for ablation sweep.
Picks N images from the hybrid dataset, prioritizing synsets with Guillaumin
GT mask (so foreground analysis remains valid for as many subset rows as
possible). Within each preference tier, samples are deterministic given --seed.
Output: a new dir with N images + metadata.json (same schema as parent dataset).
Usage:
python scripts/sample_hybrid_subset.py --n 200 \
--in data/in1k_hybrid_1k \
--out data/in1k_hybrid_1k_subset200 \
--seed 42
"""
from __future__ import annotations
import argparse
import json
import random
import shutil
import sys
from pathlib import Path
def _find_project_root() -> Path:
cur = Path(__file__).resolve().parent
for parent in [cur, *cur.parents]:
if (parent / "requirements.txt").exists():
return parent
raise RuntimeError(f"project root not found from {__file__}")
PROJECT_ROOT = _find_project_root()
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--in", dest="in_dir", type=Path,
default=PROJECT_ROOT / "data" / "in1k_hybrid_1k")
parser.add_argument("--out", type=Path,
default=PROJECT_ROOT / "data" / "in1k_hybrid_1k_subset200")
parser.add_argument("--n", type=int, default=200)
parser.add_argument("--seed", type=int, default=42)
parser.add_argument("--use-symlinks", action="store_true")
args = parser.parse_args()
rng = random.Random(args.seed)
parent_meta = args.in_dir / "metadata.json"
if not parent_meta.exists():
print(f"ERROR: parent metadata not found: {parent_meta}")
print(f"Run `python scripts/build_in1k_hybrid_1k.py` first.")
return 1
payload = json.loads(parent_meta.read_text())
samples = payload.get("samples") or []
if len(samples) != 1000:
print(f"ERROR: parent has {len(samples)} samples, expected 1000")
return 1
# Stratified sampling: pegar TODOS os has_mask=True primeiro (~95),
# depois preencher com aleatórios sem mask até atingir n.
with_mask = [s for s in samples if s.get("has_mask")]
without_mask = [s for s in samples if not s.get("has_mask")]
print(f"Parent dataset: {len(with_mask)} com mask, {len(without_mask)} sem mask")
if args.n <= len(with_mask):
# Subset cabe inteirinho dentro dos com mask: amostra deterministicamente
chosen = sorted(rng.sample(with_mask, args.n), key=lambda s: s["imagenet_id"])
else:
n_extra = args.n - len(with_mask)
extras = sorted(rng.sample(without_mask, n_extra),
key=lambda s: s["imagenet_id"])
chosen = sorted(with_mask + extras, key=lambda s: s["imagenet_id"])
n_chosen_mask = sum(1 for s in chosen if s.get("has_mask"))
print(f"Selected: {len(chosen)} ({n_chosen_mask} com mask)")
args.out.mkdir(parents=True, exist_ok=True)
for s in chosen:
src = args.in_dir / s["filename"]
dst = args.out / s["filename"]
if not src.exists():
print(f"ERROR: source missing: {src}")
return 1
if args.use_symlinks:
if dst.exists() or dst.is_symlink():
dst.unlink()
dst.symlink_to(src.resolve())
else:
shutil.copy2(src, dst)
# Metadata schema: same as parent for compat with run_attack_sweep loader
out_meta = {
"description": (
f"Stratified subset of in1k_hybrid_1k (N={len(chosen)}). "
f"Prioritizes Guillaumin∩IN-1k synsets (has_mask=True) for "
f"foreground analysis coverage. Same schema as parent dataset."
),
"version": "1.0",
"seed": args.seed,
"parent": str(args.in_dir.relative_to(PROJECT_ROOT)),
"total": len(chosen),
"n_with_mask": n_chosen_mask,
"n_without_mask": len(chosen) - n_chosen_mask,
"downloaded_files": [s["filename"] for s in chosen],
"suggested_classes": [
{
"synset": s["synset"],
"imagenet_id": s["imagenet_id"],
"in_imagenet_1k": True,
"source": s["source"],
"has_mask": s["has_mask"],
}
for s in chosen
],
"samples": chosen,
}
(args.out / "metadata.json").write_text(json.dumps(out_meta, indent=2))
print(f"\n✓ Subset OK")
print(f" {len(chosen)} imgs em {args.out} ({n_chosen_mask} com mask)")
return 0
if __name__ == "__main__":
sys.exit(main())
|