Download code/tt_bevdet/tests/test_reference_cpu.py from changh95/bevdet-p150: direct link, hf CLI and curl.
- Browser
- Download file 10.8 kB
-
https://huggingface.co/changh95/bevdet-p150/resolve/main/code/tt_bevdet/tests/test_reference_cpu.py
- Command line
-
hf download hf://changh95/bevdet-p150/code/tt_bevdet/tests/test_reference_cpu.py
-
curl -L -o test_reference_cpu.py https://huggingface.co/changh95/bevdet-p150/resolve/main/code/tt_bevdet/tests/test_reference_cpu.py
10.8 kB
| # SPDX-License-Identifier: Apache-2.0 | |
| """The CPU fp32 reference (``tt_bevdet.reference``) against ONNX Runtime on the deployed ONNX (PLAN.md 0.3 item 1). | |
| ORT cannot run ``bevdet_one_lt_d.onnx`` as a whole (4 TensorRT plugins), so the oracle is the SPEC's (section 6): | |
| ORT on the two plugin-free sub-graphs cut out of the deployed file (``reference.ort``) + the research plugin | |
| re-implementations and goldens. Gates: | |
| - before any data-dependent selection: PCC >= 0.9999 (depth, feat, every head map, the BEV maps); | |
| - after it (decode threshold, top-500, NMS, node filter): set-based detection agreement with the research goldens. | |
| Runs in the research venv (onnxruntime); skipped where onnxruntime, the weights or the inputs are absent:: | |
| cd bundles/bevdet-p150 && OMP_NUM_THREADS=3 PYTHONPATH=$PWD/code \\ | |
| /home/ubuntu/experiments/tt-models/tools/research-venv/bin/python -m pytest -q -s -p no:cacheprovider \\ | |
| code/tt_bevdet/tests/test_reference_cpu.py | |
| Inputs: the shipped PandaSet sample (always), the research goldens of the nuScenes key-frame ``sample0`` and of the | |
| PandaSet sequence (``research/bevdet``; internal validation data, never shipped). | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import numpy as np | |
| import pytest | |
| ort = pytest.importorskip("onnxruntime") | |
| torch = pytest.importorskip("torch") | |
| pytest.importorskip("onnx") | |
| from tt_bevdet.host import inputs as hin # noqa: E402 | |
| from tt_bevdet.host import postprocess as hp # noqa: E402 | |
| from tt_bevdet.reference import port_form # noqa: E402 | |
| from tt_bevdet.reference.config import HEAD_NAMES, default_config # noqa: E402 | |
| from tt_bevdet.tests import paths # noqa: E402 | |
| from tt_bevdet.ttaw.metrics import match_detections, pcc # noqa: E402 | |
| REF_PCC = 0.9999 # PLAN.md 0.3 item 1 | |
| CFG = default_config() | |
| THREADS = int(os.environ.get("BEVDET_TEST_THREADS", "3")) # workspace rule: <= 4 CPU threads | |
| def onnx_path(): | |
| p = paths.weights_onnx() | |
| if p is None: | |
| pytest.skip("bevdet_one_lt_d.onnx not found (set BEVDET_WEIGHTS_DIR)") | |
| return p | |
| def oracle(onnx_path): | |
| from tt_bevdet.reference.ort import OrtOracle | |
| return OrtOracle(onnx_path, threads=THREADS) | |
| def reference(onnx_path): | |
| from tt_bevdet.reference.pipeline import BEVDetReference | |
| return BEVDetReference(onnx_path, threads=THREADS) | |
| def pandaset_f38(): | |
| kw = hin.load_sample(paths.require_pandaset() / "pandaset_019_f38.json") # staged until PandaSet may ship | |
| return hin.prepare_frame(kw["images"], kw["calibration"], CFG) | |
| def _report(name, ours, ref): | |
| p = pcc(ours, ref) | |
| print(f" {name:12s} pcc={p:.10f} max_abs={float(np.abs(ours - ref).max()):.3e}") | |
| return p | |
| # ---------------------------------------------------------------------------------------------- the oracle | |
| def test_subgraphs_are_faithful_extracts(onnx_path, oracle): | |
| """Every node and initializer of the extracted sub-graphs is the deployed one (no custom op, none changed).""" | |
| import onnx | |
| from onnx import numpy_helper | |
| dep = onnx.load(str(onnx_path)) | |
| nodes = {(n.op_type, tuple(n.input), tuple(n.output)) for n in dep.graph.node} | |
| inits = {t.name: t for t in dep.graph.initializer} | |
| for path, n_expect in ((oracle.img_path, 221), (oracle.bev_path, 63)): # SPEC 6: 221 + 63 nodes | |
| sub = onnx.load(str(path)) | |
| assert len(sub.graph.node) == n_expect | |
| assert all((n.op_type, tuple(n.input), tuple(n.output)) in nodes for n in sub.graph.node) | |
| assert all(n.domain in ("", "ai.onnx") for n in sub.graph.node) | |
| for t in sub.graph.initializer: | |
| assert np.array_equal(numpy_helper.to_array(t), numpy_helper.to_array(inits[t.name])), t.name | |
| with pytest.raises(Exception, match="Preprocess"): | |
| ort.InferenceSession(str(onnx_path), providers=["CPUExecutionProvider"]) # S:410: ORT cannot run it as is | |
| # ---------------------------------------------------------------------------------------------- image encoder | |
| def _encoder_inputs(): | |
| out = [("pandaset_f38", None)] | |
| if paths.research_golden("sample0.npz") is not None: | |
| out.append(("sample0", paths.research_golden("sample0.npz"))) | |
| return out | |
| def test_image_encoder_vs_ort(reference, oracle, pandaset_f38, name, golden): | |
| if golden is None: | |
| crop, calib = pandaset_f38.crop_u8, pandaset_f38.calib | |
| img, cp, depth, feat = reference.encode(crop, calib) | |
| else: | |
| with np.load(golden) as g: | |
| img, cp = g["input"], g["cam_params"] | |
| with torch.no_grad(): | |
| d, f = reference.net.image_encoder(img, cp) | |
| depth, feat = d.numpy(), f.numpy() | |
| o_depth, o_feat = oracle.encode(img, cp) | |
| print(f"\n[{name}] image encoder vs ORT") | |
| assert _report("depth", depth, o_depth) >= REF_PCC | |
| assert _report("feat", feat, o_feat) >= REF_PCC | |
| # the TT port's form: host camera gates (BN1d folded, fp64), 1x3 ASPP, GAP bias, softmax over 128 | |
| gates = port_form.camera_gates(reference.weights, cp) | |
| with torch.no_grad(): | |
| c4, c5 = reference.net.backbone(torch.from_numpy(np.ascontiguousarray(img))) | |
| x = reference.net.neck(c4, c5) | |
| p_depth, p_feat = port_form.forward_depth_net_port(reference.net, x, gates, reference.weights, CFG) | |
| assert _report("depth(port)", p_depth.numpy(), o_depth) >= REF_PCC | |
| assert _report("feat(port)", p_feat.numpy(), o_feat) >= REF_PCC | |
| # ---------------------------------------------------------------------------------------------- BEV decoder | |
| def _decoder_inputs(): | |
| out = [("random", None)] | |
| if paths.research_golden("sample0.npz") is not None: | |
| out.append(("sample0", paths.research_golden("sample0.npz"))) | |
| pub = paths.public_data() | |
| full = pub / "pandaset" / "goldens" / "port2hz__ps019_2hz" / "f40_full.npz" if pub else None | |
| if full is not None and full.is_file(): | |
| out.append(("pandaset_f40_motion", full)) | |
| return out | |
| def test_bev_decoder_vs_ort(reference, oracle, name, golden): | |
| if golden is None: | |
| rng = np.random.default_rng(0) | |
| bev_in = np.abs(rng.normal(size=(1, 720, 128, 128))).astype(np.float32) * 5 | |
| else: | |
| with np.load(golden) as g: | |
| bev_in = g["bev_in"] | |
| heads = {k: v.numpy() for k, v in reference.net.bev_decoder(bev_in).items()} | |
| o_heads = oracle.decode(bev_in) | |
| print(f"\n[{name}] BEV decoder vs ORT") | |
| for k in HEAD_NAMES: | |
| assert _report(k, heads[k], o_heads[k]) >= REF_PCC, k | |
| # ---------------------------------------------------------------------------------------------- end to end | |
| def _match(det, golden_boxes, thr=0.2): | |
| g = [b for b in golden_boxes if b["score"] >= thr] | |
| gc = np.array([[b["x"], b["y"]] for b in g]).reshape(-1, 2) | |
| m = match_detections(det.boxes[:, :2], det.label_ids, det.scores, gc, np.array([b["label"] for b in g]), | |
| np.array([b["score"] for b in g]), max_dist=0.5) | |
| return m, len(g) | |
| def test_end_to_end_pandaset_sequence_vs_research(reference): | |
| """The stateful reference (the API's semantics: fresh stream, identity ego pose, frames 38, 39, 40) against the | |
| research goldens of the same Autoware-mode run (``public_data/pandaset/goldens/autoware__ps019_f38-40``).""" | |
| pub = paths.public_data() | |
| gdir = pub / "pandaset" / "goldens" / "autoware__ps019_f38-40" if pub else None | |
| if gdir is None or not gdir.is_dir(): | |
| pytest.skip("research PandaSet goldens not present") | |
| from tt_bevdet.ttaw.golden import TapRegistry | |
| reference.reset() | |
| pdir = paths.require_pandaset() | |
| for n in (38, 39, 40): | |
| taps = TapRegistry(include=["depth", "feat", "bev.curr", "head.*_0"]) | |
| det = reference(**hin.load_sample(pdir / f"pandaset_019_f{n}.json"), taps=taps) | |
| with np.load(gdir / f"f{n}.npz") as g: | |
| print(f"\n[pandaset f{n}] stateful reference vs research ORT golden") | |
| assert _report("depth", taps["depth"], g["depth"]) >= REF_PCC | |
| assert _report("feat", taps["feat"], g["feat"]) >= REF_PCC | |
| assert _report("curr", taps["bev.curr"], g["curr_bevfeat"]) >= REF_PCC | |
| for k in HEAD_NAMES: | |
| assert _report(k, taps[f"head.{k}"], g[k]) >= REF_PCC, k | |
| m, n_gold = _match(det, json.loads((gdir / f"f{n}_boxes.json").read_text())) | |
| print(f" boxes>=0.2 ours={len(det)} golden={n_gold} recall={m.recall:.4f} precision={m.precision:.4f}") | |
| assert m.recall >= 0.99 and m.precision >= 0.99 # set-based after the selection | |
| if len(m.score_errors): | |
| assert float(np.max(np.abs(m.score_errors))) < 1e-3 | |
| def test_end_to_end_sample0_reset_frame(reference): | |
| """nuScenes key-frame sample0 (reset frame, flag 0) from the raw planes: the BEV stages are bit-exact with the | |
| research golden and the boxes agree with the 52 golden boxes at score >= 0.2 (SPEC 6).""" | |
| golden = paths.research_golden("sample0.npz") | |
| if golden is None: | |
| pytest.skip("research golden sample0.npz not present") | |
| from tt_bevdet.host import calib as hc | |
| from tt_bevdet.host import preprocess as hpre | |
| with np.load(golden) as g: | |
| planes = g["planes_bgr_u8"] | |
| crop = hpre.crop_u8(planes, CFG) | |
| np.testing.assert_array_equal(hpre.normalize(crop, CFG), g["input"]) | |
| cp = g["cam_params"] | |
| K = np.stack([np.array([[c[0], 0, c[2]], [0, c[1], c[3]], [0, 0, 1]], np.float32) for c in cp[0]]) | |
| R = cp[0][:, [15, 16, 17, 19, 20, 21, 23, 24, 25]].reshape(6, 3, 3) | |
| t = cp[0][:, [18, 22, 26]] | |
| calib = hc.calibration_from_arrays(K, R, t) | |
| tab = reference.tables(calib) | |
| np.testing.assert_array_equal(tab.ranks_bev, g["ranks_bev"]) | |
| out = reference.run_frame(crop, calib, g["adj_feats"], g["transforms"], int(g["flag"][0, 0])) | |
| print("\n[sample0] run_frame vs research golden") | |
| assert _report("curr", out.curr, g["curr_bevfeat"]) >= REF_PCC | |
| assert _report("bev_in", out.bev_in, g["bev_in"]) >= REF_PCC | |
| for k in HEAD_NAMES: | |
| assert _report(k, out.heads[k], g[k]) >= REF_PCC, k | |
| golden_boxes = json.loads(golden.with_name("sample0_boxes.json").read_text()) | |
| det = hp.boxes_to_detections(out.boxes, CFG) | |
| m, n_gold = _match(det, golden_boxes) | |
| print(f" boxes>=0.2 ours={len(det)} golden={n_gold} recall={m.recall:.4f} precision={m.precision:.4f}") | |
| assert n_gold == 52 and m.recall >= 0.99 and m.precision >= 0.99 | |