Land the MeroMero v2-31B NVFP4A16 quant and record the pinned-transformers trap
The v2 dense quant had failed four times. Attempt 5 lands it at 19 G. The blocker was not what it looked like. `AmbiguousGlobalPerLayerAttributeError` on `head_dim` read as a malformed upload -- DogOnKeyboard's config carries a `per_layer_config` key zerofata's canonical one lacks -- and the standing fix was to force `allow_global_per_layer_attribute_access=True`. Both halves were wrong. `pip install llmcompressor==0.13.0` downgrades transformers 5.16.1 -> 5.14.1. The config was serialized by 5.16.1, which materializes `per_layer_config` from `global_head_dim` + `layer_types`; 5.14.1 carries the heterogeneity guard but not the gemma4 resolver. Under the image's own transformers the same config loads fine. `:latest` was also re-pulled during attempt 4 and no earlier run, so the toolchain moved mid-diagnosis. Two things separated "malformed upload" from "moved toolchain": reproducing the real failing call (a bare AutoConfig load does not reproduce it; the trigger is reached through AutoTokenizer) and keeping zerofata's canonical tree, quantized cleanly on 2026-08-21, as a positive control. The fix drops `per_layer_config` rather than forcing global access. It is exactly redundant -- keys are precisely the ten full_attention layer indices, sole value (512, 4), verbatim the global fields -- and forcing instead would make `config.head_dim` answer 256 to the callers building the 512-wide layers. patch_perlayer.py re-proves that redundancy at apply time and refuses if it ever stops holding. Verified on the tensor table rather than the exit code: the output is identical family-for-family and count-for-count to the August canonical quant, with 356 BF16 vision-tower tensors preserved and input_activations=None. A GPU-free load leaves 0 tensors on meta and generates coherent prose. The section 4.4 serve test has NOT run -- GPU1 has 19.9 GB free against 19.5 GB of weights, so it needs a live seat displaced. Also fixes the A4B output, which had a truncation cap baked into its tokenizer (max_length 8192) from being quantized with the calibration corpus. Playbook gains section 3.17 for the pinned-transformers class and sharpens 3.16 to say drop the dataset outright for any A16 scheme.
This commit is contained in:
@@ -471,6 +471,47 @@ Reference: `services/erp-seat-quant/quant_nvfp4a16_gemma4_moe.py` (linearize_moe
|
||||
11,520 expert Linears + post-steps; the published `prithivMLmods/gemma-4-26B-A4B-it-NVFP4A16`
|
||||
recipe replicated, 222→252 ignore entries with audio/norm/router regexes added).
|
||||
|
||||
**So for any `*A16` scheme, do not pass a dataset at all** — not a shorter one, none. It removes
|
||||
the §3.14 tokenizer bake-in *and* llm-compressor's "initialize model processor ... required when a
|
||||
dataset is provided" demand, which is fatal on any upload that ships no `processor_config.json`.
|
||||
Both of those cost an attempt on MeroMero v2 (2026-09-10); dropping the corpus costs nothing,
|
||||
because a `DataFreePipeline` was never going to read it. Driver:
|
||||
`services/meromero-quant/quant_a16_datafree.py`. **Confirmed twice more the same day**: the A4B
|
||||
heretic quant, run *with* the corpus, shipped `max_length: 8192` in its `tokenizer.json`; the v2
|
||||
dense, run without it, came out `truncation: null`.
|
||||
|
||||
### 3.17 ⭐⭐ The transformers you measured is not the transformers that ran — llm-compressor pins it
|
||||
|
||||
**Measured 2026-09-10, MeroMero v2-31B, and it cost a full misdiagnosis.** The quant died in
|
||||
`AutoTokenizer.from_pretrained` with
|
||||
`AmbiguousGlobalPerLayerAttributeError: 'head_dim' is a per-layer attribute`. The obvious reading
|
||||
was that the source config carried a `per_layer_config` key the canonical one lacked, so that key
|
||||
was the defect. It was not.
|
||||
|
||||
`pip install llmcompressor==0.13.0` **downgrades transformers underneath you** — 5.16.1 → 5.14.1 in
|
||||
the `vllm/vllm-openai` image. The config had been serialized *by* 5.16.1, which materializes
|
||||
`per_layer_config` from `global_head_dim` + `layer_types`; 5.14.1 carries the heterogeneity guard
|
||||
but not the gemma4 resolver, so it refuses the global read. Under the image's own 5.16.1 the very
|
||||
same config loads fine, which is exactly what makes this class expensive: **the version you print
|
||||
at the top of the script is not the version the quant runs on.**
|
||||
|
||||
- **Print the version AFTER the install**, in the same container, and put it in the log.
|
||||
`python3 -c 'import transformers; print(transformers.__version__)'` as a pipeline step, not a
|
||||
thing you check by hand once.
|
||||
- **Pin the image by digest for the length of a campaign.** `:latest` was re-pulled between
|
||||
attempts 3 and 4 of this run and moved the toolchain mid-diagnosis, so the same command produced
|
||||
a different error for reasons that had nothing to do with the change under test.
|
||||
- **Reproduce the actual failing call, not a paraphrase.** A bare `AutoConfig.from_pretrained` did
|
||||
not reproduce this at all — the trigger was reached through `AutoTokenizer`. Testing the config
|
||||
in isolation would have "cleared" it.
|
||||
- **Keep a known-good tree as the positive control.** zerofata's canonical v2, quantized cleanly
|
||||
three weeks earlier, is what separated "this config is malformed" from "this toolchain moved".
|
||||
Without it, four green variants and one red one are just noise.
|
||||
|
||||
Related but distinct from §3.4, which is about version deadlocks you can *see*. This one is a
|
||||
silent downgrade inside a line you already trusted. Instruments: `services/meromero-quant/`
|
||||
(`tok_repro.py`, `perlayer_test.py`).
|
||||
|
||||
### 3.14 ⭐⭐ Calibration BAKES a truncation cap into the shipped tokenizer
|
||||
|
||||
**Symptom (on a newer transformers, at startup, on a vision model):**
|
||||
@@ -579,6 +620,10 @@ Never optional, always in this order, and the last one **verifies rather than as
|
||||
|
||||
Reference implementation: `services/gen-seat-mixed-quant/post_quant.py`.
|
||||
|
||||
On Gemma-4 steps 1 and 3 are N/A — the family ships no MTP head at all — which leaves 2 and 4, and
|
||||
4 is the one that fires. `services/meromero-quant/post_quant_gemma4.py` runs them idempotently with
|
||||
a `--check` mode; point it at a tree you already trust before you trust its verdict on a new one.
|
||||
|
||||
### 4.4 Test on a temp port, never on the live seat
|
||||
|
||||
Serve the candidate on an alt port with the live seat's **exact** flags, run the gate (§5), and
|
||||
@@ -698,6 +743,7 @@ above, and where the two disagree, **this file wins**.
|
||||
|---|---|
|
||||
| `services/gen-seat-mixed-quant/` | **current reference.** Mixed NVFP4+FP8 on Qwen3.8-27B-Uncensored: scripts, acceptance harness, raw measurements. |
|
||||
| `stacks/gen-seat/README.md` | the live `gen` seat (7 LiteLLM aliases) |
|
||||
| `services/meromero-quant/` | NVFP4A16 on Gemma-4 (MeroMero A4B + v2-31B ablits): the five-attempt failure chain, the pinned-transformers trap (§3.17), and the GPU-free verification instruments. |
|
||||
| `stacks/meromero-charrp/README.md` | Gemma-4 seat — the **tool-call/reasoning-parser** trap (a parser default that returns null `content` for all prose) |
|
||||
| `services/heretic2-nvfp4-quant/` | modelopt-format MTP seat — historical; see §7 before following it |
|
||||
| `tools/mistral-small4-nvfp4/` | MoE + native-convert path; source of §3.6 |
|
||||
|
||||
Reference in New Issue
Block a user