KitsuVp commited on
Commit
53deef0
·
verified ·
1 Parent(s): 5d2ae2a

Update modeling_neollm.py

Browse files
Files changed (1) hide show
  1. modeling_neollm.py +122 -46
modeling_neollm.py CHANGED
@@ -1971,30 +1971,36 @@ def _masked_value_sum_from_pairs(
1971
 
1972
 
1973
  def _causal_value_sum(
1974
- value_expanded: torch.Tensor,
1975
  query_len: int,
1976
  window_left: Optional[int] = None,
1977
  ) -> torch.Tensor:
1978
  """
1979
  Compute Σ_{j∈A(i)} V_j for causal or sliding-window causal attention.
1980
 
1981
- value_expanded: [B,H,Sk,d]. The returned tensor is [B,H,Sq,d].
1982
  For cached decoding, Sq may be smaller than Sk; query rows are aligned to
1983
  the last Sq key positions, matching causal self-attention with cache.
 
 
 
 
1984
  """
1985
- key_len = value_expanded.shape[2]
1986
- prefix = value_expanded.cumsum(dim=2) # [B,H,Sk,d]
1987
 
1988
- if window_left is None or window_left < 0 or window_left >= key_len:
1989
  sums_all = prefix
1990
  else:
1991
- # Valid keys for absolute position i are max(0, i-window_left) ... i.
1992
- prefix_padded = F.pad(prefix, (0, 0, 1, 0)) # [B,H,Sk+1,d]
1993
- end_idx = torch.arange(key_len, device=value_expanded.device) + 1
1994
- start_idx = torch.clamp(end_idx - (int(window_left) + 1), min=0)
1995
- end_vals = prefix_padded.index_select(2, end_idx)
1996
- start_vals = prefix_padded.index_select(2, start_idx)
1997
- sums_all = end_vals - start_vals
 
 
1998
 
1999
  if query_len == key_len:
2000
  return sums_all
@@ -2002,6 +2008,41 @@ def _causal_value_sum(
2002
  return sums_all[:, :, start:start + query_len, :]
2003
 
2004
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2005
  def _affine_valid_value_sum(
2006
  value_expanded: torch.Tensor,
2007
  attention_mask: Optional[torch.Tensor],
@@ -2293,24 +2334,49 @@ def affine_scaled_flash_attention_forward(
2293
  # flash_out: [B, S, H_q, d_head] — HF wrappers all return this layout
2294
 
2295
  # ── Term 2: β · Σ_{j∈A(i)} V_j ──────────────────────────────────────
2296
- # Expand V from KV heads to query heads for GQA, then sum only over the
2297
- # same valid keys used by the backend attention path.
2298
- value_expanded = repeat_kv(value, module.num_key_value_groups) # [B, H_q, S_k, d_head]
2299
- value_sum = _affine_valid_value_sum(
2300
- value_expanded,
2301
- attention_mask=attention_mask,
2302
- query_len=query.shape[-2],
2303
- sliding_window=kwargs.get("sliding_window", None),
2304
- ) # [B, H_q, S_q, d_head]
2305
- # Transpose to match flash_out layout.
2306
- v_cumsum_t = value_sum.transpose(1, 2).contiguous() # [B, S_q, H_q, d_head]
2307
 
2308
- # α, β: [B, H_q, S, 1] → [B, S, H_q, 1] to broadcast over d_head.
2309
- alpha_t = alpha.permute(0, 2, 1, 3) # [B, S, H_q, 1]
2310
- beta_t = beta.permute(0, 2, 1, 3) # [B, S, H_q, 1]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2311
 
2312
  # ── Combine and apply dropout to the full affine output ───────────────
2313
- output = alpha_t * flash_out + beta_t * v_cumsum_t # [B, S, H_q, d_head]
2314
  output = nn.functional.dropout(output, p=dropout, training=module.training)
2315
 
2316
  # attn_weights is None — flash never exposes the softmax weight matrix.
@@ -3863,30 +3929,40 @@ class NeoLLMAttention(nn.Module):
3863
  # α, β arrive pre-expanded to [B, H_q, S·P, 1]; permute to [B,S·P,H,1]
3864
  # to match the flash output layout [B, S·P, H_q, d].
3865
  if use_affine:
3866
- value_expanded = repeat_kv(v, self.num_key_value_groups) # [B,H_q,S·P,d]
 
 
 
 
3867
  window_left = window[0] if isinstance(window, tuple) else None
3868
  if is_packed:
3869
- value_sum = torch.empty_like(value_expanded)
3870
- start = 0
3871
- for _seg_len in doc_lengths_p.to(dtype=torch.long).tolist():
3872
- end = start + int(_seg_len)
3873
- value_sum[:, :, start:end, :] = _causal_value_sum(
3874
- value_expanded[:, :, start:end, :],
3875
- query_len=int(_seg_len),
3876
- window_left=window_left,
3877
- )
3878
- start = end
3879
  else:
3880
- value_sum = _causal_value_sum(
3881
- value_expanded,
3882
  query_len=SP,
3883
  window_left=window_left,
3884
- )
3885
- v_cumsum_t = value_sum.transpose(1, 2).contiguous() # [B,S·P,H_q,d]
3886
- alpha_t = alpha.permute(0, 2, 1, 3) # [B,S·P,H_q,1]
3887
- beta_t = beta.permute(0, 2, 1, 3) # [B,S·P,H_q,1]
3888
- attn_out = alpha_t * flash_out + beta_t * v_cumsum_t # [B,S·P,H_q,d]
3889
- attn_out = torch.nn.functional.dropout(
 
 
 
 
 
 
 
 
 
 
 
3890
  attn_out, p=dropout, training=self.training
3891
  )
3892
  else:
 
1971
 
1972
 
1973
  def _causal_value_sum(
1974
+ value_states: torch.Tensor,
1975
  query_len: int,
1976
  window_left: Optional[int] = None,
1977
  ) -> torch.Tensor:
1978
  """
1979
  Compute Σ_{j∈A(i)} V_j for causal or sliding-window causal attention.
1980
 
1981
+ value_states: [B,H,Sk,d]. The returned tensor is [B,H,Sq,d].
1982
  For cached decoding, Sq may be smaller than Sk; query rows are aligned to
1983
  the last Sq key positions, matching causal self-attention with cache.
1984
+
1985
+ The sliding-window path uses prefix differences with slices, not padded
1986
+ gathers. This keeps the operation exact while avoiding extra index tensors
1987
+ and repeated index_select calls in the hot path.
1988
  """
1989
+ key_len = value_states.shape[2]
1990
+ prefix = value_states.cumsum(dim=2) # [B,H,Sk,d]
1991
 
1992
+ if window_left is None or window_left < 0 or int(window_left) >= key_len:
1993
  sums_all = prefix
1994
  else:
1995
+ left = int(window_left)
1996
+ sums_all = prefix.clone()
1997
+ if left + 1 < key_len:
1998
+ # For i > left:
1999
+ # sum_{j=i-left}^{i} V_j = prefix[i] - prefix[i-left-1].
2000
+ # The first left+1 rows are already the causal prefixes.
2001
+ sums_all[:, :, left + 1 :, :] = (
2002
+ prefix[:, :, left + 1 :, :] - prefix[:, :, : -(left + 1), :]
2003
+ )
2004
 
2005
  if query_len == key_len:
2006
  return sums_all
 
2008
  return sums_all[:, :, start:start + query_len, :]
2009
 
2010
 
2011
+ def _segmented_causal_value_sum(
2012
+ value_states: torch.Tensor,
2013
+ segment_lengths: torch.Tensor,
2014
+ window_left: Optional[int] = None,
2015
+ ) -> torch.Tensor:
2016
+ """
2017
+ Compute segmented causal/window value sums without a Python loop over
2018
+ documents. Used only for packed IHA where B=1 and position_ids reset.
2019
+
2020
+ value_states: [1,H,Sk,d]
2021
+ segment_lengths: [num_segments] lengths in the same expanded sequence axis
2022
+ return: [1,H,Sk,d]
2023
+ """
2024
+ key_len = value_states.shape[2]
2025
+ if key_len == 0:
2026
+ return value_states
2027
+
2028
+ lengths = segment_lengths.to(device=value_states.device, dtype=torch.long)
2029
+ seg_ends = lengths.cumsum(0)
2030
+ seg_starts = seg_ends - lengths
2031
+ seg_start_per_pos = torch.repeat_interleave(seg_starts, lengths)
2032
+
2033
+ pos = torch.arange(key_len, device=value_states.device, dtype=torch.long)
2034
+ if window_left is None or window_left < 0:
2035
+ start_idx = seg_start_per_pos
2036
+ else:
2037
+ start_idx = torch.maximum(seg_start_per_pos, pos - int(window_left))
2038
+ end_idx = pos + 1
2039
+
2040
+ prefix = F.pad(value_states.cumsum(dim=2), (0, 0, 1, 0)) # [1,H,Sk+1,d]
2041
+ end_vals = prefix.index_select(2, end_idx)
2042
+ start_vals = prefix.index_select(2, start_idx)
2043
+ return end_vals - start_vals
2044
+
2045
+
2046
  def _affine_valid_value_sum(
2047
  value_expanded: torch.Tensor,
2048
  attention_mask: Optional[torch.Tensor],
 
2334
  # flash_out: [B, S, H_q, d_head] — HF wrappers all return this layout
2335
 
2336
  # ── Term 2: β · Σ_{j∈A(i)} V_j ──────────────────────────────────────
2337
+ # Fast path: compute the valid-key value sum in KV-head space, then
2338
+ # broadcast it to query heads during the final affine combine. Since the
2339
+ # sum is linear, WindowSum(repeat_kv(V)) == repeat_kv(WindowSum(V)); this
2340
+ # avoids doing cumsum/window arithmetic on the expanded H_q heads.
2341
+ alpha_t = alpha.permute(0, 2, 1, 3) # [B, S_q, H_q, 1]
2342
+ beta_t = beta.permute(0, 2, 1, 3) # [B, S_q, H_q, 1]
 
 
 
 
 
2343
 
2344
+ if attention_mask is not None and attention_mask.ndim == 4:
2345
+ # Arbitrary 4D additive masks may be expressed in query-head space, so
2346
+ # keep the exact fallback on repeated V. This path is not the regular
2347
+ # causal/window hot path.
2348
+ value_expanded = repeat_kv(value, module.num_key_value_groups)
2349
+ value_sum = _affine_valid_value_sum(
2350
+ value_expanded,
2351
+ attention_mask=attention_mask,
2352
+ query_len=query.shape[-2],
2353
+ sliding_window=kwargs.get("sliding_window", None),
2354
+ )
2355
+ v_sum_t = value_sum.transpose(1, 2) # [B,S_q,H_q,d]
2356
+ output = alpha_t * flash_out + beta_t * v_sum_t
2357
+ else:
2358
+ value_sum_kv = _affine_valid_value_sum(
2359
+ value,
2360
+ attention_mask=attention_mask,
2361
+ query_len=query.shape[-2],
2362
+ sliding_window=kwargs.get("sliding_window", None),
2363
+ ) # [B,H_kv,S_q,d]
2364
+ v_sum_t = value_sum_kv.transpose(1, 2) # [B,S_q,H_kv,d]
2365
+ H_q = flash_out.shape[2]
2366
+ H_kv = v_sum_t.shape[2]
2367
+ groups = module.num_key_value_groups
2368
+ if groups == 1 or H_q == H_kv:
2369
+ output = alpha_t * flash_out + beta_t * v_sum_t
2370
+ else:
2371
+ Bsz, Sq, _, Dh = flash_out.shape
2372
+ flash_g = flash_out.reshape(Bsz, Sq, H_kv, groups, Dh)
2373
+ alpha_g = alpha_t.reshape(Bsz, Sq, H_kv, groups, 1)
2374
+ beta_g = beta_t.reshape(Bsz, Sq, H_kv, groups, 1)
2375
+ output = (alpha_g * flash_g + beta_g * v_sum_t.unsqueeze(3)).reshape(
2376
+ Bsz, Sq, H_q, Dh
2377
+ )
2378
 
2379
  # ── Combine and apply dropout to the full affine output ───────────────
 
2380
  output = nn.functional.dropout(output, p=dropout, training=module.training)
2381
 
2382
  # attn_weights is None — flash never exposes the softmax weight matrix.
 
3929
  # α, β arrive pre-expanded to [B, H_q, S·P, 1]; permute to [B,S·P,H,1]
3930
  # to match the flash output layout [B, S·P, H_q, d].
3931
  if use_affine:
3932
+ # Compute the β value-sum in KV-head space first. For GQA this is
3933
+ # exact because the valid-key sum is linear and independent of the
3934
+ # repeated query-head view:
3935
+ # Sum(repeat_kv(V)) == repeat_kv(Sum(V)).
3936
+ # The broadcast to H_q happens only in the final affine combine.
3937
  window_left = window[0] if isinstance(window, tuple) else None
3938
  if is_packed:
3939
+ value_sum_kv = _segmented_causal_value_sum(
3940
+ v,
3941
+ segment_lengths=doc_lengths_p,
3942
+ window_left=window_left,
3943
+ ) # [1,H_kv,S·P,d]
 
 
 
 
 
3944
  else:
3945
+ value_sum_kv = _causal_value_sum(
3946
+ v,
3947
  query_len=SP,
3948
  window_left=window_left,
3949
+ ) # [B,H_kv,S·P,d]
3950
+
3951
+ alpha_t = alpha.permute(0, 2, 1, 3) # [B,S·P,H_q,1]
3952
+ beta_t = beta.permute(0, 2, 1, 3) # [B,S·P,H_q,1]
3953
+ value_sum_t = value_sum_kv.transpose(1, 2) # [B,S·P,H_kv,d]
3954
+
3955
+ if self.num_key_value_groups == 1 or H_q == H_kv:
3956
+ attn_out = alpha_t * flash_out + beta_t * value_sum_t
3957
+ else:
3958
+ flash_g = flash_out.reshape(B, SP, H_kv, self.num_key_value_groups, d_head)
3959
+ alpha_g = alpha_t.reshape(B, SP, H_kv, self.num_key_value_groups, 1)
3960
+ beta_g = beta_t.reshape(B, SP, H_kv, self.num_key_value_groups, 1)
3961
+ attn_out = (
3962
+ alpha_g * flash_g + beta_g * value_sum_t.unsqueeze(3)
3963
+ ).reshape(B, SP, H_q, d_head)
3964
+
3965
+ attn_out = torch.nn.functional.dropout(
3966
  attn_out, p=dropout, training=self.training
3967
  )
3968
  else: