diff --git a/backends/nxp/tests/generic_tests/test_aot_example.py b/backends/nxp/tests/generic_tests/test_aot_example.py index 4beded3403a..e9b1bf06c72 100644 --- a/backends/nxp/tests/generic_tests/test_aot_example.py +++ b/backends/nxp/tests/generic_tests/test_aot_example.py @@ -125,7 +125,7 @@ def _assert_profiling( def test_aot_example__mobilenet_v2(): - """Test that mobilenet can be lowered to Neutron backend via `aot_neutron_compile.py` and all ops are delegated.""" + """Test that MobileNetV2 can be lowered to Neutron backend via `aot_neutron_compile.py` and all ops are delegated.""" num_random_samples = 1 @@ -154,7 +154,7 @@ def test_aot_example__mobilenet_v2(): def test_aot_example__mobilenet_v2__profiling(): - """Test that mobilenet_v2 can be lowered to Neutron backend via `aot_neutron_compile.py`, all ops are delegated, + """Test that MobileNetV2 can be lowered to Neutron backend via `aot_neutron_compile.py`, all ops are delegated, the output model is profilable and ETRecord is generated properly.""" num_random_samples = 1 diff --git a/backends/nxp/tests/models/test_mobilenet_v3.py b/backends/nxp/tests/models/test_mobilenet_v3.py new file mode 100644 index 00000000000..a981a28d918 --- /dev/null +++ b/backends/nxp/tests/models/test_mobilenet_v3.py @@ -0,0 +1,102 @@ +# Copyright 2026 NXP +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +from functools import partial + +import numpy as np + +# noinspection PyUnusedImports +import pytest +import torch + +from executorch.backends.nxp.tests.dataset_creator import ( + FromCalibrationDataDatasetCreator, +) +from executorch.backends.nxp.tests.executorch_pipeline import ModelInputSpec +from executorch.backends.nxp.tests.graph_verifier import ( + BaseGraphVerifier, + NonDelegatedNode, +) +from executorch.backends.nxp.tests.model_output_comparator import ( + NumericalStatsOutputComparator, +) +from executorch.backends.nxp.tests.nsys_testing import lower_run_compare +from executorch.backends.nxp.tests.use_qat import * # noqa F403 +from executorch.examples.nxp.models.mobilenet_v3 import MobileNetV3 + +BOUNDS_MSE = { + "PTQ": {"channels-last": 7e-03, "channels-first": 8e-03}, + "QAT": {"channels-last": 6e-03, "channels-first": 3e-03}, +} + + +@pytest.fixture(autouse=True) +def reseed_model_per_test_run(): + torch.manual_seed(23) + np.random.seed(23) + + +@pytest.mark.parametrize("channels_last", [False, True]) +def test_mobilenet_v3_mse_cpu_vs_npu( + mocker, + request, + channels_last, + use_qat, +): + num_samples = 1 + + mobilenet_v3 = MobileNetV3( + num_samples=num_samples, + use_random_dataset=True, + balanced_dataset=False, + ) + model = mobilenet_v3.get_eager_model() + dataset = mobilenet_v3.dataset + labels = mobilenet_v3.labels + + dataset_creator = FromCalibrationDataDatasetCreator( + dataset, num_examples=num_samples, idx_to_label=labels + ) + + input_spec = ModelInputSpec(mobilenet_v3.input_shape) + if channels_last: + model.to(memory_format=torch.channels_last) + input_spec.dim_order = torch.channels_last + quant_type_key = "QAT" if use_qat else "PTQ" + format_key = "channels-last" if channels_last else "channels-first" + + mse = BOUNDS_MSE[quant_type_key][format_key] + comparator = NumericalStatsOutputComparator( + max_mse_error=mse, is_classification_task=True + ) + model_verifier = BaseGraphVerifier( + 1, + [ + NonDelegatedNode("aten_add_tensor", 13), + NonDelegatedNode("aten_div_tensor", 9), + NonDelegatedNode("aten_clamp_default", 18), + ], + ) + train_fn = ( + partial(mobilenet_v3.train_model_fn, channels_last=channels_last) + if use_qat + else None + ) + + ref_input_spec = ModelInputSpec(input_spec.shape) + ref_input_spec.dim_order = torch.contiguous_format + + lower_run_compare( + model, + [input_spec], + model_verifier, + request, + dataset_creator=dataset_creator, + output_comparator=comparator, + reference_input_spec=[ref_input_spec], + mocker=mocker, + use_qat=use_qat, + train_fn=train_fn, + ) diff --git a/examples/nxp/README.md b/examples/nxp/README.md index 247c7253756..40abf65b6a8 100644 --- a/examples/nxp/README.md +++ b/examples/nxp/README.md @@ -23,6 +23,10 @@ $ ./examples/nxp/setup.sh ## Supported models * CifarNet * MobileNetV2 +* MLPerfTinyAnomalyDetection +* MLPerfTinyImageClassification +* MLPerfTinyKeywordSpotting +* MLPerfTinyVisualWakeWords ## PyTorch Model Delegation to Neutron Backend First we will start with an example script converting the model. This example show the CifarNet model preparation. diff --git a/examples/nxp/models/mobilenet_v3.py b/examples/nxp/models/mobilenet_v3.py new file mode 100644 index 00000000000..43494c34f44 --- /dev/null +++ b/examples/nxp/models/mobilenet_v3.py @@ -0,0 +1,21 @@ +# Copyright 2026 NXP +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +import torch + +from executorch.examples.models.mobilenet_v3 import MV3Model +from executorch.examples.nxp.models.mobilenet_v2 import MobileNetV2 + + +class MobileNetV3(MobileNetV2): + """ + MobileNet V3 model. Shares most parameters with MobileNet V2. + + Calibration data for MobileNet V2 and V3 can be generated by backends/nxp/tests/prepare_calibration_data_mobilenet_v2.py + from Imagenette dataset, a subset of 10 classes from ImageNet dataset the model is trained on. + """ + + def _init_eager_model(self) -> torch.nn.Module: + return MV3Model().get_eager_model().eval()