Thuta Learning
AdvancedAIintermediate

Generative Vision Models

What you'll walk away with

  • Explain the core ideas behind Generative Vision Models
  • Run the sample code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

In a generative adversarial network (GAN), two networks train simultaneously. The generator takes a random noise vector and transforms it into a 'fake' image, while the discriminator tries to tell real images from the training set apart from the generator's fakes. The two are trained as an adversarial minimax game — the generator's gradient signal comes from how well it fooled the discriminator, and the discriminator's signal comes from its classification accuracy. As training progresses, the generator gets better at producing realistic images while the discriminator gets better at spotting fakes, and in the ideal outcome the generator's output becomes indistinguishable from real data.

Connect it to a real scenario

The Tutorial Platform needs a way to flag whether an uploaded course thumbnail is a genuine screenshot or diagram versus a suspicious AI-generated synthetic image, for content-policy compliance. The Trust & Safety team adversarially trains a discriminator-style classifier that scores each upload for how 'authentic-looking' it is, routing suspiciously synthetic images to a manual review queue — the generator itself is discarded after training and only ever exists to make the discriminator a better judge.

Try the working example

python
import torch
import torch.nn as nn

torch.manual_seed(0)


class Generator(nn.Module):
    def __init__(self, noise_dim=16, image_size=32):
        super().__init__()
        self.image_size = image_size
        self.net = nn.Sequential(
            nn.Linear(noise_dim, 64),
            nn.ReLU(),
            nn.Linear(64, 3 * image_size * image_size),
            nn.Tanh(),  # output pixels squashed to [-1, 1]
        )

    def forward(self, noise):
        out = self.net(noise)
        return out.view(-1, 3, self.image_size, self.image_size)


class Discriminator(nn.Module):
    def __init__(self, image_size=32):
        super().__init__()
        self.net = nn.Sequential(
            nn.Flatten(),
            nn.Linear(3 * image_size * image_size, 64),
            nn.ReLU(),
            nn.Linear(64, 1),
            nn.Sigmoid(),  # score in [0, 1]: probability the image is "real"
        )

    def forward(self, image):
        return self.net(image)


generator = Generator()
discriminator = Discriminator()

batch_size = 4
noise = torch.randn(batch_size, 16)  # random noise vectors
print("Noise shape:", noise.shape)

fake_images = generator(noise)  # noise -> generator -> fake image
print("Fake image batch shape:", fake_images.shape)

scores = discriminator(fake_images)  # fake image -> discriminator -> real/fake score
print("Discriminator score shape:", scores.shape)
print("Discriminator scores:", scores.squeeze(1))
You should see
Prints Noise shape: torch.Size([4, 16]). Then Fake image batch shape: torch.Size([4, 3, 32, 32]) — four noise vectors turned into four RGB-shaped images. Then Discriminator score shape: torch.Size([4, 1]), followed by the squeezed Discriminator scores: tensor([...]) containing four floats between 0.0 and 1.0 (the Sigmoid output). Since neither network is trained, these scores are effectively random-ish values scattered around 0.5, and don't yet reflect any real judgment about image realism.

5-minute try-it

Modify the code to create a batch of pretend 'real' images with torch.rand(batch_size, 3, 32, 32) and pass them through the same discriminator (note that torch.rand's [0, 1] range doesn't match the generator's Tanh output range of [-1, 1]). Print and compare the discriminator's score distribution for the fake images versus the pretend-real images.

One important caution

The generator's output lives in the [-1, 1] range because of the Tanh activation; forgetting to rescale it to [0, 1] or [0, 255] before treating it as displayable image pixels produces washed-out or inverted-looking images.

It's tempting to read a discriminator score near 0.5 as 'the generator is fooling the discriminator,' but with untrained, randomly initialized networks those scores are just near-random noise, not a meaningful judgment about image realism — early scores don't tell you anything about generation quality yet.

Wikipedia — Generative adversarial networkComputer Vision

Easy traps

  • The generator's output lives in the [-1, 1] range because of the Tanh activation; forgetting to rescale it to [0, 1] or [0, 255] before treating it as displayable image pixels produces washed-out or inverted-looking images.
  • It's tempting to read a discriminator score near 0.5 as 'the generator is fooling the discriminator,' but with untrained, randomly initialized networks those scores are just near-random noise, not a meaningful judgment about image realism — early scores don't tell you anything about generation quality yet.
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Modify the code to create a batch of pretend 'real' images with torch.rand(batch_size, 3, 32, 32) and pass them through the same discriminator (note that torch.rand's [0, 1] range doesn't match the generator's Tanh output range of [-1, 1]). Print and compare the discriminator's score distribution for the fake images versus the pretend-real images.

You'll know it worked when: Prints Noise shape: torch.Size([4, 16]). Then Fake image batch shape: torch.Size([4, 3, 32, 32]) — four noise vectors turned into four RGB-shaped images. Then Discriminator score shape: torch.Size([4, 1]), followed by the squeezed Discriminator scores: tensor([...]) containing four floats between 0.0 and 1.0 (the Sigmoid output). Since neither network is trained, these scores are effectively random-ish values scattered around 0.5, and don't yet reflect any real judgment about image realism.

Generative Vision Models | Thuta Learning