Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Semantik segmentatsiya — har piksel klassifikatsiyasi
- 2.2. To'liq konvolyutsion tarmoq (FCN)
- 2.3. U-Net: kodlovchi, dekoder va skip connection
- 2.4. Kattalashtirish: ConvTranspose2d va interpolatsiya
- 2.5. Metrikalar: piksel aniqligi, IoU va Dice
- 2.6. Nomutanosiblik: vaznli loss va Dice loss
- 2.7. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Piksel aniqligi, IoU va Dice ni qo'lda
- Misol 2 — Kattalashtirish va to'liq konvolyutsion tarmoq
- Misol 3 — Kichik U-Net: skip bilan va skipsiz
- Misol 4 — Nomutanosiblik: BCE, vaznli BCE va Dice loss
- 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
22.13-dars: Segmentatsiya
22-QISM — KOMPYUTER KO'RISH · 13-dars
1. Kirish va motivatsiya
22.12-darsda obyektni quti bilan belgiladik. Quti — qo'pol javob: doira shaklidagi dog' uchun uning to'rt burchagidagi fon ham "obyekt" bo'lib qoladi, qiyshiq tirnalish uchun esa quti deyarli butunlay fondan iborat bo'ladi. Ko'p vazifalarda obyektning aniq shakli kerak: o'smaning chegarasi (jarrohlik rejasi uchun), yo'lning qaysi piksellari bo'sh (avtopilot uchun), sun'iy yo'ldosh suratida qaysi maydon ekin, qaysi biri o'rmon.
Semantik segmentatsiya bu savolga eng to'g'ridan-to'g'ri javob beradi: rasmning har bir pikseliga sinf beradi. 32x32 rasm uchun bu bitta klassifikatsiya emas, 1024 ta klassifikatsiya. Chiqish endi (K,) vektor emas, (K, H, W) xarita — har piksel uchun sinf logitlari.
Buning uchun arxitekturani o'zgartirish kerak. Klassifikatsiya CNN i rasmni pooling bilan asta-sekin kichraytiradi va oxirida Linear bilan bitta javob beradi. Segmentatsiya tarmog'i esa kichraytirgandan keyin yana kattalashtirishi va asl o'lchamdagi niqob qaytarishi kerak. Bu kodlovchi–dekoder (encoder–decoder) tuzilmasiga olib keladi, uning eng mashhur vakili — U-Net.
Segmentatsiyada baholash ham o'z tuzog'iga ega. Odatda fon piksellari juda ko'p: nuqson rasmning 2-3 foizini egallaydi. "Hamma piksel fon" deb javob beradigan model 97% piksel aniqligi oladi va bitta ham nuqsonni topmaydi. Shuning uchun segmentatsiyada IoU va Dice asosiy metrika, loss esa ko'pincha nomutanosiblikka moslab tanlanadi.
Real vaziyat. Metall list sirtidagi korroziyani topish loyihasida birinchi model hisobotda "99.1% aniqlik" ko'rsatdi. Texnolog uni sinab ko'rib, model hech qachon korroziyani belgilamasligini aniqladi — korroziya piksellarning 0.9 foizi edi. Model "hammasi toza" deb o'rgangandi va aynan shu 99.1 foizni olgandi. Metrika IoU ga, loss esa Dice ga almashtirilgach, xuddi shu arxitektura korroziyaning ko'p qismini topa boshladi. Bu darsning 4-misoli xuddi shu hodisani kichik ma'lumotda qayta ko'rsatadi.
Bu darsda har pikselni klassifikatsiya qiladigan tarmoq quramiz va uni halol baholaymiz.
Bu darsda:
- Semantik segmentatsiya = har piksel klassifikatsiyasi
- To'liq konvolyutsion tarmoq (FCN)
- U-Net: kodlovchi, dekoder va skip connection
ConvTranspose2dva interpolatsiya bilan kattalashtirish- Piksel aniqligi, IoU va Dice
- Nomutanosiblik: vaznli loss va Dice loss
- Tuzoqlar
- Amaliy: kichik U-Net va nuqson niqoblari
ℹ Misollar real torch/numpy bilan (Python 3.14, torch 2.14 CPU).
2. Nazariya — chuqur tushuntirish
2.1. Semantik segmentatsiya — har piksel klassifikatsiyasi
KLASSIFIKATSIYA:
kirish (B, C, H, W)
chiqish (B, K) bitta rasmga K ta logit
yorliq (B,) butun son 0..K-1
loss cross_entropy(chiqish, yorliq)
SEMANTIK SEGMENTATSIYA:
kirish (B, C, H, W)
chiqish (B, K, H, W) HAR PIKSELGA K ta logit
niqob (B, H, W) har piksel - butun son 0..K-1 (long!)
loss cross_entropy(chiqish, niqob)
- PyTorch 2-o'lchamni sinf deb oladi va
barcha piksellar bo'yicha o'rtacha oladi
bashorat chiqish.argmax(1) -> (B, H, W)
IKKILIK HOLAT (obyekt / fon):
chiqish (B, 1, H, W) yoki (B, H, W) logit
niqob float 0/1
loss binary_cross_entropy_with_logits
SEMANTIK va INSTANCE:
semantik: ikki mushuk -> ikkalasi ham "mushuk" sinfi, bitta niqob
instance: mushuk-1 va mushuk-2 - alohida niqoblar (Mask R-CNN)
panoptik: ikkalasi birga (osmon, yo'l - semantik; mashinalar - instance)Formal jihatdan segmentatsiya — oddiy klassifikatsiya, faqat har piksel uchun. F.cross_entropy buni to'g'ridan-to'g'ri qo'llab-quvvatlaydi: (B, K, H, W) logit va (B, H, W) butun son niqob beriladi. Hech qanday view yoki flatten kerak emas.
Lekin piksellar mustaqil emas. Bitta pikselning sinfini uning o'zi emas, atrofi belgilaydi: kulrang piksel yo'lning ham, osmonning ham, mashinaning ham bir qismi bo'lishi mumkin. Tarmoq bir vaqtning o'zida ikki narsani bilishi kerak: keng kontekst ("bu hudud — yo'l") va aniq joylashuv ("chegara aynan shu pikseldan o'tadi"). Bu ikki talab bir-biriga zid: kontekst uchun rasmni kichraytirish (pooling) kerak, aniq joylashuv uchun esa to'liq o'lcham. U-Net aynan shu ziddiyatni hal qiladi 2.3-bob.
Segmentatsiya = har piksel uchun klassifikatsiya; chiqish (B, K, H, W), niqob (B, H, W) long.
2.2. To'liq konvolyutsion tarmoq (FCN)
ODDIY CNN:
conv -> pool -> conv -> pool -> flatten -> Linear -> (K,)
Linear kirish o'lchamini QOTIRADI -> faqat bitta rasm o'lchami
FCN (Long va boshq., 2015):
Linear O'RNIGA 1x1 conv:
1x1 conv = har pikselda alohida Linear (kanal bo'yicha)
-> istalgan H, W ni qabul qiladi
-> chiqish - xarita, vektor emas
conv -> pool -> conv -> pool -> 1x1 conv (K kanal) -> kattalashtirish
(1, 32, 32) ... (16, 8, 8) -> (K, 8, 8) -> (K, 32, 32)
O'LCHAM SHARTI:
2 marta pool (stride 2) -> jami qadam 4
H va W 4 ga BO'LINISHI kerak, aks holda chiqish qisqaradi
30 -> 15 -> 7 -> kattalashtirish 28 (30 emas!)
yechim: padding bilan 32 ga to'ldirish yoki F.interpolate(size=...)FCN ning g'oyasi oddiy: klassifikatsiya tarmog'idagi Linear qatlamni 1x1 konvolyutsiya bilan almashtirish. 1x1 konvolyutsiya har pikselda kanallar bo'yicha bir xil chiziqli akslantirishni qo'llaydi — ya'ni bu Linear, lekin har piksel uchun alohida va barcha piksellar uchun umumiy og'irlik bilan. Natijada tarmoq istalgan o'lchamdagi rasmni qabul qiladi va o'lchamga mos xarita qaytaradi.
2-misolda kichik FCN 32x32, 48x64 va 16x24 rasmlarni o'zgarishsiz qabul qildi. Lekin 30x30 rasmda chiqish 28x28 bo'lib qoldi — 30 soni umumiy qadam 4 ga bo'linmaydi. Bu xato jim o'tadi va niqob bilan loss hisoblashda shakl xatosi beradi yoki, undan yomoni, kesilgan niqob bilan noto'g'ri solishtiriladi.
FCN da Linear yo'q — shuning uchun istalgan o'lcham ishlaydi, lekin o'lcham kodlovchining umumiy qadamiga karrali bo'lishi kerak.
2.3. U-Net: kodlovchi, dekoder va skip connection
KODLOVCHI (kontekst) DEKODER (joylashuv)
(1,24,24) -> e1 --------------- skip ---------------> concat -> d1 -> 1x1 -> (K,24,24)
(k,24,24) | pool ^ up (x2)
v |
(k,12,12) -> e2 ----------- skip -----------> concat -> d2
(2k,12,12) | pool ^ up (x2)
v |
(2k,6,6) -> O'RTA (bottleneck) (4k,6,6) -----+
HAR BLOK: conv3x3 -> BN -> ReLU -> conv3x3 -> BN -> ReLU 22.7-bob
SKIP CONNECTION:
kodlovchining shu o'lchamdagi xaritasi dekoderga KANAL bo'yicha qo'shiladi
torch.cat([up(h), s], dim=1) -> kanal soni 2 barobar
(ResNet da skip - QO'SHISH, U-Net da - CONCAT)
NEGA KERAK:
pooling "qayerda" ma'lumotini yo'qotadi: 6x6 xaritada har katak 4x4 piksel
dekoder faqat 6x6 dan kattalashtirsa - chegaralar xira
skip esa 24x24 dagi aniq tafsilotni qaytaradi
kodlovchi: "bu - obyekt", skip: "chegarasi aynan shu yerda"U-Net (Ronneberger va boshq., 2015) tibbiy tasvirlar uchun yaratilgan va hozir ham segmentatsiyaning standart boshlang'ich nuqtasi. Uning chap yarmi — oddiy klassifikatsiya CNN i: har pog'onada o'lcham ikki marta kichrayadi, kanallar ikki marta ko'payadi. O'ng yarmi — ko'zgu aksi: o'lcham kattalashadi, kanallar kamayadi.
Skip connection bo'lmasa, dekoder faqat eng kichik (6x6) xaritadan ish boshlaydi. Undagi har katak asl rasmning 4x4 bo'lagini ifodalaydi — chegaraning aniq qaysi pikseldan o'tishi u yerda saqlanmagan. Dekoder uni taxmin qilishga majbur bo'ladi, natijada niqob chetlari yumaloqlashadi. Skip connection kodlovchining yuqori aniqlikdagi xaritasini dekoderga to'g'ridan-to'g'ri uzatadi.
3-misolda bir xil tuzilmali ikki modelni solishtirdik — farq faqat torch.cat da. Ikkala seedda U-Net mIoU ni oshirdi (0.837-0.847 dan 0.908-0.912 ga, farq 0.071 va 0.065), eng katta farq esa chegara piksellarida bo'ldi: chet aniqligi 0.793-0.816 dan 0.889-0.898 ga ko'tarildi. Piksel aniqligidagi farq esa atigi ~2 foiz punkt (0.955-0.960 ga qarshi 0.976-0.978). Parametrlar soni esa atigi ~11% ga ko'paydi (26779 dan 29659 ga).
Kodlovchi kontekstni, skip connection aniq joylashuvni beradi; U-Net ikkalasini birlashtiradi.
2.4. Kattalashtirish: ConvTranspose2d va interpolatsiya
INTERPOLATSIYA (o'rganilmaydi):
F.interpolate(x, scale_factor=2, mode="nearest") - har qiymat 2x2 ga
F.interpolate(x, scale_factor=2, mode="bilinear",
align_corners=False) - silliq o'tish
parametr yo'q; ko'pincha keyin conv3x3 qo'yiladi ("resize-conv")
TRANSPOZITSIYALANGAN KONVOLYUTSIYA (o'rganiladi):
nn.ConvTranspose2d(k_in, k_out, kernel, stride, padding)
chiqish = (H - 1) * stride - 2 * padding + kernel + output_padding
kernel=2, stride=2, padding=0 -> 2H (U-Net da odatiy)
kernel=4, stride=2, padding=1 -> 2H
kernel=3, stride=2, padding=1 -> 2H - 1 (!)
kernel=2, stride=2, og'irliklar 1 -> nearest bilan AYNAN teng
parametrlar: k_in * k_out * kernel^2 + k_out
SHAXMAT ARTEFAKTI (checkerboard):
kernel stride ga bo'linmasa (3 va 2) - chiqish piksellari
turli sonli kirishdan hissa oladi -> 1, 2, 1, 2 naqsh
yechim: kernel = stride ning karralisi (2/2, 4/2) yoki interpolatsiya + convKattalashtirishning ikki yo'li bor. Interpolatsiya — belgilangan qoida: nearest qiymatni takrorlaydi, bilinear qo'shni qiymatlar orasida chiziqli o'tish qiladi. Unda o'rganiladigan hech narsa yo'q, u tez va artefaktsiz.
ConvTranspose2d — o'rganiladigan kattalashtirish. Uni "teskari konvolyutsiya" deyishadi, lekin matematik jihatdan u konvolyutsiyaning teskarisi emas, balki uning gradient operatsiyasi: har kirish qiymati yadro bilan ko'paytirilib chiqishga "yoyiladi" va qoplangan joylar qo'shiladi. 2-misolda kernel=2, stride=2 va birlik og'irliklar bilan u nearest interpolatsiyaga aynan teng chiqdi — ya'ni nearest uning xususiy holati, o'rgatish esa yaxshiroq og'irlik topishga imkon beradi.
Yoyilgan bo'laklar qoplanmasa yoki notekis qoplansa, shaxmat artefakti paydo bo'ladi. 2-misolda kernel=3, stride=2 bir xil kirishda 1, 2, 1, 2 naqshli chiqish berdi, kernel=4, stride=2 esa tekis 4, 4, 4 chiqardi. Shuning uchun U-Net da kernel=2, stride=2 yoki "interpolatsiya + conv3x3" ishlatiladi.
Kernel stride ga karrali bo'lsin — aks holda ConvTranspose2d shaxmat naqshini chizadi.
2.5. Metrikalar: piksel aniqligi, IoU va Dice
Ikkilik niqob uchun (obyekt = 1):
TP - obyekt, obyekt deb topildi
FP - fon, obyekt deb belgilandi
FN - obyekt, fon deb qoldirildi
TN - fon, fon
PIKSEL ANIQLIGI = (TP + TN) / hammasi
- TN (fon) ko'p bo'lsa, u hukmronlik qiladi
IoU (Jaccard) = TP / (TP + FP + FN)
- TN QATNASHMAYDI -> fon ko'pligi yashirinmaydi
DICE (F1) = 2 TP / (2 TP + FP + FN)
- piksel darajasidagi F1 (18-qism)
BOG'LIQLIK:
Dice = 2 IoU / (1 + IoU) IoU = Dice / (2 - Dice)
Dice >= IoU har doim; tartib bir xil (monoton)
KO'P SINF:
har sinf uchun IoU (o'sha sinf = 1, qolgani = 0)
mIoU = sinflar bo'yicha o'rtacha (fon ham sinf!)
kichik sinfning past IoU si mIoU da ko'rinadi, piksel aniqligida - yo'q
BO'SH NIQOB: TP + FP + FN = 0 -> 0/0
kelishuv: 1 (to'g'ri "hech narsa yo'q") yoki eps bilan silliqlashUch metrika bir xil to'rtta sondan (TP, FP, FN, TN) hisoblanadi, lekin turli savolga javob beradi. Piksel aniqligi "piksellarning qancha qismi to'g'ri" deydi — va fon piksellari ko'p bo'lsa, bu savol ma'nosiz. IoU va Dice TN ni umuman ishlatmaydi: ular faqat obyekt atrofida nima bo'lganiga qaraydi.
1-misoldagi raqamlar farqni yaqqol ko'rsatadi. Obyekt 64x64 rasmning 2.8 foizini egallagan holda "hammasi fon" javobi 0.972 aniqlik oldi, IoU va Dice esa 0. Obyektni 3 pikselga siljitib chizgan bashorat deyarli bir xil aniqlik (0.975) oldi, IoU esa 0.378 — aniqlik ikki mutlaqo boshqa sifatdagi javobni ajrata olmadi.
Dice va IoU bir-biriga monoton bog'liq (Dice = 2 IoU / (1 + IoU)), shuning uchun modellarni tartiblashda bir xil natija beradi. Farq faqat shkalada: Dice doim kattaroq ko'rinadi (1-misolda IoU 0.286 bo'lganda Dice 0.444). Hisobotda qaysi biri ishlatilganini aniq yozing.
IoU va Dice TN ni ishlatmaydi — shuning uchun fon ko'pligi ularni aldamaydi, piksel aniqligini esa aldaydi.
2.6. Nomutanosiblik: vaznli loss va Dice loss
MUAMMO:
nuqson piksellari 2-4%, fon 96-98%
oddiy BCE / CE - har piksel TENG vaznda
-> "hammasi fon" yechimi loss ni tez tushiradi va shu yerda qotib qoladi
1. VAZNLI BCE:
F.binary_cross_entropy_with_logits(logit, y, pos_weight=w)
w ~ fon / obyekt (masalan 25)
ko'p sinf: F.cross_entropy(logit, y, weight=torch.tensor([...]))
+ recall keskin oshadi
- precision tushadi (obyekt ortiqcha "chiziladi"),
w juda katta bo'lsa chegara qalinlashadi
2. DICE LOSS (yumshoq Dice):
p = sigmoid(logit)
dice = (2 * sum(p * y) + eps) / (sum(p) + sum(y) + eps)
loss = 1 - dice
+ to'g'ridan-to'g'ri IoU/Dice ga o'xshash maqsadni optimallaydi
+ fon piksellarining SONI ahamiyatsiz
- kichik batchda shovqinli; bo'sh niqoblarda eps muhim
3. BIRGALIKDA: BCE + Dice (amalda eng keng tarqalgan)
FOCAL LOSS: oson (fon) piksellar vaznini kamaytiradi
BAHOLASH:
asosiy: IoU / Dice, sinf bo'yicha recall
piksel aniqligi - faqat qo'shimcha, bazaviy bilan birga
bazaviy: "hammasi fon" modeli - uning aniqligini doim yozingNomutanosiblik segmentatsiyada klassifikatsiyadagidan ham kuchliroq: rasmlarning o'zi muvozanatli bo'lsa ham (har rasmda nuqson bor), piksellar darajasida fon baribir ustun. Oddiy BCE har pikselga teng vazn beradi va "hammasi fon" deyish loss ni darhol past qiymatga tushiradi. Bu yechimdan chiqish uchun gradient signali juda zaif.
4-misolda aynan shu ro'y berdi: oddiy BCE bilan o'rgatilgan U-Net 6 davr davomida validatsiyada IoU 0.000 da qoldi va aniqligi (0.961) "hammasi fon" bazaviysi bilan bir xil chiqdi. Vaznli BCE (pos_weight = 25.6) recall ni 0.986 ga chiqardi, lekin precision 0.699 bo'ldi — model nuqsonni ortiqcha qalin chizdi. Dice loss esa birinchi davrdayoq IoU 0.529 berdi va 6-davrda IoU 0.861 ga yetdi, recall va precision muvozanatli bo'ldi (0.923 va 0.928).
Bu bitta seed va qisqa o'rgatish natijasi: ko'proq davr bilan oddiy BCE ham oxir-oqibat bu yechimdan chiqishi mumkin. Lekin xulosa o'zgarmaydi — nomutanosib niqobda loss tanlovi o'rgatish tezligi va natija sifatini keskin o'zgartiradi, piksel aniqligi esa buni ko'rsatmaydi (hamma variantlar 0.961-0.994 orasida).
Fon ko'p bo'lsa — Dice loss yoki vaznli loss, baholash esa IoU/Dice va recall bilan.
2.7. Tuzoqlar
Asosiy tuzoqlar: niqobni float (ko'p sinfda) yoki (B, 1, H, W) shaklda berish (cross_entropy (B, H, W) long kutadi); piksel aniqligini asosiy metrika qilish; "hammasi fon" bazaviysini yozmaslik; mIoU da fonni tashlab yuborib, buni aytmaslik; rasm o'lchamini kodlovchi qadamiga karrali qilmaslik; kernel stride ga bo'linmaydigan ConvTranspose2d (shaxmat artefakti); niqobni bilinear bilan o'lchamini o'zgartirish (sinf raqamlari aralashadi — faqat nearest); augmentatsiyada rasmni akslantirib, niqobni akslantirmaslik; bo'sh niqoblarda Dice ni eps siz hisoblash (0/0); vaznli loss da pos_weight ni juda katta qo'yib, chegaralarni qalinlashtirish; bitta rasmda metrikani o'rtachalash va butun to'plamda hisoblashni aralashtirish.
3. Tez ma'lumotnoma
import torch
import torch.nn as nn
import torch.nn.functional as F
# ko'p sinfli segmentatsiya
logit = model(x) # (B, K, H, W)
loss = F.cross_entropy(logit, niqob) # niqob (B, H, W) long
bashorat = logit.argmax(1) # (B, H, W)
# ikkilik + nomutanosiblik
loss = F.binary_cross_entropy_with_logits(logit, y, pos_weight=torch.tensor(40.0))
def dice_loss(logit, y, eps=1.0):
p = torch.sigmoid(logit)
kes = (p * y).sum((1, 2))
return (1 - (2 * kes + eps) / (p.sum((1, 2)) + y.sum((1, 2)) + eps)).mean()
# metrikalar
tp = ((p == 1) & (y == 1)).sum(); fp = ((p == 1) & (y == 0)).sum()
fn = ((p == 0) & (y == 1)).sum()
iou, dice = tp / (tp + fp + fn), 2 * tp / (2 * tp + fp + fn)
# kattalashtirish
nn.ConvTranspose2d(64, 32, kernel_size=2, stride=2) # 2x, o'rganiladi
F.interpolate(x, scale_factor=2, mode="bilinear", align_corners=False)
F.interpolate(niqob[:, None].float(), size=(H, W), mode="nearest") # niqob uchun
# U-Net skip
h = self.dekoder(torch.cat([self.up(h), skip], dim=1))Segmentatsiya xulosasi
segmentatsiya = har piksel klassifikatsiyasi: (B, K, H, W) vs (B, H, W) long
FCN: Linear o'rniga 1x1 conv -> istalgan o'lcham (qadamga karrali)
U-Net: kodlovchi (kontekst) + dekoder + skip concat (aniq chegara)
kattalashtirish: ConvTranspose2d(k=2, s=2) yoki interpolatsiya + conv
metrika: IoU = TP/(TP+FP+FN), Dice = 2TP/(2TP+FP+FN); aniqlik aldaydi
nomutanosiblik: Dice loss, vaznli BCE, BCE + Dice; bazaviy "hammasi fon"4. Batafsil misollar
Misollar real torch/numpy bilan (Python 3.14, torch 2.14 CPU).
Misol 1 — Piksel aniqligi, IoU va Dice ni qo'lda
"""Segmentatsiya metrikalari qo'lda: piksel aniqligi, IoU, Dice."""
import numpy as np
def metrikalar(bashorat, haqiqiy):
"""Ikkilik niqoblar (0/1) uchun asosiy metrikalar."""
tp = int(np.sum((bashorat == 1) & (haqiqiy == 1)))
fp = int(np.sum((bashorat == 1) & (haqiqiy == 0)))
fn = int(np.sum((bashorat == 0) & (haqiqiy == 1)))
tn = int(np.sum((bashorat == 0) & (haqiqiy == 0)))
aniqlik = (tp + tn) / (tp + tn + fp + fn)
iou = tp / (tp + fp + fn) if tp + fp + fn > 0 else 1.0
dice = 2 * tp / (2 * tp + fp + fn) if tp + fp + fn > 0 else 1.0
return {"tp": tp, "fp": fp, "fn": fn, "tn": tn,
"aniqlik": aniqlik, "iou": iou, "dice": dice}
def doira(r_markaz, c_markaz, radius, n=64):
yy, xx = np.mgrid[0:n, 0:n]
return ((yy - r_markaz) ** 2 + (xx - c_markaz) ** 2
<= radius ** 2).astype(np.int64)
def main() -> None:
print("=== 1. Kichik misol: 6x6 niqob ===")
haqiqiy = np.zeros((6, 6), dtype=np.int64)
haqiqiy[1:4, 1:4] = 1 # 9 piksel obyekt
bashorat = np.zeros((6, 6), dtype=np.int64)
bashorat[2:5, 2:5] = 1 # 1 pikselga siljigan
for nom, m in [("haqiqiy", haqiqiy), ("bashorat", bashorat)]:
print(f" {nom}:")
for q in m:
print(" " + " ".join("#" if v else "." for v in q))
m = metrikalar(bashorat, haqiqiy)
print(f" TP={m['tp']} FP={m['fp']} FN={m['fn']} TN={m['tn']}")
print(f" piksel aniqligi = (TP+TN)/hammasi = {m['aniqlik']:.3f}")
print(f" IoU = TP/(TP+FP+FN) = {m['tp']}/{m['tp'] + m['fp'] + m['fn']}"
f" = {m['iou']:.3f}")
print(f" Dice = 2TP/(2TP+FP+FN) = {2 * m['tp']}/"
f"{2 * m['tp'] + m['fp'] + m['fn']} = {m['dice']:.3f}")
print(f" tekshiruv: 2*IoU/(1+IoU) = {2 * m['iou'] / (1 + m['iou']):.3f}")
print("\n=== 2. Nomutanosiblik: 64x64, kichik obyekt ===")
haqiqiy = doira(20, 40, 6)
ulush = haqiqiy.mean()
print(f" obyekt piksellari: {haqiqiy.sum()} / {haqiqiy.size} "
f"({100 * ulush:.1f}%)")
variantlar = {
"hammasi fon": np.zeros_like(haqiqiy),
"yarim radius": doira(20, 40, 3),
"3 px siljigan": doira(23, 43, 6),
"deyarli to'g'ri": doira(20, 41, 6),
"hammasi obyekt": np.ones_like(haqiqiy),
}
print(f" {'bashorat':<17} {'aniqlik':>8} {'IoU':>6} {'Dice':>6}")
natijalar = {}
for nom, b in variantlar.items():
r = metrikalar(b, haqiqiy)
natijalar[nom] = r
print(f" {nom:<17} {r['aniqlik']:>8.3f} {r['iou']:>6.3f} "
f"{r['dice']:>6.3f}")
fon = natijalar["hammasi fon"]
if fon["aniqlik"] > 0.95 and fon["iou"] == 0:
print(f" 'hammasi fon' {fon['aniqlik']:.1%} aniqlik oldi, lekin "
"obyektning bitta pikselini ham topmadi")
print("\n=== 3. Ko'p sinfli: sinf bo'yicha IoU va mIoU ===")
rng = np.random.default_rng(0)
h = np.zeros((64, 64), dtype=np.int64)
h[doira(20, 20, 12) == 1] = 1 # katta obyekt
h[doira(48, 48, 4) == 1] = 2 # kichik obyekt
b = h.copy()
b[doira(48, 50, 4) == 1] = 2 # kichikni biroz sur
b[(h == 2) & (doira(48, 50, 4) == 0)] = 0
shovqin = rng.random(h.shape) < 0.02
b[shovqin & (h == 1)] = 0 # katta obyekt ichida
print(f" {'sinf':<8} {'piksel':>7} {'IoU':>6}")
ioular = []
for k, nom in enumerate(["fon", "katta", "kichik"]):
r = metrikalar((b == k).astype(int), (h == k).astype(int))
ioular.append(r["iou"])
print(f" {nom:<8} {int((h == k).sum()):>7} {r['iou']:>6.3f}")
print(f" mIoU (sinflar o'rtachasi) = {np.mean(ioular):.3f}")
print(f" piksel aniqligi = {(b == h).mean():.3f}")
print(" ⭐ Piksel aniqligi fon ko'pligini yashiradi; IoU/Dice yashirmaydi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Kichik misol: 6x6 niqob ===
haqiqiy:
. . . . . .
. # # # . .
. # # # . .
. # # # . .
. . . . . .
. . . . . .
bashorat:
. . . . . .
. . . . . .
. . # # # .
. . # # # .
. . # # # .
. . . . . .
TP=4 FP=5 FN=5 TN=22
piksel aniqligi = (TP+TN)/hammasi = 0.722
IoU = TP/(TP+FP+FN) = 4/14 = 0.286
Dice = 2TP/(2TP+FP+FN) = 8/18 = 0.444
tekshiruv: 2*IoU/(1+IoU) = 0.444
=== 2. Nomutanosiblik: 64x64, kichik obyekt ===
obyekt piksellari: 113 / 4096 (2.8%)
bashorat aniqlik IoU Dice
hammasi fon 0.972 0.000 0.000
yarim radius 0.979 0.257 0.408
3 px siljigan 0.975 0.378 0.549
deyarli to'g'ri 0.994 0.794 0.885
hammasi obyekt 0.028 0.028 0.054
'hammasi fon' 97.2% aniqlik oldi, lekin obyektning bitta pikselini ham topmadi
=== 3. Ko'p sinfli: sinf bo'yicha IoU va mIoU ===
sinf piksel IoU
fon 3606 0.988
katta 441 0.971
kichik 49 0.508
mIoU (sinflar o'rtachasi) = 0.822
piksel aniqligi = 0.989
⭐ Piksel aniqligi fon ko'pligini yashiradi; IoU/Dice yashirmaydiNima ko'rsatdi: 2.5, 2.6-bo'limlar.
Misol 2 — Kattalashtirish va to'liq konvolyutsion tarmoq
"""Kattalashtirish: ConvTranspose2d, interpolatsiya va to'liq konvolyutsion tarmoq."""
import torch
import torch.nn as nn
import torch.nn.functional as F
def jadval(t):
return "\n".join(" " + " ".join(f"{v:5.2f}" for v in q)
for q in t.tolist())
class KichikFCN(nn.Module):
"""To'liq konvolyutsion: Linear yo'q -> istalgan o'lchamdagi rasm."""
def __init__(self, sinflar=3):
super().__init__()
self.enc = nn.Sequential(
nn.Conv2d(1, 8, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
nn.Conv2d(8, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2))
self.bosh = nn.Conv2d(16, sinflar, 1) # 1x1 conv = piksel
self.up = nn.ConvTranspose2d(sinflar, sinflar, 4, stride=4)
def forward(self, x):
return self.up(self.bosh(self.enc(x)))
def main() -> None:
torch.manual_seed(0)
x = torch.tensor([[1.0, 2.0], [3.0, 4.0]]).view(1, 1, 2, 2)
print("=== 1. Interpolatsiya: 2x2 -> 4x4 ===")
print(" nearest:")
print(jadval(F.interpolate(x, scale_factor=2, mode="nearest")[0, 0]))
print(" bilinear (align_corners=False):")
print(jadval(F.interpolate(x, scale_factor=2, mode="bilinear",
align_corners=False)[0, 0]))
print(" interpolatsiyada o'rganiladigan parametr yo'q")
print("\n=== 2. ConvTranspose2d chiqish o'lchami ===")
print(" formula: (H - 1) * stride - 2 * padding + kernel "
"+ output_padding")
print(f" {'kernel':>6} {'stride':>6} {'padding':>7} {'8x8 ->':>8} "
f"{'formula':>8}")
for k, s, p in [(2, 2, 0), (4, 2, 1), (3, 2, 1), (3, 1, 1), (4, 4, 0)]:
ct = nn.ConvTranspose2d(1, 1, k, stride=s, padding=p)
h = ct(torch.zeros(1, 1, 8, 8)).shape[-1]
f = (8 - 1) * s - 2 * p + k
print(f" {k:>6} {s:>6} {p:>7} {h:>8} {f:>8}")
print("\n=== 3. kernel=2, stride=2, og'irlik 1 -> nearest bilan bir xil ===")
ct = nn.ConvTranspose2d(1, 1, 2, stride=2, bias=False)
with torch.no_grad():
ct.weight.fill_(1.0)
y = ct(x)
print(jadval(y[0, 0].detach()))
teng = torch.equal(y.detach(), F.interpolate(x, scale_factor=2))
print(f" nearest bilan teng: {teng}")
print(f" o'rganiladigan parametrlar (1->1 kanal): "
f"{sum(p.numel() for p in ct.parameters())}")
ct64 = nn.ConvTranspose2d(64, 32, 2, stride=2)
print(f" 64->32 kanal, kernel 2: "
f"{sum(p.numel() for p in ct64.parameters())} parametr")
print("\n=== 4. Shaxmat artefakti: kernel stride ga bo'linmasa ===")
for k, s, p in [(3, 2, 1), (4, 2, 1)]:
ct = nn.ConvTranspose2d(1, 1, k, stride=s, padding=p, bias=False)
with torch.no_grad():
ct.weight.fill_(1.0)
q = ct(torch.ones(1, 1, 6, 6))[0, 0, 4, 2:9].detach()
print(f" kernel={k}, stride={s}: markaziy qator "
f"{[round(v, 1) for v in q.tolist()]}")
print(" bir xil kirish -> notekis chiqish (3/2 da 1,2,1,2 naqsh)")
print("\n=== 5. FCN istalgan o'lchamni qabul qiladi ===")
fcn = KichikFCN()
for h, w in [(32, 32), (48, 64), (16, 24)]:
chiqish = fcn(torch.randn(2, 1, h, w))
print(f" kirish (2, 1, {h}, {w}) -> chiqish {tuple(chiqish.shape)}")
print(f" parametrlar: {sum(p.numel() for p in fcn.parameters())}")
ch = fcn(torch.randn(1, 1, 30, 30)).shape
print(f" kirish (1, 1, 30, 30) -> chiqish {tuple(ch)}")
if ch[-1] != 30:
print(" 30 soni 4 ga bo'linmaydi -> niqob kirishdan KICHIK chiqdi")
print(" ⭐ O'lchamni kodlovchining umumiy qadamiga karrali qiling")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Interpolatsiya: 2x2 -> 4x4 ===
nearest:
1.00 1.00 2.00 2.00
1.00 1.00 2.00 2.00
3.00 3.00 4.00 4.00
3.00 3.00 4.00 4.00
bilinear (align_corners=False):
1.00 1.25 1.75 2.00
1.50 1.75 2.25 2.50
2.50 2.75 3.25 3.50
3.00 3.25 3.75 4.00
interpolatsiyada o'rganiladigan parametr yo'q
=== 2. ConvTranspose2d chiqish o'lchami ===
formula: (H - 1) * stride - 2 * padding + kernel + output_padding
kernel stride padding 8x8 -> formula
2 2 0 16 16
4 2 1 16 16
3 2 1 15 15
3 1 1 8 8
4 4 0 32 32
=== 3. kernel=2, stride=2, og'irlik 1 -> nearest bilan bir xil ===
1.00 1.00 2.00 2.00
1.00 1.00 2.00 2.00
3.00 3.00 4.00 4.00
3.00 3.00 4.00 4.00
nearest bilan teng: True
o'rganiladigan parametrlar (1->1 kanal): 4
64->32 kanal, kernel 2: 8224 parametr
=== 4. Shaxmat artefakti: kernel stride ga bo'linmasa ===
kernel=3, stride=2: markaziy qator [1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0]
kernel=4, stride=2: markaziy qator [4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0]
bir xil kirish -> notekis chiqish (3/2 da 1,2,1,2 naqsh)
=== 5. FCN istalgan o'lchamni qabul qiladi ===
kirish (2, 1, 32, 32) -> chiqish (2, 3, 32, 32)
kirish (2, 1, 48, 64) -> chiqish (2, 3, 48, 64)
kirish (2, 1, 16, 24) -> chiqish (2, 3, 16, 24)
parametrlar: 1446
kirish (1, 1, 30, 30) -> chiqish (1, 3, 28, 28)
30 soni 4 ga bo'linmaydi -> niqob kirishdan KICHIK chiqdi
⭐ O'lchamni kodlovchining umumiy qadamiga karrali qilingNima ko'rsatdi: 2.2, 2.4-bo'limlar.
Misol 3 — Kichik U-Net: skip bilan va skipsiz
"""Kichik U-Net: skip connection bilan va skipsiz taqqoslash."""
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
R = 24
SINFLAR = ["fon", "yorug'", "qorong'i"]
def sahnalar(n, seed):
"""Har rasmda 2-4 obyekt (doira yoki kvadrat shaklida, yorug' yoki
qorong'i) + shovqin; niqob (H, W): 0 fon, 1 yorug', 2 qorong'i."""
rng = np.random.default_rng(seed)
yy, xx = np.mgrid[0:R, 0:R]
X = rng.normal(0.0, 0.5, (n, 1, R, R)).astype(np.float32)
Y = np.zeros((n, R, R), dtype=np.int64)
for i in range(n):
for _ in range(rng.integers(2, 5)):
s = int(rng.integers(0, 2)) + 1
r = rng.uniform(2.0, 5.0)
cy, cx = rng.uniform(r, R - r, 2)
if rng.random() < 0.5:
m = (yy - cy) ** 2 + (xx - cx) ** 2 <= r ** 2
else:
m = ((np.abs(yy - cy) <= r * 0.85)
& (np.abs(xx - cx) <= r * 0.85))
Y[i][m] = s
X[i, 0] += (Y[i] == 1) * 1.0 - (Y[i] == 2) * 1.0
return torch.tensor(X), torch.tensor(Y)
def blok(k_in, k_out):
return nn.Sequential(
nn.Conv2d(k_in, k_out, 3, padding=1), nn.BatchNorm2d(k_out),
nn.ReLU(),
nn.Conv2d(k_out, k_out, 3, padding=1), nn.BatchNorm2d(k_out),
nn.ReLU())
class KichikUNet(nn.Module):
"""Kodlovchi 24 -> 12 -> 6, dekoder 6 -> 12 -> 24; skip - concat."""
def __init__(self, skip=True, k=8, sinflar=3):
super().__init__()
self.skip = skip
self.e1, self.e2 = blok(1, k), blok(k, 2 * k)
self.orta = blok(2 * k, 4 * k)
self.up2 = nn.ConvTranspose2d(4 * k, 2 * k, 2, stride=2)
self.d2 = blok(4 * k if skip else 2 * k, 2 * k)
self.up1 = nn.ConvTranspose2d(2 * k, k, 2, stride=2)
self.d1 = blok(2 * k if skip else k, k)
self.bosh = nn.Conv2d(k, sinflar, 1)
def forward(self, x):
s1 = self.e1(x) # (k, 24, 24)
s2 = self.e2(F.max_pool2d(s1, 2)) # (2k, 12, 12)
h = self.orta(F.max_pool2d(s2, 2)) # (4k, 6, 6)
h = self.up2(h)
h = self.d2(torch.cat([h, s2], 1) if self.skip else h)
h = self.up1(h)
h = self.d1(torch.cat([h, s1], 1) if self.skip else h)
return self.bosh(h) # (sinflar, 24, 24)
def orgat(model, X, Y, davrlar, seed):
g = torch.Generator().manual_seed(seed)
opt = torch.optim.Adam(model.parameters(), lr=3e-3)
for _ in range(davrlar):
model.train()
tartib = torch.randperm(len(Y), generator=g)
for i in range(0, len(Y), 32):
b = tartib[i:i + 32]
loss = F.cross_entropy(model(X[b]), Y[b])
opt.zero_grad()
loss.backward()
opt.step()
return model
def baho(model, X, Y):
model.eval()
with torch.no_grad():
p = model(X).argmax(1)
ioular = []
for k in range(len(SINFLAR)):
kes = ((p == k) & (Y == k)).sum().item()
bir = ((p == k) | (Y == k)).sum().item()
ioular.append(kes / bir)
# chegara piksellari: 3x3 qo'shnisida boshqa sinf bor
chet = (F.max_pool2d(Y[:, None].float(), 3, 1, 1)
!= -F.max_pool2d(-Y[:, None].float(), 3, 1, 1))[:, 0]
return {"aniqlik": (p == Y).float().mean().item(),
"iou": ioular, "miou": float(np.mean(ioular)),
"chet_aniqlik": (p == Y)[chet].float().mean().item()}
def main() -> None:
X_tr, Y_tr = sahnalar(600, seed=1)
X_te, Y_te = sahnalar(300, seed=2)
print("=== 1. Ma'lumot ===")
print(f" rasmlar {tuple(X_tr.shape)}, niqoblar {tuple(Y_tr.shape)}")
ulush = torch.bincount(Y_tr.flatten(), minlength=3).float()
ulush = ulush / ulush.sum()
print(f" piksel ulushlari: "
f"{dict(zip(SINFLAR, [round(v, 3) for v in ulush.tolist()]))}")
print("\n=== 2. Ikki model ===")
for skip in [True, False]:
m = KichikUNet(skip=skip)
print(f" skip={str(skip):<5} parametrlar "
f"{sum(p.numel() for p in m.parameters())}")
print("\n=== 3. O'rgatish va test (2 seed) ===")
print(f" {'model':<10} {'seed':>4} {'aniqlik':>8} {'mIoU':>6} "
f"{'yorug':>6} {'qorongi':>8} {'chet':>6}")
natija = {True: [], False: []}
for seed in [0, 1]:
for skip in [True, False]:
torch.manual_seed(seed)
m = orgat(KichikUNet(skip=skip), X_tr, Y_tr, 6, seed)
r = baho(m, X_te, Y_te)
natija[skip].append(r)
nom = "U-Net" if skip else "skipsiz"
print(f" {nom:<10} {seed:>4} {r['aniqlik']:>8.3f} "
f"{r['miou']:>6.3f} {r['iou'][1]:>6.3f} "
f"{r['iou'][2]:>8.3f} {r['chet_aniqlik']:>6.3f}")
print("\n=== 4. Xulosa ===")
farq_miou = [a["miou"] - b["miou"]
for a, b in zip(natija[True], natija[False])]
farq_chet = [a["chet_aniqlik"] - b["chet_aniqlik"]
for a, b in zip(natija[True], natija[False])]
print(f" mIoU farqi (U-Net - skipsiz) seedlar bo'yicha: "
f"{[round(v, 3) for v in farq_miou]}")
print(f" chet aniqligi farqi: {[round(v, 3) for v in farq_chet]}")
if min(farq_miou) > 0:
print(" har ikki seedda skip connection mIoU ni oshirdi")
else:
print(" skip connection foydasi seedlarda izchil emas")
print(" ⭐ Skip dekoderga pooling da yo'qolgan chegara tafsilotini "
"qaytaradi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumot ===
rasmlar (600, 1, 24, 24), niqoblar (600, 24, 24)
piksel ulushlari: {'fon': 0.816, "yorug'": 0.087, "qorong'i": 0.096}
=== 2. Ikki model ===
skip=True parametrlar 29659
skip=False parametrlar 26779
=== 3. O'rgatish va test (2 seed) ===
model seed aniqlik mIoU yorug qorongi chet
U-Net 0 0.976 0.908 0.863 0.890 0.889
skipsiz 0 0.955 0.837 0.792 0.772 0.793
U-Net 1 0.978 0.912 0.875 0.886 0.898
skipsiz 1 0.960 0.847 0.795 0.791 0.816
=== 4. Xulosa ===
mIoU farqi (U-Net - skipsiz) seedlar bo'yicha: [0.071, 0.065]
chet aniqligi farqi: [0.096, 0.082]
har ikki seedda skip connection mIoU ni oshirdi
⭐ Skip dekoderga pooling da yo'qolgan chegara tafsilotini qaytaradiNima ko'rsatdi: 2.1, 2.3-bo'limlar.
Misol 4 — Nomutanosiblik: BCE, vaznli BCE va Dice loss
"""Nomutanosib segmentatsiya: BCE, vaznli BCE va Dice loss taqqoslash."""
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
R = 24
def rasmlar(n, seed):
"""Teksturali fon, ustida mayda dog'lar va ingichka tirnalishlar."""
rng = np.random.default_rng(seed)
yy, xx = np.mgrid[0:R, 0:R]
X = rng.normal(0.0, 0.5, (n, 1, R, R)).astype(np.float32)
Y = np.zeros((n, R, R), dtype=np.float32)
for i in range(n):
for _ in range(rng.integers(1, 3)): # dog'lar
r = rng.uniform(1.0, 2.2)
cy, cx = rng.uniform(3, R - 3, 2)
Y[i][(yy - cy) ** 2 + (xx - cx) ** 2 <= r ** 2] = 1
if rng.random() < 0.6: # tirnalish
y0, x0 = rng.uniform(4, R - 4, 2)
burchak = rng.uniform(0, np.pi)
for t in np.linspace(-7, 7, 36):
py = int(round(y0 + t * np.sin(burchak)))
px = int(round(x0 + t * np.cos(burchak)))
if 0 <= py < R and 0 <= px < R:
Y[i, py, px] = 1
X[i, 0] += Y[i] * 1.5
return torch.tensor(X), torch.tensor(Y)
def blok(k_in, k_out):
return nn.Sequential(
nn.Conv2d(k_in, k_out, 3, padding=1), nn.BatchNorm2d(k_out),
nn.ReLU(),
nn.Conv2d(k_out, k_out, 3, padding=1), nn.BatchNorm2d(k_out),
nn.ReLU())
class KichikUNet(nn.Module):
def __init__(self, k=8):
super().__init__()
self.e1, self.e2 = blok(1, k), blok(k, 2 * k)
self.orta = blok(2 * k, 4 * k)
self.up2 = nn.ConvTranspose2d(4 * k, 2 * k, 2, stride=2)
self.d2 = blok(4 * k, 2 * k)
self.up1 = nn.ConvTranspose2d(2 * k, k, 2, stride=2)
self.d1 = blok(2 * k, k)
self.bosh = nn.Conv2d(k, 1, 1)
def forward(self, x):
s1 = self.e1(x)
s2 = self.e2(F.max_pool2d(s1, 2))
h = self.orta(F.max_pool2d(s2, 2))
h = self.d2(torch.cat([self.up2(h), s2], 1))
h = self.d1(torch.cat([self.up1(h), s1], 1))
return self.bosh(h)[:, 0] # logit (B, H, W)
def dice_loss(logit, y, eps=1.0):
p = torch.sigmoid(logit)
kes = (p * y).sum((1, 2))
maxraj = p.sum((1, 2)) + y.sum((1, 2))
return (1 - (2 * kes + eps) / (maxraj + eps)).mean()
def orgat(X, Y, X_va, Y_va, loss_turi, pos_weight, seed=0, davrlar=6):
"""Har davr oxirida validatsiyadagi metrikalarni yozib boradi."""
torch.manual_seed(seed)
model = KichikUNet()
g = torch.Generator().manual_seed(seed)
opt = torch.optim.Adam(model.parameters(), lr=3e-3)
pw = torch.tensor(pos_weight)
tarix = []
for _ in range(davrlar):
model.train()
tartib = torch.randperm(len(Y), generator=g)
for i in range(0, len(Y), 16):
b = tartib[i:i + 16]
logit = model(X[b])
if loss_turi == "BCE":
loss = F.binary_cross_entropy_with_logits(logit, Y[b])
elif loss_turi == "vaznli BCE":
loss = F.binary_cross_entropy_with_logits(
logit, Y[b], pos_weight=pw)
else:
loss = dice_loss(logit, Y[b])
opt.zero_grad()
loss.backward()
opt.step()
model.eval()
with torch.no_grad():
p = (torch.sigmoid(model(X_va)) > 0.5).float()
tarix.append(metrikalar(p, Y_va))
return tarix
def metrikalar(p, y):
tp = ((p == 1) & (y == 1)).sum().item()
fp = ((p == 1) & (y == 0)).sum().item()
fn = ((p == 0) & (y == 1)).sum().item()
return {"aniqlik": (p == y).float().mean().item(),
"iou": tp / max(tp + fp + fn, 1),
"dice": 2 * tp / max(2 * tp + fp + fn, 1),
"recall": tp / max(tp + fn, 1),
"precision": tp / max(tp + fp, 1)}
def main() -> None:
X_tr, Y_tr = rasmlar(400, seed=1)
X_va, Y_va = rasmlar(200, seed=2)
ulush = Y_tr.mean().item()
print("=== 1. Ma'lumot ===")
print(f" nuqson piksellari ulushi: {ulush:.3f} "
f"(fon {1 - ulush:.3f})")
pos_w = (1 - ulush) / ulush
print(f" pos_weight = fon / nuqson = {pos_w:.1f}")
print("\n=== 2. Bazaviy: hammasini fon deymiz ===")
r0 = metrikalar(torch.zeros_like(Y_va), Y_va)
print(f" aniqlik {r0['aniqlik']:.3f}, IoU {r0['iou']:.3f}, "
f"Dice {r0['dice']:.3f}, recall {r0['recall']:.3f}")
print("\n=== 3. Davrlar bo'yicha validatsiya IoU si ===")
tarixlar = {t: orgat(X_tr, Y_tr, X_va, Y_va, t, pos_w)
for t in ["BCE", "vaznli BCE", "Dice"]}
print(f" {'loss':<11}" + "".join(f"{d:>7}" for d in range(1, 7)))
for t, tarix in tarixlar.items():
print(f" {t:<11}" + "".join(f"{r['iou']:>7.3f}" for r in tarix))
print("\n=== 4. Oxirgi davr: to'liq metrikalar ===")
print(f" {'loss':<12} {'aniqlik':>8} {'IoU':>6} {'Dice':>6} "
f"{'recall':>7} {'precision':>10}")
natija = {"hammasi fon": r0}
natija.update({t: tarix[-1] for t, tarix in tarixlar.items()})
for t, r in natija.items():
print(f" {t:<12} {r['aniqlik']:>8.3f} {r['iou']:>6.3f} "
f"{r['dice']:>6.3f} {r['recall']:>7.3f} "
f"{r['precision']:>10.3f}")
print("\n=== 5. Xulosa (natijadan) ===")
aniq = [v["aniqlik"] for v in natija.values()]
print(f" aniqlik diapazoni: {min(aniq):.3f} - {max(aniq):.3f} "
f"(farq {max(aniq) - min(aniq):.3f})")
iou = {k: v["iou"] for k, v in natija.items()}
print(f" IoU diapazoni: {min(iou.values()):.3f} - "
f"{max(iou.values()):.3f}")
print(f" IoU bo'yicha eng yaxshi: {max(iou, key=iou.get)}")
if natija["BCE"]["iou"] == 0 and natija["BCE"]["aniqlik"] >= r0["aniqlik"]:
print(" oddiy BCE 'hammasi fon' yechimida qolib ketdi - "
"aniqligi bazaviy bilan bir xil")
vb = natija["vaznli BCE"]
if vb["recall"] > vb["precision"] + 0.2:
print(f" vaznli BCE: recall {vb['recall']:.3f}, precision "
f"{vb['precision']:.3f} - vazn nuqsonni ortiqcha chizdiradi")
print(" ⭐ Nomutanosib niqobda IoU/Dice va recall ga qarang")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumot ===
nuqson piksellari ulushi: 0.038 (fon 0.962)
pos_weight = fon / nuqson = 25.6
=== 2. Bazaviy: hammasini fon deymiz ===
aniqlik 0.961, IoU 0.000, Dice 0.000, recall 0.000
=== 3. Davrlar bo'yicha validatsiya IoU si ===
loss 1 2 3 4 5 6
BCE 0.000 0.000 0.000 0.000 0.000 0.000
vaznli BCE 0.345 0.419 0.567 0.588 0.630 0.692
Dice 0.529 0.623 0.783 0.836 0.844 0.861
=== 4. Oxirgi davr: to'liq metrikalar ===
loss aniqlik IoU Dice recall precision
hammasi fon 0.961 0.000 0.000 0.000 0.000
BCE 0.961 0.000 0.000 0.000 0.000
vaznli BCE 0.983 0.692 0.818 0.986 0.699
Dice 0.994 0.861 0.926 0.923 0.928
=== 5. Xulosa (natijadan) ===
aniqlik diapazoni: 0.961 - 0.994 (farq 0.033)
IoU diapazoni: 0.000 - 0.861
IoU bo'yicha eng yaxshi: Dice
oddiy BCE 'hammasi fon' yechimida qolib ketdi - aniqligi bazaviy bilan bir xil
vaznli BCE: recall 0.986, precision 0.699 - vazn nuqsonni ortiqcha chizdiradi
⭐ Nomutanosib niqobda IoU/Dice va recall ga qarangNima ko'rsatdi: 2.6-bo'lim.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "97% piksel aniqligi — yaxshi model" | "Hammasi fon" ham shuncha oladi |
| "Segmentatsiya uchun maxsus loss kerak" | Oddiy cross_entropy (B, K, H, W) ni qabul qiladi |
| "Dice va IoU turli modelni tanlaydi" | Monoton bog'liq, tartib bir xil |
| "Skip connection — faqat chuqur tarmoq uchun" | Chegaralar aniqligini beradi |
"ConvTranspose2d — konvolyutsiyaning teskarisi" |
Gradient operatsiyasi, yoyish va qo'shish |
| "Har qanday kernel/stride ishlaydi" | Bo'linmasa — shaxmat artefakti |
"Niqobni ham bilinear bilan kichraytirish mumkin" |
Faqat nearest |
| "Vaznli loss — hamma muammoning yechimi" | Recall oshadi, precision tushadi |
6. Keng tarqalgan xatolar va yechimlari
1. Niqob shakli va turi
F.cross_entropy(logit, niqob[:, None].float()) # ⚠️ (B, 1, H, W) float
F.cross_entropy(logit, niqob.long()) # ✅ (B, H, W) long2. Piksel aniqligi bilan hisobot
print((p == y).float().mean()) # ⚠️ 0.961 = hammasi fon
print(iou, dice, recall, bazaviy_aniqlik) # ✅3. Niqobni bilinear bilan o'lchash
F.interpolate(niqob.float(), size=s, mode="bilinear") # ⚠️ 0.5 sinf yo'q
F.interpolate(niqob.float(), size=s, mode="nearest") # ✅4. Shaxmat artefakti
nn.ConvTranspose2d(c, c, kernel_size=3, stride=2) # ⚠️ 1,2,1,2 naqsh
nn.ConvTranspose2d(c, c, kernel_size=2, stride=2) # ✅5. Qadamga karrali bo'lmagan o'lcham
model(torch.randn(1, 1, 30, 30)) # ⚠️ chiqish 28x28
model(F.pad(x, (1, 1, 1, 1))) # ✅ 32x326. Augmentatsiyada niqob unutilgan
x = x.flip(-1) # ⚠️ niqob eski holatda
x, niqob = x.flip(-1), niqob.flip(-1) # ✅ birga7. Dice da eps yo'q
2 * kes / (p.sum() + y.sum()) # ⚠️ bo'sh niqobda 0/0
(2 * kes + 1) / (p.sum() + y.sum() + 1) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 18-qism (o'tilgan): Precision, recall, F1 — Dice aslida piksel darajasidagi F1
- 22.3-22.4-darslar (o'tilgan): Konvolyutsiya va pooling o'lcham formulalari
- 22.6-22.7-darslar (o'tilgan): Skip connection (ResNet) va BatchNorm2d — U-Net bloklari
- 22.12-dars (o'tilgan): IoU — endi qutilar emas, piksellar to'plami uchun
- 22.14-dars: Nuqson loyihasi — nomutanosiblik va sinf bo'yicha recall
- Generativ AI qismida: Diffuziya modellarining ichida ham U-Net ishlaydi
- MLOps qismida: Segmentatsiya modelini ishlab chiqarishda kuzatish
8. Eng yaxshi amaliyotlar
Har doim "hammasi fon" bazaviysining aniqligini yozing.
Asosiy metrika — IoU yoki Dice, sinf bo'yicha va o'rtacha.
Fon ko'p bo'lsa, Dice loss yoki BCE + Dice bilan boshlang.
Rasm o'lchamini kodlovchi qadamiga karrali qiling.
ConvTranspose2dda kernel = stride (yoki interpolatsiya + conv).Niqobni faqat
nearestbilan o'lchang va rasm bilan birga augmentatsiya qiling.Chegara piksellaridagi sifatni alohida tekshiring — skip connection foydasi aynan shu yerda.
Bashorat niqoblarini rasm ustiga chizib ko'ring — raqam yashirgan xatolar ko'rinadi.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # segmentatsiya chiqishi shakli?
2. # cross_entropy uchun niqob shakli va turi?
3. # 1x1 conv nimaga teng?
4. # FCN nega istalgan o'lchamni qabul qiladi?
5. # 2 ta pool dan keyin 30x30 kirish -> chiqish?
6. # ConvTranspose2d(k=2, s=2) 8x8 -> ?
7. # ConvTranspose2d(k=3, s=2, p=1) 8x8 -> ?
8. # U-Net skip - qo'shishmi yoki concat?
9. # IoU = 0.5 bo'lsa Dice = ?
10. # 2.8% obyektda "hammasi fon" aniqligi?
11. # pos_weight qanday tanlanadi?
12. # niqobni qaysi interpolatsiya bilan o'lchash kerak?Javoblar
(B, K, H, W)(B, H, W),long- Har pikselda alohida
Linear(kanallar bo'yicha) Linearyo'q — barcha qatlamlar lokal28x28(30 soni 4 ga bo'linmaydi)16x1615x15- Concat (kanal bo'yicha)
2 * 0.5 / 1.5 = 0.6670.972- Taxminan
fon / obyekt, keyin validatsiyada sozlanadi nearest
Vazifa 2: Xatolarni tuzating
1. loss = F.cross_entropy(logit, niqob.float()[:, None])
2. niqob_kichik = F.interpolate(niqob, scale_factor=0.5, mode="bilinear")
3. self.up = nn.ConvTranspose2d(64, 32, kernel_size=3, stride=2)
4. dice = 2 * (p * y).sum() / (p.sum() + y.sum())
5. print(f"aniqlik {(p == y).float().mean():.3f}") # yagona metrikaJavoblar
1. loss = F.cross_entropy(logit, niqob.long())
2. niqob_kichik = F.interpolate(niqob[:, None].float(), scale_factor=0.5,
mode="nearest")[:, 0].long()
3. self.up = nn.ConvTranspose2d(64, 32, kernel_size=2, stride=2)
4. dice = (2 * (p * y).sum() + 1) / (p.sum() + y.sum() + 1)
5. print(f"IoU {iou:.3f}, Dice {dice:.3f}, recall {rec:.3f}, "
f"bazaviy aniqlik {1 - y.float().mean():.3f}")Vazifa 3: Metrikalar
Modellang:
- TP, FP, FN, TN dan uchta metrika
Dice = 2 IoU / (1 + IoU)ni tekshiring- Obyekt ulushi 1%, 5%, 20% da "hammasi fon" aniqligi
- Ko'p sinf uchun sinf bo'yicha IoU va mIoU
Vazifa 4: Kattalashtirish
Modellang:
nearestvabilinearjadvaliConvTranspose2do'lcham formulasi — 5 xil kernel/stride- Shaxmat artefaktini ko'rsating
- Interpolatsiya + conv3x3 bilan shaxmat yo'qligini tekshiring
Vazifa 5: U-Net
Modellang:
- Kichik U-Net va skipsiz varianti
- Ikkalasi 2 seed bilan
- mIoU va chegara piksellari aniqligi
- Skip ni concat o'rniga qo'shish bilan almashtiring — natija?
Vazifa 6: Nomutanosiblik
Modellang:
- Obyekt ulushi 2-3% bo'lgan niqoblar
- BCE, vaznli BCE, Dice, BCE + Dice
- Davrlar bo'yicha IoU jadvali
pos_weight= 5, 12, 25 — precision va recall qanday o'zgaradi?
Vazifa 7: O'ylash
Qishloq xo'jaligi startapi sun'iy yo'ldosh suratlarida kasallangan ekin maydonlarini segmentatsiya qiladi. Kasal hudud suratlarning o'rtacha 4 foizini egallaydi. Jamoa ikki modelni solishtirdi: A modeli — piksel aniqligi 97.1%, Dice 0.41; B modeli — piksel aniqligi 96.2%, Dice 0.63. Direktor "A aniqroq ekan, shuni olamiz" dedi. Nima deysiz va yana nimani so'raysiz?
Javob
Qisqa javob: B modelini tanlash kerak — asosiy metrika Dice, va unda B ancha yaxshi. A ning aniqlikdagi ustunligi deyarli to'liq fon piksellari hisobidan.
Nima uchun:
1. Bazaviyni hisoblang. Kasal hudud 4% bo'lsa, "hammasi sog'lom" modeli 96% aniqlik oladi. A ning 97.1 foizi bazaviydan atigi 1.1 foiz punkt yuqori, B ning 96.2 foizi esa deyarli bazaviy darajasida. Ya'ni aniqlik bu ikki modelni amalda ajrata olmaydi — 1-misolda ham "hammasi fon" (0.972) va 3 pikselga siljigan bashorat (0.975) deyarli bir xil aniqlik oldi.
2. Dice aniqlikning yashirgan qismini ko'rsatadi. Dice TN ni ishlatmaydi — u faqat kasal hudud atrofida nima bo'lganiga qaraydi. 0.41 va 0.63 farqi katta: IoU ga o'girsak (IoU = Dice / (2 - Dice)), bu taxminan 0.26 va 0.46.
3. Nega A aniqligi yuqoriroq bo'lishi mumkin? A ehtiyotkor — u kasal deb kam belgilaydi, shuning uchun fonda kam xato qiladi (yuqori TN), lekin kasal hududning ko'p qismini o'tkazib yuboradi (katta FN). Bu 4-misoldagi oddiy BCE ning xulqiga o'xshaydi: uning aniqligi bazaviy bilan bir xil edi, IoU esa 0.
Nimani so'raysiz:
- Recall va precision alohida qancha? Biznes uchun qaysi xato qimmatroq — kasal maydonni o'tkazib yuborishmi (hosil yo'qoladi) yoki sog'lom maydonga ortiqcha dori sepishmi?
- Dice butun to'plam bo'yicha hisoblanganmi yoki har surat bo'yicha o'rtachalanganmi? Kasal hudud yo'q suratlar qanday hisobga olingan?
- Natijalar bir necha seed yoki fold da barqarormi?
- Kichik kasal o'choqlar (bir necha piksel) uchun sifat qanday — ular erta ogohlantirish uchun eng muhimi bo'lishi mumkin.
Muhim nuans: B modeli ham mukammal emas — Dice 0.63 hali ancha yaxshilanishi mumkin. Lekin qaror "qaysi biri yaxshiroq" haqida, va to'g'ri metrika bo'yicha javob aniq: B. Direktorga aniqlikning bazaviy bilan birga ko'rsatilishi odatda eng tez tushuntiradi.
Nimani mustahkamlaydi: 2.5, 2.6-bo'limlar.
Xulosa
Bu darsda har pikselni klassifikatsiya qiladigan tarmoq qurdik va uni halol baholashni o'rgandik.
Eng muhim uch fikr:
Piksel aniqligi nomutanosib niqobda aldaydi, IoU va Dice aldamaydi. Obyekt rasmning 2.8 foizi bo'lganda "hammasi fon" javobi
0.972aniqlik oldi, IoU esa0. 4-misolda oddiy BCE bilan o'rgatilgan U-Net 6 davr davomida aynan shu yechimda qolib ketdi — aniqlik0.961, IoU0.000. Barcha variantlarning aniqligi0.961-0.994oralig'ida bo'ldi, IoU esa0dan0.861gacha tarqaldi.Skip connection chegaralarni tiklaydi. Bir xil tuzilmali ikki modelda faqat
torch.catfarq qildi: U-Net ikkala seedda ham mIoU ni0.065-0.071ga oshirdi, chegara piksellaridagi aniqlik esa0.08-0.10ga ko'tarildi. Parametrlar atigi ~11% ko'paydi — bu arzon va samarali qo'shimcha.Loss tanlovi nomutanosiblikda hal qiluvchi. Vaznli BCE (
pos_weight = 25.6) recall ni0.986ga chiqardi, lekin precision0.699bo'lib qoldi. Dice loss birinchi davrdanoq o'rgana boshladi va IoU0.861ga, recall va precision esa0.923va0.928ga yetdi. Kattalashtirish qatlamlarida esakernel = strideqoidasi shaxmat artefaktidan saqlaydi.
Keyingi darsda 22-qismning hamma bilimini bitta loyihaga yig'amiz: sanoat nuqsonlarini klassifikatsiya qiluvchi to'liq quvur, bazaviy bilan juftlashgan taqqoslash, Grad-CAM tekshiruvi va topshirish paketi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!