В этой статье мы узнаем, как построить GAN с нуля, используя слои свертки. Для демонстрации и быстрой работы мы будем использовать набор данных Fashion MNIST. И после того, как вы его выучите, то же самое вы можете сделать и с лицами. Прежде чем вдаваться в подробности, я хочу, чтобы вы подумали: можем ли мы использовать глубокое обучение для создания чего-то из ничего? Чтобы ответить на этот вопрос, мы собираемся внедрить GAN и увидеть результаты своими глазами.

GAN — это объединение двух нейронных сетей: генератора и дискриминатора. генератор создает новые экземпляры данных, а другой, дискриминатор, оценивает их подлинность; то есть дискриминатор решает, принадлежит ли каждый экземпляр данных, которые он просматривает, фактическому набору обучающих данных или нет. Теперь давайте реализуем и посмотрим сами.

Сначала загрузите следующие основные зависимости

  1. Tensorflow (я тестировал 1.12)
  2. Керас
  3. Панды
  4. OpenCV

Теперь вы можете начать с открытия блокнота Jupyter и начать вводить каждый фрагмент кода в блокноте.

(x_train,y_train), (x_test, y_test) = fashion_mnist.load_data()#We next normalize the imagesx_train = (x_train.astype(np.float32) - 127.5)/127.5

Теперь у нас есть данные для обучения,содержащие 60 тысяч изображений формы (28 x 28 x 1). Итак, нам нужно создать Генератор, который принимает изображения этой конкретной формы.

def create_discriminator():
    discriminator=Sequential()
    discriminator.add(Dense(units=512,input_dim=28*28))
    #discriminator.add(Conv2D(64, (3,3), strides =(1,1), padding ='same', input_shape = [28,28,1]))
    discriminator.add(LeakyReLU(0.2))
    discriminator.add(Dropout(0.3))
    #discriminator.add(Flatten())
       
    
    discriminator.add(Dense(units=512))
    discriminator.add(LeakyReLU(0.2))
    discriminator.add(Dropout(0.3))
       
    discriminator.add(Dense(units=256))
    discriminator.add(LeakyReLU(0.2))
    
    discriminator.add(Dense(units=1, activation='sigmoid'))
    
    discriminator.compile(loss='binary_crossentropy', optimizer=adam_optimizer())
    return discriminator
d =create_discriminator()
d.summary()
#Summary of the GeneratorLayer (type)                 Output Shape              Param #    ================================================================= conv2d_3 (Conv2D)            (None, 10, 10, 64)        640        _________________________________________________________________ batch_normalization_3 (Batch (None, 10, 10, 64)        256        _________________________________________________________________ leaky_re_lu_13 (LeakyReLU)   (None, 10, 10, 64)        0          _________________________________________________________________ conv2d_4 (Conv2D)            (None, 4, 4, 32)          18464      _________________________________________________________________ batch_normalization_4 (Batch (None, 4, 4, 32)          128        _________________________________________________________________ leaky_re_lu_14 (LeakyReLU)   (None, 4, 4, 32)          0          _________________________________________________________________ flatten_3 (Flatten)          (None, 512)               0          _________________________________________________________________ dense_15 (Dense)             (None, 512)               262656     _________________________________________________________________ leaky_re_lu_15 (LeakyReLU)   (None, 512)               0          _________________________________________________________________ dense_16 (Dense)             (None, 1024)              525312     _________________________________________________________________ leaky_re_lu_16 (LeakyReLU)   (None, 1024)              0          _________________________________________________________________ dense_17 (Dense)             (None, 784)               803600     ================================================================= Total params: 1,611,056 Trainable params: 1,610,864 Non-trainable params: 192

Мы создали Генератор, который состоит из двух сверточных слоев, за которыми следуют два плотных слоя. Последний плотный слой имеет форму (28x28), так что мы можем реконструировать его обратно в изображения, как в наборе данных.

Затем мы создаем дискриминатор, который будет определять, являются ли изображения, созданные Генератором, поддельными или реальными. Если дискриминатор предсказывает, что эти изображения являются поддельными, то генератор изменяет свой вес до тех пор, пока не сможет обмануть дискриминатор. Так Генератор научится создавать изображения из шумов.

def create_discriminator():
    discriminator=Sequential()
    discriminator.add(Dense(units=512,input_dim=28*28))
    #discriminator.add(Conv2D(64, (3,3), strides =(1,1), padding ='same', input_shape = [28,28,1]))
    discriminator.add(LeakyReLU(0.2))
    discriminator.add(Dropout(0.3))
    #discriminator.add(Flatten())
       
    
    discriminator.add(Dense(units=512))
    discriminator.add(LeakyReLU(0.2))
    discriminator.add(Dropout(0.3))
       
    discriminator.add(Dense(units=256))
    discriminator.add(LeakyReLU(0.2))
    
    discriminator.add(Dense(units=1, activation='sigmoid'))
    
    discriminator.compile(loss='binary_crossentropy', optimizer=adam_optimizer())
    return discriminator
d =create_discriminator()
d.summary()

Мы создали Генератор и Дискриминатор. Затем мы объединяем их обоих для создания GAN. В приведенном ниже коде мы объединяем два модуля и обучаем их.

def create_gan(discriminator, generator):
    discriminator.trainable=False
    img_shape=(28, 28, 1)
    gan_input = Input(img_shape)
    x = generator(gan_input)
    gan_output= discriminator(x)
    gan= Model(inputs=gan_input, outputs=gan_output)
    gan.compile(loss='binary_crossentropy',     optimizer=adam_optimizer())
    return gan

Теперь давайте обучим GAN, объединив Генератор с Дискриминатором.

generator= create_generator()
discriminator= create_discriminator()
gan = create_gan(discriminator, generator)

batch_size = 128
Epochs = 300
for epcs in range(1, Epochs ):
        print("Epoch Number %d" %epcs)
        for _ in tqdm(range(batch_size)):
        #generate  random noise of shape 28*28
            noise= np.random.normal(0,1, [batch_size, 28,28,1])
       
            # Now predict the noise by feeding it to generator
            generated_images = generator.predict(noise)
            
            # Take real images from the Fashion MNIST
            image_batch =x_train[np.random.randint
                        (low=0,high=x_train.shape[0],size=128)]
            # The discriminator accepts vector of dimension 784
            image_batch = image_batch.reshape(-1,28*28)
           
            #We concate both real and fake images 
            X= np.concatenate([image_batch, generated_images])
           
            # We Next assign label to X
            y_discriminator=np.zeros(2*batch_size)
            
            # First  images are true, so we label them 1
            y_discriminator[:batch_size]=1
            
            #Pretrain discriminator on  fake and real data 
            discriminator.trainable=True
            discriminator.train_on_batch(X, y_discriminator)
            
            # We trick the noises to be real
            noise= np.random.normal(0,1, [batch_size, 28,28,1])
            y_label = np.ones(batch_size)
            
           
            discriminator.trainable=False
         
            # Let us train the GAN  
            gan.train_on_batch(noise, y_label)

После завершения обучения вы можете предсказать Генератор на основе шума.

# feed the noises to the generator and predict them
generated_images = generator.predict(noise)#we are going to see the first image i.e at index 0
x =generated_images[0].reshape(28,28)# Let us plot the image
plt.imshow(x, cmap ="Greys")

Ниже приведены изображения, сгенерированные из шумов, и теперь вы можете начать тестирование на своих собственных изображениях.