IlmHamroh
Data Science va sun'iy intellekt/Generativ AI10/10-dars52 daqiqa
Mundarija (21)

26.10-dars: Amaliyot — shartli raqam generatori loyihasi

26-QISM — GENERATIV AI · 10-dars


1. Kirish va motivatsiya

26-qismda generativ modellarning butun oilasini ko'rib chiqdik: generativ model nima va u nimani o'rganadi, avtoenkoder va VAE, GAN va uning muammolari, diffusion asoslari, sampling va classifier-free guidance, generativ modellarni baholash, matn-rasm va multimodal modellar va nihoyat — etika va xavfsizlik. Endi ularni bitta tekshiriladigan loyihaga yig'amiz va loyihaning eng muhim savoliga halol javob beramiz: bizning vazifamiz uchun qaysi generativ model kerak — va "eng zamonaviy" model haqiqatan ham eng yaxshisimi?

Vazifa — shartli raqam generatori: foydalanuvchi sinfni (0-9) beradi, tizim shu raqamning yangi 8x8 rasmini qaytaradi. Uch nomzod soddalik tartibida: shartli GMM (PCA fazosida har sinfga Gauss aralashmasi — 16-qismdagi usul), shartli VAE 26.2-bob va shartli diffusion classifier-free guidance bilan (26.5-26.6). Hammasi bir xil ma'lumot, bir xil baholash to'plami va bir xil qaror qoidasi bilan solishtiriladi.

Loyiha 22.14 va 24.12-darslardagi skeletni saqlaydi: konfig, testni qulflash, tez tekshiruvlar, bir necha urug'da juftlashgan taqqoslash, "eng sodda munosib model" qoidasi, testni bir marta ochish va weights_only=True bilan yuklanadigan paket. Generativ modelga xos qo'shimchalar: baholash klassifikatori (u ham faqat o'quv qismida), yodlash darvozasi 26.9-bob, deterministik namuna nazorati va chiqishga watermark.

Real vaziyat. Ta'lim platformasi bolalar uchun "raqam yozishni o'rgan" mashqlariga har safar yangi raqam rasmlari kerak edi. Jamoa darhol "hozir hamma diffusion ishlatadi" deb diffusion modelni tanladi, GPU ijaraga oldi va bir necha hafta sozladi. Keyin kimdir oddiy bazaviyni sinab ko'rdi — PCA va Gauss aralashmasi, bir necha soniyada o'rgatiladi, bitta matritsa ko'paytmasi bilan namuna beradi. Bir xil metrikada bazaviy yaxshiroq chiqdi. Bu darsning 3-misoli aynan shu holatni raqam bilan ko'rsatadi: kichik 8x8 ma'lumotda va kichik CPU byudjetida GMM ning FD si 2.151, diffusion niki 4.584, farq sezilarli — va GMM bitta namuna uchun taxminan 4400 marta kam FLOP sarflaydi.

Bu darsda to'liq generativ loyiha quramiz: ma'lumotdan watermark qo'shilgan, tekshirilgan paketgacha.

Bu darsda:

  • Baholash asbobi: o'z klassifikatorimiz, FD, k-NN precision/recall, yodlash — va ularni kalibrlash
  • Asosiy metrikani OLDINDAN belgilash
  • Uch nomzod: shartli GMM, shartli VAE, shartli diffusion (CFG)
  • Guidance kuchini validatsiyada tanlash
  • Uch urug'da juftlashgan taqqoslash, yodlash darvozasi va qaror
  • Hisob narxi: parametrlar, tarmoq chaqiruvlari, FLOP
  • Test bir marta; weights_only=True paket va deterministik nazorat
  • Chiqishga watermark va uni aniqlash
  • Tuzoqlar

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


2. Nazariya — chuqur tushuntirish

2.1. Loyiha xaritasi

text
Konfig (frozen dataclass): pca, gmm_k, vae_*, dif_*, cfg_w, lr, bs, urug_soni, nusxa_max
  |
  v
Ma'lumot: digits 8x8, [0, 1]; stratifikatsiyalangan bo'lish
  o'quv 1077 / val 360 / test 360 -> TEST QULFLANADI
  |
  v
Baholash asbobi (FAQAT o'quvda): klassifikator -> xususiyatlar
  metrikalar: to'g'ri sinf, FD, precision/recall (k-NN), yodlash, nusxa
  kalibrlash: nusxachi, shovqinli nusxa, sinf o'rtachasi, shovqin      (1-misol)
  |
  v
Nomzodlar: GMM < cVAE < diffusion (soddalik tartibida)
  diffusion uchun w validatsiyada tanlanadi                             (2-misol)
  |
  v
3 urug' juftlashgan FD + yodlash darvozasi -> eng sodda munosib model  (3-misol)
  |
  v
Test BIR MARTA -> paket (weights_only) -> deterministik nazorat
  -> watermark -> hisobot / model kartasi                               (4-misol)

22.14 va 24.12-darslardagi xarita bilan solishtirsak, skelet o'sha. Farq baholashda: klassifikatsiyada "to'g'ri javob" bor edi — generatsiyada yo'q. Shuning uchun biz o'lchov asbobini o'zimiz quramiz — klassifikator va uning xususiyatlari — va bu asbob ham modelning bir qismi kabi qat'iy qoidaga bo'ysunadi: u faqat o'quv qismida o'rgatiladi, val va test uning uchun ham yopiq.

Skelet o'sha; generativ loyihaga xos qo'shimcha — o'lchov asbobini qurish va kalibrlash, yodlash darvozasi va chiqishni belgilash.

2.2. Baholash asbobi va uni kalibrlash

text
KLASSIFIKATOR: MLP 64-128-64-10 (17 226 parametr), faqat o'quvda
  val aniqligi 0.972; xususiyatlar - 64 o'lchamli oxirgi yashirin qatlam

METRIKALAR 26.7-bob:
  togri      - klassifikator generatsiyani so'ralgan sinf deb taniydimi
  FD         - xususiyatlarda Frechet masofa (FID g'oyasi), mos yozuv: val
  precision  - generatsiya haqiqiy k-NN sharlariga tushadimi (sifat)
  recall     - haqiqiy namunalar generatsiya sharlariga tushadimi (qamrov)
  yodlash    - mean NN(G -> o'quv) / mean NN(val -> o'quv); ~1 yaxshi
  nusxa      - NN(G -> o'quv) < q05 bo'lganlar ulushi;
               q05 = haqiqiy yangi rasmlarning 5-foizligi -> haqiqiyda 0.05

KALIBRLASH (1-misol, val da):
  generator             togri    FD     recall  yodlash  nusxa
  nusxachi (o'quvdan)   1.000   1.434   0.886   0.000    1.000
  nusxa + shovqin 0.1   0.994   1.625   0.906   0.595    0.817
  nusxa + shovqin 0.3   0.867  10.185   0.944   1.662    0.000
  sinf o'rtachasi       1.000  10.002   0.000   0.898    0.000
  tasodifiy shovqin     0.094 188.548   0.011   3.274    0.000

FD NAMUNALAR SONIGA BOG'LIQ (nusxachi): n=90 -> 6.474, 180 -> 2.538, 360 -> 1.434

Kalibrlash jadvali butun loyihaning kaliti. Nusxachi — o'quv rasmlarini qaytaruvchi "generator" — FD bo'yicha eng yaxshi (1.434), chunki u haqiqiy taqsimotdan olingan. Ya'ni FD mukammal generator va nusxa ko'chiruvchini ajrata olmaydi — buni faqat yodlash darvozasi ushlaydi (nusxa 1.000). Sinf o'rtachasi 100% to'g'ri taniladi, lekin recall 0.000 — "to'g'ri sinf ulushi" yolg'iz qaror uchun yaroqsiz. Shovqinli nusxa sigma 0.1 da ushlandi (0.817), sigma 0.3 da esa darvozadan o'tib ketdi — lekin FD 10.185 ga oshdi: nusxani shunchalik buzish kerakki, sifat ham buziladi.

FD ning namunalar soniga kuchli bog'liqligi (6.474 → 1.434) — FID ning ma'lum xossasi: kichik n da u yuqoriga siljigan. Shuning uchun barcha nomzodlar aynan bir xil n (360) va bir xil mos yozuv to'plami bilan solishtiriladi; FD ning mutlaq qiymati emas, farqi ma'noli.

Asosiy metrika oldindan belgilanadi: FD; darvoza — nusxa ulushi ≤ 0.10; qolganlari diagnostika. Asbobni ma'lum "generatorlar"da tekshirmasdan unga ishonmang.

2.3. Nomzodlar va o'rgatish

text
NOMZODLAR (soddalik tartibida):
  1) GMM        PCA(20) + har sinfga GaussianMixture(3, full)
                namuna: komponent -> z = mu + L e -> PCA teskari
  2) cVAE       enkoder [x, one-hot] -> z(8); dekoder [z, one-hot] -> 64
                BCE + KL; 800 qadam; namuna = dekoder o'rtachasi
  3) diffusion  eps-bashoratchi MLP (192), T = 50, sinf embeddingi,
                10% shartsiz (null sinf) -> classifier-free guidance
                1000 qadam, cosine lr, EMA 0.99; DDPM sampling

GUIDANCE (2-misol, val, seed 0):
  w     togri   FD      recall
  0.0   0.811   8.886   0.828
  0.3   0.928   4.452   0.797    <- FD bo'yicha tanlandi
  0.5   0.969   4.857   0.786
  1.0   1.000  13.950   0.653
  w oshsa: to'g'ri sinf ulushi oshadi, recall tushadi 26.6-bob

HISOB NARXI:
  model       parametrlar  chaqiruv/namuna   ~FLOP/namuna
  GMM              8 274         0                3 360
  cVAE            22 352         1               20 992
  diffusion       74 528       100           14 745 600

Guidance jadvali 26.6-darsdagi murosani aniq ko'rsatadi: w = 0 da diffusion so'ralgan sinfni atigi 81.1% hollarda beradi, w = 1.0 da 100% — lekin recall 0.828 dan 0.653 ga tushadi va FD 13.950 ga oshadi: model har sinfning eng "tipik" variantini chiqaradi. FD bo'yicha eng yaxshi — w = 0.3, u Konfig.cfg_w ga yozildi. Diqqat: w validatsiyada tanlandi va keyin taqqoslash ham validatsiyada — bu diffusion foydasiga kichik tanlov siljishi; biz buni bilamiz va hisobotga yozamiz (u faqat diffusion ni yaxshiroq ko'rsatadi, ya'ni qarorni o'zgartira olmaydi).

Hisob narxi alohida ustun sifatida: diffusion bitta namuna uchun 100 marta tarmoq chaqiradi (50 qadam × 2, CFG uchun shartli va shartsiz), GMM esa birortasini ham — faqat 20x20 va 20x64 matritsa ko'paytmalari.

Har nomzodning giperparametrini (masalan, w) validatsiyada tanlang va narxni sifat yonida yozing.

2.4. Taqqoslash va qaror

text
3 URUG' (har urug'da: model init, o'rgatish batchlari, namuna urug'i 1000 + s)
  bir xil 360 ta shart (val sinflari), bir xil mos yozuv (val), bir xil asbob

JUFTLASHGAN FARQ (asosiy metrika - FD):
  d_s = FD_nomzod,s - FD_eng,s;  SE = std(d) / sqrt(3);  d > 2 * SE -> sezilarli yomon

3-MISOL:
  model       togri   FD      recall  nusxa (max)
  GMM         0.978   2.151   0.806   0.031
  cVAE        1.000   6.605   0.021   0.053
  diffusion   0.923   4.584   0.793   0.000
  cVAE - GMM:      +4.454, SE 0.121 -> sezilarli yomon
  diffusion - GMM: +2.433, SE 0.146 -> sezilarli yomon

QOIDA: eng yaxshisidan SEZILARLI yomon bo'lmagan + darvozadan o'tgan ENG SODDA
  -> GMM

Natija aniq: GMM eng yaxshi ham, eng sodda ham. cVAE so'ralgan sinfni har doim beradi (1.000), lekin recall 0.021 — dekoder o'rtachasi xira, har sinfda deyarli bitta "o'rtacha" raqam (26.2-darsdagi VAE xiraligi). Diffusion qamrovda GMM ga teng (0.793 va 0.806), lekin uning namunalari shovqinli: yodlash nisbati 1.457 — namunalar har qanday haqiqiy rasmdan haqiqiy yangi rasmlarga qaraganda uzoqroq, to'g'ri sinf ulushi 0.923.

Bu natijani qanday tushunish kerak? Bu "diffusion yomon" degani emas. 8x8 digits — kichik, sodda taqsimot: PCA ning 20 komponenti deyarli butun dispersiyani tushuntiradi, va Gauss aralashmasi uni yaxshi ifodalaydi. Diffusion esa kichik CPU byudjetida (1000 qadam) o'rgatildi — katta byudjet va katta rasmlarda aynan u ustun (26.5-26.8). To'g'ri xulosa: "bu vazifa va bu byudjetda murakkab nomzodlar murakkabligini oqlamadi". Xuddi 23.14-darsdagi kabi — va 24.12-darsdagidan farqli o'laroq, u yerda transformer o'zini oqlagan edi.

Qarorni juftlashgan farq, darvoza va soddalik tartibi belgilaydi — model nomining mashhurligi emas. Byudjet shartini qarorning bir qismi sifatida yozing.

2.5. Test, paket va deterministik nazorat

text
YAKUNIY MODEL: GMM, o'quv + val (1437 rasm) da; asbob - o'sha (faqat o'quvda)
TEST BIR MARTA (360 rasm, 3 namuna urug'i):
  nusxachi (pastki chegara) FD 2.176
  GMM: FD 2.480, 2.237, 2.368 -> 2.362 (+- 0.122); to'g'ri sinf 0.984
  nusxa max 0.039 (haqiqiyda 0.05) -> darvozadan o'tdi

PAKET:
  {"format", "konfig", "tanlov", "holat": {komp, orta, w, mu, L},
   "metrika", "watermark": {usul, a, kalit_id}, "versiyalar",
   "nazorat": {c, seed, namuna}}
  holat - FAQAT tenzorlar (sklearn obyekti yo'q) -> weights_only=True bilan yuklanadi
  watermark KALITI paketga yozilmaydi (sir)

DETERMINISTIK NAZORAT:
  yuklangan model namuna(c, seed=7) == saqlangan namuna  -> OK (torch.equal)
  bir xil urug' ikki marta -> True; boshqa urug' -> farqli

Test natijasi validatsiyaga mos (2.362 va 2.151; testda mos yozuv boshqa to'plam, shuning uchun mutlaq qiymat biroz farq qiladi). Eng ma'noli taqqoslash — xuddi shu test to'plamidagi nusxachi: 2.176. GMM ning FD si "mukammal generator" chegarasiga juda yaqin, nusxa ulushi esa 0.039 — haqiqiy yangi rasmlardagi 0.05 dan ham past.

Paketning muhim dizayn qarori — sklearn obyektlarini emas, tenzorlarni saqlash. GaussianMixture ni pickle bilan saqlash mumkin edi, lekin weights_only=True uni yuklamaydi — va bu to'g'ri: pickle ixtiyoriy kodni bajarishi mumkin 22.14-bob. Namuna olish ham torch da, torch.Generator bilan qayta yozildi — shuning uchun "urug' 7 → aynan shu 20 ta rasm" nazorati torch.equal bilan o'tadi.

Generativ paket = konfig + tenzor holat + urug'li nazorat namunasi; nazorat "model o'zgarmadi" va "sampling deterministik" ni birga tekshiradi.

2.6. Watermark va hisobot

text
XIZMAT FORMATI: 8x8 -> 16x16 (kron + 3x3 silliqlash) + a * naqsh(kalit), a = 0.04
4-MISOL (360 ta chiqish, chegara z > 4):
  belgili chiqish      100.0%   z 9.98
  belgili + JPEG 70     99.7%   z 5.79
  belgisiz chiqish       0.0%   z 1.02
  haqiqiy test           0.0%   z 0.92
FOYDALILIK: klassifikator to'g'ri sinf ulushi 0.967 -> 0.969 (watermark bilan)

MODEL KARTASI (qisqa hisobot):
  vazifa, metrika, qaror (byudjet sharti bilan), test raqamlari,
  maxfiylik (nusxa ulushi), paket, cheklovlar

26.9-darsdagi watermark bu yerda mahsulotning bir qismi bo'ldi. Muhim tekshiruv — foydalilik: watermark rasmni "buzmadi" — klassifikator uni o'sha darajada taniydi (0.967 va 0.969). Belgisiz chiqish va haqiqiy test rasmlarida yolg'on musbat 0.0%, lekin ularning z o'rtachasi ~1 — 26.9 dagi kabi nazariy nol emas; chegara 4 bilan zaxira yetarli.

Hisobotdagi cheklovlar qatori ham raqamlar kabi muhim: a'zolik hujumi bu loyihada tekshirilmagan (26.9 dagi usul bilan qo'shish mumkin), watermark xiralash va siljitishga chidamsiz, FD kichik n da shovqinli.

Hisobot = model kartasi: har da'vo 1-4-misollardagi raqam bilan, cheklovlar ochiq.

2.7. Tuzoqlar

Asosiy tuzoqlar: baholash klassifikatorini val yoki test bilan o'rgatish (asbob ham sizadi); asbobni kalibrlamay unga ishonish; FD ni turli n yoki turli mos yozuv to'plami bilan solishtirish; asosiy metrikani natijalarni ko'rgandan keyin tanlash; faqat "to'g'ri sinf ulushi" bilan baholash (sinf o'rtachasi 100% oladi); nusxachini FD bo'yicha "eng yaxshi" deb tanlash (yodlash darvozasisiz); guidance w ni testda tanlash; bitta urug' bilan "diffusion yomon" yoki "GMM yaxshi" deyish; byudjet shartini qarordan olib tashlab "har doim" deb yozish; testni bir necha marta ochish; sklearn obyektini pickle bilan saqlab weights_only=False bilan yuklash; namuna olishni global urug' bilan qilish (nazorat takrorlanmaydi); watermark kalitini paketga yozish; watermark foydalilikni buzmaganini tekshirmaslik; hisobotda cheklovlarni yozmaslik.


3. Tez ma'lumotnoma

python
k = Konfig()
X, y, tr, va, te = malumot(k)                                  # test QULF
klf = orgat_klf(k, X[tr], y[tr])                               # asbob - faqat o'quvda
baho = Baholovchi(klf, X[tr], X[va], y[va])                    # mos yozuv: val
r = baho(model.namuna(y[va], seed=1000 + s), y[va])            # togri, FD, P, R, yodlash, nusxa
d = fd[nomzod] - fd[eng]; yomon = d.mean() > 2 * d.std(ddof=1) / np.sqrt(3)
tanlov = next(n for n in SODDALIK if not yomon[n] and nusxa_max[n] <= k.nusxa_max)
baho_test = Baholovchi(klf, X[ish], X[te], y[te])             # test BIR MARTA
torch.save({"konfig": asdict(k), "holat": {...tenzorlar...},
            "nazorat": {"c": c, "seed": 7, "namuna": namuna}}, yol)
p = torch.load(yol, weights_only=True)
assert torch.equal(torch.tensor(YuklanganGMM(p["holat"]).namuna(c, seed=7)), p["nazorat"]["namuna"])
z = z_ball(chiqish(G8, KALIT, k.wm_a), KALIT)                  # z > 4 -> watermark bor

Amaliyot xulosasi

konfig -> bo'lish (test qulf) -> asbob (faqat o'quvda) -> asbobni kalibrlash
asosiy metrika oldindan: FD; darvoza: nusxa ulushi
nomzodlar: GMM < cVAE < diffusion; w validatsiyada
3 urug' juftlashgan FD -> eng sodda munosib (bu yerda: GMM)
test bir marta; paket: tenzorlar + urug'li nazorat; weights_only=True
chiqishga watermark; foydalilik va FPR tekshiruvi; model kartasi

4. Batafsil misollar

Misollar real numpy/sklearn/torch bilan (Python 3.14, torch 2.14 CPU). Har misol mustaqil ishlaydi, shuning uchun umumiy kod (konfig, ma'lumot, asbob, modellar) har birida takrorlanadi.

Misol 1 — Konfig, bo'lish, baholash asbobi va uni kalibrlash

python
"""1-qadam: konfig, bo'lish, baholash klassifikatori va o'lchov asbobini kalibrlash."""

import warnings
from dataclasses import dataclass

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from scipy import linalg
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split

@dataclass(frozen=True)
class Konfig:
    seed: int = 0
    pca: int = 20
    gmm_k: int = 3
    vae_z: int = 8
    vae_h: int = 128
    vae_qadam: int = 800
    dif_T: int = 50
    dif_h: int = 192
    dif_qadam: int = 1000
    ema: float = 0.99
    p_shartsiz: float = 0.1
    cfg_w: float = 0.3                     # 2-qadamda validatsiyada tanlangan
    bs: int = 128
    lr: float = 2e-3
    klf_qadam: int = 600
    urug_soni: int = 3
    nusxa_max: float = 0.10                # yodlash darvozasi (haqiqiyda ~0.05)
    wm_a: float = 0.04                     # watermark kuchi (26.9)


def malumot(k):
    X, y = load_digits(return_X_y=True)
    X = (X / 16).astype(np.float32)
    i = np.arange(len(X))
    ish, te = train_test_split(i, test_size=0.2, stratify=y, random_state=k.seed)
    tr, va = train_test_split(ish, test_size=0.25, stratify=y[ish], random_state=k.seed)
    return X, y, tr, va, te


# ---------------- baholash asbobi (faqat o'quv qismida o'rgatiladi) ----------------
class Klf(nn.Module):
    def __init__(self):
        super().__init__()
        self.f = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU())
        self.bosh = nn.Linear(64, 10)

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


def orgat_klf(k, X, y):
    torch.manual_seed(k.seed)
    m = Klf()
    opt = torch.optim.AdamW(m.parameters(), lr=k.lr, weight_decay=1e-3)
    X, y = torch.tensor(X), torch.tensor(y)
    g = torch.Generator().manual_seed(k.seed)
    for _ in range(k.klf_qadam):
        i = torch.randint(0, len(X), (128,), generator=g)
        xb = (X[i] + 0.05 * torch.randn(X[i].shape, generator=g)).clamp(0, 1)
        loss = F.cross_entropy(m(xb), y[i])
        opt.zero_grad()
        loss.backward()
        opt.step()
    return m.eval()


def frechet(a, b):
    m1, m2 = a.mean(0), b.mean(0)
    s1, s2 = np.cov(a, rowvar=False), np.cov(b, rowvar=False)
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        cs = np.real(linalg.sqrtm(s1 @ s2))
    return float(((m1 - m2) ** 2).sum() + np.trace(s1 + s2 - 2 * cs))


def prec_recall(real, gen, k=3):
    """k-NN precision/recall: namuna boshqa to'plamning k-NN sharlaridan biriga tushadimi."""
    real, gen = torch.tensor(real), torch.tensor(gen)
    rr = torch.cdist(real, real).kthvalue(k + 1, dim=1).values
    rg = torch.cdist(gen, gen).kthvalue(k + 1, dim=1).values
    d = torch.cdist(gen, real)
    return (float((d <= rr[None]).any(1).float().mean()),
            float((d.T <= rg[None]).any(1).float().mean()))


class Baholovchi:
    """Bitta mos yozuvli haqiqiy to'plam (val yoki test) bilan barcha metrikalar."""

    def __init__(self, klf, X_oquv, X_ref, y_ref):
        self.klf, self.X_oquv = klf, torch.tensor(X_oquv)
        self.y_ref = y_ref
        self.f_ref = self.xus(X_ref)
        d = torch.cdist(torch.tensor(X_ref), self.X_oquv).min(1).values
        self.d_real = float(d.mean())                    # yangi haqiqiy namuna o'quvdan qancha uzoq
        self.q05 = float(d.quantile(0.05))

    @torch.no_grad()
    def xus(self, X):
        return self.klf.f(torch.as_tensor(X, dtype=torch.float32)).double().numpy()

    @torch.no_grad()
    def __call__(self, G, c):
        G = torch.as_tensor(G, dtype=torch.float32)
        togri = float((self.klf(G).argmax(1).numpy() == c).mean())
        fg = self.xus(G)
        p, r = prec_recall(self.f_ref, fg)
        d = torch.cdist(G, self.X_oquv).min(1).values
        return {"togri": togri, "FD": frechet(fg, self.f_ref), "precision": p, "recall": r,
                "yodlash": float(d.mean()) / self.d_real, "nusxa": float((d < self.q05).float().mean())}


def main() -> None:
    torch.set_num_threads(1)
    k = Konfig()
    print("=== 1. Konfig va ma'lumot ===")
    print(f"  {k}")
    X, y, tr, va, te = malumot(k)
    print(f"  digits: {len(X)} rasm 8x8, qiymatlar [0, 1]; o'quv {len(tr)}, val {len(va)}, "
          f"test {len(te)} (sinf bo'yicha stratifikatsiya)")
    print(f"  umumiy indekslar: o'quv-val {len(np.intersect1d(tr, va))}, "
          f"o'quv-test {len(np.intersect1d(tr, te))}, val-test {len(np.intersect1d(va, te))}")
    d = torch.cdist(torch.tensor(X[te]), torch.tensor(X[tr])).min(1).values
    print(f"  testda o'quvdagi rasmning AYNAN nusxasi: {int((d == 0).sum())} ta")
    print("  sinflar (o'quv): " + " ".join(str(int(v)) for v in np.bincount(y[tr])))
    print("  TEST QULFLANDI - faqat 4-qadamda bir marta ochiladi")

    print("\n=== 2. Baholash klassifikatori (faqat o'quv qismida) ===")
    klf = orgat_klf(k, X[tr], y[tr])
    with torch.no_grad():
        acc = (klf(torch.tensor(X[va])).argmax(1).numpy() == y[va]).mean()
    n_par = sum(p.numel() for p in klf.parameters())
    print(f"  MLP 64-128-64-10 ({n_par} parametr), val aniqligi {acc:.3f}")
    print("  xususiyatlar: 64 o'lchamli oxirgi yashirin qatlam (FD va k-NN uchun)")

    print("\n=== 3. O'lchov asbobini kalibrlash: ma'lum 'generatorlar' ===")
    baho = Baholovchi(klf, X[tr], X[va], y[va])
    c = y[va]
    rng = np.random.default_rng(k.seed)
    nusxachi = np.array([rng.choice(tr[y[tr] == s]) for s in c])
    orta = np.stack([X[tr][y[tr] == s].mean(0) for s in range(10)])
    etalon = {
        "nusxachi (o'quvdan)": X[nusxachi],
        "nusxa + shovqin 0.1": np.clip(X[nusxachi] + rng.normal(0, 0.1, (len(c), 64)), 0, 1),
        "nusxa + shovqin 0.3": np.clip(X[nusxachi] + rng.normal(0, 0.3, (len(c), 64)), 0, 1),
        "sinf o'rtachasi": orta[c],
        "tasodifiy shovqin": rng.random((len(c), 64)),
    }
    metrikalar = ["togri", "FD", "precision", "recall", "yodlash", "nusxa"]
    print("  generator             " + "".join(f"{m:>10}" for m in metrikalar))
    natija = {}
    for nom, G in etalon.items():
        natija[nom] = baho(G.astype(np.float32), c)
        print(f"  {nom:<21} " + "".join(f"{natija[nom][m]:>10.3f}" for m in metrikalar))
    print(f"  haqiqiy yangi namuna (val): yodlash 1.000, nusxa 0.050 (ta'rif bo'yicha); "
          f"q05 = {baho.q05:.3f}")

    print("\n=== 4. Asbob nimani ushlaydi (natijadan) ===")
    n = natija["nusxachi (o'quvdan)"]
    if n["FD"] < 2 * natija["nusxa + shovqin 0.3"]["FD"] and n["nusxa"] > 0.9:
        print(f"  nusxachi: FD {n['FD']:.3f} - eng yaxshi ko'rinadi, lekin nusxa {n['nusxa']:.3f}"
              " -> FAQAT yodlash darvozasi ushlaydi")
    o = natija["sinf o'rtachasi"]
    print(f"  sinf o'rtachasi: to'g'ri sinf {o['togri']:.3f}, lekin recall {o['recall']:.3f} "
          "-> aniqlik yolg'iz aldaydi")
    s = natija["nusxa + shovqin 0.1"]
    print(f"  shovqinli nusxa 0.1-bob: nusxa ulushi {s['nusxa']:.3f} "
          f"-> {'ushlandi' if s['nusxa'] > k.nusxa_max else 'ushlanmadi'}")
    s = natija["nusxa + shovqin 0.3"]
    print(f"  shovqinli nusxa 0.3-bob: nusxa ulushi {s['nusxa']:.3f} "
          f"-> {'ushlandi' if s['nusxa'] > k.nusxa_max else 'ushlanmadi'}, lekin FD {s['FD']:.3f}")

    print("\n=== 5. FD namunalar soniga bog'liq (nusxachi, turli n) ===")
    for m in (90, 180, 360):
        i = rng.choice(len(va), m, replace=False)
        b = Baholovchi(klf, X[tr], X[va][i], y[va][i])
        print(f"  n = {m:>3}: FD {b(X[nusxachi][i], c[i])['FD']:.3f}")
    print("  -> barcha nomzodlar AYNAN bir xil n va bir xil mos yozuv bilan solishtiriladi")
    print("  ASOSIY METRIKA (oldindan belgilangan): FD; darvoza: nusxa <= "
          f"{k.nusxa_max:.2f}; qolganlari - diagnostika")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Konfig va ma'lumot ===
  Konfig(seed=0, pca=20, gmm_k=3, vae_z=8, vae_h=128, vae_qadam=800, dif_T=50, dif_h=192, dif_qadam=1000, ema=0.99, p_shartsiz=0.1, cfg_w=0.3, bs=128, lr=0.002, klf_qadam=600, urug_soni=3, nusxa_max=0.1, wm_a=0.04)
  digits: 1797 rasm 8x8, qiymatlar [0, 1]; o'quv 1077, val 360, test 360 (sinf bo'yicha stratifikatsiya)
  umumiy indekslar: o'quv-val 0, o'quv-test 0, val-test 0
  testda o'quvdagi rasmning AYNAN nusxasi: 0 ta
  sinflar (o'quv): 107 109 106 109 109 109 109 107 104 108
  TEST QULFLANDI - faqat 4-qadamda bir marta ochiladi

=== 2. Baholash klassifikatori (faqat o'quv qismida) ===
  MLP 64-128-64-10 (17226 parametr), val aniqligi 0.972
  xususiyatlar: 64 o'lchamli oxirgi yashirin qatlam (FD va k-NN uchun)

=== 3. O'lchov asbobini kalibrlash: ma'lum 'generatorlar' ===
  generator                  togri        FD precision    recall   yodlash     nusxa
  nusxachi (o'quvdan)        1.000     1.434     0.919     0.886     0.000     1.000
  nusxa + shovqin 0.1        0.994     1.625     0.894     0.906     0.595     0.817
  nusxa + shovqin 0.3        0.867    10.185     0.664     0.944     1.662     0.000
  sinf o'rtachasi            1.000    10.002     1.000     0.000     0.898     0.000
  tasodifiy shovqin          0.094   188.548     0.061     0.011     3.274     0.000
  haqiqiy yangi namuna (val): yodlash 1.000, nusxa 0.050 (ta'rif bo'yicha); q05 = 0.715

=== 4. Asbob nimani ushlaydi (natijadan) ===
  nusxachi: FD 1.434 - eng yaxshi ko'rinadi, lekin nusxa 1.000 -> FAQAT yodlash darvozasi ushlaydi
  sinf o'rtachasi: to'g'ri sinf 1.000, lekin recall 0.000 -> aniqlik yolg'iz aldaydi
  shovqinli nusxa 0.1-bob: nusxa ulushi 0.817 -> ushlandi
  shovqinli nusxa 0.3-bob: nusxa ulushi 0.000 -> ushlanmadi, lekin FD 10.185

=== 5. FD namunalar soniga bog'liq (nusxachi, turli n) ===
  n =  90: FD 6.474
  n = 180: FD 2.538
  n = 360: FD 1.434
  -> barcha nomzodlar AYNAN bir xil n va bir xil mos yozuv bilan solishtiriladi
  ASOSIY METRIKA (oldindan belgilangan): FD; darvoza: nusxa <= 0.10; qolganlari - diagnostika

Nima ko'rsatdi: 2.1, 2.2-bo'limlar. Bo'linishlar orasida umumiy indeks yo'q va testda o'quv rasmining aynan nusxasi 0 ta — test haqiqatan yangi. Sinflar muvozanatli (104-109). Asbob — 17 226 parametrli MLP — val da 0.972 aniqlik berdi. Kalibrlash jadvali asbobning har qismi nima uchun kerakligini ko'rsatdi: FD nusxachini "eng yaxshi" deb biladi, uni faqat nusxa ulushi ushlaydi; to'g'ri sinf ulushi sinf o'rtachasini ham 100% deb biladi, uni recall ushlaydi; FD esa n ga kuchli bog'liq. Shu kalibrlashdan keyin asosiy metrika (FD) va darvoza (nusxa ≤ 0.10) natijalarni ko'rmasdan oldin belgilandi.

Misol 2 — Uch nomzod validatsiyada: guidance, metrikalar, narx va namunalar

python
"""2-qadam: uch nomzod validatsiyada - guidance tanlovi, metrikalar, hisob narxi va namunalar."""


import copy
import math
import warnings
from dataclasses import dataclass

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from scipy import linalg
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.mixture import GaussianMixture
from sklearn.model_selection import train_test_split


SODDALIK = ["GMM", "cVAE", "diffusion"]          # soddadan murakkabga


@dataclass(frozen=True)
class Konfig:
    seed: int = 0
    pca: int = 20
    gmm_k: int = 3
    vae_z: int = 8
    vae_h: int = 128
    vae_qadam: int = 800
    dif_T: int = 50
    dif_h: int = 192
    dif_qadam: int = 1000
    ema: float = 0.99
    p_shartsiz: float = 0.1
    cfg_w: float = 0.3                     # 2-qadamda validatsiyada tanlangan
    bs: int = 128
    lr: float = 2e-3
    klf_qadam: int = 600
    urug_soni: int = 3
    nusxa_max: float = 0.10                # yodlash darvozasi (haqiqiyda ~0.05)
    wm_a: float = 0.04                     # watermark kuchi (26.9)


def malumot(k):
    X, y = load_digits(return_X_y=True)
    X = (X / 16).astype(np.float32)
    i = np.arange(len(X))
    ish, te = train_test_split(i, test_size=0.2, stratify=y, random_state=k.seed)
    tr, va = train_test_split(ish, test_size=0.25, stratify=y[ish], random_state=k.seed)
    return X, y, tr, va, te


# ---------------- baholash asbobi (faqat o'quv qismida o'rgatiladi) ----------------
class Klf(nn.Module):
    def __init__(self):
        super().__init__()
        self.f = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU())
        self.bosh = nn.Linear(64, 10)

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


def orgat_klf(k, X, y):
    torch.manual_seed(k.seed)
    m = Klf()
    opt = torch.optim.AdamW(m.parameters(), lr=k.lr, weight_decay=1e-3)
    X, y = torch.tensor(X), torch.tensor(y)
    g = torch.Generator().manual_seed(k.seed)
    for _ in range(k.klf_qadam):
        i = torch.randint(0, len(X), (128,), generator=g)
        xb = (X[i] + 0.05 * torch.randn(X[i].shape, generator=g)).clamp(0, 1)
        loss = F.cross_entropy(m(xb), y[i])
        opt.zero_grad()
        loss.backward()
        opt.step()
    return m.eval()


def frechet(a, b):
    m1, m2 = a.mean(0), b.mean(0)
    s1, s2 = np.cov(a, rowvar=False), np.cov(b, rowvar=False)
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        cs = np.real(linalg.sqrtm(s1 @ s2))
    return float(((m1 - m2) ** 2).sum() + np.trace(s1 + s2 - 2 * cs))


def prec_recall(real, gen, k=3):
    """k-NN precision/recall: namuna boshqa to'plamning k-NN sharlaridan biriga tushadimi."""
    real, gen = torch.tensor(real), torch.tensor(gen)
    rr = torch.cdist(real, real).kthvalue(k + 1, dim=1).values
    rg = torch.cdist(gen, gen).kthvalue(k + 1, dim=1).values
    d = torch.cdist(gen, real)
    return (float((d <= rr[None]).any(1).float().mean()),
            float((d.T <= rg[None]).any(1).float().mean()))


class Baholovchi:
    """Bitta mos yozuvli haqiqiy to'plam (val yoki test) bilan barcha metrikalar."""

    def __init__(self, klf, X_oquv, X_ref, y_ref):
        self.klf, self.X_oquv = klf, torch.tensor(X_oquv)
        self.y_ref = y_ref
        self.f_ref = self.xus(X_ref)
        d = torch.cdist(torch.tensor(X_ref), self.X_oquv).min(1).values
        self.d_real = float(d.mean())                    # yangi haqiqiy namuna o'quvdan qancha uzoq
        self.q05 = float(d.quantile(0.05))

    @torch.no_grad()
    def xus(self, X):
        return self.klf.f(torch.as_tensor(X, dtype=torch.float32)).double().numpy()

    @torch.no_grad()
    def __call__(self, G, c):
        G = torch.as_tensor(G, dtype=torch.float32)
        togri = float((self.klf(G).argmax(1).numpy() == c).mean())
        fg = self.xus(G)
        p, r = prec_recall(self.f_ref, fg)
        d = torch.cdist(G, self.X_oquv).min(1).values
        return {"togri": togri, "FD": frechet(fg, self.f_ref), "precision": p, "recall": r,
                "yodlash": float(d.mean()) / self.d_real, "nusxa": float((d < self.q05).float().mean())}


# ---------------- nomzodlar ----------------
class ShartliGMM:
    """Har sinfga PCA fazosida GaussianMixture; namuna torch.Generator bilan."""

    def __init__(self, k, X, y, seed):
        pca = PCA(k.pca, random_state=seed).fit(X)
        Z = pca.transform(X)
        self.komp = torch.tensor(pca.components_, dtype=torch.float64)
        self.orta = torch.tensor(pca.mean_, dtype=torch.float64)
        w, mu, L = [], [], []
        for c in range(10):
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                gm = GaussianMixture(k.gmm_k, random_state=seed).fit(Z[y == c])
            w.append(gm.weights_)
            mu.append(gm.means_)
            L.append(np.linalg.cholesky(gm.covariances_ + 1e-6 * np.eye(k.pca)))
        self.w, self.mu, self.L = (torch.tensor(np.array(a)) for a in (w, mu, L))

    def namuna(self, c, seed):
        g = torch.Generator().manual_seed(seed)
        c = torch.as_tensor(c)
        j = torch.multinomial(self.w[c], 1, generator=g)[:, 0]
        e = torch.randn(len(c), self.mu.shape[2], generator=g, dtype=torch.float64)
        z = self.mu[c, j] + (self.L[c, j] @ e[:, :, None])[:, :, 0]
        return (z @ self.komp + self.orta).clamp(0, 1).float().numpy()

    def parametrlar(self):
        return sum(t.numel() for t in (self.komp, self.orta, self.w, self.mu)) + \
            10 * self.L.shape[1] * self.L.shape[2] * (self.L.shape[2] + 1) // 2


class CVAE(nn.Module):
    def __init__(self, k):
        super().__init__()
        self.z = k.vae_z
        self.enc = nn.Sequential(nn.Linear(74, k.vae_h), nn.ReLU(), nn.Linear(k.vae_h, 2 * k.vae_z))
        self.dec = nn.Sequential(nn.Linear(k.vae_z + 10, k.vae_h), nn.ReLU(), nn.Linear(k.vae_h, 64))

    def loss(self, x, c, g):
        oh = F.one_hot(c, 10).float()
        mu, logv = self.enc(torch.cat([x, oh], 1)).chunk(2, 1)
        z = mu + torch.randn(mu.shape, generator=g) * (0.5 * logv).exp()
        r = self.dec(torch.cat([z, oh], 1))
        kl = -0.5 * (1 + logv - mu ** 2 - logv.exp()).sum(1)
        return (F.binary_cross_entropy_with_logits(r, x, reduction="none").sum(1) + kl).mean()

    @torch.no_grad()
    def namuna(self, c, seed):
        g = torch.Generator().manual_seed(seed)
        c = torch.as_tensor(c)
        z = torch.randn(len(c), self.z, generator=g)
        return torch.sigmoid(self.dec(torch.cat([z, F.one_hot(c, 10).float()], 1))).numpy()


def jadval(T):
    beta = torch.linspace(1e-4 * 1000 / T, 0.02 * 1000 / T, T)
    return beta, 1 - beta, torch.cumprod(1 - beta, 0)


class Diffusion(nn.Module):
    """eps-bashoratchi MLP; sinf 10 = shartsiz (classifier-free guidance uchun)."""

    def __init__(self, k):
        super().__init__()
        self.T, self.w = k.dif_T, k.cfg_w
        self.emb = nn.Embedding(11, 32)
        self.net = nn.Sequential(nn.Linear(128, k.dif_h), nn.SiLU(), nn.Linear(k.dif_h, k.dif_h),
                                 nn.SiLU(), nn.Linear(k.dif_h, 64))

    def forward(self, x, t, c):
        f = torch.exp(-math.log(1000) * torch.arange(16) / 16)
        a = t.float()[:, None] * f[None]
        return self.net(torch.cat([x, a.sin(), a.cos(), self.emb(c)], 1))

    def loss(self, x, c, g, p_shartsiz):
        _, _, ab = jadval(self.T)
        x = x * 2 - 1
        t = torch.randint(0, self.T, (len(x),), generator=g)
        e = torch.randn(x.shape, generator=g)
        a = ab[t][:, None]
        c = torch.where(torch.rand(len(x), generator=g) < p_shartsiz, 10, c)
        return F.mse_loss(self(a.sqrt() * x + (1 - a).sqrt() * e, t, c), e)

    @torch.no_grad()
    def namuna(self, c, seed, w=None):
        w = self.w if w is None else w
        g = torch.Generator().manual_seed(seed)
        beta, alfa, ab = jadval(self.T)
        c = torch.as_tensor(c)
        n = len(c)
        x = torch.randn(n, 64, generator=g)
        for t in reversed(range(self.T)):
            tt = torch.full((2 * n,), t)
            e = self(torch.cat([x, x]), tt, torch.cat([c, torch.full_like(c, 10)]))
            e = (1 + w) * e[:n] - w * e[n:]                      # classifier-free guidance
            x = (x - beta[t] / (1 - ab[t]).sqrt() * e) / alfa[t].sqrt()
            if t > 0:
                x = x + beta[t].sqrt() * torch.randn(n, 64, generator=g)
        return ((x.clamp(-1, 1) + 1) / 2).numpy()


def orgat_nn(k, model, X, y, seed, qadamlar, ema=None):
    torch.manual_seed(seed)
    opt = torch.optim.Adam(model.parameters(), lr=k.lr)
    jad = torch.optim.lr_scheduler.LambdaLR(opt, lambda s: 0.5 * (1 + math.cos(math.pi * s / qadamlar)))
    X, y = torch.tensor(X), torch.tensor(y)
    g = torch.Generator().manual_seed(seed)
    orta = copy.deepcopy(model) if ema else None
    for _ in range(qadamlar):
        i = torch.randint(0, len(X), (k.bs,), generator=g)
        if isinstance(model, Diffusion):
            loss = model.loss(X[i], y[i], g, k.p_shartsiz)
        else:
            loss = model.loss(X[i], y[i], g)
        opt.zero_grad()
        loss.backward()
        opt.step()
        jad.step()
        if orta is not None:
            with torch.no_grad():
                for po, p in zip(orta.parameters(), model.parameters()):
                    po.lerp_(p, 1 - ema)
    return (orta if orta is not None else model).eval()


def yarat(nom, k, X, y, seed):
    if nom == "GMM":
        return ShartliGMM(k, X, y, seed)
    torch.manual_seed(seed)
    if nom == "cVAE":
        return orgat_nn(k, CVAE(k), X, y, seed, k.vae_qadam)
    return orgat_nn(k, Diffusion(k), X, y, seed, k.dif_qadam, ema=k.ema)


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


def chiz(x):
    q = np.clip(np.round(x.reshape(8, 8) * 9), 0, 9).astype(int)
    return ["".join(BELGI[v] * 2 for v in qator) for qator in q]


def narx(nom, m, k):
    """Parametrlar, bitta namunaga tarmoq chaqiruvlari va taxminiy FLOP."""
    if nom == "GMM":
        return m.parametrlar(), 0, 2 * k.pca * k.pca + 2 * k.pca * 64
    n_par = sum(p.numel() for p in m.parameters())
    if nom == "cVAE":
        return n_par, 1, 2 * ((k.vae_z + 10) * k.vae_h + k.vae_h * 64)
    bir = 2 * (128 * k.dif_h + k.dif_h * k.dif_h + k.dif_h * 64)
    return n_par, 2 * k.dif_T, 2 * k.dif_T * bir            # CFG: har qadamda 2 chaqiruv


def main() -> None:
    torch.set_num_threads(1)
    k = Konfig()
    X, y, tr, va, te = malumot(k)
    klf = orgat_klf(k, X[tr], y[tr])
    baho = Baholovchi(klf, X[tr], X[va], y[va])
    c = y[va]
    metrikalar = ["togri", "FD", "precision", "recall", "yodlash", "nusxa"]

    print("=== 1. Diffusion: guidance kuchi w ni validatsiyada tanlash (seed 0) ===")
    dif = yarat("diffusion", k, X[tr], y[tr], seed=0)
    print("  w     " + "".join(f"{m:>10}" for m in metrikalar))
    fd_w = {}
    for w in (0.0, 0.3, 0.5, 1.0):
        r = baho(dif.namuna(c, seed=1000, w=w), c)
        fd_w[w] = r["FD"]
        print(f"  {w:<5} " + "".join(f"{r[m]:>10.3f}" for m in metrikalar))
    w_eng = min(fd_w, key=fd_w.get)
    print(f"  FD bo'yicha eng yaxshi w = {w_eng} (Konfig.cfg_w = {k.cfg_w})")
    print("  w oshsa: to'g'ri sinf ulushi oshadi, recall tushadi (26.6 murosasi)")

    print("\n=== 2. Uch nomzod validatsiyada (seed 0, bir xil 360 ta shart) ===")
    modellar = {"GMM": yarat("GMM", k, X[tr], y[tr], seed=0),
                "cVAE": yarat("cVAE", k, X[tr], y[tr], seed=0), "diffusion": dif}
    print("  model     " + "".join(f"{m:>10}" for m in metrikalar))
    namunalar = {}
    for nom, m in modellar.items():
        namunalar[nom] = m.namuna(c, seed=1000)
        r = baho(namunalar[nom], c)
        print(f"  {nom:<9} " + "".join(f"{r[m_]:>10.3f}" for m_ in metrikalar))

    print("\n=== 3. Hisob narxi ===")
    print("  model      parametrlar  tarmoq chaqiruvi/namuna   ~FLOP/namuna")
    for nom, m in modellar.items():
        p, ch, fl = narx(nom, m, k)
        print(f"  {nom:<10} {p:>11} {ch:>24} {fl:>14,}".replace(",", " "))

    print("\n=== 4. '3' raqami: haqiqiy (val) va har modeldan bittadan namuna ===")
    i = int(np.flatnonzero(c == 3)[0])
    rasmlar = [X[va][i]] + [namunalar[n][i] for n in modellar]
    print(("  " + "".join(f"{n:<20}" for n in ["haqiqiy"] + list(modellar))).rstrip())
    for qatorlar in zip(*[chiz(r) for r in rasmlar]):
        print("  " + "".join(f"{q:<20}" for q in qatorlar).rstrip())


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Diffusion: guidance kuchi w ni validatsiyada tanlash (seed 0) ===
  w          togri        FD precision    recall   yodlash     nusxa
  0.0        0.811     8.886     0.800     0.828     1.536     0.000
  0.3        0.928     4.452     0.828     0.797     1.462     0.000
  0.5        0.969     4.857     0.839     0.786     1.416     0.000
  1.0        1.000    13.950     0.847     0.653     1.346     0.000
  FD bo'yicha eng yaxshi w = 0.3 (Konfig.cfg_w = 0.3)
  w oshsa: to'g'ri sinf ulushi oshadi, recall tushadi (26.6 murosasi)

=== 2. Uch nomzod validatsiyada (seed 0, bir xil 360 ta shart) ===
  model          togri        FD precision    recall   yodlash     nusxa
  GMM            0.989     1.951     0.931     0.781     0.953     0.031
  cVAE           1.000     6.586     1.000     0.014     0.915     0.036
  diffusion      0.928     4.452     0.828     0.797     1.462     0.000

=== 3. Hisob narxi ===
  model      parametrlar  tarmoq chaqiruvi/namuna   ~FLOP/namuna
  GMM               8274                        0          3 360
  cVAE             22352                        1         20 992
  diffusion        74528                      100     14 745 600

=== 4. '3' raqami: haqiqiy (val) va har modeldan bittadan namuna ===
  haqiqiy             GMM                 cVAE                diffusion
      ..++%%--            ..**%%##..          ++%%%%**..          **++@@==
      %%**--**          ..@@##**@@::        ::##**####::        ::@@..@@@@  --
    ::%%..::%%          --%%  **@@          ..::::##++          ..    @@@@::
      --..##++          ::    @@**            ..++%%::              --@@++
          **##..            ::@@%%..          ..++%%--                @@%%
            ==**          ..  --##::          ..::****..      --        @@##
      ##++::::%%          --==**%%::          ==++##++        ==..::  ==@@##
      ..**@@%%**..        ..####++::          **%%**..            ==--@@@@++

Nima ko'rsatdi: 2.3-bo'lim. Guidance kuchi FD bo'yicha tanlandi (w = 0.3) — ham w = 0 (so'ralgan sinf 81.1%), ham w = 1.0 (recall 0.653) dan yaxshi. Bitta urug'da (seed 0) GMM FD 1.951, diffusion 4.452, cVAE 6.586. ASCII rasmlar metrikalarni "ko'z bilan" tasdiqlaydi: GMM ning "3" i haqiqiyga o'xshash, cVAE niki silliq va o'rtachalashgan (26.2 dagi xiralik), diffusion niki esa "3" shaklida, lekin shovqinli piksellar bilan. Lekin qaror ko'zga emas, keyingi misoldagi uch urug'ga asoslanadi. Narx jadvali: diffusion 74 528 parametr, har namunaga 100 tarmoq chaqiruvi va ~14.7 MFLOP; GMM — 8 274 parametr va ~3.4 kFLOP.

Misol 3 — Uch urug'da juftlashgan taqqoslash va qaror

python
"""3-qadam: uch urug'da juftlashgan taqqoslash, yodlash darvozasi va eng sodda munosib model."""


import copy
import math
import warnings
from dataclasses import dataclass

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from scipy import linalg
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.mixture import GaussianMixture
from sklearn.model_selection import train_test_split


SODDALIK = ["GMM", "cVAE", "diffusion"]          # soddadan murakkabga


@dataclass(frozen=True)
class Konfig:
    seed: int = 0
    pca: int = 20
    gmm_k: int = 3
    vae_z: int = 8
    vae_h: int = 128
    vae_qadam: int = 800
    dif_T: int = 50
    dif_h: int = 192
    dif_qadam: int = 1000
    ema: float = 0.99
    p_shartsiz: float = 0.1
    cfg_w: float = 0.3                     # 2-qadamda validatsiyada tanlangan
    bs: int = 128
    lr: float = 2e-3
    klf_qadam: int = 600
    urug_soni: int = 3
    nusxa_max: float = 0.10                # yodlash darvozasi (haqiqiyda ~0.05)
    wm_a: float = 0.04                     # watermark kuchi (26.9)


def malumot(k):
    X, y = load_digits(return_X_y=True)
    X = (X / 16).astype(np.float32)
    i = np.arange(len(X))
    ish, te = train_test_split(i, test_size=0.2, stratify=y, random_state=k.seed)
    tr, va = train_test_split(ish, test_size=0.25, stratify=y[ish], random_state=k.seed)
    return X, y, tr, va, te


# ---------------- baholash asbobi (faqat o'quv qismida o'rgatiladi) ----------------
class Klf(nn.Module):
    def __init__(self):
        super().__init__()
        self.f = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU())
        self.bosh = nn.Linear(64, 10)

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


def orgat_klf(k, X, y):
    torch.manual_seed(k.seed)
    m = Klf()
    opt = torch.optim.AdamW(m.parameters(), lr=k.lr, weight_decay=1e-3)
    X, y = torch.tensor(X), torch.tensor(y)
    g = torch.Generator().manual_seed(k.seed)
    for _ in range(k.klf_qadam):
        i = torch.randint(0, len(X), (128,), generator=g)
        xb = (X[i] + 0.05 * torch.randn(X[i].shape, generator=g)).clamp(0, 1)
        loss = F.cross_entropy(m(xb), y[i])
        opt.zero_grad()
        loss.backward()
        opt.step()
    return m.eval()


def frechet(a, b):
    m1, m2 = a.mean(0), b.mean(0)
    s1, s2 = np.cov(a, rowvar=False), np.cov(b, rowvar=False)
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        cs = np.real(linalg.sqrtm(s1 @ s2))
    return float(((m1 - m2) ** 2).sum() + np.trace(s1 + s2 - 2 * cs))


def prec_recall(real, gen, k=3):
    """k-NN precision/recall: namuna boshqa to'plamning k-NN sharlaridan biriga tushadimi."""
    real, gen = torch.tensor(real), torch.tensor(gen)
    rr = torch.cdist(real, real).kthvalue(k + 1, dim=1).values
    rg = torch.cdist(gen, gen).kthvalue(k + 1, dim=1).values
    d = torch.cdist(gen, real)
    return (float((d <= rr[None]).any(1).float().mean()),
            float((d.T <= rg[None]).any(1).float().mean()))


class Baholovchi:
    """Bitta mos yozuvli haqiqiy to'plam (val yoki test) bilan barcha metrikalar."""

    def __init__(self, klf, X_oquv, X_ref, y_ref):
        self.klf, self.X_oquv = klf, torch.tensor(X_oquv)
        self.y_ref = y_ref
        self.f_ref = self.xus(X_ref)
        d = torch.cdist(torch.tensor(X_ref), self.X_oquv).min(1).values
        self.d_real = float(d.mean())                    # yangi haqiqiy namuna o'quvdan qancha uzoq
        self.q05 = float(d.quantile(0.05))

    @torch.no_grad()
    def xus(self, X):
        return self.klf.f(torch.as_tensor(X, dtype=torch.float32)).double().numpy()

    @torch.no_grad()
    def __call__(self, G, c):
        G = torch.as_tensor(G, dtype=torch.float32)
        togri = float((self.klf(G).argmax(1).numpy() == c).mean())
        fg = self.xus(G)
        p, r = prec_recall(self.f_ref, fg)
        d = torch.cdist(G, self.X_oquv).min(1).values
        return {"togri": togri, "FD": frechet(fg, self.f_ref), "precision": p, "recall": r,
                "yodlash": float(d.mean()) / self.d_real, "nusxa": float((d < self.q05).float().mean())}


# ---------------- nomzodlar ----------------
class ShartliGMM:
    """Har sinfga PCA fazosida GaussianMixture; namuna torch.Generator bilan."""

    def __init__(self, k, X, y, seed):
        pca = PCA(k.pca, random_state=seed).fit(X)
        Z = pca.transform(X)
        self.komp = torch.tensor(pca.components_, dtype=torch.float64)
        self.orta = torch.tensor(pca.mean_, dtype=torch.float64)
        w, mu, L = [], [], []
        for c in range(10):
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                gm = GaussianMixture(k.gmm_k, random_state=seed).fit(Z[y == c])
            w.append(gm.weights_)
            mu.append(gm.means_)
            L.append(np.linalg.cholesky(gm.covariances_ + 1e-6 * np.eye(k.pca)))
        self.w, self.mu, self.L = (torch.tensor(np.array(a)) for a in (w, mu, L))

    def namuna(self, c, seed):
        g = torch.Generator().manual_seed(seed)
        c = torch.as_tensor(c)
        j = torch.multinomial(self.w[c], 1, generator=g)[:, 0]
        e = torch.randn(len(c), self.mu.shape[2], generator=g, dtype=torch.float64)
        z = self.mu[c, j] + (self.L[c, j] @ e[:, :, None])[:, :, 0]
        return (z @ self.komp + self.orta).clamp(0, 1).float().numpy()

    def parametrlar(self):
        return sum(t.numel() for t in (self.komp, self.orta, self.w, self.mu)) + \
            10 * self.L.shape[1] * self.L.shape[2] * (self.L.shape[2] + 1) // 2


class CVAE(nn.Module):
    def __init__(self, k):
        super().__init__()
        self.z = k.vae_z
        self.enc = nn.Sequential(nn.Linear(74, k.vae_h), nn.ReLU(), nn.Linear(k.vae_h, 2 * k.vae_z))
        self.dec = nn.Sequential(nn.Linear(k.vae_z + 10, k.vae_h), nn.ReLU(), nn.Linear(k.vae_h, 64))

    def loss(self, x, c, g):
        oh = F.one_hot(c, 10).float()
        mu, logv = self.enc(torch.cat([x, oh], 1)).chunk(2, 1)
        z = mu + torch.randn(mu.shape, generator=g) * (0.5 * logv).exp()
        r = self.dec(torch.cat([z, oh], 1))
        kl = -0.5 * (1 + logv - mu ** 2 - logv.exp()).sum(1)
        return (F.binary_cross_entropy_with_logits(r, x, reduction="none").sum(1) + kl).mean()

    @torch.no_grad()
    def namuna(self, c, seed):
        g = torch.Generator().manual_seed(seed)
        c = torch.as_tensor(c)
        z = torch.randn(len(c), self.z, generator=g)
        return torch.sigmoid(self.dec(torch.cat([z, F.one_hot(c, 10).float()], 1))).numpy()


def jadval(T):
    beta = torch.linspace(1e-4 * 1000 / T, 0.02 * 1000 / T, T)
    return beta, 1 - beta, torch.cumprod(1 - beta, 0)


class Diffusion(nn.Module):
    """eps-bashoratchi MLP; sinf 10 = shartsiz (classifier-free guidance uchun)."""

    def __init__(self, k):
        super().__init__()
        self.T, self.w = k.dif_T, k.cfg_w
        self.emb = nn.Embedding(11, 32)
        self.net = nn.Sequential(nn.Linear(128, k.dif_h), nn.SiLU(), nn.Linear(k.dif_h, k.dif_h),
                                 nn.SiLU(), nn.Linear(k.dif_h, 64))

    def forward(self, x, t, c):
        f = torch.exp(-math.log(1000) * torch.arange(16) / 16)
        a = t.float()[:, None] * f[None]
        return self.net(torch.cat([x, a.sin(), a.cos(), self.emb(c)], 1))

    def loss(self, x, c, g, p_shartsiz):
        _, _, ab = jadval(self.T)
        x = x * 2 - 1
        t = torch.randint(0, self.T, (len(x),), generator=g)
        e = torch.randn(x.shape, generator=g)
        a = ab[t][:, None]
        c = torch.where(torch.rand(len(x), generator=g) < p_shartsiz, 10, c)
        return F.mse_loss(self(a.sqrt() * x + (1 - a).sqrt() * e, t, c), e)

    @torch.no_grad()
    def namuna(self, c, seed, w=None):
        w = self.w if w is None else w
        g = torch.Generator().manual_seed(seed)
        beta, alfa, ab = jadval(self.T)
        c = torch.as_tensor(c)
        n = len(c)
        x = torch.randn(n, 64, generator=g)
        for t in reversed(range(self.T)):
            tt = torch.full((2 * n,), t)
            e = self(torch.cat([x, x]), tt, torch.cat([c, torch.full_like(c, 10)]))
            e = (1 + w) * e[:n] - w * e[n:]                      # classifier-free guidance
            x = (x - beta[t] / (1 - ab[t]).sqrt() * e) / alfa[t].sqrt()
            if t > 0:
                x = x + beta[t].sqrt() * torch.randn(n, 64, generator=g)
        return ((x.clamp(-1, 1) + 1) / 2).numpy()


def orgat_nn(k, model, X, y, seed, qadamlar, ema=None):
    torch.manual_seed(seed)
    opt = torch.optim.Adam(model.parameters(), lr=k.lr)
    jad = torch.optim.lr_scheduler.LambdaLR(opt, lambda s: 0.5 * (1 + math.cos(math.pi * s / qadamlar)))
    X, y = torch.tensor(X), torch.tensor(y)
    g = torch.Generator().manual_seed(seed)
    orta = copy.deepcopy(model) if ema else None
    for _ in range(qadamlar):
        i = torch.randint(0, len(X), (k.bs,), generator=g)
        if isinstance(model, Diffusion):
            loss = model.loss(X[i], y[i], g, k.p_shartsiz)
        else:
            loss = model.loss(X[i], y[i], g)
        opt.zero_grad()
        loss.backward()
        opt.step()
        jad.step()
        if orta is not None:
            with torch.no_grad():
                for po, p in zip(orta.parameters(), model.parameters()):
                    po.lerp_(p, 1 - ema)
    return (orta if orta is not None else model).eval()


def yarat(nom, k, X, y, seed):
    if nom == "GMM":
        return ShartliGMM(k, X, y, seed)
    torch.manual_seed(seed)
    if nom == "cVAE":
        return orgat_nn(k, CVAE(k), X, y, seed, k.vae_qadam)
    return orgat_nn(k, Diffusion(k), X, y, seed, k.dif_qadam, ema=k.ema)


def main() -> None:
    torch.set_num_threads(1)
    k = Konfig()
    X, y, tr, va, te = malumot(k)
    klf = orgat_klf(k, X[tr], y[tr])
    baho = Baholovchi(klf, X[tr], X[va], y[va])       # test TEGILMAYDI
    c = y[va]

    print(f"=== 1. {k.urug_soni} urug' x 3 nomzod, validatsiya ({len(va)} namuna) ===")
    natija = {n: [] for n in SODDALIK}
    for s in range(k.urug_soni):
        for nom in SODDALIK:
            m = yarat(nom, k, X[tr], y[tr], seed=s)
            natija[nom].append(baho(m.namuna(c, seed=1000 + s), c))
    metrikalar = ["togri", "FD", "precision", "recall", "yodlash", "nusxa"]
    print("  model (o'rtacha) " + "".join(f"{m:>10}" for m in metrikalar))
    for nom in SODDALIK:
        print(f"  {nom:<16} " + "".join(f"{np.mean([r[m] for r in natija[nom]]):>10.3f}"
                                         for m in metrikalar))
    print("  FD urug'lar bo'yicha:")
    for nom in SODDALIK:
        print(f"    {nom:<10} " + ", ".join(f"{r['FD']:.3f}" for r in natija[nom]))

    print("\n=== 2. Juftlashgan farq: asosiy metrika FD (kichik = yaxshi) ===")
    fd = {n: np.array([r["FD"] for r in natija[n]]) for n in SODDALIK}
    eng = min(SODDALIK, key=lambda n: fd[n].mean())
    print(f"  eng yaxshi (o'rtacha FD): {eng}")
    yomon = {eng: False}
    for nom in SODDALIK:
        if nom == eng:
            continue
        d = fd[nom] - fd[eng]
        se = d.std(ddof=1) / math.sqrt(len(d))
        yomon[nom] = bool(d.mean() > 2 * se)
        holat = "sezilarli yomon" if yomon[nom] else "sezilarli farq yo'q"
        print(f"  {nom:<10} - {eng}: {d.mean():+.3f}, SE {se:.3f} -> {holat}")
    for m in ("togri", "recall"):
        v = {n: np.array([r[m] for r in natija[n]]) for n in SODDALIK}
        print(f"  (diagnostika) {m}: " + ", ".join(f"{n} {v[n].mean():.3f}" for n in SODDALIK))

    print("\n=== 3. Yodlash darvozasi va qaror ===")
    otdi = {}
    for nom in SODDALIK:
        ns = np.array([r["nusxa"] for r in natija[nom]])
        yd = np.mean([r["yodlash"] for r in natija[nom]])
        otdi[nom] = bool(ns.max() <= k.nusxa_max)
        print(f"  {nom:<10} nusxa ulushi (max urug') {ns.max():.3f}, yodlash nisbati {yd:.3f} "
              f"-> {'OK' if otdi[nom] else 'XAVF'}")
    munosib = [n for n in SODDALIK if not yomon[n] and otdi[n]]
    tanlov = munosib[0] if munosib else None
    print(f"  munosib (sezilarli yomon emas + darvozadan o'tgan): {munosib}")
    print(f"  QAROR: eng sodda munosib model -> {tanlov}")
    if tanlov == SODDALIK[0]:
        print("  murakkab nomzodlar bu byudjetda murakkabligini oqlamadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. 3 urug' x 3 nomzod, validatsiya (360 namuna) ===
  model (o'rtacha)      togri        FD precision    recall   yodlash     nusxa
  GMM                   0.978     2.151     0.941     0.806     0.947     0.022
  cVAE                  1.000     6.605     1.000     0.021     0.915     0.043
  diffusion             0.923     4.584     0.787     0.793     1.457     0.000
  FD urug'lar bo'yicha:
    GMM        1.951, 2.169, 2.331
    cVAE       6.586, 6.671, 6.557
    diffusion  4.452, 4.815, 4.486

=== 2. Juftlashgan farq: asosiy metrika FD (kichik = yaxshi) ===
  eng yaxshi (o'rtacha FD): GMM
  cVAE       - GMM: +4.454, SE 0.121 -> sezilarli yomon
  diffusion  - GMM: +2.433, SE 0.146 -> sezilarli yomon
  (diagnostika) togri: GMM 0.978, cVAE 1.000, diffusion 0.923
  (diagnostika) recall: GMM 0.806, cVAE 0.021, diffusion 0.793

=== 3. Yodlash darvozasi va qaror ===
  GMM        nusxa ulushi (max urug') 0.031, yodlash nisbati 0.947 -> OK
  cVAE       nusxa ulushi (max urug') 0.053, yodlash nisbati 0.915 -> OK
  diffusion  nusxa ulushi (max urug') 0.000, yodlash nisbati 1.457 -> OK
  munosib (sezilarli yomon emas + darvozadan o'tgan): ['GMM']
  QAROR: eng sodda munosib model -> GMM
  murakkab nomzodlar bu byudjetda murakkabligini oqlamadi

Nima ko'rsatdi: 2.4-bo'lim. Uch urug'da GMM FD 1.951–2.331, diffusion 4.452–4.815, cVAE 6.557–6.671 — urug'lar orasidagi tarqalish nomzodlar orasidagi farqdan ancha kichik. Juftlashgan farqlar: cVAE +4.454 (SE 0.121), diffusion +2.433 (SE 0.146) — ikkalasi ham 2·SE dan ancha katta. Yodlash darvozasidan uchalasi ham o'tdi (nusxa max 0.031, 0.053, 0.000). Qoida GMM ni tanladi. Diagnostika ustunlari manzarani to'ldiradi: cVAE so'ralgan sinfni doim beradi (1.000), lekin qamrovi yo'q (recall 0.021); diffusion qamrovda GMM ga yaqin (0.793 va 0.806), lekin to'g'ri sinf ulushi past (0.923) va namunalari shovqinli (yodlash nisbati 1.457).

Misol 4 — Test, paket, deterministik nazorat, watermark va hisobot

python
"""4-qadam: test bir marta, weights_only paket, deterministik namuna, watermark va hisobot."""

import io
import sys
import tempfile
import warnings
from dataclasses import asdict, dataclass
from pathlib import Path

import numpy as np
import sklearn
import torch
import torch.nn as nn
import torch.nn.functional as F
from PIL import Image
from scipy import linalg
from scipy.ndimage import uniform_filter
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.mixture import GaussianMixture
from sklearn.model_selection import train_test_split


TANLOV = "GMM"                                # 3-qadam qarori


@dataclass(frozen=True)
class Konfig:
    seed: int = 0
    pca: int = 20
    gmm_k: int = 3
    vae_z: int = 8
    vae_h: int = 128
    vae_qadam: int = 800
    dif_T: int = 50
    dif_h: int = 192
    dif_qadam: int = 1000
    ema: float = 0.99
    p_shartsiz: float = 0.1
    cfg_w: float = 0.3                     # 2-qadamda validatsiyada tanlangan
    bs: int = 128
    lr: float = 2e-3
    klf_qadam: int = 600
    urug_soni: int = 3
    nusxa_max: float = 0.10                # yodlash darvozasi (haqiqiyda ~0.05)
    wm_a: float = 0.04                     # watermark kuchi (26.9)


def malumot(k):
    X, y = load_digits(return_X_y=True)
    X = (X / 16).astype(np.float32)
    i = np.arange(len(X))
    ish, te = train_test_split(i, test_size=0.2, stratify=y, random_state=k.seed)
    tr, va = train_test_split(ish, test_size=0.25, stratify=y[ish], random_state=k.seed)
    return X, y, tr, va, te


# ---------------- baholash asbobi (faqat o'quv qismida o'rgatiladi) ----------------
class Klf(nn.Module):
    def __init__(self):
        super().__init__()
        self.f = nn.Sequential(nn.Linear(64, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU())
        self.bosh = nn.Linear(64, 10)

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


def orgat_klf(k, X, y):
    torch.manual_seed(k.seed)
    m = Klf()
    opt = torch.optim.AdamW(m.parameters(), lr=k.lr, weight_decay=1e-3)
    X, y = torch.tensor(X), torch.tensor(y)
    g = torch.Generator().manual_seed(k.seed)
    for _ in range(k.klf_qadam):
        i = torch.randint(0, len(X), (128,), generator=g)
        xb = (X[i] + 0.05 * torch.randn(X[i].shape, generator=g)).clamp(0, 1)
        loss = F.cross_entropy(m(xb), y[i])
        opt.zero_grad()
        loss.backward()
        opt.step()
    return m.eval()


def frechet(a, b):
    m1, m2 = a.mean(0), b.mean(0)
    s1, s2 = np.cov(a, rowvar=False), np.cov(b, rowvar=False)
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        cs = np.real(linalg.sqrtm(s1 @ s2))
    return float(((m1 - m2) ** 2).sum() + np.trace(s1 + s2 - 2 * cs))


def prec_recall(real, gen, k=3):
    """k-NN precision/recall: namuna boshqa to'plamning k-NN sharlaridan biriga tushadimi."""
    real, gen = torch.tensor(real), torch.tensor(gen)
    rr = torch.cdist(real, real).kthvalue(k + 1, dim=1).values
    rg = torch.cdist(gen, gen).kthvalue(k + 1, dim=1).values
    d = torch.cdist(gen, real)
    return (float((d <= rr[None]).any(1).float().mean()),
            float((d.T <= rg[None]).any(1).float().mean()))


class Baholovchi:
    """Bitta mos yozuvli haqiqiy to'plam (val yoki test) bilan barcha metrikalar."""

    def __init__(self, klf, X_oquv, X_ref, y_ref):
        self.klf, self.X_oquv = klf, torch.tensor(X_oquv)
        self.y_ref = y_ref
        self.f_ref = self.xus(X_ref)
        d = torch.cdist(torch.tensor(X_ref), self.X_oquv).min(1).values
        self.d_real = float(d.mean())                    # yangi haqiqiy namuna o'quvdan qancha uzoq
        self.q05 = float(d.quantile(0.05))

    @torch.no_grad()
    def xus(self, X):
        return self.klf.f(torch.as_tensor(X, dtype=torch.float32)).double().numpy()

    @torch.no_grad()
    def __call__(self, G, c):
        G = torch.as_tensor(G, dtype=torch.float32)
        togri = float((self.klf(G).argmax(1).numpy() == c).mean())
        fg = self.xus(G)
        p, r = prec_recall(self.f_ref, fg)
        d = torch.cdist(G, self.X_oquv).min(1).values
        return {"togri": togri, "FD": frechet(fg, self.f_ref), "precision": p, "recall": r,
                "yodlash": float(d.mean()) / self.d_real, "nusxa": float((d < self.q05).float().mean())}


# ---------------- nomzodlar ----------------
class ShartliGMM:
    """Har sinfga PCA fazosida GaussianMixture; namuna torch.Generator bilan."""

    def __init__(self, k, X, y, seed):
        pca = PCA(k.pca, random_state=seed).fit(X)
        Z = pca.transform(X)
        self.komp = torch.tensor(pca.components_, dtype=torch.float64)
        self.orta = torch.tensor(pca.mean_, dtype=torch.float64)
        w, mu, L = [], [], []
        for c in range(10):
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                gm = GaussianMixture(k.gmm_k, random_state=seed).fit(Z[y == c])
            w.append(gm.weights_)
            mu.append(gm.means_)
            L.append(np.linalg.cholesky(gm.covariances_ + 1e-6 * np.eye(k.pca)))
        self.w, self.mu, self.L = (torch.tensor(np.array(a)) for a in (w, mu, L))

    def namuna(self, c, seed):
        g = torch.Generator().manual_seed(seed)
        c = torch.as_tensor(c)
        j = torch.multinomial(self.w[c], 1, generator=g)[:, 0]
        e = torch.randn(len(c), self.mu.shape[2], generator=g, dtype=torch.float64)
        z = self.mu[c, j] + (self.L[c, j] @ e[:, :, None])[:, :, 0]
        return (z @ self.komp + self.orta).clamp(0, 1).float().numpy()

    def parametrlar(self):
        return sum(t.numel() for t in (self.komp, self.orta, self.w, self.mu)) + \
            10 * self.L.shape[1] * self.L.shape[2] * (self.L.shape[2] + 1) // 2


FORMAT = 1
KALIT = 2026          # watermark kaliti - SIR: paketga yozilmaydi (sirlar omborida)
CHEGARA = 4.0


class YuklanganGMM(ShartliGMM):
    """Paketdagi tenzorlardan tiklanadi - sklearn obyekti kerak emas."""

    def __init__(self, holat):
        self.komp, self.orta = holat["komp"], holat["orta"]
        self.w, self.mu, self.L = holat["w"], holat["mu"], holat["L"]


def naqsh(kalit):
    g = torch.Generator().manual_seed(kalit)
    return (torch.randint(0, 2, (16, 16), generator=g) * 2 - 1).numpy().astype(float)


def chiqish(x8, kalit=None, a=0.0):
    """Xizmat formati: 8x8 -> 16x16; kalit berilsa ko'rinmas watermark 26.9-bob."""
    x = np.clip(uniform_filter(np.kron(x8.reshape(-1, 8, 8), np.ones((1, 2, 2))),
                               size=(1, 3, 3)), 0, 1)
    return x if kalit is None else np.clip(x + a * naqsh(kalit), 0, 1)


def z_ball(x, kalit):
    def yuq(v):
        return v - uniform_filter(v, size=(1, 3, 3), mode="nearest")
    r = yuq(x).reshape(len(x), -1)
    w = yuq(naqsh(kalit)[None]).reshape(-1)
    r, w = r - r.mean(1, keepdims=True), w - w.mean()
    return np.sqrt(r.shape[1]) * (r @ w) / (np.linalg.norm(r, axis=1) * np.linalg.norm(w) + 1e-12)


def jpeg(x, sifat):
    chiq = []
    for rasm in x:
        buf = io.BytesIO()
        Image.fromarray((rasm * 255).round().astype(np.uint8)).save(buf, "JPEG", quality=sifat)
        chiq.append(np.asarray(Image.open(io.BytesIO(buf.getvalue())), dtype=float) / 255)
    return np.array(chiq)


def kichrayt(x16):
    return x16.reshape(-1, 8, 2, 8, 2).mean((2, 4)).reshape(-1, 64).astype(np.float32)


def main() -> None:
    torch.set_num_threads(1)
    k = Konfig()
    X, y, tr, va, te = malumot(k)
    ish = np.r_[tr, va]
    klf = orgat_klf(k, X[tr], y[tr])                 # baholash asbobi - o'sha, faqat o'quvda

    print(f"=== 1. Yakuniy model (3-qadam qarori: {TANLOV}) ish to'plamida ===")
    model = ShartliGMM(k, X[ish], y[ish], seed=k.seed)
    print(f"  o'quv + val = {len(ish)} rasm; PCA {k.pca}, har sinfga {k.gmm_k} komponent; "
          f"{model.parametrlar()} parametr")

    print("\n=== 2. Testni BIR MARTA ochamiz ===")
    baho = Baholovchi(klf, X[ish], X[te], y[te])
    c = y[te]
    metrikalar = ["togri", "FD", "precision", "recall", "yodlash", "nusxa"]
    print("  manba               " + "".join(f"{m:>10}" for m in metrikalar))
    rng = np.random.default_rng(k.seed)
    nus = np.array([rng.choice(ish[y[ish] == s]) for s in c])
    r0 = baho(X[nus], c)
    print("  nusxachi (pastki FD) " + "".join(f"{r0[m]:>10.3f}" for m in metrikalar))
    test = [baho(model.namuna(c, seed=s), c) for s in (1, 2, 3)]
    for i, r in enumerate(test, 1):
        print(f"  GMM, namuna urug'i {i} " + "".join(f"{r[m]:>10.3f}" for m in metrikalar))
    tfd = np.array([r["FD"] for r in test])
    tn = max(r["nusxa"] for r in test)
    print(f"  test FD {tfd.mean():.3f} (+- {tfd.std(ddof=1):.3f}); nusxa max {tn:.3f} -> "
          f"{'darvozadan otdi' if tn <= k.nusxa_max else 'DARVOZA BUZILDI'}".replace("otdi", "o'tdi"))

    with tempfile.TemporaryDirectory() as papka:
        print("\n=== 3. Paket: torch.save + torch.load(weights_only=True) ===")
        nazorat_c = torch.arange(10).repeat(2)
        paket = {
            "format": FORMAT,
            "konfig": asdict(k),
            "tanlov": TANLOV,
            "holat": {"komp": model.komp, "orta": model.orta, "w": model.w,
                      "mu": model.mu, "L": model.L},
            "metrika": {"test_FD": round(float(tfd.mean()), 4),
                        "test_togri": round(float(np.mean([r["togri"] for r in test])), 4),
                        "nusxa_max": round(float(tn), 4)},
            "watermark": {"usul": "16x16, +-a naqsh, z-test", "a": k.wm_a, "kalit_id": "wm-01"},
            "versiyalar": {"torch": str(torch.__version__), "sklearn": sklearn.__version__,
                           "python": sys.version.split()[0]},
            "nazorat": {"c": nazorat_c, "seed": 7,
                        "namuna": torch.tensor(model.namuna(nazorat_c, seed=7))},
        }
        yol = Path(papka) / "raqam_generatori.pt"
        torch.save(paket, yol)
        p = torch.load(yol, weights_only=True)
        print(f"  kalitlar: {sorted(p)}")
        print(f"  holat: {len(p['holat'])} tenzor, sklearn obyekti yo'q; kalit paketda: "
              f"{'HA' if 'kalit' in p['watermark'] else 'yoq'}".replace("yoq", "yo'q"))
        yuk = YuklanganGMM(p["holat"])
        n = p["nazorat"]
        a1 = torch.tensor(yuk.namuna(n["c"], seed=n["seed"]))
        a2 = torch.tensor(yuk.namuna(n["c"], seed=n["seed"]))
        b = torch.tensor(yuk.namuna(n["c"], seed=n["seed"] + 1))
        print(f"  nazorat (saqlangan namuna bilan aynan): {'OK' if torch.equal(a1, n['namuna']) else 'XATO'}")
        print(f"  bir xil urug' ikki marta: {torch.equal(a1, a2)}; boshqa urug': "
              f"{'farqli' if not torch.equal(a1, b) else 'BIR XIL'}")

    print(f"\n=== 4. Chiqishga watermark (a = {k.wm_a}) va uni aniqlash ===")
    G8 = yuk.namuna(c, seed=11)
    belgili = chiqish(G8, KALIT, k.wm_a)
    toza = chiqish(yuk.namuna(c, seed=12))
    haqiqiy = chiqish(X[te])
    qatorlar = [("belgili chiqish", belgili), ("belgili + JPEG 70", jpeg(belgili, 70)),
                ("belgisiz chiqish", toza), ("haqiqiy test", haqiqiy)]
    for nom, h in qatorlar:
        z = z_ball(h, KALIT)
        print(f"  {nom:<18} 'bor' ulushi {np.mean(z > CHEGARA):>6.1%}  z o'rtacha {z.mean():>6.2f}")
    with torch.no_grad():
        t0 = (klf(torch.tensor(kichrayt(chiqish(G8)))).argmax(1).numpy() == c).mean()
        t1 = (klf(torch.tensor(kichrayt(belgili))).argmax(1).numpy() == c).mean()
    print(f"  foydalilik: to'g'ri sinf ulushi watermarksiz {t0:.3f}, watermark bilan {t1:.3f}")

    print("\n=== 5. Yakuniy hisobot (model kartasi qisqacha) ===")
    print("  VAZIFA: shartli raqam generatori (8x8, sinf 0-9)")
    print("  METRIKA: asosiy - FD (o'z klassifikator xususiyatlarida); darvoza - nusxa ulushi")
    print(f"  QAROR: 3 urug', juftlashgan FD -> {TANLOV} (eng sodda, qolganlar sezilarli yomon)")
    print(f"  TEST: FD {tfd.mean():.3f}, to'g'ri sinf {np.mean([r['togri'] for r in test]):.3f}, "
          f"recall {np.mean([r['recall'] for r in test]):.3f}; nusxachi FD {r0['FD']:.3f}")
    print(f"  MAXFIYLIK: nusxa ulushi {tn:.3f} (haqiqiy yangi rasmda 0.05)")
    print(f"  PAKET: format {FORMAT}, weights_only=True, deterministik nazorat OK; kalit alohida")
    print("  CHEKLOVLAR: 8x8 raqamlar; a'zolik hujumi tekshirilmagan; watermark xiralash")
    print("    va siljitishga chidamsiz 26.9-bob; FD kichik n da shovqinli")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Yakuniy model (3-qadam qarori: GMM) ish to'plamida ===
  o'quv + val = 1437 rasm; PCA 20, har sinfga 3 komponent; 8274 parametr

=== 2. Testni BIR MARTA ochamiz ===
  manba                    togri        FD precision    recall   yodlash     nusxa
  nusxachi (pastki FD)      0.989     2.176     0.903     0.872     0.000     1.000
  GMM, namuna urug'i 1      0.981     2.480     0.953     0.831     0.954     0.036
  GMM, namuna urug'i 2      0.983     2.237     0.947     0.803     0.949     0.039
  GMM, namuna urug'i 3      0.989     2.368     0.964     0.792     0.945     0.036
  test FD 2.362 (+- 0.122); nusxa max 0.039 -> darvozadan o'tdi

=== 3. Paket: torch.save + torch.load(weights_only=True) ===
  kalitlar: ['format', 'holat', 'konfig', 'metrika', 'nazorat', 'tanlov', 'versiyalar', 'watermark']
  holat: 5 tenzor, sklearn obyekti yo'q; kalit paketda: yo'q
  nazorat (saqlangan namuna bilan aynan): OK
  bir xil urug' ikki marta: True; boshqa urug': farqli

=== 4. Chiqishga watermark (a = 0.04) va uni aniqlash ===
  belgili chiqish    'bor' ulushi 100.0%  z o'rtacha   9.98
  belgili + JPEG 70  'bor' ulushi  99.7%  z o'rtacha   5.79
  belgisiz chiqish   'bor' ulushi   0.0%  z o'rtacha   1.02
  haqiqiy test       'bor' ulushi   0.0%  z o'rtacha   0.92
  foydalilik: to'g'ri sinf ulushi watermarksiz 0.967, watermark bilan 0.969

=== 5. Yakuniy hisobot (model kartasi qisqacha) ===
  VAZIFA: shartli raqam generatori (8x8, sinf 0-9)
  METRIKA: asosiy - FD (o'z klassifikator xususiyatlarida); darvoza - nusxa ulushi
  QAROR: 3 urug', juftlashgan FD -> GMM (eng sodda, qolganlar sezilarli yomon)
  TEST: FD 2.362, to'g'ri sinf 0.984, recall 0.808; nusxachi FD 2.176
  MAXFIYLIK: nusxa ulushi 0.039 (haqiqiy yangi rasmda 0.05)
  PAKET: format 1, weights_only=True, deterministik nazorat OK; kalit alohida
  CHEKLOVLAR: 8x8 raqamlar; a'zolik hujumi tekshirilmagan; watermark xiralash
    va siljitishga chidamsiz 26.9-bob; FD kichik n da shovqinli

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.


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

Noto'g'ri fikr To'g'risi
"Eng zamonaviy model — eng yaxshi" Bu vazifa va byudjetda GMM FD 2.151, diffusion 4.584 — sezilarli farq
"FD eng past — eng yaxshi generator" Nusxachi FD 1.434 bilan eng yaxshi; faqat nusxa darvozasi ushlaydi
"So'ralgan sinfni 100% beradi — model yaxshi" Sinf o'rtachasi va cVAE 1.000, lekin recall 0.000 va 0.021
"FD — mutlaq sifat bahosi" n ga bog'liq: 6.474 (n=90) va 1.434 (n=360) — faqat bir xil n da solishtiriladi
"Guidance qancha kuchli — shuncha yaxshi" w = 1.0 da FD 13.950, recall 0.653
"Baholash klassifikatori — shunchaki vosita" U ham faqat o'quvda o'rgatiladi, aks holda asbob sizadi
"Generativ modelni pickle bilan saqlash yetarli" Tenzorlar + weights_only=True + urug'li nazorat
"Watermark rasmni buzadi" To'g'ri sinf ulushi 0.967 → 0.969

6. Keng tarqalgan xatolar va yechimlari

1. Asbob butun ma'lumotda

python
klf = orgat_klf(k, X, y)                                    # ⚠️ val/test ham ko'rilgan
klf = orgat_klf(k, X[tr], y[tr])                            # ✅ faqat o'quv

2. Turli n bilan FD

python
fd_gmm = baho(gmm.namuna(c_1000, 0), c_1000)["FD"]; fd_dif = baho(dif.namuna(c_200, 0), c_200)["FD"]  # ⚠️
fd = {n: baho(m.namuna(c, seed=1000), c)["FD"] for n, m in modellar.items()}                        # ✅ bir xil c

3. Darvozasiz tanlov

python
tanlov = min(SODDALIK, key=lambda n: fd[n].mean())          # ⚠️ nusxachi ham o'tadi
tanlov = next(n for n in SODDALIK if not yomon[n] and otdi[n])   # ✅ + nusxa darvozasi

4. Guidance ni testda tanlash

python
w = min(ws, key=lambda w: baho_test(dif.namuna(y[te], 0, w), y[te])["FD"])  # ⚠️
w = min(ws, key=lambda w: baho(dif.namuna(y[va], 1000, w), y[va])["FD"])    # ✅ val

5. Global urug' bilan namuna

python
z = torch.randn(n, 20)                                      # ⚠️ nazorat takrorlanmaydi
z = torch.randn(n, 20, generator=torch.Generator().manual_seed(seed))       # ✅

6. sklearn obyektini saqlash

python
torch.save({"gmm": gaussian_mixture}, yol); torch.load(yol, weights_only=False)  # ⚠️ pickle
torch.save({"holat": {"mu": mu, "L": L, "w": w, ...}}, yol)                     # ✅ tenzorlar

7. Watermark kaliti paketda

python
paket["watermark"] = {"kalit": 2026, "a": 0.04}             # ⚠️ sir tarqaladi
paket["watermark"] = {"kalit_id": "wm-01", "a": 0.04}       # ✅ kalit - sirlar omborida

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 21.12, 22.14, 23.14, 24.12, 25.14-darslar (o'tilgan): Loyiha skeleti — konfig, test qulfi, juftlashgan qaror, weights_only paket
  • 16-qism (o'tilgan): PCA va Gauss aralashmasi — bu yerda ular eng kuchli bazaviy bo'ldi
  • 26.1-26.8-darslar (o'tilgan): Butun qism — VAE, GAN, diffusion, guidance, baholash metrikalari, multimodal modellar
  • 26.9-dars (o'tilgan): Yodlash tekshiruvi va watermark — bu yerda loyiha darvozasi va mahsulot qismi
  • MLOps va deploy qismida: paketni xizmatga aylantirish, sampling urug'ini so'rov ID sidan olish, watermark kalitini sirlar omborida saqlash va chiqishlarni kuzatish

8. Eng yaxshi amaliyotlar

  1. Baholash asbobini faqat o'quvda o'rgating va ma'lum "generatorlar" bilan kalibrlang.

  2. Asosiy metrika va darvozani natijani ko'rishdan oldin yozing.

  3. FD ni faqat bir xil n va bir xil mos yozuv to'plami bilan solishtiring.

  4. Nomzodlarni soddalik tartibida yozing; oddiy statistik bazaviy majburiy.

  5. Giperparametrlarni (w, qadamlar) validatsiyada tanlang; narxni sifat yonida yozing.

  6. Qarorni bir necha urug'dagi juftlashgan farq, yodlash darvozasi va soddalik qoidasi bilan qabul qiling; byudjetni qarorga yozing.

  7. Paketda faqat tenzorlar; urug'li nazorat namunasi bilan determinizmni tekshiring.

  8. Chiqishni watermark bilan belgilang, kalitni alohida saqlang va foydalilik hamda FPR ni tekshiring.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # nega baholash klassifikatori faqat o'quv qismida o'rgatiladi?
2.  # nusxachi FD bo'yicha qanday natija beradi va uni nima ushlaydi?
3.  # sinf o'rtachasi generatori qaysi metrikada 1.0, qaysisida ~0?
4.  # n = 90 va n = 360 dagi FD larni solishtirish mumkinmi?
5.  # guidance w oshsa to'g'ri sinf ulushi va recall qanday o'zgaradi?
6.  # diffusion bitta namuna uchun necha marta tarmoq chaqiradi (T = 50, CFG)?
7.  # "sezilarli yomon" mezoni (3 urug')?
8.  # GMM va diffusion farqi 2*SE ichida bo'lsa - qaysi tanlanadi?
9.  # nusxa ulushi haqiqiy yangi rasmlarda nechaga teng va nega?
10. # nega GMM paketda sklearn obyekti sifatida emas, tenzorlar sifatida saqlanadi?
11. # deterministik nazorat nimani tekshiradi?
12. # watermark kaliti qayerda saqlanadi?
Javoblar
  1. Asbob ham ma'lumotdan o'rganadi; u val/test ni ko'rsa, o'lchov o'sha to'plamlarga moslashadi (sizish)
  2. Eng yaxshi FD lardan birini (1.434) — uni faqat nusxa ulushi (1.000) ushlaydi
  3. To'g'ri sinf ulushi 1.000, recall 0.000
  4. Yo'q — FD kichik n da yuqoriga siljigan (6.474 va 1.434)
  5. To'g'ri sinf ulushi oshadi (0.811 → 1.000), recall tushadi (0.828 → 0.653)
  6. 2 * 50 = 100
  7. mean(d) > 2 * std(d) / sqrt(3)
  8. GMM — soddarog'i
  9. 0.05 — chegara q05 aynan haqiqiy yangi rasmlarning 5-foizligi
  10. weights_only=True faqat tenzor va oddiy turlarni yuklaydi; pickle ixtiyoriy kod bajarishi mumkin
  11. Yuklangan model aynan saqlangan namunani qaytarishini: model o'zgarmagan va sampling urug' bilan deterministik
  12. Paketdan tashqarida — sirlar omborida; paketda faqat kalit_id

Vazifa 2: Xatolarni tuzating

python
1.  klf = orgat_klf(k, X[np.r_[tr, va]], y[np.r_[tr, va]])
    baho = Baholovchi(klf, X[tr], X[va], y[va])

2.  for w in (0.0, 0.3, 0.5, 1.0):
        print(w, Baholovchi(klf, X[tr], X[te], y[te])(dif.namuna(y[te], 0, w), y[te])["FD"])

3.  eng = min(SODDALIK, key=lambda n: fd[n].mean())
    print("tanlov:", eng)

4.  torch.save({"model": model}, yol)
    model = torch.load(yol, weights_only=False)

5.  paket["nazorat"] = {"namuna": torch.tensor(model.namuna(c, seed=7))}
Javoblar
python
1.  klf = orgat_klf(k, X[tr], y[tr])                          # asbob faqat o'quvda
    baho = Baholovchi(klf, X[tr], X[va], y[va])

2.  for w in (0.0, 0.3, 0.5, 1.0):                             # tanlov - validatsiyada
        print(w, baho(dif.namuna(y[va], 1000, w), y[va])["FD"])

3.  munosib = [n for n in SODDALIK if not yomon[n] and otdi[n]]
    print("tanlov:", munosib[0])                               # eng sodda munosib

4.  torch.save({"holat": {"komp": m.komp, "orta": m.orta, "w": m.w, "mu": m.mu, "L": m.L}}, yol)
    m = YuklanganGMM(torch.load(yol, weights_only=True)["holat"])

5.  paket["nazorat"] = {"c": c, "seed": 7,                     # shart va urug' ham kerak
                        "namuna": torch.tensor(model.namuna(c, seed=7))}

Vazifa 3: Asbob

Modellang:

  1. Klassifikatorni boshqa arxitektura bilan (masalan, kichik CNN) — nomzodlar tartibi o'zgaradimi?
  2. FD ni 1000 ta namuna bilan — mos yozuv sifatida o'quvning bir qismini ishlatish mumkinmi?
  3. Nusxa darvozasi chegarasini 0.05, 0.10, 0.20 qiling — qaysi etalon generatorlar o'tadi?
  4. Asbobga a'zolik hujumini qo'shing (GMM da score_samples bilan)
Yechim yo'nalishi

Asbobni almashtirish — o'lchov "o'lchagichni" almashtirish; tartib saqlansa, xulosa ishonchliroq. Mos yozuv sifatida o'quvdan foydalanish mumkin emas — nusxachi va yodlagan modellar foyda ko'radi. GMM uchun a'zolik hujumi: har sinf GMM ining score_samples qiymatini o'quv va val rasmlarida solishtirib AUC hisoblang (26.9, 1-misol usuli).

Vazifa 4: Byudjet

Modellang:

  1. dif_qadam = 1000, 3000, 6000 — diffusion FD GMM ga yetib oladimi?
  2. dif_T = 25, 50, 100 — sifat va FLOP murosasi
  3. cVAE ga beta (KL og'irligi) 0.5 va 2 — recall o'zgaradimi?
  4. GMM ga gmm_k = 1, 3, 10 va pca = 10, 20, 40
Yechim yo'nalishi

dataclasses.replace(k, dif_qadam=q) bilan Konfig ni o'zgartiring va 3-misolni qayta ishga tushiring (vaqt ko'payadi — urug'larni ketma-ket ishga tushiring). Natija qanday bo'lsa, shunday yozing: agar ko'p qadamda diffusion GMM dan sezilarli yomon bo'lmay qolsa, qoida baribir GMM ni tanlaydi (soddaroq); diffusion faqat sezilarli yaxshi bo'lsagina tanlanadi. gmm_k = 10 da har sinfda ~108 ta rasm uchun 10 ta to'liq kovariatsiya — yodlash xavfi oshadi, nusxa darvozasiga qarang.

Vazifa 5: Taqqoslash

Modellang:

  1. 5 urug' — SE qanchaga kichrayadi?
  2. Asosiy metrika sifatida recall — qaror o'zgaradimi?
  3. Sinf bo'yicha FD (har raqam uchun alohida) — qaysi raqamda diffusion yaxshiroq?
  4. GAN 26.3-bob ni to'rtinchi nomzod sifatida qo'shing
Yechim yo'nalishi

Asosiy metrikani natijadan keyin almashtirish — "p-hacking" ning bir turi; buni faqat alohida tahlil sifatida, oldingi qarorni o'zgartirmasdan qiling. Sinf bo'yicha FD kichik n (36) da juda shovqinli — sinf bo'yicha to'g'ri sinf ulushi va recall ishonchliroq. GAN ni soddalik tartibida cVAE va diffusion orasiga qo'ying.

Vazifa 6: Topshirish

Modellang:

  1. Generator xizmat sinfi: generatsiya(sinf, urug') → watermarkli 16x16 PNG baytlari
  2. Paketga model kartasi maydonini qo'shing (maqsad, cheklovlar, metrikalar)
  3. Watermark aniqlovchi alohida funksiya — kalitni muhit o'zgaruvchisidan oladi
  4. So'rov ID sidan urug' (masalan, hashlib.sha256) — bir xil so'rov → bir xil rasm
Yechim yo'nalishi

PIL.Image.fromarray(...).save(buf, "PNG") — PNG siqilishsiz, watermark to'liq saqlanadi (JPEG 70 da ham 99.7% edi). Urug' uchun int(hashlib.sha256(sorov_id.encode()).hexdigest()[:8], 16). Kalitni os.environ dan oling va paketga yozmang; aniqlovchi faqat kalit_id bo'yicha kerakli kalitni topadi.

Vazifa 7: O'ylash

Mahsulot rahbari aytdi: "GMM — 1990-yillarning texnologiyasi. Investorlar 'diffusion' so'zini eshitishni xohlaydi. Diffusion ni tanlaymiz, qolgani ahamiyatsiz." Siz nima deysiz?

Javob

Qisqa javob: texnologiya tanlovi — o'lchangan natija va narxga asoslangan muhandislik qarori; bu loyihada ikkalasi ham GMM tomonida. Lekin rahbarning xavotiri ham tushunarli — uni to'g'ri savolga aylantirish kerak.

1. Natija. 3-misolda uch urug'da diffusion FD 4.584, GMM 2.151, juftlashgan farq +2.433 (SE 0.146) — sezilarli. Diffusion so'ralgan sinfni kamroq beradi (0.923 va 0.978) va namunalari shovqinli. Testda GMM FD 2.362 — "mukammal generator" chegarasi (2.176) ga yaqin.

2. Narx. Diffusion har rasmga 100 tarmoq chaqiruvi va ~14.7 MFLOP, GMM ~3.4 kFLOP — taxminan 4400 marta kam. Serverda bu to'g'ridan-to'g'ri pul va kechikish.

3. Qaror chegarasi. Natija "diffusion yomon" emas, balki "8x8 raqamlar va shu byudjetda murakkablik o'zini oqlamadi". Agar mahsulot kattaroq rasmlarga, rangli rasmlarga yoki matn bilan boshqaruvga o'tsa — xuddi shu skelet bilan qayta o'lchaymiz, va u yerda diffusion yutishi mumkin (26.5-26.8).

4. Investorlar uchun. Ularga texnologiya nomi emas, jarayon muhim: biz bir nechta zamonaviy yondashuvni halol solishtirdik, yodlashni tekshirdik, chiqishlarni watermark bilan belgiladik. Bu ishonchli jamoa belgisi.

Tavsiya:

python
# 1. Hozir: GMM ni ishga tushirish (qaror, test, paket tayyor)
# 2. Diffusion byudjet egri chizig'i: dif_qadam 1000 -> 6000 (Vazifa 4)
# 3. Mahsulot talablari o'zgarsa (16x16+, rang, matn) - xuddi shu skelet bilan qayta
# 4. Model kartasida: "diffusion sinaldi, bu vazifada sezilarli yomon, narx 4400x"

Rahbarga javob: "Diffusion ni sinab ko'rdik va o'lchadik: bu vazifada u sezilarli yomonroq va minglab marta qimmatroq. GMM ni ishga tushiramiz; mahsulot murakkablashsa, tayyor skelet bilan diffusion ni qayta sinaymiz — natija o'zgarsa, almashtiramiz."

Nimani mustahkamlaydi: 2.2, 2.4, 2.5, 2.6-bo'limlar.


Xulosa

Bu darsda to'liq generativ loyiha qurdik: ma'lumot va o'lchov asbobidan watermark qo'shilgan, tekshirilgan paketgacha.

Eng muhim uch fikr:

  1. O'lchov asbobi — generativ loyihaning markazi. 1-misolda o'quvda o'rgatilgan klassifikator (val aniqligi 0.972) asosida FD, precision/recall, yodlash va nusxa ulushini qurdik va ularni ma'lum "generatorlar" da kalibrladik: nusxachi FD bo'yicha eng yaxshi (1.434) — uni faqat nusxa darvozasi (1.000) ushlaydi; sinf o'rtachasi 100% to'g'ri taniladi, lekin recall 0.000; FD n ga kuchli bog'liq (6.474 → 1.434). Asosiy metrika (FD) va darvoza (nusxa ≤ 0.10) natijalardan oldin belgilandi.

  2. Eng sodda model yutdi — va buni juftlashgan taqqoslash ko'rsatdi. 3-misolda uch urug'da GMM FD 2.151, diffusion (CFG w = 0.3) 4.584, cVAE 6.605; farqlar +2.433 (SE 0.146) va +4.454 (SE 0.121) — sezilarli. Uchala nomzod ham yodlash darvozasidan o'tdi, qoida GMM ni tanladi. U diffusion ga nisbatan har namunada taxminan 4400 marta kam FLOP sarflaydi. Halol chegara: bu natija 8x8 ma'lumot va kichik CPU byudjeti uchun.

  3. Test bir marta ochildi, paket va watermark tekshirildi. Testda GMM FD 2.362 (nusxachi chegarasi 2.176), to'g'ri sinf 0.984, nusxa ulushi 0.039 (haqiqiyda 0.05). Paket faqat tenzorlardan iborat, weights_only=True bilan yuklandi, urug'li nazorat aynan mos keldi, watermark kaliti paketga yozilmadi. Watermark chiqishlarda 100% aniqlandi (JPEG 70 dan keyin 99.7%), belgisiz va haqiqiy rasmlarda yolg'on musbat 0.0%, foydalilik saqlandi (0.967 → 0.969).

Bu bilan 26-qism — Generativ AI yakunlandi. Endi bizda generativ modellashtirishning to'liq xaritasi bor: taqsimotni o'rganish g'oyasidan VAE ning yashirin fazosi, GAN ning raqobati va uning muammolari, diffusion ning shovqindan tiklash jarayoni, sampling va guidance tugmalarigacha; generativ modelni raqam bilan baholashdan (FD, precision/recall, yodlash) matn-rasm modellari va etika-xavfsizlik (yodlash, tarafkashlik, watermark, detektorlar) gacha — va nihoyat bularning hammasini oddiy bazaviy bilan halol solishtiradigan loyihagacha. Bu va oldingi qismlardagi loyihalarning barchasi bitta joyda to'xtadi: weights_only=True bilan yuklanadigan paket va hisobot. Keyingi qism — MLOps va deploy: paketni haqiqiy xizmatga aylantirish, versiyalash va tajribalarni kuzatish, modelni API orqali taqdim etish, ishlab chiqarishda sifat va ma'lumot siljishini kuzatish — shu jumladan bu darsdagi sampling urug'lari, watermark kalitlari va model kartasini boshqarish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
26.10-dars: Amaliyot — shartli raqam generatori loyihasi — IlmHamroh