model : add support for HrmTextForCausalLM (DFM Mimir 1B) - #27625
Conversation
HRM-Text runs two transformer stacks (low, high) in an alternating cycle over the same token stream. The low-cycle state z_l starts from a learned [n_embd] tensor and is broadcast over positions. - conversion: new writer for the fused gqkv projection (order gate,q,k,v) remapped to llama.cpp q/k/v plus a separate sigmoid gate tensor - loader: block_count = lps * h_cycles * (l_cycles + 1) cache slots aliasing 2*lps physical blocks via struct copies - graph: looped build with sigmoid-gated attention, SwiGLU FFN and parameterless RMS norms; learned embedding_scale applied in build_inp_embd - saver: pointer-deduplicated layer loop (looped archs alias tensors) - tests: hrm_text fixture (lps 1, h 2, l 3) in test-llama-archs Limitations: causal attention only - the upstream prefix-LM mode is not implemented (the prefix_lm GGUF key round-trips unused). The KV cache holds one entry per pass: 128 layers for Mimir 1B, i.e. 4x a same-width 32-layer model - about 3072 MiB at ctx 4096 in F16 (halves with q8_0 KV + FA). Every token runs all 128 block passes, so decode cost is roughly 4x a dense model of equal width (2.65 t/s BF16, 8-thread desktop CPU). Verified against the HF reference: identical argmax at 334/334 positions across 20 prompts (BF16 GGUF vs FP32 golden). q8_0 requant: 95.8% top-1, all remaining misses inside the HF top-5 (accumulated error over 128 sequential blocks). AI usage disclosure: YES Used GLM-5.3 for the majority of code AI-generated under my direction, all gates verified locally. All in all I could say that I have written less than 20% of the code and most of the heavy lifting has been done by the model. As such, this should be considered experimental.
4f66465 to
205cfb4
Compare
# Conflicts: # src/llama-arch.cpp # src/llama-arch.h # src/llama-model-saver.cpp # src/llama-model.h
|
@CISC Would ask you kindly if you would have a look at this model support, please. I know that you're very busy and I'm sorry if I interrupt you |
|
Mimir scores a third lower than it should when llama.cpp runs it, and the cause is how DFM-Mimir is a Danish 1B model. It was trained as a prefix language model. That means: This PR reads the prompt left to right only, the way an ordinary causal model does. So I measured the effect on DAISY, a public Danish quiz of 592 questions made by the same official transformers code, prompt read in both directions 8.4 % The first two rows use the same code and differ only in attention mode: 8.4 against 5.4. Script and logged outputs: github.com/bolgacg/daisy-tools What would fix it is an attention mode for this architecture where the prompt is read in |
|
@bolgacg thank you for your report.
because first I wanted to do the minimal changes for the model to be able to load before creating any additional additions needed for it working correctly. |
|
@noctrex I benefit from this PR getting sorted, since my Mimir evaluation work runs through it, so I went ahead and built the prefix-LM mode on top of your branch; now that the review is moving I wanted to share it: noctrex#1. 35 lines: the KQ mask drops the causal check for models with |
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>
replace raw add_uint32/add_bool calls with dedicated GGUFWriter methods, following the add_embedding_scale pattern Assisted-by: GLM-5.3
delegate unfused checkpoints to the base tensor mapping; training-style attn. names are renamed to self_attn. so the patterns match Assisted-by: GLM-5.3
one argument group per line, matching sibling model files Assisted-by: GLM-5.3
place the name and tensor-info entries with the other global input tensors Assisted-by: GLM-5.3
Assisted-by: GLM-5.3
keep both pre-tokenizer hash entries (gemma4/DFM-Mimir, spark2_5) Assisted-by: GLM-5.3
|
Is there a way Danish Foundation Models could support this implementation? We would be very happy to ensure stable HRMText support with full prefix LM. |
The tensor map holds concrete per-block names, so format the template with the computed layer index before handing it to super().
The four keys are arch-independent, unlike the arch-substituted Keys.LLM entries, so group them under Keys.HRM (like Keys.Split) and rename the llm_kv entries to LLM_KV_HRM_*. Only our own GGUFs carry the old hrm_text.* keys; they are regenerated.
|
@peter-sk hello and thanks for the model! |
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>
Per review: the GGUF keys stay "{arch}.h_cycles" style, so the Python
members drop the LLM_KV_HRM_ prefix and keep arch templates; C++ keeps
the LLM_KV_HRM_* enums. GGUF output is unchanged - existing files and
HF uploads stay valid.
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>
Co-authored-by: Sigbjørn Skjæret <sigbjorn.skjaeret@huggingface.co>
Generic names like add_h_cycles/add_prefix_lm are too broad on the shared GGUFWriter; prefix them with hrm_ like the metadata keys.
Cache tensors of archs that alias physical blocks across looped slots (hrm_text, nanbeige with num_loops > 1) can reference block indices without weight tensor names. Take the output projection from the layer array instead of asserting; all other lookups are unchanged.
ok, cache tensors whose block index has no weight-tensor names (aliased slots) now take the split config from their layer's output projection. |
|
@ggerganov Mind reviewing in case you have differing views on this one? :) |
|
Slightly different failure: |
The aliased cache slots rotate split states differently from their physical weights, so the meta-split execution invariants (set_rows requires the cache state to match the token indices) cannot hold for any device count. Replicate all hrm_text tensors on every meta device instead; single-device and non-meta paths are unchanged. Assisted-by: Claude Sonnet
Metal and CUDA failed for the same reason, just at a later step. |
# Conflicts: # tests/test-llama-archs.cpp
Assisted-by: pi coding agent
Overview
HRM-Text runs two transformer stacks (low, high) in an alternating cycle over the same token stream. The low-cycle state z_l starts from a learned [n_embd] tensor and is broadcast over positions.
Additional information
Limitations:
causal attention only - the upstream prefix-LM mode is not implemented (the prefix_lm GGUF key round-trips unused).
The KV cache holds one entry per pass: 128 layers for Mimir 1B, i.e. 4x a same-width 32-layer model - about 3072 MiB at ctx 4096 in F16 (halves with q8_0 KV + FA).
Every token runs all 128 block passes, so decode cost is roughly 4x a dense model of equal width (2.65 t/s BF16, 8-thread desktop CPU).
Verified against the HF reference: identical argmax at 334/334 positions across 20 prompts (BF16 GGUF vs FP32 golden).
q8_0 requant: 95.8% top-1, all remaining misses inside the HF top-5 (accumulated error over 128 sequential blocks).
I have uploaded the GGUF's here: https://huggingface.co/noctrex/DFM-Mimir
Requirements