Download code/scripts/make_goldens.py from changh95/transfusion-p150: direct link, hf CLI and curl.
- Browser
- Download file 21.6 kB
-
https://huggingface.co/changh95/transfusion-p150/resolve/main/code/scripts/make_goldens.py
- Command line
-
hf download hf://changh95/transfusion-p150/code/scripts/make_goldens.py
-
curl -L -o make_goldens.py https://huggingface.co/changh95/transfusion-p150/resolve/main/code/scripts/make_goldens.py
21.6 kB
| #!/usr/bin/env python3 | |
| # SPDX-License-Identifier: Apache-2.0 | |
| """CPU-reference goldens of transfusion-p150 (research venv: torch + onnxruntime, CPU only, <= 4 threads). | |
| cd bundles/transfusion-p150 && OMP_NUM_THREADS=4 PYTHONPATH=code \\ | |
| /home/ubuntu/experiments/tt-models/tools/research-venv/bin/python -I code/scripts/make_goldens.py \\ | |
| --weights /home/ubuntu/experiments/tt-models/assets/transfusion/hf_lidar_transfusion \\ | |
| --research /home/ubuntu/experiments/tt-models/research/transfusion \\ | |
| --out /home/ubuntu/experiments/tt-models/research/transfusion/goldens | |
| For every frame the fp32 torch reference (``reference/pipeline.py``: host pre-processing, the deployed network rebuilt | |
| from the ONNX initializers, host post-processing) runs once; ONNX Runtime runs the deployed ONNX on the same voxel | |
| tensors twice (plain: the bitwise output golden; with the module intermediates exposed: the agreement check), and the | |
| reference runs once more teacher-forced with ORT's top-k indices. The agreement (PCC per module tap before the | |
| selection, top-500 set overlap and strong-proposal coverage, teacher-forced PCC after it, detection match) is recorded | |
| with the golden. Written: | |
| - ``<out>/<frame>.io.npz``: the ONNX inputs (``voxels``, ``num_points``, ``coors``), the decorated features of the | |
| kept pillars (``features``), the reference outputs (``cls_score0``, ``bbox_pred0``, ``dir_cls_pred0``) and the | |
| plain ORT outputs (``ort_cls_score0``, ...; float32); | |
| - ``<out>/<frame>.detections.json``: the reference output (``Detections3D.to_dict``), per-stage counts, pillar and | |
| sampling statistics, the ORT agreement; | |
| - ``<out>/<frame>.taps.npz`` (``--taps`` frames only; ~100-200 MB each): every tap of ``reference.model.MODULE_TAPS`` | |
| plus ``pfn.layer0`` and ``decoder.key_pos_embed`` (float32; ``head.topk_indices`` int64): the per-module goldens of | |
| the device PCC gates, including the indices that teacher-force the device decoder; | |
| - ``<out>/manifest.json``: sha256 of every file, tool versions, inputs; | |
| - in the bundle (small, shippable: Apache-2.0 / CC BY 4.0 inputs only): ``tests/goldens/voxels.json`` (voxel tensor | |
| hashes of the samples), ``tests/goldens/detections/<sample>.json`` and the smoke reference | |
| ``samples/<stem>.reference.json``. The PandaSet sample is not shipped until the user approves PandaSet samples | |
| (research/DATASETS.md N5): its input is read from, and its small golden and reference are written to, the | |
| git-ignored ``staging_samples_pandaset/`` of the bundle (``goldens/`` there) unless it already sits in ``samples/``. | |
| ``--set selection`` (fix round 1) adds ``<out>/<frame>.selection.npz`` to every development frame above: the | |
| reference's top-500 (``head.topk_indices`` int64, ``head.topk_values``) from a fresh reference run, whose outputs must | |
| equal the stored ``io.npz`` bit for bit (nothing else is rewritten): the proposal identities of the flip-free | |
| proposal-level agreement (``tests/_agreement.py``). | |
| ``--set heldout`` (fix round 1) writes compact goldens of the HELD-OUT frames (``tests/_data.py HELDOUT``: 34 public | |
| frames never used for development or precision choices, converted by the research converter into | |
| ``research/transfusion/heldout/inputs``) to ``<out>/heldout/``: ``<frame>.io.npz`` (reference outputs, top-500 | |
| indices / values, plain ORT outputs) and ``<frame>.detections.json`` (output, counts, proposals and the same ORT | |
| agreement record as the development goldens: pre-selection PCC, top-k sets, teacher-forced PCC, detections), plus | |
| ``heldout/manifest.json``. No module taps are stored. | |
| Frames: the shipped samples (``pandaset_019_f40``, ``test_pcd``), the 16 public frames of | |
| research/transfusion/public_data (PandaSet CC BY 4.0; nuScenes / Argoverse 2 non-commercial: research only) and the | |
| Autoware sample-rosbag frames 50 / 150 / 250 (2 sweeps; frame 150 has 62,527 pillars; license unstated: research | |
| only), plus frame 150 through the ``autoware_compat`` shuffle emulation (offset 12345, the research's). | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import hashlib | |
| import json | |
| import platform | |
| 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_transfusion import __version__ # noqa: E402 | |
| from tt_transfusion import io as tio # noqa: E402 | |
| from tt_transfusion.reference.model import MODULE_TAPS, SELECTION_TAPS # noqa: E402 | |
| from tt_transfusion.reference.ort import OrtTransFusion # noqa: E402 | |
| from tt_transfusion.reference.pipeline import TransFusionReference # noqa: E402 | |
| from tt_transfusion.ttaw import __version__ as ttaw_version # noqa: E402 | |
| from tt_transfusion.ttaw.golden import TapRegistry, save_goldens # noqa: E402 | |
| from tt_transfusion.ttaw.metrics import match_detections, pcc # noqa: E402 | |
| SAMPLES = CODE / "tt_transfusion" / "samples" | |
| BUNDLE_GOLDENS = CODE / "tt_transfusion" / "tests" / "goldens" | |
| PANDASET_STAGING = CODE.parent / "staging_samples_pandaset" # git-ignored, not shipped (DATASETS.md N5) | |
| SHIPPED = ("pandaset_019_f40", "test_pcd") | |
| def sample_home(name: str) -> Path: | |
| """Where a sample lives: ``samples/`` (shipped), or the PandaSet staging copy until it ships.""" | |
| if name.startswith("pandaset") and not (SAMPLES / f"{name}.npz").is_file(): | |
| return PANDASET_STAGING | |
| return SAMPLES | |
| def small_golden_home(name: str) -> Path: | |
| return PANDASET_STAGING / "goldens" if sample_home(name) == PANDASET_STAGING else BUNDLE_GOLDENS / "detections" | |
| PUBLIC = ("ps019_f10", "ps019_f40", "ps019_f70", "ps005_f40", "ps090_f10", "ps090_f40", "ps090_f70", "ps029_f40", | |
| "ns0103_k10", "ns0103_k20", "ns0103_k30", "ns0916_k10", "ns0916_k25", "av2_280269f9_s080", | |
| "av2_51bbdd4d_s080", "av2_e13c06cb_s080") | |
| ROSBAG = ("frame_0050", "frame_0150", "frame_0250") | |
| FIELDS5 = ("x", "y", "z", "intensity", "time_lag") | |
| COMPAT = {"mode": "autoware_compat", "offset": 12345} | |
| TAP_SET = tuple(MODULE_TAPS) + ("pfn.layer0", "decoder.key_pos_embed") | |
| STRONG = 0.1 | |
| def sha(a: np.ndarray) -> str: | |
| return hashlib.sha256(np.ascontiguousarray(a).tobytes()).hexdigest() | |
| def file_sha(p: Path) -> str: | |
| return hashlib.sha256(p.read_bytes()).hexdigest() | |
| def frames(research: Path): | |
| """(name, source, sampling, license, shippable).""" | |
| for name in SHIPPED: | |
| lic = "CC BY 4.0 + PandaSet Dataset Terms" if name.startswith("pandaset") else "Apache-2.0 (autoware_universe)" | |
| yield name, sample_home(name) / f"{name}.npz", None, lic, True | |
| for name in PUBLIC: | |
| path = research / "public_data" / "inputs" / f"{name}.npy" | |
| if not path.is_file(): | |
| print(f"skip {name}: {path} missing") | |
| continue | |
| meta = json.loads(path.with_suffix(".json").read_text()) | |
| lic = meta.get("license") or ("CC BY 4.0 + PandaSet Dataset Terms" if name.startswith("ps") else | |
| "CC BY-NC-SA 4.0 (research only)") | |
| yield f"public_{name}", tio.PointCloud(np.load(path, allow_pickle=False), FIELDS5), None, lic, False | |
| for name in ROSBAG: | |
| path = research / "sample_frames" / f"{name}_sweeps2.npy" | |
| if not path.is_file(): | |
| print(f"skip {name}: {path} missing") | |
| continue | |
| cloud = tio.PointCloud(np.load(path, allow_pickle=False), FIELDS5) | |
| lic = "Autoware sample rosbag (license unstated): research only" | |
| yield f"rosbag_{name[-4:]}", cloud, None, lic, False | |
| if name == "frame_0150": | |
| yield "rosbag_0150_compat", cloud, COMPAT, lic, False | |
| def agreement(rt, ro_tapped, run, run_o, tt: TapRegistry, to: TapRegistry) -> dict: | |
| """Pre-selection PCC per tap, top-k set agreement, teacher-forced PCC after the selection, detection match.""" | |
| pre = {k: float(pcc(tt[k], to[k].reshape(tt[k].shape))) for k in to.names() | |
| if k in tt and k not in SELECTION_TAPS} | |
| mine, ref = tt["head.topk_indices"].ravel(), to["head.topk_indices"].ravel() | |
| strong = ref[to["head.topk_values"].ravel() >= STRONG] | |
| tf = TapRegistry(include=SELECTION_TAPS) | |
| rt.network(run.prepared, tf, topk_indices=to["head.topk_indices"]) | |
| post = {k: float(pcc(tf[k], to[k].reshape(tf[k].shape))) for k in SELECTION_TAPS | |
| if k != "head.topk_indices" and k in to} | |
| a, b = run.output, run_o.output | |
| m = match_detections(a.boxes, a.label_ids, a.scores, b.boxes, b.label_ids, b.scores, max_dist=0.01) | |
| return {"pre_selection_min_pcc": min(pre.values()), "pre_selection": pre, | |
| "topk": {"shared": int(len(set(mine.tolist()) & set(ref.tolist()))), "k": int(ref.size), | |
| "positional_equal": float(np.mean(mine == ref)), "strong": int(len(strong)), | |
| "strong_covered": bool(set(strong.tolist()) <= set(mine.tolist()))}, | |
| "teacher_forced_min_pcc": min(post.values()), "teacher_forced": post, | |
| "detections": {"counts_equal": run.counts == run_o.counts, "ort_counts": run_o.counts, | |
| "recall": m.recall, "precision": m.precision, | |
| "max_score_err": max(m.score_errors, default=0.0)}} | |
| def main() -> int: | |
| ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) | |
| ap.add_argument("--weights", type=Path, required=True) | |
| ap.add_argument("--research", type=Path, required=True) | |
| ap.add_argument("--out", type=Path, required=True) | |
| ap.add_argument("--taps", default="pandaset_019_f40,public_ns0103_k20,rosbag_0150", | |
| help="frames whose module taps are saved") | |
| ap.add_argument("--threads", type=int, default=4) | |
| ap.add_argument("--only", default="", help="comma list of frame names (default: all)") | |
| ap.add_argument("--set", default="dev", choices=("dev", "selection", "heldout"), | |
| help="dev: the development goldens (default); selection: add <frame>.selection.npz to them; " | |
| "heldout: the compact held-out goldens in <out>/heldout") | |
| a = ap.parse_args() | |
| if a.set == "selection": | |
| return selection_main(a) | |
| if a.set == "heldout": | |
| return heldout_main(a) | |
| import onnxruntime | |
| import torch | |
| torch.set_num_threads(a.threads) | |
| taps_for = set(filter(None, a.taps.split(","))) | |
| only = set(filter(None, a.only.split(","))) | |
| a.out.mkdir(parents=True, exist_ok=True) | |
| (BUNDLE_GOLDENS / "detections").mkdir(parents=True, exist_ok=True) | |
| manifest_path = a.out / "manifest.json" | |
| manifest = json.loads(manifest_path.read_text()) if manifest_path.is_file() else {"files": {}} | |
| rt = TransFusionReference(a.weights, backend="torch", threads=a.threads) | |
| ro = TransFusionReference(a.weights, backend="ort", threads=a.threads, intermediates=TAP_SET) | |
| plain = OrtTransFusion(a.weights, threads=a.threads, weights=rt.weights) | |
| manifest.update({"bundle_version": __version__, "ttaw": ttaw_version, "torch": torch.__version__, | |
| "onnxruntime": onnxruntime.__version__, "numpy": np.__version__, | |
| "python": platform.python_version(), "onnx_sha256": rt.weights.sha256, | |
| "weights": "AutowareFoundation/lidar_transfusion@8f07eb93 (v2.1)", | |
| "config": rt.config.describe(), "written": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), | |
| "command": " ".join(sys.argv)}) | |
| voxels = {} | |
| if (BUNDLE_GOLDENS / "voxels.json").is_file(): | |
| voxels = json.loads((BUNDLE_GOLDENS / "voxels.json").read_text()) | |
| for name, src, sampling, lic, shippable in frames(a.research): | |
| if only and name not in only: | |
| continue | |
| t0 = time.perf_counter() | |
| tt, to = TapRegistry(include=TAP_SET), TapRegistry(include=TAP_SET) | |
| run = rt.run(src, sampling=sampling, taps=tt) | |
| run_o = ro.run(src, sampling=sampling, taps=to) | |
| p = run.prepared | |
| n = p.num_pillars | |
| assert np.array_equal(p.features, run_o.prepared.features), name | |
| inputs = p.onnx_inputs() | |
| ort_out = plain.run(inputs) | |
| agree = agreement(rt, ro, run, run_o, tt, to) | |
| t1 = time.perf_counter() | |
| io_path = a.out / f"{name}.io.npz" | |
| save_goldens(io_path, {**inputs, "features": p.features[:n], **run.outputs, | |
| **{f"ort_{k}": v for k, v in ort_out.items()}}, | |
| meta={"frame": name, "num_pillars": n, "license": lic, "shippable": shippable, | |
| "sampling": p.sampling, "points_sha256": sha(p.points)}, compress=True) | |
| files = [io_path] | |
| if name in taps_for: | |
| taps_path = a.out / f"{name}.taps.npz" | |
| tt.save(taps_path, meta={"frame": name, "taps": tt.names(), "sampling": p.sampling}, compress=True) | |
| files.append(taps_path) | |
| body = run.output.to_dict() | |
| rec = {"frame": name, "license": lic, "shippable": shippable, | |
| "reference": "tt_transfusion.reference torch fp32 (ONNX initializers, BN folded)", | |
| "counts": run.counts, "pillars": {k: v for k, v in p.stats().items() if k != "densify"}, | |
| "densify": p.densify, "ort_agreement": agree, | |
| "proposals": [int(v) for v in run.objects.extra["proposal"]], "output": body} | |
| det_path = a.out / f"{name}.detections.json" | |
| det_path.write_text(json.dumps(rec, indent=1)) | |
| files.append(det_path) | |
| for f in files: | |
| manifest["files"][f.name] = {"sha256": file_sha(f), "bytes": f.stat().st_size} | |
| if name in SHIPPED: | |
| small = {k: rec[k] for k in ("frame", "license", "counts", "pillars", "proposals", "output")} | |
| small["ort_agreement"] = {"pre_selection_min_pcc": agree["pre_selection_min_pcc"], | |
| "topk": agree["topk"], "teacher_forced_min_pcc": agree["teacher_forced_min_pcc"], | |
| "detections": agree["detections"]} | |
| small_golden_home(name).mkdir(parents=True, exist_ok=True) | |
| (small_golden_home(name) / f"{name}.json").write_text(json.dumps(small, indent=1)) | |
| (sample_home(name) / f"{name}.reference.json").write_text(json.dumps(body, indent=1)) | |
| voxels[f"{name}.npz"] = {"num_pillars": n, "num_pillars_total": p.pillars.num_pillars_total, | |
| "points_sha256": sha(p.points), "features_sha256": sha(p.features[:n]), | |
| "coords_sha256": sha(p.coords[:n]), | |
| "num_points_sha256": sha(p.pillars.num_points[:n]), | |
| "canvas_index_sha256": sha(p.canvas_index)} | |
| print(f"{name}: pillars {n}/{p.pillars.num_pillars_total} counts {run.counts} | ort pre-sel min pcc " | |
| f"{agree['pre_selection_min_pcc']:.10f}, top-k shared {agree['topk']['shared']}/{agree['topk']['k']} " | |
| f"strong {agree['topk']['strong']} covered {agree['topk']['strong_covered']}, teacher-forced min pcc " | |
| f"{agree['teacher_forced_min_pcc']:.10f}, detections recall/precision " | |
| f"{agree['detections']['recall']:.3f}/{agree['detections']['precision']:.3f} " | |
| f"({t1 - t0:.1f} s + {time.perf_counter() - t1:.1f} s io)", flush=True) | |
| manifest_path.write_text(json.dumps(manifest, indent=1)) | |
| if voxels: | |
| (BUNDLE_GOLDENS / "voxels.json").write_text(json.dumps(voxels, indent=1)) | |
| return 0 | |
| SEL_TAPS = ("head.topk_indices", "head.topk_values") | |
| def _manifest(path: Path, a, rt, extra: dict) -> dict: | |
| import onnxruntime | |
| import torch | |
| m = json.loads(path.read_text()) if path.is_file() else {"files": {}} | |
| m.setdefault("files", {}) | |
| m.update({"bundle_version": __version__, "ttaw": ttaw_version, "torch": torch.__version__, | |
| "onnxruntime": onnxruntime.__version__, "numpy": np.__version__, "python": platform.python_version(), | |
| "onnx_sha256": rt.weights.sha256, "weights": "AutowareFoundation/lidar_transfusion@8f07eb93 (v2.1)", | |
| "config": rt.config.describe(), "written": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), | |
| "command": " ".join(sys.argv), **extra}) | |
| return m | |
| def selection_main(a) -> int: | |
| """``--set selection``: ``<frame>.selection.npz`` (the reference top-500) next to every development golden; the | |
| fresh reference outputs must equal the stored ones bit for bit.""" | |
| import torch | |
| torch.set_num_threads(a.threads) | |
| only = set(filter(None, a.only.split(","))) | |
| rt = TransFusionReference(a.weights, backend="torch", threads=a.threads) | |
| manifest_path = a.out / "manifest.json" | |
| manifest = json.loads(manifest_path.read_text()) | |
| for name, src, sampling, _lic, _ship in frames(a.research): | |
| if only and name not in only: | |
| continue | |
| io_path = a.out / f"{name}.io.npz" | |
| if not io_path.is_file(): | |
| print(f"skip {name}: {io_path} missing") | |
| continue | |
| taps = TapRegistry(include=SEL_TAPS) | |
| run = rt.run(src, sampling=sampling, taps=taps) | |
| with np.load(io_path, allow_pickle=False) as z: | |
| for k, v in run.outputs.items(): | |
| if not np.array_equal(np.asarray(v), z[k]): | |
| raise SystemExit(f"{name}: the reference output {k} differs from the stored golden") | |
| path = a.out / f"{name}.selection.npz" | |
| save_goldens(path, {k: taps[k] for k in SEL_TAPS}, meta={"frame": name, "io_sha256": file_sha(io_path)}, | |
| compress=True) | |
| manifest["files"][path.name] = {"sha256": file_sha(path), "bytes": path.stat().st_size} | |
| print(f"{name}: selection.npz written (strong {int((taps['head.topk_values'] >= STRONG).sum())}), outputs " | |
| f"== stored golden", flush=True) | |
| manifest["selection_written"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) | |
| manifest_path.write_text(json.dumps(manifest, indent=1)) | |
| return 0 | |
| def heldout_main(a) -> int: | |
| """``--set heldout``: compact goldens of the held-out frames (module docstring).""" | |
| import torch | |
| sys.path.insert(0, str(CODE)) | |
| from tt_transfusion.tests._data import HELDOUT | |
| torch.set_num_threads(a.threads) | |
| only = set(filter(None, a.only.split(","))) | |
| out = a.out / "heldout" | |
| out.mkdir(parents=True, exist_ok=True) | |
| rt = TransFusionReference(a.weights, backend="torch", threads=a.threads) | |
| ro = TransFusionReference(a.weights, backend="ort", threads=a.threads, intermediates=TAP_SET) | |
| plain = OrtTransFusion(a.weights, threads=a.threads, weights=rt.weights) | |
| inputs_dir = a.research / "heldout" / "inputs" | |
| manifest_path = out / "manifest.json" | |
| manifest = _manifest(manifest_path, a, rt, {"set": "heldout", "inputs": str(inputs_dir)}) | |
| for fid in HELDOUT: | |
| name = f"heldout_{fid}" | |
| if only and name not in only and fid not in only: | |
| continue | |
| path = inputs_dir / f"{fid}.npy" | |
| if not path.is_file(): | |
| print(f"skip {fid}: {path} missing") | |
| continue | |
| t0 = time.perf_counter() | |
| cloud = tio.PointCloud(np.load(path, allow_pickle=False), FIELDS5) | |
| taps, to = TapRegistry(include=TAP_SET), TapRegistry(include=TAP_SET) | |
| run = rt.run(cloud, taps=taps) | |
| run_o = ro.run(cloud, taps=to) | |
| p = run.prepared | |
| assert np.array_equal(p.features, run_o.prepared.features), name | |
| inputs = p.onnx_inputs() | |
| ort_out = plain.run(inputs) | |
| agree = agreement(rt, ro, run, run_o, taps, to) # the development goldens' ORT agreement record | |
| m = agree["detections"] | |
| io_path = out / f"{name}.io.npz" | |
| save_goldens(io_path, {**run.outputs, **{k: taps[k] for k in SEL_TAPS}, | |
| **{f"ort_{k}": v for k, v in ort_out.items()}}, | |
| meta={"frame": name, "num_pillars": p.num_pillars, "points_sha256": sha(p.points), | |
| "input_sha256": file_sha(path)}, compress=True) | |
| meta = json.loads(path.with_suffix(".json").read_text()) | |
| lic = meta.get("license") or ("CC BY 4.0 + PandaSet Dataset Terms" if fid.startswith("ps") else | |
| "CC BY-NC-SA 4.0 (research only)") | |
| rec = {"frame": name, "license": lic, "shippable": False, "set": "heldout", | |
| "reference": "tt_transfusion.reference torch fp32 (ONNX initializers, BN folded)", | |
| "counts": run.counts, "pillars": {k: v for k, v in p.stats().items() if k != "densify"}, | |
| "ort_agreement": agree, "proposals": [int(v) for v in run.objects.extra["proposal"]], | |
| "output": run.output.to_dict()} | |
| det_path = out / f"{name}.detections.json" | |
| det_path.write_text(json.dumps(rec, indent=1)) | |
| for f in (io_path, det_path): | |
| manifest["files"][f.name] = {"sha256": file_sha(f), "bytes": f.stat().st_size} | |
| manifest_path.write_text(json.dumps(manifest, indent=1)) | |
| print(f"{name}: pillars {p.num_pillars} counts {run.counts} | ort pre-sel min pcc " | |
| f"{agree['pre_selection_min_pcc']:.10f}, top-k shared {agree['topk']['shared']}/{agree['topk']['k']} " | |
| f"strong {agree['topk']['strong']} covered {agree['topk']['strong_covered']}, teacher-forced min pcc " | |
| f"{agree['teacher_forced_min_pcc']:.10f}, detections recall/precision {m['recall']:.3f}/" | |
| f"{m['precision']:.3f} counts equal {m['counts_equal']} ({time.perf_counter() - t0:.1f} s)", flush=True) | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |