smithblack-0 commited on
Commit
cf123f4
·
verified ·
1 Parent(s): cd6e11a

Update architecture and tokenizer

Browse files
Files changed (1) hide show
  1. huggingface.py +56 -85
huggingface.py CHANGED
@@ -2825,86 +2825,57 @@ class MoSRAHRouter(nn.Module):
2825
  self.expert_bias = nn.Parameter(torch.zeros(config.num_mosrah_heads))
2826
 
2827
  @staticmethod
2828
- def get_threshold(
2829
  tensor: torch.Tensor,
2830
  dim: int,
2831
  n: int | torch.Tensor,
 
2832
  ) -> torch.Tensor:
2833
- """
2834
- Returns the n-th largest value along dim, keepdim=True.
2835
 
2836
- A value >= threshold ranks within the top n along dim. Boundary cases
2837
- follow the monotone descending contract:
 
 
2838
 
2839
- n == 0 -> +inf nothing qualifies
2840
- n > dim_length -> -inf everything qualifies
 
 
 
 
 
 
 
2841
 
2842
- :param tensor: Floating-point input, no NaN.
2843
- :param dim: Dimension to reduce along.
2844
- :param n: 1-indexed rank. Scalar int or tensor of ints broadcastable
2845
- to tensor with dim removed.
2846
- :return: Threshold with size 1 along dim, same dtype/device.
2847
  """
2848
- # -------------------------------------------------------------------------
2849
- # Algorithm overview
2850
- # -------------------------------------------------------------------------
2851
- #
2852
- # Scalar n does not need a full sorted table. kthvalue selects the n-th
2853
- # rank directly, and the two boundary sentinels are returned explicitly.
2854
- #
2855
- # Tensor n requires a full sorted table because each position along the
2856
- # complementary dimensions may request a different rank. The table is
2857
- # built once by sorting descending, then sentinel values are padded at
2858
- # both ends so that boundary n values resolve correctly via gather:
2859
- #
2860
- # index 0 <- +inf sentinel (n == 0)
2861
- # index 1..dim_length <- sorted values (valid ranks, 1-indexed)
2862
- # index dim_length+1 <- -inf sentinel (n > dim_length)
2863
- #
2864
- # The critical invariant is that n is 1-indexed. This means valid ranks
2865
- # map directly to their gather index without any offset, and index 0 is
2866
- # naturally free for the +inf sentinel. n == 0 gathers +inf without
2867
- # special-casing, and overflow n gathers -inf after clamping.
2868
- #
2869
- # F.pad specifies padding from the last dimension inward. Targeting an
2870
- # arbitrary dim requires a positive index to compute how many trailing
2871
- # dimensions to skip over in the pad spec.
2872
  positive_dim = dim % tensor.ndim
2873
  dim_length = tensor.shape[positive_dim]
 
2874
 
2875
- if isinstance(n, int):
2876
- # Scalar rank selection does not need a full sorted table. kthvalue
2877
- # finds the k-th smallest; negating input and output flips the order
2878
- # to give the k-th largest. Boundary sentinels follow the descending
2879
- # contract: +inf sits above every real value (nothing qualifies),
2880
- # -inf sits below every real value (everything qualifies).
2881
- if n == 0:
2882
- shape = list(tensor.shape)
2883
- shape[positive_dim] = 1
2884
- return tensor.new_full(shape, float('inf'))
2885
- if n > dim_length:
2886
- shape = list(tensor.shape)
2887
- shape[positive_dim] = 1
2888
- return tensor.new_full(shape, float('-inf'))
2889
- return -torch.kthvalue(-tensor, n, dim=dim, keepdim=True).values
2890
 
 
 
 
 
 
 
2891
  else:
2892
- # Build the rank table once; each position gathers its own threshold.
2893
- sorted_desc = torch.sort(tensor, dim=dim, descending=True).values
2894
-
2895
- # Each trailing dimension after positive_dim contributes one (left,
2896
- # right) zero-pair before the target padding entry in the F.pad spec.
2897
- num_padding_skips = 2 * (tensor.ndim - positive_dim - 1)
2898
- leading_pad = [0] * num_padding_skips + [1, 0]
2899
- trailing_pad = [0] * num_padding_skips + [0, 1]
2900
-
2901
- sorted_desc = F.pad(sorted_desc, leading_pad, value=float('inf'))
2902
- sorted_desc = F.pad(sorted_desc, trailing_pad, value=float('-inf'))
2903
-
2904
- # unsqueeze restores the reduced dimension so gather sees the same
2905
- # rank as the padded table along dim.
2906
- gather_index = n.clamp(0, dim_length + 1).long().unsqueeze(dim)
2907
- return sorted_desc.gather(dim, gather_index)
2908
  @staticmethod
2909
  def _check_bidding_converged(converged: torch.Tensor, max_rounds: int) -> None:
2910
  """Raise if the bidding loop exhausted max_rounds without satisfying all tokens.
@@ -2935,12 +2906,14 @@ class MoSRAHRouter(nn.Module):
2935
  f"Increase mosrah_overallocation_factor or max_bid_rounds."
2936
  )
2937
 
2938
- @staticmethod
2939
  def _run_bidding(
 
2940
  logits: torch.Tensor,
2941
  remaining_capacity: int | torch.Tensor,
2942
  min_choices: int,
2943
  max_rounds: int,
 
2944
  ) -> torch.Tensor:
2945
  """Deferred-acceptance (Gale-Shapley) bidding solver for joint capacity enforcement.
2946
 
@@ -2960,6 +2933,8 @@ class MoSRAHRouter(nn.Module):
2960
  min_choices: Minimum experts each token must have accepted (K).
2961
  max_rounds: Iteration ceiling; raises via ``_check_bidding_converged``
2962
  if exhausted.
 
 
2963
 
2964
  Returns:
2965
  accepted: (B, N, L) bool — True at positions accepted by the solver.
@@ -2984,18 +2959,13 @@ class MoSRAHRouter(nn.Module):
2984
  # Tokens with fewer than min_choices accepted experts propose their
2985
  # next-best unproposed expert(s). The deficit determines how many new
2986
  # proposals each token makes this round; already-satisfied tokens
2987
- # propose nothing (deficit = 0 → bid_threshold = +inf → no new bids).
2988
  accepted_per_token = acceptances.sum(dim=-1) # (B, N)
2989
  choices_deficit = (min_choices - accepted_per_token).clamp_min(0)
2990
 
2991
  unproposed_logits = logits.masked_fill(proposals, float('-inf'))
2992
- bid_threshold = MoSRAHRouter.get_threshold(
2993
- unproposed_logits, dim=-1, n=choices_deficit,
2994
- )
2995
- new_proposals = (
2996
- (unproposed_logits >= bid_threshold)
2997
- & ~proposals
2998
- & (choices_deficit.unsqueeze(-1) > 0)
2999
  )
3000
  updated_proposals = proposals | new_proposals
3001
 
@@ -3005,10 +2975,9 @@ class MoSRAHRouter(nn.Module):
3005
  # Acceptances are recomputed from scratch each round so that a
3006
  # stronger new proposal can displace a weaker prior one.
3007
  proposed_logits = logits.masked_fill(~updated_proposals, float('-inf'))
3008
- accept_threshold = MoSRAHRouter.get_threshold(
3009
- proposed_logits, dim=-2, n=remaining_capacity,
3010
  )
3011
- updated_acceptances = updated_proposals & (proposed_logits >= accept_threshold)
3012
 
3013
  return updated_proposals, updated_acceptances, round_count + 1
3014
 
@@ -3017,7 +2986,7 @@ class MoSRAHRouter(nn.Module):
3017
  )
3018
 
3019
  converged = (acceptances.sum(dim=-1) >= min_choices).all()
3020
- MoSRAHRouter._check_bidding_converged(converged, max_rounds)
3021
  return acceptances
3022
 
3023
  @classmethod
@@ -3101,8 +3070,7 @@ class MoSRAHRouter(nn.Module):
3101
  # Mask computation runs under no_grad: the boolean mask is a hard routing
3102
  # decision and must not accumulate gradient memory through the solver.
3103
  with torch.no_grad():
3104
- col_threshold = cls.get_threshold(logits, dim=-2, n=remaining_capacity)
3105
- col_capacity_mask = logits >= col_threshold # (B, N, L)
3106
  if (col_capacity_mask.sum(dim=-1) >= min_choices).all():
3107
  return logits.masked_fill(~col_capacity_mask, mask_value)
3108
 
@@ -3110,7 +3078,7 @@ class MoSRAHRouter(nn.Module):
3110
  # enough that per-expert capacity limits leave some tokens with fewer
3111
  # than min_choices choices. The bidding solver handles this jointly.
3112
  with torch.no_grad():
3113
- accepted = cls._run_bidding(logits, remaining_capacity, min_choices, max_rounds)
3114
  return logits.masked_fill(~accepted, mask_value)
3115
  def forward(
3116
  self,
@@ -3352,7 +3320,10 @@ class MoSRAHLayer(nn.Module):
3352
  def __init__(self, config: ShramConfig) -> None:
3353
  super().__init__()
3354
  self.num_experts = config.num_mosrah_heads
3355
- self.packed_length = config.mosrah_packed_length
 
 
 
3356
 
3357
  self.router = MoSRAHRouter(config)
3358
  self.positions = SparseMoSRAHPositions(config)
@@ -3621,7 +3592,7 @@ class DecoderLayer(nn.Module):
3621
  self.mlp_norm = nn.RMSNorm(config.embedding_width, eps=config.rms_norm_eps)
3622
  self.attention = SHRAMHybridLayer(config)
3623
  self.mlp = SwiGLUMLP(config)
3624
- self.residual_gate = nn.Parameter(torch.zeros([config.embedding_width]))
3625
  def num_mosrah_parameters(self) -> int:
3626
  """Return the total number of trainable MoSRAH parameters in this decoder layer."""
3627
  return self.attention.num_mosrah_parameters()
 
2825
  self.expert_bias = nn.Parameter(torch.zeros(config.num_mosrah_heads))
2826
 
2827
  @staticmethod
2828
+ def get_mask(
2829
  tensor: torch.Tensor,
2830
  dim: int,
2831
  n: int | torch.Tensor,
2832
+ capacity_scalar: int,
2833
  ) -> torch.Tensor:
2834
+ """Return a boolean mask selecting the top-n entries along dim.
 
2835
 
2836
+ Uses topk to select exactly min(n_per_slice, dim_length) True entries
2837
+ per slice along dim. Unlike a threshold comparison, this never
2838
+ over-selects under tied logit values, which occurs when padding tokens
2839
+ contribute identical scores to multiple expert slots.
2840
 
2841
+ Args:
2842
+ tensor: Input tensor. Higher values rank first.
2843
+ dim: Dimension to select along.
2844
+ n: Per-slice selection count. Scalar int or tensor broadcastable
2845
+ to tensor with dim removed. Slices where n=0 produce all-False
2846
+ outputs.
2847
+ capacity_scalar: Static upper bound on n; used to derive topk k as
2848
+ min(tensor.shape[dim], capacity_scalar). Must be a Python int
2849
+ for compile compatibility.
2850
 
2851
+ Returns:
2852
+ Boolean mask of the same shape as tensor.
 
 
 
2853
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2854
  positive_dim = dim % tensor.ndim
2855
  dim_length = tensor.shape[positive_dim]
2856
+ k = min(dim_length, capacity_scalar)
2857
 
2858
+ topk_indices = tensor.topk(k, dim=dim).indices
2859
+
2860
+ # Rank tensor broadcast-compatible with topk_indices: rank r along dim
2861
+ # corresponds to the (r+1)-th highest value in that slice.
2862
+ rank_shape = [1] * tensor.ndim
2863
+ rank_shape[positive_dim] = k
2864
+ ranks = torch.arange(k, device=tensor.device, dtype=torch.long).view(rank_shape)
 
 
 
 
 
 
 
 
2865
 
2866
+ # element_included: True where this rank falls within the per-slice budget.
2867
+ # For scalar n all k ranks satisfy rank < n (since k = min(dim_length, n)).
2868
+ # For tensor n per-slice budgets differ; rank >= n[slice] yields False,
2869
+ # correctly excluding excess slots including those with n=0.
2870
+ if isinstance(n, int):
2871
+ element_included = ranks < n
2872
  else:
2873
+ element_included = ranks < n.unsqueeze(positive_dim)
2874
+
2875
+ mask = torch.zeros_like(tensor, dtype=torch.bool)
2876
+ mask.scatter_(dim, topk_indices, element_included.expand_as(topk_indices))
2877
+ return mask
2878
+
 
 
 
 
 
 
 
 
 
 
2879
  @staticmethod
2880
  def _check_bidding_converged(converged: torch.Tensor, max_rounds: int) -> None:
2881
  """Raise if the bidding loop exhausted max_rounds without satisfying all tokens.
 
2906
  f"Increase mosrah_overallocation_factor or max_bid_rounds."
2907
  )
2908
 
2909
+ @classmethod
2910
  def _run_bidding(
2911
+ cls,
2912
  logits: torch.Tensor,
2913
  remaining_capacity: int | torch.Tensor,
2914
  min_choices: int,
2915
  max_rounds: int,
2916
+ capacity_scalar: int,
2917
  ) -> torch.Tensor:
2918
  """Deferred-acceptance (Gale-Shapley) bidding solver for joint capacity enforcement.
2919
 
 
2933
  min_choices: Minimum experts each token must have accepted (K).
2934
  max_rounds: Iteration ceiling; raises via ``_check_bidding_converged``
2935
  if exhausted.
2936
+ capacity_scalar: Static upper bound on remaining_capacity, passed to
2937
+ ``get_mask`` as the topk k bound for the acceptance step.
2938
 
2939
  Returns:
2940
  accepted: (B, N, L) bool — True at positions accepted by the solver.
 
2959
  # Tokens with fewer than min_choices accepted experts propose their
2960
  # next-best unproposed expert(s). The deficit determines how many new
2961
  # proposals each token makes this round; already-satisfied tokens
2962
+ # propose nothing (deficit = 0 → get_mask returns all-False).
2963
  accepted_per_token = acceptances.sum(dim=-1) # (B, N)
2964
  choices_deficit = (min_choices - accepted_per_token).clamp_min(0)
2965
 
2966
  unproposed_logits = logits.masked_fill(proposals, float('-inf'))
2967
+ new_proposals = cls.get_mask(
2968
+ unproposed_logits, dim=-1, n=choices_deficit, capacity_scalar=min_choices,
 
 
 
 
 
2969
  )
2970
  updated_proposals = proposals | new_proposals
2971
 
 
2975
  # Acceptances are recomputed from scratch each round so that a
2976
  # stronger new proposal can displace a weaker prior one.
2977
  proposed_logits = logits.masked_fill(~updated_proposals, float('-inf'))
2978
+ updated_acceptances = cls.get_mask(
2979
+ proposed_logits, dim=-2, n=remaining_capacity, capacity_scalar=capacity_scalar,
2980
  )
 
2981
 
2982
  return updated_proposals, updated_acceptances, round_count + 1
2983
 
 
2986
  )
2987
 
2988
  converged = (acceptances.sum(dim=-1) >= min_choices).all()
2989
+ cls._check_bidding_converged(converged, max_rounds)
2990
  return acceptances
2991
 
2992
  @classmethod
 
3070
  # Mask computation runs under no_grad: the boolean mask is a hard routing
3071
  # decision and must not accumulate gradient memory through the solver.
3072
  with torch.no_grad():
3073
+ col_capacity_mask = cls.get_mask(logits, dim=-2, n=remaining_capacity, capacity_scalar=capacity)
 
3074
  if (col_capacity_mask.sum(dim=-1) >= min_choices).all():
3075
  return logits.masked_fill(~col_capacity_mask, mask_value)
3076
 
 
3078
  # enough that per-expert capacity limits leave some tokens with fewer
3079
  # than min_choices choices. The bidding solver handles this jointly.
3080
  with torch.no_grad():
3081
+ accepted = cls._run_bidding(logits, remaining_capacity, min_choices, max_rounds, capacity)
3082
  return logits.masked_fill(~accepted, mask_value)
3083
  def forward(
3084
  self,
 
3320
  def __init__(self, config: ShramConfig) -> None:
3321
  super().__init__()
3322
  self.num_experts = config.num_mosrah_heads
3323
+ if config.use_cache:
3324
+ self.packed_length = config.mosrah_cache_length
3325
+ else:
3326
+ self.packed_length = config.mosrah_packed_length
3327
 
3328
  self.router = MoSRAHRouter(config)
3329
  self.positions = SparseMoSRAHPositions(config)
 
3592
  self.mlp_norm = nn.RMSNorm(config.embedding_width, eps=config.rms_norm_eps)
3593
  self.attention = SHRAMHybridLayer(config)
3594
  self.mlp = SwiGLUMLP(config)
3595
+ self.residual_gate = nn.Parameter(1e-6*torch.randn([config.embedding_width]))
3596
  def num_mosrah_parameters(self) -> int:
3597
  """Return the total number of trainable MoSRAH parameters in this decoder layer."""
3598
  return self.attention.num_mosrah_parameters()