Faza 04 · lecția 09
Generarea imaginilor — GAN-uri
Scopul lecției: Un GAN este format din două rețele neuronale într-un joc cu reguli fixe. Una desenează, cealaltă critică. Ambele devin mai bune împreună până când desenele o păcălesc pe critică.
Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.
Cuprinsul lecției
- Obiective de învățare
- Problema
- Conceptul
- Cele două rețele
- Jocul
- Pierderea nesaturantă
- Regulile de arhitectură DCGAN
- Moduri de eșec și semnăturile lor
- Evaluarea
- Construiți-l
- Pasul 1: Generatorul
- Pasul 2: Discriminatorul
- Pasul 3: Pasul de antrenare
- Pasul 4: Bucla completă de antrenare pentru forme sintetice
- Pasul 5: Eșantionarea
- Pasul 6: Normalizarea spectrală
- Folosiți-l
- Livrați-l
- Exerciții
- Termeni-cheie
- Lecturi suplimentare
Un GAN este format din două rețele neuronale într-un joc cu reguli fixe. Una desenează, cealaltă critică. Ambele devin mai bune împreună până când desenele o păcălesc pe critică.
Tip: Construcție Limbaje: Python Cerințe prealabile: Faza 4 Lecția 03 (CNN-uri), Faza 3 Lecția 06 (Optimizatori), Faza 3 Lecția 07 (Regularizare) Timp: ~75 de minute
Obiective de învățare
- Explicați jocul minimax dintre generator și discriminator și de ce echilibrul corespunde lui p_model = p_data
- Implementați un DCGAN în PyTorch și faceți-l să genereze imagini sintetice coerente de 32x32 în mai puțin de 60 de linii
- Stabilizați antrenarea GAN cu cele trei tehnici standard: pierderea nesaturantă, normalizarea spectrală, TTUR (regula de actualizare cu două scale temporale)
- Citiți curbele de antrenare care disting convergența sănătoasă de colapsul modurilor, oscilație și situația în care discriminatorul câștigă complet
Problema
Clasificarea învață o rețea să mapeze imagini la etichete. Generarea inversează problema: eșantionează imagini noi care par să provină din aceeași distribuție. Nu există o ieșire „corectă” cu care să puteți face diff; există doar o distribuție pe care vreți să o imitați.
Funcțiile de pierdere standard (MSE, entropie încrucișată) nu pot măsura „a provenit acest eșantion din distribuția reală?”. Minimizarea erorii pentru fiecare pixel produce medii neclare, nu eșantioane realiste. Descoperirea a fost să învățați funcția de pierdere: antrenați o a doua rețea a cărei sarcină este să distingă imaginile reale de cele false și folosiți aprecierea ei pentru a ghida generatorul.
GAN-urile (Goodfellow et al., 2014) au definit acest cadru. Până în 2018, StyleGAN producea fețe de 1024x1024 imposibil de deosebit de fotografii. De atunci, modelele de difuzie au preluat supremația în calitate și controlabilitate, dar fiecare tehnică ce face difuzia practică — alegerile de normalizare, spațiile latente, pierderile pe caracteristici — a fost înțeleasă mai întâi pe GAN-uri.
Notă tehnică a traducerii: Afirmația despre „fiecare” tehnică este prea largă. Normalizarea pe loturi, modelele latente variaționale și pierderile perceptuale au și dezvoltări independente de GAN-uri; GAN-urile au avut un rol important în folosirea lor pentru sinteză, nu în originea tuturor acestor idei. Consultați Batch Normalization, Auto-Encoding Variational Bayes și Perceptual Losses.
Conceptul
Cele două rețele
Generatorul G primește un vector de zgomot z și produce o imagine. Discriminatorul D primește o imagine și produce un singur scalar: probabilitatea ca imaginea să fie reală.
Notă tehnică a traducerii: În formularea probabilistică,
D(x)desemnează probabilitatea ca exemplul să fie real. Implementarea de mai jos nu aplicăsigmoidînDiscriminator, deci întoarce logit-uri;F.binary_cross_entropy_with_logitsaplică intern sigmoida într-o formulare stabilă numeric. Nu adăugați o sigmoidă înaintea acestei funcții de pierdere.
Jocul
G vrea ca D să greșească. D vrea să aibă dreptate. Formal:
min_G max_D E_x[log D(x)] + E_z[log(1 - D(G(z)))]
Citiți de la dreapta la stânga: D maximizează acuratețea pentru imaginile reale (log D(real)) și false (log (1 - D(fake))). G minimizează acuratețea lui D pentru imaginile false — vrea ca D(G(z)) să fie mare.
Goodfellow a demonstrat că acest minimax are un echilibru global în care p_G = p_data, D produce 0,5 peste tot, iar divergența Jensen–Shannon dintre distribuțiile generate și reale este zero. Partea dificilă este atingerea lui.
Notă tehnică a traducerii: Acesta este un rezultat idealizat: presupune distribuții de populație, capacitate suficientă și discriminatorul optim pentru generatorul curent. Rețelele finite, seturile de date finite și optimizarea alternantă nu garantează atingerea acestui echilibru.
Pierderea nesaturantă
Forma de mai sus este instabilă numeric. La începutul antrenării, D(G(z)) este aproape de zero pentru fiecare imagine falsă, astfel încât log(1 - D(G(z))) are gradienți care dispar față de G. Corecția: inversați pierderea lui G.
L_D = -E_x[log D(x)] - E_z[log(1 - D(G(z)))]
L_G = -E_z[log D(G(z))] # nesaturantă
Acum, când D(G(z)) este aproape de zero, pierderea lui G este mare, iar gradientul său este informativ. Fiecare GAN modern se antrenează cu această variantă.
Notă tehnică a traducerii: Problema principală a obiectivului minimax original este saturația gradientului generatorului la începutul antrenării, nu o proprietate generală de instabilitate a aritmeticii în virgulă mobilă. Obiectivul nesaturant este euristica propusă în lucrarea originală pentru a oferi gradienți mai puternici generatorului; este foarte folosit, dar nu universal, deoarece Wasserstein GAN și variantele sale folosesc obiectivul Wasserstein.
Regulile de arhitectură DCGAN
Radford, Metz, Chintala (2015) au condensat ani de experimente eșuate în cinci reguli care fac antrenarea GAN stabilă:
- Înlocuiți pooling-ul cu convoluții cu stride (în ambele rețele).
- Folosiți batch norm atât în generator, cât și în discriminator, cu excepția ieșirii lui G și a intrării lui D.
- Eliminați straturile complet conectate din arhitecturile mai profunde.
- G folosește ReLU în toate straturile cu excepția ieșirii (tanh pentru ieșirea din [-1, 1]).
- D folosește LeakyReLU (negative_slope=0.2) în toate straturile.
Fiecare GAN modern bazat pe convoluții (StyleGAN, BigGAN, GigaGAN) pornește în continuare de la aceste reguli și înlocuiește componentele una câte una.
Notă tehnică a traducerii: Regulile DCGAN sunt recomandări empirice pentru configurația arhitecturală și protocolul studiate de Radford et al., nu condiții universale de stabilitate. Arhitecturile ulterioare, inclusiv StyleGAN și BigGAN, modifică substanțial normalizarea, generatorul și discriminatorul.
Moduri de eșec și semnăturile lor
- Colapsul modurilor: G găsește o imagine care păcălește D și o produce numai pe aceasta. Corecție: adăugați discriminare pe minibatch, normalizare spectrală sau condiționare pe etichete.
- Discriminatorul câștigă: D devine prea puternic prea repede, iar gradienții lui G dispar. Corecție: un D mai mic, o rată de învățare mai mică pentru D sau netezirea etichetelor reale.
- Oscilația: cele două rețele alternează victorii fără să se apropie vreodată de echilibru. Corecție: TTUR (D învață mai repede decât G cu un factor de 2–4) sau treceți la pierderea Wasserstein.
Notă tehnică a traducerii: Curbele pierderilor sunt indicii, nu diagnostice univoce: scara lor depinde de obiectiv, arhitectură, ratele de actualizare și definiția pierderii. Verificați simultan diversitatea eșantioanelor și metrici de calitate/acoperire, nu aplicați automat o singură corecție pornind doar de la două curbe.
Notă tehnică a traducerii: TTUR înseamnă scale temporale de actualizare diferite; nu fixează o direcție sau un raport universal de
2–4între ratele de învățare. Raportul adecvat depinde de obiectiv, optimizator, date și arhitectură.
Evaluarea
GAN-urile nu au adevăr de referință, așadar cum știți că funcționează?
- Inspectarea eșantioanelor — priviți pur și simplu 64 de eșantioane la sfârșitul fiecărei epoci. Nu este negociabil.
- FID (Fréchet Inception Distance) — distanța dintre distribuțiile caracteristicilor Inception-v3 ale seturilor reale și generate. Mai mic este mai bine. Standardul comunității.
- Inception Score — mai vechi, mai fragil; preferați FID.
- Precizie/recall pentru modele generative — măsoară separat calitatea (precizia) și acoperirea (recall). Mai informativ decât FID singur.
Pentru o rulare mică pe date sintetice, inspectarea eșantioanelor este suficientă.
Notă tehnică a traducerii: FID estimează distanța Fréchet dintre aproximări gaussiene ale caracteristicilor Inception și depinde de modelul de caracteristici, preprocesare, setul de referință și numărul de eșantioane. Un FID mic singur nu demonstrează absența colapsului modurilor sau calitatea pentru toate clasele; completați-l cu inspectare și metrici de precizie/recall generativ.
Construiți-l
Pasul 1: Generatorul
Un generator DCGAN mic care primește zgomot cu 64 de dimensiuni și produce o imagine de 32x32.
import torch
import torch.nn as nn
class Generator(nn.Module):
def __init__(self, z_dim=64, img_channels=3, feat=64):
super().__init__()
self.net = nn.Sequential(
nn.ConvTranspose2d(z_dim, feat * 4, kernel_size=4, stride=1, padding=0, bias=False),
nn.BatchNorm2d(feat * 4),
nn.ReLU(inplace=True),
nn.ConvTranspose2d(feat * 4, feat * 2, kernel_size=4, stride=2, padding=1, bias=False),
nn.BatchNorm2d(feat * 2),
nn.ReLU(inplace=True),
nn.ConvTranspose2d(feat * 2, feat, kernel_size=4, stride=2, padding=1, bias=False),
nn.BatchNorm2d(feat),
nn.ReLU(inplace=True),
nn.ConvTranspose2d(feat, img_channels, kernel_size=4, stride=2, padding=1, bias=False),
nn.Tanh(),
)
def forward(self, z):
return self.net(z.view(z.size(0), -1, 1, 1))
Patru convoluții transpuse, fiecare cu kernel_size=4, stride=2, padding=1, astfel încât dublează curat dimensiunea spațială. Activările de ieșire sunt în [-1, 1] prin tanh.
Pasul 2: Discriminatorul
Imaginea în oglindă a generatorului. LeakyReLU, convoluții cu stride, iar la final un logit scalar.
class Discriminator(nn.Module):
def __init__(self, img_channels=3, feat=64):
super().__init__()
self.net = nn.Sequential(
nn.Conv2d(img_channels, feat, kernel_size=4, stride=2, padding=1),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(feat, feat * 2, kernel_size=4, stride=2, padding=1, bias=False),
nn.BatchNorm2d(feat * 2),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(feat * 2, feat * 4, kernel_size=4, stride=2, padding=1, bias=False),
nn.BatchNorm2d(feat * 4),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(feat * 4, 1, kernel_size=4, stride=1, padding=0),
)
def forward(self, x):
return self.net(x).view(-1)
Ultima convoluție reduce o hartă de caracteristici 4x4 la 1x1. Ieșirea este un singur logit pentru fiecare imagine; aplicați sigmoid numai în timpul calculării pierderii.
Pasul 3: Pasul de antrenare
Alternați: actualizați D o dată, apoi G o dată, la fiecare lot.
import torch.nn.functional as F
def train_step(G, D, real, z, opt_g, opt_d, device):
real = real.to(device)
bs = real.size(0)
# D step
opt_d.zero_grad()
d_real = D(real)
d_fake = D(G(z).detach())
loss_d = (F.binary_cross_entropy_with_logits(d_real, torch.ones_like(d_real))
+ F.binary_cross_entropy_with_logits(d_fake, torch.zeros_like(d_fake)))
loss_d.backward()
opt_d.step()
# G step
opt_g.zero_grad()
d_fake = D(G(z))
loss_g = F.binary_cross_entropy_with_logits(d_fake, torch.ones_like(d_fake))
loss_g.backward()
opt_g.step()
return loss_d.item(), loss_g.item()
G(z).detach() în pasul lui D este critic: nu vrem ca gradientul să ajungă în G în timpul actualizării lui D. Uitarea acestei operații este eroarea clasică a începătorului.
Pasul 4: Bucla completă de antrenare pentru forme sintetice
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
def synthetic_images(num=2000, size=32, seed=0):
rng = np.random.default_rng(seed)
imgs = np.zeros((num, 3, size, size), dtype=np.float32) - 1.0
for i in range(num):
r = rng.uniform(6, 12)
cx, cy = rng.uniform(r, size - r, size=2)
yy, xx = np.meshgrid(np.arange(size), np.arange(size), indexing="ij")
mask = (xx - cx) ** 2 + (yy - cy) ** 2 < r ** 2
color = rng.uniform(-0.5, 1.0, size=3)
for c in range(3):
imgs[i, c][mask] = color[c]
return torch.from_numpy(imgs)
device = "cuda" if torch.cuda.is_available() else "cpu"
data = synthetic_images()
loader = DataLoader(TensorDataset(data), batch_size=64, shuffle=True)
G = Generator(z_dim=64, img_channels=3, feat=32).to(device)
D = Discriminator(img_channels=3, feat=32).to(device)
opt_g = torch.optim.Adam(G.parameters(), lr=2e-4, betas=(0.5, 0.999))
opt_d = torch.optim.Adam(D.parameters(), lr=2e-4, betas=(0.5, 0.999))
for epoch in range(10):
for (batch,) in loader:
z = torch.randn(batch.size(0), 64, device=device)
ld, lg = train_step(G, D, batch, z, opt_g, opt_d, device)
print(f"epoch {epoch} D {ld:.3f} G {lg:.3f}")
Adam(lr=2e-4, betas=(0.5, 0.999)) este configurația implicită DCGAN — beta1 scăzut împiedică termenul de momentum să stabilizeze prea mult jocul adversarial.
Pasul 5: Eșantionarea
@torch.no_grad()
def sample(G, n=16, z_dim=64, device="cpu"):
G.eval()
z = torch.randn(n, z_dim, device=device)
imgs = G(z)
imgs = (imgs + 1) / 2
return imgs.clamp(0, 1)
Treceți întotdeauna în modul de evaluare înainte de eșantionare. Pentru DCGAN, acest lucru contează fiindcă sunt folosite statisticile mobile ale batch norm în locul statisticilor lotului curent.
Notă tehnică a traducerii:
G.eval()schimbă persistent modul modulului. Dacă reluați antrenarea după apelulsample, apelațiG.train()înainte de următorul pas de antrenare;eval()nu dezactivează singur urmărirea gradientului.
Pasul 6: Normalizarea spectrală
Un înlocuitor direct pentru BN în discriminator, care garantează că rețeaua este 1-Lipschitz. Corectează majoritatea eșecurilor de tipul „D câștigă prea categoric”.
from torch.nn.utils import spectral_norm
def build_sn_discriminator(img_channels=3, feat=64):
return nn.Sequential(
spectral_norm(nn.Conv2d(img_channels, feat, 4, 2, 1)),
nn.LeakyReLU(0.2, inplace=True),
spectral_norm(nn.Conv2d(feat, feat * 2, 4, 2, 1)),
nn.LeakyReLU(0.2, inplace=True),
spectral_norm(nn.Conv2d(feat * 2, feat * 4, 4, 2, 1)),
nn.LeakyReLU(0.2, inplace=True),
spectral_norm(nn.Conv2d(feat * 4, 1, 4, 1, 0)),
)
Înlocuiți Discriminator cu build_sn_discriminator() și adesea nu mai aveți nevoie de tehnica TTUR. Normalizarea spectrală este cea mai ușor de aplicat îmbunătățire individuală a robusteții.
Notă tehnică a traducerii: Normalizarea spectrală constrânge norma spectrală estimată a fiecărui strat, nu garantează necondiționat că orice rețea care o folosește este exact 1-Lipschitz. Pentru acest lanț de straturi, o limită globală de 1 necesită norme constrânse exact și activări cu constanta Lipschitz cel mult 1; implementarea practică estimează norma prin iterație de putere. În plus,
build_sn_discriminator()întoarce forma(N, 1, 1, 1), spre deosebire de forma(N,)a luiDiscriminator; codul afișat funcționează fiindcăones_likeșizeros_likeau aceeași formă, însă alți consumatori pot necesita aplatizare.torch.nn.utils.spectral_normeste API-ul păstrat de original; documentația PyTorch recomandă varianta dintorch.nn.utils.parametrizationspentru cod nou. Consultați Miyato et al. și documentația PyTorch.
Folosiți-l
Pentru generare serioasă, folosiți ponderi preantrenate sau treceți la difuzie. Două biblioteci standard:
torch_fidelitycalculează FID / IS pentru generatorul dumneavoastră fără să scrieți cod personalizat de evaluare.pytorch-gan-zoo(legacy) șiStudioGANoferă implementări testate de DCGAN, WGAN-GP, SN-GAN, StyleGAN și BigGAN.
În 2026, GAN-urile sunt în continuare cea mai bună alegere pentru: generarea de imagini în timp real (latență <10 ms), transferul de stil, traducerea imagine-la-imagine cu control precis (Pix2Pix, CycleGAN). Difuzia câștigă la fotorealism și condiționare prin text.
Notă tehnică a traducerii: Alegerea „cea mai bună”, ținta de latență, fotorealismul și condiționarea prin text depind de arhitectură, hardware, rezoluție, optimizare, date, metrică și constrângerile produsului; nu există o garanție generală de
<10 mspentru GAN-uri și nici un câștigător permanent între familiile de modele. Afirmația din textul-sursă trebuie citită ca o regulă orientativă dependentă de moment și de sarcină, nu ca reper universal.
Livrați-l
Această lecție produce:
outputs/prompt-gan-training-triage.md— un prompt care citește descrierea unei curbe de antrenare și alege modul de eșec (colapsul modurilor, D-câștigă, oscilație), plus singura corecție recomandată.outputs/skill-dcgan-scaffold.md— o abilitate care scrie un schelet DCGAN dinz_dim,image_sizețintă șinum_channels, incluzând bucla de antrenare și salvarea eșantioanelor.
Exerciții
- (Ușor) Antrenați DCGAN-ul de mai sus pe setul de date sintetic cu cercuri și salvați o grilă de 16 eșantioane la sfârșitul fiecărei epoci. În ce epocă cercurile generate devin clar circulare?
- (Mediu) Înlocuiți batch norm-ul discriminatorului cu normalizarea spectrală. Antrenați ambele versiuni în paralel. Care converge mai repede? Care are varianță mai mică între trei seed-uri?
- (Dificil) Implementați un DCGAN condițional: furnizați eticheta clasei atât lui G, cât și lui D (concatenați codificarea one-hot cu zgomotul în G, concatenați un canal de embedding al clasei în D). Antrenați-l pe setul de date sintetic „cercuri versus pătrate” din lecția 7 și arătați că condiționarea pe clasă funcționează eșantionând cu etichete specifice.
Termeni-cheie
| Termen | Ce spun oamenii | Ce înseamnă de fapt |
|---|---|---|
| Generator (G) | „Rețeaua care desenează” | Mapează zgomotul la imagini; este antrenat să păcălească discriminatorul |
| Discriminator (D) | „Critica” | Clasificator binar; este antrenat să distingă imaginile reale de cele generate |
| Minimax | „Jocul” | min peste G, max peste D ale unei pierderi adversariale; echilibrul este p_G = p_data |
| Pierderea nesaturantă | „Versiunea numeric sănătoasă” | Pierderea lui G este -log(D(G(z))) în loc de log(1 - D(G(z))) pentru a evita gradienții care dispar la începutul antrenării |
| Colapsul modurilor | „Generatorul produce un singur lucru” | G produce numai un subset mic al distribuției datelor; corectați cu SN, discriminare pe minibatch sau un lot mai mare |
| TTUR | „Două rate de învățare” | D învață mai repede decât G, de obicei cu un factor de 2–4; stabilizează antrenarea |
| Normalizarea spectrală | „Strat 1-Lipschitz” | O normalizare a ponderilor care limitează constanta Lipschitz a fiecărui strat; împiedică D să devină arbitrar de abrupt |
| FID | „Fréchet Inception Distance” | Distanța dintre distribuțiile caracteristicilor Inception-v3 ale seturilor reale și generate; metrica standard de evaluare |
Lecturi suplimentare
- Generative Adversarial Networks (Goodfellow et al., 2014) — lucrarea care a început totul
- DCGAN (Radford, Metz, Chintala, 2015) — regulile de arhitectură care au făcut GAN-urile antrenabile
- Spectral Normalization for GANs (Miyato et al., 2018) — cea mai utilă tehnică unică de stabilizare
- StyleGAN3 (Karras et al., 2021) — GAN-ul SOTA; se citește ca un album cu cele mai bune tehnici din ultimul deceniu
Notă tehnică a traducerii: „SOTA” desemnează un rezultat raportat într-un moment, pe benchmark-uri și metrici determinate; nu este un titlu permanent al unui model. Comparați lucrările în protocolul de evaluare relevant pentru sarcina curentă.
Sursă: Originalul în limba engleză
Navigare: ← Lecția 04.08 — Segmentarea instanțelor — Mask R-CNN · Faza 4 — Viziune computerizată · Lecția 04.10 — Generarea imaginilor — modele de difuzie → · Catalog complet