diff --git a/installer.py b/installer.py index 01d5bda2d..2f347235f 100644 --- a/installer.py +++ b/installer.py @@ -285,6 +285,8 @@ def check_python(): def check_torch(): if args.quick: return + if args.skip_torch: + log.info('Skipping Torch tests') if args.profile: pr = cProfile.Profile() pr.enable() @@ -330,8 +332,6 @@ def check_torch(): torch_command = os.environ.get('TORCH_COMMAND', 'torch torchvision') if 'torch' in torch_command and not args.version: install(torch_command, 'torch torchvision') - if args.skip_torch: - log.info('Skipping Torch tests') else: try: import torch diff --git a/modules/devices.py b/modules/devices.py index 7eb92e957..ae8cf0914 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -67,7 +67,7 @@ def torch_gc(force=False): if oom > previous_oom: previous_oom = oom shared.log.warning(f'GPU out-of-memory error: {mem}') - if used > 90: + if used > 95: shared.log.warning(f'GPU high memory utilization: {used}% {mem}') force = True diff --git a/modules/img2img.py b/modules/img2img.py index 5a29b96c0..9c31296c8 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -123,8 +123,6 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s width = int(image.width * scale_by) height = int(image.height * scale_by) - assert 0. <= denoising_strength <= 1., 'can only work with strength in [0.0, 1.0]' - p = processing.StableDiffusionProcessingImg2Img( sd_model=shared.sd_model, outpath_samples=shared.opts.outdir_samples or shared.opts.outdir_img2img_samples, diff --git a/modules/processing.py b/modules/processing.py index 83890151c..9553f41c3 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -278,7 +278,7 @@ class Processed: self.sd_model_hash = shared.sd_model.sd_model_hash self.seed_resize_from_w = p.seed_resize_from_w self.seed_resize_from_h = p.seed_resize_from_h - self.denoising_strength = getattr(p, 'denoising_strength', None) + self.denoising_strength = p.denoising_strength self.extra_generation_params = p.extra_generation_params self.index_of_first_image = index_of_first_image self.styles = p.styles @@ -466,7 +466,7 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts, all_seeds, all_su "Variation seed": None if p.subseed_strength == 0 else all_subseeds[index], "Variation seed strength": None if p.subseed_strength == 0 else p.subseed_strength, "Seed resize from": None if p.seed_resize_from_w == 0 or p.seed_resize_from_h == 0 else f"{p.seed_resize_from_w}x{p.seed_resize_from_h}", - "Denoising strength": getattr(p, 'denoising_strength', None), + "Denoising strength": p.denoising_strength, "Conditional mask weight": getattr(p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) if p.is_using_inpainting_conditioning else None, "Clip skip": p.clip_skip if p.clip_skip > 1 else None, "ENSD": opts.eta_noise_seed_delta if uses_ensd else None, @@ -728,11 +728,11 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: del samples_ddim elif shared.backend == Backend.DIFFUSERS: - if (not hasattr(shared.sd_model.scheduler, 'name')) or (shared.sd_model.scheduler.name != p.sampler_name): - sampler = sd_samplers.all_samplers_map.get(p.sampler_name, None) - if sampler is None: - sampler = sd_samplers.all_samplers_map.get("UniPC") - shared.sd_model.scheduler = sd_samplers.create_sampler(sampler.name, shared.sd_model) # TODO(Patrick): For wrapped pipelines this is currently a no-op + # if (not hasattr(shared.sd_model.scheduler, 'name')) or (shared.sd_model.scheduler.name != p.sampler_name): + sampler = sd_samplers.all_samplers_map.get(p.sampler_name, None) + if sampler is None: + sampler = sd_samplers.all_samplers_map.get("UniPC") + sd_samplers.create_sampler(sampler.name, shared.sd_model) # TODO(Patrick): For wrapped pipelines this is currently a no-op cross_attention_kwargs={} if lora_state['active']: @@ -746,7 +746,6 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: # TODO(PVP): change out to latents once possible with `diffusers` task_specific_kwargs = {"image": p.init_images[0], "mask_image": p.image_mask, "strength": p.denoising_strength} - shared.sd_model.to(devices.device) pipe_args = set_pipeline_args( model=shared.sd_model, @@ -766,11 +765,11 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: shared.log.debug('Moving base model to CPU') shared.sd_model.to('cpu') - if (not hasattr(shared.sd_refiner.scheduler, 'name')) or (shared.sd_refiner.scheduler.name != p.latent_sampler): - sampler = sd_samplers.all_samplers_map.get(p.latent_sampler, None) - if sampler is None: - sampler = sd_samplers.all_samplers_map.get("UniPC") - shared.sd_refiner.scheduler = sd_samplers.create_sampler(sampler.name, shared.sd_refiner) # TODO(Patrick): For wrapped pipelines this is currently a no-op + # if (not hasattr(shared.sd_refiner.scheduler, 'name')) or (shared.sd_refiner.scheduler.name != p.latent_sampler): + sampler = sd_samplers.all_samplers_map.get(p.latent_sampler, None) + if sampler is None: + sampler = sd_samplers.all_samplers_map.get("UniPC") + sd_samplers.create_sampler(sampler.name, shared.sd_refiner) # TODO(Patrick): For wrapped pipelines this is currently a no-op shared.sd_refiner.to(devices.device) devices.torch_gc() diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index 133720685..cf77e1b70 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -47,6 +47,8 @@ def create_sampler(name, model): return sampler elif shared.backend == shared.Backend.DIFFUSERS: sampler = config.constructor(model) + if not hasattr(model, 'scheduler_config'): + model.scheduler_config = sampler.sampler.config.copy() model.scheduler = sampler.sampler return sampler.sampler else: diff --git a/modules/sd_samplers_diffusers.py b/modules/sd_samplers_diffusers.py index 7c18a2a8e..bb374fa46 100644 --- a/modules/sd_samplers_diffusers.py +++ b/modules/sd_samplers_diffusers.py @@ -22,20 +22,22 @@ except Exception as e: log.error(f'Diffusers import error: version={diffusers.__version__} error: {e}') config = { - 'All': { 'num_train_timesteps': 1000, 'beta_schedule': 'linear', 'prediction_type': 'epsilon' }, - 'UniPC': { 'beta_start': 0.0001, 'beta_end': 0.02,'solver_order': 2, 'thresholding': False, 'sample_max_value': 1.0, 'predict_x0': 'bh2', 'lower_order_final': True }, - 'DDIM': { 'beta_start': 0.0001, 'beta_end': 0.02,'clip_sample': True, 'set_alpha_to_one': True, 'steps_offset': 0, 'thresholding': False, 'clip_sample_range': 1.0, 'sample_max_value': 1.0, 'timestep_spacing': 'linspace', 'rescale_betas_zero_snr': False }, - 'DDPM': { 'beta_start': 0.0001, 'beta_end': 0.02,'variance_type': "fixed_small", 'clip_sample': True, 'thresholding': False, 'clip_sample_range': 1.0, 'sample_max_value': 1.0, 'timestep_spacing': 'linspace'}, - 'KDPM2': {'beta_start': 0.00085, 'beta_end': 0.012, 'steps_offset': 0 }, - 'KDPM2 a': {'beta_start': 0.00085, 'beta_end': 0.012, 'steps_offset': 0 }, - 'DEIS': { 'beta_start': 0.0001, 'beta_end': 0.02,'solver_order': 2, 'thresholding': False, 'sample_max_value': 1.0, 'algorithm_type': "deis", 'solver_type': "logrho", 'lower_order_final': True }, - 'Euler': {'beta_start': 0.0001, 'beta_end': 0.02, 'interpolation_type': "linear", 'use_karras_sigmas': False }, - 'Euler a': { 'beta_start': 0.0001, 'beta_end': 0.02 }, - 'Heun': { 'beta_start': 0.0001, 'beta_end': 0.02,'use_karras_sigmas': False }, - 'PNDM': { 'beta_start': 0.0001, 'beta_end': 0.02,'skip_prk_steps': False, 'set_alpha_to_one': False, 'steps_offset': 0 }, - 'DPM 1S': { 'beta_start': 0.0001, 'beta_end': 0.02,'solver_order': 2, 'thresholding': False, 'sample_max_value': 1.0, 'algorithm_type': "dpmsolver++", 'solver_type': "midpoint", 'lower_order_final': True, 'use_karras_sigmas': False }, - 'DPM 2M': { 'beta_start': 0.0001, 'beta_end': 0.02,'thresholding': False, 'sample_max_value': 1.0, 'algorithm_type': "dpmsolver++", 'solver_type': "midpoint", 'lower_order_final': True, 'use_karras_sigmas': False }, - 'LMSD': { 'beta_start': 0.0001, 'beta_end': 0.02,'use_karras_sigmas': False, 'timestep_spacing': 'linspace', 'steps_offset': 0 }, + # TODO beta_start, beta_end are typically per-scheduler, but we don't want them as they should be taken from the model itself as those are values model was trained on + # TODO prediction_type is ideally set in model as well, but it maybe needed that we do auto-detect of model type in the future + 'All': { 'num_train_timesteps': 1000, 'beta_start': 0.0001, 'beta_end': 0.02, 'beta_schedule': 'linear', 'prediction_type': 'epsilon' }, + 'DDIM': { 'clip_sample': True, 'set_alpha_to_one': True, 'steps_offset': 0, 'thresholding': False, 'clip_sample_range': 1.0, 'sample_max_value': 1.0, 'timestep_spacing': 'linspace', 'rescale_betas_zero_snr': False }, + 'DDPM': { 'variance_type': "fixed_small", 'clip_sample': True, 'thresholding': False, 'clip_sample_range': 1.0, 'sample_max_value': 1.0, 'timestep_spacing': 'linspace'}, + 'DEIS': { 'solver_order': 2, 'thresholding': False, 'sample_max_value': 1.0, 'algorithm_type': "deis", 'solver_type': "logrho", 'lower_order_final': True }, + 'DPM 1S': { 'solver_order': 2, 'thresholding': False, 'sample_max_value': 1.0, 'algorithm_type': "dpmsolver++", 'solver_type': "midpoint", 'lower_order_final': True, 'use_karras_sigmas': False }, + 'DPM 2M': { 'thresholding': False, 'sample_max_value': 1.0, 'algorithm_type': "dpmsolver++", 'solver_type': "midpoint", 'lower_order_final': True, 'use_karras_sigmas': False }, + 'Euler a': { }, + 'Euler': { 'interpolation_type': "linear", 'use_karras_sigmas': False }, + 'Heun': { 'use_karras_sigmas': False }, + 'KDPM2 a': { 'steps_offset': 0 }, + 'KDPM2': { 'steps_offset': 0 }, + 'LMSD': { 'use_karras_sigmas': False, 'timestep_spacing': 'linspace', 'steps_offset': 0 }, + 'PNDM': { 'skip_prk_steps': False, 'set_alpha_to_one': False, 'steps_offset': 0 }, + 'UniPC': { 'solver_order': 2, 'thresholding': False, 'sample_max_value': 1.0, 'predict_x0': 'bh2', 'lower_order_final': True }, } samplers_data_diffusers = [ @@ -57,17 +59,22 @@ samplers_data_diffusers = [ class DiffusionSampler: def __init__(self, name, constructor, model, **kwargs): self.config = {} - self.config = config['All'].copy() + self.config = config['All'].copy() # apply global defaults if not hasattr(model, 'scheduler'): return - for key, value in config.get(name, {}).items(): # diffusers defaults + for key, value in config.get(name, {}).items(): # apply diffusers per-scheduler defaults self.config[key] = value - for key, value in model.scheduler.config.items(): # model defaults + if hasattr(model.scheduler, 'scheduler_config'): # find model defaults + orig_config = model.scheduler.scheduler_config + else: + orig_config = model.scheduler.config + for key, value in orig_config.items(): # apply model defaults if key in self.config: self.config[key] = value - for key, value in kwargs.items(): # user args + for key, value in kwargs.items(): # apply user args, if any if key in self.config: self.config[key] = value + # finally apply user preferences if opts.schedulers_prediction_type != 'default': self.config['prediction_type'] = opts.schedulers_prediction_type if opts.schedulers_beta_schedule != 'default': diff --git a/wiki b/wiki index fea0bd7d5..f0aae4958 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit fea0bd7d596e442f73727b46ea9343021724e032 +Subproject commit f0aae4958a41cacf6a9928ea8d73376528cbd454