13-bo‘lim

Konvolyutsion tarmoqlar

Conv2d, filtrlar, padding va stride, MaxPool2d, shakl hisobi va CNN ning zich tarmoqdan afzalligi.

🕑 10 daqiqa o‘qish 📄 590 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. nn.Conv2d
  2. Nima uchun 28 emas, 26?
  3. Filtr nimani topadi
  4. MaxPool2d
  5. To'liq CNN
  6. Parametrlarni solishtiramiz
  7. Xulosa

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 #

Python
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()))
Natija
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'lchamQiymatMa'nosi
01Paketdagi rasmlar soni
11Kanallar: kulrang uchun 1, rangli uchun 3
228Balandlik
328Kenglik

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:

Python
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)}")
Natija
  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:

Natija
chiqish = (kirish + 2*padding - kernel) / stride + 1
O'lchamni saqlash qoidasi

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:

Python
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])
Natija
   [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:

Python
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())
Natija
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 #

28x28 rasmdan 10 ta logitgacha kirish 1x28x28 conv Conv+ReLU 16x28x28 padding=1 pool MaxPool 16x14x14 conv Conv+ReLU 32x14x14 pool MaxPool 32x7x7 flatten Flatten 1568 Linear 10 Xususiyat ajratuvchi qism kanal soni ortadi, o'lcham kamayadi Tasniflagich qaror qabul qiladi 32 x 7 x 7 = 1568 - bu son Linear qatlamiga kiritiladi har MaxPool2d(2) o'lchamni ikki barobar kichraytiradi: 28 -> 14 -> 7
Kanal soni ortib, fazoviy o'lcham kamayadi - CNN ning odatiy tuzilishi
Python
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))
Natija
  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 hisoblang

nn.Linear(32 * 7 * 7, 10) dagi 32 * 7 * 7 qo'lda hisoblanadi. Xato qilsangiz, shunday xabar chiqadi:

Natija
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 #

Python
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()))
Natija
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.

Amaliy topshiriq
  1. nn.Conv2d(1, 8, 3) yaratib, (1, 1, 28, 28) tenzor bering va chiqish shaklini yozing.
  2. Filtr shaklini va parametrlar sonini qo'lda hisoblab tekshiring.
  3. padding=1 qo'shib, o'lcham saqlanganini ko'ring.
  4. stride=2 bilan o'lcham qanday kamayganini kuzating.
  5. Shakl formulasini kernel=5, padding=2 uchun qo'lda qo'llang.
  6. Vertikal qirra filtrini gorizontalga o'zgartirib, natijani solishtiring.
  7. MaxPool2d va AvgPool2d ni 4x4 tenzorda sinab, farqini tushuntiring.
  8. Ikki konvolyutsion blokli CNN yozing va har qatlamdan keyingi shaklni chop eting.
  9. Linear ga noto'g'ri son berib, xato xabarini o'qing va tuzating.
  10. Zich tarmoq va CNN parametrlarini solishtirib, farq sababini yozing.

Xulosa #

  • Rasm tenzori (paket, kanal, balandlik, kenglik) shaklida bo'ladi.
  • nn.Conv2d filtrni 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 // 2 bo'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.
  • Flatten dan keyingi Linear o'lchamini to'g'ri hisoblang yoki nn.LazyLinear ishlating.
  • CNN bir xil masalada zich tarmoqdan ancha kam parametr ishlatadi.

Keyingi bo'limda ikkala modelni haqiqiy Fashion-MNIST to'plamida o'qitamiz.

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.