Sentence Similarity
sentence-transformers
Safetensors
lfm2
liquid
lfm2.5
edge
feature-extraction
custom_code
Instructions to use LiquidAI/LFM2.5-Embedding-350M with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- sentence-transformers
How to use LiquidAI/LFM2.5-Embedding-350M with sentence-transformers:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("LiquidAI/LFM2.5-Embedding-350M", trust_remote_code=True) sentences = [ "That is a happy person", "That is a happy dog", "That is a very happy person", "Today is a sunny day" ] embeddings = model.encode(sentences) similarities = model.similarity(embeddings, embeddings) print(similarities.shape) # [4, 4] - Notebooks
- Google Colab
- Kaggle
| """LFM2 backbone with bidirectional attention + non-causal short-conv, for retrieval/embedding use. | |
| Wired into the HF repo via `auto_map` in config.json so that | |
| AutoModel.from_pretrained(repo, trust_remote_code=True) | |
| SentenceTransformer(repo, trust_remote_code=True) | |
| both return a model with the encoder-style patches already applied. | |
| Supports `attn_implementation` in {"eager", "sdpa", "flash_attention_2"}: | |
| eager/sdpa consume a 4D additive pad-only mask and reproduce the exact | |
| training-time behavior; flash_attention_2 receives the 2D padding mask (or | |
| None) and runs the kernel non-causally via `Lfm2Attention.is_causal = False`, | |
| yielding outputs equivalent to the unpadded forward. | |
| Repos may set `"disable_flash_attention": true` in config.json to reject | |
| flash_attention_2 at load time (used for ColBERT, where PyLate query expansion | |
| tokens — attention_mask=0 but scored in MaxSim — are incompatible with FA2 | |
| unpadding and severely degrade retrieval quality). | |
| """ | |
| from typing import Optional | |
| import torch | |
| import torch.nn.functional as F | |
| from transformers.models.lfm2 import modeling_lfm2 as _lfm2_mod | |
| from transformers.models.lfm2.modeling_lfm2 import ( | |
| Lfm2Attention, | |
| Lfm2Model, | |
| Lfm2ShortConv, | |
| apply_mask_to_padding_states, | |
| ) | |
| def _bidirectional_mask(config, **kwargs) -> Optional[torch.Tensor]: | |
| # transformers has renamed the embeds kwarg across versions | |
| # (input_embeds <-> inputs_embeds); accept either to stay forward-compatible. | |
| embeds = kwargs.get("inputs_embeds") | |
| if embeds is None: | |
| embeds = kwargs.get("input_embeds") | |
| attention_mask = kwargs.get("attention_mask") | |
| past_key_values = kwargs.get("past_key_values") | |
| if config._attn_implementation == "flash_attention_2": | |
| # FA2 only uses the 2D padding mask to unpad sequences; causality is | |
| # controlled by `Lfm2Attention.is_causal` (set to False below). | |
| if attention_mask is not None and not attention_mask.all(): | |
| return attention_mask | |
| return None | |
| device = embeds.device | |
| dtype = embeds.dtype | |
| bsz, q_len = embeds.shape[:2] | |
| past = past_key_values.get_seq_length() if past_key_values is not None else 0 | |
| kv_len = past + q_len | |
| mask = torch.zeros((bsz, 1, q_len, kv_len), device=device, dtype=dtype) | |
| if attention_mask is not None: | |
| cur_len = attention_mask.size(-1) | |
| key_pad_flags = (attention_mask == 0).to(device=device, dtype=torch.float32) | |
| pad_vec = torch.zeros((bsz, kv_len), device=device, dtype=torch.float32) | |
| if cur_len > 0: | |
| pad_vec[:, past:past + cur_len] = key_pad_flags * -1e9 | |
| mask = mask + pad_vec.to(dtype)[:, None, None, :] | |
| return mask | |
| def _noncausal_shortconv_forward( | |
| self, | |
| hidden_states: torch.Tensor, | |
| past_key_values=None, | |
| cache_position=None, | |
| attention_mask: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| # eager/sdpa pass the 4D additive mask, on which this is a no-op — matching | |
| # the behavior the checkpoints were trained with. FA2 passes the 2D pad | |
| # mask, so pads are zeroed before the conv (closest match to the unpadded | |
| # forward, since FA2 cannot reproduce the padded sdpa pad-state evolution). | |
| x = apply_mask_to_padding_states(hidden_states, attention_mask) | |
| BCx = self.in_proj(x).transpose(-1, -2) | |
| B, C, x = BCx.chunk(3, dim=-2) | |
| Bx = B * x | |
| k = self.conv.weight.shape[-1] | |
| pad = k // 2 | |
| conv_out = F.conv1d( | |
| Bx, weight=self.conv.weight, bias=self.conv.bias, | |
| stride=1, padding=pad, dilation=1, groups=Bx.shape[1], | |
| ) | |
| if conv_out.shape[-1] > Bx.shape[-1]: | |
| conv_out = conv_out[..., :Bx.shape[-1]] | |
| elif conv_out.shape[-1] < Bx.shape[-1]: | |
| conv_out = F.pad(conv_out, (0, Bx.shape[-1] - conv_out.shape[-1])) | |
| y = C * conv_out | |
| y = y.transpose(-1, -2).contiguous() | |
| return self.out_proj(y) | |
| def _shortconv_forward(self, *args, **kwargs): | |
| return self.slow_forward(*args, **kwargs) | |
| _PATCHED = False | |
| def _install_patches() -> None: | |
| global _PATCHED | |
| if _PATCHED: | |
| return | |
| _lfm2_mod.create_causal_mask = _bidirectional_mask | |
| Lfm2ShortConv.slow_forward = _noncausal_shortconv_forward | |
| Lfm2ShortConv.forward = _shortconv_forward | |
| _PATCHED = True | |
| _install_patches() | |
| class Lfm2BidirectionalModel(Lfm2Model): | |
| """LFM2 patched for encoder-style use: full bidirectional attention + non-causal short-conv.""" | |
| def __init__(self, config): | |
| if ( | |
| getattr(config, "_attn_implementation", None) == "flash_attention_2" | |
| and getattr(config, "disable_flash_attention", False) | |
| ): | |
| raise ValueError( | |
| "flash_attention_2 is disabled for this model: query expansion " | |
| "tokens (attention_mask=0 but scored in MaxSim) are incompatible " | |
| "with FA2 unpadding and severely degrade retrieval quality. " | |
| "Load with attn_implementation='sdpa' (default) or 'eager'." | |
| ) | |
| _install_patches() | |
| super().__init__(config) | |
| for module in self.modules(): | |
| if isinstance(module, Lfm2Attention): | |
| module.is_causal = False | |