MLX
Safetensors
qwen3_5
clef
ternary
quantized
gptq
mixed-bit
structured-output
classification
8-bit precision
Instructions to use Jakevin/clef-flash-ternary-mlx with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use Jakevin/clef-flash-ternary-mlx with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] hf download Jakevin/clef-flash-ternary-mlx --local-dir clef-flash-ternary-mlx
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
File size: 10,591 Bytes
27cf837 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | """Layer-streaming MLX runtime for Cloudflare/clef-flash (text path only) + its joint-schema head.
Backbone: mlx-lm's Qwen3.5 DecoderLayer, built one layer at a time from the release shards (np.memmap of the bf16 bytes,
so only the layer being run is resident). The same sanitisation mlx-lm applies to an HF Qwen3.5 checkpoint is done per
layer: conv1d [C,1,K] -> [C,K,1]; RMSNorm weights (input/post-attention/q/k/final) stored as w are shifted to 1 + w.
Records are run layer-major in token-budgeted chunks (each record alone, batch 1, no padding), so the activations of
one chunk + one layer are what is resident.
Head: the release's JointSchemaHead (joint_schema_model.py from the snapshot, unchanged), run in torch on the CPU in
fp32 on the backbone's final-norm hidden states. It reads lm_head rows for the option tokens; lm_head is never resident:
bf16 rows come from a memmap of the shard, 4-bit rows from mlx affine codes (group 64) dequantized per lookup.
"""
import glob, json, math, os, struct, sys, time
from pathlib import Path
import numpy as np
import torch
import mlx.core as mx
import mlx.nn as nn
from mlx.utils import tree_flatten
from mlx_lm.models.qwen3_5 import TextModelArgs, DecoderLayer
from mlx_lm.models.base import create_attention_mask, create_ssm_mask
_HERE = Path(__file__).resolve().parent
# release dir: CLEF_SNAPSHOT, else this file's directory when it is a packaged release (ships joint_schema_model.py), else the HF cache
SNAP = os.environ.get("CLEF_SNAPSHOT") or (str(_HERE) if (_HERE / "joint_schema_model.py").exists() else
sorted(glob.glob("/Volumes/1Tb/hf-cache/hub/models--Cloudflare--clef-flash/snapshots/*"))[-1])
sys.path.insert(0, SNAP)
sys.dont_write_bytecode = True # keep the HF snapshot clean (no __pycache__)
from joint_schema_model import JointSchemaHead, encode_record # noqa: E402 (release code, unchanged)
mx.set_cache_limit(1 << 30) # as kev/mlx_model.py: bound MLX's buffer cache (one buffer per new record shape otherwise)
LM = "model.language_model."
NORM_SHIFT = (".input_layernorm.weight", ".post_attention_layernorm.weight", ".q_norm.weight", ".k_norm.weight")
SKIP = ("in_proj_a", "in_proj_b")
def bf16_np_to_mx(a):
return mx.view(mx.array(np.ascontiguousarray(a)), mx.bfloat16)
def bf16_np_to_torch(a):
return torch.from_numpy(np.ascontiguousarray(a).view(np.int16)).view(torch.bfloat16).float()
class Shards:
"""Header index of the release safetensors shards; tensors are memmapped on demand (bf16 bytes, never converted)."""
def __init__(self, snap=SNAP):
self.snap = Path(snap)
files = set(json.loads((self.snap / "model.safetensors.index.json").read_text())["weight_map"].values())
self.hdr = {}
for f in sorted(files):
p = self.snap / f
with open(p, "rb") as fh:
n = struct.unpack("<Q", fh.read(8))[0]; h = json.loads(fh.read(n))
for k, v in h.items():
if k != "__metadata__":
self.hdr[k] = (str(p), 8 + n + v["data_offsets"][0], v["dtype"], tuple(v["shape"]))
def memmap(self, k):
p, off, dt, shape = self.hdr[k]
assert dt == "BF16", (k, dt)
return np.memmap(p, dtype=np.uint16, mode="r", offset=off, shape=shape)
def mx(self, k): return bf16_np_to_mx(self.memmap(k))
def torch(self, k): return bf16_np_to_torch(self.memmap(k))
def text_args(snap=SNAP):
return TextModelArgs.from_dict(json.loads((Path(snap) / "config.json").read_text())["text_config"])
def sanitize(n, v):
if n.endswith("conv1d.weight") and v.shape[-1] != 1: v = v.moveaxis(2, 1)
if n == "norm.weight" or any(("." + n).endswith(s) for s in NORM_SHIFT): v = v + 1.0 # final norm too (Qwen3_5RMSNorm: 1 + w)
return v
def layer_weights(sh, i):
pre = f"{LM}layers.{i}."
return {k[len(pre):]: sanitize(k[len(pre):], sh.mx(k)) for k in sh.hdr if k.startswith(pre)}
def build_layer(args, i, weights):
layer = DecoderLayer(args, i)
layer.eval() # mlx Modules start in training mode, where GatedDeltaNet takes the slow reference ops instead of the Metal kernel
layer.load_weights(list(weights.items()), strict=True)
mx.eval(layer.parameters())
return layer
def linears(layer, i):
"""(i, name, module, packed name) for every quantizable nn.Linear of a layer (in_proj_a / in_proj_b stay bf16)."""
return [(i, n, m, f"{LM}layers.{i}.{n}.weight") for n, m in layer.named_modules()
if isinstance(m, nn.Linear) and n.split(".")[-1] not in SKIP]
def set_module(layer, name, mod):
parent = layer
for p in name.split(".")[:-1]: parent = getattr(parent, p)
setattr(parent, name.split(".")[-1], mod)
class QLoRA(nn.Module):
"""Quantized linear + bf16 low-rank compensation (CLoQ), kept unfolded: y = Q(x) + (x A^T) B^T."""
def __init__(self, q, a, b):
super().__init__(); self.q = q; self.lora_a = a; self.lora_b = b
def __call__(self, x):
return self.q(x) + (x @ self.lora_a.T) @ self.lora_b.T
def mlx_pack(codes, bits):
"""uint8 codes [N, K] -> mlx affine packed uint32 [N, K*bits/32] (element j of a word at bit j*bits)."""
N, K = codes.shape; per = 32 // bits
c = codes.to(torch.int64).reshape(N, K // per, per)
w = (c << (torch.arange(per, dtype=torch.int64) * bits)).sum(-1)
return mx.array(w.numpy().astype(np.uint32))
def qlinear(codes, scales, biases, bits, group=64):
"""Build an nn.QuantizedLinear from explicit codes / bf16 scales / bf16 biases (w = scale * code + bias)."""
N, K = codes.shape
q = nn.QuantizedLinear(K, N, bias=False, group_size=group, bits=bits)
q.weight = mlx_pack(codes, bits)
q.scales = mx.array(scales.numpy()).astype(mx.bfloat16)
q.biases = mx.array(biases.numpy()).astype(mx.bfloat16)
return q
def qembedding(w):
V, D = w.shape
e = nn.QuantizedEmbedding(V, D, group_size=64, bits=4)
e.weight, e.scales, e.biases = mx.quantize(w, group_size=64, bits=4)
return e
class Head:
"""JointSchemaHead (fp32, CPU) + lm_head row lookup in bf16 ('bf16') or 4-bit mlx affine ('q4')."""
def __init__(self, snap=SNAP, sh=None, lm_head_q=None):
from safetensors.torch import load_file
torch.set_num_threads(8)
cfg = json.loads((Path(snap) / "joint_head_config.json").read_text())
self.head = JointSchemaHead(**cfg)
self.head.load_state_dict({k: v.float() for k, v in load_file(str(Path(snap) / "joint_head.safetensors")).items()}, strict=True)
self.head.eval()
self.sh = sh; self.q = lm_head_q # (wq, scales, biases) or None
def rows_bf16(self, ids):
return bf16_np_to_torch(self.sh.memmap("lm_head.weight")[np.asarray(ids)])
def rows_q4(self, ids):
ix = mx.array(np.asarray(ids, dtype=np.int32)); wq, s, b = self.q
return torch.from_numpy(np.array(mx.dequantize(wq[ix], s[ix], b[ix], group_size=64, bits=4).astype(mx.float32)))
@torch.no_grad()
def logits(self, hidden, enc, mode):
class Rows:
def __getitem__(_, ids): return (self.rows_bf16 if mode == "bf16" else self.rows_q4)(ids.cpu().numpy())
L = len(enc.input_ids)
out = self.head(hidden.reshape(1, L, -1), torch.tensor([enc.input_ids]), torch.ones(1, L, dtype=torch.long), [enc], Rows())
return [z.float() for z in out[0]]
def chunks_by_tokens(encs, budget):
out, cur, n = [], [], 0
for j, e in enumerate(encs):
if cur and n + len(e.input_ids) > budget: out.append(cur); cur, n = [], 0
cur.append(j); n += len(e.input_ids)
if cur: out.append(cur)
return out
def run_layer(layer, xs):
ys = []
for x in xs:
m = create_ssm_mask(x) if layer.is_linear else create_attention_mask(x)
y = layer(x, mask=m, cache=None); mx.eval(y); ys.append(y)
return ys
def forward_logits(encs, embed, layer_at, final_norm, head, modes=("q4", "bf16"), budget=120_000, log=print):
"""Backbone (layer-major per chunk) + head. embed(ids)->[1,L,D]; layer_at(i)->DecoderLayer (built or resident).
Returns {mode: [per-record list of per-question fp32 logit tensors]}."""
nl = len_layers = layer_at.n
res = {m: [None] * len(encs) for m in modes}
for ci, idx in enumerate(chunks_by_tokens(encs, budget)):
t0 = time.time()
xs = [embed(encs[j].input_ids) for j in idx]
for i in range(nl):
layer = layer_at(i); xs = run_layer(layer, xs); del layer
mx.clear_cache()
for j, x in zip(idx, xs):
h = final_norm(x); mx.eval(h)
ht = torch.from_numpy(np.array(h.astype(mx.float32)))
for m in modes: res[m][j] = head.logits(ht, encs[j], m)
del xs; mx.clear_cache()
log(f"chunk {ci}: {len(idx)} records in {time.time()-t0:.0f}s, peak {mx.get_peak_memory()/1e9:.2f} GB")
return res
class FinalNorm:
def __init__(self, w, eps):
self.w = w; self.eps = eps
def __call__(self, x): return mx.fast.rms_norm(x, self.w, self.eps)
class Bf16Backbone:
"""Streams bf16 layers from the shards; embeddings gathered from the memmap (never fully resident)."""
def __init__(self, sh, args):
self.sh, self.args, self.n = sh, args, args.num_hidden_layers
self.norm = FinalNorm(sanitize("norm.weight", sh.mx(LM + "norm.weight")), args.rms_norm_eps)
self.emb = sh.memmap(LM + "embed_tokens.weight")
def __call__(self, i): return build_layer(self.args, i, layer_weights(self.sh, i))
def embed(self, ids): return bf16_np_to_mx(self.emb[np.asarray(ids)])[None]
def predictions(records, encs, logits):
"""kev prediction dicts (probabilities + logits keyed like kev.api.question_keys) from Clef per-option logits."""
out = []
for r, e, zs in zip(records, encs, logits):
probs, lg = {}, {}
for q, z in zip(e.questions, zs):
p = torch.softmax(z, -1)
probs[q.question_id] = {o: float(v) for o, v in zip(q.option_ids, p.tolist())}
lg[q.question_id] = {o: float(v) for o, v in zip(q.option_ids, z.tolist())}
out.append({"probabilities": probs, "logits": lg, "inference_temperature": 1.0, "latency_ms": 0.0, "input_tokens": len(e.input_ids)})
return out
class Cached:
"""A predictor for kev.benchmark.evaluate_records that returns precomputed predictions in record order."""
def __init__(self, preds): self.preds = preds; self.k = 0; self.temperature = 1.0
def __call__(self, record):
p = self.preds[self.k]; self.k += 1; return p
|