Update modeling_neollm.py
Browse files- 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 |
-
|
| 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 |
-
|
| 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 =
|
| 1986 |
-
prefix =
|
| 1987 |
|
| 1988 |
-
if window_left is None or window_left < 0 or window_left >= key_len:
|
| 1989 |
sums_all = prefix
|
| 1990 |
else:
|
| 1991 |
-
|
| 1992 |
-
|
| 1993 |
-
|
| 1994 |
-
|
| 1995 |
-
|
| 1996 |
-
|
| 1997 |
-
|
|
|
|
|
|
|
| 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 |
-
#
|
| 2297 |
-
#
|
| 2298 |
-
|
| 2299 |
-
|
| 2300 |
-
|
| 2301 |
-
|
| 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 |
-
|
| 2309 |
-
|
| 2310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3867 |
window_left = window[0] if isinstance(window, tuple) else None
|
| 3868 |
if is_packed:
|
| 3869 |
-
|
| 3870 |
-
|
| 3871 |
-
|
| 3872 |
-
|
| 3873 |
-
|
| 3874 |
-
value_expanded[:, :, start:end, :],
|
| 3875 |
-
query_len=int(_seg_len),
|
| 3876 |
-
window_left=window_left,
|
| 3877 |
-
)
|
| 3878 |
-
start = end
|
| 3879 |
else:
|
| 3880 |
-
|
| 3881 |
-
|
| 3882 |
query_len=SP,
|
| 3883 |
window_left=window_left,
|
| 3884 |
-
)
|
| 3885 |
-
|
| 3886 |
-
alpha_t
|
| 3887 |
-
beta_t
|
| 3888 |
-
|
| 3889 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|