TensorFlow Kullanarak Basit Bir Üretken Çekişmeli Ağ (GAN) İnşa Etmek
Giriş
Yapay zeka ve makine öğrenimi alanındaki gelişmeler, bilgisayarların sadece veriyi analiz etmekle kalmayıp, aynı zamanda yeni ve özgün veriler üretebilme yeteneğini de ortaya çıkarmıştır. Bu yeteneğin en çarpıcı örneklerinden biri, Ian Goodfellow ve ekibi tarafından 2014 yılında tanıtılan Üretken Çekişmeli Ağlar (Generative Adversarial Networks – GAN’lar) olmuştur. GAN’lar, özellikle gerçekçi görüntüler, sesler veya metinler üretme konusunda devrim niteliğinde bir potansiyele sahiptir. Bu teknoloji, sentetik veri üretimi, görüntüden görüntüye çeviri, süper çözünürlük gibi birçok farklı alanda çığır açmıştır.
Bir GAN, temelde birbiriyle rekabet eden iki sinir ağından oluşur: bir Üretici (Generator) ve bir Ayırt Edici (Discriminator). Bu iki ağ, bir “kedi fare oyunu” benzeri bir senaryoda birlikte eğitilir. Üretici, ayırt ediciyi kandırmak amacıyla gerçekçi görünümlü veri örnekleri üretmeye çalışırken, ayırt edici ise üretici tarafından oluşturulan sahte verileri gerçek verilerden ayırt etmeye çalışır. Bu çekişmeli eğitim süreci, her iki ağın da zamanla daha iyi performans göstermesini sağlar, ta ki üretici ayırt ediciyi kandırabilecek kadar gerçekçi veriler üretebilir hale gelene kadar.
Bu makalede, TensorFlow kütüphanesini kullanarak basit bir GAN modelini sıfırdan nasıl inşa edeceğimizi adım adım inceleyeceğiz. Amacımız, temel GAN mimarisini anlamak, TensorFlow’un Keras API’si ile model bileşenlerini tanımlamak ve MNIST veri kümesi üzerinde sayısal görüntüler üretebilen bir GAN’ı eğitmek olacaktır. Bu süreç boyunca, GAN’ların çalışma prensiplerini, kayıp fonksiyonlarını, optimizasyon stratejilerini ve eğitim döngüsünü derinlemesine ele alacağız.
GAN’ların Temel Yapısı
Bir Üretken Çekişmeli Ağ (GAN), adından da anlaşılacağı gibi, “üretken” ve “çekişmeli” olmak üzere iki ana bileşenden oluşur. Bu bileşenler, özel bir eğitim stratejisiyle bir araya gelerek birbiriyle rekabet eder.
Üretici (Generator)
Üretici ağın temel görevi, rastgele bir gürültü vektörünü (genellikle latent space olarak adlandırılan düşük boyutlu bir vektör) alıp, hedef veri kümesindeki örnekleri taklit eden yeni veri örnekleri üretmektir. Örneğin, bir görüntü GAN’ında, üretici rastgele bir sayı dizisini alarak gerçekçi bir görüntü oluşturmaya çalışır. Bu ağ, genellikle bir dizi yoğun (Dense) katman veya evrişimli (Convolutional) katmanlardan oluşur. Görüntü üretimi söz konusu olduğunda, üretici genellikle Conv2DTranspose (deconv) katmanlarını kullanarak düşük boyutlu bir tensörü kademeli olarak daha yüksek boyutlu bir görüntüye dönüştürür.
Üreticinin çıktı katmanı, üretilen verinin türüne bağlı olarak farklı aktivasyon fonksiyonları kullanabilir. Görüntüler için genellikle tanh aktivasyon fonksiyonu tercih edilir, çünkü bu fonksiyon çıktı değerlerini -1 ile 1 arasına sıkıştırır, bu da genellikle görüntü piksellerinin normalize edilmiş aralığına uygundur. Ara katmanlarda ise LeakyReLU veya ReLU gibi aktivasyon fonksiyonları kullanılır. Üreticinin amacı, ayırt ediciyi kandıracak kadar inandırıcı sahte veriler üretmektir.
Ayırt Edici (Discriminator)
Ayırt edici ağın görevi, kendisine sunulan bir veri örneğinin gerçek mi yoksa üretici tarafından üretilmiş sahte bir örnek mi olduğunu ayırt etmektir. Bu, ikili bir sınıflandırma problemidir. Ayırt edici, bir veri örneğini girdi olarak alır ve bu örneğin gerçek olma olasılığını temsil eden tek bir skaler değer (genellikle 0 ile 1 arasında) döndürür. 1’e yakın değerler örneğin gerçek olduğuna, 0’a yakın değerler ise sahte olduğuna işaret eder.
Ayırt edici, genellikle hedef veri kümesinin özelliklerini çıkarabilen bir dizi evrişimli (Conv2D) katmandan veya yoğun (Dense) katmandan oluşur. Görüntüler için Conv2D katmanları, görüntüdeki özellikleri öğrenmek için kullanılır. Çıkış katmanı, ikili sınıflandırma için sigmoid aktivasyon fonksiyonunu kullanır. Ara katmanlarda LeakyReLU gibi aktivasyon fonksiyonları yaygın olarak kullanılır ve genellikle Dropout katmanları, aşırı öğrenmeyi (overfitting) önlemek için eklenir. Ayırt edicinin amacı, üreticinin ürettiği sahte verileri mümkün olduğunca doğru bir şekilde tespit etmektir.
Çekişmeli Süreç (Adversarial Process)
GAN’ların gücü, üretici ve ayırt edici arasındaki çekişmeli eğitim sürecinden gelir. Bu süreç, bir oyun teorisi senaryosuna benzetilebilir:
1. Üretici Eğitimi: Üretici, rastgele gürültüden sahte veriler üretir. Bu sahte veriler, ayırt ediciye sunulur. Üreticinin amacı, ayırt edicinin bu sahte verileri “gerçek” olarak sınıflandırmasını sağlamaktır. Üretici, ayırt edicinin kendi sahte çıktılarına verdiği düşük “gerçek” puanlarını yükseltmek için ağırlıklarını günceller.
2. Ayırt Edici Eğitimi: Ayırt ediciye hem gerçek veri kümesinden örnekler hem de üretici tarafından üretilen sahte örnekler sunulur. Ayırt edicinin amacı, gerçek örnekleri “gerçek” (1) olarak, sahte örnekleri ise “sahte” (0) olarak doğru bir şekilde sınıflandırmaktır. Ayırt edici, bu sınıflandırma hatasını minimize etmek için ağırlıklarını günceller.
Bu iki aşama, bir eğitim döngüsü içinde ardışık olarak tekrarlanır. Her döngüde, üretici sahte verileri daha inandırıcı hale getirmeye çalışırken, ayırt edici de bu sahtelikleri tespit etmede daha yetenekli hale gelir. Bu süreç, bir denge noktasına ulaşana kadar devam eder. İdeal denge noktasında, üretici o kadar gerçekçi veriler üretebilir ki, ayırt edici artık gerçek ve sahte veriler arasında rastgele tahmin yapmaktan daha iyi performans gösteremez (yani %50 doğruluk oranına ulaşır). Bu durum, Nash dengesi olarak bilinir.
TensorFlow ile GAN Uygulaması
Şimdi, TensorFlow ve Keras API’sini kullanarak basit bir GAN modelini nasıl inşa edeceğimizi adım adım inceleyelim. MNIST veri kümesini kullanarak el yazısı rakamlar üreteceğiz.
Geliştirme Ortamı ve Kütüphaneler
Bu uygulama için Python, TensorFlow, Keras API’si, NumPy ve Matplotlib kütüphanelerine ihtiyacımız olacak.
import tensorflow as tf
from tensorflow.keras import layers
import numpy as np
import matplotlib.pyplot as plt
import os
import time
Veri Kümesi
MNIST veri kümesi, 28×28 piksellik gri tonlamalı el yazısı rakam görüntülerinden oluşur. Basit yapısı nedeniyle GAN’ları öğrenmek için harika bir başlangıç noktasıdır.
# MNIST veri kümesini yükle ve hazırla
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()
Görüntüleri -1 ile 1 arasına normalize et
Üreticinin tanh aktivasyonunu kullanması için bu aralık daha uygun
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5 # Normalizasyon [-1, 1] aralığına
Veri kümesini karıştır ve batch'lere ayır
BUFFER_SIZE = 60000
BATCH_SIZE = 256
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
Burada, MNIST görüntülerini yükleyip yeniden şekillendiriyoruz (60000, 28, 28, 1) ve piksel değerlerini 0-255 aralığından -1 ile 1 aralığına normalize ediyoruz. Bu normalizasyon, üreticinin çıktı katmanındaki tanh aktivasyon fonksiyonuyla uyumlu çalışmasını sağlar.
Üretici Modeli Oluşturma
Üretici, rastgele gürültü vektörünü (latent vector) girdi olarak alacak ve 28x28x1 boyutunda bir görüntü üretecektir.
def make_generator_model():
model = tf.keras.Sequential()
# Başlangıçta latent_dim boyutunda bir vektörü alıp,
# bir Dense katmanıyla daha büyük bir vektöre dönüştür
# 7x7x256'lık bir tensör haline getirilecek
model.add(layers.Dense(77256, use_bias=False, input_shape=(100,)))
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
# Tensörü (batch_size, 7, 7, 256) şeklinde yeniden şekillendir
model.add(layers.Reshape((7, 7, 256)))
assert model.output_shape == (None, 7, 7, 256) # Not: None, batch boyutunu temsil eder
# İlk Conv2DTranspose katmanı: 7x7 -> 14x14
model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
assert model.output_shape == (None, 7, 7, 128)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
# İkinci Conv2DTranspose katmanı: 14x14 -> 14x14
model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
assert model.output_shape == (None, 14, 14, 64)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
# Üçüncü Conv2DTranspose katmanı: 14x14 -> 28x28
# Çıkış katmanı, görüntü piksellerini -1 ile 1 arasına sıkıştırmak için tanh kullanır
model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
assert model.output_shape == (None, 28, 28, 1)
return model
Üretici modelimiz, 100 boyutlu bir gürültü vektörünü alır. İlk Dense katmanıyla bu vektörü büyütür ve Reshape katmanıyla 7x7x256 boyutunda bir tensöre dönüştürür. Ardından, Conv2DTranspose (ters evrişim veya dekonvolüsyon olarak da bilinir) katmanları kullanarak bu tensörü kademeli olarak 28x28x1 boyutunda bir görüntüye yükseltir. BatchNormalization katmanları eğitimi stabilize etmeye yardımcı olur ve LeakyReLU aktivasyonları gradyan akışını sağlar. Son katmanda tanh aktivasyonu, piksel değerlerinin -1 ile 1 aralığında olmasını garanti eder.
Ayırt Edici Modeli Oluşturma
Ayırt edici, 28x28x1 boyutunda bir görüntüyü girdi olarak alacak ve bu görüntünün gerçek olma olasılığını tahmin eden tek bir skaler değer (0 ile 1 arası) döndürecektir.
def make_discriminator_model():
model = tf.keras.Sequential()
# İlk Conv2D katmanı
model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same',
input_shape=[28, 28, 1]))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
# İkinci Conv2D katmanı
model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
# Düzleştirme (Flatten) ve Dense katmanı
model.add(layers.Flatten())
model.add(layers.Dense(1, activation='sigmoid')) # İkili sınıflandırma için sigmoid
return model
Ayırt edici modelimiz, 28x28x1 boyutunda bir görüntüyü alır. Conv2D katmanları, görüntüden özellikler çıkarmak için kullanılır. strides=(2,2) parametresi, mekansal boyutları küçülterek bilgiyi yoğunlaştırır. LeakyReLU aktivasyonları gradyan akışını desteklerken, Dropout katmanları aşırı öğrenmeyi önler. Son olarak, Flatten katmanı evrişimli çıktıları tek bir vektöre dönüştürür ve Dense(1, activation='sigmoid') katmanı görüntünün gerçek olma olasılığını tahmin eder.
Kayıp Fonksiyonları ve Optimizatörler
GAN’lar için özel kayıp fonksiyonlarına ve ayrı optimizatörlere ihtiyacımız var.
Kayıp Fonksiyonları:
* Ayırt Edici Kaybı (Discriminator Loss): Ayırt edicinin gerçek görüntüleri gerçek olarak, sahte görüntüleri sahte olarak doğru bir şekilde sınıflandırma yeteneğini ölçer. BinaryCrossentropy kullanılır.
* Üretici Kaybı (Generator Loss): Üreticinin ayırt ediciyi sahte görüntüleri gerçek olarak sınıflandırmaya ikna etme yeteneğini ölçer. Yine BinaryCrossentropy kullanılır, ancak burada üreticinin ürettiği tüm görüntüler için ayırt edicinin “gerçek” (1) olarak sınıflandırmasını bekleriz.
# İkili çapraz entropi kayıp fonksiyonu
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=False) # Sigmoid kullanıldığı için from_logits=False
def discriminator_loss(real_output, fake_output):
real_loss = cross_entropy(tf.ones_like(real_output), real_output) # Gerçek görüntüler için hedef 1
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output) # Sahte görüntüler için hedef 0
total_loss = real_loss + fake_loss
return total_loss
def generator_loss(fake_output):
# Üreticinin amacı, ayırt edicinin sahte görüntüleri gerçek (1) olarak sınıflandırmasını sağlamaktır.
return cross_entropy(tf.ones_like(fake_output), fake_output)
from_logits=False parametresi, model çıktısının zaten bir olasılık değeri (sigmoid aktivasyonu sonrası) olduğunu belirtir. Eğer çıktı katmanında sigmoid aktivasyonu kullanmasaydık (from_logits=True olurdu), kayıp fonksiyonu kendi içinde sigmoid uygulardı.
Optimizatörler:
Üretici ve ayırt ediciyi ayrı ayrı eğitmek için iki ayrı optimizatöre ihtiyacımız var. Adam optimizatörü yaygın olarak kullanılır.
generator_optimizer = tf.keras.optimizers.Adam(1e-4) # Öğrenme oranı 0.0001
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4) # Öğrenme oranı 0.0001
Eğitim Döngüsü
Eğitim döngüsü, her adımda hem ayırt ediciyi hem de üreticiyi eğitmeyi içerir. tf.GradientTape API’si, otomatik farklılaşma için kullanılır.
EPOCHS = 50
noise_dim = 100
num_examples_to_generate = 16
Her eğitim adımında aynı gürültü vektörünü kullanacağız
Bu, üreticinin eğitim süresince ilerlemesini görselleştirmemizi sağlar.
seed = tf.random.normal([num_examples_to_generate, noise_dim])
Tek bir eğitim adımını tanımla
@tf.function # Performans için tf.function kullanırız
def train_step(images):
# Üretici için rastgele gürültü oluştur
noise = tf.random.normal([BATCH_SIZE, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
# Üretici sahte görüntüler üretir
generated_images = generator(noise, training=True)
# Ayırt edici hem gerçek hem de sahte görüntüler üzerinde tahmin yapar
real_output = discriminator(images, training=True)
fake_output = discriminator(generated_images, training=True)
# Kayıp fonksiyonlarını hesapla
gen_loss = generator_loss(fake_output)
disc_loss = discriminator_loss(real_output, fake_output)
# Gradyanları hesapla
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
# Gradyanları uygula (ağırlıkları güncelle)
generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
Eğitim döngüsünü tanımla
def train(dataset, epochs):
for epoch in range(epochs):
start = time.time()
for image_batch in dataset:
train_step(image_batch)
# Her 15 epoch'ta bir veya belirli aralıklarla üretilen görüntüleri kaydet
if (epoch + 1) % 15 == 0:
generate_and_save_images(generator,
epoch + 1,
seed)
print (f'Epoch {epoch + 1} bitti, geçen süre: {time.time()-start:.2f} saniye')
# Son epoch'tan sonra görüntüleri oluştur ve kaydet
generate_and_save_images(generator,
epochs,
seed)
Görüntüleri oluşturma ve kaydetme fonksiyonu
def generate_and_save_images(model, epoch, test_input):
predictions = model(test_input, training=False)
fig = plt.figure(figsize=(4, 4))
for i in range(predictions.shape[0]):
plt.subplot(4, 4, i+1)
# Görüntüleri [0, 1] aralığına geri dönüştür ve göster
plt.imshow((predictions[i, :, :, 0] * 0.5 + 0.5), cmap='gray')
plt.axis('off')
plt.savefig(f'image_at_epoch_{epoch:04d}.png')
plt.show()
train_step fonksiyonu, GAN eğitiminin kalbidir. Bir batch görüntüyü alır ve şu adımları uygular:
1. Rastgele gürültü oluşturur.
2. Üretici bu gürültüden sahte görüntüler üretir.
3. Ayırt edici, hem gerçek görüntüler hem de üretilen sahte görüntüler üzerinde tahminler yapar.
4. Üretici ve ayırt edici için kayıp fonksiyonları hesaplanır.
5. tf.GradientTape kullanılarak her iki ağın da ağırlıklarına göre gradyanlar hesaplanır.
6. Adam optimizatörleri, hesaplanan gradyanları kullanarak ağların ağırlıklarını günceller.
train fonksiyonu, veri kümesi üzerinde belirli bir sayıda epoch boyunca train_step‘i çağırır ve belirli aralıklarla üretilen görüntüleri kaydeder.
Model Oluşturma ve Eğitme
Son olarak, model örneklerini oluşturup eğitim döngüsünü başlatabiliriz.
# Modelleri oluştur
generator = make_generator_model()
discriminator = make_discriminator_model()
Eğitim döngüsünü başlat
train(train_dataset, EPOCHS)
Kod Örneği
Yukarıdaki parçaları bir araya getiren tam kod örneği aşağıdadır. Bu kodu çalıştırarak kendi basit GAN’ınızı eğitebilirsiniz.
import tensorflow as tf
from tensorflow.keras import layers
import numpy as np
import matplotlib.pyplot as plt
import os
import time
Gerekli dizinleri oluştur
if not os.path.exists('generated_images'):
os.makedirs('generated_images')
Veri kümesini yükle ve hazırla
(train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5 # Normalizasyon [-1, 1] aralığına
BUFFER_SIZE = 60000
BATCH_SIZE = 256
train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
Üretici modelini tanımla
def make_generator_model():
model = tf.keras.Sequential()
model.add(layers.Dense(77256, use_bias=False, input_shape=(100,)))
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
model.add(layers.Reshape((7, 7, 256)))
model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU())
model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
return model
Ayırt edici modelini tanımla
def make_discriminator_model():
model = tf.keras.Sequential()
model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same',
input_shape=[28, 28, 1]))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3))
model.add(layers.Flatten())
model.add(layers.Dense(1, activation='sigmoid'))
return model
Kayıp fonksiyonlarını tanımla
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=False)
def discriminator_loss(real_output, fake_output):
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss
return total_loss
def generator_loss(fake_output):
return cross_entropy(tf.ones_like(fake_output), fake_output)
Optimizatörleri tanımla
generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
Eğitim parametreleri
EPOCHS = 50
noise_dim = 100
num_examples_to_generate = 16
seed = tf.random.normal([num_examples_to_generate, noise_dim])
Tek bir eğitim adımını tanımla
@tf.function
def train_step(images):
noise = tf.random.normal([BATCH_SIZE, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
generated_images = generator(noise, training=True)
real_output = discriminator(images, training=True)
fake_output = discriminator(generated_images, training=True)
gen_loss = generator_loss(fake_output)
disc_loss = discriminator_loss(real_output, fake_output)
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
Görüntüleri oluşturma ve kaydetme fonksiyonu
def generate_and_save_images(model, epoch, test_input):
predictions = model(test_input, training=False)
fig = plt.figure(figsize=(4, 4))
for i in range(predictions.shape[0]):
plt.subplot(4, 4, i+1)
plt.imshow((predictions[i, :, :, 0] * 0.5 + 0.5), cmap='gray')
plt.axis('off')
plt.savefig(os.path.join('generated_images', f'image_at_epoch_{epoch:04d}.png'))
plt.close(fig) # Figürü kapat, bellek kullanımını azalt
Eğitim döngüsünü tanımla
def train(dataset, epochs):
for epoch in range(epochs):
start = time.time()
for image_batch in dataset:
train_step(image_batch)
if (epoch + 1) % 5 == 0: # Her 5 epoch'ta bir görüntü kaydet
generate_and_save_images(generator,
epoch + 1,
seed)
print (f'Epoch {epoch + 1} bitti, geçen süre: {time.time()-start:.2f} saniye')
generate_and_save_images(generator,
epochs,
seed)
Modelleri oluştur
generator = make_generator_model()
discriminator = make_discriminator_model()
Eğitimi başlat
print("GAN eğitimi başlıyor...")
train(train_dataset, EPOCHS)
print("GAN eğitimi tamamlandı.")
Sonuçları görselleştir (isteğe bağlı)
generate_and_save_images fonksiyonu zaten kaydeder, bu kısım sadece gösterim içindir
son_epoch_resim_yolu = os.path.join('generated_images', f'image_at_epoch_{EPOCHS:04d}.png')
if os.path.exists(son_epoch_resim_yolu):
img = plt.imread(son_epoch_resim_yolu)
plt.imshow(img)
plt.axis('off')
plt.title(f'Epoch {EPOCHS} Sonrası Üretilen Görüntüler')
plt.show()
Değerlendirme ve İyileştirmeler
Basit bir GAN modeli inşa etmek, temel prensipleri anlamak için harika bir başlangıç noktasıdır. Ancak, GAN’ların eğitimi zorlu bir süreç olabilir ve karşılaşılabilecek bazı yaygın sorunlar vardır:
1. Mod Çökmesi (Mode Collapse): Üretici, tüm veri dağılımını öğrenmek yerine, ayırt ediciyi kandırmak için sadece birkaç tür veri örneği üretmeyi öğrenebilir. Örneğin, MNIST’te sadece “1” rakamını üretmeye odaklanabilir. Bu, üreticinin çeşitlilikten yoksun kalmasına neden olur.
2. Eğitim Kararsızlığı: Üretici ve ayırt edici arasındaki dengeyi korumak zordur. Bir ağ diğerine göre çok daha hızlı öğrenebilir, bu da eğitimin durmasına veya anlamsız çıktılar üretilmesine yol açabilir. Gradyanlar kaybolabilir veya patlayabilir.
3. Metrik Eksikliği: GAN’ların performansını ölçmek için net ve objektif bir metrik yoktur. Kayıp değerleri genellikle yanıltıcı olabilir; düşük kayıp her zaman iyi üretim anlamına gelmez. FID (Frechet Inception Distance) veya Inception Score gibi metrikler daha gelişmiş GAN’lar için kullanılır, ancak bu basit örnekte görsel değerlendirme daha yaygındır.
Bu sorunları gidermek ve daha iyi performans elde etmek için çeşitli iyileştirmeler ve varyasyonlar geliştirilmiştir:
* Derin Evrişimli GAN’lar (DCGAN): Evrişimli katmanların kullanımı, görüntü üretimi için daha kararlı ve yüksek kaliteli sonuçlar sağlar. Yukarıdaki örneğimizde DCGAN mimarisine benzer katmanlar kullandık.
* Wasserstein GAN (WGAN): Geleneksel çapraz entropi kaybı yerine Wasserstein mesafesini kullanan WGAN’lar, daha kararlı bir eğitime ve mod çökmesine karşı daha iyi dirence sahiptir. Ayrıca, kayıp fonksiyonu, üreticinin eğitiminin ne kadar iyi gittiği hakkında daha anlamlı bilgi sağlar.
* Koşullu GAN (cGAN): Üreticiye sadece rastgele gürültü değil, aynı zamanda koşullu bilgi (örneğin, üretilecek rakamın etiketi) sağlayarak belirli özelliklere sahip veriler üretilmesini sağlar.
* Spektral Normalizasyon (SN-GAN): Ayırt ediciye spektral normalizasyon uygulayarak eğitimin kararlılığını artırır.
* Tek Taraflı Etiket Düzeltme (One-sided Label Smoothing): Ayırt edicinin aşırı güvenli olmasını önlemek için gerçek etiketlerin 1 yerine 0.9 gibi bir değere ayarlanması.
* Farklı Optimizatörler ve Öğrenme Oranları: Adam yaygın olsa da, farklı öğrenme oranları veya diğer optimizatörler (örneğin, RMSprop) denenebilir.
* Ağ Mimarisi İyileştirmeleri: Daha fazla katman, farklı filtre boyutları, BatchNormalization ve Dropout katmanlarının stratejik kullanımı performansı etkileyebilir.
Basit GAN modelimiz, MNIST gibi nispeten basit bir veri kümesinde bile belirli bir noktaya kadar iyi sonuçlar verebilir. Ancak, daha karmaşık veri kümeleri (örneğin, gerçek dünya fotoğrafları) için daha gelişmiş GAN mimarilerine ve eğitim tekniklerine ihtiyaç duyulacaktır.
Sonuç
Bu makalede, TensorFlow kütüphanesini kullanarak basit bir Üretken Çekişmeli Ağ’ın (GAN) nasıl inşa edileceğini ve eğitileceğini ayrıntılı olarak inceledik. GAN’ların temel mimarisini oluşturan üretici ve ayırt edici ağların rollerini, çalışma prensiplerini ve birbirleriyle olan çekişmeli ilişkilerini anladık. MNIST veri kümesi üzerinde, rastgele gürültüden el yazısı rakam görüntüleri üretebilen bir GAN modelini adım adım inşa ettik.
TensorFlow’un Keras API’si, model katmanlarını tanımlamayı, kayıp fonksiyonlarını ve optimizatörleri yapılandırmayı oldukça kolaylaştırmaktadır. tf.GradientTape‘in kullanımı, özel eğitim döngüleri oluşturmak ve gradyanları manuel olarak hesaplayıp uygulamak için güçlü bir araç sağlamaktadır.
GAN’lar, sentetik veri üretimi, veri artırımı, görüntü işleme ve hatta sanat gibi birçok alanda muazzam bir potansiyele sahiptir. Bu basit uygulama, GAN’ların karmaşık dünyasına sağlam bir giriş sağlamıştır. Ancak, GAN eğitiminin doğasında var olan zorluklar (mod çökmesi, kararsızlık) ve bu sorunları aşmak için geliştirilen çeşitli ileri teknikler (DCGAN, WGAN, cGAN vb.) göz önünde bulundurulmalıdır.
Bu temel bilgiyi edinerek, okuyucular daha karmaşık GAN mimarilerini keşfetmeye ve gerçek dünya problemlerini çözmek için bu güçlü üretken modelleri kullanmaya bir adım daha yaklaşmışlardır. Gelecekte, GAN’ların daha da geliştirilmesi ve farklı uygulama alanlarında kullanılmasıyla yapay zeka alanında yeni ufuklar açılmaya devam edecektir.
