mirror of
https://github.com/wassname/denoising-diffusion-pytorch.git
synced 2026-08-28 12:48:07 +08:00
152 lines
5.4 KiB
Python
152 lines
5.4 KiB
Python
import torch
|
|
from collections import namedtuple
|
|
from math import pi, sqrt, log as ln
|
|
from inspect import isfunction
|
|
from torch import nn, einsum
|
|
from einops import rearrange
|
|
|
|
from denoising_diffusion_pytorch.denoising_diffusion_pytorch import GaussianDiffusion, extract, unnormalize_to_zero_to_one
|
|
|
|
# constants
|
|
|
|
NAT = 1. / ln(2)
|
|
|
|
ModelPrediction = namedtuple('ModelPrediction', ['pred_noise', 'pred_x_start', 'pred_variance'])
|
|
|
|
# helper functions
|
|
|
|
def exists(x):
|
|
return x is not None
|
|
|
|
def default(val, d):
|
|
if exists(val):
|
|
return val
|
|
return d() if isfunction(d) else d
|
|
|
|
# tensor helpers
|
|
|
|
def log(t, eps = 1e-15):
|
|
return torch.log(t.clamp(min = eps))
|
|
|
|
def meanflat(x):
|
|
return x.mean(dim = tuple(range(1, len(x.shape))))
|
|
|
|
def normal_kl(mean1, logvar1, mean2, logvar2):
|
|
"""
|
|
KL divergence between normal distributions parameterized by mean and log-variance.
|
|
"""
|
|
return 0.5 * (-1.0 + logvar2 - logvar1 + torch.exp(logvar1 - logvar2) + ((mean1 - mean2) ** 2) * torch.exp(-logvar2))
|
|
|
|
def approx_standard_normal_cdf(x):
|
|
return 0.5 * (1.0 + torch.tanh(sqrt(2.0 / pi) * (x + 0.044715 * (x ** 3))))
|
|
|
|
def discretized_gaussian_log_likelihood(x, *, means, log_scales, thres = 0.999):
|
|
assert x.shape == means.shape == log_scales.shape
|
|
|
|
centered_x = x - means
|
|
inv_stdv = torch.exp(-log_scales)
|
|
plus_in = inv_stdv * (centered_x + 1. / 255.)
|
|
cdf_plus = approx_standard_normal_cdf(plus_in)
|
|
min_in = inv_stdv * (centered_x - 1. / 255.)
|
|
cdf_min = approx_standard_normal_cdf(min_in)
|
|
log_cdf_plus = log(cdf_plus)
|
|
log_one_minus_cdf_min = log(1. - cdf_min)
|
|
cdf_delta = cdf_plus - cdf_min
|
|
|
|
log_probs = torch.where(x < -thres,
|
|
log_cdf_plus,
|
|
torch.where(x > thres,
|
|
log_one_minus_cdf_min,
|
|
log(cdf_delta)))
|
|
|
|
return log_probs
|
|
|
|
# https://arxiv.org/abs/2102.09672
|
|
|
|
# i thought the results were questionable, if one were to focus only on FID
|
|
# but may as well get this in here for others to try, as GLIDE is using it (and DALL-E2 first stage of cascade)
|
|
# gaussian diffusion for learned variance + hybrid eps simple + vb loss
|
|
|
|
class LearnedGaussianDiffusion(GaussianDiffusion):
|
|
def __init__(
|
|
self,
|
|
model,
|
|
vb_loss_weight = 0.001, # lambda was 0.001 in the paper
|
|
*args,
|
|
**kwargs
|
|
):
|
|
super().__init__(model, *args, **kwargs)
|
|
assert model.out_dim == (model.channels * 2), 'dimension out of unet must be twice the number of channels for learned variance - you can also set the `learned_variance` keyword argument on the Unet to be `True`'
|
|
assert not model.self_condition, 'not supported yet'
|
|
|
|
self.vb_loss_weight = vb_loss_weight
|
|
|
|
def model_predictions(self, x, t):
|
|
model_output = self.model(x, t)
|
|
model_output, pred_variance = model_output.chunk(2, dim = 1)
|
|
|
|
if self.objective == 'pred_noise':
|
|
pred_noise = model_output
|
|
x_start = self.predict_start_from_noise(x, t, model_output)
|
|
|
|
elif self.objective == 'pred_x0':
|
|
pred_noise = self.predict_noise_from_start(x, t, model_output)
|
|
x_start = model_output
|
|
|
|
return ModelPrediction(pred_noise, x_start, pred_variance)
|
|
|
|
def p_mean_variance(self, *, x, t, clip_denoised, model_output = None):
|
|
model_output = default(model_output, lambda: self.model(x, t))
|
|
pred_noise, var_interp_frac_unnormalized = model_output.chunk(2, dim = 1)
|
|
|
|
min_log = extract(self.posterior_log_variance_clipped, t, x.shape)
|
|
max_log = extract(torch.log(self.betas), t, x.shape)
|
|
var_interp_frac = unnormalize_to_zero_to_one(var_interp_frac_unnormalized)
|
|
|
|
model_log_variance = var_interp_frac * max_log + (1 - var_interp_frac) * min_log
|
|
model_variance = model_log_variance.exp()
|
|
|
|
x_start = self.predict_start_from_noise(x, t, pred_noise)
|
|
|
|
if clip_denoised:
|
|
x_start.clamp_(-1., 1.)
|
|
|
|
model_mean, _, _ = self.q_posterior(x_start, x, t)
|
|
|
|
return model_mean, model_variance, model_log_variance
|
|
|
|
def p_losses(self, x_start, t, noise = None, clip_denoised = False):
|
|
noise = default(noise, lambda: torch.randn_like(x_start))
|
|
x_t = self.q_sample(x_start = x_start, t = t, noise = noise)
|
|
|
|
# model output
|
|
|
|
model_output = self.model(x_t, t)
|
|
|
|
# calculating kl loss for learned variance (interpolation)
|
|
|
|
true_mean, _, true_log_variance_clipped = self.q_posterior(x_start = x_start, x_t = x_t, t = t)
|
|
model_mean, _, model_log_variance = self.p_mean_variance(x = x_t, t = t, clip_denoised = clip_denoised, model_output = model_output)
|
|
|
|
# kl loss with detached model predicted mean, for stability reasons as in paper
|
|
|
|
detached_model_mean = model_mean.detach()
|
|
|
|
kl = normal_kl(true_mean, true_log_variance_clipped, detached_model_mean, model_log_variance)
|
|
kl = meanflat(kl) * NAT
|
|
|
|
decoder_nll = -discretized_gaussian_log_likelihood(x_start, means = detached_model_mean, log_scales = 0.5 * model_log_variance)
|
|
decoder_nll = meanflat(decoder_nll) * NAT
|
|
|
|
# at the first timestep return the decoder NLL, otherwise return KL(q(x_{t-1}|x_t,x_0) || p(x_{t-1}|x_t))
|
|
|
|
vb_losses = torch.where(t == 0, decoder_nll, kl)
|
|
|
|
# simple loss - predicting noise, x0, or x_prev
|
|
|
|
pred_noise, _ = model_output.chunk(2, dim = 1)
|
|
|
|
simple_losses = self.loss_fn(pred_noise, noise)
|
|
|
|
return simple_losses + vb_losses.mean() * self.vb_loss_weight
|