add torch full deterministic mode

This commit is contained in:
Vladimir Mandic
2024-06-07 09:26:51 -04:00
parent 5574833f0d
commit db9718eee6
9 changed files with 61 additions and 51 deletions
+3
View File
@@ -38,6 +38,9 @@
*note*: alternative to regular hidiffusion method, but with different approach to scaling
- additional built-in 4 great custom trained **ControlNet SDXL** models from Xinsir: OpenPose, Canny, Scribble, AnimePainter
thanks @lbeltrame
- add torch **full deterministic mode**
enable in settings -> compute -> use deterministic mode
typical differences are not large and its disabled by default as it does have some performance impact
- lower overhead on generate calls
- cumulative fixes since the last release
- add python version check for torch-directml
+5 -5
View File
@@ -991,19 +991,19 @@ def check_ui(ver):
return
if ver['branch'] == ver['ui']:
return
log.warning(f'Branch mismatch: sdnext={ver["branch"]} ui={ver["ui"]}')
log.debug(f'Branch mismatch: sdnext={ver["branch"]} ui={ver["ui"]}')
cwd = os.getcwd()
try:
os.chdir('extensions-builtin/sdnext-modernui')
git('checkout ' + ver['branch'])
git('checkout ' + ver['branch'], ignore=True)
os.chdir(cwd)
ver = get_version(force=True)
if ver['branch'] == ver['ui']:
log.info(f'Branch synchronized: {ver["branch"]}')
log.debug(f'Branch synchronized: {ver["branch"]}')
else:
log.error(f'Branch synchronize: sdnext={ver["branch"]} ui={ver["ui"]}')
log.debug(f'Branch synch failed: sdnext={ver["branch"]} ui={ver["ui"]}')
except Exception as e:
log.error(f'Branch switch: {e}')
log.debug(f'Branch switch: {e}')
os.chdir(cwd)
+8 -2
View File
@@ -232,9 +232,13 @@ def set_cuda_params():
if torch.backends.cudnn.is_available():
try:
torch.backends.cudnn.deterministic = shared.opts.cudnn_deterministic
torch.use_deterministic_algorithms(shared.opts.cudnn_deterministic)
log.debug(f'Torch mode: deterministic={shared.opts.cudnn_deterministic}')
if shared.opts.cudnn_deterministic:
os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8'
torch.backends.cudnn.benchmark = True
if shared.opts.cudnn_benchmark:
log.debug('Torch enable cuDNN benchmark')
log.debug('Torch cuDNN: enable benchmark')
torch.backends.cudnn.benchmark_limit = 0
torch.backends.cudnn.allow_tf32 = True
except Exception:
@@ -363,10 +367,12 @@ def cond_cast_float(tensor):
return tensor.float() if unet_needs_upcast else tensor
def randn(seed, shape):
def randn(seed, shape=None):
torch.manual_seed(seed)
if backend == 'ipex':
torch.xpu.manual_seed_all(seed)
if shape is None:
return None
if device.type == 'mps':
return torch.randn(shape, device=cpu).to(device)
elif shared.opts.diffusers_generator_device == "CPU":
+3 -3
View File
@@ -5,13 +5,13 @@ from modules import shared
from modules.hidiffusion import hidiffusion
def apply_hidiffusion(p, model_type):
def apply(p, model_type):
if not shared.native:
return
if model_type not in ['sd', 'sdxl'] and p.hidiffusion:
shared.log.warning(f'HiDiffusion: class={shared.sd_model.__class__.__name__} not supported')
return
remove_hidiffusion(p)
unapply()
if getattr(p, 'hidiffusion', False) is True:
t0 = time.time()
hidiffusion.is_aggressive_raunet = shared.opts.hidiffusion_steps > 0
@@ -38,6 +38,6 @@ def apply_hidiffusion(p, model_type):
shared.log.debug(f'HiDiffusion apply: raunet={shared.opts.hidiffusion_raunet} attn={shared.opts.hidiffusion_attn} aggressive={shared.opts.hidiffusion_steps > 0}:{shared.opts.hidiffusion_steps} t1={shared.opts.hidiffusion_t1} t2={shared.opts.hidiffusion_t2} time={t1-t0:.2f} type={shared.sd_model_type} width={p.width} height={p.height}')
def remove_hidiffusion(p):
def unapply():
if hasattr(shared.sd_model, "unet"):
hidiffusion.remove_hidiffusion(shared.sd_model)
+27 -31
View File
@@ -3,7 +3,6 @@ import torch
import torch.nn.functional as F
from diffusers.utils.torch_utils import is_torch_version
from diffusers.pipelines import auto_pipeline
from modules.shared import log
def sd15_hidiffusion_key():
@@ -229,7 +228,7 @@ def make_diffusers_transformer_block(block_class: Type[torch.nn.Module]) -> Type
norm_hidden_states = self.norm2(hidden_states)
norm_hidden_states = norm_hidden_states * (1 + scale_mlp) + shift_mlp
if self._chunk_size is not None:
ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size)
ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) # TODO hidiffusion undefined
else:
ff_output = self.ff(norm_hidden_states)
if self.use_ada_layer_norm_zero:
@@ -268,7 +267,7 @@ def make_diffusers_cross_attn_down_block(block_class: Type[torch.nn.Module]) ->
encoder_attention_mask: Optional[torch.FloatTensor] = None,
additional_residuals: Optional[torch.FloatTensor] = None,
) -> Tuple[torch.FloatTensor, Tuple[torch.FloatTensor, ...]]:
self.max_timestep = self.info['pipeline']._num_timesteps
self.max_timestep = self.info['pipeline']._num_timesteps # pylint: disable=protected-access
# self.max_timestep = len(self.info['scheduler'].timesteps)
ori_H, ori_W = self.info['size']
if self.model == 'sd15':
@@ -303,7 +302,7 @@ def make_diffusers_cross_attn_down_block(block_class: Type[torch.nn.Module]) ->
self.T1 = int(self.max_timestep * self.T1_ratio)
output_states = ()
cross_attention_kwargs.get("scale", 1.0) if cross_attention_kwargs is not None else 1.0
_scale = cross_attention_kwargs.get("scale", 1.0) if cross_attention_kwargs is not None else 1.0 # TODO hidiffusion unused
blocks = list(zip(self.resnets, self.attentions))
@@ -407,7 +406,7 @@ def make_diffusers_cross_attn_up_block(block_class: Type[torch.nn.Module]) -> Ty
return F.interpolate(first, scale_factor=rescale, mode='bicubic')
return first
self.max_timestep = self.info['pipeline']._num_timesteps
self.max_timestep = self.info['pipeline']._num_timesteps # pylint: disable=protected-access
ori_H, ori_W = self.info['size']
if self.model == 'sd15':
if ori_H < 256 or ori_W < 256:
@@ -489,8 +488,8 @@ def make_diffusers_downsampler_block(block_class: Type[torch.nn.Module]) -> Type
aggressive_raunet = False
max_timestep = 50
def forward(self, hidden_states: torch.Tensor, scale = 1.0) -> torch.Tensor:
self.max_timestep = self.info['pipeline']._num_timesteps
def forward(self, hidden_states: torch.Tensor, scale = 1.0) -> torch.Tensor: # pylint: disable=unused-argument
self.max_timestep = self.info['pipeline']._num_timesteps # pylint: disable=protected-access
# self.max_timestep = len(self.info['scheduler'].timesteps)
ori_H, ori_W = self.info['size']
if self.model == 'sd15':
@@ -522,20 +521,20 @@ def make_diffusers_downsampler_block(block_class: Type[torch.nn.Module]) -> Type
else:
self.T1 = int(self.max_timestep * self.T1_ratio)
if self.timestep < self.T1:
self.ori_stride = self.stride
self.ori_padding = self.padding
self.ori_dilation = self.dilation
self.stride = (4,4)
self.padding = (2,2)
self.dilation = (2,2)
self.ori_stride = self.stride # pylint: disable=access-member-before-definition, attribute-defined-outside-init
self.ori_padding = self.padding # pylint: disable=access-member-before-definition, attribute-defined-outside-init
self.ori_dilation = self.dilation # pylint: disable=access-member-before-definition, attribute-defined-outside-init
self.stride = (4,4) # pylint: disable=access-member-before-definition, attribute-defined-outside-init
self.padding = (2,2) # pylint: disable=access-member-before-definition, attribute-defined-outside-init
self.dilation = (2,2) # pylint: disable=access-member-before-definition, attribute-defined-outside-init
hidden_states = F.conv2d(
hidden_states, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups
)
if self.timestep < self.T1:
self.stride = self.ori_stride
self.padding = self.ori_padding
self.dilation = self.ori_dilation
self.stride = self.ori_stride # pylint: disable=access-member-before-definition, attribute-defined-outside-init
self.padding = self.ori_padding # pylint: disable=access-member-before-definition, attribute-defined-outside-init
self.dilation = self.ori_dilation # pylint: disable=access-member-before-definition, attribute-defined-outside-init
self.timestep += 1
if self.timestep == self.max_timestep:
self.timestep = 0
@@ -557,8 +556,8 @@ def make_diffusers_upsampler_block(block_class: Type[torch.nn.Module]) -> Type[t
aggressive_raunet = False
max_timestep = 50
def forward(self, hidden_states: torch.Tensor, scale = 1.0) -> torch.Tensor:
self.max_timestep = self.info['pipeline']._num_timesteps
def forward(self, hidden_states: torch.Tensor, scale = 1.0) -> torch.Tensor: # pylint: disable=unused-argument
self.max_timestep = self.info['pipeline']._num_timesteps # pylint: disable=protected-access
# self.max_timestep = len(self.info['scheduler'].timesteps)
ori_H, ori_W = self.info['size']
if self.model == 'sd15':
@@ -645,24 +644,21 @@ def apply_hidiffusion(
modified_key = sd15_hidiffusion_key()
for key, module in diffusion_model.named_modules():
if apply_raunet and key in modified_key['down_module_key']:
make_block_fn = make_diffusers_downsampler_block
module.__class__ = make_block_fn(module.__class__)
module.__class__ = make_diffusers_downsampler_block(module.__class__)
module.switching_threshold_ratio = 'T1_ratio'
if apply_raunet and key in modified_key['down_module_key_extra']:
make_block_fn = make_diffusers_cross_attn_down_block
module.__class__ = make_block_fn(module.__class__)
module.__class__ = make_diffusers_cross_attn_down_block(module.__class__)
module.switching_threshold_ratio = 'T2_ratio'
if apply_raunet and key in modified_key['up_module_key']:
make_block_fn = make_diffusers_upsampler_block
module.__class__ = make_block_fn(module.__class__)
module.__class__ = make_diffusers_upsampler_block(module.__class__)
module.switching_threshold_ratio = 'T1_ratio'
if apply_raunet and key in modified_key['up_module_key_extra']:
make_block_fn = make_diffusers_cross_attn_up_block
module.__class__ = make_block_fn(module.__class__)
module.__class__ = make_diffusers_cross_attn_up_block(module.__class__)
module.switching_threshold_ratio = 'T2_ratio'
if apply_window_attn and key in modified_key['windown_attn_module_key']:
make_block_fn = make_diffusers_transformer_block
module.__class__ = make_block_fn(module.__class__)
module.__class__ = make_diffusers_transformer_block(module.__class__)
if hasattr(module, "_patched_forward"):
module.forward = module._patched_forward # pylint: disable=protected-access
module.model = 'sd15'
module.info = diffusion_model.info
@@ -685,7 +681,7 @@ def apply_hidiffusion(
if apply_window_attn and key in modified_key['windown_attn_module_key']:
module.__class__ = make_diffusers_transformer_block(module.__class__)
if hasattr(module, "_patched_forward"):
module.forward = module._patched_forward
module.forward = module._patched_forward # pylint: disable=protected-access
module.model = 'sdxl'
module.info = diffusion_model.info
else:
@@ -702,7 +698,7 @@ def remove_hidiffusion(model: torch.nn.Module):
module.info["hooks"].clear()
del module.info
if hasattr(module, "_forward"):
module.forward = module._forward
module.forward = module._forward # pylint: disable=protected-access
if hasattr(module, "_parent"):
module.__class__ = module._parent
module.__class__ = module._parent # pylint: disable=protected-access
return model
+5 -5
View File
@@ -10,15 +10,15 @@ orig_pipeline = None
def apply(p: processing.StableDiffusionProcessing): # pylint: disable=arguments-differ
global orig_pipeline # pylint: disable=global-statement
c = shared.sd_model.__class__ if shared.sd_loaded else None
if not shared.native:
return None
if p.pag_scale == 0:
c = shared.sd_model.__class__ if shared.sd_loaded else None
if c == StableDiffusionPAGPipeline or c == StableDiffusionXLPAGPipeline:
unapply()
return None
if c == StableDiffusionPAGPipeline or c == StableDiffusionXLPAGPipeline:
pass
elif detect.is_sd15(c):
if p.pag_scale == 0:
return
if detect.is_sd15(c):
orig_pipeline = shared.sd_model
shared.sd_model = sd_models.switch_pipe(StableDiffusionPAGPipeline, shared.sd_model)
elif detect.is_sdxl(c):
+2 -2
View File
@@ -113,7 +113,7 @@ def process_diffusers(p: processing.StableDiffusionProcessing):
t0 = time.time()
sd_models_compile.check_deepcache(enable=True)
sd_models.move_model(shared.sd_model, devices.device)
hidiffusion.apply_hidiffusion(p, shared.sd_model_type)
hidiffusion.apply(p, shared.sd_model_type)
# if 'image' in base_args:
# base_args['image'] = set_latents(p)
if hasattr(shared.sd_model, 'tgate') and getattr(p, 'gate_step', -1) > 0:
@@ -123,7 +123,7 @@ def process_diffusers(p: processing.StableDiffusionProcessing):
output = shared.sd_model(**base_args)
if isinstance(output, dict):
output = SimpleNamespace(**output)
hidiffusion.remove_hidiffusion(p)
hidiffusion.unapply()
sd_models_compile.openvino_post_compile(op="base") # only executes on compiled vino models
sd_models_compile.check_deepcache(enable=False)
if shared.cmd_opts.profile:
+3
View File
@@ -482,7 +482,10 @@ def get_generator(p):
else:
generator_device = devices.cpu if shared.opts.diffusers_generator_device == "CPU" else shared.device
try:
devices.randn(p.seeds[0])
generator = [torch.Generator(generator_device).manual_seed(s) for s in p.seeds]
seeds = [g.initial_seed() for g in generator]
shared.log.debug(f'Torch generator: device={generator_device} seeds={seeds}')
except Exception as e:
shared.log.error(f'Torch generator: seeds={p.seeds} device={generator_device} {e}')
generator = None
+5 -3
View File
@@ -406,6 +406,9 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
"math_sep": OptionInfo("<h2>Execution precision</h2>", "", gr.HTML),
"precision": OptionInfo("Autocast", "Precision type", gr.Radio, {"choices": ["Autocast", "Full"]}),
"cuda_dtype": OptionInfo("FP32" if sys.platform == "darwin" or cmd_opts.use_openvino else "BF16" if devices.backend == "ipex" else "FP16", "Device precision type", gr.Radio, {"choices": ["FP32", "FP16", "BF16"]}),
"cudnn_deterministic": OptionInfo(False, "Use deterministic mode"),
"model_sep": OptionInfo("<h2>Model options</h2>", "", gr.HTML),
"no_half": OptionInfo(False if not cmd_opts.use_openvino else True, "Full precision for model (--no-half)", None, None, None),
"no_half_vae": OptionInfo(False if not cmd_opts.use_openvino else True, "Full precision for VAE (--no-half-vae)"),
"upcast_sampling": OptionInfo(False if sys.platform != "darwin" else True, "Upcast sampling"),
@@ -415,7 +418,7 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
"nan_skip": OptionInfo(False, "Skip Generation if NaN found in latents", gr.Checkbox, {"visible": True}),
"rollback_vae": OptionInfo(False, "Attempt VAE roll back for NaN values"),
"cross_attention_sep": OptionInfo("<h2>Attention</h2>", "", gr.HTML),
"cross_attention_sep": OptionInfo("<h2>Cross Attention</h2>", "", gr.HTML),
"cross_attention_optimization": OptionInfo(cross_attention_optimization_default, "Attention optimization method", gr.Radio, lambda: {"choices": shared_items.list_crossattention(native) }),
"sdp_options": OptionInfo(sdp_options_default, "SDP options", gr.CheckboxGroup, {"choices": ['Flash attention', 'Memory attention', 'Math attention'] }),
"xformers_options": OptionInfo(['Flash attention'], "xFormers options", gr.CheckboxGroup, {"choices": ['Flash attention'] }),
@@ -425,10 +428,9 @@ options_templates.update(options_section(('cuda', "Compute Settings"), {
"sub_quad_kv_chunk_size": OptionInfo(512, "Attention kv chunk size", gr.Slider, {"minimum": 0, "maximum": 8192, "step": 8, "visible": not native}),
"sub_quad_chunk_threshold": OptionInfo(80, "Attention chunking threshold", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1, "visible": not native}),
"other_sep": OptionInfo("<h2>Execution precision</h2>", "", gr.HTML),
"other_sep": OptionInfo("<h2>Execution options</h2>", "", gr.HTML),
"opt_channelslast": OptionInfo(False, "Use channels last "),
"cudnn_benchmark": OptionInfo(False, "Full-depth cuDNN benchmark feature"),
"cudnn_deterministic": OptionInfo(False, "Use deterministic options for cuDNN"),
"diffusers_fuse_projections": OptionInfo(False, "Fused projections"),
"torch_gc_threshold": OptionInfo(80, "Torch memory threshold for GC", gr.Slider, {"minimum": 0, "maximum": 100, "step": 1}),
"torch_malloc": OptionInfo("native", "Torch memory allocator", gr.Radio, {"choices": ['native', 'cudaMallocAsync'] }),