Token Classification
MLX
eurobert
html
content-extraction
boilerplate-removal
web-scraping
encoder
custom-code
custom_code
Instructions to use Mike0021/pulpie-orange-small-mlx with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use Mike0021/pulpie-orange-small-mlx with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] huggingface-cli download --local-dir pulpie-orange-small-mlx Mike0021/pulpie-orange-small-mlx
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
| from __future__ import annotations | |
| import json | |
| from pathlib import Path | |
| from typing import Any, Dict, Optional, Tuple | |
| import mlx.core as mx | |
| import mlx.nn as nn | |
| WEIGHT_FILES = { | |
| "bf16": "model-bf16.safetensors", | |
| "16bit": "model-bf16.safetensors", | |
| "8bit": "model-8bit.safetensors", | |
| "4bit": "model-4bit.safetensors", | |
| } | |
| class EuroBertConfig: | |
| def __init__(self, **kwargs: Any): | |
| self.vocab_size = kwargs.get("vocab_size", 128257) | |
| self.hidden_size = kwargs.get("hidden_size", 768) | |
| self.intermediate_size = kwargs.get("intermediate_size", 3072) | |
| self.num_hidden_layers = kwargs.get("num_hidden_layers", 12) | |
| self.num_attention_heads = kwargs.get("num_attention_heads", 12) | |
| self.num_key_value_heads = kwargs.get( | |
| "num_key_value_heads", self.num_attention_heads | |
| ) | |
| self.head_dim = kwargs.get( | |
| "head_dim", self.hidden_size // self.num_attention_heads | |
| ) | |
| self.hidden_act = kwargs.get("hidden_act", "silu") | |
| self.rms_norm_eps = kwargs.get("rms_norm_eps", 1e-5) | |
| self.rope_theta = kwargs.get("rope_theta", 250000.0) | |
| self.attention_bias = kwargs.get("attention_bias", False) | |
| self.mlp_bias = kwargs.get("mlp_bias", False) | |
| self.pad_token_id = kwargs.get("pad_token_id", 128001) | |
| self.max_position_embeddings = kwargs.get("max_position_embeddings", 8192) | |
| self.num_labels = kwargs.get("num_labels", len(kwargs.get("id2label", {})) or 2) | |
| self.raw = kwargs | |
| def from_json(cls, path: str | Path) -> "EuroBertConfig": | |
| with open(path, "r", encoding="utf-8") as f: | |
| return cls(**json.load(f)) | |
| def _silu(x: mx.array) -> mx.array: | |
| return x * mx.sigmoid(x) | |
| def _rotate_half(x: mx.array) -> mx.array: | |
| x1 = x[..., : x.shape[-1] // 2] | |
| x2 = x[..., x.shape[-1] // 2 :] | |
| return mx.concatenate([-x2, x1], axis=-1) | |
| def _apply_rotary_pos_emb( | |
| q: mx.array, k: mx.array, cos: mx.array, sin: mx.array | |
| ) -> Tuple[mx.array, mx.array]: | |
| cos = cos[:, None, :, :] | |
| sin = sin[:, None, :, :] | |
| return (q * cos) + (_rotate_half(q) * sin), (k * cos) + (_rotate_half(k) * sin) | |
| class EuroBertRotaryEmbedding(nn.Module): | |
| def __init__(self, config: EuroBertConfig): | |
| super().__init__() | |
| self.head_dim = config.head_dim | |
| self.rope_theta = config.rope_theta | |
| def __call__(self, position_ids: mx.array, dtype: mx.Dtype) -> Tuple[mx.array, mx.array]: | |
| steps = mx.arange(0, self.head_dim, 2).astype(mx.float32) | |
| inv_freq = 1.0 / (self.rope_theta ** (steps / self.head_dim)) | |
| pos = position_ids.astype(mx.float32) | |
| freqs = pos[..., None] * inv_freq[None, None, :] | |
| emb = mx.concatenate([freqs, freqs], axis=-1) | |
| return mx.cos(emb).astype(dtype), mx.sin(emb).astype(dtype) | |
| class EuroBertAttention(nn.Module): | |
| def __init__(self, config: EuroBertConfig): | |
| super().__init__() | |
| self.num_heads = config.num_attention_heads | |
| self.num_key_value_heads = config.num_key_value_heads | |
| self.num_key_value_groups = self.num_heads // self.num_key_value_heads | |
| self.head_dim = config.head_dim | |
| self.scaling = self.head_dim**-0.5 | |
| self.q_proj = nn.Linear( | |
| config.hidden_size, | |
| config.num_attention_heads * self.head_dim, | |
| bias=config.attention_bias, | |
| ) | |
| self.k_proj = nn.Linear( | |
| config.hidden_size, | |
| config.num_key_value_heads * self.head_dim, | |
| bias=config.attention_bias, | |
| ) | |
| self.v_proj = nn.Linear( | |
| config.hidden_size, | |
| config.num_key_value_heads * self.head_dim, | |
| bias=config.attention_bias, | |
| ) | |
| self.o_proj = nn.Linear( | |
| config.num_attention_heads * self.head_dim, | |
| config.hidden_size, | |
| bias=config.attention_bias, | |
| ) | |
| def _shape(self, x: mx.array, heads: int) -> mx.array: | |
| batch, seq_len, _ = x.shape | |
| x = x.reshape(batch, seq_len, heads, self.head_dim) | |
| return mx.transpose(x, (0, 2, 1, 3)) | |
| def __call__( | |
| self, | |
| hidden_states: mx.array, | |
| position_embeddings: Tuple[mx.array, mx.array], | |
| attention_mask: Optional[mx.array], | |
| ) -> mx.array: | |
| batch, seq_len, _ = hidden_states.shape | |
| q = self._shape(self.q_proj(hidden_states), self.num_heads) | |
| k = self._shape(self.k_proj(hidden_states), self.num_key_value_heads) | |
| v = self._shape(self.v_proj(hidden_states), self.num_key_value_heads) | |
| cos, sin = position_embeddings | |
| q, k = _apply_rotary_pos_emb(q, k, cos, sin) | |
| if self.num_key_value_groups != 1: | |
| k = mx.repeat(k, self.num_key_value_groups, axis=1) | |
| v = mx.repeat(v, self.num_key_value_groups, axis=1) | |
| scores = (q @ mx.transpose(k, (0, 1, 3, 2))).astype(mx.float32) | |
| scores = scores * self.scaling | |
| if attention_mask is not None: | |
| scores = scores + attention_mask | |
| probs = mx.softmax(scores, axis=-1).astype(q.dtype) | |
| out = probs @ v | |
| out = mx.transpose(out, (0, 2, 1, 3)).reshape(batch, seq_len, -1) | |
| return self.o_proj(out) | |
| class EuroBertMLP(nn.Module): | |
| def __init__(self, config: EuroBertConfig): | |
| super().__init__() | |
| self.gate_proj = nn.Linear( | |
| config.hidden_size, config.intermediate_size, bias=config.mlp_bias | |
| ) | |
| self.up_proj = nn.Linear( | |
| config.hidden_size, config.intermediate_size, bias=config.mlp_bias | |
| ) | |
| self.down_proj = nn.Linear( | |
| config.intermediate_size, config.hidden_size, bias=config.mlp_bias | |
| ) | |
| def __call__(self, x: mx.array) -> mx.array: | |
| return self.down_proj(_silu(self.gate_proj(x)) * self.up_proj(x)) | |
| class EuroBertDecoderLayer(nn.Module): | |
| def __init__(self, config: EuroBertConfig): | |
| super().__init__() | |
| self.self_attn = EuroBertAttention(config) | |
| self.mlp = EuroBertMLP(config) | |
| self.input_layernorm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) | |
| self.post_attention_layernorm = nn.RMSNorm( | |
| config.hidden_size, eps=config.rms_norm_eps | |
| ) | |
| def __call__( | |
| self, | |
| hidden_states: mx.array, | |
| attention_mask: Optional[mx.array], | |
| position_embeddings: Tuple[mx.array, mx.array], | |
| ) -> mx.array: | |
| residual = hidden_states | |
| hidden_states = self.input_layernorm(hidden_states) | |
| hidden_states = self.self_attn( | |
| hidden_states, position_embeddings, attention_mask | |
| ) | |
| hidden_states = residual + hidden_states | |
| residual = hidden_states | |
| hidden_states = self.post_attention_layernorm(hidden_states) | |
| hidden_states = self.mlp(hidden_states) | |
| return residual + hidden_states | |
| class EuroBertModel(nn.Module): | |
| def __init__(self, config: EuroBertConfig): | |
| super().__init__() | |
| self.config = config | |
| self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) | |
| self.layers = [ | |
| EuroBertDecoderLayer(config) for _ in range(config.num_hidden_layers) | |
| ] | |
| self.norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps) | |
| self.rotary_emb = EuroBertRotaryEmbedding(config) | |
| def _attention_mask(self, attention_mask: Optional[mx.array]) -> Optional[mx.array]: | |
| if attention_mask is None: | |
| return None | |
| keep = attention_mask.astype(mx.bool_) | |
| mask = mx.where(keep[:, None, None, :], 0.0, mx.finfo(mx.float32).min) | |
| return mask.astype(mx.float32) | |
| def __call__( | |
| self, | |
| input_ids: mx.array, | |
| attention_mask: Optional[mx.array] = None, | |
| position_ids: Optional[mx.array] = None, | |
| ) -> mx.array: | |
| hidden_states = self.embed_tokens(input_ids) | |
| batch, seq_len = input_ids.shape | |
| if position_ids is None: | |
| position_ids = mx.broadcast_to(mx.arange(seq_len)[None, :], (batch, seq_len)) | |
| mask = self._attention_mask(attention_mask) | |
| position_embeddings = self.rotary_emb(position_ids, hidden_states.dtype) | |
| for layer in self.layers: | |
| hidden_states = layer(hidden_states, mask, position_embeddings) | |
| return self.norm(hidden_states) | |
| class EuroBertForTokenClassification(nn.Module): | |
| def __init__(self, config: EuroBertConfig): | |
| super().__init__() | |
| self.config = config | |
| self.model = EuroBertModel(config) | |
| self.classifier = nn.Linear(config.hidden_size, config.num_labels) | |
| def __call__( | |
| self, | |
| input_ids: mx.array, | |
| attention_mask: Optional[mx.array] = None, | |
| position_ids: Optional[mx.array] = None, | |
| ) -> mx.array: | |
| hidden_states = self.model(input_ids, attention_mask, position_ids) | |
| return self.classifier(hidden_states) | |
| def build_model( | |
| config: EuroBertConfig | Dict[str, Any], | |
| quantization: Optional[Dict[str, Any]] = None, | |
| ) -> EuroBertForTokenClassification: | |
| if isinstance(config, dict): | |
| config = EuroBertConfig(**config) | |
| model = EuroBertForTokenClassification(config) | |
| if quantization: | |
| nn.quantize( | |
| model, | |
| group_size=quantization.get("group_size", 64), | |
| bits=quantization["bits"], | |
| mode=quantization.get("mode", "affine"), | |
| ) | |
| return model | |
| def load_model( | |
| model_path: str | Path, | |
| variant: str = "bf16", | |
| ) -> EuroBertForTokenClassification: | |
| model_path = Path(model_path) | |
| with open(model_path / "config.json", "r", encoding="utf-8") as f: | |
| raw_config = json.load(f) | |
| with open(model_path / "mlx_config.json", "r", encoding="utf-8") as f: | |
| mlx_config = json.load(f) | |
| variant = variant.lower() | |
| if variant not in WEIGHT_FILES: | |
| raise ValueError(f"Unknown variant {variant!r}; expected one of {sorted(WEIGHT_FILES)}") | |
| weight_name = WEIGHT_FILES[variant] | |
| quantization = mlx_config["variants"].get(weight_name, {}).get("quantization") | |
| model = build_model(raw_config, quantization=quantization) | |
| model.load_weights(str(model_path / weight_name)) | |
| mx.eval(model.parameters()) | |
| return model | |