Files
shenduldh-ComfyUI-Lightning/utils.py
T
2025-01-16 15:17:10 +08:00

427 lines
14 KiB
Python

import torch
from torch import Tensor
from comfy.ldm.flux.layers import timestep_embedding
def has_affordable_memory(device: torch.device) -> bool:
free_memory, _ = torch.cuda.mem_get_info(device)
free_memory_gb = free_memory / (1024**3)
return free_memory_gb > 24
def is_newer_than_ada_lovelace(device: torch.device) -> int:
cc_major, cc_minor = torch.cuda.get_device_capability(device)
return cc_major * 10 + cc_minor >= 89
teacache_coef = [
4.98651651e02,
-2.83781631e02,
5.58554382e01,
-3.82021401e00,
2.64230861e-01,
]
def poly1d(x):
result = torch.zeros_like(x)
for i, coeff in enumerate(teacache_coef):
result += coeff * (x ** (len(teacache_coef) - 1 - i))
return result
def are_tensors_similar(tensor1: Tensor, tensor2: Tensor, threshold: float):
assert tensor1.shape == tensor2.shape
diff_abs_mean = (tensor1 - tensor2).abs().mean()
tensor1_abs_mean = tensor1.abs().mean()
diff = diff_abs_mean / tensor1_abs_mean
return diff.item() < threshold
def skip_forward_orig(
self,
img: Tensor,
img_ids: Tensor,
txt: Tensor,
txt_ids: Tensor,
timesteps: Tensor,
y: Tensor,
guidance: Tensor = None,
control=None,
transformer_options={},
attn_mask: Tensor = None,
) -> Tensor:
patches_replace = transformer_options.get("patches_replace", {})
ds_skip_blocks = transformer_options.get("ds_skip_blocks", [])
ss_skip_blocks = transformer_options.get("ss_skip_blocks", [])
if img.ndim != 3 or txt.ndim != 3:
raise ValueError("Input img and txt tensors must have 3 dimensions.")
# running on sequences img
img = self.img_in(img)
vec = self.time_in(timestep_embedding(timesteps, 256).to(img.dtype))
if self.params.guidance_embed:
if guidance is None:
raise ValueError(
"Didn't get guidance strength for guidance distilled model."
)
vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
vec = vec + self.vector_in(y[:, : self.params.vec_in_dim])
txt = self.txt_in(txt)
ids = torch.cat((txt_ids, img_ids), dim=1)
pe = self.pe_embedder(ids)
blocks_replace = patches_replace.get("dit", {})
for i, block in enumerate(self.double_blocks):
#### skip blocks
if i in ds_skip_blocks:
continue
if ("double_block", i) in blocks_replace:
def block_wrap(args):
out = {}
out["img"], out["txt"] = block(
img=args["img"],
txt=args["txt"],
vec=args["vec"],
pe=args["pe"],
attn_mask=args.get("attn_mask"),
)
return out
out = blocks_replace[("double_block", i)](
{"img": img, "txt": txt, "vec": vec, "pe": pe, "attn_mask": attn_mask},
{"original_block": block_wrap},
)
txt = out["txt"]
img = out["img"]
else:
img, txt = block(img=img, txt=txt, vec=vec, pe=pe, attn_mask=attn_mask)
if control is not None: # Controlnet
control_i = control.get("input")
if i < len(control_i):
add = control_i[i]
if add is not None:
img += add
img = torch.cat((txt, img), 1)
for i, block in enumerate(self.single_blocks):
if ("single_block", i) in blocks_replace:
#### skip blocks
if i in ss_skip_blocks:
continue
def block_wrap(args):
out = {}
out["img"] = block(
args["img"],
vec=args["vec"],
pe=args["pe"],
attn_mask=args.get("attn_mask"),
)
return out
out = blocks_replace[("single_block", i)](
{"img": img, "vec": vec, "pe": pe, "attn_mask": attn_mask},
{"original_block": block_wrap},
)
img = out["img"]
else:
img = block(img, vec=vec, pe=pe, attn_mask=attn_mask)
if control is not None: # Controlnet
control_o = control.get("output")
if i < len(control_o):
add = control_o[i]
if add is not None:
img[:, txt.shape[1] :, ...] += add
img = img[:, txt.shape[1] :, ...]
img = self.final_layer(img, vec) # (N, T, patch_size ** 2 * out_channels)
return img
def teacache_skip_forward_orig(
self,
img: Tensor,
img_ids: Tensor,
txt: Tensor,
txt_ids: Tensor,
timesteps: Tensor,
y: Tensor,
guidance: Tensor = None,
control=None,
transformer_options={},
attn_mask: Tensor = None,
) -> Tensor:
patches_replace = transformer_options.get("patches_replace", {})
rel_l1_threshold = transformer_options.get("rel_l1_threshold")
ds_skip_blocks = transformer_options.get("ds_skip_blocks")
ss_skip_blocks = transformer_options.get("ss_skip_blocks")
if img.ndim != 3 or txt.ndim != 3:
raise ValueError("Input img and txt tensors must have 3 dimensions.")
# running on sequences img
img = self.img_in(img)
vec = self.time_in(timestep_embedding(timesteps, 256).to(img.dtype))
if self.params.guidance_embed:
if guidance is None:
raise ValueError(
"Didn't get guidance strength for guidance distilled model."
)
vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
vec = vec + self.vector_in(y[:, : self.params.vec_in_dim])
txt = self.txt_in(txt)
ids = torch.cat((txt_ids, img_ids), dim=1)
pe = self.pe_embedder(ids)
blocks_replace = patches_replace.get("dit", {})
### TeaCache
img_, vec_ = img.clone(), vec.clone()
img_mod1, _ = self.double_blocks[0].img_mod(vec_)
curr_modulated_img = self.double_blocks[0].img_norm1(img_)
curr_modulated_img = (1 + img_mod1.scale) * curr_modulated_img + img_mod1.shift
try:
self.accum_rel_l1_distance += poly1d(
(curr_modulated_img - self.prev_modulated_img).abs().mean()
/ self.prev_modulated_img.abs().mean()
)
except:
torch._dynamo.graph_break() ### speeding up compiling
self.accum_rel_l1_distance = rel_l1_threshold
self.prev_modulated_img = curr_modulated_img
if self.accum_rel_l1_distance < rel_l1_threshold:
img += self.previous_residual
else:
self.accum_rel_l1_distance = 0
orig_img = img.clone()
for i, block in enumerate(self.double_blocks):
### skip blocks
if i in ds_skip_blocks:
continue
if ("double_block", i) in blocks_replace:
def block_wrap(args):
out = {}
out["img"], out["txt"] = block(
img=args["img"],
txt=args["txt"],
vec=args["vec"],
pe=args["pe"],
attn_mask=args.get("attn_mask"),
)
return out
out = blocks_replace[("double_block", i)](
{
"img": img,
"txt": txt,
"vec": vec,
"pe": pe,
"attn_mask": attn_mask,
},
{"original_block": block_wrap},
)
txt = out["txt"]
img = out["img"]
else:
img, txt = block(img=img, txt=txt, vec=vec, pe=pe, attn_mask=attn_mask)
if control is not None: # Controlnet
control_i = control.get("input")
if i < len(control_i):
add = control_i[i]
if add is not None:
img += add
img = torch.cat((txt, img), 1)
for i, block in enumerate(self.single_blocks):
if ("single_block", i) in blocks_replace:
### skip blocks
if i in ss_skip_blocks:
continue
def block_wrap(args):
out = {}
out["img"] = block(
args["img"],
vec=args["vec"],
pe=args["pe"],
attn_mask=args.get("attn_mask"),
)
return out
out = blocks_replace[("single_block", i)](
{"img": img, "vec": vec, "pe": pe, "attn_mask": attn_mask},
{"original_block": block_wrap},
)
img = out["img"]
else:
img = block(img, vec=vec, pe=pe, attn_mask=attn_mask)
if control is not None: # Controlnet
control_o = control.get("output")
if i < len(control_o):
add = control_o[i]
if add is not None:
img[:, txt.shape[1] :, ...] += add
img = img[:, txt.shape[1] :, ...]
self.previous_residual = img - orig_img
img = self.final_layer(img, vec) # (N, T, patch_size ** 2 * out_channels)
return img
def fbcache_skip_forward_orig(
self,
img: Tensor,
img_ids: Tensor,
txt: Tensor,
txt_ids: Tensor,
timesteps: Tensor,
y: Tensor,
guidance: Tensor = None,
control=None,
transformer_options={},
attn_mask: Tensor = None,
) -> Tensor:
patches_replace = transformer_options.get("patches_replace", {})
fbcache_threshold = transformer_options.get("fbcache_threshold")
validate_use_cached = transformer_options.get("validator")
ds_skip_blocks = transformer_options.get("ds_skip_blocks")
ss_skip_blocks = transformer_options.get("ss_skip_blocks")
if img.ndim != 3 or txt.ndim != 3:
raise ValueError("Input img and txt tensors must have 3 dimensions.")
# running on sequences img
img = self.img_in(img)
vec = self.time_in(timestep_embedding(timesteps, 256).to(img.dtype))
if self.params.guidance_embed:
if guidance is None:
raise ValueError(
"Didn't get guidance strength for guidance distilled model."
)
vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
vec = vec + self.vector_in(y[:, : self.params.vec_in_dim])
txt = self.txt_in(txt)
ids = torch.cat((txt_ids, img_ids), dim=1)
pe = self.pe_embedder(ids)
blocks_replace = patches_replace.get("dit", {})
for i, block in enumerate(self.double_blocks):
#### skip blocks
if i in ds_skip_blocks:
if i == 0:
use_cached = False
continue
if ("double_block", i) in blocks_replace:
def block_wrap(args):
out = {}
out["img"], out["txt"] = block(
img=args["img"],
txt=args["txt"],
vec=args["vec"],
pe=args["pe"],
attn_mask=args.get("attn_mask"),
)
return out
out = blocks_replace[("double_block", i)](
{"img": img, "txt": txt, "vec": vec, "pe": pe, "attn_mask": attn_mask},
{"original_block": block_wrap},
)
txt = out["txt"]
img = out["img"]
else:
img, txt = block(img=img, txt=txt, vec=vec, pe=pe, attn_mask=attn_mask)
if control is not None: # Controlnet
control_i = control.get("input")
if i < len(control_i):
add = control_i[i]
if add is not None:
img += add
### FBCache
if i == 0:
try:
use_cached = are_tensors_similar(
img, self.prev_first_block_output, fbcache_threshold
)
use_cached = validate_use_cached(use_cached, timesteps.item())
except:
torch._dynamo.graph_break() ### speeding up compiling
use_cached = False
self.prev_first_block_output = img
if use_cached:
break
if use_cached:
img += self.prev_residual
else:
img = torch.cat((txt, img), 1)
for i, block in enumerate(self.single_blocks):
if ("single_block", i) in blocks_replace:
#### skip blocks
if i in ss_skip_blocks:
continue
def block_wrap(args):
out = {}
out["img"] = block(
args["img"],
vec=args["vec"],
pe=args["pe"],
attn_mask=args.get("attn_mask"),
)
return out
out = blocks_replace[("single_block", i)](
{"img": img, "vec": vec, "pe": pe, "attn_mask": attn_mask},
{"original_block": block_wrap},
)
img = out["img"]
else:
img = block(img, vec=vec, pe=pe, attn_mask=attn_mask)
if control is not None: # Controlnet
control_o = control.get("output")
if i < len(control_o):
add = control_o[i]
if add is not None:
img[:, txt.shape[1] :, ...] += add
img = img[:, txt.shape[1] :, ...]
self.prev_residual = img - self.prev_first_block_output
img = self.final_layer(img, vec) # (N, T, patch_size ** 2 * out_channels)
return img