File size: 9,308 Bytes
5662e7f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | #!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""Goldens of the port from the fp32 CPU reference (``tt_ptv3.reference``; research venv, CPU only, no device).
OMP_NUM_THREADS=2 PYTHONPATH=code tools/research-venv/bin/python code/scripts/make_goldens.py [--frames ...]
All goldens use the **spconv-faithful** neighbour builder (13 queried taps + symmetric writes; SPEC 4.1, R11): the
earlier research golden ``research/ptv3/ref_outputs/scene10_00795_full.npz`` (26-tap emulation) is superseded.
Large (never shipped) -> ``research/ptv3/goldens/`` (``--out``):
- ``<frame>.<mode>.npz``: the research golden layout (``point_feat_0..4``, ``pred_labels``, ``pred_probs``, the 9
``det_*`` outputs, ``boxes`` [M, 11], ``point_label15``, ``point_prob``, ``stage_counts``) for every frame and
both neighbour modes (``alias-first`` = the port default, PLAN.md D3; ``exact``);
- ``<frame>.alias-first.taps.npz``: per-module taps (``reference/model.py TAP_NAMES``; ``taps.`` prefix) for the
tap frames (the shipped sample, T4 scene_10/00795, nuScenes scene-0103 k20) -- the device PCC gates' references;
- ``INDEX.json``: per golden the input sha256, the host-table digests, counts, flags, box count, file sha256.
Small -> ``<frame>.<mode>.json``: counts, flags, host-table digests, per-class voxel / point histograms and the boxes
(records) -- what the host / e2e tests check without the large files (``tests/_data.small_golden``). Only the shipped
sample's go into the bundle (``code/tt_ptv3/tests/goldens/``); the PandaSet sample's go to the git-ignored
``staging_samples_pandaset/goldens/`` (not shipped until the user approves PandaSet samples, DATASETS.md N5), the T4
and nuScenes frames' to ``research/ptv3/goldens/summaries/`` (never shipped).
Frames: the shipped sample ``test_pcd`` (Autoware's test.pcd, Apache-2.0), the PandaSet sample (PandaSet 019 f40),
the 4 T4 sample-dataset frames (SPEC 7; licence UNVERIFIED, never shipped) and nuScenes scene-0103 k20 (CC BY-NC-SA;
never shipped; below the node's 16k-voxel floor: the reference runs with the floor disabled, ``node_rejects_frame``
recorded). The 16 public frames already have research goldens
from the same reference (research/ptv3/public_data/goldens; ``test_reference_cpu.py`` checks they are reproduced).
"""
from __future__ import annotations
import argparse
import hashlib
import json
import sys
import time
from pathlib import Path
import numpy as np
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parent))
WS = Path("/home/ubuntu/experiments/tt-models")
T4 = WS / "assets" / "ptv3" / "t4_lidar_detection_sample" / "lidar_detection_sample_dataset"
FRAMES = {
"test_pcd": HERE.parent / "tt_ptv3" / "samples" / "test_pcd.npz",
"ps019_f40": WS / "research" / "ptv3" / "public_data" / "ship_sample" / "pandaset_019_f40.pcd5.npy",
"t4_scene10_00795": T4 / "scene_10/data/LIDAR_CONCAT/00795.pcd.bin",
"t4_scene6_00860": T4 / "scene_6/data/LIDAR_CONCAT/00860.pcd.bin",
"t4_scene7_00270": T4 / "scene_7/data/LIDAR_CONCAT/00270.pcd.bin",
"t4_scene7_00720": T4 / "scene_7/data/LIDAR_CONCAT/00720.pcd.bin",
"ns0103_k20": WS / "research" / "ptv3" / "public_data" / "inputs" / "ns0103_k20.pcd5.npy",
}
TAP_FRAMES = ("ps019_f40", "t4_scene10_00795", "ns0103_k20")
# taps kept in the large goldens (the CPE / attention sub-taps and the frame-independent key position embedding are
# recomputed by the tests when needed)
SKIP_TAPS = (".cpe", ".attn", "det.key_pos_embed", "det.bev_refine", "det.stem", "det.heat", "seg.labels")
OLD_T4_GOLDEN = WS / "research" / "ptv3" / "ref_outputs" / "scene10_00795_full.npz"
def load(path: Path) -> np.ndarray:
if path.suffix == ".npy":
return np.load(path)
if path.suffix == ".npz":
from tt_ptv3 import io as tio
return np.asarray(tio.load_points(str(path)).points, np.float32)
return np.fromfile(path, dtype=np.float32).reshape(-1, 5)
def sha256_file(p: Path) -> str:
h = hashlib.sha256()
with open(p, "rb") as f:
for b in iter(lambda: f.read(1 << 20), b""):
h.update(b)
return h.hexdigest()
def tap_wanted(name: str) -> bool:
return not any(s in name if s.startswith(".") else name.startswith(s) for s in SKIP_TAPS)
def hist(labels: np.ndarray, names) -> dict:
lab = np.asarray(labels)
h = np.bincount(lab[lab != 255].astype(np.int64), minlength=len(names))[:len(names)]
out = {names[i]: int(h[i]) for i in range(len(names)) if h[i]}
out["invalid"] = int((lab == 255).sum())
return out
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--weights", default=str(WS / "assets" / "ptv3" / "hf_ptv3"))
ap.add_argument("--out", default=str(WS / "research" / "ptv3" / "goldens"))
ap.add_argument("--frames", default=",".join(FRAMES))
ap.add_argument("--small-out", help="directory of the small goldens (default: per frame, see the docstring)")
ap.add_argument("--modes", default="alias-first,exact")
a = ap.parse_args(argv)
from tt_ptv3.host.preprocess import table_digests
from tt_ptv3.reference.config import DET_CLASSES, SEG_CLASSES
from tt_ptv3.reference.pipeline import Reference, set_threads
from tt_ptv3.ttaw.metrics import pcc
set_threads(2)
out = Path(a.out)
out.mkdir(parents=True, exist_ok=True)
def small_dir(fid: str) -> Path:
if a.small_out:
d = Path(a.small_out)
elif fid == "test_pcd":
d = HERE.parent / "tt_ptv3" / "tests" / "goldens" # shipped (Apache-2.0 sample)
elif fid.startswith("ps"):
d = HERE.parents[1] / "staging_samples_pandaset" / "goldens" # git-ignored until PandaSet may ship
else:
d = out / "summaries" # workspace only (T4, nuScenes)
d.mkdir(parents=True, exist_ok=True)
return d
ref = Reference(a.weights)
index_path = out / "INDEX.json"
index = json.loads(index_path.read_text()) if index_path.is_file() else {}
index["reference"] = ("bundles/ptv3-p150 code/scripts/make_goldens.py (tt_ptv3.reference: fp32 torch, BN folded "
"as the port folds, spconv-faithful neighbour tables; torch 2 threads)")
index.setdefault("goldens", {})
for fid in a.frames.split(","):
pts = load(FRAMES[fid])
for mode in a.modes.split(","):
t0 = time.time()
taps: dict = {} if (fid in TAP_FRAMES and mode == "alias-first") else None
res = ref.run(pts, neighbour_mode=mode, taps=taps, tap_filter=tap_wanted)
g = res.golden()
stem = f"{fid}.{mode}"
np.savez_compressed(out / f"{stem}.npz", **g)
if taps is not None:
np.savez_compressed(out / f"{stem}.taps.npz", **{f"taps.{k}": v for k, v in taps.items()})
fr = res.frame
summary = {
"frame": fid, "neighbour_mode": mode,
"input": str(FRAMES[fid].relative_to(WS)) if FRAMES[fid].is_relative_to(WS) else FRAMES[fid].name,
"input_sha256": sha256_file(FRAMES[fid]), **fr.describe(),
"node_rejects_frame": fr.rejected,
"seg_voxel_hist": hist(res.pred_labels, SEG_CLASSES), "seg_point_hist": hist(res.seg.labels, SEG_CLASSES),
"boxes": [dict(r, class_name=DET_CLASSES[r["label"]]) for r in res.boxes.to_records()],
"host_table_sha256": table_digests(fr),
"timing_s": {k: round(v, 2) for k, v in res.timing_s.items()},
}
if fid == "t4_scene10_00795" and mode == "exact" and OLD_T4_GOLDEN.is_file():
with np.load(OLD_T4_GOLDEN) as z: # R11: the faithful tables vs the old 26-tap golden
summary["vs_26tap_golden"] = {
"point_feat_pcc": [round(pcc(res.point_feat[i], z[f"enc_out_point_feat_{i}"]), 6) for i in range(5)],
"seg_label_agreement": round(float(np.mean(res.pred_labels == z["seg_out_pred_labels"])), 6),
"spec_expected": {"point_feat_pcc_1_4": [0.99970, 0.99831, 0.99713, 0.99403], "labels": 0.9921}}
(small_dir(fid) / f"{stem}.json").write_text(json.dumps(summary, indent=1))
index["goldens"][stem] = {k: summary[k] for k in ("frame", "neighbour_mode", "input", "input_sha256",
"stage_counts", "node_rejects_frame")}
index["goldens"][stem].update(boxes=len(summary["boxes"]), golden=f"{stem}.npz",
golden_sha256=sha256_file(out / f"{stem}.npz"),
taps=f"{stem}.taps.npz" if taps is not None else None)
index_path.write_text(json.dumps(index, indent=1))
print(f"{stem}: counts {fr.counts} rejects={fr.rejected} boxes {len(res.boxes)} "
f"taps {len(taps) if taps is not None else 0} {time.time() - t0:.1f}s"
+ (f" vs26tap {summary['vs_26tap_golden']}" if "vs_26tap_golden" in summary else ""), flush=True)
return 0
if __name__ == "__main__":
sys.exit(main())
|