From 2309398be8e554c93b45c7a0e5444e41a27a2395 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 15 Sep 2023 11:38:38 -0400 Subject: [PATCH] temp fix sdxl lora --- extensions-builtin/Lora/lora.py | 10 ++---- html/locale_en.json | 2 +- modules/lora_diffusers.py | 57 +++++++++++++++++++++------------ modules/shared.py | 35 +++++--------------- wiki | 2 +- 5 files changed, 49 insertions(+), 57 deletions(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index fbac7e8fc..02bb2092c 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -145,11 +145,11 @@ def assign_lora_names_to_compvis_modules(sd_model): sd_model.lora_layer_mapping = lora_layer_mapping -def load_diffuser_lora(name, lora_on_disk, multiplier): +def load_diffuser_lora(name, lora_on_disk, multiplier, num_loras): lora = LoraModule(name, lora_on_disk) lora.mtime = os.path.getmtime(lora_on_disk.filename) from modules.lora_diffusers import load_diffusers_lora - load_diffusers_lora(name, lora_on_disk, multiplier) + load_diffusers_lora(name, lora_on_disk, multiplier, num_loras) return lora @@ -241,22 +241,18 @@ def load_loras(names, multipliers=None): for i, name in enumerate(names): lora = already_loaded.get(name, None) if shared.backend == shared.Backend.ORIGINAL else None - lora_on_disk = loras_on_disk[i] - if lora_on_disk is not None: if lora is None or os.path.getmtime(lora_on_disk.filename) > lora.mtime: try: if shared.backend == shared.Backend.DIFFUSERS: - lora = load_diffuser_lora(name, lora_on_disk, multipliers[i] if multipliers else 1.0) + lora = load_diffuser_lora(name, lora_on_disk, multipliers[i] if multipliers else 1.0, len(names)) else: lora = load_lora(name, lora_on_disk) except Exception as e: errors.display(e, f"loading Lora {lora_on_disk.filename}") continue - lora.mentioned_name = name - lora_on_disk.read_hash() if lora is None: diff --git a/html/locale_en.json b/html/locale_en.json index 2c6e77e49..0238f9a67 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -585,7 +585,7 @@ {"id":"","label":"Enable attention slicing","localized":"","hint":"Performs attention computation in steps instead of all at once. Slower inference times, but greatly reduced memory usage"}, {"id":"","label":"Diffusers model loading variant","localized":"","hint":""}, {"id":"","label":"Diffusers VAE loading variant","localized":"","hint":""}, - {"id":"","label":"Diffusers LoRA loading variant","localized":"","hint":"'sequential apply' loads and applies each LoRA in order of appearance, 'merge and apply' loads all LoRAs and merges them in-memory before applying to model, 'diffusers default' uses single LoRA loading method"}, + {"id":"","label":"Diffusers LoRA loading variant","localized":"","hint":"'sequential apply' loads and applies each LoRA in order of appearance, 'merge and apply' loads all LoRAs and merges them in-memory before applying to model, 'diffusers' uses diffusers default LoRA loading method"}, {"id":"","label":"Torch inference mode","localized":"","hint":"Use torch inference mode"}, {"id":"","label":"inference-mode","localized":"","hint":"Use torch.inference_mode"}, {"id":"","label":"no-grad","localized":"","hint":"Use torch.no_grad"}, diff --git a/modules/lora_diffusers.py b/modules/lora_diffusers.py index 59bf57b24..82d7a10b0 100644 --- a/modules/lora_diffusers.py +++ b/modules/lora_diffusers.py @@ -1,3 +1,4 @@ +import time import diffusers import diffusers.models.lora as diffusers_lora # from modules import shared @@ -7,13 +8,15 @@ import modules.shared as shared lora_state = { # TODO Lora state for Diffusers 'multiplier': [], 'active': False, - 'loaded': 0, - 'all_loras': [] + 'loaded': [], + 'all_loras': [], } def unload_diffusers_lora(): try: pipe = shared.sd_model - if shared.opts.diffusers_lora_loader == "diffusers default": + if shared.opts.diffusers_lora_loader == "diffusers": + if len(lora_state['loaded']) > 1: + pipe.unfuse_lora() pipe.unload_lora_weights() pipe._remove_text_encoder_monkey_patch() # pylint: disable=W0212 proc_cls_name = next(iter(pipe.unet.attn_processors.values())).__class__.__name__ @@ -29,22 +32,30 @@ def unload_diffusers_lora(): if shared.opts.diffusers_lora_loader == "sequential apply": lora_network.unapply_to() lora_state['active'] = False - lora_state['loaded'] = 0 + lora_state['loaded'].clear() lora_state['all_loras'] = [] lora_state['multiplier'] = [] - except Exception as e: - shared.log.error(f"Diffusers LoRA unloading failed: {e}") + shared.log.error(f"LoRA unload failed: {e}") -def load_diffusers_lora(name, lora, strength = 1.0): +def load_diffusers_lora(name, lora, strength = 1.0, num_loras = 1): + if f'{lora.filename}:{strength}' in lora_state['loaded']: + shared.log.info(f'LoRA cached: {name} strength={strength}') + return try: + t0 = time.time() pipe = shared.sd_model lora_state['active'] = True - lora_state['loaded'] += 1 lora_state['multiplier'].append(strength) - if shared.opts.diffusers_lora_loader == "diffusers default": - pipe.load_lora_weights(lora.filename, cache_dir=shared.opts.diffusers_dir, local_files_only=True, lora_scale=strength) + fuse = 0 + if shared.opts.diffusers_lora_loader.startswith("diffusers"): + pipe.load_lora_weights(lora.filename, cache_dir=shared.opts.diffusers_dir, local_files_only=True, lora_scale=strength, low_cpu_mem_usage=True) + if num_loras > 1: + t2 = time.time() + pipe.fuse_lora(lora_scale=strength) + fuse = time.time() - t2 + lora_state['loaded'].append(f'{lora.filename}:{strength}') else: from safetensors.torch import load_file lora_sd = load_file(lora.filename) @@ -60,20 +71,24 @@ def load_diffusers_lora(name, lora, strength = 1.0): lora_network.to(shared.device, dtype=pipe.unet.dtype) lora_network.apply_to(multiplier=strength) lora_state['all_loras'].append(lora_network) - shared.log.info(f"LoRA loaded: {name} strength={strength} loader={shared.opts.diffusers_lora_loader}") + lora_state['loaded'].append(f'{lora.filename}:{strength}') + t1 = time.time() + fuse = f'fuse={fuse:.2f}s' if fuse > 0 else '' + shared.log.info(f'LoRA loaded: {name} strength={strength} loader="{shared.opts.diffusers_lora_loader}" lora={t1-t0:.2f}s {fuse}') except Exception as e: - shared.log.error(f"LoRA loading failed: {name} {e}") + lines = str(e).splitlines() + shared.log.error(f'LoRA loading failed: {name} loader="{shared.opts.diffusers_lora_loader}" {lines[0]}') # Diffusersで動くLoRA。このファイル単独で完結する。 # LoRA module for Diffusers. This file works independently. -import bisect -import math -from typing import Any, Dict, List, Mapping, Optional, Union -from diffusers import UNet2DConditionModel -from tqdm import tqdm -from transformers import CLIPTextModel -import torch +import bisect # pylint: disable=wrong-import-order +import math # pylint: disable=wrong-import-order +from typing import Any, Dict, List, Mapping, Optional, Union # pylint: disable=wrong-import-order +from diffusers import UNet2DConditionModel # pylint: disable=wrong-import-order +from tqdm import tqdm # pylint: disable=wrong-import-order +from transformers import CLIPTextModel # pylint: disable=wrong-import-order +import torch # pylint: disable=wrong-import-order def make_unet_conversion_map() -> Dict[str, str]: @@ -496,7 +511,7 @@ class LoRANetwork(torch.nn.Module): # pylint: disable=abstract-method for lora in tqdm(self.text_encoder_loras + self.unet_loras): lora.restore_from(multiplier) - def load_state_dict(self, state_dict: Mapping[str, Any], strict: bool = True): + def load_state_dict(self, state_dict: Mapping[str, Any], strict: bool = True): # pylint: disable=arguments-differ # convert SDXL Stability AI's state dict to Diffusers' based state dict map_keys = list(UNET_CONVERSION_MAP.keys()) # prefix of U-Net modules map_keys.sort() @@ -514,7 +529,7 @@ class LoRANetwork(torch.nn.Module): # pylint: disable=abstract-method # because V2 LoRA is based on U-Net created by use_linear_projection=False my_state_dict = self.state_dict() for key in state_dict.keys(): - if state_dict[key].size() != my_state_dict[key].size(): + if state_dict[key].size() != my_state_dict[key].size(): # pylint: disable=unsubscriptable-object # print(f"convert {key} from {state_dict[key].size()} to {my_state_dict[key].size()}") state_dict[key] = state_dict[key].view(my_state_dict[key].size()) diff --git a/modules/shared.py b/modules/shared.py index a6d686b9a..252baa251 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -438,7 +438,7 @@ options_templates.update(options_section(('diffusers', "Diffusers Settings"), { "diffusers_attention_slicing": OptionInfo(False, "Enable attention slicing"), "diffusers_model_load_variant": OptionInfo("default", "Diffusers model loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}), "diffusers_vae_load_variant": OptionInfo("default", "Diffusers VAE loading variant", gr.Radio, lambda: {"choices": ['default', 'fp32', 'fp16']}), - "diffusers_lora_loader": OptionInfo("diffusers default" if cmd_opts.use_openvino else "sequential apply", "Diffusers LoRA loading variant", gr.Radio, lambda: {"choices": ['sequential apply', 'merge and apply', 'diffusers default']}), + "diffusers_lora_loader": OptionInfo("diffusers", "Diffusers LoRA loading variant", gr.Radio, lambda: {"choices": ['diffusers', 'sequential apply', 'merge and apply']}), "diffusers_force_zeros": OptionInfo(True, "Force zeros for prompts when empty"), "diffusers_aesthetics_score": OptionInfo(False, "Require aesthetics score"), })) @@ -832,6 +832,7 @@ else: opts.data['sd_backend'] = 'diffusers' if backend == Backend.DIFFUSERS else 'original' opts.data['uni_pc_lower_order_final'] = opts.schedulers_use_loworder opts.data['uni_pc_order'] = opts.schedulers_solver_order +opts.data['diffusers_lora_loader'] = 'diffusers' # TODO broken in diffusers=0.21 log.info(f'Engine: backend={backend} compute={devices.backend} mode={devices.inference_context.__name__} device={devices.get_optimal_device_name()}') log.info(f'Device: {print_dict(devices.get_gpu_info())}') @@ -885,37 +886,17 @@ def reload_gradio_theme(theme_name=None): log.info(f'Loading UI theme: name={theme_name} style={opts.theme_style}') -class TotalTQDM: +class TotalTQDM: # compatibility with previous global-tqdm def __init__(self): - self._tqdm = None - + pass def reset(self): - self._tqdm = tqdm.tqdm( - desc="Total", - total=state.job_count * state.sampling_steps, - position=1, - ) - + pass def update(self): - if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars: - return - if self._tqdm is None: - self.reset() - self._tqdm.update() - + pass def updateTotal(self, new_total): - if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars: - return - if self._tqdm is None: - self.reset() - self._tqdm.total = new_total - + pass def clear(self): - if self._tqdm is not None: - self._tqdm.refresh() - self._tqdm.close() - self._tqdm = None - + pass total_tqdm = TotalTQDM() diff --git a/wiki b/wiki index fea51bf38..d43376f66 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit fea51bf38c010520dbf30fb8cb58043f94fb2e8e +Subproject commit d43376f66fe454d2911a3b284077910df2b16b23