Does the model function correctly for the use-case?
Run the validate_cutout.py script on your development machine to try the FP32 XNNPACK artifact on a sample image locally:
module-3-executorch-on-android-semantic-segmentation/scripts/aot/validate_cutout.py \
--spec module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/model_spec.json \
--pte module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/lraspp_mobilenetv3_xnnpack.pte \
--image module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/sample.jpg \
--out-dir module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/validation
The script keeps both the normalized input tensor and the letterbox dimensions needed to map the output back to the visible image:
input_tensor, letterboxed = preprocess_image(image_path, spec)
model = _load_for_executorch(str(args.pte))
with torch.inference_mode():
output = model.forward((input_tensor,))[0]
Before interpreting the logits, it rejects an output that does not match the
expected [1, 21, 520, 520] shape:
expected_shape = tuple(spec["output"]["shape"])
if tuple(output.shape) != expected_shape:
raise ValueError(
f"Unexpected output shape {tuple(output.shape)}; "
f"expected {expected_shape}"
)
It then applies argmax across the 21 class planes and selects the largest
non-background class:
mask = output[0].argmax(dim=0).cpu().numpy().astype("uint8")
background_class = spec["output"]["postprocessing"]["background_class"]
class_id = largest_non_background(mask, background=background_class)
The padding recorded during preprocessing is removed before the binary mask is used as image transparency:
selected = mask[top:bottom, left:right] == class_id
mask_image = Image.fromarray((selected.astype("uint8") * 255), mode="L")
cutout.paste(rgba, mask=mask_image)
The selected class should be dog:
Selected class: dog (12)
ExecuTorch forward: 29.18 ms
The reported time covers only the ExecuTorch forward() call. It excludes
image loading, preprocessing, and postprocessing, and, since it runs on your development machine, should not be confused with Android performance data.
Inspect these files in module-3-executorch-on-android-semantic-segmentation/artifacts/lraspp_mobilenetv3/validation/:
mask.pngshows the selected class as white pixels.overlay.pngapplies a blue tint to the selected pixels.cutout.pngmakes all other pixels transparent.
Check FP32 cutout quality
Inspect mask.png, overlay.png, and cutout.png:
- confirm that the selected class matches the expected object
- check whether the mask captures the main foreground object
- look for missing foreground regions
- look for background regions that were incorrectly retained
FP32 validation result
The FP32 .pte gives a local quality indication. If the FP32 result is poor locally, it will be poor on Android, and you may require a different model for your use-case. If the FP32 result is useful, it becomes the reference for judging whether later INT8 quantization reduces cutout quality too much.
This single-image check confirms that the model, preprocessing, and postprocessing produce a plausible cutout; it does not measure segmentation accuracy. For production evaluation, you should run the model automatically over a representative labeled validation set, compare each predicted mask with its ground-truth mask, and aggregate the results across all images. You can report per-class intersection over union (IoU) and mean IoU and consider these against acceptance thresholds specific to your use-case and needs.
Course complete
Nice work
You completed Optimize and deploy AI inference with ExecuTorch on Arm.