| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| """DPM-Solver++ 2S sampler from https://arxiv.org/abs/2211.01095.""" |
|
|
| from typing import Optional |
|
|
| from . import casting |
| from . import denoisers_base |
| from . import samplers_base as base |
| from . import samplers_utils as utils |
| from . import xarray_jax |
| import haiku as hk |
| import jax.numpy as jnp |
| import xarray |
|
|
|
|
| class Sampler(base.Sampler): |
| """Sampling using DPM-Solver++ 2S from [1]. |
| |
| This is combined with optional stochastic churn as described in [2]. |
| |
| The '2S' terminology from [1] means that this is a second-order (2), |
| single-step (S) solver. Here 'single-step' here distinguishes it from |
| 'multi-step' methods where the results of function evaluations from previous |
| steps are reused in computing updates for subsequent steps. The solver still |
| uses multiple steps though. |
| |
| [1] DPM-Solver++: Fast Solver for Guided Sampling of Diffusion Probabilistic |
| Models, https://arxiv.org/abs/2211.01095 |
| [2] Elucidating the Design Space of Diffusion-Based Generative Models, |
| https://arxiv.org/abs/2206.00364 |
| """ |
|
|
| def __init__(self, |
| denoiser: denoisers_base.Denoiser, |
| max_noise_level: float, |
| min_noise_level: float, |
| num_noise_levels: int, |
| rho: float, |
| stochastic_churn_rate: float, |
| churn_min_noise_level: float, |
| churn_max_noise_level: float, |
| noise_level_inflation_factor: float |
| ): |
| """Initializes the sampler. |
| |
| Args: |
| denoiser: A Denoiser which predicts noise-free targets. |
| max_noise_level: The highest noise level used at the start of the |
| sequence of reverse diffusion steps. |
| min_noise_level: The lowest noise level used at the end of the sequence of |
| reverse diffusion steps. |
| num_noise_levels: Determines the number of noise levels used and hence the |
| number of reverse diffusion steps performed. |
| rho: Parameter affecting the spacing of noise steps. Higher values will |
| concentrate noise steps more around zero. |
| stochastic_churn_rate: S_churn from the paper. This controls the rate |
| at which noise is re-injected/'churned' during the sampling algorithm. |
| If this is set to zero then we are performing deterministic sampling |
| as described in Algorithm 1. |
| churn_min_noise_level: Minimum noise level at which stochastic churn |
| occurs. S_min from the paper. Only used if stochastic_churn_rate > 0. |
| churn_max_noise_level: Maximum noise level at which stochastic churn |
| occurs. S_min from the paper. Only used if stochastic_churn_rate > 0. |
| noise_level_inflation_factor: This can be used to set the actual amount of |
| noise injected higher than what the denoiser is told has been added. |
| The motivation is to compensate for a tendency of L2-trained denoisers |
| to remove slightly too much noise / blur too much. S_noise from the |
| paper. Only used if stochastic_churn_rate > 0. |
| """ |
| super().__init__(denoiser) |
| self._noise_levels = utils.noise_schedule( |
| max_noise_level, min_noise_level, num_noise_levels, rho) |
| self._stochastic_churn = stochastic_churn_rate > 0 |
| self._per_step_churn_rates = utils.stochastic_churn_rate_schedule( |
| self._noise_levels, stochastic_churn_rate, churn_min_noise_level, |
| churn_max_noise_level) |
| self._noise_level_inflation_factor = noise_level_inflation_factor |
|
|
| def __call__( |
| self, |
| inputs: xarray.Dataset, |
| targets_template: xarray.Dataset, |
| forcings: Optional[xarray.Dataset] = None, |
| **kwargs) -> xarray.Dataset: |
|
|
| dtype = casting.infer_floating_dtype(targets_template) |
| noise_levels = jnp.array(self._noise_levels).astype(dtype) |
| per_step_churn_rates = jnp.array(self._per_step_churn_rates).astype(dtype) |
|
|
| def denoiser(noise_level: jnp.ndarray, x: xarray.Dataset) -> xarray.Dataset: |
| """Computes D(x, sigma, y).""" |
| bcast_noise_level = xarray_jax.DataArray( |
| jnp.tile(noise_level, x.sizes['batch']), dims=('batch',)) |
| |
| |
| return self._denoiser( |
| inputs=inputs, |
| noisy_targets=x, |
| noise_levels=bcast_noise_level, |
| forcings=forcings) |
|
|
| def body_fn(i: jnp.ndarray, x: xarray.Dataset) -> xarray.Dataset: |
| """One iteration of the sampling algorithm. |
| |
| Args: |
| i: Sampling iteration. |
| x: Noisy targets at iteration i, these will have noise level |
| self._noise_levels[i]. |
| |
| Returns: |
| Noisy targets at the next lowest noise level self._noise_levels[i+1]. |
| """ |
| def init_noise(template): |
| return noise_levels[0] * utils.spherical_white_noise_like(template) |
|
|
| |
| |
| |
| |
| |
| |
| maybe_init_noise = (i == 0).astype(noise_levels[0].dtype) |
| x = x + init_noise(x) * maybe_init_noise |
|
|
| noise_level = noise_levels[i] |
|
|
| if self._stochastic_churn: |
| |
| x, noise_level = utils.apply_stochastic_churn( |
| x, noise_level, |
| stochastic_churn_rate=per_step_churn_rates[i], |
| noise_level_inflation_factor=self._noise_level_inflation_factor) |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| next_noise_level = noise_levels[i + 1] |
| |
| |
| |
| mid_noise_level = jnp.sqrt(noise_level * next_noise_level) |
|
|
| mid_over_current = mid_noise_level / noise_level |
| x_denoised = denoiser(noise_level, x) |
| |
| |
| x_mid = mid_over_current * x + (1 - mid_over_current) * x_denoised |
|
|
| next_over_current = next_noise_level / noise_level |
| x_mid_denoised = denoiser(mid_noise_level, x_mid) |
| x_next = next_over_current * x + (1 - next_over_current) * x_mid_denoised |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| return utils.tree_where(next_noise_level == 0, x_denoised, x_next) |
|
|
| |
| |
| noise_init = xarray.zeros_like(targets_template) |
| return hk.fori_loop( |
| 0, len(noise_levels) - 1, body_fun=body_fn, init_val=noise_init) |
|
|