Downloads · 30 days
228
5% of all-time downloads
aufklarer/Qwen3.5-0.8B-Chat-MLX
Qwen3.5-0.8B-Chat-MLX is a text generation model from aufklarer. Use it when you need the model to write or continue text. It is set up for mlx. The card lists the license as apache-2.0.
Text-only extraction of Qwen3.5-0.8B, quantized with MLX for on-device chat on Apple Silicon. Three bit widths are published so the quality/size tradeoff can be made with measured numbers rather than assumed.
Downloads · 30 days
228
5% of all-time downloads
All-time downloads
4.5K
Public
Repo size
7.1 GB
Likes
1
Public
Click a slice to open those files.
.safetensors1.3 GB · 98%
From the Hugging Face model README
Text-only extraction of Qwen3.5-0.8B, quantized with MLX for on-device chat on Apple Silicon. Three bit widths are published so the quality/size tradeoff can be made with measured numbers rather than assumed.
INT8 is the recommended default. INT4 is kept for size-constrained use but is no longer recommended for anything that parses the reply as JSON — see Measured quality.
| Parameters | 752 M (text model; the tied embedding / LM head is 254 M of that) |
| Architecture | Qwen3.5 hybrid — 24 layers, 18× DeltaNet linear attention + 6× GatedAttention, pattern [linear, linear, linear, full] × 6 |
| Positional encoding | Partial RoPE, 25 % of head_dim (64 of 256 rotary dims) |
| Output head | Tied to the token embedding (lm_head = embed_tokens) |
| Format | MLX safetensors |
| Quantization | mx.quantize affine round-to-nearest, group size 64, at 4 / 5 / 8 bits |
| Declared context | 2048 (max_seq_len); the MLX path does not enforce it — DeltaNet is recurrent and the KV cache grows on demand |
| Vocabulary | 248 320 |
Norm weights, A_log, dt_bias and the causal conv1d kernels are not
quantized; they stay float. Quantized tensors are stored as MLX's
weight / scales / biases triple with fp16 scales and biases.
| Variant | File size | Quantized | Use it when |
|---|---|---|---|
| INT8 | 800 MB | 8-bit, group 64 | Default. Effectively indistinguishable from bf16. |
| INT5 | 518 MB | 5-bit, group 64 | Memory matters more than the last 2 % of quality. |
| INT4 | 424 MB | 4-bit, group 64 | Smallest download, free-form text only. Not for JSON. |
| File | Size | Description |
|---|---|---|
int4/model.safetensors | 424 MB | INT4 weights |
int4/config.json | 1.3 kB | Architecture + quantization_bits: 4, quantization_group_size: 64 |
int4/tokenizer.json | 12.8 MB | Tokenizer |
int4/tokenizer_config.json | 16.7 kB | Chat template and special tokens |
int5/model.safetensors | 518 MB | INT5 weights |
int5/config.json | 1.3 kB | Same, quantization_bits: 5 |
int5/tokenizer.json | 12.8 MB | Tokenizer |
int5/tokenizer_config.json | 16.7 kB | Chat template and special tokens |
int8/model.safetensors | 800 MB | INT8 weights |
int8/config.json | 1.3 kB | Same, quantization_bits: 8 |
int8/tokenizer.json | 12.8 MB | Tokenizer |
int8/tokenizer_config.json | 16.7 kB | Chat template and special tokens |
Each variant is self-contained: one directory is everything needed to run it.
The vision tower and the multi-token-prediction draft head (mtp.*) are not
included — no runtime here reads them. Dropping the draft head alone took 14 MB
off the INT4 download compared with the previous revision.
Every number below is measured, not estimated. Method: the exporter's exact quantize/dequantize step is reproduced on the bf16 checkpoint — same kernel, same bit width, same group size — and the result is compared against the unmodified bf16 model on identical inputs. 32 768 teacher-forced tokens per corpus. A control run with quantization disabled reproduced bf16 exactly (0.000 % perplexity change, 100 % agreement, 0 KL), so the deltas below are the quantization and nothing else.
Perplexity is relative to the bf16 checkpoint; lower is better and 0 % is the target. Top-1 agreement is how often the quantized model's most likely next token is the same as bf16's.
| Variant | Perplexity vs bf16 (wikitext) | Perplexity vs bf16 (dialogsum) | Top-1 agreement | Mean KL from bf16 |
|---|---|---|---|---|
| bf16 reference | 22.798 | 4.846 | — | — |
| INT8 | +0.19 % | −0.002 % | 97.9 % / 98.8 % | 0.0013 / 0.0008 nats |
| INT5 | +2.2 % | +1.6 % | 89.6 % / 94.7 % | 0.032 / 0.016 nats |
| INT4 | +19.9 % | +9.5 % | 78.3 % / 88.5 % | 0.149 / 0.079 nats |
24 tasks that ask for a single JSON object and are scored the way a strict parser would score them: greedy decoding, no repair pass, no leniency for markdown fences or prose. bf16 passes 24 of 24.
| Variant | Strict JSON valid | Byte-identical to bf16 |
|---|---|---|
| INT8 | 24 / 24 | 18 / 24 |
| INT5 | 23 / 24 | 12 / 24 |
| INT4 | 18 / 24 | 5 / 24 |
The INT4 failures are not near-misses. Five of the six are unbalanced braces —
replies such as {"action_items": [{"owner": "Chen", "task": "..."]} that drop
the object's closing brace, which no parser recovers from. The sixth invents a
different key name. This is why INT4 is no longer recommended for any consumer
that parses the reply: it degrades exactly where a downstream pipeline is least
able to cope. INT5's single failure is the same kind of dropped closing brace.
Raising only the embedding to 8 bits while keeping the body at 4 was measured and does not rescue INT4: +15.4 % perplexity, 23 of 24 JSON. If structured output matters, use INT5 or INT8 rather than a mixed INT4.
import Qwen3Chat
let model = try await Qwen35MLXChat.fromPretrained(quantization: .int8)
let response = try model.generate(
messages: [ChatMessage(role: .user, content: "Hello!")],
sampling: ChatSamplingConfig(temperature: 0.3, maxTokens: 100)
)
INT5 and INT8 need a runtime that reads the bit width from
config.json. Older speech-swift versions build everyQuantizedLinearandPreQuantizedEmbeddingwithbits = 4hardcoded, which fits the INT4 file only; they will not read an INT5 or INT8 file correctly. Use a speech-swift version that takesquantization_bitsandquantization_group_sizefromconfig.json, or stay on INT4.
import json
import mlx.core as mx
from huggingface_hub import snapshot_download
path = snapshot_download("aufklarer/Qwen3.5-0.8B-Chat-MLX",
allow_patterns=["int8/*"])
cfg = json.load(open(f"{path}/int8/config.json"))
weights = mx.load(f"{path}/int8/model.safetensors")
# Quantized tensors are stored as a weight/scales/biases triple. Always take the
# bit width and group size from config.json rather than assuming them.
up = mx.dequantize(
weights["layers.0.mlp.up_proj.weight"],
weights["layers.0.mlp.up_proj.scales"],
weights["layers.0.mlp.up_proj.biases"],
group_size=cfg["quantization_group_size"],
bits=cfg["quantization_bits"],
)
print(cfg["quantization"], up.shape) # int8 (3584, 1024)
The layout is the custom key set the Swift runtime loads (layers.N.linear_attn.*
for DeltaNet, layers.N.self_attn.* for the attention layers), not the
mlx-lm naming, so mlx_lm.load will not read it directly.
# One variant only — do not pull all three
hf download aufklarer/Qwen3.5-0.8B-Chat-MLX --include "int8/*" \
--local-dir ./qwen35-0.8b-int8
These files replace an earlier export in which the model's final RMSNorm
weight was written without the +1 offset that Qwen3.5 stores its norm weights
with. That mis-scaled the hidden state feeding the LM head and cost about
+19 % perplexity on its own, on top of any quantization error. If you downloaded
this repository before this revision, re-download it; the numbers in this card
apply only to the current files.
Converted from Qwen/Qwen3.5-0.8B
(Apache-2.0). The vision tower and the multi-token-prediction head are dropped;
the text model is quantized with mx.quantize at group size 64, the
zero-centered RMSNorm weights are folded to 1 + w, and the DeltaNet conv1d
kernels are transposed to MLX's channels-last layout.