Batch cached Anima Qwen suffixes

This commit is contained in:
野生の男 2026-07-17 03:37:42 +09:00
parent d0ba49e2c9
commit c26d8e6188
3 changed files with 57 additions and 23 deletions

View File

@ -90,23 +90,19 @@ def forward(transformer, cache_owner, tokens, attention_mask, embeds, num_tokens
suffix_embeds = embeds[:, common:]
if common > 0 and suffix_embeds.shape[1] > 1:
suffix_outputs = []
next_key_values = past_key_values
for index in range(suffix_embeds.shape[1]):
output = transformer(
None,
None,
embeds=suffix_embeds[:, index:index + 1],
num_tokens=[1],
intermediate_output=intermediate_output,
final_layer_norm_intermediate=final_layer_norm_intermediate,
dtype=dtype,
embeds_info=embeds_info,
past_key_values=next_key_values,
)
suffix_outputs.append(output[0])
next_key_values = output[2]
suffix_hidden = torch.cat(suffix_outputs, dim=1)
output = transformer(
None,
None,
embeds=suffix_embeds,
num_tokens=[suffix_embeds.shape[1]],
intermediate_output=intermediate_output,
final_layer_norm_intermediate=final_layer_norm_intermediate,
dtype=dtype,
embeds_info=embeds_info,
past_key_values=past_key_values,
)
suffix_hidden = output[0]
next_key_values = output[2]
else:
output = transformer(
None,

View File

@ -737,7 +737,7 @@ class Llama2_(nn.Module):
mask = mask.masked_fill(mask.to(torch.bool), torch.finfo(x.dtype).min / 4)
if seq_len > 1:
causal_mask = torch.empty(past_len + seq_len, past_len + seq_len, dtype=x.dtype, device=x.device).fill_(torch.finfo(x.dtype).min / 4).triu_(1)
causal_mask = torch.empty(seq_len, past_len + seq_len, dtype=x.dtype, device=x.device).fill_(torch.finfo(x.dtype).min / 4).triu_(past_len + 1)
if mask is not None:
mask += causal_mask
else:

View File

@ -3,7 +3,7 @@ import asyncio
import pytest
import torch
from comfy.text_encoders import anima_cache
from comfy.text_encoders import anima_cache, llama
def tokens(*ids):
@ -18,8 +18,6 @@ class TinyCausalTransformer:
def __call__(self, _, attention_mask, embeds, num_tokens, intermediate_output, final_layer_norm_intermediate, dtype, embeds_info, past_key_values=None):
self.calls.append((embeds.shape[1], past_key_values is not None))
self.num_tokens.append(tuple(num_tokens))
if past_key_values and embeds.shape[1] > 1:
raise RuntimeError("cached multi-token suffix would use an invalid causal mask")
prefix = 0
if past_key_values:
prefix = past_key_values[0][2]
@ -124,8 +122,48 @@ def test_diverging_suffix_matches_full_causal_forward(prefix_cache):
output, _ = cached_forward(transformer, [1, 2, 4, 5], owner=owner)
assert torch.equal(output, torch.tensor([[[1.0], [3.0], [7.0], [12.0]]]))
assert transformer.calls == [(3, True), (1, True), (1, True)]
assert transformer.num_tokens == [(3,), (1,), (1,)]
assert transformer.calls == [(3, True), (2, True)]
assert transformer.num_tokens == [(3,), (2,)]
cached = prefix_cache[owner]
assert cached[2] == (1, 2, 4, 5)
assert torch.equal(cached[3], output)
key, value, length = cached[4][0]
assert length == 4
assert torch.equal(key.flatten(), torch.tensor([1.0, 2.0, 4.0, 5.0]))
assert torch.equal(value.flatten(), torch.tensor([2.0, 4.0, 8.0, 10.0]))
def test_llama_cached_multi_token_causal_mask_uses_absolute_positions(monkeypatch):
masks = []
class Layer:
def __call__(self, x, attention_mask, freqs_cis, optimized_attention, past_key_value):
masks.append(attention_mask.clone())
return x, None
class Model:
layers = (Layer(),)
norm = None
def get_past_len(self, past_key_values):
return past_key_values[0][2]
def compute_freqs_cis(self, position_ids, device):
return None
monkeypatch.setattr(llama, "optimized_attention_for_device", lambda *args, **kwargs: None)
x = torch.zeros((1, 2, 1))
past_key_values = [(torch.empty((1, 1, 3, 1)), torch.empty((1, 1, 3, 1)), 3)]
llama.Llama2_.forward(Model(), None, embeds=x, past_key_values=past_key_values)
blocked = torch.finfo(x.dtype).min / 4
expected = torch.tensor([
[0.0, 0.0, 0.0, 0.0, blocked],
[0.0, 0.0, 0.0, 0.0, 0.0],
])
assert masks[0].shape == (2, 5)
assert torch.equal(masks[0], expected)
def test_cache_is_isolated_by_owner_and_transformer_identity(prefix_cache):