wan2.1 latent upscale 2x baseline

This commit is contained in:
spacepxl
2025-11-29 16:33:45 -05:00
parent b53d089a69
commit de6b123e7c
4 changed files with 186 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 789 KiB

+155
View File
@@ -0,0 +1,155 @@
import os
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from safetensors.torch import load_file
class LayerNorm3d(nn.LayerNorm):
def __init__(self, num_channels, eps=1e-6, affine=True):
super().__init__(num_channels, eps=eps, elementwise_affine=affine)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = x.permute(0, 2, 3, 4, 1)
x = F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
x = x.permute(0, 4, 1, 2, 3)
return x
class DiCoBlock3d(nn.Module):
def __init__(self, hidden_size, mlp_ratio=4.0, kernel_size=3):
super().__init__()
# self.conv1 = nn.Conv3d(hidden_size, hidden_size, kernel_size=1)
self.conv2 = nn.Conv3d(
hidden_size,
hidden_size,
kernel_size=(1, kernel_size, kernel_size),
padding=(0, kernel_size//2, kernel_size//2),
# groups=hidden_size,
padding_mode="replicate",
)
self.conv3 = nn.Conv3d(hidden_size, hidden_size, kernel_size=1)
self.cca = nn.Sequential(
nn.AdaptiveAvgPool3d(1),
nn.Conv3d(hidden_size, hidden_size , kernel_size=1),
nn.Sigmoid(),
)
ffn_channel = int(mlp_ratio * hidden_size)
self.conv4 = nn.Conv3d(hidden_size, ffn_channel, kernel_size=1)
self.conv5 = nn.Conv3d(ffn_channel, hidden_size, kernel_size=1)
self.norm1 = LayerNorm3d(hidden_size, affine=False)
self.norm2 = LayerNorm3d(hidden_size, affine=False)
def forward(self, inp):
x = self.norm1(inp)
# x = F.gelu(self.conv2(self.conv1(x)))
x = F.gelu(self.conv2(x))
x = self.conv3(x * self.cca(x))
x = inp + x
y = self.norm2(x)
y = self.conv5(F.gelu(self.conv4(y)))
return x + y
class LatentModel3d(nn.Module):
def __init__(
self,
in_channels = 16,
out_channels = 16,
hidden_size = 128,
kernel_size = 3,
mlp_ratio = 4.0,
depth = 8,
upscale = 2,
):
super().__init__()
self.upscale = int(upscale)
self.conv_in = nn.Conv3d(
in_channels,
hidden_size,
kernel_size=(1, 3, 3),
padding=(0, 1, 1),
padding_mode="replicate",
)
self.blocks = nn.ModuleList([
DiCoBlock3d(hidden_size, mlp_ratio, kernel_size)
for _ in range(depth)
])
out_ch = out_channels * self.upscale ** 2
self.conv_out = nn.Conv3d(
hidden_size,
out_ch,
kernel_size=(1, 3, 3),
padding=(0, 1, 1),
padding_mode="replicate",
)
# self.initialize_weights()
def initialize_weights(self):
def _basic_init(module):
if isinstance(module, nn.Linear) or isinstance(module, nn.Conv3d):
torch.nn.init.xavier_uniform_(module.weight)
if module.bias is not None:
nn.init.constant_(module.bias, 0)
self.apply(_basic_init)
def forward(self, x):
"""
x: (B, C, F, H, W) tensor of spatial inputs (images or latent representations of images)
"""
hidden = self.conv_in(x)
for block in self.blocks:
hidden = block(hidden)
output = self.conv_out(hidden)
if self.upscale > 1:
output = F.pixel_shuffle(output.movedim(1, 2), self.upscale).movedim(2, 1)
x = F.interpolate(x, scale_factor=(1, self.upscale, self.upscale), mode="nearest-exact")
return output + x
def Wan21_latent_upscale_2x():
model = LatentModel3d(
in_channels = 16,
out_channels = 16,
hidden_size = 128,
kernel_size = 3,
mlp_ratio = 4.0,
depth = 8,
upscale = 2,
)
model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "wan21_latent_upscale_2x.safetensors")
model.load_state_dict(load_file(model_path))
return model
latent_upscale_models = {
"Wan 2.1 latent upscale 2x": Wan21_latent_upscale_2x,
}
if __name__=="__main__":
model = LatentModel3d(upscale=2).to("cuda")
x = torch.randn(1, 16, 4, 64, 64).to("cuda")
out = model(x)
print(out.shape)
total = sum([p.numel() for p in model.parameters() if p.requires_grad])
print("trainable parameters: %.2f M" % (total / 1e6))
print(model)
Binary file not shown.
+31
View File
@@ -1,10 +1,13 @@
import copy
import torch import torch
import torch.nn.functional as F import torch.nn.functional as F
import comfy.utils import comfy.utils
import comfy.model_management
import folder_paths import folder_paths
from nodes import VAELoader from nodes import VAELoader
from .src.sd import CustomVAE from .src.sd import CustomVAE
from .latent_upscale.model import latent_upscale_models
class VAEUtils_CustomVAELoader(VAELoader): class VAEUtils_CustomVAELoader(VAELoader):
@@ -87,7 +90,35 @@ class VAEUtils_VAEDecodeTiled:
return (images,) return (images,)
class VAEUtils_LatentUpscale:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"samples": ("LATENT", ),
"model": (list(latent_upscale_models.keys()), ),
}
}
RETURN_TYPES = ("LATENT",)
FUNCTION = "upscale"
CATEGORY = "VAE-Utils"
def upscale(self, samples, model):
device = comfy.model_management.get_torch_device()
model = latent_upscale_models[model]().to(device)
latents = samples["samples"].to(dtype=torch.float32, device=device)
upscaled_latents = model(latents).to(comfy.model_management.intermediate_device())
samples = copy.deepcopy(samples)
samples["samples"] = upscaled_latents
return (samples, )
COMBINED_MAPPINGS = { COMBINED_MAPPINGS = {
"VAEUtils_CustomVAELoader": (VAEUtils_CustomVAELoader, "Load VAE (VAE Utils)"), "VAEUtils_CustomVAELoader": (VAEUtils_CustomVAELoader, "Load VAE (VAE Utils)"),
"VAEUtils_VAEDecodeTiled": (VAEUtils_VAEDecodeTiled, "VAE Decode (VAE Utils)"), "VAEUtils_VAEDecodeTiled": (VAEUtils_VAEDecodeTiled, "VAE Decode (VAE Utils)"),
"VAEUtils_LatentUpscale": (VAEUtils_LatentUpscale, "Latent Upscale (VAE Utils)"),
} }