Skip to content

gemma4 GGUF: embedding quant type hardcoded to Q6_K, so a Q4_0-embedding GGUF fails to load with a bare AssertionError #188

Description

@salekseev

Summary

convert_gemma4_to_gguf (python/freetoken/models/gemma4/gguf.py) hardcodes GGML_Q6_K
for the token embedding table and the tied LM head:

embed = GGUFEmbedding(
    num_embeddings=config.vocab_size,
    embedding_dim=config.hidden_size,
    quant_type=GGML_Q6_K,          # <-- assumed, not read from the file
    embed_scale=config.embedding_scale,
)
...
if config.tie_word_embeddings:
    model.lm_head = GGUFTiedLMHead(embed, GGML_Q6_K)

token_embd.weight is not always Q6_K. google's gemma-4-26B-A4B-it-qat-q4_0-gguf keeps it
at Q6_K, but unsloth's gemma-4-26B-A4B-it-qat-GGUF quantizes it to Q4_0, and row stride
differs by block type — 2310 bytes/row for Q6_K (2816/256 * 210) against 1584 for Q4_0
(2816/32 * 18) at hidden_size = 2816. GGUFEmbedding.__init__ sizes qweight with
row_bytes(embedding_dim, quant_type), so the buffer is allocated 2310 wide and the file
delivers 1584.

The load then dies in the shared loader on an assert with no message:

File "freetoken/layers/base.py", line 45, in load_state_dict
    assert param.shape == item.shape and param.dtype == item.dtype
AssertionError

layers/base.py:45 names neither the parameter nor the shapes, so the failure gives no
indication of which tensor or why. Instrumenting that traversal shows it:

MISMATCH model.embed_tokens.qweight: model wants (262144, 2310) torch.uint8 | file has (262144, 1584) torch.uint8

Nothing else in the checkpoint is affected — the projections and routed experts are Q4_0
and load fine, so the failure is purely this one assumption.

Environment

  • FreeToken 0.1.2 (PyPI wheel + freetoken_kernel_cache-0.1.2+cu130)
  • RTX 4080 SUPER (sm_89, 16 GB), driver 610.57.04, CUDA UMD 13.3
  • Model: unsloth/gemma-4-26B-A4B-it-qat-GGUF / gemma-4-26B-A4B-it-qat-UD-Q4_K_XL.gguf
    (despite the filename this file is general.file_type = 2 (MOSTLY_Q4_0):
    266 Q4_0 + 392 F32 tensors, general.name = "Gemma-4 26B-A4B IT (smart Q4_0, QAT-lossless)")
  • ft serve --model .../gemma-4-26B-A4B-it-qat-UD-Q4_K_XL.gguf

Reproduction

Any gemma4 GGUF whose token_embd.weight is not Q6_K. Reading the type straight out of the
file is enough to show the mismatch without a load:

from freetoken.models.gguf.reader import iter_gguf_tensors
from freetoken.models.gguf.dequant import GGML_NAME, GGML_Q6_K, row_bytes

path = ".../gemma-4-26B-A4B-it-qat-UD-Q4_K_XL.gguf"
for t in iter_gguf_tensors(path):
    if t.name == "token_embd.weight":
        print(f"file: {GGML_NAME[t.ggml_type]}, {row_bytes(2816, t.ggml_type)} bytes/row")
        print(f"code: {GGML_NAME[GGML_Q6_K]}, {row_bytes(2816, GGML_Q6_K)} bytes/row")
file: Q4_0, 1584 bytes/row
code: Q6_K, 2310 bytes/row

Suggested fix

GGUFEmbedding and GGUFTiedLMHead are already type-generic — they thread quant_type
into row_bytes() and ggml_dequantize(), and Q4_0 is in _MMVQ / _MMQ / _DEQUANT in
layers/gguf.py — so only the plumbing is missing. Reading the type from the tensor table
in parse_gguf_config and using it in convert_gemma4_to_gguf is sufficient; a
metadata-only GGUF (an FTW dir's source_metadata.gguf, which lists no tensors) has no
entry to read, so the Q6_K default still applies there.

With that change the model loads and serves correctly on this checkpoint: 179 tok/s decode
at 1200 tokens, --moe-backend offload, sane output, tool calls and reasoning parsed.

Happy to open a PR with the patch and a test if the approach looks right.

One adjacent thing, possibly worth its own issue: layers/base.py:45 should name the
tensor.
Every shape/dtype mismatch in every model surfaces as a bare AssertionError,
and the traversal has the key and both shapes in hand at that point. A one-line message
there would have turned this from an afternoon of instrumenting the loader into a minute.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions