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.
Files changed (62) hide show
  1. lycoris/__init__.py +26 -0
  2. lycoris/concept.py +254 -0
  3. lycoris/config.py +86 -0
  4. lycoris/dylora.py +175 -0
  5. lycoris/functional/__init__.py +13 -0
  6. lycoris/functional/general.py +79 -0
  7. lycoris/functional/locon.py +102 -0
  8. lycoris/functional/loha.py +187 -0
  9. lycoris/functional/lokr.py +259 -0
  10. lycoris/hcp/__init__.py +4 -0
  11. lycoris/hcp/base.py +153 -0
  12. lycoris/hcp/diag_oft.py +62 -0
  13. lycoris/hcp/loha.py +85 -0
  14. lycoris/hcp/lokr.py +131 -0
  15. lycoris/ia3.py +68 -0
  16. lycoris/kohya/__init__.py +866 -0
  17. lycoris/kohya/kohya_model_utils.py +1165 -0
  18. lycoris/kohya/kohya_utils.py +48 -0
  19. lycoris/kohya/model_utils.py +1633 -0
  20. lycoris/kohya/original_unet.py +1760 -0
  21. lycoris/kohya/sdxl_model_util.py +581 -0
  22. lycoris/kohya/sdxl_original_unet.py +1243 -0
  23. lycoris/kohya/test_hyperdream.py +527 -0
  24. lycoris/kohya/utils.py +48 -0
  25. lycoris/kohya.py +850 -0
  26. lycoris/kohya_model_utils.py +1165 -0
  27. lycoris/kohya_utils.py +48 -0
  28. lycoris/locon.py +86 -0
  29. lycoris/logging.py +36 -0
  30. lycoris/loha.py +199 -0
  31. lycoris/lokr.py +220 -0
  32. lycoris/modules/__init__.py +147 -0
  33. lycoris/modules/attention.py +259 -0
  34. lycoris/modules/base.py +278 -0
  35. lycoris/modules/boft.py +198 -0
  36. lycoris/modules/diag_oft.py +182 -0
  37. lycoris/modules/dylora.py +157 -0
  38. lycoris/modules/full.py +166 -0
  39. lycoris/modules/glokr.py +300 -0
  40. lycoris/modules/glora.py +204 -0
  41. lycoris/modules/hyperlycoris.py +3 -0
  42. lycoris/modules/hypernet/__init__.py +1 -0
  43. lycoris/modules/hypernet/generater.py +223 -0
  44. lycoris/modules/hypernet/text_encoder.py +266 -0
  45. lycoris/modules/ia3.py +130 -0
  46. lycoris/modules/locon.py +279 -0
  47. lycoris/modules/loha.py +268 -0
  48. lycoris/modules/lokr.py +469 -0
  49. lycoris/modules/norms.py +104 -0
  50. lycoris/utils/__init__.py +669 -0
  51. lycoris/utils/bnb.py +29 -0
  52. lycoris/utils/general.py +5 -0
  53. lycoris/utils/logger.py +35 -0
  54. lycoris/utils/preset.py +9 -0
  55. lycoris/utils/xformers_utils.py +13 -0
  56. lycoris/utils.py +504 -0
  57. lycoris/wrapper.py +537 -0
  58. lycoris_lora-3.0.0.dev9.dist-info/LICENSE.md +201 -0
  59. lycoris_lora-3.0.0.dev9.dist-info/METADATA +14 -0
  60. lycoris_lora-3.0.0.dev9.dist-info/RECORD +62 -0
  61. lycoris_lora-3.0.0.dev9.dist-info/WHEEL +5 -0
  62. 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,13 @@
1
+ from .general import (
2
+ rebuild_tucker,
3
+ factorization,
4
+ power2factorization,
5
+ )
6
+ from .locon import (
7
+ lora_diff_weight,
8
+ lora_bypass_forward_diff,
9
+ )
10
+ from .loha import (
11
+ loha_diff_weight,
12
+ loha_bypass_forward_diff,
13
+ )
@@ -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