ОГЛАВЛЕНИЕ:
- ВСТУПЛЕНИЕ
- ИСТОРИЯ ГАНОВ
- ИНТУИТИВНОЕ ОБЪЯСНЕНИЕ GAN
- ОБУЧАЮЩИЕ ГАНЫ
- ПРОЦЕСС ОБУЧЕНИЯ GAN
- БЛОК-ДИАГРАММА ГАНА
- РЕАЛИЗАЦИЯ GAN В KERAS НА НАБОРЕ ДАННЫХ MNIST
ВВЕДЕНИЕ
Генеративные состязательные сети, также обычно называемые GAN, используются для генерации изображений без очень небольшого ввода или без него. GAN позволяют нам генерировать изображения, созданные нашими нейронными сетями, полностью удаляя человека (да, вас) из цикла. Прежде чем мы погрузимся в теорию, я хотел бы показать вам способности GAN вызывать у вас интерес. Превратите лошадей в зебр (наоборот).
ИСТОРИЯ ГАНОВ
Генеративные состязательные сети (GAN) были представлены Яном Гудфеллоу (GANFather of GAN) и др. в 2014 году в своей статье под соответствующим названием «Генеративные состязательные сети». Он был предложен в качестве альтернативы вариационным автокодировщикам (VAE), которые изучают скрытые пространства изображений, для создания синтетических изображений. Его цель - создавать реалистичные искусственные изображения, которые почти невозможно отличить от реальных.
ИНТУИТИВНОЕ ОБЪЯСНЕНИЕ GAN
Представьте, что есть амбициозный молодой преступник, который хочет подделать деньги и продать их бандиту, который специализируется на обращении с поддельными деньгами. Сначала молодой фальшивомонетчик плохой, и наш опытный бандит говорит ему, что его деньги далеко не настоящие. Постепенно он поправляется и время от времени делает хорошие «копии». Гангстер говорит ему, когда это хорошо. Через некоторое время и фальшивомонетчик (наш фальшивомонетчик), и опытный гангстер становятся лучше в своей работе, и теперь они создали почти настоящие, но фальшивые деньги.
Сети генератора и дискриминатора:
● Цель сети генератора состоит в том, чтобы выполнить инициализацию случайного изображения и декодировать его в синтетическое изображение.
● Цель сети дискриминатора состоит в том, чтобы принять эти входные данные и предсказать, получено ли это изображение из реального набора данных или синтетический.
● Как мы только что видели, это фактически и есть GAN, две антагонистические сети, которые соревнуются друг с другом. Эти два компонента называются:
- Generator Network - в нашем примере это молодой преступник, создающий фальшивые деньги.
- Сеть Дискриминатора - мафиози в нашем примере.
ОБУЧАЮЩИЕ ГАНЫ
● Обучение 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 отсюда.