IlmHamroh
Data Science va sun'iy intellekt/Generativ AI6/10-dars51 daqiqa
Mundarija (23)

26.6-dars: Diffusion — sampling va boshqaruv

26-QISM — GENERATIV AI · 6-dars


1. Kirish va motivatsiya

26.5-darsda DDPM ni noldan qurdik: rasmga qadam-baqadam shovqin qo'shdik, tarmoqni shu shovqinni bashorat qilishga o'rgatdik va keyin teskari jarayon bilan toza shovqindan raqam "chiqarib oldik". Model ishladi, lekin ikki savol ochiq qoldi. Birinchisi — tezlik: bitta rasm uchun tarmoqni T marta, ketma-ket chaqirish kerak edi. Ikkinchisi — boshqaruv: model tasodifiy raqam chiqaradi, biz esa "menga 7 kerak" deya olmaymiz.

Bu ikki savol amaliyotda hal qiluvchi. Diffusion modellar sifat jihatidan kuchli, lekin sekin: GAN bitta forward bilan rasm beradi, DDPM esa yuzlab qadam qiladi. Foydalanuvchiga esa "tasodifiy rasm" emas, aynan so'ralgan rasm kerak — matn bo'yicha, sinf bo'yicha yoki rasmning bir qismi berilgan holda qolganini to'ldirish.

Real vaziyat. Dizayn studiyasi mahsulot rasmlarining fonini almashtirish uchun kichik diffusion modelini o'rgatdi. Ikki muammo chiqdi. Birinchisi: bitta rasm serverda bir necha soniya olardi — mijozlar kutishni xohlamadi. Jamoa sampling qadamlarini "shunchaki 1000 dan 50 ga" kamaytirdi va rasmlar shovqinli chiqa boshladi — chunki qadamlarni DDPM formulasi bilan tashlab yuborib bo'lmaydi, buning uchun boshqa sampler (DDIM) kerak. Ikkinchisi: "guidance" parametrini "kuchliroq bo'lsin" deb 15 ga qo'yishdi — rasmlar so'rovga juda mos, lekin hammasi bir-biriga o'xshash va ranglari "kuyib ketgan" edi. Bu darsning 1- va 2-misollari aynan shu ikki holatni o'lchaydi.

Bu darsda DDPM sampling narxini hisoblaymiz, DDIM ni noldan yozib qadamlar soni va sifat egri chizig'ini chizamiz, shartli diffusion va classifier-free guidance ni o'rgatib "to'g'rilik — xilma-xillik" murosasini o'lchaymiz, inpainting (rasmning yarmini to'ldirish) va latent diffusion g'oyasini kichik tajribalarda sinaymiz.

Bu darsda:

  • DDPM sampling narxi: T ta ketma-ket chaqiruv
  • DDIM: deterministik va kam qadamli sampling
  • Stoxastiklik tugmasi eta
  • Shartli diffusion: sinf embeddingi
  • Classifier-free guidance va w murosasi
  • Inpainting: ma'lum piksellarni saqlab to'ldirish
  • Latent diffusion (Stable Diffusion g'oyasi)
  • Tuzoqlar

ℹ Misollar real torch/numpy/sklearn bilan (Python 3.14, torch 2.14 CPU). Har misol o'z kichik diffusion modelini load_digits (8x8) da o'rgatadi; sifat o'zimiz o'rgatgan klassifikator bilan o'lchanadi.


2. Nazariya — chuqur tushuntirish

2.1. DDPM sampling narxi

text
26.5 DAGI TESKARI JARAYON (ajdodli, "ancestral"):
  x_T ~ N(0, I)
  t = T-1, ..., 0:
    eps = model(x_t, t)                           <- tarmoq chaqiruvi
    x0_hat = (x_t - sqrt(1 - ab_t) * eps) / sqrt(ab_t)
    x_{t-1} = mu_t(x_t, x0_hat) + sigma_t * z,  z ~ N(0, I)

NARX:
  NFE (network function evaluations) = T
  bizda: T = 100, model 173 120 parametr
    bitta chaqiruv ~ 2 * 173 120 = 0.35 MFLOP / rasm
    butun sampling ~ 35 MFLOP / rasm
  qadamlar KETMA-KET: x_{t-1} x_t ga bog'liq - vaqt bo'yicha parallellab bo'lmaydi
  (batch bo'yicha parallel - mumkin; lekin kechikish (latency) T ga proporsional)

TAQQOSLASH:
  GAN, VAE dekoderi:  1 chaqiruv
  DDPM (T = 1000 odatiy qog'ozlarda): 1000 chaqiruv

Oddiy fikr — "qadamlarni tashlab ketaylik": t = 99, 89, 79, ... bo'yicha yuramiz. Lekin DDPM formulasi qo'shni t va t-1 uchun chiqarilgan: beta_t bir qadamlik shovqin. 10 qadamni bittada bosib o'tsak, formula noto'g'ri bo'ladi. Buning to'g'ri yo'li — DDIM.

DDPM ning asosiy narxi — T ta ketma-ket tarmoq chaqiruvi; sifat tarmoqda, tezlik esa sampler da hal bo'ladi.

2.2. DDIM: deterministik va kam qadamli sampling

text
G'OYA (Song va boshq., 2020):
  o'quv maqsadi o'sha-o'sha (eps ni bashorat qilish) - qayta o'rgatish YO'Q
  faqat sampler o'zgaradi: x_t dan istalgan oldingi s < t ga sakrash

BIR QADAM (t -> s):
  eps    = model(x_t, t)
  x0_hat = (x_t - sqrt(1 - ab_t) * eps) / sqrt(ab_t)       (26.5 dagi kabi)
  x_s    = sqrt(ab_s) * x0_hat + sqrt(1 - ab_s - sigma^2) * eps + sigma * z

  sigma = eta * sqrt((1 - ab_s) / (1 - ab_t) * (1 - ab_t / ab_s))
    eta = 0: sigma = 0 -> DETERMINISTIK (bir xil x_T -> bir xil rasm)
    eta = 1: DDPM bilan bir xil shovqin darajasi

TALQIN:
  "x0 ni taxmin qil -> uni s darajasiga qayta shovqinla, lekin tasodifiy
   shovqin o'rniga model bashorat qilgan eps ni ishlat"
  eta = 0 da jarayon - oddiy differensial tenglama (ODE) ning diskret yechimi

QADAMLAR JADVALI:
  S ta teng oraliqli t: linspace(T-1, 0, S)
  S = 10 -> t = 99, 88, 77, 66, 55, 44, 33, 22, 11, 0

1-misol natijasi (500 namuna, 3 urug', FD — klassifikator xususiyatlarida, pastroq yaxshi):

text
  usul          qadam     FD          izoh
  DDIM eta=0     100     21.55
  DDIM eta=0      20     21.81
  DDIM eta=0      10     22.17       <- 10 barobar kam chaqiruv, FD +0.62
  DDIM eta=0       5     25.87
  DDIM eta=0       3     52.49
  DDIM eta=0       2    269.04       <- qulash
  o'quv rasmlarining o'zi   2.23     (pastki chegara)

DDIM — qayta o'rgatishsiz, faqat sampler almashtirish; egri chiziq "tizza" shaklida: ma'lum nuqtagacha qadamlarni kamaytirish deyarli bepul, undan keyin sifat keskin qulaydi.

2.3. Stoxastiklik tugmasi eta va DDPM bilan bog'liqlik

text
1-MISOL:
  DDPM (100 qadam)          FD 38.61
  DDIM eta=1, S = 100       FD 38.61   <- AYNAN bir xil (bir xil urug'lar)
  DDIM eta=0, S = 100       FD 21.55

  eta = 1, S = T da DDIM formulasi DDPM ning o'zi (sigma^2 = posterior dispersiya)

  eta=1 da qadamlar kamaysa sifat tezroq tushadi:
    S = 20: 41.36,  S = 10: 47.33,  S = 5: 56.91

NEGA BIZDA STOXASTIK SAMPLING YOMONROQ:
  har qadamda yangi shovqin qo'shiladi -> model uni keyingi qadamlarda
  to'liq "tozalay" olishi kerak; kichik, kam o'rgatilgan modelda
  eps xatolari yig'iladi (ehtimoliy sabab; katta modellarda farq kichikroq,
  ba'zan stoxastik sampling hatto yaxshi)

DDIM ning yana bir foydali xossasi — mos kodlash: eta = 0 da x_T rasmning "kodi" kabi ishlaydi. 1-misolda bir xil x_T dan S = 50 va S = 10 bilan olingan rasmlar o'rtacha 0.027 ga farq qildi (boshqa x_T bilan — 0.485), 95.5% holatda klassifikator bir xil sinfni ko'rdi.

eta — deterministiklik tugmasi: eta = 0 takrorlanadigan va kam qadamda barqaror, eta = 1 DDPM ning o'zi.

2.4. Shartli diffusion: sinf embeddingi

text
SHARTSIZ:  eps(x_t, t)          -> p(x)
SHARTLI:   eps(x_t, t, y)       -> p(x | y)

QANDAY BERILADI (bizning MLP):
  h = W_x x_t + emb_vaqt(t) + emb_sinf(y)       - yashirin qatlamga qo'shiladi
  emb_sinf = nn.Embedding(11, 256)               - 10 raqam + 1 "shart yo'q"

KATTA MODELLARDA:
  sinf -> embedding, vaqt embeddingiga qo'shiladi (U-Net ning har blokiga)
  matn -> matn encoder -> cross-attention (24.3 dagi mexanizm; 26.8-dars)

O'RGATISH: odatdagi eps-MSE, faqat (x0, y) juftliklari bilan

Shartli model o'z-o'zidan "to'g'ri sinf" bermaydi: 2-misolda w = 1 (oddiy shartli) da klassifikator faqat 0.830 holatda so'ralgan sinfni tanidi. Shartni kuchaytirish kerak — bu guidance.

2.5. Classifier-free guidance (CFG)

text
G'OYA (Ho va Salimans, 2022):
  bitta modelni IKKI rejimda o'rgatamiz:
    ehtimol 1 - p: shart bilan         eps(x_t, t, y)
    ehtimol p:     shart "tashlanadi"  eps(x_t, t, BOSH)     (bizda p = 0.15)

  samplingda ikkalasini aralashtiramiz:
    eps_w = eps_u + w * (eps_c - eps_u)
          = (1 - w) * eps_u + w * eps_c

  w = 0: shartsiz        w = 1: oddiy shartli
  w > 1: "shartsizdan uzoqroq, shart tomonga" - ekstrapolyatsiya

NEGA ISHLAYDI (skor talqini):
  eps ~ -grad log p(x_t) (masshtabgacha)
  eps_c - eps_u ~ -grad log p(y | x_t)   (Bayes: p(x|y) ~ p(y|x) p(x))
  w - "yashirin klassifikator" gradientining og'irligi
  -> p(x) * p(y | x)^w  ga o'xshash taqsimotdan namuna

NARX: har qadamda 2 chaqiruv (yoki 2 barobar batch)

2-misol natijasi (har sinfdan 50, 3 urug', DDIM 20 qadam):

text
    w   to'g'ri sinf    FD     xilma/haqiqiy
    0      0.097       38.51      1.63       <- shartsiz: tasodifiy (1/10)
    1      0.830       27.90      1.08
    2      0.977       16.41      0.81       <- eng past FD
    3      0.985       19.73      0.75
    5      0.985       34.37      0.74
    8      0.976       61.68      0.76       <- FD yana yomon, piksellar "kuyadi"

CFG — bitta tugma bilan murosa: w oshsa to'g'rilik oshadi, xilma-xillik kamayadi; FD esa U-shaklida — juda katta w rasmni buzadi.

2.6. Guidance ning boshqa turlari

text
CLASSIFIER GUIDANCE (Dhariwal va Nichol, 2021):
  alohida klassifikator p(y | x_t) ni SHOVQINLI rasmlarda o'rgatish
  eps ga uning gradientini qo'shish
  kamchilik: qo'shimcha model, shovqinli rasmda o'rgatish kerak

CLASSIFIER-FREE (bu dars):
  qo'shimcha model YO'Q - shartni tashlab yuborish kifoya
  bugungi matn-rasm tizimlarining odatiy usuli

SALBIY PROMPT (amaliyotda):
  eps_u o'rniga "istalmagan" shartdagi bashorat ishlatiladi:
    eps_w = eps_neg + w * (eps_c - eps_neg)

2.7. Inpainting: ma'lum piksellarni saqlab to'ldirish

text
VAZIFA: niqob m (1 - ma'lum, 0 - noma'lum), ma'lum qism x_malum
  noma'lum qismni "mos" qilib generatsiya qilish

1. SODDA: shartsiz namuna -> oxirida ma'lum qismni yopishtirish
   noma'lum qism ma'lum qismni "bilmaydi" -> mos kelmaydi

2. HAR QADAMDA ALMASHTIRISH (qayta o'rgatishsiz):
   har t da:  x_t[ma'lum] = sqrt(ab_t) * x_malum + sqrt(1 - ab_t) * z
              (ma'lum qismning t-darajadagi shovqinli nusxasi)
   keyin odatdagi DDPM qadami
   muammo: model ma'lum qismni "ko'radi", lekin uyg'unlashtirishga vaqt kam

3. QAYTA NAMUNA (RePaint g'oyasi, Lugmayr va boshq., 2022):
   t -> t-1 qadamdan keyin yana t ga shovqin qo'shib, qadamni r marta takrorlash
   noma'lum qism ma'lum qismga "moslashishga" ko'proq imkon oladi
   narx: ~r * T chaqiruv (bizda r = 5: 496 chaqiruv)

4. MAXSUS O'RGATILGAN: niqob va ma'lum qism modelga kirish sifatida beriladi
   (amaliy tizimlarda keng tarqalgan; qayta o'rgatish kerak)

3-misol natijasi (200 test raqam, yuqori 4 qator ma'lum, 3 urug'):

text
  usul                         MSE(past)  to'g'ri sinf
  o'rtacha bilan                 0.304       0.685
  eng yaqin qo'shni (3 ta)       0.239       0.882     <- eng yaxshi!
  diffusion: oxirida yopishtir   0.473       0.500
  diffusion: har qadamda         0.354       0.663
  har qadamda + qayta (r=5)      0.270       0.760

Inpainting — shartli generatsiyaning qayta o'rgatishsiz shakli; "har qadamda almashtirish" oddiy yopishtirishdan ancha yaxshi, qayta namuna yana yaxshilaydi — lekin kichik ma'lumotda eng yaqin qo'shni kabi sodda asosiy chiziq (baseline) bilan albatta solishtiring.

2.8. Latent diffusion (Stable Diffusion g'oyasi)

text
MUAMMO: katta rasmda piksel fazosi ulkan
  512 x 512 x 3 = 786 432 son; har qadam U-Net shu o'lchamda ishlaydi

G'OYA (Rombach va boshq., 2022 - "Latent Diffusion Models"):
  1. avtoenkoder (VAE) o'rgatish: rasm -> kichik latent -> rasm
     Stable Diffusion da: 512x512x3 -> 64x64x4 (8 barobar kichraytirish)
     = 16 384 son, ~48 barobar kam
  2. diffusion LATENTDA o'rgatiladi
  3. sampling: latentda S qadam -> dekoder BIR marta

AFZALLIK: har qadam arzon; dekoder mayda detallarni "biladi"
CHEKLOV: sifat avtoenkoder rekonstruksiyasidan yaxshi bo'la olmaydi

4-MISOL (digits, 64 -> 8 o'lchamli latent, bir xil 2000 o'quv qadami):
                 parametr   FLOP/qadam   sampling (S = 20)    FD
  piksel          173 120     346 K        6.92 M           20.66
  latent           39 432      79 K        1.60 M           35.48
  AE rekonstruksiya chegarasi:                                4.21
  -> latent 4.4 barobar arzon, lekin shu byudjetda sezilarli yomonroq

Latent diffusion — hisobni siqilgan fazoga ko'chirish; katta rasmlarda bu hal qiluvchi tejash, kichik 8x8 da esa piksel fazosi baribir kichik va tejash sifat narxiga tushdi.

2.9. Tuzoqlar

Asosiy tuzoqlar: DDPM formulasini qadam tashlab ishlatish (DDIM kerak); sampler ni o'zgartirib, modelni "yomon" deb qayta o'rgatish; qadamlar sonini sifatni o'lchamasdan tanlash; eta = 0 va eta = 1 ni aralashtirib taqqoslash; CFG uchun shartni tashlab yubormasdan o'rgatish (shartsiz rejim o'rganilmaydi); w ni "qancha katta — shuncha yaxshi" deb tanlash; guidance da faqat to'g'rilikni o'lchab, xilma-xillikni o'lchamaslik; inpainting da ma'lum qismni faqat oxirida yopishtirish; ma'lum qismni t darajasiga shovqinlamasdan almashtirish; latent diffusion da latentni standartlamaslik; latent sifatni AE rekonstruksiya chegarasi bilan solishtirmaslik; hamma usulni sodda asosiy chiziqlarsiz baholash.


3. Tez ma'lumotnoma

python
import torch

# DDIM qadami (t -> s), eta = 0 - deterministik
eps = model(x, t)
x0 = ((x - (1 - ab[t]).sqrt() * eps) / ab[t].sqrt()).clamp(-1, 1)
eps = (x - ab[t].sqrt() * x0) / (1 - ab[t]).sqrt()
sigma = eta * ((1 - ab[s]) / (1 - ab[t]) * (1 - ab[t] / ab[s])).sqrt()
x = ab[s].sqrt() * x0 + (1 - ab[s] - sigma ** 2).sqrt() * eps + sigma * torch.randn_like(x)

# qadamlar jadvali
qadamlar = torch.linspace(T - 1, 0, S).round().long().tolist()

# CFG: o'rgatishda shartni tashlash
y[torch.rand(B, generator=g) < 0.15] = BOSH
# samplingda aralashtirish
eps = eps_u + w * (eps_c - eps_u)

# inpainting: har qadamda ma'lum qismni almashtirish
x = niqob * (ab[t].sqrt() * x_malum + (1 - ab[t]).sqrt() * z) + (1 - niqob) * x

# latent diffusion
z = (ae.enc(X) - mu) / sd           # latentni standartlash
rasm = ae.dec(ddim(model_z, torch.randn(n, 8), S) * sd + mu)

Sampling va boshqaruv xulosasi

DDPM: T ta ketma-ket chaqiruv; qadam tashlab bo'lmaydi
DDIM: qayta o'rgatishsiz, S << T qadam; eta = 0 deterministik
shartli: emb(y) qo'shiladi; CFG: eps_u + w (eps_c - eps_u)
w oshsa: to'g'rilik oshadi, xilma-xillik kamayadi, FD U-shaklida
inpainting: har qadamda ma'lum qismni shovqinli nusxa bilan almashtirish
latent diffusion: AE latentida diffusion; chegara - AE sifati

4. Batafsil misollar

Misollar real torch/numpy/sklearn bilan (Python 3.14, torch 2.14 CPU).

Misol 1 — DDPM narxi va DDIM: qadamlar soni va sifat

python
"""DDPM sampling narxi va DDIM noldan: qadamlar soni va sifat egri chizig'i."""

import math

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.datasets import load_digits

T = 100
BELGI = " .:-=+*#%@"


def kosinus_jadval(T, s=0.008):
    t = torch.arange(T + 1, dtype=torch.float64) / T
    f = torch.cos((t + s) / (1 + s) * math.pi / 2) ** 2
    return (f / f[0])[1:].clamp(1e-5, 0.9999).float()


AB = kosinus_jadval(T)                            # alfa_bar_t, t = 0..T-1
ALFA = torch.cat([AB[:1], AB[1:] / AB[:-1]])
BETA = 1 - ALFA


def vaqt_emb(t, d=32):
    k = torch.exp(-math.log(1000.0) * torch.arange(d // 2) / (d // 2))
    a = t.float()[:, None] * k[None]
    return torch.cat([a.sin(), a.cos()], 1)


class Shovqin(nn.Module):
    """eps(x_t, t): 64 piksel + vaqt embeddingi -> MLP -> 64 (26.5-darsdagi model)."""

    def __init__(self, D=64, h=256):
        super().__init__()
        self.vaqt = nn.Sequential(nn.Linear(32, h), nn.SiLU())
        self.kir = nn.Linear(D, h)
        self.tana = nn.Sequential(nn.SiLU(), nn.Linear(h, h), nn.SiLU(), nn.Linear(h, h),
                                  nn.SiLU(), nn.Linear(h, D))

    def forward(self, x, t):
        return self.tana(self.kir(x) + self.vaqt(vaqt_emb(t)))


class Klassifikator(nn.Module):
    """Baholovchi: 64 -> 128 -> 32 (xususiyatlar) -> 10."""

    def __init__(self):
        super().__init__()
        self.xus = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 32), nn.ReLU())
        self.bosh = nn.Linear(32, 10)

    def forward(self, x):
        return self.bosh(self.xus(x))


def malumot():
    d = load_digits()
    X = torch.tensor(d.data / 8.0 - 1.0, dtype=torch.float32)      # [-1, 1]
    p = torch.tensor(np.random.default_rng(0).permutation(len(X)))
    return X[p], torch.tensor(d.target)[p]


def orgat_diffusion(X, qadamlar=2000, B=128, seed=0):
    torch.manual_seed(seed)
    m = Shovqin()
    opt = torch.optim.Adam(m.parameters(), lr=2e-3)
    jadval = torch.optim.lr_scheduler.CosineAnnealingLR(opt, qadamlar)
    g = torch.Generator().manual_seed(seed)
    for _ in range(qadamlar):
        x0 = X[torch.randint(0, len(X), (B,), generator=g)]
        t = torch.randint(0, T, (B,), generator=g)
        eps = torch.randn(x0.shape, generator=g)
        ab = AB[t][:, None]
        loss = F.mse_loss(m(ab.sqrt() * x0 + (1 - ab).sqrt() * eps, t), eps)
        opt.zero_grad()
        loss.backward()
        opt.step()
        jadval.step()
    m.eval()
    return m


def orgat_klassifikator(X, y, seed=0):
    torch.manual_seed(seed)
    k = Klassifikator()
    opt = torch.optim.Adam(k.parameters(), lr=3e-3, weight_decay=1e-4)
    g = torch.Generator().manual_seed(seed)
    for _ in range(800):
        i = torch.randint(0, len(X), (128,), generator=g)
        loss = F.cross_entropy(k(X[i] + 0.1 * torch.randn(X[i].shape, generator=g)), y[i])
        opt.zero_grad()
        loss.backward()
        opt.step()
    k.eval()
    return k


def frechet(a, b):
    """||mu1 - mu2||^2 + tr(S1 + S2 - 2 sqrt(S1 S2)) - klassifikator xususiyatlarida."""
    a, b = a.double().numpy(), b.double().numpy()
    s1, s2 = np.cov(a, rowvar=False), np.cov(b, rowvar=False)
    w, v = np.linalg.eigh(s1)
    ild = (v * np.sqrt(np.clip(w, 0, None))) @ v.T
    oz = np.linalg.eigvalsh(ild @ s2 @ ild)
    return float(((a.mean(0) - b.mean(0)) ** 2).sum() + np.trace(s1) + np.trace(s2)
                 - 2 * np.sqrt(np.clip(oz, 0, None)).sum())


@torch.no_grad()
def ddpm(m, x, g):
    """Ajdodli (ancestral) DDPM: T ta qadam, har qadamda yangi shovqin."""
    for t in range(T - 1, -1, -1):
        eps = m(x, torch.full((len(x),), t))
        x0 = ((x - (1 - AB[t]).sqrt() * eps) / AB[t].sqrt()).clamp(-1, 1)
        ab_old = AB[t - 1] if t > 0 else torch.tensor(1.0)
        orta = (ab_old.sqrt() * BETA[t] * x0 + ALFA[t].sqrt() * (1 - ab_old) * x) / (1 - AB[t])
        if t > 0:
            x = orta + (BETA[t] * (1 - ab_old) / (1 - AB[t])).sqrt() * torch.randn(x.shape, generator=g)
        else:
            x = orta
    return x


@torch.no_grad()
def ddim(m, x, S, eta=0.0, g=None):
    """DDIM: S ta qadam. eta = 0 - deterministik, eta = 1 - DDPM ga o'xshash shovqinli."""
    qadamlar = torch.linspace(T - 1, 0, S).round().long().tolist()
    for i, t in enumerate(qadamlar):
        eps = m(x, torch.full((len(x),), t))
        x0 = ((x - (1 - AB[t]).sqrt() * eps) / AB[t].sqrt()).clamp(-1, 1)
        if i + 1 == len(qadamlar):
            return x0
        ab, ab_old = AB[t], AB[qadamlar[i + 1]]
        eps = (x - ab.sqrt() * x0) / (1 - ab).sqrt()
        sigma = eta * ((1 - ab_old) / (1 - ab) * (1 - ab / ab_old)).sqrt()
        x = ab_old.sqrt() * x0 + (1 - ab_old - sigma ** 2).sqrt() * eps
        if eta > 0:
            x = x + sigma * torch.randn(x.shape, generator=g)
    return x


def ascii_qator(rasmlar):
    satrlar = []
    for r in range(8):
        qism = []
        for x in rasmlar:
            q = ((x.view(8, 8)[r] + 1) / 2).clamp(0, 1)
            qism.append("".join(BELGI[min(9, int(v * 10))] for v in q.tolist()))
        satrlar.append("    " + "   ".join(qism))
    return "\n".join(satrlar)


def main() -> None:
    torch.set_num_threads(1)
    X, y = malumot()
    Xo, yo, Xt = X[:1200], y[:1200], X[1200:]
    m = orgat_diffusion(Xo)
    k = orgat_klassifikator(Xo, yo)
    with torch.no_grad():
        f_test = k.xus(Xt)
    print(f"  test aniqligi (baholovchi): {(k(Xt).argmax(1) == y[1200:]).float().mean():.3f}")

    def baho(s):
        with torch.no_grad():
            f = k.xus(s)
            p = torch.softmax(k.bosh(f), 1)
        sinf = torch.bincount(p.argmax(1), minlength=10).float() / len(s)
        return frechet(f, f_test), p.max(1).values.mean().item(), int((sinf >= 0.05).sum())

    n_par = sum(p.numel() for p in m.parameters())
    print("\n=== 1. DDPM narxi: T ta ketma-ket tarmoq chaqiruvi ===")
    print(f"  model parametrlari: {n_par}, bitta chaqiruv ~{2 * n_par / 1e6:.2f} MFLOP/rasm")
    print(f"  DDPM: T = {T} qadam -> {T} chaqiruv -> ~{T * 2 * n_par / 1e6:.0f} MFLOP/rasm")
    print("  qadamlar KETMA-KET: t qadam t+1 natijasini kutadi - parallellab bo'lmaydi")

    print("\n=== 2. Sifat va qadamlar soni (500 namuna, 3 sampling urug'i) ===")
    n = 500
    fd_o, *_ = baho(Xo[:n])
    print(f"  pastki chegara - o'quv rasmlarining o'zi: FD {fd_o:.2f}")
    print("  usul              qadam   FD (o'rt +- SE)   ishonch  sinflar>=5%")
    natija = {}
    for nom, S, eta in ([("DDPM (ajdodli)", T, None)] +
                        [(f"DDIM eta=0", S, 0.0) for S in (100, 50, 20, 10, 5, 3, 2)] +
                        [(f"DDIM eta=1", S, 1.0) for S in (100, 20, 10, 5)]):
        qiymat = []
        for u in range(3):
            x = torch.randn((n, 64), generator=torch.Generator().manual_seed(100 + u))
            g = torch.Generator().manual_seed(200 + u)
            s = ddpm(m, x, g) if eta is None else ddim(m, x, S, eta, g)
            qiymat.append(baho(s))
        q = np.array(qiymat)
        natija[(nom, S)] = q[:, 0]
        print(f"  {nom:<16} {S:>5}   {q[:, 0].mean():>6.2f} +- {q[:, 0].std(ddof=1) / math.sqrt(3):<5.2f}"
              f"   {q[:, 1].mean():>7.3f} {q[:, 2].mean():>8.1f}")

    print("\n=== 3. Qaror: eng kam qadam, eng yaxshisidan sezilarli yomon emas ===")
    ddim0 = {S: v for (nom, S), v in natija.items() if nom == "DDIM eta=0"}
    eng = min(ddim0, key=lambda S: ddim0[S].mean())
    print(f"  juftlashgan farq (bir xil x_T) eng yaxshisidan (S = {eng}); amaliy chegara - FD ning 5%")
    maqbul = []
    for S in sorted(ddim0):
        farq = ddim0[S] - ddim0[eng]
        se = farq.std(ddof=1) / math.sqrt(len(farq))
        stat = farq.mean() > 2 * se + 1e-9
        amaliy = farq.mean() > 0.05 * ddim0[eng].mean()
        if not amaliy:
            maqbul.append(S)
        print(f"  S = {S:>3}: {farq.mean():+7.2f} (SE {se:.2f})  statistik: "
              f"{'sezilarli' if stat else 'sezilarli emas':<14}  amaliy: {'yomon' if amaliy else 'maqbul'}")
    tanlov = min(maqbul)
    print(f"  tanlov: DDIM S = {tanlov} -> {T // tanlov} barobar kam chaqiruv "
          f"(~{tanlov * 2 * n_par / 1e6:.1f} MFLOP/rasm)")
    d_ddpm = natija[("DDPM (ajdodli)", T)]
    print(f"  DDPM (100 qadam) FD {d_ddpm.mean():.2f}, DDIM S = {tanlov} FD {ddim0[tanlov].mean():.2f}")

    print("\n=== 4. DDIM deterministik: bir xil x_T -> bir xil rasm ===")
    x = torch.randn((200, 64), generator=torch.Generator().manual_seed(7))
    a1, a2 = ddim(m, x.clone(), 50), ddim(m, x.clone(), 50)
    print(f"  ikki marta, bir xil x_T: max farq {float((a1 - a2).abs().max()):.1e}")
    b10 = ddim(m, x.clone(), 10)
    boshqa = ddim(m, torch.randn((200, 64), generator=torch.Generator().manual_seed(8)), 50)
    print(f"  S = 50 va S = 10, bir xil x_T:   o'rtacha piksel farqi {float((a1 - b10).abs().mean()):.3f}")
    print(f"  S = 50, boshqa x_T:               o'rtacha piksel farqi {float((a1 - boshqa).abs().mean()):.3f}")
    with torch.no_grad():
        mos = (k(a1).argmax(1) == k(b10).argmax(1)).float().mean().item()
    print(f"  S = 50 va S = 10 da bir xil sinf: {mos:.1%}")
    print("  bir xil x_T, S = 100 / 20 / 5 / 2 (chapdan o'ngga):")
    x1 = x[:1].clone()
    print(ascii_qator([ddim(m, x1.clone(), S)[0] for S in (100, 20, 5, 2)]))


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
  test aniqligi (baholovchi): 0.975

=== 1. DDPM narxi: T ta ketma-ket tarmoq chaqiruvi ===
  model parametrlari: 173120, bitta chaqiruv ~0.35 MFLOP/rasm
  DDPM: T = 100 qadam -> 100 chaqiruv -> ~35 MFLOP/rasm
  qadamlar KETMA-KET: t qadam t+1 natijasini kutadi - parallellab bo'lmaydi

=== 2. Sifat va qadamlar soni (500 namuna, 3 sampling urug'i) ===
  pastki chegara - o'quv rasmlarining o'zi: FD 2.23
  usul              qadam   FD (o'rt +- SE)   ishonch  sinflar>=5%
  DDPM (ajdodli)     100    38.61 +- 0.69      0.855      9.0
  DDIM eta=0         100    21.55 +- 0.99      0.898     10.0
  DDIM eta=0          50    21.62 +- 0.99      0.898     10.0
  DDIM eta=0          20    21.81 +- 1.02      0.897     10.0
  DDIM eta=0          10    22.17 +- 1.03      0.895     10.0
  DDIM eta=0           5    25.87 +- 0.96      0.891      9.7
  DDIM eta=0           3    52.49 +- 1.27      0.876      9.7
  DDIM eta=0           2   269.04 +- 1.00      0.826      8.7
  DDIM eta=1         100    38.61 +- 0.69      0.855      9.0
  DDIM eta=1          20    41.36 +- 1.40      0.857      9.0
  DDIM eta=1          10    47.33 +- 2.14      0.852      9.0
  DDIM eta=1           5    56.91 +- 0.48      0.840      9.0

=== 3. Qaror: eng kam qadam, eng yaxshisidan sezilarli yomon emas ===
  juftlashgan farq (bir xil x_T) eng yaxshisidan (S = 100); amaliy chegara - FD ning 5%
  S =   2: +247.50 (SE 0.50)  statistik: sezilarli       amaliy: yomon
  S =   3:  +30.94 (SE 0.44)  statistik: sezilarli       amaliy: yomon
  S =   5:   +4.32 (SE 0.22)  statistik: sezilarli       amaliy: yomon
  S =  10:   +0.62 (SE 0.05)  statistik: sezilarli       amaliy: maqbul
  S =  20:   +0.27 (SE 0.03)  statistik: sezilarli       amaliy: maqbul
  S =  50:   +0.08 (SE 0.01)  statistik: sezilarli       amaliy: maqbul
  S = 100:   +0.00 (SE 0.00)  statistik: sezilarli emas  amaliy: maqbul
  tanlov: DDIM S = 10 -> 10 barobar kam chaqiruv (~3.5 MFLOP/rasm)
  DDPM (100 qadam) FD 38.61, DDIM S = 10 FD 22.17

=== 4. DDIM deterministik: bir xil x_T -> bir xil rasm ===
  ikki marta, bir xil x_T: max farq 0.0e+00
  S = 50 va S = 10, bir xil x_T:   o'rtacha piksel farqi 0.027
  S = 50, boshqa x_T:               o'rtacha piksel farqi 0.485
  S = 50 va S = 10 da bir xil sinf: 95.5%
  bir xil x_T, S = 100 / 20 / 5 / 2 (chapdan o'ngga):
      =:**       =:**       +-**.      @@ @@
     .**%@..    .**%@..    :##%@:    @@@@@@
     .@..%-     .@..%-     :@..#-     @@   @
       ==#        ==#        =+#         @@ @
     .%@@-      .%@@-      .%@@-.      @@@ @
     =@::-.     =@-:-.     =@+::      @@@   @
     .%  :..    .%  ...    .#  ...   @ @ @  =
    : - %+:    . = #+:    . + *+-    @@@  @@

Nima ko'rsatdi: baholovchi klassifikator test raqamlarida 0.975 aniqlikka ega — uning 32 o'lchamli yashirin qatlamidagi xususiyatlarda Frechet masofa (FD) hisoblaymiz (26.7-darsda buni batafsil tahlil qilamiz). 1-bo'lim narxni ko'rsatadi: 173 120 parametrli model bitta chaqiruvda ~`0.35 MFLOP, DDPM esa 100 ta ketma-ket chaqiruv — ~35 MFLOP/rasm. 2-bo'lim egri chiziq: DDIM (eta = 0) da 100 qadamdan 10 qadamgacha FD deyarli o'zgarmaydi (21.55→22.17), 5 qadamda 25.87, 3 da 52.49, 2 qadamda esa qulaydi (269.04, sinflar ham yo'qoladi). Pastki chegara — o'quv rasmlarining o'zi: FD 2.23; bizning kichik model undan hali ancha uzoq. Kutilmagan natija: ajdodli DDPM (38.61) 100 qadamda ham DDIM dan ancha yomon, eta = 1bilan DDIM esa aynan shu38.61ni berdi —eta = 1, S = T da DDIM formulasi DDPM ning o'zi ekanini tasdiqlaydi. Stoxastik variant qadamlar kamayganda ham tezroq yomonlashadi (56.915 qadamda). 3-bo'lim qaror qoidasi: juftlashgan farqlar (bir xilx_T) juda aniq, shuning uchun hatto +0.08 ham statistik sezilarli — shuning uchun amaliy chegara ham qo'yildi (FD ning 5%). Bu mezon bo'yicha eng arzon maqbul variant — S = 10 (+0.62), ya'ni 10 barobar kam chaqiruv. 4-bo'lim: bir xil x_Tdan ikki marta — aynan bir xil rasm (farq0); S = 50 va S = 10 natijalari o'rtacha 0.027ga farq qiladi, boshqax_Tbilan0.485— DDIMx_T ni rasmning barqaror "kodi" ga aylantiradi. ASCII qatorda S = 100, 20, 5 bir xil raqam, S = 2 esa to'yingan (@`) dog'lar. Bog'liq bo'limlar: 2.1, 2.2, 2.3.

Misol 2 — Shartli diffusion va classifier-free guidance

python
"""Shartli diffusion va classifier-free guidance: w oshsa aniqlik va xilma-xillik qanday o'zgaradi."""

import math

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.datasets import load_digits

T = 100
BOSH = 10                                          # "shartsiz" belgisi (null sinf)
BELGI = " .:-=+*#%@"


def kosinus_jadval(T, s=0.008):
    t = torch.arange(T + 1, dtype=torch.float64) / T
    f = torch.cos((t + s) / (1 + s) * math.pi / 2) ** 2
    return (f / f[0])[1:].clamp(1e-5, 0.9999).float()


AB = kosinus_jadval(T)


def vaqt_emb(t, d=32):
    k = torch.exp(-math.log(1000.0) * torch.arange(d // 2) / (d // 2))
    a = t.float()[:, None] * k[None]
    return torch.cat([a.sin(), a.cos()], 1)


class ShartliShovqin(nn.Module):
    """eps(x_t, t, y): vaqt va SINF embeddingi yashirin qatlamga qo'shiladi; y = 10 - shartsiz."""

    def __init__(self, D=64, h=256):
        super().__init__()
        self.vaqt = nn.Sequential(nn.Linear(32, h), nn.SiLU())
        self.sinf = nn.Embedding(11, h)
        self.kir = nn.Linear(D, h)
        self.tana = nn.Sequential(nn.SiLU(), nn.Linear(h, h), nn.SiLU(), nn.Linear(h, h),
                                  nn.SiLU(), nn.Linear(h, D))

    def forward(self, x, t, y):
        return self.tana(self.kir(x) + self.vaqt(vaqt_emb(t)) + self.sinf(y))


class Klassifikator(nn.Module):
    def __init__(self):
        super().__init__()
        self.xus = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 32), nn.ReLU())
        self.bosh = nn.Linear(32, 10)

    def forward(self, x):
        return self.bosh(self.xus(x))


def malumot():
    d = load_digits()
    X = torch.tensor(d.data / 8.0 - 1.0, dtype=torch.float32)
    p = torch.tensor(np.random.default_rng(0).permutation(len(X)))
    return X[p], torch.tensor(d.target)[p]


def orgat_diffusion(X, y, p_shartsiz=0.15, qadamlar=2000, B=128, seed=0):
    """Bitta model ikki ish qiladi: shartli va (p_shartsiz ulushda) shartsiz."""
    torch.manual_seed(seed)
    m = ShartliShovqin()
    opt = torch.optim.Adam(m.parameters(), lr=2e-3)
    jadval = torch.optim.lr_scheduler.CosineAnnealingLR(opt, qadamlar)
    g = torch.Generator().manual_seed(seed)
    for _ in range(qadamlar):
        i = torch.randint(0, len(X), (B,), generator=g)
        x0, yb = X[i], y[i].clone()
        yb[torch.rand(B, generator=g) < p_shartsiz] = BOSH       # shartni tashlab yuborish
        t = torch.randint(0, T, (B,), generator=g)
        eps = torch.randn(x0.shape, generator=g)
        ab = AB[t][:, None]
        loss = F.mse_loss(m(ab.sqrt() * x0 + (1 - ab).sqrt() * eps, t, yb), eps)
        opt.zero_grad()
        loss.backward()
        opt.step()
        jadval.step()
    m.eval()
    return m


def orgat_klassifikator(X, y, seed=0):
    torch.manual_seed(seed)
    k = Klassifikator()
    opt = torch.optim.Adam(k.parameters(), lr=3e-3, weight_decay=1e-4)
    g = torch.Generator().manual_seed(seed)
    for _ in range(800):
        i = torch.randint(0, len(X), (128,), generator=g)
        loss = F.cross_entropy(k(X[i] + 0.1 * torch.randn(X[i].shape, generator=g)), y[i])
        opt.zero_grad()
        loss.backward()
        opt.step()
    k.eval()
    return k


def frechet(a, b):
    a, b = a.double().numpy(), b.double().numpy()
    s1, s2 = np.cov(a, rowvar=False), np.cov(b, rowvar=False)
    w, v = np.linalg.eigh(s1)
    ild = (v * np.sqrt(np.clip(w, 0, None))) @ v.T
    oz = np.linalg.eigvalsh(ild @ s2 @ ild)
    return float(((a.mean(0) - b.mean(0)) ** 2).sum() + np.trace(s1) + np.trace(s2)
                 - 2 * np.sqrt(np.clip(oz, 0, None)).sum())


@torch.no_grad()
def cfg_sampling(m, x, y, w, S=20):
    """DDIM (eta = 0) + classifier-free guidance: eps = eps_u + w * (eps_c - eps_u)."""
    qadamlar = torch.linspace(T - 1, 0, S).round().long().tolist()
    bosh = torch.full_like(y, BOSH)
    for i, t in enumerate(qadamlar):
        tt = torch.full((len(x),), t)
        eps_c = m(x, tt, y)
        eps_u = m(x, tt, bosh)
        eps = eps_u + w * (eps_c - eps_u)
        x0 = ((x - (1 - AB[t]).sqrt() * eps) / AB[t].sqrt()).clamp(-1, 1)
        if i + 1 == len(qadamlar):
            return x0
        ab, ab_old = AB[t], AB[qadamlar[i + 1]]
        eps = (x - ab.sqrt() * x0) / (1 - ab).sqrt()
        x = ab_old.sqrt() * x0 + (1 - ab_old).sqrt() * eps
    return x


def sinf_ichi_xilma(f, y):
    """Har sinf ichida juft-juft o'rtacha masofa (xususiyatlar fazosida), sinflar bo'yicha o'rtacha."""
    q = []
    for c in range(10):
        fc = f[y == c]
        d = torch.cdist(fc, fc)
        q.append(d.sum().item() / (len(fc) * (len(fc) - 1)))
    return float(np.mean(q))


def ascii_qator(rasmlar):
    satrlar = []
    for r in range(8):
        qism = []
        for x in rasmlar:
            q = ((x.view(8, 8)[r] + 1) / 2).clamp(0, 1)
            qism.append("".join(BELGI[min(9, int(v * 10))] for v in q.tolist()))
        satrlar.append("    " + "  ".join(qism))
    return "\n".join(satrlar)


def main() -> None:
    torch.set_num_threads(1)
    X, y = malumot()
    Xo, yo, Xt, yt = X[:1200], y[:1200], X[1200:], y[1200:]
    m = orgat_diffusion(Xo, yo)
    k = orgat_klassifikator(Xo, yo)
    with torch.no_grad():
        f_test = k.xus(Xt)
    print("=== 1. Shartli model: sinf embeddingi + shartsiz rejim ===")
    print(f"  sinf embeddingi: 11 ta (0-9 raqamlar, 10 - 'shart yo'q'), o'quvda 15% shart tashlanadi")
    print(f"  baholovchi klassifikator test aniqligi: {(k(Xt).argmax(1) == yt).float().mean():.3f}")
    xil_real = sinf_ichi_xilma(f_test, yt)
    print(f"  haqiqiy test: sinf ichi xilma-xillik {xil_real:.2f} (xususiyatlar fazosida), "
          f"to'yingan piksel (|x| > 0.99) {(Xt.abs() > 0.99).float().mean():.1%}")

    print("\n=== 2. Guidance og'irligi w (har sinfdan 50, 3 urug', DDIM 20 qadam) ===")
    print("  w = 0 - shartsiz, w = 1 - oddiy shartli, w > 1 - kuchaytirilgan")
    print("    w   to'g'ri sinf      FD     xilma-xillik  xilma/haqiqiy  to'yingan piksel")
    yb = torch.arange(10).repeat_interleave(50)
    olchov = {}
    for w in (0.0, 1.0, 2.0, 3.0, 5.0, 8.0):
        q = []
        for u in range(3):
            x = torch.randn((500, 64), generator=torch.Generator().manual_seed(10 + u))
            s = cfg_sampling(m, x, yb, w)
            with torch.no_grad():
                f = k.xus(s)
                tog = (k.bosh(f).argmax(1) == yb).float().mean().item()
            q.append((tog, frechet(f, f_test), sinf_ichi_xilma(f, yb),
                      (s.abs() > 0.99).float().mean().item()))
        q = np.array(q)
        olchov[w] = q
        se = q.std(0, ddof=1) / math.sqrt(3)
        print(f"  {w:>3.0f}   {q[:, 0].mean():.3f}+-{se[0]:.3f}  {q[:, 1].mean():>6.2f}+-{se[1]:.2f}"
              f"  {q[:, 2].mean():>8.2f}    {q[:, 2].mean() / xil_real:>9.2f}  {q[:, 3].mean():>12.1%}")

    print("\n=== 3. Murosa: to'g'rilik oshadi, xilma-xillik kamayadi ===")
    wlar = sorted(olchov)
    for a, b in zip(wlar[:-1], wlar[1:]):
        d_tog = olchov[b][:, 0] - olchov[a][:, 0]
        d_xil = olchov[b][:, 2] - olchov[a][:, 2]
        se_t = d_tog.std(ddof=1) / math.sqrt(3)
        print(f"  w {a:.0f} -> {b:.0f}: to'g'ri sinf {d_tog.mean():+.3f} "
              f"({'sezilarli' if abs(d_tog.mean()) > 2 * se_t else 'sezilarli emas'}), "
              f"xilma-xillik {d_xil.mean():+.2f}")
    eng_fd = min(wlar, key=lambda w: olchov[w][:, 1].mean())
    eng_tog = max(wlar, key=lambda w: olchov[w][:, 0].mean())
    print(f"  eng past FD: w = {eng_fd:.0f};  eng yuqori to'g'ri sinf: w = {eng_tog:.0f}")
    if eng_fd != eng_tog:
        print("  bitta 'eng yaxshi' w yo'q - maqsadga qarab tanlanadi")

    print("\n=== 4. Bir xil x_T, '3' sinfi, w = 0 / 1 / 3 / 8 ===")
    x = torch.randn((4, 64), generator=torch.Generator().manual_seed(5))
    uch = torch.full((4,), 3)
    birinchi = []
    for w in (0.0, 1.0, 3.0, 8.0):
        s = cfg_sampling(m, x.clone(), uch, w)
        with torch.no_grad():
            bashorat = k(s).argmax(1).tolist()
        print(f"  w = {w:.0f}: klassifikator ko'rgan sinflar {bashorat}")
        birinchi.append(s[0])
    print("  1-x_T, chapdan o'ngga w = 0 / 1 / 3 / 8:")
    print(ascii_qator(birinchi))
    print("  hisob narxi: guidance har qadamda 2 chaqiruv (shartli + shartsiz)")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Shartli model: sinf embeddingi + shartsiz rejim ===
  sinf embeddingi: 11 ta (0-9 raqamlar, 10 - 'shart yo'q'), o'quvda 15% shart tashlanadi
  baholovchi klassifikator test aniqligi: 0.975
  haqiqiy test: sinf ichi xilma-xillik 12.23 (xususiyatlar fazosida), to'yingan piksel (|x| > 0.99) 58.0%

=== 2. Guidance og'irligi w (har sinfdan 50, 3 urug', DDIM 20 qadam) ===
  w = 0 - shartsiz, w = 1 - oddiy shartli, w > 1 - kuchaytirilgan
    w   to'g'ri sinf      FD     xilma-xillik  xilma/haqiqiy  to'yingan piksel
    0   0.097+-0.007   38.51+-0.87     19.98         1.63         26.1%
    1   0.830+-0.008   27.90+-0.13     13.21         1.08         24.4%
    2   0.977+-0.003   16.41+-0.40      9.97         0.81         26.4%
    3   0.985+-0.001   19.73+-0.51      9.16         0.75         28.4%
    5   0.985+-0.001   34.37+-1.04      9.05         0.74         32.0%
    8   0.976+-0.006   61.68+-1.59      9.32         0.76         36.4%

=== 3. Murosa: to'g'rilik oshadi, xilma-xillik kamayadi ===
  w 0 -> 1: to'g'ri sinf +0.733 (sezilarli), xilma-xillik -6.76
  w 1 -> 2: to'g'ri sinf +0.147 (sezilarli), xilma-xillik -3.24
  w 2 -> 3: to'g'ri sinf +0.009 (sezilarli), xilma-xillik -0.81
  w 3 -> 5: to'g'ri sinf -0.001 (sezilarli emas), xilma-xillik -0.10
  w 5 -> 8: to'g'ri sinf -0.009 (sezilarli emas), xilma-xillik +0.27
  eng past FD: w = 2;  eng yuqori to'g'ri sinf: w = 3
  bitta 'eng yaxshi' w yo'q - maqsadga qarab tanlanadi

=== 4. Bir xil x_T, '3' sinfi, w = 0 / 1 / 3 / 8 ===
  w = 0: klassifikator ko'rgan sinflar [0, 0, 6, 4]
  w = 1: klassifikator ko'rgan sinflar [3, 3, 3, 3]
  w = 3: klassifikator ko'rgan sinflar [3, 3, 3, 3]
  w = 8: klassifikator ko'rgan sinflar [3, 3, 3, 3]
  1-x_T, chapdan o'ngga w = 0 / 1 / 3 / 8:
    :..=@-    ..-*@:    ..=%@= .  *=-@@@ +
     --*#:     =++*=    :+*-*@-   *#%.#@@
    -#+ .-.   :*- =+ .  :=  %# :  -# .@% %
     #= *=-.   += @#..   .- @* :    % @* *
      @#%#-     %.%#-     . @#.   ::  @*
     -*  #.    ::  *:    .   @=   +-   @=
      -*+#      **:#:    .#+ #*   +--+ =#:
     .+*@+=.   .%#@@+.   .%%@@=     %@@@=.
  hisob narxi: guidance har qadamda 2 chaqiruv (shartli + shartsiz)

Nima ko'rsatdi: bitta model 15% holatda shartsiz o'rgatildi, shuning uchun u ham eps_c, ham eps_u ni beradi. 2-bo'lim — w bo'yicha murosa. w = 0 (shartsiz) da so'ralgan sinf faqat 0.097 holatda chiqdi — tasodifiy daraja (1/10); bu yerdagi "sinf ichi xilma-xillik" 1.63 ning ma'nosi yo'q, chunki "sinflar" aralash. w = 1 — oddiy shartli model — 0.830, w = 2 — 0.977, w = 3 — 0.985. Xilma-xillik haqiqiy test raqamlarining sinf ichi xilma-xilligiga nisbatan 1.08 dan 0.75 ga tushdi — model har sinfning "eng tipik" shakllariga to'plana boshlaydi. FD U-shaklida: w = 2 da eng past (16.41), w = 8 da yana 61.68 — rasm sinfga "juda" mos, lekin haqiqiy taqsimotdan uzoq; to'yingan piksellar ulushi 24.4% dan 36.4% ga o'sdi (haqiqiy rasmlarda 58.0% — ular asosan fon; generatsiyada esa w oshsa aynan chiziqlar "kuyadi"). 3-bo'lim juftlashgan farqlar: w 0 → 1 → 2 → 3 da to'g'rilik sezilarli oshadi, 3 → 5 va 5 → 8 da farq sezilarli emas (to'g'rilik 0.985 atrofida to'xtaydi); xilma-xillik esa w = 3 gacha tez kamayadi, keyin deyarli o'zgarmaydi. Eng past FD (w = 2) va eng yuqori to'g'rilik (w = 3) turli nuqtalarda — "yagona eng yaxshi w" yo'q. 4-bo'lim: bir xil x_T va "3" so'rovi — w = 0 da klassifikator [0, 0, 6, 4] ni ko'rdi, w = 1 dan boshlab to'rttasi ham "3"; w = 8 da chiziqlar atrofida ortiqcha nuqtalar paydo bo'ldi. Bog'liq bo'limlar: 2.4, 2.5.

Misol 3 — Inpainting: raqamning pastki yarmini to'ldirish

python
"""Inpainting: raqamning yuqori yarmi ma'lum, pastki yarmini diffusion bilan to'ldirish."""

import math

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.datasets import load_digits

T = 100
BELGI = " .:-=+*#%@"


def kosinus_jadval(T, s=0.008):
    t = torch.arange(T + 1, dtype=torch.float64) / T
    f = torch.cos((t + s) / (1 + s) * math.pi / 2) ** 2
    return (f / f[0])[1:].clamp(1e-5, 0.9999).float()


AB = kosinus_jadval(T)
ALFA = torch.cat([AB[:1], AB[1:] / AB[:-1]])
BETA = 1 - ALFA


def vaqt_emb(t, d=32):
    k = torch.exp(-math.log(1000.0) * torch.arange(d // 2) / (d // 2))
    a = t.float()[:, None] * k[None]
    return torch.cat([a.sin(), a.cos()], 1)


class Shovqin(nn.Module):
    def __init__(self, D=64, h=256):
        super().__init__()
        self.vaqt = nn.Sequential(nn.Linear(32, h), nn.SiLU())
        self.kir = nn.Linear(D, h)
        self.tana = nn.Sequential(nn.SiLU(), nn.Linear(h, h), nn.SiLU(), nn.Linear(h, h),
                                  nn.SiLU(), nn.Linear(h, D))

    def forward(self, x, t):
        return self.tana(self.kir(x) + self.vaqt(vaqt_emb(t)))


class Klassifikator(nn.Module):
    def __init__(self):
        super().__init__()
        self.xus = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 32), nn.ReLU())
        self.bosh = nn.Linear(32, 10)

    def forward(self, x):
        return self.bosh(self.xus(x))


def malumot():
    d = load_digits()
    X = torch.tensor(d.data / 8.0 - 1.0, dtype=torch.float32)
    p = torch.tensor(np.random.default_rng(0).permutation(len(X)))
    return X[p], torch.tensor(d.target)[p]


def orgat_diffusion(X, qadamlar=2000, B=128, seed=0):
    """Oddiy SHARTSIZ model - inpainting uchun qayta o'rgatish kerak emas."""
    torch.manual_seed(seed)
    m = Shovqin()
    opt = torch.optim.Adam(m.parameters(), lr=2e-3)
    jadval = torch.optim.lr_scheduler.CosineAnnealingLR(opt, qadamlar)
    g = torch.Generator().manual_seed(seed)
    for _ in range(qadamlar):
        x0 = X[torch.randint(0, len(X), (B,), generator=g)]
        t = torch.randint(0, T, (B,), generator=g)
        eps = torch.randn(x0.shape, generator=g)
        ab = AB[t][:, None]
        loss = F.mse_loss(m(ab.sqrt() * x0 + (1 - ab).sqrt() * eps, t), eps)
        opt.zero_grad()
        loss.backward()
        opt.step()
        jadval.step()
    m.eval()
    return m


def orgat_klassifikator(X, y, seed=0):
    torch.manual_seed(seed)
    k = Klassifikator()
    opt = torch.optim.Adam(k.parameters(), lr=3e-3, weight_decay=1e-4)
    g = torch.Generator().manual_seed(seed)
    for _ in range(800):
        i = torch.randint(0, len(X), (128,), generator=g)
        loss = F.cross_entropy(k(X[i] + 0.1 * torch.randn(X[i].shape, generator=g)), y[i])
        opt.zero_grad()
        loss.backward()
        opt.step()
    k.eval()
    return k


@torch.no_grad()
def inpainting(m, x_malum, niqob, g, har_qadamda=True, r=1):
    """DDPM sampling; har qadamda ma'lum piksellar x_malum ning t-darajali shovqinli nusxasi bilan almashtiriladi.

    har_qadamda=False - 'sodda' usul: shartsiz sampling, faqat oxirida ma'lum qismni yopishtirish.
    r > 1 - qayta namuna olish (RePaint g'oyasi): qadamni qilib, yana shovqin qo'shib, r marta takrorlash.
    """
    x = torch.randn(x_malum.shape, generator=g)
    for t in range(T - 1, -1, -1):
        for j in range(r):
            if har_qadamda:
                shovqinli = AB[t].sqrt() * x_malum + (1 - AB[t]).sqrt() * torch.randn(x.shape, generator=g)
                x = niqob * shovqinli + (1 - niqob) * x
            eps = m(x, torch.full((len(x),), t))
            x0 = ((x - (1 - AB[t]).sqrt() * eps) / AB[t].sqrt()).clamp(-1, 1)
            ab_old = AB[t - 1] if t > 0 else torch.tensor(1.0)
            orta = (ab_old.sqrt() * BETA[t] * x0 + ALFA[t].sqrt() * (1 - ab_old) * x) / (1 - AB[t])
            if t == 0:
                x = orta
                break
            x = orta + (BETA[t] * (1 - ab_old) / (1 - AB[t])).sqrt() * torch.randn(x.shape, generator=g)
            if j + 1 < r:                                  # t-1 -> t: yana bir qadam shovqin
                x = ALFA[t].sqrt() * x + BETA[t].sqrt() * torch.randn(x.shape, generator=g)
    return niqob * x_malum + (1 - niqob) * x


def ascii_qator(rasmlar):
    satrlar = []
    for r in range(8):
        qism = []
        for x in rasmlar:
            q = ((x.view(8, 8)[r] + 1) / 2).clamp(0, 1)
            qism.append("".join(BELGI[min(9, int(v * 10))] for v in q.tolist()))
        satrlar.append("    " + "  ".join(qism))
    return "\n".join(satrlar)


def main() -> None:
    torch.set_num_threads(1)
    X, y = malumot()
    Xo, yo, Xt, yt = X[:1200], y[:1200], X[1200:1400], y[1200:1400]
    m = orgat_diffusion(Xo)
    k = orgat_klassifikator(Xo, yo)
    niqob = torch.zeros(64)
    niqob[:32] = 1.0                                       # yuqori 4 qator ma'lum
    past = niqob == 0

    print("=== 1. Vazifa: 200 test raqami, yuqori 4 qator ma'lum, pastki 4 qator yashirin ===")
    with torch.no_grad():
        print(f"  to'liq rasm: klassifikator aniqligi {(k(Xt).argmax(1) == yt).float().mean():.3f}")
        yarim = Xt * niqob - (1 - niqob)                   # pastki yarim "bo'sh" (-1)
        print(f"  pastki yarim o'chirilgan: aniqlik {(k(yarim).argmax(1) == yt).float().mean():.3f}")

    print("\n=== 2. To'ldirish usullari (har biri 3 marta, turli urug') ===")
    print("  usul                        MSE(past)  to'g'ri sinf  xilma-xillik")
    natija = {}

    def baho(nom, varlar):
        v = torch.stack(varlar)                                # (3, 200, 64)
        mse = ((v[:, :, past] - Xt[None][:, :, past]) ** 2).mean(dim=(1, 2)).numpy()
        with torch.no_grad():
            tog = np.array([(k(s).argmax(1) == yt).float().mean().item() for s in v])
        xil = v[:, :, past].std(0).mean().item()
        natija[nom] = (mse, tog)
        print(f"  {nom:<27} {mse.mean():>8.3f}   {tog.mean():>10.3f}   {xil:>10.3f}")

    ortacha = Xo.mean(0)
    baho("o'rtacha bilan", [niqob * Xt + (1 - niqob) * ortacha] * 3)
    d = torch.cdist(Xt[:, :32], Xo[:, :32])                    # yuqori yarim bo'yicha qo'shni
    yaqin = d.topk(3, largest=False).indices                   # 3 ta eng yaqin
    baho("eng yaqin qo'shni (3 ta)", [niqob * Xt + (1 - niqob) * Xo[yaqin[:, j]] for j in range(3)])
    baho("diffusion: oxirida yopishtir",
         [inpainting(m, Xt, niqob, torch.Generator().manual_seed(u), False) for u in range(3)])
    baho("diffusion: har qadamda",
         [inpainting(m, Xt, niqob, torch.Generator().manual_seed(u)) for u in range(3)])
    varlar = [inpainting(m, Xt, niqob, torch.Generator().manual_seed(u), r=5) for u in range(3)]
    baho("har qadamda + qayta (r=5)", varlar)

    print("\n=== 3. Juftlashgan taqqoslash (3 urug' bo'yicha) ===")
    asos = "har qadamda + qayta (r=5)"
    print(f"  asos: '{asos}' minus boshqa usul")
    for nom in ("o'rtacha bilan", "eng yaqin qo'shni (3 ta)", "diffusion: oxirida yopishtir",
                "diffusion: har qadamda"):
        for j, olchov in ((0, "MSE"), (1, "to'g'ri sinf")):
            f = natija[asos][j] - natija[nom][j]
            se = f.std(ddof=1) / math.sqrt(len(f))
            xulosa = "sezilarli" if abs(f.mean()) > 2 * se + 1e-9 else "sezilarli emas"
            print(f"    {nom:<29} {olchov:<13} {f.mean():+.3f} (SE {se:.3f}) -> {xulosa}")
    print(f"  tarmoq chaqiruvlari: har qadamda {T}, qayta namuna bilan {5 * (T - 1) + 1}")

    print("\n=== 4. Bitta raqam: asl | yarimi | 3 xil to'ldirish (r=5) ===")
    i = int((yt == 5).nonzero()[0])
    with torch.no_grad():
        sinflar = [int(k(v[i:i + 1]).argmax()) for v in varlar[:3]]
    print(f"  asl sinf {int(yt[i])}, to'ldirilganlar klassifikator bo'yicha: {sinflar}")
    print(ascii_qator([Xt[i], Xt[i] * niqob - (1 - niqob)] + [v[i] for v in varlar[:3]]))
    print("  ⭐ Yuqori yarmi bir xil, pastki yarmi har urug'da boshqacha - lekin mos")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Vazifa: 200 test raqami, yuqori 4 qator ma'lum, pastki 4 qator yashirin ===
  to'liq rasm: klassifikator aniqligi 0.975
  pastki yarim o'chirilgan: aniqlik 0.250

=== 2. To'ldirish usullari (har biri 3 marta, turli urug') ===
  usul                        MSE(past)  to'g'ri sinf  xilma-xillik
  o'rtacha bilan                 0.304        0.685        0.000
  eng yaqin qo'shni (3 ta)       0.239        0.882        0.234
  diffusion: oxirida yopishtir    0.473        0.500        0.312
  diffusion: har qadamda         0.354        0.663        0.268
  har qadamda + qayta (r=5)      0.270        0.760        0.208

=== 3. Juftlashgan taqqoslash (3 urug' bo'yicha) ===
  asos: 'har qadamda + qayta (r=5)' minus boshqa usul
    o'rtacha bilan                MSE           -0.034 (SE 0.003) -> sezilarli
    o'rtacha bilan                to'g'ri sinf  +0.075 (SE 0.010) -> sezilarli
    eng yaqin qo'shni (3 ta)      MSE           +0.031 (SE 0.020) -> sezilarli emas
    eng yaqin qo'shni (3 ta)      to'g'ri sinf  -0.122 (SE 0.016) -> sezilarli
    diffusion: oxirida yopishtir  MSE           -0.203 (SE 0.004) -> sezilarli
    diffusion: oxirida yopishtir  to'g'ri sinf  +0.260 (SE 0.010) -> sezilarli
    diffusion: har qadamda        MSE           -0.084 (SE 0.007) -> sezilarli
    diffusion: har qadamda        to'g'ri sinf  +0.097 (SE 0.009) -> sezilarli
  tarmoq chaqiruvlari: har qadamda 100, qayta namuna bilan 496

=== 4. Bitta raqam: asl | yarimi | 3 xil to'ldirish (r=5) ===
  asl sinf 5, to'ldirilganlar klassifikator bo'yicha: [5, 5, 5]
      %@##-     %@##-     %@##-     %@##-     %@##-
     :@++-     :@++-     :@++-     :@++-     :@++-
     =%        =%        =%        =%        =%
     +@%@=     +@%@=     +@%@=     +@%@=     +@%@=
      --+#               .++-+.     -.*=     :#+**.
        #*                 :.=:    .. -#     .. -+:
      .%%                 -=:=-    .#-*#.   . :++=.
     .%*                 .@@=.:     %%+:      #@+:
  ⭐ Yuqori yarmi bir xil, pastki yarmi har urug'da boshqacha - lekin mos

Nima ko'rsatdi: 1-bo'lim vazifani o'lchaydi: to'liq rasmda klassifikator 0.975 aniq, pastki yarmi o'chirilsa — 0.250. 2-bo'limda beshta usul, har biri 3 marta. Sodda "oxirida yopishtirish" eng yomon (MSE 0.473, to'g'ri sinf 0.500) — shartsiz namuna ma'lum qismni hisobga olmaydi, faqat tasodifan mos keladi. Har qadamda almashtirish ancha yaxshi (0.354 va 0.663), qayta namuna (r = 5) yana yaxshi (0.270 va 0.760). Lekin halol natija: eng yaqin qo'shni (yuqori yarmi eng o'xshash o'quv raqamining pastki yarmi) to'g'ri sinf bo'yicha hammasidan yaxshi (0.882), MSE bo'yicha esa diffusion bilan farq sezilarli emas (+0.031, SE 0.020). Sabab: digits da bir yozuvchining ko'p raqami bor, o'quv to'plamida deyarli "egizak"lar ko'p; kichik MLP diffusion esa bunday ma'lumotda "qidiruv" ni yengolmaydi. 3-bo'lim juftlashgan farqlar: qayta namuna "har qadamda" dan MSE va to'g'rilik bo'yicha sezilarli yaxshi (-0.084, +0.097), lekin narxi ~5 barobar (496 va 100 chaqiruv). Xilma-xillik ustunida: o'rtacha bilan to'ldirish — 0 (har doim bir xil), diffusion esa har urug'da boshqa variant beradi. 4-bo'lim: "5" raqamining uchta to'ldirilishi turlicha, lekin klassifikator uchalasini ham "5" deb tanidi. Bog'liq bo'lim: 2.7.

Misol 4 — Latent diffusion va piksel fazosidagi diffusion

python
"""Latent diffusion g'oyasi: avtoenkoder latentida diffusion va piksel fazosida diffusion."""

import math

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.datasets import load_digits

T = 100


def kosinus_jadval(T, s=0.008):
    t = torch.arange(T + 1, dtype=torch.float64) / T
    f = torch.cos((t + s) / (1 + s) * math.pi / 2) ** 2
    return (f / f[0])[1:].clamp(1e-5, 0.9999).float()


AB = kosinus_jadval(T)


def vaqt_emb(t, d=32):
    k = torch.exp(-math.log(1000.0) * torch.arange(d // 2) / (d // 2))
    a = t.float()[:, None] * k[None]
    return torch.cat([a.sin(), a.cos()], 1)


class Shovqin(nn.Module):
    def __init__(self, D, h):
        super().__init__()
        self.vaqt = nn.Sequential(nn.Linear(32, h), nn.SiLU())
        self.kir = nn.Linear(D, h)
        self.tana = nn.Sequential(nn.SiLU(), nn.Linear(h, h), nn.SiLU(), nn.Linear(h, h),
                                  nn.SiLU(), nn.Linear(h, D))

    def forward(self, x, t):
        return self.tana(self.kir(x) + self.vaqt(vaqt_emb(t)))


class AE(nn.Module):
    """64 piksel -> z (8 o'lcham) -> 64 piksel (26.2-darsdagi avtoenkoder)."""

    def __init__(self, z=8):
        super().__init__()
        self.enc = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, z))
        self.dec = nn.Sequential(nn.Linear(z, 128), nn.ReLU(), nn.Linear(128, 64), nn.Tanh())


class Klassifikator(nn.Module):
    def __init__(self):
        super().__init__()
        self.xus = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 32), nn.ReLU())
        self.bosh = nn.Linear(32, 10)

    def forward(self, x):
        return self.bosh(self.xus(x))


def malumot():
    d = load_digits()
    X = torch.tensor(d.data / 8.0 - 1.0, dtype=torch.float32)
    p = torch.tensor(np.random.default_rng(0).permutation(len(X)))
    return X[p], torch.tensor(d.target)[p]


def orgat_ae(X, qadamlar=1500, B=128, seed=0):
    torch.manual_seed(seed)
    ae = AE()
    opt = torch.optim.Adam(ae.parameters(), lr=2e-3)
    g = torch.Generator().manual_seed(seed)
    for _ in range(qadamlar):
        x = X[torch.randint(0, len(X), (B,), generator=g)]
        loss = F.mse_loss(ae.dec(ae.enc(x)), x)
        opt.zero_grad()
        loss.backward()
        opt.step()
    ae.eval()
    return ae


def orgat_diffusion(X, h, qadamlar=2000, B=128, seed=0):
    torch.manual_seed(seed)
    m = Shovqin(X.shape[1], h)
    opt = torch.optim.Adam(m.parameters(), lr=2e-3)
    jadval = torch.optim.lr_scheduler.CosineAnnealingLR(opt, qadamlar)
    g = torch.Generator().manual_seed(seed)
    for _ in range(qadamlar):
        x0 = X[torch.randint(0, len(X), (B,), generator=g)]
        t = torch.randint(0, T, (B,), generator=g)
        eps = torch.randn(x0.shape, generator=g)
        ab = AB[t][:, None]
        loss = F.mse_loss(m(ab.sqrt() * x0 + (1 - ab).sqrt() * eps, t), eps)
        opt.zero_grad()
        loss.backward()
        opt.step()
        jadval.step()
    m.eval()
    return m


def orgat_klassifikator(X, y, seed=0):
    torch.manual_seed(seed)
    k = Klassifikator()
    opt = torch.optim.Adam(k.parameters(), lr=3e-3, weight_decay=1e-4)
    g = torch.Generator().manual_seed(seed)
    for _ in range(800):
        i = torch.randint(0, len(X), (128,), generator=g)
        loss = F.cross_entropy(k(X[i] + 0.1 * torch.randn(X[i].shape, generator=g)), y[i])
        opt.zero_grad()
        loss.backward()
        opt.step()
    k.eval()
    return k


def frechet(a, b):
    a, b = a.double().numpy(), b.double().numpy()
    s1, s2 = np.cov(a, rowvar=False), np.cov(b, rowvar=False)
    w, v = np.linalg.eigh(s1)
    ild = (v * np.sqrt(np.clip(w, 0, None))) @ v.T
    oz = np.linalg.eigvalsh(ild @ s2 @ ild)
    return float(((a.mean(0) - b.mean(0)) ** 2).sum() + np.trace(s1) + np.trace(s2)
                 - 2 * np.sqrt(np.clip(oz, 0, None)).sum())


@torch.no_grad()
def ddim(m, x, S=20, chegara=None):
    qadamlar = torch.linspace(T - 1, 0, S).round().long().tolist()
    for i, t in enumerate(qadamlar):
        eps = m(x, torch.full((len(x),), t))
        x0 = (x - (1 - AB[t]).sqrt() * eps) / AB[t].sqrt()
        if chegara is not None:
            x0 = x0.clamp(-chegara, chegara)
        if i + 1 == len(qadamlar):
            return x0
        ab, ab_old = AB[t], AB[qadamlar[i + 1]]
        eps = (x - ab.sqrt() * x0) / (1 - ab).sqrt()
        x = ab_old.sqrt() * x0 + (1 - ab_old).sqrt() * eps
    return x


def main() -> None:
    torch.set_num_threads(1)
    X, y = malumot()
    Xo, yo, Xt = X[:1200], y[:1200], X[1200:]
    k = orgat_klassifikator(Xo, yo)
    with torch.no_grad():
        f_test = k.xus(Xt)

    def baho(s):
        with torch.no_grad():
            f = k.xus(s)
            p = torch.softmax(k.bosh(f), 1)
        sinf = torch.bincount(p.argmax(1), minlength=10).float() / len(s)
        return frechet(f, f_test), p.max(1).values.mean().item(), int((sinf >= 0.05).sum())

    print("=== 1. Avtoenkoder: 64 piksel -> 8 o'lchamli latent ===")
    ae = orgat_ae(Xo)
    with torch.no_grad():
        z = ae.enc(Xo)
        mu, sd = z.mean(0), z.std(0)
        zn = (z - mu) / sd                                   # latentni standartlash
        qayta = ae.dec(ae.enc(Xo[:500]))
    print(f"  test rekonstruksiya MSE: {F.mse_loss(ae.dec(ae.enc(Xt)), Xt).item():.4f}")
    fd_q, ish_q, _ = baho(qayta)
    print(f"  enc -> dec qilingan 500 o'quv rasmi: FD {fd_q:.2f}, ishonch {ish_q:.3f}")
    print("  (latent diffusion bundan yaxshi bo'la olmaydi - dekoder 'shifti')")

    print("\n=== 2. Ikki diffusion, bir xil o'quv qadamlari (2000 x 128) ===")
    piksel = orgat_diffusion(Xo, h=256)
    latent = orgat_diffusion(zn, h=128)
    n_pik = sum(p.numel() for p in piksel.parameters())
    n_lat = sum(p.numel() for p in latent.parameters())
    n_dec = sum(p.numel() for p in ae.dec.parameters())
    S = 20
    print("  model        fazo  parametr  FLOP/qadam   sampling FLOP (S = 20, 1 rasm)")
    print(f"  piksel       64    {n_pik:>8}  {2 * n_pik / 1e3:>7.0f} K   {S * 2 * n_pik / 1e6:>6.2f} M")
    print(f"  latent        8    {n_lat:>8}  {2 * n_lat / 1e3:>7.0f} K   "
          f"{(S * 2 * n_lat + 2 * n_dec) / 1e6:>6.2f} M (dekoder bilan)")
    print(f"  o'quv FLOP nisbati (piksel / latent): ~{n_pik / n_lat:.1f} barobar")

    print("\n=== 3. Sifat (500 namuna, 3 urug') ===")
    fd_o, ish_o, _ = baho(Xo[:500])
    print(f"  o'quv rasmlarining o'zi:  FD {fd_o:.2f}, ishonch {ish_o:.3f}")
    natija = {}
    for nom in ("piksel", "latent"):
        q = []
        for u in range(3):
            g = torch.Generator().manual_seed(50 + u)
            if nom == "piksel":
                s = ddim(piksel, torch.randn((500, 64), generator=g), S, chegara=1.0)
            else:
                zs = ddim(latent, torch.randn((500, 8), generator=g), S)
                with torch.no_grad():
                    s = ae.dec(zs * sd + mu)
            q.append(baho(s))
        q = np.array(q)
        natija[nom] = q
        se = q[:, 0].std(ddof=1) / math.sqrt(3)
        print(f"  {nom:<7} diffusion: FD {q[:, 0].mean():6.2f} +- {se:.2f}, "
              f"ishonch {q[:, 1].mean():.3f}, sinflar>=5%: {q[:, 2].mean():.1f}")
    f = natija["latent"][:, 0] - natija["piksel"][:, 0]
    se = f.std(ddof=1) / math.sqrt(3)
    xulosa = ("latent sezilarli yaxshi" if f.mean() < -2 * se else
              "piksel sezilarli yaxshi" if f.mean() > 2 * se else "sezilarli farq yo'q")
    print(f"  latent - piksel FD: {f.mean():+.2f} (SE {se:.2f}) -> {xulosa}")

    print("\n=== 4. Xulosa (natijadan) ===")
    print(f"  latent model {n_pik / n_lat:.1f} barobar kichik, har qadam shuncha arzon")
    print(f"  latent sifat chegarasi - AE rekonstruksiyasi: FD {fd_q:.2f}")
    if natija["latent"][:, 0].mean() > 2 * fd_q:
        print("  latent diffusion bu chegaradan hali ancha uzoq - xato asosan diffusion qismida")
    else:
        print("  latent diffusion chegaraga yaqin - yaxshilash uchun avval AE ni yaxshilash kerak")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Avtoenkoder: 64 piksel -> 8 o'lchamli latent ===
  test rekonstruksiya MSE: 0.0474
  enc -> dec qilingan 500 o'quv rasmi: FD 4.21, ishonch 0.986
  (latent diffusion bundan yaxshi bo'la olmaydi - dekoder 'shifti')

=== 2. Ikki diffusion, bir xil o'quv qadamlari (2000 x 128) ===
  model        fazo  parametr  FLOP/qadam   sampling FLOP (S = 20, 1 rasm)
  piksel       64      173120      346 K     6.92 M
  latent        8       39432       79 K     1.60 M (dekoder bilan)
  o'quv FLOP nisbati (piksel / latent): ~4.4 barobar

=== 3. Sifat (500 namuna, 3 urug') ===
  o'quv rasmlarining o'zi:  FD 2.23, ishonch 0.999
  piksel  diffusion: FD  20.66 +- 0.21, ishonch 0.897, sinflar>=5%: 9.0
  latent  diffusion: FD  35.48 +- 2.91, ishonch 0.947, sinflar>=5%: 8.3
  latent - piksel FD: +14.82 (SE 2.93) -> piksel sezilarli yaxshi

=== 4. Xulosa (natijadan) ===
  latent model 4.4 barobar kichik, har qadam shuncha arzon
  latent sifat chegarasi - AE rekonstruksiyasi: FD 4.21
  latent diffusion bu chegaradan hali ancha uzoq - xato asosan diffusion qismida

Nima ko'rsatdi: 1-bo'lim: avtoenkoder 64 pikselni 8 songa siqadi, test rekonstruksiya MSE 0.0474. Rekonstruksiya qilingan o'quv rasmlarining FD si 4.21 — o'quv rasmlarining o'zidagi 2.23 dan yomonroq: dekoder rasmni biroz "silliqlaydi", va latent diffusion bundan yaxshi natija bera olmaydi. 2-bo'lim hisob: latent model 39 432 parametr (piksel — 173 120), har qadam ~4.4 barobar arzon; 20 qadamli sampling dekoder bilan birga 1.60 va 6.92 MFLOP. 3-bo'lim: bir xil 2000 o'quv qadamida piksel diffusion FD 20.66, latent — 35.48; farq +14.82, SE 2.93 — piksel sezilarli yaxshi. Latent namunalar klassifikatorga "ishonchliroq" ko'rinadi (0.947 va 0.897) — dekoder chiqishi silliq va tipik, lekin taqsimot jihatidan (FD) haqiqiydan uzoqroq. 4-bo'lim: latent diffusion AE chegarasidan (4.21) hali ancha uzoq, ya'ni xato asosan diffusion qismida — 8 o'lchamli latentning taqsimoti murakkab va kichik model uni yetarli o'rganmagan. Xulosa halol: 8x8 rasmda piksel fazosi baribir kichik, shuning uchun latentning tejashi sifat narxiga tushdi. Stable Diffusion kabi tizimlarda esa piksel fazosi o'n minglab barobar katta va har qadamni ~48 barobar arzonlashtirish hal qiluvchi — shu yerda latent g'oyasi o'zini oqlaydi. Bog'liq bo'lim: 2.8.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Tezlashtirish uchun modelni qayta o'rgatish kerak" DDIM — faqat sampler; o'sha model 10 qadamda FD 22.17 (100 da 21.55)
"DDPM da qadamlarni shunchaki tashlab ketsa bo'ladi" DDPM formulasi qo'shni qadamlar uchun; sakrash uchun DDIM formulasi kerak
"Qadamlar qancha kam — shuncha yaxshi" 2 qadamda FD 269.04 — egri chiziq tizzasidan keyin qulash
"Stoxastik sampling har doim yaxshi" Bizda eta = 1 FD 38.61, eta = 0 — 21.55
"Shartli model so'ralgan sinfni har doim beradi" w = 1 da 0.830; guidance kerak
"Guidance og'irligi qancha katta — shuncha yaxshi" w = 8 da FD 61.68, w = 2 da 16.41
"CFG uchun alohida klassifikator kerak" Yo'q — bitta model, shart 15% tashlanadi
"Inpainting uchun maxsus model shart" Har qadamda almashtirish oddiy shartsiz model bilan ishlaydi
"Diffusion inpainting har doim eng yaxshi" Digits da eng yaqin qo'shni to'g'ri sinf bo'yicha yutdi (0.882)
"Latent diffusion har doim yaxshi" U arzonroq; sifat AE va latent modelga bog'liq — 8x8 da yomonroq chiqdi

6. Keng tarqalgan xatolar va yechimlari

1. DDPM qadamini tashlab ishlatish

python
for t in range(T - 1, -1, -10):                       # DDPM formulasi bilan  # ⚠️
    x = ddpm_qadam(model, x, t)
qadamlar = torch.linspace(T - 1, 0, 10).round().long().tolist()                # ✅
x = ddim(model, x, S=10)

2. CFG uchun shartni tashlamasdan o'rgatish

python
loss = F.mse_loss(model(xt, t, y), eps)               # eps_u o'rganilmaydi   # ⚠️
y = y.clone()
y[torch.rand(len(y), generator=g) < 0.15] = BOSH                              # ✅
loss = F.mse_loss(model(xt, t, y), eps)

3. Guidance formulasini teskari yozish

python
eps = eps_c + w * (eps_u - eps_c)                     # shartdan UZOQlashadi  # ⚠️
eps = eps_u + w * (eps_c - eps_u)                                             # ✅

4. w ni o'lchamasdan tanlash

python
w = 15.0                                              # "kuchliroq bo'lsin"   # ⚠️
for w in (1, 2, 3, 5, 8):                                                     # ✅
    olchov[w] = (togri_sinf(w), fd(w), xilma_xillik(w))

5. Inpaintingda ma'lum qismni shovqinsiz almashtirish

python
x = niqob * x_malum + (1 - niqob) * x                 # t-darajaga mos emas   # ⚠️
x = niqob * (ab[t].sqrt() * x_malum + (1 - ab[t]).sqrt() * z) + (1 - niqob) * x  # ✅

6. Latentni standartlamaslik

python
z = ae.enc(X)                                         # masshtab N(0, I) emas # ⚠️
z = (ae.enc(X) - mu) / sd                                                     # ✅
rasm = ae.dec(z_namuna * sd + mu)

7. Bitta sampling urug'i bilan qaror

python
fd_10 = fd(ddim(model, torch.randn(500, 64), 10))    # bitta tasodifiy x_T    # ⚠️
farq = [fd(ddim(m, x_u, 10)) - fd(ddim(m, x_u, 100)) for x_u in x_urug]       # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 26.5-dars (o'tilgan): DDPM, shovqin jadvali, eps-bashorat — bu darsning barcha samplerlari o'sha modelga tayanadi
  • 26.2-dars (o'tilgan): avtoenkoder va VAE — latent diffusion ning birinchi bosqichi
  • 24.3-dars (o'tilgan): multi-head attention — matn sharti cross-attention orqali beriladi
  • 25.3-dars (o'tilgan): generatsiya parametrlari — w ham temperature kabi "to'g'rilik — xilma-xillik" tugmasi
  • 18-qism (o'tilgan): juftlashgan taqqoslash, SE, bir necha urug'
  • Keyingi darslar: 26.7 Generativ modellarni baholash — FD, precision/recall va yodlab olish tekshiruvi; 26.8 Matn-rasm va multimodal modellar — matn sharti, CLIP va cross-attention; MLOps va deploy qismida — sampling qadamlari soni kechikish va narxning asosiy tugmasi

8. Eng yaxshi amaliyotlar

  1. Sampler ni modeldan alohida sozlang: DDIM bilan qadamlar soni va sifat egri chizig'ini chizing.

  2. Qadamlar sonini "tizza" dan biroz o'ngda tanlang; statistik va amaliy chegarani birga qo'llang.

  3. Taqqoslashda bir xil x_T (juftlashgan) va bir necha urug' ishlating.

  4. Shartli modelni doim shartsiz rejim bilan birga o'rgating (10-20% shart tashlash) — CFG uchun.

  5. w ni to'rda tanlang va kamida uchta o'lchovni kuzating: to'g'rilik, FD, xilma-xillik.

  6. Inpaintingda ma'lum qismni har qadamda t-darajali shovqin bilan almashtiring; kerak bo'lsa qayta namuna oling.

  7. Har yangi usulni sodda asosiy chiziqlar (o'rtacha, eng yaqin qo'shni) bilan solishtiring.

  8. Latent diffusion da avval AE rekonstruksiya chegarasini o'lchang — u sifatning shifti.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # T = 100 DDPM, model 0.35 MFLOP/chaqiruv - bitta rasm necha MFLOP?
2.  # DDIM uchun modelni qayta o'rgatish kerakmi?
3.  # eta = 0 DDIM da bir xil x_T ikki marta berilsa?
4.  # eta = 1, S = T bo'lgan DDIM nimaga teng?
5.  # CFG formulasi va w = 0, w = 1 ning ma'nosi?
6.  # CFG bilan bitta sampling qadami necha chaqiruv?
7.  # w juda katta bo'lsa xilma-xillik va FD bilan nima bo'ladi?
8.  # inpaintingda ma'lum qism t-qadamda nima bilan almashtiriladi?
9.  # qayta namuna (r = 5, T = 100) necha chaqiruv?
10. # Stable Diffusion latenti 512x512x3 rasmdan necha barobar kichik?
11. # latent diffusion sifatining yuqori chegarasi nima?
12. # nega juftlashgan farqda +0.08 ham "sezilarli" chiqishi mumkin?
Javoblar
  1. ~35 MFLOP (100 * 0.35)
  2. Yo'q — o'quv maqsadi bir xil, faqat sampler o'zgaradi
  3. Aynan bir xil rasm (1-misol: max farq 0)
  4. DDPM ning o'ziga (1-misol: ikkalasi ham FD 38.61)
  5. eps_u + w * (eps_c - eps_u); w = 0 — shartsiz, w = 1 — oddiy shartli
  6. 2 ta (shartli va shartsiz)
  7. Xilma-xillik kamayadi, FD bir nuqtadan keyin yana o'sadi (2-misol: w = 8 da 61.68)
  8. sqrt(ab_t) * x_malum + sqrt(1 - ab_t) * z — ma'lum qismning t-darajali shovqinli nusxasi
  9. 5 * 99 + 1 = 496 (3-misol)
  10. ~48 barobar (786 432 / 16 384)
  11. Avtoenkoder rekonstruksiyasining sifati (4-misol: FD 4.21)
  12. Bir xil x_T bilan farq juda barqaror, SE juda kichik; shuning uchun amaliy chegara ham kerak

Vazifa 2: Xatolarni tuzating

python
1.  eps = model(x, t)
    x0 = (x - (1 - ab[t]).sqrt() * eps) / ab[t].sqrt()
    x = ab[t - 10].sqrt() * x0 + (1 - ab[t - 10]).sqrt() * torch.randn_like(x)

2.  eps = eps_c + w * (eps_c - eps_u)

3.  for t in reversed(range(T)):
        x = niqob * x_malum + (1 - niqob) * x
        x = ddpm_qadam(model, x, t)

4.  z = ae.enc(X)
    model_z = orgat_diffusion(z)
    rasm = ae.dec(ddim(model_z, torch.randn(n, 8), 20))

5.  print("S = 10 yaxshi" if fd(ddim(m, torch.randn(500, 64), 10)) < 20 else "yomon")
Javoblar
python
1.  # tasodifiy shovqin emas - bashorat qilingan eps (DDIM, eta = 0)
    eps = model(x, t)
    x0 = ((x - (1 - ab[t]).sqrt() * eps) / ab[t].sqrt()).clamp(-1, 1)
    eps = (x - ab[t].sqrt() * x0) / (1 - ab[t]).sqrt()
    x = ab[s].sqrt() * x0 + (1 - ab[s]).sqrt() * eps

2.  eps = eps_u + w * (eps_c - eps_u)

3.  for t in reversed(range(T)):
        shovqinli = ab[t].sqrt() * x_malum + (1 - ab[t]).sqrt() * torch.randn_like(x)
        x = niqob * shovqinli + (1 - niqob) * x
        x = ddpm_qadam(model, x, t)
    x = niqob * x_malum + (1 - niqob) * x

4.  z = ae.enc(X); mu, sd = z.mean(0), z.std(0)
    model_z = orgat_diffusion((z - mu) / sd)
    rasm = ae.dec(ddim(model_z, torch.randn(n, 8), 20) * sd + mu)

5.  # bir xil x_T lar, bir necha urug', juftlashgan farq va asosiy chiziq (S = 100)
    farq = [fd(ddim(m, x_u, 10)) - fd(ddim(m, x_u, 100)) for x_u in x_urug]

Vazifa 3: Qadamlar egri chizig'i

Modellang:

  1. DDIM (eta = 0) S = 100, 50, 20, 10, 5, 3, 2
  2. eta = 1 bilan xuddi shu jadval
  3. Juftlashgan farq va amaliy chegara bilan tanlov
  4. Kosinus o'rniga chiziqli shovqin jadvalida egri chiziq qanday o'zgaradi?

Vazifa 4: Guidance murosasi

Modellang:

  1. Shart tashlash ulushi p = 0.05, 0.15, 0.3 — w = 3 dagi natija
  2. w to'ri: to'g'rilik, FD, sinf ichi xilma-xillik
  3. Faqat ba'zi qadamlarda guidance (masalan, faqat katta t larda)
  4. Salbiy shart: "3 emas, 8" — eps_neg bilan

Vazifa 5: Inpainting

Modellang:

  1. Chap yarmi ma'lum, o'ng yarmi to'ldiriladi
  2. Qayta namuna r = 1, 3, 5, 10 — sifat va narx
  3. Markaziy 4x4 teshikni to'ldirish
  4. Eng yaqin qo'shni asosiy chizig'i bilan juftlashgan taqqoslash

Vazifa 6: Latent diffusion

Modellang:

  1. Latent o'lchami 4, 8, 16 — AE chegarasi va diffusion FD
  2. Bir xil FLOP byudjeti: latent modelga 4.4 barobar ko'p qadam
  3. VAE latenti va oddiy AE latenti
  4. Latentda CFG

Vazifa 7: O'ylash

Mahsulot menejeri aytdi: "Rasm generatsiyasi sekin. Keling, sampling qadamlarini 50 dan 2 ga tushiramiz va guidance ni 15 ga ko'taramiz — tez ham, so'rovga mos ham bo'ladi." Siz nima deysiz?

Javob

Qisqa javob: ikkala o'zgarish ham o'lchanmagan va, bizning tajribalarga ko'ra, ikkalasi ham sifatni buzadi. To'g'ri yo'l — egri chiziqlarni o'lchab, "tizza" nuqtalarini tanlash.

1. Qadamlar. 1-misolda DDIM 10 qadamda 100 qadamga deyarli teng (FD 22.17 va 21.55), 5 qadamda sezilarli yomon (25.87), 2 qadamda qulaydi (269.04). Demak tezlashtirish mumkin, lekin chegarasi bor — uni o'z modelingizda o'lchash kerak. Samplerni DDIM ga (yoki boshqa kam qadamli ODE sampler ga) almashtirish qayta o'rgatishni talab qilmaydi.

2. Guidance. 2-misolda w 3 dan keyin to'g'rilik oshmadi (0.985), xilma-xillik ham tiklanmadi, FD w = 8 da 61.68 gacha o'sdi va piksellar "kuyib" ketdi. w = 15 — "so'rovga juda mos, lekin hammasi bir xil va sun'iy" rasmlar.

3. Narx. CFG har qadamda 2 chaqiruv. 50 qadam CFG bilan = 100 chaqiruv; 10 qadamli DDIM + CFG = 20 chaqiruv — 5 barobar tezroq va sifat deyarli o'sha.

4. Nima o'lchash kerak. Har sozlamada: to'g'rilik (so'rovga moslik), FD yoki boshqa taqsimot o'lchovi, xilma-xillik va kechikish. Bir xil x_T va bir necha urug' bilan juftlashgan taqqoslash.

Tavsiya:

python
# 1. DDIM: S = 50, 25, 15, 10, 5 - FD va to'g'rilik egri chizig'i
# 2. w to'ri: 1, 2, 3, 5, 8 - to'g'rilik, FD, xilma-xillik
# 3. tanlov: eng arzon S va eng kichik w, eng yaxshisidan amaliy yomon emas
# 4. A/B: foydalanuvchilarda (26.7-dars)

Menejerga javob: "Tezlashtirish mumkin — lekin 2 qadamga emas: o'lchovlarimizda 10 qadam deyarli bepul, 2 qadam rasmni buzadi. Guidance ni esa ko'tarish o'rniga o'rtacha qiymatda qoldiramiz: 15 da rasmlar bir xil va sun'iy bo'lib qoladi. Aniq sonlarni bir kunlik o'lchov bilan beraman."

Nimani mustahkamlaydi: 2.2, 2.3, 2.5, 2.7, 2.8-bo'limlar.


Xulosa

Bu darsda DDPM sampling narxini hisobladik, DDIM ni noldan yozdik, shartli diffusion va classifier-free guidance ni o'rgatdik, inpainting va latent diffusion g'oyalarini kichik tajribalarda sinadik.

Eng muhim uch fikr:

  1. Tezlik — sampler masalasi. DDPM 100 ta ketma-ket chaqiruv qiladi (~`35MFLOP/rasm). DDIM qayta o'rgatishsiz, o'sha model bilan 10 qadamda deyarli bir xil sifat berdi (FD22.17va21.55), lekin 3 qadamda 52.49, 2 qadamda 269.04— egri chiziq "tizza" shaklida.eta = 1, S = 100 da DDIM aynan DDPM ga teng chiqdi (38.61`), bizning kichik modelda deterministik sampling esa sezilarli yaxshi.

  2. Classifier-free guidance — to'g'rilik va xilma-xillik orasidagi tugma. 15% holatda shartni tashlab o'rgatilgan bitta model w = 1 da so'ralgan sinfni 0.830, w = 3 da 0.985 holatda berdi; sinf ichi xilma-xillik esa haqiqiyning 1.08 dan 0.75 qismiga tushdi. FD U-shaklida: w = 2 da 16.41, w = 8 da 61.68. Yagona "eng yaxshi w" yo'q — maqsadga qarab o'lchab tanlanadi.

  3. Inpainting va latent diffusion — o'sha modeldan yangi imkoniyatlar, lekin asosiy chiziqlar bilan. Har qadamda ma'lum qismni almashtirish va qayta namuna olish oddiy yopishtirishdan ancha yaxshi (to'g'ri sinf 0.500 → 0.760), lekin digits da eng yaqin qo'shni yanada yaxshi chiqdi (0.882). Latent diffusion har qadamni 4.4 barobar arzonlashtirdi, ammo 8x8 rasmda sifat yomonroq (FD 35.48 va 20.66) — uning kuchi katta rasmlarda, piksel fazosi o'n minglab barobar katta bo'lganda.

Keyingi darsda Generativ modellarni baholash: bu darsda ishlatgan FD va boshqa o'lchovlarni noldan quramiz, har biri qaysi buzilishni ushlashini va qaysini ushlamasligini "sun'iy buzilgan" generatorlarda tekshiramiz.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
26.6-dars: Diffusion — sampling va boshqaruv — IlmHamroh