@@ -232,6 +232,13 @@ def _sparse_decode(expert_output: Tensor, slots: Tensor, gates: Tensor, num_toke
232232 return combined .to (expert_output .dtype )
233233
234234
235+ def _route_norm_enabled (route_norm : Optional [bool ], legacy_default : bool ) -> bool :
236+ """None keeps that gate's historical combine-weight rule. True or False overrides every gate."""
237+ if route_norm is None :
238+ return legacy_default
239+ return route_norm
240+
241+
235242def top1gating (logits : Tensor ,
236243 capacity_factor : float ,
237244 min_capacity : int ,
@@ -241,8 +248,13 @@ def top1gating(logits: Tensor,
241248 use_rts : bool = True ,
242249 ep_group : Union [torch .distributed .ProcessGroup , None ] = None ,
243250 sparse_routes : bool = False ,
244- use_tutel : bool = False ) -> Tuple [Tensor , Tensor , Tensor , Tensor ]:
245- """Implements Top1Gating on logits."""
251+ use_tutel : bool = False ,
252+ route_norm : Optional [bool ] = None ) -> Tuple [Tensor , Tensor , Tensor , Tensor ]:
253+ """Implements Top1Gating on logits.
254+
255+ route_norm=None keeps the historical weights: the chosen expert's softmax probability, with no
256+ second normalization. The auxiliary loss is computed from those probabilities either way.
257+ """
246258 if noisy_gate_policy == 'RSample' :
247259 logits_w_noise = logits + gumbel_rsample (logits .shape , device = logits .device )
248260 # everything is in fp32 in this function
@@ -319,16 +331,25 @@ def top1gating(logits: Tensor,
319331
320332 if sparse_routes :
321333 gates1_s = (gates * mask1 ).sum (dim = 1 )
334+ # Historical top-1 does not renormalize. An explicit True makes a kept token's weight 1.
335+ # A dropped token sums to 0, and 0 / eps stays 0.
336+ if _route_norm_enabled (route_norm , False ):
337+ denom_s = torch .clamp (gates1_s , min = torch .finfo (gates1_s .dtype ).eps )
338+ gates1_s = gates1_s / denom_s
322339 locations1_s = torch .sum (locations1 * mask1 , dim = 1 )
323340 return (l_aux , capacity , num_experts , indices1_s .to (torch .int32 ).unsqueeze (0 ),
324341 locations1_s .to (torch .int32 ).unsqueeze (0 ), gates1_s .unsqueeze (0 ), exp_counts )
325342
326343 # Store the capacity location for each token
327344 locations1_s = torch .sum (locations1 * mask1 , dim = 1 )
328345
329- # Normalize gate probabilities
346+ # Keep the assigned expert's softmax probability. route_norm rescales a kept row to 1.
330347 mask1_float = mask1 .float ()
331348 gates = gates * mask1_float
349+ if _route_norm_enabled (route_norm , False ):
350+ denom_s = torch .sum (gates , dim = 1 , keepdim = True )
351+ denom_s = torch .clamp (denom_s , min = torch .finfo (denom_s .dtype ).eps )
352+ gates = gates / denom_s
332353
333354 locations1_sc = _one_hot_to_float (locations1_s , capacity )
334355 combine_weights = einsum ("se,sc->sec" , gates , locations1_sc )
@@ -344,8 +365,13 @@ def top2gating(logits: Tensor,
344365 drop_tokens : bool = True ,
345366 ep_group : Union [torch .distributed .ProcessGroup , None ] = None ,
346367 top2_2nd_expert_sampling : bool = True ,
347- sparse_routes : bool = False ) -> Tuple [Tensor , Tensor , Tensor , Tensor ]:
348- """Implements Top2Gating on logits."""
368+ sparse_routes : bool = False ,
369+ route_norm : Optional [bool ] = None ) -> Tuple [Tensor , Tensor , Tensor , Tensor ]:
370+ """Implements Top2Gating on logits.
371+
372+ route_norm=None keeps the historical weights: the two kept probabilities are rescaled to sum to 1.
373+ The auxiliary loss is computed before that rescaling.
374+ """
349375 # everything is in fp32 in this function
350376 gates = F .softmax (logits , dim = 1 )
351377
@@ -399,16 +425,17 @@ def top2gating(logits: Tensor,
399425 locations1_s = torch .sum (locations1 * mask1 , dim = 1 )
400426 locations2_s = torch .sum (locations2 * mask2 , dim = 1 )
401427
402- # Normalize gate probabilities
428+ # Normalize gate probabilities. route_norm=None keeps this rescaling; False leaves the raw softmax values.
403429 mask1_float = mask1 .float ()
404430 mask2_float = mask2 .float ()
405431 gates1_s = einsum ("se,se->s" , gates , mask1_float )
406432 gates2_s = einsum ("se,se->s" , gates , mask2_float )
407- denom_s = gates1_s + gates2_s
408- # Avoid divide-by-zero
409- denom_s = torch .clamp (denom_s , min = torch .finfo (denom_s .dtype ).eps )
410- gates1_s /= denom_s
411- gates2_s /= denom_s
433+ if _route_norm_enabled (route_norm , True ):
434+ denom_s = gates1_s + gates2_s
435+ # Avoid divide-by-zero. A dropped token is 0 / eps and stays 0.
436+ denom_s = torch .clamp (denom_s , min = torch .finfo (denom_s .dtype ).eps )
437+ gates1_s /= denom_s
438+ gates2_s /= denom_s
412439
413440 if sparse_routes :
414441 # Routes evicted by the capacity limit are flagged with a negative expert index.
@@ -440,8 +467,13 @@ def topkgating(
440467 ep_group : Union [torch .distributed .ProcessGroup , None ] = None ,
441468 drop_policy : str = "probs" ,
442469 sparse_routes : bool = False ,
470+ route_norm : Optional [bool ] = None ,
443471) -> Tuple [Tensor , Tensor , Tensor , Tensor ]:
444- """Implements TopKGating on logits."""
472+ """Implements TopKGating on logits.
473+
474+ route_norm=None keeps the historical weights: the kept probabilities are rescaled to sum to 1.
475+ The auxiliary loss is computed before that rescaling.
476+ """
445477
446478 # everything is in fp32 in this function
447479 # gating decisions
@@ -494,11 +526,12 @@ def topkgating(
494526 capacity = new_capacity
495527 locations = torch .cumsum (mask , dim = 0 ) - 1
496528
497- # normalize gates
529+ # normalize gates. route_norm=None keeps this rescaling; False leaves the raw softmax values.
498530 gates_masked = gates * mask
499- gates_s = torch .sum (gates_masked , dim = - 1 , keepdim = True )
500- denom_s = torch .clamp (gates_s , min = torch .finfo (gates_masked .dtype ).eps )
501- gates_masked = gates_masked / denom_s
531+ if _route_norm_enabled (route_norm , True ):
532+ gates_s = torch .sum (gates_masked , dim = - 1 , keepdim = True )
533+ denom_s = torch .clamp (gates_s , min = torch .finfo (gates_masked .dtype ).eps )
534+ gates_masked = gates_masked / denom_s
502535
503536 if locations is None :
504537 raise ValueError (f"Locations is not set: { locations } " )
@@ -539,6 +572,10 @@ class TopKGate(Module):
539572 size of model embedding dimension
540573 num_experts (int):
541574 number of experts in model
575+ route_norm (bool, optional):
576+ None keeps the historical combine weights (no renorm for k=1, renorm for k>=2).
577+ True renormalizes the kept weights for every k. False leaves the raw softmax values.
578+ The auxiliary loss does not use this flag.
542579 """
543580
544581 wg : torch .nn .Linear
@@ -554,7 +591,8 @@ def __init__(self,
554591 drop_tokens : bool = True ,
555592 use_rts : bool = True ,
556593 ep_group : Union [torch .distributed .ProcessGroup , None ] = None ,
557- top2_2nd_expert_sampling : bool = True ) -> None :
594+ top2_2nd_expert_sampling : bool = True ,
595+ route_norm : Optional [bool ] = None ) -> None :
558596 super ().__init__ ()
559597
560598 self .wg = torch .nn .Linear (model_dim , num_experts , bias = False )
@@ -570,6 +608,8 @@ def __init__(self,
570608 self .drop_tokens = drop_tokens
571609 self .use_rts = use_rts
572610 self .top2_2nd_expert_sampling = top2_2nd_expert_sampling
611+ # None is forwarded so each gating function keeps its own historical default.
612+ self .route_norm = route_norm
573613
574614 def _set_ep_group (self , ep_group ):
575615 assert self .ep_group is None , 'Attempting to override an existing ep_group'
@@ -591,22 +631,36 @@ def forward(self,
591631 logits = torch .nn .functional .linear (input_fp32 , weight = self .wg .weight .float (), bias = None )
592632
593633 if self .k == 1 :
594- gate_output = top1gating (logits , self .capacity_factor if self .training else self .eval_capacity_factor ,
595- self .min_capacity , used_token , self .noisy_gate_policy if self .training else None ,
596- self .drop_tokens , self .use_rts , self .ep_group , sparse_routes , use_tutel )
634+ gate_output = top1gating (logits ,
635+ self .capacity_factor if self .training else self .eval_capacity_factor ,
636+ self .min_capacity ,
637+ used_token ,
638+ self .noisy_gate_policy if self .training else None ,
639+ self .drop_tokens ,
640+ self .use_rts ,
641+ self .ep_group ,
642+ sparse_routes ,
643+ use_tutel ,
644+ route_norm = self .route_norm )
597645
598646 elif self .k == 2 :
599- gate_output = top2gating (logits , self .capacity_factor if self .training else self .eval_capacity_factor ,
600- self .min_capacity , self .drop_tokens , self .ep_group , self .top2_2nd_expert_sampling ,
601- sparse_routes )
647+ gate_output = top2gating (logits ,
648+ self .capacity_factor if self .training else self .eval_capacity_factor ,
649+ self .min_capacity ,
650+ self .drop_tokens ,
651+ self .ep_group ,
652+ self .top2_2nd_expert_sampling ,
653+ sparse_routes ,
654+ route_norm = self .route_norm )
602655 else :
603656 gate_output = topkgating (logits ,
604657 self .k ,
605658 self .capacity_factor if self .training else self .eval_capacity_factor ,
606659 self .min_capacity ,
607660 self .drop_tokens ,
608661 self .ep_group ,
609- sparse_routes = sparse_routes )
662+ sparse_routes = sparse_routes ,
663+ route_norm = self .route_norm )
610664
611665 if self .wall_clock_breakdown :
612666 self .timers (TOPK_GATE_TIMER ).stop ()
0 commit comments