|
Download VERIFICATION_2026-10-09.md from changh95/frnet-p150: direct link, hf CLI and curl.
- Browser
- Download file 14.1 kB
-
https://huggingface.co/changh95/frnet-p150/resolve/fe51a63dce52be0fafbf33bc5263ba101f4730c9/VERIFICATION_2026-10-09.md
- Command line
-
hf download hf://changh95/frnet-p150@fe51a63dce52be0fafbf33bc5263ba101f4730c9/VERIFICATION_2026-10-09.md
-
curl -L -o VERIFICATION_2026-10-09.md https://huggingface.co/changh95/frnet-p150/resolve/fe51a63dce52be0fafbf33bc5263ba101f4730c9/VERIFICATION_2026-10-09.md
14.1 kB
| # frnet-p150: independent verification, 2026-10-09 | |
| ## Verdict | |
| **PASS.** This is the light verification pass of the first publish (`research/PLAN.md` §6.3): the frozen gates | |
| re-run, the baseline numbers re-checked, and every device-open path audited for ETH dispatch. It was made by the agent | |
| that wrote the release docs, which is neither the porter, nor the adversarial verifier of `VERIFY_PORT.md`, nor the | |
| baseline agent, on the release-code commit `04c39b5` (the model code of the baseline `0dd52b2`; the shipped sample | |
| became a synthetic scan per serve profile, the PandaSet sample is staged and not shipped, test hardening; the docs | |
| commit that follows changes `make_demo.py`'s render captions and the docs only). The device runs ran on 2026-10-09 | |
| 15:53-16:10 UTC in one `bin/devrun` window (a first queueing attempt died while waiting for the lock, when the | |
| workspace's `bin/devrun` was edited under it; it had not touched the device). Against the baseline commit `0dd52b2`: | |
| - **Frozen gates:** the device suite (`test_pcc_device.py` + `test_e2e_device.py`, one pytest) passed **14 / 14** | |
| tests. Every existing gate passes with values **bit-identical** to the baseline run (the `_m5` and `_m5docs` reports: | |
| 2,144 leaves compared, 0 differ; timings excluded), including the binding frame: OT128 min agreement 98.907 % | |
| (nuScenes 0916 kf40) against the 98.5 % gate. The two new tests (`test_shipped_sample`, and `test_public_frames` | |
| now requiring all 17 / 5 frames) passed; the four new shipped-sample gates were frozen at this first green run | |
| (label agreement OT128 0.99550, QT128 0.99623; filtered-cloud agreement 0.99772 / 0.99872; threshold 0.985). | |
| - **Baseline re-check:** the device rows of `OPT_BASELINE.md` repeat to within 0.03 ms (OT128 210.02 / 209.95 ms | |
| trace / b2b on the synthetic sample vs 210.00 / 209.95 ms now; QT128 133.53 / 133.43 vs 133.50 / 133.43 ms). Host | |
| rows within the load noise (OT128 `model()` 292.6 -> 289.3 ms p50 on the synthetic sample, 355.7 -> 360.1 ms on | |
| PandaSet, at load average 4.1). | |
| - **Shipped samples:** the shipped `synthetic_<variant>.npz` are byte-identical to the baseline's synthetic bench | |
| inputs (`cmp`), so the baseline's synthetic rows are the shipped sample's. p150 vs the stored CPU reference: | |
| OT128 99.550 % of 52,613 points, QT128 99.623 % of 69,553, the same through `bench.py`, the device test, the | |
| quickstart, `make_demo.py` and the served smoke test. | |
| - **Public data (113 frames per profile, production API):** pooled class agreement OT128 PandaSet 99.66 % / nuScenes | |
| 99.19 %, QT128 99.29 % / 99.44 %; identical scored rows to the CPU reference on all 226 frame-profile pairs, no | |
| profile skip. 3 pairs fall below 98.5 % (not gated; see "Review findings"). Mapped-label mIoU on the p150 equals the | |
| CPU reference's within 0.002. | |
| - **ETH dispatch:** every device-open path opens ETH / 1 CQ / 12×10 by default; every run of this pass printed | |
| `dispatch eth`, `grid 12x10`, `cores 120`, `fallback null`, `eth_patch true` (both serve profiles, API and server). | |
| - **No numerics change** and no new bug: nothing in the model code was changed by this pass. | |
| ## Setup | |
| | item | value | | |
| |---|---| | |
| | Hardware | one Blackhole p150b, ETH dispatch, 1 CQ, 12×10 compute grid (120 cores), AICLK 1350 MHz median (min 1343) during the benches | | |
| | tt-metal | `44d66500520` with `patches/tt-metal-eth-dispatch.patch` (sha256 `08d0ddf6…`, byte-identical to the workspace copy) | | |
| | Weights | `AutowareFoundation/lidar_frnet` @ `a53a1c11b0b28d31fd50a03abef5db0e830a5b3c` (tag `v2.0`): the device suite and the demo run read the sha256-pinned workspace copy; the quickstart, the served runs and the load-time runs resolved the HF cache at the pinned revision offline (downloaded without a token for this pass; every file identical to the workspace copy, ONNX sha256 ot128 `d29bf595…`, qt128 `647af927…`) | | |
| | Verified commit | `04c39b5` (ttaw 0.20.0, `common` `89dec49`, `source_dirty: false`) | | |
| | Baseline commit | `0dd52b2` (`OPT_BASELINE.md`) | | |
| | Workload | the shipped samples `code/tt_frnet/samples/synthetic_{ot128,qt128}.npz` and the staged PandaSet 019 f40 (107,146 points, local), batch 1, warm; the 17 / 5 gate frames; 113 converted public frames per profile (local only) | | |
| | Bench method | `code/scripts/bench.py --iters 100 --warmup 10 --b2b-iters 50 --b2b-rounds 5`, one run per serve profile (the baseline's settings), with each output checked against its stored CPU reference | | |
| | Served method | uvicorn on the host with the serve pins of `tt-model.yaml` (the container's app; `FRNET_VARIANT` per profile), the bundle's `smoke_test.py` against it, then 5 warm-up + 50 requests of the shipped sample from a loopback client, the README's `client.py` + `curl` recipe, a knobs request and a request without calibration | | |
| | Accuracy reference | the fp32 CPU reference (`code/tt_frnet/reference/`, ONNX Runtime on the deployed ONNX; VERIFY_PORT: within 1e-11 of an independent ORT run); for the 113-frame sets the research ORT runs (`research/frnet/public_data/runs`) | | |
| | Host | AMD EPYC VM, 8 vCPUs, shared with other agents' jobs (load average 4.1-6.1 during the runs) | | |
| | Logs | `logs/frnet/m5_docs/` of the workspace (job scripts in `scripts/`) | | |
| ## Measured baseline vs re-check | |
| All times in ms, p50 of 100 iterations (b2b: 5 rounds of 50 replays). Baseline: `OPT_BASELINE.md` (`0dd52b2`); | |
| re-check: `code/scripts/bench.py` with the baseline's settings on `04c39b5` | |
| (`logs/frnet/m5_docs/bench_recheck_{ot128,qt128}.json`). | |
| | row | baseline | re-check | | |
| |---|---:|---:| | |
| | OT128, shipped sample: trace, one replay / back-to-back | 210.02 / 209.948 | 210.00 / 209.946 | | |
| | OT128, PandaSet 019 f40: trace / back-to-back | 211.72 / 211.643 | 211.73 / 211.639 | | |
| | QT128, shipped sample: trace / back-to-back | 133.53 / 133.434 | 133.50 / 133.429 | | |
| | QT128, PandaSet 019 f40: trace / back-to-back | 134.91 / 134.866 | 134.92 / 134.856 | | |
| | OT128 H2D / D2H, shipped sample | 5.42 / 7.65 | 5.42 / 7.56 | | |
| | OT128 host pre-processing, shipped sample / PandaSet | 55.3 / 107.2 | 54.7 / 107.4 | | |
| | OT128 `model()` p50, shipped sample / PandaSet | 292.6 / 355.7 | 289.3 / 360.1 | | |
| | QT128 `model()` p50, shipped sample / PandaSet | 221.1 / 244.3 | 205.4 / 241.5 | | |
| | served `/predict` `timing_ms.total`, OT128 / QT128 (median of 50) | – | 310.4 / 228.9 | | |
| | served client round trip, loopback | – | 328.1 / 247.3 | | |
| | `from_pretrained`, empty JIT cache / warm cache, OT128 | – / 6.5 s | 156.6 / 7.5 s | | |
| | `from_pretrained`, empty JIT cache / warm cache, QT128 | – / 6.9 s | 151.9 / 6.6 s | | |
| The served and cold-load rows are new for the card (the baseline measured neither). The first two calls after a load | |
| took 320.0 / 304.7 ms (OT128) and 241.1 / 214.9 ms (QT128): the trace is captured during the load. The cold JIT cache | |
| holds 682 MB (OT128) / 644 MB (QT128) after the load. | |
| ## Accuracy gate results | |
| `logs/frnet/m5_docs/suite.log`; reports `logs/frnet/{pcc,pcc_small,e2e,latency}_{ot128,qt128}_m5docs.json`. | |
| | check | gate | OT128 | QT128 | | |
| |---|---|---|---| | |
| | every gated module tap (PCC vs the fp32 reference, PandaSet sample, replay outputs) | ≥ 0.999 | min 0.999729 (`point_fusion1`) | min 0.999649 (`attention4`) | | |
| | `pred_probs` PCC / raw-point agreement, PandaSet sample (staged) | ≥ 0.998 / ≥ 98.5 % | 0.999942 / 99.581 % | 0.999844 / 99.295 % | | |
| | public frames (17 / 5): min `pred_probs` PCC | ≥ 0.998 | 0.999688 | 0.999805 | | |
| | public frames: min raw-point agreement | ≥ 98.5 % | **98.907 %** (nuScenes 0916 kf40) | 99.158 % (PandaSet 029 f40) | | |
| | shipped sample vs its stored reference body: labels / filtered-cloud mask (new) | ≥ 98.5 % | 99.550 % / 99.772 % | 99.623 % / 99.872 % | | |
| | replay == eager | bit-exact | 45 / 45 taps | 45 / 45 taps | | |
| | API == `/predict` handler | identical | yes | yes | | |
| Host suite (fake ttnn, gates read-only): **117 passed, 15 skipped** (the 14 device tests and the onnxruntime-only | |
| test), `logs/frnet/m5_docs/host_suite.log`. Research venv (ONNX Runtime + torch): **85 passed, 2 skipped** (by design), | |
| `logs/frnet/m5_docs/research_venv.log`. The counts differ from VERIFY_PORT's (112 / 13 host, 83 / 2 research venv) by the tests the release added or | |
| changed (release hygiene of the shipped samples, the top-level `PYTHON.md` copy, the shipped-sample device test). | |
| ### Public-frame agreement beyond the gates | |
| `code/scripts/make_demo.py run` (the production API, `model(points, calibration=...)`) on every converted public frame | |
| of `research/frnet/public_data/inputs`, both profiles, against the research ORT runs | |
| (`logs/frnet/m5_docs/demo/agreement.json`): | |
| | set | frames | scored raw points | OT128 pooled (worst frame) | QT128 pooled (worst frame) | | |
| |---|---:|---:|---:|---:| | |
| | PandaSet 019 + 029, every 5th frame | 32 | 3,442,737 | 99.66 % (99.35 %) | 99.29 % (98.69 %) | | |
| | nuScenes v1.0-mini mini_val, all key-frames | 81 | 2,067,589 | 99.19 % (98.37 %) | 99.44 % (98.10 %) | | |
| Scored rows equal the reference's on all 226 frame-profile pairs (same crop, same profile decisions); no frame was | |
| skipped. Mapped-label mIoU (`research/frnet/public_data/scripts/frnet_public_eval.py`, not a paper metric; | |
| `logs/frnet/m5_docs/miou/`): OT128 PandaSet coarse 0.7136 (p150) vs 0.7146 (CPU), fine 0.4471 vs 0.4479; nuScenes | |
| coarse 0.4827 vs 0.4831, fine 0.4515 vs 0.4531 (fine = mean over the classes with at least 1,000 labelled points, | |
| `miou_classes_ge_1000_gt_points`; over all labelled classes: PandaSet 0.3229 vs 0.3235, nuScenes 0.3285 vs 0.3292); QT128 PandaSet coarse 0.1869 vs 0.1873, nuScenes 0.3255 vs 0.3266. | |
| ## Disclosed numerics changes | |
| None: the model code, the precision knobs and the kernels are those of the baseline, and every existing gate value is | |
| bit-identical to it. The release changed what ships (samples), the tests (stricter) and the docs. | |
| ## Review findings | |
| - **VERIFY_PORT round 1, low findings** (all resolved or disclosed): | |
| 1. stale PORT_LOG header and latency: fixed (ttaw 0.20.0 @ `89dec49`; the superseded first-cut latency replaced by | |
| the current numbers; the duplicated known-issue paragraphs removed); | |
| 2. `test_public_frames` skipped missing goldens silently: inside the workspace a missing golden, research frame or | |
| staged sample now FAILS (`tests/_data.py need()`), and the test asserts 17 / 5 frames; | |
| 3. narrow precision margin (0.41 pt on nuScenes 0916 kf40): disclosed on the card (Caveats) and in `OPT_REPORT.md` | |
| as the binding constraint and backlog item 14; | |
| 4. end-to-end latency depends on the host load: the card quotes the stage bench with its load conditions and the | |
| device rows separately; | |
| 5. `test_stage_pcc` replays on its capture inputs: unchanged (hardening note); input rewriting is covered by | |
| `test_small_graph`, `test_public_frames` (17 / 5 frames through one trace per profile) and the new `test_shipped_sample`. | |
| - **New, low:** on the wider (ungated) public set, 3 of 226 frame-profile pairs agree below 98.5 %: nuScenes | |
| scene-0916 key-frames 11 (98.374 %) and 1 (98.499 %) on OT128, key-frame 0 (98.105 %) on QT128. They are on the same | |
| out-of-domain scene (32-beam HDL-32E, Singapore) as the binding gated frame, are consistent with its 0.41-pt margin, | |
| and are disclosed on the card. They do not touch a frozen gate. The optimization phase should add them to the | |
| frames it judges precision changes on. | |
| - **Gates:** no threshold was changed; the gate JSON gained only the four shipped-sample entries (0.985, the PLAN.md 2.8 | |
| argmax gate). No test compares TT with TT except replay == eager. | |
| - **Samples and data:** `code/` ships only the two synthetic scans with their presets and references | |
| (`test_only_redistributable_samples_ship`); no PandaSet, nuScenes or rosbag data is tracked; the PandaSet sample | |
| lives in the git-ignored `staging_samples_pandaset/`. nuScenes appears only as two renders labelled non-commercial. | |
| ## p150 ETH-dispatch compliance | |
| Every path that opens the device goes through `ttaw.device.open_device`, whose default is | |
| `DispatchCoreConfig(ETH)` (falling back to WORKER only with a `RuntimeWarning`, or never with `allow_fallback=False`): | |
| | path | how it opens | dispatch | evidence | | |
| |---|---|---|---| | |
| | `FRNet.from_pretrained` (API, quickstart, bench, profile, make_demo) | `ModelBase.device_config` -> `DeviceConfig` (`DEVICE_DEFAULTS` < `FRNET_*` env < args) -> `open_device` | `eth` default; `FRNET_DISPATCH` / `dispatch=` switch | every run printed `dispatch eth, grid 12x10, fallback None` | | |
| | HTTP server (`tt_frnet.server.app`, the container) | the lifespan calls `FRNet.from_pretrained` with the env; `serve.env` pins `FRNET_DISPATCH=eth`, `FRNET_NUM_CQS=1` | `eth` (pinned; `check_bundle.py` refuses another value) | served runs: `/info` `dispatch eth`, `grid 12x10`; `smoke_test.py` fails on anything else | | |
| | device tests (`conftest.py` `device` fixture) | `tt_frnet.device.open_device(allow_fallback=False)` | `eth`, no fallback | suite log | | |
| | `code/scripts/visualizer_report.py` | `open_device(allow_fallback=False)` | `eth` | baseline log | | |
| | `code/scripts/diag_conv_precision.py`, `diag_frame_taps.py` | `tt_frnet.device.device_session()` | `eth` default | code read | | |
| | `tests/test_tt_host.py` | `ttaw.device.open_device(0, dispatch="eth")` on the fake ttnn only | – (no device) | host suite | | |
| The grid is never hard-coded: it comes from `device.compute_with_storage_grid_size()` (`tt/layers.py`, K1 | |
| `segment.py`); the only 12×10 literal is the expected-grid warning in `ttaw/device.py` and the smoke test's assertion. | |
| The image's `verify:` line asserts the patch marker in `tt_metal/impl/dispatch/topology.cpp`. | |
| ## Not covered by this pass | |
| - The container image: it is built and smoke-tested per serve profile (`ot128`, `qt128`) at packaging | |
| (`code/scripts/container_smoke.sh`), after this pass. | |
| - A full adversarial verification of the release (that is `VERIFY_PORT.md` for the port; the next full verification | |
| follows the first optimization round). | |
| - TensorRT parity: the reference is the fp32 ONNX Runtime run of the deployed ONNX, not Autoware's fp16 TensorRT engine. | |