ОГЛАВЛЕНИЕ:

  1. ВСТУПЛЕНИЕ
  2. ИСТОРИЯ ГАНОВ
  3. ИНТУИТИВНОЕ ОБЪЯСНЕНИЕ GAN
  4. ОБУЧАЮЩИЕ ГАНЫ
  5. ПРОЦЕСС ОБУЧЕНИЯ GAN
  6. БЛОК-ДИАГРАММА ГАНА
  7. РЕАЛИЗАЦИЯ GAN В KERAS НА НАБОРЕ ДАННЫХ MNIST

ВВЕДЕНИЕ

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

ИСТОРИЯ ГАНОВ

Генеративные состязательные сети (GAN) были представлены Яном Гудфеллоу (GANFather of GAN) и др. в 2014 году в своей статье под соответствующим названием «Генеративные состязательные сети». Он был предложен в качестве альтернативы вариационным автокодировщикам (VAE), которые изучают скрытые пространства изображений, для создания синтетических изображений. Его цель - создавать реалистичные искусственные изображения, которые почти невозможно отличить от реальных.

ИНТУИТИВНОЕ ОБЪЯСНЕНИЕ GAN

Представьте, что есть амбициозный молодой преступник, который хочет подделать деньги и продать их бандиту, который специализируется на обращении с поддельными деньгами. Сначала молодой фальшивомонетчик плохой, и наш опытный бандит говорит ему, что его деньги далеко не настоящие. Постепенно он поправляется и время от времени делает хорошие «копии». Гангстер говорит ему, когда это хорошо. Через некоторое время и фальшивомонетчик (наш фальшивомонетчик), и опытный гангстер становятся лучше в своей работе, и теперь они создали почти настоящие, но фальшивые деньги.

Сети генератора и дискриминатора:

● Цель сети генератора состоит в том, чтобы выполнить инициализацию случайного изображения и декодировать его в синтетическое изображение.
● Цель сети дискриминатора состоит в том, чтобы принять эти входные данные и предсказать, получено ли это изображение из реального набора данных или синтетический.

● Как мы только что видели, это фактически и есть GAN, две антагонистические сети, которые соревнуются друг с другом. Эти два компонента называются:

  1. Generator Network - в нашем примере это молодой преступник, создающий фальшивые деньги.
  2. Сеть Дискриминатора - мафиози в нашем примере.

ОБУЧАЮЩИЕ ГАНЫ

● Обучение GAN очень сложно. В CNN мы использовали градиентный спуск, чтобы изменить наши веса, чтобы уменьшить наши потери.

● Однако в GAN каждое изменение веса меняет весь баланс нашей динамической системы.

● В GAN мы не стремимся минимизировать потери, а находим равновесие между двумя нашими противостоящими Сетями.

ПРОЦЕСС ОБУЧЕНИЯ GAN

1. Ввод случайным образом генерирует зашумленные изображения в нашей сети генераторов для генерации образца изображения.

2. Мы берем несколько образцов изображений из наших реальных данных и смешиваем их с некоторыми из наших сгенерированных изображений.

3. Введите эти смешанные изображения в наш Дискриминатор, который затем будет обучен на этом смешанном наборе и соответственно обновит его веса.

4. Затем мы делаем еще несколько фальшивых изображений и вводим их в Дискриминатор, но помечаем все как настоящие. Это сделано для обучения Генератора. На этом этапе мы заморозили веса дискриминатора (обучение дискриминатора остановлено), и теперь мы используем обратную связь от дискриминатора, чтобы обновить веса генератора. Вот как мы обучаем как наш Генератор (для создания более качественных синтетических изображений), так и Дискриминатор, чтобы лучше распознавать подделки.

Блок-схема GAN

В этой статье мы будем генерировать рукописные числа с использованием набора данных MNIST. Архитектура этой GAN:

РЕАЛИЗАЦИЯ GAN В KERAS НА НАБОРЕ ДАННЫХ MNIST

Полный код проекта можно найти здесь.

Сначала загружаем все необходимые библиотеки

import os
os.environ["KERAS_BACKEND"] = "tensorflow"
import numpy as np
from tqdm import tqdm
import matplotlib.pyplot as plt

from keras.layers import Input
from keras.models import Model, Sequential
from keras.layers.core import Reshape, Dense, Dropout, Flatten
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import Convolution2D, UpSampling2D
from keras.layers.normalization import BatchNormalization
from keras.datasets import mnist
from keras.optimizers import Adam
from keras import backend as K
from keras import initializers

K.set_image_dim_ordering('th')

# Deterministic output.
# Tired of seeing the same results every time? Remove the line below.
np.random.seed(1000)

# The results are a little better when the dimensionality of the random vector is only 10.
# The dimensionality has been left at 100 for consistency with other GAN implementations.
randomDim = 100

Теперь загружаем наш набор данных. Для этого блога используется набор данных MNIST, поэтому не нужно загружать набор данных отдельно.

(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = (X_train.astype(np.float32) - 127.5)/127.5
X_train = X_train.reshape(60000, 784)

Далее мы определяем архитектуру нашего генератора и дискриминатора.

# Optimizer
adam = Adam(lr=0.0002, beta_1=0.5)
#generator
generator = Sequential()
generator.add(Dense(256, input_dim=randomDim, kernel_initializer=initializers.RandomNormal(stddev=0.02)))
generator.add(LeakyReLU(0.2))
generator.add(Dense(512))
generator.add(LeakyReLU(0.2))
generator.add(Dense(1024))
generator.add(LeakyReLU(0.2))
generator.add(Dense(784, activation='tanh'))
generator.compile(loss='binary_crossentropy', optimizer=adam)
#discriminator
discriminator = Sequential()
discriminator.add(Dense(1024, input_dim=784, kernel_initializer=initializers.RandomNormal(stddev=0.02)))
discriminator.add(LeakyReLU(0.2))
discriminator.add(Dropout(0.3))
discriminator.add(Dense(512))
discriminator.add(LeakyReLU(0.2))
discriminator.add(Dropout(0.3))
discriminator.add(Dense(256))
discriminator.add(LeakyReLU(0.2))
discriminator.add(Dropout(0.3))
discriminator.add(Dense(1, activation='sigmoid'))
discriminator.compile(loss='binary_crossentropy', optimizer=adam)

Теперь мы объединяем наш генератор и дискриминатор для одновременного обучения.

# Combined network
discriminator.trainable = False
ganInput = Input(shape=(randomDim,))
x = generator(ganInput)
ganOutput = discriminator(x)
gan = Model(inputs=ganInput, outputs=ganOutput)
gan.compile(loss='binary_crossentropy', optimizer=adam)

dLosses = []
gLosses = []

Три функции для построения и сохранения результатов через каждые 20 эпох и сохранения модели.

# Plot the loss from each batch
def plotLoss(epoch):
    plt.figure(figsize=(10, 8))
    plt.plot(dLosses, label='Discriminitive loss')
    plt.plot(gLosses, label='Generative loss')
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.legend()
    plt.savefig('images/gan_loss_epoch_%d.png' % epoch)

# Create a wall of generated MNIST images
def plotGeneratedImages(epoch, examples=100, dim=(10, 10), figsize=(10, 10)):
    noise = np.random.normal(0, 1, size=[examples, randomDim])
    generatedImages = generator.predict(noise)
    generatedImages = generatedImages.reshape(examples, 28, 28)

    plt.figure(figsize=figsize)
    for i in range(generatedImages.shape[0]):
        plt.subplot(dim[0], dim[1], i+1)
        plt.imshow(generatedImages[i], interpolation='nearest', cmap='gray_r')
        plt.axis('off')
    plt.tight_layout()
    plt.savefig('images/gan_generated_image_epoch_%d.png' % epoch)

# Save the generator and discriminator networks (and weights) for later use
def saveModels(epoch):
    generator.save('models/gan_generator_epoch_%d.h5' % epoch)
    discriminator.save('models/gan_discriminator_epoch_%d.h5' % epoch)

Функция поезда

def train(epochs=1, batchSize=128):
    batchCount = X_train.shape[0] / batchSize
    print 'Epochs:', epochs
    print 'Batch size:', batchSize
    print 'Batches per epoch:', batchCount

    for e in xrange(1, epochs+1):
        print '-'*15, 'Epoch %d' % e, '-'*15
        for _ in tqdm(xrange(batchCount)):
            # Get a random set of input noise and images
            noise = np.random.normal(0, 1, size=[batchSize, randomDim])
            imageBatch = X_train[np.random.randint(0, X_train.shape[0], size=batchSize)]

            # Generate fake MNIST images
            generatedImages = generator.predict(noise)
            # print np.shape(imageBatch), np.shape(generatedImages)
            X = np.concatenate([imageBatch, generatedImages])

            # Labels for generated and real data
            yDis = np.zeros(2*batchSize)
            # One-sided label smoothing
            yDis[:batchSize] = 0.9

            # Train discriminator
            discriminator.trainable = True
            dloss = discriminator.train_on_batch(X, yDis)

            # Train generator
            noise = np.random.normal(0, 1, size=[batchSize, randomDim])
            yGen = np.ones(batchSize)
            discriminator.trainable = False
            gloss = gan.train_on_batch(noise, yGen)

        # Store loss of most recent batch from this epoch
        dLosses.append(dloss)
        gLosses.append(gloss)

        if e == 1 or e % 20 == 0:
            plotGeneratedImages(e)
            saveModels(e)

    # Plot losses from every epoch
    plotLoss(e)

train(200, 128)

Чтобы оставаться на связи, подписывайтесь на меня здесь.

ПРОЧИТАЙТЕ МОЙ ПРЕДЫДУЩИЙ БЛОГ: ПОНИМАНИЕ U-Net отсюда.