VLAlert / tools /relabel_dad_corpus.py
AsianPlayer's picture
Add VLAlert code
1e05592 verified
Raw
History Blame Contribute Delete
4.42 kB
"""Rewrite DAD per-frame action labels in cot_corpus_v3 manifests per user rule:
DAD positives (event at t=3s of 4s @ 25fps clip):
β†’ all 8 frames of every tick β†’ ALERT
β†’ tick_action = ALERT
DAD negatives (no event):
β†’ all 8 frames β†’ SILENT
β†’ tick_action = SILENT
No OBSERVE state for DAD.
Reads: data/cot_corpus_v3/v4_sft_{train,val,test}_full.jsonl
Writes: data/cot_corpus_v3/v4_sft_{train,val,test}_full_relabeled.jsonl
"""
from __future__ import annotations
import json
import logging
from collections import Counter
from pathlib import Path
ROOT = Path("PROJECT_ROOT")
COT_DIR = ROOT / "data/cot_corpus_v3"
SPLITS = ["v4_sft_train_full", "v4_sft_val_full", "v4_sft_test_full"]
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger("dad_relabel")
def is_dad_positive(rec: dict) -> bool:
"""A DAD record is positive iff its tta_raw indicates a known accident.
DAD positives have tta_raw > 0 in the manifest (they're aligned so the
last frame is near t=3s, the hardcoded event time)."""
tta = rec.get("tick_tta_raw", -1.0)
return rec.get("source") == "dad" and tta is not None and tta >= 0
def relabel_dad(rec: dict) -> tuple[dict, str]:
"""Return (new_record, change_kind) where change_kind ∈ {kept, alert, silent}."""
if rec.get("source") != "dad":
return rec, "kept"
new = dict(rec)
if is_dad_positive(rec):
# All 8 frames β†’ ALERT
new["actions_per_frame"] = ["ALERT"] * 8
new["tick_action"] = "ALERT"
change = "alert"
else:
# Safe / negative β†’ all SILENT
new["actions_per_frame"] = ["SILENT"] * 8
new["tick_action"] = "SILENT"
change = "silent"
return new, change
def process_split(split_tag: str) -> dict:
in_path = COT_DIR / f"{split_tag}.jsonl"
out_path = COT_DIR / f"{split_tag}_relabeled.jsonl"
if not in_path.exists():
logger.warning(f"[skip] {in_path} not found")
return {}
n_total = n_dad = n_alert = n_silent = n_other = 0
before_tick = Counter()
after_tick = Counter()
by_src = Counter()
with in_path.open() as fin, out_path.open("w") as fout:
for ln in fin:
ln = ln.strip()
if not ln: continue
rec = json.loads(ln)
n_total += 1
src = rec.get("source", "?")
by_src[src] += 1
before_tick[(src, rec.get("tick_action", "?"))] += 1
new, kind = relabel_dad(rec)
if src == "dad":
n_dad += 1
if kind == "alert": n_alert += 1
elif kind == "silent": n_silent += 1
else: n_other += 1
after_tick[(new.get("source", "?"), new.get("tick_action", "?"))] += 1
fout.write(json.dumps(new) + "\n")
logger.info(f"[{split_tag}] N={n_total} DAD records={n_dad} "
f"β†’ ALERT={n_alert} β†’ SILENT={n_silent} unchanged={n_other}")
logger.info(f"[{split_tag}] saved β†’ {out_path}")
return {
"split": split_tag,
"in_path": str(in_path),
"out_path": str(out_path),
"n_total": n_total,
"n_dad": n_dad,
"n_dad_positive_to_alert": n_alert,
"n_dad_negative_to_silent": n_silent,
"by_source_before": {f"{k[0]}/{k[1]}": v for k, v in sorted(before_tick.items())
if k[0] == "dad"},
"by_source_after": {f"{k[0]}/{k[1]}": v for k, v in sorted(after_tick.items())
if k[0] == "dad"},
}
def main():
out_summary = []
for tag in SPLITS:
out_summary.append(process_split(tag))
summary_path = COT_DIR / "_relabel_dad_summary.json"
summary_path.write_text(json.dumps(out_summary, indent=2))
logger.info(f"[summary] saved β†’ {summary_path}")
# Verification log
print("\n=== DAD RELABEL SUMMARY ===")
for s in out_summary:
print(f"\n{s['split']}: {s['n_dad']} DAD records β†’ "
f"{s['n_dad_positive_to_alert']} ALERT, {s['n_dad_negative_to_silent']} SILENT")
print(" before:")
for k, v in s["by_source_before"].items():
print(f" {k}: {v}")
print(" after:")
for k, v in s["by_source_after"].items():
print(f" {k}: {v}")
if __name__ == "__main__":
main()