Kernels
Jangwoong Kim commited on
Commit
9765db5
·
unverified ·
2 Parent(s): 8d60c300669c49

Merge pull request #34 from MotifTechnologies/fix/qk-clip-rotary-full-gamma

Browse files
torch-ext/optimizer/muon.py CHANGED
@@ -479,6 +479,18 @@ class Muon(torch.optim.Optimizer):
479
  p, qk_clip_state, n
480
  ) if qk_clip_state is not None else None
481
  if scales_full is not None:
 
 
 
 
 
 
 
 
 
 
 
 
482
  ratio = p.shape[0] // scales_full.shape[0]
483
  idx0 = local_indices[0]
484
  if isinstance(idx0, slice):
@@ -486,7 +498,7 @@ class Muon(torch.optim.Optimizer):
486
  idx0 = torch.arange(start,
487
  idx0.stop,
488
  device=scales_full.device)
489
- row_scales = scales_full[idx0 // ratio]
490
  p._local_tensor.mul_(row_scales.view(-1, 1))
491
 
492
  if not batch_params:
@@ -558,6 +570,18 @@ class Muon(torch.optim.Optimizer):
558
  p, qk_clip_state, n
559
  ) if qk_clip_state is not None else None
560
  if scales_full is not None:
 
 
 
 
 
 
 
 
 
 
 
 
561
  ratio = p.shape[0] // scales_full.shape[0]
562
  idx0 = local_indices[0]
563
  if isinstance(idx0, slice):
@@ -565,7 +589,7 @@ class Muon(torch.optim.Optimizer):
565
  idx0 = torch.arange(start,
566
  idx0.stop,
567
  device=scales_full.device)
568
- row_scales = scales_full[idx0 // ratio]
569
  p._local_tensor.mul_(row_scales.view(-1, 1))
570
 
571
  def _setup_parallel(self, names, params, group, qk_logits):
 
479
  p, qk_clip_state, n
480
  ) if qk_clip_state is not None else None
481
  if scales_full is not None:
482
+ # scales_full is raw per-head γ; apply the content
483
+ # exponent per head (MHA √γ, wq_b γ^(1-r), wkv_b
484
+ # γ^r). NOTE: this per-head form cannot resolve the
485
+ # MLA rotary/value sub-regions (q_pe → full γ, v
486
+ # untouched) — see qk_clip; the sharded path scales
487
+ # q_pe/v by the content exponent instead.
488
+ if not qk_clip_state.is_mla:
489
+ e = 0.5
490
+ elif qk_clip_state.kind == 'wq_b':
491
+ e = 1.0 - qk_clip_state.k_ratio
492
+ else:
493
+ e = qk_clip_state.k_ratio
494
  ratio = p.shape[0] // scales_full.shape[0]
495
  idx0 = local_indices[0]
496
  if isinstance(idx0, slice):
 
498
  idx0 = torch.arange(start,
499
  idx0.stop,
500
  device=scales_full.device)
501
+ row_scales = (scales_full**e)[idx0 // ratio]
502
  p._local_tensor.mul_(row_scales.view(-1, 1))
503
 
504
  if not batch_params:
 
570
  p, qk_clip_state, n
571
  ) if qk_clip_state is not None else None
572
  if scales_full is not None:
573
+ # scales_full is raw per-head γ; apply the content
574
+ # exponent per head (MHA √γ, wq_b γ^(1-r), wkv_b
575
+ # γ^r). NOTE: this per-head form cannot resolve the
576
+ # MLA rotary/value sub-regions (q_pe → full γ, v
577
+ # untouched) — see qk_clip; the sharded path scales
578
+ # q_pe/v by the content exponent instead.
579
+ if not qk_clip_state.is_mla:
580
+ e = 0.5
581
+ elif qk_clip_state.kind == 'wq_b':
582
+ e = 1.0 - qk_clip_state.k_ratio
583
+ else:
584
+ e = qk_clip_state.k_ratio
585
  ratio = p.shape[0] // scales_full.shape[0]
586
  idx0 = local_indices[0]
587
  if isinstance(idx0, slice):
 
589
  idx0 = torch.arange(start,
590
  idx0.stop,
591
  device=scales_full.device)
592
+ row_scales = (scales_full**e)[idx0 // ratio]
593
  p._local_tensor.mul_(row_scales.view(-1, 1))
594
 
595
  def _setup_parallel(self, names, params, group, qk_logits):
torch-ext/optimizer/qk_clip.py CHANGED
@@ -1,5 +1,4 @@
1
  import logging
2
- import math
3
  from dataclasses import dataclass
4
 
5
  import torch
@@ -127,17 +126,16 @@ def get_qk_clip_info(clip_config, n, qk_logits):
127
 
128
 
129
  def compute_scales(p, qk_clip_state, name: str = ''):
130
- """Compute per-head scaling factors for QK clipping.
131
 
132
- Returns scales tensor per head if any head exceeds threshold, else None.
 
 
 
 
133
 
134
- Scale convention (let γ = threshold/value):
135
- MHA/GQA wq/wk : scale = √γ on both Q and K; product = γ.
136
- MLA wq_b : scale = γ^(1 - k_ratio). Q carries (1-k_ratio) of the
137
- log-reduction; k_ratio=0 → full γ on Q only (K unclipped).
138
- MLA wkv_b : scale = γ^k_ratio applied to k_nope rows only (v rows
139
- untouched). Returns None when k_ratio=0 (K unclipped).
140
- Together wq_b × wkv_b deliver γ^(1-k_ratio) × γ^k_ratio = γ end-to-end.
141
  """
142
  kind = qk_clip_state.kind
143
 
@@ -150,32 +148,18 @@ def compute_scales(p, qk_clip_state, name: str = ''):
150
  threshold = qk_clip_state.threshold
151
  logit = qk_clip_state.logit
152
 
153
- # Exponent for (threshold/v_ele)^exponent:
154
- # MLA wq_b → 1 - k_ratio (Q carries this fraction of log-reduction)
155
- # MLA wkv_b → k_ratio (K carries the rest; applied to k_nope rows only)
156
- # MHA/GQA → 0.5 (equal split; equivalent to k_ratio=0.5)
157
- #
158
- # Principled k_ratio for Q:K = G:1 GQA: 1 / (1 + sqrt(G))
159
- # K's gradient is a sum of G Q-head terms that partially cancel, so its
160
- # effective recovery signal scales as 1/sqrt(G) (random-walk argument).
161
- # Setting clip/recovery equal for both sides:
162
- # (1-r)/1 = r/(1/sqrt(G)) => r = 1/(1+sqrt(G))
163
- # G=1 → 0.5 (recovers MHA), G=4 → ≈0.33, G=8 → ≈0.26.
164
- if qk_clip_state.is_mla:
165
- exponent = (1.0 - qk_clip_state.k_ratio) if kind == 'wq_b' else qk_clip_state.k_ratio
166
- else:
167
- exponent = 0.5
168
-
169
  head_scales = {}
170
  for logit_idx, head_idx in enumerate(indices):
171
  v_ele = float(logit[logit_idx])
172
  if v_ele > threshold:
173
- new_scale = (threshold / v_ele) ** exponent
174
- if head_idx not in head_scales or new_scale < head_scales[head_idx]:
175
- head_scales[head_idx] = new_scale
 
 
176
  logger.info(
177
  f"[{kind}] {name} Head {head_idx} exceeded threshold "
178
- f"(value={v_ele:.4f}, threshold={threshold:.4f}) -> applying scale={new_scale:.4f}"
179
  )
180
 
181
  if not head_scales:
@@ -195,36 +179,40 @@ def compute_scales(p, qk_clip_state, name: str = ''):
195
 
196
 
197
  def qk_clip(p, scales, info):
198
- """Apply per-head scaling to a Q/K projection weight matrix.
 
199
 
200
  Args:
201
  p: Parameter (nn.Parameter or raw tensor).
202
- scales: [n_heads] tensor of per-head scale factors (γ^exponent).
203
- Exponent semantics depend on kind — see compute_scales docstring.
204
- info: QKClipInfo with kind, head_dim, and MLA sub-head dimensions.
205
-
206
- MLA wq_b: γ^(1-k_ratio) applied uniformly to q_nope and q_pe rows.
207
- MLA wkv_b: γ^k_ratio applied to k_nope rows only; v rows untouched.
208
- MHA/GQA: √γ applied to all rows in each head.
 
 
 
 
209
  """
210
  W = p.data if isinstance(p, torch.nn.Parameter) else p
 
211
 
212
  if not info.is_mla:
213
- # MHA/GQA: uniform √γ applied to all rows in each head
214
- W.view(-1, info.head_dim, W.shape[1]).mul_(scales.view(-1, 1, 1))
215
  return
216
 
217
- # MLA: vectorized sub-region scaling within each head
218
  if info.kind == 'wq_b':
219
  qk_nope = info.qk_nope_head_dim
220
  qk_head_dim = qk_nope + info.qk_rope_head_dim
221
  W_3d = W.view(-1, qk_head_dim, W.shape[1]) # [H, qk_head_dim, in_dim]
222
- W_3d[:, :qk_nope, :].mul_(scales.view(-1, 1, 1)) # q_nope → γ^(1-k_ratio)
223
- W_3d[:, qk_nope:, :].mul_(scales.view(-1, 1, 1)) # q_pe → γ^(1-k_ratio)
224
 
225
  elif info.kind == 'wkv_b':
226
  qk_nope = info.qk_nope_head_dim
227
  kv_stride = qk_nope + info.v_head_dim
228
  W_3d = W.view(-1, kv_stride, W.shape[1]) # [H, kv_stride, in_dim]
229
- W_3d[:, :qk_nope, :].mul_(scales.view(-1, 1, 1)) # k_nope → γ^k_ratio
230
- # v rows: not touched (k_R shared rotary unchanged)
 
1
  import logging
 
2
  from dataclasses import dataclass
3
 
4
  import torch
 
126
 
127
 
128
  def compute_scales(p, qk_clip_state, name: str = ''):
129
+ """Compute the per-head raw clip factor γ = threshold / max_logit.
130
 
131
+ Returns a per-head tensor of γ (1.0 for heads under the threshold) if any
132
+ head requires clipping, else None. The per-region exponent on γ is NOT
133
+ applied here — it is applied at multiply time (``qk_clip`` for the whole
134
+ tensor, or the sharded path in muon.py) so that one per-head γ can drive
135
+ different sub-regions (q_nope, q_pe, k_nope, v) with different exponents.
136
 
137
+ wkv_b with k_ratio=0 returns None: the whole reduction is placed on Q and
138
+ the key projection is left unclipped.
 
 
 
 
 
139
  """
140
  kind = qk_clip_state.kind
141
 
 
148
  threshold = qk_clip_state.threshold
149
  logit = qk_clip_state.logit
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  head_scales = {}
152
  for logit_idx, head_idx in enumerate(indices):
153
  v_ele = float(logit[logit_idx])
154
  if v_ele > threshold:
155
+ gamma = threshold / v_ele # raw clip factor; exponent applied later
156
+ # Within a shared (GQA) group, keep the smallest γ, i.e. the
157
+ # strongest required clipping.
158
+ if head_idx not in head_scales or gamma < head_scales[head_idx]:
159
+ head_scales[head_idx] = gamma
160
  logger.info(
161
  f"[{kind}] {name} Head {head_idx} exceeded threshold "
162
+ f"(value={v_ele:.4f}, threshold={threshold:.4f}) -> γ={gamma:.4f}"
163
  )
164
 
165
  if not head_scales:
 
179
 
180
 
181
  def qk_clip(p, scales, info):
182
+ """Apply QK-clip to a Q/K projection weight; each sub-region is raised to
183
+ its own exponent so every logit component is reduced by exactly γ.
184
 
185
  Args:
186
  p: Parameter (nn.Parameter or raw tensor).
187
+ scales: [n_heads] tensor of per-head raw γ (= threshold/value), 1.0 for
188
+ unclipped heads, as returned by ``compute_scales``.
189
+ info: QKClipInfo with kind, head_dim, k_ratio and MLA sub-head dims.
190
+
191
+ Exponents:
192
+ MHA : √γ on all rows.
193
+ MLA q_nope : γ^(1-k_ratio), k_nope : γ^k_ratio (content product = γ).
194
+ MLA q_pe : γ (full) — the decoupled rotary key is shared and left
195
+ unchanged, so the rotary query carries the whole γ for
196
+ every k_ratio; γ^(1-k_ratio) here would under-clip rotary.
197
+ MLA v rows : untouched.
198
  """
199
  W = p.data if isinstance(p, torch.nn.Parameter) else p
200
+ r = info.k_ratio
201
 
202
  if not info.is_mla:
203
+ W.view(-1, info.head_dim, W.shape[1]).mul_((scales**0.5).view(-1, 1, 1))
 
204
  return
205
 
 
206
  if info.kind == 'wq_b':
207
  qk_nope = info.qk_nope_head_dim
208
  qk_head_dim = qk_nope + info.qk_rope_head_dim
209
  W_3d = W.view(-1, qk_head_dim, W.shape[1]) # [H, qk_head_dim, in_dim]
210
+ W_3d[:, :qk_nope, :].mul_((scales**(1.0 - r)).view(-1, 1, 1)) # q_nope → γ^(1-r)
211
+ W_3d[:, qk_nope:, :].mul_(scales.view(-1, 1, 1)) # q_pe → γ (full)
212
 
213
  elif info.kind == 'wkv_b':
214
  qk_nope = info.qk_nope_head_dim
215
  kv_stride = qk_nope + info.v_head_dim
216
  W_3d = W.view(-1, kv_stride, W.shape[1]) # [H, kv_stride, in_dim]
217
+ W_3d[:, :qk_nope, :].mul_((scales**r).view(-1, 1, 1)) # k_nope → γ^r
218
+ # v rows: untouched (shared rotary key k_rope is unchanged)