| """Minimal inference example for DeiT-Tiny INT8 using ExecuTorch. |
| |
| Loads a quantized .pte model and runs inference on a single image, |
| printing the top-5 ImageNet class predictions with probabilities. |
| """ |
|
|
| import json |
| from pathlib import Path |
|
|
| import torch |
| from executorch.runtime import Runtime |
| from PIL import Image |
| from torchvision import transforms |
| from torchvision.models import GoogLeNet_Weights |
|
|
|
|
| |
| MODEL_PATH = "deit_raspberry_executorch_optimized.pte" |
| IMAGE_PATH = "sample_input.jpg" |
| INPUT_SIZE = (224, 224) |
| RESIZE_SIZE = 256 |
| MEAN = [0.485, 0.456, 0.406] |
| STD = [0.229, 0.224, 0.225] |
| TOP_K = 5 |
|
|
|
|
| |
| 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") |
|
|
|
|
| |
| def preprocess(image_path: str) -> torch.Tensor: |
| """Load and preprocess image for model input. |
| |
| Pipeline: |
| 1. Resize shortest edge to 256 px (bicubic) |
| 2. Center-crop to 224x224 |
| 3. Convert to float32 tensor in [0, 1] |
| 4. Normalize with ImageNet mean/std |
| """ |
| transform = transforms.Compose( |
| [ |
| transforms.Resize( |
| RESIZE_SIZE, interpolation=transforms.InterpolationMode.BICUBIC |
| ), |
| transforms.CenterCrop(INPUT_SIZE), |
| transforms.ToTensor(), |
| transforms.Normalize(mean=MEAN, std=STD), |
| ] |
| ) |
| image = Image.open(image_path).convert("RGB") |
| tensor = transform(image) |
| return tensor.unsqueeze(0) |
|
|
|
|
| |
| 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] |
|
|
|
|
| |
| def postprocess(raw_output: torch.Tensor, labels: list) -> list[dict]: |
| """Convert raw logits to top-k class predictions. |
| |
| Applies softmax over the 1000-class logit vector, then returns the |
| top-k (class name, probability) pairs sorted by descending probability. |
| """ |
| logits = raw_output.squeeze(0) |
| probabilities = torch.softmax(logits, dim=-1) |
| top_probs, top_indices = torch.topk(probabilities, TOP_K) |
| return [ |
| {"class": labels[idx.item()], "probability": round(prob.item(), 6)} |
| for prob, idx in zip(top_probs, top_indices, strict=False) |
| ] |
|
|
|
|
| |
| def save_results(results: list[dict], script_dir: Path) -> None: |
| """Save top-k predictions to predictions.json in the script's directory.""" |
| output_path = script_dir / "predictions.json" |
| with open(output_path, "w") as f: |
| json.dump(results, f, indent=2) |
| print(f"Saved predictions to {output_path}") |
|
|
|
|
| |
| def main() -> None: |
| script_dir = Path(__file__).parent |
|
|
| |
| WEIGHTS = GoogLeNet_Weights.IMAGENET1K_V1 |
| IMAGENET_CLASSES = WEIGHTS.meta["categories"] |
|
|
| |
| method = load_model(str(script_dir / MODEL_PATH)) |
| input_tensor = preprocess(str(script_dir / IMAGE_PATH)) |
| raw_output = run_inference(method, input_tensor) |
| results = postprocess(raw_output, IMAGENET_CLASSES) |
|
|
| |
| print(f"Top-{TOP_K} predictions:") |
| for i, r in enumerate(results, 1): |
| print(f" {i}. {r['class']} ({r['probability'] * 100:.2f}%)") |
|
|
| save_results(results, script_dir) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|