ptv3-p150 / code /scripts /bringup.py
changh95's picture
tt-model push ptv3-p150 (container)
5662e7f verified
Raw History Blame Contribute Delete
8.15 kB
# SPDX-License-Identifier: Apache-2.0
"""Device bring-up of the PTv3 graph on a (cropped) frame: build the graph at a capacity bucket fitted to the frame,
capture the tap variants, replay them on the frame's tables and compare every module tap with the fp32 CPU
reference fed the identical host tables (PCC over the frame's real rows); optionally replay vs eager bit check.
bin/devrun -t 1800 -- python code/scripts/bringup.py --crop 8 [--det] [--no-teacher] [--eager-check]
PLAN.md 4.4: the first run of a new config is a small crop under ``TT_METAL_WATCHER=2``. Writes a JSON report
(``--json``). Device-opening: run it through ``bin/devrun``.
"""
from __future__ import annotations
import argparse
import json
import sys
import time
from pathlib import Path
import numpy as np
HERE = Path(__file__).resolve()
sys.path.insert(0, str(HERE.parents[1]))
def _rows_nchw(a):
a = np.asarray(a, np.float32)
return a[0].transpose(1, 2, 0).reshape(-1, a.shape[1])
def compare(dev_taps, ref_taps, counts, *, det_names=()):
from tt_ptv3.ttaw.metrics import error_stats
out = {}
for name, ref in ref_taps.items():
if name not in dev_taps:
continue
d = np.asarray(dev_taps[name], np.float32)
r = np.asarray(ref, np.float32)
if r.ndim == 4: # det NCHW maps -> rows
r = _rows_nchw(r)
d = d.reshape(-1, d.shape[-1])
n = r.shape[0]
d = d[:n, :r.shape[1]]
out[name] = {k: float(v) for k, v in error_stats(d, r).items()}
return out
def main(argv=None) -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--frame", default="sample")
ap.add_argument("--crop", type=float, default=8.0, help="keep |x|, |y| < crop metres (0: the whole frame)")
ap.add_argument("--det", action="store_true")
ap.add_argument("--no-teacher", action="store_true")
ap.add_argument("--qhead", action="store_true", help="also run the C26 head on the reference's dense maps")
ap.add_argument("--eager-check", action="store_true")
ap.add_argument("--mode", default="alias-first")
ap.add_argument("--margin", type=int, default=0)
ap.add_argument("--json", default=None)
ap.add_argument("--threads", type=int, default=2)
args = ap.parse_args(argv)
from tt_ptv3.host import preprocess as pre
from tt_ptv3.reference.pipeline import Reference, onnx_dir_default, set_threads
from tt_ptv3.tests._data import load_frame
from tt_ptv3.tt import inputs as tin
set_threads(args.threads)
pts = load_frame(args.frame)
if args.crop > 0:
keep = (np.abs(pts[:, 0]) < args.crop) & (np.abs(pts[:, 1]) < args.crop)
pts = pts[keep]
frame = pre.prepare_frame(pts, neighbour_mode=args.mode, capacities=None)
caps = tin.fitted_capacities(frame.counts, args.margin)
print(f"points {len(pts)} counts {frame.counts} caps {caps}", flush=True)
t0 = time.time()
ref = Reference(onnx_dir_default(), det=args.det)
taps = {}
res = ref.run(None, frame=frame, det=args.det, taps=taps)
print(f"reference {time.time() - t0:.1f} s", flush=True)
from tt_ptv3.device import close_device, describe_device, open_device
from tt_ptv3.tt.model import TtPTv3, build_runner, teacher_indices, unpack_seg
dev = open_device(allow_fallback=False)
report = {"frame": args.frame, "crop": args.crop, "counts": frame.counts, "caps": list(caps),
"device": describe_device(dev)}
try:
graph = TtPTv3(dev, ref.weights, det=args.det)
variants = ["taps"] + (["dettaps"] + ([] if args.no_teacher else ["detteacher"]) if args.det else [])
variants += ["qhead"] if (args.det and args.qhead) else []
runner = build_runner(graph, {"b": caps}, variants=variants)
t0 = time.time()
runner.capture()
report["capture_s"] = time.time() - t0
print(f"capture {report['capture_s']:.1f} s", flush=True)
inputs = {f"b.{k}": v for k, v in tin.frame_inputs(frame, caps, det=args.det).items()}
if args.det and not args.no_teacher:
inputs["b.topk"] = teacher_indices(taps["det.topk_indices"], graph.kp)
t0 = time.time()
dev_taps = runner("taps_b", inputs=inputs)
report["replay_taps_s"] = time.time() - t0
seg = unpack_seg(dev_taps["seg"], frame.counts[0], caps[0])
report["seg_label_agreement"] = float((seg["labels"] == res.pred_labels).mean())
p = np.asarray(res.pred_probs, np.float64)
ent = -(np.where(p > 0, p * np.log(np.where(p > 0, p, 1)), 0)).sum(1) / np.log(15)
report["seg_prob_max_abs"] = float(np.abs(seg["prob"] - p.max(1)).max())
report["seg_entropy_max_abs"] = float(np.abs(seg["entropy"] - ent).max())
report["taps"] = compare(dev_taps, taps, frame.counts)
if args.eager_check:
eager = runner.run_eager("taps_b")
eager_np = {k: np.asarray(v) for k, v in eager.items()} if isinstance(eager, dict) else eager
report["replay_vs_eager_seg_equal"] = bool(np.array_equal(np.asarray(dev_taps["seg"]),
np.asarray(eager_np["seg"])))
if args.det:
d = runner("dettaps_b")
report["det_taps"] = compare(d, taps, frame.counts)
from tt_ptv3.ttaw.ops.topk import selection_metrics
idx = np.rint(np.asarray(d["indices"], np.float64).reshape(-1)[:graph.num_proposals]).astype(np.int64)
report["det_topk_shared"] = int(len(set(idx.tolist()) & set(taps["det.topk_indices"].tolist())))
if not args.no_teacher:
t = runner("detteacher_b")
rep = {"det.norm1": t["det.decoder.self_attn"], "det.norm2": t["det.decoder.cross_attn"],
"det.norm3": t["det.decoder.out"]}
det_out = graph.assemble({"heads": t["heads"], "query_heat": t["query_heat"],
"indices": t["indices"]})
for name in ("center", "height", "dim", "rot", "vel", "heatmap"):
rep[f"det.head.{name}"] = det_out[name][0].T
report["det_teacher"] = compare(rep, taps, frame.counts)
report["det_teacher"]["query_heatmap_score"] = compare(
{"q": det_out["query_heatmap_score"][0].T}, {"q": res.det["query_heatmap_score"][0].T},
frame.counts)["q"]
if args.qhead:
qin = {"b.lidar": _rows_nchw(taps["det.shared"]).reshape(1, 1, -1, 128),
"b.topk": teacher_indices(taps["det.topk_indices"], graph.kp)}
h = np.zeros((1, 1, graph.cells, 32), np.float32)
h[0, 0, :, :graph.num_det_classes] = _rows_nchw(taps["det.heat_nms"])
qin["b.heatin"] = h
q = runner("qhead_b", inputs=qin)
rep = {"det.norm1": q["det.decoder.self_attn"], "det.norm2": q["det.decoder.cross_attn"],
"det.norm3": q["det.decoder.out"]}
det_out = graph.assemble({"heads": q["heads"], "query_heat": q["query_heat"], "indices": q["indices"]})
for name in ("center", "height", "dim", "rot", "vel", "heatmap"):
rep[f"det.head.{name}"] = det_out[name][0].T
report["det_qhead_golden_inputs"] = compare(rep, taps, frame.counts)
runner.release()
finally:
close_device(dev)
worst = {k: v["pcc"] for k, v in report["taps"].items()}
for k in sorted(worst, key=lambda k: worst[k])[:8]:
print(f" {k:28s} pcc {worst[k]:.6f}")
print(json.dumps({k: v for k, v in report.items() if k not in ("taps", "det_taps", "det_teacher", "det_qhead_golden_inputs")}, indent=1))
for sec in ("det_taps", "det_teacher", "det_qhead_golden_inputs"):
for k, v in report.get(sec, {}).items():
print(f" {sec} {k:26s} pcc {v['pcc']:.6f} max_abs {v['max_abs']:.4g}")
if args.json:
Path(args.json).write_text(json.dumps(report, indent=1))
return 0
if __name__ == "__main__":
raise SystemExit(main())