Add files via upload

This commit is contained in:
Extraltodeus
2025-02-10 21:30:58 +01:00
committed by GitHub
parent 55524ea00c
commit 7e7f012d38
+46 -22
View File
@@ -2,11 +2,38 @@ from comfy.ldm.modules.attention import optimized_attention
from comfy.model_management import interrupt_current_processing
from copy import deepcopy
import torch
import math
def scaled_dot_product_attention_with_negative(query, key, value, negative_key=None, negative_value=None, negative_strength=1, attn_mask=None, is_causal=False, scale=None, renorm=False) -> torch.Tensor:
L, S = query.size(-2), key.size(-2)
scale_factor = 1 / math.sqrt(query.size(-1)) if scale is None else scale
attn_bias = torch.zeros(L, S, dtype=query.dtype, device=query.device)
if is_causal:
assert attn_mask is None
temp_mask = torch.ones(L, S, dtype=torch.bool).tril(diagonal=0)
attn_bias.masked_fill_(temp_mask.logical_not(), float("-inf"))
attn_bias.to(query.dtype)
if attn_mask is not None:
if attn_mask.dtype == torch.bool:
attn_bias.masked_fill_(attn_mask.logical_not(), float("-inf")).to(query.device)
else:
attn_bias += attn_mask
attn_weight = query @ key.transpose(-2, -1) * scale_factor
negative_attn_weight = query @ negative_key.transpose(-2, -1) * scale_factor
diff_val = (torch.softmax(attn_weight+attn_bias, dim=-1) @ value) - (torch.softmax(negative_attn_weight+attn_bias, dim=-1) @ negative_value)
proj = torch.nn.Linear(diff_val.size(-1), S, bias=False, dtype=diff_val.dtype).to(diff_val.device)
diff = proj(diff_val)
attn_weight = (attn_weight + attn_bias + diff * negative_strength).softmax(dim=-1)
return attn_weight @ value
class attention_patch():
def __init__(self, renorm, negative_strength):
def __init__(self, negative_strength):
self.negative_strength = negative_strength
self.renorm = renorm
def attention_with_negative(self, q, k, v, extra_options, mask=None, attn_precision=None):
heads = extra_options if isinstance(extra_options, int) else extra_options['n_heads']
@@ -14,24 +41,22 @@ class attention_patch():
if k.shape[-2] // 77 <= 1:
return optimized_attention(q, k, v, heads, mask, attn_precision)
negative_k = k[:, k.size(-2)//2:,:]
negative_v = v[:, v.size(-2)//2:,:]
b, _, dim_head = q.shape
dim_head //= heads
q, k, v = map(
lambda t: t.view(b, -1, heads, dim_head).transpose(1, 2),
(q, k, v),
)
out_pos = optimized_attention(q, k[:,:k.size(-2)//2 ,:], v[:,:v.size(-2)//2 ,:],heads,mask,attn_precision)
negative_k = k[:,:, k.size(-2)//2:,:]
negative_v = v[:,:, k.size(-2)//2:,:]
if self.negative_strength == 0:
return out_pos
out_neg = optimized_attention(q, negative_k, negative_v, heads, mask, attn_precision)
if self.renorm:
out_norm = out_pos.norm()
out = out_pos + (out_pos - out_neg) * self.negative_strength
if self.renorm:
out = out * out_norm / out.norm()
scale = 1 / math.sqrt(q.size(-1))
out = scaled_dot_product_attention_with_negative(q, k[:,:,:k.size(-2)//2 ,:], v[:,:,:v.size(-2)//2 ,:], negative_key=negative_k, negative_value=negative_v, negative_strength=self.negative_strength, attn_mask=mask, scale=scale)
out = (
out.transpose(1, 2).reshape(b, -1, heads * dim_head)
)
return out
def nan_interrupt_patch(model):
@@ -50,8 +75,7 @@ class NegativeAttentionPatchNode:
def INPUT_TYPES(s):
return {"required": {
"model": ("MODEL",),
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 1/10, "round":1/1000}),
"rescale_after": ("BOOLEAN", {"default": False, "tooltip": "Ensures that the scale of the output is the same as before taking the difference.\nThis can fix over bright/dark results and help to raise the scale higher."}),
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 1/4, "round":1/1000}),
}
}
@@ -62,14 +86,14 @@ class NegativeAttentionPatchNode:
CATEGORY = "model_patches/negative attention"
def patch(self, model, strength, rescale_after):
def patch(self, model, strength):
m = model.clone()
m = nan_interrupt_patch(m) # high scales can cause black images, let's not sample this.
m = nan_interrupt_patch(m)
levels = ["input","middle","output"]
layer_names = [[l, n, True] for l in levels for n in range(12)]
patch = attention_patch(negative_strength=strength, renorm=rescale_after)
patch = attention_patch(negative_strength=strength)
for current_level, b_number, toggle in layer_names:
m.set_model_attn2_replace(patch.attention_with_negative, current_level, b_number)