Download code/scripts/precision_emulation.py from changh95/meteor-p150: direct link, hf CLI and curl.
- Browser
- Download file 9.4 kB
-
https://huggingface.co/changh95/meteor-p150/resolve/main/code/scripts/precision_emulation.py
- Command line
-
hf download hf://changh95/meteor-p150/code/scripts/precision_emulation.py
-
curl -L -o precision_emulation.py https://huggingface.co/changh95/meteor-p150/resolve/main/code/scripts/precision_emulation.py
9.4 kB
| # SPDX-License-Identifier: Apache-2.0 | |
| """CPU emulation of the device's activation precision on the class decisions (PORT_LOG.md E20; no device). | |
| cd bundles/meteor-p150 && OMP_NUM_THREADS=4 PYTHONPATH=$PWD/code \\ | |
| /home/ubuntu/experiments/tt-models/tools/research-venv/bin/python code/scripts/precision_emulation.py image bb tt Bt | |
| ... precision_emulation.py bev | |
| ... precision_emulation.py det meteor_valday_f040 fff bff fbf ffb bbb nbb bFb nFF | |
| ... precision_emulation.py absent ff ft fT TT tt bb | |
| ``image``: the CPU reference's image branch with every conv / resize output rounded to bf16 (``b``), TF32 (``t``: 10 | |
| explicit mantissa bits, what the device's fp32 conv path computes: its unpacker rounds fp32 activations to TF32) or | |
| kept fp32 (``f``), per (encoder, heads) pair; ``B`` = the stem output alone in bf16 (``ttnn.max_pool2d`` is bf16 | |
| only). Prints the depth / seg2d argmax agreement with the fp32 reference (PLAN 2.13 gate: >= 0.99) and the depth | |
| top-2 logit margins. ``bev``: the lane decoder + seg refiner from the golden raw BEV with bf16 activations (the convs | |
| whose outputs the device keeps fp32 stay fp32) -> lane agreement. ``det FRAME RDB ...`` (PORT_LOG.md E22): the det | |
| stem + merged heads (``D``) and the box refiner (``B``) from the golden raw BEV (``R``) of FRAME, each letter one of | |
| ``f`` (fp32), ``b`` (bf16 conv / resize outputs), ``t`` (TF32), ``F`` (fp32 convs = three bf16 terms, TF32 resizes: | |
| the device's terms3 path), and for ``R`` also ``n`` (bf16 after 0.3 % relative noise: the chained device raw) -> the | |
| strict det3d recall / precision of the decoded boxes vs the golden outputs (``tests/box_agreement.py``) and the hm | |
| error. ``absent CR ...`` (PORT_LOG.md E23 / I7): the image branch on an absent camera (the all-zero normalised input | |
| of ``input_norm=imagenet``) with conv outputs ``C`` and resize outputs ``R`` rounded as above, plus ``T`` (TF32 by | |
| TRUNCATION) -> seg2d / depth agreement with the variant golden's absent camera and with the device's argmax maps | |
| (``logs/meteor/variants_device_absent_argmax.npz``, written by ``tests/test_variants_device.py``). | |
| Goldens: ``research/meteor/goldens``. | |
| Measured (PandaSet 019 f40): image bb 0.9819 / 0.9908, bt 0.9856 / 0.9948, tb 0.9913 / 0.9917, tt 0.9981 / 0.9988, | |
| Bt 0.9874 / 0.9951; bev lane 0.99966 (bf16) / 0.99996 (fp32). det (valday #40, strict recall / precision): fff | |
| 1.0 / 1.0, bff 1.0 / 1.0, fbf 0.971 / 1.0, ffb 1.0 / 1.0, bbb 0.914 / 1.0, nbb 0.914 / 1.0, bFb 1.0 / 1.0, nFF | |
| 1.0 / 1.0 (nuScenes nbb 0.821 / 0.885, nFF 1.0 / 1.0): the bf16 det stem is the loss. absent (seg2d / depth vs the | |
| reference; vs the device): ff 1.0 / 1.0; fT 0.9975 / 0.9993; tt 0.9966 / 0.9981; TT 0.9856 / 0.9901 (0.9927 / 0.9933 | |
| vs the device); bb 0.9724 / 0.9800; the device 0.9832 / 0.9913 (camera 6): it behaves like TF32-truncated conv outputs. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| from pathlib import Path | |
| import numpy as np | |
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) | |
| GOLD = Path(os.environ.get("METEOR_GOLDENS", "/home/ubuntu/experiments/tt-models/research/meteor/goldens")) | |
| FP32_OUT_BEV = {"dec/out/out.3", "lane_branch/lane_branch.6", "refiner/seg/out/out.3"} | |
| def rnd(t, m): | |
| import torch | |
| if m == "f": | |
| return t | |
| if m == "T": # TF32 by truncation | |
| a = t.contiguous().numpy().view(np.uint32) & np.uint32(0xFFFFE000) | |
| return torch.from_numpy(a.view(np.float32).copy()) | |
| if m == "b": | |
| return t.to(torch.bfloat16).to(torch.float32) | |
| a = t.contiguous().numpy().view(np.uint32).astype(np.uint64) | |
| a = ((a + 0x1000) & 0xFFFFE000).astype(np.uint32) # round to 10 explicit mantissa bits (TF32) | |
| return torch.from_numpy(a.view(np.float32)) | |
| def main() -> None: | |
| import torch | |
| from tt_meteor.reference.model import MeteorNet | |
| from tt_meteor.reference.weights import MeteorWeights | |
| torch.set_num_threads(int(os.environ.get("OMP_NUM_THREADS", "4"))) | |
| what = sys.argv[1] if len(sys.argv) > 1 else "image" | |
| net = MeteorNet(MeteorWeights(), check_graph=False) | |
| z = np.load(GOLD / "pandaset_019_f40" / "taps.npz") | |
| cur = {"m": "b", "stem": None} | |
| conv0, resize0 = net.conv, net.resize | |
| def conv(x, module, relu=False): | |
| y = conv0(x, module, relu) | |
| if what == "bev" and module in FP32_OUT_BEV: | |
| return y | |
| if module == "stem/stem.0" and cur["stem"]: | |
| return rnd(y, cur["stem"]) | |
| return rnd(y, cur["m"]) | |
| if what == "det": | |
| return det(net, sys.argv[2], sys.argv[3:]) | |
| if what == "absent": | |
| return absent(net, sys.argv[2:]) | |
| net.conv = conv | |
| net.resize = lambda x, hw: rnd(resize0(x, hw), cur["m"]) | |
| with torch.inference_mode(): | |
| if what == "bev": | |
| raw = rnd(torch.from_numpy(z["bev.raw"].astype(np.float32)), "b") | |
| for m in ("b", "f"): | |
| cur["m"] = m | |
| ll = net.seg_refiner(net.lane_decoder(raw)) | |
| lane = torch.argmax(ll, dim=1).numpy().astype(np.uint8) | |
| print(f"bev {m}: lane agreement {(lane == z['lane']).mean():.5f}") | |
| return | |
| x = net.normalize(torch.from_numpy(z["input.imgs"])) | |
| for cfg in sys.argv[2:] or ["bb", "tt"]: | |
| enc, head = cfg[0], cfg[1] | |
| cur["stem"] = "b" if enc == "B" else None | |
| cur["m"] = "t" if enc == "B" else enc | |
| f = net.image_encoder(x) | |
| cur["m"], cur["stem"] = head, None | |
| dl, _ = net.depth_head(f) | |
| seg = net.seg2d_head(f) | |
| d = torch.argmax(dl.reshape(1, 8, 64, 108, 192), dim=2).numpy() | |
| s = torch.argmax(torch.clamp(seg, -30, 30).reshape(1, 8, 21, 108, 192), dim=2).numpy() | |
| print(f"encoder {enc} heads {head}: depth {(d == z['depth']).mean():.4f} seg2d {(s == z['seg2d']).mean():.4f}", | |
| flush=True) | |
| srt = np.sort(z["depth.logits"].astype(np.float32), axis=1) | |
| margin = srt[:, -1] - srt[:, -2] | |
| print("depth top-2 margin quantiles (0.05, 0.1, 0.25, 0.5):", | |
| np.round(np.quantile(margin, [0.05, 0.1, 0.25, 0.5]), 4).tolist()) | |
| def det(net, frame: str, configs) -> None: | |
| """``det`` mode (module docstring).""" | |
| import torch | |
| from tt_meteor.host.postprocess import PostConfig | |
| from tt_meteor.tests.box_agreement import box_agreement, decode_3d | |
| g = np.load(GOLD / frame / "taps.npz") | |
| conv0, resize0 = net.conv, net.resize | |
| cur = {"m": "f"} | |
| conv_mode = lambda m: "f" if m == "F" else m # noqa: E731 | |
| resize_mode = lambda m: "t" if m == "F" else m # noqa: E731 | |
| net.conv = lambda x, module, relu=False: rnd(conv0(x, module, relu), conv_mode(cur["m"])) | |
| net.resize = lambda x, hw: rnd(resize0(x, hw), resize_mode(cur["m"])) | |
| cfg = PostConfig() | |
| ref = decode_3d({"hm": g["hm"], "reg": g["reg"]}, cfg) | |
| torch.manual_seed(0) | |
| for c in configs or ["bbb", "bFF"]: | |
| r_m, d_m, b_m = c | |
| with torch.inference_mode(): | |
| raw = torch.from_numpy(g["bev.raw"].astype(np.float32)) | |
| raw = rnd(raw + torch.randn_like(raw) * raw.abs() * 0.003, "b") if r_m == "n" else rnd(raw, r_m) | |
| cur["m"] = d_m | |
| _, hm_pre, reg_pre = net.det_stem(raw) | |
| cur["m"] = b_m | |
| hm, reg = net.box_refiner(hm_pre, reg_pre) | |
| r = box_agreement(decode_3d({"hm": hm.numpy(), "reg": reg.numpy()}, cfg), ref, cfg) | |
| err = np.abs(hm.numpy() - g["hm"]).ravel() | |
| print(f"{frame} raw {r_m} det {d_m} refiner {b_m}: recall {r['recall']:.4f} precision {r['precision']:.4f} " | |
| f"hm max abs {err.max():.4f}", flush=True) | |
| def absent(net, configs) -> None: | |
| """``absent`` mode (module docstring).""" | |
| import torch | |
| g = np.load(GOLD / "pandaset_019_f40_imagenet_linear" / "taps.npz") | |
| cam = int(np.flatnonzero(~np.asarray(g["input.present"], bool))[0]) | |
| ref_s, ref_d = g["seg2d"][0][cam], g["depth"][0][cam] | |
| dev_path = Path(os.environ.get("TT_MODELS_ROOT", "/home/ubuntu/experiments/tt-models")) / "logs" / "meteor" / \ | |
| "variants_device_absent_argmax.npz" | |
| dev = None | |
| if dev_path.is_file(): | |
| with np.load(dev_path) as d: | |
| i = int(np.flatnonzero(d["absent"] == cam)[0]) | |
| dev = {"seg2d": d["seg2d"][i], "depth": d["depth"][i]} | |
| conv0, resize0 = net.conv, net.resize | |
| cur = {"c": "f", "r": "f"} | |
| net.conv = lambda x, module, relu=False: rnd(conv0(x, module, relu), cur["c"]) | |
| net.resize = lambda x, hw: rnd(resize0(x, hw), cur["r"]) | |
| x = torch.zeros(1, 3, *g["input.imgs"].shape[-2:]) # imagenet: absent = zero AFTER normalising | |
| for c in configs or ["ff", "TT"]: | |
| cur["c"], cur["r"] = c[0], c[1] | |
| with torch.inference_mode(): | |
| f = net.image_encoder(x) | |
| dl, _ = net.depth_head(f) | |
| seg = net.seg2d_head(f) | |
| s = torch.argmax(torch.clamp(seg, -30, 30), dim=1).numpy()[0] | |
| d = torch.argmax(dl.reshape(1, -1, *s.shape), dim=1).numpy()[0] | |
| msg = f"absent cam {cam} conv {c[0]} resize {c[1]}: seg2d {(s == ref_s).mean():.4f} depth {(d == ref_d).mean():.4f}" | |
| if dev is not None: | |
| msg += f" | vs device seg2d {(s == dev['seg2d']).mean():.4f} depth {(d == dev['depth']).mean():.4f}" | |
| print(msg, flush=True) | |
| if __name__ == "__main__": | |
| main() | |