mlboydaisuke commited on
Commit
7864796
·
verified ·
1 Parent(s): 0e0af3b

Upload README.md with huggingface_hub

Browse files
Files changed (1) hide show
  1. README.md +70 -83
README.md CHANGED
@@ -12,98 +12,85 @@ base_model:
12
  ---
13
  # multilingual-e5-large — ExecuTorch
14
 
15
- Multilingual sentence embeddings: text in any of about a hundred languages in, one
16
- 1024-dimensional vector out, comparable across languages. For search and retrieval that
17
- never leaves the device.
18
 
19
- - **Source**: intfloat/multilingual-e5-large — 560M parameters, 24 XLM-RoBERTa layers,
20
- 250k vocabulary, 1024-dimensional output
21
- - **License**: MIT
22
  - **Input**: `input_ids` and `attention_mask`, both `[1, 256]` int64
23
- - **Output**: `[1, 1024]`, mean-pooled over the mask and L2-normalised inside the graph
24
 
25
- ## Two parts of the recipe, and only one of them is in the graph
26
 
27
- **The pooling is in.** sentence-transformers keeps it per model, and the six embedding
28
- models on this shelf do not agree:
 
 
 
29
 
30
- | | pooling | normalised |
31
- |---|---|---|
32
- | all-MiniLM-L6-v2 | mean | yes |
33
- | all-MiniLM-L12-v2 | mean | yes |
34
- | bge-small-en-v1.5 | **CLS** | yes |
35
- | multilingual-e5-base | mean | yes |
36
- | **multilingual-e5-large** | **mean** | **yes** |
37
- | paraphrase-multilingual-L12 | mean | **no** |
38
 
39
- Read off `modules.json` and `1_Pooling/config.json` rather than assumed from the family
40
- name paraphrase-multilingual is the same architecture family with the same pooling, and
41
- it does *not* normalise.
42
-
43
- **The prefix is not.** E5 is trained with `"query: "` in front of a search query and
44
- `"passage: "` in front of a document, and it expects them at inference:
45
-
46
- ```
47
- query: query: how do I keep data on the phone?
48
- passage: passage: On-device inference keeps the data on the phone.
49
- ```
50
-
51
- That is text, so it happens before tokenisation and the `.pte` never sees it as anything
52
- but tokens. Leaving it out does not throw and does not look wrong — it returns a
53
- plausible vector that retrieves worse. The conversion repo's checker applies it, so the
54
- numbers below are for the recipe as the model intends it.
55
 
56
  ## Verification
57
 
58
- | build | file | size | latency | worst cosine vs eager |
59
- |---|---|---|---|---|
60
- | XNNPACK fp32 | `embed_multilingual_e5_large_xnnpack_fp32.pte` | 2235.7 MB | 106.5 ms | 1.000000 |
61
- | XNNPACK fp16 | `embed_multilingual_e5_large_xnnpack_fp16.pte` | 1118.3 MB | 221.2 ms | 0.999999 |
62
- | Core ML fp32 | `embed_multilingual_e5_large_coreml_all.pte` | 1119.3 MB | **27.2 ms** | 0.999993 |
63
-
64
- Mac arm64, median of 10, one 256-token sequence — a reference point for relative cost,
65
- not a device number. Eager fp32 on the same input is 101.1 ms. Cosine is measured against
66
- the model run in eager through its own pooling, over eight sentences.
67
-
68
- **And that the vectors are useful**, which agreement alone cannot show. A paraphrase
69
- against an unrelated sentence, and then the same test across languages
70
- "機械学習のモデルを端末の上で動かす" against "On-device inference keeps the data on the
71
- phone":
72
-
73
- ```
74
- same language: 0.852 same meaning vs 0.661 unrelated
75
- across languages: 0.833 same meaning vs 0.739 unrelated
76
- ```
77
-
78
- The cross-lingual row is what this model is for, and it is the row the English-only
79
- models on this shelf fail. Note the scale: E5 puts everything high, so 0.70 for an
80
- unrelated pair is normal and the **gap** is what carries the signal, not the absolute
81
- number.
82
-
83
- ```bash
84
- python convert/check_embed.py multilingual_e5_large fp32 # or fp16, int8, coreml
85
- ```
86
-
87
- ## Not shipped
88
-
89
- **int8** converts and holds worst cosine 0.997435, and it still separates the pairs
90
- but it comes out at **1330.7 MB against fp16's 1118.3 MB**. Dynamic int8 quantises the
91
- linear weights and leaves the token embedding table alone, and with a 250k vocabulary at
92
- 1024 dimensions that table is **1024 MB of the 2235.7 MB model**, 46% of it.
93
-
94
- The base model is the same story with a different ratio, and the pair is worth reading
95
- together: its table is 69% of the file and int8 lands 54% above fp16; here the table is
96
- 46% and int8 lands 19% above. The depth doubled and the vocabulary did not. This shelf's
97
- rule of thumb int8 beats fp16 only when the embedding table is under about a third of
98
- the weights holds at both points, and large is closer to the crossing.
99
-
100
- ## Worth knowing about the speed
101
-
102
- XNNPACK fp32 is level with PyTorch eager here (106.5 ms against 101.1), and fp16 is
103
- twice as slow while halving the file XNNPACK has no fp16 kernels for this graph and
104
- inserts casts instead. Core ML is the one that pays: **27.2 ms**, roughly four times
105
- eager, 100% delegated in a single subgraph. On iOS take the Core ML build; on Android the choice is
106
- fp32 for speed or fp16 for half the disk.
 
 
 
 
107
 
108
  torch.export -> to_edge_transform_and_lower(partitioner) -> .pte
109
  (conversion scripts: [executorch-models](https://github.com/john-rocky/executorch-models))
 
12
  ---
13
  # multilingual-e5-large — ExecuTorch
14
 
15
+ Multilingual E5 at 24 layers. Text in, one
16
+ 1024-dimensional vector out, for search and retrieval that never leaves the
17
+ device.
18
 
19
+ - **Source**: intfloat/multilingual-e5-large — 24 layers, 1024 dimensions, 250,002 vocabulary
20
+ - **License**: mit
 
21
  - **Input**: `input_ids` and `attention_mask`, both `[1, 256]` int64
22
+ - **Output**: `[1, 1024]`, mean-pooled and L2-normalised inside the graph
23
 
24
+ ## The recipe is in the graph, and it was read off this repo
25
 
26
+ sentence-transformers stores it per model, and the shelf's seven embedding models do
27
+ not agree. This one pools **mean** and
28
+ **normalises**, read from
29
+ `1_Pooling/config.json` and `modules.json` rather than inferred from the family name.
30
+ Getting it wrong does not throw; it returns vectors that look fine and rank wrong.
31
 
32
+ ## The prefix is not in the graph
 
 
 
 
 
 
 
33
 
34
+ This model is trained with `query: ` in front of the text and expects it at
35
+ inference. That happens before tokenisation, so the `.pte` never sees it as anything
36
+ but tokens — and leaving it out does not throw. It returns a plausible vector that
37
+ retrieves worse.
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  ## Verification
40
 
41
+ | build | file | size (MB) | Mac ms* | backend takes | worst cosine vs eager | retrieval budget |
42
+ |---|---|---|---|---|---|---|
43
+ | fp32 | `embed_multilingual_e5_large_xnnpack_fp32.pte` | 2235.7 | 103.6 | 80.7% | 1.000000 | 0% |
44
+ | fp16 | `embed_multilingual_e5_large_xnnpack_fp16.pte` | 1118.2 | 145.5 | 69.6% | 0.999999 | 6% |
45
+ | Core ML (fp16, iOS) | `embed_multilingual_e5_large_coreml_all.pte` | 1119.5 | 28.1 | 100.0% | 0.999992 | 40% |
46
+
47
+ \*Mac arm64, median of 10, one 256-token sequence — a reference point for relative
48
+ cost, not a device number. Torch eager fp32 on the same machine is
49
+ 135.6 ms.
50
+
51
+ Cosine is measured against the model run in eager through its own pooling, over eight
52
+ sentences. The last column is the one that decides: rank those eight against each
53
+ other, and ask whether this build's score error is smaller than the gap between the
54
+ document a query retrieves and the runner-up. Every shipped build keeps all eight
55
+ top-1 results.
56
+
57
+ ## The attention is eager, and that is the faster export
58
+
59
+ `F.scaled_dot_product_attention` does not survive export as one operation. The edge
60
+ dialect lowers it through `_safe_softmax`, whose guard against a row with no unmasked
61
+ key at all leaves **eleven operations XNNPACK cannot take, in every attention block**
62
+ `scalar_tensor`, `where`, `mul.Scalar`, `logical_not`, `eq`, `full_like`, `any.dim`.
63
+ Each one cuts the subgraph in two. The count is exact and does not vary by family:
64
+ measured across this shelf, from a 4-layer cross-encoder to a 28-layer causal reranker,
65
+ it is 11 per block every time.
66
+
67
+ The guard is emitted whether or not it can ever fire, and here it cannot. It triggers
68
+ only on `-inf`, which reaches the graph only because the sdpa path hands `F.sdpa` a
69
+ **boolean** mask for PyTorch to fill; `attn_implementation="eager"` masks with
70
+ `torch.finfo(dtype).min`, a large finite number, and never produces one. So the two
71
+ arms differ only on rows that have no unmasked key — sdpa zeroes them, eager gives them
72
+ a uniform rowand those are padding rows, which the pooling discards and every real
73
+ query row masks out. Measured with all but eight positions masked, as adversarial as
74
+ this shape gets, the two graphs agree to 1.4e-07.
75
+
76
+ XNNPACK fp32 goes from **63.8% to 80.7%** delegated.
77
+
78
+ ## Not shipped: int8
79
+
80
+ `embed_multilingual_e5_large_xnnpack_int8.pte` is **1330.6 MB** against fp16's 1118.2 MB. Dynamic int8 quantises the
81
+ linear weights and leaves the token embedding table in fp32, and here that table is
82
+ 1024 MB of the 2235.7 MB model — **46%**. The size a build comes out at is
83
+ `0.5 + 1.5 x (table share)` times the fp16 build; at 46% that is
84
+ 1.19, so there was never a smaller file to be had.
85
+
86
+ It is withheld on the number that decides. Ranking the eight test sentences against
87
+ each other, this build moves a pair score by at most **0.0083** while the
88
+ closest fp32 decision the gap between the document a query retrieves and the
89
+ runner-up is **0.0014**. That is **590%** of
90
+ the room available, against a bar of 50%.
91
+
92
+ Correlation reads 0.997579 for this build, which no correlation gate
93
+ would stop.
94
 
95
  torch.export -> to_edge_transform_and_lower(partitioner) -> .pte
96
  (conversion scripts: [executorch-models](https://github.com/john-rocky/executorch-models))