4  Derin Bilgisayarlı Görü

Convolution + ReLU + pooling — kenardan nesneye uzanan hiyerarşi

NotBölüm bilgisi

4.1 Bu Derste Ne Var?

Ders 1 sabit boyutlu vektör veriyi, Ders 2 dizileri işledi. Sırada en zengin modalite: görüntüler. İnsanlar için görü öyle doğaldır ki tümüyle hafife alırız — ama bir bilgisayar için piksellerden anlam çıkarmak çetin bir problemdir. Bugünkü kameralar, otonom araçlar, tıbbi görüntüleme, telefonundaki yüz tanıma — hepsi bu derste anlatacağımız bir tek mimariye dayanır: convolutional neural network (CNN).

“sight and vision these are one of the most important human senses… vision is so much more than just recognizing… it’s actually about understanding the dynamics of your scene.” — Amini, 0:24

Dersin yolculuğu:

  1. Görüntü = sayılar matrisi — grayscale 2B, RGB 2B × 3 kanal.
  2. Hiyerarşik öznitelikler — düşük (kenar/köşe) → orta (şekil) → yüksek (nesne).
  3. Convolution — aynı küçük filtreyi görüntünün her yamasında kaydırmak: uzamsal yapıyı koruyan weight sharing.
  4. CNN’in üç operasyonu — convolution + nonlinearity (ReLU) + pooling.
  5. Ötesi — aynı öznitelikler nesne tespiti, semantik segmentasyon, otonom kontrol için tekrar kullanılır.
Şekil 4.1: Bu bölümün kavram haritası — pikselden nesneye, üç operasyonla
İpucuBuilder Notu — ML Köprüleri

Bu ders, önceki üç kursla özellikle 18.06 ve Calculus üzerinden derinden bağlanır:

  • Görüntü = matris → 18.06 — grayscale 2B matris, RGB üç kanalın üst üste binmesi (tensor).
  • Convolution → 18.06 lineer operatör; öteleme-değişmez (translation-equivariant) bir dönüşüm. Filtre · yama = 18.06 iç çarpım (Ders 1, 15).
  • Kenar tespiti filtreleri → Calculus türev. Sobel/Laplacian, yoğunluk gradient’ini, yani görüntünün türevini hesaplar (Calculus Ders 2, 10).
  • Hiyerarşi (kenar → şekil → nesne) → Calculus Ders 4 zincir kuralı / fonksiyon bileşkesi; backprop bu bileşkenin türevidir.
  • Pooling → yerel özetleme; Stat 110 sıra istatistikleri (max → Ders 25) ve beklenen değer (avg → Ders 9).
  • Multiple filters → çıktı volume → 18.06 paralel lineer projeksiyonlar yığını.

İleriye köprüler: vision transformer (ViT), object detection (YOLO/R-CNN), segmentation (U-Net), self-driving multi-modal füzyon, mobile/on-device quantization.

4.2 Görü Nedir? Ne Görüyoruz?

Amini dersi bir gözlemle açıyor: görü, “ne’nin nerede olduğuna bakarak bilmek” olarak tanımlanır; ama gerçek görü bundan çok daha fazlasıdır. Bir trafik sahnesini düşün: ikisi de “araba” olan iki nesne tanımlayabilirsin, ama yol kenarındaki park edilmiş olan ile yayalar için duran biri tamamen farklı dinamiklere sahiptir. Birinin biraz sonra hareket edeceğini sezmek de görünün parçasıdır.

“vision is so much more than just recognizing an image… it’s actually about understanding the dynamics of your scene of your environment.” — Amini, 0:53

Derin öğrenme bu zengin anlamayı çeşitli uygulamalara taşıdı: yüz algılama/tanıma, otonom sürüş (Amini’nin MIT/Toyota arabası uçtan-uca CV ile sürülür), sağlık (röntgen/MR taramalarından kanser tespiti), erişilebilirlik, robotik, biyoloji.

İpucuBuilder Notu — Sahneyi Anlamak

Geriye: “Sahneyi anlamak” sezgisi, yalnızca pikselleri eşleştirmek yerine yapısal ilişkiler kurmaktır — bu, 18.06’nın “vektörler arası geometri” diline ve Stat 110’un nedensellik/koşullu olasılık çerçevesine bağlanır.

İleriye: Görü artık tek-modalite değil; modern sistemler görüntü + dil + sensör füzyonu yapar (CLIP, Gemini görsel modeller). Bu dersin convolutional omurgası, bu sistemlerin görü tarafında hâlâ yaygındır; transformer (ViT) ise rekabette öne geçiyor.

4.3 Görüntü Bilgisayara Nasıl Görünür?

Bilgisayarın gözü yoktur; gördüğü tek şey sayılardır. Bir grayscale görüntü, bilgisayar için bir 2B matristir: her hücrede o pikselin parlaklığı (0 koyu, 1 parlak) yazar.

\[ I \in \mathbb{R}^{H \times W} \]

Renkli görüntüler için bu matrisi üç kanala (kırmızı, yeşil, mavi) genişletiriz. Üç adet \(H \times W\) matrisi üst üste binince üç boyutlu bir tensor olur:

\[ I \in \mathbb{R}^{H \times W \times 3} \]

import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(0)

# Sentetik bir "kedi" benzeri imge: 2 göz + burun
H = W = 32
img = 0.15 + 0.1 * rng.standard_normal((H, W))
# arka plan
yy, xx = np.mgrid[0:H, 0:W]
# Sol göz
img[(yy - 11) ** 2 + (xx - 11) ** 2 < 9] = 0.95
# Sağ göz
img[(yy - 11) ** 2 + (xx - 21) ** 2 < 9] = 0.95
# Burun
img[(yy - 19) ** 2 + (xx - 16) ** 2 < 4] = 0.65
img = np.clip(img, 0, 1)

# RGB kanal sentezi
R = img * 0.9 + 0.1 * (xx / W)
G = img * 0.6 + 0.2 * (yy / H)
B = img * 0.4 + 0.3 * (1 - yy / H)
rgb = np.stack([R, G, B], axis=-1)
rgb = np.clip(rgb, 0, 1)

fig, axes = plt.subplots(1, 4, figsize=(12, 4.5))

# Grayscale + sayısal değerler
ax = axes[0]
im = ax.imshow(img, cmap='gray', vmin=0, vmax=1)
ax.set_title('Grayscale matris\n$I \\in \\mathbb{R}^{32 \\times 32}$', fontsize=11)
# Sayısal değerleri yazdır (sub-grid)
sub = img[8:14, 8:14]
for i in range(sub.shape[0]):
    for j in range(sub.shape[1]):
        ax.text(8 + j, 8 + i, f'{sub[i,j]:.1f}', ha='center', va='center',
                fontsize=6, color='red' if sub[i,j] < 0.5 else 'black')
ax.set_xlabel(f'W = {W}'); ax.set_ylabel(f'H = {H}')

# R kanalı
for ax, channel, cmap_name, title, color in zip(
    axes[1:], [R, G, B], ['Reds', 'Greens', 'Blues'],
    ['Kırmızı (R)', 'Yeşil (G)', 'Mavi (B)'],
    ['#dc2626', '#16a34a', '#2563eb']
):
    ax.imshow(channel, cmap=cmap_name, vmin=0, vmax=1)
    ax.set_title(title, fontsize=11, color=color)
    ax.axis('off')

plt.tight_layout()
plt.show()
Şekil 4.2: Sentetik bir ‘kedi yüzü’ örneği. Sol: grayscale matris (parlaklık değerleri). Sağ: RGB üç kanal — kırmızı, yeşil, mavi ayrı ayrı.

Yani senin gördüğün bir köpek fotoğrafı, bilgisayar için \(H \times W \times 3\) boyutlu bir sayı kümesidir — başka bir şey değil.

İpucuBuilder Notu — Pipeline’ın Başı

Geriye (18.06): Grayscale görüntü tam olarak bir matris (Ders 1); RGB ise 3 kanalın istiflenmesidir. CNN’in tüm işlemleri (convolution, pooling, lineer katmanlar) bu tensorler üzerinde tanımlı lineer cebir operasyonlarıdır.

İleriye: Pratikte ham piksel değerlerini doğrudan ağa vermek nadiren ideal — normalization (kanal başına ortalama-sapma çıkarma) eğitim kararlılığını artırır. PyTorch’ta torchvision.transforms bu işi yapar.

4.4 Sınıflandırma ve Hiyerarşik Öznitelikler

Görü için klasik bir görev: bir görüntüyü bir sınıfa ata. 2B görüntüden, sabit boyutlu 1B kategori vektörüne git. Peki ağ neye bakarak karar verir? İnsan olarak şöyle yaparsın: önce çizgileri, sonra köşeleri, sonra göz/burun/ağız gibi yapıları, en sonunda yüzü ararsın. Ağ da aynı hiyerarşiyi kurar — ama farkla: bu hiyerarşiyi sen elle tanımlamazsın, ağ veriden öğrenir.

  • Düşük seviye (alt katmanlar): kenar, köşe, renk yamaları.
  • Orta seviye: göz, tekerlek, kapı kolu gibi parça desenleri.
  • Yüksek seviye (üst katmanlar): yüz, araba, ev gibi nesneler.
rng = np.random.default_rng(2)

# Katman 1: kenar/yönlü filtreler
def edge_filter(angle, size=7):
    yy, xx = np.mgrid[-size//2+1:size//2+1, -size//2+1:size//2+1]
    rot = np.cos(np.deg2rad(angle)) * xx + np.sin(np.deg2rad(angle)) * yy
    return np.sign(rot) * np.exp(-(yy**2 + xx**2) / 10)

# Katman 2: köşeler, dalgalar
def corner_filter(t, size=7):
    yy, xx = np.mgrid[-size//2+1:size//2+1, -size//2+1:size//2+1]
    r = np.sqrt(xx**2 + yy**2)
    return np.sin(t + r * 0.6) * np.exp(-r**2 / 12)

# Katman 3: küçük göz-benzeri yamalar
def eye_like(seed, size=11):
    yy, xx = np.mgrid[-size//2+1:size//2+1, -size//2+1:size//2+1]
    r2 = xx**2 + yy**2
    base = np.exp(-r2 / 4)
    rngl = np.random.default_rng(seed)
    base += 0.3 * rngl.standard_normal((size, size))
    return base

# Katman 4: tam yüz (kompozit)
def face(seed, size=15):
    yy, xx = np.mgrid[-size//2+1:size//2+1, -size//2+1:size//2+1]
    base = np.exp(-(xx**2 + yy**2) / 30)
    base -= 0.6 * np.exp(-((xx + 3)**2 + (yy + 2)**2) / 2)
    base -= 0.6 * np.exp(-((xx - 3)**2 + (yy + 2)**2) / 2)
    base -= 0.4 * np.exp(-(xx**2 + (yy - 3)**2) / 3)
    return base

fig, axes = plt.subplots(4, 4, figsize=(12, 4.5),
                          gridspec_kw={'wspace': 0.1, 'hspace': 0.3})

layers = [
    (edge_filter, [0, 45, 90, 135], 'Katman 1 — kenarlar', '#4f46e5'),
    (corner_filter, [0, 1.2, 2.4, 3.8], 'Katman 2 — basit şekiller', '#f59e0b'),
    (eye_like, [1, 2, 3, 4], 'Katman 3 — parçalar (göz/burun)', '#10b981'),
    (face, [10, 20, 30, 40], 'Katman 4 — nesneler (yüz)', '#7c3aed'),
]

for row, (fn, args, title, color) in enumerate(layers):
    for col, a in enumerate(args):
        ax = axes[row, col]
        ax.imshow(fn(a), cmap='RdBu_r')
        ax.axis('off')
        if col == 0:
            ax.set_ylabel(title, fontsize=10, color=color, rotation=0,
                          ha='right', va='center', labelpad=80, weight='bold')

plt.suptitle('Hiyerarşi: alt katman düşük seviye desenler, üst katman bütün nesneler',
             fontsize=12, y=1.02)
plt.tight_layout()
plt.show()
Şekil 4.3: Öznitelik hiyerarşisi: düşük seviye (kenarlar) → orta (basit şekiller) → yüksek (nesne parçaları). Her katmanın önceki üzerine kurulan örnek filtre örüntüleri.
İpucuBuilder Notu — Fonksiyon Bileşkesi

Geriye (Calculus): Öznitelik hiyerarşisi, fonksiyon bileşkesidir — Calculus Ders 4’ün zincir kuralı zemini: \(f_3(f_2(f_1(x)))\). Derin ağ tam olarak bu bileşkedir; backprop, gradient’i bu bileşkeden geriye taşır.

İleriye: Bu hiyerarşi modern modellerin de iskeleti: ResNet, VGG, EfficientNet. Transfer learning bu yüzden çalışır — bir görevde öğrenilen düşük seviye filtreler başka göreve taşınabilir (ImageNet → tıbbi görüntü).

4.5 Dense Ağ Neden Görüntüde Çöker?

Ders 1’in fully connected (dense) ağı 1B girdi bekler. 2B bir görüntüyü vermek için “flatten” ederiz — satırları arka arkaya dizip tek bir uzun vektöre çeviririz. İşte iki büyük problem buradan başlar:

Problem 1 — Uzamsal bilgi ölür. Görüntüde yan yana iki piksel anlamlı bir ilişkiye sahiptir; ama flatten edip 1B’ye çekince bu komşuluk bilgisi tümden kaybolur. Ağ “komşu piksel” diye bir kavram bilmeden öğrenmeye çalışır.

“just by doing this one operation, even though in theory this could be learned… you’ve really killed… so much of the learning capacity of this model just by flattening this input.” — Amini, 12:55

Problem 2 — Parametre patlaması. \(100 \times 100\)’lük küçük bir görüntü bile 10.000 boyutlu girdi vektörüdür. 1000 nöronlu bir gizli katmanla = 10 milyon parametre. RGB ve gerçek boyutlarda (\(224 \times 224 \times 3\)) bu sayı yüzlerce milyona çıkar.

\[ \text{params (ilk katman)} = (H \cdot W \cdot C) \times \text{hidden} \]

İpucuBuilder Notu — Yapısal Önyargı

Geriye (18.06): Parametre sayısı doğrudan matris boyutlarından gelir. “Uzamsal yapıyı koru” fikri, kanonik bazın yapısını yok etmemek demektir.

İleriye: Aynı parametre patlaması derdi modern büyük modellerin de temel kısıtıdır; çözüm hep “yapısal sınırlama” (weight sharing, low-rank, sparsity, MoE). CNN’in convolution’ı bu çözümün prototipidir — milyonlar yerine onlarca öğrenilebilir ağırlık.

4.6 Convolution: Yerel Yamalar ve Filtreler

Çözüm fikir olarak basittir: görüntüyü 1B’ye çekme; uzamsal yapısını koru. Bir sonraki katmanın bir nöronunu görüntünün her pikseline değil, yalnızca küçük bir yamasına (patch) bağla. Aynı görüntüde tek bir yama yetmez; aynı küçük filtreyi görüntü boyunca kaydırarak uygularız — her konumda aynı ağırlıkları kullanırız.

“this operation that I just described to you, this patch-based operation where we look at every patch as opposed to the entire image — this is called the convolution.” — Amini, 21:24

Bir filtre, küçük bir ağırlık matrisidir (örn. \(3 \times 3 = 9\) ağırlık). Her konumda, yamanın piksellerini filtre ağırlıklarıyla eleman-eleman çarpıp toplarız. Matematiksel olarak:

\[ (I * K)(x,\, y) = \sum_{i,\, j} I(x+i,\, y+j) \cdot K(i,\, j) \]

I = np.array([
    [1, 1, 1, 0, 0],
    [0, 1, 1, 1, 0],
    [0, 0, 1, 1, 1],
    [0, 0, 1, 1, 0],
    [0, 1, 1, 0, 0],
], dtype=float)
K = np.array([
    [1, 0, 1],
    [0, 1, 0],
    [1, 0, 1],
], dtype=float)

# Çıktı haritası
Ho, Wo = I.shape[0] - K.shape[0] + 1, I.shape[1] - K.shape[1] + 1
O = np.zeros((Ho, Wo))
for i in range(Ho):
    for j in range(Wo):
        O[i, j] = (I[i:i+3, j:j+3] * K).sum()

fig, axes = plt.subplots(1, 4, figsize=(12, 4.5))

# 1) Girdi
ax = axes[0]
ax.imshow(I, cmap='Blues', vmin=-1, vmax=2)
for i in range(I.shape[0]):
    for j in range(I.shape[1]):
        ax.text(j, i, f'{int(I[i,j])}', ha='center', va='center',
                fontsize=14, weight='bold')
ax.set_title('Girdi $I$ (5×5)', fontsize=11)
ax.set_xticks([]); ax.set_yticks([])

# Yamayı vurgula (sol-üst)
from matplotlib.patches import Rectangle
ax.add_patch(Rectangle((-0.5, -0.5), 3, 3, fill=False, edgecolor='red', lw=2.5))

# 2) Filtre
ax = axes[1]
ax.imshow(K, cmap='Reds', vmin=-1, vmax=2)
for i in range(K.shape[0]):
    for j in range(K.shape[1]):
        ax.text(j, i, f'{int(K[i,j])}', ha='center', va='center',
                fontsize=14, weight='bold')
ax.set_title('Filtre $K$ (3×3)', fontsize=11)
ax.set_xticks([]); ax.set_yticks([])

# 3) Adım: yamayı filtre ile çarp
ax = axes[2]
patch = I[:3, :3]
prod = patch * K
ax.imshow(prod, cmap='Purples', vmin=-1, vmax=2)
for i in range(3):
    for j in range(3):
        ax.text(j, i, f'{int(patch[i,j])}×{int(K[i,j])}\n={int(prod[i,j])}',
                ha='center', va='center', fontsize=9, weight='bold')
ax.set_title(f'Yama × Filtre\ntoplam = {int(prod.sum())}', fontsize=11)
ax.set_xticks([]); ax.set_yticks([])

# 4) Çıktı haritası
ax = axes[3]
ax.imshow(O, cmap='Greens', vmin=O.min(), vmax=O.max())
for i in range(O.shape[0]):
    for j in range(O.shape[1]):
        ax.text(j, i, f'{int(O[i,j])}', ha='center', va='center',
                fontsize=14, weight='bold')
ax.set_title('Çıktı haritası (3×3)\n(her hücre bir yama)', fontsize=11)
ax.set_xticks([]); ax.set_yticks([])
ax.add_patch(Rectangle((-0.5, -0.5), 1, 1, fill=False, edgecolor='red', lw=2.5))

plt.tight_layout()
plt.show()
Şekil 4.4: Convolution adım adım: 5×5 girdiyi 3×3 filtre ile tara. Her konumda 3×3 yama × filtre eleman-eleman çarpılır ve toplanır → 3×3 çıktı haritası.

Sonuçta ortaya çıkan 2B haritaya özellik haritası (feature map) denir: filtrenin görüntünün hangi konumlarında “tetiklendiğini” söyler.

# Sentetik bir geometrik şekil
img = np.zeros((50, 50))
img[10:40, 10:40] = 1.0   # iç kare
img[15:35, 20:30] = 0.0   # iç boşluk
img[25:32, 25:45] = 0.7   # küçük dikdörtgen

Kx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
Ky = Kx.T

def conv2d(I, K):
    Hi, Wi = I.shape
    Hk, Wk = K.shape
    Ho, Wo = Hi - Hk + 1, Wi - Wk + 1
    out = np.zeros((Ho, Wo))
    for i in range(Ho):
        for j in range(Wo):
            out[i, j] = (I[i:i+Hk, j:j+Wk] * K).sum()
    return out

ex = conv2d(img, Kx)
ey = conv2d(img, Ky)
mag = np.sqrt(ex**2 + ey**2)

fig, axes = plt.subplots(1, 4, figsize=(12, 4))
axes[0].imshow(img, cmap='gray'); axes[0].set_title('Girdi görüntü', fontsize=11)
axes[1].imshow(ex, cmap='RdBu_r'); axes[1].set_title('Sobel-x\n(dikey kenarlar)', fontsize=11)
axes[2].imshow(ey, cmap='RdBu_r'); axes[2].set_title('Sobel-y\n(yatay kenarlar)', fontsize=11)
axes[3].imshow(mag, cmap='magma'); axes[3].set_title('Gradient büyüklüğü\n$\\sqrt{e_x^2 + e_y^2}$', fontsize=11)
for ax in axes:
    ax.axis('off')
plt.tight_layout()
plt.show()
Şekil 4.5: Klasik kenar filtreleri = öğrenilebilir türev. Sobel-x dikey kenarları, Sobel-y yatay kenarları öne çıkarır.

“convolution preserves the spatial relationships between pixels by learning these image features in small patches.” — Amini, 21:30

İpucuBuilder Notu — Convolution = Öğrenilebilir Türev

Geriye (18.06 + Calculus): Convolution, bir lineer operatördür ve öteleme-değişmez — görüntüyü kaydırırsan, çıktısı da aynı kadar kayar. Daha keskin bir köprü: kenar tespiti filtreleri (Sobel, Laplacian) tam olarak Calculus türevidir — piksel yoğunluğunun uzamsal gradient’ini hesaplarlar. Yani convolution = öğrenilebilir türev operatörü. Ders 2’de RNN aynı \(W\)’yi zaman boyunca paylaşıyordu; CNN aynı filtreyi uzam boyunca paylaşır.

İleriye: Pratikte convolution’ın iki parametre seti: filtre boyutu ve stride + padding. PyTorch’ta nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding). Dilated/depthwise/grouped convolution varyantları farklı verimlilik-ifade dengelerini hedefler (MobileNet, EfficientNet).

4.7 CNN’in Üç Operasyonu: Conv + ReLU + Pool

Convolution, üç işlemden ilki. CNN’in mimarisi, her tekrar bloğunda üç adımı sırayla uygular:

(1) Convolution. Az önce gördük — küçük filtreyi her yamada uygula, feature map’leri üret.

(2) Doğrusal-olmama (genelde ReLU). Convolution lineer bir işlemdir; üst üste binerse tek bir lineer operatöre çöker. Her convolution sonrası bir ReLU geçirerek doğrusal-olmama katarız:

\[ \text{ReLU}(z) = \max(0,\, z) \]

Sezgisel olarak bu, bir eşik gibi davranır: negatif tepki = 0; pozitif tepki = olduğu gibi geç.

(3) Pooling — örnekleme. Bir feature map’i alıp uzamsal olarak küçültür (downsample). En yaygın yöntem max pooling: her küçük (örn. \(2 \times 2\)) yamada maksimum değeri tut, kalanı at.

rng = np.random.default_rng(5)
fm = rng.uniform(0, 1, (8, 8))

def maxpool(x, k=2):
    H, W = x.shape
    out = np.zeros((H // k, W // k))
    for i in range(H // k):
        for j in range(W // k):
            out[i, j] = x[i*k:(i+1)*k, j*k:(j+1)*k].max()
    return out

pooled = maxpool(fm, 2)

fig, axes = plt.subplots(1, 2, figsize=(11, 4.5))

ax = axes[0]
ax.imshow(fm, cmap='YlOrRd', vmin=0, vmax=1)
for i in range(8):
    for j in range(8):
        ax.text(j, i, f'{fm[i,j]:.2f}', ha='center', va='center', fontsize=8)
# 2x2 yamayı çiz
from matplotlib.patches import Rectangle
for i in range(0, 8, 2):
    for j in range(0, 8, 2):
        ax.add_patch(Rectangle((j - 0.5, i - 0.5), 2, 2,
                                fill=False, edgecolor='blue', lw=1.5))
ax.set_title('Feature map (8×8)\n2×2 yamalara böl', fontsize=11)
ax.set_xticks([]); ax.set_yticks([])

ax = axes[1]
ax.imshow(pooled, cmap='YlOrRd', vmin=0, vmax=1)
for i in range(4):
    for j in range(4):
        ax.text(j, i, f'{pooled[i,j]:.2f}', ha='center', va='center', fontsize=11, weight='bold')
ax.set_title('Max pooling sonrası (4×4)\n2× downsample', fontsize=11)
ax.set_xticks([]); ax.set_yticks([])

plt.tight_layout()
plt.show()
Şekil 4.6: Max pooling 2×2: her yamadan maksimum değer tutulur. Feature map H ve W boyutlarında 2× küçülür; kanal sayısı değişmez.

Pooling neden işe yarar?

  • Hiyerarşi tetiklenir: Pooling ile görüntü küçülünce, aynı küçük filtre bir sonraki katmanda daha geniş uzamsal bölgeyi görür — yani alıcı alan derinlikle büyür.
  • Uzamsal değişmezlik: Küçük kaymalara dayanıklılık.
  • Hesap/bellek tasarrufu: Daha küçük tensorler.

“the whole point of what we’re doing here is to learn this hierarchy.” — Amini, 37:42

İpucuBuilder Notu — Pooling = Sıra İstatistiği

Geriye (Stat 110 + Calculus): Max pooling, bir bölgenin maksimum sıra istatistiğidir (Stat 110 Ders 25); average pooling ise bölgenin beklenen değeridir (Ders 9). “Filtre + ReLU + pooling”in bileşkesi, Calculus Ders 4 zincir kuralının somut bir uygulamasıdır.

İleriye: Modern mimariler max pooling yerine sıklıkla stride’lı convolution (örn. stride=2) kullanır — downsampling’i öğrenilebilir hâle getirir. Global average pooling sınıflandırma kafasından önce uzamsal boyutu tek bir vektöre indirir.

4.8 Tam CNN Mimarisi: Feature Extractor + Classifier

Üç operasyonu bir araya getirip uçtan uca bir görüntü sınıflandırıcı kuralım. Bir CNN iki belirgin parçaya ayrılır:

Parça 1 — Feature extractor: Conv + ReLU + Pool blokları üst üste istiflenir. Erken bloklar küçük desenleri, derin bloklar karmaşık parçaları yakalar. Her blokta filtre sayısı genelde artar (32 → 64 → 128), uzamsal boyut pooling ile küçülür.

Parça 2 — Classifier head: Yüksek seviye özellikleri flatten edip (veya global average pool’a verip) fully connected katmanlardan geçirir. Çıkışta sınıf sayısı kadar nöron + softmax ile olasılık dağılımı üretir.

Şekil 4.7: Tipik CNN: Conv-ReLU-Pool blokları (feature extractor) ardından flatten + dense + softmax (classifier head).

PyTorch’ta küçük bir CNN:

import torch.nn as nn

cnn = nn.Sequential(
    # ---- feature extractor ----
    nn.Conv2d(3, 32, kernel_size=3, padding=1), nn.ReLU(),
    nn.MaxPool2d(2),                          # H,W -> H/2, W/2
    nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.ReLU(),
    nn.MaxPool2d(2),                          # /2 yeniden
    nn.Conv2d(64, 128, kernel_size=3, padding=1), nn.ReLU(),
    nn.AdaptiveAvgPool2d(1),                  # global average pooling
    # ---- classifier head ----
    nn.Flatten(),
    nn.Linear(128, 10),                       # 10 sinif
    # softmax cross-entropy ile birlikte gelir (nn.CrossEntropyLoss)
)

Eğitim döngüsünde forward pass + cross-entropy + loss.backward() + optimizer.step() — yani Ders 1’in eğitim mekaniğinin aynısı, yalnızca model gövdesi değişti.

İpucuBuilder Notu — Transfer Learning

Geriye: Mimari, Ders 1’in (loss, gradient descent, backprop) ve Ders 2’nin (weight sharing) sezgilerini görüye uyarlar. Convolutional katmanları basitçe lokal dot product katmanları olarak görmek (18.06 Ders 30), CNN’i “sihirli” görmekten uzaklaştırır.

İleriye: Modern CNN’ler bu çekirdeğe residual bağlantılar (ResNet), batch normalization, depthwise/grouped convolution ekler. Transfer learning: ImageNet üzerinde önceden eğitilmiş feature extractor’ı al, classifier head’i kendi görevin için yeniden eğit — az veride bir builder’ın varsayılan başlangıcıdır.

4.9 Convolution’ın Ötesinde: Detection, Segmentation, Control

CNN’in en güzel yanı: feature extractor genel amaçlıdır. Bir görevden öğrendiği düşük/orta seviye filtreleri, çok farklı görevler için tekrar kullanabilirsin — yalnızca head’i değiştirirsin.

“very general-purpose feature learning across an image… outputs of all these convolutional layers are very general purpose features that can be reused.” — Amini, 44:25

Birkaç klasik görev:

  • Nesne tespiti (object detection). Görüntüdeki her nesnenin bounding box (x, y, h, w) ve sınıfını ver. Modern: Faster R-CNN, YOLO, DETR — uçtan-uca türevlenebilir kutu önerisi.
  • Semantik segmentasyon. Her piksele bir sınıf ata. Mimari encoder-decoder: küçült (conv+pool), sonra upsampling ile geri büyüt. U-Net bu desenin en bilinen örneği.
  • Otonom kontrol. Çoklu kamera özelliklerini birleştir, çıktıda araç komutlarını (direksiyon, gaz/fren) dağılım olarak öğren — bir kavşakta hem sola hem sağa dönmek mümkündür; çok-modlu olasılık dağılımı.
İpucuBuilder Notu — Tek Omurga, Çok Head

Geriye (18.06): “Feature extractor + değişen head” deseni, paylaşılan bir gösterim alt-uzayı üzerine farklı projeksiyon kafaları koymaktır (18.06 Ders 15 projeksiyon).

İleriye: Bu desen modern multi-task learning’in iskeletidir; tek bir omurga + birden çok task-specific head (Tesla HydraNet, Meta SAM). Foundation model çağında “pre-train edilmiş omurga + küçük head’ler” yaklaşımı standart.

4.10 CNN’in Bugünü ve Yarını

Convolution yalnızca 2B görüntüye özgü değil. Aynı “yerel filtre + weight sharing” fikri:

  • 1B üzerinde: ses dalgaları, EKG/EEG, hatta dil dizileri (1D conv RNN’lere bir alternatiftir).
  • 3B üzerinde: video (zaman dahil) ve hacimsel taramalar (MR, CT).

Görü tarafında en büyük rakip — ve yer yer yeni baskın mimari — Vision Transformer (ViT): görüntüyü küçük yamalara (örn. \(16 \times 16\)) böl, her yamayı bir token olarak gör, Ders 2’de gördüğümüz self-attention’ı bu tokenlere uygula. Yeterince veri olduğunda ViT genellikle CNN’i yakalar veya geçer; ama az veri ve hesap rejimlerinde CNN’in yerleşik öneliği (locality, weight sharing) hâlâ kazandırır.

İpucuBuilder Notu — İki Dersin Buluşması

Geriye: ViT, Ders 2’nin attention’ını görüye uygulamaktır — bu kursun en güçlü iki dersinin (RNN→attention ve CNN) son adımda buluştuğu yer.

İleriye: Self-supervised ön-eğitim devrimi (DINO, MAE, CLIP) ile CNN/ViT artık görsel foundation model’lerin omurgası. Bir builder olarak “baştan eğit” yerine “hazır omurga + transfer” varsayılan başlangıç olmalı.

4.11 Bu Dersin Özeti

  1. Görü, sadece tanıma değil — sahne dinamiklerini ve nedensel ilişkileri anlamaktır.
  2. Görüntü = sayılar matrisi: grayscale \(H \times W\), RGB \(H \times W \times 3\) tensor.
  3. Dense ağ görüntüde çöker: flatten uzamsal bilgiyi yok eder ve parametre patlar.
  4. Convolution: küçük bir filtreyi her yamada uygula (weight sharing); yerel alıcı alan korunur.
  5. Feature map, filtrenin görüntü boyunca tetiklendiği yerleri gösterir (dot product büyüklüğü).
  6. CNN’in üç operasyonu: convolution + ReLU + pooling. Pooling, alıcı alanı derinlikle büyütür → öznitelik hiyerarşisi doğal olarak çıkar.
  7. Mimari iki parça: feature extractor + classifier head. Eğitim, Ders 1’in cross-entropy + gradient descent + backprop’unun aynısıdır.
  8. Aynı omurga, farklı görevler: detection, segmentation, kontrol; head değişir, feature extractor genelde aynı kalır.
  9. Convolution 1B ve 3B’ye genişler; modern alternatif olarak Vision Transformer (Ders 2’nin attention’ı görüde).
ÖnemliTek bir cümle

Convolutional neural network, aynı küçük filtreyi tüm uzamsal konumlarda paylaşan bir lineer operatörün (convolution), bir doğrusal-olmamanın (ReLU) ve bir özetleme adımının (pooling) istiflenmesidir — ve bu üçlü, kenardan nesneye uzanan öznitelik hiyerarşisini veriden öğrenir.

4.12 Kontrol Soruları

Cevap: (0,0) konumundaki \(3 \times 3\) yama: \([[0,1,1],[0,1,1],[0,1,1]]\). Eleman-eleman çarpıp topla:

\[ (0)(-1) + (1)(0) + (1)(1) + (0)(-1) + (1)(0) + (1)(1) + (0)(-1) + (1)(0) + (1)(1) = 3 \]

Sonuç +3 (güçlü pozitif). Filtre “soldan koyu, sağdan parlak” desenini arıyor; bu yamada da soldan koyu (0’lar) sağdan parlak (1’ler) geçiş var → filtre tetikleniyor. Bu filtre aslında piksel yoğunluğunun uzamsal türevidir (Calculus Ders 2/10) — convolution = öğrenilebilir türev.

Cevap: Fully connected: girdi \(224 \cdot 224 \cdot 3 = 150\,528\); ilk katman ağırlıkları \(150\,528 \times 1000 \approx\) 150 milyon parametre.

Conv katmanı: \(32 \times (3 \times 3 \times 3) + 32 = 32 \cdot 27 + 32 =\) 896 parametre. Yüz binlerce kat daha az.

Aradaki farkın iki kaynağı: (1) weight sharing — aynı filtre tüm uzamsal konumlarda; (2) yerellik — her nöron yalnızca küçük yamaya bağlı. Bu yapısal önyargılar, görüntülerin doğal istatistiklerine (komşu pikseller ilişkilidir, desenler uzamda kayabilir) uyar.

Cevap: Max pooling, yerel yamadaki maksimum aktivasyonu tutar — “buralarda filtre tetiklendi mi?” sorusuna keskin cevap; küçük kaymalara doğal dayanıklı. Average pooling ise yamanın ortalamasını alır — yumuşak özetleme.

Alıcı alan büyümesi: Pooling feature map’i 2× küçültür. Aynı \(3 \times 3\) filtreyi sonraki katmanda uygularsan, o filtrenin “gördüğü” bölge orijinal görüntüde artık \(\sim 6 \times 6\)’ya karşılık gelir. Birkaç katman sonra küçük filtreler büyük bölgeleri özetler — kenardan şekle, şekilden nesneye hiyerarşi doğal olarak çıkar.

Cevap: ViT görüntüyü küçük yamalara böler, her yamayı bir token olarak ele alır; Ders 2’nin self-attention’ını (\(\mathbf{Q}\mathbf{K}^\top\) + softmax) bu token dizisine uygular.

Kaybedilen: CNN’in iki yapısal önyargısı — yerellik ve öteleme-değişmezlik — ViT’te açıkça yoktur; büyük veriden öğrenmesi gerekir. Bu yüzden az veriyle CNN’i yenmekte zorlanır.

Kazanılan: Küresel ilişkiler en alttan itibaren kurulur; attention daha paralelleştirilebilir. Yeterli veri ve hesap olduğunda ViT genellikle daha iyi ölçeklenir.

Cevap: ReLU’suz CNN, art arda lineer (convolution) operatörlerden oluşur. İki lineer operatörün bileşkesi yine lineerdir (18.06): \(W_2(W_1 x) = (W_2 W_1) x = W' x\). Yani kaç convolution katmanı istiflersen istifle, sonuç tek bir lineer filtreye çöker. Modelin ifade gücü dramatik azalır — yalnızca lineer ayrılabilir desenleri yakalayabilir; “kenar → şekil → nesne” hiyerarşisi imkânsız. Bu yüzden her conv sonrası bir doğrusal-olmama şarttır.

4.13 Egzersizler

Egzersiz 1 (Convolution’ı elle kur). NumPy ile 2B convolution’ı sıfırdan yaz. Sobel x-yön kenar filtresi \(K = [[-1,0,1],[-2,0,2],[-1,0,1]]\) kullan; küçük bir gri görüntüye uygula ve sonucu görselleştir.

import numpy as np

def conv2d(I, K):
    Hi, Wi = I.shape
    Hk, Wk = K.shape
    Ho, Wo = Hi - Hk + 1, Wi - Wk + 1
    out = np.zeros((Ho, Wo))
    for i in range(Ho):
        for j in range(Wo):
            out[i, j] = (I[i:i+Hk, j:j+Wk] * K).sum()   # eleman carpim + toplam
    return out

K = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])   # Sobel x
I = np.random.rand(6, 6)
print(conv2d(I, K))

Egzersiz 2 (Dense vs CNN parametre sayısı). MNIST (\(28 \times 28\)) için iki model parametre sayısını karşılaştır: (a) Flatten → Dense(128) → Dense(10); (b) Conv2d(1, 32, 3) → MaxPool → Conv2d(32, 64, 3) → MaxPool → Flatten → Dense(10). Hangi mimaride kaç parametre var? Bölüm 5’te tartıştığımız “parametre patlaması” argümanını sayılarla doğrula.

Egzersiz 3 (Alıcı alan hesabı). Bir CNN şu sırada: Conv \(3 \times 3\) (stride 1) → Conv \(3 \times 3\) (stride 1) → MaxPool \(2 \times 2\) (stride 2) → Conv \(3 \times 3\) (stride 1). Son katmanın bir nöronunun orijinal girdideki alıcı alanı kaç piksele karşılık gelir? (İpucu: her conv \(+2\), her stride-2 pool \(2 \times\) ölçeklendirme yapar.)

Egzersiz 4 (Tiny CNN — PyTorch). PyTorch ile küçük bir CNN tanımla ve rastgele bir \(32 \times 32\) RGB tensorle forward pass yap. Çıktının şekli ve toplam parametre sayısını yazdır.

import torch
import torch.nn as nn

cnn = nn.Sequential(
    nn.Conv2d(3, 32, kernel_size=3, padding=1), nn.ReLU(),
    nn.MaxPool2d(2),
    nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.ReLU(),
    nn.MaxPool2d(2),
    nn.AdaptiveAvgPool2d(1),
    nn.Flatten(),
    nn.Linear(64, 10),
)
x = torch.randn(1, 3, 32, 32)        # 1 batch, 3 kanal, 32x32
y = cnn(x)
print("cikti  :", y.shape)
print("toplam :", sum(p.numel() for p in cnn.parameters()))

Egzersiz 5 (Sonraki dersin habercisi). Bu derste ayırt edici (discriminative) bir model kurduk: \(P(\text{sınıf} \mid \text{görüntü})\) öğreniyor. Ders 4’te üretken (generative) modeller var: \(P(\text{görüntü})\) öğrenip yeni görüntüler üretiyor. (a) Bir kediyi tanımak ile yeni bir kedi resmi yaratmak — hangisinin daha büyük bir bilgi gerektireceğini açıkla. (b) \(28 \times 28\) grayscale bir görüntünün taşıyabileceği toplam piksel kombinasyonu sayısını hesapla (\(256^{28 \cdot 28}\)); bu uzayda “gerçekçi rakam” alt kümesinin oranı hakkında ne söyleyebilirsin? Bu, Ders 4’ün manifold hipotezinin habercisidir.

4.14 Sonraki Ders İçin Hazırlık

Ders 4: Derin Üretken Modelleme (Deep Generative Modeling) — Ava Soleimany

Şimdiye kadar her ders tahmin yaptı. Ders 4 farklı bir soruyu cevaplıyor: “Yeni bir şey üretebilir miyim?” Yeni yüz, yeni cümle, yeni protein. Bunun için modelin verinin dağılımı \(P(x)\)’i öğrenmesi ve örnekleme yapması gerekir.

Ana konular:

  • Discriminative vs generative ayrımı; ne öğreniyoruz?
  • Autoencoder ve Variational Autoencoder (VAE): verinin gizli (latent) uzayını öğrenmek.
  • Generative Adversarial Networks (GAN): üretici vs ayırıcı oyunu.
  • Diffusion modelleri: gürültüyü adım adım geri çevirerek üretmek.
UyarıDers 4 öncesi yapılacak
  • Egzersizleri çöz — özellikle 4 (tiny CNN) ve 5 (manifold hipotezi sezgisi).
  • CNN’in feature extractor + head ayrımını kendi cümlenle anlat; bu ayrım Ders 4’teki encoder-decoder mimarisinin habercisi.
  • Ana cümleyi tekrar oku: “CNN, weight sharing + locality ile öznitelik hiyerarşisini veriden öğrenen mimari.”

4.15 Anahtar Kavramlar (Cheat Sheet)

Kavram Tanım Amini’de
Görüntü tensoru Grayscale \(H \times W\) matrisi; RGB \(H \times W \times 3\) tensor 4m42
Hiyerarşik öznitelik Düşük (kenar) → orta (şekil) → yüksek (nesne); veriden öğrenilir 7m23
Flatten problemi 2B’yi 1B’ye sıkıştırmak uzamsal bilgiyi yok eder + param patlatır 12m55
Yerel alıcı alan Bir nöronun yalnızca küçük yamaya bağlı olması 14m58
Convolution Aynı filtreyi her yamada uygulamak: weight sharing + lokallik 21m24
Filtre / Kernel Küçük öğrenilebilir ağırlık matrisi (örn. \(3 \times 3\)) 17m05
Feature map Filtrenin girdide hangi konumlarda tetiklendiğinin haritası 23m38
Stride / padding Adım büyüklüğü / kenar dolgusu — çıktı boyutunu belirler 23m15
ReLU (CNN’de) Negatif aktivasyonları 0’a eşik; her conv sonrası uygulanır 33m55
Max pooling Yerel yamanın maksimumunu al; uzamsal downsampling 35m13
Alıcı alan büyümesi Pooling + derinlik, küçük filtrelerin geniş bölgeleri görmesini sağlar 38m00
CNN mimari Feature extractor (conv+pool yığını) + classifier head 40m52
Multiple filters Bir katmanda \(N\) filtre → \(N\) feature map; çıktı bir volume 32m26
Object detection Bounding box + sınıf; uçtan uca türevlenebilir öneriler 45m54
Semantic segmentation Piksel bazında sınıf etiketi; encoder-decoder (U-Net) 51m20
1D / 3D conv Sıra (ses) ve hacim (video, MR) için aynı operatör 40m32

4.16 ML Builder Bağlantıları

İpucu9 köprü
  1. Görüntü tensoru → 18.06 matris/tensor temsili; çoklu kanal = üst üste binmiş matrisler. İleriye: normalization, transform pipeline.
  2. Convolution = öğrenilebilir lineer operatör → 18.06 lineer dönüşüm (Ders 30); öteleme-değişmez yapı. İleriye: nn.Conv2d, depthwise/grouped varyantlar.
  3. Filtre · yama = dot product → 18.06 iç çarpım, projeksiyon (Ders 1, 15); Ders 2’deki attention’la aynı çekirdek. İleriye: hibrit attention+conv mimariler.
  4. Kenar/sharpening filtreleri → Calculus uzamsal türev (Ders 2, 10) — Sobel = birinci türev, Laplacian = ikinci türev. CNN bunları öğrenir.
  5. Hiyerarşi (kenar → şekil → nesne) → Calculus Ders 4 fonksiyon bileşkesi / zincir kuralı; backprop bu bileşkenin türevini akıtır.
  6. Weight sharing → 18.06 yapısal sınırlama; Ders 2’deki zaman ekseni weight sharing’in uzamsal kardeşi.
  7. Pooling (max / avg) → Stat 110 sıra istatistikleri (max → Ders 25) ve beklenen değer (avg → Ders 9).
  8. Feature extractor + head deseni → 18.06 paylaşılan alt-uzay + farklı projeksiyon kafaları. İleriye: multi-task learning, foundation models, transfer learning.
  9. ViT geçişi → Ders 2’nin attention’ı görüde; iki büyük dersin buluşması. İleriye: foundation görsel modeller, self-supervised pre-training (DINO, MAE, CLIP).
ÖnemliBu dersten tek bir şey alıp gideceksen

CNN bir mimari değil, üç ilkenin somutlaşmasıdır — weight sharing (aynı filtre her yerde), yerellik (her nöron sadece yakın piksellere bakar) ve derinlikle büyüyen alıcı alan (pooling + composition). Bu üçü, görüntülerin doğal istatistiklerine uyan yapısal önyargılardır; dense ağın yapamadığı şeyi yapabilmesinin sebebi tam olarak budur. Aynı çekirdek, attention çağında bile altta yaşamaya devam ediyor.