echo / code /eval /metrics /run_all_metrics.py
amonshano's picture
Add Echo-Memory codebase used for this run (CC BY 4.0, JD Echo Team) (part 2)
eafbe80 verified
Raw
History Blame Contribute Delete
4.4 kB
#!/usr/bin/env python3
"""
Entry script for all memory eval dimensions. Run after evals_ep0 has produced videos.
Usage:
python run_all_metrics.py --evals_root /path/to/ckpt_dir/evals_ep0 [--dataset /path/to/dataset] [--dims 1 2 3 4 5 6] [--output_dir ...]
Output: JSON (and optional CSV summary) under output_dir or evals_root/metrics/.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
# Ensure package-relative imports work when run as script
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
if _SCRIPT_DIR not in sys.path:
sys.path.insert(0, _SCRIPT_DIR)
from . import long_horizon_consistency
from . import loop_closure
from . import identity_preservation
from . import state_tracking
from . import temporal_coherence
from . import semantic_consistency
DIMENSIONS = {
"1": ("long_horizon_consistency", long_horizon_consistency.run_long_horizon_consistency),
"2": ("loop_closure", loop_closure.run_loop_closure),
"3": ("identity_preservation", identity_preservation.run_identity_preservation),
"4": ("state_tracking", state_tracking.run_state_tracking),
"5": ("temporal_coherence", temporal_coherence.run_temporal_coherence),
"6": ("semantic_consistency", semantic_consistency.run_semantic_consistency),
}
def main():
p = argparse.ArgumentParser(description="Run all memory eval metrics on evals_ep0 output")
p.add_argument("--evals_root", type=str, required=True, help="Path to evals_ep0 root (e.g. ckpt_dir/evals_ep0)")
p.add_argument("--dataset", type=str, default=None, help="Optional dataset base for loop_closure trajectory ref")
p.add_argument("--dims", type=str, nargs="*", default=list(DIMENSIONS.keys()), help="Which dimensions to run (default: all 1-6)")
p.add_argument("--output_dir", type=str, default=None, help="Write results here; default: evals_root/metrics")
p.add_argument("--write_csv", action="store_true", help="Write aggregate CSV summary")
p.add_argument("--use_clip", action="store_true", help="Use CLIP in identity_preservation when available")
args = p.parse_args()
evals_root = os.path.abspath(args.evals_root)
if not os.path.isdir(evals_root):
print(f"[run_all_metrics] evals_root not found: {evals_root}", file=sys.stderr)
sys.exit(1)
output_dir = args.output_dir or os.path.join(evals_root, "metrics")
os.makedirs(output_dir, exist_ok=True)
results = {}
for dim in args.dims:
if dim not in DIMENSIONS:
print(f"[run_all_metrics] Unknown dim {dim}, skip.", file=sys.stderr)
continue
name, fn = DIMENSIONS[dim]
kwargs = {"evals_root": evals_root}
if name == "loop_closure":
kwargs["dataset_base"] = args.dataset
if name == "identity_preservation":
kwargs["use_clip"] = args.use_clip
print(f"[run_all_metrics] Running {name} ...", file=sys.stderr)
try:
out = fn(**kwargs)
results[name] = out
with open(os.path.join(output_dir, f"{name}.json"), "w") as f:
json.dump(out, f, indent=2)
except Exception as e:
print(f"[run_all_metrics] {name} failed: {e}", file=sys.stderr)
results[name] = {"error": str(e)}
summary_path = os.path.join(output_dir, "all_metrics_summary.json")
with open(summary_path, "w") as f:
json.dump(results, f, indent=2)
print(f"[run_all_metrics] Summary written to {summary_path}", file=sys.stderr)
if args.write_csv:
import csv
rows = []
for name, data in results.items():
if "aggregate" not in data or isinstance(data.get("aggregate"), str):
continue
row = {"dimension": name}
for k, v in data["aggregate"].items():
if isinstance(v, (int, float)) and "note" not in k.lower():
row[k] = v
rows.append(row)
if rows:
keys = list(rows[0].keys())
csv_path = os.path.join(output_dir, "aggregate_summary.csv")
with open(csv_path, "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=keys, extrasaction="ignore")
w.writeheader()
w.writerows(rows)
print(f"[run_all_metrics] CSV written to {csv_path}", file=sys.stderr)
if __name__ == "__main__":
main()