Spaces:
Running on Zero
Running on Zero
| import torch | |
| import numpy as np | |
| import utils.training_utils as utils | |
| class EDM(): | |
| """ | |
| Definition of most of the diffusion parameterization, following ( Karras et al., "Elucidating...", 2022) | |
| """ | |
| def __init__(self, args): | |
| """ | |
| Args: | |
| args (dictionary): hydra arguments | |
| sigma_data (float): | |
| """ | |
| self.args=args | |
| self.sigma_min = args.diff_params.sigma_min | |
| self.sigma_max =args.diff_params.sigma_max | |
| self.P_mean=args.diff_params.P_mean | |
| self.P_std=args.diff_params.P_std | |
| self.ro=args.diff_params.ro | |
| self.ro_train=args.diff_params.ro_train | |
| self.sigma_data=args.diff_params.sigma_data #depends on the training data!! precalculated variance of the dataset | |
| #parameters stochastic sampling | |
| self.Schurn=args.diff_params.Schurn | |
| self.Stmin=args.diff_params.Stmin | |
| self.Stmax=args.diff_params.Stmax | |
| self.Snoise=args.diff_params.Snoise | |
| #perceptual filter | |
| if self.args.diff_params.aweighting.use_aweighting: | |
| self.AW=utils.FIRFilter(filter_type="aw", fs=args.exp.sample_rate, ntaps=self.args.diff_params.aweighting.ntaps) | |
| def get_gamma(self, t): | |
| """ | |
| Get the parameter gamma that defines the stochasticity of the sampler | |
| Args | |
| t (Tensor): shape: (N_steps, ) Tensor of timesteps, from which we will compute gamma | |
| """ | |
| N=t.shape[0] | |
| gamma=torch.zeros(t.shape).to(t.device) | |
| #If desired, only apply stochasticity between a certain range of noises Stmin is 0 by default and Stmax is a huge number by default. (Unless these parameters are specified, this does nothing) | |
| indexes=torch.logical_and(t>self.Stmin , t<self.Stmax) | |
| #We use Schurn=5 as the default in our experiments | |
| gamma[indexes]=gamma[indexes]+torch.min(torch.Tensor([self.Schurn/N, 2**(1/2) -1])) | |
| return gamma | |
| def create_schedule(self,nb_steps): | |
| """ | |
| Define the schedule of timesteps | |
| Args: | |
| nb_steps (int): Number of discretized steps | |
| """ | |
| i=torch.arange(0,nb_steps+1) | |
| t=(self.sigma_max**(1/self.ro) +i/(nb_steps-1) *(self.sigma_min**(1/self.ro) - self.sigma_max**(1/self.ro)))**self.ro | |
| t[-1]=0 | |
| return t | |
| def sample_ptrain(self,N): | |
| """ | |
| For training, getting t as a normal distribution, folowing Karras et al. | |
| I'm not using this | |
| Args: | |
| N (int): batch size | |
| """ | |
| lnsigma=np.random.randn(N)*self.P_std +self.P_mean | |
| return np.clip(np.exp(lnsigma),self.sigma_min, self.sigma_max) #not sure if clipping here is necessary, but makes sense to me | |
| def sample_ptrain_safe(self,N): | |
| """ | |
| For training, getting t according to the same criteria as sampling | |
| Args: | |
| N (int): batch size | |
| """ | |
| a=torch.rand(N) | |
| t=(self.sigma_max**(1/self.ro_train) +a *(self.sigma_min**(1/self.ro_train) - self.sigma_max**(1/self.ro_train)))**self.ro_train | |
| return t | |
| def sample_prior(self,shape,sigma): | |
| """ | |
| Just sample some gaussian noise, nothing more | |
| Args: | |
| shape (tuple): shape of the noise to sample, something like (B,T) | |
| sigma (float): noise level of the noise | |
| """ | |
| n=torch.randn(shape).to(sigma.device)*sigma | |
| return n | |
| def cskip(self, sigma): | |
| """ | |
| Just one of the preconditioning parameters | |
| Args: | |
| sigma (float): noise level (equal to timestep is sigma=t, which is our default) | |
| """ | |
| return self.sigma_data**2 *(sigma**2+self.sigma_data**2)**-1 | |
| def cout(self,sigma ): | |
| """ | |
| Just one of the preconditioning parameters | |
| Args: | |
| sigma (float): noise level (equal to timestep is sigma=t, which is our default) | |
| """ | |
| return sigma*self.sigma_data* (self.sigma_data**2+sigma**2)**(-0.5) | |
| def cin(self, sigma): | |
| """ | |
| Just one of the preconditioning parameters | |
| Args: | |
| sigma (float): noise level (equal to timestep is sigma=t, which is our default) | |
| """ | |
| return (self.sigma_data**2+sigma**2)**(-0.5) | |
| def cnoise(self,sigma ): | |
| """ | |
| preconditioning of the noise embedding | |
| Args: | |
| sigma (float): noise level (equal to timestep is sigma=t, which is our default) | |
| """ | |
| return (1/4)*torch.log(sigma) | |
| def lambda_w(self,sigma): | |
| return (sigma*self.sigma_data)**(-2) * (self.sigma_data**2+sigma**2) | |
| def denoiser(self, xn , net, sigma): | |
| """ | |
| This method does the whole denoising step, which implies applying the model and the preconditioning | |
| Args: | |
| x (Tensor): shape: (B,T) Intermediate noisy latent to denoise | |
| model (nn.Module): Model of the denoiser | |
| sigma (float): noise level (equal to timestep is sigma=t, which is our default) | |
| """ | |
| if len(sigma.shape)==1: | |
| sigma=sigma.unsqueeze(-1) | |
| cskip=self.cskip(sigma) | |
| cout=self.cout(sigma) | |
| cin=self.cin(sigma) | |
| cnoise=self.cnoise(sigma) | |
| return cskip * xn +cout*net(cin*xn, cnoise) #this will crash because of broadcasting problems, debug later! | |
| def prepare_train_preconditioning(self, x, sigma): | |
| #weight=self.lambda_w(sigma) | |
| #Is calling the denoiser here a good idea? Maybe it would be better to apply directly the preconditioning as in the paper, even though Karras et al seem to do it this way in their code | |
| print(x.shape) | |
| noise=self.sample_prior(x.shape,sigma) | |
| cskip=self.cskip(sigma) | |
| cout=self.cout(sigma) | |
| cin=self.cin(sigma) | |
| cnoise=self.cnoise(sigma) | |
| target=(1/cout)*(x-cskip*(x+noise)) | |
| return cin*(x+noise), target, cnoise | |
| def loss_fn(self, net, x): | |
| """ | |
| Loss function, which is the mean squared error between the denoised latent and the clean latent | |
| Args: | |
| net (nn.Module): Model of the denoiser | |
| x (Tensor): shape: (B,T) Intermediate noisy latent to denoise | |
| sigma (float): noise level (equal to timestep is sigma=t, which is our default) | |
| """ | |
| sigma=self.sample_ptrain_safe(x.shape[0]).unsqueeze(-1).to(x.device) | |
| input, target, cnoise= self.prepare_train_preconditioning(x, sigma) | |
| estimate=net(input,cnoise) | |
| error=(estimate-target) | |
| try: | |
| #this will only happen if the model is cqt-based, if it crashes it is normal | |
| if self.args.net.use_cqt_DC_correction: | |
| error=net.CQTransform.apply_hpf_DC(error) #apply the DC correction to the error as we dont want to propagate the DC component of the error as the network is discarding it. It also applies for the nyquit frequency, but this is less critical. | |
| except: | |
| pass | |
| #APPLY A-WEIGHTING | |
| if self.args.diff_params.aweighting.use_aweighting: | |
| error=self.AW(error) | |
| #here we have the chance to apply further emphasis to the error, as some kind of perceptual frequency weighting could be | |
| return error**2, sigma | |