Upload README.md with huggingface_hub
Browse files
README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- executorch
|
| 5 |
+
- xnnpack
|
| 6 |
+
- pte
|
| 7 |
+
- on-device
|
| 8 |
+
- image-text-to-text
|
| 9 |
+
- image-to-text
|
| 10 |
+
- object-detection
|
| 11 |
+
base_model:
|
| 12 |
+
- microsoft/Florence-2-base
|
| 13 |
+
---
|
| 14 |
+
# Florence-2-base β ExecuTorch (vision + text encoder + text decoder)
|
| 15 |
+
|
| 16 |
+
One set of weights that captions, detects, reads text and grounds phrases, with the
|
| 17 |
+
task chosen by the prompt you send. 0.23 B parameters. Three `.pte` files, split the
|
| 18 |
+
way Whisper is on this shelf and for the same reason: the vision tower and the text
|
| 19 |
+
encoder run once per image, the decoder runs once per generated token.
|
| 20 |
+
|
| 21 |
+
```
|
| 22 |
+
vision : pixel_values (1,3,768,768) -> image_features (1,577,768)
|
| 23 |
+
encoder: (image_features, input_ids (1,32), mask (1,32)) -> hidden (1,609,768)
|
| 24 |
+
decoder: (hidden, mask (1,32), decoder_input_ids (1,128)) -> logits (1,128,51328)
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
Every file takes and returns fp32 tensors, so a precision is a file swap, and the
|
| 28 |
+
three parts can be mixed β an fp16 vision tower pairs with an fp32 encoder.
|
| 29 |
+
|
| 30 |
+
| part | build | file | MB | corr vs fp32 eager | Mac ms* |
|
| 31 |
+
|---|---|---|---|---|---|
|
| 32 |
+
| vision | XNNPACK fp32 | `florence2_base_vision_xnnpack_fp32.pte` | 365.7 | 1.000000 | 360.0 |
|
| 33 |
+
| vision | XNNPACK int8 | `florence2_base_vision_xnnpack_int8.pte` | **112.5** | 0.998901 | 334.7 |
|
| 34 |
+
| vision | XNNPACK fp16 | `florence2_base_vision_xnnpack_fp16.pte` | 196.2 | 0.999946 | 1034.5 |
|
| 35 |
+
| vision | Core ML (iOS) | `florence2_base_vision_coreml_all.pte` | 185.1 | 0.998304 | 59.7 |
|
| 36 |
+
| encoder | XNNPACK fp32 | `florence2_base_encoder_xnnpack_fp32.pte` | 331.0 | 1.000000 | 51.0 |
|
| 37 |
+
| encoder | XNNPACK int8 | `florence2_base_encoder_xnnpack_int8.pte` | **203.8** | 0.999534 | 44.3 |
|
| 38 |
+
| encoder | Core ML (iOS) | `florence2_base_encoder_coreml_all.pte` | 165.1 | 0.999993 | 13.9 |
|
| 39 |
+
| decoder | XNNPACK fp32 | `florence2_base_decoder_xnnpack_fp32.pte` | 545.5 | 1.000000 | 35.7 |
|
| 40 |
+
| decoder | XNNPACK int8 | `florence2_base_decoder_xnnpack_int8.pte` | **257.9** | 0.999470 | 33.1 |
|
| 41 |
+
| decoder | Core ML (iOS) | `florence2_base_decoder_coreml_all.pte` | 193.0 | 0.999995 | 4.7 |
|
| 42 |
+
|
| 43 |
+
Three sets: **1242 MB** all-fp32, **574 MB** all-int8, **543 MB** Core ML. The int8
|
| 44 |
+
set is the one to reach for on Android β it is smaller than fp32 by more than half
|
| 45 |
+
and no slower, and it returns the same captions (below). The int8 recipe is dynamic
|
| 46 |
+
quantisation, which reaches the linear layers and leaves the 51328Γ768 token
|
| 47 |
+
embedding table in fp32; that table is 158 MB of the decoder's file, which is why the
|
| 48 |
+
decoder halves rather than quarters.
|
| 49 |
+
|
| 50 |
+
\*Mac arm64, single process, median of 10 β a reference point for relative cost, not
|
| 51 |
+
a device number. Torch eager fp32 on the same machine: vision 466.0 ms, encoder
|
| 52 |
+
30.8 ms, decoder 23.5 ms.
|
| 53 |
+
|
| 54 |
+
A caption of *n* tokens costs one vision pass, one encoder pass and *n* decoder
|
| 55 |
+
passes: about 0.95 s for a 15-token caption on the fp32 XNNPACK set, 0.88 s on the
|
| 56 |
+
int8 set, 0.14 s on Core ML, on this Mac. The decoder graph is a fixed 128-token
|
| 57 |
+
window, so every step costs the same whether it is the first token or the fiftieth.
|
| 58 |
+
|
| 59 |
+
## Running it
|
| 60 |
+
|
| 61 |
+
**1. The image.** RGB, divide by 255, ImageNet normalise (mean .485/.456/.406, std
|
| 62 |
+
.229/.224/.225), bicubic resize to 768Γ768. No crop. Run the vision `.pte`.
|
| 63 |
+
|
| 64 |
+
**2. The prompt.** Florence-2's task tokens are shorthand the processor expands into
|
| 65 |
+
a sentence before tokenising β the model never sees `<CAPTION>`:
|
| 66 |
+
|
| 67 |
+
| task | the sentence that is actually tokenised |
|
| 68 |
+
|---|---|
|
| 69 |
+
| `<CAPTION>` | What does the image describe? |
|
| 70 |
+
| `<DETAILED_CAPTION>` | Describe in detail what is shown in the image. |
|
| 71 |
+
| `<MORE_DETAILED_CAPTION>` | Describe with a paragraph what is shown in the image. |
|
| 72 |
+
| `<OD>` | Locate the objects with category name in the image. |
|
| 73 |
+
| `<DENSE_REGION_CAPTION>` | Locate the objects in the image, with their descriptions. |
|
| 74 |
+
| `<REGION_PROPOSAL>` | Locate the region proposals in the image. |
|
| 75 |
+
| `<OCR>` | What is the text in the image? |
|
| 76 |
+
| `<OCR_WITH_REGION>` | What is the text in the image, with regions? |
|
| 77 |
+
| `<CAPTION_TO_PHRASE_GROUNDING>` | Locate the phrases in the caption: {your caption} |
|
| 78 |
+
| `<OPEN_VOCABULARY_DETECTION>` | Locate {your phrase} in the image. |
|
| 79 |
+
|
| 80 |
+
Tokenise that sentence as `<s> sentence </s>` with the repo's tokenizer, right-pad to
|
| 81 |
+
32 with the pad id (1), and build an `attention_mask` that is 1 on the real tokens and
|
| 82 |
+
0 on the padding. Run the encoder `.pte` with `(image_features, input_ids, mask)`.
|
| 83 |
+
|
| 84 |
+
The 577 image tokens are handled inside the graphs. The prompt sequence the original
|
| 85 |
+
model sees is `<image>Γ577 + <s> prompt </s>`, and because the image tokens are a
|
| 86 |
+
contiguous prefix, the encoder here concatenates the vision features in front of the
|
| 87 |
+
text embeddings instead of scattering them into placeholder positions. Same
|
| 88 |
+
arithmetic, no data-dependent mask, and nothing for the caller to line up.
|
| 89 |
+
|
| 90 |
+
**3. Greedy decoding.** Fill a `(1,128)` int64 window with the pad id, write the
|
| 91 |
+
decoder start token (2) at position 0, then for step `t = 0, 1, 2, ...`:
|
| 92 |
+
|
| 93 |
+
```
|
| 94 |
+
logits = decoder(hidden, mask, window) # mask is the same one the encoder took
|
| 95 |
+
next = argmax(logits[0, t])
|
| 96 |
+
if next == 2: stop # </s>
|
| 97 |
+
window[0, t + 1] = next
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
Detokenise the collected ids with the repo's tokenizer. There is no KV cache: the
|
| 101 |
+
decoder is a plain static graph over the window, which is what makes it a single
|
| 102 |
+
`.pte` with no state to carry between calls.
|
| 103 |
+
|
| 104 |
+
**4. Reading a detection answer.** Grounded tasks answer with `<loc_N>` tokens,
|
| 105 |
+
`N` in 0..999. Four in a row are a box, and each coordinate is `(N + 0.5) Γ side /
|
| 106 |
+
1000` in the original image's pixels β `side` being the image's width for x and its
|
| 107 |
+
height for y, not 768. That is the same arithmetic the processor's own parser does.
|
| 108 |
+
|
| 109 |
+
## Verification
|
| 110 |
+
|
| 111 |
+
The three wrappers reproduce `Florence2ForConditionalGeneration` **exactly**:
|
| 112 |
+
composition `max_abs_diff` 0.000e+00 against the full model's logits on the same
|
| 113 |
+
image and prompt.
|
| 114 |
+
|
| 115 |
+
End to end through the three `.pte` files, greedy `<CAPTION>` on five photographs
|
| 116 |
+
against the same decoding in eager PyTorch: **5/5 captions identical**, character for
|
| 117 |
+
character. Correlation is measured per part in the table above; the caption test is
|
| 118 |
+
what says the split, the prompt layout and the decode agree.
|
| 119 |
+
|
| 120 |
+
```
|
| 121 |
+
A black and white photo of a person playing a piano.
|
| 122 |
+
A wooden walkway leading to the ocean on a sunny day.
|
| 123 |
+
A group of dead trees in a forest under a cloudy sky.
|
| 124 |
+
A long empty road in the middle of a forest.
|
| 125 |
+
A couple of wooden benches sitting on top of a field of leaves.
|
| 126 |
+
```
|
| 127 |
+
|
| 128 |
+
The **int8 set answers with the same five sentences**, character for character, with
|
| 129 |
+
all three parts quantised at once β which is how they would be used together.
|
| 130 |
+
|
| 131 |
+
```bash
|
| 132 |
+
python convert/check_florence2.py fp32 # or int8
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
## Not shipped, and why
|
| 136 |
+
|
| 137 |
+
**fp16 for the encoder and the decoder does not export.** BART clamps its activations
|
| 138 |
+
when, and only when, they are half precision:
|
| 139 |
+
|
| 140 |
+
```python
|
| 141 |
+
if hidden_states.dtype == torch.float16 and not torch.isfinite(hidden_states).all():
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
In fp32 that line short-circuits and never reaches the graph. Halve the model and it
|
| 145 |
+
becomes a question about values `torch.export` cannot answer, and export stops with
|
| 146 |
+
`GuardOnDataDependentSymNode`. The clamp could be shimmed away, but then the shipped
|
| 147 |
+
graph would lack the overflow guard the original has, which is not a trade worth
|
| 148 |
+
making silently. Nothing is lost by it: int8 is both smaller and faster than fp16
|
| 149 |
+
here, and the Core ML builds compute in fp16 internally anyway, holding at
|
| 150 |
+
correlation 0.999993 and 0.999995.
|
| 151 |
+
|
| 152 |
+
**The fp16 vision tower ships and is probably not what you want.** It passes both
|
| 153 |
+
gates β 196.2 MB at correlation 0.999946 β but the int8 tower is 112.5 MB and three
|
| 154 |
+
times faster (334.7 ms against 1034.5 ms), because XNNPACK has no fp16 kernels here
|
| 155 |
+
and inserts casts instead. It is in the repo for anyone who needs more fidelity than
|
| 156 |
+
int8 without carrying the fp32 file.
|
| 157 |
+
|
| 158 |
+
## Conversion notes
|
| 159 |
+
|
| 160 |
+
Converted from
|
| 161 |
+
[florence-community/Florence-2-base](https://huggingface.co/florence-community/Florence-2-base),
|
| 162 |
+
the transformers-format mirror of
|
| 163 |
+
[microsoft/Florence-2-base](https://huggingface.co/microsoft/Florence-2-base) β same
|
| 164 |
+
MIT weights. The original repo predates the in-tree implementation and its weight
|
| 165 |
+
names do not match it: loading it into `Florence2ForConditionalGeneration` prints a
|
| 166 |
+
load report where every key is unexpected and hands back a randomly initialised model
|
| 167 |
+
without raising.
|
| 168 |
+
|
| 169 |
+
The checkpoint declares `torch_dtype: float16` and transformers honours it, so
|
| 170 |
+
`from_pretrained` must be given `dtype=torch.float32` explicitly. Without it the
|
| 171 |
+
"fp32" build serialises fp16 weights β 196 MB for a 90 M parameter tower β and the
|
| 172 |
+
lowered vision graph returns NaN.
|
| 173 |
+
|
| 174 |
+
- **Source**: microsoft/Florence-2-base (via florence-community mirror)
|
| 175 |
+
- **License**: MIT
|
| 176 |
+
|
| 177 |
+
torch.export -> to_edge_transform_and_lower(partitioner) -> .pte
|
| 178 |
+
(conversion scripts: [executorch-models](https://github.com/john-rocky/executorch-models))
|