mlboydaisuke commited on
Commit
b94530c
Β·
verified Β·
1 Parent(s): 66757d3

Upload README.md with huggingface_hub

Browse files
Files changed (1) hide show
  1. README.md +102 -0
README.md ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ tags:
4
+ - executorch
5
+ - xnnpack
6
+ - pte
7
+ - on-device
8
+ - feature-extraction
9
+ - sentence-similarity
10
+ base_model:
11
+ - nomic-ai/nomic-embed-text-v1.5
12
+ ---
13
+ # nomic-embed-text-v1.5 β€” ExecuTorch
14
+
15
+ A BERT with rotary embeddings and SwiGLU, trained so that a prefix of the vector is still a usable vector. Text in, one
16
+ 768-dimensional vector out, for search and retrieval that never leaves the
17
+ device.
18
+
19
+ - **Source**: nomic-ai/nomic-embed-text-v1.5 β€” 12 layers, 768 dimensions, 30,528 vocabulary
20
+ - **License**: apache-2.0
21
+ - **Input**: `input_ids` and `attention_mask`, both `[1, 256]` int64
22
+ - **Output**: `[1, 768]`, mean-pooled and **not** 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
+ **does not normalise**, 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 `search_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* | worst cosine vs eager | retrieval budget |
42
+ |---|---|---|---|---|---|
43
+ | fp32 | `embed_nomic_embed_text_v15_xnnpack_fp32.pte` | 547.2 | 52.0 | 1.000000 | 0% |
44
+ | fp16 | `embed_nomic_embed_text_v15_xnnpack_fp16.pte` | 273.9 | 124.5 | 0.999999 | 8% |
45
+ | Core ML (fp16, iOS) | `embed_nomic_embed_text_v15_coreml_all.pte` | 274.8 | 8.2 | 0.999795 | 44% |
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
+ 41.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
+ ## Matryoshka: the vector truncates
58
+
59
+ This model is trained so that a **prefix** of the vector is still a usable vector β€”
60
+ 768 down to 512, 256, 128 or 64 dimensions, trading accuracy for index size. The
61
+ graph returns the full 768, because the dimension is the caller's choice, and the
62
+ truncation recipe is three lines:
63
+
64
+ ```python
65
+ import torch.nn.functional as F
66
+ v = F.layer_norm(v, (v.shape[1],)) # before truncating, not after
67
+ v = v[:, :dim] # 512 / 256 / 128 / 64
68
+ v = F.normalize(v, p=2, dim=1)
69
+ ```
70
+
71
+ **The `layer_norm` is what makes the prefix usable, and it is easy to skip.** At the
72
+ full 768 it barely matters β€” measured on this shelf, going through the layer_norm
73
+ changes the direction of the vector by a cosine of 0.999944 and leaves the test pair's
74
+ score at 0.8233 either way. It earns its place only once you truncate.
75
+
76
+ **The four prefixes are a real part of the model.** `search_document: ` for what goes
77
+ in the index, `search_query: ` for what is asked of it, plus `classification: ` and
78
+ `clustering: `. Unlike E5's symmetric mode, the two retrieval prefixes are not
79
+ interchangeable.
80
+
81
+ **The architecture is not stock BERT.** Rotary embeddings and a SwiGLU MLP, with the
82
+ modelling code in `nomic-ai/nomic-bert-2048` rather than in transformers β€” loading it
83
+ needs `trust_remote_code=True` and `einops` installed. None of that reaches the `.pte`,
84
+ which is a plain graph once exported.
85
+
86
+ ## Not shipped: int8
87
+
88
+ `embed_nomic_embed_text_v15_xnnpack_int8.pte` is **208.0 MB** β€” smaller than fp16's 273.9 MB, because the token embedding
89
+ table is only 94 MB of the 547.2 MB model (17%), leaving most of the
90
+ weight in linears for int8 to shrink.
91
+
92
+ It is withheld on the number that decides. Ranking the eight test sentences against
93
+ each other, this build moves a pair score by at most **0.0368** while the
94
+ closest fp32 decision β€” the gap between the document a query retrieves and the
95
+ runner-up β€” is **0.0077**. That is **480%** of
96
+ the room available, against a bar of 50%.
97
+
98
+ Correlation reads 0.992203 for this build, which no correlation gate
99
+ would stop.
100
+
101
+ torch.export -> to_edge_transform_and_lower(partitioner) -> .pte
102
+ (conversion scripts: [executorch-models](https://github.com/john-rocky/executorch-models))