Compare commits

..

3 Commits

Author SHA1 Message Date
Lincoln Stein
020fb6c1e4 bump version number for release 2.3.5 2023-04-24 23:12:32 -04:00
Lincoln Stein
c63ef500b6 Merge branch 'v2.3' into bugfix/control-model-revision 2023-04-25 03:58:00 +01:00
Lincoln Stein
5f1d311c52 Merge branch 'v2.3' into bugfix/control-model-revision 2023-04-25 03:23:12 +01:00
24 changed files with 63 additions and 162 deletions

2
.gitignore vendored
View File

@@ -233,3 +233,5 @@ installer/install.sh
installer/update.bat
installer/update.sh
# no longer stored in source directory
models

View File

@@ -41,16 +41,6 @@ Windows systems). If the `loras` folder does not already exist, just
create it. The vast majority of LoRA models use the Kohya file format,
which is a type of `.safetensors` file.
!!! warning "LoRA Naming Restrictions"
InvokeAI will only recognize LoRA files that contain the
characters a-z, A-Z, 0-9 and the underscore character
_. Other characters, including the hyphen, will cause the
LoRA file not to load. These naming restrictions may be
relaxed in the future, but for now you will need to rename
files that contain hyphens, commas, brackets, and other
non-word characters.
You may change where InvokeAI looks for the `loras` folder by passing the
`--lora_directory` option to the `invoke.sh`/`invoke.bat` launcher, or
by placing the option in `invokeai.init`. For example:

View File

@@ -33,11 +33,6 @@ title: Overview
Restore mangled faces and make images larger with upscaling. Also see
the [Embiggen Upscaling Guide](EMBIGGEN.md).
- The [Using LoRA Models](LORAS.md)
Add custom subjects and styles using HuggingFace's repository of
embeddings.
- The [Concepts Library](CONCEPTS.md)
Add custom subjects and styles using HuggingFace's repository of

View File

@@ -79,7 +79,7 @@ title: Manual Installation, Linux
and obtaining an access token for downloading. It will then download and
install the weights files for you.
Please look [here](../020_INSTALL_MANUAL.md) for a manual process for doing
Please look [here](../INSTALL_MANUAL.md) for a manual process for doing
the same thing.
7. Start generating images!

View File

@@ -75,7 +75,7 @@ Note that you will need NVIDIA drivers, Python 3.10, and Git installed beforehan
obtaining an access token for downloading. It will then download and install the
weights files for you.
Please look [here](../020_INSTALL_MANUAL.md) for a manual process for doing the
Please look [here](../INSTALL_MANUAL.md) for a manual process for doing the
same thing.
8. Start generating images!

View File

@@ -1,5 +0,0 @@
mkdocs
mkdocs-material>=8, <9
mkdocs-git-revision-date-localized-plugin
mkdocs-redirects==1.2.0

View File

@@ -243,15 +243,16 @@ class InvokeAiInstance:
# Note that we're installing pinned versions of torch and
# torchvision here, which *should* correspond to what is
# in pyproject.toml.
# in pyproject.toml. This is to prevent torch 2.0 from
# being installed and immediately uninstalled and replaced with 1.13
pip = local[self.pip]
(
pip[
"install",
"--require-virtualenv",
"torch~=2.0.0",
"torchvision>=0.14.1",
"torch~=1.13.1",
"torchvision~=0.14.1",
"--force-reinstall",
"--find-links" if find_links is not None else None,
find_links,

View File

@@ -25,7 +25,7 @@ from invokeai.backend.modules.parameters import parameters_to_command
import invokeai.frontend.dist as frontend
from ldm.generate import Generate
from ldm.invoke.args import Args, APP_ID, APP_VERSION, calculate_init_img_hash
from ldm.invoke.concepts_lib import get_hf_concepts_lib
from ldm.invoke.concepts_lib import HuggingFaceConceptsLibrary
from ldm.invoke.conditioning import (
get_tokens_for_prompt_object,
get_prompt_structure,
@@ -538,7 +538,7 @@ class InvokeAIWebServer:
try:
local_triggers = self.generate.model.textual_inversion_manager.get_all_trigger_strings()
locals = [{'name': x} for x in sorted(local_triggers, key=str.casefold)]
concepts = get_hf_concepts_lib().list_concepts(minimum_likes=5)
concepts = HuggingFaceConceptsLibrary().list_concepts(minimum_likes=5)
concepts = [{'name': f'<{x}>'} for x in sorted(concepts, key=str.casefold) if f'<{x}>' not in local_triggers]
socketio.emit("foundTextualInversionTriggers", {'local_triggers': locals, 'huggingface_concepts': concepts})
except Exception as e:

View File

@@ -13,16 +13,11 @@ import time
import traceback
from typing import List
import warnings
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning)
import torch
import cv2
import diffusers
import numpy as np
import skimage
import torch
import transformers
from diffusers.pipeline_utils import DiffusionPipeline
from diffusers.utils.import_utils import is_xformers_available
@@ -984,15 +979,13 @@ class Generate:
seed_everything(random.randrange(0, np.iinfo(np.uint32).max))
if self.embedding_path and not model_data.get("ti_embeddings_loaded"):
print(f'>> Loading embeddings from {self.embedding_path}')
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning)
for root, _, files in os.walk(self.embedding_path):
for name in files:
ti_path = os.path.join(root, name)
self.model.textual_inversion_manager.load_textual_inversion(
ti_path, defer_injecting_tokens=True
)
model_data["ti_embeddings_loaded"] = True
for root, _, files in os.walk(self.embedding_path):
for name in files:
ti_path = os.path.join(root, name)
self.model.textual_inversion_manager.load_textual_inversion(
ti_path, defer_injecting_tokens=True
)
model_data["ti_embeddings_loaded"] = True
print(
f'>> Textual inversion triggers: {", ".join(sorted(self.model.textual_inversion_manager.get_all_trigger_strings()))}'
)

View File

@@ -9,6 +9,7 @@ from pathlib import Path
from typing import Union
import click
from compel import PromptParser
if sys.platform == "darwin":

View File

@@ -1,3 +1 @@
__version__='2.3.5.post2'
__version__='2.3.5-rc1'

View File

@@ -620,10 +620,7 @@ def convert_ldm_vae_checkpoint(checkpoint, config):
for key in keys:
if key.startswith(vae_key):
vae_state_dict[key.replace(vae_key, "")] = checkpoint.get(key)
new_checkpoint = convert_ldm_vae_state_dict(vae_state_dict,config)
return new_checkpoint
def convert_ldm_vae_state_dict(vae_state_dict, config):
new_checkpoint = {}
new_checkpoint["encoder.conv_in.weight"] = vae_state_dict["encoder.conv_in.weight"]

View File

@@ -12,14 +12,6 @@ from urllib import request, error as ul_error
from huggingface_hub import HfFolder, hf_hub_url, ModelSearchArguments, ModelFilter, HfApi
from ldm.invoke.globals import Globals
singleton = None
def get_hf_concepts_lib():
global singleton
if singleton is None:
singleton = HuggingFaceConceptsLibrary()
return singleton
class HuggingFaceConceptsLibrary(object):
def __init__(self, root=None):
'''

View File

@@ -6,7 +6,6 @@ import os
import platform
import psutil
import requests
import pkg_resources
from rich import box, print
from rich.console import Console, group
from rich.panel import Panel
@@ -40,7 +39,7 @@ def invokeai_is_running()->bool:
if matches:
print(f':exclamation: [bold red]An InvokeAI instance appears to be running as process {p.pid}[/red bold]')
return True
except (psutil.AccessDenied,psutil.NoSuchProcess):
except psutil.AccessDenied:
continue
return False
@@ -73,20 +72,10 @@ def welcome(versions: dict):
)
console.line()
def get_extras():
extras = ''
try:
dist = pkg_resources.get_distribution('xformers')
extras = '[xformers]'
except pkg_resources.DistributionNotFound:
pass
return extras
def main():
versions = get_versions()
if invokeai_is_running():
print(f':exclamation: [bold red]Please terminate all running instances of InvokeAI before updating.[/red bold]')
input('Press any key to continue...')
return
welcome(versions)
@@ -105,15 +94,13 @@ def main():
elif choice=='4':
branch = Prompt.ask('Enter an InvokeAI branch name')
extras = get_extras()
print(f':crossed_fingers: Upgrading to [yellow]{tag if tag else release}[/yellow]')
if release:
cmd = f"pip install 'invokeai{extras} @ {INVOKE_AI_SRC}/{release}.zip' --use-pep517 --upgrade"
cmd = f'pip install {INVOKE_AI_SRC}/{release}.zip --use-pep517 --upgrade'
elif tag:
cmd = f"pip install 'invokeai{extras} @ {INVOKE_AI_TAG}/{tag}.zip' --use-pep517 --upgrade"
cmd = f'pip install {INVOKE_AI_TAG}/{tag}.zip --use-pep517 --upgrade'
else:
cmd = f"pip install 'invokeai{extras} @ {INVOKE_AI_BRANCH}/{branch}.zip' --use-pep517 --upgrade"
cmd = f'pip install {INVOKE_AI_BRANCH}/{branch}.zip --use-pep517 --upgrade'
print('')
print('')
if os.system(cmd)==0:

View File

@@ -111,6 +111,7 @@ def install_requested_models(
if len(external_models)>0:
print("== INSTALLING EXTERNAL MODELS ==")
for path_url_or_repo in external_models:
print(f'DEBUG: path_url_or_repo = {path_url_or_repo}')
try:
model_manager.heuristic_import(
path_url_or_repo,

View File

@@ -400,15 +400,8 @@ class StableDiffusionGeneratorPipeline(StableDiffusionPipeline):
@property
def _submodels(self) -> Sequence[torch.nn.Module]:
module_names, _, _ = self.extract_init_dict(dict(self.config))
submodels = []
for name in module_names.keys():
if hasattr(self, name):
value = getattr(self, name)
else:
value = getattr(self.config, name)
if isinstance(value, torch.nn.Module):
submodels.append(value)
return submodels
values = [getattr(self, name) for name in module_names.keys()]
return [m for m in values if isinstance(m, torch.nn.Module)]
def image_from_embeddings(self, latents: torch.Tensor, num_inference_steps: int,
conditioning_data: ConditioningData,
@@ -479,7 +472,7 @@ class StableDiffusionGeneratorPipeline(StableDiffusionPipeline):
step_count=len(self.scheduler.timesteps)
):
yield PipelineIntermediateState(run_id=run_id, step=-1, timestep=self.scheduler.config.num_train_timesteps,
yield PipelineIntermediateState(run_id=run_id, step=-1, timestep=self.scheduler.num_train_timesteps,
latents=latents)
batch_size = latents.shape[0]
@@ -763,7 +756,7 @@ class StableDiffusionGeneratorPipeline(StableDiffusionPipeline):
@property
def channels(self) -> int:
"""Compatible with DiffusionWrapper"""
return self.unet.config.in_channels
return self.unet.in_channels
def decode_latents(self, latents):
# Explicit call to get the vae loaded, since `decode` isn't the forward method.

View File

@@ -9,6 +9,7 @@ from __future__ import annotations
import contextlib
import gc
import hashlib
import io
import os
import re
import sys
@@ -30,10 +31,11 @@ from huggingface_hub import scan_cache_dir
from omegaconf import OmegaConf
from omegaconf.dictconfig import DictConfig
from picklescan.scanner import scan_file_path
from ldm.invoke.devices import CPU_DEVICE
from ldm.invoke.generator.diffusers_pipeline import StableDiffusionGeneratorPipeline
from ldm.invoke.globals import Globals, global_cache_dir
from ldm.util import ask_user, download_with_resume, url_attachment_name
from ldm.util import ask_user, download_with_resume, instantiate_from_config, url_attachment_name
class SDLegacyType(Enum):
@@ -368,9 +370,8 @@ class ModelManager(object):
print(
f">> Converting legacy checkpoint {model_name} into a diffusers model..."
)
from .ckpt_to_diffuser import (
load_pipeline_from_original_stable_diffusion_ckpt,
)
from ldm.invoke.ckpt_to_diffuser import load_pipeline_from_original_stable_diffusion_ckpt
if self._has_cuda():
torch.cuda.empty_cache()
pipeline = load_pipeline_from_original_stable_diffusion_ckpt(
@@ -432,7 +433,7 @@ class ModelManager(object):
**fp_args,
)
except OSError as e:
if 'Revision Not Found' in str(e):
if str(e).startswith("fp16 is not a valid"):
pass
else:
print(
@@ -1155,7 +1156,7 @@ class ModelManager(object):
return self.device.type == "cuda"
def _diffuser_sha256(
self, name_or_path: Union[str, Path], chunksize=16777216
self, name_or_path: Union[str, Path], chunksize=4096
) -> Union[str, bytes]:
path = None
if isinstance(name_or_path, Path):
@@ -1229,17 +1230,6 @@ class ModelManager(object):
return vae_path
def _load_vae(self, vae_config) -> AutoencoderKL:
using_fp16 = self.precision == "float16"
dtype = torch.float16 if using_fp16 else torch.float32
# Handle the common case of a user shoving a VAE .ckpt into
# the vae field for a diffusers. We convert it into diffusers
# format and use it.
if isinstance(vae_config,(str,Path)):
return self.convert_vae(vae_config).to(dtype=dtype)
elif isinstance(vae_config,DictConfig) and (vae_path := vae_config.get('path')):
return self.convert_vae(vae_path).to(dtype=dtype)
vae_args = {}
try:
name_or_path = self.model_name_or_path(vae_config)
@@ -1247,6 +1237,7 @@ class ModelManager(object):
return None
if name_or_path is None:
return None
using_fp16 = self.precision == "float16"
vae_args.update(
cache_dir=global_cache_dir("hub"),
@@ -1286,32 +1277,6 @@ class ModelManager(object):
return vae
@staticmethod
def convert_vae(vae_path: Union[Path,str])->AutoencoderKL:
print(" | A checkpoint VAE was detected. Converting to diffusers format.")
vae_path = Path(Globals.root,vae_path).resolve()
from .ckpt_to_diffuser import (
create_vae_diffusers_config,
convert_ldm_vae_state_dict,
)
vae_path = Path(vae_path)
if vae_path.suffix in ['.pt','.ckpt']:
vae_state_dict = torch.load(vae_path, map_location="cpu")
else:
vae_state_dict = safetensors.torch.load_file(vae_path)
if 'state_dict' in vae_state_dict:
vae_state_dict = vae_state_dict['state_dict']
# TODO: see if this works with 1.x inpaint models and 2.x models
config_file_path = Path(Globals.root,"configs/stable-diffusion/v1-inference.yaml")
original_conf = OmegaConf.load(config_file_path)
vae_config = create_vae_diffusers_config(original_conf, image_size=512) # TODO: fix
diffusers_vae = convert_ldm_vae_state_dict(vae_state_dict,vae_config)
vae = AutoencoderKL(**vae_config)
vae.load_state_dict(diffusers_vae)
return vae
@staticmethod
def _delete_model_from_cache(repo_id):
cache_info = scan_cache_dir(global_cache_dir("diffusers"))

View File

@@ -13,7 +13,7 @@ import re
import atexit
from typing import List
from ldm.invoke.args import Args
from ldm.invoke.concepts_lib import get_hf_concepts_lib
from ldm.invoke.concepts_lib import HuggingFaceConceptsLibrary
from ldm.invoke.globals import Globals
from ldm.modules.lora_manager import LoraManager
@@ -287,7 +287,7 @@ class Completer(object):
def _concept_completions(self, text, state):
if self.concepts is None:
# cache Concepts() instance so we can check for updates in concepts_list during runtime.
self.concepts = get_hf_concepts_lib()
self.concepts = HuggingFaceConceptsLibrary()
self.embedding_terms.update(set(self.concepts.list_concepts()))
else:
self.embedding_terms.update(set(self.concepts.list_concepts()))

View File

@@ -14,6 +14,7 @@ from torch import nn
from compel.cross_attention_control import Arguments
from diffusers.models.unet_2d_condition import UNet2DConditionModel
from diffusers.models.cross_attention import AttnProcessor
from ldm.invoke.devices import torch_dtype
@@ -162,7 +163,7 @@ class Context:
class InvokeAICrossAttentionMixin:
"""
Enable InvokeAI-flavoured Attention calculation, which does aggressive low-memory slicing and calls
Enable InvokeAI-flavoured CrossAttention calculation, which does aggressive low-memory slicing and calls
through both to an attention_slice_wrangler and a slicing_strategy_getter for custom attention map wrangling
and dymamic slicing strategy selection.
"""
@@ -177,7 +178,7 @@ class InvokeAICrossAttentionMixin:
Set custom attention calculator to be called when attention is calculated
:param wrangler: Callback, with args (module, suggested_attention_slice, dim, offset, slice_size),
which returns either the suggested_attention_slice or an adjusted equivalent.
`module` is the current Attention module for which the callback is being invoked.
`module` is the current CrossAttention module for which the callback is being invoked.
`suggested_attention_slice` is the default-calculated attention slice
`dim` is -1 if the attenion map has not been sliced, or 0 or 1 for dimension-0 or dimension-1 slicing.
If `dim` is >= 0, `offset` and `slice_size` specify the slice start and length.
@@ -325,7 +326,7 @@ def setup_cross_attention_control_attention_processors(unet: UNet2DConditionMode
def get_cross_attention_modules(model, which: CrossAttentionType) -> list[tuple[str, InvokeAICrossAttentionMixin]]:
from ldm.modules.attention import CrossAttention # avoid circular import # TODO: rename as in diffusers?
from ldm.modules.attention import CrossAttention # avoid circular import
cross_attention_class: type = InvokeAIDiffusersCrossAttention if isinstance(model,UNet2DConditionModel) else CrossAttention
which_attn = "attn1" if which is CrossAttentionType.SELF else "attn2"
attention_module_tuples = [(name,module) for name, module in model.named_modules() if
@@ -431,7 +432,7 @@ def get_mem_free_total(device):
class InvokeAIDiffusersCrossAttention(diffusers.models.attention.Attention, InvokeAICrossAttentionMixin):
class InvokeAIDiffusersCrossAttention(diffusers.models.attention.CrossAttention, InvokeAICrossAttentionMixin):
def __init__(self, **kwargs):
super().__init__(**kwargs)
@@ -456,8 +457,8 @@ class InvokeAIDiffusersCrossAttention(diffusers.models.attention.Attention, Invo
"""
# base implementation
class AttnProcessor:
def __call__(self, attn: Attention, hidden_states, encoder_hidden_states=None, attention_mask=None):
class CrossAttnProcessor:
def __call__(self, attn: CrossAttention, hidden_states, encoder_hidden_states=None, attention_mask=None):
batch_size, sequence_length, _ = hidden_states.shape
attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length)
@@ -486,7 +487,7 @@ from dataclasses import field, dataclass
import torch
from diffusers.models.attention_processor import Attention, AttnProcessor, SlicedAttnProcessor
from diffusers.models.cross_attention import CrossAttention, CrossAttnProcessor, SlicedAttnProcessor
@dataclass
@@ -531,7 +532,7 @@ class SlicedSwapCrossAttnProcesser(SlicedAttnProcessor):
# TODO: dynamically pick slice size based on memory conditions
def __call__(self, attn: Attention, hidden_states, encoder_hidden_states=None, attention_mask=None,
def __call__(self, attn: CrossAttention, hidden_states, encoder_hidden_states=None, attention_mask=None,
# kwargs
swap_cross_attn_context: SwapCrossAttnContext=None):

View File

@@ -5,6 +5,7 @@ from typing import Callable, Optional, Union, Any
import numpy as np
import torch
from diffusers import UNet2DConditionModel
from typing_extensions import TypeAlias

View File

@@ -6,7 +6,7 @@ from torch import nn
import sys
from ldm.invoke.concepts_lib import get_hf_concepts_lib
from ldm.invoke.concepts_lib import HuggingFaceConceptsLibrary
from ldm.data.personalized import per_img_token_list
from transformers import CLIPTokenizer
from functools import partial
@@ -39,7 +39,7 @@ class EmbeddingManager(nn.Module):
super().__init__()
self.embedder = embedder
self.concepts_library=get_hf_concepts_lib()
self.concepts_library=HuggingFaceConceptsLibrary()
self.string_to_token_dict = {}
self.string_to_param_dict = nn.ParameterDict()

View File

@@ -9,7 +9,7 @@ from safetensors.torch import load_file
from torch.utils.hooks import RemovableHandle
from transformers import CLIPTextModel
from ..invoke.globals import global_lora_models_dir, Globals
from ..invoke.globals import global_lora_models_dir
from ..invoke.devices import choose_torch_device
"""
@@ -456,25 +456,16 @@ class LoRA:
class KohyaLoraManager:
lora_path = Path(global_lora_models_dir())
vector_length_cache_path = lora_path / '.vectorlength.cache'
def __init__(self, pipe):
self.vector_length_cache_path = self.lora_path / '.vectorlength.cache'
self.unet = pipe.unet
self.wrapper = LoRAModuleWrapper(pipe.unet, pipe.text_encoder)
self.text_encoder = pipe.text_encoder
self.device = torch.device(choose_torch_device())
self.dtype = pipe.unet.dtype
@classmethod
@property
def lora_path(cls)->Path:
return Path(global_lora_models_dir())
@classmethod
@property
def vector_length_cache_path(cls)->Path:
return cls.lora_path / '.vectorlength.cache'
def load_lora_module(self, name, path_file, multiplier: float = 1.0):
print(f" | Found lora {name} at {path_file}")
if path_file.suffix == ".safetensors":

View File

@@ -3,16 +3,14 @@ from dataclasses import dataclass
from pathlib import Path
from typing import Optional, Union
import warnings
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning)
import safetensors.torch
import torch
import safetensors.torch
import torch
from picklescan.scanner import scan_file_path
from transformers import CLIPTextModel, CLIPTokenizer
from compel.embeddings_provider import BaseTextualInversionManager
from ldm.invoke.concepts_lib import get_hf_concepts_lib
from ldm.invoke.concepts_lib import HuggingFaceConceptsLibrary
@dataclass
class TextualInversion:
@@ -36,7 +34,7 @@ class TextualInversionManager(BaseTextualInversionManager):
self.tokenizer = tokenizer
self.text_encoder = text_encoder
self.full_precision = full_precision
self.hf_concepts_library = get_hf_concepts_lib()
self.hf_concepts_library = HuggingFaceConceptsLibrary()
self.trigger_to_sourcefile = dict()
default_textual_inversions: list[TextualInversion] = []
self.textual_inversions = default_textual_inversions

View File

@@ -32,9 +32,9 @@ dependencies = [
"albumentations",
"click",
"clip_anytorch",
"compel~=1.1.5",
"compel~=1.1.0",
"datasets",
"diffusers[torch]~=0.16.1",
"diffusers[torch]==0.14",
"dnspython==2.2.1",
"einops",
"eventlet",
@@ -76,7 +76,7 @@ dependencies = [
"taming-transformers-rom1504",
"test-tube>=0.7.5",
"torch-fidelity",
"torch~=2.0.0",
"torch~=1.13.1",
"torchmetrics",
"torchvision>=0.14.1",
"transformers~=4.26",
@@ -108,7 +108,7 @@ requires-python = ">=3.9, <3.11"
"test" = ["pytest-cov", "pytest>6.0.0"]
"xformers" = [
"triton; sys_platform=='linux'",
"xformers~=0.0.19; sys_platform!='darwin'",
"xformers~=0.0.16; sys_platform!='darwin'",
]
[project.scripts]