mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into slider-fix
This commit is contained in:
commit
d1d12e4f92
@ -93,6 +93,7 @@ voxel_art-1.0:
|
|||||||
format: ckpt
|
format: ckpt
|
||||||
vae:
|
vae:
|
||||||
repo_id: stabilityai/sd-vae-ft-mse
|
repo_id: stabilityai/sd-vae-ft-mse
|
||||||
|
file: vae-ft-mse-840000-ema-pruned.ckpt
|
||||||
recommended: False
|
recommended: False
|
||||||
width: 512
|
width: 512
|
||||||
height: 512
|
height: 512
|
||||||
@ -102,7 +103,7 @@ ft-mse-improved-autoencoder-840000:
|
|||||||
format: ckpt
|
format: ckpt
|
||||||
config: VAE/default
|
config: VAE/default
|
||||||
file: vae-ft-mse-840000-ema-pruned.ckpt
|
file: vae-ft-mse-840000-ema-pruned.ckpt
|
||||||
recommended: False
|
recommended: True
|
||||||
width: 512
|
width: 512
|
||||||
height: 512
|
height: 512
|
||||||
trinart_vae:
|
trinart_vae:
|
||||||
|
@ -852,6 +852,7 @@ class Generate:
|
|||||||
model_data = cache.get_model(model_name)
|
model_data = cache.get_model(model_name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'** model {model_name} could not be loaded: {str(e)}')
|
print(f'** model {model_name} could not be loaded: {str(e)}')
|
||||||
|
print(traceback.format_exc(), file=sys.stderr)
|
||||||
if previous_model_name is None:
|
if previous_model_name is None:
|
||||||
raise e
|
raise e
|
||||||
print(f'** trying to reload previous model')
|
print(f'** trying to reload previous model')
|
||||||
|
@ -578,7 +578,7 @@ def import_model(model_path:str, gen, opt, completer):
|
|||||||
elif re.match('^[\w.+-]+/[\w.+-]+$',model_path):
|
elif re.match('^[\w.+-]+/[\w.+-]+$',model_path):
|
||||||
model_name = import_diffuser_model(model_path, gen, opt, completer)
|
model_name = import_diffuser_model(model_path, gen, opt, completer)
|
||||||
elif os.path.isdir(model_path):
|
elif os.path.isdir(model_path):
|
||||||
model_name = import_diffuser_model(model_path, gen, opt, completer)
|
model_name = import_diffuser_model(Path(model_path), gen, opt, completer)
|
||||||
else:
|
else:
|
||||||
print(f'** {model_path} is neither the path to a .ckpt file nor a diffusers repository id. Can\'t import.')
|
print(f'** {model_path} is neither the path to a .ckpt file nor a diffusers repository id. Can\'t import.')
|
||||||
|
|
||||||
@ -590,7 +590,7 @@ def import_model(model_path:str, gen, opt, completer):
|
|||||||
gen.model_manager.del_model(model_name)
|
gen.model_manager.del_model(model_name)
|
||||||
return
|
return
|
||||||
|
|
||||||
if input('Make this the default model? [n] ') in ('y','Y'):
|
if input('Make this the default model? [n] ').strip() in ('y','Y'):
|
||||||
gen.model_manager.set_default_model(model_name)
|
gen.model_manager.set_default_model(model_name)
|
||||||
|
|
||||||
gen.model_manager.commit(opt.conf)
|
gen.model_manager.commit(opt.conf)
|
||||||
@ -607,10 +607,14 @@ def import_diffuser_model(path_or_repo:str, gen, opt, completer)->str:
|
|||||||
model_name=default_name,
|
model_name=default_name,
|
||||||
model_description=default_description
|
model_description=default_description
|
||||||
)
|
)
|
||||||
|
vae = None
|
||||||
|
if input('Replace this model\'s VAE with "stabilityai/sd-vae-ft-mse"? [n] ').strip() in ('y','Y'):
|
||||||
|
vae = dict(repo_id='stabilityai/sd-vae-ft-mse')
|
||||||
|
|
||||||
if not manager.import_diffuser_model(
|
if not manager.import_diffuser_model(
|
||||||
path_or_repo,
|
path_or_repo,
|
||||||
model_name = model_name,
|
model_name = model_name,
|
||||||
|
vae = vae,
|
||||||
description = model_description):
|
description = model_description):
|
||||||
print('** model failed to import')
|
print('** model failed to import')
|
||||||
return None
|
return None
|
||||||
@ -628,17 +632,28 @@ def import_ckpt_model(path_or_url:str, gen, opt, completer)->str:
|
|||||||
)
|
)
|
||||||
config_file = None
|
config_file = None
|
||||||
default = Path(Globals.root,'configs/stable-diffusion/v1-inference.yaml')
|
default = Path(Globals.root,'configs/stable-diffusion/v1-inference.yaml')
|
||||||
|
|
||||||
completer.complete_extensions(('.yaml','.yml'))
|
completer.complete_extensions(('.yaml','.yml'))
|
||||||
completer.set_line(str(default))
|
completer.set_line(str(default))
|
||||||
done = False
|
done = False
|
||||||
while not done:
|
while not done:
|
||||||
config_file = input('Configuration file for this model: ').strip()
|
config_file = input('Configuration file for this model: ').strip()
|
||||||
done = os.path.exists(config_file)
|
done = os.path.exists(config_file)
|
||||||
|
|
||||||
|
completer.complete_extensions(('.ckpt','.safetensors'))
|
||||||
|
vae = None
|
||||||
|
default = Path(Globals.root,'models/ldm/stable-diffusion-v1/vae-ft-mse-840000-ema-pruned.ckpt')
|
||||||
|
completer.set_line(str(default))
|
||||||
|
done = False
|
||||||
|
while not done:
|
||||||
|
vae = input('VAE file for this model (leave blank for none): ').strip() or None
|
||||||
|
done = (not vae) or os.path.exists(vae)
|
||||||
completer.complete_extensions(None)
|
completer.complete_extensions(None)
|
||||||
|
|
||||||
if not manager.import_ckpt_model(
|
if not manager.import_ckpt_model(
|
||||||
path_or_url,
|
path_or_url,
|
||||||
config = config_file,
|
config = config_file,
|
||||||
|
vae = vae,
|
||||||
model_name = model_name,
|
model_name = model_name,
|
||||||
model_description = model_description,
|
model_description = model_description,
|
||||||
commit_to_conf = opt.conf,
|
commit_to_conf = opt.conf,
|
||||||
@ -710,7 +725,7 @@ def optimize_model(model_name_or_path:str, gen, opt, completer):
|
|||||||
return
|
return
|
||||||
|
|
||||||
completer.update_models(gen.model_manager.list_models())
|
completer.update_models(gen.model_manager.list_models())
|
||||||
if input(f'Load optimized model {model_name}? [y] ') not in ('n','N'):
|
if input(f'Load optimized model {model_name}? [y] ').strip() not in ('n','N'):
|
||||||
gen.set_model(model_name)
|
gen.set_model(model_name)
|
||||||
|
|
||||||
response = input(f'Delete the original .ckpt file at ({ckpt_path} ? [n] ')
|
response = input(f'Delete the original .ckpt file at ({ckpt_path} ? [n] ')
|
||||||
@ -726,7 +741,12 @@ def del_config(model_name:str, gen, opt, completer):
|
|||||||
if model_name not in gen.model_manager.config:
|
if model_name not in gen.model_manager.config:
|
||||||
print(f"** Unknown model {model_name}")
|
print(f"** Unknown model {model_name}")
|
||||||
return
|
return
|
||||||
gen.model_manager.del_model(model_name)
|
|
||||||
|
if input(f'Remove {model_name} from the list of models known to InvokeAI? [y] ').strip().startswith(('n','N')):
|
||||||
|
return
|
||||||
|
|
||||||
|
delete_completely = input('Completely remove the model file or directory from disk? [n] ').startswith(('y','Y'))
|
||||||
|
gen.model_manager.del_model(model_name,delete_files=delete_completely)
|
||||||
gen.model_manager.commit(opt.conf)
|
gen.model_manager.commit(opt.conf)
|
||||||
print(f'** {model_name} deleted')
|
print(f'** {model_name} deleted')
|
||||||
completer.update_models(gen.model_manager.list_models())
|
completer.update_models(gen.model_manager.list_models())
|
||||||
|
@ -18,7 +18,9 @@ import traceback
|
|||||||
import warnings
|
import warnings
|
||||||
import safetensors.torch
|
import safetensors.torch
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from shutil import move, rmtree
|
||||||
from typing import Union, Any
|
from typing import Union, Any
|
||||||
|
from huggingface_hub import scan_cache_dir
|
||||||
from ldm.util import download_with_progress_bar
|
from ldm.util import download_with_progress_bar
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@ -35,6 +37,9 @@ from ldm.invoke.globals import Globals, global_models_dir, global_autoscan_dir,
|
|||||||
from ldm.util import instantiate_from_config, ask_user
|
from ldm.util import instantiate_from_config, ask_user
|
||||||
|
|
||||||
DEFAULT_MAX_MODELS=2
|
DEFAULT_MAX_MODELS=2
|
||||||
|
VAE_TO_REPO_ID = { # hack, see note in convert_and_import()
|
||||||
|
'vae-ft-mse-840000-ema-pruned': 'stabilityai/sd-vae-ft-mse',
|
||||||
|
}
|
||||||
|
|
||||||
class ModelManager(object):
|
class ModelManager(object):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
@ -230,7 +235,7 @@ class ModelManager(object):
|
|||||||
line = f'\033[1m{line}\033[0m'
|
line = f'\033[1m{line}\033[0m'
|
||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
def del_model(self, model_name:str) -> None:
|
def del_model(self, model_name:str, delete_files:bool=False) -> None:
|
||||||
'''
|
'''
|
||||||
Delete the named model.
|
Delete the named model.
|
||||||
'''
|
'''
|
||||||
@ -238,9 +243,25 @@ class ModelManager(object):
|
|||||||
if model_name not in omega:
|
if model_name not in omega:
|
||||||
print(f'** Unknown model {model_name}')
|
print(f'** Unknown model {model_name}')
|
||||||
return
|
return
|
||||||
|
# save these for use in deletion later
|
||||||
|
conf = omega[model_name]
|
||||||
|
repo_id = conf.get('repo_id',None)
|
||||||
|
path = self._abs_path(conf.get('path',None))
|
||||||
|
weights = self._abs_path(conf.get('weights',None))
|
||||||
|
|
||||||
del omega[model_name]
|
del omega[model_name]
|
||||||
if model_name in self.stack:
|
if model_name in self.stack:
|
||||||
self.stack.remove(model_name)
|
self.stack.remove(model_name)
|
||||||
|
if delete_files:
|
||||||
|
if weights:
|
||||||
|
print(f'** deleting file {weights}')
|
||||||
|
Path(weights).unlink(missing_ok=True)
|
||||||
|
elif path:
|
||||||
|
print(f'** deleting directory {path}')
|
||||||
|
rmtree(path,ignore_errors=True)
|
||||||
|
elif repo_id:
|
||||||
|
print(f'** deleting the cached model directory for {repo_id}')
|
||||||
|
self._delete_model_from_cache(repo_id)
|
||||||
|
|
||||||
def add_model(self, model_name:str, model_attributes:dict, clobber:bool=False) -> None:
|
def add_model(self, model_name:str, model_attributes:dict, clobber:bool=False) -> None:
|
||||||
'''
|
'''
|
||||||
@ -417,7 +438,7 @@ class ModelManager(object):
|
|||||||
safety_checker=None,
|
safety_checker=None,
|
||||||
local_files_only=not Globals.internet_available
|
local_files_only=not Globals.internet_available
|
||||||
)
|
)
|
||||||
if 'vae' in mconfig:
|
if 'vae' in mconfig and mconfig['vae'] is not None:
|
||||||
vae = self._load_vae(mconfig['vae'])
|
vae = self._load_vae(mconfig['vae'])
|
||||||
pipeline_args.update(vae=vae)
|
pipeline_args.update(vae=vae)
|
||||||
if not isinstance(name_or_path,Path):
|
if not isinstance(name_or_path,Path):
|
||||||
@ -523,11 +544,12 @@ class ModelManager(object):
|
|||||||
print('>> Model scanned ok!')
|
print('>> Model scanned ok!')
|
||||||
|
|
||||||
def import_diffuser_model(self,
|
def import_diffuser_model(self,
|
||||||
repo_or_path:Union[str,Path],
|
repo_or_path:Union[str,Path],
|
||||||
model_name:str=None,
|
model_name:str=None,
|
||||||
description:str=None,
|
description:str=None,
|
||||||
commit_to_conf:Path=None,
|
vae:dict=None,
|
||||||
)->bool:
|
commit_to_conf:Path=None,
|
||||||
|
)->bool:
|
||||||
'''
|
'''
|
||||||
Attempts to install the indicated diffuser model and returns True if successful.
|
Attempts to install the indicated diffuser model and returns True if successful.
|
||||||
|
|
||||||
@ -543,6 +565,7 @@ class ModelManager(object):
|
|||||||
description = description or f'imported diffusers model {model_name}'
|
description = description or f'imported diffusers model {model_name}'
|
||||||
new_config = dict(
|
new_config = dict(
|
||||||
description=description,
|
description=description,
|
||||||
|
vae=vae,
|
||||||
format='diffusers',
|
format='diffusers',
|
||||||
)
|
)
|
||||||
if isinstance(repo_or_path,Path) and repo_or_path.exists():
|
if isinstance(repo_or_path,Path) and repo_or_path.exists():
|
||||||
@ -556,18 +579,22 @@ class ModelManager(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def import_ckpt_model(self,
|
def import_ckpt_model(self,
|
||||||
weights:Union[str,Path],
|
weights:Union[str,Path],
|
||||||
config:Union[str,Path]='configs/stable-diffusion/v1-inference.yaml',
|
config:Union[str,Path]='configs/stable-diffusion/v1-inference.yaml',
|
||||||
model_name:str=None,
|
vae:Union[str,Path]=None,
|
||||||
model_description:str=None,
|
model_name:str=None,
|
||||||
commit_to_conf:Path=None,
|
model_description:str=None,
|
||||||
)->bool:
|
commit_to_conf:Path=None,
|
||||||
|
)->bool:
|
||||||
'''
|
'''
|
||||||
Attempts to install the indicated ckpt file and returns True if successful.
|
Attempts to install the indicated ckpt file and returns True if successful.
|
||||||
|
|
||||||
"weights" can be either a path-like object corresponding to a local .ckpt file
|
"weights" can be either a path-like object corresponding to a local .ckpt file
|
||||||
or a http/https URL pointing to a remote model.
|
or a http/https URL pointing to a remote model.
|
||||||
|
|
||||||
|
"vae" is a Path or str object pointing to a ckpt or safetensors file to be used
|
||||||
|
as the VAE for this model.
|
||||||
|
|
||||||
"config" is the model config file to use with this ckpt file. It defaults to
|
"config" is the model config file to use with this ckpt file. It defaults to
|
||||||
v1-inference.yaml. If a URL is provided, the config will be downloaded.
|
v1-inference.yaml. If a URL is provided, the config will be downloaded.
|
||||||
|
|
||||||
@ -594,6 +621,8 @@ class ModelManager(object):
|
|||||||
width=512,
|
width=512,
|
||||||
height=512
|
height=512
|
||||||
)
|
)
|
||||||
|
if vae:
|
||||||
|
new_config['vae'] = vae
|
||||||
self.add_model(model_name, new_config, True)
|
self.add_model(model_name, new_config, True)
|
||||||
if commit_to_conf:
|
if commit_to_conf:
|
||||||
self.commit(commit_to_conf)
|
self.commit(commit_to_conf)
|
||||||
@ -633,7 +662,7 @@ class ModelManager(object):
|
|||||||
|
|
||||||
def convert_and_import(self,
|
def convert_and_import(self,
|
||||||
ckpt_path:Path,
|
ckpt_path:Path,
|
||||||
diffuser_path:Path,
|
diffusers_path:Path,
|
||||||
model_name=None,
|
model_name=None,
|
||||||
model_description=None,
|
model_description=None,
|
||||||
commit_to_conf:Path=None,
|
commit_to_conf:Path=None,
|
||||||
@ -645,46 +674,56 @@ class ModelManager(object):
|
|||||||
new_config = None
|
new_config = None
|
||||||
from ldm.invoke.ckpt_to_diffuser import convert_ckpt_to_diffuser
|
from ldm.invoke.ckpt_to_diffuser import convert_ckpt_to_diffuser
|
||||||
import transformers
|
import transformers
|
||||||
if diffuser_path.exists():
|
if diffusers_path.exists():
|
||||||
print(f'ERROR: The path {str(diffuser_path)} already exists. Please move or remove it and try again.')
|
print(f'ERROR: The path {str(diffusers_path)} already exists. Please move or remove it and try again.')
|
||||||
return
|
return
|
||||||
|
|
||||||
model_name = model_name or diffuser_path.name
|
model_name = model_name or diffusers_path.name
|
||||||
model_description = model_description or 'Optimized version of {model_name}'
|
model_description = model_description or 'Optimized version of {model_name}'
|
||||||
print(f'>> {model_name}: optimizing (30-60s).')
|
print(f'>> Optimizing {model_name} (30-60s)')
|
||||||
try:
|
try:
|
||||||
verbosity =transformers.logging.get_verbosity()
|
verbosity =transformers.logging.get_verbosity()
|
||||||
transformers.logging.set_verbosity_error()
|
transformers.logging.set_verbosity_error()
|
||||||
convert_ckpt_to_diffuser(ckpt_path, diffuser_path,extract_ema=True)
|
convert_ckpt_to_diffuser(ckpt_path, diffusers_path,extract_ema=True)
|
||||||
transformers.logging.set_verbosity(verbosity)
|
transformers.logging.set_verbosity(verbosity)
|
||||||
print(f'>> Success. Optimized model is now located at {str(diffuser_path)}')
|
print(f'>> Success. Optimized model is now located at {str(diffusers_path)}')
|
||||||
print(f'>> Writing new config file entry for {model_name}...',end='')
|
print(f'>> Writing new config file entry for {model_name}')
|
||||||
new_config = dict(
|
new_config = dict(
|
||||||
path=str(diffuser_path),
|
path=str(diffusers_path),
|
||||||
description=model_description,
|
description=model_description,
|
||||||
format='diffusers',
|
format='diffusers',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# HACK (LS): in the event that the original entry is using a custom ckpt VAE, we try to
|
||||||
|
# map that VAE onto a diffuser VAE using a hard-coded dictionary.
|
||||||
|
# I would prefer to do this differently: We load the ckpt model into memory, swap the
|
||||||
|
# VAE in memory, and then pass that to convert_ckpt_to_diffuser() so that the swapped
|
||||||
|
# VAE is built into the model. However, when I tried this I got obscure key errors.
|
||||||
|
if model_name in self.config and (vae_ckpt_path := self.model_info(model_name)['vae']):
|
||||||
|
vae_basename = Path(vae_ckpt_path).stem
|
||||||
|
diffusers_vae = None
|
||||||
|
if (diffusers_vae := VAE_TO_REPO_ID.get(vae_basename,None)):
|
||||||
|
print(f'>> {vae_basename} VAE corresponds to known {diffusers_vae} diffusers version')
|
||||||
|
new_config.update(
|
||||||
|
vae = {'repo_id': diffusers_vae}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f'** Custom VAE "{vae_basename}" found, but corresponding diffusers model unknown')
|
||||||
|
print(f'** Using "stabilityai/sd-vae-ft-mse"; If this isn\'t right, please edit the model config')
|
||||||
|
new_config.update(
|
||||||
|
vae = {'repo_id': 'stabilityai/sd-vae-ft-mse'}
|
||||||
|
)
|
||||||
|
|
||||||
self.del_model(model_name)
|
self.del_model(model_name)
|
||||||
self.add_model(model_name, new_config, True)
|
self.add_model(model_name, new_config, True)
|
||||||
if commit_to_conf:
|
if commit_to_conf:
|
||||||
self.commit(commit_to_conf)
|
self.commit(commit_to_conf)
|
||||||
|
print('>> Conversion succeeded')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'** Conversion failed: {str(e)}')
|
print(f'** Conversion failed: {str(e)}')
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
print('done.')
|
|
||||||
return new_config
|
return new_config
|
||||||
|
|
||||||
def del_config(self, model_name:str, gen, opt, completer):
|
|
||||||
current_model = gen.model_name
|
|
||||||
if model_name == current_model:
|
|
||||||
print("** Can't delete active model. !switch to another model first. **")
|
|
||||||
return
|
|
||||||
gen.model_manager.del_model(model_name)
|
|
||||||
gen.model_manager.commit(opt.conf)
|
|
||||||
print(f'** {model_name} deleted')
|
|
||||||
completer.del_model(model_name)
|
|
||||||
|
|
||||||
def search_models(self, search_folder):
|
def search_models(self, search_folder):
|
||||||
print(f'>> Finding Models In: {search_folder}')
|
print(f'>> Finding Models In: {search_folder}')
|
||||||
models_folder_ckpt = Path(search_folder).glob('**/*.ckpt')
|
models_folder_ckpt = Path(search_folder).glob('**/*.ckpt')
|
||||||
@ -766,7 +805,6 @@ class ModelManager(object):
|
|||||||
|
|
||||||
print('** Legacy version <= 2.2.5 model directory layout detected. Reorganizing.')
|
print('** Legacy version <= 2.2.5 model directory layout detected. Reorganizing.')
|
||||||
print('** This is a quick one-time operation.')
|
print('** This is a quick one-time operation.')
|
||||||
from shutil import move, rmtree
|
|
||||||
|
|
||||||
# transformer files get moved into the hub directory
|
# transformer files get moved into the hub directory
|
||||||
if cls._is_huggingface_hub_directory_present():
|
if cls._is_huggingface_hub_directory_present():
|
||||||
@ -982,6 +1020,27 @@ class ModelManager(object):
|
|||||||
|
|
||||||
return vae
|
return vae
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _delete_model_from_cache(repo_id):
|
||||||
|
cache_info = scan_cache_dir(global_cache_dir('diffusers'))
|
||||||
|
|
||||||
|
# I'm sure there is a way to do this with comprehensions
|
||||||
|
# but the code quickly became incomprehensible!
|
||||||
|
hashes_to_delete = set()
|
||||||
|
for repo in cache_info.repos:
|
||||||
|
if repo.repo_id==repo_id:
|
||||||
|
for revision in repo.revisions:
|
||||||
|
hashes_to_delete.add(revision.commit_hash)
|
||||||
|
strategy = cache_info.delete_revisions(*hashes_to_delete)
|
||||||
|
print(f'** deletion of this model is expected to free {strategy.expected_freed_size_str}')
|
||||||
|
strategy.execute()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _abs_path(path:Union(str,Path))->Path:
|
||||||
|
if path is None or Path(path).is_absolute():
|
||||||
|
return path
|
||||||
|
return Path(Globals.root,path).resolve()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_huggingface_hub_directory_present() -> bool:
|
def _is_huggingface_hub_directory_present() -> bool:
|
||||||
return os.getenv('HF_HOME') is not None or os.getenv('XDG_CACHE_HOME') is not None
|
return os.getenv('HF_HOME') is not None or os.getenv('XDG_CACHE_HOME') is not None
|
||||||
|
Loading…
Reference in New Issue
Block a user