key = jax.random.PRNGKey(0)
mix = d2l.GaussianMixture(means=[[-2.5, -1.5], [2.5, -1.5], [0.0, 2.5]],
weights=[0.5, 0.3, 0.2], std=0.5)
key, k1, k2 = jax.random.split(key, 3)
data, test = mix.sample(k1, 2000), mix.sample(k2, 2000)
def denoising_loss(score, x, eps, sigma):
return 0.5 * ((score(x + sigma * eps) + eps / sigma) ** 2).sum(1).mean()
def fisher_divergence(score, x, sigma=0.0):
"""0.5 E||s(x) - grad log p_sigma(x)||^2 on the given points."""
return float(0.5 * ((score(x) - mix.score(x, sigma=sigma)) ** 2).sum(1).mean())
@nnx.jit
def dsm_step(score, optimizer, x, eps, sigma):
loss, grads = nnx.value_and_grad(denoising_loss)(score, x, eps, sigma)
optimizer.update(score, grads)
return loss
def train(sigma, key, steps=2000, lr=1e-3, seed=1):
score = d2l.ScoreNet(rngs=nnx.Rngs(seed))
optimizer = nnx.Optimizer(score, optax.adam(lr), wrt=nnx.Param)
for step in range(steps):
key, k1, k2 = jax.random.split(key, 3)
x = data[jax.random.randint(k1, (256,), 0, len(data))]
dsm_step(score, optimizer, x, jax.random.normal(k2, x.shape), sigma)
return score
scores = {}
print(f'{"sigma":>5} {"vs. score of p_sigma":>21} {"vs. score of the data":>22}')
for sigma in (0.1, 0.3, 1.0):
key, k1, k2 = jax.random.split(key, 3)
scores[sigma] = train(sigma, k1)
noisy_test = test + sigma * jax.random.normal(k2, test.shape)
print(f'{sigma:>5} {fisher_divergence(scores[sigma], noisy_test, sigma):>21.3f} '
f'{fisher_divergence(scores[sigma], test):>22.3f}')