sentence-transformers
Safetensors
English
nomic_bert
flash-attention
code-retrieval
nomic-bert
bf16
custom_code
Instructions to use handwoven8588/CodeRankEmbed-flash-attn with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- sentence-transformers
How to use handwoven8588/CodeRankEmbed-flash-attn with sentence-transformers:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("handwoven8588/CodeRankEmbed-flash-attn", trust_remote_code=True) sentences = [ "The weather is lovely today.", "It's so sunny outside!", "He drove to the stadium." ] embeddings = model.encode(sentences) similarities = model.similarity(embeddings, embeddings) print(similarities.shape) # [3, 3] - Notebooks
- Google Colab
- Kaggle
Padding-free encoder, flattening opt-in, torch.compile recipe; card measured on public corpora
Browse files- README.md +194 -62
- modeling_hf_nomic_bert.py +178 -5
README.md
CHANGED
|
@@ -65,6 +65,17 @@ file ships all three paths itself, so no runtime patching or post-load hooks are
|
|
| 65 |
override=...` INFO log line (one line per distinct `(impl, device)`). When `flash_attn` is
|
| 66 |
unavailable the line names why (`flash_attn=absent(<error>)`); an installed `flash_attn` that
|
| 67 |
fails to import also logs a WARNING.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
- **`revision=` pins everything.** Loading with `revision=<commit>` fetches the code, config,
|
| 69 |
tokenizer **and weights** from that commit.
|
| 70 |
- **Loads bf16 by default.** `flash_attn` and `torch_varlen` both require half precision and the
|
|
@@ -93,73 +104,188 @@ q = model.encode(queries, normalize_embeddings=True)
|
|
| 93 |
d = model.encode(codes, normalize_embeddings=True)
|
| 94 |
```
|
| 95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
## Parity & performance
|
| 97 |
|
| 98 |
The weights are the original CodeRankEmbed weights (bf16-cast), so embeddings match the fp32
|
| 99 |
original to within bf16 precision. The table below measures every tier **on the same GPU**, so
|
| 100 |
`eager` is the O(seq²) baseline the varlen tiers are compared against.
|
| 101 |
|
| 102 |
-
**Protocol.**
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
|
| 124 |
-
|
|
| 125 |
-
| RTX
|
| 126 |
-
| RTX
|
| 127 |
-
| RTX
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
Cosines rounded to 6 decimal places, VRAM to the nearest MiB. `torch 2.12.1+cu130`,
|
| 130 |
`transformers 5.11.0`, `sentence-transformers 6.1.0`, `flash-attn 2.8.3` (`2.8.3.post1` on the
|
| 131 |
-
3090 Ti)
|
| 132 |
|
| 133 |
-
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
|
| 136 |
**Real documents at the model defaults.** 768 Python files sampled with a fixed seed from one shard
|
| 137 |
of [`HuggingFaceTB/stack-edu`](https://huggingface.co/datasets/HuggingFaceTB/stack-edu) (730,431
|
| 138 |
tokens after truncation), encoded with sentence-transformers at this model's default
|
| 139 |
-
`max_seq_length` of 8192
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
| 147 |
-
|
|
| 148 |
-
| RTX 3090 Ti (sm_86) | 4 | `
|
| 149 |
-
| RTX 3090 Ti (sm_86) | 32 | `torch_varlen` |
|
| 150 |
-
| RTX 3090 Ti (sm_86) | 64 | `torch_varlen` |
|
| 151 |
-
| RTX 3090 Ti (sm_86) |
|
| 152 |
-
| RTX
|
| 153 |
-
| RTX
|
| 154 |
-
| RTX
|
| 155 |
-
| RTX
|
| 156 |
-
| RTX
|
| 157 |
-
| RTX
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
## What changed vs the source repo
|
| 165 |
|
|
@@ -178,19 +304,25 @@ cosine > 0.997 against the fp32 `nomic-ai/CodeRankEmbed` reference.
|
|
| 178 |
builds that have it installed), and `eager` (the original padded attention algorithm, numerically
|
| 179 |
unchanged, and the default off CUDA sm_80+; it now converts a raw 2-D `[B, S]` mask to the
|
| 180 |
additive form before adding it, for the case where a `device_map` split hands it a mask a varlen
|
| 181 |
-
tier upstream never passes).
|
| 182 |
-
helpers (a replacement for `flash_attn.bert_padding`)
|
| 183 |
-
the
|
| 184 |
-
the
|
| 185 |
-
the
|
| 186 |
-
|
| 187 |
-
`NOMIC_BERT_ATTN_IMPL=torch_varlen|flash_attn|eager`
|
| 188 |
-
`model[0].auto_model.attention_impl` and the
|
|
|
|
| 189 |
4. **`from_pretrained` forwards the Hub revision**: the upstream custom `from_pretrained` fetched
|
| 190 |
the weight file without the caller's `revision` (and cache/token options), so a pinned load
|
| 191 |
still took the weights from the default branch. The copy here passes them through, so a
|
| 192 |
pinned load fetches the weights from the pinned commit too. It also accepts transformers'
|
| 193 |
-
`dtype=` alongside the older `torch_dtype=`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
|
| 195 |
## License & attribution
|
| 196 |
|
|
|
|
| 65 |
override=...` INFO log line (one line per distinct `(impl, device)`). When `flash_attn` is
|
| 66 |
unavailable the line names why (`flash_attn=absent(<error>)`); an installed `flash_attn` that
|
| 67 |
fails to import also logs a WARNING.
|
| 68 |
+
- **Padding-free on the varlen tiers.** The model unpads the batch once, right after the input
|
| 69 |
+
ids, runs the embeddings and every encoder layer on the real tokens only, and pads back once at
|
| 70 |
+
the end. `last_hidden_state` keeps its `[B, S, 768]` shape, with zeros at padding positions, so
|
| 71 |
+
pooling is unchanged.
|
| 72 |
+
- **Flattened batches (opt-in).** Loading with
|
| 73 |
+
`model_kwargs={"attn_implementation": "flash_attention_2"}` lets sentence-transformers send each
|
| 74 |
+
batch flattened (`[1, ΣL]` token ids with sequence boundaries, no padding at all), and the model
|
| 75 |
+
returns one `[1, ΣL, 768]` output that sentence-transformers pools per sequence. The opt-in needs
|
| 76 |
+
the `flash_attn` package installed, because transformers checks for it at load and raises
|
| 77 |
+
otherwise, even though the attention itself still runs on whichever tier the dispatch picks. On
|
| 78 |
+
the `eager` tier the flattened batch is re-padded and runs the padded path.
|
| 79 |
- **`revision=` pins everything.** Loading with `revision=<commit>` fetches the code, config,
|
| 80 |
tokenizer **and weights** from that commit.
|
| 81 |
- **Loads bf16 by default.** `flash_attn` and `torch_varlen` both require half precision and the
|
|
|
|
| 104 |
d = model.encode(codes, normalize_embeddings=True)
|
| 105 |
```
|
| 106 |
|
| 107 |
+
With `flash_attn` installed, opt in to flattened batches:
|
| 108 |
+
|
| 109 |
+
```python
|
| 110 |
+
model = SentenceTransformer(
|
| 111 |
+
"handwoven8588/CodeRankEmbed-flash-attn",
|
| 112 |
+
trust_remote_code=True,
|
| 113 |
+
model_kwargs={"attn_implementation": "flash_attention_2"},
|
| 114 |
+
)
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
On a varlen tier the encoder layers run on packed tokens only, so they compile cleanly. For about
|
| 118 |
+
1.2× the throughput and a third less peak memory (see [torch.compile](#torchcompile) below):
|
| 119 |
+
|
| 120 |
+
```python
|
| 121 |
+
import torch
|
| 122 |
+
|
| 123 |
+
model[0].auto_model.encoder = torch.compile(model[0].auto_model.encoder, dynamic=True)
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
`dynamic=True` matters: the number of packed tokens changes with every batch.
|
| 127 |
+
|
| 128 |
## Parity & performance
|
| 129 |
|
| 130 |
The weights are the original CodeRankEmbed weights (bf16-cast), so embeddings match the fp32
|
| 131 |
original to within bf16 precision. The table below measures every tier **on the same GPU**, so
|
| 132 |
`eager` is the O(seq²) baseline the varlen tiers are compared against.
|
| 133 |
|
| 134 |
+
**Protocol.** The first 16,384 `document` fields (real Python functions) of
|
| 135 |
+
[`lightonai/cornstack`](https://huggingface.co/datasets/lightonai/cornstack)'s Python split, in
|
| 136 |
+
shard order (`train-00000-of-00682.parquet`, then `train-00001-…`): 2,684,878 tokens, 7 to 4,746
|
| 137 |
+
per function, 164 mean, 92 median. They are encoded with sentence-transformers in one `encode()`
|
| 138 |
+
call per row at the stated `batch_size=` (64 to 4,096 batches per row), under the default load
|
| 139 |
+
and under the flattened opt-in. Each row encodes once untimed, then again for the measurement.
|
| 140 |
+
Cosine similarity is taken on fp32-renormalized output against one encode by the **pre-dispatch
|
| 141 |
+
revision of this repo (`e361c6f`) on the same GPU at batch 32**, which runs the original
|
| 142 |
+
`flash_attn` path on padded batches. `torch_varlen` is a different, torch-native kernel and
|
| 143 |
+
`eager` a different algorithm, so their cosines measure those differences. `flash_attn` is the
|
| 144 |
+
same attention kernel; where it sits below 1.000000, the rest of the network runs on packed
|
| 145 |
+
tokens or on differently composed batches, so its matrix shapes, and with them its bf16
|
| 146 |
+
rounding, differ. `auto`, the default with no override, kept `torch_varlen` on both cards and
|
| 147 |
+
matches its rows. **Encode peak** is the encode's peak CUDA allocation above what was allocated
|
| 148 |
+
before it (the loaded model's ~266 MiB of bf16 weights). Wall time covers all 16,384 functions.
|
| 149 |
+
`eager` runs at batch 4 only: padded attention needs `batch × heads × seq²`, which at this
|
| 150 |
+
corpus's 4,746-token maximum is about 17 GB of attention scores at batch 32. For comparison, the
|
| 151 |
+
pre-dispatch revision at batch 32 peaks at 4,456 MiB and takes 23.8 s (3090 Ti) and 21.7 s
|
| 152 |
+
(5090 Laptop).
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
| GPU | load | batch size | tier | mean cos | min cos | encode peak | wall |
|
| 156 |
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
| 157 |
+
| RTX 3090 Ti (sm_86) | default | 4 | `eager` | 0.999923 | 0.999404 | 4,321 MiB | 41.791 s |
|
| 158 |
+
| RTX 3090 Ti (sm_86) | default | 32 | `torch_varlen` | 0.999948 | 0.999403 | 2,511 MiB | 16.327 s |
|
| 159 |
+
| RTX 3090 Ti (sm_86) | default | 32 | `flash_attn` | 0.999974 | 0.999814 | 2,511 MiB | 16.330 s |
|
| 160 |
+
| RTX 3090 Ti (sm_86) | default | 256 | `torch_varlen` | 0.999949 | 0.999403 | 11,135 MiB | 15.027 s |
|
| 161 |
+
| RTX 3090 Ti (sm_86) | default | 256 | `flash_attn` | 0.999979 | 0.999776 | 11,136 MiB | 15.056 s |
|
| 162 |
+
| RTX 3090 Ti (sm_86) | default | 1,024 | `torch_varlen` | out of memory | | | |
|
| 163 |
+
| RTX 3090 Ti (sm_86) | default | 1,024 | `flash_attn` | out of memory | | | |
|
| 164 |
+
| RTX 3090 Ti (sm_86) | default | 2,048 | `torch_varlen` | out of memory | | | |
|
| 165 |
+
| RTX 3090 Ti (sm_86) | default | 2,048 | `flash_attn` | out of memory | | | |
|
| 166 |
+
| RTX 3090 Ti (sm_86) | flattened | 4 | `eager` | 0.999923 | 0.999404 | 4,329 MiB | 51.793 s |
|
| 167 |
+
| RTX 3090 Ti (sm_86) | flattened | 32 | `torch_varlen` | 0.999948 | 0.999403 | 1,468 MiB | 15.529 s |
|
| 168 |
+
| RTX 3090 Ti (sm_86) | flattened | 32 | `flash_attn` | 0.999967 | 0.999776 | 1,466 MiB | 15.496 s |
|
| 169 |
+
| RTX 3090 Ti (sm_86) | flattened | 256 | `torch_varlen` | 0.999949 | 0.999403 | 7,073 MiB | 13.622 s |
|
| 170 |
+
| RTX 3090 Ti (sm_86) | flattened | 256 | `flash_attn` | 0.999978 | 0.999776 | 7,072 MiB | 13.641 s |
|
| 171 |
+
| RTX 3090 Ti (sm_86) | flattened | 1,024 | `torch_varlen` | 0.999949 | 0.999403 | 17,411 MiB | 13.371 s |
|
| 172 |
+
| RTX 3090 Ti (sm_86) | flattened | 1,024 | `flash_attn` | 0.999978 | 0.999776 | 17,412 MiB | 13.478 s |
|
| 173 |
+
| RTX 3090 Ti (sm_86) | flattened | 2,048 | `torch_varlen` | out of memory | | | |
|
| 174 |
+
| RTX 3090 Ti (sm_86) | flattened | 2,048 | `flash_attn` | out of memory | | | |
|
| 175 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 4 | `eager` | 0.999925 | 0.999721 | 4,321 MiB | 33.589 s |
|
| 176 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 32 | `torch_varlen` | 0.999949 | 0.999748 | 2,511 MiB | 14.391 s |
|
| 177 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 32 | `flash_attn` | 0.999976 | 0.999766 | 2,511 MiB | 14.423 s |
|
| 178 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 256 | `torch_varlen` | 0.999951 | 0.999748 | 11,134 MiB | 15.171 s |
|
| 179 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 256 | `flash_attn` | 0.999988 | 0.999811 | 11,136 MiB | 15.155 s |
|
| 180 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 1,024 | `torch_varlen` | out of memory | | | |
|
| 181 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 1,024 | `flash_attn` | out of memory | | | |
|
| 182 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 2,048 | `torch_varlen` | out of memory | | | |
|
| 183 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 2,048 | `flash_attn` | out of memory | | | |
|
| 184 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 4 | `eager` | 0.999924 | 0.998160 | 4,329 MiB | 44.613 s |
|
| 185 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 32 | `torch_varlen` | 0.999950 | 0.999748 | 1,468 MiB | 13.699 s |
|
| 186 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 32 | `flash_attn` | 0.999985 | 0.999811 | 1,466 MiB | 13.758 s |
|
| 187 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 256 | `torch_varlen` | 0.999951 | 0.999748 | 7,072 MiB | 14.402 s |
|
| 188 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 256 | `flash_attn` | 0.999988 | 0.999811 | 7,072 MiB | 14.478 s |
|
| 189 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 1,024 | `torch_varlen` | 0.999951 | 0.999748 | 17,412 MiB | 14.739 s |
|
| 190 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 1,024 | `flash_attn` | 0.999988 | 0.999811 | 17,412 MiB | 14.783 s |
|
| 191 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 2,048 | `torch_varlen` | out of memory | | | |
|
| 192 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 2,048 | `flash_attn` | out of memory | | | |
|
| 193 |
|
| 194 |
Cosines rounded to 6 decimal places, VRAM to the nearest MiB. `torch 2.12.1+cu130`,
|
| 195 |
`transformers 5.11.0`, `sentence-transformers 6.1.0`, `flash-attn 2.8.3` (`2.8.3.post1` on the
|
| 196 |
+
3090 Ti).
|
| 197 |
|
| 198 |
+
Memory tracks the real tokens in the heaviest batch, not the number of functions: both
|
| 199 |
+
loads run the encoder padding-free, and each out-of-memory row failed allocating one 3,072-wide
|
| 200 |
+
MLP activation over exactly that batch's tokens. sentence-transformers length-sorts the corpus,
|
| 201 |
+
so under the default load the first batch of 1,024 gathers the longest functions, 869,948 tokens,
|
| 202 |
+
which does not fit on 24 GB. When it flattens, it interleaves longest with shortest to even out
|
| 203 |
+
batches, so its heaviest batch of 1,024 holds 593,716 tokens and fits at 17.4 GB; at 2,048 even
|
| 204 |
+
that reaches 889,626 tokens and does not. Up to that limit, flattening cuts encode memory by
|
| 205 |
+
36–42% and wall time by 5–9% against the default load. The interleaving
|
| 206 |
+
also changes which texts share a padded `eager` batch, which is why the flattened `eager` row's
|
| 207 |
+
minimum differs on the 5090 Laptop: its lowest text is a 2,166-token function padded next to a
|
| 208 |
+
2,195-token one (the same text alone scores 0.999936).
|
| 209 |
|
| 210 |
**Real documents at the model defaults.** 768 Python files sampled with a fixed seed from one shard
|
| 211 |
of [`HuggingFaceTB/stack-edu`](https://huggingface.co/datasets/HuggingFaceTB/stack-edu) (730,431
|
| 212 |
tokens after truncation), encoded with sentence-transformers at this model's default
|
| 213 |
+
`max_seq_length` of 8192, under the default load and under the flattened opt-in. **Sorted** is
|
| 214 |
+
one `encode()` call over the whole corpus, so sentence-transformers length-sorts the files before
|
| 215 |
+
batching; **file** is one `encode()` call per batch of consecutive files, so each batch mixes short
|
| 216 |
+
and long files. Wall time covers the whole corpus; peak VRAM is the peak CUDA allocation, weights
|
| 217 |
+
included. The largest sorted batch on each card is the largest batch size that fit (the 5090
|
| 218 |
+
Laptop was sharing about 0.4 GiB with another process).
|
| 219 |
+
|
| 220 |
+
| GPU | load | order | batch size | tier | wall (768 files) | peak VRAM |
|
| 221 |
+
| --- | --- | --- | --- | --- | --- | --- |
|
| 222 |
+
| RTX 3090 Ti (sm_86) | default | sorted | 4 | `torch_varlen` | 5.61 s | 1,296 MiB |
|
| 223 |
+
| RTX 3090 Ti (sm_86) | default | sorted | 32 | `torch_varlen` | 4.79 s | 6,186 MiB |
|
| 224 |
+
| RTX 3090 Ti (sm_86) | default | sorted | 64 | `torch_varlen` | 4.80 s | 9,253 MiB |
|
| 225 |
+
| RTX 3090 Ti (sm_86) | default | sorted | 353 | `torch_varlen` | 5.77 s | 18,637 MiB |
|
| 226 |
+
| RTX 3090 Ti (sm_86) | default | sorted | 32 | `flash_attn` | 4.79 s | 6,187 MiB |
|
| 227 |
+
| RTX 3090 Ti (sm_86) | default | file | 4 | `torch_varlen` | 6.49 s | 636 MiB |
|
| 228 |
+
| RTX 3090 Ti (sm_86) | default | file | 32 | `torch_varlen` | 6.39 s | 1,614 MiB |
|
| 229 |
+
| RTX 3090 Ti (sm_86) | default | file | 64 | `torch_varlen` | 6.44 s | 2,801 MiB |
|
| 230 |
+
| RTX 3090 Ti (sm_86) | flattened | sorted | 4 | `torch_varlen` | 5.51 s | 792 MiB |
|
| 231 |
+
| RTX 3090 Ti (sm_86) | flattened | sorted | 32 | `torch_varlen` | 4.54 s | 3,843 MiB |
|
| 232 |
+
| RTX 3090 Ti (sm_86) | flattened | sorted | 64 | `torch_varlen` | 4.47 s | 6,237 MiB |
|
| 233 |
+
| RTX 3090 Ti (sm_86) | flattened | sorted | 628 | `torch_varlen` | 4.60 s | 19,658 MiB |
|
| 234 |
+
| RTX 3090 Ti (sm_86) | flattened | sorted | 32 | `flash_attn` | 4.54 s | 3,844 MiB |
|
| 235 |
+
| RTX 3090 Ti (sm_86) | flattened | file | 4 | `torch_varlen` | 5.83 s | 634 MiB |
|
| 236 |
+
| RTX 3090 Ti (sm_86) | flattened | file | 32 | `torch_varlen` | 4.73 s | 1,607 MiB |
|
| 237 |
+
| RTX 3090 Ti (sm_86) | flattened | file | 64 | `torch_varlen` | 4.53 s | 2,787 MiB |
|
| 238 |
+
| RTX 5090 Laptop GPU (sm_120) | default | sorted | 4 | `torch_varlen` | 4.75 s | 1,319 MiB |
|
| 239 |
+
| RTX 5090 Laptop GPU (sm_120) | default | sorted | 32 | `torch_varlen` | 4.72 s | 6,209 MiB |
|
| 240 |
+
| RTX 5090 Laptop GPU (sm_120) | default | sorted | 64 | `torch_varlen` | 4.84 s | 9,276 MiB |
|
| 241 |
+
| RTX 5090 Laptop GPU (sm_120) | default | sorted | 353 | `torch_varlen` | 5.98 s | 18,661 MiB |
|
| 242 |
+
| RTX 5090 Laptop GPU (sm_120) | default | sorted | 32 | `flash_attn` | 4.71 s | 6,210 MiB |
|
| 243 |
+
| RTX 5090 Laptop GPU (sm_120) | default | file | 4 | `torch_varlen` | 5.64 s | 660 MiB |
|
| 244 |
+
| RTX 5090 Laptop GPU (sm_120) | default | file | 32 | `torch_varlen` | 5.70 s | 1,636 MiB |
|
| 245 |
+
| RTX 5090 Laptop GPU (sm_120) | default | file | 64 | `torch_varlen` | 5.84 s | 2,823 MiB |
|
| 246 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | sorted | 4 | `torch_varlen` | 4.56 s | 816 MiB |
|
| 247 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | sorted | 32 | `torch_varlen` | 4.56 s | 3,866 MiB |
|
| 248 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | sorted | 64 | `torch_varlen` | 4.63 s | 6,260 MiB |
|
| 249 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | sorted | 628 | `torch_varlen` | 4.75 s | 19,681 MiB |
|
| 250 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | sorted | 32 | `flash_attn` | 4.57 s | 3,867 MiB |
|
| 251 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | file | 4 | `torch_varlen` | 4.75 s | 658 MiB |
|
| 252 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | file | 32 | `torch_varlen` | 4.70 s | 1,630 MiB |
|
| 253 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | file | 64 | `torch_varlen` | 4.66 s | 2,811 MiB |
|
| 254 |
+
|
| 255 |
+
Flattening keeps peak memory well below the default load at every sorted batch size and fits a
|
| 256 |
+
batch of 628 on 24 GB, where the default load fits 353. It also makes a text's embedding
|
| 257 |
+
independent of the other texts in its batch: between the file-order and sorted runs, the minimum
|
| 258 |
+
cosine over the 768 files is 0.9999998 on both cards and both varlen tiers under the flattened
|
| 259 |
+
load, against as low as 0.99987 under the default load.
|
| 260 |
+
|
| 261 |
+
### torch.compile
|
| 262 |
+
|
| 263 |
+
The parity table's 16,384 functions on `torch_varlen`, with the encoder layers eager and compiled
|
| 264 |
+
(`torch.compile(model[0].auto_model.encoder, dynamic=True)`). Cosine is against the same
|
| 265 |
+
pre-dispatch encode as the parity table.
|
| 266 |
+
|
| 267 |
+
| GPU | load | batch size | eager | compiled | compiled min cos (mean) |
|
| 268 |
+
| --- | --- | --- | --- | --- | --- |
|
| 269 |
+
| RTX 3090 Ti (sm_86) | default | 32 | 16.38 s, 2,511 MiB | 14.05 s, 1,635 MiB | 0.998156 (0.999943) |
|
| 270 |
+
| RTX 3090 Ti (sm_86) | default | 256 | 15.06 s, 11,135 MiB | 12.74 s, 7,249 MiB | 0.998156 (0.999943) |
|
| 271 |
+
| RTX 3090 Ti (sm_86) | default | 1,024 | out of memory | 13.81 s, 16,701 MiB | 0.998156 (0.999943) |
|
| 272 |
+
| RTX 3090 Ti (sm_86) | flattened | 32 | 15.58 s, 1,468 MiB | 13.16 s, 956 MiB | 0.998156 (0.999943) |
|
| 273 |
+
| RTX 3090 Ti (sm_86) | flattened | 256 | 13.67 s, 7,073 MiB | 11.37 s, 4,602 MiB | 0.998156 (0.999943) |
|
| 274 |
+
| RTX 3090 Ti (sm_86) | flattened | 1,024 | 13.40 s, 17,411 MiB | 10.90 s, 11,325 MiB | 0.998156 (0.999943) |
|
| 275 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 32 | 14.33 s, 2,511 MiB | 12.19 s, 1,637 MiB | 0.998657 (0.999944) |
|
| 276 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 256 | 15.08 s, 11,134 MiB | 12.30 s, 7,249 MiB | 0.998657 (0.999944) |
|
| 277 |
+
| RTX 5090 Laptop GPU (sm_120) | default | 1,024 | out of memory | 13.39 s, 16,701 MiB | 0.999250 (0.999944) |
|
| 278 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 32 | 13.67 s, 1,468 MiB | 11.72 s, 956 MiB | 0.999250 (0.999944) |
|
| 279 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 256 | 14.35 s, 7,072 MiB | 11.66 s, 4,601 MiB | 0.999250 (0.999944) |
|
| 280 |
+
| RTX 5090 Laptop GPU (sm_120) | flattened | 1,024 | 14.67 s, 17,412 MiB | 11.80 s, 11,326 MiB | 0.999250 (0.999944) |
|
| 281 |
+
|
| 282 |
+
Compiling makes the encode 1.17–1.24× faster and cuts its peak memory by about a third, because
|
| 283 |
+
inductor fuses the element-wise work around each attention call; the default load's batch of 1,024
|
| 284 |
+
then fits. It compiles one graph with no graph breaks, once per process (the first encode took about
|
| 285 |
+
4 s longer on the 5090 Laptop and 10 s on the 3090 Ti), and does not recompile as batch lengths
|
| 286 |
+
change. The one further compile comes once a batch passes about 699,000 tokens, where inductor
|
| 287 |
+
switches to 64-bit indexing. The mean cosine is unchanged; a few texts move by bf16 reassociation,
|
| 288 |
+
to a minimum of 0.998156 against 0.999403 eager on the 3090 Ti.
|
| 289 |
|
| 290 |
## What changed vs the source repo
|
| 291 |
|
|
|
|
| 304 |
builds that have it installed), and `eager` (the original padded attention algorithm, numerically
|
| 305 |
unchanged, and the default off CUDA sm_80+; it now converts a raw 2-D `[B, S]` mask to the
|
| 306 |
additive form before adding it, for the case where a `device_map` split hands it a mask a varlen
|
| 307 |
+
tier upstream never passes). On both varlen tiers `NomicBertModel.forward` unpads the batch once
|
| 308 |
+
with torch-native `_unpad`/`_pad` helpers (a replacement for `flash_attn.bert_padding`), keeps
|
| 309 |
+
the hidden states packed `[ΣL, 768]` through the embeddings and every layer, and pads back once at
|
| 310 |
+
the end. Rotary embeddings rotate each packed token by its position within its own sequence,
|
| 311 |
+
the same rotation it gets in the padded batch. On the eager tier `NomicBertModel.forward` builds
|
| 312 |
+
the additive attention mask inline instead of calling the (now-removed-upstream)
|
| 313 |
+
`get_extended_attention_mask` helper. Set `NOMIC_BERT_ATTN_IMPL=torch_varlen|flash_attn|eager`
|
| 314 |
+
to force a tier (raises if it can't engage); `model[0].auto_model.attention_impl` and the
|
| 315 |
+
one-time INFO log line report which tier engaged.
|
| 316 |
4. **`from_pretrained` forwards the Hub revision**: the upstream custom `from_pretrained` fetched
|
| 317 |
the weight file without the caller's `revision` (and cache/token options), so a pinned load
|
| 318 |
still took the weights from the default branch. The copy here passes them through, so a
|
| 319 |
pinned load fetches the weights from the pinned commit too. It also accepts transformers'
|
| 320 |
+
`dtype=` alongside the older `torch_dtype=`, and returns the model in eval mode, as
|
| 321 |
+
transformers' own `from_pretrained` does.
|
| 322 |
+
5. **Flattened input**: `NomicBertModel.forward` also accepts sentence-transformers' flattened
|
| 323 |
+
batches (`input_ids [1, ΣL]` with per-sequence `position_ids` and `cu_seq_lens_q`), and the
|
| 324 |
+
model declares flash-attention support so that sentence-transformers sends them when loaded
|
| 325 |
+
with `attn_implementation="flash_attention_2"` (see Behavior).
|
| 326 |
|
| 327 |
## License & attribution
|
| 328 |
|
modeling_hf_nomic_bert.py
CHANGED
|
@@ -12,7 +12,7 @@ import os
|
|
| 12 |
import re
|
| 13 |
from collections import OrderedDict
|
| 14 |
from functools import partial
|
| 15 |
-
from typing import List, Optional, Tuple, Union
|
| 16 |
|
| 17 |
import torch
|
| 18 |
import torch.nn as nn
|
|
@@ -236,6 +236,21 @@ def _pad(x_u: torch.Tensor, indices: torch.Tensor, B: int, S: int) -> torch.Tens
|
|
| 236 |
return out.view(B, S, *x_u.shape[1:])
|
| 237 |
|
| 238 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
def select_attention_impl(device: torch.device) -> str:
|
| 240 |
"""The attention tier for ``device`` under the current ``NOMIC_BERT_ATTN_IMPL``.
|
| 241 |
|
|
@@ -541,6 +556,10 @@ class NomicBertPreTrainedModel(PreTrainedModel):
|
|
| 541 |
supports_gradient_checkpointing = True
|
| 542 |
_no_split_modules = ["Block"]
|
| 543 |
_skip_keys_device_placement = "past_key_values"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 544 |
|
| 545 |
def __init__(self, config, *inputs, **kwargs):
|
| 546 |
super().__init__(config)
|
|
@@ -598,6 +617,9 @@ class NomicBertPreTrainedModel(PreTrainedModel):
|
|
| 598 |
if rotary_scaling_factor:
|
| 599 |
config.rotary_scaling_factor = rotary_scaling_factor
|
| 600 |
|
|
|
|
|
|
|
|
|
|
| 601 |
if config.n_positions <= 0 and config.rotary_emb_fraction > 0:
|
| 602 |
config.n_positions = 2048
|
| 603 |
if num_labels:
|
|
@@ -663,6 +685,9 @@ class NomicBertPreTrainedModel(PreTrainedModel):
|
|
| 663 |
logger.warning(load_return)
|
| 664 |
else:
|
| 665 |
logger.debug(load_return)
|
|
|
|
|
|
|
|
|
|
| 666 |
return model
|
| 667 |
|
| 668 |
def _set_gradient_checkpointing(self, module, value=False):
|
|
@@ -929,6 +954,29 @@ class NomicBertRotaryEmbedding(nn.Module):
|
|
| 929 |
return torch.stack((q_rot, k_rot, qkv[:, :, 2]), dim=2)
|
| 930 |
|
| 931 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 932 |
class NomicBertDynamicNTKRotaryEmbedding(NomicBertRotaryEmbedding):
|
| 933 |
def __init__(self, rotary_scaling_factor, max_position_embeddings, **kwargs):
|
| 934 |
super().__init__(**kwargs)
|
|
@@ -1069,8 +1117,12 @@ class NomicBertAttention(nn.Module):
|
|
| 1069 |
is_padded_inputs: Optional[bool] = True,
|
| 1070 |
cu_seqlens: Optional[torch.Tensor] = None,
|
| 1071 |
max_seq_len: Optional[int] = None,
|
|
|
|
| 1072 |
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
|
| 1073 |
|
|
|
|
|
|
|
|
|
|
| 1074 |
has_layer_past = past_key_value is not None
|
| 1075 |
|
| 1076 |
if has_layer_past:
|
|
@@ -1172,6 +1224,23 @@ class NomicBertAttention(nn.Module):
|
|
| 1172 |
|
| 1173 |
return attn_output
|
| 1174 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1175 |
|
| 1176 |
class NomicBertBlock(nn.Module):
|
| 1177 |
def __init__(
|
|
@@ -1225,6 +1294,7 @@ class NomicBertBlock(nn.Module):
|
|
| 1225 |
use_cache: Optional[bool] = False,
|
| 1226 |
cu_seqlens: Optional[torch.Tensor] = None,
|
| 1227 |
max_seq_len: Optional[int] = None,
|
|
|
|
| 1228 |
):
|
| 1229 |
r"""Pass the input through the encoder layer.
|
| 1230 |
Args:
|
|
@@ -1244,6 +1314,7 @@ class NomicBertBlock(nn.Module):
|
|
| 1244 |
is_padded_inputs=is_padded_inputs,
|
| 1245 |
cu_seqlens=cu_seqlens,
|
| 1246 |
max_seq_len=max_seq_len,
|
|
|
|
| 1247 |
)
|
| 1248 |
|
| 1249 |
dropped = self.dropout2(hidden_states)
|
|
@@ -1260,6 +1331,7 @@ class NomicBertBlock(nn.Module):
|
|
| 1260 |
is_padded_inputs=is_padded_inputs,
|
| 1261 |
cu_seqlens=cu_seqlens,
|
| 1262 |
max_seq_len=max_seq_len,
|
|
|
|
| 1263 |
)
|
| 1264 |
hidden_states = self.norm1((self.dropout1(attn_outputs) + hidden_states).to(dtype=self.norm1.weight.dtype))
|
| 1265 |
mlp_out = self.mlp(hidden_states)
|
|
@@ -1287,6 +1359,7 @@ class NomicBertEncoder(nn.Module):
|
|
| 1287 |
output_hidden_states: Optional[bool] = None,
|
| 1288 |
return_dict: Optional[bool] = None,
|
| 1289 |
is_padded_inputs: Optional[bool] = True,
|
|
|
|
| 1290 |
):
|
| 1291 |
"""If subset_mask is not None, we only want output for the subset of the sequence.
|
| 1292 |
This means that we only compute the last layer output for these tokens.
|
|
@@ -1314,6 +1387,7 @@ class NomicBertEncoder(nn.Module):
|
|
| 1314 |
None,
|
| 1315 |
None,
|
| 1316 |
is_padded_inputs,
|
|
|
|
| 1317 |
# if you freeze ANY layers, you need `use_reentrant=False`
|
| 1318 |
# https://github.com/huggingface/transformers/issues/21381
|
| 1319 |
# https://discuss.pytorch.org/t/checkpoint-with-no-grad-requiring-inputs-problem/19117/7
|
|
@@ -1331,6 +1405,7 @@ class NomicBertEncoder(nn.Module):
|
|
| 1331 |
is_padded_inputs,
|
| 1332 |
output_attentions,
|
| 1333 |
use_cache,
|
|
|
|
| 1334 |
)
|
| 1335 |
return hidden_states
|
| 1336 |
|
|
@@ -1427,16 +1502,26 @@ class NomicBertModel(NomicBertPreTrainedModel):
|
|
| 1427 |
token_type_ids=None,
|
| 1428 |
position_ids=None,
|
| 1429 |
return_dict=None,
|
|
|
|
|
|
|
|
|
|
| 1430 |
):
|
| 1431 |
if token_type_ids is None:
|
| 1432 |
token_type_ids = torch.zeros_like(input_ids)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1433 |
hidden_states = self.embeddings(input_ids, position_ids=position_ids, token_type_ids=token_type_ids)
|
| 1434 |
hidden_states = self.emb_ln(hidden_states)
|
| 1435 |
hidden_states = self.emb_drop(hidden_states)
|
| 1436 |
-
|
| 1437 |
-
impl = select_attention_impl(hidden_states.device)
|
| 1438 |
-
self.attention_impl = impl
|
| 1439 |
-
_log_attention_impl_once(impl, hidden_states.device)
|
| 1440 |
if impl == "eager":
|
| 1441 |
# No varlen tier engaged (CPU, pre-Ampere, forced, or no kernel):
|
| 1442 |
# build the additive [B, 1, 1, S] mask inline for the eager block.
|
|
@@ -1454,6 +1539,94 @@ class NomicBertModel(NomicBertPreTrainedModel):
|
|
| 1454 |
pooler_output=pooled_output,
|
| 1455 |
)
|
| 1456 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1457 |
|
| 1458 |
class NomicBertForPreTraining(NomicBertPreTrainedModel):
|
| 1459 |
_tied_weights_keys = ["predictions.decoder.bias", "cls.predictions.decoder.weight"]
|
|
|
|
| 12 |
import re
|
| 13 |
from collections import OrderedDict
|
| 14 |
from functools import partial
|
| 15 |
+
from typing import NamedTuple, List, Optional, Tuple, Union
|
| 16 |
|
| 17 |
import torch
|
| 18 |
import torch.nn as nn
|
|
|
|
| 236 |
return out.view(B, S, *x_u.shape[1:])
|
| 237 |
|
| 238 |
|
| 239 |
+
class _Packed(NamedTuple):
|
| 240 |
+
"""Metadata for hidden states kept packed ``[N, D]`` across the encoder.
|
| 241 |
+
|
| 242 |
+
``cu_seqlens`` / ``max_seqlen`` feed the varlen kernel; ``positions`` (int64,
|
| 243 |
+
``[N]``) is each token's column in the padded batch, the position RoPE uses on
|
| 244 |
+
the dense path; ``rope_seqlen`` is the padded width ``S``, the length the dense
|
| 245 |
+
path sizes the RoPE cache to.
|
| 246 |
+
"""
|
| 247 |
+
|
| 248 |
+
cu_seqlens: torch.Tensor
|
| 249 |
+
max_seqlen: int
|
| 250 |
+
positions: torch.Tensor
|
| 251 |
+
rope_seqlen: int
|
| 252 |
+
|
| 253 |
+
|
| 254 |
def select_attention_impl(device: torch.device) -> str:
|
| 255 |
"""The attention tier for ``device`` under the current ``NOMIC_BERT_ATTN_IMPL``.
|
| 256 |
|
|
|
|
| 556 |
supports_gradient_checkpointing = True
|
| 557 |
_no_split_modules = ["Block"]
|
| 558 |
_skip_keys_device_placement = "past_key_values"
|
| 559 |
+
# Pre-packed input (cu_seq_lens_q / position_ids through forward's **kwargs) runs on
|
| 560 |
+
# either varlen tier, so the model can accept a flattened batch.
|
| 561 |
+
_supports_attention_backend = True
|
| 562 |
+
_supports_flash_attn = True
|
| 563 |
|
| 564 |
def __init__(self, config, *inputs, **kwargs):
|
| 565 |
super().__init__(config)
|
|
|
|
| 617 |
if rotary_scaling_factor:
|
| 618 |
config.rotary_scaling_factor = rotary_scaling_factor
|
| 619 |
|
| 620 |
+
if kwargs.get("attn_implementation") == "flash_attention_2":
|
| 621 |
+
# Opt-in: advertises flash attention so sentence-transformers flattens batches.
|
| 622 |
+
config._attn_implementation = "flash_attention_2"
|
| 623 |
if config.n_positions <= 0 and config.rotary_emb_fraction > 0:
|
| 624 |
config.n_positions = 2048
|
| 625 |
if num_labels:
|
|
|
|
| 685 |
logger.warning(load_return)
|
| 686 |
else:
|
| 687 |
logger.debug(load_return)
|
| 688 |
+
# transformers' native from_pretrained returns the model in eval mode; this
|
| 689 |
+
# override must too, or a direct forward runs the config's dropout.
|
| 690 |
+
model.eval()
|
| 691 |
return model
|
| 692 |
|
| 693 |
def _set_gradient_checkpointing(self, module, value=False):
|
|
|
|
| 954 |
return torch.stack((q_rot, k_rot, qkv[:, :, 2]), dim=2)
|
| 955 |
|
| 956 |
|
| 957 |
+
def _rotary_packed(rotary: "NomicBertRotaryEmbedding", qkv_u: torch.Tensor, packed: _Packed) -> torch.Tensor:
|
| 958 |
+
"""RoPE on packed ``[N, 3, H, D]``: each token rotated by its own column position.
|
| 959 |
+
|
| 960 |
+
Same arithmetic as ``NomicBertRotaryEmbedding.forward`` on the dense
|
| 961 |
+
``[B, S, 3, H, D]`` tensor, so every real token gets bit-identical q/k. The caller
|
| 962 |
+
sizes the cache first (``NomicBertModel._size_rotary_caches``), outside the encoder:
|
| 963 |
+
the update branches on the module's cached length, which ``torch.compile`` guards as
|
| 964 |
+
a static int, so updating it here would recompile a compiled encoder on every growth.
|
| 965 |
+
"""
|
| 966 |
+
pattern = "... d -> ... 1 (2 d)" if not rotary.interleaved else "... d -> ... 1 (d 2)"
|
| 967 |
+
cos = repeat(rotary._cos_cached[packed.positions], pattern)
|
| 968 |
+
sin = repeat(rotary._sin_cached[packed.positions], pattern)
|
| 969 |
+
ro_dim = cos.shape[-1]
|
| 970 |
+
|
| 971 |
+
def rot(x):
|
| 972 |
+
return torch.cat(
|
| 973 |
+
[x[..., :ro_dim] * cos + rotate_half(x[..., :ro_dim], rotary.interleaved) * sin, x[..., ro_dim:]],
|
| 974 |
+
dim=-1,
|
| 975 |
+
)
|
| 976 |
+
|
| 977 |
+
return torch.stack((rot(qkv_u[:, 0]), rot(qkv_u[:, 1]), qkv_u[:, 2]), dim=1)
|
| 978 |
+
|
| 979 |
+
|
| 980 |
class NomicBertDynamicNTKRotaryEmbedding(NomicBertRotaryEmbedding):
|
| 981 |
def __init__(self, rotary_scaling_factor, max_position_embeddings, **kwargs):
|
| 982 |
super().__init__(**kwargs)
|
|
|
|
| 1117 |
is_padded_inputs: Optional[bool] = True,
|
| 1118 |
cu_seqlens: Optional[torch.Tensor] = None,
|
| 1119 |
max_seq_len: Optional[int] = None,
|
| 1120 |
+
packed: Optional[_Packed] = None,
|
| 1121 |
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
|
| 1122 |
|
| 1123 |
+
if packed is not None:
|
| 1124 |
+
return self._forward_packed(hidden_states, packed)
|
| 1125 |
+
|
| 1126 |
has_layer_past = past_key_value is not None
|
| 1127 |
|
| 1128 |
if has_layer_past:
|
|
|
|
| 1224 |
|
| 1225 |
return attn_output
|
| 1226 |
|
| 1227 |
+
def _forward_packed(self, hidden_states: torch.Tensor, packed: _Packed) -> torch.Tensor:
|
| 1228 |
+
"""Varlen attention on packed ``[N, D]`` hidden states; returns ``[N, D]``."""
|
| 1229 |
+
qkv = self.Wqkv(hidden_states)
|
| 1230 |
+
qkv = rearrange(qkv, "n (three h d) -> n three h d", three=3, d=self.head_dim)
|
| 1231 |
+
if self.rotary_emb_dim > 0:
|
| 1232 |
+
qkv = _rotary_packed(self.rotary_emb, qkv, packed)
|
| 1233 |
+
orig_dtype = qkv.dtype
|
| 1234 |
+
if orig_dtype not in (torch.float16, torch.bfloat16):
|
| 1235 |
+
qkv = qkv.to(torch.bfloat16)
|
| 1236 |
+
cu, max_s = packed.cu_seqlens, packed.max_seqlen
|
| 1237 |
+
if select_attention_impl(hidden_states.device) == "torch_varlen":
|
| 1238 |
+
q, k, v = qkv.unbind(1)
|
| 1239 |
+
out = _torch_varlen_attn(q, k, v, cu, cu, max_s, max_s)
|
| 1240 |
+
else:
|
| 1241 |
+
out = flash_attn_varlen_qkvpacked_func(qkv, cu, max_s, dropout_p=0.0, causal=False)
|
| 1242 |
+
return self.out_proj(rearrange(out.to(orig_dtype), "n h d -> n (h d)"))
|
| 1243 |
+
|
| 1244 |
|
| 1245 |
class NomicBertBlock(nn.Module):
|
| 1246 |
def __init__(
|
|
|
|
| 1294 |
use_cache: Optional[bool] = False,
|
| 1295 |
cu_seqlens: Optional[torch.Tensor] = None,
|
| 1296 |
max_seq_len: Optional[int] = None,
|
| 1297 |
+
packed: Optional[_Packed] = None,
|
| 1298 |
):
|
| 1299 |
r"""Pass the input through the encoder layer.
|
| 1300 |
Args:
|
|
|
|
| 1314 |
is_padded_inputs=is_padded_inputs,
|
| 1315 |
cu_seqlens=cu_seqlens,
|
| 1316 |
max_seq_len=max_seq_len,
|
| 1317 |
+
packed=packed,
|
| 1318 |
)
|
| 1319 |
|
| 1320 |
dropped = self.dropout2(hidden_states)
|
|
|
|
| 1331 |
is_padded_inputs=is_padded_inputs,
|
| 1332 |
cu_seqlens=cu_seqlens,
|
| 1333 |
max_seq_len=max_seq_len,
|
| 1334 |
+
packed=packed,
|
| 1335 |
)
|
| 1336 |
hidden_states = self.norm1((self.dropout1(attn_outputs) + hidden_states).to(dtype=self.norm1.weight.dtype))
|
| 1337 |
mlp_out = self.mlp(hidden_states)
|
|
|
|
| 1359 |
output_hidden_states: Optional[bool] = None,
|
| 1360 |
return_dict: Optional[bool] = None,
|
| 1361 |
is_padded_inputs: Optional[bool] = True,
|
| 1362 |
+
packed: Optional[_Packed] = None,
|
| 1363 |
):
|
| 1364 |
"""If subset_mask is not None, we only want output for the subset of the sequence.
|
| 1365 |
This means that we only compute the last layer output for these tokens.
|
|
|
|
| 1387 |
None,
|
| 1388 |
None,
|
| 1389 |
is_padded_inputs,
|
| 1390 |
+
packed=packed,
|
| 1391 |
# if you freeze ANY layers, you need `use_reentrant=False`
|
| 1392 |
# https://github.com/huggingface/transformers/issues/21381
|
| 1393 |
# https://discuss.pytorch.org/t/checkpoint-with-no-grad-requiring-inputs-problem/19117/7
|
|
|
|
| 1405 |
is_padded_inputs,
|
| 1406 |
output_attentions,
|
| 1407 |
use_cache,
|
| 1408 |
+
packed=packed,
|
| 1409 |
)
|
| 1410 |
return hidden_states
|
| 1411 |
|
|
|
|
| 1502 |
token_type_ids=None,
|
| 1503 |
position_ids=None,
|
| 1504 |
return_dict=None,
|
| 1505 |
+
cu_seq_lens_q=None,
|
| 1506 |
+
max_length_q=None,
|
| 1507 |
+
**kwargs,
|
| 1508 |
):
|
| 1509 |
if token_type_ids is None:
|
| 1510 |
token_type_ids = torch.zeros_like(input_ids)
|
| 1511 |
+
if cu_seq_lens_q is not None:
|
| 1512 |
+
self.attention_impl = select_attention_impl(input_ids.device)
|
| 1513 |
+
_log_attention_impl_once(self.attention_impl, input_ids.device)
|
| 1514 |
+
return self._forward_prepacked(input_ids, token_type_ids, position_ids, cu_seq_lens_q, max_length_q)
|
| 1515 |
+
|
| 1516 |
+
impl = select_attention_impl(input_ids.device)
|
| 1517 |
+
self.attention_impl = impl
|
| 1518 |
+
_log_attention_impl_once(impl, input_ids.device)
|
| 1519 |
+
if impl != "eager" and (attention_mask is None or attention_mask.ndim == 2):
|
| 1520 |
+
return self._forward_packed(input_ids, attention_mask, token_type_ids, position_ids)
|
| 1521 |
+
|
| 1522 |
hidden_states = self.embeddings(input_ids, position_ids=position_ids, token_type_ids=token_type_ids)
|
| 1523 |
hidden_states = self.emb_ln(hidden_states)
|
| 1524 |
hidden_states = self.emb_drop(hidden_states)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1525 |
if impl == "eager":
|
| 1526 |
# No varlen tier engaged (CPU, pre-Ampere, forced, or no kernel):
|
| 1527 |
# build the additive [B, 1, 1, S] mask inline for the eager block.
|
|
|
|
| 1539 |
pooler_output=pooled_output,
|
| 1540 |
)
|
| 1541 |
|
| 1542 |
+
def _size_rotary_caches(self, seqlen, device):
|
| 1543 |
+
"""Grow each layer's RoPE cos/sin cache to ``seqlen`` before the packed encoder runs
|
| 1544 |
+
(see ``_rotary_packed``), in the dtype its q/k projection produces."""
|
| 1545 |
+
for layer in self.encoder.layers:
|
| 1546 |
+
attn = layer.attn
|
| 1547 |
+
if attn.rotary_emb_dim > 0:
|
| 1548 |
+
attn.rotary_emb._update_cos_sin_cache(seqlen, device=device, dtype=attn.Wqkv.weight.dtype)
|
| 1549 |
+
|
| 1550 |
+
def _forward_packed(self, input_ids, attention_mask, token_type_ids, position_ids):
|
| 1551 |
+
"""Varlen tiers: unpad once, run embeddings and every layer on real tokens only.
|
| 1552 |
+
|
| 1553 |
+
``last_hidden_state`` comes back padded ``[B, S, D]`` with zeros at padding
|
| 1554 |
+
positions, so masked pooling downstream is unchanged.
|
| 1555 |
+
"""
|
| 1556 |
+
B, S = input_ids.shape
|
| 1557 |
+
mask = (
|
| 1558 |
+
torch.ones(B, S, device=input_ids.device, dtype=torch.bool)
|
| 1559 |
+
if attention_mask is None
|
| 1560 |
+
else attention_mask.to(torch.bool)
|
| 1561 |
+
)
|
| 1562 |
+
seqlens = mask.sum(-1, dtype=torch.int32)
|
| 1563 |
+
max_s = int(seqlens.max())
|
| 1564 |
+
if max_s == 0:
|
| 1565 |
+
raise RuntimeError(
|
| 1566 |
+
"NomicBertModel.forward: every sequence is empty (max_s == 0); "
|
| 1567 |
+
"pre-filter empty inputs before encoding."
|
| 1568 |
+
)
|
| 1569 |
+
indices = torch.nonzero(mask.flatten(), as_tuple=False).flatten()
|
| 1570 |
+
cu = F.pad(torch.cumsum(seqlens, 0, dtype=torch.int32), (1, 0))
|
| 1571 |
+
columns = indices % S
|
| 1572 |
+
pos_u = position_ids.flatten()[indices] if position_ids is not None else columns
|
| 1573 |
+
hidden_states = self.embeddings(
|
| 1574 |
+
input_ids.flatten()[indices][None],
|
| 1575 |
+
position_ids=pos_u[None],
|
| 1576 |
+
token_type_ids=token_type_ids.flatten()[indices][None],
|
| 1577 |
+
)[0]
|
| 1578 |
+
hidden_states = self.emb_drop(self.emb_ln(hidden_states))
|
| 1579 |
+
packed = _Packed(cu_seqlens=cu, max_seqlen=max_s, positions=columns, rope_seqlen=S)
|
| 1580 |
+
self._size_rotary_caches(packed.rope_seqlen, hidden_states.device)
|
| 1581 |
+
hidden_states = self.encoder(hidden_states, packed=packed)
|
| 1582 |
+
sequence_output = _pad(hidden_states, indices, B, S)
|
| 1583 |
+
pooled_output = self.pooler(sequence_output) if self.pooler is not None else None
|
| 1584 |
+
return BaseModelOutputWithPoolingAndCrossAttentions(
|
| 1585 |
+
last_hidden_state=sequence_output,
|
| 1586 |
+
pooler_output=pooled_output,
|
| 1587 |
+
)
|
| 1588 |
+
|
| 1589 |
+
def _forward_prepacked(self, input_ids, token_type_ids, position_ids, cu_seq_lens_q, max_length_q):
|
| 1590 |
+
"""Input already packed by the caller: ``input_ids [1, ΣL]`` with per-sequence
|
| 1591 |
+
``position_ids`` and ``cu_seq_lens_q`` boundaries. Returns ``[1, ΣL, D]``."""
|
| 1592 |
+
if self.attention_impl == "eager":
|
| 1593 |
+
return self._forward_prepacked_eager(input_ids, token_type_ids, cu_seq_lens_q)
|
| 1594 |
+
if position_ids is None:
|
| 1595 |
+
raise ValueError("NomicBertModel.forward: pre-packed input needs per-sequence position_ids")
|
| 1596 |
+
positions = position_ids.reshape(-1).long()
|
| 1597 |
+
max_s = int(max_length_q) if max_length_q is not None else int((cu_seq_lens_q[1:] - cu_seq_lens_q[:-1]).max())
|
| 1598 |
+
hidden_states = self.embeddings(input_ids, position_ids=position_ids, token_type_ids=token_type_ids)[0]
|
| 1599 |
+
hidden_states = self.emb_drop(self.emb_ln(hidden_states))
|
| 1600 |
+
packed = _Packed(cu_seqlens=cu_seq_lens_q.to(torch.int32), max_seqlen=max_s, positions=positions,
|
| 1601 |
+
rope_seqlen=max_s)
|
| 1602 |
+
self._size_rotary_caches(packed.rope_seqlen, hidden_states.device)
|
| 1603 |
+
sequence_output = self.encoder(hidden_states, packed=packed)[None]
|
| 1604 |
+
pooled_output = self.pooler(sequence_output) if self.pooler is not None else None
|
| 1605 |
+
return BaseModelOutputWithPoolingAndCrossAttentions(
|
| 1606 |
+
last_hidden_state=sequence_output,
|
| 1607 |
+
pooler_output=pooled_output,
|
| 1608 |
+
)
|
| 1609 |
+
|
| 1610 |
+
def _forward_prepacked_eager(self, input_ids, token_type_ids, cu_seq_lens_q):
|
| 1611 |
+
"""Pre-packed input on the eager tier (forced, or no varlen kernel on this device):
|
| 1612 |
+
re-pad to ``[B, S]`` (right padding, so each token keeps its per-sequence
|
| 1613 |
+
position), run the padded eager forward, and gather the real tokens back to
|
| 1614 |
+
``[1, ΣL, D]``, the shape the caller's pooling expects."""
|
| 1615 |
+
cu = cu_seq_lens_q.long()
|
| 1616 |
+
seqlens = cu[1:] - cu[:-1]
|
| 1617 |
+
B, S = seqlens.shape[0], int(seqlens.max())
|
| 1618 |
+
mask = torch.arange(S, device=input_ids.device)[None, :] < seqlens[:, None]
|
| 1619 |
+
indices = torch.nonzero(mask.flatten(), as_tuple=False).flatten()
|
| 1620 |
+
ids = _pad(input_ids.reshape(-1), indices, B, S)
|
| 1621 |
+
types = _pad(token_type_ids.reshape(-1), indices, B, S)
|
| 1622 |
+
out = self.forward(ids, attention_mask=mask.long(), token_type_ids=types)
|
| 1623 |
+
sequence_output = out.last_hidden_state.flatten(0, 1)[indices][None]
|
| 1624 |
+
pooled_output = self.pooler(sequence_output) if self.pooler is not None else None
|
| 1625 |
+
return BaseModelOutputWithPoolingAndCrossAttentions(
|
| 1626 |
+
last_hidden_state=sequence_output,
|
| 1627 |
+
pooler_output=pooled_output,
|
| 1628 |
+
)
|
| 1629 |
+
|
| 1630 |
|
| 1631 |
class NomicBertForPreTraining(NomicBertPreTrainedModel):
|
| 1632 |
_tied_weights_keys = ["predictions.decoder.bias", "cls.predictions.decoder.weight"]
|