Upload README.md with huggingface_hub
Browse files
README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
base_model: google/siglip2-base-patch16-256
|
| 4 |
+
tags:
|
| 5 |
+
- coreml
|
| 6 |
+
- siglip2
|
| 7 |
+
- siglip
|
| 8 |
+
- clip
|
| 9 |
+
- image-text
|
| 10 |
+
- embeddings
|
| 11 |
+
- vision
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# SigLIP2 base-patch16-256 β CoreML (fp16)
|
| 15 |
+
|
| 16 |
+
CoreML `.mlpackage` conversion of
|
| 17 |
+
[`google/siglip2-base-patch16-256`](https://huggingface.co/google/siglip2-base-patch16-256)
|
| 18 |
+
for on-device (macOS/Apple Silicon) text/image embedding β built for
|
| 19 |
+
[Alarian](https://alarian.ai)'s semantic frame search.
|
| 20 |
+
|
| 21 |
+
**Provenance**: converted directly from the Hugging Face `transformers`
|
| 22 |
+
reference implementation and `coremltools`, by nodevorg. Not affiliated with
|
| 23 |
+
Google. The conversion script is public:
|
| 24 |
+
[`tools/siglip2-coreml/convert.py`](https://github.com/nodevorg/studio) (in
|
| 25 |
+
the `nodevorg/studio` repo, kept uncommitted pending review β ask if you want
|
| 26 |
+
a copy). This project intentionally never looked at or referenced any
|
| 27 |
+
third-party GPLv3 conversion script; it was written from the HF
|
| 28 |
+
`transformers` SigLIP2 modeling code and `coremltools` docs only.
|
| 29 |
+
|
| 30 |
+
**License**: apache-2.0, inherited from the base model. Same terms apply
|
| 31 |
+
here β free for commercial and closed-source use.
|
| 32 |
+
|
| 33 |
+
## What's in this repo
|
| 34 |
+
|
| 35 |
+
| file | what it is |
|
| 36 |
+
|---|---|
|
| 37 |
+
| `ImageEncoder.mlpackage.zip` | image tower β 768-dim embedding |
|
| 38 |
+
| `TextEncoder.mlpackage.zip` | text tower β 768-dim embedding |
|
| 39 |
+
| `tokenizer.json` | Gemma BPE tokenizer (256k vocab), verbatim from the source repo |
|
| 40 |
+
| `tokenizer_config.json` | tokenizer config (padding_side=right, do_lower_case=true), verbatim |
|
| 41 |
+
| `special_tokens_map.json` | special tokens, verbatim |
|
| 42 |
+
|
| 43 |
+
### A note on architecture
|
| 44 |
+
|
| 45 |
+
`google/siglip2-base-patch16-256`'s own `config.json` self-describes as
|
| 46 |
+
`model_type: siglip` (not `siglip2`) β at this fixed-256-resolution, non-NaFlex
|
| 47 |
+
tier, the SigLIP2-trained weights are architecturally identical to SigLIP v1
|
| 48 |
+
(`transformers.SiglipModel`); only the weights and the tokenizer (Gemma BPE,
|
| 49 |
+
256k vocab, vs. SigLIP v1's 32k SentencePiece) differ. `AutoModel.from_pretrained`
|
| 50 |
+
resolves this checkpoint to `SiglipModel`, and that's what was converted.
|
| 51 |
+
|
| 52 |
+
## Inputs / outputs
|
| 53 |
+
|
| 54 |
+
### ImageEncoder.mlpackage
|
| 55 |
+
|
| 56 |
+
- **Input**: `pixel_values` β a CoreML `ImageType`, RGB, 256Γ256. The exact
|
| 57 |
+
SigLIP preprocessing (`SiglipImageProcessor` defaults for this checkpoint:
|
| 58 |
+
`rescale_factor=1/255`, `image_mean=[.5,.5,.5]`, `image_std=[.5,.5,.5]`) is
|
| 59 |
+
**baked into the model** as `scale=1/127.5, bias=[-1,-1,-1]` β i.e. the
|
| 60 |
+
model itself maps raw RGB `[0,255]` to `[-1, 1]`. Just resize/squash your
|
| 61 |
+
image to 256Γ256 and hand it the raw RGB pixels; no separate normalization
|
| 62 |
+
step needed on the caller side. (Aspect-ratio squash, not center-crop β the
|
| 63 |
+
model saw squashed images in training.)
|
| 64 |
+
- **Output**: `image_embedding` β 768-dim float16, **already L2-normalized**.
|
| 65 |
+
|
| 66 |
+
### TextEncoder.mlpackage
|
| 67 |
+
|
| 68 |
+
- **Input**: `input_ids` β 1Γ64 **int32** token ids. No `attention_mask`
|
| 69 |
+
input: SigLIP's text tower trains with unmasked bidirectional attention
|
| 70 |
+
over the full fixed-length 64-token sequence and pools the hidden state at
|
| 71 |
+
the *last* sequence position (`SiglipTextModel.forward`, not an EOS-aware
|
| 72 |
+
pool) β passing a mask would deviate from training. Tokenize with the
|
| 73 |
+
included `tokenizer.json`/`tokenizer_config.json` using
|
| 74 |
+
`padding="max_length", max_length=64`, and **lowercase the input text**
|
| 75 |
+
(`do_lower_case=True` in the tokenizer config; the tokenizer will also do
|
| 76 |
+
this itself if you feed it through its normalizer, but be aware if you
|
| 77 |
+
hand-roll token ids).
|
| 78 |
+
- **Output**: `text_embedding` β 768-dim float16, **already L2-normalized**.
|
| 79 |
+
|
| 80 |
+
Both encoders normalize their pooled output inside the graph, so cosine
|
| 81 |
+
similarity between an image and text embedding is just a dot product.
|
| 82 |
+
|
| 83 |
+
## Conversion details
|
| 84 |
+
|
| 85 |
+
- `minimum_deployment_target`: macOS 15
|
| 86 |
+
- `compute_precision`: `ct.precision.FLOAT16`
|
| 87 |
+
- `convert_to`: `mlprogram`
|
| 88 |
+
- Traced via `torch.jit.trace` on thin wrapper modules around
|
| 89 |
+
`SiglipModel.vision_model` / `.text_model` (not the full contrastive model β
|
| 90 |
+
each `.mlpackage` is just the one branch).
|
| 91 |
+
- Pinned toolchain (see the sha256 table below for exact artifact hashes):
|
| 92 |
+
`torch==2.7.0`, `transformers==5.14.1`, `coremltools==9.0`, `numpy==2.3.5`.
|
| 93 |
+
**Gotcha**: `numpy>=2.4.0` breaks `coremltools==9.0`'s PyTorch frontend with
|
| 94 |
+
`TypeError: only 0-dimensional arrays can be converted to Python scalars`
|
| 95 |
+
on any model using `torch.nn.MultiheadAttention` (SigLIP's vision pooling
|
| 96 |
+
head hits this) β confirmed via
|
| 97 |
+
[apple/coremltools#2633](https://github.com/apple/coremltools/issues/2633);
|
| 98 |
+
pin `numpy<2.4.0` until a coremltools release ships the fix from
|
| 99 |
+
[#2632](https://github.com/apple/coremltools/pull/2632).
|
| 100 |
+
|
| 101 |
+
## Parity (PyTorch fp32 reference vs. converted CoreML fp16, cosine similarity)
|
| 102 |
+
|
| 103 |
+
4 test images (solid red / green / blue squares + a synthetic sunset
|
| 104 |
+
gradient) Γ 4 test strings, L2-normalized before comparison. Threshold: > 0.99
|
| 105 |
+
per item.
|
| 106 |
+
|
| 107 |
+
| item | cosine(pytorch, coreml) |
|
| 108 |
+
|---|---|
|
| 109 |
+
| image: solid_red | 0.9999988 |
|
| 110 |
+
| image: solid_green | 0.9999992 |
|
| 111 |
+
| image: solid_blue | 0.9999992 |
|
| 112 |
+
| image: gradient_sky | 0.9999987 |
|
| 113 |
+
| text: "a red square" | 0.9999995 |
|
| 114 |
+
| text: "a photo of a sunset over the ocean" | 0.9999997 |
|
| 115 |
+
| text: "a solid blue color" | 0.9999995 |
|
| 116 |
+
| text: "a green field" | 0.9999996 |
|
| 117 |
+
|
| 118 |
+
**Worst per-item cosine similarity: 0.9999987** β well above the 0.99
|
| 119 |
+
threshold.
|
| 120 |
+
|
| 121 |
+
Textβimage ranking also matches exactly between PyTorch and CoreML across
|
| 122 |
+
all 4Γ4 pairs (e.g. "a red square" ranks `solid_red` highest in both
|
| 123 |
+
backends; "a photo of a sunset over the ocean" ranks `gradient_sky` highest
|
| 124 |
+
in both).
|
| 125 |
+
|
| 126 |
+
## sha256
|
| 127 |
+
|
| 128 |
+
| file | sha256 |
|
| 129 |
+
|---|---|
|
| 130 |
+
| `ImageEncoder.mlpackage.zip` | `406938456c8f8e91f01632cb74131fdfb993064d8d0bc8f6fbbd142709f1ba66` |
|
| 131 |
+
| `TextEncoder.mlpackage.zip` | `4f31f38a1729a0044f0ffc95a940dc1a6a2fdcd5fcf78a03cbb662bada6cbafb` |
|
| 132 |
+
| `tokenizer.json` | `cb9140fae3ac5122c972d37adf83e1248471a38147ad76f8215c8872c6fd8322` |
|
| 133 |
+
| `tokenizer_config.json` | `14afe629fe4959b9e0d51e1852b8d9f7ad074f90a1a7125a4fcdd17f06e78fc8` |
|
| 134 |
+
| `special_tokens_map.json` | `baec30ea10906f16adb8c18af7a34023002c1746542612b8b41c9f09e1351351` |
|
| 135 |
+
|
| 136 |
+
## File sizes
|
| 137 |
+
|
| 138 |
+
| file | size |
|
| 139 |
+
|---|---|
|
| 140 |
+
| `ImageEncoder.mlpackage.zip` | 163 MiB |
|
| 141 |
+
| `TextEncoder.mlpackage.zip` | 506 MiB |
|
| 142 |
+
| `tokenizer.json` | 33 MiB |
|
| 143 |
+
|
| 144 |
+
The text encoder is large mostly because of the Gemma tokenizer's 256k-entry
|
| 145 |
+
vocabulary embedding table (256000 Γ 768 Γ 2 bytes β 375 MiB alone, fp16).
|