Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/pull.yml
Original file line number Diff line number Diff line change
Expand Up @@ -860,8 +860,9 @@ jobs:
# Run pytest
PYTHON_EXECUTABLE=python bash backends/nxp/run_unittests.sh

# Run aot example:
PYTHON_EXECUTABLE=python bash examples/nxp/run_aot_example.sh
# Run aot examples:
PYTHON_EXECUTABLE=python bash examples/nxp/run_aot_example.sh cifar10
PYTHON_EXECUTABLE=python bash examples/nxp/run_aot_example.sh mobilenetv2


test-vulkan-models-linux:
Expand Down
7 changes: 7 additions & 0 deletions backends/nxp/tests/executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ def inference(
return output.detach().numpy()
elif isinstance(output, tuple) and len(output) == 1:
return output[0].detach().numpy()
elif isinstance(output, tuple):
output_names = self.edge_program.graph_signature.user_outputs

return {
name: tensor.detach().numpy()
for (name, tensor) in zip(output_names, output)
}

raise RuntimeError(
"Edge program inference with multiple outputs not implemented"
Expand Down
5 changes: 4 additions & 1 deletion examples/nxp/aot_neutron_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@

from .experimental.cifar_net.cifar_net import CifarNet, test_cifarnet_model

from .models.mobilenet_v2 import MobilenetV2

FORMAT = "[%(levelname)s %(asctime)s %(filename)s:%(lineno)s] %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)

Expand Down Expand Up @@ -87,7 +89,7 @@ def get_model_and_inputs_from_name(model_name: str):
logging.warning(
"Using a model from examples/models not all of these are currently supported"
)
model, example_inputs, _ = EagerModelFactory.create_model(
model, example_inputs, _, _ = EagerModelFactory.create_model(
*MODEL_NAME_TO_MODEL[model_name]
)
else:
Expand All @@ -100,6 +102,7 @@ def get_model_and_inputs_from_name(model_name: str):

models = {
"cifar10": CifarNet,
"mobilenetv2": MobilenetV2,
}


Expand Down
114 changes: 114 additions & 0 deletions examples/nxp/models/mobilenet_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Copyright 2025 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 itertools
from typing import Iterator

import torch
import torchvision

from executorch.examples.models.mobilenet_v2 import MV2Model
from torch.utils.data import DataLoader
from torchvision import transforms


class MobilenetV2(MV2Model):

def get_calibration_inputs(
self, batch_size: int = 1
) -> Iterator[tuple[torch.Tensor]]:
"""
Returns an iterator for the Imagenette validation dataset, downloading it if necessary.

Args:
batch_size (int): The batch size for the iterator.

Returns:
iterator: An iterator that yields batches of images from the Imagnetette validation dataset.
"""
dataloader = self.get_dataset(batch_size)

# Return the iterator
dataloader_iterable = itertools.starmap(
lambda data, label: (data,), iter(dataloader)
)

# We want approximately 500 samples
batch_count = 500 // batch_size
return itertools.islice(dataloader_iterable, batch_count)

def get_dataset(self, batch_size):
# Define data transformations
data_transforms = transforms.Compose(
[
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]
), # ImageNet stats
]
)

dataset = torchvision.datasets.Imagenette(
root="./data", split="val", transform=data_transforms, download=True
)
dataloader = torch.utils.data.DataLoader(
dataset,
batch_size=batch_size,
shuffle=False,
num_workers=1,
)
return dataloader


def gather_samples_per_class_from_dataloader(
dataloader, num_samples_per_class=10
) -> list[tuple]:
"""
Gathers a specified number of samples for each class from a DataLoader.

Args:
dataloader (DataLoader): The PyTorch DataLoader object.
num_samples_per_class (int): The number of samples to gather for each class. Defaults to 10.

Returns:
samples: A list of (sample, label) tuples.
"""

if not isinstance(dataloader, DataLoader):
raise TypeError("dataloader must be a torch.utils.data.DataLoader object")
if not isinstance(num_samples_per_class, int) or num_samples_per_class <= 0:
raise ValueError("num_samples_per_class must be a positive integer")

labels = sorted(
set([label for _, label in dataloader.dataset])
) # Get unique labels from the dataset
samples_per_label = {label: [] for label in labels} # Initialize dictionary

for sample, label in dataloader:
label = label.item()
if len(samples_per_label[label]) < num_samples_per_class:
samples_per_label[label].append((sample, label))

samples = []

for label in labels:
samples.extend(samples_per_label[label])

return samples


def generate_input_samples_file():
model = MobilenetV2()
dataloader = model.get_dataset(batch_size=1)
samples = gather_samples_per_class_from_dataloader(
dataloader, num_samples_per_class=2
)

torch.save(samples, "calibration_data.pt")


if __name__ == "__main__":
generate_input_samples_file()
5 changes: 3 additions & 2 deletions examples/nxp/run_aot_example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ set -eux

SCRIPT_DIR=$(dirname $(readlink -fm $0))
EXECUTORCH_DIR=$(dirname $(dirname $SCRIPT_DIR))
MODEL=${1:-"cifar10"}

cd $EXECUTORCH_DIR

# Run the AoT example
python -m examples.nxp.aot_neutron_compile --quantize \
--delegate --neutron_converter_flavor SDK_25_03 -m cifar10
--delegate --neutron_converter_flavor SDK_25_03 -m ${MODEL}
# verify file exists
test -f cifar10_nxp_delegate.pte
test -f ${MODEL}_nxp_delegate.pte
Loading