File size: 8,255 Bytes
a95f6c0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
from tqdm import tqdm
import torch
import numpy as np
from nara_wpe.wpe import wpe
from nara_wpe.utils import stft, istft
from utils.losses import get_loss

from testing.EulerHeunSampler import EulerHeunSampler

class EulerHeunSamplerDPS(EulerHeunSampler):
    """
        Euler Heun sampler for DPS 
        inverse problem solver
    """

    def __init__(self, model, diff_params, args):
        super().__init__(model, diff_params, args)
        self.zeta = self.args.tester.posterior_sampling.zeta

    def initialize_x(self, shape, device, schedule):
        if self.args.tester.posterior_sampling.warm_initialization.mode == "none":
            x = schedule[0]*torch.randn(shape).to(device)

        elif self.args.tester.posterior_sampling.warm_initialization.mode == "reverb_scaled":
            x = self.args.tester.posterior_sampling.warm_initialization.scaling_factor * self.y.clone() / self.y.std() + schedule[0] * torch.randn(shape).to(device)
        
        elif self.args.tester.posterior_sampling.warm_initialization.mode == "wpe_scaled":
            print("Processing WPE")
            stft_options = dict(size=512, shift=128)

            delay = self.args.tester.posterior_sampling.warm_initialization.wpe.delay
            iterations = self.args.tester.posterior_sampling.warm_initialization.wpe.iterations
            taps = self.args.tester.posterior_sampling.warm_initialization.wpe.taps
            
            Y = stft(self.y.cpu().numpy(), **stft_options)
            Y = Y.transpose(2, 0, 1)
            Z = wpe(
                Y,
                taps=taps,
                delay=delay,
                iterations=iterations,
                statistics_mode='full'
            ).transpose(1, 2, 0)
            x_pred = torch.from_numpy(istft(Z, size=stft_options['size'], shift=stft_options['shift'])).to(self.y.device).type(self.y.dtype)
            if x_pred.shape[-1] > self.y.shape[-1]:
                x_pred = x_pred[..., :self.y.shape[-1]]

            x_pred = self.args.tester.posterior_sampling.warm_initialization.scaling_factor * x_pred / x_pred.std()
            x = x_pred + schedule[0] * torch.randn(shape).to(device)

        else:
            raise NotImplementedError
        
        return x
    
    def get_likelihood_score(self, x_den, x, t):

        y_hat = self.operator.degradation(x_den, mode="waveform")
        rec = self.rec_loss(self.y, y_hat)
        rec_grads = torch.autograd.grad(outputs=rec, inputs=x)[0]

        # Normalize weighting parameter zeta
        normguide = torch.norm(rec_grads)/(self.args.exp.audio_len**0.5)
        return self.zeta / (normguide+1e-8) * rec_grads, rec
        
    def optimize_op(self, x_den, t):
        """
        Optimize the operator parameters
        """

        for _ in range(self.args.tester.posterior_sampling.blind_hp.op_updates_per_step):

            for k in range(len(self.operator.params)):
                self.operator.params[k].requires_grad=True
            for k in range(len(self.operator.params_phases)):
                self.operator.params_phases[k].requires_grad=True

            self.operator.update_H()

            # Reconstruction loss
            y_hat = self.operator.degradation(x_den, mode="waveform")
            if self.rec_loss_params is not None:
                rec_loss = self.rec_loss_params(self.y, y_hat)
                loss = rec_loss
                assert (torch.isnan(rec_loss).any()==False), f"rec_loss is Nan"
            else:
                loss = 0.

            # RIR noise regularization
            if self.RIR_noise_regularization_loss is not None:
                rir_time = self.operator.get_time_RIR()
                rir_noise = torch.randn_like(rir_time).to(x_den.device)
                t_op = max(min(t, self.args.tester.posterior_sampling.RIR_noise_regularization.crop_sigma_max), self.args.tester.posterior_sampling.RIR_noise_regularization.crop_sigma_min)
                rir_noisy = rir_time + t_op * rir_noise
                reg_loss = self.RIR_noise_regularization_loss(rir_time, rir_noisy.detach()) #detach gradients so that we do not backpropagate through the RIR operator
                loss += reg_loss

            assert (torch.isnan(loss).any()==False), f"loss is Nan"

            self.optimizer_operator.zero_grad()
            loss.backward()
            self.optimizer_operator.step()

            for p in self.operator.params:
                p.detach_()
            self.operator.project_params()
            for p in self.operator.params:
                p.requires_grad=True

    def step(self, x_i, t_i, t_iplus1, gamma_i, blind=False):

        x_hat, t_hat = self.stochastic_timestep(x_i, t_i, gamma_i)
        x_hat.requires_grad = True
        x_den = self.get_Tweedie_estimate(x_hat, t_hat)

        if blind:
            self.optimize_op(x_den.clone().detach(), t_hat)

        lh_score, rec_loss_value = self.get_likelihood_score(x_den, x_hat, t_hat)
        x_hat.detach_()

        # Rescale denoised speech estimate magnitude to constraint absolute magnitudes of RIR / speech estimate
        if self.args.tester.posterior_sampling.constraint_speech_magnitude.use:
            x_den = self.args.tester.posterior_sampling.constraint_speech_magnitude.speech_scaling / x_den.detach().std() * x_den #Match the sigma_data of dataset

        score = self.Tweedie2score(x_den, x_hat, t_hat)

        ode_integrand = self.diff_params._ode_integrand(x_hat, t_hat, score) + lh_score
        dt = t_iplus1 - t_hat

        if t_iplus1 !=0 and self.order == 2: #second order correction
            t_prime = t_iplus1
            x_prime = x_hat + dt * ode_integrand
            x_prime.requires_grad_(True)
            x_den = self.get_Tweedie_estimate(x_prime, t_prime)

            if blind:
                self.optimize_op(x_den.clone().detach(), t_prime)

            lh_score_next, rec_loss_value = self.get_likelihood_score(x_den, x_prime, t_prime)
            x_prime.detach_()

            score = self.Tweedie2score(x_den, x_prime, t_prime)

            ode_integrand_next = self.diff_params._ode_integrand(x_prime, t_prime, score) + lh_score_next
            ode_integrand_midpoint = .5 * (ode_integrand + ode_integrand_next)
            x_iplus1 = x_hat + dt * ode_integrand_midpoint
            
        else:
            x_iplus1 = x_hat + dt * ode_integrand

        return x_iplus1.detach_(), x_den.detach()

    def predict(
        self,
        shape, 
        device,
        blind=False
    ):
        # get the noise schedule
        t = self.create_schedule().to(device)

        # sample prior
        x = self.initialize_x(shape,device, t)

        # parameter for langevin stochasticity, if Schurn is 0, gamma will be 0 to, so the sampler will be deterministic
        gamma = self.get_gamma(t).to(device)

        for i in tqdm(range(0, self.T, 1)):
            self.step_counter=i
            x, x_den = self.step(x, t[i] , t[i+1], gamma[i], blind)
            
        return x_den.detach()
    
    def predict_unconditional(self, *args, **kwargs):
        raise ValueError("DPS not made for unconditional sampling")

    def predict_conditional(
        self,
        y,  #observations 
        operator, #degradation operator (assuming we define it in the tester)
        shape=None,
        blind=False,
        **kwargs
    ):

        self.operator = operator
        self.y = y
        self.rec_loss = get_loss(self.args.tester.posterior_sampling.rec_loss, operator=self.operator)

        if blind:
            self.rec_loss_params = get_loss(self.args.tester.posterior_sampling.rec_loss_params, operator=self.operator)
            self.optimizer_operator = torch.optim.Adam(self.operator.params + self.operator.params_phases, lr=self.args.tester.posterior_sampling.blind_hp.lr_op, weight_decay=self.args.tester.posterior_sampling.blind_hp.weight_decay, betas=(self.args.tester.posterior_sampling.blind_hp.beta1, self.args.tester.posterior_sampling.blind_hp.beta2))
            self.RIR_noise_regularization_loss = get_loss(self.args.tester.posterior_sampling.RIR_noise_regularization.loss, operator=self.operator)

        if shape is None:
            shape = y.shape

        return self.predict(shape, y.device, blind)