Mundarija (24)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Matn-rasm generatsiyasi arxitekturasi
- 2.2. Cross-attention bilan shart berish
- 2.3. CLIP: kontrastiv o'rganish va InfoNCE
- 2.4. Zero-shot klassifikatsiya va kompozitsion umumlashma
- 2.5. Matn → rasm qidiruvi va o'lchovlari
- 2.6. Matn encoderining cheklovlari
- 2.7. Matn sharti bilan generator
- 2.8. CLIP bilan qayta saralash (best-of-K)
- 2.9. Real tizimlar: umumiy tuzilish
- 2.10. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — CLIP noldan: InfoNCE va zero-shot klassifikatsiya
- Misol 2 — Matn → rasm qidiruvi va matn encoderining cheklovlari
- Misol 3 — Matn sharti bilan kichik generator (shartli VAE)
- Misol 4 — CLIP bilan qayta saralash (best-of-K)
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
26.8-dars: Matn-rasm va multimodal modellar
26-QISM — GENERATIV AI · 8-dars
1. Kirish va motivatsiya
26.6-darsda diffusion modelni sinf bilan boshqardik: "menga 7 raqami kerak" — va classifier-free guidance bilan so'ralgan sinf 0.985 holatda chiqdi. Lekin sinf — yopiq ro'yxat: 10 ta raqam, boshqa hech narsa. Foydalanuvchi esa "kichik ko'k uchburchak" yoki "qizil rangli katta doira" deb erkin matn yozadi — va, eng muhimi, o'quv ma'lumotida hech qachon birga uchramagan so'zlar kombinatsiyasini so'raydi. Matn-rasm modellarining asosiy va'dasi ham shu: so'zlarning ma'nosini alohida o'rganib, ularni yangi kombinatsiyalarda qo'shib ishlatish (kompozitsion umumlashma).
Bunga ikki qism kerak. Birinchisi — matn va rasmni bitta fazoga joylashtiradigan model: "qizil katta doira" matni va qizil katta doira rasmi yaqin vektorlarga aylansin. Bu CLIP g'oyasi — kontrastiv o'rganish. Ikkinchisi — shu matn vektori bilan boshqariladigan generator: diffusion yoki VAE, sharti matn embeddingi.
Real vaziyat. Onlayn do'kon mahsulot katalogi uchun "matn bo'yicha rasm qidiruvi" ni ishga tushirdi. Sinovda hammasi ajoyib edi: "qora charm sumka" — qora charm sumkalar. Keyin foydalanuvchilar "qora emas, jigarrang sumka" deb yozishdi va yana qora sumkalar chiqdi. Katalogda kam uchraydigan "yashil charm poyabzal" ga esa yashil matodan poyabzallar va boshqa rangdagi charm poyabzallar aralash chiqdi. Ikki muammo: matn encoderi inkorni tushunmaydi va model ko'rmagan atribut kombinatsiyalarida adashadi. Bu darsning 1- va 2-misollari aynan shu ikki holatni kichik, lekin to'liq nazorat qilinadigan ma'lumotda o'lchaydi.
Bu darsda matn-rasm tizimlarining umumiy arxitekturasini ko'rib chiqamiz, CLIP ni noldan quramiz — kod ichida chizilgan 16x16 shakllar va ularning o'zbekcha tavsiflari bilan — InfoNCE loss, zero-shot klassifikatsiya va matn → rasm qidiruvini o'lchaymiz, keyin matn sharti bilan kichik generator (shartli VAE) o'rgatib, o'quvda ko'rilmagan atribut kombinatsiyalarida natija qanchalik to'g'ri ekanini o'z atribut klassifikatorlarimiz bilan tekshiramiz.
Bu darsda:
- Matn-rasm generatsiyasi arxitekturasi: matn encoder → shart → generator
- Cross-attention bilan shart berish
- CLIP: kontrastiv o'rganish va InfoNCE
- Zero-shot klassifikatsiya va kompozitsion umumlashma
- Matn → rasm qidiruvi: P@k, R-precision, R@k
- Matn encoderining cheklovlari: tartib, inkor
- Matn sharti bilan generator: kompozitsion shart va ID shart
- CLIP bilan qayta saralash (best-of-K)
- Real tizimlar: umumiy tuzilish
- Tuzoqlar
ℹ Misollar real torch/numpy/sklearn bilan (Python 3.14, torch 2.14 CPU). Ma'lumot kod ichida chiziladi: 3 shakl x 3 rang x 2 o'lcham = 18 kombinatsiya, ulardan 4 tasi o'quvda umuman ko'rsatilmaydi.
2. Nazariya — chuqur tushuntirish
2.1. Matn-rasm generatsiyasi arxitekturasi
UMUMIY QUVUR:
matn ("kichik ko'k uchburchak")
-> TOKENIZATOR -> MATN ENCODER (Transformer, 24-qism)
-> matn embeddinglari (har token uchun vektor yoki bitta vektor)
-> SHART sifatida generatorga
-> GENERATOR (diffusion U-Net yoki Transformer; 26.5-26.6)
-> (latent diffusion bo'lsa) VAE DEKODER -> rasm
SHART QANDAY BERILADI:
1. qo'shish/konkatenatsiya: bitta matn vektori c -> h = h + W c
(26.6-darsdagi sinf embeddingi kabi; 3-misoldagi VAE)
2. CROSS-ATTENTION (24.3-dars): rasm xususiyatlari - so'rov (Q),
matn tokenlari - kalit va qiymat (K, V)
attention(Q = W_q h_rasm, K = W_k e_matn, V = W_v e_matn)
har rasm joyi "qaysi so'zga qarash kerak"ni o'zi tanlaydi
3. classifier-free guidance 26.6-bob: matn shartini 10-20% tashlab o'rgatish,
samplingda eps_u + w * (eps_c - eps_u)
MATN ENCODERI QAYERDAN:
oldindan o'rgatilgan CLIP matn encoderi yoki katta til modeli encoderi
(odatda muzlatilgan); generator faqat uning chiqishini "o'qishni" o'rganadiMatn-rasm modeli = matn encoder + shartli generator; matnni tushunish sifati asosan encoderga, rasm sifati generatorga bog'liq.
2.2. Cross-attention bilan shart berish
NEGA BITTA VEKTOR YETMAYDI:
"chapda qizil doira, o'ngda ko'k kvadrat" - qaysi rang qaysi shaklga
tegishli ekani so'zlar orasidagi munosabatda; bitta o'rtacha vektorda
bu munosabat yo'qoladi (2-misol: so'zlar xaltasi tartibni ko'rmaydi)
CROSS-ATTENTION (24.3 dagi formula):
Q = h_rasm W_q (n_joy, d) - rasmning har joyi
K = e_matn W_k (n_token, d) - matn tokenlari
V = e_matn W_v
chiqish = softmax(Q K^T / sqrt(d)) V
-> har joy o'ziga kerakli so'zlarni "o'qiydi"
DIFFUSION U-NET DA:
har rezolyutsiya blokida: self-attention (rasm ichida) + cross-attention (matnga)
attention xaritalari - qaysi so'z rasmning qaysi qismiga ta'sir qilgani
(tahrirlash usullari shu xaritalarni boshqaradi)Cross-attention — matnni token darajasida berish; bu dars kichik tajribalarida bitta vektor bilan cheklanamiz va uning chegaralarini o'lchaymiz.
2.3. CLIP: kontrastiv o'rganish va InfoNCE
G'OYA (Radford va boshq., 2021 - CLIP):
ikki encoder: f_rasm(x) -> z_r, f_matn(t) -> z_t; ikkalasi ham normallangan
maqsad: mos juftlik (x_i, t_i) yaqin, mos bo'lmagan (x_i, t_j) uzoq
internetdan olingan juda ko'p rasm-matn juftliklarida o'rgatilgan
INFONCE (batchda B ta juftlik):
S = tau * Z_r Z_t^T (B, B) o'xshashlik matritsasi
tau = exp(log_harorat) - o'rganiladigan harorat
loss_r = CE(S, [0..B-1]) - har rasm uchun B ta matndan to'g'risi
loss_t = CE(S^T, [0..B-1]) - har matn uchun B ta rasmdan to'g'risi
loss = (loss_r + loss_t) / 2
diagonal - musbat juftliklar, qolgan B-1 ta - manfiy
tasodifiy boshlanishda loss ~ log B (bizda log 128 = 4.852)
1-MISOL:
loss 5.275 -> 2.001 (urug' 0), harorat 14.3 -> 19.3
boshida log B dan yuqori: harorat tasodifiy o'xshashliklarni kuchaytiradi
oxirida ~2: batchda bir kombinatsiyadan ~9 juftlik bor ->
ular bir-biriga "soxta manfiy" -> pastki chegara ~ log 9 = 2.2 atrofida
BIZNING MODEL:
rasm encoder: Conv(3->16) -> max-pool 4x4 -> 256 -> 64 -> 32
matn encoder: so'z embeddinglari O'RTACHASI -> MLP -> 32 (so'zlar xaltasi)
400 qadam, batch 128, 14 ta kombinatsiya, 6 ta tavsif shabloniCLIP = ikki encoder + InfoNCE: batchdagi boshqa juftliklar tayyor manfiy misollar; o'rganilgan fazoda matn va rasmni to'g'ridan-to'g'ri solishtirish mumkin.
2.4. Zero-shot klassifikatsiya va kompozitsion umumlashma
ZERO-SHOT:
har sinf uchun matn: "katta qizil doira", ..., (18 ta)
bashorat = argmax_k cos(f_rasm(x), f_matn(matn_k))
klassifikatorni qayta o'rgatish YO'Q - yangi sinf = yangi matn
KOMPOZITSION TEST (bizning tajriba):
o'quvda 14 kombinatsiya; 4 tasi hech qachon ko'rsatilmagan:
ko'k katta uchburchak, qizil kichik kvadrat,
qizil kichik uchburchak, yashil katta doira
lekin har so'z (ko'k, katta, uchburchak ...) o'quvda boshqa kombinatsiyalarda bor
1-MISOL (3 urug'):
ko'rilgan kombinatsiyalar: 0.974
ko'rilmagan kombinatsiyalar: 0.519 (tasodifiy 0.056)
ko'rilmaganlarda: rang 1.000, o'lcham 0.948, shakl 0.540
"qizil kichik kvadrat" -> 88% holatda "qizil kichik doira"
sabab: o'quvda qizil kichik shakl FAQAT doira edi ->
model "qizil + kichik => doira" yorlig'ini (shortcut) o'rgangan
ODDIY KLASSIFIKATOR BILAN FARQ:
14 sinfli klassifikator ko'rilmagan kombinatsiyani umuman ayta olmaydi
CLIP esa 0.519 - tasodifiydan 9 barobar yuqori, lekin 0.974 dan ancha pastZero-shot — matn bilan yangi sinf qo'shish; kompozitsion umumlashma ishlaydi, lekin to'liq emas: ko'rilmagan kombinatsiyada model o'quvdagi korrelyatsiyalarga yopishib qoladi.
2.5. Matn → rasm qidiruvi va o'lchovlari
QIDIRUV: so'rov z_t, galereya {z_r}; kosinus bo'yicha tartiblash
O'LCHOVLAR:
P@k (precision@k) - top-k dagi to'g'ri natijalar ulushi
R-precision - top-R da, R = to'g'ri natijalar soni (bizda 40)
R@k (recall@k) - to'g'ri javob top-k ichidami (bitta to'g'ri bo'lsa)
(25.7-25.9 darslardagi semantik qidiruv o'lchovlari bilan bir xil)
2-MISOL (18 so'rov, 720 rasm, 3 urug'):
ko'rilgan ko'rilmagan
matn->rasm P@10 0.955 0.758
matn->rasm R-prec 0.923 0.673
rasm->matn R@1 0.974 0.519
rasm->matn R@3 0.999 0.898
qidiruv (matn->rasm) ko'rilmaganlarda zero-shot dan yaxshiroq:
top-10 uchun galereyadagi eng "ishonchli" 10 ta rasm yetarli2.6. Matn encoderining cheklovlari
SO'ZLAR XALTASI (bizning encoder):
"katta qizil doira" ~ "qizil katta doira" kosinus 1.000 (tartib yo'q)
"qizil katta doira" ~ "qizil emas katta doira" kosinus 1.000 ("emas" lug'atda yo'q)
so'rov "qizil emas katta doira" -> top-20 ning 1.00 qismi QIZIL
KATTA CLIP MODELLARIDA HAM MA'LUM ZAIF JOYLAR:
inkor ("... emas", "... siz")
sanash ("uchta olma")
fazoviy munosabatlar ("chapda", "ustida")
atributni to'g'ri obyektga bog'lash ("qizil kub va ko'k shar")
sabab: kontrastiv maqsad ko'pincha "qaysi so'zlar bor" ni bilish bilan
yechiladi - tartib va munosabatni o'rganishga majburlamaydi
AMALDA:
tartibni ko'radigan encoder (Transformer) + munosabatli qiyin manfiy
misollar (hard negatives) bilan o'rgatishKontrastiv encoder "qaysi so'zlar bor" ni yaxshi, "ular qanday bog'langan" ni yomon biladi; inkor va munosabatlarni alohida sinab ko'ring.
2.7. Matn sharti bilan generator
SHARTLI VAE (26.2 dagi VAE + shart c):
encoder: q(z | x, c) dekoder: p(x | z, c)
loss = ||x - x_hat||^2 / (2 * 0.1^2) + KL(q(z|x,c) || N(0, I))
generatsiya: z ~ N(0, I), c = matn(tavsif) -> dekoder
IKKI XIL SHART (3-misol):
matn: c = MLP(so'z embeddinglari o'rtachasi) - so'zlar orqali
ID: c = Embedding(18)[kombinatsiya raqami] - har kombinatsiyaga alohida vektor
ID shartida ko'rilmagan kombinatsiyaning vektori HECH QACHON o'rgatilmaydi
BAHOLASH:
o'z atribut klassifikatorlarimiz (rang, o'lcham, shakl) - HAMMA 18
kombinatsiyada o'rgatilgan hakam; generatsiyaning har atributi to'g'rimi
3-MISOL ('hammasi to'g'ri', 3 urug'):
ko'rilgan ko'rilmagan
matn 0.396 0.304
ID 0.473 0.000
-> ko'rilganda ID biroz yaxshi, ko'rilmaganda faqat matn sharti ishlaydiKompozitsion shart — so'zlar orqali: ko'rilmagan kombinatsiyaga faqat so'z darajasida o'rgangan shart umumlasha oladi; "har kombinatsiyaga alohida ID" esa umuman umumlashmaydi.
2.8. CLIP bilan qayta saralash (best-of-K)
G'OYA (dastlabki matn-rasm tizimlarida ishlatilgan):
bir so'rovga K ta nomzod generatsiya qilish
CLIP bilan cos(f_rasm(nomzod), f_matn(so'rov)) hisoblash
eng yuqorisini qaytarish
4-MISOL ('hammasi to'g'ri'):
K ko'rilgan ko'rilmagan
1 0.333 0.285
8 0.460 0.375
AUC (CLIP bahosi -> to'g'rilik): ko'rilgan 0.896, ko'rilmagan 0.764
NARX: K barobar generatsiya + K ta CLIP baholash
CHEKLOV: tanlovchi ham o'sha ma'lumotda o'rgangan - ko'r nuqtalari umumiy
(ko'rilmagan kombinatsiyada foyda kichikroq)2.9. Real tizimlar: umumiy tuzilish
(faqat umumiy ma'lum arxitektura; aniq o'lcham va xarajat raqamlari
bu yerda keltirilmaydi)
STABLE DIFFUSION (latent diffusion, 26.6):
VAE: rasm -> kichik latent (8 barobar kichraytirilgan)
matn encoder: oldindan o'rgatilgan CLIP matn encoderi (muzlatilgan)
U-Net: latentda diffusion, matn - cross-attention orqali
classifier-free guidance, kam qadamli samplerlar (DDIM va boshq.)
DALL-E (OpenAI oilasi):
birinchi versiya - rasm tokenlari ustida avtoregressiv Transformer
+ CLIP bilan qayta saralash 2.8-bob
keyingi versiyalar - diffusion asosidagi dekoder, CLIP embeddinglari
yoki til modeli bilan kuchaytirilgan matn tushunish
UMUMIY TENDENSIYALAR:
kuchliroq matn encoderi -> so'rovga yaxshiroq moslik
latent fazo -> arzonroq hisob
guidance -> moslik va xilma-xillik murosasi 26.6-bob
baholash: FID + CLIP-score + inson baholashi (26.7)2.10. Tuzoqlar
Asosiy tuzoqlar: zero-shot aniqlikni faqat o'quvdagi kombinatsiyalarda o'lchab "model kompozitsion" deyish; InfoNCE da batchdagi bir xil sinfli juftliklar (soxta manfiylar) borligini unutish; haroratni juda katta yoki qat'iy qo'yish; matn va rasm vektorlarini normallamaslik; qidiruvni faqat bitta o'lchov bilan baholash; so'zlar xaltasi encoderida tartib va inkorni kutish; ko'rilmagan kombinatsiyalarni ID sharti bilan generatsiya qilishga urinish; generatsiyani o'sha CLIP bilan ham tanlab, ham baholash (o'z-o'zini tekshirish); baholovchi atribut klassifikatorining o'z aniqligini tekshirmaslik; real tizimlarning parametr soni va o'quv xarajati haqida tekshirilmagan raqamlarni keltirish.
3. Tez ma'lumotnoma
import torch
import torch.nn.functional as F
# ikki encoder, normallangan vektorlar
zi = F.normalize(rasm_encoder(X), dim=-1)
zt = F.normalize(matn_encoder(matnlar), dim=-1)
# InfoNCE (simmetrik), o'rganiladigan harorat
logit = log_harorat.exp().clamp(max=100) * zi @ zt.T
nishon = torch.arange(len(zi))
loss = (F.cross_entropy(logit, nishon) + F.cross_entropy(logit.T, nishon)) / 2
# zero-shot
bashorat = (zi @ matn_encoder(sinf_tavsiflari).T).argmax(1)
# matn -> rasm qidiruvi va P@10
top = (matn_encoder([sorov]) @ galereya.T)[0].argsort(descending=True)[:10]
p10 = (Y[top] == togri_sinf).float().mean()
# shartli VAE: shart - matn embeddingi
c = matn_shart(tavsiflar)
rasm = dekoder(torch.cat([torch.randn(n, z_dim, generator=g), c], 1))
# best-of-K: CLIP bahosi bo'yicha eng yaxshi nomzod
ball = (rasm_encoder(nomzodlar) * matn_encoder([sorov])).sum(1)
eng = nomzodlar[ball.argmax()]Matn-rasm xulosasi
matn encoder -> shart (vektor yoki cross-attention) -> generator
CLIP: ikki encoder, InfoNCE, o'rganiladigan harorat
zero-shot: sinf = matn; kompozitsion umumlashma - qisman
qidiruv: P@k, R-precision, R@k; ko'rilgan va ko'rilmaganni alohida
so'zlar xaltasi: tartib va inkor yo'q
kompozitsion shart umumlashadi, ID shart - yo'q
best-of-K: CLIP bilan qayta saralash, narxi K barobar4. Batafsil misollar
Misollar real torch/numpy/sklearn bilan (Python 3.14, torch 2.14 CPU). Hamma misollarda bir xil ma'lumot generatori: 16x16 RGB rasm, bitta shakl (joyi va rangi biroz tasodifiy), 6 xil o'zbekcha tavsif shabloni.
Misol 1 — CLIP noldan: InfoNCE va zero-shot klassifikatsiya
"""CLIP g'oyasi noldan: sintetik shakllar va o'zbekcha tavsiflar, InfoNCE, zero-shot klassifikatsiya."""
import math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
SHAKLLAR = ["doira", "kvadrat", "uchburchak"]
RANGLAR = ["qizil", "yashil", "ko'k"]
OLCHAMLAR = ["katta", "kichik"]
RGB = {"qizil": (0.9, 0.15, 0.15), "yashil": (0.15, 0.8, 0.2), "ko'k": (0.2, 0.3, 0.95)}
KOMBO = [(r, o, s) for s in SHAKLLAR for r in RANGLAR for o in OLCHAMLAR] # 18 ta
KORILMAGAN = {("ko'k", "katta", "uchburchak"), ("qizil", "kichik", "kvadrat"),
("yashil", "katta", "doira"), ("qizil", "kichik", "uchburchak")}
SHABLONLAR = ["{r} {o} {s}", "{o} {r} {s}", "{r} rangli {o} {s}", "bu {o} {r} {s}",
"{o} {s}, rangi {r}", "rasmda {r} {s}, o'lchami {o}"]
def chiz(rang, olcham, shakl, rng):
"""16x16 RGB rasm: fon + bitta shakl (joyi, rangi biroz tasodifiy)."""
r = 6.0 if olcham == "katta" else 3.2
cy, cx = rng.uniform(r + 0.5, 15.5 - r, size=2)
y, x = np.mgrid[0:16, 0:16] + 0.5
if shakl == "doira":
m = (y - cy) ** 2 + (x - cx) ** 2 <= r ** 2
elif shakl == "kvadrat":
m = (np.abs(y - cy) <= 0.85 * r) & (np.abs(x - cx) <= 0.85 * r)
else:
m = (y >= cy - r) & (y <= cy + r) & (np.abs(x - cx) <= (y - (cy - r)) / 2)
rasm = 0.1 + 0.05 * rng.standard_normal((3, 16, 16))
rang_q = np.array(RGB[rang]) + rng.uniform(-0.08, 0.08, 3)
rasm[:, m] = rang_q[:, None]
return rasm.astype(np.float32)
def toplam(kombolar, n_har, seed):
rng = np.random.default_rng(seed)
X, T, Y = [], [], []
for ki, (r, o, s) in enumerate(KOMBO):
if (r, o, s) not in kombolar:
continue
for _ in range(n_har):
X.append(chiz(r, o, s, rng))
T.append(SHABLONLAR[rng.integers(len(SHABLONLAR))].format(r=r, o=o, s=s))
Y.append(ki)
return torch.tensor(np.array(X)), T, torch.tensor(Y)
class RasmEncoder(nn.Module):
"""Kichik CNN: 3x16x16 -> konvolyutsiya (16 kanal) -> max-pool 4x4 -> 256 -> 64 -> 32."""
def __init__(self, d=32):
super().__init__()
self.tarmoq = nn.Sequential(
nn.Conv2d(3, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(4),
nn.Flatten(), nn.Linear(16 * 16, 64), nn.ReLU(), nn.Linear(64, d))
def forward(self, x):
return F.normalize(self.tarmoq(x), dim=-1)
class MatnEncoder(nn.Module):
"""So'z embeddinglari o'rtachasi (bag of words) + MLP. So'z tartibini KO'RMAYDI."""
def __init__(self, lugat, d=32):
super().__init__()
self.lugat = {w: i + 1 for i, w in enumerate(lugat)} # 0 - to'ldiruvchi
self.emb = nn.Embedding(len(lugat) + 1, 32, padding_idx=0)
self.mlp = nn.Sequential(nn.Linear(32, 64), nn.ReLU(), nn.Linear(64, d))
def tokenla(self, matnlar):
idx = [[self.lugat.get(w, 0) for w in m.replace(",", "").split()] for m in matnlar]
L = max(len(i) for i in idx)
return torch.tensor([i + [0] * (L - len(i)) for i in idx])
def forward(self, matnlar):
t = self.tokenla(matnlar)
e = self.emb(t)
o = e.sum(1) / (t > 0).sum(1, keepdim=True).clamp(min=1)
return F.normalize(self.mlp(o), dim=-1)
def lugat_yasash():
sozlar = set()
for r, o, s in KOMBO:
for sh in SHABLONLAR:
sozlar.update(sh.format(r=r, o=o, s=s).replace(",", "").split())
return sorted(sozlar)
def orgat_clip(X, T, seed, qadamlar=400, B=128):
torch.manual_seed(seed)
re_, me = RasmEncoder(), MatnEncoder(lugat_yasash())
log_harorat = nn.Parameter(torch.tensor(math.log(1 / 0.07)))
opt = torch.optim.Adam(list(re_.parameters()) + list(me.parameters()) + [log_harorat], lr=2e-3)
g = torch.Generator().manual_seed(seed)
for q in range(qadamlar):
i = torch.randperm(len(X), generator=g)[:B]
zi, zt = re_(X[i]), me([T[j] for j in i.tolist()])
logit = log_harorat.exp().clamp(max=100) * zi @ zt.T # (B, B)
nishon = torch.arange(B)
loss = (F.cross_entropy(logit, nishon) + F.cross_entropy(logit.T, nishon)) / 2 # InfoNCE
opt.zero_grad()
loss.backward()
opt.step()
if q == 0:
boshlangich = loss.item()
re_.eval()
me.eval()
return re_, me, boshlangich, loss.item(), log_harorat.exp().item()
def main() -> None:
torch.set_num_threads(1)
korilgan = set(KOMBO) - KORILMAGAN
X, T, _ = toplam(korilgan, 150, seed=0)
Xt, _, Yt = toplam(set(KOMBO), 40, seed=1)
print("=== 1. Ma'lumot: 16x16 shakllar va o'zbekcha tavsiflar ===")
print(f" atributlar: {len(SHAKLLAR)} shakl x {len(RANGLAR)} rang x {len(OLCHAMLAR)} o'lcham = {len(KOMBO)} kombinatsiya")
print(f" o'quvda ko'rilmagan {len(KORILMAGAN)} ta: "
+ "; ".join(" ".join(k) for k in sorted(KORILMAGAN)))
print(f" o'quv juftliklari: {len(X)}, test rasmlari: {len(Xt)} (har kombinatsiyadan 40)")
print(f" tavsif namunalari: {T[0]!r}, {T[1]!r}, {T[2]!r}")
print(f" lug'at: {len(lugat_yasash())} so'z")
print("\n=== 2. InfoNCE bilan o'rgatish (3 urug') ===")
yozuvlar = [f"{o} {r} {s}" for r, o, s in KOMBO]
korilmagan_m = torch.tensor([KOMBO[y] in KORILMAGAN for y in Yt.tolist()])
natija, birinchi = [], None
for seed in range(3):
re_, me, l0, l1, harorat = orgat_clip(X, T, seed)
with torch.no_grad():
bashorat = (re_(Xt) @ me(yozuvlar).T).argmax(1)
tog = bashorat == Yt
if birinchi is None:
birinchi = bashorat
attr = [np.mean([KOMBO[p][j] == KOMBO[y][j] for p, y in zip(bashorat.tolist(), Yt.tolist())
if KOMBO[y] in KORILMAGAN]) for j in range(3)]
natija.append((tog[~korilmagan_m].float().mean().item(), tog[korilmagan_m].float().mean().item(), *attr))
print(f" urug' {seed}: loss {l0:.3f} -> {l1:.3f} (log B = {math.log(128):.3f}), harorat {harorat:.1f}")
print("\n=== 3. Zero-shot: 18 ta tavsifdan eng o'xshashi ===")
v = np.array(natija)
se = v.std(0, ddof=1) / math.sqrt(3)
print(f" tasodifiy daraja: {1 / 18:.3f}")
print(f" ko'rilgan kombinatsiyalar: aniqlik {v[:, 0].mean():.3f} +- {se[0]:.3f}")
print(f" ko'rilmagan kombinatsiyalar: aniqlik {v[:, 1].mean():.3f} +- {se[1]:.3f}")
print(f" ko'rilmaganlarda atribut bo'yicha: rang {v[:, 2].mean():.3f}, o'lcham {v[:, 3].mean():.3f}, "
f"shakl {v[:, 4].mean():.3f}")
f = v[:, 0] - v[:, 1]
fse = f.std(ddof=1) / math.sqrt(3)
print(f" ko'rilgan - ko'rilmagan: {f.mean():+.3f} (SE {fse:.3f}) -> "
+ ("sezilarli" if f.mean() > 2 * fse else "sezilarli emas"))
print(" oddiy 14 sinfli klassifikator ko'rilmagan kombinatsiyani umuman ayta olmaydi (sinf yo'q)")
print("\n=== 4. Ko'rilmagan kombinatsiyalar: model nimani tanladi (urug' 0) ===")
for kombo in sorted(KORILMAGAN):
ki = KOMBO.index(kombo)
p = birinchi[Yt == ki].tolist()
eng = max(set(p), key=lambda c: (p.count(c), -c))
print(f" {' '.join(kombo):<24} to'g'ri {p.count(ki) / len(p):.2f}; eng ko'p tanlangan: "
f"{' '.join(KOMBO[eng])} ({p.count(eng) / len(p):.2f})")
print(" ⭐ rang va o'lcham kombinatsiyalanadi; shakl - eng qiyin qism")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumot: 16x16 shakllar va o'zbekcha tavsiflar ===
atributlar: 3 shakl x 3 rang x 2 o'lcham = 18 kombinatsiya
o'quvda ko'rilmagan 4 ta: ko'k katta uchburchak; qizil kichik kvadrat; qizil kichik uchburchak; yashil katta doira
o'quv juftliklari: 2100, test rasmlari: 720 (har kombinatsiyadan 40)
tavsif namunalari: 'qizil katta doira', 'qizil katta doira', 'katta qizil doira'
lug'at: 13 so'z
=== 2. InfoNCE bilan o'rgatish (3 urug') ===
urug' 0: loss 5.275 -> 2.001 (log B = 4.852), harorat 19.3
urug' 1: loss 5.106 -> 2.025 (log B = 4.852), harorat 20.3
urug' 2: loss 5.590 -> 1.930 (log B = 4.852), harorat 18.8
=== 3. Zero-shot: 18 ta tavsifdan eng o'xshashi ===
tasodifiy daraja: 0.056
ko'rilgan kombinatsiyalar: aniqlik 0.974 +- 0.010
ko'rilmagan kombinatsiyalar: aniqlik 0.519 +- 0.043
ko'rilmaganlarda atribut bo'yicha: rang 1.000, o'lcham 0.948, shakl 0.540
ko'rilgan - ko'rilmagan: +0.455 (SE 0.050) -> sezilarli
oddiy 14 sinfli klassifikator ko'rilmagan kombinatsiyani umuman ayta olmaydi (sinf yo'q)
=== 4. Ko'rilmagan kombinatsiyalar: model nimani tanladi (urug' 0) ===
ko'k katta uchburchak to'g'ri 0.82; eng ko'p tanlangan: ko'k katta uchburchak 0.82-bob
qizil kichik kvadrat to'g'ri 0.12; eng ko'p tanlangan: qizil kichik doira 0.88-bob
qizil kichik uchburchak to'g'ri 0.15; eng ko'p tanlangan: qizil kichik doira 0.80-bob
yashil katta doira to'g'ri 0.97; eng ko'p tanlangan: yashil katta doira 0.97-bob
⭐ rang va o'lcham kombinatsiyalanadi; shakl - eng qiyin qismNima ko'rsatdi: 1-bo'lim — ma'lumot: 18 kombinatsiyadan 14 tasida 2100 juftlik, 13 so'zli lug'at; tavsiflar bir xil kombinatsiya uchun turlicha (qizil katta doira, katta qizil doira). 2-bo'lim — InfoNCE: loss boshida 5.1–5.6 (log B = 4.852 dan yuqori — boshlang'ich harorat 14.3 tasodifiy o'xshashliklarni kuchaytiradi), oxirida ~`2.0; nolga tushmaydi, chunki 128 talik batchda har kombinatsiyadan o'rtacha ~9 juftlik bor va ular bir-biriga "soxta manfiy" — bu yerda loss ning pastki chegarasi ~log 9. Harorat o'z-o'zidan 19–20gacha o'sdi. 3-bo'lim — zero-shot: ko'rilgan kombinatsiyalarda0.974, ko'rilmaganlarda 0.519(tasodifiy0.056); farq +0.455, SE 0.050— sezilarli. Atributlar bo'yicha ko'rilmaganlarda rang1.000, o'lcham 0.948, shakl esa 0.540— rang va o'lcham kombinatsiyalanadi, shakl — yo'q. 4-bo'lim sababni ko'rsatadi:ko'k katta uchburchak (0.82) va yashil katta doira (0.97) yaxshi, lekin qizil kichik kvadratvaqizil kichik uchburchakning 88% va 80% iqizil kichik doira` deb tanildi. O'quvda qizil kichik shakl faqat doira bo'lgan — model "qizil + kichik → doira" yorlig'ini o'rgangan. Bog'liq bo'limlar: 2.3, 2.4.
Misol 2 — Matn → rasm qidiruvi va matn encoderining cheklovlari
"""Matn -> rasm qidiruvi (P@k, R-precision, R@k) va so'zlar xaltasi matn encoderining cheklovlari."""
import math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
SHAKLLAR = ["doira", "kvadrat", "uchburchak"]
RANGLAR = ["qizil", "yashil", "ko'k"]
OLCHAMLAR = ["katta", "kichik"]
RGB = {"qizil": (0.9, 0.15, 0.15), "yashil": (0.15, 0.8, 0.2), "ko'k": (0.2, 0.3, 0.95)}
KOMBO = [(r, o, s) for s in SHAKLLAR for r in RANGLAR for o in OLCHAMLAR] # 18 ta
KORILMAGAN = {("ko'k", "katta", "uchburchak"), ("qizil", "kichik", "kvadrat"),
("yashil", "katta", "doira"), ("qizil", "kichik", "uchburchak")}
SHABLONLAR = ["{r} {o} {s}", "{o} {r} {s}", "{r} rangli {o} {s}", "bu {o} {r} {s}",
"{o} {s}, rangi {r}", "rasmda {r} {s}, o'lchami {o}"]
def chiz(rang, olcham, shakl, rng):
"""16x16 RGB rasm: fon + bitta shakl (joyi, rangi biroz tasodifiy)."""
r = 6.0 if olcham == "katta" else 3.2
cy, cx = rng.uniform(r + 0.5, 15.5 - r, size=2)
y, x = np.mgrid[0:16, 0:16] + 0.5
if shakl == "doira":
m = (y - cy) ** 2 + (x - cx) ** 2 <= r ** 2
elif shakl == "kvadrat":
m = (np.abs(y - cy) <= 0.85 * r) & (np.abs(x - cx) <= 0.85 * r)
else:
m = (y >= cy - r) & (y <= cy + r) & (np.abs(x - cx) <= (y - (cy - r)) / 2)
rasm = 0.1 + 0.05 * rng.standard_normal((3, 16, 16))
rang_q = np.array(RGB[rang]) + rng.uniform(-0.08, 0.08, 3)
rasm[:, m] = rang_q[:, None]
return rasm.astype(np.float32)
def toplam(kombolar, n_har, seed):
rng = np.random.default_rng(seed)
X, T, Y = [], [], []
for ki, (r, o, s) in enumerate(KOMBO):
if (r, o, s) not in kombolar:
continue
for _ in range(n_har):
X.append(chiz(r, o, s, rng))
T.append(SHABLONLAR[rng.integers(len(SHABLONLAR))].format(r=r, o=o, s=s))
Y.append(ki)
return torch.tensor(np.array(X)), T, torch.tensor(Y)
class RasmEncoder(nn.Module):
"""Kichik CNN: 3x16x16 -> konvolyutsiya (16 kanal) -> max-pool 4x4 -> 256 -> 64 -> 32."""
def __init__(self, d=32):
super().__init__()
self.tarmoq = nn.Sequential(
nn.Conv2d(3, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(4),
nn.Flatten(), nn.Linear(16 * 16, 64), nn.ReLU(), nn.Linear(64, d))
def forward(self, x):
return F.normalize(self.tarmoq(x), dim=-1)
class MatnEncoder(nn.Module):
"""So'z embeddinglari o'rtachasi (bag of words) + MLP. So'z tartibini KO'RMAYDI."""
def __init__(self, lugat, d=32):
super().__init__()
self.lugat = {w: i + 1 for i, w in enumerate(lugat)} # 0 - to'ldiruvchi
self.emb = nn.Embedding(len(lugat) + 1, 32, padding_idx=0)
self.mlp = nn.Sequential(nn.Linear(32, 64), nn.ReLU(), nn.Linear(64, d))
def tokenla(self, matnlar):
idx = [[self.lugat.get(w, 0) for w in m.replace(",", "").split()] for m in matnlar]
L = max(len(i) for i in idx)
return torch.tensor([i + [0] * (L - len(i)) for i in idx])
def forward(self, matnlar):
t = self.tokenla(matnlar)
e = self.emb(t)
o = e.sum(1) / (t > 0).sum(1, keepdim=True).clamp(min=1)
return F.normalize(self.mlp(o), dim=-1)
def lugat_yasash():
sozlar = set()
for r, o, s in KOMBO:
for sh in SHABLONLAR:
sozlar.update(sh.format(r=r, o=o, s=s).replace(",", "").split())
return sorted(sozlar)
def orgat_clip(X, T, seed, qadamlar=400, B=128):
torch.manual_seed(seed)
re_, me = RasmEncoder(), MatnEncoder(lugat_yasash())
log_harorat = nn.Parameter(torch.tensor(math.log(1 / 0.07)))
opt = torch.optim.Adam(list(re_.parameters()) + list(me.parameters()) + [log_harorat], lr=2e-3)
g = torch.Generator().manual_seed(seed)
for q in range(qadamlar):
i = torch.randperm(len(X), generator=g)[:B]
zi, zt = re_(X[i]), me([T[j] for j in i.tolist()])
logit = log_harorat.exp().clamp(max=100) * zi @ zt.T # (B, B)
nishon = torch.arange(B)
loss = (F.cross_entropy(logit, nishon) + F.cross_entropy(logit.T, nishon)) / 2 # InfoNCE
opt.zero_grad()
loss.backward()
opt.step()
if q == 0:
boshlangich = loss.item()
re_.eval()
me.eval()
return re_, me, boshlangich, loss.item(), log_harorat.exp().item()
def main() -> None:
torch.set_num_threads(1)
korilgan = set(KOMBO) - KORILMAGAN
X, T, _ = toplam(korilgan, 150, seed=0)
Xt, _, Yt = toplam(set(KOMBO), 40, seed=1)
yozuvlar = [f"{o} {r} {s}" for r, o, s in KOMBO]
yangi = torch.tensor([k in KORILMAGAN for k in KOMBO])
print("=== 1. Matn -> rasm qidiruvi: 18 so'rov, galereya 720 rasm (har kombinatsiyadan 40) ===")
print(" P@10 - top-10 dagi to'g'ri rasmlar ulushi; R-precision - top-40 da (40 ta to'g'ri bor)")
print(" rasm -> matn R@1 / R@3 - to'g'ri tavsif 18 tadan top-1 / top-3 da")
natija = []
modellar = []
for seed in range(3):
re_, me, *_ = orgat_clip(X, T, seed)
modellar.append((re_, me))
with torch.no_grad():
o = me(yozuvlar) @ re_(Xt).T # (18, 720)
top = o.argsort(1, descending=True)
togri = Yt[top] == torch.arange(18)[:, None] # (18, 720)
p10 = togri[:, :10].float().mean(1)
rp = togri[:, :40].float().mean(1)
rank = (o.T.argsort(1, descending=True) == Yt[:, None]).float().argmax(1) # rasm -> matn
r1 = (rank < 1).float()
r3 = (rank < 3).float()
yangi_r = yangi[Yt]
natija.append([p10[~yangi].mean(), p10[yangi].mean(), rp[~yangi].mean(), rp[yangi].mean(),
r1[~yangi_r].mean(), r1[yangi_r].mean(), r3[~yangi_r].mean(), r3[yangi_r].mean()])
v = np.array(natija, dtype=float)
print(" ko'rilgan ko'rilmagan")
for j, nom in enumerate(("matn->rasm P@10", "matn->rasm R-prec", "rasm->matn R@1", "rasm->matn R@3")):
a, b = v[:, 2 * j], v[:, 2 * j + 1]
print(f" {nom:<19} {a.mean():.3f} +- {a.std(ddof=1) / math.sqrt(3):.3f} "
f"{b.mean():.3f} +- {b.std(ddof=1) / math.sqrt(3):.3f}")
print(f" tasodifiy daraja: P@10 {1 / 18:.3f}, R@1 {1 / 18:.3f}, R@3 {3 / 18:.3f}")
print("\n=== 2. Ko'rilmagan so'rovlar: top-10 da qaysi rasmlar (urug' 0) ===")
re_, me = modellar[0]
with torch.no_grad():
galereya = re_(Xt)
for kombo in sorted(KORILMAGAN):
so = me([" ".join((kombo[1], kombo[0], kombo[2]))])
top = (so @ galereya.T)[0].argsort(descending=True)[:10]
topilgan = [KOMBO[i] for i in Yt[top].tolist()]
sanoq = {j: [t[j] == kombo[j] for t in topilgan].count(True) for j in range(3)}
print(f" {' '.join(kombo):<24} to'liq mos {topilgan.count(kombo):>2}/10; rang {sanoq[0]}/10, "
f"o'lcham {sanoq[1]}/10, shakl {sanoq[2]}/10")
print("\n=== 3. Matn encoderining cheklovlari (so'zlar xaltasi) ===")
with torch.no_grad():
juftlar = [("katta qizil doira", "qizil katta doira"),
("qizil katta doira", "qizil emas katta doira"),
("qizil katta doira", "katta doira, rangi qizil"),
("qizil katta doira", "yashil katta doira"),
("qizil katta doira", "qizil kichik kvadrat")]
for a, b in juftlar:
kos = (me([a]) @ me([b]).T).item()
print(f" {a!r:<22} ~ {b!r:<28} kosinus {kos:.3f}")
so = me(["qizil emas katta doira"])
top = (so @ galereya.T)[0].argsort(descending=True)[:20]
qizil = np.mean([KOMBO[i][0] == "qizil" for i in Yt[top].tolist()])
print(f" so'rov 'qizil emas katta doira': top-20 dagi qizil rasmlar ulushi {qizil:.2f}")
print(" 'emas' lug'atda yo'q -> e'tiborsiz; so'z tartibi ham ko'rinmaydi")
print(" ⭐ inkor va munosabatlar ('chapda', 'emas') - CLIP turidagi modellarning ma'lum zaif joyi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Matn -> rasm qidiruvi: 18 so'rov, galereya 720 rasm (har kombinatsiyadan 40) ===
P@10 - top-10 dagi to'g'ri rasmlar ulushi; R-precision - top-40 da (40 ta to'g'ri bor)
rasm -> matn R@1 / R@3 - to'g'ri tavsif 18 tadan top-1 / top-3 da
ko'rilgan ko'rilmagan
matn->rasm P@10 0.955 +- 0.006 0.758 +- 0.042
matn->rasm R-prec 0.923 +- 0.013 0.673 +- 0.045
rasm->matn R@1 0.974 +- 0.010 0.519 +- 0.043
rasm->matn R@3 0.999 +- 0.001 0.898 +- 0.033
tasodifiy daraja: P@10 0.056, R@1 0.056, R@3 0.167
=== 2. Ko'rilmagan so'rovlar: top-10 da qaysi rasmlar (urug' 0) ===
ko'k katta uchburchak to'liq mos 10/10; rang 10/10, o'lcham 10/10, shakl 10/10
qizil kichik kvadrat to'liq mos 6/10; rang 10/10, o'lcham 10/10, shakl 6/10
qizil kichik uchburchak to'liq mos 6/10; rang 7/10, o'lcham 10/10, shakl 9/10
yashil katta doira to'liq mos 10/10; rang 10/10, o'lcham 10/10, shakl 10/10
=== 3. Matn encoderining cheklovlari (so'zlar xaltasi) ===
'katta qizil doira' ~ 'qizil katta doira' kosinus 1.000
'qizil katta doira' ~ 'qizil emas katta doira' kosinus 1.000
'qizil katta doira' ~ 'katta doira, rangi qizil' kosinus 0.891
'qizil katta doira' ~ 'yashil katta doira' kosinus 0.614
'qizil katta doira' ~ 'qizil kichik kvadrat' kosinus 0.167
so'rov 'qizil emas katta doira': top-20 dagi qizil rasmlar ulushi 1.00
'emas' lug'atda yo'q -> e'tiborsiz; so'z tartibi ham ko'rinmaydi
⭐ inkor va munosabatlar ('chapda', 'emas') - CLIP turidagi modellarning ma'lum zaif joyiNima ko'rsatdi: 1-bo'lim — qidiruv o'lchovlari (3 urug'): ko'rilgan so'rovlarda P@10 0.955, R-precision 0.923; ko'rilmaganlarda 0.758 va 0.673. Rasm → matn yo'nalishida R@1 0.974 va 0.519 (1-misoldagi zero-shot aniqlikning o'zi), R@3 esa ko'rilmaganlarda ham 0.898 — to'g'ri tavsif ko'pincha top-3 da, lekin birinchi emas. Matn → rasm qidiruvi ko'rilmagan kombinatsiyada zero-shot dan yaxshiroq, chunki top-10 uchun galereyadagi eng mos 10 ta rasmni topish kifoya. 2-bo'lim: ko'k katta uchburchak va yashil katta doira so'rovlarida top-10 ning 10 tasi ham to'liq mos; qizil kichik kvadrat da 6/10 — rang va o'lcham 10/10, xato faqat shaklda; qizil kichik uchburchak da ham 6/10. 3-bo'lim — so'zlar xaltasi encoderining cheklovlari: katta qizil doira va qizil katta doira kosinusi 1.000 (tartib ko'rinmaydi), qizil emas katta doira ham 1.000 — emas lug'atda yo'q va e'tiborsiz qoldi; shu so'rovning top-20 natijasining 1.00 qismi aynan qizil. Boshqa shablondagi katta doira, rangi qizil 0.891 — yaqin, rang almashtirilsa 0.614, hamma atribut boshqa bo'lsa 0.167. Bog'liq bo'limlar: 2.5, 2.6.
Misol 3 — Matn sharti bilan kichik generator (shartli VAE)
"""Matn sharti bilan kichik generator (shartli VAE): ko'rilmagan kombinatsiyalarda atributlar to'g'riligi."""
import math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
SHAKLLAR = ["doira", "kvadrat", "uchburchak"]
RANGLAR = ["qizil", "yashil", "ko'k"]
OLCHAMLAR = ["katta", "kichik"]
RGB = {"qizil": (0.9, 0.15, 0.15), "yashil": (0.15, 0.8, 0.2), "ko'k": (0.2, 0.3, 0.95)}
KOMBO = [(r, o, s) for s in SHAKLLAR for r in RANGLAR for o in OLCHAMLAR] # 18 ta
KORILMAGAN = {("ko'k", "katta", "uchburchak"), ("qizil", "kichik", "kvadrat"),
("yashil", "katta", "doira"), ("qizil", "kichik", "uchburchak")}
SHABLONLAR = ["{r} {o} {s}", "{o} {r} {s}", "{r} rangli {o} {s}", "bu {o} {r} {s}",
"{o} {s}, rangi {r}", "rasmda {r} {s}, o'lchami {o}"]
def chiz(rang, olcham, shakl, rng):
"""16x16 RGB rasm: fon + bitta shakl (joyi, rangi biroz tasodifiy)."""
r = 6.0 if olcham == "katta" else 3.2
cy, cx = rng.uniform(r + 0.5, 15.5 - r, size=2)
y, x = np.mgrid[0:16, 0:16] + 0.5
if shakl == "doira":
m = (y - cy) ** 2 + (x - cx) ** 2 <= r ** 2
elif shakl == "kvadrat":
m = (np.abs(y - cy) <= 0.85 * r) & (np.abs(x - cx) <= 0.85 * r)
else:
m = (y >= cy - r) & (y <= cy + r) & (np.abs(x - cx) <= (y - (cy - r)) / 2)
rasm = 0.1 + 0.05 * rng.standard_normal((3, 16, 16))
rang_q = np.array(RGB[rang]) + rng.uniform(-0.08, 0.08, 3)
rasm[:, m] = rang_q[:, None]
return rasm.astype(np.float32)
def toplam(kombolar, n_har, seed):
rng = np.random.default_rng(seed)
X, T, Y = [], [], []
for ki, (r, o, s) in enumerate(KOMBO):
if (r, o, s) not in kombolar:
continue
for _ in range(n_har):
X.append(chiz(r, o, s, rng))
T.append(SHABLONLAR[rng.integers(len(SHABLONLAR))].format(r=r, o=o, s=s))
Y.append(ki)
return torch.tensor(np.array(X)), T, torch.tensor(Y)
class MatnEncoder(nn.Module):
"""So'z embeddinglari o'rtachasi (bag of words) + MLP. So'z tartibini KO'RMAYDI."""
def __init__(self, lugat, d=32):
super().__init__()
self.lugat = {w: i + 1 for i, w in enumerate(lugat)} # 0 - to'ldiruvchi
self.emb = nn.Embedding(len(lugat) + 1, 32, padding_idx=0)
self.mlp = nn.Sequential(nn.Linear(32, 64), nn.ReLU(), nn.Linear(64, d))
def tokenla(self, matnlar):
idx = [[self.lugat.get(w, 0) for w in m.replace(",", "").split()] for m in matnlar]
L = max(len(i) for i in idx)
return torch.tensor([i + [0] * (L - len(i)) for i in idx])
def forward(self, matnlar):
t = self.tokenla(matnlar)
e = self.emb(t)
o = e.sum(1) / (t > 0).sum(1, keepdim=True).clamp(min=1)
return F.normalize(self.mlp(o), dim=-1)
def lugat_yasash():
sozlar = set()
for r, o, s in KOMBO:
for sh in SHABLONLAR:
sozlar.update(sh.format(r=r, o=o, s=s).replace(",", "").split())
return sorted(sozlar)
class AtributKlassifikator(nn.Module):
"""Baholovchi: bitta CNN tanasi, uchta bosh (rang, o'lcham, shakl). BARCHA 18 kombinatsiyada o'rgatiladi."""
def __init__(self):
super().__init__()
self.tana = nn.Sequential(nn.Conv2d(3, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(4),
nn.Flatten(), nn.Linear(256, 64), nn.ReLU())
self.boshlar = nn.ModuleList([nn.Linear(64, 3), nn.Linear(64, 2), nn.Linear(64, 3)])
def forward(self, x):
h = self.tana(x)
return [b(h) for b in self.boshlar]
def atribut_nishon(Y):
k = torch.tensor([[RANGLAR.index(r), OLCHAMLAR.index(o), SHAKLLAR.index(s)] for r, o, s in KOMBO])
return k[Y]
def orgat_atribut(X, Y, seed=0):
torch.manual_seed(seed)
m = AtributKlassifikator()
opt = torch.optim.Adam(m.parameters(), lr=5e-3)
A = atribut_nishon(Y)
g = torch.Generator().manual_seed(seed)
for _ in range(500):
i = torch.randperm(len(X), generator=g)[:128]
loss = sum(F.cross_entropy(o, A[i, j]) for j, o in enumerate(m(X[i])))
opt.zero_grad()
loss.backward()
opt.step()
m.eval()
return m
class ShartliVAE(nn.Module):
"""Shartli VAE: shart c - matn embeddingi (kompozitsion) yoki kombinatsiya ID si (one-hot)."""
def __init__(self, shart_turi, z=8, d=32):
super().__init__()
self.shart_turi = shart_turi
if shart_turi == "matn":
self.shart = MatnEncoder(lugat_yasash(), d)
else:
self.shart = nn.Embedding(len(KOMBO), d)
self.enc = nn.Sequential(nn.Linear(768 + d, 128), nn.ReLU(), nn.Linear(128, 2 * z))
self.dec = nn.Sequential(nn.Linear(z + d, 128), nn.ReLU(), nn.Linear(128, 768), nn.Sigmoid())
self.z = z
def c(self, matnlar, idx):
if self.shart_turi == "matn":
return self.shart.mlp(self._ortacha(matnlar))
return self.shart(idx)
def _ortacha(self, matnlar):
t = self.shart.tokenla(matnlar)
return self.shart.emb(t).sum(1) / (t > 0).sum(1, keepdim=True).clamp(min=1)
def forward(self, x, c):
mu, logvar = self.enc(torch.cat([x.flatten(1), c], 1)).chunk(2, 1)
return mu, logvar
def dekod(self, z, c):
return self.dec(torch.cat([z, c], 1)).view(-1, 3, 16, 16)
def orgat_cvae(X, T, Y, shart_turi, seed, qadamlar=500, B=128):
torch.manual_seed(seed)
m = ShartliVAE(shart_turi)
opt = torch.optim.Adam(m.parameters(), lr=2e-3)
g = torch.Generator().manual_seed(seed)
for _ in range(qadamlar):
i = torch.randperm(len(X), generator=g)[:B]
c = m.c([T[j] for j in i.tolist()], Y[i])
mu, logvar = m(X[i], c)
z = mu + (0.5 * logvar).exp() * torch.randn(mu.shape, generator=g)
qayta = ((m.dekod(z, c) - X[i]) ** 2).sum((1, 2, 3)) / (2 * 0.1 ** 2)
kl = 0.5 * (mu ** 2 + logvar.exp() - 1 - logvar).sum(1)
loss = (qayta + kl).mean()
opt.zero_grad()
loss.backward()
opt.step()
m.eval()
return m
def rangli_ascii(rasmlar):
"""Har piksel: nuqta - fon, Q/Y/K - eng kuchli kanal (qizil/yashil/ko'k)."""
satrlar = []
for r in range(16):
qism = []
for x in rasmlar:
q = ""
for c in range(16):
p = x[:, r, c]
q += "\u00b7" if p.max() < 0.4 else "QYK"[int(p.argmax())]
qism.append(q)
satrlar.append(" " + " ".join(qism))
return "\n".join(satrlar)
def main() -> None:
torch.set_num_threads(1)
korilgan = set(KOMBO) - KORILMAGAN
X, T, Y = toplam(korilgan, 150, seed=0)
Xa, _, Ya = toplam(set(KOMBO), 60, seed=2) # baholovchi uchun: hamma kombinatsiya
Xt, _, Yt = toplam(set(KOMBO), 20, seed=3)
baholovchi = orgat_atribut(Xa, Ya)
with torch.no_grad():
aniq = [(o.argmax(1) == atribut_nishon(Yt)[:, j]).float().mean().item()
for j, o in enumerate(baholovchi(Xt))]
print("=== 1. Baholovchi: atribut klassifikatorlari (haqiqiy test rasmlarida) ===")
print(f" rang {aniq[0]:.3f}, o'lcham {aniq[1]:.3f}, shakl {aniq[2]:.3f}")
print(" (baholovchi hamma 18 kombinatsiyani ko'rgan - u faqat hakam, generator emas)")
print("\n=== 2. Shartli VAE: matn sharti va kombinatsiya ID si (3 urug') ===")
print(" har kombinatsiyaga 40 namuna, shart - 'o'lcham rang shakl' tavsifi")
yozuvlar = [f"{o} {r} {s}" for r, o, s in KOMBO]
nishon = atribut_nishon(torch.arange(len(KOMBO)).repeat_interleave(40))
yangi = torch.tensor([KOMBO[i] in KORILMAGAN for i in range(len(KOMBO))]).repeat_interleave(40)
natija, rasmlar = {}, {}
for tur in ("matn", "ID"):
for seed in range(3):
m = orgat_cvae(X, T, Y, tur, seed)
g = torch.Generator().manual_seed(100 + seed)
with torch.no_grad():
idx = torch.arange(len(KOMBO)).repeat_interleave(40)
c = m.c([yozuvlar[i] for i in idx.tolist()], idx)
s = m.dekod(torch.randn((len(idx), m.z), generator=g), c)
tog = torch.stack([o.argmax(1) == nishon[:, j] for j, o in enumerate(baholovchi(s))], 1)
hamma = tog.all(1)
natija.setdefault(tur, []).append([tog[~yangi].float().mean(0).tolist() + [hamma[~yangi].float().mean().item()],
tog[yangi].float().mean(0).tolist() + [hamma[yangi].float().mean().item()]])
if seed == 0:
rasmlar[tur] = s
print(" shart to'plam rang o'lcham shakl hammasi")
for tur, v in natija.items():
v = np.array(v) # (urug', to'plam, 4)
for j, nom in enumerate(("ko'rilgan", "ko'rilmagan")):
q = v[:, j].mean(0)
print(f" {tur:<6} {nom:<12} {q[0]:>6.3f} {q[1]:>6.3f} {q[2]:>6.3f} {q[3]:>7.3f}")
print("\n=== 3. Ko'rilmagan kombinatsiyalarda 'hammasi to'g'ri': matn - ID (juftlashgan) ===")
a = np.array(natija["matn"])[:, 1, 3]
b = np.array(natija["ID"])[:, 1, 3]
f = a - b
se = f.std(ddof=1) / math.sqrt(3)
print(f" matn {a.mean():.3f}, ID {b.mean():.3f}, farq {f.mean():+.3f} (SE {se:.3f}) -> "
+ ("sezilarli" if abs(f.mean()) > 2 * se else "sezilarli emas"))
print(f" tasodifiy daraja (3 x 2 x 3 atribut): {1 / 18:.3f}")
print(" ID sharti: ko'rilmagan kombinatsiya embeddingi hech qachon o'rgatilmagan (tasodifiy)")
print("\n=== 4. Ko'rilmagan so'rov 'katta ko'k uchburchak' (urug' 0, 2 namuna) ===")
ki = KOMBO.index(("ko'k", "katta", "uchburchak"))
print(" chapda: matn sharti | o'ngda: ID sharti (Q/Y/K - qizil/yashil/ko'k)")
print(rangli_ascii([rasmlar["matn"][ki * 40], rasmlar["matn"][ki * 40 + 1],
rasmlar["ID"][ki * 40], rasmlar["ID"][ki * 40 + 1]]))
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Baholovchi: atribut klassifikatorlari (haqiqiy test rasmlarida) ===
rang 1.000, o'lcham 1.000, shakl 0.958
(baholovchi hamma 18 kombinatsiyani ko'rgan - u faqat hakam, generator emas)
=== 2. Shartli VAE: matn sharti va kombinatsiya ID si (3 urug') ===
har kombinatsiyaga 40 namuna, shart - 'o'lcham rang shakl' tavsifi
shart to'plam rang o'lcham shakl hammasi
matn ko'rilgan 0.810 1.000 0.474 0.396
matn ko'rilmagan 0.642 0.994 0.417 0.304
ID ko'rilgan 0.911 1.000 0.501 0.473
ID ko'rilmagan 0.377 0.477 0.250 0.000
=== 3. Ko'rilmagan kombinatsiyalarda 'hammasi to'g'ri': matn - ID (juftlashgan) ===
matn 0.304, ID 0.000, farq +0.304 (SE 0.015) -> sezilarli
tasodifiy daraja (3 x 2 x 3 atribut): 0.056
ID sharti: ko'rilmagan kombinatsiya embeddingi hech qachon o'rgatilmagan (tasodifiy)
=== 4. Ko'rilmagan so'rov 'katta ko'k uchburchak' (urug' 0, 2 namuna) ===
chapda: matn sharti | o'ngda: ID sharti (Q/Y/K - qizil/yashil/ko'k)
················ ················ ················ ················
················ ················ ·····Q·Q········ ················
················ ················ ················ ················
······KKK······· ················ ················ ················
····KKKKKKKK···· ·····KKKKK······ ············Q··· ················
····KKKKKKKKK··· ········Q·K·K··· ················ ············Y···
····KKKKKKKK···· ·······QQK·KKK·· ·············Q·· ···········Y····
···KKKKKKKKKK··· ·······QQ·KKKK·· ···········Y···· ··········QY·Y··
···KKKKKKKKKK··· ·······KKKKKK··· ·······Y········ ···········YYY··
···KKKKKKKKKK··· ······QQKKKKKK·· ·····Y·Y········ ·········YYYYY··
···KKKKKKKKKK··· ····KKKKKKKKKK·· ·····YYYYYY····· ········YYYYYY··
···KKKKKKKKKK··· ····KKKKKKKKKK·· ······YYY······· ·······YYYYYYY··
····KKKKKKKKK··· ····KQKKKKKKKK·· ················ ·········Y······
····K·····QQQ··· ····QKKKKQKQKQ·· ················ ···········Q····
················ ······QQQKQQ···· ················ ················
················ ················ ················ ················Nima ko'rsatdi: 1-bo'lim — hakam: atribut klassifikatorlari haqiqiy test rasmlarida rang va o'lchamda 1.000, shaklda 0.958 — ya'ni shakl bo'yicha hakamning o'zi ham ~4% xato qiladi. 2-bo'lim — ikki xil shart, 3 urug'. Ko'rilgan kombinatsiyalarda ikkalasi yaqin: "hammasi to'g'ri" matn sharti 0.396, ID sharti 0.473 (ID rangni yaxshiroq beradi: 0.911 va 0.810). Ko'rilmaganlarda farq keskin: matn sharti 0.304, ID sharti 0.000 — ID ning rang, o'lcham va shakl aniqligi tasodifiy darajada (0.377, 0.477, 0.250), chunki bu kombinatsiyalarning embeddingi hech qachon o'rgatilmagan. Matn sharti esa ko'rilmaganlarda o'lchamni deyarli mukammal (0.994) va rangni 0.642 beradi. Hamma joyda eng zaif atribut — shakl (0.417–0.501): kichik MLP dekoder o'rtacha, xira shakl chizadi; chegara aniqligi 16x16 da shaklni ajratish uchun hal qiluvchi. 3-bo'lim: ko'rilmaganlarda matn - ID farqi +0.304, SE 0.015 — sezilarli. 4-bo'lim — ASCII: katta ko'k uchburchak (o'quvda ko'rilmagan) so'roviga matn sharti ko'k (K) katta shakl chizdi (biri pastga kengayuvchi, uchburchakka o'xshash), ID sharti esa kichik yashil-qizil dog'lar — so'rovga aloqasiz. Bog'liq bo'lim: 2.7.
Misol 4 — CLIP bilan qayta saralash (best-of-K)
"""CLIP bilan qayta saralash: shartli VAE nomzodlaridan eng mosini tanlash (best-of-K)."""
import math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.metrics import roc_auc_score
SHAKLLAR = ["doira", "kvadrat", "uchburchak"]
RANGLAR = ["qizil", "yashil", "ko'k"]
OLCHAMLAR = ["katta", "kichik"]
RGB = {"qizil": (0.9, 0.15, 0.15), "yashil": (0.15, 0.8, 0.2), "ko'k": (0.2, 0.3, 0.95)}
KOMBO = [(r, o, s) for s in SHAKLLAR for r in RANGLAR for o in OLCHAMLAR] # 18 ta
KORILMAGAN = {("ko'k", "katta", "uchburchak"), ("qizil", "kichik", "kvadrat"),
("yashil", "katta", "doira"), ("qizil", "kichik", "uchburchak")}
SHABLONLAR = ["{r} {o} {s}", "{o} {r} {s}", "{r} rangli {o} {s}", "bu {o} {r} {s}",
"{o} {s}, rangi {r}", "rasmda {r} {s}, o'lchami {o}"]
def chiz(rang, olcham, shakl, rng):
"""16x16 RGB rasm: fon + bitta shakl (joyi, rangi biroz tasodifiy)."""
r = 6.0 if olcham == "katta" else 3.2
cy, cx = rng.uniform(r + 0.5, 15.5 - r, size=2)
y, x = np.mgrid[0:16, 0:16] + 0.5
if shakl == "doira":
m = (y - cy) ** 2 + (x - cx) ** 2 <= r ** 2
elif shakl == "kvadrat":
m = (np.abs(y - cy) <= 0.85 * r) & (np.abs(x - cx) <= 0.85 * r)
else:
m = (y >= cy - r) & (y <= cy + r) & (np.abs(x - cx) <= (y - (cy - r)) / 2)
rasm = 0.1 + 0.05 * rng.standard_normal((3, 16, 16))
rang_q = np.array(RGB[rang]) + rng.uniform(-0.08, 0.08, 3)
rasm[:, m] = rang_q[:, None]
return rasm.astype(np.float32)
def toplam(kombolar, n_har, seed):
rng = np.random.default_rng(seed)
X, T, Y = [], [], []
for ki, (r, o, s) in enumerate(KOMBO):
if (r, o, s) not in kombolar:
continue
for _ in range(n_har):
X.append(chiz(r, o, s, rng))
T.append(SHABLONLAR[rng.integers(len(SHABLONLAR))].format(r=r, o=o, s=s))
Y.append(ki)
return torch.tensor(np.array(X)), T, torch.tensor(Y)
class RasmEncoder(nn.Module):
"""Kichik CNN: 3x16x16 -> konvolyutsiya (16 kanal) -> max-pool 4x4 -> 256 -> 64 -> 32."""
def __init__(self, d=32):
super().__init__()
self.tarmoq = nn.Sequential(
nn.Conv2d(3, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(4),
nn.Flatten(), nn.Linear(16 * 16, 64), nn.ReLU(), nn.Linear(64, d))
def forward(self, x):
return F.normalize(self.tarmoq(x), dim=-1)
class MatnEncoder(nn.Module):
"""So'z embeddinglari o'rtachasi (bag of words) + MLP. So'z tartibini KO'RMAYDI."""
def __init__(self, lugat, d=32):
super().__init__()
self.lugat = {w: i + 1 for i, w in enumerate(lugat)} # 0 - to'ldiruvchi
self.emb = nn.Embedding(len(lugat) + 1, 32, padding_idx=0)
self.mlp = nn.Sequential(nn.Linear(32, 64), nn.ReLU(), nn.Linear(64, d))
def tokenla(self, matnlar):
idx = [[self.lugat.get(w, 0) for w in m.replace(",", "").split()] for m in matnlar]
L = max(len(i) for i in idx)
return torch.tensor([i + [0] * (L - len(i)) for i in idx])
def forward(self, matnlar):
t = self.tokenla(matnlar)
e = self.emb(t)
o = e.sum(1) / (t > 0).sum(1, keepdim=True).clamp(min=1)
return F.normalize(self.mlp(o), dim=-1)
def lugat_yasash():
sozlar = set()
for r, o, s in KOMBO:
for sh in SHABLONLAR:
sozlar.update(sh.format(r=r, o=o, s=s).replace(",", "").split())
return sorted(sozlar)
def orgat_clip(X, T, seed, qadamlar=400, B=128):
torch.manual_seed(seed)
re_, me = RasmEncoder(), MatnEncoder(lugat_yasash())
log_harorat = nn.Parameter(torch.tensor(math.log(1 / 0.07)))
opt = torch.optim.Adam(list(re_.parameters()) + list(me.parameters()) + [log_harorat], lr=2e-3)
g = torch.Generator().manual_seed(seed)
for q in range(qadamlar):
i = torch.randperm(len(X), generator=g)[:B]
zi, zt = re_(X[i]), me([T[j] for j in i.tolist()])
logit = log_harorat.exp().clamp(max=100) * zi @ zt.T # (B, B)
nishon = torch.arange(B)
loss = (F.cross_entropy(logit, nishon) + F.cross_entropy(logit.T, nishon)) / 2 # InfoNCE
opt.zero_grad()
loss.backward()
opt.step()
if q == 0:
boshlangich = loss.item()
re_.eval()
me.eval()
return re_, me, boshlangich, loss.item(), log_harorat.exp().item()
class AtributKlassifikator(nn.Module):
"""Baholovchi: bitta CNN tanasi, uchta bosh (rang, o'lcham, shakl). BARCHA 18 kombinatsiyada o'rgatiladi."""
def __init__(self):
super().__init__()
self.tana = nn.Sequential(nn.Conv2d(3, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(4),
nn.Flatten(), nn.Linear(256, 64), nn.ReLU())
self.boshlar = nn.ModuleList([nn.Linear(64, 3), nn.Linear(64, 2), nn.Linear(64, 3)])
def forward(self, x):
h = self.tana(x)
return [b(h) for b in self.boshlar]
def atribut_nishon(Y):
k = torch.tensor([[RANGLAR.index(r), OLCHAMLAR.index(o), SHAKLLAR.index(s)] for r, o, s in KOMBO])
return k[Y]
def orgat_atribut(X, Y, seed=0):
torch.manual_seed(seed)
m = AtributKlassifikator()
opt = torch.optim.Adam(m.parameters(), lr=5e-3)
A = atribut_nishon(Y)
g = torch.Generator().manual_seed(seed)
for _ in range(500):
i = torch.randperm(len(X), generator=g)[:128]
loss = sum(F.cross_entropy(o, A[i, j]) for j, o in enumerate(m(X[i])))
opt.zero_grad()
loss.backward()
opt.step()
m.eval()
return m
class ShartliVAE(nn.Module):
"""Shartli VAE: shart c - matn embeddingi (kompozitsion) yoki kombinatsiya ID si (one-hot)."""
def __init__(self, shart_turi, z=8, d=32):
super().__init__()
self.shart_turi = shart_turi
if shart_turi == "matn":
self.shart = MatnEncoder(lugat_yasash(), d)
else:
self.shart = nn.Embedding(len(KOMBO), d)
self.enc = nn.Sequential(nn.Linear(768 + d, 128), nn.ReLU(), nn.Linear(128, 2 * z))
self.dec = nn.Sequential(nn.Linear(z + d, 128), nn.ReLU(), nn.Linear(128, 768), nn.Sigmoid())
self.z = z
def c(self, matnlar, idx):
if self.shart_turi == "matn":
return self.shart.mlp(self._ortacha(matnlar))
return self.shart(idx)
def _ortacha(self, matnlar):
t = self.shart.tokenla(matnlar)
return self.shart.emb(t).sum(1) / (t > 0).sum(1, keepdim=True).clamp(min=1)
def forward(self, x, c):
mu, logvar = self.enc(torch.cat([x.flatten(1), c], 1)).chunk(2, 1)
return mu, logvar
def dekod(self, z, c):
return self.dec(torch.cat([z, c], 1)).view(-1, 3, 16, 16)
def orgat_cvae(X, T, Y, shart_turi, seed, qadamlar=500, B=128):
torch.manual_seed(seed)
m = ShartliVAE(shart_turi)
opt = torch.optim.Adam(m.parameters(), lr=2e-3)
g = torch.Generator().manual_seed(seed)
for _ in range(qadamlar):
i = torch.randperm(len(X), generator=g)[:B]
c = m.c([T[j] for j in i.tolist()], Y[i])
mu, logvar = m(X[i], c)
z = mu + (0.5 * logvar).exp() * torch.randn(mu.shape, generator=g)
qayta = ((m.dekod(z, c) - X[i]) ** 2).sum((1, 2, 3)) / (2 * 0.1 ** 2)
kl = 0.5 * (mu ** 2 + logvar.exp() - 1 - logvar).sum(1)
loss = (qayta + kl).mean()
opt.zero_grad()
loss.backward()
opt.step()
m.eval()
return m
def main() -> None:
torch.set_num_threads(1)
korilgan = set(KOMBO) - KORILMAGAN
X, T, Y = toplam(korilgan, 150, seed=0)
Xa, _, Ya = toplam(set(KOMBO), 60, seed=2)
baholovchi = orgat_atribut(Xa, Ya)
re_, me, *_ = orgat_clip(X, T, seed=0) # hakam EMAS - generatsiya qismi
gen = orgat_cvae(X, T, Y, "matn", seed=0)
yozuvlar = [f"{o} {r} {s}" for r, o, s in KOMBO]
print("=== 1. Quvur: matn -> shartli VAE (K ta nomzod) -> CLIP bahosi -> eng yaxshisi ===")
print(" CLIP va VAE faqat ko'rilgan 14 kombinatsiyada o'rgatilgan; baholovchi - alohida")
G, K = 12, 8 # har so'rovga 12 guruh x 8 nomzod
def namunalar(seed):
g = torch.Generator().manual_seed(seed)
idx = torch.arange(len(KOMBO)).repeat_interleave(G * K)
with torch.no_grad():
c = gen.c([yozuvlar[i] for i in idx.tolist()], idx)
s = gen.dekod(torch.randn((len(idx), gen.z), generator=g), c)
ball = (re_(s) * me(yozuvlar)[idx]).sum(1) # kosinus: rasm va o'z so'rovi
nishon = atribut_nishon(idx)
tog = torch.stack([o.argmax(1) == nishon[:, j] for j, o in enumerate(baholovchi(s))], 1).all(1)
return idx.view(len(KOMBO), G, K), ball.view(len(KOMBO), G, K), tog.view(len(KOMBO), G, K)
yangi = torch.tensor([k in KORILMAGAN for k in KOMBO])
print("\n=== 2. Best-of-K: CLIP bo'yicha eng yuqori nomzod (3 urug', 'hammasi to'g'ri' ulushi) ===")
print(" K ko'rilgan ko'rilmagan")
natija = {}
for seed in range(3):
_, ball, tog = namunalar(50 + seed)
for k in (1, 2, 4, 8):
eng = ball[:, :, :k].argmax(2, keepdim=True)
tanlangan = tog[:, :, :k].gather(2, eng)[:, :, 0].float().mean(1) # (18,)
natija.setdefault(k, []).append((tanlangan[~yangi].mean().item(), tanlangan[yangi].mean().item()))
for k, v in natija.items():
v = np.array(v)
print(f" {k:>3} {v[:, 0].mean():.3f} {v[:, 1].mean():.3f}")
for j, nom in enumerate(("ko'rilgan", "ko'rilmagan")):
f = np.array(natija[8])[:, j] - np.array(natija[1])[:, j]
se = f.std(ddof=1) / math.sqrt(3)
print(f" {nom:<12} K = 8 - K = 1: {f.mean():+.3f} (SE {se:.3f}) -> "
+ ("sezilarli" if abs(f.mean()) > 2 * se else "sezilarli emas"))
print("\n=== 3. CLIP bahosi to'g'rilikni qanchalik ajratadi (AUC, urug' 50) ===")
idx, ball, tog = namunalar(50)
for nom, m in (("ko'rilgan", ~yangi), ("ko'rilmagan", yangi)):
b, t = ball[m].flatten().numpy(), tog[m].flatten().numpy()
print(f" {nom:<12} to'g'ri ulushi {t.mean():.3f}, AUC(CLIP bahosi -> hammasi to'g'ri) "
f"{roc_auc_score(t, b):.3f}")
print(" AUC 0.5 - tasodifiy, 1.0 - mukammal ajratish")
print("\n=== 4. Narx va xulosa ===")
print(f" best-of-{K}: {K} barobar ko'p generatsiya + {K} ta CLIP baholash har so'rovga")
k8 = np.array(natija[8]).mean(0)
k1 = np.array(natija[1]).mean(0)
print(f" ko'rilmagan: {k1[1]:.3f} -> {k8[1]:.3f}; ko'rilgan: {k1[0]:.3f} -> {k8[0]:.3f}")
if k8[1] - k1[1] < k8[0] - k1[0]:
print(" qayta saralash ko'rilmagan kombinatsiyada kamroq yordam berdi: tanlovchi CLIP ham")
print(" bu kombinatsiyalarni yomonroq taniydi (1-misol) - ko'r nuqtalari generator bilan umumiy")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Quvur: matn -> shartli VAE (K ta nomzod) -> CLIP bahosi -> eng yaxshisi ===
CLIP va VAE faqat ko'rilgan 14 kombinatsiyada o'rgatilgan; baholovchi - alohida
=== 2. Best-of-K: CLIP bo'yicha eng yuqori nomzod (3 urug', 'hammasi to'g'ri' ulushi) ===
K ko'rilgan ko'rilmagan
1 0.333 0.285
2 0.361 0.333
4 0.433 0.375
8 0.460 0.375
ko'rilgan K = 8 - K = 1: +0.127 (SE 0.010) -> sezilarli
ko'rilmagan K = 8 - K = 1: +0.090 (SE 0.018) -> sezilarli
=== 3. CLIP bahosi to'g'rilikni qanchalik ajratadi (AUC, urug' 50) ===
ko'rilgan to'g'ri ulushi 0.337, AUC(CLIP bahosi -> hammasi to'g'ri) 0.896
ko'rilmagan to'g'ri ulushi 0.273, AUC(CLIP bahosi -> hammasi to'g'ri) 0.764
AUC 0.5 - tasodifiy, 1.0 - mukammal ajratish
=== 4. Narx va xulosa ===
best-of-8: 8 barobar ko'p generatsiya + 8 ta CLIP baholash har so'rovga
ko'rilmagan: 0.285 -> 0.375; ko'rilgan: 0.333 -> 0.460
qayta saralash ko'rilmagan kombinatsiyada kamroq yordam berdi: tanlovchi CLIP ham
bu kombinatsiyalarni yomonroq taniydi (1-misol) - ko'r nuqtalari generator bilan umumiyNima ko'rsatdi: quvur uch qismdan: matn sharti bilan VAE nomzodlar yaratadi, 1-misoldagi CLIP ularni so'rovga mosligi bo'yicha baholaydi, alohida atribut klassifikatorlari esa hakam. 2-bo'lim — best-of-K: "hammasi to'g'ri" ulushi ko'rilganlarda 0.333 (K = 1) dan 0.460 (K = 8) ga, ko'rilmaganlarda 0.285 dan 0.375 ga oshdi; ikkala farq ham sezilarli (+0.127, SE 0.010 va +0.090, SE 0.018). Ko'rilmaganlarda K = 4 dan keyin yaxshilanish to'xtadi (0.375 va 0.375). 3-bo'lim sababni o'lchaydi: CLIP bahosi to'g'ri nomzodni ko'rilgan kombinatsiyalarda AUC 0.896 bilan ajratadi, ko'rilmaganlarda — 0.764: tanlovchi ham bu kombinatsiyalarni yomonroq taniydi (1-misolda zero-shot 0.519). 4-bo'lim: narxi — 8 barobar generatsiya va 8 ta CLIP baholash; foydasi ko'rilgan kombinatsiyalarda kattaroq. Muhim metodologik nuqta: agar natijani o'sha CLIP bilan baholaganimizda, best-of-K o'z-o'zidan "mukammal" ko'rinardi — shuning uchun hakam alohida model. Bog'liq bo'limlar: 2.8, 2.10.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "CLIP har qanday so'z kombinatsiyasini tushunadi" | Ko'rilganlarda 0.974, ko'rilmaganlarda 0.519 |
| "Zero-shot = o'quvsiz" | Encoderlar o'rgatilgan; faqat yangi sinf uchun klassifikator o'rgatilmaydi |
| "InfoNCE loss nolga tushishi kerak" | Batchdagi soxta manfiylar tufayli ~`2.0` da to'xtadi |
| "Model barcha atributlarni bir xil yaxshi kombinatsiyalaydi" | Rang 1.000, shakl 0.540 (ko'rilmaganlarda) |
| "Qidiruv va zero-shot bir xil qiyin" | Ko'rilmaganlarda P@10 0.758, R@1 0.519 |
| "Matn encoderi inkorni tushunadi" | qizil emas katta doira → top-20 ning 1.00 qismi qizil |
| "Har kombinatsiyaga alohida shart vektori — eng aniq" | Ko'rilmaganlarda ID sharti 0.000, matn sharti 0.304 |
| "Best-of-K har doim ko'p foyda beradi" | Ko'rilmaganlarda K = 4 dan keyin o'smadi |
| "Generatsiyani CLIP bilan baholash yetarli" | Tanlovchi va hakam bir model bo'lsa — o'z-o'zini tekshirish |
6. Keng tarqalgan xatolar va yechimlari
1. Vektorlarni normallamaslik
logit = zi @ zt.T # uzunlik ham ta'sir qiladi # ⚠️
zi, zt = F.normalize(zi, dim=-1), F.normalize(zt, dim=-1) # ✅
logit = log_harorat.exp().clamp(max=100) * zi @ zt.T2. Faqat bir yo'nalishli InfoNCE
loss = F.cross_entropy(logit, nishon) # faqat rasm -> matn # ⚠️
loss = (F.cross_entropy(logit, nishon) + F.cross_entropy(logit.T, nishon)) / 2 # ✅3. Haroratni cheklamaslik
logit = log_harorat.exp() * zi @ zt.T # portlashi mumkin # ⚠️
logit = log_harorat.exp().clamp(max=100) * zi @ zt.T # ✅4. Kompozitsion umumlashmani ko'rilgan kombinatsiyalarda o'lchash
aniqlik = (bashorat == Y_test).float().mean() # test = o'quv kombinatsiyalari # ⚠️
aniqlik_yangi = (bashorat[yangi] == Y_test[yangi]).float().mean() # ✅5. Ko'rilmagan kombinatsiyaga ID shart
c = nn.Embedding(18, 32)(torch.tensor([yangi_kombo])) # o'rgatilmagan vektor # ⚠️
c = matn_shart(["katta ko'k uchburchak"]) # so'zlar orqali # ✅6. Tanlovchi va hakam bir model
eng = nomzodlar[clip_ball.argmax()]; baho = clip_ball.max() # ⚠️
eng = nomzodlar[clip_ball.argmax()]; baho = atribut_hakam(eng) # ✅7. Noma'lum so'zlarni jimgina tashlash
idx = [lugat.get(w, 0) for w in matn.split()] # "emas" yo'qoladi # ⚠️
nomalum = [w for w in matn.split() if w not in lugat] # ✅
if nomalum:
print("ogohlantirish: noma'lum so'zlar", nomalum)7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 24.3-dars (o'tilgan): multi-head attention — cross-attention bilan matn sharti
- 25.7-dars (o'tilgan): embeddinglar va semantik qidiruv — CLIP o'sha g'oyani rasm va matn orasida qo'llaydi
- 26.2-dars (o'tilgan): VAE — 3-misoldagi shartli VAE ning asosi
- 26.6-dars (o'tilgan): shartli diffusion, classifier-free guidance, latent diffusion
- 26.7-dars (o'tilgan): baholash — alohida hakam, bir necha urug', juftlashgan farq
- Keyingi darslar: 26.9 Generativ AI etikasi va xavfsizligi — matn-rasm tizimlarining suiiste'moli, yodlab olish, watermark va kelib chiqishni belgilash; 26.10 Amaliyot — generativ loyiha boshidan oxirigacha; MLOps va deploy qismida — embedding qidiruv xizmatlari va generatsiya kechikishi
8. Eng yaxshi amaliyotlar
Kompozitsion umumlashmani alohida o'lchang: o'quvda ko'rilmagan kombinatsiyalarni oldindan ajrating.
Zero-shot va qidiruv natijalarini atributlar bo'yicha ham ko'ring — qaysi qism umumlashmayotganini topasiz.
InfoNCE da batch tarkibini tushuning: bir xil sinfli juftliklar soxta manfiy bo'ladi.
Matn encoderini inkor, tartib va munosabat so'rovlarida alohida sinang.
Generator shartini so'z/token darajasida bering (kompozitsion), kombinatsiya ID sida emas.
Generatsiyani tanlovchidan mustaqil hakam bilan baholang; hakamning o'z aniqligini ham o'lchang.
Best-of-K ni narx bilan birga baholang: K oshgan sari foyda kamayadi.
Real tizimlar haqida faqat tekshirilgan faktlarni keltiring; parametr va xarajat raqamlarini to'qimang.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # CLIP ning ikki encoderi va umumiy fazo nima uchun kerak?
2. # InfoNCE da B = 128 bo'lsa tasodifiy boshlanishdagi loss taxminan?
3. # nega bizning InfoNCE loss nolga tushmadi?
4. # zero-shot da yangi sinf qo'shish uchun nima kerak?
5. # 18 ta tavsifdan tasodifiy tanlashda aniqlik?
6. # "qizil kichik kvadrat" nega "qizil kichik doira" deb tanildi?
7. # R-precision ta'rifi (R = 40)?
8. # so'zlar xaltasi encoderi uchun "katta qizil doira" va "qizil katta doira" kosinusi?
9. # ko'rilmagan kombinatsiyada ID sharti nima beradi?
10. # cross-attention da Q, K, V qayerdan olinadi?
11. # best-of-8 ning narxi?
12. # nega tanlovchi CLIP bilan baholash noto'g'ri?Javoblar
- Matn va rasmni bevosita kosinus bilan solishtirish uchun — qidiruv, zero-shot, qayta saralash
log 128 ≈ 4.85(harorat sababli biroz yuqori bo'lishi mumkin: bizda5.1–5.6)- Batchda bir kombinatsiyadan ~9 juftlik — soxta manfiylar; pastki chegara ~`log 9`
- Faqat yangi matn tavsifi
1/18 ≈ 0.056- O'quvda qizil kichik shakl faqat doira edi — model yorliq (shortcut) o'rgangan
- Top-40 natijadagi to'g'ri natijalar ulushi
1.000— tartib ko'rinmaydi- Tasodifiy natija — embedding o'rgatilmagan (3-misol: "hammasi to'g'ri"
0.000) - Q — rasm xususiyatlaridan, K va V — matn tokenlaridan
- 8 barobar generatsiya + 8 ta CLIP baholash har so'rovga
- Tanlov aynan shu bahoni maksimallashtiradi — natija o'z-o'zidan yaxshi ko'rinadi; mustaqil hakam kerak
Vazifa 2: Xatolarni tuzating
1. zi, zt = rasm_enc(X), matn_enc(T)
loss = F.cross_entropy(zi @ zt.T, torch.arange(len(X)))
2. test_X, _, test_Y = toplam(korilgan, 40, seed=1)
print("kompozitsion aniqlik:", zero_shot(test_X, test_Y))
3. shart = nn.Embedding(18, 32)
rasm = dekoder(z, shart(torch.tensor([KOMBO.index(("ko'k", "katta", "uchburchak"))])))
4. nomzodlar = [generatsiya(sorov) for _ in range(8)]
eng = max(nomzodlar, key=lambda r: clip_ball(r, sorov))
print("sifat:", clip_ball(eng, sorov))
5. top = (matn_enc(["qizil emas doira"]) @ galereya.T).argsort(descending=True)[:10]
print("qizil bo'lmagan doiralar topildi")Javoblar
1. zi, zt = F.normalize(rasm_enc(X), dim=-1), F.normalize(matn_enc(T), dim=-1)
logit = log_harorat.exp().clamp(max=100) * zi @ zt.T
nishon = torch.arange(len(X))
loss = (F.cross_entropy(logit, nishon) + F.cross_entropy(logit.T, nishon)) / 2
2. test_X, _, test_Y = toplam(set(KOMBO), 40, seed=1) # hamma 18 kombinatsiya
yangi = torch.tensor([KOMBO[y] in KORILMAGAN for y in test_Y.tolist()])
print("ko'rilgan:", zero_shot(test_X[~yangi], test_Y[~yangi]),
"ko'rilmagan:", zero_shot(test_X[yangi], test_Y[yangi]))
3. c = matn_shart(["katta ko'k uchburchak"]) # kompozitsion shart
rasm = dekoder(z, c)
4. eng = max(nomzodlar, key=lambda r: clip_ball(r, sorov))
print("sifat:", atribut_hakam(eng, sorov)) # mustaqil hakam
5. # so'zlar xaltasi "emas" ni tushunmaydi: natijani tekshiring
rang = [KOMBO[i][0] for i in Y[top].tolist()]
print("qizil ulushi:", rang.count("qizil") / len(rang)) # kutilgan: yuqoriVazifa 3: CLIP
Modellang:
- Batch hajmi 32, 128, 256 — zero-shot aniqlik va loss chegarasi
- Qat'iy harorat (10, 30, 100) va o'rganiladigan harorat
- Soxta manfiylarni niqoblash: bir xil kombinatsiyali juftliklarni loss dan chiqarish
- Ko'rilmagan kombinatsiyalarni boshqacha tanlang (masalan, har rangda bitta) — natija qanday o'zgaradi?
Vazifa 4: Matn encoderi
Modellang:
- So'zlar xaltasi o'rniga kichik Transformer yoki GRU encoder
- "emas" bilan tavsiflar qo'shing ("qizil emas, ko'k doira") — inkor o'rganiladimi?
- Ikki shakl bitta rasmda: "chapda qizil doira, o'ngda ko'k kvadrat"
- Atributni bog'lash testi: "qizil doira va ko'k kvadrat" va "ko'k doira va qizil kvadrat"
Vazifa 5: Shartli generator
Modellang:
- Shartli VAE o'rniga shartli diffusion (26.6 dagi model, 768 o'lcham)
- Classifier-free guidance bilan matn sharti —
wbo'yicha atribut aniqligi - Dekoderni kichik CNN ga almashtiring — shakl aniqligi o'zgaradimi?
- Ko'rilmagan kombinatsiyalar soni 2, 4, 8 — umumlashma qanday o'zgaradi?
Vazifa 6: Baholash
Modellang:
- CLIP-score (o'rtacha kosinus) va atribut hakami — ular qanchalik mos?
- Best-of-K: K = 1, 2, 4, 8, 16 va narx egri chizig'i
- Generatsiya uchun FD 26.7-bob atribut hakamining xususiyatlarida
- Yodlab olish tekshiruvi: generatsiya o'quv rasmlariga qanchalik yaqin?
Vazifa 7: O'ylash
Marketing bo'limi aytdi: "Bizning matn-rasm generatorimiz sinovda 95% so'rovlarga to'g'ri rasm chizdi. Endi mijozlar istalgan mahsulot va rang kombinatsiyasini so'rashi mumkin, deb reklama qilamiz." Siz nima deysiz?
Javob
Qisqa javob: 95% — ehtimol o'quv ma'lumotiga o'xshash so'rovlardagi natija. "Istalgan kombinatsiya" degan va'da aynan ko'rilmagan kombinatsiyalarda o'lchanishi kerak — va bizning tajribalarda u yerda natija ancha past.
1. Nimada o'lchangan. Sinov so'rovlari qanday tanlangan? Agar ular katalogdagi mavjud mahsulot-rang kombinatsiyalari bo'lsa, bu 1-misoldagi "ko'rilgan" holatga o'xshaydi (0.974). Ko'rilmagan kombinatsiyalarda zero-shot 0.519, generatorda "hammasi to'g'ri" 0.304 edi.
2. Qaysi qism buziladi. Atributlar teng umumlashmaydi: bizda rang va o'lcham yaxshi, shakl yomon; ayniqsa o'quvda kuchli korrelyatsiya bo'lgan joyda ("qizil kichik → doira") model yorliqqa yopishadi. Real katalogda ham "bu mahsulot odatda shu rangda" kabi korrelyatsiyalar bor.
3. Kim baholagan. Agar "to'g'ri" ni generatsiyani tanlagan o'sha CLIP aytgan bo'lsa — bu o'z-o'zini tekshirish. Mustaqil hakam (atribut klassifikatorlari yoki inson baholashi, 26.7) kerak.
4. Matn tushunish. "Qora emas", "logosiz", "ikkita" kabi so'rovlar — ma'lum zaif joylar (2-misol: inkor butunlay e'tiborsiz qoldi).
Tavsiya:
# 1. sinovni ikkiga ajrating: katalogda BOR va YO'Q kombinatsiyalar
# 2. atribut bo'yicha aniqlik (rang, material, shakl) - mustaqil hakam
# 3. inkor, sanash, munosabat so'rovlari uchun alohida to'plam
# 4. inson A/B: 26.7 dagi quvvat hisobi bilanMarketingga javob: "95% — mavjud kombinatsiyalar uchun yaxshi natija. 'Istalgan kombinatsiya' deyishdan oldin katalogda yo'q kombinatsiyalarda alohida o'lchaymiz: kichik tajribalarimizda bunday so'rovlarda tanish aniqligi 0.97 dan 0.52 ga tushdi, ayniqsa shakl va inkor bo'yicha."
Nimani mustahkamlaydi: 2.3, 2.4, 2.6, 2.7, 2.8-bo'limlar.
Xulosa
Bu darsda matn-rasm tizimlarining arxitekturasini ko'rdik, CLIP ni noldan qurib zero-shot klassifikatsiya va qidiruvni o'lchadik, matn sharti bilan kichik generator o'rgatib, ko'rilmagan atribut kombinatsiyalarida uning to'g'riligini mustaqil hakam bilan tekshirdik.
Eng muhim uch fikr:
CLIP — ikki encoder va InfoNCE, lekin kompozitsion umumlashma qisman. 14 kombinatsiyada o'rgatilgan kichik CLIP ko'rilgan kombinatsiyalarda zero-shot
0.974, ko'rilmaganlarda0.519aniqlik berdi (tasodifiy0.056). Rang va o'lcham yangi kombinatsiyalarda deyarli mukammal (1.000,0.948), shakl esa0.540: o'quvda "qizil kichik" faqat doira bo'lgani uchun model shu yorliqni o'rgangan vaqizil kichik kvadratning 88% ini doira deb tanidi.Matn encoderi nimani ko'rmasa, butun tizim ham ko'rmaydi. Qidiruvda ko'rilmagan so'rovlar P@10
0.758(ko'rilganlarda0.955) berdi. So'zlar xaltasi encoderi so'z tartibini va inkorni umuman ko'rmaydi:qizil emas katta doiraso'rovi top-20 da faqat qizil rasmlarni qaytardi. Katta tizimlarda ham inkor, sanash va munosabatlar ma'lum zaif joylar.Generatsiyada shart kompozitsion bo'lishi kerak — va natija mustaqil hakam bilan baholanishi kerak. Ko'rilmagan kombinatsiyalarda so'zlar orqali berilgan matn sharti "hammasi to'g'ri"
0.304, kombinatsiya ID shart0.000berdi. CLIP bilan best-of-8 qayta saralash to'g'rilikni ko'rilganlarda0.333dan0.460ga, ko'rilmaganlarda0.285dan0.375ga oshirdi — lekin 8 barobar narxda, va ko'rilmaganlarda tanlovchining o'zi ham yomonroq ajratadi (AUC0.764va0.896).
Keyingi darsda Generativ AI etikasi va xavfsizligi: bu qismda qurgan generatorlar qanday suiiste'mol qilinishi mumkinligi, yodlab olish va maxfiylik, sintetik kontentni aniqlash, watermark va kelib chiqishni belgilash usullarini himoya nuqtai nazaridan ko'rib chiqamiz.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!