SpiceNet / code /make_source_specific_manifests.py
Noushad999's picture
Upload folder using huggingface_hub
1ea7ba6 verified
Raw
History Blame Contribute Delete
4.61 kB
"""
Build source-specific manifests from the unified benchmark.
Outputs three new manifests:
outputs/manifest_ss_only.json — SpiceSpectrum samples only (11 classes)
outputs/manifest_indian_only.json — Mendeley Indian samples only (19 classes)
outputs/manifest_overlap_only.json — only the 8 classes present in both sources
Each preserves the same 70/15/15 split structure as the unified manifest.
Class indices are RENUMBERED contiguously within each manifest (so SS-only has
classes 0..10, Indian-only 0..18, overlap 0..7).
"""
import json
from pathlib import Path
ROOT = Path(__file__).parent
UNIFIED = ROOT / "outputs" / "unified_benchmark.json"
OUT_DIR = ROOT / "outputs"
def _source_of(path: str) -> str:
p = path.replace("\\", "/").lower()
if "/spice_spectrum/" in p:
return "spice_spectrum"
if "/indian_spices/" in p:
return "indian"
return "unknown"
def _filter_manifest(uni, keep_source: str = None, keep_overlap_only: bool = False):
"""Return a new manifest containing only samples matching the filter."""
out = {"version": uni["version"], "seed": uni["seed"],
"splits": uni["splits"], "classes": [], "samples": {"train": [], "val": [], "test": []}}
# Determine which classes to keep
overlap_names = [c["name"] for c in uni["classes"] if len(c["sources"]) > 1]
if keep_overlap_only and keep_source == "spice_spectrum":
keep_class_names = overlap_names
elif keep_overlap_only and keep_source == "indian":
keep_class_names = overlap_names
elif keep_overlap_only:
keep_class_names = overlap_names
elif keep_source == "spice_spectrum":
keep_class_names = [c["name"] for c in uni["classes"]
if any(s.startswith("spice_spectrum:") for s in c["sources"])]
elif keep_source == "indian":
keep_class_names = [c["name"] for c in uni["classes"]
if any(s.startswith("indian:") for s in c["sources"])]
else:
keep_class_names = [c["name"] for c in uni["classes"]]
# Re-number classes
name_to_new_idx = {n: i for i, n in enumerate(sorted(keep_class_names))}
old_idx_to_name = {c["index"]: c["name"] for c in uni["classes"]}
# Filter samples
for split in ("train", "val", "test"):
for path, old_idx in uni["samples"][split]:
name = old_idx_to_name[old_idx]
if name not in name_to_new_idx:
continue
if keep_source is not None and _source_of(path) != keep_source:
continue
new_idx = name_to_new_idx[name]
out["samples"][split].append([path, new_idx])
# Rebuild class list with correct per-split counts after filtering
name_to_counts = {n: {"train": 0, "val": 0, "test": 0} for n in keep_class_names}
for split in ("train", "val", "test"):
for _, idx in out["samples"][split]:
inv = list(name_to_new_idx.keys())[idx]
name_to_counts[inv][split] += 1
for name in sorted(keep_class_names):
c = next(c for c in uni["classes"] if c["name"] == name)
cnt = name_to_counts[name]
total = cnt["train"] + cnt["val"] + cnt["test"]
out["classes"].append({
"name": name, "index": name_to_new_idx[name],
"sources": c["sources"], "total": total,
"train": cnt["train"], "val": cnt["val"], "test": cnt["test"],
})
return out
def main():
with open(UNIFIED) as f:
uni = json.load(f)
cfg = [
("manifest_ss_only.json", {"keep_source": "spice_spectrum"}),
("manifest_indian_only.json", {"keep_source": "indian"}),
("manifest_overlap_only.json", {"keep_overlap_only": True}),
("manifest_overlap_ss.json", {"keep_source": "spice_spectrum", "keep_overlap_only": True}),
("manifest_overlap_indian.json", {"keep_source": "indian", "keep_overlap_only": True}),
]
for fname, kwargs in cfg:
m = _filter_manifest(uni, **kwargs)
out_path = OUT_DIR / fname
with open(out_path, "w") as f:
json.dump(m, f, indent=2)
print(f"{fname}: {len(m['classes'])} classes | "
f"train={len(m['samples']['train'])} "
f"val={len(m['samples']['val'])} "
f"test={len(m['samples']['test'])}")
print(f" classes: {[c['name'] for c in m['classes']]}")
print(f" saved: {out_path}\n")
if __name__ == "__main__":
main()