-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils_diffusion.py
More file actions
180 lines (159 loc) · 6.04 KB
/
Copy pathutils_diffusion.py
File metadata and controls
180 lines (159 loc) · 6.04 KB
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
import jax.numpy as jnp
import numpy as np
import jax
from jax import grad, vmap, jit, jacrev
from functools import partial
import jax.random as random
#from jax.experimental import optimizers
import jax.example_libraries.optimizers as optimizers
from jax.scipy.optimize import minimize
from jax.lax import scan
from jax.nn import softplus
from jax.config import config
from jax.flatten_util import ravel_pytree
import optax
import flax.linen as nn
config.update("jax_enable_x64", True)
beta_min = 0.001
beta_max = 3
def beta_t(t):
"""
t: time (number)
returns beta_t as explained above
"""
return beta_min + t*(beta_max - beta_min)
def alpha_t(t):
"""
t: time (number)
returns alpha_t as explained above
"""
return t*beta_min + 0.5 * t**2 * (beta_max - beta_min)
def drift(x, t):
"""
x: location of J particles in N dimensions, shape (J, N)
t: time (number)
returns the drift of a time-changed OU-process for each batch member, shape (J, N)
"""
return -0.5*beta_t(t)*x
def dispersion(t):
"""
t: time (number)
returns the dispersion
"""
return jnp.sqrt(beta_t(t))
def mean_factor(t):
"""
t: time (number)
returns m_t as above
"""
return jnp.exp(-0.5 * alpha_t(t))
def var(t):
"""
t: time (number)
returns v_t as above
"""
return 1 - jnp.exp(-alpha_t(t))
R = 1000
train_ts = jnp.arange(1, R)/(R-1)
#we jit the function, but we have to mark some of the arguments as static,
#which means the function is recompiled every time these arguments are changed,
#since they are directly compiled into the binary code. This is necessary
#since jitted-functions cannot have functions as arguments. But it also
#no problem since these arguments will never/rarely change in our case,
#therefore not triggering re-compilation.
@partial(jit, static_argnums=[1,2,3,4,5])
def reverse_sde(rng, N, n_samples, forward_drift, dispersion, score, ts=train_ts):
"""
rng: random number generator (JAX rng)
N: dimension in which the reverse SDE runs
N_initial: How many samples from the initial distribution N(0, I), number
forward_drift: drift function of the forward SDE (we implemented it above)
disperion: dispersion function of the forward SDE (we implemented it above)
score: The score function to use as additional drift in the reverse SDE
ts: a discretization {t_i} of [0, T], shape 1d-array
"""
def f(carry, params):
t, dt = params
x, rng = carry
rng, step_rng = jax.random.split(rng)
disp = dispersion(1-t)
t = jnp.ones((x.shape[0], 1)) * t
drift = -forward_drift(x, 1-t) + disp**2 * score(x, 1-t)
noise = random.normal(step_rng, x.shape)
x = x + dt * drift + jnp.sqrt(dt)*disp*noise
return (x, rng), ()
rng, step_rng = random.split(rng)
initial = random.normal(step_rng, (n_samples, N))
dts = ts[1:] - ts[:-1]
params = jnp.stack([ts[:-1], dts], axis=1)
(x, _), _ = scan(f, (initial, rng), params)
return x
class ApproximateScore(nn.Module):
"""A simple model with multiple fully connected layers and some fourier features for the time variable."""
@nn.compact
def __call__(self, x, t):
in_size = x.shape[1]
n_hidden = 256
act = nn.relu
t = jnp.concatenate([t - 0.5, jnp.cos(2*jnp.pi*t)],axis=1)
x = jnp.concatenate([x, t],axis=1)
x = nn.Dense(n_hidden)(x)
x = nn.relu(x)
x = nn.Dense(n_hidden)(x)
x = nn.relu(x)
x = nn.Dense(n_hidden)(x)
x = nn.relu(x)
x = nn.Dense(in_size)(x)
return x
def loss_fn(params, model, rng, batch):
"""
params: the current weights of the model
model: the score function
rng: random number generator from jax
batch: a batch of samples from the training data, representing samples from \mu_text{data}, shape (J, N)
returns an random (MC) approximation to the loss \bar{L} explained above
"""
rng, step_rng = random.split(rng)
N_batch = batch.shape[0]
t = random.randint(step_rng, (N_batch,1), 1, R)/(R-1)
mean_coeff = mean_factor(t)
#is it right to have the square root here for the loss?
vs = var(t)
stds = jnp.sqrt(vs)
rng, step_rng = random.split(rng)
noise = random.normal(step_rng, batch.shape)
xt = batch * mean_coeff + noise * stds
output = model.apply(params, xt, t)
loss = jnp.mean((noise + output*vs)**2)
return loss
@partial(jit, static_argnums=(4,5,))
def update_step(params, rng, batch, opt_state, model, optimizer):
"""
params: the current weights of the model
rng: random number generator from jax
batch: a batch of samples from the training data, representing samples from \mu_text{data}, shape (J, N)
opt_state: the internal state of the optimizer
model: the score function
takes the gradient of the loss function and updates the model weights (params) using it. Returns
the value of the loss function (for metrics), the new params and the new optimizer state
"""
val, grads = jax.value_and_grad(loss_fn)(params, model, rng, batch)
updates, opt_state = optimizer.update(grads, opt_state)
params = optax.apply_updates(params, updates)
return val, params, opt_state
def train_diffusion(inputs, model, N_epochs, train_size, batch_size, steps_per_epoch, rng, params, optimizer, opt_state): # model is score_model
for k in range(N_epochs):
rng, step_rng = random.split(rng)
perms = jax.random.permutation(step_rng, train_size)
perms = perms[:steps_per_epoch * batch_size] # skip incomplete batch
perms = perms.reshape((steps_per_epoch, batch_size))
losses = []
for perm in perms:
batch = inputs[perm, :] #this is supposed to be
rng, step_rng = random.split(rng)
loss, params, opt_state = update_step(params, step_rng, batch, opt_state, model, optimizer)
losses.append(loss)
mean_loss = jnp.mean(np.array(losses))
if k % 1000 == 0:
print("Epoch %d \t, Loss %f " % (k, mean_loss))
return params