Üretken Çekişmeli Ağlar Kullanarak Kedi Üretmek ve Temel Bileşen Analizi
Amaç
Bu makale size Keras kitaplığı kullanılarak yazılan bir Üretken Çekişme Ağı’nın genel çerçevesini sağlayacaktır. Kendi görüntü veri kümelerinizi kullanarak üretken modelleri eğitmek için bu genel GAN şablonunu kullanabileceksiniz.
Kodun tam sürümü bu GitHub deposunda mevcuttur.
Veri Kümesi
Girdiler
from keras.layers import Input, Dense, Reshape, Flatten
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import UpSampling2D, Conv2D, MaxPooling2D
from keras.models import Sequential, Model
from keras.optimizers import Adam
from sklearn.utils import shuffle
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
import numpy as np
import os
from PIL import Image
Using TensorFlow backend
- Keras (2.3.1 kullanıyorum)
- Tensorflow (1.14.0 kullanıyorum)
- Sklearn
- Scipy
- Numpy
- Matplotlib
- PIL
- Keract (Model Görselleştirme için İsteğe Bağlı)
Parametreleri Ayarlayın
# Folder containing dataset data_path = r'D:\Downloads\cats-faces-64x64-for-generative-models\cats' # Dimensions of the images inside the dataset img_dimensions = (64,64,3) # Folder where you want to save to model as well as generated samples model_path = r"C:\Users\Vee\Desktop\python\GAN\pca_new" # How many epochs between saving your model interval = 10 # How many epochs to run the model epoch = 500 # How many images to train at one time. # Ideally this number would be a factor of the size of your dataset batch = 181 # How many convolutional filters for each convolutional layer of the generator and the discrminator conv_filters = 64 # Size of kernel used in the convolutional layers kernel = (5,5) # Boolean flag, set to True if the data has pngs to remove alpha layer from images png = False
Derin Evrişimli GAN Sınıfı Oluşturun
- __init __ (self): Sınıf, girdi vektörünün boyutları ve çıktı görüntüsü tanımlanarak başlatılır. Generator ve Discriminator modelleri build_generator() ve build_discriminator() kullanılarak başlatılır.
- build_generator(self): Generator modelini tanımlar. 8x8x8’den 64x64x3’e yukarı örneklenen 5 evrişimli katman vardır. DCGAN sınıfı başlatıldığında çağrılır.
- build_discriminator(self): Discriminator modelini tanımlar. 64x64x3’ten 1 skaler tahmine altörneklenen 5 evrişimli katman vardır. DCGAN sınıfı başlatıldığında çağrılır.
- load_data(self): Kullanıcı tarafından belirtilen dosya yolundan, data_path verileri yükler. Görüntü veri kümesini X_Train veri kümesi olarak daha düşük bir boyuta yansıtmak için PCA kullanır. Görüntü veri kümesini işler ve Y_Train veri kümesi için 4 boyuta yeniden şekillendirir. train() yönteminde çağrılır.
- train(self, epochs, batch_size, save_interval): Generative Adversarial Network’ü eğitir. Her dönem modeli, batch_size ile tanımlanan parçalara ayrılmış veri kümesinin tamamını kullanarak eğitir. Dönem, save_interval‘deyse, yöntem, örnekleri oluşturmak için save_imgs() öğesini çağırır ve mevcut dönemin modelini kaydeder.
- save_imgs (self, epoch, gen_imgs, y_points): Modeli kaydeder ve kullanıcı tarafından belirtilen yol, model_path‘ta belirli bir dönem için tahmin örnekleri oluşturur. Her örnek, oluşturulan 8 tahmin ve 8 eğitim örneği içerir.
Başlatma
class DCGAN(): # Initialize parameters, generator, and discriminator models def __init__(self): # Set dimensions of the output image self.img_rows = img_dimensions[0] self.img_cols = img_dimensions[1] self.channels = img_dimensions[2] self.img_shape = (self.img_rows, self.img_cols, self.channels) # Set dimensions of the input noise self.latent_dim = 512 # Chose optimizer for the models optimizer = Adam(0.0002, 0.5) # Build and compile the discriminator self.discriminator = self.build_discriminator() self.discriminator.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy']) # Build the generator self.generator = self.build_generator() generator = self.generator # The generator takes noise as input and generates imgs z = Input(shape=(self.latent_dim,)) img = self.generator(z) # For the combined model we will only train the generator self.discriminator.trainable = False # The discriminator takes generated images as input and determines validity valid = self.discriminator(img) # The combined model (stacked generator and discriminator) # Trains the generator to fool the discriminator self.combined = Model(z, valid) self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)
DCGAN sınıfı başlatıldığında, Sinir Ağının veri setinden beklemesi gereken görüntülerin boyutunu tanımlarız. Bu, Tuple img_dimensions tarafından belirtilir. Ayrıca, PCA’dan oluşturulan girdi vektörümüzün boyutu olan gizli boyutları da tanımlarız. Bu, latent_dim tamsayısı ile belirtilir.
Her iki model için de kullandığımız optimize edici, Adam optimize edicidir. Optimize edicinin öğrenme oranı ve beta değerleri ile denemekten çekinmeyin ve ne tür sonuçlar aldığınızı görün.
Generative Adversarial Network’ün mimarisi, her iki modelde İkili Çapraz Entropi kaybını kullanarak burada tanımlanmıştır. Kayıp fonksiyonu olarak İkili Çapraz Entropi seçimi burada açıklanmıştır. Diğer kayıp fonksiyonlarını denemekten çekinmeyin, ancak her iki modelin de aynı kayıp fonksiyonunu kullanması gerektiğini unutmayın.
Veri yükle
class DCGAN(): # Initialize parameters, generator, and discriminator models def __init__(self): # Set dimensions of the output image self.img_rows = img_dimensions[0] self.img_cols = img_dimensions[1] self.channels = img_dimensions[2] self.img_shape = (self.img_rows, self.img_cols, self.channels) # Set dimensions of the input noise self.latent_dim = 512 # Chose optimizer for the models optimizer = Adam(0.0002, 0.5) # Build and compile the discriminator self.discriminator = self.build_discriminator() self.discriminator.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy']) # Build the generator self.generator = self.build_generator() generator = self.generator # The generator takes noise as input and generates imgs z = Input(shape=(self.latent_dim,)) img = self.generator(z) # For the combined model we will only train the generator self.discriminator.trainable = False # The discriminator takes generated images as input and determines validity valid = self.discriminator(img) # The combined model (stacked generator and discriminator) # Trains the generator to fool the discriminator self.combined = Model(z, valid) self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)
DCGAN sınıfına eklediğimiz ilk yöntem load_data() ‘dır. Bu, kullanıcının belirlediği yol olan data_path içindeki tüm görüntüleri önceden işleyecektir. Bu yöntem, eğitimden önce verileri yüklemek için train() yönteminin içinde çağrılır.
Bu, veri kümesinin PCA kullanılarak daha düşük boyutlu bir alana yansıtıldığı yerdir. Sklearn PCA yöntemi, aktarılan verilerin 2 boyutlu olmasını gerektirir, bu nedenle 64x64x3 resimleri 12288 vektör olacak şekilde sıkıştırıyoruz. Ayrıca verileri 0 ile 1 arasında olacak şekilde normalleştiriyoruz. RGB piksel değerleri 0 ile 255 arasında değişir, bu nedenle yeniden şekillendirilmiş vektörü 255’e böleriz.
Son olarak, iki diziyi döndürmeden önce train() veri kümelerini karıştırıyoruz. Veri kümesindeki modelleri sıralı olarak eğitmek için train() yöntemini yazdım, her yinelemede toplu iş boyutunu artırdım. Bu nedenle, veri kümesinin sıralı olarak sıralanma şekline ilişkin tuhaf önyargılar getirmemek için veri kümesini karıştırmak önemlidir.
Oluşturucu İnşa Etmek
# Define Generator model. There are 5 convolutional filters, upsampling from (8x8x8) to (64x64x3) def build_generator(self): model = Sequential() # Input Layer model.add(Dense(8 * 8 * 8, input_dim=self.latent_dim)) model.add(Reshape((8, 8, 8))) # 1st Convolutional Layer model.add(Conv2D(conv_filters, kernel_size=kernel, padding="same")) model.add(LeakyReLU(alpha=0.2)) # Upsample the data (8x8 to 16x16) model.add(UpSampling2D()) # 2nd Convolutional Layer model.add(Conv2D(conv_filters, kernel_size=kernel, padding="same")) model.add(LeakyReLU(alpha=0.2)) # Upsample the data (16x16 to 32x32) model.add(UpSampling2D()) # 3rd Convolutional Layer model.add(Conv2D(conv_filters, kernel_size=kernel, padding="same")) model.add(LeakyReLU(alpha=0.2)) # Upsample the data (32x32 to 64x64) model.add(UpSampling2D()) # 4th Convolutional Layer model.add(Conv2D(conv_filters, kernel_size=kernel, padding="same")) model.add(LeakyReLU(alpha=0.2)) # 5th Convolutional Layer (Output Layer) model.add(Conv2D(3, kernel_size=kernel, padding="same")) model.add(LeakyReLU(alpha=0.2)) model.summary() noise = Input(shape=(self.latent_dim,)) img = model(noise) return Model(noise, img)
DCGAN sınıfına eklediğimiz ikinci yöntem build_generator()‘dır. Bu yöntem, sınıf ilk kez başlatıldığında çağrılır. Generator modelinin mimarisi burada tasarlanmıştır. Model özeti, bu modelde gerçekte neler olduğu konusunda size daha net bir fikir verecektir.
Generator modelinin girdisi 512 sayılık bir vektördür. Vektör daha sonra 8x8x8 tensöre yeniden şekillendirilir. Bu tensör daha sonra 16×16, 32×32 ve son olarak 64×64’e yükseltilir. Çıktı Evrişimli katmanı, RGB görüntüsünün sırasıyla Kırmızı, Yeşil ve Mavi kanallarını temsil eden 3 filtre içerir.
Ayrımcı (Discriminator) Oluşturun
# Define Discriminator model. There are 5 convolutional filters, downsampling from (64x64x3) to (1) scalar prediction def build_discriminator(self): model = Sequential() # Input Layer model.add(Conv2D(conv_filters, kernel_size=kernel, input_shape=self.img_shape,activation = "relu", padding="same")) # Downsample the data (64x64 to 32x32) model.add(MaxPooling2D(pool_size=(2, 2))) # 1st Convolutional Layer model.add(Conv2D(conv_filters, kernel_size=kernel, activation='relu', padding="same")) # Downsample the data (32x32 to 16x16) model.add(MaxPooling2D(pool_size=(2, 2))) # 2nd Convolutional Layer model.add(Conv2D(conv_filters, kernel_size=kernel, activation='relu', padding="same")) # Downsample the data (16x16 to 8x8) model.add(MaxPooling2D(pool_size=(2, 2))) # 3rd Convolutional Layer model.add(Conv2D(conv_filters, kernel_size=kernel, activation='relu', padding="same")) # 4th Convolutional Layer model.add(Conv2D(conv_filters, kernel_size=kernel, activation='relu', padding="same")) # 5th Convolutional Layer model.add(Conv2D(conv_filters, kernel_size=kernel, activation='relu', padding="same")) model.add(Flatten()) # Output Layer model.add(Dense(1, activation='sigmoid')) model.summary() img = Input(shape=self.img_shape) validity = model(img) return Model(img, validity)
DCGAN sınıfına eklediğimiz üçüncü yöntem build_discriminator() ‘dür. Bu yöntem, sınıf ilk kez başlatıldığında çağrılır. Discriminator modelinin mimarisi burada tasarlanmıştır. Model özeti, bu modelde gerçekte neler olduğu konusunda size daha net bir fikir verecektir.
Discriminator modelinin girdisi 64x64x3 tensördür. Tensör daha sonra 32×32, 16×16 ve 8×8’e alt örneklenir. Bu 8×8 tensör daha sonra düzleştirilir ve çıktı katmanına aktarılır. Son yoğun katman, ayırıcı modelin tahminini temsil eden tek bir skaler sayı verir. Bu tahmin, giriş görüntüsünün “gerçek” olup olmadığını belirlemede modelin güvenini temsil eder. 1 tahmini, modelin görüntünün orijinal veri kümesinden geldiğini düşündüğü anlamına gelir. 0 tahmini, modelin görüntünün Generator modeli tarafından oluşturulduğunu düşündüğü anlamına gelir.
Eğitmek
# Train the Generative Adversarial Network def train(self, epochs, batch_size, save_interval): # Prevent script from crashing from bad user input if(epochs <= 0): epochs = 1 if(batch_size <= 0): batch_size = 1 # Load the dataset X_train, Y_train = self.load_data() # Normalizing data to be between 0 and 1 Y_train = Y_train/255 # Adversarial ground truths valid = np.ones((batch_size, 1)) fake = np.zeros((batch_size, 1)) # Placeholder arrays for Loss function values g_loss_epochs = np.zeros((epochs, 1)) d_loss_epochs = np.zeros((epochs, 1)) # Training the GAN for epoch in range(1, epochs + 1): # Initialize indexes for training data start = 0 end = start + batch_size # Array to sum up all loss function values discriminator_loss_real = [] discriminator_loss_fake = [] generator_loss = [] # Iterate through dataset training one batch at a time for i in range(int(len(X_train)/batch_size)): # Get batch of images imgs = Y_train[start:end] noise = X_train[start:end] # --------------------- # Train Discriminator # --------------------- # Make predictions on current batch using generator gen_imgs = self.generator.predict(noise) # Train the discriminator (real classified as ones and generated as zero) d_loss_real = self.discriminator.train_on_batch(imgs, valid) d_loss_fake = self.discriminator.train_on_batch(gen_imgs, fake) d_loss = 0.5 * np.add(d_loss_real, d_loss_fake) # --------------------- # Train Generator # --------------------- # Train the generator (wants discriminator to mistake images as real) g_loss = self.combined.train_on_batch(noise, valid) # Add loss for current batch to sum over entire epoch discriminator_loss_real.append(d_loss[0]) discriminator_loss_fake.append(d_loss[1]) generator_loss.append(g_loss) # Increment image indexes start = start + batch_size end = end + batch_size # Get average loss over the entire epoch loss_data = [np.average(discriminator_loss_real),np.average(discriminator_loss_fake),np.average(generator_loss)] #save loss history g_loss_epochs[epoch - 1] = loss_data[2] # Average loss of real data classification and fake data accuracy d_loss_epochs[epoch - 1] = (loss_data[0] + (1 - loss_data[1])) / 2 # Print average loss over current epoch print ("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, loss_data[0], loss_data[1]*100, loss_data[2])) # If epoch is at intervale, save model and generate image samples if epoch % save_interval == 0: # Select 8 random indexes idx = np.random.randint(0, X_train.shape[0], 8) # Get batch of generated images and training images x_points = X_train[idx] y_points = Y_train[idx] # Plot the predictions next to the training imgaes self.save_imgs(epoch, self.generator.predict(x_points), y_points) return g_loss_epochs, d_loss_epochs # Save the model and generate prediction samples for a given epoch def save_imgs(self, epoch, gen_imgs, y_points): # Define number of columns and rows r, c = 4, 4 # Placeholder array for MatPlotLib Figure Subplots subplots = [] # Unnormalize data to be between 0 and 255 for RGB image gen_imgs = np.array(gen_imgs) * 255 gen_imgs = gen_imgs.astype(int) y_points = np.array(y_points) * 255 y_points = y_points.astype(int) # Create figure with title fig = plt.figure(figsize= (40, 40)) fig.suptitle("Epoch: " + str(epoch), fontsize=65) # Initialize counters needed to track indexes across multiple arrays img_count = 0; index_count = 0; y_count = 0; # Loop through columns and rows of the figure for i in range(1, c+1): for j in range(1, r+1): # If row is even, plot the predictions if(j % 2 == 0): img = gen_imgs[index_count] index_count = index_count + 1 # If row is odd, plot the training image else: img = y_points[y_count] y_count = y_count + 1 # Add image to figure, add subplot to array subplots.append(fig.add_subplot(r, c, img_count + 1)) plt.imshow(img) img_count = img_count + 1 # Add title to columns of figure subplots[0].set_title("Training", fontsize=45) subplots[1].set_title("Predicted", fontsize=45) subplots[2].set_title("Training", fontsize=45) subplots[3].set_title("Predicted", fontsize=45) # Save figure to .png image in specified folder fig.savefig(model_path + "\\epoch_%d.png" % epoch) plt.close() # save model to .h5 file in specified folder self.generator.save(model_path + "\\generator" + str(epoch) + ".h5")
DCGAN sınıfına eklediğimiz dördüncü yöntem train(). Bu yöntem, ağı toplu iş boyutu tarafından belirtilen artışlarla belirtilen sayıda dönem için eğitecektir. Eğitim tamamlandığında, yöntem her dönem boyunca her iki modelin kayıp değerlerini temsil eden iki dizi döndürecektir. Kayıp değerleri Matplotlib kullanılarak çizilebilir.
Kayıp değerlerini takip etmeli ve çökmeye başlarsa ağı eğitmeyi bırakmalısınız. Modellerden biri 0 kaybına yaklaşırsa ağ çöker.
Generator 0 kaybına yaklaşırsa, bu, Generator’un her seferinde ayırıcıyı kandıracak bir görüntüyü nasıl oluşturacağını bulduğu anlamına gelir. Bu genellikle Generator’ün mod çökmesi olarak da bilinen tek bir görüntü türü üretmesine neden olur.
Discriminator, 0 kaybına yaklaşırsa, bu, Discriminator’ın eğitim verileri ile oluşturulan görüntüleri çok doğru bir şekilde nasıl ayırt edeceğini bulduğu anlamına gelir. Bu, Generator’ün, kaybolan gradyan problemi olarak da bilinen, ayırıcıdan öğrenmeye devam edememesine neden olacaktır.
Ağımız çöktüğünde ilerlememizi kaybetmemek için, modeli her birkaç dönemde bir kaydedeceğiz. Kullanıcı tanımlı parametre olan interval, modelin ne sıklıkla kaydedileceğini belirleyecektir. Geçerli dönem tanımlanan aralığa her geldiğinde, save_imgs() çağrılır. Yöntem, modelin o dönemde ne kadar iyi olduğuna dair bir anlık görüntü elde etmek için tahmin edilen bazı örneklerin bir görüntüsünü kaydedecektir.
Görüntüleri Kaydedin
# Save the model and generate prediction samples for a given epoch def save_imgs(self, epoch, gen_imgs, y_points): # Define number of columns and rows r, c = 4, 4 # Placeholder array for MatPlotLib Figure Subplots subplots = [] # Unnormalize data to be between 0 and 255 for RGB image gen_imgs = np.array(gen_imgs) * 255 gen_imgs = gen_imgs.astype(int) y_points = np.array(y_points) * 255 y_points = y_points.astype(int) # Create figure with title fig = plt.figure(figsize= (40, 40)) fig.suptitle("Epoch: " + str(epoch), fontsize=65) # Initialize counters needed to track indexes across multiple arrays img_count = 0; index_count = 0; y_count = 0; # Loop through columns and rows of the figure for i in range(1, c+1): for j in range(1, r+1): # If row is even, plot the predictions if(j % 2 == 0): img = gen_imgs[index_count] index_count = index_count + 1 # If row is odd, plot the training image else: img = y_points[y_count] y_count = y_count + 1 # Add image to figure, add subplot to array subplots.append(fig.add_subplot(r, c, img_count + 1)) plt.imshow(img) img_count = img_count + 1 # Add title to columns of figure subplots[0].set_title("Training", fontsize=45) subplots[1].set_title("Predicted", fontsize=45) subplots[2].set_title("Training", fontsize=45) subplots[3].set_title("Predicted", fontsize=45) # Save figure to .png image in specified folder fig.savefig(model_path + "\\epoch_%d.png" % epoch) plt.close() # save model to .h5 file in specified folder self.generator.save(model_path + "\\generator" + str(epoch) + ".h5")
DCGAN sınıfına eklediğimiz beşinci ve son yöntem, save_imgs() yöntemidir. Bu yöntem, modeli mevcut çağda kaydedecek ve tahmin edilen değerlerin yanı sıra 8 eğitim görüntüsünü çizecektir.
Bu yöntem şu anda her 5 çağda bir kaydedecek şekilde yapılandırılmıştır. Bu, aralık (interval) parametresi ile ayarlanabilir. Modelinizi sık sık kaydetmek, eğitim sürecinde ağınızın kaydettiği ilerlemeyi izlemenin iyi bir yoludur.
DCGAN Sınıfını Başlatma
Artık DCGAN sınıfını oluşturmayı bitirdik ve Generative Adversarial Network’ümüzü eğitmeye hazırız. Öncelikle, sınıfın bir örneğini oluşturmalı ve onu bir değişkene atamalıyız.
Girdi [4]:
dcgan = DCGAN()
Bu, Generator ve Discriminator modellerini başlatacak ve bunların özetlerini yazdıracaktır.
Üretken Çekişmeli Ağın Eğitimi
Artık DCGAN sınıf nesnemize sahip olduğumuza göre, eğitime başlamak için train() yöntemini çağırmamız gerekiyor. Bu komut dosyasıyla, genellikle eğitim için çok sayıda dönem seçmeli ve süreç boyunca kayıp değerlerini izlemelisiniz. Ağ çökmeye başlarsa eğitimi erken durdurun ve hangi modelin en iyi performans gösteren model olduğunu bulmak için oluşturulan örnekleri kontrol edin.
Train() yöntemi, eğitim boyunca iki modelin kayıp değerlerini içeren iki dizi döndürür. Bu değerleri g_loss ve d_loss’a atayacağız ve grafiğini çizeceğiz.
Girdi [5]:
g_loss, d_loss = dcgan.train(epochs=epoch, batch_size=batch, save_interval=interval)
1 [D loss: 0.680393, acc.: 61.54%] [G loss: 0.732704] 2 [D loss: 0.654482, acc.: 60.98%] [G loss: 0.835647] 3 [D loss: 0.680507, acc.: 59.27%] [G loss: 0.832127] 4 [D loss: 0.667612, acc.: 61.23%] [G loss: 0.890128]
5 [D loss: 0.678923, acc.: 57.80%] [G loss: 0.878026]
6 [D loss: 0.669563, acc.: 59.07%] [G loss: 0.857507] 7 [D loss: 0.674293, acc.: 59.85%] [G loss: 0.880131] 8 [D loss: 0.667477, acc.: 58.76%] [G loss: 0.876913] 9 [D loss: 0.663820, acc.: 59.30%] [G loss: 0.891338]
10 [D loss: 0.659955, acc.: 59.45%] [G loss: 0.909291]
Plot Kaybı
plt.plot(g_loss) plt.plot(d_loss) plt.title('GAN Loss') plt.ylabel('Loss') plt.xlabel('Epoch') plt.legend(['Generator', 'Discriminator'], loc='upper left') plt.show()
Sonuç
Bu makale size Keras kullanarak bir Üretken Çekişmeli Ağı eğitmek için genel bir çerçeve sağlar. Bu komut dosyasını kullanarak kendi veri kümelerinizle kendi üretken modellerinizi oluşturabileceksiniz. Bu kodun tam sürümüne buradan ulaşabilirsiniz. Komut dosyası şu anda 64×64 görüntüler için yapılandırılmıştır. Farklı boyuttaki görüntülere sahip veri kümelerini kullanmak istiyorsanız, img_dimensions parametresini ayarlamanız ve buna göre UpSampling2D ve MaxPooling2D katmanlarını ayarlamanız gerekir.
Memnun olduğunuz bir modeli eğittikten sonra, çıktılar oluşturmak ve sonuçlarınızı analiz etmek için PCA GAN Çıkarım komut dosyasını kullanabilirsiniz. Bu komut dosyası ayrıca, makalenin başında sağlananlar gibi Generator modelinin gizli alanında yürüyen GIF’ler oluşturmak için kod sağlayacaktır.
Modeliniz bir tahmin yaptığında her evrişimli katmanda neler olup bittiğine bakmak için Model Görselleştirme komut dosyasını da kullanabilirsiniz.