| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| import os, sys, pdb |
| import math |
| from dataclasses import dataclass |
| from typing import List, Optional, Tuple, Union |
|
|
| import numpy as np |
| import torch |
|
|
| from diffusers.configuration_utils import ConfigMixin, register_to_config |
| from diffusers.utils import BaseOutput, randn_tensor |
| from diffusers.schedulers.scheduling_utils import KarrasDiffusionSchedulers, SchedulerMixin |
|
|
|
|
| @dataclass |
| |
| class DDIMSchedulerOutput(BaseOutput): |
| """ |
| Output class for the scheduler's step function output. |
| |
| Args: |
| prev_sample (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)` for images): |
| Computed sample (x_{t-1}) of previous timestep. `prev_sample` should be used as next model input in the |
| denoising loop. |
| pred_original_sample (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)` for images): |
| The predicted denoised sample (x_{0}) based on the model output from the current timestep. |
| `pred_original_sample` can be used to preview progress or for guidance. |
| """ |
|
|
| prev_sample: torch.FloatTensor |
| pred_original_sample: Optional[torch.FloatTensor] = None |
|
|
|
|
| def betas_for_alpha_bar(num_diffusion_timesteps, max_beta=0.999) -> torch.Tensor: |
| """ |
| Create a beta schedule that discretizes the given alpha_t_bar function, which defines the cumulative product of |
| (1-beta) over time from t = [0,1]. |
| |
| Contains a function alpha_bar that takes an argument t and transforms it to the cumulative product of (1-beta) up |
| to that part of the diffusion process. |
| |
| |
| Args: |
| num_diffusion_timesteps (`int`): the number of betas to produce. |
| max_beta (`float`): the maximum beta to use; use values lower than 1 to |
| prevent singularities. |
| |
| Returns: |
| betas (`np.ndarray`): the betas used by the scheduler to step the model outputs |
| """ |
|
|
| def alpha_bar(time_step): |
| return math.cos((time_step + 0.008) / 1.008 * math.pi / 2) ** 2 |
|
|
| 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(t2) / alpha_bar(t1), max_beta)) |
| return torch.tensor(betas) |
|
|
|
|
| class DDIMInverseScheduler(SchedulerMixin, ConfigMixin): |
| """ |
| Denoising diffusion implicit models is a scheduler that extends the denoising procedure introduced in denoising |
| diffusion probabilistic models (DDPMs) with non-Markovian guidance. |
| |
| [`~ConfigMixin`] takes care of storing all config attributes that are passed in the scheduler's `__init__` |
| function, such as `num_train_timesteps`. They can be accessed via `scheduler.config.num_train_timesteps`. |
| [`SchedulerMixin`] provides general loading and saving functionality via the [`SchedulerMixin.save_pretrained`] and |
| [`~SchedulerMixin.from_pretrained`] functions. |
| |
| For more details, see the original paper: https://arxiv.org/abs/2010.02502 |
| |
| Args: |
| num_train_timesteps (`int`): number of diffusion steps used to train the model. |
| beta_start (`float`): the starting `beta` value of inference. |
| beta_end (`float`): the final `beta` value. |
| beta_schedule (`str`): |
| the beta schedule, a mapping from a beta range to a sequence of betas for stepping the model. Choose from |
| `linear`, `scaled_linear`, or `squaredcos_cap_v2`. |
| trained_betas (`np.ndarray`, optional): |
| option to pass an array of betas directly to the constructor to bypass `beta_start`, `beta_end` etc. |
| clip_sample (`bool`, default `True`): |
| option to clip predicted sample between -1 and 1 for numerical stability. |
| set_alpha_to_one (`bool`, default `True`): |
| each diffusion step uses the value of alphas product at that step and at the previous one. For the final |
| step there is no previous alpha. When this option is `True` the previous alpha product is fixed to `1`, |
| otherwise it uses the value of alpha at step 0. |
| steps_offset (`int`, default `0`): |
| an offset added to the inference steps. You can use a combination of `offset=1` and |
| `set_alpha_to_one=False`, to make the last step use step 0 for the previous alpha product, as done in |
| stable diffusion. |
| prediction_type (`str`, default `epsilon`, optional): |
| prediction type of the scheduler function, one of `epsilon` (predicting the noise of the diffusion |
| process), `sample` (directly predicting the noisy sample`) or `v_prediction` (see section 2.4 |
| https://imagen.research.google/video/paper.pdf) |
| """ |
|
|
| _compatibles = [e.name for e in KarrasDiffusionSchedulers] |
| order = 1 |
|
|
| @register_to_config |
| def __init__( |
| self, |
| num_train_timesteps: int = 1000, |
| beta_start: float = 0.0001, |
| beta_end: float = 0.02, |
| beta_schedule: str = "linear", |
| trained_betas: Optional[Union[np.ndarray, List[float]]] = None, |
| clip_sample: bool = True, |
| set_alpha_to_one: bool = True, |
| steps_offset: int = 0, |
| prediction_type: str = "epsilon", |
| ): |
| if trained_betas is not None: |
| self.betas = torch.tensor(trained_betas, dtype=torch.float32) |
| elif beta_schedule == "linear": |
| self.betas = torch.linspace(beta_start, beta_end, num_train_timesteps, dtype=torch.float32) |
| elif beta_schedule == "scaled_linear": |
| |
| self.betas = ( |
| torch.linspace(beta_start**0.5, beta_end**0.5, num_train_timesteps, dtype=torch.float32) ** 2 |
| ) |
| elif beta_schedule == "squaredcos_cap_v2": |
| |
| self.betas = betas_for_alpha_bar(num_train_timesteps) |
| else: |
| raise NotImplementedError(f"{beta_schedule} does is not implemented for {self.__class__}") |
|
|
| self.alphas = 1.0 - self.betas |
| self.alphas_cumprod = torch.cumprod(self.alphas, dim=0) |
|
|
| |
| |
| |
| |
| self.final_alpha_cumprod = torch.tensor(1.0) if set_alpha_to_one else self.alphas_cumprod[0] |
|
|
| |
| self.init_noise_sigma = 1.0 |
|
|
| |
| self.num_inference_steps = None |
| self.timesteps = torch.from_numpy(np.arange(0, num_train_timesteps)[::-1].copy().astype(np.int64)) |
|
|
| def scale_model_input(self, sample: torch.FloatTensor, timestep: Optional[int] = None) -> torch.FloatTensor: |
| """ |
| Ensures interchangeability with schedulers that need to scale the denoising model input depending on the |
| current timestep. |
| |
| Args: |
| sample (`torch.FloatTensor`): input sample |
| timestep (`int`, optional): current timestep |
| |
| Returns: |
| `torch.FloatTensor`: scaled input sample |
| """ |
| return sample |
|
|
| def _get_variance(self, timestep, prev_timestep): |
| alpha_prod_t = self.alphas_cumprod[timestep] |
| alpha_prod_t_prev = self.alphas_cumprod[prev_timestep] if prev_timestep >= 0 else self.final_alpha_cumprod |
| beta_prod_t = 1 - alpha_prod_t |
| beta_prod_t_prev = 1 - alpha_prod_t_prev |
|
|
| variance = (beta_prod_t_prev / beta_prod_t) * (1 - alpha_prod_t / alpha_prod_t_prev) |
|
|
| return variance |
|
|
| def set_timesteps(self, num_inference_steps: int, device: Union[str, torch.device] = None): |
| """ |
| Sets the discrete timesteps used for the diffusion chain. Supporting function to be run before inference. |
| |
| Args: |
| num_inference_steps (`int`): |
| the number of diffusion steps used when generating samples with a pre-trained model. |
| """ |
|
|
| if num_inference_steps > self.config.num_train_timesteps: |
| raise ValueError( |
| f"`num_inference_steps`: {num_inference_steps} cannot be larger than `self.config.train_timesteps`:" |
| f" {self.config.num_train_timesteps} as the unet model trained with this scheduler can only handle" |
| f" maximal {self.config.num_train_timesteps} timesteps." |
| ) |
|
|
| self.num_inference_steps = num_inference_steps |
| step_ratio = self.config.num_train_timesteps // self.num_inference_steps |
| |
| |
| timesteps = (np.arange(0, num_inference_steps) * step_ratio).round()[::-1].copy().astype(np.int64) |
| self.timesteps = torch.from_numpy(timesteps).to(device) |
| self.timesteps += self.config.steps_offset |
|
|
| def step( |
| self, |
| model_output: torch.FloatTensor, |
| timestep: int, |
| sample: torch.FloatTensor, |
| eta: float = 0.0, |
| use_clipped_model_output: bool = False, |
| generator=None, |
| variance_noise: Optional[torch.FloatTensor] = None, |
| return_dict: bool = True, |
| reverse=False |
| ) -> Union[DDIMSchedulerOutput, Tuple]: |
| |
|
|
| e_t = model_output |
|
|
| x = sample |
| prev_timestep = timestep + self.config.num_train_timesteps // self.num_inference_steps |
| |
| a_t = alpha_prod_t = self.alphas_cumprod[timestep-1] |
| a_prev = alpha_t_prev = self.alphas_cumprod[prev_timestep-1] if prev_timestep >= 0 else self.final_alpha_cumprod |
| beta_prod_t = 1 - alpha_prod_t |
|
|
| pred_x0 = (x - (1-a_t)**0.5 * e_t) / a_t.sqrt() |
| |
| dir_xt = (1. - a_prev).sqrt() * e_t |
| x = a_prev.sqrt()*pred_x0 + dir_xt |
| if not return_dict: |
| return (x,) |
| return DDIMSchedulerOutput(prev_sample=x, pred_original_sample=pred_x0) |
|
|
|
|
|
|
|
|
|
|
| def add_noise( |
| self, |
| original_samples: torch.FloatTensor, |
| noise: torch.FloatTensor, |
| timesteps: torch.IntTensor, |
| ) -> torch.FloatTensor: |
| |
| self.alphas_cumprod = self.alphas_cumprod.to(device=original_samples.device, dtype=original_samples.dtype) |
| timesteps = timesteps.to(original_samples.device) |
|
|
| sqrt_alpha_prod = self.alphas_cumprod[timesteps] ** 0.5 |
| sqrt_alpha_prod = sqrt_alpha_prod.flatten() |
| while len(sqrt_alpha_prod.shape) < len(original_samples.shape): |
| sqrt_alpha_prod = sqrt_alpha_prod.unsqueeze(-1) |
|
|
| sqrt_one_minus_alpha_prod = (1 - self.alphas_cumprod[timesteps]) ** 0.5 |
| sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.flatten() |
| while len(sqrt_one_minus_alpha_prod.shape) < len(original_samples.shape): |
| sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.unsqueeze(-1) |
|
|
| noisy_samples = sqrt_alpha_prod * original_samples + sqrt_one_minus_alpha_prod * noise |
| return noisy_samples |
|
|
| def get_velocity( |
| self, sample: torch.FloatTensor, noise: torch.FloatTensor, timesteps: torch.IntTensor |
| ) -> torch.FloatTensor: |
| |
| self.alphas_cumprod = self.alphas_cumprod.to(device=sample.device, dtype=sample.dtype) |
| timesteps = timesteps.to(sample.device) |
|
|
| sqrt_alpha_prod = self.alphas_cumprod[timesteps] ** 0.5 |
| sqrt_alpha_prod = sqrt_alpha_prod.flatten() |
| while len(sqrt_alpha_prod.shape) < len(sample.shape): |
| sqrt_alpha_prod = sqrt_alpha_prod.unsqueeze(-1) |
|
|
| sqrt_one_minus_alpha_prod = (1 - self.alphas_cumprod[timesteps]) ** 0.5 |
| sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.flatten() |
| while len(sqrt_one_minus_alpha_prod.shape) < len(sample.shape): |
| sqrt_one_minus_alpha_prod = sqrt_one_minus_alpha_prod.unsqueeze(-1) |
|
|
| velocity = sqrt_alpha_prod * noise - sqrt_one_minus_alpha_prod * sample |
| return velocity |
|
|
| def __len__(self): |
| return self.config.num_train_timesteps |
|
|