This commit is contained in:
smthemex
2025-04-21 17:11:11 +08:00
committed by GitHub
parent 21ef49f784
commit 0da44468f2
5 changed files with 997 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
# Copyright (c) 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import torch
from dataclasses import dataclass
from abc import ABC
from typing import Optional, Union, List
@dataclass
class SchedulerConversionOutput:
pred_epsilon: torch.Tensor
pred_original_sample: torch.Tensor
pred_velocity: torch.Tensor
@dataclass
class SchedulerStepOutput:
prev_sample: torch.Tensor
pred_original_sample: Optional[torch.Tensor] = None
class Scheduler(ABC):
prediction_types = ["epsilon", "sample", "v_prediction"]
timesteps_types = ["leading", "linspace", "trailing"]
def __init__(
self,
num_train_timesteps: int,
num_inference_timesteps: int,
betas: torch.Tensor,
inference_timesteps: Union[str, List[int]] = "trailing",
set_alpha_to_one: bool = True,
device: Optional[Union[str, torch.device]] = None,
dtype: torch.dtype = torch.float32
):
assert num_train_timesteps > 0
assert num_train_timesteps >= num_inference_timesteps
assert num_train_timesteps == betas.size(0)
assert betas.ndim == 1
self.device = device or betas.device
self.dtype = dtype
self.num_train_timesteps = num_train_timesteps
self.num_inference_timesteps = num_inference_timesteps
self.betas = betas.to(device=device, dtype=dtype)
self.alphas = 1.0 - self.betas
self.alphas_cumprod = torch.cumprod(self.alphas, dim=0)
self.final_alpha_cumprod = torch.tensor(1.0, device=self.device, dtype=self.dtype) if set_alpha_to_one else self.alphas_cumprod[0]
if isinstance(inference_timesteps, list):
# If user defines a custom inference timestep, directly assign it.
assert len(inference_timesteps) == num_inference_timesteps
self.timesteps = torch.tensor(inference_timesteps, device=self.device, dtype=torch.int)
elif inference_timesteps == "trailing":
# Example 20 steps: [999, 949, 899, 849, 799, 749, 699, 649, 599, 549, 499, 449, 399, 349, 299, 249, 199, 149, 99, 49]
self.timesteps = torch.arange(num_train_timesteps - 1, -1, -num_train_timesteps / num_inference_timesteps, device=self.device).round().int()
elif inference_timesteps == "linspace":
# Example 20 steps: [999, 946, 894, 841, 789, 736, 684, 631, 578, 526, 473, 421, 368, 315, 263, 210, 158, 105, 53, 0]
self.timesteps = torch.linspace(0, num_train_timesteps - 1, num_inference_timesteps, device=self.device).round().int().flip(0)
elif inference_timesteps == "leading":
# Original SD and DDIM paper may have a bug: <https://github.com/huggingface/diffusers/issues/2585>
# The inference timestep does not start from 999.
# Example 20 steps: [950, 900, 850, 800, 750, 700, 650, 600, 550, 500, 450, 400, 350, 300, 250, 200, 150, 100, 50, 0]
self.timesteps = torch.arange(0, num_train_timesteps, num_train_timesteps // num_inference_timesteps, device=self.device, dtype=torch.int).flip(0)
else:
raise NotImplementedError
def reset(self):
pass
def add_noise(
self,
original_samples: torch.Tensor,
noise: torch.Tensor,
timesteps: Union[torch.Tensor, int],
) -> torch.Tensor:
alpha_prod_t = self.alphas_cumprod[timesteps].reshape(-1, *([1] * (original_samples.ndim - 1)))
return alpha_prod_t ** (0.5) * original_samples + (1 - alpha_prod_t) ** (0.5) * noise
def convert_output(
self,
model_output: torch.Tensor,
model_output_type: str,
sample: torch.Tensor,
timesteps: Union[torch.Tensor, int]
) -> SchedulerConversionOutput:
assert model_output_type in self.prediction_types
alpha_prod_t = self.alphas_cumprod[timesteps].reshape(-1, *([1] * (sample.ndim - 1)))
beta_prod_t = 1 - alpha_prod_t
if model_output_type == "epsilon":
pred_epsilon = model_output
pred_original_sample = (sample - beta_prod_t ** (0.5) * pred_epsilon) / alpha_prod_t ** (0.5)
pred_velocity = alpha_prod_t ** (0.5) * pred_epsilon - (1 - alpha_prod_t) ** (0.5) * pred_original_sample
elif model_output_type == "sample":
pred_original_sample = model_output
pred_epsilon = (sample - alpha_prod_t ** (0.5) * pred_original_sample) / beta_prod_t ** (0.5)
pred_velocity = alpha_prod_t ** (0.5) * pred_epsilon - (1 - alpha_prod_t) ** (0.5) * pred_original_sample
elif model_output_type == "v_prediction":
pred_velocity = model_output
pred_original_sample = (alpha_prod_t**0.5) * sample - (beta_prod_t**0.5) * model_output
pred_epsilon = (alpha_prod_t**0.5) * model_output + (beta_prod_t**0.5) * sample
else:
raise ValueError("Unknown prediction type")
return SchedulerConversionOutput(
pred_epsilon=pred_epsilon,
pred_original_sample=pred_original_sample,
pred_velocity=pred_velocity)
def get_velocity(
self,
sample: torch.Tensor,
noise: torch.Tensor,
timesteps: torch.Tensor
) -> torch.FloatTensor:
alpha_prod_t = self.alphas_cumprod[timesteps].reshape(-1, *([1] * (sample.ndim - 1)))
return alpha_prod_t ** (0.5) * noise - (1 - alpha_prod_t) ** (0.5) * sample
+85
View File
@@ -0,0 +1,85 @@
# Copyright (c) 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .base import *
class DDIMScheduler(Scheduler):
def step(
self,
model_output: torch.Tensor,
model_output_type: str,
timestep: Union[torch.Tensor, int],
sample: torch.Tensor,
eta: float = 0.0,
clip_sample: bool = False,
dynamic_threshold: Optional[float] = None,
variance_noise: Optional[torch.Tensor] = None,
) -> SchedulerStepOutput:
# 1. get previous step value (t-1)
if not isinstance(timestep, torch.Tensor):
timestep = torch.tensor(timestep, device=self.device, dtype=torch.int)
idx = timestep.reshape(-1, 1).eq(self.timesteps.reshape(1, -1)).nonzero()[:, 1]
prev_timestep = self.timesteps[idx.add(1).clamp_max(self.num_inference_timesteps - 1)]
# 2. compute alphas, betas
alpha_prod_t = self.alphas_cumprod[timestep].reshape(-1, *([1] * (sample.ndim - 1)))
alpha_prod_t_prev = torch.where(idx < self.num_inference_timesteps - 1, self.alphas_cumprod[prev_timestep], self.final_alpha_cumprod).reshape(-1, *([1] * (sample.ndim - 1)))
beta_prod_t = 1 - alpha_prod_t
beta_prod_t_prev = 1 - alpha_prod_t_prev
# 3. compute predicted original sample from predicted noise also called
model_output_conversion = self.convert_output(model_output, model_output_type, sample, timestep)
pred_original_sample = model_output_conversion.pred_original_sample
pred_epsilon = model_output_conversion.pred_epsilon
# 4. Clip or threshold "predicted x_0"
if clip_sample:
pred_original_sample = torch.clamp(pred_original_sample, -1, 1)
pred_epsilon = self.convert_output(pred_original_sample, "sample", sample, timestep).pred_epsilon
if dynamic_threshold is not None:
# Dynamic thresholding in https://arxiv.org/abs/2205.11487
dynamic_max_val = pred_original_sample \
.flatten(1) \
.abs() \
.float() \
.quantile(dynamic_threshold, dim=1) \
.type_as(pred_original_sample) \
.clamp_min(1) \
.view(-1, *([1] * (pred_original_sample.ndim - 1)))
pred_original_sample = pred_original_sample.clamp(-dynamic_max_val, dynamic_max_val) / dynamic_max_val
pred_epsilon = self.convert_output(pred_original_sample, "sample", sample, timestep).pred_epsilon
# 5. compute variance: "sigma_t(η)" -> see formula (16) from https://arxiv.org/pdf/2010.02502.pdf
# σ_t = sqrt((1 − α_t−1)/(1 − α_t)) * sqrt(1 − α_t/α_t−1)
variance = (beta_prod_t_prev / beta_prod_t) * (1 - alpha_prod_t / alpha_prod_t_prev)
std_dev_t = eta * variance ** (0.5)
# 6. compute "direction pointing to x_t" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf
pred_sample_direction = (1 - alpha_prod_t_prev - std_dev_t**2) ** (0.5) * pred_epsilon
# 7. compute x_t without "random noise" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf
prev_sample = alpha_prod_t_prev ** (0.5) * pred_original_sample + pred_sample_direction
# 8. add "random noise" if needed.
if eta > 0:
if variance_noise is None:
variance_noise = torch.randn_like(model_output)
prev_sample = prev_sample + std_dev_t * variance_noise
return SchedulerStepOutput(
prev_sample=prev_sample,
pred_original_sample=pred_original_sample)
+412
View File
@@ -0,0 +1,412 @@
# Copyright (c) 2024 Bytedance Ltd. and/or its affiliates
# Copyright 2023 TSAIL Team and The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# DISCLAIMER: This file is strongly influenced by https://github.com/LuChengTHU/dpm-solver
from typing import List, Optional, Union
import numpy as np
import torch
from .base import *
class DPMSolverMultistepScheduler(Scheduler):
def __init__(
self,
# Generic scheduler settings
num_inference_timesteps: int,
betas: torch.Tensor,
num_train_timesteps: int = 1000,
inference_timesteps: Union[str, List[str]] = "trailing",
set_alpha_to_one: bool = True,
device: Optional[torch.device] = None,
dtype: torch.dtype = torch.float32,
# DPM scheduler settings
solver_order: int = 2,
algorithm_type: str = "dpmsolver++",
solver_type: str = "midpoint",
lower_order_final: bool = True,
use_karras_sigmas: bool = False,
):
super().__init__(
num_train_timesteps=num_train_timesteps,
num_inference_timesteps=num_inference_timesteps,
betas=betas,
inference_timesteps=inference_timesteps,
set_alpha_to_one=set_alpha_to_one,
device=device,
dtype=dtype,
)
self.solver_order = solver_order
self.solver_type = solver_type
self.lower_order_final = lower_order_final
self.algorithm_type = algorithm_type
# Currently we only support VP-type noise schedule
self.alpha_t = torch.sqrt(self.alphas_cumprod)
self.sigma_t = torch.sqrt(1 - self.alphas_cumprod)
self.lambda_t = torch.log(self.alpha_t) - torch.log(self.sigma_t)
sigmas = torch.sqrt((1 - self.alphas_cumprod) / self.alphas_cumprod)
if use_karras_sigmas:
log_sigmas = torch.log(sigmas)
sigmas = self._convert_to_karras(
in_sigmas=sigmas, num_inference_timesteps=num_inference_timesteps)
timesteps = np.array([self._sigma_to_t(sigma, log_sigmas)
for sigma in sigmas]).round()
timesteps = np.flip(timesteps).copy().astype(np.int64)
self.timesteps = torch.from_numpy(timesteps).to(device)
sigmas = torch.from_numpy(sigmas).to(device)
self.sigmas = sigmas
# standard deviation of the initial noise distribution
self.init_noise_sigma = 1.0
# settings for DPM-Solver
if algorithm_type not in ["dpmsolver", "dpmsolver++", "sde-dpmsolver", "sde-dpmsolver++", "deis"]:
raise NotImplementedError(
f"{algorithm_type} does is not implemented for {self.__class__}")
if solver_type not in ["midpoint", "heun", "logrho", "bh1", "bh2"]:
raise NotImplementedError(
f"{solver_type} does is not implemented for {self.__class__}")
# setable values
self.reset()
def reset(self):
self.model_outputs = [None] * self.solver_order
self.lower_order_nums = 0
def _sigma_to_t(self, sigma, log_sigmas):
# get log sigma
log_sigma = np.log(sigma)
# get distribution
dists = log_sigma - log_sigmas[:, np.newaxis]
# get sigmas range
low_idx = np.cumsum((dists >= 0), axis=0).argmax(
axis=0).clip(max=log_sigmas.shape[0] - 2)
high_idx = low_idx + 1
low = log_sigmas[low_idx]
high = log_sigmas[high_idx]
# interpolate sigmas
w = (low - log_sigma) / (low - high)
w = np.clip(w, 0, 1)
# transform interpolation to time range
t = (1 - w) * low_idx + w * high_idx
t = t.reshape(sigma.shape)
return t
# Copied from diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler._convert_to_karras
def _convert_to_karras(self, in_sigmas: torch.FloatTensor, num_inference_timesteps) -> torch.FloatTensor:
"""Constructs the noise schedule of Karras et al. (2022)."""
sigma_min: float = in_sigmas[-1].item()
sigma_max: float = in_sigmas[0].item()
rho = 7.0 # 7.0 is the value used in the paper
ramp = np.linspace(0, 1, num_inference_timesteps)
min_inv_rho = sigma_min ** (1 / rho)
max_inv_rho = sigma_max ** (1 / rho)
sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho)) ** rho
return sigmas
def dpm_solver_first_order_update(
self,
model_output: torch.FloatTensor,
timestep: int,
prev_timestep: int,
sample: torch.FloatTensor,
noise: Optional[torch.FloatTensor] = None,
) -> torch.FloatTensor:
"""
One step for the first-order DPM-Solver (equivalent to DDIM).
See https://arxiv.org/abs/2206.00927 for the detailed derivation.
Args:
model_output (`torch.FloatTensor`): direct output from learned diffusion model.
timestep (`int`): current discrete timestep in the diffusion chain.
prev_timestep (`int`): previous discrete timestep in the diffusion chain.
sample (`torch.FloatTensor`):
current instance of sample being created by diffusion process.
Returns:
`torch.FloatTensor`: the sample tensor at the previous timestep.
"""
lambda_t, lambda_s = self.lambda_t[prev_timestep], self.lambda_t[timestep]
alpha_t, alpha_s = self.alpha_t[prev_timestep], self.alpha_t[timestep]
sigma_t, sigma_s = self.sigma_t[prev_timestep], self.sigma_t[timestep]
h = lambda_t - lambda_s
if self.algorithm_type == "dpmsolver++":
x_t = (sigma_t / sigma_s) * sample - \
(alpha_t * (torch.exp(-h) - 1.0)) * model_output
elif self.algorithm_type == "dpmsolver":
x_t = (alpha_t / alpha_s) * sample - \
(sigma_t * (torch.exp(h) - 1.0)) * model_output
elif self.algorithm_type == "sde-dpmsolver++":
assert noise is not None
x_t = (
(sigma_t / sigma_s * torch.exp(-h)) * sample
+ (alpha_t * (1 - torch.exp(-2.0 * h))) * model_output
+ sigma_t * torch.sqrt(1.0 - torch.exp(-2 * h)) * noise
)
elif self.algorithm_type == "sde-dpmsolver":
assert noise is not None
x_t = (
(alpha_t / alpha_s) * sample
- 2.0 * (sigma_t * (torch.exp(h) - 1.0)) * model_output
+ sigma_t * torch.sqrt(torch.exp(2 * h) - 1.0) * noise
)
return x_t
def multistep_dpm_solver_second_order_update(
self,
model_output_list: List[torch.FloatTensor],
timestep_list: List[int],
prev_timestep: int,
sample: torch.FloatTensor,
noise: Optional[torch.FloatTensor] = None,
) -> torch.FloatTensor:
"""
One step for the second-order multistep DPM-Solver.
Args:
model_output_list (`List[torch.FloatTensor]`):
direct outputs from learned diffusion model at current and latter timesteps.
timestep (`int`): current and latter discrete timestep in the diffusion chain.
prev_timestep (`int`): previous discrete timestep in the diffusion chain.
sample (`torch.FloatTensor`):
current instance of sample being created by diffusion process.
Returns:
`torch.FloatTensor`: the sample tensor at the previous timestep.
"""
t, s0, s1 = prev_timestep, timestep_list[-1], timestep_list[-2]
m0, m1 = model_output_list[-1], model_output_list[-2]
lambda_t, lambda_s0, lambda_s1 = self.lambda_t[t], self.lambda_t[s0], self.lambda_t[s1]
alpha_t, alpha_s0 = self.alpha_t[t], self.alpha_t[s0]
sigma_t, sigma_s0 = self.sigma_t[t], self.sigma_t[s0]
h, h_0 = lambda_t - lambda_s0, lambda_s0 - lambda_s1
r0 = h_0 / h
D0, D1 = m0, (1.0 / r0) * (m0 - m1)
if self.algorithm_type == "dpmsolver++":
# See https://arxiv.org/abs/2211.01095 for detailed derivations
if self.solver_type == "midpoint":
x_t = (
(sigma_t / sigma_s0) * sample
- (alpha_t * (torch.exp(-h) - 1.0)) * D0
- 0.5 * (alpha_t * (torch.exp(-h) - 1.0)) * D1
)
elif self.solver_type == "heun":
x_t = (
(sigma_t / sigma_s0) * sample
- (alpha_t * (torch.exp(-h) - 1.0)) * D0
+ (alpha_t * ((torch.exp(-h) - 1.0) / h + 1.0)) * D1
)
elif self.algorithm_type == "dpmsolver":
# See https://arxiv.org/abs/2206.00927 for detailed derivations
if self.solver_type == "midpoint":
x_t = (
(alpha_t / alpha_s0) * sample
- (sigma_t * (torch.exp(h) - 1.0)) * D0
- 0.5 * (sigma_t * (torch.exp(h) - 1.0)) * D1
)
elif self.solver_type == "heun":
x_t = (
(alpha_t / alpha_s0) * sample
- (sigma_t * (torch.exp(h) - 1.0)) * D0
- (sigma_t * ((torch.exp(h) - 1.0) / h - 1.0)) * D1
)
elif self.algorithm_type == "sde-dpmsolver++":
assert noise is not None
if self.solver_type == "midpoint":
x_t = (
(sigma_t / sigma_s0 * torch.exp(-h)) * sample
+ (alpha_t * (1 - torch.exp(-2.0 * h))) * D0
+ 0.5 * (alpha_t * (1 - torch.exp(-2.0 * h))) * D1
+ sigma_t * torch.sqrt(1.0 - torch.exp(-2 * h)) * noise
)
elif self.solver_type == "heun":
x_t = (
(sigma_t / sigma_s0 * torch.exp(-h)) * sample
+ (alpha_t * (1 - torch.exp(-2.0 * h))) * D0
+ (alpha_t * ((1.0 - torch.exp(-2.0 * h)) / (-2.0 * h) + 1.0)) * D1
+ sigma_t * torch.sqrt(1.0 - torch.exp(-2 * h)) * noise
)
elif self.algorithm_type == "sde-dpmsolver":
assert noise is not None
if self.solver_type == "midpoint":
x_t = (
(alpha_t / alpha_s0) * sample
- 2.0 * (sigma_t * (torch.exp(h) - 1.0)) * D0
- (sigma_t * (torch.exp(h) - 1.0)) * D1
+ sigma_t * torch.sqrt(torch.exp(2 * h) - 1.0) * noise
)
elif self.solver_type == "heun":
x_t = (
(alpha_t / alpha_s0) * sample
- 2.0 * (sigma_t * (torch.exp(h) - 1.0)) * D0
- 2.0 * (sigma_t * ((torch.exp(h) - 1.0) / h - 1.0)) * D1
+ sigma_t * torch.sqrt(torch.exp(2 * h) - 1.0) * noise
)
return x_t
def multistep_dpm_solver_third_order_update(
self,
model_output_list: List[torch.FloatTensor],
timestep_list: List[int],
prev_timestep: int,
sample: torch.FloatTensor,
) -> torch.FloatTensor:
"""
One step for the third-order multistep DPM-Solver.
Args:
model_output_list (`List[torch.FloatTensor]`):
direct outputs from learned diffusion model at current and latter timesteps.
timestep (`int`): current and latter discrete timestep in the diffusion chain.
prev_timestep (`int`): previous discrete timestep in the diffusion chain.
sample (`torch.FloatTensor`):
current instance of sample being created by diffusion process.
Returns:
`torch.FloatTensor`: the sample tensor at the previous timestep.
"""
t, s0, s1, s2 = prev_timestep, timestep_list[-1], timestep_list[-2], timestep_list[-3]
m0, m1, m2 = model_output_list[-1], model_output_list[-2], model_output_list[-3]
lambda_t, lambda_s0, lambda_s1, lambda_s2 = (
self.lambda_t[t],
self.lambda_t[s0],
self.lambda_t[s1],
self.lambda_t[s2],
)
alpha_t, alpha_s0 = self.alpha_t[t], self.alpha_t[s0]
sigma_t, sigma_s0 = self.sigma_t[t], self.sigma_t[s0]
h, h_0, h_1 = lambda_t - lambda_s0, lambda_s0 - lambda_s1, lambda_s1 - lambda_s2
r0, r1 = h_0 / h, h_1 / h
D0 = m0
D1_0, D1_1 = (1.0 / r0) * (m0 - m1), (1.0 / r1) * (m1 - m2)
D1 = D1_0 + (r0 / (r0 + r1)) * (D1_0 - D1_1)
D2 = (1.0 / (r0 + r1)) * (D1_0 - D1_1)
if self.algorithm_type == "dpmsolver++":
# See https://arxiv.org/abs/2206.00927 for detailed derivations
x_t = (
(sigma_t / sigma_s0) * sample
- (alpha_t * (torch.exp(-h) - 1.0)) * D0
+ (alpha_t * ((torch.exp(-h) - 1.0) / h + 1.0)) * D1
- (alpha_t * ((torch.exp(-h) - 1.0 + h) / h**2 - 0.5)) * D2
)
elif self.algorithm_type == "dpmsolver":
# See https://arxiv.org/abs/2206.00927 for detailed derivations
x_t = (
(alpha_t / alpha_s0) * sample
- (sigma_t * (torch.exp(h) - 1.0)) * D0
- (sigma_t * ((torch.exp(h) - 1.0) / h - 1.0)) * D1
- (sigma_t * ((torch.exp(h) - 1.0 - h) / h**2 - 0.5)) * D2
)
return x_t
def step(
self,
model_output: torch.FloatTensor,
model_output_type: str,
timestep: int,
sample: torch.FloatTensor,
) -> SchedulerStepOutput:
"""
Step function propagating the sample with the multistep DPM-Solver.
Args:
model_output (`torch.FloatTensor`): direct output from learned diffusion model.
timestep (`int`): current discrete timestep in the diffusion chain.
sample (`torch.FloatTensor`):
current instance of sample being created by diffusion process.
return_dict (`bool`): option for returning tuple rather than SchedulerOutput class
Returns:
[`~scheduling_utils.SchedulerOutput`] or `tuple`: [`~scheduling_utils.SchedulerOutput`] if `return_dict` is
True, otherwise a `tuple`. When returning a tuple, the first element is the sample tensor.
"""
if self.num_inference_timesteps is None:
raise ValueError(
"Number of inference steps is 'None', you need to run 'set_timesteps' after creating the scheduler"
)
if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)
step_index = (self.timesteps == timestep).nonzero()
if len(step_index) == 0:
step_index = len(self.timesteps) - 1
else:
step_index = step_index.item()
prev_timestep = 0 if step_index == len(
self.timesteps) - 1 else self.timesteps[step_index + 1]
lower_order_final = (
(step_index == len(self.timesteps) -
1) and self.lower_order_final and len(self.timesteps) < 15
)
lower_order_second = (
(step_index == len(self.timesteps) -
2) and self.lower_order_final and len(self.timesteps) < 15
)
model_output_convert = self.convert_output(
model_output, model_output_type=model_output_type, sample=sample, timesteps=timestep)
# DPM-Solver++ needs to solve an integral of the data prediction model.
if self.algorithm_type in ["dpmsolver++", "sde-dpmsolver++"]:
model_output = model_output_convert.pred_original_sample
# DPM-Solver needs to solve an integral of the noise prediction model.
elif self.algorithm_type in ["dpmsolver", "sde-dpmsolver"]:
model_output = model_output_convert.pred_epsilon
for i in range(self.solver_order - 1):
self.model_outputs[i] = self.model_outputs[i + 1]
self.model_outputs[-1] = model_output
if self.algorithm_type in ["sde-dpmsolver", "sde-dpmsolver++"]:
noise = torch.randn_like(
model_output, device=model_output.device, dtype=model_output.dtype)
else:
noise = None
if self.solver_order == 1 or self.lower_order_nums < 1 or lower_order_final:
prev_sample = self.dpm_solver_first_order_update(
model_output, timestep, prev_timestep, sample, noise=noise
)
elif self.solver_order == 2 or self.lower_order_nums < 2 or lower_order_second:
timestep_list = [self.timesteps[step_index - 1], timestep]
prev_sample = self.multistep_dpm_solver_second_order_update(
self.model_outputs, timestep_list, prev_timestep, sample, noise=noise
)
else:
timestep_list = [self.timesteps[step_index - 2],
self.timesteps[step_index - 1], timestep]
prev_sample = self.multistep_dpm_solver_third_order_update(
self.model_outputs, timestep_list, prev_timestep, sample
)
if self.lower_order_nums < self.solver_order:
self.lower_order_nums += 1
return SchedulerStepOutput(prev_sample=prev_sample, pred_original_sample=model_output_convert.pred_original_sample)
+243
View File
@@ -0,0 +1,243 @@
# Copyright (c) 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .base import *
class DPMSolverSingleStepScheduler(Scheduler):
def __init__(
self,
# Generic scheduler settings
num_train_timesteps: int,
num_inference_timesteps: int,
betas: torch.Tensor,
inference_timesteps: Union[str, List[int]] = "trailing",
set_alpha_to_one: bool = True,
device: Optional[Union[str, torch.device]] = None,
dtype: torch.dtype = torch.float32,
# DPM scheduler settings
algorithm_type: str = "dpmsolver++",
solver_type: str = "midpoint",
solver_order: int = 2,
lower_order_final: bool = True,
):
super().__init__(
num_train_timesteps=num_train_timesteps,
num_inference_timesteps=num_inference_timesteps,
betas=betas,
inference_timesteps=inference_timesteps,
set_alpha_to_one=set_alpha_to_one,
device=device,
dtype=dtype,
)
self.solver_order = solver_order
self.solver_type = solver_type
self.lower_order_final = lower_order_final
self.algorithm_type = algorithm_type
self.alpha_t = torch.sqrt(self.alphas_cumprod)
self.sigma_t = torch.sqrt(1 - self.alphas_cumprod)
self.lambda_t = torch.log(self.alpha_t) - torch.log(self.sigma_t)
self.reset()
def reset(self):
self.model_outputs = [None] * self.solver_order
self.sample = None
self.order_list = self.get_order_list()
self.last_step_index = None
def get_order_list(self) -> List[int]:
steps = self.num_inference_timesteps
order = self.solver_order
# First step must be order 1
# Second step must be order 1 in case of terminal zero SNR
orders = [1] + [(i % order) + 1 for i in range(steps - 1)] + [1]
# Last step should be order 1 for better quality.
if self.lower_order_final:
orders[-1] = 1
return orders
def dpm_solver_first_order_update(
self,
model_output: torch.FloatTensor,
timestep: int,
prev_timestep: int,
sample: torch.FloatTensor,
) -> torch.FloatTensor:
lambda_t, lambda_s = self.lambda_t[prev_timestep], self.lambda_t[timestep]
alpha_t, alpha_s = self.alpha_t[prev_timestep], self.alpha_t[timestep]
sigma_t, sigma_s = self.sigma_t[prev_timestep], self.sigma_t[timestep]
h = lambda_t - lambda_s
if self.algorithm_type == "dpmsolver++":
x_t = (sigma_t / sigma_s) * sample - (alpha_t * (torch.exp(-h) - 1.0)) * model_output
elif self.algorithm_type == "dpmsolver":
x_t = (alpha_t / alpha_s) * sample - (sigma_t * (torch.exp(h) - 1.0)) * model_output
return x_t
def singlestep_dpm_solver_second_order_update(
self,
model_output_list: List[torch.FloatTensor],
timestep_list: List[int],
prev_timestep: int,
sample: torch.FloatTensor,
) -> torch.FloatTensor:
t, s0, s1 = prev_timestep, timestep_list[-1], timestep_list[-2]
m0, m1 = model_output_list[-1], model_output_list[-2]
lambda_t, lambda_s0, lambda_s1 = self.lambda_t[t], self.lambda_t[s0], self.lambda_t[s1]
alpha_t, alpha_s1 = self.alpha_t[t], self.alpha_t[s1]
sigma_t, sigma_s1 = self.sigma_t[t], self.sigma_t[s1]
h, h_0 = lambda_t - lambda_s1, lambda_s0 - lambda_s1
r0 = h_0 / h
D0, D1 = m1, (1.0 / r0) * (m0 - m1)
if self.algorithm_type == "dpmsolver++":
# See https://arxiv.org/abs/2211.01095 for detailed derivations
if self.solver_type == "midpoint":
x_t = (
(sigma_t / sigma_s1) * sample
- (alpha_t * (torch.exp(-h) - 1.0)) * D0
- 0.5 * (alpha_t * (torch.exp(-h) - 1.0)) * D1
)
elif self.solver_type == "heun":
x_t = (
(sigma_t / sigma_s1) * sample
- (alpha_t * (torch.exp(-h) - 1.0)) * D0
+ (alpha_t * ((torch.exp(-h) - 1.0) / h + 1.0)) * D1
)
elif self.algorithm_type == "dpmsolver":
# See https://arxiv.org/abs/2206.00927 for detailed derivations
if self.solver_type == "midpoint":
x_t = (
(alpha_t / alpha_s1) * sample
- (sigma_t * (torch.exp(h) - 1.0)) * D0
- 0.5 * (sigma_t * (torch.exp(h) - 1.0)) * D1
)
elif self.solver_type == "heun":
x_t = (
(alpha_t / alpha_s1) * sample
- (sigma_t * (torch.exp(h) - 1.0)) * D0
- (sigma_t * ((torch.exp(h) - 1.0) / h - 1.0)) * D1
)
return x_t
def singlestep_dpm_solver_third_order_update(
self,
model_output_list: List[torch.FloatTensor],
timestep_list: List[int],
prev_timestep: int,
sample: torch.FloatTensor,
) -> torch.FloatTensor:
t, s0, s1, s2 = prev_timestep, timestep_list[-1], timestep_list[-2], timestep_list[-3]
m0, m1, m2 = model_output_list[-1], model_output_list[-2], model_output_list[-3]
lambda_t, lambda_s0, lambda_s1, lambda_s2 = (
self.lambda_t[t],
self.lambda_t[s0],
self.lambda_t[s1],
self.lambda_t[s2],
)
alpha_t, alpha_s2 = self.alpha_t[t], self.alpha_t[s2]
sigma_t, sigma_s2 = self.sigma_t[t], self.sigma_t[s2]
h, h_0, h_1 = lambda_t - lambda_s2, lambda_s0 - lambda_s2, lambda_s1 - lambda_s2
r0, r1 = h_0 / h, h_1 / h
D0 = m2
D1_0, D1_1 = (1.0 / r1) * (m1 - m2), (1.0 / r0) * (m0 - m2)
D1 = (r0 * D1_0 - r1 * D1_1) / (r0 - r1)
D2 = 2.0 * (D1_1 - D1_0) / (r0 - r1)
if self.algorithm_type == "dpmsolver++":
# See https://arxiv.org/abs/2206.00927 for detailed derivations
if self.solver_type == "midpoint":
x_t = (
(sigma_t / sigma_s2) * sample
- (alpha_t * (torch.exp(-h) - 1.0)) * D0
+ (alpha_t * ((torch.exp(-h) - 1.0) / h + 1.0)) * D1_1
)
elif self.solver_type == "heun":
x_t = (
(sigma_t / sigma_s2) * sample
- (alpha_t * (torch.exp(-h) - 1.0)) * D0
+ (alpha_t * ((torch.exp(-h) - 1.0) / h + 1.0)) * D1
- (alpha_t * ((torch.exp(-h) - 1.0 + h) / h**2 - 0.5)) * D2
)
elif self.algorithm_type == "dpmsolver":
# See https://arxiv.org/abs/2206.00927 for detailed derivations
if self.solver_type == "midpoint":
x_t = (
(alpha_t / alpha_s2) * sample
- (sigma_t * (torch.exp(h) - 1.0)) * D0
- (sigma_t * ((torch.exp(h) - 1.0) / h - 1.0)) * D1_1
)
elif self.solver_type == "heun":
x_t = (
(alpha_t / alpha_s2) * sample
- (sigma_t * (torch.exp(h) - 1.0)) * D0
- (sigma_t * ((torch.exp(h) - 1.0) / h - 1.0)) * D1
- (sigma_t * ((torch.exp(h) - 1.0 - h) / h**2 - 0.5)) * D2
)
return x_t
def step(
self,
model_output: torch.FloatTensor,
model_output_type: str,
timestep: int,
sample: torch.FloatTensor,
) -> SchedulerStepOutput:
step_index = (self.timesteps == timestep).nonzero().item()
# Check if this step is the follow-up of the previous step.
# If not, then we reset and treat it as a new run.
if self.last_step_index and self.last_step_index != step_index - 1:
self.reset()
self.last_step_index = step_index
prev_timestep = 0 if step_index == len(self.timesteps) - 1 else self.timesteps[step_index + 1]
model_output_convert = self.convert_output(model_output, model_output_type, sample, timestep)
if self.algorithm_type == "dpmsolver++":
model_output = model_output_convert.pred_original_sample
else:
model_output = model_output_convert.pred_epsilon
for i in range(self.solver_order - 1):
self.model_outputs[i] = self.model_outputs[i + 1]
self.model_outputs[-1] = model_output
order = self.order_list[step_index]
# For img2img denoising might start with order>1 which is not possible
# In this case make sure that the first two steps are both order=1
while self.model_outputs[-order] is None:
order -= 1
# For single-step solvers, we use the initial value at each time with order = 1.
if order == 1:
self.sample = sample
timestep_list = [self.timesteps[step_index - i] for i in range(order - 1, 0, -1)] + [timestep]
if order == 1:
prev_sample = self.dpm_solver_first_order_update(self.model_outputs[-1], timestep_list[-1], prev_timestep, self.sample)
elif order == 2:
prev_sample = self.singlestep_dpm_solver_second_order_update(self.model_outputs, timestep_list, prev_timestep, self.sample)
elif order == 3:
prev_sample = self.singlestep_dpm_solver_third_order_update(self.model_outputs, timestep_list, prev_timestep, self.sample)
else:
raise NotImplementedError
return SchedulerStepOutput(
prev_sample=prev_sample,
pred_original_sample=model_output_convert.pred_original_sample
)
+124
View File
@@ -0,0 +1,124 @@
# Copyright (c) 2024 Bytedance Ltd. and/or its affiliates
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import math
from typing import List
import torch
def get_betas(name: str, num_steps: int = 1000, shift_snr: float = 1, terminal_pure_noise: bool = False):
# Get betas
max_beta = 1 if terminal_pure_noise else 0.999
if name == "squared_linear":
betas = torch.linspace(0.00085**0.5, 0.012**0.5, num_steps) ** 2
elif name == "cosine":
betas = get_cosine_betas(num_steps, max_beta=max_beta)
elif name == "alphas_cumprod_linear":
betas = get_alphas_cumprod_linear_betas(num_steps, max_beta=max_beta)
elif name == "sigmoid":
betas = get_sigmoid_betas(num_steps, max_beta=max_beta, square=True, slop=0.7)
else:
raise NotImplementedError
# Shift snr
betas = shift_betas_by_snr_factor(betas, shift_snr)
# Ensure terminal pure noise
# Only non-cosine schedule uses rescale, cosine schedule can directly set max_beta=1 to ensure temrinal pure noise.
if name == "squared_linear" and terminal_pure_noise:
betas = rescale_betas_to_ensure_terminal_pure_noise(betas)
return betas
def validate_betas(betas: List[float]) -> bool:
"""
Validate betas is monotonic and within 0 to 1 range, i.e. 0 < beta_{t-1} < beta_{t} <= 1
Args:
betas (List[float]): betas
Returns:
bool: True if betas is correct
"""
return all(b1 < b2 for b1, b2 in zip(betas, betas[1:])) and betas[0] > 0 and betas[-1] <= 1
def betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar_fn, max_beta=0.999):
betas = []
for i in range(num_diffusion_timesteps):
t1 = i / num_diffusion_timesteps
t2 = (i + 1) / num_diffusion_timesteps
betas.append(min(1 - alpha_bar_fn(t2) / alpha_bar_fn(t1), max_beta))
if not validate_betas(betas):
import logging
logging.warning("No feasible betas for given alpha bar")
return torch.tensor(betas, dtype=torch.float32)
def get_cosine_betas(num_diffusion_timesteps, max_beta=0.999) -> torch.Tensor:
def alpha_bar_fn(time_step):
return math.cos((time_step + 0.008) / 1.008 * math.pi / 2) ** 2
return betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar_fn, max_beta)
def get_sigmoid_betas(num_diffusion_timesteps, max_beta, square=False, slop=1):
def alpha_bar_fn(t):
def sigmoid(x):
return 1 / (1 + math.exp(-x * slop))
s = 6 # (-6, 6) from geodiff
vb = sigmoid(-s)
ve = sigmoid(s)
return ((sigmoid(s - t * 2 * s) - vb) / (ve - vb))**(1 if not square else 2)
return betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar_fn, max_beta)
def get_alphas_cumprod_linear_betas(num_diffusion_timesteps, max_beta):
def alpha_bar_fn(t):
return 1 - t
return betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar_fn, max_beta=max_beta)
def shift_betas_by_snr_factor(betas: torch.Tensor, factor: float) -> torch.Tensor:
if factor == 1.0:
return betas
# Convert betas to snr
alphas = 1 - betas
alphas_cumprod = alphas.cumprod(dim=0)
snr = alphas_cumprod / (1 - alphas_cumprod)
# Shift snr
snr *= factor
# Convert snr to betas
alphas_cumprod = snr / (1 + snr)
alphas = torch.cat(
[alphas_cumprod[0:1], alphas_cumprod[1:] / alphas_cumprod[:-1]])
betas = 1 - alphas
return betas
def rescale_betas_to_ensure_terminal_pure_noise(betas: torch.Tensor) -> torch.Tensor:
# Convert betas to alphas_cumprod_sqrt
alphas = 1 - betas
alphas_cumprod = alphas.cumprod(0)
alphas_cumprod_sqrt = alphas_cumprod.sqrt()
# Rescale alphas_cumprod_sqrt such that alphas_cumprod_sqrt[0] remains unchanged but alphas_cumprod_sqrt[-1] = 0
alphas_cumprod_sqrt = (alphas_cumprod_sqrt - alphas_cumprod_sqrt[-1]) / (
alphas_cumprod_sqrt[0] - alphas_cumprod_sqrt[-1]) * alphas_cumprod_sqrt[0]
# Convert alphas_cumprod_sqrt to betas
alphas_cumprod = alphas_cumprod_sqrt ** 2
alphas = torch.cat(
[alphas_cumprod[0:1], alphas_cumprod[1:] / alphas_cumprod[:-1]])
betas = 1 - alphas
return betas