Downloads · 30 days
0
mchen04/jevlite-decision-heads
jevlite-decision-heads is a text classification model from mchen04. Use it when you need a label for a piece of text. It is set up for mlx. The card lists the license as other.
Hi — for-fun side-project stuff, same project as mchen04/jev-local-lab-decision-heads (commit history spans 2026-09-19 to 2026-09-22). The question here was narrower: if you chop a small quantised Qwen2.5 down to its…
Downloads · 30 days
0
Access
Public
Updated Sep 22, 2026
Repo size
115 MB
Likes
0
Public
Click a slice to open those files.
.safetensors122 MB · 100%
From the Hugging Face model README
Hi — for-fun side-project stuff, same project as
mchen04/jev-local-lab-decision-heads
(commit history spans 2026-09-19 to 2026-09-22).
The question here was narrower: if you chop a small quantised Qwen2.5 down to its first L
transformer layers and bolt a tiny trained head on the end, how much accuracy do you lose and how
much latency do you save? These are the 39 heads that came out of that sweep.
Not production-ready. Not a reproduction of any commercial product. Does not reach parity with the published "Jev" numbers I was comparing against — on an external workflow suite these heads scored 0.215–0.312 agreement where a plain zero-shot read-out on the same backbones scored 0.582–0.585 and the published commercial reference scored 0.909. That gap is the main thing this repo documents.
tl;dr of the honest results:
emotion_label
leaves 0.15 on emotion against 0.83–0.93 on the tasks it did see.39 decision heads of 0.46–1.32 M parameters each. A head takes one hidden state from a frozen
Qwen2.5 backbone — optionally one truncated to its first L layers — plus the mean-pooled input
embeddings of a label set, and returns a distribution over those labels. Same cosine-bilinear scorer
as the phase-3 heads; the new variable here is backbone depth.
Two backbones, both frozen and unmodified:
mlx-community/Qwen2.5-1.5B-Instruct-4bit (hidden 1536, 28 layers) and
mlx-community/Qwen2.5-0.5B-Instruct-4bit (hidden 896, 24 layers).
mlx-community/Qwen2.5-1.5B-Instruct-4bit at revision
8b403126fc14f14cfc99bb4cfa72ecbc129ea677.No public code repository; the engine that produces the hidden state is a private local checkout. The head maths is documented in full below so the artifacts are not a black box.
Weights are supplied — 39 real jevlite_head.safetensors files, plus the recipe/metrics JSON
each was trained with. Backbone weights are not included: download them from their own repos.
phase1/<variant>/jevlite_head.safetensors + jevlite.json (5 heads)
phase2/<variant>/jevlite_head.safetensors + metrics.json (34 heads)
manifest.json (all 39, with sha256)
Trained on 15 000 items. dev here is a 60-item-per-task development set (small — treat with care).
| variant | layers | dev accuracy | role |
|---|---|---|---|
jevlite-Qwen2.5-1.5B-Instruct-4bit-L28-r256-all | 28 / 28 | 0.7700 | full-depth ceiling |
jevlite-Qwen2.5-1.5B-Instruct-4bit-L20-r256-all | 20 / 28 | 0.7833 | the depth I picked |
jevlite-Qwen2.5-1.5B-Instruct-4bit-L4-r256-all | 4 / 28 | 0.4567 | floor, shows the sweep is real |
…-L20-r256-holdout_boolq_answer | 20 / 28 | 0.7833 (4 seen tasks) | boolq_answer removed from training |
…-L20-r256-holdout_emotion_label | 20 / 28 | 0.6733 (incl. held-out task at 0.15) | emotion_label removed |
dev2 is a 600-item development set over six tasks. Full per-variant numbers are in
manifest.json and each variant's metrics.json; the shape of it:
| backbone | layers | plain dev2 | distilled dev2 | distillation worth |
|---|---|---|---|---|
| 0.5B | 8 / 24 | 0.4533 | 0.4717 | +1.8 pp |
| 0.5B | 12 / 24 | 0.5150 | 0.5283 | +1.3 pp |
| 0.5B | 16 / 24 | 0.6133 | 0.6117 | −0.2 pp |
| 0.5B | 24 / 24 | 0.6267 | 0.6283 | +0.2 pp |
| 1.5B | 8 / 28 | 0.4050 | 0.4267 | +2.2 pp |
| 1.5B | 12 / 28 | 0.4600 | 0.4867 | +2.7 pp |
| 1.5B | 16 / 28 | 0.5850 | 0.5867 | +0.2 pp |
| 1.5B | 20 / 28 | 0.6650 | 0.6450 | −2.0 pp |
| 1.5B | 28 / 28 | 0.6683 | — | — |
The mlp head variants (nonlinear projection) were worse than the plain bilinear one at every
depth tried — e.g. 1.5B L12 mlp 0.3650 against bilinear 0.4600. They are kept here because a
negative architecture result is still a result.
Reproducing the depth/latency trade-off measurement, or checking my numbers. If you are curious what a ~0.5 M-parameter head can read out of a half-depth 0.5B model, the artifacts are all here.
emotion_label gives
0.15 on emotion. On a suite of real workflow decisions, the best phase-2 head agreed with the
reference on 0.312 of 330 pairs where a zero-shot read-out on the same backbone reached 0.582.
A trained head on five academic classification tasks is not a general decision reader.Yelp/yelp_review_full) and two have no resolvable
licence — see Licence and upstream attribution.For unseen tasks, use a plain restricted read-out on the un-truncated backbone instead. That is what the measurements say. Publish these as a record, not a recommendation.
| phase | tasks | source dataset | published licence (Hub tag) |
|---|---|---|---|
| 1, 2 | sst2_sentiment | stanfordnlp/sst2 | unknown |
| 1, 2 | agnews_topic | fancyzhx/ag_news | unknown |
| 1, 2 | emotion_label | dair-ai/emotion | other |
| 1, 2 | boolq_answer | google/boolq | cc-by-sa-3.0 |
| 1, 2 | yelp_stars | Yelp/yelp_review_full | other (Yelp's own dataset terms apply) |
Phase 2's dev2 additionally scores dbpedia_topic. Items are drawn from each source's train
split (up to 3000 per task, 500 per task for the training-dev fold); the development and test suites
come from validation/test splits and are disjoint. No dataset text is in this repo and none was
uploaded.
What that does NOT establish: no extraction, memorisation or privacy evaluation was run on these heads. I cannot tell you what is or is not recoverable from the weights, and a small parameter count is not evidence that nothing is. Treat it as untested.
Backbone frozen throughout; only the head trains, on pre-cached hidden states and frozen label embeddings.
(1 − α) · CE(student, gold) + α · τ² · KL(teacher ‖ student), both at temperature τ, for the distil
variants. Phase 2 swept α ∈ {0.25, 0.5, 0.75, 0.9} at τ = 2.0; manifest.json records α per head.L entries before the forward pass.
No retraining or healing of the truncated stack — the head alone absorbs the change.Head training is 3–5 seconds per variant on cached hidden states (phase 1 records 4.1 s; phase 2 records 3.2 s for the 0.5B L16 distil head). Inference latency, warm p50 per question on the hardware below: 126 ms at 20/28 layers, 174 ms at 28/28. Checkpoints are 1.8–5.3 MB.
test suite — 1000 items over the five training task families, held out from
training, opened after development stopped. Metric: accuracy.dev2 — 600 items over six tasks; development data, looked at repeatedly.Phase-1 test, n = 1000, five trained task families:
| head | layers | test accuracy | warm p50 / question |
|---|---|---|---|
L28-r256-all | 28 / 28 | 0.736 | 174 ms |
L20-r256-all | 20 / 28 | 0.716 | 126 ms |
L20-r256-holdout_boolq_answer | 20 / 28 | 0.703 | — |
L20-r256-holdout_emotion_label | 20 / 28 | 0.651 | — |
L4-r256-all | 4 / 28 | 0.384 | — |
Held-out-task failure, phase-1 development set: training without emotion_label leaves
0.15 accuracy on emotion_label while sst2 sits at 0.825 and agnews at 0.933. The head did
not learn a task-general skill.
External workflow suite, n = 330 pairs — this is the number that matters most for honesty:
| system | agreement |
|---|---|
| published commercial reference ("Jev", published, never run here) | 0.909 |
| zero-shot read-out, 7B-4bit, no trained head (mine) | 0.585 |
| zero-shot read-out, 1.5B-4bit, no trained head (mine) | 0.582 |
jevlite 0.5B L16/24 head (this repo) | 0.312 |
jevlite 1.5B L20/28 head (this repo) | 0.303 |
jevlite 0.5B L24/24 head (this repo) | 0.215 |
A trained head that is good at five academic classification tasks is roughly half as good as no head at all on real workflow decisions. That is the clearest single result in this repo.
Layer truncation is a real and cheap latency lever within the training distribution: 20 of 28 layers cost 2.0 accuracy points and saved 28 % of the per-question time. Everything about generalisation is negative. Cross-backbone distillation helped a little where the student was weakest and not at all where it was already fine.
The 0.909 figure is published by the benchmark's authors for a commercial product with closed weights and early-access API. It was never run on my hardware. Nothing in this repo reproduces, approximates or matches it; the tables above are the measured distance, not a claim of closeness. Agreement with a published reference consensus is also not ground-truth accuracy.
test evaluation for the 34 phase-2 heads — phase 2 reported dev2 and the
external suite only.Identical scorer to the phase-3 heads. For hidden state h [hidden] and label embeddings E
[n_labels, hidden], with rms(x) = x / sqrt(mean(x²) + 1e-6):
hn = rms(h) * g ; En = rms(E) * ge
a = hn @ Wh ; B = En @ We # bilinear variants
a = gelu(hn @ W1 + b1) @ W2 # mlp variants instead
a, B L2-normalised on the last axis
s = clip(exp(logit_scale), 1, 50) * (a @ B.T) + (En @ b) + c
Tensors: g, ge, Wh, We, b, c, logit_scale (float32); mlp variants carry W1, b1,
W2 in place of Wh. hidden is 1536 (1.5B) or 896 (0.5B). E[i] is the mean of the backbone's
input embeddings for the tokens of " " + label[i].
mlx 0.32.x, mlx-lm 0.31.3), Python 3.12.The head is a plain safetensors dict; the surrounding engine is not published, so this is not a
from_pretrained drop-in. Reference implementation of the head itself:
# pip install safetensors numpy
import hashlib, json, math
import numpy as np
from safetensors.numpy import load_file
V = "phase1/jevlite-Qwen2.5-1.5B-Instruct-4bit-L20-r256-all"
W = load_file(f"{V}/jevlite_head.safetensors")
man = json.load(open("manifest.json"))
rec = next(x for x in man["phase1"] + man["phase2"] if x["path"].startswith(V))
assert hashlib.sha256(open(f"{V}/jevlite_head.safetensors", "rb").read()).hexdigest() == rec["sha256"]
def rms(x, eps=1e-6):
return x / np.sqrt((x * x).mean(axis=-1, keepdims=True) + eps)
# the trained `mlp` heads used EXACT GELU (x * Phi(x)), not the tanh approximation and not ReLU
_erf = np.vectorize(math.erf)
def gelu(x):
return x * (1.0 + _erf(x / math.sqrt(2.0))) / 2.0
def head_scores(h, E, W):
hn, En = rms(h.astype(np.float32)) * W["g"], rms(E.astype(np.float32)) * W["ge"]
a = hn @ W["Wh"] if "Wh" in W else gelu(hn @ W["W1"] + W["b1"]) @ W["W2"]
B = En @ W["We"]
a = a / max(np.linalg.norm(a), 1e-6)
B = B / np.maximum(np.linalg.norm(B, axis=-1, keepdims=True), 1e-6)
scale = float(np.clip(np.exp(W["logit_scale"][0]), 1, 50))
return scale * (a @ B.T) + (En @ W["b"]) + W["c"]
print(rec["variant"], "| layers", rec["depth"], "/", rec["full_depth"], "| params", rec["head_params"])
Both branches of head_scores — the bilinear one and the mlp one — were checked against the
trained MLX scorer that produced these weights, on deterministic inputs, and agree to < 1e-5 max
absolute difference. The check is reproduced in the repo's own terms below, so you can confirm the
reference implementation is the real one rather than a paraphrase of it:
| head | variant checked | max abs difference vs the MLX scorer |
|---|---|---|
bilinear | phase1/…-L20-r256-all | 1.311e-06 |
bilinear | phase2/…-0.5B-…-L16-distil05 | 5.960e-07 |
mlp | phase2/…-1.5B-…-L16-mlp | 9.786e-07 |
mlp | phase2/…-1.5B-…-L20-distil_mlp | 5.911e-07 |
Full method and inputs: REFERENCE_CHECK.md in this repo. The first public version of this card used
ReLU in the mlp branch "for brevity" while admitting in prose that the trained heads used GELU —
that was a knowingly wrong implementation shipped as usable, and it is gone.
To use a head end to end you must truncate the backbone to depth layers, run your input, take the
hidden state at the read position, and build E. The read position comes from unpublished code, so
expect different numbers if you read somewhere else.
Upstream, clear:
mlx-community/Qwen2.5-1.5B-Instruct-4bit
(revision 8b403126fc14f14cfc99bb4cfa72ecbc129ea677) and
mlx-community/Qwen2.5-0.5B-Instruct-4bit, both Apache-2.0, derived from Alibaba Cloud's
Qwen2.5 family. I did not create them; I froze, truncated and read from them.Yelp/yelp_review_full is tagged other and Yelp's own
dataset terms apply; stanfordnlp/sst2 and fancyzhx/ag_news carry unknown; google/boolq is
CC-BY-SA-3.0, which is a share-alike licence.These head weights: no open-source licence has been selected, so copyright is retained by the author and they are published for inspection and reproduction. This is deliberate — I would rather say so than attach a label I am not sure is right. Ask in a discussion if you need specific rights, and note you would still need to satisfy the upstream dataset terms yourself.
LICENSE.md restates this in one place.
Michael Chen. Community tab for questions or corrections.
No paper. Please describe it as a personal side-project experiment, negative results included.