Sentence Similarity
Safetensors
sentence-transformers
PyLate
lfm2
liquid
lfm2.5
edge
ColBERT
feature-extraction
custom_code
Instructions to use LiquidAI/LFM2.5-ColBERT-350M with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- sentence-transformers
How to use LiquidAI/LFM2.5-ColBERT-350M with sentence-transformers:
from pylate import models queries = [ "Which planet is known as the Red Planet?", "What is the largest planet in our solar system?", ] documents = [ ["Mars is the Red Planet.", "Venus is Earth's twin."], ["Jupiter is the largest planet.", "Saturn has rings."], ] model = models.ColBERT(model_name_or_path="LiquidAI/LFM2.5-ColBERT-350M") queries_emb = model.encode(queries, is_query=True) docs_emb = model.encode(documents, is_query=False) - Notebooks
- Google Colab
- Kaggle
Add FA2-capable modeling file but disable flash_attention_2 via config (incompatible with PyLate query expansion; ~10 ndcg@10 loss)
Browse files- README.md +4 -0
- config.json +2 -1
- modeling_lfm2_bidirectional.py +37 -1
README.md
CHANGED
|
@@ -67,3 +67,7 @@ reranked = rank.rerank(
|
|
| 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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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`).
|
config.json
CHANGED
|
@@ -60,5 +60,6 @@
|
|
| 60 |
"vocab_size": 64402,
|
| 61 |
"auto_map": {
|
| 62 |
"AutoModel": "modeling_lfm2_bidirectional.Lfm2BidirectionalModel"
|
| 63 |
-
}
|
|
|
|
| 64 |
}
|
|
|
|
| 60 |
"vocab_size": 64402,
|
| 61 |
"auto_map": {
|
| 62 |
"AutoModel": "modeling_lfm2_bidirectional.Lfm2BidirectionalModel"
|
| 63 |
+
},
|
| 64 |
+
"disable_flash_attention": true
|
| 65 |
}
|
modeling_lfm2_bidirectional.py
CHANGED
|
@@ -6,6 +6,17 @@ Wired into the HF repo via `auto_map` in config.json so that
|
|
| 6 |
SentenceTransformer(repo, trust_remote_code=True)
|
| 7 |
|
| 8 |
both return a model with the encoder-style patches already applied.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
"""
|
| 10 |
|
| 11 |
from typing import Optional
|
|
@@ -14,6 +25,7 @@ import torch
|
|
| 14 |
import torch.nn.functional as F
|
| 15 |
from transformers.models.lfm2 import modeling_lfm2 as _lfm2_mod
|
| 16 |
from transformers.models.lfm2.modeling_lfm2 import (
|
|
|
|
| 17 |
Lfm2Model,
|
| 18 |
Lfm2ShortConv,
|
| 19 |
apply_mask_to_padding_states,
|
|
@@ -27,7 +39,14 @@ def _bidirectional_mask(
|
|
| 27 |
cache_position: Optional[torch.LongTensor],
|
| 28 |
past_key_values,
|
| 29 |
position_ids: Optional[torch.LongTensor],
|
| 30 |
-
) -> torch.Tensor:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
device = input_embeds.device
|
| 32 |
dtype = input_embeds.dtype
|
| 33 |
bsz, q_len = input_embeds.shape[:2]
|
|
@@ -52,6 +71,10 @@ def _noncausal_shortconv_forward(
|
|
| 52 |
cache_position=None,
|
| 53 |
attention_mask: Optional[torch.Tensor] = None,
|
| 54 |
) -> torch.Tensor:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
x = apply_mask_to_padding_states(hidden_states, attention_mask)
|
| 56 |
|
| 57 |
BCx = self.in_proj(x).transpose(-1, -2)
|
|
@@ -98,5 +121,18 @@ class Lfm2BidirectionalModel(Lfm2Model):
|
|
| 98 |
"""LFM2 patched for encoder-style use: full bidirectional attention + non-causal short-conv."""
|
| 99 |
|
| 100 |
def __init__(self, config):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
_install_patches()
|
| 102 |
super().__init__(config)
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
SentenceTransformer(repo, trust_remote_code=True)
|
| 7 |
|
| 8 |
both return a model with the encoder-style patches already applied.
|
| 9 |
+
|
| 10 |
+
Supports `attn_implementation` in {"eager", "sdpa", "flash_attention_2"}:
|
| 11 |
+
eager/sdpa consume a 4D additive pad-only mask and reproduce the exact
|
| 12 |
+
training-time behavior; flash_attention_2 receives the 2D padding mask (or
|
| 13 |
+
None) and runs the kernel non-causally via `Lfm2Attention.is_causal = False`,
|
| 14 |
+
yielding outputs equivalent to the unpadded forward.
|
| 15 |
+
|
| 16 |
+
Repos may set `"disable_flash_attention": true` in config.json to reject
|
| 17 |
+
flash_attention_2 at load time (used for ColBERT, where PyLate query expansion
|
| 18 |
+
tokens — attention_mask=0 but scored in MaxSim — are incompatible with FA2
|
| 19 |
+
unpadding and severely degrade retrieval quality).
|
| 20 |
"""
|
| 21 |
|
| 22 |
from typing import Optional
|
|
|
|
| 25 |
import torch.nn.functional as F
|
| 26 |
from transformers.models.lfm2 import modeling_lfm2 as _lfm2_mod
|
| 27 |
from transformers.models.lfm2.modeling_lfm2 import (
|
| 28 |
+
Lfm2Attention,
|
| 29 |
Lfm2Model,
|
| 30 |
Lfm2ShortConv,
|
| 31 |
apply_mask_to_padding_states,
|
|
|
|
| 39 |
cache_position: Optional[torch.LongTensor],
|
| 40 |
past_key_values,
|
| 41 |
position_ids: Optional[torch.LongTensor],
|
| 42 |
+
) -> Optional[torch.Tensor]:
|
| 43 |
+
if config._attn_implementation == "flash_attention_2":
|
| 44 |
+
# FA2 only uses the 2D padding mask to unpad sequences; causality is
|
| 45 |
+
# controlled by `Lfm2Attention.is_causal` (set to False below).
|
| 46 |
+
if attention_mask is not None and not attention_mask.all():
|
| 47 |
+
return attention_mask
|
| 48 |
+
return None
|
| 49 |
+
|
| 50 |
device = input_embeds.device
|
| 51 |
dtype = input_embeds.dtype
|
| 52 |
bsz, q_len = input_embeds.shape[:2]
|
|
|
|
| 71 |
cache_position=None,
|
| 72 |
attention_mask: Optional[torch.Tensor] = None,
|
| 73 |
) -> torch.Tensor:
|
| 74 |
+
# eager/sdpa pass the 4D additive mask, on which this is a no-op — matching
|
| 75 |
+
# the behavior the checkpoints were trained with. FA2 passes the 2D pad
|
| 76 |
+
# mask, so pads are zeroed before the conv (closest match to the unpadded
|
| 77 |
+
# forward, since FA2 cannot reproduce the padded sdpa pad-state evolution).
|
| 78 |
x = apply_mask_to_padding_states(hidden_states, attention_mask)
|
| 79 |
|
| 80 |
BCx = self.in_proj(x).transpose(-1, -2)
|
|
|
|
| 121 |
"""LFM2 patched for encoder-style use: full bidirectional attention + non-causal short-conv."""
|
| 122 |
|
| 123 |
def __init__(self, config):
|
| 124 |
+
if (
|
| 125 |
+
getattr(config, "_attn_implementation", None) == "flash_attention_2"
|
| 126 |
+
and getattr(config, "disable_flash_attention", False)
|
| 127 |
+
):
|
| 128 |
+
raise ValueError(
|
| 129 |
+
"flash_attention_2 is disabled for this model: query expansion "
|
| 130 |
+
"tokens (attention_mask=0 but scored in MaxSim) are incompatible "
|
| 131 |
+
"with FA2 unpadding and severely degrade retrieval quality. "
|
| 132 |
+
"Load with attn_implementation='sdpa' (default) or 'eager'."
|
| 133 |
+
)
|
| 134 |
_install_patches()
|
| 135 |
super().__init__(config)
|
| 136 |
+
for module in self.modules():
|
| 137 |
+
if isinstance(module, Lfm2Attention):
|
| 138 |
+
module.is_causal = False
|