Compare commits

...

32 Commits

Author SHA1 Message Date
Lincoln Stein
1cb88960fe this is release candidate 2.3.3-rc1
Incorporates a modified version of the dialog-based invoke.sh script
suggested by JoshuaKimsey:
https://discord.com/channels/1020123559063990373/1089119602425995304
2023-03-25 16:58:08 -04:00
Eugene Brodsky
610a1483b7 installer: fix indentation in invoke.sh template (tabs -> spaces) 2023-03-25 13:52:37 -04:00
Lincoln Stein
b4e7fc0d1d prevent infinite loop when launching developer's console 2023-03-25 13:52:37 -04:00
blessedcoolant
b792b7d68c Security patch: Scan all pickle files, including VAEs; default to safetensor loading (#3011)
Several related security fixes:

1. Port #2946 from main to 2.3.2 branch - this closes a hole that allows
a pickle checkpoint file to masquerade as a safetensors file.
2. Add pickle scanning to the checkpoint to diffusers conversion script.
3. Pickle scan VAE non-safetensors files
4. Avoid running scanner twice on same file during the probing and
conversion process.
5. Clean up diagnostic messages.
2023-03-24 22:35:15 +13:00
blessedcoolant
abaa91195d Merge branch 'v2.3' into security/scan-ckpt-models 2023-03-24 22:11:34 +13:00
Lincoln Stein
1806bfb755 fix batch generation logfile name to be compatible with Windows OS (#3018)
- The command `invokeai-batch --invoke` was created a time-stamped
logfile with colons in its name, which is a Windows no-no. This corrects
the problem by writing the timestamp out as "13-06-2023_8-35-10"

- Closes #3005
2023-03-24 01:32:24 -04:00
blessedcoolant
7377855c02 Merge branch 'v2.3' into bugfix/batch-logfile-format 2023-03-24 18:10:00 +13:00
Lincoln Stein
5f2a6f24cf fix corrupted outputs/.next_prefix file (#3020)
- Since 2.3.2 invokeai stores the next PNG file's numeric prefix in a
file named `.next_prefix` in the outputs directory. This avoids the
overhead of doing a directory listing to find out what file number comes
next.

- The code uses advisory locking to prevent corruption of this file in
the event that multiple invokeai's try to access it simultaneously, but
some users have experienced corruption of the file nevertheless.

- This PR addresses the problem by detecting a potentially corrupted
`.next_prefix` file and falling back to the directory listing method. A
fixed version of the file is then written out.

- Closes #3001
2023-03-23 23:53:10 -04:00
Lincoln Stein
5b8b92d957 Merge branch 'v2.3' into bugfix/batch-logfile-format 2023-03-23 23:34:05 -04:00
Lincoln Stein
352202a7bc Merge branch 'v2.3' into bugfix/fix-corrupted-image-sequence-file 2023-03-23 23:28:11 -04:00
blessedcoolant
82144de85f Fix textual inversion documentation and code (#3015)
This PR addresses issues raised by #3008.
    
1. Update documentation to indicate the correct maximum batch size for
TI training when xformers is and isn't used.
    
2. Update textual inversion code so that the default for batch size is
aware of xformer availability.
    
3. Add documentation for how to launch TI with distributed learning.
2023-03-24 16:14:47 +13:00
Lincoln Stein
b70d713e89 Merge branch 'v2.3' into bugfix/batch-logfile-format 2023-03-23 23:12:43 -04:00
blessedcoolant
e39dde4140 Merge branch 'v2.3' into feat/adjust-ti-param-for-xformers 2023-03-24 15:40:38 +13:00
blessedcoolant
c151541703 bump version to 2.3.3-rc1 (#3019)
Lots of little bugs have been squashed since 2.3.2 and a new minor point
release is imminent. This PR updates the version number in preparation
for a RC.
2023-03-24 15:27:57 +13:00
Lincoln Stein
29b348ece1 fix corrupted outputs/.next_prefix file
- Since 2.3.2 invokeai stores the next PNG file's numeric prefix in a
  file named `.next_prefix` in the outputs directory. This avoids the
  overhead of doing a directory listing to find out what file number
  comes next.

- The code uses advisory locking to prevent corruption of this file in
  the event that multiple invokeai's try to access it simultaneously,
  but some users have experienced corruption of the file nevertheless.

- This PR addresses the problem by detecting a potentially corrupted
  `.next_prefix` file and falling back to the directory listing method.
  A fixed version of the file is then written out.

- Closes #3001
2023-03-23 22:07:05 -04:00
Lincoln Stein
9f7c86c33e bump version to 2.3.3-rc1
Lots of little bugs have been squashed since 2.3.2 and a new minor
point release is imminent. This PR updates the version number in
preparation for a RC.
2023-03-23 21:47:56 -04:00
Lincoln Stein
a79d40519c fix batch generation logfile name to be compatible with Windows OS
- `invokeai-batch --invoke` was created a time-stamped logfile with colons in its
  name, which is a Windows no-no. This corrects the problem by writing
  the timestamp out as "13-06-2023_8-35-10"

- Closes #3005
2023-03-23 21:43:21 -04:00
Lincoln Stein
4515d52a42 fix textual inversion documentation and code
This PR addresses issues raised by #3008.

1. Update documentation to indicate the correct maximum batch size for
   TI training when xformers is and isn't used.

2. Update textual inversion code so that the default for batch size
   is aware of xformer availability.

3. Add documentation for how to launch TI with distributed learning.
2023-03-23 21:00:54 -04:00
Lincoln Stein
2a8513eee0 adjust textual inversion training parameters according to xformers availability
- If xformers is available, then default "use xformers" checkbox to on.
- Increase batch size to 8 (from 3).
2023-03-23 19:49:13 -04:00
Jonathan
b856fac713 Keep torch version at 1.13.1 (#2985)
Now that torch 2.0 is out, Invoke 2.3 should lock down its version to 1.13.1 for new installs and upgrades.
2023-03-23 15:27:12 -04:00
Lincoln Stein
4a3951681c prevent double-scanning during convert
- Avoid running scanner twice on same file during the probing and
  conversion process.

- Clean up diagnostic messages.
2023-03-23 14:24:10 -04:00
Lincoln Stein
ba89444e36 scan legacy checkpoint models in converter script prior to unpickling
Two related security fixes:

1. Port #2946 from main to 2.3.2 branch - this closes a hole that
   allows a pickle checkpoint file to masquerade as a safetensors
   file.

2. Add pickle scanning to the checkpoint to diffusers conversion
   script. This will be ported to main in a separate PR.
2023-03-23 13:44:08 -04:00
Lincoln Stein
a044403ac3 Bugfix/fix 2.3.2 upgrade path (#2943)
This fixes #2930 by adding a missing line in `pyproject.toml` needed to create the `config/stable-diffusion` directory.
2023-03-13 10:14:37 -07:00
Lincoln Stein
16dea46b79 remove outdated comment 2023-03-13 12:51:27 -04:00
Lincoln Stein
1f80b5335b reenable run_patches() 2023-03-13 10:38:08 -04:00
Lincoln Stein
eee7f13771 add back stable diffusion config files 2023-03-13 10:35:39 -04:00
Lincoln Stein
6db509a4ff add --upgrade to update script 2023-03-13 10:15:33 -04:00
Lincoln Stein
b7965e1ee6 restore find-packages to pyproject.toml 2023-03-13 10:11:37 -04:00
Lincoln Stein
c3d292e8f9 bump version to post1 2023-03-13 09:35:25 -04:00
Lincoln Stein
206593ec99 update version number 2023-03-13 09:34:00 -04:00
Lincoln Stein
1b62c781d7 temporarily disable run-patches 2023-03-13 09:33:32 -04:00
Lincoln Stein
c4de509983 fix failure to update to 2.3.2
- fixes #2930 #2941
2023-03-13 09:19:26 -04:00
11 changed files with 242 additions and 127 deletions

View File

@@ -154,8 +154,11 @@ training sets will converge with 2000-3000 steps.
This adjusts how many training images are processed simultaneously in
each step. Higher values will cause the training process to run more
quickly, but use more memory. The default size will run with GPUs with
as little as 12 GB.
quickly, but use more memory. The default size is selected based on
whether you have the `xformers` memory-efficient attention library
installed. If `xformers` is available, the batch size will be 8,
otherwise 3. These values were chosen to allow training to run with
GPUs with as little as 12 GB VRAM.
### Learning rate
@@ -172,8 +175,10 @@ learning rate to improve performance.
### Use xformers acceleration
This will activate XFormers memory-efficient attention. You need to
have XFormers installed for this to have an effect.
This will activate XFormers memory-efficient attention, which will
reduce memory requirements by half or more and allow you to select a
higher batch size. You need to have XFormers installed for this to
have an effect.
### Learning rate scheduler
@@ -250,6 +255,49 @@ invokeai-ti \
--only_save_embeds
```
## Using Distributed Training
If you have multiple GPUs on one machine, or a cluster of GPU-enabled
machines, you can activate distributed training. See the [HuggingFace
Accelerate pages](https://huggingface.co/docs/accelerate/index) for
full information, but the basic recipe is:
1. Enter the InvokeAI developer's console command line by selecting
option [8] from the `invoke.sh`/`invoke.bat` script.
2. Configurate Accelerate using `accelerate config`:
```sh
accelerate config
```
This will guide you through the configuration process, including
specifying how many machines you will run training on and the number
of GPUs pe rmachine.
You only need to do this once.
3. Launch training from the command line using `accelerate launch`. Be sure
that your current working directory is the InvokeAI root directory (usually
named `invokeai` in your home directory):
```sh
accelerate launch .venv/bin/invokeai-ti \
--model=stable-diffusion-1.5 \
--resolution=512 \
--learnable_property=object \
--initializer_token='*' \
--placeholder_token='<shraddha>' \
--train_data_dir=/home/lstein/invokeai/text-inversion-training-data/shraddha \
--output_dir=/home/lstein/invokeai/text-inversion-training/shraddha \
--scale_lr \
--train_batch_size=10 \
--gradient_accumulation_steps=4 \
--max_train_steps=2000 \
--learning_rate=0.0005 \
--lr_scheduler=constant \
--mixed_precision=fp16 \
--only_save_embeds
```
## Using Embeddings
After training completes, the resultant embeddings will be saved into your `$INVOKEAI_ROOT/embeddings/<trigger word>/learned_embeds.bin`.

View File

@@ -1,5 +1,8 @@
#!/bin/bash
# coauthored by Lincoln Stein, Eugene Brodsky and JoshuaKimsey
# Copyright 2023, The InvokeAI Development Team
####
# This launch script assumes that:
# 1. it is located in the runtime directory,
@@ -18,78 +21,135 @@ cd "$scriptdir"
. .venv/bin/activate
export INVOKEAI_ROOT="$scriptdir"
PARAMS=$@
# set required env var for torch on mac MPS
if [ "$(uname -s)" == "Darwin" ]; then
export PYTORCH_ENABLE_MPS_FALLBACK=1
fi
while true
do
if [ "$0" != "bash" ]; then
echo "Do you want to generate images using the"
echo "1. command-line interface"
echo "2. browser-based UI"
echo "3. run textual inversion training"
echo "4. merge models (diffusers type only)"
echo "5. download and install models"
echo "6. change InvokeAI startup options"
echo "7. re-run the configure script to fix a broken install"
echo "8. open the developer console"
echo "9. update InvokeAI"
echo "10. command-line help"
echo "Q - Quit"
echo ""
read -p "Please enter 1-10, Q: [2] " yn
choice=${yn:='2'}
case $choice in
1)
echo "Starting the InvokeAI command-line..."
invokeai $@
do_choice() {
case $1 in
1)
echo "Generate images with a browser-based interface"
clear
invokeai --web $PARAMS
;;
2)
echo "Starting the InvokeAI browser-based UI..."
invokeai --web $@
2)
echo "Generate images using a command-line interface"
clear
invokeai $PARAMS
;;
3)
echo "Starting Textual Inversion:"
invokeai-ti --gui $@
3)
echo "Textual inversion training"
clear
invokeai-ti --gui $PARAMS
;;
4)
echo "Merging Models:"
invokeai-merge --gui $@
4)
echo "Merge models (diffusers type only)"
clear
invokeai-merge --gui $PARAMS
;;
5)
5)
echo "Download and install models"
clear
invokeai-model-install --root ${INVOKEAI_ROOT}
;;
6)
6)
echo "Change InvokeAI startup options"
clear
invokeai-configure --root ${INVOKEAI_ROOT} --skip-sd-weights --skip-support-models
;;
7)
7)
echo "Re-run the configure script to fix a broken install"
clear
invokeai-configure --root ${INVOKEAI_ROOT} --yes --default_only
;;
;;
8)
echo "Developer Console:"
echo "Open the developer console"
clear
file_name=$(basename "${BASH_SOURCE[0]}")
bash --init-file "$file_name"
;;
9)
echo "Update:"
9)
echo "Update InvokeAI"
clear
invokeai-update
;;
10)
10)
echo "Command-line help"
clear
invokeai --help
;;
[qQ])
exit 0
*)
echo "Exiting..."
exit
;;
*)
echo "Invalid selection"
exit;;
esac
clear
}
do_dialog() {
while true
do
options=(
1 "Generate images with a browser-based interface"
2 "Generate images using a command-line interface"
3 "Textual inversion training"
4 "Merge models (diffusers type only)"
5 "Download and install models"
6 "Change InvokeAI startup options"
7 "Re-run the configure script to fix a broken install"
8 "Open the developer console"
9 "Update InvokeAI"
10 "Command-line help")
choice=$(dialog --clear \
--backtitle "InvokeAI" \
--title "What you like to run?" \
--menu "Select an option:" \
0 0 0 \
"${options[@]}" \
2>&1 >/dev/tty) || clear
do_choice "$choice"
done
clear
}
do_line_input() {
echo " ** For a more attractive experience, please install the 'dialog' utility. **"
echo ""
while true
do
echo "Do you want to generate images using the"
echo "1. browser-based UI"
echo "2. command-line interface"
echo "3. run textual inversion training"
echo "4. merge models (diffusers type only)"
echo "5. download and install models"
echo "6. change InvokeAI startup options"
echo "7. re-run the configure script to fix a broken install"
echo "8. open the developer console"
echo "9. update InvokeAI"
echo "10. command-line help"
echo "Q - Quit"
echo ""
read -p "Please enter 1-10, Q: [1] " yn
choice=${yn:='1'}
do_choice $choice
done
}
if [ "$0" != "bash" ]; then
# Dialog seems to be a standard installtion for most Linux distros, but this checks to ensure it is present regardless
if command -v dialog &> /dev/null ; then
do_dialog
else
do_line_input
fi
else # in developer console
python --version
echo "Press ^D to exit"
export PS1="(InvokeAI) \u@\h \w> "
fi
done

View File

@@ -1,2 +1,2 @@
__version__='2.3.2'
__version__='2.3.3-rc1'

View File

@@ -327,10 +327,10 @@ def convert_ldm_unet_checkpoint(checkpoint, config, path=None, extract_ema=False
unet_key = "model.diffusion_model."
# at least a 100 parameters have to start with `model_ema` in order for the checkpoint to be EMA
if sum(k.startswith("model_ema") for k in keys) > 100:
print(f" | Checkpoint {path} has both EMA and non-EMA weights.")
print(f" | Checkpoint {path} has both EMA and non-EMA weights.")
if extract_ema:
print(
' | Extracting EMA weights (usually better for inference)'
' | Extracting EMA weights (usually better for inference)'
)
for key in keys:
if key.startswith("model.diffusion_model"):
@@ -338,7 +338,7 @@ def convert_ldm_unet_checkpoint(checkpoint, config, path=None, extract_ema=False
unet_state_dict[key.replace(unet_key, "")] = checkpoint.pop(flat_ema_key)
else:
print(
' | Extracting only the non-EMA weights (usually better for fine-tuning)'
' | Extracting only the non-EMA weights (usually better for fine-tuning)'
)
for key in keys:
@@ -809,6 +809,7 @@ def load_pipeline_from_original_stable_diffusion_ckpt(
vae:AutoencoderKL=None,
precision:torch.dtype=torch.float32,
return_generator_pipeline:bool=False,
scan_needed:bool=True,
)->Union[StableDiffusionPipeline,StableDiffusionGeneratorPipeline]:
'''
Load a Stable Diffusion pipeline object from a CompVis-style `.ckpt`/`.safetensors` file and (ideally) a `.yaml`
@@ -843,7 +844,12 @@ def load_pipeline_from_original_stable_diffusion_ckpt(
verbosity = dlogging.get_verbosity()
dlogging.set_verbosity_error()
checkpoint = load_file(checkpoint_path) if Path(checkpoint_path).suffix == '.safetensors' else torch.load(checkpoint_path)
if Path(checkpoint_path).suffix == '.ckpt':
if scan_needed:
ModelManager.scan_model(checkpoint_path,checkpoint_path)
checkpoint = torch.load(checkpoint_path)
else:
checkpoint = load_file(checkpoint_path)
cache_dir = global_cache_dir('hub')
pipeline_class = StableDiffusionGeneratorPipeline if return_generator_pipeline else StableDiffusionPipeline
@@ -851,7 +857,7 @@ def load_pipeline_from_original_stable_diffusion_ckpt(
if "global_step" in checkpoint:
global_step = checkpoint["global_step"]
else:
print(" | global_step key not found in model")
print(" | global_step key not found in model")
global_step = None
# sometimes there is a state_dict key and sometimes not
@@ -953,14 +959,14 @@ def load_pipeline_from_original_stable_diffusion_ckpt(
# Convert the VAE model, or use the one passed
if not vae:
print(' | Using checkpoint model\'s original VAE')
print(' | Using checkpoint model\'s original VAE')
vae_config = create_vae_diffusers_config(original_config, image_size=image_size)
converted_vae_checkpoint = convert_ldm_vae_checkpoint(checkpoint, vae_config)
vae = AutoencoderKL(**vae_config)
vae.load_state_dict(converted_vae_checkpoint)
else:
print(' | Using external VAE specified in config')
print(' | Using VAE specified in config')
# Convert the text model.
model_type = pipeline_type

View File

@@ -72,7 +72,7 @@ def main():
tag = Prompt.ask('Enter an InvokeAI tag or branch name')
print(f':crossed_fingers: Upgrading to [yellow]{tag}[/yellow]')
cmd = f'pip install {INVOKE_AI_SRC}/{tag}.zip --use-pep517'
cmd = f'pip install {INVOKE_AI_SRC}/{tag}.zip --use-pep517 --upgrade'
print('')
print('')
if os.system(cmd)==0:

View File

@@ -157,7 +157,7 @@ def _run_invoke(
):
pid = os.getpid()
logdir.mkdir(parents=True, exist_ok=True)
logfile = Path(logdir, f'{time.strftime("%Y-%m-%d-%H:%M:%S")}-pid={pid}.txt')
logfile = Path(logdir, f'{time.strftime("%Y-%m-%d_%H-%M-%S")}-pid={pid}.txt')
print(
f">> Process {pid} running on GPU {gpu}; logging to {logfile}", file=sys.stderr
)

View File

@@ -282,13 +282,13 @@ class ModelManager(object):
self.stack.remove(model_name)
if delete_files:
if weights:
print(f"** deleting file {weights}")
print(f"** Deleting file {weights}")
Path(weights).unlink(missing_ok=True)
elif path:
print(f"** deleting directory {path}")
print(f"** Deleting directory {path}")
rmtree(path, ignore_errors=True)
elif repo_id:
print(f"** deleting the cached model directory for {repo_id}")
print(f"** Deleting the cached model directory for {repo_id}")
self._delete_model_from_cache(repo_id)
def add_model(
@@ -420,11 +420,6 @@ class ModelManager(object):
"NOHASH",
)
# scan model
self.scan_model(model_name, weights)
print(f">> Loading {model_name} from {weights}")
# for usage statistics
if self._has_cuda():
torch.cuda.reset_peak_memory_stats()
@@ -438,10 +433,13 @@ class ModelManager(object):
weight_bytes = f.read()
model_hash = self._cached_sha256(weights, weight_bytes)
sd = None
if weights.endswith(".safetensors"):
sd = safetensors.torch.load(weight_bytes)
else:
if weights.endswith(".ckpt"):
self.scan_model(model_name, weights)
sd = torch.load(io.BytesIO(weight_bytes), map_location="cpu")
else:
sd = safetensors.torch.load(weight_bytes)
del weight_bytes
# merged models from auto11 merge board are flat for some reason
if "state_dict" in sd:
@@ -464,18 +462,12 @@ class ModelManager(object):
vae = os.path.normpath(os.path.join(Globals.root, vae))
if os.path.exists(vae):
print(f" | Loading VAE weights from: {vae}")
vae_ckpt = None
vae_dict = None
if vae.endswith(".safetensors"):
vae_ckpt = safetensors.torch.load_file(vae)
vae_dict = {k: v for k, v in vae_ckpt.items() if k[0:4] != "loss"}
else:
if vae.endswith((".ckpt",".pt")):
self.scan_model(vae,vae)
vae_ckpt = torch.load(vae, map_location="cpu")
vae_dict = {
k: v
for k, v in vae_ckpt["state_dict"].items()
if k[0:4] != "loss"
}
else:
vae_ckpt = safetensors.torch.load_file(vae)
vae_dict = {k: v for k, v in vae_ckpt.items() if k[0:4] != "loss"}
model.first_stage_model.load_state_dict(vae_dict, strict=False)
else:
print(f" | VAE file {vae} not found. Skipping.")
@@ -497,9 +489,9 @@ class ModelManager(object):
print(f">> Loading diffusers model from {name_or_path}")
if using_fp16:
print(" | Using faster float16 precision")
print(" | Using faster float16 precision")
else:
print(" | Using more accurate float32 precision")
print(" | Using more accurate float32 precision")
# TODO: scan weights maybe?
pipeline_args: dict[str, Any] = dict(
@@ -551,7 +543,7 @@ class ModelManager(object):
width = pipeline.unet.config.sample_size * pipeline.vae_scale_factor
height = width
print(f" | Default image dimensions = {width} x {height}")
print(f" | Default image dimensions = {width} x {height}")
return pipeline, width, height, model_hash
@@ -591,13 +583,14 @@ class ModelManager(object):
if self._has_cuda():
torch.cuda.empty_cache()
@classmethod
def scan_model(self, model_name, checkpoint):
"""
Apply picklescanner to the indicated checkpoint and issue a warning
and option to exit if an infected file is identified.
"""
# scan model
print(f">> Scanning Model: {model_name}")
print(f" | Scanning Model: {model_name}")
scan_result = scan_file_path(checkpoint)
if scan_result.infected_files != 0:
if scan_result.infected_files == 1:
@@ -620,7 +613,7 @@ class ModelManager(object):
print("### Exiting InvokeAI")
sys.exit()
else:
print(">> Model scanned ok")
print(" | Model scanned ok")
def import_diffuser_model(
self,
@@ -800,19 +793,20 @@ class ModelManager(object):
print(f">> Probing {thing} for import")
if thing.startswith(("http:", "https:", "ftp:")):
print(f" | {thing} appears to be a URL")
print(f" | {thing} appears to be a URL")
model_path = self._resolve_path(
thing, "models/ldm/stable-diffusion-v1"
) # _resolve_path does a download if needed
is_temporary = True
elif Path(thing).is_file() and thing.endswith((".ckpt", ".safetensors")):
if Path(thing).stem in ["model", "diffusion_pytorch_model"]:
print(
f" | {Path(thing).name} appears to be part of a diffusers model. Skipping import"
f" | {Path(thing).name} appears to be part of a diffusers model. Skipping import"
)
return
else:
print(f" | {thing} appears to be a checkpoint file on disk")
print(f" | {thing} appears to be a checkpoint file on disk")
model_path = self._resolve_path(thing, "models/ldm/stable-diffusion-v1")
elif Path(thing).is_dir() and Path(thing, "model_index.json").exists():
@@ -869,11 +863,12 @@ class ModelManager(object):
return model_path.stem
# another round of heuristics to guess the correct config file.
checkpoint = (
safetensors.torch.load_file(model_path)
if model_path.suffix == ".safetensors"
else torch.load(model_path)
)
checkpoint = None
if model_path.suffix.endswith((".ckpt",".pt")):
self.scan_model(model_path,model_path)
checkpoint = torch.load(model_path)
else:
checkpoint = safetensors.torch.load_file(model_path)
# additional probing needed if no config file provided
if model_config_file is None:
model_type = self.probe_model_type(checkpoint)
@@ -918,7 +913,7 @@ class ModelManager(object):
if model_config_file.name.startswith('v2'):
convert = True
print(
" | This SD-v2 model will be converted to diffusers format for use"
" | This SD-v2 model will be converted to diffusers format for use"
)
if convert:
@@ -933,6 +928,7 @@ class ModelManager(object):
model_description=description,
original_config_file=model_config_file,
commit_to_conf=commit_to_conf,
scan_needed=False,
)
# in the event that this file was downloaded automatically prior to conversion
# we do not keep the original .ckpt/.safetensors around
@@ -957,14 +953,15 @@ class ModelManager(object):
return model_name
def convert_and_import(
self,
ckpt_path: Path,
diffusers_path: Path,
model_name=None,
model_description=None,
vae=None,
original_config_file: Path = None,
commit_to_conf: Path = None,
self,
ckpt_path: Path,
diffusers_path: Path,
model_name=None,
model_description=None,
vae=None,
original_config_file: Path = None,
commit_to_conf: Path = None,
scan_needed: bool=True,
) -> str:
"""
Convert a legacy ckpt weights file to diffuser model and import
@@ -999,11 +996,12 @@ class ModelManager(object):
extract_ema=True,
original_config_file=original_config_file,
vae=vae_model,
scan_needed=scan_needed,
)
print(
f" | Success. Optimized model is now located at {str(diffusers_path)}"
f" | Success. Optimized model is now located at {str(diffusers_path)}"
)
print(f" | Writing new config file entry for {model_name}")
print(f" | Writing new config file entry for {model_name}")
new_config = dict(
path=str(diffusers_path),
description=model_description,
@@ -1293,7 +1291,7 @@ class ModelManager(object):
with open(hashpath) as f:
hash = f.read()
return hash
print(" | Calculating sha256 hash of model files")
print(" | Calculating sha256 hash of model files")
tic = time.time()
sha = hashlib.sha256()
count = 0
@@ -1305,7 +1303,7 @@ class ModelManager(object):
sha.update(chunk)
hash = sha.hexdigest()
toc = time.time()
print(f" | sha256 = {hash} ({count} files hashed in", "%4.2fs)" % (toc - tic))
print(f" | sha256 = {hash} ({count} files hashed in", "%4.2fs)" % (toc - tic))
with open(hashpath, "w") as f:
f.write(hash)
return hash
@@ -1350,12 +1348,12 @@ class ModelManager(object):
local_files_only=not Globals.internet_available,
)
print(f" | Loading diffusers VAE from {name_or_path}")
print(f" | Loading diffusers VAE from {name_or_path}")
if using_fp16:
vae_args.update(torch_dtype=torch.float16)
fp_args_list = [{"revision": "fp16"}, {}]
else:
print(" | Using more accurate float32 precision")
print(" | Using more accurate float32 precision")
fp_args_list = [{}]
vae = None
@@ -1396,7 +1394,7 @@ class ModelManager(object):
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}"
f"** Deletion of this model is expected to free {strategy.expected_freed_size_str}"
)
strategy.execute()

View File

@@ -30,14 +30,17 @@ class PngWriter:
prefix = self._unused_prefix()
else:
with open(next_prefix_file,'r') as file:
prefix=int(file.readline() or int(self._unused_prefix())-1)
prefix+=1
prefix = 0
try:
prefix=int(file.readline())
except (TypeError, ValueError):
prefix=self._unused_prefix()
with open(next_prefix_file,'w') as file:
file.write(str(prefix))
file.write(str(prefix+1))
return f'{prefix:06}'
# gives the next unique prefix in outdir
def _unused_prefix(self):
def _unused_prefix(self)->int:
# sort reverse alphabetically until we find max+1
dirlist = sorted(os.listdir(self.outdir), reverse=True)
# find the first filename that matches our pattern or return 000000.0.png
@@ -45,8 +48,7 @@ class PngWriter:
(f for f in dirlist if re.match('^(\d+)\..*\.png', f)),
'0000000.0.png',
)
basecount = int(existing_name.split('.', 1)[0]) + 1
return f'{basecount:06}'
return int(existing_name.split('.', 1)[0]) + 1
# saves image named _image_ to outdir/name, writing metadata from prompt
# returns full path of output

View File

@@ -17,6 +17,7 @@ from pathlib import Path
from typing import List, Tuple
import npyscreen
from diffusers.utils.import_utils import is_xformers_available
from npyscreen import widget
from omegaconf import OmegaConf
@@ -29,7 +30,7 @@ from ldm.invoke.training.textual_inversion_training import (
TRAINING_DATA = "text-inversion-training-data"
TRAINING_DIR = "text-inversion-output"
CONF_FILE = "preferences.conf"
XFORMERS_AVAILABLE = is_xformers_available()
class textualInversionForm(npyscreen.FormMultiPageAction):
resolutions = [512, 768, 1024]
@@ -178,7 +179,7 @@ class textualInversionForm(npyscreen.FormMultiPageAction):
out_of=10000,
step=500,
lowest=1,
value=saved_args.get("max_train_steps", 3000),
value=saved_args.get("max_train_steps", 2500),
scroll_exit=True,
)
self.train_batch_size = self.add_widget_intelligent(
@@ -187,7 +188,7 @@ class textualInversionForm(npyscreen.FormMultiPageAction):
out_of=50,
step=1,
lowest=1,
value=saved_args.get("train_batch_size", 8),
value=saved_args.get("train_batch_size", 8 if XFORMERS_AVAILABLE else 3),
scroll_exit=True,
)
self.gradient_accumulation_steps = self.add_widget_intelligent(
@@ -225,7 +226,7 @@ class textualInversionForm(npyscreen.FormMultiPageAction):
self.enable_xformers_memory_efficient_attention = self.add_widget_intelligent(
npyscreen.Checkbox,
name="Use xformers acceleration",
value=saved_args.get("enable_xformers_memory_efficient_attention", False),
value=saved_args.get("enable_xformers_memory_efficient_attention", XFORMERS_AVAILABLE),
scroll_exit=True,
)
self.lr_scheduler = self.add_widget_intelligent(
@@ -428,8 +429,7 @@ def do_front_end(args: Namespace):
print(str(e))
print("** DETAILS:")
print(traceback.format_exc())
def main():
args = parse_args()
global_set_root(args.root_dir or Globals.root)

View File

@@ -67,7 +67,7 @@ else:
"nearest": PIL.Image.NEAREST,
}
# ------------------------------------------------------------------------------
XFORMERS_AVAILABLE = is_xformers_available
# Will error if the minimal version of diffusers is not installed. Remove at your own risks.
check_min_version("0.10.0.dev0")
@@ -227,7 +227,7 @@ def parse_args():
training_group.add_argument(
"--train_batch_size",
type=int,
default=16,
default=8 if XFORMERS_AVAILABLE else 3,
help="Batch size (per device) for the training dataloader.",
)
training_group.add_argument("--num_train_epochs", type=int, default=100)
@@ -324,6 +324,7 @@ def parse_args():
parser.add_argument(
"--enable_xformers_memory_efficient_attention",
action="store_true",
default=XFORMERS_AVAILABLE,
help="Whether or not to use xformers.",
)
@@ -536,7 +537,7 @@ def do_textual_inversion_training(
seed: int = None,
resolution: int = 512,
center_crop: bool = False,
train_batch_size: int = 16,
train_batch_size: int = 4,
num_train_epochs: int = 100,
max_train_steps: int = 5000,
gradient_accumulation_steps: int = 1,

View File

@@ -70,7 +70,7 @@ dependencies = [
"taming-transformers-rom1504",
"test-tube>=0.7.5",
"torch-fidelity",
"torch>=1.13.1",
"torch~=1.13.1",
"torchmetrics",
"torchvision>=0.14.1",
"transformers~=4.26",
@@ -147,7 +147,7 @@ version = {attr = "ldm.invoke.__version__"}
[tool.setuptools.package-data]
"invokeai.assets.web" = ["**.png"]
"invokeai.configs" = ["**.example", "**.txt", "**.yaml"]
"invokeai.configs" = ["**.example", "**.txt", "**.yaml", "**/*.yaml"]
"invokeai.frontend.dist" = ["**"]
[tool.black]