File size: 4,959 Bytes
48a89ad
 
 
 
 
ea3b5c8
 
 
 
 
 
 
 
b504556
ea3b5c8
48a89ad
 
67d0363
ea3b5c8
 
48a89ad
ea3b5c8
 
 
 
 
48a89ad
ea3b5c8
48a89ad
ea3b5c8
 
 
 
 
48a89ad
ea3b5c8
48a89ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ea3b5c8
 
48a89ad
ea3b5c8
 
48a89ad
ea3b5c8
48a89ad
ea3b5c8
 
 
 
48a89ad
b504556
48a89ad
 
 
 
 
 
ea3b5c8
 
48a89ad
b504556
48a89ad
 
ea3b5c8
 
48a89ad
ea3b5c8
48a89ad
ea3b5c8
 
 
48a89ad
ea3b5c8
 
48a89ad
ea3b5c8
 
67d0363
b504556
 
 
ea3b5c8
48a89ad
ea3b5c8
 
 
b504556
ea3b5c8
48a89ad
 
ea3b5c8
48a89ad
ea3b5c8
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
"""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


# ── Configuration ──────────────────────────────────────────────────────────────
MODEL_PATH = "deit_raspberry_executorch_optimized.pte"
IMAGE_PATH = "sample_input.jpg"
INPUT_SIZE = (224, 224)
RESIZE_SIZE = 256  # resize shorter edge before center crop
MEAN = [0.485, 0.456, 0.406]
STD = [0.229, 0.224, 0.225]
TOP_K = 5


# ── 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.

    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)  # [1, 3, 224, 224]


# ── 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: 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)  # [1000]
    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)
    ]


# ── Save Results ───────────────────────────────────────────────────────────────
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}")


# ── Main ───────────────────────────────────────────────────────────────────────
def main() -> None:
    script_dir = Path(__file__).parent

    # ImageNet class labels (1000 classes)
    WEIGHTS = GoogLeNet_Weights.IMAGENET1K_V1
    IMAGENET_CLASSES = WEIGHTS.meta["categories"]

    # Load model, run inference, decode results
    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 top-k predictions
    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()