Download code/scripts/make_goldens.py from changh95/bevformer-p150: direct link, hf CLI and curl.
- Browser
- Download file 11.9 kB
-
https://huggingface.co/changh95/bevformer-p150/resolve/main/code/scripts/make_goldens.py
- Command line
-
hf download hf://changh95/bevformer-p150/code/scripts/make_goldens.py
-
curl -L -o make_goldens.py https://huggingface.co/changh95/bevformer-p150/resolve/main/code/scripts/make_goldens.py
11.9 kB
| #!/usr/bin/env python3 | |
| # SPDX-License-Identifier: Apache-2.0 | |
| """Write the CPU fp32 goldens of the TT port with the bundle's own reference (``tt_bevformer.reference``, port form: | |
| host geometry, rebatched SCA, rotation by gather) and cross-check them against the research goldens. | |
| Large goldens (internal validation data, never shipped) go to ``research/bevformer/goldens/`` (``--out``): | |
| ============================================== ================================================================== | |
| ``pandaset_019/f30.npz``, ``f35.npz``, ``f40`` the shipped sample as a stateful sequence (reset at f30): outputs, | |
| ``bev_embed``, inputs, host geometry; f35 (history + ego motion) | |
| also every per-module tap and the network input ``image`` | |
| ``sample0/f1.npz``, ``f2``, ``f3`` the bevdet_vendor nuScenes key-frame (PLAN.md 2.11 detection | |
| gates on frames 1-2; frame 3 = +2 m / +3 deg synthetic motion); | |
| f1 also the image, backbone.layer4, neck and transformer taps | |
| ``nuscenes_scene-0103/kf01.npz`` mini_val, teacher-forced with the public golden kf00 bev_embed; | |
| image + transformer taps (in-domain, real 4.3 m / -1.0 deg motion) | |
| ============================================== ================================================================== | |
| Taps above 20 M elements (backbone.layer1-3) are stored as float16 (their PCC against float32 changes by < 1e-7); | |
| everything else float32; ``__meta__`` records the dtypes, the generator, the bundle commit, the weights digest and | |
| the PCCs against the research goldens. Small goldens (PandaSet-derived, CC BY 4.0) go to the bundle: | |
| ``staging_samples_pandaset/tests/goldens/pandaset_019_f30-40.json`` (detections, counts, PCCs; git-ignored until the PandaSet sample ships), | |
| ``pandaset_019_f30-40_last_layer.npz`` (last-layer logits / boxes) and the smoke test's stored reference | |
| ``staging_samples_pandaset/samples/pandaset_019_f30.reference.json`` (``to_dict()`` of a fresh-stream request of frame 30). | |
| cd bundles/bevformer-p150 && OMP_NUM_THREADS=4 PYTHONPATH=$PWD/code \\ | |
| /home/ubuntu/experiments/tt-models/tools/research-venv/bin/python code/scripts/make_goldens.py --threads 4 | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import fnmatch | |
| import hashlib | |
| import json | |
| import subprocess | |
| import sys | |
| import time | |
| from pathlib import Path | |
| import numpy as np | |
| HERE = Path(__file__).resolve().parent | |
| CODE = HERE.parent | |
| sys.path.insert(0, str(CODE)) | |
| from tt_bevformer import __version__ # noqa: E402 | |
| from tt_bevformer.host import postprocess as hpost # noqa: E402 | |
| from tt_bevformer.host.inputs import load_frame # noqa: E402 | |
| from tt_bevformer.reference.pipeline import BEVFormerReference # noqa: E402 | |
| from tt_bevformer.tests import paths # noqa: E402 | |
| from tt_bevformer.ttaw.golden import save_goldens # noqa: E402 | |
| from tt_bevformer.ttaw.metrics import pcc # noqa: E402 | |
| FP16_ABOVE = 20_000_000 | |
| OUTS = ("bev_embed", "outputs_classes", "outputs_coords") | |
| GEOMETRY = ("ref_cam", "sca_rows", "sca_counts", "sca_gather", "sca_scale", "shift", "rot_index") | |
| ALL_TAPS = ["backbone.layer1", "backbone.layer2", "backbone.layer3", "backbone.layer4", "neck", "enc.*", "ref_cam", | |
| "encoder.*", "decoder.*", "head.*"] | |
| TRANSFORMER_TAPS = ["backbone.layer4", "neck", "enc.*", "ref_cam", "encoder.layer0.*", "encoder.layer0", | |
| "encoder.layer1", "encoder.layer2", "decoder.*", "head.*"] | |
| def _git_head(path: Path) -> str: | |
| """``<short sha>`` of the bundle, with ``+dirty`` when the working tree differs from it.""" | |
| try: | |
| head = subprocess.run(["git", "-C", str(path), "rev-parse", "--short", "HEAD"], capture_output=True, | |
| text=True, check=True).stdout.strip() | |
| dirty = subprocess.run(["git", "-C", str(path), "status", "--porcelain", "--untracked-files=no"], | |
| capture_output=True, text=True, check=True).stdout.strip() | |
| return head + ("+dirty" if dirty else "") | |
| except (OSError, subprocess.CalledProcessError): | |
| return "unknown" | |
| def _select(taps, patterns): | |
| return {k: v for k, v in taps.items() if any(fnmatch.fnmatch(k, p) for p in patterns)} | |
| def _pack(res, taps_patterns, with_image, meta): | |
| """Golden tensors of one frame + the dtype record.""" | |
| t = {name: getattr(res, name) for name in OUTS} | |
| for k, v in res.inputs.items(): | |
| if k == "image" and not with_image: | |
| continue | |
| if k == "prev_bev": | |
| meta["prev_bev_sha256"] = hashlib.sha256(np.ascontiguousarray(v).tobytes()).hexdigest() | |
| continue | |
| t[f"input.{k}"] = v | |
| for k in GEOMETRY: | |
| t[f"geometry.{k}"] = res.geometry[k] | |
| dtypes = {} | |
| for k, v in _select(res.taps, taps_patterns).items(): | |
| arr = np.asarray(v) | |
| if arr.size > FP16_ABOVE: | |
| arr = arr.astype(np.float16) | |
| t[f"tap.{k}"] = arr | |
| dtypes[k] = str(arr.dtype) | |
| meta["tap_dtypes"] = dtypes | |
| return t | |
| def _compare(res, golden_path: Path, report: dict, key: str) -> None: | |
| if golden_path is None or not Path(golden_path).is_file(): | |
| return | |
| out = {} | |
| with np.load(golden_path) as g: | |
| for name in OUTS: | |
| for gk in (name, f"{key}_{name}"): | |
| if gk in g.files: | |
| out[name] = pcc(getattr(res, name), g[gk]) | |
| for k in g.files: | |
| if k.startswith("tap_") and k[4:] in res.taps: | |
| out["tap." + k[4:]] = pcc(res.taps[k[4:]].reshape(g[k].shape), g[k]) | |
| report[key] = out | |
| print(f" {key}: " + ", ".join(f"{k} {v:.10f}" for k, v in out.items()), flush=True) | |
| def main() -> int: | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--out", type=Path, default=paths.golden_dir()) | |
| ap.add_argument("--threads", type=int, default=4) | |
| ap.add_argument("--only", nargs="*", default=["pandaset", "sample0", "nuscenes"]) | |
| a = ap.parse_args() | |
| import torch | |
| torch.set_num_threads(a.threads) | |
| wpath = paths.weights_file() | |
| if wpath is None: | |
| raise SystemExit("weights not found (BEVFORMER_WEIGHTS_DIR)") | |
| ref = BEVFormerReference(wpath) | |
| base_meta = {"generator": "bundles/bevformer-p150/code/scripts/make_goldens.py", "bundle_version": __version__, | |
| "bundle_commit": _git_head(CODE.parent), "weights": str(wpath), | |
| "weights_sha256": ref.weights.sha256, "reference_form": "port (BEVFormerNet.forward_port)", | |
| "fp16_taps_above_elements": FP16_ABOVE} | |
| report: dict = {} | |
| t_all = time.time() | |
| if "pandaset" in a.only: | |
| pub = paths.public_data() | |
| ref.reset() | |
| small = {"frames": [], "doc": "PandaSet 019 f30-40 (shipped sample, CC BY 4.0 + PandaSet Dataset Terms): " | |
| "CPU fp32 reference (port form), stateful sequence reset at f30; Autoware " | |
| "post-processing (score > 0.2)"} | |
| last = {} | |
| for f in (30, 35, 40): | |
| t0 = time.time() | |
| fr = load_frame(paths.PANDASET_SAMPLES / f"pandaset_019_f{f}.json") | |
| res = ref.step(fr, port=True, taps=(f == 35), workers=4) | |
| meta = dict(base_meta, frame=f, sequence="pandaset_019_f30-40", reset=bool(fr.stream["reset"])) | |
| g = pub / "pandaset" / "goldens" / "ps019_f30-40" / f"f{f}.npz" if pub else None | |
| _compare(res, g, report, f"pandaset_019/f{f}") | |
| meta["pcc_vs_research"] = report.get(f"pandaset_019/f{f}", {}) | |
| save_goldens(a.out / "pandaset_019" / f"f{f}.npz", _pack(res, ALL_TAPS if f == 35 else [], f == 35, meta), | |
| meta, compress=True) | |
| dets = res.detections | |
| small["frames"].append({"frame": f, "use_prev_bev": float(res.inputs["use_prev_bev"][0]), | |
| "n_detections": len(dets), "pcc_vs_research": meta["pcc_vs_research"], | |
| "detections": [{k: (round(v, 5) if isinstance(v, float) else v) | |
| for k, v in d.items()} for d in dets.to_records()]}) | |
| last[f"f{f}_outputs_classes"] = res.outputs_classes[-1, 0] | |
| last[f"f{f}_outputs_coords"] = res.outputs_coords[-1, 0] | |
| if f == 30: | |
| body = hpost.to_output(dets, model="bevformer-p150").to_dict() | |
| (paths.PANDASET_SAMPLES / "pandaset_019_f30.reference.json").write_text(json.dumps(body, indent=1) + "\n") | |
| print(f"pandaset f{f}: {len(dets)} detections, {time.time() - t0:.0f} s", flush=True) | |
| paths.PANDASET_GOLDENS.mkdir(parents=True, exist_ok=True) | |
| (paths.PANDASET_GOLDENS / "pandaset_019_f30-40.json").write_text(json.dumps(small, indent=1) + "\n") | |
| np.savez_compressed(paths.PANDASET_GOLDENS / "pandaset_019_f30-40_last_layer.npz", **last) | |
| if "sample0" in a.only: | |
| g0 = paths.research_file("golden", "sample0_golden.npz") | |
| if g0 is not None: | |
| with np.load(g0) as g: | |
| gs = {k: g[k] for k in g.files} | |
| prev = gs["f1_prev_bev"] | |
| for f in (1, 2, 3): | |
| t0 = time.time() | |
| res = ref.run_frame(gs["image"], prev, gs[f"f{f}_use_prev_bev"], gs[f"f{f}_can_bus"], gs["lidar2img"], | |
| port=True, taps=(f == 1)) | |
| meta = dict(base_meta, frame=f, sequence="sample0 (bevdet_vendor nuScenes key-frame; internal only)") | |
| _compare(res, g0, report, f"f{f}") | |
| report[f"sample0/f{f}"] = report.pop(f"f{f}") | |
| meta["pcc_vs_research"] = report[f"sample0/f{f}"] | |
| meta["n_detections"] = len(res.detections) | |
| save_goldens(a.out / "sample0" / f"f{f}.npz", | |
| _pack(res, TRANSFORMER_TAPS if f == 1 else [], f == 1, meta), meta, compress=True) | |
| prev = res.bev_embed | |
| print(f"sample0 f{f}: {len(res.detections)} detections, {time.time() - t0:.0f} s", flush=True) | |
| if "nuscenes" in a.only and paths.public_data() is not None: | |
| P = paths.import_public_common() | |
| pub = paths.public_data() | |
| t0 = time.time() | |
| x = P.load_frame_inputs(str(pub / "nuscenes" / "inputs" / "scene-0103" / "kf01.npz")) | |
| with np.load(pub / "nuscenes" / "goldens" / "scene-0103" / "kf00.npz") as g: | |
| prev = g["bev_embed"] | |
| res = ref.run_frame(x["image"], prev, x["use_prev_bev"], x["can_bus"], x["lidar2img"], port=True, taps=True) | |
| meta = dict(base_meta, frame="kf01", sequence="nuScenes mini_val scene-0103 (CC BY-NC-SA 4.0; internal only)", | |
| teacher_forced="prev_bev = public-data golden kf00 bev_embed") | |
| _compare(res, pub / "nuscenes" / "goldens" / "scene-0103" / "kf01.npz", report, "nuscenes_scene-0103/kf01") | |
| meta["pcc_vs_research"] = report["nuscenes_scene-0103/kf01"] | |
| save_goldens(a.out / "nuscenes_scene-0103" / "kf01.npz", _pack(res, TRANSFORMER_TAPS, True, meta), meta, | |
| compress=True) | |
| print(f"nuscenes kf01: {len(res.detections)} detections, {time.time() - t0:.0f} s", flush=True) | |
| a.out.mkdir(parents=True, exist_ok=True) | |
| worst = min((v for r in report.values() for v in r.values()), default=float("nan")) | |
| (a.out / "report.json").write_text(json.dumps({"meta": base_meta, "pcc_vs_research": report, | |
| "worst_pcc": worst, "seconds": time.time() - t_all}, indent=1)) | |
| print(f"done in {time.time() - t_all:.0f} s; worst PCC vs research {worst:.10f}") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |