jumbo merge part two

pull/1436/head
Vladimir Mandic 2023-06-14 11:22:59 -04:00
parent a9f66cb33e
commit 0ddf613b49
28 changed files with 175 additions and 213 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# defaults
__pycache__
.ruff_cache
/cache.json
/metadata.json
/config.json

View File

@ -1,23 +1,27 @@
# Change Log for SD.Next
## Update for 06/14/2023
- simplify token merging
- reorganize some settings
## Update for 06/13/2023
One bigger update...
Just a day later and one *bigger update*...
Both some **new functionality** as well as **massive merges** from upstream
- new cache for models/lora/lyco metadata: `metadata.json`
drastically reduces disk access on app startup
- allow saving of **ui default values**
- allow saving/resetting of **ui default values**
settings -> ui defaults
- ability to run server without loaded model
default is to auto-load model on startup, can be changed in settings -> stable diffusion
if disabled, model will be loaded on first request, e.g. when you click generate
this is useful when you want to start server to perform other tasks like upscaling which do not rely on model
useful when you want to start server to perform other tasks like upscaling which do not rely on model
- updated `accelerate` and `xformers`
- huge nubmer of changes ported from **A1111** upstream
this was a massive merge
hopefully this does not cause any regressions
this was a massive merge, hopefully this does not cause any regressions
and still a bit more pending...
## Update for 06/12/2023
@ -84,7 +88,7 @@ Another bigger one...And more to come in the next few days...
- new live preview mode: taesd
i really like this one, so its enabled as default for new installs
- settings search feature
- new sampler: sde++ 2m sde
- new sampler: dpm++ 2m sde
- fully common save/zip/delete (new) options in all tabs
which (again) meant rework of process image tab
- system info tab: live gpu utilization/memory graphs for nvidia gpus

13
TODO.md
View File

@ -23,8 +23,17 @@ Stuff to be investigated...
Pick & merge PRs from main repo...
- TODO: <https://github.com/AUTOMATIC1111/stable-diffusion-webui/compare/89f9faa...baf6946>
- STATUS: up-to-date 05/13/2023
- List:
- <https://github.com/AUTOMATIC1111/stable-diffusion-webui/compare/89f9faa...baf6946>
- Last: 1e5afd4
- Todo:
- <https://github.com/AUTOMATIC1111/stable-diffusion-webui/commit/f6fc791>
- Skipped:
- add explict hires prompt: unnecessarily complicated and spread over large number of commits due to many regressions
- allow scripts to add cross-optimization methods: dangerous
- load extension info in threads: unnecessary as other optimizations already in place perform equally good
- Broken:
- sub-quadratic optimization changes in <https://github.com/AUTOMATIC1111/stable-diffusion-webui/commit/c8732df>
## Integration

@ -1 +1 @@
Subproject commit 123d1da15d802823480f8020312ce449523f10e2
Subproject commit 025dea96720197dd4486a5bb8e2f4d72a95a3088

@ -1 +1 @@
Subproject commit b28c03b10f45d44d09b570b20fe8a4f61061b294
Subproject commit 152203139e41029a55f2162155853ec6506272b4

View File

@ -80,7 +80,7 @@
{"id":"","label":"CFG Scale","localized":"","hint":"Classifier Free Guidance scale: how strongly the image should conform to prompt. Lower values produce more creative results, higher values make it follow the prompt more strictly; recommended values between 5-10"},
{"id":"","label":"CLIP skip","localized":"","hint":"Clip skip is a feature that allows users to control the level of specificity of the prompt, the higher the CLIP skip value, the less deep the prompt will be interpreted. CLIP Skip 1 is typical while some anime models produce better results at CLIP skip 2"},
{"id":"","label":"Seed","localized":"","hint":"A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result"},
{"id":"","label":"Extra","localized":"","hint":""},
{"id":"","label":"Extra","localized":"","hint":"Show additional options"},
{"id":"","label":"Variation seed","localized":"","hint":"Seed of a different picture to be mixed into the generation"},
{"id":"","label":"Variation strength","localized":"","hint":"How strong of a variation to produce. At 0, there will be no effect. At 1, you will get the complete picture with variation seed (except for ancestral samplers, where you will just get something)"},
{"id":"","label":"Resize seed from width","localized":"","hint":"Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution"},
@ -98,10 +98,10 @@
{"id":"","label":"Show result images","localized":"","hint":"Enable to show the processed images in the image pane"},
{"id":"","label":"Resize","localized":"","hint":"Factor for resizing 1x mean no upscale, 4x means 4 times upscale, high values might lead to memory issues on small graphics cards"},
{"id":"","label":"Crop to fit","localized":"","hint":"If the dimensions of your source image (e.g. 512x510) deviate from your target dimensions (e.g. 1024x768) this function will fit your upscaled image into your target size image. Excess will be cropped"},
{"id":"","label":"Secondary Upscaler","localized":"","hint":""},
{"id":"","label":"Upscaler 2 visibility","localized":"","hint":""},
{"id":"","label":"GFPGAN visibility","localized":"","hint":""},
{"id":"","label":"CodeFormer visibility","localized":"","hint":""},
{"id":"","label":"Secondary Upscaler","localized":"","hint":"Select secondary upscaler to run after initial upscaler"},
{"id":"","label":"Upscaler 2 visibility","localized":"","hint":"Strength of the secondary upscaler"},
{"id":"","label":"GFPGAN visibility","localized":"","hint":"Strength of GFPGAN face restore network, 0=disabled"},
{"id":"","label":"CodeFormer visibility","localized":"","hint":"Strength of CodeFormer face restore network, 0=disabled"},
{"id":"","label":"CodeFormer weight (0 = max, 1 = min)","localized":"","hint":""}
],
"settings menu": [
@ -281,7 +281,7 @@
{"id":"","label":"Prompt attention mean normalization","localized":"","hint":""},
{"id":"","label":"Disable conditional batching enabled on low memory systems","localized":"","hint":""},
{"id":"","label":"Enable samplers quantization for sharper and cleaner results","localized":"","hint":""},
{"id":"","label":"Increase coherency by padding from the last comma within n tokens when using more than 75 tokens","localized":"","hint":""},
{"id":"","label":"Prompt padding for long prompts","localized":"","hint":"Increase coherency by padding from the last comma within n tokens when using more than 75 tokens"},
{"id":"","label":"Original","localized":"","hint":""},
{"id":"","label":"Diffusers","localized":"","hint":""},
{"id":"","label":"VRAM usage polls per second during generation","localized":"","hint":""},
@ -425,14 +425,14 @@
{"id":"","label":"DPM++ 2M SDE Karras","localized":"","hint":""},
{"id":"","label":"DDIM","localized":"","hint":"Denoising Diffusion Implicit Models - best at inpainting"},
{"id":"","label":"UniPC","localized":"","hint":"Unified Predictor-Corrector Framework for Fast Sampling of Diffusion Models"},
{"id":"","label":"Secondary sampler","localized":"","hint":""},
{"id":"","label":"Force latent upscaler sampler","localized":"","hint":""},
{"id":"","label":"Secondary sampler","localized":"","hint":"Use specific sampler as fallback sampler if primary is not supported for specific operation"},
{"id":"","label":"Force latent upscaler sampler","localized":"","hint":"Force specific sampler for second pass operations"},
{"id":"","label":"Noise multiplier for ancestral samplers (eta)","localized":"","hint":""},
{"id":"","label":"Noise multiplier for DDIM (eta)","localized":"","hint":""},
{"id":"","label":"uniform","localized":"","hint":""},
{"id":"","label":"quad","localized":"","hint":""},
{"id":"","label":"sigma churn","localized":"","hint":""},
{"id":"","label":"Negative Guidance minimum sigma","localized":"","hint":""},
{"id":"","label":"Negative Guidance minimum sigma","localized":"","hint":"Skip negative prompt for some steps when the image is almost ready, 0=disable"},
{"id":"","label":"sigma tmin","localized":"","hint":""},
{"id":"","label":"sigma noise","localized":"","hint":""},
{"id":"","label":"Noise seed delta (eta)","localized":"","hint":""},
@ -494,10 +494,9 @@
{"id":"","label":"Card height for Extra Networks (px)","localized":"","hint":""},
{"id":"","label":"Extra text to add before <...> when adding extra network to prompt","localized":"","hint":""},
{"id":"","label":"Add hypernetwork to prompt","localized":"","hint":""},
{"id":"","label":"Enable redundant token merging via tomesd for speed and memory improvements","localized":"","hint":""},
{"id":"","label":"Token merging Ratio. Higher merging ratio = faster generation, smaller VRAM usage, lower quality","localized":"","hint":""},
{"id":"","label":"Apply only to high-res fix pass. Disabling can yield a ~20-35% speedup on contemporary resolutions","localized":"","hint":""},
{"id":"","label":"Merging Ratio (high-res pass) - If 'Apply only to high-res' is enabled, this will always be the ratio used","localized":"","hint":""},
{"id":"","label":"Token merging ratio","localized":"","hint":"Enable redundant token merging via tomesd for speed and memory improvements, 0=disabled"},
{"id":"","label":"Token merging ratio for img2img","localized":"","hint":"Enable redundant token merging for img2img via tomesd for speed and memory improvements, 0=disabled"},
{"id":"","label":"Token merging ratio for hires pass","localized":"","hint":"Enable redundant token merging for hires pass via tomesd for speed and memory improvements, 0=disabled"},
{"id":"","label":"Use random perturbations - Can improve outputs for certain samplers. For others, it may cause visual artifacting","localized":"","hint":""},
{"id":"","label":"Merge attention (Recommend on)","localized":"","hint":""},
{"id":"","label":"Merge cross attention (Recommend off)","localized":"","hint":""},

View File

@ -42,18 +42,28 @@ const re_extranet = /<([^:]+:[^:]+):[\d\.]+>/;
const re_extranet_g = /\s+<([^:]+:[^:]+):[\d\.]+>/g;
function tryToRemoveExtraNetworkFromPrompt(textarea, text) {
let m = text.match(re_extranet);
if (!m) return false;
const partToSearch = m[1];
let replaced = false;
const newTextareaText = textarea.value.replaceAll(re_extranet_g, (found, index) => {
m = found.match(re_extranet);
if (m[1] === partToSearch) {
replaced = true;
return '';
}
return found;
});
var m = text.match(re_extranet);
var replaced = false;
var newTextareaText;
if (m) {
var partToSearch = m[1];
newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found) {
m = found.match(re_extranet);
if (m[1] == partToSearch) {
replaced = true;
return "";
}
return found;
});
} else {
newTextareaText = textarea.value.replaceAll(new RegExp(text, "g"), function(found) {
if (found == text) {
replaced = true;
return "";
}
return found;
});
}
if (replaced) {
textarea.value = newTextareaText;
return true;

View File

@ -97,12 +97,16 @@ document.addEventListener('keydown', (e) => {
* checks that a UI element is not in another hidden element or tab content
*/
function uiElementIsVisible(el) {
let isVisible = !el.closest('.\\!hidden');
if (el === document) return true;
const computedStyle = getComputedStyle(el);
const isVisible = computedStyle.display !== 'none';
if (!isVisible) return false;
while (isVisible = el.closest('.tabitem')?.style.display !== 'none') {
if (!isVisible) return false;
if (el.parentElement) el = el.parentElement;
else break;
}
return isVisible;
return uiElementIsVisible(el.parentNode);
}
function uiElementInSight(el) {
const clRect = el.getBoundingClientRect();
const windowHeight = window.innerHeight;
const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight;
return isOnScreen;
}

View File

@ -5,6 +5,7 @@ import shlex
import logging
import subprocess
import installer
from functools import lru_cache
commandline_args = os.environ.get('COMMANDLINE_ARGS', "")
@ -32,6 +33,7 @@ def init_modules():
extensions_dir = modules.paths_internal.extensions_dir
@lru_cache()
def commit_hash(): # compatbility function
global stored_commit_hash # pylint: disable=global-statement
if stored_commit_hash is not None:
@ -43,6 +45,7 @@ def commit_hash(): # compatbility function
return stored_commit_hash
@lru_cache()
def run(command, desc=None, errdesc=None, custom_env=None, live=False): # compatbility function
if desc is not None:
installer.log.info(desc)
@ -65,18 +68,22 @@ def check_run(command): # compatbility function
return result.returncode == 0
@lru_cache()
def is_installed(package): # compatbility function
return installer.installed(package)
@lru_cache()
def repo_dir(name): # compatbility function
return os.path.join(script_path, dir_repos, name)
@lru_cache()
def run_python(code, desc=None, errdesc=None): # compatbility function
return run(f'"{sys.executable}" -c "{code}"', desc, errdesc)
@lru_cache()
def run_pip(pkg, desc=None): # compatbility function
if desc is None:
desc = pkg
@ -84,6 +91,7 @@ def run_pip(pkg, desc=None): # compatbility function
return run(f'"{sys.executable}" -m pip {pkg} --prefer-binary{index_url_line}', desc=f"Installing {desc}", errdesc=f"Couldn't install {desc}")
@lru_cache()
def check_run_python(code): # compatbility function
return check_run(f'"{sys.executable}" -c "{code}"')

View File

@ -81,7 +81,6 @@ def compatibility_args(opts, args):
group.add_argument("--opt-channelslast", help=argparse.SUPPRESS, default=opts.opt_channelslast)
group.add_argument("--xformers", default = (opts.cross_attention_optimization == "xFormers"), action='store_true', help=argparse.SUPPRESS)
group.add_argument("--disable-nan-check", help=argparse.SUPPRESS, default=opts.disable_nan_check)
group.add_argument("--token-merging", help=argparse.SUPPRESS, default=opts.token_merging)
group.add_argument("--rollback-vae", help=argparse.SUPPRESS, default=opts.rollback_vae)
group.add_argument("--no-half", help=argparse.SUPPRESS, default=opts.no_half)
group.add_argument("--no-half-vae", help=argparse.SUPPRESS, default=opts.no_half_vae)

View File

@ -154,7 +154,7 @@ class UpscalerESRGAN(Upscaler):
if "http" in path:
filename = load_file_from_url(
url=self.model_url,
model_dir=self.model_path,
model_dir=self.model_download_path,
file_name=f"{self.model_name}.pth",
progress=True,
)

View File

@ -126,8 +126,6 @@ def list_extensions():
extension_paths = []
extension_names = []
extension_folders = [extensions_builtin_dir] if shared.cmd_opts.safe else [extensions_builtin_dir, extensions_dir]
if shared.cmd_opts.base:
extension_folders = []
for dirname in extension_folders:
if not os.path.isdir(dirname):
return

View File

@ -188,8 +188,9 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_
output_modelname = os.path.join(ckpt_dir, filename)
shared.state.nextjob()
shared.state.textinfo = "Saving"
metadata = {"format": "pt", "sd_merge_models": {}, "sd_merge_recipe": None}
metadata = None
if save_metadata:
metadata = {"format": "pt", "sd_merge_models": {}}
merge_recipe = {
"type": "webui", # indicate this model was merged with webui's built-in merger
"primary_model_hash": primary_model_info.sha256,

View File

@ -275,7 +275,6 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
else:
res[k] = v
# Missing CLIP skip means it was set to 1 (the default)
if "Clip skip" not in res:
res["Clip skip"] = "1"
@ -285,28 +284,6 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model
if "Hires resize-1" not in res:
res["Hires resize-1"] = 0
res["Hires resize-2"] = 0
# Infer additional override settings for token merging
token_merging_ratio = res.get("Token merging ratio", None)
token_merging_ratio_hr = res.get("Token merging ratio hr", None)
if token_merging_ratio is not None or token_merging_ratio_hr is not None:
res["Token merging"] = 'True'
if token_merging_ratio is None:
res["Token merging hr only"] = 'True'
else:
res["Token merging hr only"] = 'False'
if res.get("Token merging random", None) is None:
res["Token merging random"] = 'False'
if res.get("Token merging merge attention", None) is None:
res["Token merging merge attention"] = 'True'
if res.get("Token merging merge cross attention", None) is None:
res["Token merging merge cross attention"] = 'False'
if res.get("Token merging merge mlp", None) is None:
res["Token merging merge mlp"] = 'False'
if res.get("Token merging stride x", None) is None:
res["Token merging stride x"] = '2'
if res.get("Token merging stride y", None) is None:
res["Token merging stride y"] = '2'
restore_old_hires_fix_params(res)
return res
@ -326,17 +303,8 @@ infotext_to_setting_name_mapping = [
('UniPC skip type', 'uni_pc_skip_type'),
('UniPC order', 'uni_pc_order'),
('UniPC lower order final', 'uni_pc_lower_order_final'),
('Token merging', 'token_merging'),
('Token merging ratio', 'token_merging_ratio'),
('Token merging hr only', 'token_merging_hr_only'),
('Token merging ratio hr', 'token_merging_ratio_hr'),
('Token merging random', 'token_merging_random'),
('Token merging merge attention', 'token_merging_merge_attention'),
('Token merging merge cross attention', 'token_merging_merge_cross_attention'),
('Token merging merge mlp', 'token_merging_merge_mlp'),
('Token merging maximum downsampling', 'token_merging_maximum_down_sampling'),
('Token merging stride x', 'token_merging_stride_x'),
('Token merging stride y', 'token_merging_stride_y')
]

View File

@ -86,7 +86,7 @@ def load_models(model_path: str, model_url: str = None, command_path: str = None
if model_url is not None and len(output) == 0:
if download_name is not None:
from basicsr.utils.download_util import load_file_from_url
dl = load_file_from_url(model_url, model_path, True, download_name)
dl = load_file_from_url(model_url, places[0], True, download_name)
output.append(dl)
else:
output.append(model_url)
@ -181,7 +181,10 @@ def load_upscalers():
for cls in reversed(used_classes.values()):
name = cls.__name__
cmd_name = f"{name.lower().replace('upscaler', '')}_models_path"
scaler = cls(commandline_options.get(cmd_name, None))
commandline_model_path = commandline_options.get(cmd_name, None)
scaler = cls(commandline_model_path)
scaler.user_path = commandline_model_path
scaler.model_download_path = commandline_model_path or scaler.model_path
datas += scaler.scalers
shared.sd_upscalers = sorted(

View File

@ -9,7 +9,6 @@ import torch
import numpy as np
from PIL import Image, ImageFilter, ImageOps
import cv2
import tomesd
from skimage import exposure
from ldm.data.util import AddMiDaS
from ldm.models.diffusion.ddpm import LatentDepth2ImageDiffusion
@ -130,6 +129,8 @@ class StableDiffusionProcessing:
self.override_settings_restore_afterwards = override_settings_restore_afterwards
self.is_using_inpainting_conditioning = False
self.disable_extra_networks = False
self.token_merging_ratio = 0
self.token_merging_ratio_hr = 0
if not seed_enable_extras:
self.subseed = -1
self.subseed_strength = 0
@ -145,7 +146,7 @@ class StableDiffusionProcessing:
self.clip_skip = clip_skip
self.iteration = 0
self.is_hr_pass = False
opts.data['clip_skip'] = clip_skip
# opts.data['clip_skip'] = clip_skip # todo is this necessary?
@property
@ -243,6 +244,11 @@ class StableDiffusionProcessing:
def close(self):
self.sampler = None # pylint: disable=attribute-defined-outside-init
def get_token_merging_ratio(self, for_hr=False):
if for_hr:
return self.token_merging_ratio_hr or opts.token_merging_ratio_hr or self.token_merging_ratio or opts.token_merging_ratio
return self.token_merging_ratio or opts.token_merging_ratio
class Processed:
def __init__(self, p: StableDiffusionProcessing, images_list, seed=-1, info="", subseed=None, all_prompts=None, all_negative_prompts=None, all_seeds=None, all_subseeds=None, index_of_first_image=0, infotexts=None, comments=""):
@ -278,6 +284,7 @@ class Processed:
self.s_tmin = p.s_tmin
self.s_tmax = p.s_tmax
self.s_noise = p.s_noise
self.s_min_uncond = p.s_min_uncond
self.sampler_noise_scheduler_override = p.sampler_noise_scheduler_override
self.prompt = self.prompt if type(self.prompt) != list else self.prompt[0]
self.negative_prompt = self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0]
@ -288,6 +295,8 @@ class Processed:
self.all_negative_prompts = all_negative_prompts or p.all_negative_prompts or [self.negative_prompt]
self.all_seeds = all_seeds or p.all_seeds or [self.seed]
self.all_subseeds = all_subseeds or p.all_subseeds or [self.subseed]
self.token_merging_ratio = p.token_merging_ratio
self.token_merging_ratio_hr = p.token_merging_ratio_hr
self.infotexts = infotexts or [info]
def js(self):
@ -326,6 +335,9 @@ class Processed:
def infotext(self, p: StableDiffusionProcessing, index):
return create_infotext(p, self.all_prompts, self.all_seeds, self.all_subseeds, comments=[], position_in_batch=index % self.batch_size, iteration=index // self.batch_size)
def get_token_merging_ratio(self, for_hr=False):
return self.token_merging_ratio_hr if for_hr else self.token_merging_ratio
# from https://discuss.pytorch.org/t/help-regarding-slerp-function-for-generative-model-sampling/32475/3
def slerp(val, low, high):
@ -426,6 +438,9 @@ def fix_seed(p):
def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_subseeds, comments=None, iteration=0, position_in_batch=0): # pylint: disable=unused-argument
index = position_in_batch + iteration * p.batch_size
enable_hr = getattr(p, 'enable_hr', False)
token_merging_ratio = p.get_token_merging_ratio()
token_merging_ratio_hr = p.get_token_merging_ratio(for_hr=True)
uses_ensd = opts.eta_noise_seed_delta != 0
if uses_ensd:
uses_ensd = sd_samplers_common.is_sampler_using_eta_noise_seed_delta(p)
@ -451,14 +466,8 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su
"ENSD": opts.eta_noise_seed_delta if uses_ensd else None,
"Init image hash": getattr(p, 'init_img_hash', None),
"Version": git_commit,
"Token merging ratio": None if not (opts.token_merging or cmd_opts.token_merging) or opts.token_merging_hr_only else opts.token_merging_ratio,
"Token merging ratio hr": None if not (opts.token_merging or cmd_opts.token_merging) else opts.token_merging_ratio_hr,
"Token merging random": None if opts.token_merging_random is False else opts.token_merging_random,
"Token merging merge attention": None if opts.token_merging_merge_attention is True else opts.token_merging_merge_attention,
"Token merging merge cross attention": None if opts.token_merging_merge_cross_attention is False else opts.token_merging_merge_cross_attention,
"Token merging merge mlp": None if opts.token_merging_merge_mlp is False else opts.token_merging_merge_mlp,
"Token merging stride x": None if opts.token_merging_stride_x == 2 else opts.token_merging_stride_x,
"Token merging stride y": None if opts.token_merging_stride_y == 2 else opts.token_merging_stride_y,
"Token merging ratio": None if token_merging_ratio == 0 else token_merging_ratio,
"Token merging ratio hr": None if not enable_hr or token_merging_ratio_hr == 0 else token_merging_ratio_hr,
"Parser": opts.prompt_attention,
}
generation_params.update(p.extra_generation_params)
@ -511,9 +520,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
if k == 'sd_vae':
sd_vae.reload_vae_weights()
if (opts.token_merging or cmd_opts.token_merging) and not opts.token_merging_hr_only:
sd_models.apply_token_merging(sd_model=p.sd_model, hr=False)
log.debug('Token merging applied')
sd_models.apply_token_merging(p.sd_model, p.get_token_merging_ratio())
if cmd_opts.profile:
"""
@ -531,9 +538,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
else:
res = process_images_inner(p)
finally:
if opts.token_merging or cmd_opts.token_merging:
tomesd.remove_patch(p.sd_model)
log.debug('Token merging model optimizations removed')
sd_models.apply_token_merging(p.sd_model, 0)
if p.override_settings_restore_afterwards: # restore opts to original state
for k, v in stored_opts.items():
setattr(opts, k, v)
@ -642,11 +647,8 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
processed = Processed(p, [], p.seed, "")
file.write(processed.infotext(p, 0))
step_multiplier = 1
if not shared.opts.dont_fix_second_order_samplers_schedule:
try:
step_multiplier = 2 if sd_samplers.all_samplers_map.get(p.sampler_name).aliases[0] in ['k_dpmpp_2s_a', 'k_dpmpp_2s_a_ka', 'k_dpmpp_sde', 'k_dpmpp_sde_ka', 'k_dpm_2', 'k_dpm_2_a', 'k_heun'] else 1
except Exception:
pass
sampler_config = sd_samplers.find_sampler_config(p.sampler_name)
step_multiplier = 2 if sampler_config and sampler_config.options.get("second_order", False) else 1
if p.n_iter > 1:
shared.state.job = f"Batch {n+1} out of {p.n_iter}"
@ -774,7 +776,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
images_list=output_images,
seed=p.all_seeds[0],
info=infotext(),
comments="".join(f"\n\n{comment}" for comment in comments),
comments="\n".join(comments),
subseed=p.all_subseeds[0],
index_of_first_image=index_of_first_image,
infotexts=infotexts,
@ -943,16 +945,9 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
noise = create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, p=self)
x = None
devices.torch_gc() # GC now before running the next img2img to prevent running out of memory
# apply token merging optimizations from tomesd for high-res pass
if (cmd_opts.token_merging or opts.token_merging) and (opts.token_merging_hr_only or opts.token_merging_ratio_hr != opts.token_merging_ratio):
# case where user wants to use separate merge ratios
if not opts.token_merging_hr_only:
# clean patch done by first pass. (clobbering the first patch might be fine? this might be excessive)
tomesd.remove_patch(self.sd_model)
log.debug('Temporarily removed token merging optimizations in preparation for next pass')
sd_models.apply_token_merging(sd_model=self.sd_model, hr=True)
log.debug('Applied token merging for high-res pass')
sd_models.apply_token_merging(self.sd_model, self.get_token_merging_ratio(for_hr=True))
samples = self.sampler.sample_img2img(self, samples, noise, conditioning, unconditional_conditioning, steps=self.hr_second_pass_steps or self.steps, image_conditioning=image_conditioning)
sd_models.apply_token_merging(self.sd_model, self.get_token_merging_ratio())
self.is_hr_pass = False
return samples
@ -1081,3 +1076,6 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
del x
devices.torch_gc()
return samples
def get_token_merging_ratio(self, for_hr=False):
return self.token_merging_ratio or ("token_merging_ratio" in self.override_settings and opts.token_merging_ratio) or opts.token_merging_ratio_img2img or opts.token_merging_ratio

View File

@ -73,7 +73,7 @@ class UpscalerRealESRGAN(Upscaler):
print(f"Unable to find model info: {path}")
return None
if info.local_data_path.startswith("http"):
info.local_data_path = load_file_from_url(url=info.data_path, model_dir=self.model_path, progress=True)
info.local_data_path = load_file_from_url(url=info.data_path, model_dir=self.model_download_path, progress=True)
return info
except Exception as e:
errors.display(e, 'real-esrgan model list')

View File

@ -76,10 +76,6 @@ class AfterCFGCallbackParams:
self.total_sampling_steps = total_sampling_steps
"""Total number of sampling steps planned"""
self.output_altered = False
"""A flag for CFGDenoiser indicating whether the output has been altered by the callback"""
class UiTrainTabParams:
def __init__(self, txt2img_preview_params):

View File

@ -637,7 +637,6 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False):
timer.record("config")
if sd_model is None or checkpoint_config != sd_model.used_config:
del sd_model
checkpoints_loaded.clear()
if shared.backend == shared.Backend.ORIGINAL:
load_model(checkpoint_info, already_loaded_state_dict=state_dict, timer=timer)
else:
@ -676,26 +675,23 @@ def unload_model_weights(sd_model=None, _info=None):
return sd_model
def apply_token_merging(sd_model, hr: bool):
def apply_token_merging(sd_model, token_merging_ratio):
"""
Applies speed and memory optimizations from tomesd.
Args:
hr (bool): True if called in the context of a high-res pass
"""
ratio = shared.opts.token_merging_ratio
if hr:
ratio = shared.opts.token_merging_ratio_hr
tomesd.apply_patch(
sd_model,
ratio=ratio,
max_downsample=shared.opts.token_merging_maximum_down_sampling,
sx=shared.opts.token_merging_stride_x,
sy=shared.opts.token_merging_stride_y,
use_rand=shared.opts.token_merging_random,
merge_attn=shared.opts.token_merging_merge_attention,
merge_crossattn=shared.opts.token_merging_merge_cross_attention,
merge_mlp=shared.opts.token_merging_merge_mlp
)
current_token_merging_ratio = getattr(sd_model, 'applied_token_merged_ratio', 0)
shared.log.debug(f'Appplying token merging: current={current_token_merging_ratio} target={token_merging_ratio}')
if current_token_merging_ratio == token_merging_ratio:
return
if current_token_merging_ratio > 0:
tomesd.remove_patch(sd_model)
if token_merging_ratio > 0:
tomesd.apply_patch(
sd_model,
ratio=token_merging_ratio,
use_rand=False, # can cause issues with some samplers
merge_attn=True,
merge_crossattn=False,
merge_mlp=False
)
sd_model.applied_token_merged_ratio = token_merging_ratio

View File

@ -26,7 +26,7 @@ approximation_indexes = {"Full VAE": 0, "Approximate NN": 1, "Approximate simple
def single_sample_to_image(sample, approximation=None):
if approximation is None:
if approximation is None or approximation not in approximation_indexes.keys():
approximation = approximation_indexes.get(opts.show_progress_type, 0)
if approximation == 0:
x_sample = processing.decode_first_stage(shared.sd_model, sample.unsqueeze(0))[0] * 0.5 + 0.5

View File

@ -93,17 +93,11 @@ def resolve_vae(checkpoint_file):
return shared.cmd_opts.vae, 'forced'
is_automatic = shared.opts.sd_vae in {"Automatic", "auto"} # "auto" for people with old config
vae_near_checkpoint = find_vae_near_checkpoint(checkpoint_file)
if vae_near_checkpoint is not None and (shared.opts.sd_vae_as_default):
if vae_near_checkpoint is not None:
return vae_near_checkpoint, 'near checkpoint'
if is_automatic:
for named_vae_location in [
os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".pt"),
os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".ckpt"),
os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".safetensors"),
os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".vae.pt"),
os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".vae.ckpt"),
os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0] + ".vae.safetensors"),
]:
basename = os.path.join(vae_path, os.path.splitext(os.path.basename(checkpoint_file))[0])
for named_vae_location in [basename + ".pt", basename + ".ckpt", basename + ".safetensors", basename + ".vae.pt", basename + ".vae.ckpt", basename + ".vae.safetensors"]:
if os.path.isfile(named_vae_location):
return named_vae_location, 'in VAE dir'
if shared.opts.sd_vae == "None":

View File

@ -293,20 +293,24 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), {
"sd_vae_checkpoint_cache": OptionInfo(0, "Number of cached VAE checkpoints", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
"sd_vae": OptionInfo("Automatic", "Select VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list),
"sd_model_dict": OptionInfo('None', "Stable Diffusion checkpoint dict", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints),
"sd_vae_sliced_encode": OptionInfo(False, "Enable splitting of hires batch processing"),
"stream_load": OptionInfo(False, "When loading models attempt stream loading optimized for slow or network storage"),
"model_reuse_dict": OptionInfo(False, "When loading models attempt to reuse previous model dictionary"),
"prompt_attention": OptionInfo("Full parser", "Prompt attention parser", gr.Radio, lambda: {"choices": ["Full parser", "Compel parser", "A1111 parser", "Fixed attention"] }),
"prompt_mean_norm": OptionInfo(True, "Prompt attention mean normalization"),
"comma_padding_backtrack": OptionInfo(20, "Prompt padding for long prompts", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }),
"sd_backend": OptionInfo("Original", "Stable Diffusion backend (experimental)", gr.Radio, lambda: {"choices": ["Original", "Diffusers"] }),
}))
options_templates.update(options_section(('optimizations', "Optimizations"), {
"cross_attention_optimization": OptionInfo(cross_attention_optimization_default, "Cross-attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention() }),
"cross_attention_options": OptionInfo([], "Cross-attention advanced options", gr.CheckboxGroup, lambda: {"choices": ['xFormers enable flash Attention', 'SDP disable memory attention']}),
"sub_quad_q_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention query chunk size", gr.Slider, {"minimum": 16, "maximum": 8192, "step": 8}),
"sub_quad_kv_chunk_size": OptionInfo(512, "Sub-quadratic cross-attention kv chunk size", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8}),
"sub_quad_chunk_threshold": OptionInfo(80, "Sub-quadratic cross-attention chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}),
"prompt_attention": OptionInfo("Full parser", "Prompt attention parser", gr.Radio, lambda: {"choices": ["Full parser", "Compel parser", "A1111 parser", "Fixed attention"] }),
"prompt_mean_norm": OptionInfo(True, "Prompt attention mean normalization"),
"always_batch_cond_uncond": OptionInfo(False, "Disable conditional batching enabled on low memory systems"),
"enable_quantization": OptionInfo(True, "Enable samplers quantization for sharper and cleaner results"),
"comma_padding_backtrack": OptionInfo(20, "Increase coherency by padding from the last comma within n tokens when using more than 75 tokens", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1 }),
"sd_backend": OptionInfo("Original", "Stable Diffusion backend (experimental)", gr.Radio, lambda: {"choices": ["Original", "Diffusers"] }),
"token_merging_ratio": OptionInfo(0.0, "Token merging ratio", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}),
"token_merging_ratio_img2img": OptionInfo(0.0, "Token merging ratio for img2img", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}),
"token_merging_ratio_hr": OptionInfo(0.0, "Token merging ratio for hires pass", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}),
"sd_vae_sliced_encode": OptionInfo(False, "Enable splitting of hires batch processing"),
}))
options_templates.update(options_section(('cuda', "Compute Settings"), {
@ -444,6 +448,8 @@ options_templates.update(options_section(('sampler-params', "Sampler Settings"),
"show_samplers": OptionInfo(["Euler a", "UniPC", "DDIM", "DPM++ 2M SDE", "DPM++ 2M SDE Karras", "DPM2 Karras", "DPM++ 2M Karras"], "Show samplers in user interface", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers() if x.name != "PLMS"]}),
"fallback_sampler": OptionInfo("Euler a", "Secondary sampler", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}),
"force_latent_sampler": OptionInfo("None", "Force latent upscaler sampler", gr.Dropdown, lambda: {"choices": ["None"] + [x.name for x in list_samplers()]}),
"always_batch_cond_uncond": OptionInfo(False, "Disable conditional batching enabled on low memory systems"),
"enable_quantization": OptionInfo(True, "Enable samplers quantization for sharper and cleaner results"),
"eta_ancestral": OptionInfo(1.0, "Noise multiplier for ancestral samplers (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
"eta_ddim": OptionInfo(0.0, "Noise multiplier for DDIM (eta)", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
"ddim_discretize": OptionInfo('uniform', "DDIM discretize img2img", gr.Radio, {"choices": ['uniform', 'quad']}),
@ -496,6 +502,9 @@ options_templates.update(options_section(('interrogate', "Interrogate"), {
}))
options_templates.update(options_section(('upscaling', "Upscaling"), {
"face_restoration_model": OptionInfo("CodeFormer", "Face restoration model", gr.Radio, lambda: {"choices": [x.name() for x in face_restorers]}),
"code_former_weight": OptionInfo(0.2, "CodeFormer weight parameter; 0 = maximum effect; 1 = minimum effect", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
"face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"),
"upscaler_for_img2img": OptionInfo("None", "Default upscaler for image resize operations", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}),
"realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Real-ESRGAN available models", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}),
"ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers (0 = no tiling)", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}),
@ -506,19 +515,10 @@ options_templates.update(options_section(('upscaling', "Upscaling"), {
"dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers"),
}))
options_templates.update(options_section(('lora', "Lora"), {
options_templates.update(options_section(('extra_networks', "Extra Networks"), {
"lyco_patch_lora": OptionInfo(False, "Use LyCoris handler for all Lora types", gr.Checkbox, { "visible": True }),
"lora_disable": OptionInfo(False, "Disable built-in Lora handler", gr.Checkbox, { "visible": True }, onchange=lora_disable),
"lora_functional": OptionInfo(False, "Use Kohya method for handling multiple Loras", gr.Checkbox, { "visible": True }),
}))
options_templates.update(options_section(('face-restoration', "Face restoration"), {
"face_restoration_model": OptionInfo("CodeFormer", "Face restoration model", gr.Radio, lambda: {"choices": [x.name() for x in face_restorers]}),
"code_former_weight": OptionInfo(0.2, "CodeFormer weight parameter; 0 = maximum effect; 1 = minimum effect", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
"face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"),
}))
options_templates.update(options_section(('extra_networks', "Extra Networks"), {
"extra_networks_default_view": OptionInfo("cards", "Default view for Extra Networks", gr.Dropdown, {"choices": ["cards", "thumbs"]}),
"extra_networks_default_multiplier": OptionInfo(1.0, "Multiplier for extra networks", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
"extra_networks_card_width": OptionInfo(0, "Card width for Extra Networks (px)"),
@ -527,20 +527,6 @@ options_templates.update(options_section(('extra_networks', "Extra Networks"), {
"sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: {"choices": ["None"] + list(hypernetworks.keys())}, refresh=reload_hypernetworks),
}))
options_templates.update(options_section(('token_merging', 'Token Merging'), {
"token_merging": OptionInfo(False, "Enable redundant token merging via tomesd for speed and memory improvements", gr.Checkbox),
"token_merging_ratio": OptionInfo(0.5, "Token merging Ratio. Higher merging ratio = faster generation, smaller VRAM usage, lower quality", gr.Slider, {"minimum": 0, "maximum": 0.9, "step": 0.1}),
"token_merging_hr_only": OptionInfo(True, "Apply only to high-res fix pass. Disabling can yield a ~20-35% speedup on contemporary resolutions", gr.Checkbox),
"token_merging_ratio_hr": OptionInfo(0.5, "Merging Ratio for high-res pass", gr.Slider, {"minimum": 0, "maximum": 0.9, "step": 0.1}),
"token_merging_random": OptionInfo(False, "Use random perturbations - Can improve outputs for certain samplers. For others, it may cause visual artifacting", gr.Checkbox),
"token_merging_merge_attention": OptionInfo(True, "Merge attention (Recommend on)", gr.Checkbox),
"token_merging_merge_cross_attention": OptionInfo(False, "Merge cross attention (Recommend off)", gr.Checkbox),
"token_merging_merge_mlp": OptionInfo(False, "Merge mlp (Strongly recommend off)", gr.Checkbox),
"token_merging_maximum_down_sampling": OptionInfo(1, "Maximum down sampling", gr.Radio, lambda: {"choices": [1, 2, 4, 8]}),
"token_merging_stride_x": OptionInfo(2, "Stride - X", gr.Slider, {"minimum": 2, "maximum": 8, "step": 2}),
"token_merging_stride_y": OptionInfo(2, "Stride - Y", gr.Slider, {"minimum": 2, "maximum": 8, "step": 2})
}))
options_templates.update(options_section((None, "Hidden options"), {
"disabled_extensions": OptionInfo([], "Disable these extensions"),
"disable_all_extensions": OptionInfo("none", "Disable all extensions (preserves the list of disabled extensions)", gr.Radio, {"choices": ["none", "user", "all"]}),

View File

@ -50,7 +50,7 @@ class StyleDatabase:
self.save_styles(self.path)
with open(self.path, "r", encoding="utf-8-sig", newline='') as file:
reader = csv.DictReader(file)
reader = csv.DictReader(file, skipinitialspace=True)
for row in reader:
try:
prompt = row["prompt"] if "prompt" in row else row["text"]

View File

@ -16,16 +16,15 @@ from typing import Optional, NamedTuple, List
import torch
from torch import Tensor
from torch.utils.checkpoint import checkpoint
import numpy as np
def narrow_trunc(
input: Tensor, # pylint: disable=redefined-builtin
tensor: Tensor,
dim: int,
start: int,
length: int
) -> Tensor:
return torch.narrow(input, dim, start, length if input.shape[dim] >= start + length else input.shape[dim] - start)
return torch.narrow(tensor, dim, start, length if tensor.shape[dim] >= start + length else tensor.shape[dim] - start)
class AttnChunk(NamedTuple):
@ -81,7 +80,7 @@ def _query_chunk_attention(
kv_chunk_size: int,
) -> Tensor:
_batch_x_heads, k_tokens, _k_channels_per_head = key.shape
_, _, _v_channels_per_head = value.shape
# _, _, v_channels_per_head = value.shape
def chunk_scanner(chunk_idx: int) -> AttnChunk:
key_chunk = narrow_trunc(
@ -202,14 +201,11 @@ def efficient_dot_product_attention(
value=value,
)
res = torch.zeros_like(query)
for i in range(math.ceil(q_tokens / query_chunk_size)):
attn_scores = compute_query_chunk_attn(
res = torch.cat([
compute_query_chunk_attn(
query=get_query_chunk(i * query_chunk_size),
key=key,
value=value,
)
res[:, i * query_chunk_size:i * query_chunk_size + attn_scores.shape[1], :] = attn_scores
) for i in range(math.ceil(q_tokens / query_chunk_size))
], dim=1)
return res

View File

@ -122,7 +122,9 @@ def make_commit_link(commit_hash, remote, text=None):
if text is None:
text = commit_hash[:8]
if remote.startswith("https://github.com/"):
href = os.path.join(remote, "commit", commit_hash)
if remote.endswith(".git"):
remote = remote[:-4]
href = remote + "/commit/" + commit_hash
return f'<a href="{href}" target="_blank">{text}</a>'
else:
return text

View File

@ -175,7 +175,7 @@ class ExtraNetworksPage:
"""
Find a preview PNG for a given path (without extension) and call link_preview on it.
"""
preview_extensions = ["jpg", "png", "webp", "tiff", "jp2"]
preview_extensions = ["jpg", "jpeg", "png", "webp", "tiff", "jp2"]
potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in preview_extensions], [])
for file in potential_files:
if os.path.isfile(file):

View File

@ -58,5 +58,5 @@ protobuf==3.20.3
pytorch_lightning==1.9.4
transformers==4.26.1
timm==0.6.13
tomesd==0.1.2
tomesd==0.1.3
urllib3==1.26.15

View File

@ -141,17 +141,10 @@ def apply_face_restore(p, opt, x):
p.restore_faces = is_active
def apply_token_merging_ratio_hr(p, x, xs):
shared.opts.data["token_merging_ratio_hr"] = x
def apply_token_merging_ratio(p, x, xs):
shared.opts.data["token_merging_ratio"] = x
def apply_token_merging_random(p, x, xs):
is_active = x.lower() in ('true', 'yes', 'y', '1')
shared.opts.data["token_merging_random"] = is_active
def apply_override(field):
def fun(p, x, xs):
p.override_settings[field] = x
return fun
def format_value_add_label(p, opt, x):
@ -233,9 +226,8 @@ axis_options = [
AxisOptionImg2Img("Image Mask Weight", float, apply_field("inpainting_mask_weight")),
AxisOption("UniPC Order", int, apply_uni_pc_order, cost=0.5),
AxisOption("Face restore", str, apply_face_restore, fmt=format_value),
AxisOption("ToMe ratio",float, apply_token_merging_ratio),
AxisOption("ToMe ratio for Hires fix",float, apply_token_merging_ratio_hr),
AxisOption("ToMe random pertubations",str, apply_token_merging_random, choices = lambda: ["Yes","No"])
AxisOption("Token merging ratio", float, apply_override('token_merging_ratio')),
AxisOption("Token merging ratio high-res", float, apply_override('token_merging_ratio_hr')),
]
@ -349,7 +341,6 @@ class SharedSettingsStackHelper(object):
self.uni_pc_order = shared.opts.uni_pc_order
self.token_merging_ratio_hr = shared.opts.token_merging_ratio_hr
self.token_merging_ratio = shared.opts.token_merging_ratio
self.token_merging_random = shared.opts.token_merging_random
self.sd_model_checkpoint = shared.opts.sd_model_checkpoint
self.sd_model_dict = shared.opts.sd_model_dict
self.sd_vae_checkpoint = shared.opts.sd_vae
@ -361,11 +352,10 @@ class SharedSettingsStackHelper(object):
shared.opts.data["uni_pc_order"] = self.uni_pc_order
shared.opts.data["token_merging_ratio_hr"] = self.token_merging_ratio_hr
shared.opts.data["token_merging_ratio"] = self.token_merging_ratio
shared.opts.data["token_merging_random"] = self.token_merging_random
shared.opts.data["force_latent_sampler"] = self.force_latent_sampler
if (self.sd_model_dict != shared.opts.sd_model_dict):
if self.sd_model_dict != shared.opts.sd_model_dict:
shared.opts.data["sd_model_dict"] = self.sd_model_dict
if (self.sd_model_checkpoint != shared.opts.sd_model_checkpoint):
if self.sd_model_checkpoint != shared.opts.sd_model_checkpoint:
shared.opts.data["sd_model_checkpoint"] = self.sd_model_checkpoint
sd_models.reload_model_weights()
if self.sd_vae_checkpoint != shared.opts.sd_vae: