Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,9 @@ def __call__(
prompt_embeds_edit[1:2] += edit_direction

# 10. Second denoising loop to generate the edited image.
self.scheduler.set_timesteps(num_inference_steps, device=device)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@patrickvonplaten

pix2pix_zero has two denoising loops so we have to reset the timesteps before the second one to set the _step_index to None and restart the counter again

is this ok?

timesteps = self.scheduler.timesteps

latents = latents_init
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
with self.progress_bar(total=num_inference_steps) as progress_bar:
Expand Down
51 changes: 40 additions & 11 deletions src/diffusers/schedulers/scheduling_consistency_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def __init__(
self.timesteps = torch.from_numpy(timesteps)
self.custom_timesteps = False
self.is_scale_input_called = False
self._step_index = None

def index_for_timestep(self, timestep, schedule_timesteps=None):
if schedule_timesteps is None:
Expand All @@ -110,6 +111,13 @@ def index_for_timestep(self, timestep, schedule_timesteps=None):
indices = (schedule_timesteps == timestep).nonzero()
return indices.item()

@property
def step_index(self):
"""
The index counter for current timestep. It will increae 1 after each scheduler step.
"""
return self._step_index

def scale_model_input(
self, sample: torch.FloatTensor, timestep: Union[float, torch.FloatTensor]
) -> torch.FloatTensor:
Expand All @@ -123,10 +131,10 @@ def scale_model_input(
`torch.FloatTensor`: scaled input sample
"""
# Get sigma corresponding to timestep
if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)
step_idx = self.index_for_timestep(timestep)
sigma = self.sigmas[step_idx]
if self.step_index is None:
self._init_step_index(timestep)

sigma = self.sigmas[self.step_index]

sample = sample / ((sigma**2 + self.config.sigma_data**2) ** 0.5)

Expand Down Expand Up @@ -219,6 +227,8 @@ def set_timesteps(
else:
self.timesteps = torch.from_numpy(timesteps).to(device=device)

self._step_index = None

# Modified _convert_to_karras implementation that takes in ramp as argument
def _convert_to_karras(self, ramp):
"""Constructs the noise schedule of Karras et al. (2022)."""
Expand Down Expand Up @@ -261,6 +271,24 @@ def get_scalings_for_boundary_condition(self, sigma):
c_out = (sigma - sigma_min) * sigma_data / (sigma**2 + sigma_data**2) ** 0.5
return c_skip, c_out

# Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._init_step_index
def _init_step_index(self, timestep):
if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)

index_candidates = (self.timesteps == timestep).nonzero()

# The sigma index that is taken for the **very** first `step`
# is always the second index (or the last index if there is only 1)
# This way we can ensure we don't accidentally skip a sigma in
# case we start in the middle of the denoising schedule (e.g. for image-to-image)
if len(index_candidates) > 1:
step_index = index_candidates[1]
else:
step_index = index_candidates[0]

self._step_index = step_index.item()

def step(
self,
model_output: torch.FloatTensor,
Expand Down Expand Up @@ -305,18 +333,16 @@ def step(
"See `StableDiffusionPipeline` for a usage example."
)

if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)

sigma_min = self.config.sigma_min
sigma_max = self.config.sigma_max

step_index = self.index_for_timestep(timestep)
if self.step_index is None:
self._init_step_index(timestep)

# sigma_next corresponds to next_t in original implementation
sigma = self.sigmas[step_index]
if step_index + 1 < self.config.num_train_timesteps:
sigma_next = self.sigmas[step_index + 1]
sigma = self.sigmas[self.step_index]
if self.step_index + 1 < self.config.num_train_timesteps:
sigma_next = self.sigmas[self.step_index + 1]
else:
# Set sigma_next to sigma_min
sigma_next = self.sigmas[-1]
Expand Down Expand Up @@ -345,6 +371,9 @@ def step(
# tau = sigma_hat, eps = sigma_min
prev_sample = denoised + z * (sigma_hat**2 - sigma_min**2) ** 0.5

# upon completion increase step index by one
self._step_index += 1

if not return_dict:
return (prev_sample,)

Expand Down
62 changes: 45 additions & 17 deletions src/diffusers/schedulers/scheduling_euler_ancestral_discrete.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ def __init__(
self.timesteps = torch.from_numpy(timesteps)
self.is_scale_input_called = False

self._step_index = None

@property
def init_noise_sigma(self):
# standard deviation of the initial noise distribution
Expand All @@ -174,6 +176,13 @@ def init_noise_sigma(self):

return (self.sigmas.max() ** 2 + 1) ** 0.5

@property
def step_index(self):
"""
The index counter for current timestep. It will increae 1 after each scheduler step.
"""
return self._step_index

def scale_model_input(
self, sample: torch.FloatTensor, timestep: Union[float, torch.FloatTensor]
) -> torch.FloatTensor:
Expand All @@ -187,10 +196,11 @@ def scale_model_input(
Returns:
`torch.FloatTensor`: scaled input sample
"""
if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)
step_index = (self.timesteps == timestep).nonzero().item()
sigma = self.sigmas[step_index]

if self.step_index is None:
self._init_step_index(timestep)

sigma = self.sigmas[self.step_index]
sample = sample / ((sigma**2 + 1) ** 0.5)
self.is_scale_input_called = True
return sample
Expand All @@ -209,20 +219,20 @@ def set_timesteps(self, num_inference_steps: int, device: Union[str, torch.devic

# "linspace", "leading", "trailing" corresponds to annotation of Table 2. of https://arxiv.org/abs/2305.08891
if self.config.timestep_spacing == "linspace":
timesteps = np.linspace(0, self.config.num_train_timesteps - 1, num_inference_steps, dtype=float)[
timesteps = np.linspace(0, self.config.num_train_timesteps - 1, num_inference_steps, dtype=np.float32)[
::-1
].copy()
elif self.config.timestep_spacing == "leading":
step_ratio = self.config.num_train_timesteps // self.num_inference_steps
# creates integer timesteps by multiplying by ratio
# casting to int to avoid issues when num_inference_step is power of 3
timesteps = (np.arange(0, num_inference_steps) * step_ratio).round()[::-1].copy().astype(float)
timesteps = (np.arange(0, num_inference_steps) * step_ratio).round()[::-1].copy().astype(np.float32)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@patrickvonplaten
made this change based on this PR #3925
because this can simplify the code quiet a lot and help this refactor

Let me know if we need to run more tests before we can change everything to float32

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think this is fine indeed

timesteps += self.config.steps_offset
elif self.config.timestep_spacing == "trailing":
step_ratio = self.config.num_train_timesteps / self.num_inference_steps
# creates integer timesteps by multiplying by ratio
# casting to int to avoid issues when num_inference_step is power of 3
timesteps = (np.arange(self.config.num_train_timesteps, 0, -step_ratio)).round().copy().astype(float)
timesteps = (np.arange(self.config.num_train_timesteps, 0, -step_ratio)).round().copy().astype(np.float32)
timesteps -= 1
else:
raise ValueError(
Expand All @@ -233,11 +243,27 @@ def set_timesteps(self, num_inference_steps: int, device: Union[str, torch.devic
sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas)
sigmas = np.concatenate([sigmas, [0.0]]).astype(np.float32)
self.sigmas = torch.from_numpy(sigmas).to(device=device)
if str(device).startswith("mps"):
# mps does not support float64
self.timesteps = torch.from_numpy(timesteps).to(device, dtype=torch.float32)

self.timesteps = torch.from_numpy(timesteps).to(device=device)
self._step_index = None

# Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._init_step_index
def _init_step_index(self, timestep):
if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)

index_candidates = (self.timesteps == timestep).nonzero()

# The sigma index that is taken for the **very** first `step`
# is always the second index (or the last index if there is only 1)
# This way we can ensure we don't accidentally skip a sigma in
# case we start in the middle of the denoising schedule (e.g. for image-to-image)
if len(index_candidates) > 1:
step_index = index_candidates[1]
else:
self.timesteps = torch.from_numpy(timesteps).to(device=device)
step_index = index_candidates[0]
Comment thread
yiyixuxu marked this conversation as resolved.

self._step_index = step_index.item()

def step(
self,
Expand Down Expand Up @@ -285,11 +311,10 @@ def step(
"See `StableDiffusionPipeline` for a usage example."
)

if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)
if self.step_index is None:
self._init_step_index(timestep)

step_index = (self.timesteps == timestep).nonzero().item()
sigma = self.sigmas[step_index]
sigma = self.sigmas[self.step_index]

# 1. compute predicted original sample (x_0) from sigma-scaled predicted noise
if self.config.prediction_type == "epsilon":
Expand All @@ -304,8 +329,8 @@ def step(
f"prediction_type given as {self.config.prediction_type} must be one of `epsilon`, or `v_prediction`"
)

sigma_from = self.sigmas[step_index]
sigma_to = self.sigmas[step_index + 1]
sigma_from = self.sigmas[self.step_index]
sigma_to = self.sigmas[self.step_index + 1]
sigma_up = (sigma_to**2 * (sigma_from**2 - sigma_to**2) / sigma_from**2) ** 0.5
sigma_down = (sigma_to**2 - sigma_up**2) ** 0.5

Expand All @@ -321,6 +346,9 @@ def step(

prev_sample = prev_sample + noise * sigma_up

# upon completion increase step index by one
self._step_index += 1

if not return_dict:
return (prev_sample,)

Expand Down
59 changes: 42 additions & 17 deletions src/diffusers/schedulers/scheduling_euler_discrete.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ def __init__(
self.is_scale_input_called = False
self.use_karras_sigmas = use_karras_sigmas

self._step_index = None

@property
def init_noise_sigma(self):
# standard deviation of the initial noise distribution
Expand All @@ -185,6 +187,13 @@ def init_noise_sigma(self):

return (self.sigmas.max() ** 2 + 1) ** 0.5

@property
def step_index(self):
"""
The index counter for current timestep. It will increae 1 after each scheduler step.
"""
return self._step_index

def scale_model_input(
self, sample: torch.FloatTensor, timestep: Union[float, torch.FloatTensor]
) -> torch.FloatTensor:
Expand All @@ -198,11 +207,10 @@ def scale_model_input(
Returns:
`torch.FloatTensor`: scaled input sample
"""
if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)
step_index = (self.timesteps == timestep).nonzero().item()
sigma = self.sigmas[step_index]
if self.step_index is None:
self._init_step_index(timestep)

sigma = self.sigmas[self.step_index]
sample = sample / ((sigma**2 + 1) ** 0.5)

self.is_scale_input_called = True
Expand All @@ -222,20 +230,20 @@ def set_timesteps(self, num_inference_steps: int, device: Union[str, torch.devic

# "linspace", "leading", "trailing" corresponds to annotation of Table 2. of https://arxiv.org/abs/2305.08891
if self.config.timestep_spacing == "linspace":
timesteps = np.linspace(0, self.config.num_train_timesteps - 1, num_inference_steps, dtype=float)[
timesteps = np.linspace(0, self.config.num_train_timesteps - 1, num_inference_steps, dtype=np.float32)[
::-1
].copy()
elif self.config.timestep_spacing == "leading":
step_ratio = self.config.num_train_timesteps // self.num_inference_steps
# creates integer timesteps by multiplying by ratio
# casting to int to avoid issues when num_inference_step is power of 3
timesteps = (np.arange(0, num_inference_steps) * step_ratio).round()[::-1].copy().astype(float)
timesteps = (np.arange(0, num_inference_steps) * step_ratio).round()[::-1].copy().astype(np.float32)
timesteps += self.config.steps_offset
elif self.config.timestep_spacing == "trailing":
step_ratio = self.config.num_train_timesteps / self.num_inference_steps
# creates integer timesteps by multiplying by ratio
# casting to int to avoid issues when num_inference_step is power of 3
timesteps = (np.arange(self.config.num_train_timesteps, 0, -step_ratio)).round().copy().astype(float)
timesteps = (np.arange(self.config.num_train_timesteps, 0, -step_ratio)).round().copy().astype(np.float32)
timesteps -= 1
else:
raise ValueError(
Expand All @@ -261,11 +269,9 @@ def set_timesteps(self, num_inference_steps: int, device: Union[str, torch.devic

sigmas = np.concatenate([sigmas, [0.0]]).astype(np.float32)
self.sigmas = torch.from_numpy(sigmas).to(device=device)
if str(device).startswith("mps"):
# mps does not support float64
self.timesteps = torch.from_numpy(timesteps).to(device, dtype=torch.float32)
else:
self.timesteps = torch.from_numpy(timesteps).to(device=device)

self.timesteps = torch.from_numpy(timesteps).to(device=device)
self._step_index = None

def _sigma_to_t(self, sigma, log_sigmas):
# get log sigma
Expand Down Expand Up @@ -304,6 +310,23 @@ def _convert_to_karras(self, in_sigmas: torch.FloatTensor, num_inference_steps)
sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho)) ** rho
return sigmas

def _init_step_index(self, timestep):
if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)

index_candidates = (self.timesteps == timestep).nonzero()

# The sigma index that is taken for the **very** first `step`
# is always the second index (or the last index if there is only 1)
# This way we can ensure we don't accidentally skip a sigma in
# case we start in the middle of the denoising schedule (e.g. for image-to-image)
if len(index_candidates) > 1:
step_index = index_candidates[1]
else:
step_index = index_candidates[0]

self._step_index = step_index.item()

def step(
self,
model_output: torch.FloatTensor,
Expand Down Expand Up @@ -358,11 +381,10 @@ def step(
"See `StableDiffusionPipeline` for a usage example."
)

if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)
if self.step_index is None:
self._init_step_index(timestep)

step_index = (self.timesteps == timestep).nonzero().item()
sigma = self.sigmas[step_index]
sigma = self.sigmas[self.step_index]

gamma = min(s_churn / (len(self.sigmas) - 1), 2**0.5 - 1) if s_tmin <= sigma <= s_tmax else 0.0

Expand Down Expand Up @@ -394,10 +416,13 @@ def step(
# 2. Convert to an ODE derivative
derivative = (sample - pred_original_sample) / sigma_hat

dt = self.sigmas[step_index + 1] - sigma_hat
dt = self.sigmas[self.step_index + 1] - sigma_hat

prev_sample = sample + derivative * dt

# upon completion increase step index by one
self._step_index += 1

if not return_dict:
return (prev_sample,)

Expand Down
Loading