Instructions to use RESMP-DEV/LFM2.5-Encoder-350M-MLX-FP32 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use RESMP-DEV/LFM2.5-Encoder-350M-MLX-FP32 with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] hf download RESMP-DEV/LFM2.5-Encoder-350M-MLX-FP32 --local-dir LFM2.5-Encoder-350M-MLX-FP32
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
Download model.py from RESMP-DEV/LFM2.5-Encoder-350M-MLX-FP32: direct link, hf CLI and curl.
- Browser
- Download file 8.24 kB
-
https://huggingface.co/RESMP-DEV/LFM2.5-Encoder-350M-MLX-FP32/resolve/main/model.py
- Command line
-
hf download hf://RESMP-DEV/LFM2.5-Encoder-350M-MLX-FP32/model.py
-
curl -L -o model.py https://huggingface.co/RESMP-DEV/LFM2.5-Encoder-350M-MLX-FP32/resolve/main/model.py
8.24 kB
| """Native MLX implementation of LiquidAI/LFM2.5-Encoder-350M. | |
| This is an encoder/masked-language model: attention and short convolutions are | |
| bidirectional. It intentionally does not use mlx-lm's causal generation API. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Optional | |
| import mlx.core as mx | |
| import mlx.nn as nn | |
| class ModelArgs: | |
| vocab_size: int | |
| hidden_size: int | |
| num_hidden_layers: int | |
| num_attention_heads: int | |
| num_key_value_heads: int | |
| norm_eps: float | |
| conv_bias: bool | |
| conv_L_cache: int | |
| block_dim: int | |
| intermediate_size: int | |
| block_multiple_of: int | |
| block_ffn_dim_multiplier: float | |
| block_auto_adjust_ff_dim: bool | |
| layer_types: list[str] | |
| rope_theta: float = 1_000_000.0 | |
| def from_dict(cls, config: dict) -> "ModelArgs": | |
| rope = config.get("rope_parameters") or {} | |
| fields = { | |
| "vocab_size", | |
| "hidden_size", | |
| "num_hidden_layers", | |
| "num_attention_heads", | |
| "num_key_value_heads", | |
| "norm_eps", | |
| "conv_bias", | |
| "conv_L_cache", | |
| "block_dim", | |
| "intermediate_size", | |
| "block_multiple_of", | |
| "block_ffn_dim_multiplier", | |
| "block_auto_adjust_ff_dim", | |
| "layer_types", | |
| } | |
| values = {key: config[key] for key in fields} | |
| values["rope_theta"] = rope.get("rope_theta", config.get("rope_theta", 1_000_000.0)) | |
| return cls(**values) | |
| class Attention(nn.Module): | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| dim = args.hidden_size | |
| self.n_heads = args.num_attention_heads | |
| self.n_kv_heads = args.num_key_value_heads | |
| self.head_dim = dim // self.n_heads | |
| self.scale = self.head_dim**-0.5 | |
| self.q_layernorm = nn.RMSNorm(self.head_dim, eps=args.norm_eps) | |
| self.k_layernorm = nn.RMSNorm(self.head_dim, eps=args.norm_eps) | |
| self.q_proj = nn.Linear(dim, self.n_heads * self.head_dim, bias=False) | |
| self.k_proj = nn.Linear(dim, self.n_kv_heads * self.head_dim, bias=False) | |
| self.v_proj = nn.Linear(dim, self.n_kv_heads * self.head_dim, bias=False) | |
| self.out_proj = nn.Linear(self.n_heads * self.head_dim, dim, bias=False) | |
| self.rope = nn.RoPE(self.head_dim, base=args.rope_theta, traditional=False) | |
| def __call__(self, x: mx.array, mask: Optional[mx.array] = None) -> mx.array: | |
| batch, length, _ = x.shape | |
| queries = self.q_proj(x).reshape(batch, length, self.n_heads, -1) | |
| keys = self.k_proj(x).reshape(batch, length, self.n_kv_heads, -1) | |
| values = self.v_proj(x).reshape(batch, length, self.n_kv_heads, -1) | |
| queries = self.q_layernorm(queries).transpose(0, 2, 1, 3) | |
| keys = self.k_layernorm(keys).transpose(0, 2, 1, 3) | |
| values = values.transpose(0, 2, 1, 3) | |
| queries = self.rope(queries) | |
| keys = self.rope(keys) | |
| output = mx.fast.scaled_dot_product_attention( | |
| queries, keys, values, scale=self.scale, mask=mask | |
| ) | |
| output = output.transpose(0, 2, 1, 3).reshape(batch, length, -1) | |
| return self.out_proj(output) | |
| class ShortConv(nn.Module): | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| self.kernel_size = args.conv_L_cache | |
| self.conv = nn.Conv1d( | |
| in_channels=args.hidden_size, | |
| out_channels=args.hidden_size, | |
| kernel_size=self.kernel_size, | |
| groups=args.hidden_size, | |
| bias=args.conv_bias, | |
| ) | |
| self.in_proj = nn.Linear(args.hidden_size, 3 * args.hidden_size, bias=args.conv_bias) | |
| self.out_proj = nn.Linear(args.hidden_size, args.hidden_size, bias=args.conv_bias) | |
| def __call__(self, x: mx.array, padding_mask: Optional[mx.array] = None) -> mx.array: | |
| if padding_mask is not None: | |
| x = mx.where(padding_mask[..., None], x, 0) | |
| gate_b, gate_c, value = mx.split(self.in_proj(x), 3, axis=-1) | |
| gated = gate_b * value | |
| pad = self.kernel_size // 2 | |
| gated = mx.pad(gated, [(0, 0), (pad, pad), (0, 0)]) | |
| conv_out = self.conv(gated) | |
| if conv_out.shape[1] > x.shape[1]: | |
| conv_out = conv_out[:, : x.shape[1], :] | |
| elif conv_out.shape[1] < x.shape[1]: | |
| conv_out = mx.pad(conv_out, [(0, 0), (0, x.shape[1] - conv_out.shape[1]), (0, 0)]) | |
| return self.out_proj(gate_c * conv_out) | |
| class MLP(nn.Module): | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| ff_dim = args.intermediate_size | |
| if args.block_auto_adjust_ff_dim: | |
| ff_dim = int(2 * ff_dim / 3) | |
| ff_dim = int(args.block_ffn_dim_multiplier * ff_dim) | |
| multiple = args.block_multiple_of | |
| ff_dim = multiple * ((ff_dim + multiple - 1) // multiple) | |
| self.w1 = nn.Linear(args.block_dim, ff_dim, bias=False) | |
| self.w3 = nn.Linear(args.block_dim, ff_dim, bias=False) | |
| self.w2 = nn.Linear(ff_dim, args.block_dim, bias=False) | |
| def __call__(self, x: mx.array) -> mx.array: | |
| return self.w2(nn.silu(self.w1(x)) * self.w3(x)) | |
| class EncoderLayer(nn.Module): | |
| def __init__(self, args: ModelArgs, layer_type: str): | |
| super().__init__() | |
| self.is_attention_layer = layer_type == "full_attention" | |
| if self.is_attention_layer: | |
| self.self_attn = Attention(args) | |
| else: | |
| self.conv = ShortConv(args) | |
| self.feed_forward = MLP(args) | |
| self.operator_norm = nn.RMSNorm(args.hidden_size, eps=args.norm_eps) | |
| self.ffn_norm = nn.RMSNorm(args.hidden_size, eps=args.norm_eps) | |
| def __call__( | |
| self, | |
| x: mx.array, | |
| attention_mask: Optional[mx.array], | |
| padding_mask: Optional[mx.array], | |
| ) -> mx.array: | |
| normalized = self.operator_norm(x) | |
| if self.is_attention_layer: | |
| residual = self.self_attn(normalized, mask=attention_mask) | |
| else: | |
| residual = self.conv(normalized, padding_mask=padding_mask) | |
| hidden = x + residual | |
| return hidden + self.feed_forward(self.ffn_norm(hidden)) | |
| class Lfm2BidirectionalModel(nn.Module): | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size) | |
| self.layers = [EncoderLayer(args, layer_type) for layer_type in args.layer_types] | |
| self.embedding_norm = nn.RMSNorm(args.hidden_size, eps=args.norm_eps) | |
| def __call__( | |
| self, input_ids: mx.array, attention_mask: Optional[mx.array] = None | |
| ) -> mx.array: | |
| hidden = self.embed_tokens(input_ids) | |
| padding_mask = None | |
| additive_mask = None | |
| if attention_mask is not None: | |
| padding_mask = attention_mask.astype(mx.bool_) | |
| additive_mask = mx.where( | |
| padding_mask[:, None, None, :], | |
| mx.array(0.0, dtype=hidden.dtype), | |
| mx.array(-1e9, dtype=hidden.dtype), | |
| ) | |
| for layer in self.layers: | |
| hidden = layer(hidden, additive_mask, padding_mask) | |
| return self.embedding_norm(hidden) | |
| class Model(nn.Module): | |
| """Bidirectional encoder with a tied masked-language-model head.""" | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| self.args = args | |
| self.lfm2 = Lfm2BidirectionalModel(args) | |
| def __call__( | |
| self, | |
| input_ids: mx.array, | |
| attention_mask: Optional[mx.array] = None, | |
| *, | |
| return_hidden: bool = False, | |
| ) -> mx.array | tuple[mx.array, mx.array]: | |
| hidden = self.lfm2(input_ids, attention_mask=attention_mask) | |
| logits = self.lfm2.embed_tokens.as_linear(hidden) | |
| return (logits, hidden) if return_hidden else logits | |
| def load_model(path: str | Path) -> Model: | |
| path = Path(path) | |
| with (path / "config.json").open() as handle: | |
| args = ModelArgs.from_dict(json.load(handle)) | |
| model = Model(args) | |
| weights = mx.load(str(path / "model.safetensors")) | |
| model.load_weights(list(weights.items()), strict=True) | |
| mx.eval(model.parameters()) | |
| return model | |