lycoris-lora 3.0.0.dev9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lycoris/__init__.py +26 -0
- lycoris/concept.py +254 -0
- lycoris/config.py +86 -0
- lycoris/dylora.py +175 -0
- lycoris/functional/__init__.py +13 -0
- lycoris/functional/general.py +79 -0
- lycoris/functional/locon.py +102 -0
- lycoris/functional/loha.py +187 -0
- lycoris/functional/lokr.py +259 -0
- lycoris/hcp/__init__.py +4 -0
- lycoris/hcp/base.py +153 -0
- lycoris/hcp/diag_oft.py +62 -0
- lycoris/hcp/loha.py +85 -0
- lycoris/hcp/lokr.py +131 -0
- lycoris/ia3.py +68 -0
- lycoris/kohya/__init__.py +866 -0
- lycoris/kohya/kohya_model_utils.py +1165 -0
- lycoris/kohya/kohya_utils.py +48 -0
- lycoris/kohya/model_utils.py +1633 -0
- lycoris/kohya/original_unet.py +1760 -0
- lycoris/kohya/sdxl_model_util.py +581 -0
- lycoris/kohya/sdxl_original_unet.py +1243 -0
- lycoris/kohya/test_hyperdream.py +527 -0
- lycoris/kohya/utils.py +48 -0
- lycoris/kohya.py +850 -0
- lycoris/kohya_model_utils.py +1165 -0
- lycoris/kohya_utils.py +48 -0
- lycoris/locon.py +86 -0
- lycoris/logging.py +36 -0
- lycoris/loha.py +199 -0
- lycoris/lokr.py +220 -0
- lycoris/modules/__init__.py +147 -0
- lycoris/modules/attention.py +259 -0
- lycoris/modules/base.py +278 -0
- lycoris/modules/boft.py +198 -0
- lycoris/modules/diag_oft.py +182 -0
- lycoris/modules/dylora.py +157 -0
- lycoris/modules/full.py +166 -0
- lycoris/modules/glokr.py +300 -0
- lycoris/modules/glora.py +204 -0
- lycoris/modules/hyperlycoris.py +3 -0
- lycoris/modules/hypernet/__init__.py +1 -0
- lycoris/modules/hypernet/generater.py +223 -0
- lycoris/modules/hypernet/text_encoder.py +266 -0
- lycoris/modules/ia3.py +130 -0
- lycoris/modules/locon.py +279 -0
- lycoris/modules/loha.py +268 -0
- lycoris/modules/lokr.py +469 -0
- lycoris/modules/norms.py +104 -0
- lycoris/utils/__init__.py +669 -0
- lycoris/utils/bnb.py +29 -0
- lycoris/utils/general.py +5 -0
- lycoris/utils/logger.py +35 -0
- lycoris/utils/preset.py +9 -0
- lycoris/utils/xformers_utils.py +13 -0
- lycoris/utils.py +504 -0
- lycoris/wrapper.py +537 -0
- lycoris_lora-3.0.0.dev9.dist-info/LICENSE.md +201 -0
- lycoris_lora-3.0.0.dev9.dist-info/METADATA +14 -0
- lycoris_lora-3.0.0.dev9.dist-info/RECORD +62 -0
- lycoris_lora-3.0.0.dev9.dist-info/WHEEL +5 -0
- lycoris_lora-3.0.0.dev9.dist-info/top_level.txt +1 -0
lycoris/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from . import kohya
|
|
3
|
+
except:
|
|
4
|
+
pass
|
|
5
|
+
from . import (
|
|
6
|
+
modules,
|
|
7
|
+
utils,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from .modules.locon import LoConModule
|
|
11
|
+
from .modules.loha import LohaModule
|
|
12
|
+
from .modules.lokr import LokrModule
|
|
13
|
+
from .modules.dylora import DyLoraModule
|
|
14
|
+
from .modules.glora import GLoRAModule
|
|
15
|
+
from .modules.norms import NormModule
|
|
16
|
+
from .modules.full import FullModule
|
|
17
|
+
from .modules.diag_oft import DiagOFTModule
|
|
18
|
+
from .modules import make_module
|
|
19
|
+
|
|
20
|
+
from .wrapper import (
|
|
21
|
+
LycorisNetwork,
|
|
22
|
+
create_lycoris,
|
|
23
|
+
create_lycoris_from_weights,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from .logging import logger
|
lycoris/concept.py
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import os, sys
|
|
2
|
+
sys.path.insert(0, os.getcwd())
|
|
3
|
+
import math
|
|
4
|
+
from warnings import warn
|
|
5
|
+
import os
|
|
6
|
+
from typing import List
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
import torch
|
|
10
|
+
import torch.nn as nn
|
|
11
|
+
import torch.nn.functional as F
|
|
12
|
+
|
|
13
|
+
from .kohya_utils import *
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ConceptModule(nn.Module):
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
lora_name, org_module: nn.Module,
|
|
20
|
+
multiplier=1.0,
|
|
21
|
+
):
|
|
22
|
+
""" if alpha == 0 or None, alpha is rank (no scaling). """
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.lora_name = lora_name
|
|
25
|
+
|
|
26
|
+
if org_module.__class__.__name__ == 'Conv2d':
|
|
27
|
+
# For general LoCon
|
|
28
|
+
stride = org_module.stride
|
|
29
|
+
padding = org_module.padding
|
|
30
|
+
self.op = F.conv2d
|
|
31
|
+
self.extra_args = {
|
|
32
|
+
'stride': stride,
|
|
33
|
+
'padding': padding
|
|
34
|
+
}
|
|
35
|
+
else:
|
|
36
|
+
self.op = F.linear
|
|
37
|
+
self.extra_args = {}
|
|
38
|
+
|
|
39
|
+
self.concept_mask = nn.Parameter(torch.ones_like(
|
|
40
|
+
org_module.weight
|
|
41
|
+
))
|
|
42
|
+
self.concept_neuron = None
|
|
43
|
+
|
|
44
|
+
self.multiplier = multiplier
|
|
45
|
+
self.org_module = [org_module]
|
|
46
|
+
|
|
47
|
+
def apply_to(self):
|
|
48
|
+
self.org_module[0].forward = self.forward
|
|
49
|
+
|
|
50
|
+
def forward(self, x):
|
|
51
|
+
bias = None if self.org_module[0].bias is None else self.org_module[0].bias.data
|
|
52
|
+
if torch.max(self.concept_mask)-torch.min(self.concept_mask)<0.1:
|
|
53
|
+
return self.op(
|
|
54
|
+
x, self.org_module[0].weight.data * self.concept_mask,
|
|
55
|
+
bias, **self.extra_args
|
|
56
|
+
)
|
|
57
|
+
else:
|
|
58
|
+
if self.concept_neuron is None:
|
|
59
|
+
print('Found neurons')
|
|
60
|
+
mask = self.concept_mask
|
|
61
|
+
threshold = torch.quantile(mask, 0.05)
|
|
62
|
+
neuron = mask<threshold
|
|
63
|
+
self.concept_mask.requires_grad_(False)
|
|
64
|
+
self.concept_mask[mask>=threshold] = 0
|
|
65
|
+
self.concept_mask[neuron] = mask[neuron]-1
|
|
66
|
+
self.concept_mask.requires_grad_(True)
|
|
67
|
+
self.concept_neuron = neuron
|
|
68
|
+
return self.op(
|
|
69
|
+
x, self.org_module[0].weight.data * (1+self.concept_mask * self.concept_neuron),
|
|
70
|
+
bias, **self.extra_args
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ConceptNetwork(torch.nn.Module):
|
|
75
|
+
'''
|
|
76
|
+
LoRA + LoCon
|
|
77
|
+
'''
|
|
78
|
+
# Ignore proj_in or proj_out, their channels is only a few.
|
|
79
|
+
UNET_TARGET_REPLACE_MODULE = [
|
|
80
|
+
"Transformer2DModel",
|
|
81
|
+
"Attention",
|
|
82
|
+
]
|
|
83
|
+
TEXT_ENCODER_TARGET_REPLACE_MODULE = ["CLIPAttention"]
|
|
84
|
+
LORA_PREFIX_UNET = 'lora_unet'
|
|
85
|
+
LORA_PREFIX_TEXT_ENCODER = 'lora_te'
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
text_encoder, unet,
|
|
90
|
+
multiplier=1.0,
|
|
91
|
+
*args,
|
|
92
|
+
**kwargs
|
|
93
|
+
) -> None:
|
|
94
|
+
super().__init__()
|
|
95
|
+
self.multiplier = multiplier
|
|
96
|
+
|
|
97
|
+
total_params = 0
|
|
98
|
+
# create module instances
|
|
99
|
+
def create_modules(prefix, root_module: torch.nn.Module, target_replace_modules) -> List[ConceptModule]:
|
|
100
|
+
nonlocal total_params
|
|
101
|
+
print('Create Concept Module')
|
|
102
|
+
loras = []
|
|
103
|
+
for name, module in root_module.named_modules():
|
|
104
|
+
if module.__class__.__name__ in target_replace_modules:
|
|
105
|
+
for child_name, child_module in module.named_modules():
|
|
106
|
+
if child_name.rsplit('.', 1)[-1] not in ['to_k', 'to_v', 'k_proj', 'v_proj']:
|
|
107
|
+
continue
|
|
108
|
+
lora_name = prefix + '.' + name + '.' + child_name
|
|
109
|
+
lora_name = lora_name.replace('.', '_')
|
|
110
|
+
total_params += child_module.weight.size().numel()
|
|
111
|
+
lora = ConceptModule(
|
|
112
|
+
lora_name, child_module, self.multiplier,
|
|
113
|
+
)
|
|
114
|
+
loras.append(lora)
|
|
115
|
+
return loras
|
|
116
|
+
|
|
117
|
+
self.text_encoder_loras = create_modules(
|
|
118
|
+
ConceptNetwork.LORA_PREFIX_TEXT_ENCODER,
|
|
119
|
+
text_encoder,
|
|
120
|
+
ConceptNetwork.TEXT_ENCODER_TARGET_REPLACE_MODULE
|
|
121
|
+
)
|
|
122
|
+
print(f"create LoRA for Text Encoder: {len(self.text_encoder_loras)} modules.")
|
|
123
|
+
|
|
124
|
+
self.unet_loras = create_modules(ConceptNetwork.LORA_PREFIX_UNET, unet, ConceptNetwork.UNET_TARGET_REPLACE_MODULE)
|
|
125
|
+
print(f"create LoRA for U-Net: {len(self.unet_loras)} modules.")
|
|
126
|
+
|
|
127
|
+
self.weights_sd = None
|
|
128
|
+
|
|
129
|
+
print(f'Total concept param: {total_params}')
|
|
130
|
+
|
|
131
|
+
# assertion
|
|
132
|
+
names = set()
|
|
133
|
+
for lora in self.text_encoder_loras + self.unet_loras:
|
|
134
|
+
assert lora.lora_name not in names, f"duplicated lora name: {lora.lora_name}"
|
|
135
|
+
names.add(lora.lora_name)
|
|
136
|
+
|
|
137
|
+
def set_multiplier(self, multiplier):
|
|
138
|
+
self.multiplier = multiplier
|
|
139
|
+
for lora in self.text_encoder_loras + self.unet_loras:
|
|
140
|
+
lora.multiplier = self.multiplier
|
|
141
|
+
|
|
142
|
+
def load_weights(self, file):
|
|
143
|
+
if os.path.splitext(file)[1] == '.safetensors':
|
|
144
|
+
from safetensors.torch import load_file, safe_open
|
|
145
|
+
self.weights_sd = load_file(file)
|
|
146
|
+
else:
|
|
147
|
+
self.weights_sd = torch.load(file, map_location='cpu')
|
|
148
|
+
|
|
149
|
+
def apply_to(self, text_encoder, unet, apply_text_encoder=None, apply_unet=None):
|
|
150
|
+
if self.weights_sd:
|
|
151
|
+
weights_has_text_encoder = weights_has_unet = False
|
|
152
|
+
for key in self.weights_sd.keys():
|
|
153
|
+
if key.startswith(ConceptNetwork.LORA_PREFIX_TEXT_ENCODER):
|
|
154
|
+
weights_has_text_encoder = True
|
|
155
|
+
elif key.startswith(ConceptNetwork.LORA_PREFIX_UNET):
|
|
156
|
+
weights_has_unet = True
|
|
157
|
+
|
|
158
|
+
if apply_text_encoder is None:
|
|
159
|
+
apply_text_encoder = weights_has_text_encoder
|
|
160
|
+
else:
|
|
161
|
+
assert apply_text_encoder == weights_has_text_encoder, f"text encoder weights: {weights_has_text_encoder} but text encoder flag: {apply_text_encoder} / 重みとText Encoderのフラグが矛盾しています"
|
|
162
|
+
|
|
163
|
+
if apply_unet is None:
|
|
164
|
+
apply_unet = weights_has_unet
|
|
165
|
+
else:
|
|
166
|
+
assert apply_unet == weights_has_unet, f"u-net weights: {weights_has_unet} but u-net flag: {apply_unet} / 重みとU-Netのフラグが矛盾しています"
|
|
167
|
+
else:
|
|
168
|
+
assert apply_text_encoder is not None and apply_unet is not None, f"internal error: flag not set"
|
|
169
|
+
|
|
170
|
+
if apply_text_encoder:
|
|
171
|
+
print("enable LoRA for text encoder")
|
|
172
|
+
else:
|
|
173
|
+
self.text_encoder_loras = []
|
|
174
|
+
|
|
175
|
+
if apply_unet:
|
|
176
|
+
print("enable LoRA for U-Net")
|
|
177
|
+
else:
|
|
178
|
+
self.unet_loras = []
|
|
179
|
+
|
|
180
|
+
for lora in self.text_encoder_loras + self.unet_loras:
|
|
181
|
+
lora.apply_to()
|
|
182
|
+
self.add_module(lora.lora_name, lora)
|
|
183
|
+
|
|
184
|
+
if self.weights_sd:
|
|
185
|
+
# if some weights are not in state dict, it is ok because initial LoRA does nothing (lora_up is initialized by zeros)
|
|
186
|
+
info = self.load_state_dict(self.weights_sd, False)
|
|
187
|
+
print(f"weights are loaded: {info}")
|
|
188
|
+
|
|
189
|
+
def enable_gradient_checkpointing(self):
|
|
190
|
+
# not supported
|
|
191
|
+
def make_ckpt(module):
|
|
192
|
+
if isinstance(module, torch.nn.Module):
|
|
193
|
+
module.grad_ckpt = True
|
|
194
|
+
self.apply(make_ckpt)
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
def prepare_optimizer_params(self, text_encoder_lr, unet_lr):
|
|
198
|
+
def enumerate_params(loras):
|
|
199
|
+
params = []
|
|
200
|
+
for lora in loras:
|
|
201
|
+
params.extend(lora.parameters())
|
|
202
|
+
return params
|
|
203
|
+
|
|
204
|
+
self.requires_grad_(True)
|
|
205
|
+
all_params = []
|
|
206
|
+
|
|
207
|
+
if self.text_encoder_loras:
|
|
208
|
+
param_data = {'params': enumerate_params(self.text_encoder_loras)}
|
|
209
|
+
if text_encoder_lr is not None:
|
|
210
|
+
param_data['lr'] = text_encoder_lr
|
|
211
|
+
all_params.append(param_data)
|
|
212
|
+
|
|
213
|
+
if self.unet_loras:
|
|
214
|
+
param_data = {'params': enumerate_params(self.unet_loras)}
|
|
215
|
+
if unet_lr is not None:
|
|
216
|
+
param_data['lr'] = unet_lr
|
|
217
|
+
all_params.append(param_data)
|
|
218
|
+
|
|
219
|
+
return all_params
|
|
220
|
+
|
|
221
|
+
def prepare_grad_etc(self, text_encoder, unet):
|
|
222
|
+
self.requires_grad_(True)
|
|
223
|
+
|
|
224
|
+
def on_epoch_start(self, text_encoder, unet):
|
|
225
|
+
self.train()
|
|
226
|
+
|
|
227
|
+
def get_trainable_params(self):
|
|
228
|
+
return self.parameters()
|
|
229
|
+
|
|
230
|
+
def save_weights(self, file, dtype, metadata):
|
|
231
|
+
if metadata is not None and len(metadata) == 0:
|
|
232
|
+
metadata = None
|
|
233
|
+
|
|
234
|
+
state_dict = self.state_dict()
|
|
235
|
+
|
|
236
|
+
if dtype is not None:
|
|
237
|
+
for key in list(state_dict.keys()):
|
|
238
|
+
v = state_dict[key]
|
|
239
|
+
v = v.detach().clone().to("cpu").to(dtype)
|
|
240
|
+
state_dict[key] = v
|
|
241
|
+
|
|
242
|
+
if os.path.splitext(file)[1] == '.safetensors':
|
|
243
|
+
from safetensors.torch import save_file
|
|
244
|
+
|
|
245
|
+
# Precalculate model hashes to save time on indexing
|
|
246
|
+
if metadata is None:
|
|
247
|
+
metadata = {}
|
|
248
|
+
model_hash, legacy_hash = precalculate_safetensors_hashes(state_dict, metadata)
|
|
249
|
+
metadata["sshs_model_hash"] = model_hash
|
|
250
|
+
metadata["sshs_legacy_hash"] = legacy_hash
|
|
251
|
+
|
|
252
|
+
save_file(state_dict, file, metadata)
|
|
253
|
+
else:
|
|
254
|
+
torch.save(state_dict, file)
|
lycoris/config.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
PRESET = {
|
|
2
|
+
"full": {
|
|
3
|
+
"enable_conv": True,
|
|
4
|
+
"unet_target_module": [
|
|
5
|
+
"Transformer2DModel",
|
|
6
|
+
"ResnetBlock2D",
|
|
7
|
+
"Downsample2D",
|
|
8
|
+
"Upsample2D",
|
|
9
|
+
],
|
|
10
|
+
"unet_target_name": [
|
|
11
|
+
"conv_in",
|
|
12
|
+
"conv_out",
|
|
13
|
+
"time_embedding.linear_1",
|
|
14
|
+
"time_embedding.linear_2",
|
|
15
|
+
],
|
|
16
|
+
"text_encoder_target_module": ["CLIPAttention", "CLIPMLP"],
|
|
17
|
+
"text_encoder_target_name": [],
|
|
18
|
+
},
|
|
19
|
+
"full-lin": {
|
|
20
|
+
"enable_conv": False,
|
|
21
|
+
"unet_target_module": [
|
|
22
|
+
"Transformer2DModel",
|
|
23
|
+
"ResnetBlock2D",
|
|
24
|
+
],
|
|
25
|
+
"unet_target_name": [
|
|
26
|
+
"time_embedding.linear_1",
|
|
27
|
+
"time_embedding.linear_2",
|
|
28
|
+
],
|
|
29
|
+
"text_encoder_target_module": ["CLIPAttention", "CLIPMLP"],
|
|
30
|
+
"text_encoder_target_name": [],
|
|
31
|
+
},
|
|
32
|
+
"attn-mlp": {
|
|
33
|
+
"enable_conv": False,
|
|
34
|
+
"unet_target_module": [
|
|
35
|
+
"Transformer2DModel",
|
|
36
|
+
],
|
|
37
|
+
"unet_target_name": [],
|
|
38
|
+
"text_encoder_target_module": ["CLIPAttention", "CLIPMLP"],
|
|
39
|
+
"text_encoder_target_name": [],
|
|
40
|
+
},
|
|
41
|
+
"attn-only": {
|
|
42
|
+
"enable_conv": False,
|
|
43
|
+
"unet_target_module": [
|
|
44
|
+
"CrossAttention",
|
|
45
|
+
],
|
|
46
|
+
"unet_target_name": [],
|
|
47
|
+
"text_encoder_target_module": ["CLIPAttention"],
|
|
48
|
+
"text_encoder_target_name": [],
|
|
49
|
+
},
|
|
50
|
+
"unet-only": {
|
|
51
|
+
"enable_conv": True,
|
|
52
|
+
"unet_target_module": [
|
|
53
|
+
"Transformer2DModel",
|
|
54
|
+
"ResnetBlock2D",
|
|
55
|
+
"Downsample2D",
|
|
56
|
+
"Upsample2D",
|
|
57
|
+
],
|
|
58
|
+
"unet_target_name": [
|
|
59
|
+
"conv_in",
|
|
60
|
+
"conv_out",
|
|
61
|
+
"time_embedding.linear_1",
|
|
62
|
+
"time_embedding.linear_2",
|
|
63
|
+
],
|
|
64
|
+
"text_encoder_target_module": [],
|
|
65
|
+
"text_encoder_target_name": [],
|
|
66
|
+
},
|
|
67
|
+
"unet-transformer-only": {
|
|
68
|
+
"enable_conv": False,
|
|
69
|
+
"unet_target_module": [
|
|
70
|
+
"Transformer2DModel",
|
|
71
|
+
],
|
|
72
|
+
"unet_target_name": [],
|
|
73
|
+
"text_encoder_target_module": [],
|
|
74
|
+
"text_encoder_target_name": [],
|
|
75
|
+
},
|
|
76
|
+
"unet-convblock-only": {
|
|
77
|
+
"enable_conv": True,
|
|
78
|
+
"unet_target_module": ["ResnetBlock2D", "Downsample2D", "Upsample2D"],
|
|
79
|
+
"unet_target_name": [
|
|
80
|
+
"conv_in",
|
|
81
|
+
"conv_out",
|
|
82
|
+
],
|
|
83
|
+
"text_encoder_target_module": [],
|
|
84
|
+
"text_encoder_target_name": [],
|
|
85
|
+
},
|
|
86
|
+
}
|
lycoris/dylora.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import random
|
|
3
|
+
|
|
4
|
+
from collections import OrderedDict, abc as container_abcs
|
|
5
|
+
|
|
6
|
+
import torch
|
|
7
|
+
import torch.nn as nn
|
|
8
|
+
import torch.nn.functional as F
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DyLoraModule(nn.Module):
|
|
12
|
+
"""
|
|
13
|
+
Hadamard product Implementaion for Dynamic Low Rank adaptation
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
lora_name,
|
|
19
|
+
org_module: nn.Module,
|
|
20
|
+
multiplier=1.0,
|
|
21
|
+
lora_dim=4, alpha=1,
|
|
22
|
+
dropout=0.,
|
|
23
|
+
use_cp=False,
|
|
24
|
+
block_size=1,
|
|
25
|
+
**kwargs,
|
|
26
|
+
):
|
|
27
|
+
""" if alpha == 0 or None, alpha is rank (no scaling). """
|
|
28
|
+
super().__init__()
|
|
29
|
+
self.lora_name = lora_name
|
|
30
|
+
self.lora_dim = lora_dim
|
|
31
|
+
assert lora_dim % block_size == 0, 'lora_dim must be a multiple of block_size'
|
|
32
|
+
self.block_count = lora_dim//block_size
|
|
33
|
+
self.block_size = block_size
|
|
34
|
+
|
|
35
|
+
self.shape = org_module.weight.shape
|
|
36
|
+
if org_module.__class__.__name__ == 'Conv2d':
|
|
37
|
+
in_dim = org_module.in_channels
|
|
38
|
+
k_size = org_module.kernel_size
|
|
39
|
+
out_dim = org_module.out_channels
|
|
40
|
+
shape = (out_dim, in_dim*k_size[0]*k_size[1])
|
|
41
|
+
self.op = F.conv2d
|
|
42
|
+
self.extra_args = {
|
|
43
|
+
"stride": org_module.stride,
|
|
44
|
+
"padding": org_module.padding,
|
|
45
|
+
"dilation": org_module.dilation,
|
|
46
|
+
"groups": org_module.groups
|
|
47
|
+
}
|
|
48
|
+
else:
|
|
49
|
+
in_dim = org_module.in_features
|
|
50
|
+
out_dim = org_module.out_features
|
|
51
|
+
shape = (out_dim, in_dim)
|
|
52
|
+
self.op = F.linear
|
|
53
|
+
self.extra_args = {}
|
|
54
|
+
|
|
55
|
+
self.lora_dim = lora_dim
|
|
56
|
+
self.up_list = nn.ParameterList([
|
|
57
|
+
torch.empty(shape[0], 1)
|
|
58
|
+
for i in range(lora_dim)
|
|
59
|
+
])
|
|
60
|
+
self.up_list.requires_grad_(False)
|
|
61
|
+
self.up_update = [
|
|
62
|
+
torch.zeros_like(self.up_list[i])
|
|
63
|
+
for i in range(lora_dim)
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
self.down_list = nn.ParameterList([
|
|
67
|
+
torch.empty(1, shape[1])
|
|
68
|
+
for i in range(lora_dim)
|
|
69
|
+
])
|
|
70
|
+
self.down_list.requires_grad_(False)
|
|
71
|
+
self.down_update = [
|
|
72
|
+
torch.zeros_like(self.down_list[i])
|
|
73
|
+
for i in range(lora_dim)
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
self.index = 0
|
|
77
|
+
|
|
78
|
+
if type(alpha) == torch.Tensor:
|
|
79
|
+
alpha = alpha.detach().float().numpy() # without casting, bf16 causes error
|
|
80
|
+
alpha = lora_dim if alpha is None or alpha == 0 else alpha
|
|
81
|
+
self.scale = alpha / self.lora_dim
|
|
82
|
+
self.register_buffer('alpha', torch.tensor(alpha)) # 定数として扱える
|
|
83
|
+
|
|
84
|
+
# Need more experiences on init method
|
|
85
|
+
|
|
86
|
+
for v in self.down_list:
|
|
87
|
+
torch.nn.init.kaiming_uniform_(v, a=math.sqrt(5))
|
|
88
|
+
for v in self.up_list:
|
|
89
|
+
torch.nn.init.zeros_(v)
|
|
90
|
+
for i, v in enumerate(self.up_update):
|
|
91
|
+
v.copy_(self.up_list[i])
|
|
92
|
+
for i, v in enumerate(self.down_update):
|
|
93
|
+
v.copy_(self.down_list[i])
|
|
94
|
+
|
|
95
|
+
self.multiplier = multiplier
|
|
96
|
+
self.org_module = [org_module] # remove in applying
|
|
97
|
+
self.grad_ckpt = False
|
|
98
|
+
|
|
99
|
+
self.apply_train(0)
|
|
100
|
+
|
|
101
|
+
def state_dict(self, *args, destination=None, prefix='', keep_vars=False):
|
|
102
|
+
# TODO: Remove `args` and the parsing logic when BC allows.
|
|
103
|
+
if len(args) > 0:
|
|
104
|
+
if destination is None:
|
|
105
|
+
destination = args[0]
|
|
106
|
+
if len(args) > 1 and prefix == '':
|
|
107
|
+
prefix = args[1]
|
|
108
|
+
if len(args) > 2 and keep_vars is False:
|
|
109
|
+
keep_vars = args[2]
|
|
110
|
+
# DeprecationWarning is ignored by default
|
|
111
|
+
|
|
112
|
+
if destination is None:
|
|
113
|
+
destination = OrderedDict()
|
|
114
|
+
destination._metadata = OrderedDict()
|
|
115
|
+
|
|
116
|
+
local_metadata = dict(version=self._version)
|
|
117
|
+
if hasattr(destination, "_metadata"):
|
|
118
|
+
destination._metadata[prefix[:-1]] = local_metadata
|
|
119
|
+
|
|
120
|
+
destination[f'{prefix}alpha'] = self.alpha
|
|
121
|
+
destination[f'{prefix}lora_up.weight'] = nn.Parameter(
|
|
122
|
+
torch.concat(self.up_update, dim=1)
|
|
123
|
+
)
|
|
124
|
+
destination[f'{prefix}lora_down.weight'] = nn.Parameter(
|
|
125
|
+
torch.concat(self.down_update)
|
|
126
|
+
)
|
|
127
|
+
return destination
|
|
128
|
+
|
|
129
|
+
def apply_to(self):
|
|
130
|
+
self.org_module[0].forward = self.forward
|
|
131
|
+
|
|
132
|
+
def apply_train(self, b:int):
|
|
133
|
+
self.up_list.requires_grad_(False)
|
|
134
|
+
self.down_list.requires_grad_(False)
|
|
135
|
+
|
|
136
|
+
for i in range(self.index*self.block_size, (self.index+1)*self.block_size):
|
|
137
|
+
self.up_update[i].copy_(self.up_list[i])
|
|
138
|
+
self.down_update[i].copy_(self.down_list[i])
|
|
139
|
+
|
|
140
|
+
for i in range(b*self.block_size, (b+1)*self.block_size):
|
|
141
|
+
self.up_list[i].copy_(self.up_update[i])
|
|
142
|
+
self.down_list[i].copy_(self.down_update[i])
|
|
143
|
+
|
|
144
|
+
self.up_list.requires_grad_(True)
|
|
145
|
+
self.down_list.requires_grad_(True)
|
|
146
|
+
self.index = b
|
|
147
|
+
|
|
148
|
+
@torch.enable_grad()
|
|
149
|
+
def forward(self, x):
|
|
150
|
+
b = random.randint(0, self.block_count-1)
|
|
151
|
+
if self.up_update[b].device != self.up_list[b].device:
|
|
152
|
+
device = self.up_list[b].device
|
|
153
|
+
for i in range(self.lora_dim):
|
|
154
|
+
self.up_update[i] = self.up_update[i].to(device)
|
|
155
|
+
self.down_update[i] = self.down_update[i].to(device)
|
|
156
|
+
|
|
157
|
+
if self.training:
|
|
158
|
+
self.apply_train(b)
|
|
159
|
+
down = torch.concat(
|
|
160
|
+
list(self.down_update[:b*self.block_size])
|
|
161
|
+
+ list(self.down_list[b*self.block_size:(b+1)*self.block_size])
|
|
162
|
+
)
|
|
163
|
+
up = torch.concat(
|
|
164
|
+
list(self.up_update[:b*self.block_size])
|
|
165
|
+
+ list(self.up_list[b*self.block_size:(b+1)*self.block_size]),
|
|
166
|
+
dim=1
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
bias = None if self.org_module[0].bias is None else self.org_module[0].bias.data
|
|
170
|
+
return self.op(
|
|
171
|
+
x,
|
|
172
|
+
self.org_module[0].weight + (up@down).view(self.shape) * self.alpha/(b+1),
|
|
173
|
+
bias,
|
|
174
|
+
**self.extra_args
|
|
175
|
+
)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
FUNC_LIST = [None, None, F.linear, F.conv1d, F.conv2d, F.conv3d]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def rebuild_tucker(t, wa, wb):
|
|
10
|
+
rebuild2 = torch.einsum("i j ..., i p, j r -> p r ...", t, wa, wb)
|
|
11
|
+
return rebuild2
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def factorization(dimension: int, factor: int = -1) -> tuple[int, int]:
|
|
15
|
+
"""
|
|
16
|
+
return a tuple of two value of input dimension decomposed by the number closest to factor
|
|
17
|
+
second value is higher or equal than first value.
|
|
18
|
+
|
|
19
|
+
In LoRA with Kroneckor Product, first value is a value for weight scale.
|
|
20
|
+
second value is a value for weight.
|
|
21
|
+
|
|
22
|
+
Because of non-commutative property, A⊗B ≠ B⊗A. Meaning of two matrices is slightly different.
|
|
23
|
+
|
|
24
|
+
examples)
|
|
25
|
+
factor
|
|
26
|
+
-1 2 4 8 16 ...
|
|
27
|
+
127 -> 1, 127 127 -> 1, 127 127 -> 1, 127 127 -> 1, 127 127 -> 1, 127
|
|
28
|
+
128 -> 8, 16 128 -> 2, 64 128 -> 4, 32 128 -> 8, 16 128 -> 8, 16
|
|
29
|
+
250 -> 10, 25 250 -> 2, 125 250 -> 2, 125 250 -> 5, 50 250 -> 10, 25
|
|
30
|
+
360 -> 8, 45 360 -> 2, 180 360 -> 4, 90 360 -> 8, 45 360 -> 12, 30
|
|
31
|
+
512 -> 16, 32 512 -> 2, 256 512 -> 4, 128 512 -> 8, 64 512 -> 16, 32
|
|
32
|
+
1024 -> 32, 32 1024 -> 2, 512 1024 -> 4, 256 1024 -> 8, 128 1024 -> 16, 64
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
if factor > 0 and (dimension % factor) == 0:
|
|
36
|
+
m = factor
|
|
37
|
+
n = dimension // factor
|
|
38
|
+
if m > n:
|
|
39
|
+
n, m = m, n
|
|
40
|
+
return m, n
|
|
41
|
+
if factor < 0:
|
|
42
|
+
factor = dimension
|
|
43
|
+
m, n = 1, dimension
|
|
44
|
+
length = m + n
|
|
45
|
+
while m < n:
|
|
46
|
+
new_m = m + 1
|
|
47
|
+
while dimension % new_m != 0:
|
|
48
|
+
new_m += 1
|
|
49
|
+
new_n = dimension // new_m
|
|
50
|
+
if new_m + new_n > length or new_m > factor:
|
|
51
|
+
break
|
|
52
|
+
else:
|
|
53
|
+
m, n = new_m, new_n
|
|
54
|
+
if m > n:
|
|
55
|
+
n, m = m, n
|
|
56
|
+
return m, n
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def power2factorization(dimension: int, factor: int = -1) -> tuple[int, int]:
|
|
60
|
+
"""
|
|
61
|
+
m = 2k
|
|
62
|
+
n = 2**p
|
|
63
|
+
m*n = dim
|
|
64
|
+
"""
|
|
65
|
+
if factor == -1:
|
|
66
|
+
factor = dimension
|
|
67
|
+
|
|
68
|
+
# Find the first solution and check if it is even doable
|
|
69
|
+
m = n = 0
|
|
70
|
+
while m <= factor:
|
|
71
|
+
m += 2
|
|
72
|
+
while dimension % m != 0 and m < dimension:
|
|
73
|
+
m += 2
|
|
74
|
+
if m > factor:
|
|
75
|
+
break
|
|
76
|
+
if sum(int(i) for i in f"{dimension//m:b}") == 1:
|
|
77
|
+
n = dimension // m
|
|
78
|
+
|
|
79
|
+
return dimension // n, n
|