Download code/tt_bevdet/tests/test_reference_host.py from changh95/bevdet-p150: direct link, hf CLI and curl.
- Browser
- Download file 6.25 kB
-
https://huggingface.co/changh95/bevdet-p150/resolve/main/code/tt_bevdet/tests/test_reference_host.py
- Command line
-
hf download hf://changh95/bevdet-p150/code/tt_bevdet/tests/test_reference_host.py
-
curl -L -o test_reference_host.py https://huggingface.co/changh95/bevdet-p150/resolve/main/code/tt_bevdet/tests/test_reference_host.py
6.25 kB
| # SPDX-License-Identifier: Apache-2.0 | |
| """The CPU reference's building blocks that need torch but neither onnxruntime nor the full network run: | |
| weights addressed by consuming node + the deployed-graph check, the three plugins against the research numpy | |
| implementations, and the TT port's exact rewrites (``reference.port_form``) against the deployed form. | |
| TT_VISIBLE_DEVICES=none python -m pytest -q code/tt_bevdet/tests/test_reference_host.py | |
| Needs the pinned ONNX (``tests/paths.py``: ``$BEVDET_WEIGHTS_DIR``, the HF cache or the workspace download); the | |
| weight tests skip without it. The end-to-end reference against ONNX Runtime is ``test_reference_cpu.py``. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| import pytest | |
| torch = pytest.importorskip("torch") | |
| from tt_bevdet.reference import plugins, port_form # noqa: E402 | |
| from tt_bevdet.reference.config import HEAD_NAMES, default_config # noqa: E402 | |
| from tt_bevdet.reference.weights import ONNX_SHA256, BEVDetWeights, module_names # noqa: E402 | |
| from tt_bevdet.tests import paths # noqa: E402 | |
| from tt_bevdet.ttaw.metrics import pcc # noqa: E402 | |
| CFG = default_config() | |
| DN = "img_view_transformer.depth_net." | |
| def weights(): | |
| path = paths.weights_onnx() | |
| if path is None: | |
| pytest.skip("bevdet_one_lt_d.onnx not found (set BEVDET_WEIGHTS_DIR)") | |
| pytest.importorskip("onnx") | |
| return BEVDetWeights(path) | |
| def net(weights): | |
| from tt_bevdet.reference.model import BEVDetNet | |
| torch.set_num_threads(2) | |
| return BEVDetNet(weights, CFG) | |
| def test_weights_by_consuming_node(weights): | |
| assert weights.sha256 == ONNX_SHA256 | |
| names = module_names() | |
| n_conv = sum(len(v) for k, v in names.items() if k != "depth_net_linear") | |
| assert n_conv == 107 == len(weights.onnx.nodes("Conv")) # SPEC 4.2: Conv 107 | |
| c = weights.conv("img_backbone.conv1") | |
| assert c.node == "Conv_1" and c.weight.shape == (64, 3, 7, 7) and c.stride == (2, 2) and c.padding == (3, 3) | |
| assert [weights.conv(DN + f"depth_conv.3.aspp{i}.atrous_conv").dilation for i in (2, 3, 4)] == \ | |
| [(6, 6), (12, 12), (18, 18)] | |
| assert weights.conv("img_bev_encoder_backbone.layers.0.0.downsample").stride == (2, 2) | |
| w, b = weights.linear(DN + "context_mlp.fc1") | |
| assert w.shape == (512, 27) and b.shape == (512,) | |
| bn = weights.depth_bn() | |
| assert bn.channels == 27 and bn.eps == pytest.approx(1e-5) | |
| n_params = sum(c.weight.size + (0 if c.bias is None else c.bias.size) for _, c in weights.iter_convs()) | |
| n_params += sum(a.size for m in names["depth_net_linear"] for a in weights.linear(m)) + 4 * 27 | |
| assert n_params == 76_187_718 # SPEC 4.3 | |
| def test_align_gather_bevpool_vs_research(): | |
| bc = paths.import_research() | |
| if bc is None: | |
| pytest.skip("research reference not present") | |
| rng = np.random.default_rng(2) | |
| adj = rng.normal(size=(1, 8, 4, 20, 24)).astype(np.float32) | |
| trs = np.zeros((1, 8, 6), np.float32) | |
| for n in range(8): | |
| a = rng.uniform(-0.3, 0.3) | |
| trs[0, n] = [np.cos(a), -np.sin(a), rng.uniform(-6, 6), np.sin(a), np.cos(a), rng.uniform(-6, 6)] | |
| trs[0, 3] = [1, 0, 0, 0, 1, 0] | |
| got = plugins.align_bev(adj, trs).numpy() | |
| np.testing.assert_array_equal(got, bc.align_bev(adj, trs)) # bit for bit | |
| np.testing.assert_array_equal(got[0, 3], adj[0, 3]) # identity = copy | |
| curr = rng.normal(size=(1, 4, 20, 24)).astype(np.float32) | |
| for flag in (0, 1): | |
| np.testing.assert_array_equal(plugins.gather_bev(got, curr, flag).numpy(), bc.gather_bev(got, curr, flag)) | |
| def test_camera_gates_host_fold_vs_graph(weights, net): | |
| rng = np.random.default_rng(4) | |
| cp = np.zeros((1, 6, 27), np.float32) | |
| cp[0, :, :4] = rng.uniform([1000, 1000, 780, 430], [1300, 1300, 830, 470], size=(6, 4)) | |
| cp[0, :, 4:15] = [0.44, 0, 0, 0, 0.44, -140, 1, 0, 0, 1, 1] | |
| cp[0, :, 15:] = rng.normal(size=(6, 12)) | |
| g_ctx, g_depth = port_form.camera_gates(weights, cp) | |
| t_ctx, t_depth = net.camera_gates(torch.from_numpy(cp)) | |
| assert np.abs(g_ctx - t_ctx.numpy()).max() < 1e-5 and np.abs(g_depth - t_depth.numpy()).max() < 1e-5 | |
| w, b = weights.linear(DN + "depth_mlp.fc1") | |
| bn = weights.depth_bn() | |
| wf, bf = port_form.fold_bn_into_linear_input(w, b, bn) | |
| x = cp[0].astype(np.float64) | |
| np.testing.assert_allclose(x @ wf.T + bf, bn.apply(x, axis=1) @ w.astype(np.float64).T + b, rtol=1e-9, atol=1e-9) | |
| def test_depthnet_port_form_matches_deployed(weights, net): | |
| """1x3 ASPP branch 4, GAP branch as a per-frame bias, depth logits padded to 128 with -1e4, host gates.""" | |
| rng = np.random.default_rng(6) | |
| x = torch.from_numpy((rng.normal(size=(2, 512, 16, 44)) * 0.5).astype(np.float32)) | |
| gates = (rng.uniform(0, 1, (2, 512)).astype(np.float32), rng.uniform(0, 1, (2, 512)).astype(np.float32)) | |
| with torch.no_grad(): | |
| d_ref, f_ref = net.depth_net(x, gates=tuple(torch.from_numpy(g) for g in gates)) | |
| d_port, f_port = port_form.forward_depth_net_port(net, x, gates, weights, CFG) | |
| np.testing.assert_array_equal(f_port.numpy(), f_ref.numpy()) | |
| assert d_port.shape == d_ref.shape and np.abs(d_port.numpy() - d_ref.numpy()).max() < 2e-6 | |
| assert pcc(d_port.numpy(), d_ref.numpy()) > 0.99999999 | |
| pad = port_form.pad_depth_logits(weights) | |
| assert pad.weight.shape[0] == 128 and pad.n_valid == 118 and np.all(pad.bias[118:] == -1e4) | |
| assert float(np.exp(np.float32(-1e4) - np.float32(-50.0))) == 0.0 # the pad columns vanish in fp32 | |
| def test_merged_head_matches_deployed(weights, net): | |
| rng = np.random.default_rng(8) | |
| x = torch.from_numpy(rng.normal(size=(1, 256, 32, 32)).astype(np.float32)) | |
| with torch.no_grad(): | |
| ref = net.center_head(x) | |
| got = port_form.forward_center_head_port(net, x, weights) | |
| m = port_form.merge_center_head(weights) | |
| assert m.w1.shape == (384, 64, 3, 3) and m.w2.shape == (20, 384, 3, 3) and [s[0] for s in m.splits] == \ | |
| list(HEAD_NAMES) | |
| for k in HEAD_NAMES: | |
| assert np.abs(got[k].numpy() - ref[k].numpy()).max() < 5e-5, k | |
| assert pcc(got[k].numpy(), ref[k].numpy()) > 0.9999999, k | |