THUDM / THUDM/slime

[Bug] Using q instead of normalized q in Megatron's DSA MLA indexer for GLM 5 models

Open
#2,165 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
8.5k
Forks
1.3k
Avg merge
5h 36m
Merged PRs (30d)
22

Description

Bug Description

In slime_plugins/models/glm5/glm5.py, the DSA lightning-indexer projects its query from the raw q_a_proj output (q_compressed), i.e. it skips the q_a_layernorm RMSNorm. The reference GLM-5 / DeepSeek-V3.2 implementations (HF transformers and vLLM) feed the indexer the q_a_layernorm-normalized q.

I've run an experiment to see the difference of doing this or not and couldn't see a significant difference in terms of abs diff between the logprobs of vLLM and megatron, but wanted to raise this still, since I believe this should actually change the top k and thus make the model a little bit off policy.

Locations in the code

I've let AI generate views of where this happens (or rather not) in slime vs HF reference to find it easily in the code:

Where slime skips the norm

q_layernorm is IdentityOp, so the q-a-layernorm is fused only into the main linear_q_up_proj; the indexer reads the pre-norm q_compressed:

# slime_plugins/models/glm5/glm5.py
q_compressed, _ = self.linear_q_down_proj(hidden_states)   # L537: raw q_a_proj output
...
q_compressed = self.q_layernorm(q_compressed)              # L551: q_layernorm = IdentityOp -> no-op
q, _ = self.linear_q_up_proj(q_compressed)                 # L552: main path normalizes internally (fused LN)
...
q_compressed = q_compressed.detach()                       # L616: still un-normalized
index_q, _ = self.wq_b(q_compressed)                       # L620: indexer query from RAW q
Reference (HF transformers)

GlmMoeDsaAttention computes q_resid = q_a_layernorm(q_a_proj(hidden)) and passes that to the indexer, whose wq_b consumes it:

# transformers/models/glm_moe_dsa/modeling_glm_moe_dsa.py
q_resid = self.q_a_layernorm(self.q_a_proj(hidden_states))   # L423
...
topk_indices = self.indexer(hidden_states, q_resid, ...)     # L446

# class GlmMoeDsaIndexer.forward:
#   q_resid: Query residual from `q_a_layernorm(q_a_proj(x))`  (L220, docstring)
q = self.wq_b(q_resid)                                       # L231
Steps to Reproduce

Just any normal GLM 5 training.

Expected Behavior

We are very close to expected behavior, this is more of a slight imprecision.

Actual Behavior

No normalization, where HF normalizes.

Environment

none

Logs

Additional Context

No response

Pre-submission Checklist
  • I have read the CONTRIBUTING.md and understand the collaboration scope.
  • I have read the documentation and my issue is not addressed there.
  • I have searched for existing issues and this is not a duplicate.
  • I have provided a minimal, reproducible example.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read slime_plugins/models/glm5/glm5.py around lines 537-620, especially q_layernorm, q_compressed, and the indexer call. Compare the data flow with the referenced Hugging Face transformers implementation around lines 423, 446, and 220-231; done when the indexer receives the normalized query and normal GLM 5 training can be checked for the expected behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
machine-learning
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.