Download example.py from Arm/resnet-50-int8-xnnpack-executorch: direct link, hf CLI and curl.
- Browser
- Download file 7 kB
-
https://huggingface.co/Arm/resnet-50-int8-xnnpack-executorch/resolve/main/example.py
- Command line
-
hf download hf://Arm/resnet-50-int8-xnnpack-executorch/example.py
-
curl -L -o example.py https://huggingface.co/Arm/resnet-50-int8-xnnpack-executorch/resolve/main/example.py
7 kB
| """Minimal inference example for microsoft/resnet-50 INT8 using ExecuTorch. | |
| Loads a quantized .pte model and runs image classification on a single image. | |
| Outputs top-5 predictions with class labels and probabilities. | |
| """ | |
| 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-50_raspberry_executorch_optimized.pte" | |
| IMAGE_PATH = "sample_input.jpg" | |
| INPUT_SIZE = (224, 224) | |
| TOP_K = 5 | |
| # ImageNet normalization constants | |
| 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.""" | |
| runtime = Runtime.get() | |
| program = runtime.load_program(pte_path) | |
| return program.load_method("forward") | |
| # ββ Preprocessing ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def preprocess(image_path: str) -> torch.Tensor: | |
| """Load and preprocess image for model input. | |
| Resizes to 256, center-crops to 224x224, converts to float32 tensor in | |
| [0, 1], then applies ImageNet mean/std normalization. | |
| """ | |
| image = Image.open(image_path).convert("RGB") | |
| transform = transforms.Compose( | |
| [ | |
| transforms.Resize(256), | |
| transforms.CenterCrop(INPUT_SIZE), | |
| transforms.ToTensor(), | |
| transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD), | |
| ] | |
| ) | |
| return transform(image).unsqueeze(0) # [1, 3, 224, 224] | |
| # ββ Inference ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def run_inference(method, input_tensor: torch.Tensor) -> torch.Tensor: | |
| """Run forward pass and return raw logits tensor.""" | |
| outputs = method.execute([input_tensor]) | |
| return outputs[0] # [1, 1000] | |
| # ββ Postprocessing βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def postprocess(raw_output: torch.Tensor, labels: dict[str, str], top_k: int = TOP_K) -> list: | |
| """Decode raw logits into top-k class predictions with probabilities. | |
| Applies softmax to convert logits to probabilities, then returns the | |
| top-k classes sorted by descending probability. | |
| """ | |
| # Ensure 2D: [1, 1000] β [1000] | |
| logits = raw_output | |
| if logits.dim() > 1: | |
| logits = logits.squeeze(0) | |
| probabilities = torch.softmax(logits, dim=-1) | |
| k = min(top_k, logits.shape[-1]) | |
| top_scores, top_indices = probabilities.topk(k) | |
| # 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.tolist(), top_indices.tolist(), strict=False), | |
| key=lambda item: (-item[0], item[1]), | |
| ) | |
| results = [] | |
| for score, idx in ranked: | |
| class_label = labels[str(idx)] | |
| results.append( | |
| {"class_index": idx, "class": class_label, "probability": round(score, 6)} | |
| ) | |
| return results | |
| # ββ Save Results βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def save_results(results: list, script_dir: Path) -> None: | |
| """Save top-k predictions as predictions.json in the script directory.""" | |
| 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_output_image(image_path: str, top_prediction: dict, script_dir: Path) -> None: | |
| """Overlay the top prediction label on the original image and save as sample_output.jpg.""" | |
| image = Image.open(image_path).convert("RGB") | |
| draw = ImageDraw.Draw(image) | |
| label = f"{top_prediction['class']}: {top_prediction['probability'] * 100:.1f}%" | |
| font_size = max(16, image.width // 20) | |
| font: ImageFont.ImageFont | ImageFont.FreeTypeFont | |
| try: | |
| font = ImageFont.truetype( | |
| "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", font_size | |
| ) | |
| except OSError: | |
| try: | |
| font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", font_size) | |
| except OSError: | |
| font = ImageFont.load_default() | |
| bbox = draw.textbbox((0, 0), label, font=font) | |
| text_w, text_h = bbox[2] - bbox[0], bbox[3] - bbox[1] | |
| padding = 8 | |
| x, y = padding, image.height - text_h - padding * 3 | |
| draw.rectangle( | |
| [x - padding, y - padding, x + text_w + padding, y + text_h + padding], | |
| fill=(0, 0, 0, 180), | |
| ) | |
| draw.text((x, y), label, fill=(255, 255, 255), font=font) | |
| output_path = script_dir / "sample_output.jpg" | |
| image.save(output_path, quality=95) | |
| print(f"Output image saved to: {output_path}") | |
| # ββ Main βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def main() -> None: | |
| script_dir = Path(__file__).parent | |
| # Resolve paths relative to script location | |
| model_path = script_dir / MODEL_PATH | |
| image_path = script_dir / IMAGE_PATH | |
| # Load model | |
| method = load_model(str(model_path)) | |
| # Preprocess input image | |
| input_tensor = preprocess(str(image_path)) | |
| # Run inference | |
| raw_output = run_inference(method, input_tensor) | |
| # Decode predictions | |
| results = postprocess(raw_output, IMAGENET_CLASSES) | |
| # Print top-k predictions | |
| print(f"\nTop-{TOP_K} predictions for: {image_path.name}") | |
| print("-" * 50) | |
| for rank, pred in enumerate(results, 1): | |
| print(f" {rank}. {pred['class']:<40} {pred['probability']*100:.2f}%") | |
| # Save predictions.json | |
| save_results(results, script_dir) | |
| # Save labeled output image | |
| save_output_image(str(image_path), results[0], script_dir) | |
| if __name__ == "__main__": | |
| main() | |