| name | gan-computer-vision |
| description | GANs pour la vision par ordinateur — StyleGAN, CycleGAN, pix2pix, SRGAN, DCGAN, WGAN, GigaGAN, GANs conditionnels, super-résolution, inpainting, translation. En français. |
GANs pour la Vision par Ordinateur
Generative Adversarial Networks : deux réseaux en compétition (Générateur vs Discriminateur). Utilisés pour la génération d'images, super-résolution, translation, inpainting, et bien plus.
1. Fondamentaux GANs
Loss Functions
import torch
import torch.nn as nn
import torch.nn.functional as F
def gan_loss(disc_real, disc_fake):
bce = nn.BCEWithLogitsLoss()
real_loss = bce(disc_real, torch.ones_like(disc_real))
fake_loss = bce(disc_fake, torch.zeros_like(disc_fake))
return real_loss + fake_loss
def wgan_gp_loss(disc_real, disc_fake, interpolates, gradient_penalty_weight=10.0):
d_loss = disc_fake.mean() - disc_real.mean()
g_loss = -disc_fake.mean()
gradients = torch.autograd.grad(
outputs=disc_interpolates,
inputs=interpolates,
grad_outputs=torch.ones_like(disc_interpolates),
create_graph=True,
retain_graph=True,
)[0]
gradients = gradients.view(gradients.size(0), -1)
gradient_penalty = ((gradients.norm(2, dim=1) - 1) ** 2).mean() * gradient_penalty_weight
d_loss += gradient_penalty
return d_loss, g_loss
def lsgan_loss(disc_real, disc_fake):
d_loss = 0.5 * (F.mse_loss(disc_real, torch.ones_like(disc_real)) +
F.mse_loss(disc_fake, torch.zeros_like(disc_fake)))
g_loss = 0.5 * F.mse_loss(disc_fake, torch.ones_like(disc_fake))
return d_loss, g_loss
def hinge_loss(disc_real, disc_fake):
d_loss = F.relu(1.0 - disc_real).mean() + F.relu(1.0 + disc_fake).mean()
g_loss = -disc_fake.mean()
return d_loss, g_loss
Générateur DCGAN
class Generator(nn.Module):
"""DCGAN Generator : latent → image 64×64"""
def __init__(self, latent_dim=100, channels=3):
super().__init__()
self.model = nn.Sequential(
nn.ConvTranspose2d(latent_dim, 1024, 4, 1, 0, bias=False),
nn.BatchNorm2d(1024),
nn.ReLU(True),
nn.ConvTranspose2d(1024, 512, 4, 2, 1, bias=False),
nn.BatchNorm2d(512),
nn.ReLU(True),
nn.ConvTranspose2d(512, 256, 4, 2, 1, bias=False),
nn.BatchNorm2d(256),
nn.ReLU(True),
nn.ConvTranspose2d(256, 128, 4, 2, 1, bias=False),
nn.BatchNorm2d(128),
nn.ReLU(True),
nn.ConvTranspose2d(128, channels, 4, , , bias=),
nn.Tanh(),
)
():
z = z.view(z.size(), -, , )
.model(z)
Discriminateur DCGAN
class Discriminator(nn.Module):
def __init__(self, channels=3):
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(channels, 128, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(128, 256, 4, 2, 1, bias=False),
nn.BatchNorm2d(256),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(256, 512, 4, 2, 1, bias=False),
nn.BatchNorm2d(512),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(512, 1024, 4, 2, 1, bias=False),
nn.BatchNorm2d(1024),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(1024, 1, 4, , , bias=),
nn.Sigmoid(),
)
():
.model(img).view(-, )
2. Entraînement GAN
generator = Generator(latent_dim=100).cuda()
discriminator = Discriminator().cuda()
g_optim = torch.optim.Adam(generator.parameters(), lr=0.0002, betas=(0.5, 0.999))
d_optim = torch.optim.Adam(discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))
for epoch in range(epochs):
for real_imgs, _ in dataloader:
real_imgs = real_imgs.cuda()
batch_size = real_imgs.size(0)
d_real = discriminator(real_imgs)
z = torch.randn(batch_size, 100).cuda()
fake_imgs = generator(z).detach()
d_fake = discriminator(fake_imgs)
d_loss = (F.binary_cross_entropy(d_real, torch.ones_like(d_real)) +
F.binary_cross_entropy(d_fake, torch.zeros_like(d_fake)))
d_optim.zero_grad()
d_loss.backward()
d_optim.step()
valid = torch.full((batch_size, 1), 0.9, device="cuda")
z = torch.randn(batch_size, 100).cuda()
fake_imgs = generator(z)
d_fake = discriminator(fake_imgs)
g_loss = F.binary_cross_entropy(d_fake, valid)
g_optim.zero_grad()
g_loss.backward()
g_optim.step()
3. StyleGAN (Style-Based Generator)
import torch
import stylegan3
device = torch.device('cuda')
G = stylegan3.load_network('stylegan3-r-ffhq-1024x1024.pkl')
G.eval().to(device)
z = torch.randn(1, 512).to(device)
w = G.mapping(z, None)
img = G.synthesis(w)
z1 = torch.randn(1, 512).to(device)
z2 = torch.randn(1, 512).to(device)
w1 = G.mapping(z1, None)
w2 = G.mapping(z2, None)
mix_style = torch.cat([w1[:, :4], w2[:, 4:]], dim=1)
img_mixed = G.synthesis(mix_style)
w_avg = G.mapping.w_avg
truncation_psi =
w = w_avg + truncation_psi * (w - w_avg)
img = G.synthesis(w)
4. pix2pix (Image-to-Image Translation)
class UNetGenerator(nn.Module):
"""U-Net avec skip connections pour pix2pix"""
def __init__(self, in_channels=3, out_channels=3):
super().__init__()
self.down1 = self.down_block(in_channels, 64, norm=False)
self.down2 = self.down_block(64, 128)
self.down3 = self.down_block(128, 256)
self.down4 = self.down_block(256, 512)
self.down5 = self.down_block(512, 512)
self.down6 = self.down_block(512, 512)
self.down7 = self.down_block(512, 512)
self.down8 = self.down_block(512, 512, norm=False)
self.up1 = .up_block(, , dropout=)
.up2 = .up_block(, , dropout=)
.up3 = .up_block(, , dropout=)
.up4 = .up_block(, )
.up5 = .up_block(, )
.up6 = .up_block(, )
.up7 = .up_block(, )
.up8 = nn.Sequential(
nn.ConvTranspose2d(, out_channels, , , ),
nn.Tanh(),
)
():
d1 = .down1(x)
d2 = .down2(d1)
d3 = .down3(d2)
d4 = .down4(d3)
d5 = .down5(d4)
d6 = .down6(d5)
d7 = .down7(d6)
d8 = .down8(d7)
u1 = .up1(d8)
u2 = .up2(torch.cat([u1, d7], ))
u3 = .up3(torch.cat([u2, d6], ))
u4 = .up4(torch.cat([u3, d5], ))
u5 = .up5(torch.cat([u4, d4], ))
u6 = .up6(torch.cat([u5, d3], ))
u7 = .up7(torch.cat([u6, d2], ))
.up8(torch.cat([u7, d1], ))
():
layers = [nn.Conv2d(in_c, out_c, , , ), nn.LeakyReLU()]
norm:
layers.append(nn.BatchNorm2d(out_c))
nn.Sequential(*layers)
():
layers = [nn.ConvTranspose2d(in_c, out_c, , , ), nn.ReLU()]
dropout:
layers.append(nn.Dropout())
layers.append(nn.BatchNorm2d(out_c))
nn.Sequential(*layers)
(nn.Module):
():
().__init__()
.model = nn.Sequential(
nn.Conv2d(in_channels, , , , ),
nn.LeakyReLU(),
nn.Conv2d(, , , , ),
nn.BatchNorm2d(),
nn.LeakyReLU(),
nn.Conv2d(, , , , ),
nn.BatchNorm2d(),
nn.LeakyReLU(),
nn.Conv2d(, , , , ),
nn.BatchNorm2d(),
nn.LeakyReLU(),
nn.Conv2d(, , , , ),
)
():
.model(x)
criterion_gan = nn.BCEWithLogitsLoss()
criterion_l1 = nn.L1Loss()
lambda_l1 =
g_loss = criterion_gan(d_fake, valid) + lambda_l1 * criterion_l1(fake, real)
5. CycleGAN (Unpaired Image Translation)
class CycleLoss(nn.Module):
def __init__(self, lambda_cycle=10.0, lambda_identity=5.0):
super().__init__()
self.lambda_cycle = lambda_cycle
self.lambda_identity = lambda_identity
self.l1 = nn.L1Loss()
def forward(self, real, reconstructed, identity=None):
loss = self.lambda_cycle * self.l1(real, reconstructed)
if identity is not None:
loss += self.lambda_identity * self.l1(real, identity)
return loss
Applications pix2pix/CycleGAN
| Tâche | Entrée | Sortie | Type |
|---|
| Colorisation | Gris | Couleur | pix2pix |
| Esquisse → Photo | Croquis | Réaliste | pix2pix |
| Carte → Satellite | Carte | Vue satellite | pix2pix |
| Jour → Nuit | Jour | Nuit | CycleGAN |
| Été → Hiver | Été | Hiver | CycleGAN |
| Photo → Style artistique | Photo | Monet/Van Gogh | CycleGAN |
| Défloutage | Flou | Net | pix2pix |
| Suppression objet | Avec objet | Sans objet | pix2pix |
6. Super-Résolution (SRGAN, ESRGAN, Real-ESRGAN)
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan import RealESRGANer
model = RealESRGANer(
scale=4,
model_path="RealESRGAN_x4plus.pth",
model=RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4),
tile=400,
tile_pad=10,
pre_pad=0,
half=True,
)
output, _ = model.enhance(input_img, outscale=4)
class VGGLoss(nn.Module):
"""Perceptual loss basé sur VGG19"""
def __init__(self):
super().__init__()
vgg = models.vgg19(pretrained=True).features
self.layers = nn.Sequential(*list(vgg.children())[:35])
.layers.()
p .layers.parameters():
p.requires_grad =
.criterion = nn.L1Loss()
():
pred_features = .layers(pred)
target_features = .layers(target)
.criterion(pred_features, target_features)
7. Inpainting (EdgeConnect, LaMa)
from lama_cleaner import LaMa
model = LaMa()
result = model(image, mask)
8. GigaGAN / BigGAN (Large Scale)
9. Métriques GAN
from pytorch_fid import fid_score
fid_value = fid_score.calculate_fid_given_paths(
["path/to/real", "path/to/fake"],
batch_size=50,
device="cuda",
dims=2048,
)
from torchmetrics.image.inception import InceptionScore
is_metric = InceptionScore()
is_metric.update(fake_images)
precision, recall = is_metric.compute()
from lpips import LPIPS
lpips_fn = LPIPS(net="alex")
dist = lpips_fn(img1, img2)
10. Techniques Avancées
Spectral Normalization
from torch.nn.utils import spectral_norm
conv = spectral_norm(nn.Conv2d(256, 512, 3, 1, 1))
linear = spectral_norm(nn.Linear(512, 1024))
Self-Attention (SA-GAN)
class SelfAttention(nn.Module):
"""Auto-attention pour GAN (SA-GAN)"""
def __init__(self, in_channels):
super().__init__()
self.query = nn.Conv1d(in_channels, in_channels // 8, 1)
self.key = nn.Conv1d(in_channels, in_channels // 8, 1)
self.value = nn.Conv1d(in_channels, in_channels, 1)
self.gamma = nn.Parameter(torch.zeros(1))
def forward(self, x):
batch, C, H, W = x.shape
Q = self.query(x.view(batch, C, -1))
K = self.key(x.view(batch, C, -1))
V = self.value(x.view(batch, C, -1))
attention = F.softmax(torch.bmm(Q.transpose(1, 2), K), dim=-1)
out = torch.bmm(V, attention.transpose(1, 2))
out = out.view(batch, C, H, W)
return self.gamma * out + x
Progressive Growing
Références