diff --git a/CHANGELOG.md b/CHANGELOG.md index a1ae0c998..0174c5bdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,19 @@ ## Update for 2023-09-07 -Service release with many fixes +Mostly a service release +- tons of fixes +- new option **inference mode** + - default is standard `torch.no_grad` + new option is `torch.inference_only` which is slightly faster and uses less vram, but only works on some gpus +- cache samplers between run + reduces overhead between generate calls slightly +- clean-up logging + - capture system info in startup log + - capture extension output + - capture ldm output + - cleaner server restart + ## Update for 2023-09-06 diff --git a/installer.py b/installer.py index 435d2a654..6796cf7cf 100644 --- a/installer.py +++ b/installer.py @@ -108,6 +108,10 @@ def setup_logging(): # logging.getLogger("DeepSpeed").handlers = log.handlers +def print_dict(d): + return ' '.join([f'{k}={v}' for k, v in d.items()]) + + def print_profile(profile: cProfile.Profile, msg: str): try: from rich import print # pylint: disable=redefined-builtin @@ -265,6 +269,26 @@ def clone(url, folder, commithash=None): git(f'-C "{folder}" checkout {commithash}') +def get_platform(): + try: + if platform.system() == 'Windows': + release = platform.platform(aliased = True, terse = False) + else: + release = platform.release() + return { + # 'host': platform.node(), + 'arch': platform.machine(), + 'cpu': platform.processor(), + 'system': platform.system(), + 'release': release, + # 'platform': platform.platform(aliased = True, terse = False), + # 'version': platform.version(), + 'python': platform.python_version(), + } + except Exception as e: + return { 'error': e } + + # check python version def check_python(): supported_minors = [9, 10, 11] @@ -873,11 +897,13 @@ def extensions_preload(parser): from modules.script_loading import preload_extensions from modules.paths_internal import extensions_builtin_dir, extensions_dir extension_folders = [extensions_builtin_dir] if args.safe else [extensions_builtin_dir, extensions_dir] + preload_time = {} for ext_dir in extension_folders: t0 = time.time() preload_extensions(ext_dir, parser) t1 = time.time() - log.info(f'Extension preload: {round(t1 - t0, 1)}s {ext_dir}') + preload_time[ext_dir] = round(t1 - t0, 2) + log.info(f'Extension preload: {preload_time}') except Exception: log.error('Error running extension preloading') if args.profile: diff --git a/launch.py b/launch.py index a019d9356..773ad9ac6 100644 --- a/launch.py +++ b/launch.py @@ -40,7 +40,7 @@ def get_custom_args(): current = getattr(args, arg) if current != default: custom[arg] = getattr(args, arg) - installer.log.info(f'Command line args: {custom}') + installer.log.info(f'Command line args: {installer.print_dict(custom)}') @lru_cache() @@ -137,7 +137,8 @@ def start_server(immediate=True, server=None): collected = gc.collect() if not immediate: time.sleep(3) - installer.log.debug(f'Memory {get_memory_stats()} Collected {collected}') + if collected > 0: + installer.log.debug(f'Memory {get_memory_stats()} Collected {collected}') module_spec = importlib.util.spec_from_file_location('webui', 'webui.py') # installer.log.debug(f'Loading module: {module_spec}') server = importlib.util.module_from_spec(module_spec) @@ -174,6 +175,7 @@ if __name__ == "__main__": if args.skip_git: installer.log.info('Skipping GIT operations') installer.check_version() + installer.log.info(f'Platform: {installer.print_dict(installer.get_platform())}') installer.set_environment() installer.check_torch() installer.check_modified_files() diff --git a/modules/codeformer_model.py b/modules/codeformer_model.py index e6e75b219..34e3a7a5a 100644 --- a/modules/codeformer_model.py +++ b/modules/codeformer_model.py @@ -97,7 +97,7 @@ def setup_model(dirname): cropped_face_t = cropped_face_t.unsqueeze(0).to(devices.device_codeformer) try: - with torch.no_grad(): + with devices.inference_context(): output = self.net(cropped_face_t, w=w if w is not None else shared.opts.code_former_weight, adain=True)[0] restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1)) del output diff --git a/modules/deepbooru.py b/modules/deepbooru.py index 547e1b4c6..de50853d6 100644 --- a/modules/deepbooru.py +++ b/modules/deepbooru.py @@ -56,7 +56,7 @@ class DeepDanbooru: pic = images.resize_image(2, pil_image.convert("RGB"), 512, 512) a = np.expand_dims(np.array(pic, dtype=np.float32), 0) / 255 - with torch.no_grad(), devices.autocast(): + with devices.inference_context(), devices.autocast(): x = torch.from_numpy(a).to(devices.device) y = self.model(x)[0].detach().cpu().numpy() diff --git a/modules/devices.py b/modules/devices.py index 8f306adbe..db05ed39a 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -17,6 +17,51 @@ def has_mps() -> bool: return mac_specific.has_mps +def get_gpu_info(): + def get_driver(): + import os + import subprocess + if torch.cuda.is_available() and torch.version.cuda: + try: + result = subprocess.run('nvidia-smi --query-gpu=driver_version --format=csv,noheader', shell=True, check=False, env=os.environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + version = result.stdout.decode(encoding="utf8", errors="ignore").strip() + return version + except Exception: + return '' + else: + return '' + + if not torch.cuda.is_available(): + return {} + else: + try: + if torch.version.cuda: + return { + 'device': f'{torch.cuda.get_device_name(torch.cuda.current_device())} ({str(torch.cuda.device_count())}) ({torch.cuda.get_arch_list()[-1]}) {str(torch.cuda.get_device_capability(device))}', + 'cuda': torch.version.cuda, + 'cudnn': torch.backends.cudnn.version(), + 'driver': get_driver(), + } + elif torch.version.hip: + return { + 'device': f'{torch.cuda.get_device_name(torch.cuda.current_device())} ({str(torch.cuda.device_count())})', + 'hip': torch.version.hip, + } + else: + try: + import intel_extension_for_pytorch as ipex# pylint: disable=import-error, unused-import + return { + 'device': f'{torch.xpu.get_device_name(torch.xpu.current_device())} ({str(torch.xpu.device_count())})', + 'ipex': ipex.__version__, + } + except Exception: + return { + 'device': 'unknown' + } + except Exception as ex: + return { 'error': ex } + + def extract_device_id(args, name): # pylint: disable=redefined-outer-name for x in range(len(args)): if name in args[x]: @@ -95,8 +140,8 @@ def test_fp16(): _y = layerNorm(x) shared.log.debug('Torch FP16 test passed') return True - except Exception as e: - shared.log.warning(f'Torch FP16 test failed: Forcing FP32 operations: {e}') + except Exception as ex: + shared.log.warning(f'Torch FP16 test failed: Forcing FP32 operations: {ex}') shared.opts.cuda_dtype = 'FP32' shared.opts.no_half = True shared.opts.no_half_vae = True @@ -133,7 +178,7 @@ def set_cuda_params(): torch.backends.cudnn.allow_tf32 = True except Exception: pass - global dtype, dtype_vae, dtype_unet, unet_needs_upcast # pylint: disable=global-statement + global dtype, dtype_vae, dtype_unet, unet_needs_upcast, inference_context # pylint: disable=global-statement if shared.opts.cuda_dtype == 'FP32': dtype = torch.float32 dtype_vae = torch.float32 @@ -159,12 +204,14 @@ def set_cuda_params(): shared.log.info('Torch override VAE dtype: no-half set') dtype_vae = torch.float32 unet_needs_upcast = shared.opts.upcast_sampling + inference_context = torch.inference_mode if shared.opts.inference_mode == 'inference-mode' else torch.no_grad shared.log.debug(f'Desired Torch parameters: dtype={shared.opts.cuda_dtype} no-half={shared.opts.no_half} no-half-vae={shared.opts.no_half_vae} upscast={shared.opts.upcast_sampling}') - shared.log.info(f'Setting Torch parameters: dtype={dtype} vae={dtype_vae} unet={dtype_unet}') + shared.log.info(f'Setting Torch parameters: dtype={dtype} vae={dtype_vae} unet={dtype_unet} context={inference_context.__name__}') shared.log.debug(f'Torch default device: {torch.device(get_optimal_device_name())}') args = cmd_args.parser.parse_args() +backend = 'not set' if args.use_ipex or (hasattr(torch, 'xpu') and torch.xpu.is_available()): backend = 'ipex' from modules.intel.ipex import ipex_init @@ -188,6 +235,7 @@ elif sys.platform == 'darwin': else: backend = 'cpu' +inference_context = torch.no_grad cuda_ok = torch.cuda.is_available() cpu = torch.device("cpu") device = device_interrogate = device_gfpgan = device_esrgan = device_codeformer = None diff --git a/modules/dml/hijack/kdiffusion.py b/modules/dml/hijack/kdiffusion.py index 19e16b013..d772dc88f 100644 --- a/modules/dml/hijack/kdiffusion.py +++ b/modules/dml/hijack/kdiffusion.py @@ -1,7 +1,7 @@ import torch from tqdm.auto import tqdm from k_diffusion import sampling -from modules.shared import device +import modules.devices as devices def dpm_solver_adaptive(self, x, t_start, t_end, order=3, rtol=0.05, atol=0.0078, h_init=0.05, pcoeff=0., icoeff=1., dcoeff=0., accept_safety=0.81, eta=0., s_noise=1., noise_sampler=None): @@ -12,8 +12,8 @@ def dpm_solver_adaptive(self, x, t_start, t_end, order=3, rtol=0.05, atol=0.0078 if not forward and eta: raise ValueError('eta must be 0 for reverse sampling') h_init = abs(h_init) * (1 if forward else -1) - atol = torch.tensor(atol, device=device) - rtol = torch.tensor(rtol, device=device) + atol = torch.tensor(atol, device=devices.device) + rtol = torch.tensor(rtol, device=devices.device) s = t_start x_prev = x accept = True @@ -58,7 +58,7 @@ def dpm_solver_adaptive(self, x, t_start, t_end, order=3, rtol=0.05, atol=0.0078 return x, info -@torch.no_grad() +@devices.inference_context() def sample_dpm_fast(model, x, sigma_min, sigma_max, n, extra_args=None, callback=None, disable=None, eta=0., s_noise=1., noise_sampler=None): """DPM-Solver-Fast (fixed step size). See https://arxiv.org/abs/2206.00927.""" if sigma_min <= 0 or sigma_max <= 0: @@ -67,10 +67,10 @@ def sample_dpm_fast(model, x, sigma_min, sigma_max, n, extra_args=None, callback dpm_solver = sampling.DPMSolver(model, extra_args, eps_callback=pbar.update) if callback is not None: dpm_solver.info_callback = lambda info: callback({'sigma': dpm_solver.sigma(info['t']), 'sigma_hat': dpm_solver.sigma(info['t_up']), **info}) - return dpm_solver.dpm_solver_fast(x, dpm_solver.t(torch.tensor(sigma_max, device=device)), dpm_solver.t(torch.tensor(sigma_min, device=device)), n, eta, s_noise, noise_sampler) + return dpm_solver.dpm_solver_fast(x, dpm_solver.t(torch.tensor(sigma_max, device=devices.device)), dpm_solver.t(torch.tensor(sigma_min, device=devices.device)), n, eta, s_noise, noise_sampler) -@torch.no_grad() +@devices.inference_context() def sample_dpm_adaptive(model, x, sigma_min, sigma_max, extra_args=None, callback=None, disable=None, order=3, rtol=0.05, atol=0.0078, h_init=0.05, pcoeff=0., icoeff=1., dcoeff=0., accept_safety=0.81, eta=0., s_noise=1., noise_sampler=None, return_info=False): """DPM-Solver-12 and 23 (adaptive step size). See https://arxiv.org/abs/2206.00927.""" if sigma_min <= 0 or sigma_max <= 0: @@ -79,7 +79,7 @@ def sample_dpm_adaptive(model, x, sigma_min, sigma_max, extra_args=None, callbac dpm_solver = sampling.DPMSolver(model, extra_args, eps_callback=pbar.update) if callback is not None: dpm_solver.info_callback = lambda info: callback({'sigma': dpm_solver.sigma(info['t']), 'sigma_hat': dpm_solver.sigma(info['t_up']), **info}) - x, info = dpm_solver.dpm_solver_adaptive(x, dpm_solver.t(torch.tensor(sigma_max, device=device)), dpm_solver.t(torch.tensor(sigma_min, device=device)), order, rtol, atol, h_init, pcoeff, icoeff, dcoeff, accept_safety, eta, s_noise, noise_sampler) + x, info = dpm_solver.dpm_solver_adaptive(x, dpm_solver.t(torch.tensor(sigma_max, device=devices.device)), dpm_solver.t(torch.tensor(sigma_min, device=devices.device)), order, rtol, atol, h_init, pcoeff, icoeff, dcoeff, accept_safety, eta, s_noise, noise_sampler) if return_info: return x, info return x diff --git a/modules/dml/hijack/plms.py b/modules/dml/hijack/plms.py index 2baef815d..a8afcbd05 100644 --- a/modules/dml/hijack/plms.py +++ b/modules/dml/hijack/plms.py @@ -1,11 +1,10 @@ import torch - from ldm.models.diffusion.ddim import noise_like - import modules.sd_hijack_inpainting as plms_hijack +import modules.devices as devices -@torch.no_grad() +@devices.inference_context() def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False, temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, unconditional_guidance_scale=1., unconditional_conditioning=None, old_eps=None, t_next=None, dynamic_threshold=None): diff --git a/modules/dml/hijack/realesrgan_model.py b/modules/dml/hijack/realesrgan_model.py index 341e9aded..ad3b01cce 100644 --- a/modules/dml/hijack/realesrgan_model.py +++ b/modules/dml/hijack/realesrgan_model.py @@ -1,6 +1,5 @@ import math import torch - from realesrgan import RealESRGANer diff --git a/modules/dml/hijack/stablediffusion.py b/modules/dml/hijack/stablediffusion.py index b14b0ece1..3c634e802 100644 --- a/modules/dml/hijack/stablediffusion.py +++ b/modules/dml/hijack/stablediffusion.py @@ -1,9 +1,10 @@ import torch - from ldm.models.diffusion.ddim import DDIMSampler from ldm.modules.diffusionmodules.util import noise_like +import modules.devices as devices -@torch.no_grad() + +@devices.inference_context() def p_sample_ddim(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False, temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None, unconditional_guidance_scale=1., unconditional_conditioning=None, diff --git a/modules/esrgan_model.py b/modules/esrgan_model.py index e0b79069d..a7656ad58 100644 --- a/modules/esrgan_model.py +++ b/modules/esrgan_model.py @@ -199,7 +199,7 @@ def upscale_without_tiling(model, img): img = np.ascontiguousarray(np.transpose(img, (2, 0, 1))) / 255 img = torch.from_numpy(img).float() img = img.unsqueeze(0).to(devices.device_esrgan) - with torch.no_grad(): + with devices.inference_context(): output = model(img) output = output.squeeze().float().cpu().clamp_(0, 1).numpy() output = 255. * np.moveaxis(output, 0, 2) diff --git a/modules/img2img.py b/modules/img2img.py index fcfc50d90..01304b509 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -83,7 +83,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s shared.log.warning('Model not loaded') return [], '', '', 'Error: model not loaded' - shared.log.debug(f'img2img: id_task={id_task}|mode={mode}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|init_img={init_img}|sketch={sketch}|init_img_with_mask={init_img_with_mask}|inpaint_color_sketch={inpaint_color_sketch}|inpaint_color_sketch_orig={inpaint_color_sketch_orig}|init_img_inpaint={init_img_inpaint}|init_mask_inpaint={init_mask_inpaint}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|mask_blur={mask_blur}|mask_alpha={mask_alpha}|inpainting_fill={inpainting_fill}|full_quality={full_quality}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|image_cfg_scale={image_cfg_scale}|clip_skip={clip_skip}|denoising_strength={denoising_strength}|seed={seed}|subseed{subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}|selected_scale_tab={selected_scale_tab}|height={height}|width={width}|scale_by={scale_by}|resize_mode={resize_mode}|inpaint_full_res={inpaint_full_res}|inpaint_full_res_padding={inpaint_full_res_padding}|inpainting_mask_invert={inpainting_mask_invert}|img2img_batch_files={img2img_batch_files}|img2img_batch_input_dir={img2img_batch_input_dir}|img2img_batch_output_dir={img2img_batch_output_dir}|img2img_batch_inpaint_mask_dir={img2img_batch_inpaint_mask_dir}|override_settings_texts={override_settings_texts}|args={args}') + shared.log.debug(f'img2img: id_task={id_task}|mode={mode}|prompt={prompt}|negative_prompt={negative_prompt}|prompt_styles={prompt_styles}|init_img={init_img}|sketch={sketch}|init_img_with_mask={init_img_with_mask}|inpaint_color_sketch={inpaint_color_sketch}|inpaint_color_sketch_orig={inpaint_color_sketch_orig}|init_img_inpaint={init_img_inpaint}|init_mask_inpaint={init_mask_inpaint}|steps={steps}|sampler_index={sampler_index}|latent_index={latent_index}|mask_blur={mask_blur}|mask_alpha={mask_alpha}|inpainting_fill={inpainting_fill}|full_quality={full_quality}|restore_faces={restore_faces}|tiling={tiling}|n_iter={n_iter}|batch_size={batch_size}|cfg_scale={cfg_scale}|image_cfg_scale={image_cfg_scale}|clip_skip={clip_skip}|denoising_strength={denoising_strength}|seed={seed}|subseed{subseed}|subseed_strength={subseed_strength}|seed_resize_from_h={seed_resize_from_h}|seed_resize_from_w={seed_resize_from_w}|selected_scale_tab={selected_scale_tab}|height={height}|width={width}|scale_by={scale_by}|resize_mode={resize_mode}|inpaint_full_res={inpaint_full_res}|inpaint_full_res_padding={inpaint_full_res_padding}|inpainting_mask_invert={inpainting_mask_invert}|img2img_batch_files={img2img_batch_files}|img2img_batch_input_dir={img2img_batch_input_dir}|img2img_batch_output_dir={img2img_batch_output_dir}|img2img_batch_inpaint_mask_dir={img2img_batch_inpaint_mask_dir}|override_settings_texts={override_settings_texts}') if init_img is None: shared.log.debug('Init image not set') diff --git a/modules/intel/openvino/__init__.py b/modules/intel/openvino/__init__.py index b39eb3cdb..b6bcec4ab 100644 --- a/modules/intel/openvino/__init__.py +++ b/modules/intel/openvino/__init__.py @@ -8,7 +8,7 @@ from torch._dynamo.backends.registry import register_backend from torch.fx.experimental.proxy_tensor import make_fx from torch._inductor.compile_fx import compile_fx from hashlib import sha256 -from modules import shared +from modules import shared, devices @register_backend @fake_tensor_unsupported @@ -89,7 +89,7 @@ def openvino_fx(subgraph, example_inputs): else: example_inputs.reverse() model = make_fx(subgraph)(*example_inputs) - with torch.no_grad(): + with devices.inference_context(): model.eval() partitioner = Partitioner() compiled_model = partitioner.make_partitions(model) diff --git a/modules/interrogate.py b/modules/interrogate.py index 61bb14cc9..d95209792 100644 --- a/modules/interrogate.py +++ b/modules/interrogate.py @@ -174,7 +174,7 @@ class InterrogateModels: transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)) ])(pil_image).unsqueeze(0).type(self.dtype).to(devices.device_interrogate) - with torch.no_grad(): + with devices.inference_context(): caption = self.blip_model.generate(gpu_image, sample=False, num_beams=shared.opts.interrogate_clip_num_beams, min_length=shared.opts.interrogate_clip_min_length, max_length=shared.opts.interrogate_clip_max_length) return caption[0] @@ -197,7 +197,7 @@ class InterrogateModels: clip_image = self.clip_preprocess(pil_image).unsqueeze(0).type(self.dtype).to(devices.device_interrogate) - with torch.no_grad(), devices.autocast(): + with devices.inference_context(), devices.autocast(): image_features = self.clip_model.encode_image(clip_image).type(self.dtype) image_features /= image_features.norm(dim=-1, keepdim=True) diff --git a/modules/loader.py b/modules/loader.py index f9c68c5d4..54dafa9e4 100644 --- a/modules/loader.py +++ b/modules/loader.py @@ -8,7 +8,6 @@ from modules import timer, errors initialized = False logging.getLogger("DeepSpeed").disabled = True import torch # pylint: disable=C0411 -errors.log.debug(f'Loaded Torch=={torch.__version__}') try: import intel_extension_for_pytorch as ipex # pylint: disable=import-error, unused-import errors.log.debug(f'Loaded IPEX=={ipex.__version__}') @@ -29,10 +28,9 @@ timer.startup.record("torch") from fastapi import FastAPI # pylint: disable=W0611,C0411 import gradio # pylint: disable=W0611,C0411 -errors.log.debug(f'Loaded Gradio=={gradio.__version__}') timer.startup.record("gradio") errors.install([gradio]) import diffusers # pylint: disable=W0611,C0411 -errors.log.debug(f'Loaded Diffusers=={diffusers.__version__}') timer.startup.record("diffusers") +errors.log.debug(f'Loaded packages: torch={torch.__version__} diffusers={diffusers.__version__} gradio={gradio.__version__}') diff --git a/modules/models/diffusion/uni_pc/sampler.py b/modules/models/diffusion/uni_pc/sampler.py index 21991d2b9..1800b9c2a 100644 --- a/modules/models/diffusion/uni_pc/sampler.py +++ b/modules/models/diffusion/uni_pc/sampler.py @@ -21,7 +21,7 @@ class UniPCSampler(object): # persist steps so we can eventually find denoising strength self.inflated_steps = ddim_num_steps - @torch.no_grad() + @devices.inference_context() def stochastic_encode(self, x0, t, use_original_steps=False, noise=None): if noise is None: noise = torch.randn_like(x0) @@ -119,7 +119,7 @@ class UniPCSampler(object): self.after_sample = after_sample self.after_update = after_update - @torch.no_grad() + @devices.inference_context() def sample(self, S, batch_size, diff --git a/modules/models/diffusion/uni_pc/uni_pc.py b/modules/models/diffusion/uni_pc/uni_pc.py index 6dddbcfbd..56604a7b6 100644 --- a/modules/models/diffusion/uni_pc/uni_pc.py +++ b/modules/models/diffusion/uni_pc/uni_pc.py @@ -3,7 +3,7 @@ import torch.nn.functional as F import math import time from rich.progress import Progress, TextColumn, BarColumn, TaskProgressColumn, TimeRemainingColumn, TimeElapsedColumn -from modules import shared +from modules import shared, devices class NoiseScheduleVP: @@ -760,7 +760,7 @@ class UniPC: with Progress(TextColumn('[cyan]{task.description}'), BarColumn(), TaskProgressColumn(), TimeRemainingColumn(), TimeElapsedColumn()) as progress: task = progress.add_task(description="Initializing", total=steps) t = time.time() - with torch.no_grad(): + with devices.inference_context(): vec_t = timesteps[0].expand((x.shape[0])) model_prev_list = [self.model_fn(x, vec_t)] t_prev_list = [vec_t] diff --git a/modules/processing.py b/modules/processing.py index f1ccc4901..985773c80 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -707,7 +707,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: return '' ema_scope_context = p.sd_model.ema_scope if shared.backend == shared.Backend.ORIGINAL else nullcontext - with torch.no_grad(), ema_scope_context(): + with devices.inference_context(), ema_scope_context(): t0 = time.time() with devices.autocast(): p.init(p.all_prompts, p.all_seeds, p.all_subseeds) @@ -894,7 +894,6 @@ def old_hires_fix_first_pass_dimensions(width, height): class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): - sampler = None def __init__(self, enable_hr: bool = False, denoising_strength: float = 0.75, firstphase_width: int = 0, firstphase_height: int = 0, hr_scale: float = 2.0, hr_upscaler: str = None, hr_second_pass_steps: int = 0, hr_resize_x: int = 0, hr_resize_y: int = 0, refiner_steps: int = 5, refiner_start: float = 0, refiner_prompt: str = '', refiner_negative: str = '', **kwargs): @@ -920,11 +919,11 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): self.refiner_start = refiner_start self.refiner_prompt = refiner_prompt self.refiner_negative = refiner_negative + self.sampler = None def init(self, all_prompts, all_seeds, all_subseeds): if shared.backend == shared.Backend.DIFFUSERS: modules.sd_models.set_diffuser_pipe(self.sd_model, modules.sd_models.DiffusersTaskType.TEXT_2_IMAGE) - self.width = self.width or 512 self.height = self.height or 512 @@ -1052,7 +1051,6 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): - sampler = None def __init__(self, init_images: list = None, resize_mode: int = 0, denoising_strength: float = 0.3, image_cfg_scale: float = None, mask: Any = None, mask_blur: int = 4, inpainting_fill: int = 0, inpaint_full_res: bool = True, inpaint_full_res_padding: int = 0, inpainting_mask_invert: int = 0, initial_noise_multiplier: float = None, refiner_steps: int = 5, refiner_start: float = 0, refiner_prompt: str = '', refiner_negative: str = '', **kwargs): super().__init__(**kwargs) @@ -1080,6 +1078,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.enable_hr = None self.is_batch = False self.scale_by = 1.0 + self.sampler = None def init(self, all_prompts, all_seeds, all_subseeds): if shared.backend == shared.Backend.DIFFUSERS and self.image_mask is None: diff --git a/modules/script_loading.py b/modules/script_loading.py index b28f6b65d..49489c4ed 100644 --- a/modules/script_loading.py +++ b/modules/script_loading.py @@ -1,6 +1,9 @@ +import io import os +import contextlib import importlib.util import modules.errors as errors +from installer import setup_logging preloaded = [] @@ -10,13 +13,18 @@ def load_module(path): module_spec = importlib.util.spec_from_file_location(os.path.basename(path), path) module = importlib.util.module_from_spec(module_spec) try: - module_spec.loader.exec_module(module) + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + module_spec.loader.exec_module(module) + setup_logging() # reset since scripts can hijaack logging + for line in stdout.getvalue().splitlines(): + if len(line) > 0: + errors.log.info(f'Extension: script={os.path.relpath(path)} {line.strip()}') except Exception as e: errors.display(e, f'Module load: {path}') return module - def preload_extensions(extensions_dir, parser): if not os.path.isdir(extensions_dir): return diff --git a/modules/sd_hijack_inpainting.py b/modules/sd_hijack_inpainting.py index 7b392b4f4..882560a1c 100644 --- a/modules/sd_hijack_inpainting.py +++ b/modules/sd_hijack_inpainting.py @@ -1,8 +1,11 @@ +import io +import contextlib import torch - -import ldm.models.diffusion.ddpm -import ldm.models.diffusion.ddim -import ldm.models.diffusion.plms +stdout = io.StringIO() +with contextlib.redirect_stdout(stdout): + import ldm.models.diffusion.ddpm + import ldm.models.diffusion.ddim + import ldm.models.diffusion.plms from ldm.models.diffusion.ddpm import LatentDiffusion # pylint: disable=unused-import from ldm.models.diffusion.plms import PLMSSampler # pylint: disable=unused-import diff --git a/modules/sd_models.py b/modules/sd_models.py index 72422b498..14b78921a 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -1,5 +1,3 @@ -import collections -import os.path import re import io import sys @@ -7,6 +5,9 @@ import json import time import logging import threading +import contextlib +import collections +import os.path from os import mkdir from urllib import request from enum import Enum @@ -990,13 +991,17 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None, timer.record("config") shared.log.debug(f'Model config loaded: {memory_stats()}') sd_model = None - # shared.log.debug(f'Model config: {sd_config.model.get("params", dict())}') - try: - clip_is_included_into_sd = sd1_clip_weight in state_dict or sd2_clip_weight in state_dict - with sd_disable_initialization.DisableInitialization(disable_clip=clip_is_included_into_sd): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + try: + clip_is_included_into_sd = sd1_clip_weight in state_dict or sd2_clip_weight in state_dict + with sd_disable_initialization.DisableInitialization(disable_clip=clip_is_included_into_sd): + sd_model = instantiate_from_config(sd_config.model) + except Exception: sd_model = instantiate_from_config(sd_config.model) - except Exception: - sd_model = instantiate_from_config(sd_config.model) + for line in stdout.getvalue().splitlines(): + if len(line) > 0: + shared.log.info(f'LDM: {line.strip()}') shared.log.debug(f"Model created from config: {checkpoint_config}") sd_model.used_config = checkpoint_config timer.record("create") diff --git a/modules/sd_models_config.py b/modules/sd_models_config.py index 819bebd34..40a0ed638 100644 --- a/modules/sd_models_config.py +++ b/modules/sd_models_config.py @@ -2,7 +2,7 @@ import os import torch -from modules import paths, sd_disable_initialization +from modules import paths, sd_disable_initialization, devices sd_repo_configs_path = os.path.join(paths.paths['Stable Diffusion'], "configs", "stable-diffusion") config_default = paths.sd_default_config @@ -47,7 +47,7 @@ def is_using_v_parameterization_for_sd2(state_dict): ) unet.eval() - with torch.no_grad(): + with devices.inference_context(): unet_sd = {k.replace("model.diffusion_model.", ""): v for k, v in state_dict.items() if "model.diffusion_model." in k} unet.load_state_dict(unet_sd, strict=True) unet.to(device=device, dtype=torch.float) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index e0f6493c8..c624ea8d7 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -34,10 +34,18 @@ def find_sampler_config(name): return config +last_sampler = None + + def create_sampler(name, model): + global last_sampler # pylint: disable=global-statement + if last_sampler is not None and last_sampler.name == name: + return last_sampler if name == 'Default' and hasattr(model, 'scheduler'): config = {k: v for k, v in model.scheduler.config.items() if not k.startswith('_')} shared.log.debug(f'Sampler default {type(model.scheduler).__name__}: {config}') + last_sampler = model.scheduler + last_sampler.name = type(model.scheduler).__name__ return model.scheduler config = find_sampler_config(name) if config is None: @@ -48,6 +56,8 @@ def create_sampler(name, model): sampler.config = config sampler.name = name shared.log.debug(f'Sampler: sampler={sampler.name} config={sampler.config.options}') + last_sampler = sampler + last_sampler.name = sampler.name return sampler elif shared.backend == shared.Backend.DIFFUSERS: sampler = config.constructor(model) @@ -55,6 +65,8 @@ def create_sampler(name, model): model.scheduler_config = sampler.sampler.config.copy() model.scheduler = sampler.sampler shared.log.debug(f'Sampler: sampler={sampler.name} config={sampler.config}') + last_sampler = sampler.sampler + last_sampler.name = sampler.name return sampler.sampler else: return None diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index 2134ed0d7..e39f6ab19 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -142,6 +142,16 @@ class CFGDenoiser(torch.nn.Module): else: cond_in = torch.cat([tensor, uncond]) + """ + adjusted_cond_scale = cond_scale # Adjusted cond_scale for uncond + last_uncond_steps = max(0, state.sampling_steps - 2) # Determine the last two steps before uncond stops + if self.step >= last_uncond_steps: # Check if we're in the last two steps before uncond stops + adjusted_cond_scale *= 1.5 # Apply uncond with 150% cond_scale + else: + if (self.step - last_uncond_steps) % 3 == 0: # Check if it's one of every three steps after uncond stops + adjusted_cond_scale *= 1.5 # Apply uncond with 150% cond_scale + """ + if shared.batch_cond_uncond: x_out = self.inner_model(x_in, sigma_in, cond=make_condition_dict([cond_in], image_cond_in)) else: diff --git a/modules/shared.py b/modules/shared.py index d68a744ce..86a8af86c 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -1,8 +1,10 @@ +import io import os import sys import time import json import datetime +import contextlib import urllib.request from urllib.parse import urlparse from enum import Enum @@ -17,6 +19,7 @@ import modules.memmon import modules.styles import modules.devices as devices # pylint: disable=R0402 import modules.paths_internal as paths +from installer import print_dict from installer import log as central_logger # pylint: disable=E0611 @@ -271,6 +274,7 @@ def temp_disable_extensions(): cmd_opts.lyco_dir = opts.lora_dir if 'Lora' not in opts.disabled_extensions: disabled.append('Lora') + cmd_opts.controlnet_loglevel = 'WARNING' return disabled @@ -358,6 +362,7 @@ elif devices.backend == "rocm": else: # cuda cross_attention_optimization_default ="Scaled-Dot-Product" + options_templates.update(options_section(('sd', "Execution & Models"), { "sd_backend": OptionInfo("diffusers" if cmd_opts.use_openvino else "original", "Execution backend", gr.Radio, lambda: {"choices": ["original", "diffusers"] }), "sd_checkpoint_autoload": OptionInfo(True, "Model autoload on server start"), @@ -384,6 +389,7 @@ options_templates.update(options_section(('optimizations', "Optimizations"), { "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}), + "inference_mode": OptionInfo("no-grad", "Torch inference mode", gr.Radio, lambda: {"choices": ["no-grad", "inference-only"]}), "sd_vae_sliced_encode": OptionInfo(False, "VAE Slicing (original)"), })) @@ -400,8 +406,9 @@ options_templates.update(options_section(('cuda', "Compute Settings"), { "rollback_vae": OptionInfo(False, "Attempt VAE roll back when produced NaN values (experimental)"), "opt_channelslast": OptionInfo(False, "Use channels last as torch memory format "), "cudnn_benchmark": OptionInfo(False, "Enable full-depth cuDNN benchmark feature"), - # "cuda_allow_tf32": OptionInfo(True, "Allow TF32 math ops"), - # "cuda_allow_tf16_reduced": OptionInfo(True, "Allow TF16 reduced precision math ops"), + "ipex_optimize": OptionInfo(True if devices.backend == "ipex" else False, "Enable IPEX Optimize for Intel GPUs"), + "directml_memory_provider": OptionInfo(default_memory_provider, 'DirectML memory stats provider', gr.Radio, lambda: {"choices": memory_providers}), + "cuda_compile_sep": OptionInfo("