yarden077 commited on
Commit
2e852df
Β·
verified Β·
1 Parent(s): 0f5ecaf

Create README.md

Browse files
Files changed (1) hide show
  1. README.md +232 -0
README.md ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - he
4
+ tags:
5
+ - hebrew
6
+ - semantic-retrieval
7
+ - information-retrieval
8
+ - dense-retrieval
9
+ - reranking
10
+ - rrf
11
+ - sentence-transformers
12
+ - competition
13
+ pipeline_tag: sentence-similarity
14
+ license: other
15
+ ---
16
+
17
+ # Hebrew Semantic Retrieval β€” 2nd Place Solution
18
+
19
+ **Competition:** Hebrew Semantic Retrieval Challenge by MAFAT DDR&D (Directorate of Defense Research & Development) in partnership with the **Israel National NLP Program**
20
+
21
+ **Result:** πŸ₯ˆ **2nd place** β€” nDCG@20 = **0.656792** (private test set) Β· **0.460408** (public test set)
22
+
23
+ **Author:** itk77
24
+
25
+ ---
26
+
27
+ ## Overview
28
+
29
+ This repository contains the complete inference code and fine-tuned models for the 2nd-place solution to the **Hebrew Semantic Retrieval Challenge**. The challenge tasked participants with ranking Hebrew paragraphs from a 127,731-passage corpus in response to natural-language Hebrew queries, evaluated by **NDCG@20**.
30
+
31
+ Hebrew is a morphologically rich Semitic language written in an almost consonant-only script, creating significant lexical ambiguity and making retrieval substantially harder than for high-resource languages. The solution addresses this with a carefully engineered three-stage pipeline: sparse + dual-dense retrieval fused via Weighted Reciprocal Rank Fusion (WRRF), followed by a BGE cross-encoder reranker fine-tuned specifically on the challenge corpus, and a final conditional score blending step.
32
+
33
+ ---
34
+
35
+ ## The Challenge
36
+
37
+ | Property | Detail |
38
+ |---|---|
39
+ | Organizer | MAFAT DDR&D + Israel National NLP Program |
40
+ | Corpus size | 127,731 Hebrew paragraphs |
41
+ | Data sources | Hebrew Wikipedia, Kol-Zchut (legal/civil-rights), Knesset committee protocols |
42
+ | Evaluation metric | NDCG@20 |
43
+ | Phase I | Public leaderboard (Codabench) |
44
+ | Phase II | Private test set with additional human annotation of previously unseen retrievals |
45
+ | Relevance scale | 0–4 (human annotated) |
46
+
47
+ ---
48
+
49
+ ## Solution Architecture
50
+
51
+ The solution is a **three-stage pipeline**: sparse + dual-dense retrieval fused with Weighted RRF, cross-encoder reranking, and conditional score blending.
52
+
53
+ ```
54
+ Query
55
+ β”‚
56
+ β”œβ”€β–Ί [BM25 (k1=1.3, b=0.7, w=1.0)] ──┐
57
+ β”œβ”€β–Ί [E5-large fine-tuned (w=1.2)] β”œβ”€β–Ί WRRF Fusion (k=35)
58
+ └─► [multilingual-E5-large (w=1.4)] β”˜
59
+ β”‚
60
+ β–Ό
61
+ Top-190 Candidates
62
+ β”‚
63
+ β–Ό
64
+ [BGE Cross-Encoder Reranker] (fine-tuned, max_len=640)
65
+ β”‚
66
+ β–Ό
67
+ Conditional Score Blending
68
+ β”‚
69
+ β–Ό
70
+ Final Top-20 Results
71
+ ```
72
+
73
+ ### Stage 1 β€” Weighted Reciprocal Rank Fusion (WRRF)
74
+
75
+ Three independent rankers each produce a ranked list of up to 190 candidates. Their lists are fused using **Weighted Reciprocal Rank Fusion**:
76
+
77
+ $$\text{WRRF}(d) = \frac{w_\text{BM25}}{k + r_\text{BM25}(d) + 1} + \frac{w_\text{E5-ft}}{k + r_\text{E5-ft}(d) + 1} + \frac{w_\text{E5-base}}{k + r_\text{E5-base}(d) + 1}$$
78
+
79
+ with $k = 35$ (RRF smoothing constant).
80
+
81
+ | Ranker | Model | Weight | Max Length | Notes |
82
+ |---|---|---|---|---|
83
+ | BM25 | Custom Hebrew BM25 (bm25s backend) | 1.0 | β€” | Strip nikkud, NFKC norm, prefix stripping |
84
+ | E5 (fine-tuned) | `e5-large-ft_v6` | 1.2 | 512 tokens | Mean pooling + L2 norm, `query:` / `passage:` prefixes |
85
+ | E5 (base) | `multilingual-e5-large` | 1.4 | 512 tokens | Via SentenceTransformers, BF16; labeled `GemmaEmbedder` in code but loads E5 |
86
+
87
+ **Hebrew-specific tokenization (BM25):** Unicode NFKC normalization, nikkud stripping (`\u0591–\u05C7`), Hebrew prefix removal (`Χ•`,`Χ”`,`Χ‘`,`ל`,`Χ›`,`מ`,`Χ©`) with both the stripped and original form indexed, and a custom Hebrew stopword list.
88
+
89
+ ### Stage 2 β€” BGE Cross-Encoder Reranking
90
+
91
+ The top-190 WRRF candidates are reranked by `bge-reranker-hsrc-pairwise-rrf-V1.4`, a BGE cross-encoder fine-tuned on the challenge corpus using **pairwise training with RRF-mined triples**. Pairs are scored with a max sequence length of 640 tokens.
92
+
93
+ ### Stage 3 β€” Conditional Score Blending
94
+
95
+ The final score uses a non-linear conditional boost that amplifies the WRRF signal where the reranker is uncertain:
96
+
97
+ $$\text{score}_\text{final} = \hat{s}_\text{BGE} + (1 - w_\text{BGE}) \cdot \hat{s}_\text{WRRF} \cdot (1 - \hat{s}_\text{BGE})$$
98
+
99
+ where $w_\text{BGE} = 0.07$, and both scores are **min-max normalized** to $[0, 1]$ over the candidate pool. When the reranker assigns a high score ($\hat{s}_\text{BGE} \approx 1$), the WRRF boost vanishes; when it is uncertain ($\hat{s}_\text{BGE} \approx 0$), the WRRF signal takes over.
100
+
101
+ ---
102
+
103
+ ## Included Models (fine-tuned)
104
+
105
+ | Path in repo | Base model | Fine-tuning |
106
+ |---|---|---|
107
+ | `models/e5-large-ft_v6/` | `intfloat/multilingual-e5-large` | Fine-tuned on the challenge corpus (v6 checkpoint) |
108
+ | `models/bge-reranker-hsrc-pairwise-rrf-V1.4/` | `BAAI/bge-reranker-v2-m3` | Fine-tuned on RRF-mined pairwise triples from the challenge corpus |
109
+ | `models/multilingual-e5-large/` | `intfloat/multilingual-e5-large` | Off-the-shelf (no fine-tuning) |
110
+
111
+ ---
112
+
113
+ ## Repository Structure
114
+
115
+ ```
116
+ model.py ← Full inference pipeline (preprocess + predict)
117
+ bm25_backends.py ← Pluggable BM25 backends (bm25s / pure-Python fallback)
118
+ text_utils.py ← Hebrew normalization & tokenization utilities
119
+ models/
120
+ e5-large-ft_v6/ ← Fine-tuned E5 embedder ✨
121
+ bge-reranker-hsrc-pairwise-rrf-V1.4/ ← Fine-tuned BGE reranker ✨
122
+ multilingual-e5-large/ ← Off-the-shelf secondary embedder
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Usage
128
+
129
+ The pipeline exposes two functions matching the competition API:
130
+
131
+ ```python
132
+ from model import preprocess, predict
133
+
134
+ # Build corpus index (run once)
135
+ # corpus_dict: {doc_id: {"passage": "..."}, ...}
136
+ preprocessed = preprocess(corpus_dict)
137
+
138
+ # Query at inference time
139
+ results = predict({"query": "ΧžΧ” Χ”Χ–Χ›Χ•Χ™Χ•Χͺ של Χ©Χ•Χ›Χ¨Χ™ Χ“Χ™Χ¨Χ”?"}, preprocessed)
140
+ # Returns: [{"paragraph_uuid": "...", "score": 0.87}, ...] (top-20)
141
+ ```
142
+
143
+ **Requirements:**
144
+ ```
145
+ torch
146
+ transformers
147
+ sentence-transformers
148
+ bm25s
149
+ scikit-learn
150
+ numpy
151
+ ```
152
+
153
+ A CUDA-capable GPU is strongly recommended (two large encoder models + one cross-encoder are loaded simultaneously, all in BF16/FP16).
154
+
155
+ ---
156
+
157
+ ## Training Pipeline
158
+
159
+ The full training pipeline is located in `repro/documentation/complete_pipeline/` and orchestrated by `pipeline.py`. It automates four sequential stages:
160
+
161
+ | Stage | Script | Description |
162
+ |---|---|---|
163
+ | 1 | `finetune_e5_large.py` | Fine-tunes E5 on the challenge corpus (12 runs, 2 epochs, lr=2e-6, batch=4) |
164
+ | 2 | `stage1_weight_sweep.py` | Offline grid sweep of WRRF weights (BM25, E5, Gemma) |
165
+ | 3 | `train_bge_ce_pairwise_rrf.py` | Trains the BGE cross-encoder reranker (lr=2e-5, max_len=640, batch=4Γ—accum=8) |
166
+ | 4 | `sweep_final2_from_components.py` | Offline sweep for the final blending weight |
167
+
168
+ ### Reranker Training Modes
169
+
170
+ The pipeline supports two parallel reranker training paths:
171
+
172
+ - **Deterministic mode** (`--rr_det_runs`): trains from **pinned triples** (`repro/documentation/triples/triples.jsonl`), enabling reproducible results.
173
+ - **Non-deterministic mining mode** (`--rr_nd_runs`): the first run mines fresh triples from the best E5 checkpoint; subsequent runs reuse them. ~1 in 7 runs matches submitted model quality.
174
+
175
+ ### Example Full Run Command
176
+
177
+ ```bash
178
+ python3 repro/documentation/complete_pipeline/pipeline.py \
179
+ --e5_runs 12 --e5_seed0 45 --e5_seed_stride 0 \
180
+ --e5_epochs 2 --e5_batch 4 --e5_lr 2e-6 \
181
+ --stage1_w_bm25 1.0,2.0,0.1 \
182
+ --stage1_w_e5 1.0,2.0,0.1 \
183
+ --stage1_w_gm 1.0,2.0,0.1 \
184
+ --rr_det_runs 1 --rr_det_seed0 42 --rr_det_seed_stride 0 \
185
+ --rr_det_triples_in repro/documentation/triples/triples.jsonl \
186
+ --rr_nd_runs 15 --rr_nd_seed0 42 --rr_nd_seed_stride 0 \
187
+ --rr_bsz 4 --rr_accum 8 --rr_lr 2e-5 --rr_max_len 640 \
188
+ --rr_sweep_rounds 2000
189
+ ```
190
+
191
+ **Hardware:** Original model trained on RTX 3080 Ti; reproducibility runs executed on L40S (~24 hours for the full pipeline with 12 E5 + 15 reranker runs).
192
+
193
+ ---
194
+
195
+ ## Evaluation Protocol
196
+
197
+ - **Holdout set:** First 100 queries of the provided training file (fixed split, never changed during development).
198
+ - **Local evaluation script:** `scripts/eval_std_final.py` β€” runs silently when `EVAL_STD_MODE=1`.
199
+ - **Score discrepancy:** 7 of the 100 holdout queries have no labels > 0 (empty relevance). The local script does not ignore these by default, resulting in a local nDCG ~0.615 vs. the public leaderboard score. When empty-label queries are excluded, local scores align with the official leaderboard.
200
+
201
+ ---
202
+
203
+ ## Technical Notes
204
+
205
+ - All models are loaded in **BF16** (E5, Gemma) or **FP16** (BGE reranker) to reduce GPU memory usage.
206
+ - **Corpus embedding caching:** E5 and Gemma corpus embeddings can be cached to disk (keyed by SHA-1 of document IDs + model path + corpus size) to skip re-encoding on repeated runs.
207
+ - **BM25 backend fallback chain:** `bm25_backends.py` β†’ direct `bm25s` β†’ pure-Python deterministic BM25 (guaranteed to work without external dependencies).
208
+ - **Dominant source of non-determinism:** GPU FP16/SDPA kernel behavior. Deterministic kernels are available but increase runtime ~3.6Γ— and may exceed GPU memory limits.
209
+
210
+ ---
211
+
212
+ ## Results
213
+
214
+ | Phase | NDCG@20 | Rank |
215
+ |---|---|---|
216
+ | Public (Phase I) | **0.460408** | πŸ₯ˆ 2nd |
217
+ | Private (Phase II) | **0.656792** | πŸ₯ˆ 2nd |
218
+
219
+ > The large gap between public and private scores is expected: the private phase incorporated additional human annotation of previously un-annotated retrieved documents, significantly impacting NDCG for systems that retrieved relevant but un-annotated paragraphs.
220
+
221
+ ---
222
+
223
+ ## Citation
224
+
225
+ If you use this solution or the models in this repository, please acknowledge the **Hebrew Semantic Retrieval Challenge** by MAFAT DDR&D and the Israel National NLP Program, and credit **itk77** as the solution author.
226
+
227
+ ---
228
+
229
+ ## Acknowledgements
230
+
231
+ - MAFAT DDR&D and the **Israel National NLP Program** for organizing the challenge and providing the annotated Hebrew corpus.
232
+ - The authors of `intfloat/multilingual-e5-large` and `BAAI/bge-reranker-v2-m3`.