processed image (8)

Derin Öğrenme ve Bilgisayarlı Görü Mimarileri: Teoriden PyTorch ve Keras Implementasyonlarına

Derin Öğrenme ve Bilgisayarlı Görü Mimarileri: Teoriden PyTorch ve Keras Implementasyonlarına
Yapay Zeka & Derin Öğrenme

Derin Öğrenme ve Bilgisayarlı Görü Mimarileri: Teoriden PyTorch ve Keras Implementasyonlarına

Giriş: Derin Öğrenmenin Derinliklerine Yolculuk

Bilgisayarlı Görü (Computer Vision) alanı, piksel seviyesindeki ham verileri anlamlandırma yolculuğunda son yıllarda devrim niteliğinde bir dönüşüm yaşadı. Geleneksel görüntü işleme metotlarında ve klasik makine öğrenmesi yaklaşımlarında, bir görsel üzerindeki öznitelikleri (edges, corners, textures) elle tanımlamak (hand-crafted features: SIFT, HOG vb.) zorundaydık. Ancak verinin hacmi, çeşitliliği ve karmaşıklığı arttıkça bu manuel öznitelik mühendisliği yaklaşımı yetersiz kalmaya başladı.

İşte tam bu noktada Derin Öğrenme (Deep Learning), ham veriden temsil düzeyinde öznitelikleri kendi kendine, hiyerarşik bir yapıda öğrenebilen (Representation Learning) mimarileriyle sahneye çıktı. Bir AI mühendisi olarak bilgisayarlı görü projelerinde karşılaştığımız temel zorluk, yalnızca veriyi bir modele beslemek değil; problemin doğasına en uygun mimariyi seçmek, matematiksel ve teorik sınırlarını anlamak ve bunu doğru çatılarla (PyTorch, Keras/TensorFlow) uçtan uca prodüksiyon kalitesinde implemente edebilmektir.

Geliştirdiğim açık kaynaklı 5-Deep_Learning Repository projesinden ve sektörel deneyimlerimden yola çıkarak hazırladığım bu kapsamlı rehberde, derin öğrenmenin temel taşlarından en ileri seviye generative (üretken) mimarilerine kadar uzanan 5 ana yapıyı hem teorisiyle hem de pratik kodlama yaklaşımlarıyla ele alacağız:

  1. Evrişimli Sinir Ağları (CNNs): Uzamsal hiyerarşiyi koruyarak öznitelik çıkarımı ve veri artırma (Data Augmentation) teknikleri.
  2. Derin Artık Ağlar (ResNets): Ağı derinleştirdiğimizde karşılaştığımız Kaybolan Gradyan (Vanishing Gradient) probleminin tespiti ve Skip Connections (Kestirme Bağlantılar) mimarisi.
  3. Transfer Öğrenme (Transfer Learning): Devasa veri setlerinde (ImageNet) eğitilmiş ağırlıkları (VGG16, VGG19) kendi problemlerimize adapte etme, Fine-Tuning ve boyut uyumsuzluklarını aşma stratejileri.
  4. Oto-Kodlayıcılar (Autoencoders): Gözetimsiz öğrenme (Unsupervised Learning) ile gizil alan (Latent Space) temsili, boyut indirgeme ve görsel rekonstrüksiyonu.
  5. Çekişmeli Üretken Ağlar (GANs): Game Theory (Oyun Teorisi) prensiplerine dayanan Generator-Discriminator rekabeti ile sentetik veri üretimi.

Bu rehber boyunca sadece teorik denklemlere odaklanmayacak; PyTorch ile nesne yönelimli modüler mimari tasarımlarından Keras ile hızlı prototiplemeye, kızılötesi (IR) yaya tespitinden sentetik görüntü üretimine kadar gerçek senaryolar üzerinden adım adım ilerleyeceğiz.

Bölüm 1: Bilgisayarlı Görünün Temel Taşı: Evrişimli Sinir Ağları (CNNs)

Bilgisayarlı Görü (Computer Vision) projelerinde ham pikselleri doğrudan geleneksel tam bağlantılı (Fully Connected) sinir ağlarına (ANN) beslemek, iki kritik dezavantajı beraberinde getirir: yüksek parametre maliyeti ve uzamsal bağlamın (spatial hierarchy) kaybolması. Görüntü piksel matrisleri, piksel uzayındaki komşuluk ilişkilerini barındırır. Evrişimli Sinir Ağları (Convolutional Neural Networks - CNN), tam olarak bu uzamsal bağımlılıkları korumak ve veriden hiyerarşik öznitelikler çıkarmak için tasarlanmıştır.

Bir AI mühendisi olarak bir CNN mimarisi tasarlarken yapıyı iki ana bloğa ayırırız: Öznitelik Çıkarımı (Feature Extraction) ve Sınıflandırma (Classification). Bu bölümde, teorik temelleri pekiştirmek adına Keras altyapısını kullanarak geliştirdiğimiz iki temel projeyi inceleyeceğiz: Fruit360 (görüntü sınıflandırma ve veri artırma) ve MNIST (sayı sınıflandırma ve batch normalization optimizasyonları).

1.1 Teorik Mimarinin Anatomisi

Bir CNN modelinin çekirdeğinde yer alan katmanlar ve operasyonlar şu şekilde çalışır:

  • Konvolüsyon Katmanı (Conv2D): Girdi matrisi (örneğin 2D/3D görüntüler) üzerinde öğrenilebilir ağırlıklardan oluşan filtreler (kernels) kaydırılarak Feature Map (Öznitelik Haritası) çıktısı üretilir. Bu filtreler, sırasıyla kenarlar, köşeler, dokular ve daha derin katmanlarda nesnenin karmaşık parçalarını (yüksek seviyeli öznitelikler) yakalar.
  • Aktivasyon Fonksiyonları (ReLU): Doğrusal olmayan (non-linear) dönüşümler sağlamak için evrişim katmanlarının ardından ReLU (Rectified Linear Unit) kullanılır. Aksi takdirde üst üste binen evrişim katmanları matematiksel olarak tek bir doğrusal fonksiyona indirgenirdi.
  • Havuzlama Katmanları (Max Pooling): Uzamsal boyutları (spatial dimensions) küçülterek (down-sampling) hesaplama maliyetini düşürür, modelin öznitelik kaymalarına karşı direncini artırır ve Overfitting (aşırı öğrenme) riskini azaltır.
  • Regülasyon ve Düzleştirme (Flatten, Dropout & Batch Normalization): Öznitelik haritaları 1D vektöre dönüştürülmeden önce (Flatten), Dropout katmanlarıyla nöronların rastgele bir yüzdesi (örneğin %20 veya %50) kapatılarak ağın ezber yapması engellenir. Benzer şekilde Batch Normalization ile katman girdileri normalize edilerek eğitim süreci kararlı hale getirilir ve gradyan patlamalarının önüne geçilir.

1.2 Uygulama 1: Fruit360 Veri Seti ile CNN ve Veri Artırma (Data Augmentation)

Derin öğrenme modellerinin başarısı, beslendikleri verinin çeşitliliğine ve miktarına doğrudan bağlıdır. Gerçek dünya senaryolarında her bir sınıftan binlerce kusursuz görüntü bulamayabiliriz. Örneğin Fruit360 veri setinde sınıflar bazında kısıtlı sayıda görsel bulunması, doğrudan eğitime alındığında ağın ezberlemesine (overfitting) yol açabilir.

Bu problemi çözmek için Data Augmentation tekniğini devreye sokuyoruz. Keras kütüphanesindeki ImageDataGenerator sınıfı, mevcut görselleri dinamik olarak ölçeklendirirken (rescale=1./255), aynı zamanda onlara rastgele döndürme, kaydırma (shear_range=0.3), zoom yapma (zoom_range=0.3) ve yatay eksende çevirme (horizontal_flip=True) gibi geometrik dönüşümler uygulayarak modelin genelleştirme yeteneğini katbekat artırır.

Aşağıdaki mimari, 95 farklı meyve sınıfını sınıflandırmak üzere kurgulanmış konvolüsyon bloklarını, Dropout regülasyonunu ve categorical_crossentropy kayıp fonksiyonunu içermektedir:

from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array
from glob import glob

train_path = "fruits-360/Training/"
test_path = "fruits-360/Test/"

# Sınıf sayısının dinamik olarak tespiti
className = glob(train_path + '/*')
numberOfClass = len(className)

model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(100, 100, 3)))
model.add(Activation("relu"))
model.add(MaxPooling2D())

model.add(Conv2D(32, (3, 3)))
model.add(Activation("relu"))
model.add(MaxPooling2D())

model.add(Conv2D(64, (3, 3)))
model.add(Activation("relu"))
model.add(MaxPooling2D())

model.add(Flatten())
model.add(Dense(1024))
model.add(Activation("relu"))
model.add(Dropout(0.5))
model.add(Dense(numberOfClass))
model.add(Activation("softmax"))

model.compile(loss="categorical_crossentropy",
              optimizer="rmsprop",
              metrics=["accuracy"])

Model eğitimi aşamasında, disk I/O yükünü optimize etmek ve bellek sızıntılarını önlemek adına flow_from_directory yapısı kullanılır. Günlerce sürebilecek eğitim döngülerinde elde edilen ağırlıkların (model.save_weights) ve metrik geçmişinin (json & codecs modülleri ile history kayıtları) diske serialize edilmesi, bir AI mühendisi için kritik bir mühendislik refleksidir.

1.3 Uygulama 2: MNIST El Yazısı Rakam Sınıflandırma ve Batch Normalization Entegrasyonu

Görüntü verilerinin CSV tablosu formatından okunup işlenmesi ve tensör formatına getirilmesi (reshape(-1, 28, 28, 1)), bilgisayarlı görü projelerinde sıkça karşılaşılan bir veri mühendisliği adımıdır. MNIST veri setinde, etiketlerin (labels) model tarafından anlaşılabilir hale gelmesi için One-Hot Encoding (to_categorical) dönüşümüne tabi tutulması şarttır.

Aşağıdaki mimaride, evrişim katmanlarının arkasına yerleştirilen BatchNormalization katmanları sayesinde ağın her bir mini-batch için aktivasyonları merkezileştirmesi sağlanmış ve Adam optimizasyon algoritması ile hızlı yakınsama (convergence) elde edilmiştir:

from keras.models import Sequential
from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, Dropout, BatchNormalization
from keras.utils import to_categorical
import pandas as pd
import numpy as np

def load_and_preprocess(data_path):
    data = pd.read_csv(data_path).values
    np.random.shuffle(data)
    x = data[:, 1:].reshape(-1, 28, 28, 1) / 255.0
    y = data[:, 0].astype(np.int32)
    y = to_categorical(y, num_classes=len(set(y)))
    return x, y

# Model Kurulumu
model = Sequential()
model.add(Conv2D(16, (3, 3), input_shape=(28, 28, 1)))
model.add(BatchNormalization())
model.add(Activation("relu"))
model.add(MaxPooling2D())

model.add(Conv2D(64, (3, 3)))
model.add(BatchNormalization())
model.add(Activation("relu"))
model.add(MaxPooling2D())

model.add(Conv2D(128, (3, 3)))
model.add(BatchNormalization())
model.add(Activation("relu"))
model.add(MaxPooling2D())

model.add(Flatten())
model.add(Dense(256))
model.add(Activation("relu"))
model.add(Dropout(0.2))
model.add(Dense(numberOfClass))
model.add(Activation("softmax"))

model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])

Bu kod bloğunda, büyük batch boyutları (batch_size=4000) kullanılarak donanım kaynaklarının paralel işleme gücünden maksimum düzeyde yararlanılmış ve model eğitimi kararlı bir şekilde tamamlanmıştır.

Bölüm 2: Derinlik Problemi ve Çözümü: Deep Residual Networks (ResNets)

Ağları sığ yapılardan daha derin yapılara dönüştürmek, makine öğrenmesinde kapasiteyi artırmanın en doğal yoludur. Ancak evrişimli sinir ağlarında katman sayısı arttıkça teoride beklenen temsil gücü artışı, pratikte beklenmedik bir kilitlenmeyle karşılaşır.

Bu bölümde, derin öğrenme literatürünün dönüm noktalarından biri olan Kaybolan Gradyan (Vanishing Gradient) problemini, Microsoft Research tarafından geliştirilen ResNet (Residual Networks) mimarisini ve bu yapının kızılötesi (IR) yaya tespiti senaryosu üzerinden PyTorch ile nesne yönelimli (OOP) olarak nasıl sıfırdan inşa edildiğini inceleyeceğiz.

2.1 Teorik Altyapı: Kaybolan Gradyan Problemi ve Kestirme Bağlantılar (Skip Connections)

Ağ derinleştirildiğinde yaşanan performans düşüşü yalnızca ezberleme (overfitting) kaynaklı değildir; öyle olsaydı eğitim hatası düşer, yalnızca test hatası yükselirdi. Oysa 50 katmanlı standart bir CNN, 20 katmanlı bir CNN'e kıyasla eğitim kümesinde de daha kötü performans gösterir.

[Standart Katman Akışı] x ───► [ Conv / BN / ReLU ] ───► [ Conv / BN ] ───► f(x) [Residual Block Akışı] x ───┬──► [ Conv / BN / ReLU ] ───► [ Conv / BN ] ───► f(x) │ │ └───────────────────── (Identity) ───────────────────► ( + ) ───► ReLU ───► h(x) = f(x) + x

Vanishing Gradient Matematiksel Özeti: Backpropagation sırasında zincir kuralı (chain rule) gereği türevler geriye doğru çarpılarak aktarılır. Katman sayısı arttıkça 1'den küçük türev değerlerinin çarpımı ($0.1 \times 0.1 \times 0.1 ...$) geriye, yani girdi katmanlarına (low-level features) ulaştığında sıfıra yaklaşır. Bu durum, ilk katmanlardaki ağırlıkların güncellenememesine ve ağın öğrenmeyi durdurmasına neden olur.

Kestirme Bağlantılar (Skip Connections / Shortcuts): ResNet mimarisi, $h(x)$ doğrudan öğrenilmek yerine $f(x) = h(x) - x$ farkını (artık/residual) öğrenmeyi hedefler. Ağ identity mapping ($f(x) = 0$) yapmayı seçtiğinde bile, kestirme bağlantı sayesinde $h(x) = x$ kalarak bilgi ve gradyan aktarımı kesintiye uğramadan alt katmanlara akabilir.

2.2 Veri Mühendisliği ve LSIFIR IR Pedestrian Veri Seti

Uygulamada, kızılötesi (IR - Infrared) kameralardan elde edilmiş LSIFIR (Infrared Pedestrian) veri seti kullanılmıştır. IR görseller, gece görüşü veya zorlu hava koşullarında ısı imzalarını yakalayabildiği için otonom sürüş sistemlerinde hayati önem taşır.

Görseller $64 \times 32$ boyutlarında tek kanallı (grayscale) görüntülerdir. Veri yükleme adımında diski doğrudan okuyup tensör formatına dönüştüren modüler bir boru hattı (pipeline) kurulmuştur:

import os
import numpy as np
import torch
import torch.utils.data
from PIL import Image

def read_images (path, num_img):
    array = np.zeros([num_img, 64*32])
    i = 0
    for img_name in os.listdir(path):
        img_path = os.path.join(path, img_name)
        img = Image.open(img_path, mode='r')
        data = np.asarray(img, dtype="uint8").flatten()
        array[i] = data
        i += 1
    return array

# Pozitif (yaya var) ve Negatif (yaya yok) verilerin yüklenmesi
train_neg_arr = read_images (r"LSIFIR/Classification/Train/neg", 43390)
train_pos_arr = read_images (r"LSIFIR/Classification/Train/pos", 10208)

# Tensör dönüşümleri ve etiketleme (Negatif: 0, Pozitif: 1)
x_train_neg = torch.from_numpy(train_neg_arr[:42000, :])
y_train_neg = torch.zeros(42000, dtype=torch.long)

x_train_pos = torch.from_numpy(train_pos_arr[:10000, :])
y_train_pos = torch.ones(10000, dtype=torch.long)

# Verilerin dikey eksende birleştirilmesi (Concat)
x_train = torch.cat((x_train_neg, x_train_pos), 0)
y_train = torch.cat((y_train_neg, y_train_pos), 0)

# PyTorch DataLoader yapısının kurulması
train_dataset = torch.utils.data.TensorDataset(x_train, y_train)
trainloader = torch.utils.data.DataLoader(train_dataset, batch_size=2000, shuffle=True)

2.3 Standart CNN vs Custom ResNet: PyTorch Nesne Yönelimli Implementasyonu

Karşılaştırma amacıyla önce geleneksel evrişimli bir sinir ağı (Net), ardından modüler BasicBlock bileşenlerini içeren ResNet mimarisi PyTorch ile yazılmıştır.

Standart CNN Mimarisi

Geleneksel mimaride bilgi akışı sıralıdır, katmanlar derinleştikçe gradyan akışı zayıflar:

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(10, 16, 5)
        self.fc1 = nn.Linear(16 * 13 * 5, 520)
        self.fc2 = nn.Linear(520, 130)
        self.fc3 = nn.Linear(130, 2)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 13 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        return self.fc3(x)

Modüler ResNet Mimarisi (BasicBlock & ResNet)

ResNet yapısında temel blok (BasicBlock), iki evrişim katmanı, Batch Normalization, Dropout ve atlamayı sağlayan downsample mekanizmasını barındırır. Boyut veya kanal sayısı değiştiğinde kestirme hattı $1 \times 1$ evrişim (conv1x1) ile hizalanır:

def conv3x3(in_planes, out_planes, stride=1):
    return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False)

def conv1x1(in_planes, out_planes, stride=1):
    return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(BasicBlock, self).__init__()
        self.conv1 = conv3x3(inplanes, planes, stride)
        self.bn1 = nn.BatchNorm2d(planes)
        self.relu = nn.ReLU(inplace=True)
        self.drop = nn.Dropout(0.9)
        self.conv2 = conv3x3(planes, planes)
        self.bn2 = nn.BatchNorm2d(planes)
        self.downsample = downsample

    def forward(self, x):
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)
        out = self.drop(out)
        out = self.conv2(out)
        out = self.bn2(out)
        out = self.drop(out)

        # Boyut hizalama (Downsampling)
        if self.downsample is not None:
            identity = self.downsample(x)

        out += identity # Skip connection toplamı
        out = self.relu(out)
        return out

class ResNet(nn.Module):
    def __init__(self, block, layers, num_classes=2):
        super(ResNet, self).__init__()
        self.inplanes = 64
        self.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        
        self.layer1 = self._make_layer(block, 64, layers[0], stride=1)
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(256 * block.expansion, num_classes)

        # Kaiming Normal Ağırlık İlklendirmesi (Weight Initialization)
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

    def _make_layer(self, block, planes, blocks, stride=1):
        downsample = None
        if stride != 1 or self.inplanes != planes * block.expansion:
            downsample = nn.Sequential(
                conv1x1(self.inplanes, planes * block.expansion, stride),
                nn.BatchNorm2d(planes * block.expansion)
            )
        layers = [block(self.inplanes, planes, stride, downsample)]
        self.inplanes = planes * block.expansion
        for _ in range(1, blocks):
            layers.append(block(self.inplanes, planes))

        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.maxpool(self.relu(self.bn1(self.conv1(x))))
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        return self.fc(x)

# ResNet [2, 2, 2] bloğu ile modelin başlatılması
model = ResNet(BasicBlock, [2, 2, 2])

2.4 Eğitim Döngüsü, Donanım Hızlandırma ve Başarım Analizi

Modelin eğitimi sırasında PyTorch'un torch.no_grad() bloğu kullanılarak doğrulama adımlarında belleği zorlayan gradyan hesaplamaları devre dışı bırakılmıştır. Donanım hızlandırma için CUDA kontrolü (device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')) dinamik olarak boru hattına entegre edilmiştir.

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)

loss_list, train_acc, test_acc = , , 

for epoch in range(100):
    for i, (images, labels) in enumerate(trainloader):
        images = images.view(-1, 1, 64, 32).float()
        
        # Forward pass & Loss
        outputs = model(images)
        loss = criterion(outputs, labels)
        
        # Backward pass & Optimization
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    # Train & Test Doğruluk Değerlendirmesi
    with torch.no_grad():
        correct, total = 0, 0
        for images, labels in testloader:
            images = images.view(-1, 1, 64, 32).float()
            outputs = model(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
        
        test_acc.append(100 * correct / total)
        loss_list.append(loss.item())

Performans Karşılaştırması:

  • Standart CNN: Eğitim ilerledikçe katmanlar arası bilgi kaybı ve gradyan sönümlenmesi sebebiyle başarım belirsiz bir noktada doyuma ulaşır (~%88-%89).
  • Custom ResNet: Skip Connections sayesinde bilgi kaybı engellenmiş, derin mimariye rağmen kararlı yakınsama ile %95+ test başarımı ve belirgin biçimde daha düşük kayıp (loss) değerleri elde edilmiştir.

Bölüm 3: Modelleri Yeniden Kullanma Sanatı: Transfer Learning

Sıfırdan bir derin sinir ağı eğitmek (Training from Scratch); milyonlarca öğrenilebilir parametre, devasa veri setleri ve yüksek hesaplama gücü (GPU/TPU maliyetleri) gerektirir. Ancak pratikte her problem için sıfırdan ağ eğitmek hem zaman hem de kaynak açısından verimsizdir. Bilgisayarlı görme dünyasında bu problemi aşmanın en efektif yolu Transfer Learning (Transfer Öğrenme) yöntemidir.

Bu bölümde; devasa ImageNet veri kümesi üzerinde eğitilmiş VGG16 ve VGG19 mimarilerini, kendi özel problem alanlarımıza (Fruit360 ve CIFAR-10) nasıl adapte edeceğimizi, boyut uyumsuzluklarını OpenCV ile nasıl aşacağımızı ve ağırlıkları dondurma (Layer Freezing) tekniklerini inceleyeceğiz.

3.1 Teorik Altyapı: Pre-trained Ağırlıklar, Freezing ve Fine-Tuning

Transfer Learning'in temel felsefesi şuna dayanır: ImageNet gibi 14 milyonun üzerinde görüntü ve 1000 farklı sınıf içeren veri setlerinde eğitilmiş derin ağlar, kenar (edge), köşe (corner) ve doku (texture) gibi jenerik/genel öznitelikleri son derece başarılı biçimde öğrenmiştir. Ağın alt katmanlarında (low-level/mid-level features) öğrenilen bu jenerik filtreler, yeni bir görüntü sınıflandırma görevinde doğrudan kullanılabilir.

[ Girdi Görüntüsü ] │ ▼ ┌─────────────────────────────────┐ │ Pre-trained Omurga (Backbone) │ ───► Ağırlıklar Dondurulur (trainable = False) │ (VGG16 / VGG19 Convolution) │ Öznitelik Çıkarıcı (Feature Extractor) └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ Yeni Özelleştirilmiş Katmanlar│ ───► Ağırlıklar Eğitilir (trainable = True) │ (Flatten + Dense + Softmax) │ Hedef Sınıf Sayısına Göre Özelleştirme └─────────────────────────────────┘
  • Dondurma (Layer Freezing): Önceden eğitilmiş evrişim omurgasındaki (convolutional backbone) filtrelerin ağırlık güncellemelerini durdurma işlemidir (layer.trainable = False). Böylece geri yayılım (backpropagation) sırasında ImageNet'ten gelen hassas öznitelik süzgeçleri bozulmaz.
  • Tam Bağlantılı Katman Değişimi (Classification Head Replacement): Modelin orijinalindeki 1000 sınıflı Dense çıkış katmanı çıkarılır; yerine kendi problemimizdeki sınıf sayısı kadar (örneğin Fruit360 için 95, CIFAR-10 için 10) yeni katmanlar eklenir.
  • Fine-Tuning: İhtiyaca göre önceden eğitilmiş omurganın son birkaç katmanı çözülerek (unfreeze) çok düşük bir öğrenme oranıyla (learning rate) hedef veri setine özel olarak yeniden eğitilebilir.

3.2 Uygulama 1: Fruit360 Veri Seti ve VGG16 Mimarisi Entegrasyonu

VGG16 mimarisi, ardışık $3 \times 3$ evrişim katmanları ve $2 \times 2$ max pooling katmanlarından oluşan derin ancak sıralı (sequential) bir yapıdır. Bu uygulamada, VGG16 omurgasını alıp son çıktı katmanını dondurarak 95 farklı meyve sınıfı için Keras ile özelleştiriyoruz:

from keras.applications.vgg16 import VGG16
from keras.models import Sequential
from keras.layers import Dense
from keras.preprocessing.image import ImageDataGenerator
from glob import glob
import json, codecs

train_path = "fruits-360/Training/"
test_path = "fruits-360/Test/"

# Sınıf sayısını dinamik çekme (95 sınıf)
numberOfClass = len(glob(train_path + "/*"))

# VGG16 Modelinin Yüklenmesi
vgg = VGG16()
vgg_layer_list = vgg.layers

# Orijinal VGG16'nın son katmanı hariç (Dense-1000) tüm katmanları yeni modele aktarma
model = Sequential()
for i in range(len(vgg_layer_list) - 1):
    model.add(vgg_layer_list[i])

# Pre-trained katmanların ağırlıklarını dondurma
for layer in model.layers:
    layer.trainable = False

# Kendi hedef sınıf sayımıza uygun çıkış katmanını ekleme
model.add(Dense(numberOfClass, activation="softmax"))

model.compile(loss="categorical_crossentropy",
              optimizer="rmsprop",
              metrics=["accuracy"])

# VGG16 varsayılan olarak 224x224 boyutunda girdi kabul eder
train_data = ImageDataGenerator().flow_from_directory(train_path, target_size=(224, 224))
test_data = ImageDataGenerator().flow_from_directory(test_path, target_size=(224, 224))

batch_size = 32

hist = model.fit_generator(
    train_data,
    steps_per_epoch=1600 // batch_size,
    epochs=25,
    validation_data=test_data,
    validation_steps=800 // batch_size
)

# Ağırlıkların ve metrik geçmişinin diske kaydedilmesi
model.save_weights("transfer_vgg16_fruit.h5")
with open("transfer_learning_fruit_hist.json", "w") as f:
    json.dump(hist.history, f)

Katman dondurma işlemi sayesinde eğitilecek parametre sayısı milyonlardan yalnızca son katmandaki ağırlıklara düşürülmüş, eğitim süresi ciddi oranda kısaltılırken yüksek başarım elde edilmiştir.

3.3 Uygulama 2: CIFAR-10, VGG19 ve OpenCV ile Boyut Dönüştürme (Resizing Pipeline)

Transfer öğrenmede en sık karşılaşılan teknik engellerden biri girdi boyutu uyumsuzluğudur. CIFAR-10 veri setindeki görseller $32 \times 32 \times 3$ boyutundayken, VGG19 mimarisi yapısındaki evrişim ve havuzlama katmanlarının eksiltme oranları gereği en az $48 \times 48 \times 3$ boyutunda girdi kabul etmektedir.

Bu problemi çözmek için OpenCV (cv2.resize) kütüphanesinden yararlanarak tüm görüntü matrislerini boyutlandıran ve VGG19'un include_top=False parametresiyle öznitelik çıkarıcı omurgasını oluşturan boru hattı (pipeline) inşa edilmiştir:

import cv2
import numpy as np
import json, codecs
from keras.datasets import cifar10
from keras.utils import to_categorical
from keras.applications.vgg19 import VGG19
from keras.models import Sequential
from keras.layers import Dense, Flatten

# 1. Verinin Yüklenmesi ve Ön İşlenmesi
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
numberOfClass = 10

y_train = to_categorical(y_train, numberOfClass)
y_test = to_categorical(y_test, numberOfClass)

# 2. OpenCV ile Boyut Artırma (Interpolation / Resizing Pipeline)
def resize_img(img):
    numberOfImage = img.shape[0]
    new_array = np.zeros((numberOfImage, 48, 48, 3))
    for i in range(numberOfImage):
        new_array[i] = cv2.resize(img[i, :, :, :], (48, 48))
    return new_array

x_train = resize_img(x_train)
x_test = resize_img(x_test)

# 3. VGG19 Omurgasının Yapılandırılması (include_top = False)
vgg = VGG19(include_top=False, weights="imagenet", input_shape=(48, 48, 3))

model = Sequential()
for layer in vgg.layers:
    model.add(layer)

# VGG19 Katmanlarının Dondurulması
for layer in model.layers:
    layer.trainable = False

# Özel Sınıflandırma Kafasının (Classification Head) Eklenmesi
model.add(Flatten())
model.add(Dense(128))
model.add(Dense(numberOfClass, activation="softmax"))

model.compile(loss="categorical_crossentropy",
              optimizer="rmsprop",
              metrics=["accuracy"])

# Model Eğitimi
hist = model.fit(x_train, y_train, validation_split=0.2, epochs=5, batch_size=1000)

# Eğitim ve Metrik Geçmişinin Saklanması
model.save_weights("transfer_vgg19_cifar10.h5")
with open("transfer_learning_vgg19_cfar10.json", "w") as f:
    json.dump(hist.history, f)

3.4 Deneysel Sonuçlar ve Performans Metrikleri

VGG16 ve VGG19 modelleri ile yapılan transfer öğrenme deneylerinde şu kritik bulgular elde edilmiştir:

  • Eğitim Hızı ve Yakınsama (Convergence): Sıfırdan eğitilen CNN modellerine kıyasla, Transfer Learning modelleri ilk 3-5 epoch içerisinde %90'ın üzerinde eğitim doğruluğuna (train accuracy) ulaşmaktadır.
  • Overfitting Kontrolü: ImageNet zenginliğinden gelen genel filtreler sayesinde küçük veri setlerinde (küçük batch boyutlarında dahi) aşırı öğrenme riski minimize edilmiştir.
  • Boyut Dönüştürme Etkisi: OpenCV ile $32 \times 32 \rightarrow 48 \times 48$ boyut dönüşümü interpolation kaynaklı hafif bilgi bulanıklaşmasına yol açsa da, VGG19 omurgasının temsil gücü sayesinde sınıflandırma başarımı yüksek seviyede korunmuştur.

Bölüm 4: Gözetimsiz Öğrenme ve Temsil Tespiti: Autoencoders

Şimdiye kadar ele aldığımız evrişimli sinir ağları ve transfer öğrenme mimarileri, veri setinde açıkça tanımlanmış etiketlerin (labels) bulunduğu Gözetimli Öğrenme (Supervised Learning) prensibine dayanıyordu. Ancak gerçek dünya senaryolarında elimizdeki devasa verilerin etiketlenmesi son derece maliyetlidir ya da etiket kavramının olmadığı problemlerle karşılaşırız.

İşte bu noktada, verinin kendi içsel yapısını ve temsil gücünü etiketlere ihtiyaç duymadan öğrenen Gözetimsiz Öğrenme (Unsupervised Learning) mimarilerinden Autoencoder (Oto-Kodlayıcı) ağları devreye girer.

4.1 Teorik Altyapı: Encoder-Decoder Mimarisi, Latent Space ve Sıkıştırma

Bir Autoencoder mimarisinin temel amacı; girdiyi ($x$) alır, onu daha düşük boyutlu gizil bir alana (Latent Space / Code) sıkıştırır ve ardından bu sıkıştırılmış koddan orijinal girdiyi ($x'$) mümkün olan en az bilgi kaybıyla yeniden inşa eder ($x \approx x'$).

┌──────────────┐ ┌────────────────────────┐ ┌──────────────┐ │ Girdi (x) │ ──────► │ Encoder (Sıkıştırma) │ ──────► │ Bottleneck │ │ (784 Boyut) │ │ (Dense: 784 -> 32 -> 16)│ │ (Latent: 16) │ └──────────────┘ └────────────────────────┘ └──────────────┘ │ ┌──────────────┐ ┌────────────────────────┐ │ │ Çıktı (x') │ ◄────── │ Decoder (Rekonstrüksiyon)◄───────────────┘ │ (784 Boyut) │ │ (Dense: 16 -> 32 -> 784)│ └──────────────┘ └────────────────────────┘

Autoencoder üç ana bileşenden oluşur:

  1. Encoder (Kodlayıcı): Yüksek boyutlu girdi verisini ($x \in \mathbb{R}^d$) kademeli olarak daha düşük boyutlu bir gizil temsil $h = f(x)$ alanına indirger. Bu işlem, verideki gereksiz gürültüleri filtreler ve yalnızca en baskın öznitelikleri (principal features) tutmaya zorlar.
  2. Bottleneck / Latent Space (Darboğaz / Gizil Alan): Ağın bilgi akışındaki en dar noktasıdır ($h \in \mathbb{R}^k, k \ll d$). Eğer ağ darboğaza uğratılmazsa ($k \ge d$) girdi doğrudan çıktıya kopyalanır (identity mapping) ve ağ hiçbir öznitelik öğrenemez.
  3. Decoder (Kod Çözücü): Gizil alandaki sıkıştırılmış temsili alarak $x' = g(h)$ dönüşümü ile orijinal girdiyi yeniden inşa etmeye çalışır.

Kayıp Fonksiyonu (Reconstruction Loss): Ağın eğitimi, etiketler ($y$) ile tahminler ($\hat{y}$) arasındaki farkı değil; orijinal girdi ($x$) ile rekonstrükte edilen çıktı ($x'$) arasındaki Binary Cross-Entropy veya Mean Squared Error (MSE) kayıp değerini minimize ederek gerçekleştirilir ($L(x, g(f(x)))$).

4.2 Veri Ön İşleme ve Düzleştirme Pipeline'ı (Fashion MNIST)

Fashion MNIST veri seti; $28 \times 28$ boyutlarında, tek kanallı 60.000 eğitim ve 10.000 test giysi görüntüsünden oluşur. Autoencoder modeline tam bağlantılı katmanlar (Dense) üzerinden besleneceği için veri matrisleri normalize edilir ([0, 1] aralığına getirilir) ve 784 elemanlı tek boyutlu vektörlere dönüştürülür (flatten):

import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import fashion_mnist

# 1. Verinin Yüklenmesi
(x_train, _), (x_test, _) = fashion_mnist.load_data()

# 2. Normalizasyon ([0, 255] -> [0.0, 1.0])
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0

# 3. Düzleştirme (28x28 -> 784 Vektör)
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))

print("x_train shape:", x_train.shape) # (60000, 784)

4.3 Keras ile Derin Autoencoder Modeli İnşası

Aşağıdaki mimaride, 784 boyutlu bir girdi görüntüsü kademeli olarak 784 -> 32 -> 16 boyutuna indirgenmekte (Encoder), ardından 16 -> 32 -> 784 boyutuna genişletilerek (Decoder) rekonstrüksiyon gerçekleştirilmektedir. Çıkış katmanında sigmoid aktivasyon fonksiyonu kullanılmıştır, çünkü girdiler 0 ile 1 arasında normalize edilmiştir ve çıktıların da aynı aralıkta kalarak olasılıksal piksel değerlerine dönüştürülmesi gerekir:

from keras.models import Model
from keras.layers import Input, Dense
import json, codecs

# Girdi Katmanı (784 Vektör)
input_img = Input(shape=(784,))

# ENCODER BLOK
encoded = Dense(32, activation="relu")(input_img)
encoded = Dense(16, activation="relu")(encoded) # Bottleneck / Latent Space (16 Boyut)

# DECODER BLOK
decoded = Dense(32, activation="relu")(encoded)
decoded = Dense(784, activation="sigmoid")(decoded) # Orijinal Boyuta Dönüş

# Tam Autoencoder Modeli
autoencoder = Model(input_img, decoded)

# Model Derleme (Binary Cross-Entropy Loss)
autoencoder.compile(optimizer="rmsprop", loss="binary_crossentropy")

# Model Eğitimi (Girdi Çıktı -> x_train, x_train)
hist = autoencoder.fit(
    x_train,
    x_train,
    epochs=200,
    batch_size=256,
    shuffle=True,
    validation_data=(x_test, x_test)
)

# Ağırlıkların ve Metrik Geçmişinin Kaydedilmesi
autoencoder.save_weights("autoencoder_model.h5")
with open("autoencoders_hist.json", "w") as f:
    json.dump(hist.history, f)

Eğitim sırasında validation_data kısmına etiket yerine yine (x_test, x_test) geçirilmiştir. Bu, ağın daha önce hiç görmediği görselleri ne kadar başarılı rekonstrükte edebildiğini ölçecektir.

4.4 Gizil Alan Temsili (Latent Space) ve Rekonstrüksiyon Analizi

Autoencoder eğitildikten sonra, yalnızca Encoder kısmını ayrı bir model olarak ayırarak görsellerin 16 boyutlu gizil alandaki sıkıştırılmış kodlarını inceleyebiliriz (4x4 piksel):

# 1. Sadece Encoder Modelinin Ayrıştırılması
encoder = Model(input_img, encoded)
encoded_imgs = encoder.predict(x_test)

# Orijinal Görsel (28x28 = 784 Piksel)
plt.imshow(x_test[1500].reshape(28, 28), cmap="gray")
plt.axis("off")
plt.show()

# Gizil Alandaki Sıkıştırılmış Temsil (4x4 = 16 Piksel)
plt.figure()
plt.imshow(encoded_imgs[1500].reshape(4, 4), cmap="gray")
plt.axis("off")
plt.show()

# 2. Tam Autoencoder Rekonstrüksiyon Testi
decoded_imgs = autoencoder.predict(x_test)

n = 10 # Gösterilecek görsel sayısı
plt.figure(figsize=(20, 4))
for i in range(n):
    # Orijinal Test Görselleri
    ax = plt.subplot(2, n, i + 1)
    plt.imshow(x_test[i].reshape(28, 28), cmap="gray")
    plt.axis("off")

    # Rekonstrükte Edilmiş Çıktılar
    ax = plt.subplot(2, n, i + n + 1)
    plt.imshow(decoded_imgs[i].reshape(28, 28), cmap="gray")
    plt.axis("off")
plt.show()

Rekonstrüksiyon Sonuçları ve Değerlendirme:

  • Bilgi Sıkıştırma Başarımı: 784 piksellik bir görüntü, yalnızca 16 piksellik gizil alana (%97.9 boyut indirgeme) sıkıştırılmasına rağmen; ayakkabı, çanta ve tişört gibi giysilerin genel konturları ve siluetleri yüksek başarıyla yeniden inşa edilmiştir.
  • Gürültü Temizleme (Denoising Potansiyeli): Sıkıştırma esnasında detay pikseller filtrelendiği için Autoencoder'lar; sinyal işleme, görüntüdeki pürüzleri/gürültüleri giderme (Denoising) ve anomali tespiti (Anomaly Detection) gibi kritik mühendislik problemlerinde standart bir çözüm haline gelmiştir.

Bölüm 5: Gerçekçi Veri Üretimi: Generative Adversarial Networks (GANs)

Derin öğrenme dünyasında modelleri genel olarak iki ana sınıfa ayırırız: Verilen bir girdiden etiket veya öznitelik tahmini yapan Ayrıştırıcı/Ayırt Edici (Discriminative) modeller ile verinin dağılımını öğrenerek var olmayan yeni örnekler üreten Üretken (Generative) modeller. 2014 yılında Ian Goodfellow ve ekibi tarafından literatüre kazandırılan Generative Adversarial Networks (GAN - Çekişmeli Üretken Ağlar), bilgisayarlı görü ve yapay zekâ dünyasında paradigmaları değiştiren bir dönüm noktası olmuştur.

Bu bölümde; Oyun Teorisi (Game Theory) prensiplerine dayanan GAN mimarisinin teorik dinamiklerini, Generator (Kalpazan) ve Discriminator (Dedektif) ağları arasındaki Min-Max çekişmesini ve Keras kütüphanesi kullanarak MNIST el yazısı rakam veri seti üzerinde sıfırdan sentetik görsel üretim adımlarını ele alacağız.

5.1 Teorik Altyapı: Adversarial Training ve Oyun Teorisi (Min-Max Game)

GAN mimarisi, iki farklı yapay sinir ağının birbiriyle sıfır toplamlı bir oyun (zero-sum game) çerçevesinde rekabet ettiği sıradışı bir eğitim dinamiğine sahiptir.

Gürültü Vektörü z ~ N(0,1) │ ▼ [ Generator Network (G) ] ───► Sentetik Görüntü (G(z)) │ ▼ [ Gerçek Veri Seti (x_train)] ──► [ Discriminator Network (D) ] ──► Tahmin: Gerçek (1) mi? Sahte (0) mı?

İki Ana Ağın Rolü ve Analojisi:

  1. Generator (Üreteç - Kalpazan): Rastgele bir gürültü vektörünü ($z \sim \mathcal{N}(0, 1)$) girdi olarak alır ve bunu gerçek veri kümesinin dağılımına ($p_{data}$) benzeyen sentetik görsellere ($G(z)$) dönüştürür. Amacı, ürettiği sahte görsellerin etiketini Discriminator'a "Gerçek (1)" olarak kabul ettirmektir.
  2. Discriminator (Ayırt Edici - Dedektif): Önüne gelen bir görselin gerçek veri kümesinden mi yoksa Generator tarafından üretilmiş sahte bir görsel mi ($G(z)$) olduğunu ayırt etmeye çalışan bir sınıflandırıcıdır (binary classifier). Amacı, gerçek görsellere "1", sahte görsellere "0" etiketini doğru bir şekilde basmaktır.

Matematiksel Oyun Formülasyonu:

GAN eğitimi, $V(D, G)$ değer fonksiyonu (value function) üzerinden tanımlanan bir Minimax Oyunudur:

$$\min_{G} \max_{D} V(D, G) = \mathbb{E}_{x \sim p_{data}(x)}[\log D(x)] + \mathbb{E}_{z \sim p_{z}(z)}[\log (1 - D(G(z)))]$$

  • Discriminator'ın Amacı ($\max_{D}$): Gerçek veri $x$ için $D(x) \rightarrow 1$ yaparken, sahte veri $G(z)$ için $D(G(z)) \rightarrow 0$ yapmak; yani $V(D, G)$ değerini maksimize etmektir.
  • Generator'ın Amacı ($\min_{G}$): $D(G(z)) \rightarrow 1$ yapmaya çalışarak $1 - D(G(z)) \rightarrow 0$ olmasını sağlamak, yani Discriminator'ı yanıltıp $V(D, G)$ değerini minimize etmektir.

Eğitim kararlı bir dengeye ulaştığında (Nash Dengesi), Generator gerçek verinin istatistiksel dağılımını tam olarak öğrenmiş olur ($p_{g} = p_{data}$) ve Discriminator sahte ile gerçeği ayırt edemez hale gelir ($D(x) = 0.5$).

5.2 Veri Ön İşleme ve Normalizasyon Pipeline'ı

MNIST el yazısı rakam veri setindeki görüntüler $28 \times 28$ boyutundadır ve gri tonlamalı pikselleri 0, 255 aralığındadır. GAN mimarilerinde Generator'ın çıkış katmanında tanh aktivasyon fonksiyonu tercih edilir; çünkü tanh, çıkışlarını [-1, 1] aralığına sıkıştırarak gradyan akışını daha kararlı tutar. Dolayısıyla, girdi verileri de [-1, 1] aralığına ölçeklendirilir ve 784 elemanlı vektörlere dönüştürülür:

import numpy as np
from keras.datasets import mnist

# 1. Veri Yükleme
(x_train, _), _ = mnist.load_data()

# 2. Normalizasyon: [0, 255] -> [-1, 1] aralığına dönüştürme (tanh uyumu)
x_train = (x_train.astype(np.float32) - 127.5) / 127.5

# 3. Düzleştirme (28x28 -> 784 Vektör)
x_train = x_train.reshape(x_train.shape[0], x_train.shape[1] * x_train.shape[2])

print("İşlenmiş Veri Şekli:", x_train.shape) # (60000, 784)

5.3 Mimarinin İnşası: Generator, Discriminator ve Birleşik GAN Modeli

Keras ile GAN kurarken en kritik mühendislik detayı, eğitim aşamalarında ağların ağırlık dondurma (trainable = False) durumlarını doğru yönetmektir.

1. Generator Ağının Kurulumu

100 boyutlu rastgele normal dağılımlı gürültü vektörünü girdi alıp 784 boyutlu sentetik görüntü üreten ağdır:

from keras.models import Sequential
from keras.layers import Dense, ReLU
from keras.optimizers import Adam

def create_generator():
    generator = Sequential()
    generator.add(Dense(units=512, input_dim=100))
    generator.add(ReLU())
    
    generator.add(Dense(units=512))
    generator.add(ReLU())
    
    generator.add(Dense(units=1024))
    generator.add(ReLU())
    
    # Çıkış katmanı: 784 piksel ve [-1, 1] aralığı için tanh
    generator.add(Dense(units=784, activation="tanh"))
    
    generator.compile(loss="binary_crossentropy",
                      optimizer=Adam(lr=0.0001, beta_1=0.5))
    return generator

g = create_generator()

2. Discriminator Ağının Kurulumu

784 boyutlu görüntüyü alıp görüntünün gerçeklik olasılığını ([0, 1] aralığında) veren binary sınıflandırıcıdır:

from keras.layers import Dropout

def create_discriminator():
    discriminator = Sequential()
    discriminator.add(Dense(units=1024, input_dim=784))
    discriminator.add(ReLU())
    discriminator.add(Dropout(0.4))
    
    discriminator.add(Dense(units=512))
    discriminator.add(ReLU())
    discriminator.add(Dropout(0.4))
    
    discriminator.add(Dense(units=256))
    discriminator.add(ReLU())
    
    # Çıkış katmanı: Gerçeklik olasılığı için Sigmoid (1 veya 0)
    discriminator.add(Dense(units=1, activation="sigmoid"))
    
    discriminator.compile(loss="binary_crossentropy",
                          optimizer=Adam(lr=0.0001, beta_1=0.5))
    return discriminator

d = create_discriminator()

3. Birleşik GAN (Combined GAN) Modelinin Kurulumu

Generator'ı eğitirken, ürettiği sahte görseller Discriminator'dan geçirilir. Ancak sadece Generator ağırlıkları güncelleneceği için bu adımda Discriminator dondurulur (d.trainable = False):

from keras.models import Model
from keras.layers import Input

def create_gan(discriminator, generator):
    # GAN eğitimi sırasında Discriminator ağırlıklarının değişmesini engelleme
    discriminator.trainable = False
    
    gan_input = Input(shape=(100,))
    x = generator(gan_input)
    gan_output = discriminator(x)
    
    gan = Model(inputs=gan_input, outputs=gan_output)
    gan.compile(loss="binary_crossentropy", optimizer="adam")
    return gan

gan = create_gan(d, g)

5.4 Adversarial Eğitim Döngüsü ve Sentetik Görsel Üretimi

GAN eğitimi geleneksel model.fit() metodolojisiyle yapılamaz; çünkü her döngüde adım adım iki ayrı ağ için eğitim adımları işletilmelidir:

  1. Adım - Discriminator Eğitimi (train_on_batch): Gerçek görüntüler ($y=1$) ve sahte görüntüler ($y=0$) birleştirilerek Discriminator eğitilir.
  2. Adım - Generator Eğitimi (train_on_batch): Rastgele gürültü birleşik GAN modeline beslenir, hedefler bilinçli şekilde $y=1$ verilerek Generator'ın Discriminator'ı kandırma yönündeki kayıp değeri üzerinden ağırlıkları güncellenir.
import matplotlib.pyplot as plt

epochs = 50
batch_size = 256

for e in range(epochs):
    for _ in range(batch_size):
        # 1. SAHTE VE GERÇEK VERİ BATCH'LERİNİ HAZIRLAMA
        noise = np.random.normal(0, 1, [batch_size, 100])
        generated_images = g.predict(noise)
        
        image_batch = x_train[np.random.randint(low=0, high=x_train.shape[0], size=batch_size)]
        
        # Gerçek ve sahte verileri birleştirme
        x = np.concatenate([image_batch, generated_images])
        
        # Etiketler: Gerçekler için 1, Sahteler için 0
        y_dis = np.zeros(batch_size * 2)
        y_dis[:batch_size] = 1
        
        # 2. DISCRIMINATOR EĞİTİMİ
        d.trainable = True
        d.train_on_batch(x, y_dis)
        
        # 3. GENERATOR EĞİTİMİ (Discriminator dondurulur)
        noise = np.random.normal(0, 1, [batch_size, 100])
        y_gen = np.ones(batch_size) # Discriminator'ı kandırma hedefi (1)
        
        d.trainable = False
        gan.train_on_batch(noise, y_gen)
        
    print(f"Epoch: {e+1}/{epochs} tamamlandı.")

# Model Ağırlıklarının Saklanması
g.save_weights('gans_model.h5')

# Sentetik Görsel Üretimi ve Görselleştirme
noise = np.random.normal(loc=0, scale=1, size=[100, 100])
generated_images = g.predict(noise)
generated_images = generated_images.reshape(100, 28, 28)

plt.imshow(generated_images[66], cmap='gray', interpolation='nearest')
plt.axis('off')
plt.title("GAN Tarafından Üretilen Sentetik Rakam")
plt.show()

Eğitim Analizi ve Karşılaşılan Zorluklar:

  • Mode Collapse (Mod Çökmesi): Generator'ın Discriminator'ı kandırmanın kolay bir yolunu bulup yalnızca tek bir el yazısı rakam tipini (örneğin sadece "1") üretmeye başlaması durumudur. Bunu engellemek için mimaride Dropout katmanları ve özel optimizer parametreleri (Adam(beta_1=0.5)) kullanılmıştır.
  • Sentetik Üretim Kalitesi: Başlangıçta anlamsız rastgele gürültüler üreten Generator, Adversarial feedback halkası sayesinde 50 epoch sonunda gerçeğinden ayırt edilmesi güç net el yazısı rakam örneklerini sıfırdan oluşturmayı başarmıştır.

Sonuç ve Gelecek Perspektifi

Bu rehberde, bilgisayarlı görü (Computer Vision) ve derin öğrenme alanındaki en kritik 5 temel mimariyi; matematiksel teorisinden PyTorch ve Keras implementasyonlarına, veri ön işleme boru hatlarından (pipeline) model değerlendirme metriklerine kadar uçtan uca ele aldık. Bir Yapay Zekâ Mühendisi olarak gerçek dünya problemlerine yaklaşırken en önemli beceri, sadece bir modeli eğitmek değil; verinin yapısına, hacmine ve problemin kısıtlarına en uygun mimariyi doğru seçebilmektir.

Hangi Problem İçin Hangi Mimari?

Geliştirdiğimiz projeler doğrultusunda mimarilerin sektörel kullanım senaryolarını şu şekilde özetleyebiliriz:

Mimari Temel Odak Noktası Öne Çıkan Kullanım Senaryosu
CNNs Uzamsal hiyerarşik öznitelik çıkarımı. Yeterli verinin bulunduğu temel görüntü sınıflandırma projeleri.
ResNets Vanishing Gradient problemini aşma, derin ağlar. Karmaşık, derinlik gerektiren tespit ve sınıflandırma (ör. IR yaya tespiti).
Transfer Learning Önceden eğitilmiş ağırlıkları yeniden kullanma. Sınırlı veri setlerinde hızlı prototipleme ve yüksek doğruluk ihtiyacı.
Autoencoders Gözetimsiz temsil öğrenimi ve boyut indirgeme. Görüntü gürültüsü temizleme (denoising), anomali tespiti, gizil alan çıkarımı.
GANs Dağılım öğrenerek sentetik veri üretimi. Veri çoğaltma, eksik veri tamamlama, gerçekçi görsel üretimi.

Üretim Ortamı (Production) ve Mühendislik Tavsiyeleri

Proje geliştirme süreçlerinde edindiğimiz tecrübeler ışığında dikkat edilmesi gereken temel mühendislik ilkeleri şunlardır:

  • Modüler Kod Tasarımı: PyTorch gibi çatılarda nesne yönelimli (OOP) yapılar kullanarak BasicBlock ve ResNet gibi modüler katmanlar yazmak, kodun yeniden kullanılabilirliğini ve sürdürülebilirliğini artırır.
  • Boyut ve Donanım Hizalaması: Farklı mimarilerin girdi beklentileri (VGG19 için $48 \times 48$ vb.) OpenCV veya Keras preprocessing katmanlarıyla uyumlu hale getirilmelidir. Büyük veri setlerinde bellek (RAM/VRAM) taşmalarını önlemek için DataLoader veya ImageDataGenerator gibi jeneratör yapıları tercih edilmelidir.
  • Ağırlık ve Metrik Saklama Refleksi: Uzun süren eğitim süreçlerinde her epoch sonunda ağırlıkları (.h5 veya .pt) ve metrik geçmişini (json) serialize etmek, olası kesintilerde iş gücü kaybını engeller.

Açık Kaynak İnceleme ve İletişim

Bu rehber boyunca incelediğimiz tüm PyTorch ve Keras kodlarına, veri işleme betiklerine ve model mimarilerine açık kaynaklı GitHub depom üzerinden erişebilirsiniz:

GitHub Reposu: Rasittekin18/5-Deep_LearningAdvanced

Derin öğrenme dünyası, her geçen gün gelişen mimarilerle devrim yaratmaya devam ediyor. Teoriyi pratikle birleştiren bu tarz projeler geliştirmek, algoritmanın arka planındaki matematiğe hâkim olmanın en sağlam yoludur. Projelerle ilgili sorularınızı veya katkılarınızı GitHub reposu üzerinden iletebilirsiniz.

Yorum bırakın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir