Skip to content

Converter: per-tensor-type mixed precision (int8 for shared/o_proj/kv_b_proj, int4 rest)#243

Closed
woolcoxm wants to merge 1 commit into
JustVugg:devfrom
woolcoxm:feat/mixed-precision-converter
Closed

Converter: per-tensor-type mixed precision (int8 for shared/o_proj/kv_b_proj, int4 rest)#243
woolcoxm wants to merge 1 commit into
JustVugg:devfrom
woolcoxm:feat/mixed-precision-converter

Conversation

@woolcoxm

Copy link
Copy Markdown
Contributor

Summary

Splits the converter's resident weight classification into 5 sub-types, each independently controllable to a different bit width. The recommended plan puts the 3 most error-sensitive tensors (shared expert, o_proj, kv_b_proj) at int8 and everything else at grouped int4 β€” for only +5.3 GB RAM.

Closes #237.

Problem

Not all tensors suffer equally from int4 quantization. Three categories of weights compound their error on every token:

  1. Shared expert β€” fires on every token regardless of routing. Error pollutes every forward pass.
  2. o_proj β€” reconstructs the attention output. The last matmul before the residual stream.
  3. kv_b_proj β€” reconstructs the KV cache on every decode step. Error is cumulative β€” once in the KV cache, it persists for the entire conversation.

The other ~95% of weight mass (routed experts, dense MLP, other attn projections) is either routed (error isolated to one expert) or lower-sensitivity.

Changes

c/tools/convert_fp8_to_int4.py (+52 lines, -8 lines):

The classify() function is split from 3 types (resident/expert/io) into 5 sub-types:

Type Tensors Default bits
sh shared expert ebits (4)
o o_proj ebits (4)
kvb kv_b_proj ebits (4)
attn q_a, q_b, kv_a projections ebits (4)
dmlp dense MLP (first 3 layers) ebits (4)

New CLI args:

--shared-bits N    # shared expert (default: ebits)
--o-bits N         # o_proj
--kvb-bits N       # kv_b_proj
--attn-bits N      # other attention projections
--dmlp-bits N      # dense MLP

Recommended usage

python tools/convert_fp8_to_int4.py \
  --indir /path/to/GLM-5.2-FP8 \
  --outdir /path/to/glm52_i4_mixed \
  --ebits 4 --io-bits 8 --group-size 128 \
  --shared-bits 8 --o-bits 8 --kvb-bits 8

Cost

Tensor Count Extra RAM (int4β†’int8)
shared expert 3Γ—78 +6 MB
o_proj 78 +234 MB
kv_b_proj 78 +117 MB
Total +5.3 GB (1.4% of 372 GB)

Backward compatibility

  • With no --*-bits flags, behavior is identical to the previous converter
  • Composes with --group-size 128 (orthogonal: grouped scales reduce error, mixed precision protects sensitive tensors)
  • No engine changes β€” the engine already supports int8 (fmt=1) and int4 (fmt=2/4) side-by-side, auto-detected per-tensor

Stacked on

This PR includes the grouped-quant base (#242) because quant_int4_grouped is used by the mixed-precision path when --group-size > 0 and bits <= 4.

Test plan

Split the resident weight classification into 5 sub-types so each can
get different precision:
  sh   = shared expert (highest sensitivity, fires every token)
  o    = o_proj (reconstructs output, biggest attn tensor)
  kvb  = kv_b_proj (reconstructs KV cache on every decode)
  attn = q_a/q_b/kv_a (other attention projections)
  dmlp = dense MLP (first 3 layers)

New args: --shared-bits, --o-bits, --kvb-bits, --attn-bits, --dmlp-bits
Each defaults to ebits (backward compat). When set, the converter applies
that precision to just that tensor type.

Research-backed plan: put the 3 compounding tensors (shared expert, o_proj,
kv_b_proj) at int8 and everything else at grouped int4. Extra RAM cost:
only +5.3 GB (those tensors are small vs the 372 GB expert pool on disk).
@JustVugg

Copy link
Copy Markdown
Owner

Closing β€” this is already on dev, and you're the one who put it there.

The whole thing (classify splitting resident weights into sh/o/kvb/attn/dmlp, bits_map, the five --*-bits flags) landed in 69f65d5 β€” "Windows-dev: grouped quantization + mixed precision + expert budget + download tool". It's at convert_fp8_to_int4.py:141-231 right now.

I checked rather than assumed: I trial-merged this PR onto current dev and git diff --stat dev...HEAD came back empty. Merging it is a literal no-op.

For what it's worth, I reviewed it as if it were new before I found that out, and it's good work: single caller for classify, no hidden kind == "q" checks left behind, and empty bits_map falls back to ebits so the old behaviour is bit-identical. I also verified the engine side actually supports it β€” qt_from_disk detects the format per tensor from the byte counts (nb==O*I β†’ int8, nb==O*ceil(I/2) β†’ int4, then group-size detection), so a model with int8 o_proj and int4 experts really is read correctly. The feature works end to end.

The reason this PR is a ghost is the same thing you were laughing about on #298: that mega-commit bundled grouped quantization + mixed precision + expert budget + the download tool together, so its pieces landed without their own PRs closing. Three of your issues (#237, #238, #239) were in the same boat β€” all delivered, all still open. Closing those too.

The one that bundle also carried was EXPERT_BUDGET, which is now quarantined (#303). Worth noting as the argument for smaller commits: when everything arrives together, the good parts can't be told from the one that had to be pulled back out.

@woolcoxm

woolcoxm commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

ok will keep that in mind, was it one of my experimentation branches by chance?? i work on them for hours at a time and when i find things that work i submit them, they usually contains changes that i didnt mean to submit.

usually i will create a new branch for the experiments, but if its the one im thinking it was, that one had a ton of experimentation done on it and i tried to submit one change at a time but couldnt figure out how to do it properly. it had 2-3 hours worth of work im sure, sorry about that :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants