from ipywidgets import IntSlider, Output, VBox, Label
def discriminator_at(cp):
# A discriminator with the weights it had at checkpoint `cp`
# (spectral normalization stores its weights under the name 'weight_orig')
spectral = any(k.endswith('weight_orig') for k in cp["d_state_dict"])
# fork_rng keeps the new network's random initialization from changing the random numbers of later cells
with torch.random.fork_rng():
D_cp = build_discriminator(hidden_dim=256, spectral_normalization=spectral)
D_cp.load_state_dict({k: v.to(device) for k, v in cp["d_state_dict"].items()})
# We keep D_cp in training mode, so spectral normalization refines its estimate of each layer's
# largest singular value on every call, just as it does during training
return D_cp
def plot_discriminator_map(D_net, fake_np, ax=None, lim=None, title='', X_real=None):
# D's logit over the data space (red: D says "real", blue: D says "fake", black line: logit 0),
# the real data in gray and the generator's samples in orange
if X_real is None:
X_real = X_ring
if ax is None:
fig, ax = plt.subplots(figsize=(6.5, 5.5))
if lim is None:
# Show at least [-5, 5] x [-5, 5], and widen the window if the generator's samples lie outside it
lim = max(5.0, 1.1 * np.percentile(np.abs(fake_np).max(axis=1), 99))
grid_1d = np.linspace(-lim, lim, 201)
xx, yy = np.meshgrid(grid_1d, grid_1d)
grid = torch.tensor(np.stack([xx.ravel(), yy.ravel()], axis=1), dtype=torch.float32, device=device)
with torch.no_grad():
logit = D_net(grid).cpu().numpy().reshape(xx.shape)
v = np.abs(logit).max()
im = ax.contourf(xx, yy, logit, levels=np.linspace(-v, v, 31), cmap='RdBu_r')
ax.contour(xx, yy, logit, levels=[0], colors='k', linewidths=0.8)
ax.scatter(X_real[:800, 0], X_real[:800, 1], s=4, color='0.35', alpha=0.4, label='Real')
ax.scatter(fake_np[:500, 0], fake_np[:500, 1], s=6, color='tab:orange', label='Generated')
ax.set_xlim(-lim, lim)
ax.set_ylim(-lim, lim)
ax.set_aspect('equal')
ax.set_xlabel('x1')
ax.set_ylabel('x2')
ax.set_title(title, fontsize=11)
plt.colorbar(im, ax=ax, fraction=0.046, label='Discriminator logit', ticks=np.linspace(-v, v, 5), format='%.3g')
return ax
def plot_discriminator_strip(H, epochs, title=''):
# Discriminator maps at six checkpoints (two rows of three), all with the same window
cps = [cp for cp in H.snapshots if cp["epoch"] in epochs]
lim = max(max(5.0, 1.1 * np.percentile(np.abs(cp["samples"]).max(axis=1), 99)) for cp in cps)
fig, axes = plt.subplots(2, 3, figsize=(14, 9))
axes = axes.ravel()
for ax, cp in zip(axes, cps):
plot_discriminator_map(discriminator_at(cp), cp["samples"], ax=ax, lim=lim,
title=f"Epoch {cp['epoch']}")
axes[0].legend(loc='upper left', fontsize=8)
fig.suptitle(title)
plt.tight_layout()
plt.show()
def checkpoint_browser(H):
# Slider over the snapshots stored in history H
out = Output()
def show_checkpoint(idx: int):
cp = H.snapshots[idx]
# Load generator state into a temporary generator instance
G_temp = build_generator(noise_dim=2, hidden_dim=256)
# load state dict (ensure tensors are moved to device)
state = {k: v.to(device) for k, v in cp["state_dict"].items()}
G_temp.load_state_dict(state)
with out:
out.clear_output(wait=True)
# Plot diagnostics, the discriminator map and latent interpolation for this checkpoint
print(f"Checkpoint: index={idx} | epoch={cp['epoch']}")
plot_model_diagnostics(H, X_ring, G_temp, noise_dim=2, title_suffix=f" (Checkpoint ep {cp['epoch']})")
plot_discriminator_map(discriminator_at(cp), cp["samples"],
title=f"Discriminator logit (Checkpoint ep {cp['epoch']})")
plt.show()
plot_latent_interpolation(G_temp, noise_dim=2, title_suffix=f" (Checkpoint ep {cp['epoch']})", real_samples=X_ring, latent_limits=ring_latent_limits, data_limits=ring_data_limits)
slider = IntSlider(value=0, min=0, max=len(H.snapshots) - 1, step=1, description='Checkpoint')
def on_change(change):
if change['name'] == 'value':
show_checkpoint(change['new'])
slider.observe(on_change)
display(VBox([Label('Checkpoint browser'), slider, out]))
# show initial
show_checkpoint(0)