13-bo‘lim
Konvolyutsion tarmoqlar
Conv2d, filtrlar, padding va stride, MaxPool2d, shakl hisobi va CNN ning zich tarmoqdan afzalligi.
Ushbu bo‘lim mundarijasi
Rasmni nn.Linear ga berish mumkin - 28x28 rasmni 784 ta songa yoyib
yuborasiz. Lekin bunda piksellarning qo'shnichiligi yo'qoladi: model
uchun chap yuqoridagi piksel bilan o'ng pastdagisi bir xil masofada
turadi.
Konvolyutsion tarmoq (CNN) bu ma'lumotni saqlaydi.
nn.Conv2d #
import torch
import torch.nn as nn
torch.manual_seed(42)
rasm = torch.randn(1, 1, 28, 28)
konv = nn.Conv2d(in_channels=1, out_channels=8, kernel_size=3)
chiqish = konv(rasm)
print("kirish: ", tuple(rasm.shape))
print("chiqish:", tuple(chiqish.shape))
print("filtr shakli:", tuple(konv.weight.shape))
print("parametrlar:", sum(p.numel() for p in konv.parameters()))
kirish: (1, 1, 28, 28)
chiqish: (1, 8, 26, 26)
filtr shakli: (8, 1, 3, 3)
parametrlar: 80
Rasm tenzorining shakli har doim (paket, kanal, balandlik, kenglik)
tartibida bo'ladi.
| O'lcham | Qiymat | Ma'nosi |
|---|---|---|
| 0 | 1 | Paketdagi rasmlar soni |
| 1 | 1 | Kanallar: kulrang uchun 1, rangli uchun 3 |
| 2 | 28 | Balandlik |
| 3 | 28 | Kenglik |
Filtrda 8 x 1 x 3 x 3 = 72 og'irlik va 8 ta siljish bor - jami 80.
Rasm qanchalik katta bo'lishidan qat'i nazar, parametrlar soni
o'zgarmaydi. Bu CNN ning asosiy g'oyasi: bitta filtr butun rasm bo'ylab
siljitiladi.
Nima uchun 28 emas, 26? #
3x3 filtr chetga chiqa olmaydi, shuning uchun har tomondan 1 piksel
yo'qoladi. padding buni tuzatadi:
for nom, kw in [("padding=0", {}), ("padding=1", {"padding": 1}),
("stride=2", {"stride": 2}),
("padding=1, stride=2", {"padding": 1, "stride": 2}),
("kernel=5, padding=2", {"kernel_size": 5, "padding": 2})]:
k = kw.pop("kernel_size", 3)
c = nn.Conv2d(1, 8, kernel_size=k, **kw)
print(f" {nom:22s} -> {tuple(c(rasm).shape)}")
padding=0 -> (1, 8, 26, 26)
padding=1 -> (1, 8, 28, 28)
stride=2 -> (1, 8, 13, 13)
padding=1, stride=2 -> (1, 8, 14, 14)
kernel=5, padding=2 -> (1, 8, 28, 28)
Formula:
chiqish = (kirish + 2*padding - kernel) / stride + 1
kernel_size=3 uchun padding=1, kernel_size=5 uchun padding=2 -
jadvaldagi oxirgi ikki qator shuni tasdiqlaydi. Umumiy qoida:
padding = kernel_size // 2 bo'lsa, o'lcham o'zgarmaydi.
Bu qulay, chunki shakl hisobini MaxPool2d ga qoldirasiz.
Filtr nimani topadi #
Qo'lda yozilgan filtr bilan tekshiramiz. Chap yarmi qora, o'ng yarmi oq rasmni olamiz:
qirra = torch.tensor([[-1., 0., 1.]]).repeat(3, 1).view(1, 1, 3, 3)
test = torch.zeros(1, 1, 6, 6)
test[:, :, :, 3:] = 1.0
natija = torch.nn.functional.conv2d(test, qirra, padding=1)
for q in natija[0, 0].tolist():
print(" ", [round(v, 1) for v in q])
[0.0, 0.0, 2.0, 2.0, 0.0, -2.0]
[0.0, 0.0, 3.0, 3.0, 0.0, -3.0]
[0.0, 0.0, 3.0, 3.0, 0.0, -3.0]
[0.0, 0.0, 3.0, 3.0, 0.0, -3.0]
[0.0, 0.0, 3.0, 3.0, 0.0, -3.0]
[0.0, 0.0, 2.0, 2.0, 0.0, -2.0]
Tekis joylarda 0, qora-oq chegarasida esa katta qiymatlar chiqdi. Bu filtr vertikal qirrani topdi.
Haqiqiy CNN da bunday filtrlarni siz yozmaysiz - model ularni o'zi o'rganadi. Birinchi qatlamlar qirra va burchaklarni, keyingilari shakl va naqshlarni topadi.
MaxPool2d #
Pooling rasmni kichraytiradi va eng kuchli signalni saqlab qoladi:
kichik = torch.tensor([[[[1., 2., 3., 4.],
[5., 6., 7., 8.],
[9., 10., 11., 12.],
[13., 14., 15., 16.]]]])
print("MaxPool2d(2) ->", nn.MaxPool2d(2)(kichik)[0, 0].tolist())
print("AvgPool2d(2) ->", nn.AvgPool2d(2)(kichik)[0, 0].tolist())
MaxPool2d(2) -> [[6.0, 8.0], [14.0, 16.0]]
AvgPool2d(2) -> [[3.5, 5.5], [11.5, 13.5]]
MaxPool2d(2) har 2x2 kvadratdan eng kattasini oldi: 1,2,5,6 dan 6 ni.
AvgPool2d esa o'rtachasini: (1+2+5+6)/4 = 3.5.
Poolingda parametr yo'q - u shunchaki kichraytiradi.
To'liq CNN #
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.xususiyat = nn.Sequential(
nn.Conv2d(1, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
)
self.tasnif = nn.Sequential(nn.Flatten(), nn.Linear(32 * 7 * 7, 10))
def forward(self, x):
return self.tasnif(self.xususiyat(x))
cnn = CNN()
x = torch.randn(4, 1, 28, 28)
v = x
for qat in cnn.xususiyat:
v = qat(v)
print(f" {type(qat).__name__:10s} -> {tuple(v.shape)}")
print("tasniflagichdan keyin:", tuple(cnn(x).shape))
Conv2d -> (4, 16, 28, 28)
ReLU -> (4, 16, 28, 28)
MaxPool2d -> (4, 16, 14, 14)
Conv2d -> (4, 32, 14, 14)
ReLU -> (4, 32, 14, 14)
MaxPool2d -> (4, 32, 7, 7)
tasniflagichdan keyin: (4, 10)
Linear ga beriladigan sonni to'g'ri hisoblangnn.Linear(32 * 7 * 7, 10) dagi 32 * 7 * 7 qo'lda hisoblanadi.
Xato qilsangiz, shunday xabar chiqadi:
RuntimeError: mat1 and mat2 shapes cannot be multiplied (4x1568 and 800x10)
4x1568 - haqiqiy shakl, 800x10 esa siz yozgan qatlam. Demak 1568
yozish kerak edi. Yuqoridagi sikl bilan shakllarni chop etib ko'ring -
eng tez usul shu.
Muqobil yo'l: nn.LazyLinear(10) birinchi chaqiruvda o'lchamni o'zi
aniqlaydi.
Parametrlarni solishtiramiz #
zich = nn.Sequential(nn.Flatten(), nn.Linear(784, 128), nn.ReLU(), nn.Linear(128, 10))
print("Zich tarmoq parametrlari:", sum(p.numel() for p in zich.parameters()))
print("CNN parametrlari: ", sum(p.numel() for p in cnn.parameters()))
Zich tarmoq parametrlari: 101770
CNN parametrlari: 20490
CNN da parametrlar 5 barobar kam. Keyingi bo'limda ikkalasini haqiqiy ma'lumotda o'qitib, qaysi biri aniqroq ekanini ko'ramiz.
nn.Conv2d(1, 8, 3)yaratib,(1, 1, 28, 28)tenzor bering va chiqish shaklini yozing.- Filtr shaklini va parametrlar sonini qo'lda hisoblab tekshiring.
padding=1qo'shib, o'lcham saqlanganini ko'ring.stride=2bilan o'lcham qanday kamayganini kuzating.- Shakl formulasini
kernel=5, padding=2uchun qo'lda qo'llang. - Vertikal qirra filtrini gorizontalga o'zgartirib, natijani solishtiring.
MaxPool2dvaAvgPool2dni 4x4 tenzorda sinab, farqini tushuntiring.- Ikki konvolyutsion blokli CNN yozing va har qatlamdan keyingi shaklni chop eting.
Linearga noto'g'ri son berib, xato xabarini o'qing va tuzating.- Zich tarmoq va CNN parametrlarini solishtirib, farq sababini yozing.
Xulosa #
- Rasm tenzori
(paket, kanal, balandlik, kenglik)shaklida bo'ladi. nn.Conv2dfiltrni butun rasm bo'ylab siljitadi - shuning uchun parametrlar soni rasm o'lchamiga bog'liq emas.- Chiqish o'lchami:
(kirish + 2*padding - kernel) / stride + 1. padding = kernel_size // 2bo'lsa, o'lcham o'zgarmaydi.- Filtrlar qirra va naqshlarni topadi; ularni model o'zi o'rganadi.
MaxPool2d(2)o'lchamni ikki barobar kichraytiradi va unda parametr yo'q.- CNN ikki qismdan iborat: xususiyat ajratuvchi va tasniflagich.
- Qatlamlar bo'ylab kanal soni ortadi, fazoviy o'lcham kamayadi.
Flattendan keyingiLinearo'lchamini to'g'ri hisoblang yokinn.LazyLinearishlating.- CNN bir xil masalada zich tarmoqdan ancha kam parametr ishlatadi.
Keyingi bo'limda ikkala modelni haqiqiy Fashion-MNIST to'plamida o'qitamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.