ALJIACHI commited on
Commit
ea26409
·
verified ·
1 Parent(s): f36896a

Release Mizan-Rerank-v3

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,3 +1,358 @@
1
  ---
2
  license: apache-2.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
+ language:
4
+ - ar
5
+ base_model:
6
+ - Alibaba-NLP/gte-multilingual-reranker-base
7
+ pipeline_tag: text-ranking
8
+ library_name: sentence-transformers
9
+ tags:
10
+ - sentence-transformers
11
+ - cross-encoder
12
+ - reranker
13
+ - text-ranking
14
+ - arabic
15
+ - long-context
16
+ - rag
17
+ - islamic
18
+ datasets:
19
+ - NAMAA-Space/Arabic-Triplet-With-Multi-Negatives
20
+ metrics:
21
+ - ndcg
22
+ - mrr
23
  ---
24
+
25
+ # Mizan-Rerank-v3
26
+
27
+ **An Arabic cross-encoder reranker trained to tell the passage that answers the question apart from passages that only look like they do.**
28
+
29
+ ![Parameters](https://img.shields.io/badge/Parameters-305M-green)
30
+ ![Language](https://img.shields.io/badge/Language-Arabic-blue)
31
+ ![Context](https://img.shields.io/badge/Trained%20context-3072%20tokens-purple)
32
+ ![License](https://img.shields.io/badge/License-Apache%202.0-brightgreen)
33
+
34
+ Mizan-Rerank-v3 is a new model, not a continuation of Mizan-Rerank-V2. It was fine-tuned directly from
35
+ [Alibaba-NLP/gte-multilingual-reranker-base](https://huggingface.co/Alibaba-NLP/gte-multilingual-reranker-base)
36
+ on 71k Arabic listwise groups. Each group pairs the correct passage with adversarial "trap" passages that
37
+ share most of its words but change its meaning: a negated ruling, a swapped entity, a shifted number or date,
38
+ an exception applied to the wrong case. It is meant for reranking in Arabic search and RAG pipelines, especially
39
+ over long, dense religious and encyclopedic text.
40
+
41
+ ![Summary](assets/summary.png)
42
+
43
+ ## Highlights
44
+
45
+ - **Best of the four compared models on 4 of 5 held-out benchmarks.** Mean nDCG@10 is **0.845**, vs 0.802 for
46
+ bge-reranker-v2-m3 (568M), 0.783 for its gte base and 0.723 for Mizan-Rerank-V2.
47
+ - **Much better at picking the right passage first on adversarial data.** Hit@1 on held-out long-context
48
+ adversarial queries is **0.570**, vs 0.336 for bge-reranker-v2-m3, 0.314 for gte base and 0.227 for Mizan-Rerank-V2.
49
+ - **Far less likely to rank a contradicting passage first.** On held-out adversarial tests, the top-ranked passage
50
+ is a contradicting trap in 20% of short and 33% of long-context queries. For bge-reranker-v2-m3 the figures are
51
+ 44% and 62%; for Mizan-Rerank-V2, 62% and 76%.
52
+ - **Better than Mizan-Rerank-V2 on every benchmark**, including real user queries about Islamic jurisprudence
53
+ (+0.052 nDCG@10, 95% CI [+0.025, +0.078]).
54
+ - **Same size and speed as V2 and gte base** (305M parameters, XLM-R vocabulary, RoPE positions).
55
+
56
+ It is **not** the best model on everything. bge-reranker-v2-m3 remains ahead on MTEB NamaaMrTydi and, by a clear
57
+ margin, on real Islamic-jurisprudence search queries (see [Limitations](#limitations)).
58
+
59
+ ## Usage
60
+
61
+ ### Sentence Transformers
62
+
63
+ ```bash
64
+ pip install -U sentence-transformers
65
+ ```
66
+
67
+ ```python
68
+ from sentence_transformers import CrossEncoder
69
+
70
+ model = CrossEncoder("ALJIACHI/Mizan-Rerank-v3", trust_remote_code=True, max_length=3072)
71
+
72
+ query = "ما هي فوائد فيتامين د؟"
73
+ passages = [
74
+ "يساعد فيتامين د على امتصاص الكالسيوم وتقوية العظام، كما يدعم عمل الجهاز المناعي.",
75
+ "يستخدم فيتامين د في بعض الصناعات الغذائية كمادة مضافة لتدعيم الحليب.",
76
+ "أطلقت وزارة الزراعة حملة وطنية لزيادة الوعي بأهمية الزراعة العضوية.",
77
+ ]
78
+
79
+ scores = model.predict([(query, passage) for passage in passages])
80
+ print(scores) # ≈ [0.875, 0.023, 0.010] (sigmoid relevance scores)
81
+
82
+ for hit in model.rank(query, passages, return_documents=True):
83
+ print(f"{hit['score']:.3f} {hit['text']}")
84
+ ```
85
+
86
+ ### Transformers
87
+
88
+ ```python
89
+ import torch
90
+ from transformers import AutoModelForSequenceClassification, AutoTokenizer
91
+
92
+ tokenizer = AutoTokenizer.from_pretrained("ALJIACHI/Mizan-Rerank-v3")
93
+ model = AutoModelForSequenceClassification.from_pretrained(
94
+ "ALJIACHI/Mizan-Rerank-v3", trust_remote_code=True, torch_dtype=torch.float16
95
+ ).to("cuda").eval()
96
+
97
+ query = "ما هي فوائد فيتامين د؟"
98
+ passages = [
99
+ "يساعد فيتامين د على امتصاص الكالسيوم وتقوية العظام، كما يدعم عمل الجهاز المناعي.",
100
+ "أطلقت وزارة الزراعة حملة وطنية لزيادة الوعي بأهمية الزراعة العضوية.",
101
+ ]
102
+
103
+ with torch.inference_mode():
104
+ features = tokenizer([query] * len(passages), passages, padding=True, truncation=True,
105
+ max_length=3072, return_tensors="pt").to("cuda")
106
+ scores = torch.sigmoid(model(**features).logits.squeeze(-1).float())
107
+
108
+ for passage, score in sorted(zip(passages, scores.tolist()), key=lambda item: -item[1]):
109
+ print(f"{score:.3f} {passage}")
110
+ ```
111
+
112
+ ### Practical notes
113
+
114
+ - **Sequence length.** The model was trained on query + passage inputs of up to 3,072 tokens and is benchmarked
115
+ here at 2,048. The architecture accepts 8,192 positions, but quality beyond 3,072 tokens has not been measured.
116
+ If your query + passage inputs fit in 512 tokens, `max_length=512` is much faster.
117
+ - **Scores.** The sigmoid of the single output logit is a relevance score. It works for ranking and for coarse
118
+ cut-offs, but it is not a calibrated probability: tune any threshold on your own data.
119
+ - **`trust_remote_code=True`** is required. The architecture code (`modeling.py`, `configuration.py`) comes from
120
+ the gte base model and ships in this repository.
121
+
122
+ ## Evaluation
123
+
124
+ All four models were scored by the same script ([`benchmark/benchmark_rerankers.py`](benchmark/benchmark_rerankers.py))
125
+ with the same inputs: float32, `max_length=2048`, and the full candidate list of every query. Metrics use graded
126
+ gains (correct passage 3, partially correct passage 1, trap or irrelevant passage 0).
127
+
128
+ | Metric | Meaning |
129
+ |---|---|
130
+ | nDCG@10 | quality of the whole ranking |
131
+ | MRR@10 | 1 / rank of the correct passage |
132
+ | Hit@1 | share of queries where the correct passage is ranked first |
133
+
134
+ ### Held-out benchmarks
135
+
136
+ | Benchmark | Queries | Mizan-Rerank-v3 | Mizan-Rerank-V2 | gte-multilingual-reranker-base | bge-reranker-v2-m3 |
137
+ |---|---:|---:|---:|---:|---:|
138
+ | MTEB NamaaMrTydi, unseen subset¹ | 573 | 0.8784 | 0.8220 | 0.8771 | **0.8897** |
139
+ | Arabic Hard Negatives, unseen subset¹ | 314 | **0.9200** | 0.8780 | 0.9062 | 0.9035 |
140
+ | Adversarial short (test)² | 602 | **0.8432** | 0.6965 | 0.7565 | 0.7938 |
141
+ | Adversarial long-context (test)² | 1,703 | **0.8280** | 0.6203 | 0.6747 | 0.6873 |
142
+ | Multi-LLM benchmark² | 361 | **0.7536** | 0.5998 | 0.6992 | 0.7365 |
143
+ | **Average nDCG@10** | | **0.8446** | 0.7233 | 0.7827 | 0.8022 |
144
+ | **Average MRR@10** | | **0.7908** | 0.6312 | 0.7160 | 0.7427 |
145
+ | **Average Hit@1** | | **0.6461** | 0.4221 | 0.5307 | 0.5764 |
146
+
147
+ ¹ Queries whose question and correct passage never occur in the v3 training pool (see [Training-data overlap](#training-data-overlap-with-public-benchmarks)).
148
+ ² Internal test sets, not released. They share no queries or passages with the training data (verified by exact
149
+ match), and the long-context split is by source article.
150
+
151
+ ![Public benchmarks](assets/public_benchmarks.png)
152
+
153
+ ![Adversarial benchmarks](assets/adversarial_benchmarks.png)
154
+
155
+ **Where the gain comes from:** ranking the correct passage *first*. Hit@1 is where v3 differs most from the other
156
+ models, especially when traps share most of their text with the answer:
157
+
158
+ ![Hit@1](assets/hit_at_1.png)
159
+
160
+ ### Contradiction ranked first
161
+
162
+ For RAG the worst failure is not a missing answer but a passage that says the *opposite* ranked at the top:
163
+ a negated ruling, a reversed condition, a wrong number. Every adversarial test query has one correct passage
164
+ and several contradicting traps. The table shows how often a trap was ranked #1 (lower is better):
165
+
166
+ | Test set | Queries | Mizan-Rerank-v3 | Mizan-Rerank-V2 | gte-multilingual-reranker-base | bge-reranker-v2-m3 |
167
+ |---|---:|---:|---:|---:|---:|
168
+ | Adversarial short | 602 | **20.1%** (121) | 62.1% (374) | 51.3% (309) | 43.5% (262) |
169
+ | Adversarial long-context | 1,703 | **33.4%** (568) | 76.4% (1,301) | 68.2% (1,161) | 62.0% (1,056) |
170
+ | Multi-LLM | 361 | **49.3%** (178) | 75.3% (272) | 64.0% (231) | 54.0% (195) |
171
+
172
+ Across all 2,666 queries, v3 ranks a trap first 32.5% of the time. For bge-reranker-v2-m3 the figure is 56.8%;
173
+ for gte base, 63.8%; for Mizan-Rerank-V2, 73.0%:
174
+
175
+ ![Contradiction ranked first](assets/contradiction_first.png)
176
+
177
+ Reproduce with `python compare_generated_test_models.py --dataset-file <test.jsonl> ... --chart contradiction_first.png`
178
+ (in the training repository; the test sets are internal).
179
+
180
+ <details>
181
+ <summary><b>Full per-benchmark tables (nDCG@10 / MRR@10 / Hit@1), including full-set public scores</b></summary>
182
+
183
+ **nDCG@10**
184
+
185
+ | Model | NamaaMrTydi (full, 918) | NamaaMrTydi (unseen, 573) | Arabic Hard Neg. (full, 12,373)³ | Arabic Hard Neg. (unseen, 314) | Adversarial short | Adversarial long-ctx | Multi-LLM |
186
+ |---|---:|---:|---:|---:|---:|---:|---:|
187
+ | Mizan-Rerank-v3 | 0.8885 | 0.8784 | 0.9103 | **0.9200** | **0.8432** | **0.8280** | **0.7536** |
188
+ | Mizan-Rerank-V2 | 0.8312 | 0.8220 | 0.8447 | 0.8780 | 0.6965 | 0.6203 | 0.5998 |
189
+ | gte-multilingual-reranker-base | 0.8874 | 0.8771 | 0.8960 | 0.9062 | 0.7565 | 0.6747 | 0.6992 |
190
+ | bge-reranker-v2-m3 | **0.8995** | **0.8897** | 0.9082 | 0.9035 | 0.7938 | 0.6873 | 0.7365 |
191
+
192
+ **MRR@10**
193
+
194
+ | Model | NamaaMrTydi (full) | NamaaMrTydi (unseen) | Arabic Hard Neg. (full)³ | Arabic Hard Neg. (unseen) | Adversarial short | Adversarial long-ctx | Multi-LLM |
195
+ |---|---:|---:|---:|---:|---:|---:|---:|
196
+ | Mizan-Rerank-v3 | 0.8518 | 0.8385 | 0.8796 | **0.8926** | **0.7731** | **0.7658** | **0.6841** |
197
+ | Mizan-Rerank-V2 | 0.7755 | 0.7635 | 0.7919 | 0.8362 | 0.5952 | 0.4983 | 0.4629 |
198
+ | gte-multilingual-reranker-base | 0.8498 | 0.8362 | 0.8607 | 0.8743 | 0.6826 | 0.5765 | 0.6105 |
199
+ | bge-reranker-v2-m3 | **0.8662** | **0.8531** | 0.8771 | 0.8711 | 0.7374 | 0.5879 | 0.6643 |
200
+
201
+ **Hit@1**
202
+
203
+ | Model | NamaaMrTydi (full) | NamaaMrTydi (unseen) | Arabic Hard Neg. (full)³ | Arabic Hard Neg. (unseen) | Adversarial short | Adversarial long-ctx | Multi-LLM |
204
+ |---|---:|---:|---:|---:|---:|---:|---:|
205
+ | Mizan-Rerank-v3 | 0.7691 | 0.7522 | 0.7882 | **0.8121** | **0.6146** | **0.5696** | **0.4820** |
206
+ | Mizan-Rerank-V2 | 0.6492 | 0.6335 | 0.6433 | 0.7134 | 0.3405 | 0.2267 | 0.1967 |
207
+ | gte-multilingual-reranker-base | 0.7549 | 0.7347 | 0.7600 | 0.7834 | 0.4618 | 0.3136 | 0.3601 |
208
+ | bge-reranker-v2-m3 | **0.7865** | **0.7627** | 0.7916 | 0.7866 | 0.5449 | 0.3365 | 0.4515 |
209
+
210
+ ³ 97% of these queries occur in the v3 training pool, so the full-set score is not a fair measure for v3 and is
211
+ not bolded or used in any average.
212
+
213
+ </details>
214
+
215
+ ### Real Islamic-jurisprudence search queries (fiqh eval)
216
+
217
+ This is the use case the model was built for. The eval set has 224 real, answerable user queries from a production
218
+ Arabic assistant that answers questions from a collection of fatwas. Each query comes with the 25 candidate passages
219
+ its dense retriever returned, all graded 0–3 by two independent LLM judges (the lower grade is kept; weighted
220
+ κ = 0.81). None of these queries or candidate passages occurs in the training data (exact match; the legacy
221
+ sources were also filtered by 8-word shingles). Scoring
222
+ uses float16 and `max_length=512`, the production settings. The set contains private user queries and is not released.
223
+
224
+ | Model | nDCG@10 | MRR@10 | P@1 | Misleading doc in top 3 ↓ | v3 − model, nDCG@10 [95% CI] |
225
+ |---|---:|---:|---:|---:|---:|
226
+ | Retriever order (no reranker) | 0.5732 | 0.4691 | 0.5045 | 0.3482 | +0.096 [+0.065, +0.132] |
227
+ | **Mizan-Rerank-v3** | **0.6693** | 0.5807 | 0.6429 | 0.3080 | – |
228
+ | Mizan-Rerank-V2 | 0.6176 | 0.4513 | 0.4955 | 0.3705 | +0.052 [+0.025, +0.078] |
229
+ | gte-multilingual-reranker-base | 0.6567 | 0.5346 | 0.5893 | 0.3259 | +0.013 [−0.002, +0.027] |
230
+ | bge-reranker-v2-m3 | **0.7372** | **0.6601** | **0.7098** | **0.2946** | −0.068 [−0.094, −0.043] |
231
+
232
+ "Misleading" means a passage the judges marked as a ruling for a *different* case than the one asked about is
233
+ ranked in the top 3. Confidence intervals come from a paired bootstrap over queries (2,000 resamples).
234
+
235
+ ![Fiqh eval](assets/fiqh_eval.png)
236
+
237
+ On this task v3 clearly improves on Mizan-Rerank-V2. Its gain over its own gte base is small and not
238
+ statistically significant, and bge-reranker-v2-m3 is clearly better. If this is your use case, benchmark
239
+ bge-reranker-v2-m3 too.
240
+
241
+ ### Training-data overlap with public benchmarks
242
+
243
+ v3 was trained with a retrieval replay set built from the Arabic Mr. TyDi training data
244
+ ([NAMAA-Space/Arabic-Triplet-With-Multi-Negatives](https://huggingface.co/datasets/NAMAA-Space/Arabic-Triplet-With-Multi-Negatives)).
245
+ Both public benchmarks are also derived from Arabic Mr. TyDi, so we hashed every benchmark query and correct passage
246
+ against the training pool:
247
+
248
+ | Benchmark | Queries | Query or correct passage in v3 training pool | Unseen subset |
249
+ |---|---:|---:|---:|
250
+ | MTEB NamaaMrTydiReranking | 918 | 345 (correct passage only; 0 queries) | 573 |
251
+ | Omartificial-Intelligence-Space/Arabic-With-Ranked-Hard-Negatives | 12,373 | 12,059 | 314 |
252
+
253
+ The headline numbers use the unseen subsets. The hashes are shipped in
254
+ [`benchmark/seen_in_training.json`](benchmark/seen_in_training.json), so anyone can reproduce the split. The other
255
+ models may also have been trained on Mr. TyDi; the unseen subsets only control for v3's own training data.
256
+ `NAMAA-Space/mteb-eval-mrtydi` is a flat copy of MTEB NamaaMrTydiReranking (identical queries and candidates), so it
257
+ is not reported separately.
258
+
259
+ ## Training
260
+
261
+ ### Data (71,044 listwise groups)
262
+
263
+ Every group is one query with one correct passage, up to 5 hard negatives and, for adversarial groups, one
264
+ partially correct passage.
265
+
266
+ | Source | Groups | Share | What it teaches |
267
+ |---|---:|---:|---|
268
+ | Long-context adversarial (WikiShia articles, LLM-generated) | 36,280 | 51% | find the answer inside long, dense passages, whether it sits at the beginning, middle or end |
269
+ | Short adversarial (LLM-generated) | 12,030 | 17% | reject near-duplicate traps in short passages |
270
+ | Arabic Mr. TyDi retrieval replay | 8,525 | 12% | keep general open-domain retrieval ability |
271
+ | Hadith question–passage pairs | 4,736 | 7% | classical religious text |
272
+ | Arabic news similarity pairs with LLM hard negatives | 4,737 | 7% | topical near-misses |
273
+ | Fatwa Q&A pairs with TF-IDF-mined negatives | 4,736 | 7% | jurisprudence rulings on nearby cases |
274
+
275
+ The adversarial negatives cover 12 trap types, each a minimal edit that changes the meaning while keeping most
276
+ of the words: `negation_flip`, `exception_scope`, `entity_role_reversal`, `entity_swap`, `ordered_precedence`,
277
+ `temporal_boundary`, `numeric_boundary`, `quantifier_change`, `causal_direction`, `condition_swap`,
278
+ `conclusion_flip`, `attribution_shift`.
279
+
280
+ Leakage controls: the adversarial train, validation and test splits are split by source article. Extra training
281
+ data sharing a source article or a query with any validation or test set was removed. The three legacy sources were
282
+ additionally filtered against every evaluation set (including the fiqh eval) by 8-word shingles. All held-out
283
+ sets above were re-checked against the final training pool by exact query and passage match.
284
+
285
+ ### Objective
286
+
287
+ For each group the model scores all candidates jointly:
288
+
289
+ - **Pointwise:** binary cross-entropy, targets 1 / 0.5 / 0 for correct / partial / negative, `pos_weight=1.24`, weight 0.5.
290
+ - **Pairwise:** softplus margin loss `softplus(s_neg − s_pos + 0.2)` for every negative, weight 1.0.
291
+ - **Partial order:** the same margin loss for correct > partial > negative, weight 0.5 within the pairwise term.
292
+
293
+ ### Configuration
294
+
295
+ | Parameter | Value |
296
+ |---|---|
297
+ | Initialization | Alibaba-NLP/gte-multilingual-reranker-base |
298
+ | Max sequence length (query + passage) | 3,072 tokens |
299
+ | Candidates per group | 1 correct + 1 partial (if present) + up to 5 negatives |
300
+ | Batch | 4 groups/GPU × 8 accumulation × 2 GPUs = 64 groups per update |
301
+ | Optimizer | AdamW, lr 1e-6, weight decay 0.01, grad-norm clip 1.0 |
302
+ | Schedule | cosine, 10% warmup, 7 epochs (7,777 updates) |
303
+ | Precision | bf16, gradient checkpointing, SDPA attention |
304
+ | Hardware | 2 × NVIDIA RTX 4090 |
305
+ | Checkpoint selection | epoch with the best mean validation nDCG@10 (long-context + short adversarial) → **epoch 6** |
306
+
307
+ ![Training curve](assets/training_curve.png)
308
+
309
+ ## Limitations
310
+
311
+ - **Real fiqh queries: bge-reranker-v2-m3 is better.** It leads by 0.068 nDCG@10 (see above). v3's advantage is
312
+ concentrated on adversarial, trap-heavy passages. Short, natural user queries over similar-looking rulings remain
313
+ hard.
314
+ - **Contradiction traps are reduced, not solved.** A contradicting passage is still ranked first for 33% of
315
+ long-context and 20% of short adversarial test queries, and for 49% of the multi-LLM set. Verify critical answers, such as religious rulings or
316
+ medical and legal statements, rather than relying on the top-ranked passage alone.
317
+ - **In-distribution test sets.** The adversarial test sets come from the same generation pipeline as the training
318
+ data. They are disjoint from it, but share its style. Treat them as a measure of the targeted skill, not of
319
+ general-domain quality.
320
+ - **Arabic only.** No English or other-language data was used, and cross-lingual quality relative to the
321
+ multilingual base model is untested.
322
+ - **Long inputs.** Quality beyond 3,072 tokens is unmeasured.
323
+
324
+ ## Reproducing the benchmarks
325
+
326
+ ```bash
327
+ git clone https://huggingface.co/ALJIACHI/Mizan-Rerank-v3 && cd Mizan-Rerank-v3/benchmark
328
+ pip install torch transformers huggingface_hub pyarrow matplotlib
329
+ python benchmark_rerankers.py # 4 models on the public benchmarks → results.json
330
+ python benchmark_rerankers.py --models mizan-v3 --fp16
331
+ python benchmark_rerankers.py --model mine=path/to/reranker --models mine mizan-v3
332
+ python benchmark_rerankers.py --listwise my_set=my_test.jsonl # your own listwise JSONL
333
+ ```
334
+
335
+ Public datasets are downloaded at pinned revisions. The internal and fiqh results above were produced by the same
336
+ metric code (`results_full.json`, `internal_results.json`). `plot_results.py` regenerates every chart in `assets/`.
337
+
338
+ ## Framework versions
339
+
340
+ Python 3.10.14 · PyTorch 2.8.0+cu126 · Transformers 4.55.4 · Sentence Transformers 5.4.1 · Accelerate 1.10.0 ·
341
+ Tokenizers 0.21.0
342
+
343
+ ## Citation
344
+
345
+ ```bibtex
346
+ @software{Mizan_Rerank_v3_2026,
347
+ author = {Ali Aljiachi},
348
+ title = {Mizan-Rerank-v3: Adversarially Trained Arabic Long-Context Reranker},
349
+ year = {2026},
350
+ publisher = {Hugging Face},
351
+ url = {https://huggingface.co/ALJIACHI/Mizan-Rerank-v3}
352
+ }
353
+ ```
354
+
355
+ ## License
356
+
357
+ Apache 2.0, the same license as the base model
358
+ [Alibaba-NLP/gte-multilingual-reranker-base](https://huggingface.co/Alibaba-NLP/gte-multilingual-reranker-base).
assets/adversarial_benchmarks.png ADDED
assets/contradiction_first.png ADDED
assets/fiqh_eval.png ADDED
assets/hit_at_1.png ADDED
assets/public_benchmarks.png ADDED
assets/summary.png ADDED
assets/training_curve.png ADDED
benchmark/benchmark_rerankers.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Benchmark Arabic rerankers on public Hugging Face reranking sets.
2
+
3
+ Compares Mizan-Rerank-v3 with Mizan-Rerank-V2, gte-multilingual-reranker-base and
4
+ bge-reranker-v2-m3 (any other cross-encoder can be added with --model name=repo_or_path).
5
+
6
+ Datasets (downloaded from the Hub at pinned revisions):
7
+
8
+ namaa_mrtydi mteb/NamaaMrTydiReranking (MTEB) 918 queries, 1 positive + ~4 hard negatives
9
+ arabic_hard_negatives Omartificial-Intelligence-Space/
10
+ Arabic-With-Ranked-Hard-Negatives 12,373 queries, 1 positive + 4 mined negatives
11
+
12
+ Both are also reported on their "unseen" subset: cases whose query or positive passage does
13
+ not occur in the Mizan-Rerank-v3 training pool (hashes in seen_in_training.json). Your own
14
+ listwise JSONL files can be added with --listwise name=path.jsonl (fields: query, positive,
15
+ optional partial_or_neutral, hard_negatives as strings or {"text": ...}).
16
+
17
+ Metrics per query (graded gains: positive 3, partial 1, negative 0):
18
+ nDCG@10, MRR@10 (rank of the positive), Hit@1 (positive ranked first).
19
+ "average" = unweighted mean over the --average datasets (default: every dataset, using the
20
+ _unseen subset instead of the full set wherever one exists, so no query is counted twice).
21
+
22
+ pip install torch transformers huggingface_hub pyarrow
23
+ python benchmark_rerankers.py # all four models, public sets
24
+ python benchmark_rerankers.py --models mizan-v3 --fp16
25
+ python benchmark_rerankers.py --model mine=./my_reranker --models mine bge-v2-m3
26
+ """
27
+
28
+ import argparse
29
+ import gc
30
+ import hashlib
31
+ import json
32
+ import math
33
+ import statistics
34
+ import time
35
+ from dataclasses import dataclass
36
+ from pathlib import Path
37
+
38
+ import pyarrow.parquet as pq
39
+ import torch
40
+ from huggingface_hub import snapshot_download
41
+ from transformers import AutoModelForSequenceClassification, AutoTokenizer
42
+
43
+ SCRIPT_DIR = Path(__file__).resolve().parent
44
+ SEEN_FILE = SCRIPT_DIR / "seen_in_training.json"
45
+ MODELS = {
46
+ "mizan-v3": "ALJIACHI/Mizan-Rerank-v3",
47
+ "mizan-v2": "ALJIACHI/Mizan-Rerank-V2",
48
+ "gte-base": "Alibaba-NLP/gte-multilingual-reranker-base",
49
+ "bge-v2-m3": "BAAI/bge-reranker-v2-m3",
50
+ }
51
+ HUB_DATASETS = {
52
+ "namaa_mrtydi": ("mteb/NamaaMrTydiReranking", "ecf1ff8c18469251bfe6acf1219e63e287bbb037"),
53
+ "arabic_hard_negatives": ("Omartificial-Intelligence-Space/Arabic-With-Ranked-Hard-Negatives", "fec243e3767f502d393d330bf97bfb6dce868830"),
54
+ }
55
+ METRICS = ("ndcg@10", "mrr@10", "hit@1")
56
+
57
+
58
+ @dataclass(frozen=True)
59
+ class Group:
60
+ query: str
61
+ documents: tuple[str, ...]
62
+ gains: tuple[int, ...]
63
+
64
+
65
+ def normalize(text: str) -> str:
66
+ return " ".join(str(text).split())
67
+
68
+
69
+ def digest(text: str) -> str:
70
+ return hashlib.sha256(normalize(text).encode("utf-8")).hexdigest()
71
+
72
+
73
+ def read_parquet_dir(directory: Path) -> list[dict]:
74
+ files = sorted(directory.glob("*.parquet"))
75
+ if not files:
76
+ raise FileNotFoundError(f"No parquet files in {directory}")
77
+ return [row for path in files for row in pq.read_table(path).to_pylist()]
78
+
79
+
80
+ def download(repo: str, revision: str | None) -> Path:
81
+ return Path(snapshot_download(repo, repo_type="dataset", revision=revision))
82
+
83
+
84
+ def load_namaa_mrtydi(root: Path) -> list[Group]:
85
+ corpus = {row["_id"]: row["text"] or "" for row in read_parquet_dir(root / "corpus")}
86
+ queries = {row["_id"]: row["text"] for row in read_parquet_dir(root / "queries")}
87
+ relevant = {(row["query-id"], row["corpus-id"]) for row in read_parquet_dir(root / "data") if row["score"] > 0}
88
+ groups = []
89
+ for row in read_parquet_dir(root / "top_ranked"):
90
+ ids = row["corpus-ids"]
91
+ groups.append(Group(queries[row["query-id"]], tuple(corpus[i] for i in ids), tuple(3 if (row["query-id"], i) in relevant else 0 for i in ids)))
92
+ return groups
93
+
94
+
95
+ def load_arabic_hard_negatives(root: Path) -> list[Group]:
96
+ groups = []
97
+ for row in read_parquet_dir(root / "data"):
98
+ negatives = [row[f"negative{i}"] for i in range(1, 5) if row.get(f"negative{i}") and str(row[f"negative{i}"]).strip()]
99
+ groups.append(Group(row["query"], (row["positive"], *negatives), (3, *([0] * len(negatives)))))
100
+ return groups
101
+
102
+
103
+ def load_listwise(path: Path) -> list[Group]:
104
+ groups = []
105
+ for line in path.read_text(encoding="utf-8").splitlines():
106
+ if not line.strip():
107
+ continue
108
+ record = json.loads(line)
109
+ documents, gains = [record["positive"]], [3]
110
+ if record.get("partial_or_neutral"):
111
+ documents.append(record["partial_or_neutral"])
112
+ gains.append(1)
113
+ for negative in record["hard_negatives"]:
114
+ documents.append(negative["text"] if isinstance(negative, dict) else negative)
115
+ gains.append(0)
116
+ groups.append(Group(record["query"], tuple(documents), tuple(gains)))
117
+ return groups
118
+
119
+
120
+ def unseen_indices(groups: list[Group], seen: dict) -> list[int]:
121
+ queries, passages = set(seen["query_sha256"]), set(seen["passage_sha256"])
122
+ return [
123
+ index for index, group in enumerate(groups)
124
+ if digest(group.query) not in queries and digest(group.documents[group.gains.index(3)]) not in passages
125
+ ]
126
+
127
+
128
+ def dcg(gains: list[int], k: int) -> float:
129
+ return sum((2**gain - 1) / math.log2(rank + 2) for rank, gain in enumerate(gains[:k]))
130
+
131
+
132
+ def group_metrics(group: Group, scores: list[float]) -> dict[str, float]:
133
+ order = sorted(range(len(scores)), key=lambda index: scores[index], reverse=True)
134
+ ranked = [group.gains[index] for index in order]
135
+ ideal = dcg(sorted(group.gains, reverse=True), 10)
136
+ rank = ranked.index(3) + 1
137
+ return {"ndcg@10": dcg(ranked, 10) / ideal, "mrr@10": 1.0 / rank if rank <= 10 else 0.0, "hit@1": float(rank == 1)}
138
+
139
+
140
+ def score(model_path: str, groups: list[Group], device: str, batch_size: int, max_length: int, fp16: bool) -> list[list[float]]:
141
+ tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
142
+ dtype = torch.float16 if fp16 else torch.float32
143
+ model = AutoModelForSequenceClassification.from_pretrained(model_path, trust_remote_code=True, torch_dtype=dtype).to(device).eval()
144
+ pairs = [(group.query, document) for group in groups for document in group.documents]
145
+ order = sorted(range(len(pairs)), key=lambda index: len(pairs[index][0]) + len(pairs[index][1]))
146
+ flat = [0.0] * len(pairs)
147
+ with torch.inference_mode():
148
+ for start in range(0, len(order), batch_size):
149
+ batch = order[start : start + batch_size]
150
+ features = tokenizer(
151
+ [pairs[index][0] for index in batch], [pairs[index][1] for index in batch],
152
+ padding=True, truncation=True, max_length=max_length, return_tensors="pt",
153
+ ).to(device)
154
+ logits = model(**features).logits.float()
155
+ logits = logits.squeeze(-1) if logits.shape[-1] == 1 else logits[:, -1]
156
+ for index, value in zip(batch, logits.cpu().tolist()):
157
+ flat[index] = value
158
+ del model
159
+ gc.collect()
160
+ if torch.cuda.is_available():
161
+ torch.cuda.empty_cache()
162
+ grouped, offset = [], 0
163
+ for group in groups:
164
+ grouped.append(flat[offset : offset + len(group.documents)])
165
+ offset += len(group.documents)
166
+ return grouped
167
+
168
+
169
+ def summarize(rows: list[dict]) -> dict:
170
+ return {"queries": len(rows), **{metric: statistics.fmean(row[metric] for row in rows) for metric in METRICS}}
171
+
172
+
173
+ def print_tables(results: dict, datasets: list[str], average: list[str]) -> None:
174
+ for metric in METRICS:
175
+ header = "| Model | " + " | ".join(datasets) + " | **Average** |"
176
+ print(f"\n### {metric}\n\n{header}\n|" + "---|" * (len(datasets) + 2))
177
+ for name, row in sorted(results.items(), key=lambda item: -item[1]["average"][metric]):
178
+ cells = " | ".join(f"{row['datasets'][dataset][metric]:.4f}" for dataset in datasets)
179
+ print(f"| {name} | {cells} | **{row['average'][metric]:.4f}** |")
180
+ print(f"\nAverage = unweighted mean over: {', '.join(average)}")
181
+
182
+
183
+ def parse_named(values: list[str], flag: str) -> dict[str, str]:
184
+ named = {}
185
+ for value in values or []:
186
+ name, _, target = value.partition("=")
187
+ if not name or not target:
188
+ raise ValueError(f"{flag} expects name=value, got {value!r}")
189
+ named[name] = target
190
+ return named
191
+
192
+
193
+ def build_parser() -> argparse.ArgumentParser:
194
+ parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
195
+ parser.add_argument("--model", action="append", help="name=repo_or_path; adds or overrides a model")
196
+ parser.add_argument("--models", nargs="+", help="Model names to run (default: all)")
197
+ parser.add_argument("--datasets", nargs="+", choices=list(HUB_DATASETS), default=list(HUB_DATASETS))
198
+ parser.add_argument("--listwise", action="append", help="name=path.jsonl extra listwise dataset")
199
+ parser.add_argument("--no-unseen", action="store_true", help="Skip the *_unseen subsets")
200
+ parser.add_argument("--average", nargs="+", help="Datasets included in the average (default: all scored)")
201
+ parser.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
202
+ parser.add_argument("--batch-size", type=int, default=32)
203
+ parser.add_argument("--max-length", type=int, default=2048)
204
+ parser.add_argument("--fp16", action="store_true", help="Score in float16 (faster; results differ slightly from float32)")
205
+ parser.add_argument("--limit", type=int, default=None, help="Use only the first N queries of each dataset (smoke tests)")
206
+ parser.add_argument("--output", type=Path, default=SCRIPT_DIR / "results.json")
207
+ return parser
208
+
209
+
210
+ def main() -> int:
211
+ args = build_parser().parse_args()
212
+ models = {**MODELS, **parse_named(args.model, "--model")}
213
+ selected = args.models or list(models)
214
+ unknown = [name for name in selected if name not in models]
215
+ if unknown:
216
+ raise ValueError(f"Unknown model(s) {unknown}; known: {sorted(models)}")
217
+
218
+ datasets: dict[str, list[Group]] = {}
219
+ subsets: dict[str, tuple[str, list[int]]] = {}
220
+ loaders = {"namaa_mrtydi": load_namaa_mrtydi, "arabic_hard_negatives": load_arabic_hard_negatives}
221
+ seen = json.loads(SEEN_FILE.read_text(encoding="utf-8")) if SEEN_FILE.is_file() and not args.no_unseen else None
222
+ for name in args.datasets:
223
+ datasets[name] = loaders[name](download(*HUB_DATASETS[name]))
224
+ for name, path in parse_named(args.listwise, "--listwise").items():
225
+ datasets[name] = load_listwise(Path(path))
226
+ if args.limit:
227
+ datasets = {name: groups[: args.limit] for name, groups in datasets.items()}
228
+ if seen:
229
+ subsets = {f"{name}_unseen": (name, unseen_indices(datasets[name], seen)) for name in args.datasets}
230
+ columns = [column for name in datasets for column in (name, f"{name}_unseen") if column in datasets or column in subsets]
231
+ for column in columns:
232
+ size = len(subsets[column][1]) if column in subsets else len(datasets[column])
233
+ print(f"{column:<32} {size:>6} queries")
234
+ average = args.average or [column for column in columns if f"{column}_unseen" not in subsets]
235
+ missing = [name for name in average if name not in columns]
236
+ if missing:
237
+ raise ValueError(f"--average names not scored: {missing}; scored: {columns}")
238
+
239
+ results = {}
240
+ for name in selected:
241
+ started = time.perf_counter()
242
+ print(f"\nScoring {name} ({models[name]})")
243
+ per_dataset = {}
244
+ for dataset, groups in datasets.items():
245
+ rows = [group_metrics(group, values) for group, values in zip(groups, score(models[name], groups, args.device, args.batch_size, args.max_length, args.fp16))]
246
+ per_dataset[dataset] = summarize(rows)
247
+ subset = subsets.get(f"{dataset}_unseen")
248
+ if subset:
249
+ per_dataset[f"{dataset}_unseen"] = summarize([rows[index] for index in subset[1]])
250
+ for column in columns:
251
+ print(f" {column:<30} " + " ".join(f"{metric} {per_dataset[column][metric]:.4f}" for metric in METRICS))
252
+ results[name] = {
253
+ "model": models[name],
254
+ "seconds": time.perf_counter() - started,
255
+ "datasets": per_dataset,
256
+ "average": {metric: statistics.fmean(per_dataset[dataset][metric] for dataset in average) for metric in METRICS},
257
+ }
258
+
259
+ print_tables(results, columns, average)
260
+ args.output.write_text(json.dumps({
261
+ "settings": {"max_length": args.max_length, "batch_size": args.batch_size, "fp16": args.fp16, "device": args.device, "limit": args.limit},
262
+ "datasets": {column: len(subsets[column][1]) if column in subsets else len(datasets[column]) for column in columns},
263
+ "average_over": average,
264
+ "results": results,
265
+ }, indent=2), encoding="utf-8")
266
+ print(f"\nResults written to {args.output}")
267
+ return 0
268
+
269
+
270
+ if __name__ == "__main__":
271
+ raise SystemExit(main())
benchmark/internal_results.json ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "fiqh_eval": {
3
+ "queries": 224,
4
+ "graded_pairs": 5600,
5
+ "settings": "fp16, max_length 512, candidates = production retriever top-25",
6
+ "models": {
7
+ "retriever": {
8
+ "ndcg@5": 0.5067,
9
+ "ndcg@8": 0.5482,
10
+ "ndcg@10": 0.5732,
11
+ "mrr@10": 0.4691,
12
+ "p@1": 0.5045,
13
+ "relevant_kept@8": 0.574,
14
+ "misleading@3": 0.3482,
15
+ "v3_minus_this_ndcg@10": [
16
+ 0.0961,
17
+ 0.0646,
18
+ 0.1317
19
+ ]
20
+ },
21
+ "bge-v2-m3": {
22
+ "ndcg@5": 0.6831,
23
+ "ndcg@8": 0.7173,
24
+ "ndcg@10": 0.7372,
25
+ "mrr@10": 0.6601,
26
+ "p@1": 0.7098,
27
+ "relevant_kept@8": 0.7401,
28
+ "misleading@3": 0.2946,
29
+ "v3_minus_this_ndcg@10": [
30
+ -0.0679,
31
+ -0.0943,
32
+ -0.0428
33
+ ]
34
+ },
35
+ "gte-base": {
36
+ "ndcg@5": 0.5932,
37
+ "ndcg@8": 0.6293,
38
+ "ndcg@10": 0.6567,
39
+ "mrr@10": 0.5346,
40
+ "p@1": 0.5893,
41
+ "relevant_kept@8": 0.6661,
42
+ "misleading@3": 0.3259,
43
+ "v3_minus_this_ndcg@10": [
44
+ 0.0126,
45
+ -0.0024,
46
+ 0.0266
47
+ ]
48
+ },
49
+ "mizan-v2": {
50
+ "ndcg@5": 0.5471,
51
+ "ndcg@8": 0.5929,
52
+ "ndcg@10": 0.6176,
53
+ "mrr@10": 0.4513,
54
+ "p@1": 0.4955,
55
+ "relevant_kept@8": 0.6514,
56
+ "misleading@3": 0.3705,
57
+ "v3_minus_this_ndcg@10": [
58
+ 0.0518,
59
+ 0.0253,
60
+ 0.078
61
+ ]
62
+ },
63
+ "mizan-v3": {
64
+ "ndcg@5": 0.6151,
65
+ "ndcg@8": 0.6463,
66
+ "ndcg@10": 0.6693,
67
+ "mrr@10": 0.5807,
68
+ "p@1": 0.6429,
69
+ "relevant_kept@8": 0.6764,
70
+ "misleading@3": 0.308,
71
+ "v3_minus_this_ndcg@10": [
72
+ 0.0,
73
+ 0.0,
74
+ 0.0
75
+ ]
76
+ }
77
+ }
78
+ },
79
+ "training_curve": {
80
+ "epoch": [
81
+ 0,
82
+ 1,
83
+ 2,
84
+ 3,
85
+ 4,
86
+ 5,
87
+ 6,
88
+ 7
89
+ ],
90
+ "longctx": [
91
+ 0.6917,
92
+ 0.7646,
93
+ 0.8032,
94
+ 0.8182,
95
+ 0.8206,
96
+ 0.8248,
97
+ 0.8279,
98
+ 0.8271
99
+ ],
100
+ "gemini": [
101
+ 0.7654,
102
+ 0.8098,
103
+ 0.8369,
104
+ 0.8532,
105
+ 0.8603,
106
+ 0.8611,
107
+ 0.8641,
108
+ 0.8631
109
+ ]
110
+ },
111
+ "in_training_test": {
112
+ "test": {
113
+ "ndcg": 0.8301,
114
+ "mrr": 0.7691,
115
+ "hit_at_1": 0.5749,
116
+ "positive_over_negative": 0.6213,
117
+ "contradiction_first": 0.33
118
+ },
119
+ "test_gemini": {
120
+ "ndcg": 0.8445,
121
+ "mrr": 0.7748,
122
+ "hit_at_1": 0.6179,
123
+ "positive_over_negative": 0.7276,
124
+ "contradiction_first": 0.1944
125
+ }
126
+ }
127
+ }
benchmark/plot_results.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Render the model-card charts from results_full.json and internal_results.json into ../assets/."""
2
+
3
+ import argparse
4
+ import json
5
+ from pathlib import Path
6
+
7
+ import matplotlib
8
+
9
+ matplotlib.use("Agg")
10
+ import matplotlib.pyplot as plt # noqa: E402
11
+
12
+ SCRIPT_DIR = Path(__file__).resolve().parent
13
+ ASSETS = SCRIPT_DIR.parent / "assets"
14
+ ORDER = ("mizan-v3", "mizan-v2", "gte-base", "bge-v2-m3")
15
+ LABELS = {
16
+ "mizan-v3": "Mizan-Rerank-v3 (305M)",
17
+ "mizan-v2": "Mizan-Rerank-V2 (305M)",
18
+ "gte-base": "gte-multilingual-reranker-base (305M)",
19
+ "bge-v2-m3": "bge-reranker-v2-m3 (568M)",
20
+ "retriever": "Retriever only (no reranker)",
21
+ }
22
+ COLORS = {"mizan-v3": "#0F766E", "mizan-v2": "#94A3B8", "gte-base": "#CBD5E1", "bge-v2-m3": "#F59E0B", "retriever": "#E2E8F0"}
23
+ DATASET_LABELS = {
24
+ "namaa_mrtydi": "NamaaMrTydi\n(MTEB, full)",
25
+ "namaa_mrtydi_unseen": "NamaaMrTydi\n(unseen subset)",
26
+ "arabic_hard_negatives": "Arabic Hard Negatives\n(full)",
27
+ "arabic_hard_negatives_unseen": "Arabic Hard Negatives\n(unseen subset)",
28
+ "gemini_test": "Adversarial short\n(Gemini test)",
29
+ "longctx_test": "Adversarial long-context\n(test)",
30
+ "multi_llm": "Multi-LLM\n(6 generators)",
31
+ }
32
+ HELD_OUT = ("namaa_mrtydi_unseen", "arabic_hard_negatives_unseen", "gemini_test", "longctx_test", "multi_llm")
33
+
34
+
35
+ def style(ax, title: str, ylabel: str) -> None:
36
+ ax.set_title(title, fontsize=13, fontweight="bold", loc="left")
37
+ ax.set_ylabel(ylabel)
38
+ ax.spines[["top", "right"]].set_visible(False)
39
+ ax.grid(axis="y", alpha=0.3)
40
+ ax.set_axisbelow(True)
41
+
42
+
43
+ def grouped_bars(results: dict, datasets: tuple[str, ...], metric: str, title: str, path: Path, floor: float) -> None:
44
+ fig, ax = plt.subplots(figsize=(11, 5.2), dpi=150)
45
+ width = 0.8 / len(ORDER)
46
+ for position, key in enumerate(ORDER):
47
+ values = [results[key]["datasets"][dataset][metric] for dataset in datasets]
48
+ bars = ax.bar([index + (position - (len(ORDER) - 1) / 2) * width for index in range(len(datasets))], values, width, label=LABELS[key], color=COLORS[key], edgecolor="white")
49
+ for bar, value in zip(bars, values):
50
+ ax.text(bar.get_x() + bar.get_width() / 2, value + 0.004, f"{value:.3f}", ha="center", va="bottom", fontsize=7.5, rotation=90)
51
+ ax.set_xticks(range(len(datasets)), [DATASET_LABELS[dataset] for dataset in datasets], fontsize=9)
52
+ ax.set_ylim(floor, 1.0)
53
+ style(ax, title, metric.replace("ndcg", "nDCG").replace("hit", "Hit").replace("mrr", "MRR"))
54
+ ax.legend(frameon=False, fontsize=8.5, ncol=2, loc="upper right")
55
+ fig.tight_layout()
56
+ fig.savefig(path)
57
+ plt.close(fig)
58
+
59
+
60
+ def summary_chart(results: dict, fiqh: dict, path: Path) -> None:
61
+ held_out = {key: sum(results[key]["datasets"][dataset]["ndcg@10"] for dataset in HELD_OUT) / len(HELD_OUT) for key in ORDER}
62
+ panels = (
63
+ ("Held-out benchmarks\n(mean nDCG@10 over 5 sets)", held_out),
64
+ ("Real fiqh search queries\n(nDCG@10, 224 queries)", {key: fiqh[key]["ndcg@10"] for key in ORDER}),
65
+ )
66
+ fig, axes = plt.subplots(1, 2, figsize=(12, 3.8), dpi=150)
67
+ for ax, (title, values) in zip(axes, panels):
68
+ keys = sorted(ORDER, key=lambda key: values[key])
69
+ bars = ax.barh([LABELS[key] for key in keys], [values[key] for key in keys], color=[COLORS[key] for key in keys])
70
+ for bar, key in zip(bars, keys):
71
+ ax.text(bar.get_width() + 0.003, bar.get_y() + bar.get_height() / 2, f"{values[key]:.3f}", va="center", fontsize=9, fontweight="bold" if key == "mizan-v3" else None)
72
+ low = min(values.values())
73
+ ax.set_xlim(max(0.0, low - 0.08), max(values.values()) + 0.04)
74
+ ax.set_title(title, fontsize=11, fontweight="bold", loc="left")
75
+ ax.spines[["top", "right"]].set_visible(False)
76
+ ax.grid(axis="x", alpha=0.3)
77
+ ax.set_axisbelow(True)
78
+ fig.tight_layout()
79
+ fig.savefig(path)
80
+ plt.close(fig)
81
+
82
+
83
+ def fiqh_chart(fiqh: dict, path: Path) -> None:
84
+ keys = ("retriever", *ORDER)
85
+ metrics = (("ndcg@10", "nDCG@10"), ("p@1", "Precision@1"), ("misleading@3", "Misleading doc in top 3\n(lower is better)"))
86
+ fig, axes = plt.subplots(1, 3, figsize=(13, 4.2), dpi=150)
87
+ for ax, (metric, label) in zip(axes, metrics):
88
+ values = [fiqh[key][metric] for key in keys]
89
+ bars = ax.bar(range(len(keys)), values, color=[COLORS[key] for key in keys], edgecolor="white")
90
+ for bar, value in zip(bars, values):
91
+ ax.text(bar.get_x() + bar.get_width() / 2, value + 0.005, f"{value:.3f}", ha="center", fontsize=8)
92
+ ax.set_xticks(range(len(keys)), ["Retriever", "Mizan v3", "Mizan V2", "gte-base", "bge-v2-m3"], fontsize=8.5, rotation=20)
93
+ ax.set_ylim(0, max(values) * 1.18)
94
+ style(ax, label, "")
95
+ fig.suptitle("Fiqh eval: 224 real fatwa search queries, 25 retrieved candidates each, graded 0-3", fontsize=11, x=0.01, ha="left")
96
+ fig.tight_layout()
97
+ fig.savefig(path)
98
+ plt.close(fig)
99
+
100
+
101
+ def training_chart(curve: dict, path: Path) -> None:
102
+ fig, ax = plt.subplots(figsize=(8, 4.2), dpi=150)
103
+ ax.plot(curve["epoch"], curve["longctx"], marker="o", color="#0F766E", label="Long-context adversarial (validation)")
104
+ ax.plot(curve["epoch"], curve["gemini"], marker="s", color="#F59E0B", label="Short adversarial (validation)")
105
+ best = max(range(len(curve["epoch"])), key=lambda index: curve["longctx"][index] + curve["gemini"][index])
106
+ ax.axvline(curve["epoch"][best], color="#64748B", linestyle="--", linewidth=1)
107
+ ax.text(curve["epoch"][best] - 0.1, curve["longctx"][best] - 0.012, f"released checkpoint\n(epoch {curve['epoch'][best]})", fontsize=8.5, color="#334155", ha="right", va="top")
108
+ ax.set_xticks(curve["epoch"], ["gte base"] + [str(epoch) for epoch in curve["epoch"][1:]])
109
+ ax.set_xlabel("epoch")
110
+ style(ax, "Validation nDCG@10 during training", "nDCG@10")
111
+ ax.legend(frameon=False, fontsize=9, loc="lower right")
112
+ fig.tight_layout()
113
+ fig.savefig(path)
114
+ plt.close(fig)
115
+
116
+
117
+ def main() -> int:
118
+ parser = argparse.ArgumentParser(description=__doc__)
119
+ parser.add_argument("--results", type=Path, default=SCRIPT_DIR / "results_full.json")
120
+ parser.add_argument("--internal", type=Path, default=SCRIPT_DIR / "internal_results.json")
121
+ parser.add_argument("--output-dir", type=Path, default=ASSETS)
122
+ args = parser.parse_args()
123
+ results = json.loads(args.results.read_text(encoding="utf-8"))["results"]
124
+ internal = json.loads(args.internal.read_text(encoding="utf-8"))
125
+ fiqh = internal["fiqh_eval"]["models"]
126
+ args.output_dir.mkdir(parents=True, exist_ok=True)
127
+ summary_chart(results, fiqh, args.output_dir / "summary.png")
128
+ grouped_bars(results, ("namaa_mrtydi", "namaa_mrtydi_unseen", "arabic_hard_negatives_unseen"), "ndcg@10", "Public Arabic reranking benchmarks", args.output_dir / "public_benchmarks.png", 0.6)
129
+ grouped_bars(results, ("gemini_test", "longctx_test", "multi_llm"), "ndcg@10", "Adversarial Arabic reranking (held-out test sets)", args.output_dir / "adversarial_benchmarks.png", 0.5)
130
+ grouped_bars(results, HELD_OUT, "hit@1", "Hit@1: correct passage ranked first", args.output_dir / "hit_at_1.png", 0.0)
131
+ fiqh_chart(fiqh, args.output_dir / "fiqh_eval.png")
132
+ training_chart(internal["training_curve"], args.output_dir / "training_curve.png")
133
+ for path in sorted(args.output_dir.glob("*.png")):
134
+ print(path)
135
+ return 0
136
+
137
+
138
+ if __name__ == "__main__":
139
+ raise SystemExit(main())
benchmark/results_full.json ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "settings": {
3
+ "max_length": 2048,
4
+ "batch_size": 16,
5
+ "fp16": false,
6
+ "device": "cuda",
7
+ "limit": null
8
+ },
9
+ "datasets": {
10
+ "namaa_mrtydi": 918,
11
+ "namaa_mrtydi_unseen": 573,
12
+ "arabic_hard_negatives": 12373,
13
+ "arabic_hard_negatives_unseen": 314,
14
+ "gemini_test": 602,
15
+ "longctx_test": 1703,
16
+ "multi_llm": 361
17
+ },
18
+ "average_over": [
19
+ "namaa_mrtydi_unseen",
20
+ "arabic_hard_negatives_unseen",
21
+ "gemini_test",
22
+ "longctx_test",
23
+ "multi_llm"
24
+ ],
25
+ "results": {
26
+ "mizan-v3": {
27
+ "model": "ALJIACHI/Mizan-Rerank-v3",
28
+ "seconds": 185.3771512100002,
29
+ "datasets": {
30
+ "namaa_mrtydi": {
31
+ "queries": 918,
32
+ "ndcg@10": 0.8884840152217407,
33
+ "mrr@10": 0.8518155410312274,
34
+ "hit@1": 0.7690631808278867
35
+ },
36
+ "namaa_mrtydi_unseen": {
37
+ "queries": 573,
38
+ "ndcg@10": 0.8783831689091802,
39
+ "mrr@10": 0.8384816753926702,
40
+ "hit@1": 0.7521815008726004
41
+ },
42
+ "arabic_hard_negatives": {
43
+ "queries": 12373,
44
+ "ndcg@10": 0.9102925902659809,
45
+ "mrr@10": 0.8795832323069048,
46
+ "hit@1": 0.7881677846924755
47
+ },
48
+ "arabic_hard_negatives_unseen": {
49
+ "queries": 314,
50
+ "ndcg@10": 0.920014765859062,
51
+ "mrr@10": 0.8926220806794056,
52
+ "hit@1": 0.8121019108280255
53
+ },
54
+ "gemini_test": {
55
+ "queries": 602,
56
+ "ndcg@10": 0.843170933731119,
57
+ "mrr@10": 0.7731173864894796,
58
+ "hit@1": 0.6146179401993356
59
+ },
60
+ "longctx_test": {
61
+ "queries": 1703,
62
+ "ndcg@10": 0.8280241783387726,
63
+ "mrr@10": 0.7658250146799765,
64
+ "hit@1": 0.5695830886670581
65
+ },
66
+ "multi_llm": {
67
+ "queries": 361,
68
+ "ndcg@10": 0.7536379978428707,
69
+ "mrr@10": 0.6840720221606648,
70
+ "hit@1": 0.481994459833795
71
+ }
72
+ },
73
+ "average": {
74
+ "ndcg@10": 0.8446462089362008,
75
+ "mrr@10": 0.7908236358804392,
76
+ "hit@1": 0.6460957800801629
77
+ }
78
+ },
79
+ "mizan-v2": {
80
+ "model": "ALJIACHI/Mizan-Rerank-V2",
81
+ "seconds": 193.55545658099982,
82
+ "datasets": {
83
+ "namaa_mrtydi": {
84
+ "queries": 918,
85
+ "ndcg@10": 0.8312075449070145,
86
+ "mrr@10": 0.7755446623093682,
87
+ "hit@1": 0.6492374727668845
88
+ },
89
+ "namaa_mrtydi_unseen": {
90
+ "queries": 573,
91
+ "ndcg@10": 0.8220284961389768,
92
+ "mrr@10": 0.7635253054101222,
93
+ "hit@1": 0.6335078534031413
94
+ },
95
+ "arabic_hard_negatives": {
96
+ "queries": 12373,
97
+ "ndcg@10": 0.8447173491693125,
98
+ "mrr@10": 0.7919327029284194,
99
+ "hit@1": 0.6433362967752364
100
+ },
101
+ "arabic_hard_negatives_unseen": {
102
+ "queries": 314,
103
+ "ndcg@10": 0.8779930928755129,
104
+ "mrr@10": 0.8361995753715499,
105
+ "hit@1": 0.7133757961783439
106
+ },
107
+ "gemini_test": {
108
+ "queries": 602,
109
+ "ndcg@10": 0.6965218593427552,
110
+ "mrr@10": 0.5952104097452935,
111
+ "hit@1": 0.34053156146179403
112
+ },
113
+ "longctx_test": {
114
+ "queries": 1703,
115
+ "ndcg@10": 0.6202716837624505,
116
+ "mrr@10": 0.4982537818415681,
117
+ "hit@1": 0.22665883734586026
118
+ },
119
+ "multi_llm": {
120
+ "queries": 361,
121
+ "ndcg@10": 0.5998300071547739,
122
+ "mrr@10": 0.4628611001187179,
123
+ "hit@1": 0.19667590027700832
124
+ }
125
+ },
126
+ "average": {
127
+ "ndcg@10": 0.7233290278548938,
128
+ "mrr@10": 0.6312100344974503,
129
+ "hit@1": 0.42214998973322954
130
+ }
131
+ },
132
+ "gte-base": {
133
+ "model": "Alibaba-NLP/gte-multilingual-reranker-base",
134
+ "seconds": 190.08565529099997,
135
+ "datasets": {
136
+ "namaa_mrtydi": {
137
+ "queries": 918,
138
+ "ndcg@10": 0.8873742282014164,
139
+ "mrr@10": 0.8497639796659405,
140
+ "hit@1": 0.7549019607843137
141
+ },
142
+ "namaa_mrtydi_unseen": {
143
+ "queries": 573,
144
+ "ndcg@10": 0.8770828555665509,
145
+ "mrr@10": 0.8361547411285631,
146
+ "hit@1": 0.7347294938917975
147
+ },
148
+ "arabic_hard_negatives": {
149
+ "queries": 12373,
150
+ "ndcg@10": 0.8960300465581245,
151
+ "mrr@10": 0.8606549206605781,
152
+ "hit@1": 0.7599612058514508
153
+ },
154
+ "arabic_hard_negatives_unseen": {
155
+ "queries": 314,
156
+ "ndcg@10": 0.9061892172759463,
157
+ "mrr@10": 0.8742569002123142,
158
+ "hit@1": 0.7834394904458599
159
+ },
160
+ "gemini_test": {
161
+ "queries": 602,
162
+ "ndcg@10": 0.7565245351746636,
163
+ "mrr@10": 0.6825581395348836,
164
+ "hit@1": 0.46179401993355484
165
+ },
166
+ "longctx_test": {
167
+ "queries": 1703,
168
+ "ndcg@10": 0.6747391472836209,
169
+ "mrr@10": 0.5765022509297318,
170
+ "hit@1": 0.31356429829712273
171
+ },
172
+ "multi_llm": {
173
+ "queries": 361,
174
+ "ndcg@10": 0.6992032289960537,
175
+ "mrr@10": 0.6104801477377655,
176
+ "hit@1": 0.3601108033240997
177
+ }
178
+ },
179
+ "average": {
180
+ "ndcg@10": 0.7827477968593671,
181
+ "mrr@10": 0.7159904359086516,
182
+ "hit@1": 0.5307276211784869
183
+ }
184
+ },
185
+ "bge-v2-m3": {
186
+ "model": "BAAI/bge-reranker-v2-m3",
187
+ "seconds": 416.3012057730002,
188
+ "datasets": {
189
+ "namaa_mrtydi": {
190
+ "queries": 918,
191
+ "ndcg@10": 0.8995429966626842,
192
+ "mrr@10": 0.8662309368191722,
193
+ "hit@1": 0.7864923747276689
194
+ },
195
+ "namaa_mrtydi_unseen": {
196
+ "queries": 573,
197
+ "ndcg@10": 0.8897374272909373,
198
+ "mrr@10": 0.8530831878999419,
199
+ "hit@1": 0.7626527050610821
200
+ },
201
+ "arabic_hard_negatives": {
202
+ "queries": 12373,
203
+ "ndcg@10": 0.9082092244432917,
204
+ "mrr@10": 0.8771451278321075,
205
+ "hit@1": 0.7915622726905358
206
+ },
207
+ "arabic_hard_negatives_unseen": {
208
+ "queries": 314,
209
+ "ndcg@10": 0.9034780246477423,
210
+ "mrr@10": 0.871072186836518,
211
+ "hit@1": 0.7866242038216561
212
+ },
213
+ "gemini_test": {
214
+ "queries": 602,
215
+ "ndcg@10": 0.7938330217019347,
216
+ "mrr@10": 0.7374031007751938,
217
+ "hit@1": 0.5448504983388704
218
+ },
219
+ "longctx_test": {
220
+ "queries": 1703,
221
+ "ndcg@10": 0.687281090274787,
222
+ "mrr@10": 0.5878505718200374,
223
+ "hit@1": 0.33646506165590134
224
+ },
225
+ "multi_llm": {
226
+ "queries": 361,
227
+ "ndcg@10": 0.7365014887447433,
228
+ "mrr@10": 0.6643120960295475,
229
+ "hit@1": 0.4515235457063712
230
+ }
231
+ },
232
+ "average": {
233
+ "ndcg@10": 0.8021662105320289,
234
+ "mrr@10": 0.7427442286722477,
235
+ "hit@1": 0.5764232029167762
236
+ }
237
+ }
238
+ },
239
+ "note": "Public sets from the Hub; gemini_test, longctx_test and multi_llm are internal listwise test sets (not released), scored with --listwise."
240
+ }
benchmark/seen_in_training.json ADDED
The diff for this file is too large to render. See raw diff
 
config.json ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "NewForSequenceClassification"
4
+ ],
5
+ "attention_probs_dropout_prob": 0.0,
6
+ "auto_map": {
7
+ "AutoConfig": "configuration.NewConfig",
8
+ "AutoModel": "Alibaba-NLP/new-impl--modeling.NewModel",
9
+ "AutoModelForMaskedLM": "Alibaba-NLP/new-impl--modeling.NewForMaskedLM",
10
+ "AutoModelForMultipleChoice": "Alibaba-NLP/new-impl--modeling.NewForMultipleChoice",
11
+ "AutoModelForQuestionAnswering": "Alibaba-NLP/new-impl--modeling.NewForQuestionAnswering",
12
+ "AutoModelForSequenceClassification": "modeling.NewForSequenceClassification",
13
+ "AutoModelForTokenClassification": "Alibaba-NLP/new-impl--modeling.NewForTokenClassification"
14
+ },
15
+ "classifier_dropout": 0.0,
16
+ "hidden_act": "gelu",
17
+ "hidden_dropout_prob": 0.1,
18
+ "hidden_size": 768,
19
+ "id2label": {
20
+ "0": "LABEL_0"
21
+ },
22
+ "initializer_range": 0.02,
23
+ "intermediate_size": 3072,
24
+ "label2id": {
25
+ "LABEL_0": 0
26
+ },
27
+ "layer_norm_eps": 1e-12,
28
+ "layer_norm_type": "layer_norm",
29
+ "logn_attention_clip1": false,
30
+ "logn_attention_scale": false,
31
+ "max_position_embeddings": 8192,
32
+ "model_type": "new",
33
+ "num_attention_heads": 12,
34
+ "num_hidden_layers": 12,
35
+ "pack_qkv": true,
36
+ "pad_token_id": 1,
37
+ "position_embedding_type": "rope",
38
+ "rope_scaling": {
39
+ "factor": 8.0,
40
+ "type": "ntk"
41
+ },
42
+ "rope_theta": 20000,
43
+ "torch_dtype": "float32",
44
+ "transformers_version": "4.55.4",
45
+ "type_vocab_size": 1,
46
+ "unpad_inputs": false,
47
+ "use_memory_efficient_attention": false,
48
+ "vocab_size": 250048
49
+ }
config_sentence_transformers.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "__version__": {
3
+ "pytorch": "2.8.0+cu126",
4
+ "sentence_transformers": "5.4.1",
5
+ "transformers": "4.55.4"
6
+ },
7
+ "activation_fn": "torch.nn.modules.activation.Sigmoid",
8
+ "default_prompt_name": null,
9
+ "model_type": "CrossEncoder",
10
+ "prompts": {}
11
+ }
configuration.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2024 The GTE Team Authors and Alibaba Group.
3
+ # Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """ NEW model configuration"""
17
+ from transformers.configuration_utils import PretrainedConfig
18
+ from transformers.utils import logging
19
+
20
+ logger = logging.get_logger(__name__)
21
+
22
+
23
+ class NewConfig(PretrainedConfig):
24
+ r"""
25
+ This is the configuration class to store the configuration of a [`NewModel`] or a [`TFNewModel`]. It is used to
26
+ instantiate a NEW model according to the specified arguments, defining the model architecture. Instantiating a
27
+ configuration with the defaults will yield a similar configuration to that of the NEW
28
+ [izhx/new-base-en](https://huggingface.co/izhx/new-base-en) architecture.
29
+
30
+ Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
31
+ documentation from [`PretrainedConfig`] for more information.
32
+
33
+
34
+ Args:
35
+ vocab_size (`int`, *optional*, defaults to 30522):
36
+ Vocabulary size of the NEW model. Defines the number of different tokens that can be represented by the
37
+ `inputs_ids` passed when calling [`NewModel`] or [`TFNewModel`].
38
+ hidden_size (`int`, *optional*, defaults to 768):
39
+ Dimensionality of the encoder layers and the pooler layer.
40
+ num_hidden_layers (`int`, *optional*, defaults to 12):
41
+ Number of hidden layers in the Transformer encoder.
42
+ num_attention_heads (`int`, *optional*, defaults to 12):
43
+ Number of attention heads for each attention layer in the Transformer encoder.
44
+ intermediate_size (`int`, *optional*, defaults to 3072):
45
+ Dimensionality of the "intermediate" (often named feed-forward) layer in the Transformer encoder.
46
+ hidden_act (`str` or `Callable`, *optional*, defaults to `"gelu"`):
47
+ The non-linear activation function (function or string) in the encoder and pooler. If string, `"gelu"`,
48
+ `"relu"`, `"silu"` and `"gelu_new"` are supported.
49
+ hidden_dropout_prob (`float`, *optional*, defaults to 0.1):
50
+ The dropout probability for all fully connected layers in the embeddings, encoder, and pooler.
51
+ attention_probs_dropout_prob (`float`, *optional*, defaults to 0.1):
52
+ The dropout ratio for the attention probabilities.
53
+ max_position_embeddings (`int`, *optional*, defaults to 512):
54
+ The maximum sequence length that this model might ever be used with. Typically set this to something large
55
+ just in case (e.g., 512 or 1024 or 2048).
56
+ type_vocab_size (`int`, *optional*, defaults to 2):
57
+ The vocabulary size of the `token_type_ids` passed when calling [`NewModel`] or [`TFNewModel`].
58
+ initializer_range (`float`, *optional*, defaults to 0.02):
59
+ The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
60
+ layer_norm_eps (`float`, *optional*, defaults to 1e-12):
61
+ The epsilon used by the layer normalization layers.
62
+ position_embedding_type (`str`, *optional*, defaults to `"rope"`):
63
+ Type of position embedding. Choose one of `"absolute"`, `"rope"`.
64
+ rope_theta (`float`, *optional*, defaults to 10000.0):
65
+ The base period of the RoPE embeddings.
66
+ rope_scaling (`Dict`, *optional*):
67
+ Dictionary containing the scaling configuration for the RoPE embeddings. Currently supports two scaling
68
+ strategies: linear and dynamic. Their scaling factor must be a float greater than 1. The expected format is
69
+ `{"type": strategy name, "factor": scaling factor}`. When using this flag, don't update
70
+ `max_position_embeddings` to the expected new maximum. See the following thread for more information on how
71
+ these scaling strategies behave:
72
+ https://www.reddit.com/r/LocalLLaMA/comments/14mrgpr/dynamically_scaled_rope_further_increases/. This is an
73
+ experimental feature, subject to breaking API changes in future versions.
74
+ classifier_dropout (`float`, *optional*):
75
+ The dropout ratio for the classification head.
76
+
77
+ Examples:
78
+
79
+ ```python
80
+ >>> from transformers import NewConfig, NewModel
81
+
82
+ >>> # Initializing a NEW izhx/new-base-en style configuration
83
+ >>> configuration = NewConfig()
84
+
85
+ >>> # Initializing a model (with random weights) from the izhx/new-base-en style configuration
86
+ >>> model = NewModel(configuration)
87
+
88
+ >>> # Accessing the model configuration
89
+ >>> configuration = model.config
90
+ ```"""
91
+
92
+ model_type = "new"
93
+
94
+ def __init__(
95
+ self,
96
+ vocab_size=30528,
97
+ hidden_size=768,
98
+ num_hidden_layers=12,
99
+ num_attention_heads=12,
100
+ intermediate_size=3072,
101
+ hidden_act="gelu",
102
+ hidden_dropout_prob=0.1,
103
+ attention_probs_dropout_prob=0.0,
104
+ max_position_embeddings=2048,
105
+ type_vocab_size=1,
106
+ initializer_range=0.02,
107
+ layer_norm_type='layer_norm',
108
+ layer_norm_eps=1e-12,
109
+ # pad_token_id=0,
110
+ position_embedding_type="rope",
111
+ rope_theta=10000.0,
112
+ rope_scaling=None,
113
+ classifier_dropout=None,
114
+ pack_qkv=True,
115
+ unpad_inputs=False,
116
+ use_memory_efficient_attention=False,
117
+ logn_attention_scale=False,
118
+ logn_attention_clip1=False,
119
+ **kwargs,
120
+ ):
121
+ super().__init__(**kwargs)
122
+
123
+ self.vocab_size = vocab_size
124
+ self.hidden_size = hidden_size
125
+ self.num_hidden_layers = num_hidden_layers
126
+ self.num_attention_heads = num_attention_heads
127
+ self.hidden_act = hidden_act
128
+ self.intermediate_size = intermediate_size
129
+ self.hidden_dropout_prob = hidden_dropout_prob
130
+ self.attention_probs_dropout_prob = attention_probs_dropout_prob
131
+ self.max_position_embeddings = max_position_embeddings
132
+ self.type_vocab_size = type_vocab_size
133
+ self.initializer_range = initializer_range
134
+ self.layer_norm_type = layer_norm_type
135
+ self.layer_norm_eps = layer_norm_eps
136
+ self.position_embedding_type = position_embedding_type
137
+ self.rope_theta = rope_theta
138
+ self.rope_scaling = rope_scaling
139
+ self.classifier_dropout = classifier_dropout
140
+
141
+ self.pack_qkv = pack_qkv
142
+ self.unpad_inputs = unpad_inputs
143
+ self.use_memory_efficient_attention = use_memory_efficient_attention
144
+ self.logn_attention_scale = logn_attention_scale
145
+ self.logn_attention_clip1 = logn_attention_clip1
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a10bab1ac9b78b9d882a0a5c604a5c3d9aed472e8c1128ea0cc71c2775816c84
3
+ size 1223854204
modeling.py ADDED
@@ -0,0 +1,1418 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2024 The GTE Team Authors and Alibaba Group.
3
+ # Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """PyTorch NEW model."""
17
+
18
+ import math
19
+ from dataclasses import dataclass
20
+ from typing import List, Optional, Tuple, Union
21
+
22
+ import torch
23
+ import torch.utils.checkpoint
24
+ from torch import nn
25
+
26
+ from transformers.activations import ACT2FN
27
+ from transformers.modeling_outputs import (
28
+ BaseModelOutput,
29
+ BaseModelOutputWithPooling,
30
+ MaskedLMOutput,
31
+ MultipleChoiceModelOutput,
32
+ QuestionAnsweringModelOutput,
33
+ SequenceClassifierOutput,
34
+ ModelOutput,
35
+ )
36
+ from transformers.modeling_utils import PreTrainedModel
37
+ from transformers.utils import logging
38
+
39
+ try:
40
+ import xformers.ops as xops
41
+ except ImportError as e:
42
+ xops = None
43
+
44
+ from .configuration import NewConfig
45
+
46
+
47
+ logger = logging.get_logger(__name__)
48
+
49
+
50
+ # Adapted from https://github.com/HazyResearch/flash-attention/blob/main/flash_attn/bert_padding.py
51
+ # Which was adapted from https://github.com/mlcommons/training_results_v1.1/blob/main/NVIDIA/benchmarks/bert/implementations/pytorch/padding.py
52
+ class IndexFirstAxis(torch.autograd.Function):
53
+ @staticmethod
54
+ def forward(ctx, input, indices):
55
+ ctx.save_for_backward(indices)
56
+ assert input.ndim >= 2
57
+ ctx.first_axis_dim, other_shape = input.shape[0], input.shape[1:]
58
+ second_dim = other_shape.numel()
59
+ # TD [2022-03-04] For some reason torch.gather is a bit faster than indexing.
60
+ # return input[indices]
61
+ # return torch.gather(
62
+ # rearrange(input, "b ... -> b (...)"), 0, repeat(indices, "z -> z d", d=second_dim)
63
+ # ).reshape(-1, *other_shape)
64
+ return torch.gather(
65
+ input.view(ctx.first_axis_dim, second_dim),
66
+ 0,
67
+ indices.unsqueeze(-1).expand(indices.size(0), second_dim)
68
+ ).reshape(-1, *other_shape)
69
+
70
+ @staticmethod
71
+ def backward(ctx, grad_output):
72
+ (indices,) = ctx.saved_tensors
73
+ assert grad_output.ndim >= 2
74
+ other_shape = grad_output.shape[1:]
75
+ # grad_output = rearrange(grad_output, "b ... -> b (...)")
76
+ grad_output = grad_output.view(grad_output.size(0), other_shape.numel())
77
+ grad_input = torch.zeros(
78
+ [ctx.first_axis_dim, grad_output.shape[1]],
79
+ device=grad_output.device,
80
+ dtype=grad_output.dtype,
81
+ )
82
+ # TD [2022-03-04] For some reason torch.scatter is a bit faster than indexing.
83
+ # grad_input[indices] = grad_output
84
+ # grad_input.scatter_(0, repeat(indices, "z -> z d", d=grad_output.shape[1]), grad_output)
85
+ grad_input.scatter_(
86
+ 0, indices.unsqueeze(-1).expand(indices.size(0), grad_output.size(1)), grad_output
87
+ )
88
+ return grad_input.reshape(ctx.first_axis_dim, *other_shape), None
89
+
90
+
91
+ index_first_axis = IndexFirstAxis.apply
92
+
93
+
94
+ def unpad_input(hidden_states, attention_mask=None, indices=None):
95
+ """
96
+ Arguments:
97
+ hidden_states: (batch, seqlen, ...)
98
+ attention_mask: (batch, seqlen), bool / int, 1 means valid and 0 means not valid.
99
+ indices: (total_nnz), the indices of non-masked tokens from the flattened input sequence.
100
+ Return:
101
+ hidden_states: (total_nnz, ...), where total_nnz = number of tokens in selected in attention_mask.
102
+ """
103
+ if indices is None:
104
+ assert attention_mask is not None
105
+ indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
106
+
107
+ # TD [2022-03-04] We don't want to index with a bool mask, because Pytorch will expand the
108
+ # bool mask, then call nonzero to get the indices, then index with those. The indices is @dim
109
+ # times larger than it needs to be, wasting memory. It's faster and more memory-efficient to
110
+ # index with integer indices. Moreover, torch's index is a bit slower than it needs to be,
111
+ # so we write custom forward and backward to make it a bit faster.
112
+ hidden_states = hidden_states.view(-1, *hidden_states.shape[2:])
113
+ return index_first_axis(hidden_states, indices)
114
+
115
+
116
+ class IndexPutFirstAxis(torch.autograd.Function):
117
+ @staticmethod
118
+ def forward(
119
+ ctx,
120
+ values: torch.Tensor,
121
+ indices: torch.Tensor,
122
+ first_axis_dim
123
+ ) -> torch.Tensor:
124
+ ctx.save_for_backward(indices)
125
+ assert indices.ndim == 1
126
+ assert values.ndim >= 2
127
+ output = torch.zeros(
128
+ first_axis_dim, *values.shape[1:], device=values.device, dtype=values.dtype
129
+ )
130
+ output[indices] = values
131
+ return output
132
+
133
+ @staticmethod
134
+ def backward(ctx, grad_output: torch.Tensor) -> Tuple[torch.Tensor, None, None]:
135
+ indices, = ctx.saved_tensors
136
+ grad_values = grad_output[indices]
137
+ return grad_values, None, None
138
+
139
+
140
+ index_put_first_axis = IndexPutFirstAxis.apply
141
+
142
+
143
+ def pad_input(inputs: torch.Tensor, indices: torch.Tensor, batch: int, seqlen: int) -> torch.Tensor:
144
+ """Add padding to sequences.
145
+
146
+ Arguments:
147
+ inputs: (total_nnz, ...), where total_nnz = number of tokens in selected in attention_mask.
148
+ indices: (total_nnz), `indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()`
149
+ batch: int batch_size
150
+ seqlen: int max sequence length
151
+
152
+ Returns:
153
+ inputs: (batch, seqlen, ...)
154
+ """
155
+ output = index_put_first_axis(inputs, indices, batch * seqlen)
156
+ return output.view(batch, seqlen, *inputs.shape[1:])
157
+
158
+
159
+ def rotate_half(x):
160
+ """Rotates half the hidden dims of the input."""
161
+ x1 = x[..., : x.shape[-1] // 2]
162
+ x2 = x[..., x.shape[-1] // 2 :]
163
+ return torch.cat((-x2, x1), dim=-1)
164
+
165
+
166
+ def apply_rotary_pos_emb(q, k, cos, sin):
167
+ """Applies Rotary Position Embedding to the query and key tensors.
168
+
169
+ Args:
170
+ q (`torch.Tensor`): The query tensor.
171
+ k (`torch.Tensor`): The key tensor.
172
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
173
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
174
+ Returns:
175
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
176
+ """
177
+ cos, sin = cos.to(q.dtype), sin.to(q.dtype)
178
+ q_embed = (q * cos) + (rotate_half(q) * sin)
179
+ k_embed = (k * cos) + (rotate_half(k) * sin)
180
+ return q_embed, k_embed
181
+
182
+
183
+ class RotaryEmbedding(torch.nn.Module):
184
+ def __init__(self, dim, max_position_embeddings=512, base=10000.0, device=None):
185
+ super().__init__()
186
+
187
+ self.dim = dim
188
+ self.max_position_embeddings = max_position_embeddings
189
+ self.base = base
190
+ inv_freq = 1.0 / (self.base ** (torch.arange(0, self.dim, 2).float().to(device) / self.dim))
191
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
192
+
193
+ # Build here to make `torch.jit.trace` work.
194
+ self._set_cos_sin_cache(
195
+ seq_len=max_position_embeddings, device=self.inv_freq.device, dtype=torch.get_default_dtype()
196
+ )
197
+
198
+ def _set_cos_sin_cache(self, seq_len, device, dtype):
199
+ self.max_seq_len_cached = seq_len
200
+ t = torch.arange(self.max_seq_len_cached, device=device, dtype=torch.float32)
201
+
202
+ freqs = torch.einsum("i,j->ij", t, self.inv_freq)
203
+ # Different from paper, but it uses a different permutation in order to obtain the same calculation
204
+ emb = torch.cat((freqs, freqs), dim=-1)
205
+ self.register_buffer("cos_cached", emb.cos().to(dtype), persistent=False)
206
+ self.register_buffer("sin_cached", emb.sin().to(dtype), persistent=False)
207
+
208
+ def forward(self, x, seq_len=None):
209
+ # x: [bs, num_attention_heads, seq_len, head_size]
210
+ if seq_len > self.max_seq_len_cached:
211
+ self._set_cos_sin_cache(seq_len=seq_len, device=x.device, dtype=x.dtype)
212
+
213
+ return (
214
+ self.cos_cached[:seq_len, ...].to(dtype=x.dtype),
215
+ self.sin_cached[:seq_len, ...].to(dtype=x.dtype),
216
+ )
217
+
218
+
219
+ class NTKScalingRotaryEmbedding(RotaryEmbedding):
220
+ """RotaryEmbedding extended with fixed and mixed NTK scaling. https://kexue.fm/archives/9706 """
221
+
222
+ def __init__(self, dim, max_position_embeddings=512, base=10000, device=None, scaling_factor=1.0, mixed_b=None):
223
+ self.scaling_factor = scaling_factor
224
+ self.mixed_b = mixed_b
225
+ super().__init__(dim, max_position_embeddings, base, device)
226
+ max_position_embeddings = max_position_embeddings * self.scaling_factor
227
+ self._set_cos_sin_cache(max_position_embeddings, self.inv_freq.device, torch.get_default_dtype())
228
+
229
+ def _set_cos_sin_cache(self, seq_len, device, dtype):
230
+ self.max_seq_len_cached = seq_len
231
+
232
+ if seq_len > self.max_position_embeddings:
233
+ base = self.base * (self.scaling_factor if self.mixed_b is None else 1)
234
+ inv_freq = 1.0 / (base ** (torch.arange(0, self.dim, 2).float().to(device) / self.dim))
235
+
236
+ if self.mixed_b is None:
237
+ inv_freq = inv_freq / self.scaling_factor ** (2 / self.dim) # (6)
238
+ else:
239
+ a = torch.tensor(self.scaling_factor).log() / (self.dim / 2) ** self.mixed_b # (13)
240
+ lambda_1_m = (a * torch.arange(1, self.dim // 2 + 1).float().to(device) ** self.mixed_b).exp() # (12)
241
+ inv_freq = inv_freq / lambda_1_m # (10)
242
+
243
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
244
+
245
+ t = torch.arange(self.max_seq_len_cached, device=device, dtype=torch.float32)
246
+
247
+ freqs = torch.einsum("i,j->ij", t, self.inv_freq)
248
+ # Different from paper, but it uses a different permutation in order to obtain the same calculation
249
+ emb = torch.cat((freqs, freqs), dim=-1)
250
+ self.register_buffer("cos_cached", emb.cos().to(dtype), persistent=False)
251
+ self.register_buffer("sin_cached", emb.sin().to(dtype), persistent=False)
252
+
253
+
254
+ class RMSNorm(nn.Module):
255
+ def __init__(self, hidden_size, eps=1e-6):
256
+ """
257
+ RMSNorm is equivalent to T5LayerNorm
258
+ """
259
+ super().__init__()
260
+ self.weight = nn.Parameter(torch.ones(hidden_size))
261
+ self.variance_epsilon = eps
262
+
263
+ def forward(self, hidden_states):
264
+ input_dtype = hidden_states.dtype
265
+ hidden_states = hidden_states.to(torch.float32)
266
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
267
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
268
+ return self.weight * hidden_states.to(input_dtype)
269
+
270
+
271
+ LAYER_NORM = {
272
+ 'layer_norm': nn.LayerNorm,
273
+ 'rms_norm': RMSNorm
274
+ }
275
+
276
+
277
+ class NewEmbeddings(nn.Module):
278
+ """
279
+ Embedding and Unpadding.
280
+ """
281
+
282
+ def __init__(self, config: NewConfig):
283
+ super().__init__()
284
+ self.padding_idx = config.pad_token_id
285
+ self.word_embeddings = nn.Embedding(
286
+ config.vocab_size, config.hidden_size, padding_idx=self.padding_idx
287
+ )
288
+
289
+ self.position_embedding_type = config.position_embedding_type
290
+ if self.position_embedding_type == 'absolute':
291
+ self.position_embeddings = nn.Embedding(
292
+ config.max_position_embeddings, config.hidden_size, padding_idx=self.padding_idx
293
+ )
294
+ elif self.position_embedding_type == 'rope':
295
+ self._init_rope(config)
296
+ else:
297
+ raise ValueError
298
+
299
+ self.type_vocab_size = config.type_vocab_size
300
+ if self.type_vocab_size > 0:
301
+ self.token_type_embeddings = nn.Embedding(config.type_vocab_size, config.hidden_size)
302
+
303
+ # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load
304
+ # any TensorFlow checkpoint file
305
+ self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
306
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
307
+ # position_ids is contiguous in memory and excluded when serialized
308
+ self.register_buffer(
309
+ "position_ids", torch.arange(config.max_position_embeddings), persistent=False
310
+ )
311
+
312
+ def _init_rope(self, config):
313
+ kwargs = dict(
314
+ dim=int(config.hidden_size / config.num_attention_heads),
315
+ max_position_embeddings=config.max_position_embeddings,
316
+ base=config.rope_theta
317
+ )
318
+ if config.rope_scaling is None:
319
+ self.rotary_emb = RotaryEmbedding(**kwargs)
320
+ else:
321
+ kwargs.update(scaling_factor=config.rope_scaling["factor"])
322
+ scaling_type = config.rope_scaling["type"]
323
+ if scaling_type == 'ntk':
324
+ kwargs.update(mixed_b=config.rope_scaling.get('mixed_b', None))
325
+ self.rotary_emb = NTKScalingRotaryEmbedding(**kwargs)
326
+ # elif scaling_type == "linear":
327
+ # self.rotary_emb = LinearScalingRotaryEmbedding(**kwargs)
328
+ # elif scaling_type == "dynamic":
329
+ # self.rotary_emb = DynamicNTKScalingRotaryEmbedding(**kwargs)
330
+ else:
331
+ raise ValueError(f"Unknown RoPE scaling type {scaling_type}")
332
+
333
+ def forward(
334
+ self,
335
+ unpad_inputs: bool,
336
+ input_ids: Optional[torch.Tensor] = None,
337
+ attention_mask: Optional[torch.Tensor] = None,
338
+ length: Optional[List[int]] = None,
339
+ token_type_ids: Optional[torch.Tensor] = None,
340
+ position_ids: Optional[torch.Tensor] = None,
341
+ inputs_embeds: Optional[torch.Tensor] = None,
342
+ ) -> Tuple[torch.Tensor, torch.Tensor, Optional[Tuple], Optional[List[int]]]:
343
+ """
344
+ """
345
+ if inputs_embeds is None:
346
+ device, input_shape = input_ids.device, input_ids.shape
347
+ else:
348
+ device, input_shape = inputs_embeds.device, inputs_embeds.shape[:2]
349
+ batch_size, seq_length = input_shape
350
+
351
+ # Set attention_mask if it's None
352
+ if attention_mask is None:
353
+ attention_mask = torch.ones(input_shape, device=device)
354
+ if length is not None:
355
+ for i, l in enumerate(length):
356
+ attention_mask[i, l:] = 0
357
+
358
+ # Set attention_mask_bool for unpadding
359
+ if unpad_inputs:
360
+ attention_mask_bool = attention_mask.bool()
361
+ if length is None:
362
+ length = attention_mask.sum(-1).tolist()
363
+
364
+ # Get word embeddings
365
+ if inputs_embeds is None:
366
+ if unpad_inputs:
367
+ input_ids = input_ids[attention_mask_bool].unsqueeze(0)
368
+ inputs_embeds = self.word_embeddings(input_ids)
369
+ else:
370
+ if unpad_inputs:
371
+ inputs_embeds = inputs_embeds[attention_mask_bool].unsqueeze(0)
372
+ embeddings = inputs_embeds
373
+
374
+ # Set and unpad position_ids
375
+ if position_ids is None:
376
+ if seq_length > self.position_ids.size(0):
377
+ self.register_buffer(
378
+ "position_ids", torch.arange(seq_length, device=embeddings.device), persistent=False
379
+ )
380
+ if unpad_inputs:
381
+ # [1, cumsum_seq_len]
382
+ position_ids = torch.cat([self.position_ids[:l] for l in length]).unsqueeze(0)
383
+ else:
384
+ # [bs, seq_len]
385
+ position_ids = self.position_ids[:seq_length].expand(batch_size, -1)
386
+ elif unpad_inputs:
387
+ position_ids = position_ids[attention_mask_bool].unsqueeze(0) # [1, cumsum_seq_len]
388
+
389
+ # Compute rotary embedding
390
+ if self.position_embedding_type == 'rope':
391
+ rope_cos, rope_sin = self.rotary_emb(inputs_embeds, seq_len=seq_length)
392
+ rope_cos = rope_cos[position_ids].unsqueeze(2) # [bs, seq_len, 1, dim]
393
+ rope_sin = rope_sin[position_ids].unsqueeze(2) # [bs, seq_len, 1, dim]
394
+ rope_embeds = rope_cos, rope_sin
395
+ else:
396
+ rope_embeds = None
397
+
398
+ if self.type_vocab_size > 0:
399
+ if token_type_ids is None:
400
+ token_type_ids = position_ids.mul(0)
401
+ else:
402
+ if self.type_vocab_size < 2:
403
+ token_type_ids.mul_(0)
404
+ if unpad_inputs:
405
+ token_type_ids = token_type_ids[attention_mask_bool].unsqueeze(0)
406
+
407
+ token_type_embeddings = self.token_type_embeddings(token_type_ids)
408
+ embeddings = embeddings + token_type_embeddings
409
+
410
+ # BERT position
411
+ if self.position_embedding_type == "absolute":
412
+ position_embeddings = self.position_embeddings(position_ids)
413
+ embeddings = embeddings + position_embeddings
414
+
415
+ embeddings = self.LayerNorm(embeddings)
416
+ embeddings = self.dropout(embeddings)
417
+
418
+ return embeddings, attention_mask, rope_embeds, length
419
+
420
+
421
+ class NewAttention(nn.Module):
422
+ def __init__(self, config: NewConfig, pack_qkv=None, use_memory_efficient_attention=None):
423
+ super().__init__()
424
+ self.config = config
425
+ if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"):
426
+ raise ValueError(
427
+ f"The hidden size ({config.hidden_size}) is not a multiple of the number of attention "
428
+ f"heads ({config.num_attention_heads})"
429
+ )
430
+
431
+ self.hidden_size = config.hidden_size
432
+ self.num_attention_heads = config.num_attention_heads
433
+ self.attention_head_size = int(config.hidden_size / config.num_attention_heads)
434
+ self.all_head_size = self.num_attention_heads * self.attention_head_size
435
+
436
+ if pack_qkv is None:
437
+ pack_qkv = config.pack_qkv
438
+ self.pack_qkv = pack_qkv
439
+
440
+ if self.pack_qkv:
441
+ self.qkv_proj = nn.Linear(config.hidden_size, self.all_head_size * 3, bias=True)
442
+ else:
443
+ self.q_proj = nn.Linear(config.hidden_size, self.all_head_size, bias=True)
444
+ self.k_proj = nn.Linear(config.hidden_size, self.all_head_size, bias=True)
445
+ self.v_proj = nn.Linear(config.hidden_size, self.all_head_size, bias=True)
446
+
447
+ self.dropout = nn.Dropout(config.attention_probs_dropout_prob)
448
+ self.o_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=True)
449
+
450
+ if use_memory_efficient_attention is None:
451
+ use_memory_efficient_attention = self.config.use_memory_efficient_attention
452
+ self.use_memory_efficient_attention = use_memory_efficient_attention
453
+ self.memory_efficient_attention = None if xops is None else xops.memory_efficient_attention
454
+ if self.use_memory_efficient_attention:
455
+ assert self.memory_efficient_attention is not None, 'please install xformers'
456
+
457
+ def forward(
458
+ self,
459
+ hidden_states: torch.Tensor,
460
+ attention_bias: torch.FloatTensor,
461
+ rope_embeds: Optional[Tuple[torch.FloatTensor, torch.FloatTensor]] = None,
462
+ padding_inputs: Optional[Tuple] = None, # indices, batch, seqlen
463
+ attention_scale: Optional[torch.FloatTensor] = None,
464
+ head_mask: Optional[torch.FloatTensor] = None,
465
+ output_attentions: Optional[bool] = False,
466
+ qkv_inputs: Optional[Tuple] = None, # For RetroMAE
467
+ ) -> Tuple[torch.Tensor, ...]:
468
+ shape_hd = (self.num_attention_heads, self.attention_head_size)
469
+ # qkv
470
+ if self.pack_qkv and qkv_inputs is None:
471
+ qkv_pack = self.qkv_proj(hidden_states).split(self.all_head_size, dim=-1)
472
+ else:
473
+ if qkv_inputs is None:
474
+ qkv_inputs = (hidden_states, hidden_states, hidden_states)
475
+ qkv_pack = [
476
+ getattr(self, n + '_proj')(s) for s, n in zip(qkv_inputs, 'qkv')
477
+ ]
478
+ query_states, key_states, value_states = [t.view(t.shape[:-1] + shape_hd) for t in qkv_pack]
479
+
480
+ if self.config.position_embedding_type == 'rope':
481
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, *rope_embeds)
482
+
483
+ dtype = query_states.dtype
484
+
485
+ if self.config.logn_attention_scale and attention_scale is not None:
486
+ # https://kexue.fm/archives/8823
487
+ query_states = query_states * attention_scale.to(dtype)
488
+
489
+ if padding_inputs is not None:
490
+ query_states = pad_input(query_states.squeeze(), *padding_inputs)
491
+ key_states = pad_input(key_states.squeeze(), *padding_inputs)
492
+ value_states = pad_input(value_states.squeeze(), *padding_inputs)
493
+
494
+ if self.use_memory_efficient_attention:
495
+ assert self.memory_efficient_attention is not None, "xformers is not loaded"
496
+ assert output_attentions is False, "memory_efficient_attention do not output attentions"
497
+ assert head_mask is None, "Not support yet"
498
+ attention_probs = None
499
+ if torch.is_tensor(attention_bias):
500
+ attention_bias = attention_bias.to(dtype)
501
+ context_layer = self.memory_efficient_attention(
502
+ query_states,
503
+ key_states,
504
+ value_states,
505
+ attn_bias=attention_bias,
506
+ p=self.dropout.p
507
+ )
508
+ else:
509
+ if output_attentions and isinstance(self, NewSdpaAttention):
510
+ raise RuntimeError("SDPA do not output attentions")
511
+ context_layer, attention_probs = self._attention(
512
+ query_states, key_states, value_states, attention_bias, head_mask
513
+ )
514
+
515
+ if padding_inputs is not None:
516
+ context_layer = unpad_input(context_layer, indices=padding_inputs[0])
517
+
518
+ new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)
519
+ context_layer = context_layer.view(new_context_layer_shape)
520
+
521
+ # output proj
522
+ attn_output = self.o_proj(context_layer)
523
+
524
+ # add attentions if we output them
525
+ outputs = (attn_output, attention_probs) if output_attentions else (attn_output,)
526
+ return outputs
527
+
528
+ def _attention(self, query_states, key_states, value_states, attention_bias, head_mask):
529
+ """
530
+ Args:
531
+ q/k/v: (B, L, n_head, head_dim),
532
+ Returns:
533
+ attn_output: (B L, n_head, head_dim)
534
+ """
535
+ query_states = query_states.transpose(1, 2)
536
+ key_states = key_states.transpose(1, 2)
537
+ value_states = value_states.transpose(1, 2)
538
+ # Take the dot product between "query" and "key" to get the raw attention scores.
539
+ attention_scores = torch.matmul(query_states, key_states.transpose(-1, -2))
540
+
541
+ attention_scores = attention_scores / math.sqrt(self.attention_head_size)
542
+ if attention_bias is not None:
543
+ # Apply the attention mask is (precomputed for all layers in BertModel forward() function)
544
+ attention_scores = attention_scores + attention_bias
545
+
546
+ # Normalize the attention scores to probabilities.
547
+ attention_probs = nn.functional.softmax(attention_scores, dim=-1)
548
+
549
+ # This is actually dropping out entire tokens to attend to, which might
550
+ # seem a bit unusual, but is taken from the original Transformer paper.
551
+ if self.dropout.p > 0:
552
+ attention_probs = self.dropout(attention_probs)
553
+
554
+ # Mask heads if we want to
555
+ if head_mask is not None:
556
+ attention_probs = attention_probs * head_mask
557
+
558
+ context_layer = torch.matmul(attention_probs, value_states)
559
+
560
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
561
+ return context_layer, attention_probs
562
+
563
+
564
+ class NewSdpaAttention(NewAttention):
565
+ """
566
+ New attention module using torch.nn.functional.scaled_dot_product_attention. This module inherits from
567
+ `NewAttention` as the weights of the module stays untouched. The only changes are on the forward pass to adapt to
568
+ SDPA API.
569
+ """
570
+ def __init__(self, config: NewConfig, **kwargs):
571
+ super().__init__(config, **kwargs)
572
+ # torch.backends.cuda.enable_mem_efficient_sdp(False)
573
+ # logger.warning(
574
+ # "Disable memory efficient attention kernel for `NewSdpaAttention`, you can set "
575
+ # "`use_memory_efficient_attention=True` if it expected to use."
576
+ # )
577
+
578
+ def _attention(self, query_states, key_states, value_states, attention_bias, head_mask):
579
+ attn_output = torch.nn.functional.scaled_dot_product_attention(
580
+ query_states.transpose(1, 2),
581
+ key_states.transpose(1, 2),
582
+ value_states.transpose(1, 2),
583
+ attn_mask=attention_bias,
584
+ dropout_p=self.dropout.p if self.training else 0.0,
585
+ )
586
+ attn_output = attn_output.permute(0, 2, 1, 3).contiguous()
587
+ return attn_output, None
588
+
589
+
590
+ NEW_ATTENTION_CLASSES = {
591
+ "eager": NewAttention,
592
+ # "flash_attention_2": , # TODO
593
+ "sdpa": NewSdpaAttention,
594
+ }
595
+
596
+
597
+ class NewGatedMLP(nn.Module):
598
+ """
599
+ GLU Variants Improve Transformer.
600
+ """
601
+
602
+ def __init__(self, config: NewConfig):
603
+ super().__init__()
604
+ self.intermediate_size = config.intermediate_size
605
+ self.up_gate_proj = nn.Linear(config.hidden_size, self.intermediate_size * 2, bias=False)
606
+ self.down_proj = nn.Linear(self.intermediate_size, config.hidden_size, bias=True)
607
+ self.act_fn = ACT2FN[config.hidden_act]
608
+ if config.hidden_dropout_prob > 0:
609
+ self.hidden_dropout = nn.Dropout(config.hidden_dropout_prob)
610
+ else:
611
+ self.hidden_dropout = None
612
+
613
+ def forward(self, hidden_states):
614
+ up_gate = self.up_gate_proj(hidden_states)
615
+ up_states, gate = torch.split(up_gate, self.intermediate_size, dim=-1)
616
+ gate = self.act_fn(gate)
617
+ gated_states = gate * up_states
618
+ if self.hidden_dropout is not None:
619
+ gated_states = self.hidden_dropout(gated_states)
620
+ down_states = self.down_proj(gated_states)
621
+ return down_states
622
+
623
+
624
+ class NewLayer(nn.Module):
625
+ def __init__(
626
+ self,
627
+ config: NewConfig,
628
+ pack_qkv=None,
629
+ use_memory_efficient_attention=None,
630
+ attn_implementation=None
631
+ ):
632
+ super().__init__()
633
+ if attn_implementation is None:
634
+ attn_implementation = config._attn_implementation
635
+ if use_memory_efficient_attention is None:
636
+ use_memory_efficient_attention = config.use_memory_efficient_attention
637
+ if use_memory_efficient_attention:
638
+ if attn_implementation != 'eager':
639
+ logger.warning_once(f"Override {attn_implementation=} to 'eager' as {use_memory_efficient_attention=}")
640
+ attn_implementation = 'eager' # Since it will be SDPA by default for torch>=2.1.1
641
+ self.attention = NEW_ATTENTION_CLASSES[attn_implementation](
642
+ config, pack_qkv=pack_qkv, use_memory_efficient_attention=use_memory_efficient_attention
643
+ )
644
+ self.mlp = NewGatedMLP(config)
645
+
646
+ ln_class = LAYER_NORM[config.layer_norm_type]
647
+ self.attn_ln = ln_class(config.hidden_size, eps=config.layer_norm_eps)
648
+ self.mlp_ln = ln_class(config.hidden_size, eps=config.layer_norm_eps)
649
+
650
+ if config.hidden_dropout_prob > 0:
651
+ self.hidden_dropout = nn.Dropout(config.hidden_dropout_prob)
652
+ else:
653
+ self.hidden_dropout = None
654
+
655
+ def forward(
656
+ self,
657
+ hidden_states: torch.Tensor,
658
+ attention_bias: torch.FloatTensor,
659
+ rope_embeds: Optional[Tuple[torch.FloatTensor, torch.FloatTensor]] = None,
660
+ padding_inputs: Optional[Tuple] = None, # indices, batch, seqlen
661
+ attention_scale: Optional[torch.FloatTensor] = None,
662
+ subset_indices: Optional[torch.LongTensor] = None,
663
+ head_mask: Optional[torch.FloatTensor] = None,
664
+ output_attentions: Optional[bool] = False,
665
+ qkv_inputs: Optional[Tuple] = None, # For RetroMAE
666
+ ) -> Tuple[torch.Tensor, ...]:
667
+ # Multi head self attention
668
+ residual = hidden_states if qkv_inputs is None else qkv_inputs[0]
669
+ attention_outputs = self.attention(
670
+ hidden_states,
671
+ attention_bias,
672
+ rope_embeds,
673
+ padding_inputs,
674
+ attention_scale,
675
+ head_mask,
676
+ output_attentions=output_attentions,
677
+ qkv_inputs=qkv_inputs,
678
+ )
679
+ hidden_states = attention_outputs[0]
680
+ if self.hidden_dropout is not None:
681
+ hidden_states = self.hidden_dropout(hidden_states)
682
+ hidden_states = residual + hidden_states
683
+
684
+ # In pretraining, after the attention of last layer, we only need the masked tokens.
685
+ if subset_indices is not None:
686
+ hidden_states = hidden_states[subset_indices]
687
+
688
+ hidden_states = self.attn_ln(hidden_states)
689
+
690
+ # Fully Connected
691
+ residual = hidden_states
692
+ hidden_states = self.mlp(hidden_states)
693
+ if self.hidden_dropout is not None:
694
+ hidden_states = self.hidden_dropout(hidden_states)
695
+ hidden_states = residual + hidden_states
696
+ hidden_states = self.mlp_ln(hidden_states)
697
+
698
+ # add self attentions if we output attention weights
699
+ outputs = (hidden_states,) + attention_outputs[1:]
700
+ return outputs
701
+
702
+
703
+ class NewEncoder(nn.Module):
704
+ def __init__(self, config):
705
+ super().__init__()
706
+ self.config = config
707
+ self.layer = nn.ModuleList([NewLayer(config) for _ in range(config.num_hidden_layers)])
708
+ self.gradient_checkpointing = False
709
+
710
+ def forward(
711
+ self,
712
+ hidden_states: torch.Tensor,
713
+ attention_bias: Optional[torch.FloatTensor] = None,
714
+ rope_embeds: Optional[Tuple[torch.FloatTensor, torch.FloatTensor]] = None,
715
+ padding_inputs: Optional[Tuple] = None, # indices, batch, seqlen
716
+ attention_scale: Optional[torch.FloatTensor] = None,
717
+ subset_indices: Optional[torch.LongTensor] = None,
718
+ head_mask: Optional[torch.FloatTensor] = None,
719
+ output_attentions: Optional[bool] = False,
720
+ output_hidden_states: Optional[bool] = False,
721
+ return_dict: Optional[bool] = True,
722
+ ) -> Union[Tuple[torch.Tensor], BaseModelOutput]:
723
+ all_hidden_states = () if output_hidden_states else None
724
+ all_self_attentions = () if output_attentions else None
725
+
726
+ for i, layer_module in enumerate(self.layer):
727
+ if output_hidden_states:
728
+ all_hidden_states = all_hidden_states + (hidden_states,)
729
+
730
+ if i >= len(self.layer) - 1:
731
+ layer_subset_indices = subset_indices
732
+ else:
733
+ layer_subset_indices = None
734
+
735
+ layer_head_mask = head_mask[i] if head_mask is not None else None
736
+
737
+ if self.gradient_checkpointing and self.training:
738
+ layer_outputs = self._gradient_checkpointing_func(
739
+ layer_module.__call__,
740
+ hidden_states,
741
+ attention_bias,
742
+ rope_embeds,
743
+ padding_inputs,
744
+ attention_scale,
745
+ layer_subset_indices,
746
+ layer_head_mask,
747
+ )
748
+ else:
749
+ layer_outputs = layer_module(
750
+ hidden_states,
751
+ attention_bias,
752
+ rope_embeds,
753
+ padding_inputs,
754
+ attention_scale,
755
+ layer_subset_indices,
756
+ layer_head_mask,
757
+ output_attentions,
758
+ )
759
+
760
+ hidden_states = layer_outputs[0]
761
+ if output_attentions:
762
+ all_self_attentions = all_self_attentions + (layer_outputs[1],)
763
+
764
+ if output_hidden_states:
765
+ all_hidden_states = all_hidden_states + (hidden_states,)
766
+
767
+ if not return_dict:
768
+ return tuple(
769
+ v
770
+ for v in [
771
+ hidden_states,
772
+ all_hidden_states,
773
+ all_self_attentions,
774
+ ]
775
+ if v is not None
776
+ )
777
+ return BaseModelOutput(
778
+ last_hidden_state=hidden_states,
779
+ hidden_states=all_hidden_states,
780
+ attentions=all_self_attentions,
781
+ )
782
+
783
+
784
+ # Copied from transformers.models.bert.modeling_bert.BertPooler with Bert->New
785
+ class NewPooler(nn.Module):
786
+ def __init__(self, config):
787
+ super().__init__()
788
+ self.dense = nn.Linear(config.hidden_size, config.hidden_size)
789
+ self.activation = nn.Tanh()
790
+
791
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
792
+ # We "pool" the model by simply taking the hidden state corresponding
793
+ # to the first token.
794
+ first_token_tensor = hidden_states[:, 0]
795
+ pooled_output = self.dense(first_token_tensor)
796
+ pooled_output = self.activation(pooled_output)
797
+ return pooled_output
798
+
799
+
800
+ class NewPreTrainedModel(PreTrainedModel):
801
+ """
802
+ An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained
803
+ models.
804
+ """
805
+
806
+ config_class = NewConfig
807
+ base_model_prefix = "new"
808
+ supports_gradient_checkpointing = True
809
+ _supports_sdpa = True
810
+
811
+ def _init_weights(self, module):
812
+ """Initialize the weights"""
813
+ if isinstance(module, nn.Linear):
814
+ # Slightly different from the TF version which uses truncated_normal for initialization
815
+ # cf https://github.com/pytorch/pytorch/pull/5617
816
+ module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
817
+ if module.bias is not None:
818
+ module.bias.data.zero_()
819
+ elif isinstance(module, nn.Embedding):
820
+ module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
821
+ if module.padding_idx is not None:
822
+ module.weight.data[module.padding_idx].zero_()
823
+ elif isinstance(module, nn.LayerNorm):
824
+ module.bias.data.zero_()
825
+ module.weight.data.fill_(1.0)
826
+
827
+
828
+ class NewModel(NewPreTrainedModel):
829
+ """
830
+ The bare New Model transformer outputting raw hidden-states without any specific head on top.
831
+ """
832
+
833
+ def __init__(self, config: NewConfig, add_pooling_layer=False):
834
+ super().__init__(config)
835
+ self.config = config
836
+
837
+ self.embeddings = NewEmbeddings(config)
838
+ self.encoder = NewEncoder(config)
839
+
840
+ self.pooler = NewPooler(config) if add_pooling_layer else None
841
+
842
+ # Initialize weights and apply final processing
843
+ self.post_init()
844
+
845
+ def get_input_embeddings(self):
846
+ return self.embeddings.word_embeddings
847
+
848
+ def set_input_embeddings(self, value):
849
+ self.embeddings.word_embeddings = value
850
+
851
+ def forward(
852
+ self,
853
+ input_ids: Optional[torch.Tensor] = None,
854
+ attention_mask: Optional[torch.Tensor] = None,
855
+ length: Optional[List[int]] = None,
856
+ subset_indices: Optional[torch.LongTensor] = None,
857
+ token_type_ids: Optional[torch.Tensor] = None,
858
+ position_ids: Optional[torch.Tensor] = None,
859
+ head_mask: Optional[torch.Tensor] = None,
860
+ inputs_embeds: Optional[torch.Tensor] = None,
861
+ output_attentions: Optional[bool] = None,
862
+ output_hidden_states: Optional[bool] = None,
863
+ return_dict: Optional[bool] = None,
864
+ unpad_inputs: Optional[bool] = None,
865
+ ) -> Union[Tuple[torch.Tensor], BaseModelOutputWithPooling]:
866
+ r"""
867
+ length (`list` of length `batch_size`, *optional*):
868
+ If is `None`, return padded `last_hidden_state`.
869
+ subset_indices ():
870
+ pass
871
+ unpad_inputs (`bool`, *optional*):
872
+ pass
873
+ """
874
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
875
+ output_hidden_states = (
876
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
877
+ )
878
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
879
+ unpad_inputs = unpad_inputs if unpad_inputs is not None else self.config.unpad_inputs
880
+ output_padded = length is None
881
+
882
+ if input_ids is not None and inputs_embeds is not None:
883
+ raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
884
+ elif input_ids is not None:
885
+ self.warn_if_padding_and_no_attention_mask(input_ids, attention_mask)
886
+ input_shape = input_ids.size()
887
+ elif inputs_embeds is not None:
888
+ input_shape = inputs_embeds.size()[:-1]
889
+ else:
890
+ raise ValueError("You have to specify either input_ids or inputs_embeds")
891
+
892
+ # TODO: not used
893
+ # # Prepare head mask if needed
894
+ # # 1.0 in head_mask indicate we keep the head
895
+ # # attention_probs has shape bsz x n_heads x N x N
896
+ # # input head_mask has shape [num_heads] or [num_hidden_layers x num_heads]
897
+ # # and head_mask is converted to shape [num_hidden_layers x batch x num_heads x seq_length x seq_length]
898
+ # head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers)
899
+
900
+ # Get embeddings, may unpad them
901
+ (embedding_output, attention_mask, rope_embeds, length) = self.embeddings(
902
+ unpad_inputs,
903
+ input_ids=input_ids,
904
+ attention_mask=attention_mask,
905
+ length=length,
906
+ token_type_ids=token_type_ids,
907
+ position_ids=position_ids,
908
+ inputs_embeds=inputs_embeds
909
+ )
910
+
911
+ batch_size, seq_length = input_shape
912
+ if unpad_inputs and self.config.use_memory_efficient_attention:
913
+ attention_bias = xops.fmha.attn_bias.BlockDiagonalMask.from_seqlens(length)
914
+ else:
915
+ # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length]
916
+ # ourselves in which case we just need to make it broadcastable to all heads.
917
+ attention_bias = self.get_extended_attention_mask(attention_mask, input_shape)
918
+ if self.config.use_memory_efficient_attention:
919
+ # Invalid shape for attention bias: torch.Size([48, 1, 1, 512]) (expected (48, 12, 512, 512))
920
+ attention_bias = attention_bias.expand(-1, self.config.num_attention_heads, seq_length, -1)
921
+
922
+ padding_inputs = None
923
+ if unpad_inputs and (output_padded or not self.config.use_memory_efficient_attention):
924
+ indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
925
+ if not self.config.use_memory_efficient_attention:
926
+ padding_inputs = (indices, *input_shape)
927
+
928
+ attention_scale = None
929
+ if self.config.logn_attention_scale:
930
+ logger.warning_once("TODO: logn_attention_scale")
931
+ # # attention scale log_512(input_len)
932
+ # attention_scale = attention_mask.sum(1).log() / torch.tensor(self.config.max_position_embeddings).log()
933
+ # # inference-time logn scale need clip 1
934
+ # if self.config.logn_attention_clip1:
935
+ # attention_scale.clip_(1)
936
+ # attention_scale = attention_scale[:, None, None, None]
937
+ # else:
938
+ # attention_scale = None
939
+
940
+ encoder_outputs = self.encoder(
941
+ embedding_output,
942
+ attention_bias=attention_bias,
943
+ rope_embeds=rope_embeds,
944
+ padding_inputs=padding_inputs,
945
+ attention_scale=attention_scale,
946
+ subset_indices=subset_indices,
947
+ head_mask=head_mask,
948
+ output_attentions=output_attentions,
949
+ output_hidden_states=output_hidden_states,
950
+ return_dict=return_dict,
951
+ )
952
+ sequence_output = encoder_outputs[0]
953
+ if unpad_inputs and output_padded:
954
+ sequence_output = pad_input(
955
+ sequence_output.squeeze(), indices, batch_size, seq_length
956
+ )
957
+
958
+ pooled_output = self.pooler(sequence_output) if self.pooler is not None else None
959
+
960
+ if not return_dict:
961
+ return (sequence_output, pooled_output) + encoder_outputs[1:]
962
+
963
+ return BaseModelOutputWithPooling(
964
+ last_hidden_state=sequence_output,
965
+ pooler_output=pooled_output,
966
+ hidden_states=encoder_outputs.hidden_states,
967
+ attentions=encoder_outputs.attentions,
968
+ )
969
+
970
+
971
+ class NewLMPredictionHead(nn.Module):
972
+ def __init__(self, config):
973
+ super().__init__()
974
+ self.dense = nn.Linear(config.hidden_size, config.hidden_size)
975
+ self.transform_act_fn = ACT2FN[config.hidden_act]
976
+ self.norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
977
+
978
+ # The output weights are the same as the input embeddings, but there is
979
+ # an output-only bias for each token.
980
+ self.decoder = nn.Linear(config.hidden_size, config.vocab_size)
981
+
982
+ def forward(self, hidden_states):
983
+ hidden_states = self.dense(hidden_states)
984
+ hidden_states = self.transform_act_fn(hidden_states)
985
+ hidden_states = self.norm(hidden_states)
986
+ hidden_states = self.decoder(hidden_states)
987
+ return hidden_states
988
+
989
+
990
+ class NewForMaskedLM(NewPreTrainedModel):
991
+ _tied_weights_keys = ["lm_head.decoder.bias", "lm_head.decoder.weight"]
992
+
993
+ def __init__(self, config: NewConfig):
994
+ super().__init__(config)
995
+ self.new = NewModel(config, add_pooling_layer=False)
996
+ self.lm_head = NewLMPredictionHead(config)
997
+ self.loss_fct = nn.CrossEntropyLoss()
998
+
999
+ # Initialize weights and apply final processing
1000
+ self.post_init()
1001
+
1002
+ def get_output_embeddings(self):
1003
+ return self.lm_head.decoder
1004
+
1005
+ def set_output_embeddings(self, new_embeddings):
1006
+ self.lm_head.decoder = new_embeddings
1007
+
1008
+ def forward(
1009
+ self,
1010
+ input_ids: Optional[torch.Tensor] = None,
1011
+ attention_mask: Optional[torch.Tensor] = None,
1012
+ token_type_ids: Optional[torch.Tensor] = None,
1013
+ position_ids: Optional[torch.Tensor] = None,
1014
+ head_mask: Optional[torch.Tensor] = None,
1015
+ inputs_embeds: Optional[torch.Tensor] = None,
1016
+ labels: Optional[torch.Tensor] = None,
1017
+ output_attentions: Optional[bool] = None,
1018
+ output_hidden_states: Optional[bool] = None,
1019
+ return_dict: Optional[bool] = None,
1020
+ unpad_inputs: Optional[bool] = None,
1021
+ ) -> Union[Tuple[torch.Tensor], MaskedLMOutput]:
1022
+ r"""
1023
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1024
+ Labels for computing the masked language modeling loss. Indices should be in `[-100, 0, ...,
1025
+ config.vocab_size]` (see `input_ids` docstring) Tokens with indices set to `-100` are ignored (masked), the
1026
+ loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`
1027
+ """
1028
+
1029
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1030
+
1031
+ if labels is None or not self.new.config.unpad_inputs:
1032
+ length = None
1033
+ subset_indices = None
1034
+ else:
1035
+ length = attention_mask.sum(-1).tolist()
1036
+ labels = labels[attention_mask.bool()].unsqueeze(0)
1037
+ subset_indices = labels > -100
1038
+
1039
+ outputs = self.new(
1040
+ input_ids,
1041
+ attention_mask=attention_mask,
1042
+ length=length,
1043
+ subset_indices=subset_indices,
1044
+ token_type_ids=token_type_ids,
1045
+ position_ids=position_ids,
1046
+ head_mask=head_mask,
1047
+ inputs_embeds=inputs_embeds,
1048
+ output_attentions=output_attentions,
1049
+ output_hidden_states=output_hidden_states,
1050
+ return_dict=return_dict,
1051
+ unpad_inputs=unpad_inputs,
1052
+ )
1053
+
1054
+ sequence_output = outputs[0]
1055
+ prediction_scores = self.lm_head(sequence_output)
1056
+
1057
+ masked_lm_loss = None
1058
+ if labels is not None:
1059
+ if subset_indices is None:
1060
+ mask = attention_mask.bool()
1061
+ prediction_scores = prediction_scores[mask]
1062
+ labels = labels[mask]
1063
+ else:
1064
+ labels = labels[subset_indices]
1065
+ masked_lm_loss = self.loss_fct(prediction_scores, labels)
1066
+
1067
+ if not return_dict:
1068
+ output = (prediction_scores,) + outputs[2:]
1069
+ return ((masked_lm_loss,) + output) if masked_lm_loss is not None else output
1070
+
1071
+ return MaskedLMOutput(
1072
+ loss=masked_lm_loss,
1073
+ logits=prediction_scores,
1074
+ hidden_states=outputs.hidden_states,
1075
+ attentions=outputs.attentions,
1076
+ )
1077
+
1078
+
1079
+ class NewForSequenceClassification(NewPreTrainedModel):
1080
+ def __init__(self, config):
1081
+ super().__init__(config)
1082
+ self.num_labels = config.num_labels
1083
+ self.config = config
1084
+
1085
+ self.new = NewModel(config, add_pooling_layer=True)
1086
+ classifier_dropout = (
1087
+ config.classifier_dropout if config.classifier_dropout is not None else config.hidden_dropout_prob
1088
+ )
1089
+ self.dropout = nn.Dropout(classifier_dropout)
1090
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels)
1091
+
1092
+ # Initialize weights and apply final processing
1093
+ self.post_init()
1094
+
1095
+ def forward(
1096
+ self,
1097
+ input_ids: Optional[torch.Tensor] = None,
1098
+ attention_mask: Optional[torch.Tensor] = None,
1099
+ token_type_ids: Optional[torch.Tensor] = None,
1100
+ position_ids: Optional[torch.Tensor] = None,
1101
+ head_mask: Optional[torch.Tensor] = None,
1102
+ inputs_embeds: Optional[torch.Tensor] = None,
1103
+ labels: Optional[torch.Tensor] = None,
1104
+ output_attentions: Optional[bool] = None,
1105
+ output_hidden_states: Optional[bool] = None,
1106
+ return_dict: Optional[bool] = None,
1107
+ unpad_inputs: Optional[bool] = None,
1108
+ ) -> Union[Tuple[torch.Tensor], SequenceClassifierOutput]:
1109
+ r"""
1110
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
1111
+ Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
1112
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
1113
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
1114
+ """
1115
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1116
+
1117
+ outputs = self.new(
1118
+ input_ids,
1119
+ attention_mask=attention_mask,
1120
+ token_type_ids=token_type_ids,
1121
+ position_ids=position_ids,
1122
+ head_mask=head_mask,
1123
+ inputs_embeds=inputs_embeds,
1124
+ output_attentions=output_attentions,
1125
+ output_hidden_states=output_hidden_states,
1126
+ return_dict=return_dict,
1127
+ unpad_inputs=unpad_inputs,
1128
+ )
1129
+
1130
+ pooled_output = outputs[1]
1131
+
1132
+ pooled_output = self.dropout(pooled_output)
1133
+ logits = self.classifier(pooled_output)
1134
+
1135
+ loss = None
1136
+ if labels is not None:
1137
+ if self.config.problem_type is None:
1138
+ if self.num_labels == 1:
1139
+ self.config.problem_type = "regression"
1140
+ elif self.num_labels > 1 and (labels.dtype == torch.long or labels.dtype == torch.int):
1141
+ self.config.problem_type = "single_label_classification"
1142
+ else:
1143
+ self.config.problem_type = "multi_label_classification"
1144
+
1145
+ if self.config.problem_type == "regression":
1146
+ loss_fct = nn.MSELoss()
1147
+ if self.num_labels == 1:
1148
+ loss = loss_fct(logits.squeeze(), labels.squeeze())
1149
+ else:
1150
+ loss = loss_fct(logits, labels)
1151
+ elif self.config.problem_type == "single_label_classification":
1152
+ loss_fct = nn.CrossEntropyLoss()
1153
+ loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
1154
+ elif self.config.problem_type == "multi_label_classification":
1155
+ loss_fct = nn.BCEWithLogitsLoss()
1156
+ loss = loss_fct(logits, labels)
1157
+
1158
+ if not return_dict:
1159
+ output = (logits,) + outputs[2:]
1160
+ return ((loss,) + output) if loss is not None else output
1161
+
1162
+ return SequenceClassifierOutput(
1163
+ loss=loss,
1164
+ logits=logits,
1165
+ hidden_states=outputs.hidden_states,
1166
+ attentions=outputs.attentions,
1167
+ )
1168
+
1169
+
1170
+ class NewForMultipleChoice(NewPreTrainedModel):
1171
+ def __init__(self, config):
1172
+ super().__init__(config)
1173
+
1174
+ self.new = NewModel(config, add_pooling_layer=True)
1175
+ classifier_dropout = (
1176
+ config.classifier_dropout if config.classifier_dropout is not None else config.hidden_dropout_prob
1177
+ )
1178
+ self.dropout = nn.Dropout(classifier_dropout)
1179
+ self.classifier = nn.Linear(config.hidden_size, 1)
1180
+
1181
+ # Initialize weights and apply final processing
1182
+ self.post_init()
1183
+
1184
+ def forward(
1185
+ self,
1186
+ input_ids: Optional[torch.Tensor] = None,
1187
+ attention_mask: Optional[torch.Tensor] = None,
1188
+ token_type_ids: Optional[torch.Tensor] = None,
1189
+ position_ids: Optional[torch.Tensor] = None,
1190
+ head_mask: Optional[torch.Tensor] = None,
1191
+ inputs_embeds: Optional[torch.Tensor] = None,
1192
+ labels: Optional[torch.Tensor] = None,
1193
+ output_attentions: Optional[bool] = None,
1194
+ output_hidden_states: Optional[bool] = None,
1195
+ return_dict: Optional[bool] = None,
1196
+ unpad_inputs: Optional[bool] = None,
1197
+ ) -> Union[Tuple[torch.Tensor], MultipleChoiceModelOutput]:
1198
+ r"""
1199
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
1200
+ Labels for computing the multiple choice classification loss. Indices should be in `[0, ...,
1201
+ num_choices-1]` where `num_choices` is the size of the second dimension of the input tensors. (See
1202
+ `input_ids` above)
1203
+ """
1204
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1205
+ num_choices = input_ids.shape[1] if input_ids is not None else inputs_embeds.shape[1]
1206
+
1207
+ input_ids = input_ids.view(-1, input_ids.size(-1)) if input_ids is not None else None
1208
+ attention_mask = attention_mask.view(-1, attention_mask.size(-1)) if attention_mask is not None else None
1209
+ token_type_ids = token_type_ids.view(-1, token_type_ids.size(-1)) if token_type_ids is not None else None
1210
+ position_ids = position_ids.view(-1, position_ids.size(-1)) if position_ids is not None else None
1211
+ inputs_embeds = (
1212
+ inputs_embeds.view(-1, inputs_embeds.size(-2), inputs_embeds.size(-1))
1213
+ if inputs_embeds is not None
1214
+ else None
1215
+ )
1216
+
1217
+ outputs = self.new(
1218
+ input_ids,
1219
+ attention_mask=attention_mask,
1220
+ token_type_ids=token_type_ids,
1221
+ position_ids=position_ids,
1222
+ head_mask=head_mask,
1223
+ inputs_embeds=inputs_embeds,
1224
+ output_attentions=output_attentions,
1225
+ output_hidden_states=output_hidden_states,
1226
+ return_dict=return_dict,
1227
+ unpad_inputs=unpad_inputs,
1228
+ )
1229
+
1230
+ pooled_output = outputs[1]
1231
+
1232
+ pooled_output = self.dropout(pooled_output)
1233
+ logits = self.classifier(pooled_output)
1234
+ reshaped_logits = logits.view(-1, num_choices)
1235
+
1236
+ loss = None
1237
+ if labels is not None:
1238
+ loss_fct = nn.CrossEntropyLoss()
1239
+ loss = loss_fct(reshaped_logits, labels)
1240
+
1241
+ if not return_dict:
1242
+ output = (reshaped_logits,) + outputs[2:]
1243
+ return ((loss,) + output) if loss is not None else output
1244
+
1245
+ return MultipleChoiceModelOutput(
1246
+ loss=loss,
1247
+ logits=reshaped_logits,
1248
+ hidden_states=outputs.hidden_states,
1249
+ attentions=outputs.attentions,
1250
+ )
1251
+
1252
+
1253
+ @dataclass
1254
+ class NewTokenClassifierOutput(ModelOutput):
1255
+ loss: Optional[torch.FloatTensor] = None
1256
+ logits: torch.FloatTensor = None
1257
+ last_hidden_state: torch.FloatTensor = None
1258
+ hidden_states: Optional[Tuple[torch.FloatTensor, ...]] = None
1259
+ attentions: Optional[Tuple[torch.FloatTensor, ...]] = None
1260
+
1261
+
1262
+ class NewForTokenClassification(NewPreTrainedModel):
1263
+ def __init__(self, config):
1264
+ super().__init__(config)
1265
+ self.num_labels = config.num_labels
1266
+
1267
+ self.new = NewModel(config, add_pooling_layer=False)
1268
+ classifier_dropout = (
1269
+ config.classifier_dropout if config.classifier_dropout is not None else config.hidden_dropout_prob
1270
+ )
1271
+ self.dropout = nn.Dropout(classifier_dropout)
1272
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels)
1273
+
1274
+ # Initialize weights and apply final processing
1275
+ self.post_init()
1276
+
1277
+ def forward(
1278
+ self,
1279
+ input_ids: Optional[torch.Tensor] = None,
1280
+ attention_mask: Optional[torch.Tensor] = None,
1281
+ token_type_ids: Optional[torch.Tensor] = None,
1282
+ position_ids: Optional[torch.Tensor] = None,
1283
+ head_mask: Optional[torch.Tensor] = None,
1284
+ inputs_embeds: Optional[torch.Tensor] = None,
1285
+ labels: Optional[torch.Tensor] = None,
1286
+ output_attentions: Optional[bool] = None,
1287
+ output_hidden_states: Optional[bool] = None,
1288
+ return_dict: Optional[bool] = None,
1289
+ unpad_inputs: Optional[bool] = None,
1290
+ ) -> Union[Tuple[torch.Tensor], NewTokenClassifierOutput]:
1291
+ r"""
1292
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1293
+ Labels for computing the token classification loss. Indices should be in `[0, ..., config.num_labels - 1]`.
1294
+ """
1295
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1296
+
1297
+ outputs = self.new(
1298
+ input_ids,
1299
+ attention_mask=attention_mask,
1300
+ token_type_ids=token_type_ids,
1301
+ position_ids=position_ids,
1302
+ head_mask=head_mask,
1303
+ inputs_embeds=inputs_embeds,
1304
+ output_attentions=output_attentions,
1305
+ output_hidden_states=output_hidden_states,
1306
+ return_dict=return_dict,
1307
+ unpad_inputs=unpad_inputs,
1308
+ )
1309
+
1310
+ sequence_output = outputs[0]
1311
+
1312
+ sequence_output = self.dropout(sequence_output)
1313
+ logits = self.classifier(sequence_output)
1314
+
1315
+ loss = None
1316
+ if labels is not None:
1317
+ loss_fct = nn.CrossEntropyLoss()
1318
+ loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
1319
+
1320
+ if not return_dict:
1321
+ output = (logits,) + outputs[2:]
1322
+ return ((loss,) + output) if loss is not None else output
1323
+
1324
+ return NewTokenClassifierOutput(
1325
+ loss=loss,
1326
+ logits=logits,
1327
+ last_hidden_state=sequence_output,
1328
+ hidden_states=outputs.hidden_states,
1329
+ attentions=outputs.attentions,
1330
+ )
1331
+
1332
+
1333
+ class NewForQuestionAnswering(NewPreTrainedModel):
1334
+ def __init__(self, config):
1335
+ super().__init__(config)
1336
+ self.num_labels = config.num_labels
1337
+
1338
+ self.new = NewModel(config, add_pooling_layer=False)
1339
+ self.qa_outputs = nn.Linear(config.hidden_size, config.num_labels)
1340
+
1341
+ # Initialize weights and apply final processing
1342
+ self.post_init()
1343
+
1344
+ def forward(
1345
+ self,
1346
+ input_ids: Optional[torch.Tensor] = None,
1347
+ attention_mask: Optional[torch.Tensor] = None,
1348
+ token_type_ids: Optional[torch.Tensor] = None,
1349
+ position_ids: Optional[torch.Tensor] = None,
1350
+ head_mask: Optional[torch.Tensor] = None,
1351
+ inputs_embeds: Optional[torch.Tensor] = None,
1352
+ start_positions: Optional[torch.Tensor] = None,
1353
+ end_positions: Optional[torch.Tensor] = None,
1354
+ output_attentions: Optional[bool] = None,
1355
+ output_hidden_states: Optional[bool] = None,
1356
+ return_dict: Optional[bool] = None,
1357
+ unpad_inputs: Optional[bool] = None,
1358
+ ) -> Union[Tuple[torch.Tensor], QuestionAnsweringModelOutput]:
1359
+ r"""
1360
+ start_positions (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
1361
+ Labels for position (index) of the start of the labelled span for computing the token classification loss.
1362
+ Positions are clamped to the length of the sequence (`sequence_length`). Position outside of the sequence
1363
+ are not taken into account for computing the loss.
1364
+ end_positions (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
1365
+ Labels for position (index) of the end of the labelled span for computing the token classification loss.
1366
+ Positions are clamped to the length of the sequence (`sequence_length`). Position outside of the sequence
1367
+ are not taken into account for computing the loss.
1368
+ """
1369
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1370
+
1371
+ outputs = self.new(
1372
+ input_ids,
1373
+ attention_mask=attention_mask,
1374
+ token_type_ids=token_type_ids,
1375
+ position_ids=position_ids,
1376
+ head_mask=head_mask,
1377
+ inputs_embeds=inputs_embeds,
1378
+ output_attentions=output_attentions,
1379
+ output_hidden_states=output_hidden_states,
1380
+ return_dict=return_dict,
1381
+ unpad_inputs=unpad_inputs,
1382
+ )
1383
+
1384
+ sequence_output = outputs[0]
1385
+
1386
+ logits = self.qa_outputs(sequence_output)
1387
+ start_logits, end_logits = logits.split(1, dim=-1)
1388
+ start_logits = start_logits.squeeze(-1).contiguous()
1389
+ end_logits = end_logits.squeeze(-1).contiguous()
1390
+
1391
+ total_loss = None
1392
+ if start_positions is not None and end_positions is not None:
1393
+ # If we are on multi-GPU, split add a dimension
1394
+ if len(start_positions.size()) > 1:
1395
+ start_positions = start_positions.squeeze(-1)
1396
+ if len(end_positions.size()) > 1:
1397
+ end_positions = end_positions.squeeze(-1)
1398
+ # sometimes the start/end positions are outside our model inputs, we ignore these terms
1399
+ ignored_index = start_logits.size(1)
1400
+ start_positions = start_positions.clamp(0, ignored_index)
1401
+ end_positions = end_positions.clamp(0, ignored_index)
1402
+
1403
+ loss_fct = nn.CrossEntropyLoss(ignore_index=ignored_index)
1404
+ start_loss = loss_fct(start_logits, start_positions)
1405
+ end_loss = loss_fct(end_logits, end_positions)
1406
+ total_loss = (start_loss + end_loss) / 2
1407
+
1408
+ if not return_dict:
1409
+ output = (start_logits, end_logits) + outputs[2:]
1410
+ return ((total_loss,) + output) if total_loss is not None else output
1411
+
1412
+ return QuestionAnsweringModelOutput(
1413
+ loss=total_loss,
1414
+ start_logits=start_logits,
1415
+ end_logits=end_logits,
1416
+ hidden_states=outputs.hidden_states,
1417
+ attentions=outputs.attentions,
1418
+ )
modules.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "idx": 0,
4
+ "name": "0",
5
+ "path": "",
6
+ "type": "sentence_transformers.base.modules.transformer.Transformer"
7
+ }
8
+ ]
sentence_bert_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "transformer_task": "sequence-classification",
3
+ "modality_config": {
4
+ "text": {
5
+ "method": "forward",
6
+ "method_output_name": "logits"
7
+ }
8
+ },
9
+ "module_output_name": "scores"
10
+ }
special_tokens_map.json ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": {
3
+ "content": "<s>",
4
+ "lstrip": false,
5
+ "normalized": false,
6
+ "rstrip": false,
7
+ "single_word": false
8
+ },
9
+ "cls_token": {
10
+ "content": "<s>",
11
+ "lstrip": false,
12
+ "normalized": false,
13
+ "rstrip": false,
14
+ "single_word": false
15
+ },
16
+ "eos_token": {
17
+ "content": "</s>",
18
+ "lstrip": false,
19
+ "normalized": false,
20
+ "rstrip": false,
21
+ "single_word": false
22
+ },
23
+ "mask_token": {
24
+ "content": "<mask>",
25
+ "lstrip": true,
26
+ "normalized": false,
27
+ "rstrip": false,
28
+ "single_word": false
29
+ },
30
+ "pad_token": {
31
+ "content": "<pad>",
32
+ "lstrip": false,
33
+ "normalized": false,
34
+ "rstrip": false,
35
+ "single_word": false
36
+ },
37
+ "sep_token": {
38
+ "content": "</s>",
39
+ "lstrip": false,
40
+ "normalized": false,
41
+ "rstrip": false,
42
+ "single_word": false
43
+ },
44
+ "unk_token": {
45
+ "content": "<unk>",
46
+ "lstrip": false,
47
+ "normalized": false,
48
+ "rstrip": false,
49
+ "single_word": false
50
+ }
51
+ }
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3ffb37461c391f096759f4a9bbbc329da0f36952f88bab061fcf84940c022e98
3
+ size 17082999
tokenizer_config.json ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "0": {
4
+ "content": "<s>",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "1": {
12
+ "content": "<pad>",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "2": {
20
+ "content": "</s>",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ },
27
+ "3": {
28
+ "content": "<unk>",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": true
34
+ },
35
+ "250001": {
36
+ "content": "<mask>",
37
+ "lstrip": true,
38
+ "normalized": false,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": true
42
+ }
43
+ },
44
+ "bos_token": "<s>",
45
+ "clean_up_tokenization_spaces": true,
46
+ "cls_token": "<s>",
47
+ "eos_token": "</s>",
48
+ "extra_special_tokens": {},
49
+ "mask_token": "<mask>",
50
+ "max_length": 512,
51
+ "model_max_length": 8192,
52
+ "pad_to_multiple_of": null,
53
+ "pad_token": "<pad>",
54
+ "pad_token_type_id": 0,
55
+ "padding_side": "right",
56
+ "sep_token": "</s>",
57
+ "stride": 0,
58
+ "tokenizer_class": "XLMRobertaTokenizerFast",
59
+ "truncation_side": "right",
60
+ "truncation_strategy": "longest_first",
61
+ "unk_token": "<unk>"
62
+ }