Mundarija (24)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Generator va diskriminator o'yini
- 2.2. Minimax loss va optimal diskriminator
- 2.3. Non-saturating generator loss
- 2.4. O'rgatish sikli
- 2.5. 2D: modalar qamrovi
- 2.6. O'rgatish dinamikasi: loss lar nimani anglatadi
- 2.7. Frechet masofa (qisqa)
- 2.8. GAN va VAE: halol solishtirish
- 2.9. Mode collapse (qisqa)
- 2.10. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — O'yin matematikasi: optimal D va non-saturating loss
- Misol 2 — 2D: sakkizta Gauss halqasi
- Misol 3 — Digits da GAN: dinamika va namunalar
- Misol 4 — GAN va VAE bir xil o'lchovlarda
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
26.3-dars: GAN
26-QISM — GENERATIV AI · 3-dars
1. Kirish va motivatsiya
26.2-darsda VAE ni ko'rdik: u likelihood ning pastki chegarasini (ELBO) maksimallashtiradi, latent fazosi silliq, o'rgatish barqaror. Lekin uning namunalari xira — decoder har piksel uchun "o'rtacha" qiymatni chizadi. 2014 yilda Ian Goodfellow va hamkasblari butunlay boshqa yo'lni taklif qilishdi: likelihood ni umuman hisoblamaslik. Buning o'rniga ikkita tarmoq o'ynaydi. Generator tasodifiy shovqindan rasm yasaydi. Diskriminator esa rasm haqiqiymi yoki generator yasaganmi — shuni ajratishga harakat qiladi. Generatorning yagona maqsadi — diskriminatorni aldash. Bu GAN (Generative Adversarial Network — raqobatli generativ tarmoq).
G'oyaning kuchi shundaki, "yaxshi rasm nima?" degan savolga javobni biz formula bilan yozmaymiz — uni diskriminator o'zi o'rganadi. Xira rasm diskriminatorga darhol "soxta" ko'rinadi, shuning uchun generator o'tkir detallar chizishga majbur. Yillar davomida eng realistik sintetik yuzlar aynan GAN lar bilan olingan. Lekin bu kuchning narxi bor: o'yinni o'rgatish beqaror, loss qiymatlari sifat haqida deyarli hech narsa aytmaydi, generator esa ma'lumotning bir qismini "unutib" qo'yishi mumkin.
Real vaziyat. Jamoa mahsulot fotosuratlarini ko'paytirish uchun GAN o'rgatdi va har kuni ertalab loss grafigiga qarab turdi. Bir hafta davomida generator loss i kamaydi — jamoa xursand edi. Keyin kimdir namunalarni ochdi: generator bir xil mahsulotning deyarli bir xil rasmini ming xil shovqindan chizayotgan edi. Loss "yaxshi" ko'rinardi, chunki diskriminator ham shu paytda zaiflashgan edi. Bu darsda GAN ni noldan yozamiz va aynan shu narsani o'lchaymiz: loss nimani ko'rsatadi, nimani ko'rsatmaydi va sifatni qanday raqam bilan kuzatish kerak.
Bu darsda GAN o'yinining matematikasini tekshiramiz (optimal diskriminator, minimax qiymati, non-saturating loss), 2D da sakkizta Gauss aralashmasini o'rganamiz, 8x8 raqamlar uchun GAN o'rgatamiz va uni VAE bilan bir xil o'lchovlarda halol solishtiramiz.
Bu darsda:
- Generator va diskriminator o'yini
- Minimax loss va optimal diskriminator
- Non-saturating generator loss: nega kerak
- O'rgatish sikli noldan
- 2D: modalar qamrovi va sifatli namunalar
- O'rgatish dinamikasi: D va G loss lari nimani anglatadi
- Digits da GAN: tanib olinish, sinf qamrovi, Frechet masofa
- GAN va VAE — bir xil o'lchovlarda
- Mode collapse (qisqa)
- Tuzoqlar
ℹ Misollar real torch/numpy bilan (Python 3.14, torch 2.14 CPU).
2. Nazariya — chuqur tushuntirish
2.1. Generator va diskriminator o'yini
IKKI TARMOQ:
G(z): z ~ N(0, I) -> soxta x (generator, "qalbakichi")
D(x): x -> ehtimol "x haqiqiy" (diskriminator, "ekspert")
O'YIN:
D ning maqsadi: haqiqiyga 1, soxtaga 0 deyish (oddiy binar klassifikatsiya)
G ning maqsadi: D soxtaga 1 desin
NIMA YO'Q:
p(x) formulasi yo'q, likelihood yo'q, encoder yo'q
G - faqat namuna chiqaradigan "implitsit" model (26.1, 2.2: GAN - p(x) YO'Q)
sifat signali - D ning gradienti: "rasmni qaysi tomonga o'zgartirsang,
haqiqiyroq ko'rinadi"
26.2 BILAN FARQ:
VAE: x -> encoder -> z -> decoder -> x, piksel bo'yicha qayta tiklash loss
GAN: z -> G -> x; "qayta tiklash" yo'q, piksel bo'yicha loss yo'q
-> GAN piksel o'rtachasini emas, "haqiqiy ko'rinish" ni optimallashtiradiGAN — ikki tarmoqning o'yini: diskriminator oddiy klassifikator, generator esa undan gradient olib, soxta namunalarni haqiqiyga yaqinlashtiradi.
2.2. Minimax loss va optimal diskriminator
QIYMAT FUNKSIYASI:
V(D, G) = E_x~data[log D(x)] + E_z[log(1 - D(G(z)))]
min_G max_D V(D, G)
OPTIMAL D (G belgilangan bo'lsa):
D*(x) = p_data(x) / (p_data(x) + p_g(x))
1-misol, 1D: p_data = N(0, 1), p_g = N(2, 1)
x = -1: o'rgatilgan 0.984, nazariya 0.982
x = +1: o'rgatilgan 0.501, nazariya 0.500 (o'rtada - teng)
x = +3: o'rgatilgan 0.016, nazariya 0.018
D* DA QIYMAT:
V(D*, G) = -log 4 + 2 * JSD(p_data || p_g)
JSD - Jensen-Shannon divergensiyasi (simmetrik, 0 .. log 2)
1-misol: o'rgatilgan D bilan -0.712, nazariya -1.386 + 2 * 0.337 = -0.713
GLOBAL OPTIMUM:
p_g = p_data -> JSD = 0, V = -log 4 = -1.386, D* = 0.5 hamma joyda
ya'ni ideal holda D hech narsani ajrata olmaydi Ideal diskriminator bilan GAN JSD ni minimallashtiradi; optimumda D = 0.5 va V = -log 4 — lekin amalda D hech qachon ideal emas.
2.3. Non-saturating generator loss
MINIMAX (asl) G LOSS: G minimallashtiradi log(1 - D(G(z)))
NON-SATURATING G LOSS: G minimallashtiradi -log D(G(z))
ikkalasining statsionar nuqtasi bir xil, gradientlari - yo'q
LOGIT BO'YICHA GRADIENT (1-misol, 1-bo'lim):
D(G(z)) minimax non-saturating
0.001 -0.0010 -0.9990
0.01 -0.0100 -0.9900
0.5 -0.5000 -0.5000
0.9 -0.9000 -0.1000
NEGA MUHIM:
o'rgatish boshida G yomon -> D soxtani oson rad etadi -> D(G(z)) ~ 0
minimax: aynan shu paytda gradient ~ 0 ("to'yinish") -> G o'rganmaydi
non-saturating: G eng yomon bo'lganda eng kuchli gradient oladi
1-MISOL, 3-BO'LIM (kuchli D, D(G(z)) = 0.0022, G - oddiy SGD):
||grad G||: minimax 0.01362, non-saturating 4.62931 (340 barobar)
100 qadamdan keyin D(G(z)): minimax 0.0024, non-saturating 0.9972
AMALDA:
deyarli hamma GAN non-saturating loss bilan o'rgatiladi
BCE ko'rinishida: G uchun "soxtaga 1 yorlig'i":
g_loss = BCE_with_logits(D(G(z)), 1)
Adam gradient masshtabini qisman normallaydi - 2-misolda (muvozanatli D,
Adam) ikki loss orasida sezilarli farq chiqmadi Generatorni -log D(G(z)) bilan o'rgating: asl minimax loss aynan D kuchli bo'lgan paytda to'yinadi va gradient beradi.
2.4. O'rgatish sikli
HAR QADAM:
1. D qadami:
x ~ ma'lumot, soxta = G(z)
d_loss = BCE(D(x), 1) + BCE(D(soxta.detach()), 0) <- detach!
2. G qadami:
g_loss = BCE(D(soxta), 1) <- non-saturating
faqat G ning optimizatori step qiladi
TIPIK SOZLAMA (DCGAN tavsiyalari asosida):
Adam, lr = 2e-4, betas = (0.5, 0.999)
LeakyReLU0.2-bob D da
rasmlar [-1, 1] ga, G chiqishi tanh
3-misol: xuddi shu GAN [0, 1] + sigmoid bilan - buzildi
(Frechet 66.8, sinflar 3 taga qisqardi); tanh bilan - 8.1
DETACH NIMA UCHUN:
D qadamida G ga gradient kerak emas (va G ning grad lari ifloslanmasin)
G qadamida esa soxta D orqali G gacha differensiallanadi Navbat bilan: avval D (soxta detach bilan), keyin G (non-saturating); kichik detallar — tanh, betas = (0.5, 0.999) — natijani tubdan o'zgartiradi.
2.5. 2D: modalar qamrovi
MA'LUMOT: radiusi 2 bo'lgan halqada 8 ta Gauss, sigma = 0.05
O'LCHOVLAR:
sifatli namuna: eng yaqin moda markazidan < 3 sigma = 0.15
qamrab olingan moda: unga kamida 1% sifatli namuna tushgan
haqiqiy ma'lumot: 8/8, sifatli 0.988
2-MISOL (1500 qadam, 3 urug'):
non-saturating: qamrov 8/8 har urug'da; sifatli 0.554, 0.460, 0.592
minimax: qamrov 8/8 har urug'da; sifatli 0.513, 0.492, 0.512
modalardagi sonlar notekis (masalan 32 dan 271 gacha)
NEGA SIFATLI ULUSH 1 GA YETMAYDI:
G - uzluksiz funksiya, z fazosi (N(0, I)) - yaxlit
8 ta ajralgan orolni uzluksiz akslantirish bilan "ko'priksiz" hosil qilib bo'lmaydi
-> namunalarning bir qismi modalar orasidagi "ko'priklarda" (ASCII da ko'rinadi)2D — GAN ni tekshirishning eng arzon yo'li: modalar soni va sifatli ulush bilan qamrov va aniqlikni alohida o'lchash mumkin.
2.6. O'rgatish dinamikasi: loss lar nimani anglatadi
MUVOZANAT MA'LUMOTNOMASI:
D(x) = D(G(z)) = 0.5 bo'lsa:
D loss = 2 * ln 2 = 1.386, G loss = ln 2 = 0.693
NIMANI KO'RSATADI:
D(x) va D(G(z)) - kim ustun: D(x) ~ 1, D(G(z)) ~ 0 va D loss -> 0
-> D butunlay ustun, G gradienti zaif (3-misol, sigmoid varianti:
D loss 0.253, D(G(z)) 0.08 - va namunalar buzilgan)
loss lar keskin tebransa yoki nan - beqarorlik
NIMANI KO'RSATMAYDI:
namuna sifatini: 3-misolda (tanh) D loss 1000 qadamdan keyin deyarli
o'zgarmadi (0.90 .. 1.06), Frechet esa 15.8 dan 8.1 gacha yaxshilandi
xilma-xillikni: G bitta yaxshi rasm chizib ham D ni vaqtincha aldashi mumkin
G loss ning kamayishi - D zaiflashgani ham bo'lishi mumkin
SHUNING UCHUN:
har N qadamda namunalarni MUSTAQIL o'lchov bilan baholang:
hakam ishonchi, sinf entropiyasi, Frechet masofa, yodlash tekshiruviGAN loss lari — o'yin holatining ko'rsatkichi, sifat o'lchovi emas: sifatni doim namunalardan, mustaqil o'lchov bilan kuzating.
2.7. Frechet masofa (qisqa)
G'OYA (FID ning soddalashtirilgan varianti):
haqiqiy va soxta rasmlarni hakam-klassifikatorning yashirin qatlamiga
o'tkazamiz (bizda 128 o'lchov), har to'plamga Gauss moslaymiz:
FD = ||mu_1 - mu_2||^2 + tr(C_1 + C_2 - 2 * sqrt(C_1 * C_2))
0 - taqsimotlar (shu xususiyatlarda) bir xil
MA'LUMOTNOMA:
450 ta haqiqiy o'quv rasmi va test: 0.712 (namuna kichikligi tufayli 0 emas)
CHEKLOVLAR:
xususiyatlar hakamga bog'liq - boshqa hakam boshqa raqam beradi
namuna soniga bog'liq - bir xil n bilan solishtiring
sifat va xilma-xillikni bitta songa aralashtiradi
standart FID (Inception tarmog'i) - Generativ modellarni baholash darsidaFrechet masofa taqsimotlarni solishtiradi, alohida namunani emas: u ham sifatni, ham qamrovni sezadi, lekin qaysi biri yomonligini ajratmaydi.
2.8. GAN va VAE: halol solishtirish
4-MISOL (3 urug', bir xil hakam, bir xil 1000 namuna):
o'lchov GAN VAE haqiqiy
ishonch>0.9 0.632 0.568 1.000
sinf entropiya 0.861 0.995 0.998
Frechet 17.877 6.274 0.712
eng kam sinf 0.009 0.076 0.087
sinf ichi xilma 1.715 1.192 2.256
keskin piksel 0.753 0.452 0.656
(hamma farqlar 2 * SE dan katta)
TALQIN:
GAN: o'tkir (keskin piksellar haqiqiydan ham ko'p), sinf ichida xilma-xil,
lekin ba'zi sinflarni deyarli chizmaydi (eng kam sinf 0.9%)
VAE: xira, sinf ichida bir xilroq, lekin hamma sinfni tekis qamraydi
Frechet bu kichik tajribada VAE ni yaxshi deydi - qamrov og'ir tortadi
GAN Frechet i urug'lar bo'yicha keng tarqalgan (16.6, 14.1, 23.0) - beqarorlik
HALOLLIK:
GAN 1500 qadam, VAE 40 davr - sozlash byudjetlari teng emas
3-misolda GAN 3000 qadamda Frechet 8.1 ga yetdi
"GAN o'tkirroq, VAE qamrovliroq" - adabiyotdagi umumiy manzara bilan mos,
lekin aniq raqamlar kichik ma'lumot va kichik modellarga xos"Qaysi model yaxshi?" — bitta raqam bilan javob berilmaydi: GAN o'tkirlik va sinf ichidagi xilma-xillikda, VAE qamrov va Frechet da yutdi.
2.9. Mode collapse (qisqa)
HODISA:
G ma'lumotning faqat bir qismini (bir necha moda/sinf) chizadi
D ni aldash uchun hamma narsani chizish shart emas - bitta ishonchli rasm yetadi
BU DARSDA KO'RINGAN BELGILAR:
3-misol, sigmoid varianti: 1000 namunaning 654 tasi "8" (3 ta sinf)
3-misol, tanh: sinflar 5 va 9 atigi 25 tadan (boshqalari 97 .. 142)
4-misol: eng kam sinf ulushi GAN da 0.009, VAE da 0.076
O'LCHASH: sinf entropiyasi, eng kam sinf ulushi, 2D da modalar soni
CHUQUR TAHLIL VA YECHIMLAR (minibatch diskriminatsiya, WGAN, spektral
normallash va boshqalar) - keyingi darsdaMode collapse — GAN ning asosiy kasalligi; uni loss dan emas, sinf taqsimotidan ko'rasiz.
2.10. Tuzoqlar
Asosiy tuzoqlar: D qadamida soxta namunani detach qilmaslik; G qadamida D ning parametrlarini ham yangilab yuborish (bitta optimizatorga hammasini berish); asl minimax loss log(1 - D(G(z))) ni ishlatish va boshida G ning o'rganmayotganiga hayron bo'lish; log(1 - sigmoid(logit)) ni to'g'ridan-to'g'ri hisoblash (logit katta bo'lsa log 0 = -inf, nan) — to'g'risi F.logsigmoid(-logit) yoki BCE with logits; rasmlarni [0, 1] da qoldirib, tanh chiqishni ishlatish (yoki aksincha); loss grafigiga qarab sifat haqida xulosa chiqarish; namunalarni faqat "ko'z bilan" va faqat bitta urug'da baholash; GAN va VAE ni bitta o'lchov (masalan faqat "ishonch") bilan solishtirish; mode collapse ni sinf taqsimotini tekshirmay o'tkazib yuborish; Frechet masofani turli namuna soni yoki turli hakam bilan solishtirish.
3. Tez ma'lumotnoma
import torch
import torch.nn as nn
import torch.nn.functional as F
G = nn.Sequential(nn.Linear(16, 128), nn.LeakyReLU(0.2), nn.Linear(128, 128), nn.LeakyReLU(0.2),
nn.Linear(128, 64), nn.Tanh()) # rasmlar [-1, 1] da
D = nn.Sequential(nn.Linear(64, 128), nn.LeakyReLU(0.2), nn.Linear(128, 128), nn.LeakyReLU(0.2),
nn.Linear(128, 1)) # logit
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 x in batchlar:
soxta = G(torch.randn(len(x), 16, generator=g))
d_loss = (F.binary_cross_entropy_with_logits(D(x), torch.ones(len(x), 1)) +
F.binary_cross_entropy_with_logits(D(soxta.detach()), torch.zeros(len(x), 1)))
opt_d.zero_grad(); d_loss.backward(); opt_d.step()
g_loss = F.binary_cross_entropy_with_logits(D(soxta), torch.ones(len(x), 1)) # non-saturating
opt_g.zero_grad(); g_loss.backward(); opt_g.step()
# barqaror minimax ko'rinishi (faqat solishtirish uchun)
g_minimax = F.logsigmoid(-D(soxta)).mean() # log(1 - D)
# baholash: loss emas - namunalar
xs = (G(z_bah) + 1) / 2 # [0, 1] ga
ishonch, entropiya, frechet = olchovlar(hakam, xs, x_test)GAN xulosasi
min_G max_D E[log D(x)] + E[log(1 - D(G(z)))]; D* = p_data / (p_data + p_g)
optimumda JSD = 0, D = 0.5, V = -log 4
G uchun non-saturating: -log D(G(z)) - kuchli D da ham gradient
navbat: D (soxta.detach) -> G; Adam(2e-4, betas = (0.5, 0.999)); tanh + [-1, 1]
loss - o'yin holati; sifat - hakam, entropiya, Frechet, yodlash
GAN: o'tkir, lekin beqaror va mode collapse ga moyil4. Batafsil misollar
Misollar real torch/numpy bilan (Python 3.14, torch 2.14 CPU).
Misol 1 — O'yin matematikasi: optimal D va non-saturating loss
"""GAN o'yini: optimal diskriminator, minimax qiymati va non-saturating generator loss."""
import math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
def tarmoq(kirish, chiqish, h=64):
return nn.Sequential(nn.Linear(kirish, h), nn.LeakyReLU(0.2), nn.Linear(h, h), nn.LeakyReLU(0.2),
nn.Linear(h, chiqish))
def normal_zichlik(x, mu):
return np.exp(-0.5 * (x - mu) ** 2) / math.sqrt(2 * math.pi)
def main() -> None:
torch.set_num_threads(1)
print("=== 1. Generator gradienti: d(loss)/d(logit), logit - D ning soxta namunaga chiqishi ===")
print(" D(G(z)) minimax: log(1 - D) non-saturating: -log D")
for d in (0.001, 0.01, 0.1, 0.5, 0.9):
logit = torch.tensor(math.log(d / (1 - d)), requires_grad=True)
torch.log1p(-torch.sigmoid(logit)).backward() # G buni MINIMALLASHTIRADI
g_mm = logit.grad.item()
logit = torch.tensor(math.log(d / (1 - d)), requires_grad=True)
(-F.logsigmoid(logit)).backward()
g_ns = logit.grad.item()
print(f" {d:<8} {g_mm:>14.4f} {g_ns:>22.4f}")
print(" D soxtani ishonch bilan rad etganda (D -> 0) minimax gradienti ham -> 0")
print("\n=== 2. Optimal diskriminator (1D): p_data = N(0, 1), p_g = N(2, 1) ===")
torch.manual_seed(0)
D = tarmoq(1, 1, 32)
opt = torch.optim.Adam(D.parameters(), lr=3e-3)
g = torch.Generator().manual_seed(0)
for _ in range(1500):
xr = torch.randn(256, 1, generator=g)
xg = 2 + torch.randn(256, 1, generator=g)
loss = (F.binary_cross_entropy_with_logits(D(xr), torch.ones(256, 1)) +
F.binary_cross_entropy_with_logits(D(xg), torch.zeros(256, 1)))
opt.zero_grad()
loss.backward()
opt.step()
print(" x D(x) o'rgatilgan D*(x) = p_data / (p_data + p_g)")
for x in (-1.0, 0.0, 1.0, 2.0, 3.0):
with torch.no_grad():
dx = torch.sigmoid(D(torch.tensor([[x]]))).item()
ds = normal_zichlik(x, 0) / (normal_zichlik(x, 0) + normal_zichlik(x, 2))
print(f" {x:+.1f} {dx:>13.3f} {ds:>20.3f}")
with torch.no_grad():
xr = torch.randn(100000, 1, generator=g)
xg = 2 + torch.randn(100000, 1, generator=g)
v = (F.logsigmoid(D(xr)).mean() + F.logsigmoid(-D(xg)).mean()).item()
t = np.linspace(-8, 10, 20001)
pd, pg = normal_zichlik(t, 0), normal_zichlik(t, 2)
m = 0.5 * (pd + pg)
jsd = 0.5 * np.trapezoid(pd * np.log(pd / m), t) + 0.5 * np.trapezoid(pg * np.log(pg / m), t)
print(f" V(D, G) = E[log D(x)] + E[log(1 - D(G(z)))]: o'rgatilgan D bilan {v:.3f}")
print(f" nazariya: -log 4 + 2 * JSD = {-math.log(4):.3f} + 2 * {jsd:.3f} = {-math.log(4) + 2 * jsd:.3f}")
print(f" p_g = p_data bo'lsa: JSD = 0, V = -log 4 = {-math.log(4):.3f}, D* = 0.5 hamma joyda")
print("\n=== 3. Kuchli D qarshisida: generator qaysi loss bilan qochib chiqadi? (2D) ===")
modalar = torch.tensor([[2 * math.cos(2 * math.pi * k / 8), 2 * math.sin(2 * math.pi * k / 8)]
for k in range(8)])
natija = {}
print(" avval faqat D 300 qadam o'rgatiladi; keyin D muzlatiladi va G 100 ta SGD qadami qiladi")
for tur in ("minimax", "non-saturating"):
torch.manual_seed(1)
G, D = tarmoq(2, 2), tarmoq(2, 1)
opt_d = torch.optim.Adam(D.parameters(), lr=1e-3, betas=(0.5, 0.999))
opt_g = torch.optim.SGD(G.parameters(), lr=0.01) # oddiy gradient: Adam uni normallab yuboradi
g = torch.Generator().manual_seed(1)
for _ in range(300): # faqat D o'rganadi: soxtani yaxshi taniydi
x = modalar[torch.randint(0, 8, (128,), generator=g)] + 0.05 * torch.randn(128, 2, generator=g)
with torch.no_grad():
soxta = G(torch.randn(128, 2, generator=g))
loss = (F.binary_cross_entropy_with_logits(D(x), torch.ones(128, 1)) +
F.binary_cross_entropy_with_logits(D(soxta), torch.zeros(128, 1)))
opt_d.zero_grad()
loss.backward()
opt_d.step()
z = torch.randn(1000, 2, generator=torch.Generator().manual_seed(7))
logit = D(G(z))
gl = F.logsigmoid(-logit).mean() if tur == "minimax" else -F.logsigmoid(logit).mean()
G.zero_grad()
gl.backward()
norma = math.sqrt(sum((p.grad ** 2).sum().item() for p in G.parameters()))
d0 = torch.sigmoid(logit).mean().item()
if tur == "minimax":
print(f" boshlang'ich holat: D(G(z)) = {d0:.4f} - D soxtani deyarli aniq rad etadi")
for p in D.parameters():
p.requires_grad_(False) # D muzlatilgan: faqat G qadam qiladi
yol = []
for q in range(100):
logit = D(G(torch.randn(128, 2, generator=g)))
gl = F.logsigmoid(-logit).mean() if tur == "minimax" else -F.logsigmoid(logit).mean()
opt_g.zero_grad()
gl.backward()
opt_g.step()
if (q + 1) % 25 == 0:
with torch.no_grad():
yol.append(torch.sigmoid(D(G(z))).mean().item())
natija[tur] = (d0, norma)
print(f" {tur:<15} ||grad G|| = {norma:.5f}; D(G(z)) 25/50/75/100 SGD qadamda: "
+ " ".join(f"{v:.4f}" for v in yol))
nisbat = natija["non-saturating"][1] / natija["minimax"][1]
print(f" gradient normasi nisbati (ns / minimax): {nisbat:.0f} barobar")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Generator gradienti: d(loss)/d(logit), logit - D ning soxta namunaga chiqishi ===
D(G(z)) minimax: log(1 - D) non-saturating: -log D
0.001 -0.0010 -0.9990
0.01 -0.0100 -0.9900
0.1 -0.1000 -0.9000
0.5 -0.5000 -0.5000
0.9 -0.9000 -0.1000
D soxtani ishonch bilan rad etganda (D -> 0) minimax gradienti ham -> 0
=== 2. Optimal diskriminator (1D): p_data = N(0, 1), p_g = N(2, 1) ===
x D(x) o'rgatilgan D*(x) = p_data / (p_data + p_g)
-1.0 0.984 0.982
+0.0 0.885 0.881
+1.0 0.501 0.500
+2.0 0.108 0.119
+3.0 0.016 0.018
V(D, G) = E[log D(x)] + E[log(1 - D(G(z)))]: o'rgatilgan D bilan -0.712
nazariya: -log 4 + 2 * JSD = -1.386 + 2 * 0.337 = -0.713
p_g = p_data bo'lsa: JSD = 0, V = -log 4 = -1.386, D* = 0.5 hamma joyda
=== 3. Kuchli D qarshisida: generator qaysi loss bilan qochib chiqadi? (2D) ===
avval faqat D 300 qadam o'rgatiladi; keyin D muzlatiladi va G 100 ta SGD qadami qiladi
boshlang'ich holat: D(G(z)) = 0.0022 - D soxtani deyarli aniq rad etadi
minimax ||grad G|| = 0.01362; D(G(z)) 25/50/75/100 SGD qadamda: 0.0022 0.0023 0.0023 0.0024
non-saturating ||grad G|| = 4.62931; D(G(z)) 25/50/75/100 SGD qadamda: 0.9854 0.9940 0.9962 0.9972
gradient normasi nisbati (ns / minimax): 340 barobarNima ko'rsatdi: 1-bo'lim loss larning logit bo'yicha gradientini autograd bilan hisobladi: minimax loss uchun u -D, non-saturating uchun -(1 - D). D soxtani ishonch bilan rad etganda (D(G(z)) = 0.001) minimax gradienti -0.0010, non-saturating niki -0.9990 — ming barobar farq. D = 0.5 da ikkalasi teng. 2-bo'limda 1D da diskriminator o'rgatildi va uning chiqishi nazariy D* = p_data / (p_data + p_g) bilan solishtirildi: farq ko'pi bilan 0.011 (masalan x = 1 da 0.501 va 0.500). Qiymat funksiyasi ham nazariyaga mos: o'rgatilgan D bilan -0.712, -log 4 + 2 * JSD formulasi bo'yicha -0.713. 3-bo'lim amaliy manzara: D 300 qadam o'rgatilgach, soxtaga atigi 0.0022 ehtimol beradi. Shu holatda generator gradienti normasi minimax da 0.01362, non-saturating da 4.62931 — 340 barobar katta. Oddiy SGD bilan 100 qadamdan keyin minimax generator deyarli joyidan qo'zg'almadi (0.0022 -> 0.0024), non-saturating esa 25 qadamdayoq muzlatilgan D ni aldadi (0.9854). E'tibor bering: D muzlatilgani uchun bu "o'yin" emas — generator shunchaki bitta qotgan D ning zaif joyini topdi; bu yerda faqat gradient oqimini solishtiryapmiz. Bog'liq bo'limlar: 2.2, 2.3.
Misol 2 — 2D: sakkizta Gauss halqasi
"""GAN noldan 2D da: 8 ta Gauss halqasi, qamrab olingan modalar va sifatli namunalar ulushi."""
import math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
MODALAR = torch.tensor([[2 * math.cos(2 * math.pi * k / 8), 2 * math.sin(2 * math.pi * k / 8)]
for k in range(8)])
SIGMA = 0.05
def haqiqiy(n, g):
k = torch.randint(0, 8, (n,), generator=g)
return MODALAR[k] + SIGMA * torch.randn(n, 2, generator=g)
def tarmoq(kirish, chiqish, h=64):
return nn.Sequential(nn.Linear(kirish, h), nn.LeakyReLU(0.2), nn.Linear(h, h), nn.LeakyReLU(0.2),
nn.Linear(h, chiqish))
def orgat(seed, g_loss="ns", qadamlar=1500, B=128, z_dim=2):
"""Diskriminator va generator navbat bilan; g_loss: 'ns' (non-saturating) yoki 'minimax'."""
torch.manual_seed(seed)
G, D = tarmoq(z_dim, 2), tarmoq(2, 1)
opt_g = torch.optim.Adam(G.parameters(), lr=1e-3, betas=(0.5, 0.999))
opt_d = torch.optim.Adam(D.parameters(), lr=1e-3, betas=(0.5, 0.999))
g = torch.Generator().manual_seed(seed)
tarix = []
for q in range(qadamlar):
x = haqiqiy(B, g)
soxta = G(torch.randn(B, z_dim, generator=g))
# D: haqiqiy -> 1, soxta -> 0
d_loss = (F.binary_cross_entropy_with_logits(D(x), torch.ones(B, 1)) +
F.binary_cross_entropy_with_logits(D(soxta.detach()), torch.zeros(B, 1)))
opt_d.zero_grad()
d_loss.backward()
opt_d.step()
# G
logit = D(soxta)
if g_loss == "ns":
gl = F.binary_cross_entropy_with_logits(logit, torch.ones(B, 1)) # -log D(G(z))
else:
gl = -F.binary_cross_entropy_with_logits(logit, torch.zeros(B, 1)) # log(1 - D(G(z)))
opt_g.zero_grad()
gl.backward()
opt_g.step()
if (q + 1) % 500 == 0:
tarix.append((q + 1, d_loss.item(), gl.item()))
return G.eval(), tarix
@torch.no_grad()
def baholash(G, n=2000, z_dim=2, seed=99):
x = G(torch.randn(n, z_dim, generator=torch.Generator().manual_seed(seed)))
d = torch.cdist(x, MODALAR)
eng, k = d.min(1)
sifatli = eng < 3 * SIGMA
soni = torch.bincount(k[sifatli], minlength=8)
qamrov = int((soni >= 0.01 * n).sum()) # kamida 1% sifatli namuna bor modalar
return qamrov, sifatli.float().mean().item(), soni.tolist(), x
def ascii_zichlik(x, n=21):
chet = torch.linspace(-2.6, 2.6, n + 1)
c = torch.histogramdd(x.flip(1), bins=[chet, chet]).hist.flip(0)
c = (c / c.max()).sqrt()
return ["".join(" .:-=+*#%@"[min(9, int(v * 10))] for v in q) for q in c.tolist()]
def main() -> None:
torch.set_num_threads(1)
print("=== 1. Haqiqiy taqsimot: radiusi 2 bo'lgan halqada 8 ta Gauss (sigma 0.05) ===")
print(" 'sifatli' namuna: eng yaqin moda markazidan < 3 sigma = 0.15")
print(" moda 'qamrab olingan': unga namunalarning kamida 1% i sifatli tushgan")
q, s, _, _ = baholash(lambda z: haqiqiy(len(z), torch.Generator().manual_seed(5)))
print(f" haqiqiy ma'lumotning o'zi: qamrov {q}/8, sifatli {s:.3f}")
print("\n=== 2. Non-saturating GAN, 3 urug' (1500 qadam) ===")
natija = {}
for tur in ("ns", "minimax"):
natija[tur] = []
for s in range(3):
G, tarix = orgat(s, tur)
natija[tur].append(baholash(G) + (tarix,))
for s, (q, sf, soni, _, _) in enumerate(natija["ns"]):
print(f" urug' {s}: qamrov {q}/8, sifatli {sf:.3f}, modalar: {soni}")
print("\n=== 3. Saturating (minimax) generator loss bilan, xuddi shu urug'lar ===")
for s, (q, sf, soni, _, _) in enumerate(natija["minimax"]):
print(f" urug' {s}: qamrov {q}/8, sifatli {sf:.3f}, modalar: {soni}")
for i, nom in ((0, "qamrov"), (1, "sifatli")):
f = np.array([r[i] for r in natija["ns"]]) - np.array([r[i] for r in natija["minimax"]])
se = f.std(ddof=1) / math.sqrt(3)
print(f" ns - minimax, {nom:<7}: {f.mean():+.3f}, SE {se:.3f} -> "
f"{'sezilarli' if abs(f.mean()) > 2 * se else 'sezilarli emas'}")
print("\n=== 4. Loss lar (urug' 0, ns): D va G loss vaqt bo'yicha ===")
for qd, dl, gl in natija["ns"][0][4]:
print(f" qadam {qd:>4}: D loss {dl:.3f}, G loss {gl:.3f}")
print(f" (muvozanatda D(x) = 0.5: D loss = 2 ln 2 = {2 * math.log(2):.3f}, G loss = ln 2 = {math.log(2):.3f})")
print("\n=== 5. Namunalar zichligi (urug' 0, ns) va haqiqiy ===")
a = ascii_zichlik(natija["ns"][0][3])
b = ascii_zichlik(haqiqiy(2000, torch.Generator().manual_seed(5)))
print(f" {'GAN':<21} haqiqiy")
for p, r in zip(a, b):
print(f" {p} {r}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Haqiqiy taqsimot: radiusi 2 bo'lgan halqada 8 ta Gauss (sigma 0.05) ===
'sifatli' namuna: eng yaqin moda markazidan < 3 sigma = 0.15
moda 'qamrab olingan': unga namunalarning kamida 1% i sifatli tushgan
haqiqiy ma'lumotning o'zi: qamrov 8/8, sifatli 0.988
=== 2. Non-saturating GAN, 3 urug' (1500 qadam) ===
urug' 0: qamrov 8/8, sifatli 0.554, modalar: [40, 232, 58, 213, 41, 32, 271, 221]
urug' 1: qamrov 8/8, sifatli 0.460, modalar: [161, 27, 219, 45, 88, 200, 57, 123]
urug' 2: qamrov 8/8, sifatli 0.592, modalar: [52, 202, 174, 113, 62, 164, 193, 225]
=== 3. Saturating (minimax) generator loss bilan, xuddi shu urug'lar ===
urug' 0: qamrov 8/8, sifatli 0.513, modalar: [161, 33, 137, 190, 75, 104, 138, 188]
urug' 1: qamrov 8/8, sifatli 0.492, modalar: [129, 132, 193, 25, 118, 123, 168, 96]
urug' 2: qamrov 8/8, sifatli 0.512, modalar: [25, 152, 176, 152, 66, 160, 101, 191]
ns - minimax, qamrov : +0.000, SE 0.000 -> sezilarli emas
ns - minimax, sifatli: +0.030, SE 0.033 -> sezilarli emas
=== 4. Loss lar (urug' 0, ns): D va G loss vaqt bo'yicha ===
qadam 500: D loss 1.261, G loss 0.790
qadam 1000: D loss 1.128, G loss 1.007
qadam 1500: D loss 1.144, G loss 1.045
(muvozanatda D(x) = 0.5: D loss = 2 ln 2 = 1.386, G loss = ln 2 = 0.693)
=== 5. Namunalar zichligi (urug' 0, ns) va haqiqiy ===
GAN haqiqiy
.
:-=-. @
.::. .:::
=%. .-% #- -%
-. = -. .-
.: :.
: :
:. .:
- -
- - @ @.
- :
:: :
.:. :
-. .
.- - -. .-
.-: .-% %- -%
.... ::.
..-@: @
. .Nima ko'rsatdi: haqiqiy ma'lumotning o'zi o'lchovlarimizda qamrov 8/8, sifatli 0.988 beradi — bu shift. Non-saturating GAN uchala urug'da ham 8 ta modaning hammasini qamradi, sifatli namunalar esa 0.554, 0.460 va 0.592. Namunalarning qolgan qismi qayerda? 5-bo'limdagi ASCII javob beradi: GAN modalarni ko'priklar bilan ulagan — halqa bo'ylab ingichka iz. Sababi 2.5-bo'limda: generator uzluksiz funksiya, u yaxlit z fazosini sakkizta ajralgan orolga "uzib" akslantira olmaydi. Modalar bo'yicha sonlar ham notekis (masalan urug' 0 da 32 dan 271 gacha) — bu qisman mode collapse belgisi. 3-bo'lim: asl minimax loss bu yerda ham ishladi (qamrov 8/8, sifatli 0.513, 0.492, 0.512); farq +0.030, SE 0.033 — sezilarli emas. Bu 1-misolga zid emas: bu yerda D va G muvozanatda o'rgatiladi va Adam gradient masshtabini normallaydi, shuning uchun to'yinish sezilmaydi; u D keskin ustun bo'lganda xavfli. 4-bo'lim loss lar: D loss 1.261 -> 1.128 -> 1.144, G loss 0.790 -> 1.007 -> 1.045 — muvozanat qiymatlaridan (1.386 va 0.693) uzoq emas, lekin ular namunalarning 0.554 qismi sifatli ekanini umuman aytmaydi. Bog'liq bo'limlar: 2.4, 2.5, 2.6.
Misol 3 — Digits da GAN: dinamika va namunalar
"""GAN digits (8x8) da: o'rgatish dinamikasi, namunalar, tanib olinish va sinf qamrovi."""
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
BELGI = " .:-=+*#%@"
Z = 16
def ascii_qator(rasmlar, izohlar):
satrlar = [" ".join(f"{s:<8}" for s in izohlar)]
for r in range(8):
satrlar.append(" ".join("".join(BELGI[min(9, int(v * 10))] for v in im.reshape(8, 8)[r])
for im in np.clip(rasmlar, 0, 1)))
return satrlar
def malumot():
d = load_digits()
x_o, x_t, y_o, y_t = train_test_split(d.data / 16, d.target, test_size=0.25,
stratify=d.target, random_state=0)
f = lambda a: torch.tensor(a, dtype=torch.float32)
return f(x_o), f(x_t), torch.tensor(y_o), torch.tensor(y_t)
class Klassifikator(nn.Module):
def __init__(self):
super().__init__()
self.h = nn.Linear(64, 128)
self.chiq = nn.Linear(128, 10)
def forward(self, x):
return self.chiq(F.relu(self.h(x)))
def hakam_orgat(x, y, seed=0):
torch.manual_seed(seed)
m = Klassifikator()
opt = torch.optim.Adam(m.parameters(), lr=1e-2, weight_decay=1e-4)
g = torch.Generator().manual_seed(seed)
for _ in range(60):
for idx in torch.randperm(len(x), generator=g).split(64):
loss = F.cross_entropy(m(x[idx]), y[idx])
opt.zero_grad()
loss.backward()
opt.step()
return m.eval()
@torch.no_grad()
def olchovlar(hakam, x, x_ref):
"""Ishonch > 0.9 ulushi, sinf entropiyasi (0..1) va hakam xususiyatlarida Frechet masofa."""
p = torch.softmax(hakam(x), -1)
soni = np.bincount(p.argmax(1).numpy(), minlength=10)
u = soni[soni > 0] / soni.sum()
entr = float(-(u * np.log(u)).sum() / np.log(10))
f1, f2 = F.relu(hakam.h(x)).numpy().astype(np.float64), F.relu(hakam.h(x_ref)).numpy().astype(np.float64)
m1, m2 = f1.mean(0), f2.mean(0)
c1, c2 = np.cov(f1, rowvar=False), np.cov(f2, rowvar=False)
w, v = np.linalg.eigh(c1)
s1 = (v * np.sqrt(np.clip(w, 0, None))) @ v.T # sqrt(C1)
iz = np.sqrt(np.clip(np.linalg.eigvalsh(s1 @ c2 @ s1), 0, None)).sum() # tr sqrt(C1 C2)
fd = float(((m1 - m2) ** 2).sum() + np.trace(c1) + np.trace(c2) - 2 * iz)
return (p.max(1).values > 0.9).float().mean().item(), entr, fd, soni
def gan_orgat(x_o, hakam, x_t, tanh=True, seed=0, qadamlar=3000, B=64, har=500):
"""Non-saturating GAN; tanh=True: rasmlar [-1, 1] ga o'tkaziladi, G chiqishi tanh."""
torch.manual_seed(seed)
G = nn.Sequential(nn.Linear(Z, 128), nn.LeakyReLU(0.2), nn.Linear(128, 128), nn.LeakyReLU(0.2),
nn.Linear(128, 64), nn.Tanh() if tanh else nn.Sigmoid())
D = nn.Sequential(nn.Linear(64, 128), nn.LeakyReLU(0.2), nn.Linear(128, 128), nn.LeakyReLU(0.2),
nn.Linear(128, 1))
opt_g = torch.optim.Adam(G.parameters(), lr=2e-4, betas=(0.5, 0.999), foreach=True)
opt_d = torch.optim.Adam(D.parameters(), lr=2e-4, betas=(0.5, 0.999), foreach=True)
g = torch.Generator().manual_seed(seed)
xo = x_o * 2 - 1 if tanh else x_o
rasm = (lambda t: (t + 1) / 2) if tanh else (lambda t: t) # [0, 1] ga qaytarish
z_bah = torch.randn(1000, Z, generator=torch.Generator().manual_seed(9))
tarix, qator = [], []
for q in range(1, qadamlar + 1):
x = xo[torch.randint(0, len(xo), (B,), generator=g)]
soxta = G(torch.randn(B, Z, generator=g))
l_h, l_s = D(x), D(soxta.detach())
d_loss = (F.binary_cross_entropy_with_logits(l_h, torch.ones(B, 1)) +
F.binary_cross_entropy_with_logits(l_s, torch.zeros(B, 1)))
opt_d.zero_grad()
d_loss.backward()
opt_d.step()
g_loss = F.binary_cross_entropy_with_logits(D(soxta), torch.ones(B, 1)) # non-saturating
opt_g.zero_grad()
g_loss.backward()
opt_g.step()
tarix.append((d_loss.item(), g_loss.item(), torch.sigmoid(l_h).mean().item(),
torch.sigmoid(l_s).mean().item()))
if har and q % har == 0:
with torch.no_grad():
xs = rasm(G(z_bah))
ish, entr, fd, _ = olchovlar(hakam, xs, x_t)
qator.append((q, *np.mean(tarix[-100:], 0), ish, entr, fd))
with torch.no_grad():
xs = rasm(G(z_bah))
return xs, qator
def main() -> None:
torch.set_num_threads(1)
x_o, x_t, y_o, y_t = malumot()
hakam = hakam_orgat(x_o, y_o)
ish, entr, fd, _ = olchovlar(hakam, x_o[:450], x_t)
print("=== 1. Ma'lumotnoma: haqiqiy o'quv rasmlari (450 ta) test ga nisbatan ===")
print(f" ishonch>0.9 {ish:.3f}, sinf entropiyasi {entr:.3f}, Frechet masofa {fd:.1f}")
print("\n=== 2. O'rgatish dinamikasi: G (16 -> 128 -> 128 -> 64, tanh), D (64 -> 128 -> 128 -> 1) ===")
xs, qator = gan_orgat(x_o, hakam, x_t)
print(" qadam D loss G loss D(x) D(G(z)) ishonch>0.9 entropiya Frechet")
for q, dl, gl, dx, dg, ish, entr, fd in qator:
print(f" {q:>5} {dl:>8.3f} {gl:>8.3f} {dx:>6.2f} {dg:>9.2f} {ish:>12.3f} {entr:>11.3f} {fd:>9.1f}")
kor = np.corrcoef([r[1] for r in qator], [r[7] for r in qator])[0, 1]
print(f" D loss va Frechet korrelyatsiyasi (6 nuqta): {kor:+.2f}")
print("\n=== 3. Yakuniy namunalar (3000 qadam) ===")
ish, entr, fd, soni = olchovlar(hakam, xs, x_t)
print(f" ishonch>0.9 {ish:.3f}, entropiya {entr:.3f}, Frechet {fd:.1f}; sinflar: {' '.join(map(str, soni))}")
p = hakam(xs[:8]).argmax(1).tolist()
for s in ascii_qator(xs[:8].numpy(), [f"GAN:{a}" for a in p]):
print(" " + s)
d_test = torch.cdist(x_t, x_o).min(1).values
chegara = torch.quantile(d_test, 0.05)
d_gan = torch.cdist(xs, x_o).min(1).values
print(f" eng yaqin o'quv rasmigacha: test median {d_test.median():.3f}, GAN median {d_gan.median():.3f}; "
f"nusxa (< {chegara:.3f}): GAN {(d_gan < chegara).float().mean():.3f}")
print("\n=== 4. Xuddi shu GAN, lekin rasmlar [0, 1] va G chiqishi sigmoid ===")
xs2, qator2 = gan_orgat(x_o, hakam, x_t, tanh=False, qadamlar=2000, har=1000)
for q, dl, gl, dx, dg, ish, entr, fd in qator2:
print(f" {q:>5} D loss {dl:.3f}, D(G(z)) {dg:.2f}, ishonch>0.9 {ish:.3f}, "
f"entropiya {entr:.3f}, Frechet {fd:.1f}")
_, _, _, soni2 = olchovlar(hakam, xs2, x_t)
print(f" sinflar: {' '.join(map(str, soni2))}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumotnoma: haqiqiy o'quv rasmlari (450 ta) test ga nisbatan ===
ishonch>0.9 1.000, sinf entropiyasi 0.998, Frechet masofa 0.7
=== 2. O'rgatish dinamikasi: G (16 -> 128 -> 128 -> 64, tanh), D (64 -> 128 -> 128 -> 1) ===
qadam D loss G loss D(x) D(G(z)) ishonch>0.9 entropiya Frechet
500 0.840 1.280 0.72 0.36 0.228 0.518 44.0
1000 0.983 1.260 0.65 0.35 0.542 0.964 15.8
1500 0.901 1.432 0.68 0.32 0.594 0.846 16.6
2000 1.056 1.223 0.63 0.36 0.605 0.936 13.2
2500 1.002 1.312 0.65 0.35 0.571 0.972 9.3
3000 0.971 1.237 0.65 0.35 0.601 0.957 8.1
D loss va Frechet korrelyatsiyasi (6 nuqta): -0.79
=== 3. Yakuniy namunalar (3000 qadam) ===
ishonch>0.9 0.601, entropiya 0.957, Frechet 8.1; sinflar: 142 115 124 107 97 25 117 117 131 25
GAN:7 GAN:8 GAN:5 GAN:3 GAN:7 GAN:3 GAN:7 GAN:1
+@+ -@@## *@@+ +@%# .@@=. :%@* =++- %@-.
+%@: @.+@: @++% @-#@= ***@ @*-% %@ % -=%@
:%@- = :+ .@.. : #- .@@. .@ .* %@#% -@@
:*=+:. .@@ @@@. @@ +.@:*= .=*@+ : %#% *%@ -.
*@@@=. +@@ .%@- @@ :#%+%: -%% =+@@@. :@@#*
@@@ .@@ . *+: @@ =%@-- .%= . %@@ -#@*.
#%@ #:*: @.#@ --%+ :%+@. *--@: %@. :##@=
++ .*. +@@+ -#: @@= .@@= : @#@
eng yaqin o'quv rasmigacha: test median 1.044, GAN median 1.688; nusxa (< 0.714): GAN 0.000
=== 4. Xuddi shu GAN, lekin rasmlar [0, 1] va G chiqishi sigmoid ===
1000 D loss 0.354, D(G(z)) 0.13, ishonch>0.9 0.220, entropiya 0.281, Frechet 59.8
2000 D loss 0.253, D(G(z)) 0.08, ishonch>0.9 0.561, entropiya 0.381, Frechet 66.8
sinflar: 0 0 0 0 0 137 0 0 654 209Nima ko'rsatdi: 1-bo'lim ma'lumotnoma: 450 ta haqiqiy o'quv rasmi hakamga 1.000 ishonchli, sinf entropiyasi 0.998, test bilan Frechet masofa 0.7 (namuna kichikligidan nol emas). 2-bo'lim — dinamika. D loss 1000-qadamdan keyin 0.90 .. 1.06 oralig'ida, D(x) ~ 0.65, D(G(z)) ~ 0.35 — o'yin barqaror. Shu paytda Frechet 15.8 dan 8.1 gacha yaxshilandi, ya'ni sifat D loss ga qaraganda ancha ko'p o'zgardi. D loss va Frechet orasidagi korrelyatsiya -0.79 — lekin u asosan birinchi nuqtadan keladi (500-qadamda D loss past, Frechet yomon); 1000-qadamdan keyin D loss deyarli tekis, Frechet esa pasayishda davom etdi. 3-bo'lim: yakuniy namunalar o'tkir va taniladigan (ishonch 0.601, entropiya 0.957), lekin sinflar notekis — 5 va 9 atigi 25 tadan: qisman mode collapse. Yodlash yo'q: GAN namunalari o'quv rasmlaridan test rasmlariga qaraganda uzoqroq (median 1.688 va 1.044), nusxa ulushi 0.000. 4-bo'lim — kichik detalning narxi: xuddi shu GAN rasmlar [0, 1] da va sigmoid chiqish bilan butunlay buzildi. D ustun keldi (D loss 0.253, D(G(z)) = 0.08), Frechet 59.8 -> 66.8 ga yomonlashdi, 1000 namunaning 654 tasi 8 — faqat 3 ta sinf. Bog'liq bo'limlar: 2.4, 2.6, 2.9.
Misol 4 — GAN va VAE bir xil o'lchovlarda
"""GAN va VAE ni bir xil o'lchovlarda solishtirish: tanib olinish, qamrov, Frechet, yodlash."""
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
BELGI = " .:-=+*#%@"
Z = 16
def ascii_qator(rasmlar, izohlar):
satrlar = [" ".join(f"{s:<8}" for s in izohlar)]
for r in range(8):
satrlar.append(" ".join("".join(BELGI[min(9, int(v * 10))] for v in im.reshape(8, 8)[r])
for im in np.clip(rasmlar, 0, 1)))
return satrlar
def malumot():
d = load_digits()
x_o, x_t, y_o, y_t = train_test_split(d.data / 16, d.target, test_size=0.25,
stratify=d.target, random_state=0)
f = lambda a: torch.tensor(a, dtype=torch.float32)
return f(x_o), f(x_t), torch.tensor(y_o), torch.tensor(y_t)
class Klassifikator(nn.Module):
def __init__(self):
super().__init__()
self.h = nn.Linear(64, 128)
self.chiq = nn.Linear(128, 10)
def forward(self, x):
return self.chiq(F.relu(self.h(x)))
def hakam_orgat(x, y, seed=0):
torch.manual_seed(seed)
m = Klassifikator()
opt = torch.optim.Adam(m.parameters(), lr=1e-2, weight_decay=1e-4)
g = torch.Generator().manual_seed(seed)
for _ in range(60):
for idx in torch.randperm(len(x), generator=g).split(64):
loss = F.cross_entropy(m(x[idx]), y[idx])
opt.zero_grad()
loss.backward()
opt.step()
return m.eval()
@torch.no_grad()
def olchovlar(hakam, x, x_ref):
"""Ishonch > 0.9 ulushi, sinf entropiyasi (0..1) va hakam xususiyatlarida Frechet masofa."""
p = torch.softmax(hakam(x), -1)
soni = np.bincount(p.argmax(1).numpy(), minlength=10)
u = soni[soni > 0] / soni.sum()
entr = float(-(u * np.log(u)).sum() / np.log(10))
f1, f2 = F.relu(hakam.h(x)).numpy().astype(np.float64), F.relu(hakam.h(x_ref)).numpy().astype(np.float64)
m1, m2 = f1.mean(0), f2.mean(0)
c1, c2 = np.cov(f1, rowvar=False), np.cov(f2, rowvar=False)
w, v = np.linalg.eigh(c1)
s1 = (v * np.sqrt(np.clip(w, 0, None))) @ v.T # sqrt(C1)
iz = np.sqrt(np.clip(np.linalg.eigvalsh(s1 @ c2 @ s1), 0, None)).sum() # tr sqrt(C1 C2)
fd = float(((m1 - m2) ** 2).sum() + np.trace(c1) + np.trace(c2) - 2 * iz)
return (p.max(1).values > 0.9).float().mean().item(), entr, fd, soni
def gan_orgat(x_o, hakam, x_t, tanh=True, seed=0, qadamlar=3000, B=64, har=500):
"""Non-saturating GAN; tanh=True: rasmlar [-1, 1] ga o'tkaziladi, G chiqishi tanh."""
torch.manual_seed(seed)
G = nn.Sequential(nn.Linear(Z, 128), nn.LeakyReLU(0.2), nn.Linear(128, 128), nn.LeakyReLU(0.2),
nn.Linear(128, 64), nn.Tanh() if tanh else nn.Sigmoid())
D = nn.Sequential(nn.Linear(64, 128), nn.LeakyReLU(0.2), nn.Linear(128, 128), nn.LeakyReLU(0.2),
nn.Linear(128, 1))
opt_g = torch.optim.Adam(G.parameters(), lr=2e-4, betas=(0.5, 0.999), foreach=True)
opt_d = torch.optim.Adam(D.parameters(), lr=2e-4, betas=(0.5, 0.999), foreach=True)
g = torch.Generator().manual_seed(seed)
xo = x_o * 2 - 1 if tanh else x_o
rasm = (lambda t: (t + 1) / 2) if tanh else (lambda t: t) # [0, 1] ga qaytarish
z_bah = torch.randn(1000, Z, generator=torch.Generator().manual_seed(9))
tarix, qator = [], []
for q in range(1, qadamlar + 1):
x = xo[torch.randint(0, len(xo), (B,), generator=g)]
soxta = G(torch.randn(B, Z, generator=g))
l_h, l_s = D(x), D(soxta.detach())
d_loss = (F.binary_cross_entropy_with_logits(l_h, torch.ones(B, 1)) +
F.binary_cross_entropy_with_logits(l_s, torch.zeros(B, 1)))
opt_d.zero_grad()
d_loss.backward()
opt_d.step()
g_loss = F.binary_cross_entropy_with_logits(D(soxta), torch.ones(B, 1)) # non-saturating
opt_g.zero_grad()
g_loss.backward()
opt_g.step()
tarix.append((d_loss.item(), g_loss.item(), torch.sigmoid(l_h).mean().item(),
torch.sigmoid(l_s).mean().item()))
if har and q % har == 0:
with torch.no_grad():
xs = rasm(G(z_bah))
ish, entr, fd, _ = olchovlar(hakam, xs, x_t)
qator.append((q, *np.mean(tarix[-100:], 0), ish, entr, fd))
with torch.no_grad():
xs = rasm(G(z_bah))
return xs, qator
class VAE(nn.Module):
"""26.2 dagi VAE: d = 8, Bernoulli decoder."""
def __init__(self, d=8):
super().__init__()
self.enc = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 2 * d))
self.dec = nn.Sequential(nn.Linear(d, 128), nn.ReLU(), nn.Linear(128, 64))
def vae_orgat(x, seed=0, davr=40, B=128):
torch.manual_seed(seed)
m = VAE()
opt = torch.optim.Adam(m.parameters(), lr=3e-3)
g = torch.Generator().manual_seed(seed)
for _ in range(davr):
for idx in torch.randperm(len(x), generator=g).split(B):
mu, logvar = m.enc(x[idx]).chunk(2, dim=1)
z = mu + torch.exp(0.5 * logvar) * torch.randn(mu.shape, generator=g)
qayta = F.binary_cross_entropy_with_logits(m.dec(z), x[idx], reduction="none").sum(1)
kl = 0.5 * (mu ** 2 + logvar.exp() - 1 - logvar).sum(1)
loss = (qayta + kl).mean()
opt.zero_grad()
loss.backward()
opt.step()
with torch.no_grad():
return torch.sigmoid(m.dec(torch.randn(1000, 8, generator=torch.Generator().manual_seed(9))))
def main() -> None:
torch.set_num_threads(1)
x_o, x_t, y_o, y_t = malumot()
hakam = hakam_orgat(x_o, y_o)
d_test = torch.cdist(x_t, x_o).min(1).values
chegara = torch.quantile(d_test, 0.05)
def hammasi(xs):
ish, entr, fd, soni = olchovlar(hakam, xs, x_t)
with torch.no_grad():
ys = hakam(xs).argmax(1)
xilma = float(np.mean([torch.pdist(xs[ys == k]).mean().item() for k in range(10) if (ys == k).sum() > 1]))
nusxa = (torch.cdist(xs, x_o).min(1).values < chegara).float().mean().item()
keskinlik = (torch.minimum(xs, 1 - xs) < 0.1).float().mean().item() # 0 yoki 1 ga yaqin piksel
return [ish, entr, fd, soni.min() / soni.sum(), xilma, nusxa, keskinlik]
nomlar = ["ishonch>0.9", "sinf entropiya", "Frechet", "eng kam sinf", "sinf ichi xilma",
"nusxa ulushi", "keskin piksel"]
print("=== 1. Ma'lumotnoma: haqiqiy o'quv rasmlari (450 ta) ===")
ref = hammasi(x_o[:450])
ref[5] = float("nan") # o'quv rasmi o'zi bilan - nusxa ma'nosiz
print(" " + ", ".join(f"{n} {v:.3f}" for n, v in zip(nomlar, ref) if not np.isnan(v)))
print("\n=== 2. GAN (1500 qadam) va VAE (40 davr), 3 urug' ===")
natija = {"GAN": [], "VAE": []}
for s in range(3):
xs, _ = gan_orgat(x_o, hakam, x_t, seed=s, qadamlar=1500, har=0)
natija["GAN"].append(hammasi(xs))
xv = vae_orgat(x_o, seed=s)
natija["VAE"].append(hammasi(xv))
if s == 0:
namuna = {"GAN": xs[:6].numpy(), "VAE": xv[:6].numpy()}
print(f" {'olchov':<16} {'GAN':>8} {'VAE':>8} {'haqiqiy':>8} GAN - VAE (SE)")
for i, n in enumerate(nomlar):
a, b = np.array([r[i] for r in natija["GAN"]]), np.array([r[i] for r in natija["VAE"]])
f = a - b
se = f.std(ddof=1) / np.sqrt(3)
belgi = "sezilarli" if abs(f.mean()) > 2 * se else "sezilarli emas"
haq = "" if np.isnan(ref[i]) else f"{ref[i]:.3f}"
print(f" {n:<16} {a.mean():>8.3f} {b.mean():>8.3f} {haq:>8} {f.mean():+.3f} ({se:.3f}) {belgi}")
print("\n=== 3. Namunalar (urug' 0) ===")
for nom in ("GAN", "VAE"):
with torch.no_grad():
p = hakam(torch.tensor(namuna[nom])).argmax(1).tolist()
for s in ascii_qator(namuna[nom], [f"{nom}:{a}" for a in p]):
print(" " + s)
print("\n=== 4. Qaror (natijadan) ===")
g_fd = np.mean([r[2] for r in natija["GAN"]])
v_fd = np.mean([r[2] for r in natija["VAE"]])
yaxshi = "GAN" if g_fd < v_fd else "VAE"
print(f" Frechet bo'yicha yaxshiroq: {yaxshi} ({min(g_fd, v_fd):.1f} va {max(g_fd, v_fd):.1f})")
fd = np.array([r[2] for r in natija["GAN"]])
print(f" GAN Frechet urug'lar bo'yicha: {' '.join(f'{v:.1f}' for v in fd)} - beqarorlik shu yerda ko'rinadi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumotnoma: haqiqiy o'quv rasmlari (450 ta) ===
ishonch>0.9 1.000, sinf entropiya 0.998, Frechet 0.712, eng kam sinf 0.087, sinf ichi xilma 2.256, keskin piksel 0.656
=== 2. GAN (1500 qadam) va VAE (40 davr), 3 urug' ===
olchov GAN VAE haqiqiy GAN - VAE (SE)
ishonch>0.9 0.632 0.568 1.000 +0.063 0.022-bob sezilarli
sinf entropiya 0.861 0.995 0.998 -0.135 0.047-bob sezilarli
Frechet 17.877 6.274 0.712 +11.603 2.605-bob sezilarli
eng kam sinf 0.009 0.076 0.087 -0.067 0.005-bob sezilarli
sinf ichi xilma 1.715 1.192 2.256 +0.524 0.106-bob sezilarli
nusxa ulushi 0.000 0.006 -0.006 0.001-bob sezilarli
keskin piksel 0.753 0.452 0.656 +0.301 0.007-bob sezilarli
=== 3. Namunalar (urug' 0) ===
GAN:7 GAN:7 GAN:3 GAN:5 GAN:3 GAN:3
..@= +*@@* .@@ *-@@- .@@ .@@
@@*@ @@.@= .@+@. -@:+. .@#@% :@:%.
@. * - - @ + . @#@@ .@ :.
..@ . .#+ .+*+ . *-.. %@ :==#
.%#%@ .@++@: @@@ .@@@@ %@@ @%@:
:@@@ = :+ %. : @@ .-.:. %*
@%@. :.@ =-%* . @ @%*@ --#*.
*@. *@ @@*- %@ @#@% @@%%
VAE:4 VAE:7 VAE:0 VAE:6 VAE:4 VAE:6
:#: -#%#. .#* #* =#- :%*.
.%#: -%=*%. *%#= -@= :%*- #@-:
*#:=- #- ## .%+.+ %%. ##--. .@- .
:@-=%- .#-+@= :%- +: .@*-. :%*+- -@. .
=@+#@: .++#@. :%:.#+ :%%%= :#*%+. -@=+=.
-#*%# .%+ .@. *+ .#%#+. .++%*. .@+=#-
.-@- -% #*-#= =##=. :=%+ ##*#:
-#. +- .*%* #%= +%= :%%=
=== 4. Qaror (natijadan) ===
Frechet bo'yicha yaxshiroq: VAE (6.3 va 17.9)
GAN Frechet urug'lar bo'yicha: 16.6 14.1 23.0 - beqarorlik shu yerda ko'rinadiNima ko'rsatdi: GAN (1500 qadam) va 26.2-darsdagi VAE (40 davr) uch urug'da bir xil hakam, bir xil 1000 ta z va yettita o'lchov bilan solishtirildi; hamma farqlar 2 * SE dan katta. GAN hakamga ko'proq ishonchli (0.632 va 0.568), namunalari o'tkir: 0 ga yoki 1 ga yaqin piksellar ulushi 0.753 — haqiqiy rasmlardagidan (0.656) ham ko'p, VAE da 0.452 (xira). Sinf ichidagi xilma-xillik ham GAN da yuqori (1.715 va 1.192; haqiqiy 2.256). Lekin qamrovda VAE aniq yutadi: sinf entropiyasi 0.995 va 0.861, eng kam sinf ulushi 0.076 va 0.009 — GAN ba'zi raqamlarni deyarli chizmaydi. Frechet masofa bu ikkisini bitta songa yig'adi va VAE ni ancha yaxshi deydi (6.274 va 17.877). GAN ning Frechet i urug'lar bo'yicha 16.6, 14.1, 23.0 — keng tarqoq, bu beqarorlikning raqamli ko'rinishi. Nusxa ulushi ikkalasida ham deyarli nol (0.000 va 0.006). Halol eslatma: bu kichik ma'lumot va kichik modellar, byudjetlar teng emas; 3-misolda GAN 3000 qadamda Frechet 8.1 ga yetgan. Xulosa bitta raqam emas: GAN — o'tkirlik va xilma-xillik, VAE — qamrov va barqarorlik. Bog'liq bo'limlar: 2.7, 2.8, 2.9.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "GAN p(x) ni o'rganadi va uni hisoblasa bo'ladi" | GAN faqat namuna chiqaradi; likelihood yo'q |
| "Asl minimax loss — to'g'ri, non-saturating — hiyla" | Kuchli D da minimax gradienti 340 barobar kichik (0.01362 va 4.62931) |
| "G loss kamaysa — namunalar yaxshilanyapti" | 3-misolda D loss deyarli tekis, Frechet esa 15.8 -> 8.1 |
| "D loss nolga yaqin — D yaxshi, demak hammasi joyida" | Sigmoid variantida D loss 0.253, D(G(z)) = 0.08 — G buzilgan, 3 ta sinf |
| "2D da hamma moda qamrab olinsa — GAN mukammal" | 8/8, lekin sifatli atigi 0.460 .. 0.592 — ko'priklar |
"[0, 1] yoki [-1, 1] — farqi yo'q" |
Sigmoid + [0, 1]: Frechet 66.8; tanh + [-1, 1]: 8.1 |
| "GAN har doim VAE dan yaxshi" | Bu tajribada Frechet va qamrovda VAE yutdi; o'tkirlikda GAN |
| "Frechet past — hamma jihatdan yaxshi" | U o'tkirlik va qamrovni aralashtiradi; qismlarini alohida o'lchang |
| "Bitta urug' yetadi" | GAN Frechet i urug'lar bo'yicha 14.1 .. 23.0 |
6. Keng tarqalgan xatolar va yechimlari
1. D qadamida detach yo'q
d_loss = bce(D(x), 1) + bce(D(G(z)), 0); d_loss.backward() # G ga ham grad # ⚠️
soxta = G(z)
d_loss = bce(D(x), 1) + bce(D(soxta.detach()), 0) # ✅2. Asl minimax generator loss
g_loss = torch.log(1 - torch.sigmoid(D(soxta))).mean() # to'yinadi, nan # ⚠️
g_loss = F.binary_cross_entropy_with_logits(D(soxta), torch.ones(n, 1)) # ✅3. Bitta optimizator ikkala tarmoqqa
opt = torch.optim.Adam(list(G.parameters()) + list(D.parameters())) # ⚠️
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)) # ✅4. Masshtab va chiqish mos emas
x = rasm / 16; G = nn.Sequential(..., nn.Tanh()) # [0, 1] va tanh # ⚠️
x = rasm / 16 * 2 - 1; G = nn.Sequential(..., nn.Tanh()) # ✅5. Loss ga qarab to'xtatish
if g_loss.item() < eng_yaxshi: saqla(G) # ⚠️
if q % 500 == 0: fd = frechet(hakam, G(z_bah), x_test); if fd < eng: saqla(G) # ✅6. Namunani z urug'isiz baholash
xs = G(torch.randn(1000, 16)) # ⚠️
xs = G(torch.randn(1000, 16, generator=torch.Generator().manual_seed(9))) # ✅7. Mode collapse ni tekshirmaslik
print("ishonch:", (hakam(xs).softmax(-1).max(1).values > 0.9).float().mean()) # ⚠️
print("sinflar:", torch.bincount(hakam(xs).argmax(1), minlength=10)) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 14-qism (o'tilgan): binar klassifikatsiya va BCE — diskriminator aynan shu
- 21-qism (o'tilgan):
detach, bir nechta optimizator,torch.Generator - 26.1-26.2 (o'tilgan): hakam-klassifikator, sinf taqsimoti, yodlash tekshiruvi; VAE — solishtirish uchun raqib
- Keyingi darslar: 26.4 — GAN muammolari va yechimlari (mode collapse, WGAN, barqarorlashtirish); 26.5-26.6 — diffusion (GAN ning sifatini barqaror o'rgatish bilan); 26.7 — FID va boshqa o'lchovlar; 26.9 — sintetik yuzlar va deepfake ni aniqlash (etika)
8. Eng yaxshi amaliyotlar
Generator uchun non-saturating loss ishlating; barqaror hisob uchun BCE with logits.
D qadamida soxtani
detachqiling, har tarmoqqa alohida optimizator bering.Rasmlarni
[-1, 1]ga o'tkazing va G chiqishinitanhqiling; Adambetas = (0.5, 0.999).Loss lar bilan birga
D(x)vaD(G(z))ni chop eting — kim ustunligini ko'rish uchun.Har N qadamda belgilangan
zto'plamida mustaqil o'lchovlar: hakam, sinf entropiyasi, Frechet.Mode collapse uchun sinf taqsimoti va eng kam sinf ulushini kuzating; 2D da modalarni sanang.
GAN ni bir necha urug'da o'rgating — tarqoqlik ham natija.
Boshqa modellar bilan bir xil hakam, bir xil namuna soni va bir nechta o'lchov bilan solishtiring.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # GAN ning qiymat funksiyasi V(D, G)?
2. # belgilangan G uchun optimal D*(x)?
3. # p_g = p_data bo'lsa D* va V qanday?
4. # D(G(z)) = 0.01 da minimax va non-saturating logit gradienti?
5. # nega D qadamida soxta.detach() kerak?
6. # muvozanatda (D = 0.5) D loss va G loss qiymatlari?
7. # 8 ta ajralgan moda: nega GAN namunalarining bir qismi modalar orasida?
8. # D loss 0.05 ga tushdi, D(G(z)) = 0.02 - bu nimani bildiradi?
9. # tanh chiqishli G uchun rasmlar qaysi oraliqda bo'lishi kerak?
10. # Frechet masofa nimani solishtiradi?
11. # mode collapse ni qaysi o'lchov bilan ko'rasiz?
12. # nega log(1 - sigmoid(logit)) ni to'g'ridan-to'g'ri hisoblash xavfli?Javoblar
E[log D(x)] + E[log(1 - D(G(z)))],min_G max_Dp_data(x) / (p_data(x) + p_g(x))D* = 0.5hamma joyda,V = -log 4 = -1.386- Minimax
-0.01, non-saturating-0.99 - D qadamida G ga gradient kerak emas; aks holda G ning grad lari ifloslanadi va keraksiz hisob bo'ladi
- D loss
2 ln 2 = 1.386, G lossln 2 = 0.693 - G uzluksiz,
zfazosi yaxlit — ajralgan orollarni ko'priksiz hosil qila olmaydi - D butunlay ustun: G ning gradienti zaif, namunalar yomon (3-misol, sigmoid varianti)
[-1, 1]- Hakam xususiyatlaridagi ikki taqsimotning Gauss yaqinlashuvlarini (o'rtacha va kovariatsiya)
- Sinf taqsimoti, sinf entropiyasi, eng kam sinf ulushi; 2D da qamrab olingan modalar soni
logitkatta bo'lsasigmoid = 1,log 0 = -infva gradientnan;F.logsigmoid(-logit)barqaror
Vazifa 2: Xatolarni tuzating
1. opt = torch.optim.Adam(list(G.parameters()) + list(D.parameters()), lr=2e-4)
loss = bce(D(x), 1) + bce(D(G(z)), 0) + bce(D(G(z)), 1)
opt.zero_grad(); loss.backward(); opt.step()
2. g_loss = torch.log(1 - torch.sigmoid(D(G(z)))).mean()
3. x = torch.tensor(digits.data / 16)
G = nn.Sequential(nn.Linear(16, 128), nn.ReLU(), nn.Linear(128, 64), nn.Tanh())
4. if g_loss.item() < 0.7:
print("GAN tayyor!")
5. sifat = (hakam(G(z)).softmax(-1).max(1).values > 0.9).float().mean()
print("mode collapse yo'q" if sifat > 0.6 else "")Javoblar
1. soxta = G(z)
d_loss = bce(D(x), 1) + bce(D(soxta.detach()), 0)
opt_d.zero_grad(); d_loss.backward(); opt_d.step() # alohida optimizatorlar
g_loss = bce(D(soxta), 1)
opt_g.zero_grad(); g_loss.backward(); opt_g.step()
2. g_loss = F.binary_cross_entropy_with_logits(D(G(z)), torch.ones(n, 1)) # non-saturating
3. x = torch.tensor(digits.data / 16 * 2 - 1, dtype=torch.float32) # [-1, 1]
G = nn.Sequential(nn.Linear(16, 128), nn.LeakyReLU(0.2), nn.Linear(128, 64), nn.Tanh())
4. # loss - o'yin holati; sifatni namunalardan o'lchaymiz
xs = (G(z_bah) + 1) / 2
print(olchovlar(hakam, xs, x_test)) # ishonch, entropiya, Frechet
5. soni = torch.bincount(hakam(xs).argmax(1), minlength=10)
print("eng kam sinf ulushi:", (soni.min() / soni.sum()).item()) # ishonch emas - qamrovVazifa 3: Optimal diskriminator
Modellang:
p_g = N(mu_g, 1)uchunmu_g = 0.5, 1, 2, 4- O'rgatilgan D va
D*ni solishtiring V(D, G)va-log 4 + 2 * JSD—mu_goshgan sari JSD qanday o'zgaradi?
Vazifa 4: 2D GAN
Modellang:
- 25 ta Gauss to'ri (5x5) — qamrov va sifatli ulush
zo'lchovi 2 va 8 — ko'priklar kamayadimi?- 3 urug', juftlashgan farq va SE
Vazifa 5: Dinamika
Modellang:
- D ning lr ini 4 barobar oshiring —
D(x),D(G(z))va Frechet qanday o'zgaradi? - Har qadamda D ni 5 marta, G ni 1 marta yangilang
- Qaysi holatda D ustun bo'lib qoladi?
Vazifa 6: GAN va VAE
Modellang:
- GAN ni 3000 qadam o'rgating (vaqt chegarasiga e'tibor)
- 4-misol jadvalini qayta tuzing — qaysi farqlar o'zgardi?
- Eng yaxshi Frechet li checkpoint ni tanlash (val sifatida alohida haqiqiy to'plam bilan)
Vazifa 7: O'ylash
Menejer yozdi: "Bizning GAN ning generator loss i bir hafta davomida 3.2 dan 0.8 gacha kamaydi — model zo'r o'rganyapti. Shu versiyani ishlab chiqarishga chiqaramiz." Siz nima deysiz?
Javob
Qisqa javob: generator loss ning kamayishi sifat haqida hech narsa kafolatlamaydi; chiqarishdan oldin namunalarni mustaqil o'lchovlar bilan baholash kerak.
1. Loss nimani bildiradi. G loss -log D(G(z)) — joriy diskriminatorni qanchalik aldayotganini ko'rsatadi. U ikki sababdan kamayadi: G yaxshilangan yoki D zaiflashgan. Loss faqat o'yin holatini ko'rsatadi. 3-misolda D loss 1000-qadamdan keyin deyarli tekis turdi, Frechet esa 15.8 dan 8.1 gacha yaxshilandi — loss va sifat alohida harakat qiladi.
2. Nima yashirinishi mumkin. Mode collapse: G bitta ishonchli rasmni chizib ham D ni aldashi mumkin. 3-misolning sigmoid variantida 1000 namunaning 654 tasi bitta sinf edi; 4-misolda GAN ba'zi sinflarni atigi 0.9% hollarda chizdi.
3. Nima o'lchash kerak. Belgilangan z to'plamida: hakam ishonchi, sinf taqsimoti va eng kam sinf ulushi, Frechet masofa (bir xil namuna soni bilan), yodlash tekshiruvi (nusxa ulushi). Bir necha urug' — 4-misolda GAN Frechet i urug'lar bo'yicha 14.1 .. 23.0 tarqoq edi.
4. Tanlash. Checkpoint ni loss bo'yicha emas, mustaqil o'lchov (masalan Frechet) bo'yicha tanlang; oxirgi baho uchun alohida test.
Tavsiya:
# 1. har 500 qadamda: D(x), D(G(z)), hakam ishonchi, sinf entropiyasi, Frechet
# 2. checkpoint - Frechet bo'yicha (val to'plami)
# 3. mode collapse: eng kam sinf ulushi >= haqiqiy ma'lumotdagining yarmi
# 4. yodlash: nusxa ulushi test darajasida
# 5. 3 urug' - tarqoqlik ham hisobotdaMenejerga javob: "Loss kamaygani yaxshi belgi bo'lishi mumkin, lekin GAN da u sifatni o'lchamaydi — generator bitta turdagi rasmni chizib ham loss ni tushirishi mumkin. Keling, namunalarni sinf taqsimoti, Frechet masofa va nusxa tekshiruvi bilan o'lchaymiz; natijalar ma'lumotnomaga yaqin bo'lsa — chiqaramiz."
Nimani mustahkamlaydi: 2.3, 2.6, 2.8-bo'limlar.
Xulosa
Bu darsda GAN ni noldan qurdik: o'yin matematikasini tekshirdik, 2D va 8x8 raqamlarda o'rgatdik va VAE bilan bir xil o'lchovlarda solishtirdik.
Eng muhim uch fikr:
GAN — likelihood siz o'yin, va generatorga non-saturating loss kerak. 1-misolda o'rgatilgan diskriminator nazariy
D* = p_data / (p_data + p_g)ga mos keldi (x = 1da0.501va0.500), qiymat funksiyasi-0.712— formula-log 4 + 2 * JSDbo'yicha-0.713. Kuchli D qarshisida (D(G(z)) = 0.0022) minimax generator gradienti non-saturating nikidan 340 barobar kichik chiqdi va 100 SGD qadamida G deyarli qimirlamadi (0.0024), non-saturating esa 25 qadamda0.9854ga yetdi.Loss — o'yin holati, sifat emas; sifatni namunalardan o'lchang. 2-misolda GAN 8 ta modaning hammasini qamradi, lekin sifatli namunalar atigi
0.460 .. 0.592— qolgani modalar orasidagi ko'priklarda. 3-misolda D loss 1000-qadamdan keyin deyarli tekis turdi, Frechet esa15.8dan8.1gacha yaxshilandi; rasmlarni[0, 1]vasigmoidbilan qoldirish esa D ustunligi va 3 sinfga qisqarishga olib keldi (Frechet66.8).GAN va VAE — turli kuchli tomonlar. 4-misolda uch urug' bo'yicha GAN o'tkirroq (keskin piksellar
0.753va0.452) va sinf ichida xilma-xilroq (1.715va1.192), VAE esa qamrovda (eng kam sinf0.076va0.009) va Frechet da (6.274va17.877) yutdi; GAN Frechet i urug'lar bo'yicha keng tarqoq edi (14.1 .. 23.0). Mode collapse belgilari — notekis sinflar — darsning hamma GAN tajribalarida ko'rindi.
Keyingi darsda GAN muammolari va yechimlari: mode collapse va o'rgatish beqarorligini chuqur tahlil qilamiz va ularga qarshi usullarni — Wasserstein GAN, gradient jarimasi, spektral normallash va boshqalarni — xuddi shu o'lchovlar bilan sinab ko'ramiz.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!