A diffusion model trained from scratch to generate
class-conditioned images from the
Fashion MNIST dataset. Built on top of
diffusers'
UNet2DModel, it accepts a class label (0–9) at inference time and generates the corresponding clothing category.
This wrapper must be defined before loading the model, as the pretrained weights cover only the inner UNet2DModel.
1import torch
2import torch.nn as nn
3from diffusers import UNet2DModel
4
5class ClassConditionedUnet(nn.Module):
6 def __init__(self, num_classes=10, class_emb_size=4):
7 super().__init__()
8 self.class_emb = nn.Embedding(num_classes, class_emb_size)
9 self.model = UNet2DModel(
10 sample_size=28,
11 in_channels=1 + class_emb_size, # image + class embedding
12 out_channels=1,
13 layers_per_block=2,
14 block_out_channels=(64, 128, 256),
15 down_block_types=("DownBlock2D", "AttnDownBlock2D", "AttnDownBlock2D"),
16 up_block_types=("AttnUpBlock2D", "AttnUpBlock2D", "UpBlock2D"),
17 )
18
19 def forward(self, x, t, class_labels):
20 bs, ch, w, h = x.shape
21 # Embed class labels and broadcast to spatial dimensions
22 class_cond = self.class_emb(class_labels) # (bs, emb_size)
23 class_cond = class_cond.view(bs, -1, 1, 1).expand(bs, -1, w, h) # (bs, emb_size, w, h)
24 net_input = torch.cat((x, class_cond), dim=1) # (bs, 1+emb_size, w, h)
25 return self.model(net_input, t).sample
1device = "cuda" if torch.cuda.is_available() else "cpu"
2
3net = ClassConditionedUnet(num_classes=10, class_emb_size=4)
4net.model = UNet2DModel.from_pretrained(
5 "andreagemelli/UNet2DModel-fashion_mnist",
6 use_safetensors=True,
7)
8net = net.to(device)
9net.eval()
The snippet below generates 8 samples per class (80 images total) and displays them in a grid.
1import torchvision
2from diffusers import DDPMScheduler
3from matplotlib import pyplot as plt
4from tqdm.auto import tqdm
5
6# 8 samples per class, all 10 classes → 80 images
7n_per_class = 8
8x = torch.randn(n_per_class * 10, 1, 28, 28).to(device)
9y = torch.tensor([[i] * n_per_class for i in range(10)]).flatten().to(device)
10
11# Scheduler
12noise_scheduler = DDPMScheduler(num_train_timesteps=1000, beta_schedule="squaredcos_cap_v2")
13noise_scheduler.set_timesteps(1000)
14
15# Reverse diffusion loop
16for t in tqdm(noise_scheduler.timesteps, desc="Sampling"):
17 with torch.no_grad():
18 residual = net(x, t, y)
19 x = noise_scheduler.step(residual, t, x).prev_sample
20
21# Visualize
22grid = torchvision.utils.make_grid(x.detach().cpu().clip(-1, 1), nrow=n_per_class, normalize=True)
23fig, ax = plt.subplots(figsize=(12, 6))
24ax.imshow(grid.permute(1, 2, 0), cmap="Greys")
25ax.set_title("Generated Fashion MNIST Images (rows = classes 0–9)")
26ax.axis("off")
27plt.tight_layout()
28plt.show()