samisuteria commited on
Commit
bd33bcf
Β·
verified Β·
1 Parent(s): 5afd328

Upload README.md with huggingface_hub

Browse files
Files changed (1) hide show
  1. README.md +145 -0
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).