diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d92c6966..07aad999d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,6 +102,8 @@ And it also includes fixes for all reported issues so far - faster json parsing - faster lora indexing - lazy load optional imports + - batch embedding load, thanks @midcoastal + 10x+ faster embeddings load for large number of embeddings, now works for 1000+ embeddings - **extra networks** - 4x faster civitai metadata and previews lookup - better display and selection of tags & trigger words diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index 8870e3568..ba05e1ea2 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit 8870e35682f1df50fed2956e00839507ac739dd7 +Subproject commit ba05e1ea20df0e29a3dce9fcbe3ecc30777a5271 diff --git a/modules/patches.py b/modules/patches.py index 6e4fd99d1..5305b6fab 100644 --- a/modules/patches.py +++ b/modules/patches.py @@ -4,54 +4,40 @@ from typing import Optional def patch(key, obj, field, replacement, add_if_not_exists:bool = False): """Replaces a function in a module or a class. - Also stores the original function in this module, possible to be retrieved via original(key, obj, field). If the function is already replaced by this caller (key), an exception is raised -- use undo() before that. - Arguments: key: identifying information for who is doing the replacement. You can use __name__. obj: the module or the class field: name of the function as a string replacement: the new function - Returns: the original function """ - patch_key = (obj, field) if patch_key in originals[key]: raise RuntimeError(f"patch for {field} is already applied") - if not hasattr(obj, field) and not add_if_not_exists: raise AttributeError(f"type {type(obj)} '{type.__name__}' has no attribute '{field}'") - original_func = getattr(obj, field, None) originals[key][patch_key] = original_func - setattr(obj, field, replacement) - return original_func def undo(key, obj, field): """Undoes the peplacement by the patch(). - If the function is not replaced, raises an exception. - Arguments: key: identifying information for who is doing the replacement. You can use __name__. obj: the module or the class field: name of the function as a string - Returns: Always None """ - patch_key = (obj, field) - if patch_key not in originals[key]: raise RuntimeError(f"there is no patch for {field} to undo") - original_func = originals[key].pop(patch_key) if original_func is None: delattr(obj, field) @@ -62,7 +48,6 @@ def undo(key, obj, field): def original(key, obj, field): """Returns the original function for the patch created by the patch() function""" patch_key = (obj, field) - return originals[key].get(patch_key, None) diff --git a/modules/prompt_parser_diffusers.py b/modules/prompt_parser_diffusers.py index d30f95fb7..12f275631 100644 --- a/modules/prompt_parser_diffusers.py +++ b/modules/prompt_parser_diffusers.py @@ -137,7 +137,7 @@ def prepare_embedding_providers(pipe, clip_skip): provider = EmbeddingsProvider(tokenizer=pipe.tokenizer, text_encoder=pipe.text_encoder, truncate=False, returned_embeddings_type=embedding_type, device=device) embeddings_providers.append(provider) - if hasattr(pipe, "tokenizer_2") and getattr(pipe, "text_encoder_2"): + if hasattr(pipe, "tokenizer_2") and hasattr(pipe, "text_encoder_2"): provider = EmbeddingsProvider(tokenizer=pipe.tokenizer_2, text_encoder=pipe.text_encoder_2, truncate=False, returned_embeddings_type=embedding_type, device=device) embeddings_providers.append(provider) diff --git a/modules/textual_inversion/loaders.py b/modules/textual_inversion/loaders.py index f1e42f0b0..7b845fad7 100644 --- a/modules/textual_inversion/loaders.py +++ b/modules/textual_inversion/loaders.py @@ -1,24 +1,19 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Union - import torch -from diffusers.loaders.textual_inversion import ( - TextualInversionLoaderMixin, - load_textual_inversion_state_dicts, - logger, - nn, -) - +from torch import nn +from diffusers.loaders.textual_inversion import TextualInversionLoaderMixin, load_textual_inversion_state_dicts from modules import shared from modules.patches import patch_method + if TYPE_CHECKING: from transformers import PreTrainedModel, PreTrainedTokenizer - try: from accelerate.hooks import AlignDevicesHook, CpuOffload, remove_hook_from_module except Exception: pass + @patch_method(TextualInversionLoaderMixin) def load_textual_inversion( self: TextualInversionLoaderMixin, @@ -28,12 +23,10 @@ def load_textual_inversion( text_encoder: Optional["PreTrainedModel"] = None, **kwargs, # pylint: disable=W0613 ): - # 1. Set correct tokenizer and text encoder tokenizer: PreTrainedTokenizer = tokenizer or getattr(self, "tokenizer", None) text_encoder: PreTrainedModel = text_encoder or getattr(self, "text_encoder", None) loaded_model_names_or_paths = {} - assert tokenizer and text_encoder, 'Can not resolve `tokenizer` or `text_encoder`' # 2. Normalize inputs @@ -99,9 +92,7 @@ def load_textual_inversion( if hasattr(component, "_hf_hook"): is_model_cpu_offload = isinstance(getattr(component, "_hf_hook"), CpuOffload) # noqa: B009 is_sequential_cpu_offload = isinstance(getattr(component, "_hf_hook"), AlignDevicesHook) # noqa: B009 - logger.info( - "Accelerate hooks detected. Since you have called `load_textual_inversion()`, the previous hooks will be first removed. Then the textual inversion parameters will be loaded and the hooks will be applied again." - ) + shared.log.debug("Accelerate hooks detected. Since you have called `load_textual_inversion()`, the previous hooks will be first removed. Then the textual inversion parameters will be loaded and the hooks will be applied again.") remove_hook_from_module(component, recurse=is_sequential_cpu_offload) # 7.2 save expected device and dtype diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index d7a1a35ce..81fbb26d5 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -1,3 +1,4 @@ +from typing import List, Optional, Union import csv import html import os @@ -17,8 +18,8 @@ from modules.textual_inversion.ti_logging import save_settings_to_file from typing import List, Optional, Union from modules.files_cache import directory_files, directory_mtime, extension_filter -TokenToAdd = namedtuple("TokenToAdd", ["clip_l", "clip_g"]) +TokenToAdd = namedtuple("TokenToAdd", ["clip_l", "clip_g"]) TextualInversionTemplate = namedtuple("TextualInversionTemplate", ["name", "path"]) textual_inversion_templates = {} diff --git a/scripts/photomaker_model.py b/scripts/photomaker_model.py index 0e1ce34bc..25bbbe353 100755 --- a/scripts/photomaker_model.py +++ b/scripts/photomaker_model.py @@ -221,12 +221,14 @@ class PhotoMakerStableDiffusionXLPipeline(StableDiffusionXLPipeline): ): device = device or self._execution_device + """ if prompt is not None and isinstance(prompt, str): batch_size = 1 elif prompt is not None and isinstance(prompt, list): batch_size = len(prompt) else: batch_size = prompt_embeds.shape[0] + """ # Find the token id of the trigger word image_token_id = self.tokenizer_2.convert_tokens_to_ids(self.trigger_word)