Ana içeriğe geç

Orta11 dkB — Görüntü ve CNN

Konvolüsyon işlemi

Önkoşul:Neden tam bağlı ağ yetmez

Kanca

  1. derste tam bağlı ağların iki sorununu gördük: parametre patlaması ve konum bağımlılığı. Konvolüsyon, TEK bir fikirle ikisini birden çözüyor: aynı küçük filtreyi, görüntünün HER YERİNE uygulamak.

Sezgi

Konvolüsyon, küçük bir “çekirdeği” (kernel) girdinin üzerinde kaydırıp, her konumda ALTINDAKİ pikselleri çekirdekle eleman-eleman çarpıp TOPLAMAKTIR. Bu tek sayı, çıkan “özellik haritasının” (feature map) o konumdaki değerini oluşturur.

Çekirdeği kendin düzenle veya hazır bir filtre seç — pencerenin görüntü üzerinde nasıl gezindiğini ve özellik haritasının nasıl oluştuğunu izle.

Hazır filtre
Çekirdek (3×3) -- hücrelere tıklayıp düzenle

Giriş görseli (10×10)

Özellik haritası (8×8)

Pencere konumu (satır 0, sütun 0): çekirdek ile altındaki 9 pikselin eleman-eleman çarpımının TOPLAMI = 0.00. Bu tek sayı, özellik haritasındaki KARŞILIK GELEN hücreyi dolduruyor.

Mekanizma

Notebook’ta AYNI 10×10 dikey kenar görselini ve kenar çekirdeğini kullanarak, işlemi elle açalım:

Konvolüsyonu elle uygulamak

# Konvolüsyon: küçük bir "çekirdeği" (kernel), girdinin ÜZERİNDE KAYDIRARAK,
# her konumda eleman-eleman çarpıp TOPLAMAK. 26. dersteki interaktifte gördüğün
# AYNI 10x10 dikey kenar görselini ve kenar çekirdeğini kullanalım.
GIRDI_BOYUT = 10
girdi = np.zeros((GIRDI_BOYUT, GIRDI_BOYUT))
girdi[:, 5:] = 0.9  # sağ yarı açık
girdi[:, :5] = 0.2  # sol yarı koyu

kenar_kernel = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])

def konvolusyon_uygula(girdi, kernel):
    kh, kw = kernel.shape
    ch, cw = girdi.shape[0] - kh + 1, girdi.shape[1] - kw + 1
    cikis = np.zeros((ch, cw))
    for i in range(ch):
        for j in range(cw):
            pencere = girdi[i:i+kh, j:j+kw]
            cikis[i, j] = np.sum(pencere * kernel)  # eleman-eleman çarpım, sonra TOPLAM
    return cikis

cikis = konvolusyon_uygula(girdi, kenar_kernel)
print("Girdi şekli:", girdi.shape, "-> Çıkış şekli:", cikis.shape, "(3x3 çekirdekle, dolgu YOK)")
print("\nÇıkışın 4. satırı:", np.round(cikis[3], 2))
print("Tüm satırlar aynı mı (dikey bir kenar olduğu için)?", np.allclose(cikis, cikis[0]))
print("\nKenar çekirdeği, YATAY yönde değişim OLAN yerlerde (sütun 3-4) büyük değer üretiyor -- diğer her yerde SIFIR.")
Girdi şekli: (10, 10) -> Çıkış şekli: (8, 8) (3x3 çekirdekle, dolgu YOK)

Çıkışın 4. satırı: [0.  0.  0.  2.8 2.8 0.  0.  0. ]
Tüm satırlar aynı mı (dikey bir kenar olduğu için)? True

Kenar çekirdeği, YATAY yönde değişim OLAN yerlerde (sütun 3-4) büyük değer üretiyor -- diğer her yerde SIFIR.

Çıkışın HER satırı aynı (dikey bir kenar simetrik olduğu için) ve kenar sadece sütun 3-4’te (görselin ortasında, koyu-açık geçişinde) büyük bir değer (2.8) veriyor — diğer her yerde TAM SIFIR. Tek bir pencere konumunu açalım:

Tek bir pencerenin hesabı

# Tek bir pencere konumunu (satır=3, sütun=3) elle açalım -- interaktifteki "canlı denklem" budur.
pencere = girdi[3:6, 3:6]
print("\nPencere (satır 3-5, sütun 3-5):")
print(pencere)
print("\nÇekirdek:")
print(kenar_kernel)
carpim = pencere * kenar_kernel
print("\nEleman-eleman çarpım:")
print(np.round(carpim, 3))
print(f"\nToplam: {carpim.sum():.3f}  <- özellik haritasının (3,3) hücresine yazılan değer")

Pencere (satır 3-5, sütun 3-5):
[[0.2 0.2 0.9]
 [0.2 0.2 0.9]
 [0.2 0.2 0.9]]

Çekirdek:
[[-1  0  1]
 [-2  0  2]
 [-1  0  1]]

Eleman-eleman çarpım:
[[-0.2  0.   0.9]
 [-0.4  0.   1.8]
 [-0.2  0.   0.9]]

Toplam: 2.800  <- özellik haritasının (3,3) hücresine yazılan değer

Çoğu kişi “konvolüsyon karmaşık bir matematiksel işlem” sanır. Abartılı, çünkü özünde SADECE eleman-eleman çarpıp toplamak — interaktifteki her pencere konumu, tam olarak bu 9 çarpımın toplamı.

Matematik

2B konvolüsyon formülü
Yi,j=m=0k1n=0k1Xi+m,j+nKm,nY_{i,j} = \sum_{m=0}^{k-1} \sum_{n=0}^{k-1} X_{i+m,\, j+n} \cdot K_{m,n}
SembolAnlamı
XXGirdi görüntüsü
KKk×kk \times k boyutundaki çekirdek (kernel)
Yi,jY_{i,j}Çıkış özellik haritasının (i,j)(i,j) konumundaki değeri

YY‘nin boyutu, XX‘ten KÜÇÜKTÜR (dolgu — padding — yoksa): k×kk \times k‘lık bir çekirdek, her kenardan k1k-1 piksel “kaybeder”. Bu ayrıntıyı 28. derste (Filtre, stride, padding) inceleyeceğiz.

Kod

Farklı çekirdeklerin NE KADAR farklı işler yaptığını görelim, sonra PyTorch’un gerçek konvolüsyon fonksiyonuyla doğrulayalım:

Kenar vs bulanıklaştırma vs keskinleştirme

# Farklı çekirdekler, TAMAMEN farklı işler yapar. Üçünü karşılaştıralım.
bulaniklik_kernel = np.ones((3, 3)) / 9
keskinlik_kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])

cikis_bulanik = konvolusyon_uygula(girdi, bulaniklik_kernel)
cikis_keskin = konvolusyon_uygula(girdi, keskinlik_kernel)

print(f"\n{'Çekirdek':<16} {'Çıkış (satır 3)':<50}")
print(f"{'Kenar':<16} {np.round(cikis[3], 2)}")
print(f"{'Bulanıklaştırma':<16} {np.round(cikis_bulanik[3], 2)}")
print(f"{'Keskinleştirme':<16} {np.round(cikis_keskin[3], 2)}")
print("\nKenar çekirdeği kenarı VURGULAR, bulanıklaştırma geçişi YUMUŞATIR, keskinleştirme merkezi ABARTIR.")

Çekirdek         Çıkış (satır 3)                                   
Kenar            [0.  0.  0.  2.8 2.8 0.  0.  0. ]
Bulanıklaştırma  [0.2  0.2  0.2  0.43 0.67 0.9  0.9  0.9 ]
Keskinleştirme   [ 0.2  0.2  0.2 -0.5  1.6  0.9  0.9  0.9]

Kenar çekirdeği kenarı VURGULAR, bulanıklaştırma geçişi YUMUŞATIR, keskinleştirme merkezi ABARTIR.

PyTorch F.conv2d ile doğrulama

# PyTorch'un F.conv2d'siyle doğrulayalım -- gerçek CNN katmanlarının kullandığı fonksiyon budur.
girdi_t = torch.tensor(girdi, dtype=torch.float32).unsqueeze(0).unsqueeze(0)  # (batch, kanal, H, W)
kernel_t = torch.tensor(kenar_kernel, dtype=torch.float32).unsqueeze(0).unsqueeze(0)
cikis_pytorch = F.conv2d(girdi_t, kernel_t)

print(f"\nPyTorch F.conv2d çıkış şekli: {tuple(cikis_pytorch.shape)} -- (batch, kanal, H, W)")
print(f"PyTorch çıkışı (satır 3): {cikis_pytorch[0,0,3].tolist()}")
print(f"Elle hesaplanan (satır 3): {cikis[3].tolist()}")
print(f"Birebir aynı mı? {np.allclose(cikis_pytorch[0,0].numpy(), cikis)}")

PyTorch F.conv2d çıkış şekli: (1, 1, 8, 8) -- (batch, kanal, H, W)
PyTorch çıkışı (satır 3): [0.0, 0.0, 0.0, 2.799999713897705, 2.799999713897705, 0.0, 0.0, 0.0]
Elle hesaplanan (satır 3): [0.0, 0.0, 0.0, 2.8000000000000003, 2.8000000000000003, 0.0, 0.0, 0.0]
Birebir aynı mı? True

Elle hesaplanan ve PyTorch’un sonucu BİREBİR aynı — F.conv2d, gerçek CNN katmanlarının (30. ders) içinde kullandığı fonksiyondur.

Soldan sağa: dikey kenarlı girdi görseli, kenar çekirdeğinin sonucu, bulanıklaştırma sonucu, keskinleştirme sonucu.
Aynı girdi, üç farklı çekirdek -- üç tamamen farklı özellik haritası. Çekirdeğin SAYILARI, hangi örüntüyü aradığını belirler.

Nerede işe yarar

Konvolüsyon, CNN’lerin temel yapı taşıdır:

  • Geleneksel görüntü işlemede çekirdekler ELLE tasarlanır (Sobel kenar, Gauss bulanıklaştırma gibi) — notebook’taki gibi.
  • CNN’lerde ise çekirdek DEĞERLERİ, tıpkı ağırlıklar gibi, geri yayılımla ÖĞRENİLİR. Ağ, hangi filtrelerin en faydalı olduğunu kendi keşfeder.
  • AYNI çekirdek, TÜM konumlarda paylaşılır — bu, 26. dersteki parametre patlamasını ve konum bağımlılığı sorununu BİRLİKTE çözer.

Bu 3 hatayı yaparsın:

  1. Konvolüsyonu “görüntüyü küçültme” ile karıştırmak — asıl amaç ÖZELLİK ÇIKARMAKTIR, boyut küçülmesi bir yan etkidir.
  2. Çekirdek boyutunun önemsiz olduğunu düşünmek — 3×3 ve 5×5 çekirdekler farklı büyüklükte örüntüleri yakalar.
  3. CNN’lerdeki çekirdeklerin elle tasarlandığını sanmak — gerçekte hemen hemen HER ZAMAN veri üzerinden öğrenilirler.

Kendini test et

1. Konvolüsyon işleminde, özellik haritasının bir hücresindeki değer nasıl hesaplanır?
  1. Pencere altındaki piksellerin ortalaması alınarak
  2. Çekirdek ile pencere altındaki piksellerin ELEMAN-ELEMAN çarpımının TOPLAMI alınarak (doğru cevap)
  3. Sadece pencerenin merkez pikseli kopyalanarak
  4. Pencere altındaki en büyük piksel değeri seçilerek

Neden: Konvolüsyon, çekirdek ile onun altındaki aynı boyuttaki pencereyi eleman-eleman çarpıp bu çarpımların toplamını alır -- bu tek sayı, çıkış özellik haritasının o konumundaki değeri oluşturur.

2. Notebook'ta kenar çekirdeği neden görselin SADECE orta sütunlarında (3-4) büyük bir değer üretti?
  1. Rastgele bir sonuç
  2. Kenar çekirdeği, YATAY yönde bir değişim (parlaklık farkı) olan bölgelere duyarlıdır -- görselin geri kalanı düz olduğu için oralarda çıktı sıfır (doğru cevap)
  3. Görselin ortası her zaman büyük değer üretir
  4. Çekirdek hatalı tasarlanmıştı

Neden: Kenar çekirdeği, sol ve sağ komşu piksel değerleri arasındaki FARKA duyarlıdır; görselin düz (sabit renkli) bölgelerinde bu fark sıfırdır, sadece koyu-açık geçişinin olduğu sütunlarda büyük bir değer üretir.

3. CNN'lerde çekirdek (kernel) değerleri nasıl belirlenir?
  1. Her zaman elle, uzman biri tarafından tasarlanır
  2. Geri yayılımla (4. ders), tıpkı diğer ağırlıklar gibi, veriden ÖĞRENİLİR (doğru cevap)
  3. Rastgele seçilir ve hiç değiştirilmez
  4. Görüntünün boyutuna göre otomatik hesaplanır

Neden: CNN'lerdeki konvolüsyon çekirdekleri, ağın diğer ağırlıkları gibi eğitilebilir parametrelerdir -- geri yayılım ve gradyan inişiyle, hangi örüntülerin faydalı olduğu veriden öğrenilir.

Özet

Özet

  • Konvolüsyon, bir çekirdeği girdinin üzerinde kaydırıp her konumda eleman-eleman çarpıp toplayan bir işlemdir.
  • Farklı çekirdekler tamamen farklı özellikler çıkarır (kenar, bulanıklaştırma, keskinleştirme gibi).
  • Çıkış (özellik haritası), dolgu olmadan girdiden küçüktür -- çekirdek boyutuna bağlı olarak.
  • CNN'lerde çekirdek değerleri elle tasarlanmaz -- diğer ağırlıklar gibi geri yayılımla öğrenilir.
  • AYNI çekirdeğin tüm konumlarda paylaşılması, 26. dersteki parametre patlaması ve konum bağımlılığı sorunlarını birlikte çözer.
Sonraki adım: Filtre, stride, padding →