Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
AUTOMATIC1111
GitHub Repository: AUTOMATIC1111/stable-diffusion-webui
Path: blob/master/extensions-builtin/Lora/network.py
2447 views
1
from __future__ import annotations
2
import os
3
from collections import namedtuple
4
import enum
5
6
import torch.nn as nn
7
import torch.nn.functional as F
8
9
from modules import sd_models, cache, errors, hashes, shared
10
import modules.models.sd3.mmdit
11
12
NetworkWeights = namedtuple('NetworkWeights', ['network_key', 'sd_key', 'w', 'sd_module'])
13
14
metadata_tags_order = {"ss_sd_model_name": 1, "ss_resolution": 2, "ss_clip_skip": 3, "ss_num_train_images": 10, "ss_tag_frequency": 20}
15
16
17
class SdVersion(enum.Enum):
18
Unknown = 1
19
SD1 = 2
20
SD2 = 3
21
SDXL = 4
22
23
24
class NetworkOnDisk:
25
def __init__(self, name, filename):
26
self.name = name
27
self.filename = filename
28
self.metadata = {}
29
self.is_safetensors = os.path.splitext(filename)[1].lower() == ".safetensors"
30
31
def read_metadata():
32
metadata = sd_models.read_metadata_from_safetensors(filename)
33
34
return metadata
35
36
if self.is_safetensors:
37
try:
38
self.metadata = cache.cached_data_for_file('safetensors-metadata', "lora/" + self.name, filename, read_metadata)
39
except Exception as e:
40
errors.display(e, f"reading lora {filename}")
41
42
if self.metadata:
43
m = {}
44
for k, v in sorted(self.metadata.items(), key=lambda x: metadata_tags_order.get(x[0], 999)):
45
m[k] = v
46
47
self.metadata = m
48
49
self.alias = self.metadata.get('ss_output_name', self.name)
50
51
self.hash = None
52
self.shorthash = None
53
self.set_hash(
54
self.metadata.get('sshs_model_hash') or
55
hashes.sha256_from_cache(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or
56
''
57
)
58
59
self.sd_version = self.detect_version()
60
61
def detect_version(self):
62
if str(self.metadata.get('ss_base_model_version', "")).startswith("sdxl_"):
63
return SdVersion.SDXL
64
elif str(self.metadata.get('ss_v2', "")) == "True":
65
return SdVersion.SD2
66
elif len(self.metadata):
67
return SdVersion.SD1
68
69
return SdVersion.Unknown
70
71
def set_hash(self, v):
72
self.hash = v
73
self.shorthash = self.hash[0:12]
74
75
if self.shorthash:
76
import networks
77
networks.available_network_hash_lookup[self.shorthash] = self
78
79
def read_hash(self):
80
if not self.hash:
81
self.set_hash(hashes.sha256(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or '')
82
83
def get_alias(self):
84
import networks
85
if shared.opts.lora_preferred_name == "Filename" or self.alias.lower() in networks.forbidden_network_aliases:
86
return self.name
87
else:
88
return self.alias
89
90
91
class Network: # LoraModule
92
def __init__(self, name, network_on_disk: NetworkOnDisk):
93
self.name = name
94
self.network_on_disk = network_on_disk
95
self.te_multiplier = 1.0
96
self.unet_multiplier = 1.0
97
self.dyn_dim = None
98
self.modules = {}
99
self.bundle_embeddings = {}
100
self.mtime = None
101
102
self.mentioned_name = None
103
"""the text that was used to add the network to prompt - can be either name or an alias"""
104
105
106
class ModuleType:
107
def create_module(self, net: Network, weights: NetworkWeights) -> Network | None:
108
return None
109
110
111
class NetworkModule:
112
def __init__(self, net: Network, weights: NetworkWeights):
113
self.network = net
114
self.network_key = weights.network_key
115
self.sd_key = weights.sd_key
116
self.sd_module = weights.sd_module
117
118
if isinstance(self.sd_module, modules.models.sd3.mmdit.QkvLinear):
119
s = self.sd_module.weight.shape
120
self.shape = (s[0] // 3, s[1])
121
elif hasattr(self.sd_module, 'weight'):
122
self.shape = self.sd_module.weight.shape
123
elif isinstance(self.sd_module, nn.MultiheadAttention):
124
# For now, only self-attn use Pytorch's MHA
125
# So assume all qkvo proj have same shape
126
self.shape = self.sd_module.out_proj.weight.shape
127
else:
128
self.shape = None
129
130
self.ops = None
131
self.extra_kwargs = {}
132
if isinstance(self.sd_module, nn.Conv2d):
133
self.ops = F.conv2d
134
self.extra_kwargs = {
135
'stride': self.sd_module.stride,
136
'padding': self.sd_module.padding
137
}
138
elif isinstance(self.sd_module, nn.Linear):
139
self.ops = F.linear
140
elif isinstance(self.sd_module, nn.LayerNorm):
141
self.ops = F.layer_norm
142
self.extra_kwargs = {
143
'normalized_shape': self.sd_module.normalized_shape,
144
'eps': self.sd_module.eps
145
}
146
elif isinstance(self.sd_module, nn.GroupNorm):
147
self.ops = F.group_norm
148
self.extra_kwargs = {
149
'num_groups': self.sd_module.num_groups,
150
'eps': self.sd_module.eps
151
}
152
153
self.dim = None
154
self.bias = weights.w.get("bias")
155
self.alpha = weights.w["alpha"].item() if "alpha" in weights.w else None
156
self.scale = weights.w["scale"].item() if "scale" in weights.w else None
157
158
self.dora_scale = weights.w.get("dora_scale", None)
159
self.dora_norm_dims = len(self.shape) - 1
160
161
def multiplier(self):
162
if 'transformer' in self.sd_key[:20]:
163
return self.network.te_multiplier
164
else:
165
return self.network.unet_multiplier
166
167
def calc_scale(self):
168
if self.scale is not None:
169
return self.scale
170
if self.dim is not None and self.alpha is not None:
171
return self.alpha / self.dim
172
173
return 1.0
174
175
def apply_weight_decompose(self, updown, orig_weight):
176
# Match the device/dtype
177
orig_weight = orig_weight.to(updown.dtype)
178
dora_scale = self.dora_scale.to(device=orig_weight.device, dtype=updown.dtype)
179
updown = updown.to(orig_weight.device)
180
181
merged_scale1 = updown + orig_weight
182
merged_scale1_norm = (
183
merged_scale1.transpose(0, 1)
184
.reshape(merged_scale1.shape[1], -1)
185
.norm(dim=1, keepdim=True)
186
.reshape(merged_scale1.shape[1], *[1] * self.dora_norm_dims)
187
.transpose(0, 1)
188
)
189
190
dora_merged = (
191
merged_scale1 * (dora_scale / merged_scale1_norm)
192
)
193
final_updown = dora_merged - orig_weight
194
return final_updown
195
196
def finalize_updown(self, updown, orig_weight, output_shape, ex_bias=None):
197
if self.bias is not None:
198
updown = updown.reshape(self.bias.shape)
199
updown += self.bias.to(orig_weight.device, dtype=updown.dtype)
200
updown = updown.reshape(output_shape)
201
202
if len(output_shape) == 4:
203
updown = updown.reshape(output_shape)
204
205
if orig_weight.size().numel() == updown.size().numel():
206
updown = updown.reshape(orig_weight.shape)
207
208
if ex_bias is not None:
209
ex_bias = ex_bias * self.multiplier()
210
211
updown = updown * self.calc_scale()
212
213
if self.dora_scale is not None:
214
updown = self.apply_weight_decompose(updown, orig_weight)
215
216
return updown * self.multiplier(), ex_bias
217
218
def calc_updown(self, target):
219
raise NotImplementedError()
220
221
def forward(self, x, y):
222
"""A general forward implementation for all modules"""
223
if self.ops is None:
224
raise NotImplementedError()
225
else:
226
updown, ex_bias = self.calc_updown(self.sd_module.weight)
227
return y + self.ops(x, weight=updown, bias=ex_bias, **self.extra_kwargs)
228
229
230