EdoardoMosca commited on
Commit
4018611
·
verified ·
1 Parent(s): 2095737

readme: full model card with bidi/LFM2.5 details and PyLate usage

Browse files
Files changed (1) hide show
  1. README.md +185 -22
README.md CHANGED
@@ -1,16 +1,112 @@
1
- # LFM2.5 ColBERT Preview
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- A multilingual late-interaction (ColBERT-style) retriever built on the LFM2 backbone, patched for bidirectional attention. Compatible with [PyLate](https://github.com/lightonai/pylate).
4
 
5
- - **Backbone:** LFM2 350M (causal LM patched to encoder-style attention)
6
- - **Similarity:** MaxSim (late interaction)
7
- - **Output dimensionality:** 128 per token
8
- - **Query length:** 32 tokens
9
- - **Document length:** 512 tokens
10
- - **Query prefix:** `[Q] `   **Document prefix:** `[D] ` (handled automatically by PyLate)
11
- - **Source checkpoint:** `output/train_1811379_colbert-ft-s2/best`
12
 
13
- ## Usage
 
 
 
 
 
 
 
 
 
 
14
 
15
  Install PyLate:
16
 
@@ -18,32 +114,78 @@ Install PyLate:
18
  pip install -U pylate
19
  ```
20
 
21
- ### Retrieval (indexing + search)
 
 
 
 
22
 
23
  ```python
24
  from pylate import indexes, models, retrieve
25
 
 
26
  model = models.ColBERT(
27
  model_name_or_path="LiquidAI/LFM2.5-ColBERT-350M",
28
  trust_remote_code=True,
29
  )
30
  model.tokenizer.pad_token = model.tokenizer.eos_token
31
 
32
- index = indexes.PLAID(index_folder="pylate-index", index_name="index", override=True)
 
 
 
 
 
33
 
 
34
  documents_ids = ["1", "2", "3"]
35
  documents = ["document 1 text", "document 2 text", "document 3 text"]
36
 
37
- documents_embeddings = model.encode(documents, batch_size=32, is_query=False, show_progress_bar=True)
38
- index.add_documents(documents_ids=documents_ids, documents_embeddings=documents_embeddings)
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
 
 
 
 
 
 
 
 
 
 
40
  retriever = retrieve.ColBERT(index=index)
41
- queries_embeddings = model.encode(["query for document 3", "query for document 1"], batch_size=32, is_query=True)
42
- scores = retriever.retrieve(queries_embeddings=queries_embeddings, k=10)
 
 
 
 
 
 
 
 
 
 
43
  ```
44
 
45
  ### Reranking
46
 
 
 
47
  ```python
48
  from pylate import rank, models
49
 
@@ -53,21 +195,42 @@ model = models.ColBERT(
53
  )
54
 
55
  queries = ["query A", "query B"]
56
- documents = [["document A", "document B"], ["document 1", "document C", "document B"]]
57
- documents_ids = [[1, 2], [1, 3, 2]]
 
 
 
 
 
 
58
 
59
  queries_embeddings = model.encode(queries, is_query=True)
60
  documents_embeddings = model.encode(documents, is_query=False)
61
 
62
- reranked = rank.rerank(
63
  documents_ids=documents_ids,
64
  queries_embeddings=queries_embeddings,
65
  documents_embeddings=documents_embeddings,
66
  )
67
  ```
68
 
69
- `trust_remote_code=True` is required: it loads `modeling_lfm2_bidirectional.py`, which replaces the causal attention mask and short-conv padding with their non-causal equivalents. Without it the model will silently use causal attention and produce poor embeddings.
 
 
 
 
70
 
71
- ### Note on FlashAttention-2
72
 
73
- `attn_implementation="flash_attention_2"` is intentionally rejected at load time (`disable_flash_attention` in `config.json`). ColBERT query expansion pads queries with mask tokens that have `attention_mask=0` but are still scored in MaxSim; FA2's unpadding cannot compute embeddings for those positions, which severely degrades retrieval quality (~10 ndcg@10 points on NanoBEIR). Use the default `sdpa` (or `eager`).
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ - es
5
+ - de
6
+ - fr
7
+ - it
8
+ - pt
9
+ - ar
10
+ - sv
11
+ - 'no'
12
+ - ja
13
+ - ko
14
+ tags:
15
+ - liquid
16
+ - lfm2
17
+ - lfm2.5
18
+ - edge
19
+ - ColBERT
20
+ - PyLate
21
+ - sentence-transformers
22
+ - sentence-similarity
23
+ - feature-extraction
24
+ pipeline_tag: sentence-similarity
25
+ library_name: PyLate
26
+ license: other
27
+ license_name: lfm1.0
28
+ license_link: LICENSE
29
+ ---
30
+
31
+ <center>
32
+ <div style="text-align: center;">
33
+ <img
34
+ src="https://cdn-uploads.huggingface.co/production/uploads/61b8e2ba285851687028d395/7_6D7rWrLxp2hb6OHSV1p.png"
35
+ alt="Liquid AI"
36
+ style="width: 100%; max-width: 100%; height: auto; display: inline-block; margin-bottom: 0.5em; margin-top: 0.5em;"
37
+ />
38
+ </div>
39
+ <div style="display: flex; justify-content: center; gap: 0.5em;">
40
+ <a href="https://playground.liquid.ai/"><strong>Try LFM</strong></a> •
41
+ <a href="https://docs.liquid.ai/lfm/getting-started/welcome"><strong>Docs</strong></a> •
42
+ <a href="https://leap.liquid.ai/"><strong>LEAP</strong></a> •
43
+ <a href="https://discord.com/invite/liquid-ai"><strong>Discord</strong></a>
44
+ </div>
45
+ </center>
46
+
47
+ <br>
48
+
49
+ # LFM2.5-ColBERT-350M
50
+
51
+ LFM2.5-ColBERT-350M is a multilingual late-interaction retriever and the first bidirectional ColBERT in the LFM family. Built on the LFM2.5-350M-Base backbone with bidirectional patches, it converts each token into a vector and matches queries word-by-word via MaxSim, giving higher accuracy and better generalization than single-vector retrievers — at the cost of a larger index.
52
+
53
+ - **Strong multilingual & cross-lingual performance** across 11 languages.
54
+ - **Word-level matching** via late interaction for higher accuracy on hard queries.
55
+ - **Small footprint** (350M params) — runs cheaply almost anywhere.
56
+ - **Drop-in PyLate compatibility** for indexing, retrieval, and reranking.
57
+
58
+ Pair it with [LFM2.5-Embedding-350M](https://huggingface.co/LiquidAI/LFM2.5-Embedding-350M) (the matching dense bi-encoder) when you want the smallest, fastest index; pick this ColBERT when accuracy matters more than storage.
59
+
60
+ ## 📄 Model details
61
+
62
+ Late-interaction retrievers preserve much of the **expressivity** of cross-encoders while retaining the **efficiency** of bi-encoders — in practice, they retrieve at scale and rank at the same time.
63
+
64
+ Especially well-suited for **short-context** search: product catalogs, FAQ knowledge bases, support docs, and other collections that need to be searched quickly, cheaply, and reliably across languages.
65
+
66
+ | Property | **LFM2.5-ColBERT-350M** |
67
+ | --------------------- | ----------------------- |
68
+ | **Total parameters** | ~353M |
69
+ | **Layers** | 17 (10 conv + 6 attn + 1 dense) |
70
+ | **Context length** | 32,768 tokens |
71
+ | **Vocabulary size** | 64,402 |
72
+ | **Training precision**| BF16 |
73
+ | **License** | LFM Open License v1.0 |
74
+
75
+ **Document length:** 512 tokens &nbsp;&nbsp; **Query length:** 32 tokens
76
+
77
+ **Output dimensionality:** 128 per token &nbsp;&nbsp; **Similarity function:** MaxSim
78
+
79
+ **Supported languages:** English, Spanish, German, French, Italian, Portuguese, Arabic, Swedish, Norwegian, Japanese, Korean.
80
+
81
+ ```text
82
+ ColBERT(
83
+ (0): Transformer({'max_seq_length': 511, 'do_lower_case': False}) with Transformer model: Lfm2BidirectionalModel
84
+ (1): Dense({'in_features': 1024, 'out_features': 128, 'bias': False, 'activation_function': 'torch.nn.modules.linear.Identity'})
85
+ )
86
+ ```
87
+
88
+ ### Architecture: bidirectional LFM2.5
89
+
90
+ LFM2.5-ColBERT-350M starts from **LFM2.5-350M-Base** — Liquid's newer mid-trained LFM2.5 checkpoint — and applies a small set of bidirectional patches that adapt the hybrid LFM2 backbone from causal language modeling to full-context encoding:
91
+
92
+ - **Attention mask:** the causal mask becomes a pad-only **bidirectional** mask, so every token can attend to the full sequence.
93
+ - **Short convolutions:** the LFM2 short-conv layers become **non-causal**, mixing information symmetrically rather than only from the past.
94
 
95
+ This preserves the efficiency of the LFM2 architecture while producing the full-context representations that retrieval tasks need. The patches are shipped in `modeling_lfm2_bidirectional.py` inside this repo and are applied automatically when you load with `trust_remote_code=True` — see below.
96
 
97
+ ### Training
 
 
 
 
 
 
98
 
99
+ Three-stage recipe shared with the bi-encoder companion:
100
+
101
+ 1. **Large-scale contrastive pretraining** on curated multi-domain English pairs.
102
+ 2. **Multilingual and cross-lingual distillation** from a strong teacher across all 11 supported languages.
103
+ 3. **Final fine-tuning** on hard-mined negatives.
104
+
105
+ The training data combines curated internal data with open-source English retrieval datasets. LLM-based translation of queries and documents was used to expand multilingual and cross-lingual pairs in stages 2 and 3. The staged structure follows LightOn's LateOn / DenseOn releases.
106
+
107
+ This ColBERT receives **less explicit cross-lingual data** than [LFM2.5-Embedding-350M](https://huggingface.co/LiquidAI/LFM2.5-Embedding-350M), because cross-lingual retrieval emerges more naturally in late-interaction setups and benefits less from additional supervision.
108
+
109
+ ## 🏃 How to run
110
 
111
  Install PyLate:
112
 
 
114
  pip install -U pylate
115
  ```
116
 
117
+ ### Retrieval
118
+
119
+ Use this model with PyLate to index and retrieve documents. The index uses [FastPLAID](https://github.com/lightonai/fast-plaid) for efficient similarity search.
120
+
121
+ #### Indexing documents
122
 
123
  ```python
124
  from pylate import indexes, models, retrieve
125
 
126
+ # Step 1: Load the ColBERT model (trust_remote_code applies the bidirectional patches)
127
  model = models.ColBERT(
128
  model_name_or_path="LiquidAI/LFM2.5-ColBERT-350M",
129
  trust_remote_code=True,
130
  )
131
  model.tokenizer.pad_token = model.tokenizer.eos_token
132
 
133
+ # Step 2: Initialize the PLAID index
134
+ index = indexes.PLAID(
135
+ index_folder="pylate-index",
136
+ index_name="index",
137
+ override=True, # Overwrites any existing index with the same name
138
+ )
139
 
140
+ # Step 3: Encode the documents
141
  documents_ids = ["1", "2", "3"]
142
  documents = ["document 1 text", "document 2 text", "document 3 text"]
143
 
144
+ documents_embeddings = model.encode(
145
+ documents,
146
+ batch_size=32,
147
+ is_query=False,
148
+ show_progress_bar=True,
149
+ )
150
+
151
+ # Step 4: Add document embeddings to the index
152
+ index.add_documents(
153
+ documents_ids=documents_ids,
154
+ documents_embeddings=documents_embeddings,
155
+ )
156
+ ```
157
+
158
+ You don't have to rebuild the index every time. Reload an existing index by instantiating it with the same folder/name and `override=False`:
159
 
160
+ ```python
161
+ index = indexes.PLAID(
162
+ index_folder="pylate-index",
163
+ index_name="index",
164
+ )
165
+ ```
166
+
167
+ #### Retrieving top-k documents
168
+
169
+ ```python
170
  retriever = retrieve.ColBERT(index=index)
171
+
172
+ queries_embeddings = model.encode(
173
+ ["query for document 3", "query for document 1"],
174
+ batch_size=32,
175
+ is_query=True,
176
+ show_progress_bar=True,
177
+ )
178
+
179
+ scores = retriever.retrieve(
180
+ queries_embeddings=queries_embeddings,
181
+ k=10,
182
+ )
183
  ```
184
 
185
  ### Reranking
186
 
187
+ For reranking on top of a first-stage pipeline, no index is needed:
188
+
189
  ```python
190
  from pylate import rank, models
191
 
 
195
  )
196
 
197
  queries = ["query A", "query B"]
198
+ documents = [
199
+ ["document A", "document B"],
200
+ ["document 1", "document C", "document B"],
201
+ ]
202
+ documents_ids = [
203
+ [1, 2],
204
+ [1, 3, 2],
205
+ ]
206
 
207
  queries_embeddings = model.encode(queries, is_query=True)
208
  documents_embeddings = model.encode(documents, is_query=False)
209
 
210
+ reranked_documents = rank.rerank(
211
  documents_ids=documents_ids,
212
  queries_embeddings=queries_embeddings,
213
  documents_embeddings=documents_embeddings,
214
  )
215
  ```
216
 
217
+ > **Note on `trust_remote_code`.** Loading requires `trust_remote_code=True` so the repo's `modeling_lfm2_bidirectional.py` can replace the causal attention mask and short-conv padding with their non-causal equivalents. Without it the model will silently use causal attention and produce poor retrieval embeddings.
218
+
219
+ ## 📈 Performance
220
+
221
+ Detailed multilingual and cross-lingual NanoBEIR benchmarks are coming. Internally this checkpoint outperforms the previous [LFM2-ColBERT-350M](https://huggingface.co/LiquidAI/LFM2-ColBERT-350M) on multilingual NanoBEIR.
222
 
223
+ ## 📬 Contact
224
 
225
+ Questions or feedback? [Join our Discord](https://discord.com/invite/liquid-ai).
226
+
227
+ ## Citation
228
+
229
+ ```
230
+ @misc{PyLate,
231
+ title={PyLate: Flexible Training and Retrieval for Late Interaction Models},
232
+ author={Chaffin, Antoine and Sourty, Raphaël},
233
+ url={https://github.com/lightonai/pylate},
234
+ year={2024}
235
+ }
236
+ ```