| import comfy.utils |
| import folder_paths |
| import torch |
| import logging |
|
|
| def load_hypernetwork_patch(path, strength): |
| sd = comfy.utils.load_torch_file(path, safe_load=True) |
| activation_func = sd.get('activation_func', 'linear') |
| is_layer_norm = sd.get('is_layer_norm', False) |
| use_dropout = sd.get('use_dropout', False) |
| activate_output = sd.get('activate_output', False) |
| last_layer_dropout = sd.get('last_layer_dropout', False) |
|
|
| valid_activation = { |
| "linear": torch.nn.Identity, |
| "relu": torch.nn.ReLU, |
| "leakyrelu": torch.nn.LeakyReLU, |
| "elu": torch.nn.ELU, |
| "swish": torch.nn.Hardswish, |
| "tanh": torch.nn.Tanh, |
| "sigmoid": torch.nn.Sigmoid, |
| "softsign": torch.nn.Softsign, |
| "mish": torch.nn.Mish, |
| } |
|
|
| if activation_func not in valid_activation: |
| logging.error("Unsupported Hypernetwork format, if you report it I might implement it. {} {} {} {} {} {}".format(path, activation_func, is_layer_norm, use_dropout, activate_output, last_layer_dropout)) |
| return None |
|
|
| out = {} |
|
|
| for d in sd: |
| try: |
| dim = int(d) |
| except: |
| continue |
|
|
| output = [] |
| for index in [0, 1]: |
| attn_weights = sd[dim][index] |
| keys = attn_weights.keys() |
|
|
| linears = filter(lambda a: a.endswith(".weight"), keys) |
| linears = list(map(lambda a: a[:-len(".weight")], linears)) |
| layers = [] |
|
|
| i = 0 |
| while i < len(linears): |
| lin_name = linears[i] |
| last_layer = (i == (len(linears) - 1)) |
| penultimate_layer = (i == (len(linears) - 2)) |
|
|
| lin_weight = attn_weights['{}.weight'.format(lin_name)] |
| lin_bias = attn_weights['{}.bias'.format(lin_name)] |
| layer = torch.nn.Linear(lin_weight.shape[1], lin_weight.shape[0]) |
| layer.load_state_dict({"weight": lin_weight, "bias": lin_bias}) |
| layers.append(layer) |
| if activation_func != "linear": |
| if (not last_layer) or (activate_output): |
| layers.append(valid_activation[activation_func]()) |
| if is_layer_norm: |
| i += 1 |
| ln_name = linears[i] |
| ln_weight = attn_weights['{}.weight'.format(ln_name)] |
| ln_bias = attn_weights['{}.bias'.format(ln_name)] |
| ln = torch.nn.LayerNorm(ln_weight.shape[0]) |
| ln.load_state_dict({"weight": ln_weight, "bias": ln_bias}) |
| layers.append(ln) |
| if use_dropout: |
| if (not last_layer) and (not penultimate_layer or last_layer_dropout): |
| layers.append(torch.nn.Dropout(p=0.3)) |
| i += 1 |
|
|
| output.append(torch.nn.Sequential(*layers)) |
| out[dim] = torch.nn.ModuleList(output) |
|
|
| class hypernetwork_patch: |
| def __init__(self, hypernet, strength): |
| self.hypernet = hypernet |
| self.strength = strength |
| def __call__(self, q, k, v, extra_options): |
| dim = k.shape[-1] |
| if dim in self.hypernet: |
| hn = self.hypernet[dim] |
| k = k + hn[0](k) * self.strength |
| v = v + hn[1](v) * self.strength |
|
|
| return q, k, v |
|
|
| def to(self, device): |
| for d in self.hypernet.keys(): |
| self.hypernet[d] = self.hypernet[d].to(device) |
| return self |
|
|
| return hypernetwork_patch(out, strength) |
|
|
| class HypernetworkLoader: |
| @classmethod |
| def INPUT_TYPES(s): |
| return {"required": { "model": ("MODEL",), |
| "hypernetwork_name": (folder_paths.get_filename_list("hypernetworks"), ), |
| "strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| }} |
| RETURN_TYPES = ("MODEL",) |
| FUNCTION = "load_hypernetwork" |
|
|
| CATEGORY = "loaders" |
|
|
| def load_hypernetwork(self, model, hypernetwork_name, strength): |
| hypernetwork_path = folder_paths.get_full_path_or_raise("hypernetworks", hypernetwork_name) |
| model_hypernetwork = model.clone() |
| patch = load_hypernetwork_patch(hypernetwork_path, strength) |
| if patch is not None: |
| model_hypernetwork.set_model_attn1_patch(patch) |
| model_hypernetwork.set_model_attn2_patch(patch) |
| return (model_hypernetwork,) |
|
|
| NODE_CLASS_MAPPINGS = { |
| "HypernetworkLoader": HypernetworkLoader |
| } |
|
|