Download example.py from Arm/resnet-18-int8-xnnpack-executorch: direct link, hf CLI and curl.
- Browser
- Download file 7.23 kB
-
https://huggingface.co/Arm/resnet-18-int8-xnnpack-executorch/resolve/main/example.py
- Command line
-
hf download hf://Arm/resnet-18-int8-xnnpack-executorch/example.py
-
curl -L -o example.py https://huggingface.co/Arm/resnet-18-int8-xnnpack-executorch/resolve/main/example.py
7.23 kB
| """Minimal inference example for microsoft/resnet-18 INT8 using ExecuTorch. | |
| Loads a quantized .pte model and runs classification inference on a single image. | |
| Saves top-5 predictions to predictions.json and an annotated sample_output.jpg | |
| in the same directory as this script. | |
| """ | |
| import json | |
| from pathlib import Path | |
| import torch | |
| from executorch.runtime import Runtime | |
| from PIL import Image, ImageDraw, ImageFont | |
| from torchvision import transforms | |
| # ββ Configuration ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| MODEL_PATH = "resnet-18_raspberry_executorch_optimized.pte" | |
| IMAGE_PATH = "sample_input.jpg" | |
| INPUT_SIZE = (224, 224) | |
| TOP_K = 5 | |
| # ImageNet normalization constants (mean and std per channel) | |
| IMAGENET_MEAN = [0.485, 0.456, 0.406] | |
| IMAGENET_STD = [0.229, 0.224, 0.225] | |
| SCRIPT_DIR = Path(__file__).resolve().parent | |
| # ImageNet class labels (1000 classes) | |
| with (SCRIPT_DIR / "imagenet_classes.json").open(encoding="utf-8") as file: | |
| IMAGENET_CLASSES = json.load(file) | |
| # ββ Model Loading ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def load_model(pte_path: str): | |
| """Load ExecuTorch .pte model and return the forward method.""" | |
| script_dir = Path(__file__).parent | |
| runtime = Runtime.get() | |
| program = runtime.load_program(str(script_dir / pte_path)) | |
| return program.load_method("forward") | |
| # ββ Preprocessing ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def preprocess(image_path: str) -> torch.Tensor: | |
| """Load and preprocess image for model input. | |
| Pipeline: resize to 256 px (shortest edge) -> center crop to 224x224 | |
| -> convert to [0, 1] float32 tensor -> normalize with ImageNet mean/std. | |
| """ | |
| script_dir = Path(__file__).parent | |
| image = Image.open(script_dir / image_path).convert("RGB") | |
| transform = transforms.Compose( | |
| [ | |
| transforms.Resize(256), | |
| transforms.CenterCrop(INPUT_SIZE), | |
| transforms.ToTensor(), | |
| transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD), | |
| ] | |
| ) | |
| input_tensor = transform(image).unsqueeze(0) # [1, 3, 224, 224] | |
| return input_tensor | |
| # ββ Inference ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def run_inference(method, input_tensor: torch.Tensor) -> torch.Tensor: | |
| """Run forward pass and return the raw logits tensor.""" | |
| outputs = method.execute([input_tensor]) | |
| return outputs[0] | |
| # ββ Postprocessing βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def postprocess(raw_output: torch.Tensor, labels: dict[str, str]) -> list[dict]: | |
| """Convert raw logits to top-k class predictions with probabilities. | |
| Applies softmax to get class probabilities, then returns the top-5 | |
| predictions as a list of {class_id, class_name, probability} dicts. | |
| """ | |
| # Ensure output is 2D [1, 1000] | |
| if raw_output.dim() == 1: | |
| raw_output = raw_output.unsqueeze(0) | |
| probabilities = torch.softmax(raw_output, dim=-1) | |
| k = min(TOP_K, raw_output.shape[-1]) | |
| top_scores, top_indices = probabilities.topk(k, dim=-1) | |
| # Quantized logits land on a discrete grid, so exact ties are common and | |
| # topk orders them arbitrarily. Break ties by class id for a stable result. | |
| ranked = sorted( | |
| zip(top_scores[0].tolist(), top_indices[0].tolist(), strict=False), | |
| key=lambda item: (-item[0], item[1]), | |
| ) | |
| results = [] | |
| for score, idx in ranked: | |
| results.append( | |
| { | |
| "class_id": idx, | |
| "class_name": labels[str(idx)], | |
| "probability": round(score, 6), | |
| } | |
| ) | |
| return results | |
| # ββ Save Results βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def save_results(results: list[dict]) -> None: | |
| """Save top-k predictions to predictions.json in the script's directory.""" | |
| script_dir = Path(__file__).parent | |
| output_path = script_dir / "predictions.json" | |
| with open(output_path, "w") as f: | |
| json.dump(results, f, indent=2) | |
| print(f"Predictions saved to {output_path}") | |
| def save_sample_output(image_path: str, predictions: list[dict]) -> None: | |
| """Overlay top-5 predictions on the input image and save as sample_output.jpg.""" | |
| script_dir = Path(__file__).parent | |
| image = Image.open(script_dir / image_path).convert("RGB") | |
| draw = ImageDraw.Draw(image) | |
| try: | |
| font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", size=18) | |
| small_font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", size=15) | |
| except OSError: | |
| font = ImageFont.load_default() | |
| small_font = font | |
| # Semi-transparent dark banner at the top | |
| banner_h = 30 + len(predictions) * 22 | |
| draw.rectangle([(0, 0), (image.width, banner_h)], fill=(0, 0, 0, 180)) | |
| draw.text( | |
| (10, 6), "Top-5 predictions (ResNet-18 INT8)", font=font, fill=(255, 255, 255) | |
| ) | |
| for i, pred in enumerate(predictions): | |
| label = pred["class_name"].split(",")[0] | |
| pct = pred["probability"] * 100 | |
| y = 30 + i * 22 | |
| draw.text( | |
| (10, y), | |
| f"{i + 1}. {label} {pct:.1f}%", | |
| font=small_font, | |
| fill=(255, 220, 50), | |
| ) | |
| out_path = script_dir / "sample_output.jpg" | |
| image.save(out_path, quality=95) | |
| print(f"Sample output saved to {out_path}") | |
| # ββ Main βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def main() -> None: | |
| script_dir = Path(__file__).parent | |
| # Load model | |
| print(f"Loading model: {MODEL_PATH}") | |
| method = load_model(MODEL_PATH) | |
| # Preprocess input image | |
| print(f"Preprocessing image: {IMAGE_PATH}") | |
| input_tensor = preprocess(IMAGE_PATH) | |
| # Run inference | |
| print("Running inference...") | |
| raw_output = run_inference(method, input_tensor) | |
| # Postprocess outputs | |
| results = postprocess(raw_output, IMAGENET_CLASSES) | |
| # Print top-k predictions | |
| print(f"\nTop-{TOP_K} predictions:") | |
| for i, pred in enumerate(results, 1): | |
| print( | |
| f" {i}. {pred['class_name']} ({pred['class_id']}): {pred['probability']:.4f}" | |
| ) | |
| # Save predictions JSON and annotated output image | |
| save_results(results) | |
| save_sample_output(IMAGE_PATH, results) | |
| if __name__ == "__main__": | |
| main() | |