Instructions to use TiGa-RCE/needle-mlx with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use TiGa-RCE/needle-mlx with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] huggingface-cli download --local-dir needle-mlx TiGa-RCE/needle-mlx
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
| """Small MLX inference implementation for Cactus Needle.""" | |
| from __future__ import annotations | |
| import json | |
| import math | |
| from pathlib import Path | |
| from typing import Any | |
| import mlx.core as mx | |
| class NeedleModel: | |
| def __init__(self, weights: dict[str, mx.array], config: dict[str, Any]): | |
| self.weights = weights | |
| self.config = config | |
| self.num_heads = int(config["num_heads"]) | |
| self.num_kv_heads = int(config["num_kv_heads"]) | |
| self.d_model = int(config["d_model"]) | |
| self.head_dim = self.d_model // self.num_heads | |
| def from_pretrained(cls, path: str | Path) -> "NeedleModel": | |
| path = Path(path) | |
| config = json.loads((path / "config.json").read_text()) | |
| weights = mx.load(str(path / "model.safetensors")) | |
| return cls(weights, config) | |
| def _weight(self, name: str, layer: int | None = None) -> mx.array: | |
| value = self.weights[name] | |
| return value if layer is None else value[layer] | |
| def _linear(x: mx.array, kernel: mx.array, bias: mx.array | None = None) -> mx.array: | |
| output = mx.matmul(x, kernel) | |
| return output if bias is None else output + bias | |
| def _zcrms_norm(x: mx.array, scale: mx.array) -> mx.array: | |
| rms = mx.sqrt(mx.mean(mx.square(x.astype(mx.float32)), axis=-1, keepdims=True) + 1e-6) | |
| return ((1.0 + scale) * x / rms).astype(x.dtype) | |
| def _rope(self, x: mx.array) -> mx.array: | |
| sequence_length = x.shape[2] | |
| half_dim = self.head_dim // 2 | |
| positions = mx.arange(sequence_length, dtype=mx.float32) | |
| inv_freq = 1.0 / ( | |
| float(self.config["rope_theta"]) | |
| ** (mx.arange(0, self.head_dim, 2, dtype=mx.float32) / self.head_dim) | |
| ) | |
| angles = mx.outer(positions, inv_freq) | |
| cos = mx.reshape(mx.cos(angles), (1, 1, sequence_length, half_dim)) | |
| sin = mx.reshape(mx.sin(angles), (1, 1, sequence_length, half_dim)) | |
| first, second = x[..., :half_dim], x[..., half_dim:] | |
| return mx.concatenate([first * cos - second * sin, second * cos + first * sin], axis=-1) | |
| def _attention( | |
| self, | |
| q_input: mx.array, | |
| kv_input: mx.array, | |
| prefix: str, | |
| layer: int, | |
| mask: mx.array | None, | |
| rope: bool, | |
| ) -> mx.array: | |
| q = self._linear(q_input, self._weight(f"{prefix}/q_proj/kernel", layer)) | |
| k = self._linear(kv_input, self._weight(f"{prefix}/k_proj/kernel", layer)) | |
| v = self._linear(kv_input, self._weight(f"{prefix}/v_proj/kernel", layer)) | |
| batch = q.shape[0] | |
| q = mx.transpose(mx.reshape(q, (batch, -1, self.num_heads, self.head_dim)), (0, 2, 1, 3)) | |
| k = mx.transpose(mx.reshape(k, (batch, -1, self.num_kv_heads, self.head_dim)), (0, 2, 1, 3)) | |
| v = mx.transpose(mx.reshape(v, (batch, -1, self.num_kv_heads, self.head_dim)), (0, 2, 1, 3)) | |
| q = self._zcrms_norm(q, self._weight(f"{prefix}/q_norm/scale", layer)) | |
| k = self._zcrms_norm(k, self._weight(f"{prefix}/k_norm/scale", layer)) | |
| if self.num_heads != self.num_kv_heads: | |
| repeats = self.num_heads // self.num_kv_heads | |
| k = mx.repeat(k, repeats, axis=1) | |
| v = mx.repeat(v, repeats, axis=1) | |
| if rope: | |
| q, k = self._rope(q), self._rope(k) | |
| scores = mx.matmul(q.astype(mx.float32), mx.transpose(k.astype(mx.float32), (0, 1, 3, 2))) | |
| scores = scores / math.sqrt(self.head_dim) | |
| if mask is not None: | |
| scores = mx.where(mask, scores, mx.array(-1e9, dtype=mx.float32)) | |
| output = mx.matmul(mx.softmax(scores, axis=-1).astype(v.dtype), v) | |
| output = mx.reshape(mx.transpose(output, (0, 2, 1, 3)), (batch, -1, self.d_model)) | |
| return self._linear(output, self._weight(f"{prefix}/out_proj/kernel", layer)) | |
| def encode(self, source_tokens: mx.array) -> tuple[mx.array, mx.array]: | |
| source_tokens = mx.array(source_tokens, dtype=mx.int32) | |
| padding_mask = source_tokens != int(self.config["pad_token_id"]) | |
| mask = mx.reshape(padding_mask, (source_tokens.shape[0], 1, 1, source_tokens.shape[1])) | |
| x = self.weights["embedding/embedding"][source_tokens] * math.sqrt(self.d_model) | |
| for layer in range(int(self.config["num_encoder_layers"])): | |
| prefix = "encoder/layers/EncoderBlock_0" | |
| residual = x | |
| x = self._zcrms_norm(x, self._weight(f"{prefix}/ZCRMSNorm_0/scale", layer)) | |
| x = self._attention(x, x, f"{prefix}/self_attn", layer, mask, rope=True) | |
| gate = mx.sigmoid(self._weight(f"{prefix}/attn_gate", layer)) | |
| x = residual + gate * x | |
| return self._zcrms_norm(x, self.weights["encoder/final_norm/scale"]), mask | |
| def decode(self, target_tokens: mx.array, encoder_out: mx.array, cross_mask: mx.array) -> mx.array: | |
| target_tokens = mx.array(target_tokens, dtype=mx.int32) | |
| length = target_tokens.shape[1] | |
| causal = mx.tril(mx.ones((length, length), dtype=mx.bool_)) | |
| target_valid = target_tokens != int(self.config["pad_token_id"]) | |
| self_mask = mx.reshape(causal, (1, 1, length, length)) & mx.reshape( | |
| target_valid, (target_tokens.shape[0], 1, 1, length) | |
| ) | |
| x = self.weights["embedding/embedding"][target_tokens] * math.sqrt(self.d_model) | |
| prefix = "decoder/layers/DecoderBlock_0" | |
| for layer in range(int(self.config["num_decoder_layers"])): | |
| residual = x | |
| x = self._zcrms_norm(x, self._weight(f"{prefix}/ZCRMSNorm_0/scale", layer)) | |
| x = self._attention(x, x, f"{prefix}/self_attn", layer, self_mask, rope=True) | |
| x = residual + mx.sigmoid(self._weight(f"{prefix}/self_attn_gate", layer)) * x | |
| residual = x | |
| x = self._zcrms_norm(x, self._weight(f"{prefix}/ZCRMSNorm_1/scale", layer)) | |
| x = self._attention(x, encoder_out, f"{prefix}/cross_attn", layer, cross_mask, rope=False) | |
| x = residual + mx.sigmoid(self._weight(f"{prefix}/cross_attn_gate", layer)) * x | |
| x = self._zcrms_norm(x, self.weights["decoder/ZCRMSNorm_0/scale"]) | |
| return mx.matmul(x.astype(mx.float32), mx.transpose(self.weights["embedding/embedding"].astype(mx.float32))) | |
| def forward(self, source_tokens: mx.array, target_tokens: mx.array) -> mx.array: | |
| encoder_out, cross_mask = self.encode(source_tokens) | |
| return self.decode(target_tokens, encoder_out, cross_mask) | |