tan-cli 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tan/__init__.py +0 -0
- tan/__main__.py +120 -0
- tan/cli.py +939 -0
- tan/command_table.py +265 -0
- tan/commands/__init__.py +8 -0
- tan/commands/bootstrap_cmd.py +4856 -0
- tan/commands/bootstrap_patches.py +164 -0
- tan/commands/bootstrap_toolchain_fetch.py +155 -0
- tan/commands/bootstrap_toolchain_pin.py +36 -0
- tan/commands/build/__init__.py +1 -0
- tan/commands/build/configure_inputs.py +277 -0
- tan/commands/build/execute.py +1852 -0
- tan/commands/build/host_python.py +59 -0
- tan/commands/build/link_stale.py +78 -0
- tan/commands/build/manifest.py +428 -0
- tan/commands/build/materialise.py +81 -0
- tan/commands/build/token_substitution.py +381 -0
- tan/commands/build/toolchain.py +482 -0
- tan/commands/build/user_defines_wipe.py +115 -0
- tan/commands/build/workspace_patches.py +65 -0
- tan/commands/build_cmd.py +2236 -0
- tan/commands/build_output.py +331 -0
- tan/commands/clean_cmd.py +1183 -0
- tan/commands/completion_cmd.py +786 -0
- tan/commands/debug_config_cmd.py +2425 -0
- tan/commands/diff_cmd.py +940 -0
- tan/commands/doctor_cmd.py +4767 -0
- tan/commands/examples_cmd.py +644 -0
- tan/commands/explain_cmd.py +1319 -0
- tan/commands/faultdecode_cmd.py +931 -0
- tan/commands/flash_atoc_guard.py +240 -0
- tan/commands/flash_cmd.py +5731 -0
- tan/commands/flash_mram_guard.py +150 -0
- tan/commands/flash_ram.py +578 -0
- tan/commands/flash_raw.py +583 -0
- tan/commands/generate_cmd.py +1456 -0
- tan/commands/image_cmd.py +753 -0
- tan/commands/init_cmd.py +1740 -0
- tan/commands/inspect_cmd.py +377 -0
- tan/commands/kconfig_cmd.py +734 -0
- tan/commands/model_cmd.py +1514 -0
- tan/commands/model_device_cmd.py +351 -0
- tan/commands/model_device_live.py +234 -0
- tan/commands/model_host_cmd.py +430 -0
- tan/commands/model_zoo_cmd.py +363 -0
- tan/commands/monitor_actions.py +125 -0
- tan/commands/monitor_cmd.py +626 -0
- tan/commands/monitor_session.py +505 -0
- tan/commands/new_som_cmd.py +1522 -0
- tan/commands/pinmux_cmd.py +568 -0
- tan/commands/presets_cmd.py +1123 -0
- tan/commands/probe_cmd.py +421 -0
- tan/commands/reset_cmd.py +354 -0
- tan/commands/reset_confirm.py +168 -0
- tan/commands/run_cmd.py +637 -0
- tan/commands/scaffold_cmd.py +490 -0
- tan/commands/sdk_cmd.py +1723 -0
- tan/commands/size_cmd.py +1020 -0
- tan/commands/support_bundle_cmd.py +1073 -0
- tan/commands/trace_cmd.py +341 -0
- tan/commands/validate_cmd.py +2009 -0
- tan/commands/west_forward_cmd.py +736 -0
- tan/commands/workspace_patch_check.py +296 -0
- tan/core/__init__.py +1 -0
- tan/core/alp_conf_pregen.py +100 -0
- tan/core/artifact_provenance.py +240 -0
- tan/core/atoc_guard.py +276 -0
- tan/core/atoc_guard_messages.py +232 -0
- tan/core/atoc_replacement.py +118 -0
- tan/core/atomic_write.py +178 -0
- tan/core/board_context.py +351 -0
- tan/core/board_diagnostic.py +111 -0
- tan/core/board_files.py +221 -0
- tan/core/board_validator.py +542 -0
- tan/core/board_validator_compat.py +196 -0
- tan/core/board_validator_run.py +129 -0
- tan/core/board_validator_schema.py +186 -0
- tan/core/board_validator_skew.py +46 -0
- tan/core/board_yaml_edit.py +295 -0
- tan/core/board_yaml_pos.py +118 -0
- tan/core/bootstrap.py +2572 -0
- tan/core/build_plan.py +625 -0
- tan/core/consent.py +104 -0
- tan/core/console_filter.py +245 -0
- tan/core/debug_launch.py +1803 -0
- tan/core/devicetree_lint.py +168 -0
- tan/core/dir_removal.py +229 -0
- tan/core/doctor_git.py +172 -0
- tan/core/doctor_libraries.py +253 -0
- tan/core/doctor_render.py +185 -0
- tan/core/doctor_scope.py +66 -0
- tan/core/doctor_setools.py +237 -0
- tan/core/doctor_stale.py +180 -0
- tan/core/document_guards.py +723 -0
- tan/core/dp_id.py +97 -0
- tan/core/elf_load.py +63 -0
- tan/core/error_catalog.py +171 -0
- tan/core/example_catalog.py +499 -0
- tan/core/example_facets.py +151 -0
- tan/core/faultdecode.py +521 -0
- tan/core/flash_plan.py +2501 -0
- tan/core/flow_d_report.py +381 -0
- tan/core/flow_d_uptime.py +164 -0
- tan/core/fs_confine.py +130 -0
- tan/core/global_flags.py +230 -0
- tan/core/host_python.py +210 -0
- tan/core/image_bundle.py +247 -0
- tan/core/inert.py +147 -0
- tan/core/jlink_binary.py +128 -0
- tan/core/jlink_probe.py +481 -0
- tan/core/jsonc_splice.py +404 -0
- tan/core/launch_provenance.py +257 -0
- tan/core/link_refusal.py +76 -0
- tan/core/metadata_schema.py +376 -0
- tan/core/model_check.py +227 -0
- tan/core/model_device.py +449 -0
- tan/core/model_doctor.py +198 -0
- tan/core/model_host.py +48 -0
- tan/core/model_list.py +112 -0
- tan/core/model_zoo.py +353 -0
- tan/core/module_template.py +340 -0
- tan/core/mram_link.py +87 -0
- tan/core/new_som_ospi.py +94 -0
- tan/core/os_class.py +159 -0
- tan/core/patch_paths.py +87 -0
- tan/core/pending.py +40 -0
- tan/core/plain_zephyr_plan.py +138 -0
- tan/core/plan_exec.py +373 -0
- tan/core/plan_tokens.py +600 -0
- tan/core/probe.py +155 -0
- tan/core/probe_plan.py +237 -0
- tan/core/proxy.py +168 -0
- tan/core/publish.py +31 -0
- tan/core/python_floor.py +178 -0
- tan/core/ram_run.py +694 -0
- tan/core/ram_watch.py +231 -0
- tan/core/raw_write.py +160 -0
- tan/core/reset_plan.py +56 -0
- tan/core/run.py +161 -0
- tan/core/scaffold.py +2203 -0
- tan/core/scaffold_selftest_identity.py +225 -0
- tan/core/sdk_default_registry.py +565 -0
- tan/core/sdk_discovery.py +996 -0
- tan/core/sdk_removal.py +465 -0
- tan/core/serial_actions.py +175 -0
- tan/core/serial_capture.py +185 -0
- tan/core/serial_url.py +45 -0
- tan/core/setools.py +608 -0
- tan/core/setools_scratch.py +243 -0
- tan/core/shapes.py +194 -0
- tan/core/size.py +610 -0
- tan/core/som_buildability.py +204 -0
- tan/core/som_schema_version.py +62 -0
- tan/core/subprocess_env.py +201 -0
- tan/core/sysbuild_image.py +94 -0
- tan/core/system_manifest.py +827 -0
- tan/core/text_layout.py +73 -0
- tan/core/timestamp.py +182 -0
- tan/core/tool_lookup.py +383 -0
- tan/core/toolchain_pin.py +117 -0
- tan/core/toolchain_provision.py +1224 -0
- tan/core/trusted_path.py +134 -0
- tan/core/uboot_breakin.py +154 -0
- tan/core/uri_reference.py +415 -0
- tan/core/user_defines.py +152 -0
- tan/core/venv.py +332 -0
- tan/core/west_patches.py +143 -0
- tan/core/west_workspace_refusal.py +159 -0
- tan/core/zephyr_env.py +124 -0
- tan/env.py +185 -0
- tan/envelope.py +647 -0
- tan/exit_codes.py +12 -0
- tan/lazy_typer.py +185 -0
- tan/model/__init__.py +2 -0
- tan/model/_gen_fixture.py +132 -0
- tan/model/adapters/__init__.py +116 -0
- tan/model/adapters/cpu.py +26 -0
- tan/model/adapters/deepx.py +94 -0
- tan/model/adapters/drpai.py +242 -0
- tan/model/adapters/ethos_u.py +951 -0
- tan/model/adapters/executorch.py +55 -0
- tan/model/analyze.py +694 -0
- tan/model/build.py +304 -0
- tan/model/check.py +682 -0
- tan/model/manifest.py +625 -0
- tan/model/measure.py +216 -0
- tan/model/npu_ops_validate.py +130 -0
- tan/model/package.py +125 -0
- tan/model/perf.py +635 -0
- tan/model/perf_apply.py +721 -0
- tan/model/prep.py +193 -0
- tan/model/sram_fit.py +502 -0
- tan/model/targets.py +374 -0
- tan/model/tensorio.py +287 -0
- tan/net.py +73 -0
- tan/output_format.py +213 -0
- tan/planner/__init__.py +219 -0
- tan/planner/aperture.py +202 -0
- tan/planner/buildplan.py +1092 -0
- tan/planner/camera_owner.py +192 -0
- tan/planner/cameras.py +132 -0
- tan/planner/carveout.py +755 -0
- tan/planner/cli.py +274 -0
- tan/planner/headers.py +315 -0
- tan/planner/kconfig.py +2435 -0
- tan/planner/kconfig_symbols.py +473 -0
- tan/planner/libraries.py +677 -0
- tan/planner/link_target.py +237 -0
- tan/planner/linux_ownership.py +216 -0
- tan/planner/loader.py +1525 -0
- tan/planner/manifest.py +116 -0
- tan/planner/memory.py +161 -0
- tan/planner/memregion.py +56 -0
- tan/planner/models.py +533 -0
- tan/planner/orchestrator.py +874 -0
- tan/planner/ownership.py +203 -0
- tan/planner/partition.py +1160 -0
- tan/planner/paths.py +32 -0
- tan/planner/plain_slice.py +139 -0
- tan/planner/project_emit/__init__.py +60 -0
- tan/planner/project_emit/bom_netlist.py +375 -0
- tan/planner/project_emit/dts.py +580 -0
- tan/planner/project_emit/hw_info.py +221 -0
- tan/planner/project_emit/native_sim.py +136 -0
- tan/planner/project_emit/west_libs.py +213 -0
- tan/planner/project_loader.py +264 -0
- tan/planner/sdk_capability.py +203 -0
- tan/planner/sdk_compat.py +395 -0
- tan/planner/secure.py +350 -0
- tan/planner/slugs.py +394 -0
- tan/planner/som_metadata.py +325 -0
- tan/planner/strict_loaders.py +131 -0
- tan/planner/template.py +1037 -0
- tan/planner/template_pins.py +535 -0
- tan/planner/template_rewrite.py +714 -0
- tan/planner/topology.py +208 -0
- tan/planner/validate.py +365 -0
- tan/planner/whole_device_alias.py +67 -0
- tan/planner/zephyr_board.py +3510 -0
- tan/planner_cli.py +97 -0
- tan/planner_emit.py +617 -0
- tan/planner_root.py +119 -0
- tan/soc_ref.py +58 -0
- tan/templates/__init__.py +34 -0
- tan/templates/vendored/MANIFEST.md +1862 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
- tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
- tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
- tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
- tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
- tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
- tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
- tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
- tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
- tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
- tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
- tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
- tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
- tan/version.py +102 -0
- tan_cli-0.7.0.dist-info/METADATA +125 -0
- tan_cli-0.7.0.dist-info/RECORD +330 -0
- tan_cli-0.7.0.dist-info/WHEEL +5 -0
- tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
- tan_cli-0.7.0.dist-info/top_level.txt +1 -0
tan/model/prep.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""License-free model prep: validate calibration, INT8-quantize (onnxruntime QDQ
|
|
3
|
+
static), and report the fp32-vs-int8 accuracy delta. No vendor toolchain — the
|
|
4
|
+
accuracy report is the value (a silent INT8 accuracy cliff is the failure mode
|
|
5
|
+
this prevents). Vendor compile (Vela/dxcom/DRP-AI) is a separate later step.
|
|
6
|
+
|
|
7
|
+
Ported from alp-sdk `integration/model-edge-ai` (`scripts/alp_model/prep.py`,
|
|
8
|
+
alp-sdk#933, tan-cli#1287). `numpy` and `onnxruntime` (the optional `model`
|
|
9
|
+
extra) are imported INSIDE the functions that use them, so importing this
|
|
10
|
+
module costs nothing on a bare `tan` start-up; `tan.core.model_host.
|
|
11
|
+
missing_extra_modules` is what turns their absence into a coded refusal."""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PrepError(Exception):
|
|
20
|
+
"""Calibration invalid, or a quantize/inference step failed — raised instead
|
|
21
|
+
of emitting a model that didn't actually quantize or can't run."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class CalibrationInfo:
|
|
26
|
+
samples: int
|
|
27
|
+
input_name: str
|
|
28
|
+
input_shape: list[int]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def model_input(onnx_path: Path) -> tuple[str, list[int]]:
|
|
32
|
+
import onnxruntime as ort
|
|
33
|
+
try:
|
|
34
|
+
sess = ort.InferenceSession(str(onnx_path), providers=["CPUExecutionProvider"])
|
|
35
|
+
inp = sess.get_inputs()[0]
|
|
36
|
+
except Exception as exc: # onnxruntime raises broad types (bad model, escaping external data)
|
|
37
|
+
raise PrepError(f"could not load model {Path(onnx_path).name}: {exc}") from exc
|
|
38
|
+
shape = [(-1 if not isinstance(d, int) else d) for d in inp.shape]
|
|
39
|
+
return inp.name, shape
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _shape_matches(sample: Any, want: list[int]) -> bool:
|
|
43
|
+
s = list(sample.shape)
|
|
44
|
+
if len(s) != len(want):
|
|
45
|
+
# allow a sample missing the leading batch dim
|
|
46
|
+
if len(s) == len(want) - 1 and want[0] in (-1, 1):
|
|
47
|
+
s = [1, *s]
|
|
48
|
+
else:
|
|
49
|
+
return False
|
|
50
|
+
return all(w in (-1, d) for d, w in zip(s, want))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def load_calibration(cal_dir: Path, input_shape: list[int]) -> list[Any]:
|
|
54
|
+
import numpy as np
|
|
55
|
+
|
|
56
|
+
files = sorted(Path(cal_dir).glob("*.npy"))
|
|
57
|
+
if not files:
|
|
58
|
+
raise PrepError(f"no .npy calibration samples in {cal_dir}")
|
|
59
|
+
out: list[np.ndarray] = []
|
|
60
|
+
for f in files:
|
|
61
|
+
try:
|
|
62
|
+
loaded = np.load(f, allow_pickle=False)
|
|
63
|
+
if not isinstance(loaded, np.ndarray): # an .npz renamed to .npy
|
|
64
|
+
raise ValueError("not a plain .npy array")
|
|
65
|
+
arr = loaded.astype(np.float32)
|
|
66
|
+
except (ValueError, OSError, AttributeError, TypeError, EOFError) as exc:
|
|
67
|
+
raise PrepError(f"calibration sample {f.name} is not a numeric .npy array: {exc}") from exc
|
|
68
|
+
if arr.ndim == len(input_shape) - 1:
|
|
69
|
+
arr = arr[None, ...] # add batch dim
|
|
70
|
+
if not _shape_matches(arr, input_shape):
|
|
71
|
+
raise PrepError(
|
|
72
|
+
f"calibration sample {f.name} shape {list(arr.shape)} "
|
|
73
|
+
f"!= model input {input_shape}")
|
|
74
|
+
if input_shape[0] == -1 and arr.shape[0] > 1:
|
|
75
|
+
# tan-cli#1522: a batched file is N samples, not one -- the sample
|
|
76
|
+
# count, the min-samples floor and the per-image top-1 all count images.
|
|
77
|
+
out.extend(arr[i:i + 1] for i in range(arr.shape[0]))
|
|
78
|
+
else:
|
|
79
|
+
out.append(arr)
|
|
80
|
+
return out
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def validate_calibration(cal_dir: Path, onnx_path: Path, *,
|
|
84
|
+
min_samples: int = 8) -> CalibrationInfo:
|
|
85
|
+
name, shape = model_input(onnx_path)
|
|
86
|
+
samples = load_calibration(cal_dir, shape)
|
|
87
|
+
if len(samples) < min_samples:
|
|
88
|
+
raise PrepError(
|
|
89
|
+
f"calibration set has {len(samples)} samples; need >= {min_samples} "
|
|
90
|
+
f"for a representative INT8 range")
|
|
91
|
+
return CalibrationInfo(samples=len(samples), input_name=name, input_shape=shape)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _import_quant():
|
|
95
|
+
from onnxruntime.quantization import (CalibrationDataReader, QuantFormat,
|
|
96
|
+
QuantType, quantize_static)
|
|
97
|
+
from onnxruntime.quantization.shape_inference import quant_pre_process
|
|
98
|
+
return CalibrationDataReader, QuantFormat, QuantType, quantize_static, quant_pre_process
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _reader_cls(input_name: str, samples: list[Any]):
|
|
102
|
+
CalibrationDataReader = _import_quant()[0]
|
|
103
|
+
|
|
104
|
+
class _NpyReader(CalibrationDataReader):
|
|
105
|
+
def __init__(self) -> None:
|
|
106
|
+
self._it = iter([{input_name: s} for s in samples])
|
|
107
|
+
|
|
108
|
+
def get_next(self):
|
|
109
|
+
return next(self._it, None)
|
|
110
|
+
|
|
111
|
+
return _NpyReader()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def quantize(raw_onnx: Path, out_onnx: Path, cal_dir: Path, *,
|
|
115
|
+
per_channel: bool = False) -> Path:
|
|
116
|
+
if Path(raw_onnx).suffix.lower() != ".onnx":
|
|
117
|
+
raise PrepError(
|
|
118
|
+
f"model prep supports .onnx input in this release; got {Path(raw_onnx).name} "
|
|
119
|
+
f"(TFLite/PyTorch/Keras -> ONNX conversion is a follow-on)")
|
|
120
|
+
_, _, QuantType, quantize_static, quant_pre_process = _import_quant()
|
|
121
|
+
name, shape = model_input(raw_onnx)
|
|
122
|
+
samples = load_calibration(cal_dir, shape)
|
|
123
|
+
out_onnx = Path(out_onnx)
|
|
124
|
+
out_onnx.parent.mkdir(parents=True, exist_ok=True)
|
|
125
|
+
pre = out_onnx.with_suffix(".pre.onnx")
|
|
126
|
+
try:
|
|
127
|
+
# Symbolic shape inference (the quant_pre_process default) is
|
|
128
|
+
# onnxruntime's recommended pre-process, including for the
|
|
129
|
+
# dynamic-batch models this tool targets. It needs sympy, a model-prep
|
|
130
|
+
# dep (pyproject.toml) -- still license-free: that just means no
|
|
131
|
+
# vendor NPU toolchain (Vela/dxcom/DRP-AI) is involved.
|
|
132
|
+
quant_pre_process(str(raw_onnx), str(pre))
|
|
133
|
+
quantize_static(str(pre), str(out_onnx), _reader_cls(name, samples),
|
|
134
|
+
per_channel=per_channel, weight_type=QuantType.QInt8)
|
|
135
|
+
except Exception as exc: # onnxruntime raises broad types
|
|
136
|
+
raise PrepError(f"quantization failed: {exc}") from exc
|
|
137
|
+
finally:
|
|
138
|
+
if pre.is_file():
|
|
139
|
+
pre.unlink()
|
|
140
|
+
if not out_onnx.is_file():
|
|
141
|
+
raise PrepError("quantization produced no output model")
|
|
142
|
+
return out_onnx
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass(frozen=True)
|
|
146
|
+
class AccuracyReport:
|
|
147
|
+
samples: int
|
|
148
|
+
top1_agreement_pct: float
|
|
149
|
+
mean_cosine: float
|
|
150
|
+
max_abs_err: float
|
|
151
|
+
verdict: str
|
|
152
|
+
guidance: str | None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _cosine(a: Any, b: Any) -> float:
|
|
156
|
+
import numpy as np
|
|
157
|
+
|
|
158
|
+
na, nb = float(np.linalg.norm(a)), float(np.linalg.norm(b))
|
|
159
|
+
if na == 0.0 or nb == 0.0:
|
|
160
|
+
return 1.0 if na == nb else 0.0
|
|
161
|
+
return float(np.dot(a, b) / (na * nb))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def accuracy_delta(fp32_onnx: Path, int8_onnx: Path, cal_dir: Path, *,
|
|
165
|
+
degrade_threshold: float = 95.0) -> AccuracyReport:
|
|
166
|
+
import numpy as np
|
|
167
|
+
import onnxruntime as ort
|
|
168
|
+
name, shape = model_input(fp32_onnx)
|
|
169
|
+
samples = load_calibration(cal_dir, shape)
|
|
170
|
+
try:
|
|
171
|
+
s32 = ort.InferenceSession(str(fp32_onnx), providers=["CPUExecutionProvider"])
|
|
172
|
+
s8 = ort.InferenceSession(str(int8_onnx), providers=["CPUExecutionProvider"])
|
|
173
|
+
agree, coss, maxerr = 0, [], 0.0
|
|
174
|
+
for x in samples:
|
|
175
|
+
o32 = s32.run(None, {name: x})[0].ravel()
|
|
176
|
+
o8 = s8.run(None, {name: x})[0].ravel()
|
|
177
|
+
if int(o32.argmax()) == int(o8.argmax()):
|
|
178
|
+
agree += 1
|
|
179
|
+
coss.append(_cosine(o32, o8))
|
|
180
|
+
maxerr = max(maxerr, float(np.abs(o32 - o8).max()))
|
|
181
|
+
except Exception as exc: # a produced-but-unrunnable
|
|
182
|
+
# model must not escape as
|
|
183
|
+
# a raw traceback
|
|
184
|
+
raise PrepError(f"quantized model failed to run: {exc}") from exc
|
|
185
|
+
top1 = agree / len(samples) * 100.0
|
|
186
|
+
verdict = "good" if top1 >= degrade_threshold else "degraded"
|
|
187
|
+
guidance = None if verdict == "good" else (
|
|
188
|
+
"INT8 accuracy dropped: try --per-channel, add more representative "
|
|
189
|
+
"calibration samples, or keep sensitive ops in fp16.")
|
|
190
|
+
return AccuracyReport(samples=len(samples), top1_agreement_pct=round(top1, 1),
|
|
191
|
+
mean_cosine=round(float(np.mean(coss)), 4),
|
|
192
|
+
max_abs_err=round(maxerr, 6), verdict=verdict,
|
|
193
|
+
guidance=guidance)
|