Skip to content

Commit e147094

Browse files
committed
Merge remote-tracking branch 'upstream/main' into sefi-image-diffusers
2 parents ffc7baf + cae82a7 commit e147094

9 files changed

Lines changed: 104 additions & 6 deletions

src/diffusers/modular_pipelines/components_manager.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import torch
2424

2525
from ..hooks import ModelHook
26+
from ..hooks.group_offloading import _is_group_offload_enabled
2627
from ..utils import (
2728
is_accelerate_available,
2829
logging,
@@ -76,12 +77,20 @@ def add_other_hook(self, hook: "UserCustomOffloadHook"):
7677
self.other_hooks.append(hook)
7778

7879
def init_hook(self, module):
80+
# A group offloaded module holds one group at a time and refuses `.to()`. Moving it here would be a
81+
# silent no-op that leaves this hook recording an offload that never happened.
82+
if _is_group_offload_enabled(module):
83+
return module
7984
return module.to("cpu")
8085

8186
def pre_forward(self, module, *args, **kwargs):
8287
if module.device != self.execution_device:
8388
if self.other_hooks is not None:
84-
hooks_to_offload = [hook for hook in self.other_hooks if hook.model.device == self.execution_device]
89+
hooks_to_offload = [
90+
hook
91+
for hook in self.other_hooks
92+
if hook.model.device == self.execution_device and not _is_group_offload_enabled(hook.model)
93+
]
8594
# offload all other hooks
8695
start_time = time.perf_counter()
8796
if self.offload_strategy is not None:
@@ -104,7 +113,10 @@ def pre_forward(self, module, *args, **kwargs):
104113

105114
if hooks_to_offload:
106115
clear_device_cache()
107-
module.to(self.execution_device)
116+
# The strategy still runs above, so a group offloaded model can make room for itself by moving other
117+
# models — it just places itself.
118+
if not _is_group_offload_enabled(module):
119+
module.to(self.execution_device)
108120
return send_to_device(args, self.execution_device), send_to_device(kwargs, self.execution_device)
109121

110122

@@ -336,6 +348,7 @@ def __init__(self):
336348
self.collections = OrderedDict() # collection_name -> set of component_names
337349
self.model_hooks = None
338350
self._auto_offload_enabled = False
351+
self._offload_strategy = None
339352

340353
def _lookup_ids(
341354
self,
@@ -692,7 +705,12 @@ def matches_pattern(component_id, pattern, exact_match=False):
692705

693706
return get_return_dict(matches, return_dict_with_names)
694707

695-
def enable_auto_cpu_offload(self, device: str | int | torch.device = None, memory_reserve_margin="3GB"):
708+
def enable_auto_cpu_offload(
709+
self,
710+
device: str | int | torch.device = None,
711+
memory_reserve_margin="3GB",
712+
offload_strategy=None,
713+
):
696714
"""
697715
Enable automatic CPU offloading for all components.
698716
@@ -703,11 +721,19 @@ def enable_auto_cpu_offload(self, device: str | int | torch.device = None, memor
703721
4. The system tries to offload the smallest combination of models that frees enough memory
704722
5. Models stay on the execution device until another model needs memory and forces them off
705723
724+
A group offloaded model takes part in this but places itself: it can still make room by moving other models
725+
aside, and is never moved to make room for them. Either order works — group offload before or after enabling
726+
this. `AutoOffloadStrategy` sizes its decisions from model memory footprints, which do not describe a model
727+
holding one group at a time, so pass an `offload_strategy` that decides from the workflow instead.
728+
706729
Args:
707730
device (str | int | torch.device): The execution device where models are moved for forward passes
708731
memory_reserve_margin (str): The memory reserve margin to use, default is 3GB. This is the amount of
709732
memory to keep free on the device to avoid running out of memory during model
710733
execution (e.g., for intermediate activations, gradients, etc.)
734+
offload_strategy: Any callable with the signature `(hooks, model_id, model, execution_device) -> hooks`,
735+
returning which resident models to offload before the incoming one loads. Defaults to
736+
`AutoOffloadStrategy`, which frees the smallest sufficient combination.
711737
"""
712738
if not is_accelerate_available():
713739
raise ImportError("Make sure to install accelerate to use auto_cpu_offload")
@@ -732,7 +758,17 @@ def enable_auto_cpu_offload(self, device: str | int | torch.device = None, memor
732758
remove_hook_from_module(component, recurse=True)
733759

734760
self.disable_auto_cpu_offload()
735-
offload_strategy = AutoOffloadStrategy(memory_reserve_margin=memory_reserve_margin)
761+
if offload_strategy is None:
762+
offload_strategy = AutoOffloadStrategy(memory_reserve_margin=memory_reserve_margin)
763+
if any(
764+
isinstance(component, torch.nn.Module) and _is_group_offload_enabled(component)
765+
for component in self.components.values()
766+
):
767+
logger.warning(
768+
"`AutoOffloadStrategy` decides what to move from model memory footprints, which do not "
769+
"describe a group offloaded model: it holds one group at a time, not its whole weight. Pass "
770+
"an `offload_strategy` that decides from the workflow instead."
771+
)
736772

737773
all_hooks = []
738774
for name, component in self.components.items():
@@ -749,6 +785,23 @@ def enable_auto_cpu_offload(self, device: str | int | torch.device = None, memor
749785
self.model_hooks = all_hooks
750786
self._auto_offload_enabled = True
751787
self._auto_offload_device = device
788+
self._offload_strategy = offload_strategy
789+
790+
def set_offload_strategy(self, offload_strategy):
791+
"""
792+
Replace the offload strategy on all managed models. Only valid while auto CPU offloading is enabled.
793+
794+
Args:
795+
offload_strategy:
796+
Any callable with the signature `(hooks, model_id, model, execution_device) -> hooks`: it receives the
797+
hooks of the models currently on the device and returns the ones to offload before the incoming model
798+
loads. The default is `AutoOffloadStrategy`, which frees the smallest sufficient combination.
799+
"""
800+
if not self._auto_offload_enabled:
801+
raise ValueError("Auto CPU offloading is not enabled. Call `enable_auto_cpu_offload` first.")
802+
for user_hook in self.model_hooks:
803+
user_hook.hook.offload_strategy = offload_strategy
804+
self._offload_strategy = offload_strategy
752805

753806
def disable_auto_cpu_offload(self):
754807
"""
@@ -765,6 +818,7 @@ def disable_auto_cpu_offload(self):
765818
clear_device_cache()
766819
self.model_hooks = None
767820
self._auto_offload_enabled = False
821+
self._offload_strategy = None
768822

769823
def get_model_info(
770824
self,

src/diffusers/schedulers/scheduling_cosine_dpmsolver_multistep.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,9 @@ def dpm_solver_first_order_update(
459459
The direct output from the learned diffusion model.
460460
sample (`torch.Tensor`):
461461
A current instance of a sample created by the diffusion process.
462+
noise (`torch.Tensor`, *optional*):
463+
Random noise used by the stochastic (`sde-*`) solver variants. Required when `algorithm_type` is set to
464+
one of them, and unused otherwise.
462465
463466
Returns:
464467
`torch.Tensor`:
@@ -497,6 +500,9 @@ def multistep_dpm_solver_second_order_update(
497500
The direct outputs from learned diffusion model at current and latter timesteps.
498501
sample (`torch.Tensor`):
499502
A current instance of a sample created by the diffusion process.
503+
noise (`torch.Tensor`, *optional*):
504+
Random noise used by the stochastic (`sde-*`) solver variants. Required when `algorithm_type` is set to
505+
one of them, and unused otherwise.
500506
501507
Returns:
502508
`torch.Tensor`:

src/diffusers/schedulers/scheduling_ddim_cogvideox.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ def set_timesteps(
285285
Args:
286286
num_inference_steps (`int`):
287287
The number of diffusion steps used when generating samples with a pre-trained model.
288+
device (`str` or `torch.device`, *optional*):
289+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
288290
"""
289291

290292
if num_inference_steps > self.config.num_train_timesteps:

src/diffusers/schedulers/scheduling_ddim_inverse.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ def set_timesteps(
275275
Args:
276276
num_inference_steps (`int`):
277277
The number of diffusion steps used when generating samples with a pre-trained model.
278+
device (`str` or `torch.device`, *optional*):
279+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
278280
"""
279281

280282
if num_inference_steps > self.config.num_train_timesteps:

src/diffusers/schedulers/scheduling_dpm_cogvideox.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ def rescale_zero_terminal_snr(alphas_cumprod):
106106
Rescales betas to have zero terminal SNR Based on https://huggingface.co/papers/2305.08891 (Algorithm 1)
107107
108108
Args:
109-
betas (`torch.Tensor`):
110-
the betas that the scheduler is being initialized with.
109+
alphas_cumprod (`torch.Tensor`):
110+
the cumulative product of alphas that the scheduler is being initialized with.
111111
112112
Returns:
113113
`torch.Tensor`: rescaled betas with zero terminal SNR

src/diffusers/schedulers/scheduling_dpmsolver_multistep.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ def set_timesteps(
379379
The number of diffusion steps used when generating samples with a pre-trained model.
380380
device (`str` or `torch.device`, *optional*):
381381
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
382+
mu (`float`, *optional*):
383+
Exponent for the dynamic time shift. Requires `use_dynamic_shifting` and a `time_shift_type` of
384+
`"exponential"`; when passed, `flow_shift` is set to `exp(mu)`.
382385
timesteps (`list[int]`, *optional*):
383386
Custom timesteps used to support arbitrary timesteps schedule. If `None`, timesteps will be generated
384387
based on the `timestep_spacing` attribute. If `timesteps` is passed, `num_inference_steps` and `sigmas`
@@ -931,6 +934,9 @@ def multistep_dpm_solver_second_order_update(
931934
The direct outputs from learned diffusion model at current and latter timesteps.
932935
sample (`torch.Tensor`, *optional*):
933936
A current instance of a sample created by the diffusion process.
937+
noise (`torch.Tensor`, *optional*):
938+
Random noise used by the stochastic (`sde-*`) solver variants. Required when `algorithm_type` is set to
939+
one of them, and unused otherwise.
934940
935941
Returns:
936942
`torch.Tensor`:

src/diffusers/schedulers/scheduling_dpmsolver_multistep_inverse.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,9 @@ def multistep_dpm_solver_second_order_update(
787787
The direct outputs from learned diffusion model at current and latter timesteps.
788788
sample (`torch.Tensor`, *optional*):
789789
A current instance of a sample created by the diffusion process.
790+
noise (`torch.Tensor`, *optional*):
791+
Random noise used by the stochastic (`sde-*`) solver variants. Required when `algorithm_type` is set to
792+
one of them, and unused otherwise.
790793
791794
Returns:
792795
`torch.Tensor`:

src/diffusers/schedulers/scheduling_dpmsolver_singlestep.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ def set_timesteps(
342342
The number of diffusion steps used when generating samples with a pre-trained model.
343343
device (`str` or `torch.device`, *optional*):
344344
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
345+
mu (`float`, *optional*):
346+
Exponent for the dynamic time shift. Requires `use_dynamic_shifting` and a `time_shift_type` of
347+
`"exponential"`; when passed, `flow_shift` is set to `exp(mu)`.
345348
timesteps (`list[int]`, *optional*):
346349
Custom timesteps used to support arbitrary spacing between timesteps. If `None`, then the default
347350
timestep spacing strategy of equal spacing between timesteps schedule is used. If `timesteps` is
@@ -776,6 +779,9 @@ def dpm_solver_first_order_update(
776779
The previous discrete timestep in the diffusion chain.
777780
sample (`torch.Tensor`):
778781
A current instance of a sample created by the diffusion process.
782+
noise (`torch.Tensor`, *optional*):
783+
Random noise used by the stochastic (`sde-*`) solver variants. Required when `algorithm_type` is set to
784+
one of them, and unused otherwise.
779785
780786
Returns:
781787
`torch.Tensor`:
@@ -841,6 +847,9 @@ def singlestep_dpm_solver_second_order_update(
841847
The previous discrete timestep in the diffusion chain.
842848
sample (`torch.Tensor`):
843849
A current instance of a sample created by the diffusion process.
850+
noise (`torch.Tensor`, *optional*):
851+
Random noise used by the stochastic (`sde-*`) solver variants. Required when `algorithm_type` is set to
852+
one of them, and unused otherwise.
844853
845854
Returns:
846855
`torch.Tensor`:
@@ -952,6 +961,9 @@ def singlestep_dpm_solver_third_order_update(
952961
The previous discrete timestep in the diffusion chain.
953962
sample (`torch.Tensor`):
954963
A current instance of a sample created by diffusion process.
964+
noise (`torch.Tensor`, *optional*):
965+
Random noise used by the stochastic (`sde-*`) solver variants. Required when `algorithm_type` is set to
966+
one of them, and unused otherwise.
955967
956968
Returns:
957969
`torch.Tensor`:
@@ -1076,6 +1088,9 @@ def singlestep_dpm_solver_update(
10761088
A current instance of a sample created by diffusion process.
10771089
order (`int`):
10781090
The solver order at this step.
1091+
noise (`torch.Tensor`, *optional*):
1092+
Random noise used by the stochastic (`sde-*`) solver variants. Required when `algorithm_type` is set to
1093+
one of them, and unused otherwise.
10791094
10801095
Returns:
10811096
`torch.Tensor`:

src/diffusers/schedulers/scheduling_helios.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,8 @@ def convert_model_output(
386386
The current discrete timestep in the diffusion chain.
387387
sample (`torch.Tensor`):
388388
A current instance of a sample created by the diffusion process.
389+
sigma (`torch.Tensor`, *optional*):
390+
The sigma of the current step in the noise schedule.
389391
390392
Returns:
391393
`torch.Tensor`:
@@ -470,6 +472,10 @@ def multistep_uni_p_bh_update(
470472
A current instance of a sample created by the diffusion process.
471473
order (`int`):
472474
The order of UniP at this timestep (corresponds to the *p* in UniPC-p).
475+
sigma (`torch.Tensor`, *optional*):
476+
The sigma of the current step in the noise schedule.
477+
sigma_next (`torch.Tensor`, *optional*):
478+
The sigma of the next step in the noise schedule.
473479
474480
Returns:
475481
`torch.Tensor`:
@@ -607,6 +613,10 @@ def multistep_uni_c_bh_update(
607613
The generated sample after the last predictor `x_{t}`.
608614
order (`int`):
609615
The `p` of UniC-p at this step. The effective order of accuracy should be `order + 1`.
616+
sigma_before (`torch.Tensor`, *optional*):
617+
The sigma of the previous step in the noise schedule.
618+
sigma (`torch.Tensor`, *optional*):
619+
The sigma of the current step in the noise schedule.
610620
611621
Returns:
612622
`torch.Tensor`:

0 commit comments

Comments
 (0)