File size: 6,807 Bytes
fe51a63 | 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 | #!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""First-order CPU emulation of the TT numerics of FRNet, to size the fp32 islands of the port before the device runs
(research venv: torch + onnx; CPU only, no device).
cd bundles/frnet-p150 && OMP_NUM_THREADS=4 PYTHONPATH=code \\
/home/ubuntu/experiments/tt-models/tools/research-venv/bin/python code/scripts/precision_study.py \\
--out /home/ubuntu/experiments/tt-models/logs/frnet/m0/precision_study.json
The TT dataflow (``reference/rewrites.py`` ``FRNetPlanTorch``) runs with operand rounding per site
(``Emulation``): ``bf16`` (RNE), ``tf32`` (truncation to 10 mantissa bits: probe P12's model of the device's fp32
matmul, whose measured error is 1.4-1.75x that model's, so ``tf32`` rows are optimistic). Configurations:
- ``bf16_all``: bf16 everywhere (weights and activations): the research simulation's worst case;
- ``plan``: the planned functional numerics -- conv activations bf16 with fp32 weights (PLAN.md 9.3 precision policy),
the point / voxel layers as an fp32 island (device fp32 matmul ~ tf32), bf16 rows out of the ``ttnn.embedding``
gathers (bf16-only tables), fp32 interpolation matmuls (~ tf32);
- ``plan_bf16_points``: ``plan`` with bf16 activations and fp32 weights in the point layers (the fast path);
- ``plan_fp32_gather``: ``plan`` with exact fp32 gathers (what a fp32 row-gather kernel, K3, would buy);
- ``plan_fp32_convs`` / ``plan_exact_points``: ``plan`` with fp32 conv activations, resp. exact fp32 point layers
(e.g. two-term bf16 operands): which island to deepen first when a frame misses a gate.
Metrics per frame against the stored ONNX Runtime ``pred_probs`` (the reference of record): PCC over all points and
argmax agreement on the raw points (the PLAN.md 2.8 gates: >= 0.998 and >= 98.5 %), and the module PCC of the device
modules' outputs against the exact fp32 dataflow (gate >= 0.999). Indicative only: no math-fidelity phases, no tile
layouts, no on-device accumulation order.
"""
from __future__ import annotations
import argparse
import json
import sys
import time
from pathlib import Path
import numpy as np
HERE = Path(__file__).resolve()
sys.path.insert(0, str(HERE.parents[1]))
WS = Path("/home/ubuntu/experiments/tt-models")
CONFIGS = {
"bf16_all": "conv_act=bf16,conv_weight=bf16,point_act=bf16,point_weight=bf16,gather=bf16,upsample=bf16",
"plan": "conv_act=bf16,point_act=tf32,point_weight=tf32,gather=bf16,upsample=tf32",
"plan_bf16_points": "conv_act=bf16,point_act=bf16,gather=bf16,upsample=tf32",
"plan_fp32_gather": "conv_act=bf16,point_act=tf32,point_weight=tf32,upsample=tf32",
"plan_fp32_convs": "point_act=tf32,point_weight=tf32,gather=bf16,upsample=tf32",
"plan_exact_points": "conv_act=bf16,gather=bf16,upsample=tf32",
# M1b device follow-up (PORT_LOG "M1 device results"): the device's float32 conv-weight path is not exact
# (C17 / YOLOX: TF32-like, close to bf16 weights); the point island exact (two-term operands)
"exact_points_tf32_conv_w": "conv_act=bf16,conv_weight=tf32,gather=bf16,upsample=tf32",
"exact_points_bf16_conv_w": "conv_act=bf16,conv_weight=bf16,gather=bf16,upsample=tf32",
}
FRAMES = ["pandaset_019_40.ot128", "pandaset_029_40.ot128", "nuscenes_scene-0103_20.ot128",
"nuscenes_scene-0916_20.ot128", "vls128_frame000_fronthalf.ot128", "pandaset_019_40.qt128",
"vls128_frame000_fronthalf.qt128"]
MODULES = ("encoder.pf3", "encoder.voxel_feats", "backbone.stem", "backbone.point_stem", "backbone.fusion_stem",
"backbone.stage1", "backbone.stage2", "backbone.stage3", "backbone.stage4", "decoder.fuse2",
"decoder.point_fuse2", "head.logits")
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
ap.add_argument("--out", type=Path, required=True)
ap.add_argument("--frames", nargs="*", default=FRAMES)
ap.add_argument("--configs", nargs="*", default=list(CONFIGS)[:4])
ap.add_argument("--threads", type=int, default=4)
ap.add_argument("--weights-dir", default=str(WS / "assets" / "frnet" / "hf_lidar_frnet"))
a = ap.parse_args()
import torch
from tt_frnet.host.preprocess import build_plan
from tt_frnet.reference.config import FRNetConfig
from tt_frnet.reference.rewrites import Emulation, FRNetPlanTorch
from tt_frnet.reference.weights import load_weights
from tt_frnet.tests import _data
from tt_frnet.ttaw.golden import TapRegistry
from tt_frnet.ttaw.metrics import pcc
torch.set_num_threads(a.threads)
weights, configs = {}, {}
result = {"configs": {k: CONFIGS[k] for k in a.configs}, "frames": {}, "note": __doc__.split("\n\n")[2]}
for frame in a.frames:
variant = frame.rpartition(".")[2]
if variant not in weights:
weights[variant] = load_weights(a.weights_dir, variant)
configs[variant] = FRNetConfig.from_model_dir(a.weights_dir, variant)
w = weights[variant]
inputs, ort_probs, _ = _data.load_frame(frame)
plan = build_plan(inputs, configs[variant], pre_norm=(w.pre_norm_scale, w.pre_norm_shift))
n_raw = inputs.num_points_raw
ref_label = ort_probs.argmax(1)
t0 = time.time()
exact = TapRegistry(include=list(MODULES))
FRNetPlanTorch(w)(plan, taps=exact)
rows = {}
for name in a.configs:
taps = TapRegistry(include=list(MODULES))
probs = plan.unsort(FRNetPlanTorch(w, emulation=Emulation.parse(CONFIGS[name]))(plan, taps=taps))
label = probs.argmax(1)
rows[name] = {"pred_probs_pcc": float(pcc(probs, ort_probs)),
"argmax_agreement_raw": float((label[:n_raw] == ref_label[:n_raw]).mean()),
"argmax_agreement_all": float((label == ref_label).mean()),
"modules": {m: float(pcc(taps[m][: plan.n] if taps[m].shape[0] == plan.n_cap else taps[m],
exact[m][: plan.n] if exact[m].shape[0] == plan.n_cap else exact[m]))
for m in MODULES}}
r = rows[name]
worst = min(r["modules"].items(), key=lambda kv: kv[1])
print(f"{frame} {name}: pcc {r['pred_probs_pcc']:.6f} argmax(raw) {r['argmax_agreement_raw']:.4%} "
f"worst module {worst[0]} {worst[1]:.6f}", flush=True)
result["frames"][frame] = {"N": inputs.num_points, "N_raw": n_raw, "M": inputs.num_voxels,
"seconds": round(time.time() - t0, 1), "results": rows}
a.out.parent.mkdir(parents=True, exist_ok=True)
a.out.write_text(json.dumps(result, indent=1) + "\n")
return 0
if __name__ == "__main__":
sys.exit(main())
|