torch.manual_seed(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)
data, test = mix.sample(2000), mix.sample(2000)
def denoising_loss(score, x, sigma):
eps = torch.randn_like(x)
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."""
with torch.no_grad():
return 0.5 * ((score(x) - mix.score(x, sigma=sigma)) ** 2).sum(1).mean().item()
def train(loss_fn, steps=2000, lr=1e-3, seed=1):
torch.manual_seed(seed)
score = d2l.ScoreNet()
optimizer = torch.optim.Adam(score.parameters(), lr=lr)
for step in range(steps):
x = data[torch.randint(0, len(data), (256,))]
loss = loss_fn(score, x)
optimizer.zero_grad(), loss.backward(), optimizer.step()
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):
scores[sigma] = train(lambda s, x: denoising_loss(s, x, sigma))
noisy_test = test + sigma * torch.randn_like(test)
print(f'{sigma:>5} {fisher_divergence(scores[sigma], noisy_test, sigma):>21.3f} '
f'{fisher_divergence(scores[sigma], test):>22.3f}')