GAN (Python)

Aysen Çeliktaş
Türkiye UP
Published in
7 min readMay 5, 2024

--

GAN (Generative Adversial Network/Üretici Çekişmeli Ağlar) gün geçtikçe popülaritesini daha da arttırmaktadır. Sentetik veri oluşturma, boyut azaltma, görüntülerin gürültülerini giderme ve ses transferi gibi birçok uygulama için kullanılan bu mimari yapının, tıbbi görüntülemede ki kullanımlarına buradan genel bir bakış atabilirsiniz. Ayrıca derin öğrenme havuzuna dalmadan önce bilinmesi gereken genel kavramlar hakkında da buradan bilgi edinebilirsiniz.

[Yazar tarafından Canva’da oluşturuldu.]

GAN, denetimsiz makine öğrenmesi sınıfına ait bir yapay zekâ algoritmasıdır. 2014 yılında, Google araştırmacısı Ian Goodfellow tarafından bulunmuştur. [1]. Temel olarak generator ve discriminator denilen iki yapı üzerine kurulmuştur.

· Generator (Üretici): Sentetik veri üretiminin gerçekleştiği kısımdır. Tıpkı bir sanatçı gibi, istenilenler doğrultusunda, gürültüden anlamlı bir veri oluşturmaya çalışır.

· Discriminator (Ayırt Edici): Üreticiden gelen görüntüleri tıpkı bir eleştirmen gibi değerlendirir ve istenene ne kadar yakınlaştığını söyler. Sonuç istenene ne kadar yaklaşırsa, sistem o kadar başarılı sonuç oluşturmuş demektir.

İstenen ve üretici ağın oluşturduğu veriler arasında ki benzerlik oranına göre sistem bir kayıp (loss) fonksiyonu üretecektir. Böylece bu fonksiyon ile model geri beslenerek, istenen sonuca yaklaşmaya çalışacaktır. Bu işlem ayırt edici ağ için her iterasyonda, gerçek resme verdiği değeri 1’e, sahte resme verdiği değeri 0’a çekecek şekilde güncellenir. Ayırt edici ağda sahte resimlerin gerçek olma olasılıkları 1’e yakınlaştığında, üretici ağın hatası 0’a yaklaşmış olacaktır. Üretici ağdan çıkan sonuç ne kadar gerçeğe yakınsa, ayrıcının sahte ve gerçek görüntüleri ayırabilme yeteneği de azalacaktır.

[Yazar tarafından Canva’da oluşturuldu.]

Burada teknik açıdan dikkat edilmesi gereken kavramlardan biri tensör kavramıdır. Bilmelisiniz ki derin öğrenmede katmanlar (dense) arasında işlem yapılabilmesi için, veriyi tensör olarak ifade etmeniz gerekmektedir. Chollet, “Python İle Derin Öğrenme” [2] kitabında başlangıç seviyesinde bilinmesi gereken kavramları çok güzel anlatmış. Burada katmanların bir süzgeç gibi düşünebileceğine değinmiş.

Katmanlarda gürültüler ayıklanarak, en sonunda istenen çıktı görünür kılınmaya çalışılır ve bunun içinde her süzme işleminde çeşitli yöntemler kullanılır. Tensörler ise verilerin derin öğrenme için uygun biçimde ifade edilmiş halleridir. Bir nevi veri taşıyıcısı gibi de düşünülebilir. Boyutlarına göre tensörleri incelemek gerekirse:

· skaler değer = 0 boyutlu tensör

· sayı dizileri = 1 boyutlu tensör

· vektör dizileri = 2 boyutlu tensör

· matris dizileri = 3 boyutlu tensör

Bir tensörün temel özellikleri incelenmek istendiğinde “ndim” ile tensörün derecesi yani eksen sayısı, “shape” ile tensörün (x,y,z) 3 boyutlu olabilme gibi özelliklerinden şekline, “dtype” ile de uint8 veya float32 gibi veri tipine bakılabilir.

Python ortamında derin öğrenme çalışırken, Google’ın temel kütüphanelerinden biri olan Tensorflow kütüphanesi kullanılabilir. Tensorflow boyutlarda renk derinliğini en sonda kullanır. Örnek olarak vermek gerekirse bir görüntü verisi olarak, 256*256 boyutunda 128 gri ölçekli bir veri olduğunu düşünelim. Bu verinin renk aralığı gri ölçekli olduğu için renk derinliği de 1’dir. Eğer görüntü RGB olarak ele alınırsa o zaman 3 renk derinliği üzerinden renkli görüntü çalışılmalıdır. Şimdi bunları kullanarak gri ölçekli görüntünün şeklini ifade edecek olursak (örnekler, yükseklik, genişlik, kanallar) olarak (128, 256, 256, 1) karşımıza çıkacaktır. [2].

[Yazar tarafından Canva’da oluşturuldu.]

Peki söz konusu bir video verisi olsaydı tensörler nasıl çalışırdı? Eğer video, sayısal olarak ifade edilmiş resimler dizisi olarak ele alınırsa ve her resim bir kare olarak değerlendirilirse, karşımıza 5 boyutlu bir tensör yapısı çıkacaktır. Bunun ifade edilme şekli ise (örnek, kare, yükseklik, genişlik, kanallar) olacaktır.

Yazının devamında Python ortamında DCGAN (Deep Convolutional Generative Adversial Network) uygulaması görülecektir. Ayrıca üretici ağlar araştırılmak istenilirse, sadece GAN’lar ile yetinmeyip, VAE (Variational AutoEncoder) gibi çeşitli üretici ağlara da bakılmalıdır. [2, syf. 319]

Her zaman üzerinde durduğumuz gibi önce veriyi ve problemin çözümüne doğru yaklaşımda bulunmak için problemi anlamamız gerekmektedir. Burada fashion_MNIST veri seti kullanıldı. Bu veri seti içeriğinde 28*28 boyutlarında, 784 piksele sahip, toplam 60000 eğitim ve 10000 test grubu için görüntü bulunmaktadır. İçeriğinde ki 10 adet sınıf sırasıyla: tişört, pantolon, kazak, elbise, kot, sandalet, gömlek, spor ayakkabı, çanta, bot. Bu görüntülerin her birinin piksel değerleri 0 ile 255 değerleri aralığındadır. pandas kütüphanesi kullanarak, sınıf bazında verilerin pixel değerleri görülebilir. .csv dosyasına buradan ulaşabilirsiniz. Bunun için veri okutulduktan sonra headine bakıldı ve tek görüntü üzerinden histogram grafiğinde ki dağılımların farklılıklarına göz atıldı.

train_data = pd.read_csv('/content/drive/MyDrive/_/fashion-mnist_train.csv')
train_data.head()
[yazarın not defterinden]
image_data = train_data.iloc[_]

if image_data is not None:
data_array = image_data.to_numpy()
plt.hist(data_array)
plt.xlabel("Value")
plt.ylabel("Frequency")
plt.title("Histogram of Row Data")
plt.show()
else:
print("Error: Row not found.")
[yazarın not defterinden]

Veri setinin içeri aktarılması için Keras’dan faydalanıldı. Bu değerler (0, 255) aralığında olduğu için değerlerin normalleştirilmesi bu aralık dikkate alınarak (-1,1) aralığına çekildi. Kullanılan veri sınıfına ait örnek sayısı, yükseklik*genişlik ölçüsü, verinin şekli ‘shape’ üzerinden incelendi.

(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
print(x_train.max(),x_train.min())
[yazarın not defterinden]
x_train = (x_train.astype(np.float32)-127.5)/127.5
print(x_train.max(),x_train.min())
[yazarın not defterinden]
print("Eğitim Verisi Şekli:", x_train.shape, "Test Verisi Şekli:", x_test.shape)
[yazarın not defterinden]
plt.figure(figsize=(8,8))
for i in range(25):
plt.subplot(_,_,i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(x_train[i], cmap=plt.cm.binary)

plt.show()
[yazarın not defterinden]

Verimizin shapei eğitim verisi için (60000, 28, 28), test verisi için ise (10000, 28, 28) şeklindedir. Derin öğrenmeyi başlattığımız giriş katmanında verileri düzleştirerek (flatten) mimariye sokmalıyız. Bu işlem, veriler 2B, 3B ya da 4B bir tensör olması fark etmeksizin, mimari için ihtiyaç duyulan boyut uyumu, verilerin daha kolay işlenebilmesi için yapılır. Bu işleme bağlı olarak verilerin boyutu düşürülürken bir takım bilgi kayıplarına sebebiyet verilebilir fakat bu da modelin daha rahat genelleme yapabilmesi açısından işlevsel olabilir.

x_train = x_train.reshape(x_train.shape[0], x_train.shape[1]*x_train.shape[2])
print(x_train.shape)
[yazarın not defterinden]

Bu kısımdan sonrası bütünüyle, bir örnek üzerinden sistemi Python ortamı içerisinde somutlaştırmak için yapıldı. Sırasıyla generator ve discriminatorı tanımalayarak bir GAN mimarisi oluşturuldu. Burada basitçe bir mimari oluşturuldu. Daha iyi bir sonuç için katmanlar derinleştirilip farklı fonksiyonlarla güncellenebilir.

· Üretici (generator) ağın son aktivasyonunda sigmoid yerine tanh kullanmak GAN’larda ipucu olarak görülür. Bu durum çıktıyı (0,1) aralığı yerine (-1,1) aralığında ki değerlerde tutacaktır. Daha simetrik bir çıktı üretildiği gibi, eğitim süreci de daha güvenilir bir hale getirilebilir.

· Saklı uzayda örnekler seçerken Gauss dağılımı kullanılabilir. Bunun için üretici ağın girişine gürültü eklenebilir.

· Aktivasyon fonksiyonu olarak katmanlarda ReLU yerine LeakyReLU kullanılabilir. Bu negatif girişler için küçük bir eğim sağlar. Derin katmanlara doğru gidildikçe gradyanların (eğimlerin) giderek küçülme veya yok olma tehlikesini azaltır. Aktivasyonun genişletilmesiyle modele esneklik kazandırılmış olunur.

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())

generator.add(Dense(units = 784, activation = "tanh"))

generator.compile(loss = "binary_crossentropy",
optimizer = Adam(learning_rate=0.0001, beta_1=0.5))

return generator

g = create_generator()
g.summary()
[yazarın not defterinden]
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())

discriminator.add(Dense(units=1, activation="sigmoid"))

discriminator.compile(loss="binary_crossentropy",
optimizer=Adam(learning_rate=0.0001, beta_1=0.5))

return discriminator

d = create_discriminator()
d.summary()
[yazarın not defterinden]
def create_gan(discriminator, generator):

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)
gan.summary()
[yazarın not defterinden]

Üst üste eklenmiş katmanlarda, hatanın türevinin (gradient propagation) geriye yayılımı anahtar konudur.

· Katmanlar için daha uygun aktivasyon fonksiyonları

· Daha uygun ilk başlatma yöntemleri

· RMSprop ve Adam gibi daha iyi en iyileme yöntemleri

for e in range(epochs):
for _ in range(batch_size):

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)]

x=np.concatenate([image_batch, generated_images])

y_dis = np.zeros(batch_size*2)
y_dis[:batch_size]=1

d.trainable=True
d.train_on_batch(x,y_dis)

noise = np.random.normal(0,1,[batch_size,100])

y_gen= np.ones(batch_size)

d.trainable=False


gan.train_on_batch(noise, y_gen)

print("epochs:", e)

Şimdi modeli kaybetmemek için istenilen drive yoluna kaydettikten sonra çıkacak sonuçlara göz atılabilir. Bu basit uygulamadan sonra kendi mimarinizi geliştirerek daha doğru sonuçlar alabilirsiniz.

_.save('/content/drive/MyDrive/...)
fig,axe=plt.subplots(_,_)
fig.suptitle("Actual Images")
idx = 0
for i in range(_):
for j in range(_):
axe[i,j].imshow(x_train[idx].reshape(28,28),cmap='gray')
idx+=10
[yazarın not defterinden]
plt.imshow(noise, cmap='gray')
plt.title('How the noise looks')
[yazarın not defterinden]
fig,axe=plt.subplots(_,_)
fig.suptitle('Generated Images from Noise using DCGANs')
idx=0
for i in range(_):
for j in range(_):
axe[i,j].imshow(generated_images[idx].reshape(28,28),cmap='gray')
idx+=3
[yazarın not defterinden]

Referanslar

[1] Goodfellow I, Pouget-Abadie J, Mirza M, Xu B, Warde-Farley D, Ozair S, et al. “Generative adversarial nets.” Advances in neural information processing systems 27 (2014).

[2] François Chollet, (2021, 2. Baskı). Python ile Derin Öğrenme. Buzdağı Yayınevi.

--

--