Upload README.md with huggingface_hub
Browse files
README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
tags:
|
| 4 |
+
- executorch
|
| 5 |
+
- xnnpack
|
| 6 |
+
- pte
|
| 7 |
+
- on-device
|
| 8 |
+
- mask-generation
|
| 9 |
+
---
|
| 10 |
+
# MobileSAM β ExecuTorch XNNPACK (encoder + decoder)
|
| 11 |
+
|
| 12 |
+
Promptable segmentation in two `.pte` files: run the encoder once per image, the
|
| 13 |
+
decoder once per click.
|
| 14 |
+
|
| 15 |
+
- `mobilesam_encoder_xnnpack_fp32.pte` (28.3 MB) β image (1,3,1024,1024) β
|
| 16 |
+
image_embed (1,256,64,64)
|
| 17 |
+
- `mobilesam_encoder_xnnpack_int8.pte` (14.0 MB) β same, dynamically quantized,
|
| 18 |
+
corr 0.999880
|
| 19 |
+
- `mobilesam_decoder_xnnpack_fp32.pte` (20.5 MB) β (image_embed,
|
| 20 |
+
points (1,N,2) fp32 pixel coords in 1024-space, labels (1,N) fp32 1=fg/0=bg)
|
| 21 |
+
β mask logits (1,3,256,256), iou scores (1,3)
|
| 22 |
+
- `mobilesam_decoder_xnnpack_fp16.pte` (10.5 MB) β same decoder, corr 0.999825
|
| 23 |
+
|
| 24 |
+
All four take and return fp32 tensors, so any encoder file pairs with any decoder
|
| 25 |
+
file. The smallest working pair is 24.5 MB against 48.8 MB for fp32 throughout.
|
| 26 |
+
|
| 27 |
+
The encoder has no fp16 build: TinyViT does not survive half precision, and not
|
| 28 |
+
because of anything the conversion does β plain `model.half()` in eager already
|
| 29 |
+
returns corr -0.37 against the fp32 model. Dynamic int8 is the size lever here
|
| 30 |
+
instead, and it holds at corr 0.9999. The decoder has no int8 build for the opposite
|
| 31 |
+
reason: it came out at 21.8 MB, larger than its own fp32 file, because a dynamically
|
| 32 |
+
quantized transformer leaves the decoder's large constant positional embedding in
|
| 33 |
+
fp32 and adds quantization metadata on top.
|
| 34 |
+
|
| 35 |
+
MobileSAM is SAM with its ViT-H encoder replaced by TinyViT. Same prompt contract as
|
| 36 |
+
the [SAM2.1](https://huggingface.co/mlboydaisuke/SAM2.1-hiera-tiny-ExecuTorch) and
|
| 37 |
+
[EdgeTAM](https://huggingface.co/mlboydaisuke/EdgeTAM-ExecuTorch) conversions, with
|
| 38 |
+
two differences worth knowing: this decoder needs only the image embedding (no
|
| 39 |
+
high-resolution feature maps), and labels are fp32 rather than int64.
|
| 40 |
+
|
| 41 |
+
- **Source**: [ChaoningZhang/MobileSAM](https://github.com/ChaoningZhang/MobileSAM),
|
| 42 |
+
weights from [dhkim2810/MobileSAM](https://huggingface.co/dhkim2810/MobileSAM)
|
| 43 |
+
- **License**: Apache-2.0 (code) / MIT (weights)
|
| 44 |
+
- **Preprocess**: RGB, SAM norm (mean 123.675/116.28/103.53, std 58.395/57.12/57.375),
|
| 45 |
+
resize the longest side to 1024 and pad to 1024Γ1024
|
| 46 |
+
- **Postprocess**: take argmax(iou) of the 3 mask logits, threshold at > 0, upsample
|
| 47 |
+
4Γ (256β1024) back to image space, then crop the padding. The prompt encoder is
|
| 48 |
+
inside the decoder β pass raw click coordinates.
|
| 49 |
+
|
| 50 |
+
## Verification (Mac arm64, executorch 1.4.0, torch 2.13.0)
|
| 51 |
+
|
| 52 |
+
Both graphs match torch fp32 eager at corr 1.000000, and the wrappers compose back
|
| 53 |
+
to the stock modules exactly (max_abs_diff 0.000e+00).
|
| 54 |
+
|
| 55 |
+
| graph | output | shape | max_abs_diff | corr |
|
| 56 |
+
|-------|--------|-------|--------------|------|
|
| 57 |
+
| encoder | image_embed | [1, 256, 64, 64] | 3.815e-06 | 1.000000 |
|
| 58 |
+
| decoder | mask logits | [1, 3, 256, 256] | 1.717e-05 | 1.000000 |
|
| 59 |
+
| decoder | iou | [1, 3] | 1.192e-07 | 1.000000 |
|
| 60 |
+
|
| 61 |
+
Median over 10 runs, Mac arm64 single process β a relative reference, not a device
|
| 62 |
+
number: encoder 130.3 ms (torch eager 138.7 ms), decoder 20.5 ms (eager 11.2 ms).
|
| 63 |
+
XNNPACK delegate coverage: encoder 89.0%, decoder 80.6%.
|
| 64 |
+
|
| 65 |
+
## Conversion
|
| 66 |
+
|
| 67 |
+
torch.export β to_edge_transform_and_lower(XnnpackPartitioner) β .pte
|
| 68 |
+
(conversion script: [executorch-models](https://github.com/john-rocky/executorch-models))
|
| 69 |
+
|
| 70 |
+
Three rewrites were needed, and the reasons generalize to other SAM-family ports:
|
| 71 |
+
|
| 72 |
+
**The constant positional embedding is precomputed.** `TwoWayTransformer.forward`
|
| 73 |
+
opens with `image_pe.flatten(2).permute(0, 2, 1)`. That input is constant for a
|
| 74 |
+
fixed image size, and leaving the reshape in the graph corrupts the block that
|
| 75 |
+
consumes it β layer 0's keys came out at corr 0.78 against eager. Handing the
|
| 76 |
+
transformer the already-flat tensor restores corr 1.000000. This reproduces with no
|
| 77 |
+
delegate at all, and every operator involved verifies clean in isolation, so it is
|
| 78 |
+
worth knowing about rather than rediscovering.
|
| 79 |
+
|
| 80 |
+
**Boolean-mask assignment is rewritten as arithmetic.** The prompt encoder writes
|
| 81 |
+
`point_embedding[labels == -1] = 0.0` and three more masked `+=`. torch.export turns
|
| 82 |
+
each into an `index_put` behind a `nonzero`, which is a data-dependent shape. The
|
| 83 |
+
equivalent `emb * (1 - m) + m * w` form has fixed shapes and no runtime guards.
|
| 84 |
+
|
| 85 |
+
**Identity `repeat_interleave` is dropped.** The mask decoder calls
|
| 86 |
+
`torch.repeat_interleave(x, tokens.shape[0], dim=0)` with one point batch β a no-op
|
| 87 |
+
whose lowered form the delegate mis-sizes. Note this model uses the functional form,
|
| 88 |
+
not the tensor method.
|