In deze tutorial leer je wat Generative Adversarial Networks (GAN’s) zijn, zonder in de wiskundige details te duiken. Daarna leer je hoe je een simpele GAN codeert die cijfers kan maken!
Analogie
De makkelijkste manier om te begrijpen wat GAN’s zijn, is via een eenvoudige analogie:
Stel dat er een winkel is die bepaalde soorten wijn opkoopt van klanten om die later weer te verkopen.
Er zijn echter kwaadwillende klanten die nepwijn verkopen om geld te verdienen. In dit geval moet de winkelier het verschil kunnen zien tussen neppe en authentieke wijnen.
Je kunt je voorstellen dat de vervalser in het begin veel fouten maakt bij het verkopen van nepwijn en dat het voor de winkelier makkelijk is om te zien dat de wijn niet echt is. Door deze mislukkingen blijft de vervalser verschillende technieken proberen om echte wijn te imiteren, en sommige zullen uiteindelijk slagen. Nu de vervalser weet dat bepaalde technieken door de controles van de winkelier heen kwamen, kan hij de nepwijnen verder verbeteren op basis van die technieken.
Tegelijkertijd krijgt de winkelier waarschijnlijk feedback van andere winkeliers of wijnexperts dat sommige wijnen die zij heeft niet origineel zijn. Dat betekent dat de winkelier moet verbeteren hoe ze bepaalt of een wijn nep of authentiek is. Het doel van de vervalser is wijnen te maken die niet te onderscheiden zijn van de echte, en het doel van de winkelier is nauwkeurig vast te stellen of een wijn echt is of niet.
Deze competitie heen en weer is het kernidee achter GAN’s.
Componenten van een Generative Adversarial Network
Met het voorbeeld hierboven kunnen we de architectuur van een GAN opstellen.
Er zijn twee hoofdcomponenten binnen GAN’s: de generator en de discriminator. De winkelier in het voorbeeld staat bekend als een discriminatornetwerk en is meestal een convolutioneel neuraal netwerk (aangezien GAN’s vooral voor beeldtaken worden gebruikt) dat een waarschijnlijkheid toekent dat de afbeelding echt is.
De vervalser staat bekend als het generatieve netwerk, en is doorgaans ook een convolutioneel neuraal netwerk (met deconvolutielagen). Dit netwerk neemt een ruisvector en geeft een afbeelding als output. Tijdens het trainen leert het generatieve netwerk welke delen van de afbeelding het moet verbeteren of aanpassen zodat de discriminator meer moeite heeft om zijn gegenereerde beelden van echte te onderscheiden.
Het generatieve netwerk blijft afbeeldingen produceren die qua uiterlijk dichter bij de echte beelden komen, terwijl het discriminatieve netwerk probeert de verschillen tussen echte en nepafbeeldingen te bepalen. Het uiteindelijke doel is een generatief netwerk dat afbeeldingen kan produceren die niet te onderscheiden zijn van de echte.
Een eenvoudige Generative Adversarial Network met Keras
Nu je begrijpt wat GAN’s zijn en wat hun belangrijkste componenten zijn, kunnen we een heel eenvoudige versie gaan coderen. Je gebruikt Keras en als je niet bekend bent met deze Python-bibliotheek, lees dan eerst deze tutorial voordat je doorgaat. Deze tutorial is gebaseerd op de GAN die hier is ontwikkeld.
Het eerste wat je moet doen, is de volgende pakketten installeren via pip
keras
matplotlib
tensorflow
tqdm
Je gebruikt matplotlib om te plotten, tensorflow als de Keras-backendbibliotheek en tqdm om een mooie voortgangsbalk te tonen voor elke epoch (iteratie).
De volgende stap is het maken van een Python-script. In dit script importeer je eerst alle modules en functies die je gaat gebruiken. Een uitleg van elk volgt wanneer ze worden gebruikt.
import os
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
from keras.layers import Input
from keras.models import Model, Sequential
from keras.layers.core import Dense, Dropout
from keras.layers.advanced_activations import LeakyReLU
from keras.datasets import mnist
from keras.optimizers import Adam
from keras import initializers
Mogelijk gemaakt door
Nu wil je enkele variabelen instellen:
# Let Keras know that we are using tensorflow as our backend engine
os.environ["KERAS_BACKEND"]="tensorflow"# To make sure that we can reproduce the experiment and get the same results
np.random.seed(10)# The dimension of our random noise vector.
random_dim =100Mogelijk gemaakt door
Voordat je begint met het bouwen van de discriminator en generator, moet je eerst de data verzamelen en pre-processen. Je gebruikt de populaire MNIST-dataset met afbeeldingen van enkelvoudige cijfers van 0 tot en met 9.
defload_minst_data():# load the data(x_train, y_train),(x_test, y_test)= mnist.load_data()# normalize our inputs to be in the range[-1, 1]
x_train =(x_train.astype(np.float32)-127.5)/127.5# convert x_train with a shape of (60000, 28, 28) to (60000, 784) so we have# 784 columns per row
x_train = x_train.reshape(60000,784)return(x_train, y_train, x_test, y_test)Mogelijk gemaakt door
Let op dat mnist.load_data() onderdeel is van Keras en het je makkelijk maakt om de MNIST-dataset in je werkomgeving te importeren.
Nu kun je je generator- en discriminatornetwerken maken. Je gebruikt de Adam-optimizer voor beide netwerken. Voor zowel de generator als de discriminator maak je een neuraal netwerk met drie verborgen lagen, met als activatiefunctie de Leaky ReLU. Voeg voor de discriminator ook dropoutlagen toe om de robuustheid op ongeziene beelden te verbeteren.
# You will use the Adam optimizerdefget_optimizer():return Adam(lr=0.0002, beta_1=0.5)defget_generator(optimizer):
generator = Sequential()
generator.add(Dense(256, input_dim=random_dim, 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=optimizer)return generator
defget_discriminator(optimizer):
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=optimizer)return discriminator
Mogelijk gemaakt door
Het is eindelijk tijd om de generator en discriminator samen te brengen!
defget_gan_network(discriminator, random_dim, generator, optimizer):# We initially set trainable to False since we only want to train either the# generator or discriminator at a time
discriminator.trainable =False# gan input (noise) will be 100-dimensional vectors
gan_input = Input(shape=(random_dim,))# the output of the generator (an image)
x = generator(gan_input)# get the output of the discriminator (probability if the image is real or not)
gan_output = discriminator(x)
gan = Model(inputs=gan_input, outputs=gan_output)
gan.compile(loss='binary_crossentropy', optimizer=optimizer)return gan
Mogelijk gemaakt door
Voor de volledigheid kun je een functie maken die elke 20 epochs je gegenereerde afbeeldingen opslaat. Omdat dit niet de kern van deze les is, hoef je de functie niet volledig te begrijpen.
# Create a wall of generated MNIST imagesdefplot_generated_images(epoch, generator, examples=100, dim=(10,10), figsize=(10,10)):
noise = np.random.normal(0,1, size=[examples, random_dim])
generated_images = generator.predict(noise)
generated_images = generated_images.reshape(examples,28,28)
plt.figure(figsize=figsize)for i inrange(generated_images.shape[0]):
plt.subplot(dim[0], dim[1], i+1)
plt.imshow(generated_images[i], interpolation='nearest', cmap='gray_r')
plt.axis('off')
plt.tight_layout()
plt.savefig('gan_generated_image_epoch_%d.png'% epoch)Mogelijk gemaakt door
Je hebt nu het grootste deel van je netwerk gecodeerd. Het enige wat nog rest, is dit netwerk trainen en de gemaakte afbeeldingen bekijken.
deftrain(epochs=1, batch_size=128):# Get the training and testing data
x_train, y_train, x_test, y_test = load_minst_data()# Split the training data into batches of size 128
batch_count = x_train.shape[0]/ batch_size
# Build our GAN netowrk
adam = get_optimizer()
generator = get_generator(adam)
discriminator = get_discriminator(adam)
gan = get_gan_network(discriminator, random_dim, generator, adam)for e inxrange(1, epochs+1):print'-'*15,'Epoch %d'% e,'-'*15for _ in tqdm(xrange(batch_count)):# Get a random set of input noise and images
noise = np.random.normal(0,1, size=[batch_size, random_dim])
image_batch = x_train[np.random.randint(0, x_train.shape[0], size=batch_size)]# Generate fake MNIST images
generated_images = generator.predict(noise)
X = np.concatenate([image_batch, generated_images])# Labels for generated and real data
y_dis = np.zeros(2*batch_size)# One-sided label smoothing
y_dis[:batch_size]=0.9# Train discriminator
discriminator.trainable =True
discriminator.train_on_batch(X, y_dis)# Train generator
noise = np.random.normal(0,1, size=[batch_size, random_dim])
y_gen = np.ones(batch_size)
discriminator.trainable =False
gan.train_on_batch(noise, y_gen)if e ==1or e %20==0:
plot_generated_images(e, generator)if __name__ =='__main__':
train(400,128)Mogelijk gemaakt door
Na trainen gedurende 400 epochs kun je de gegenereerde afbeeldingen bekijken. Als je naar de beelden na de eerste epoch kijkt, zie je dat er nog geen echte structuur is; na 40 epochs beginnen de cijfers vorm te krijgen en tot slot tonen de afbeeldingen na 400 epochs duidelijke cijfers, al zijn er nog een paar onherkenbaar.
Resultaten na 1 epoch
Resultaten na 40 epochs
Resultaten na 400 epochs
!
Deze code kost op een CPU ongeveer 2 minuten per epoch, en dat is de belangrijkste reden dat deze code is gekozen. Je kunt experimenteren met meer epochs en met het toevoegen van meer (en andere) lagen aan zowel de generator als de discriminator. Houd er wel rekening mee dat bij complexere en diepere architecturen de looptijd ook toeneemt als je alleen een CPU gebruikt. Laat dat je echter niet weerhouden om te experimenteren!
Conclusie
Gefeliciteerd, je bent aan het einde van deze tutorial gekomen, waarin je op een intuïtieve manier de basis van Generative Adversarial Networks (GAN’s) hebt geleerd! Ook heb je je eerste model geïmplementeerd met behulp van de Keras-bibliotheek. Wil je meer weten over deep learning met Python, overweeg dan DataCamp’s cursus Deep Learning in Python.
In deze tutorial leer je wat Generative Adversarial Networks (GAN’s) zijn, zonder in de wiskundige details te duiken. Daarna leer je hoe je een simpele GAN codeert die cijfers kan maken!
Analogie
De makkelijkste manier om te begrijpen wat GAN’s zijn, is via een eenvoudige analogie:
Stel dat er een winkel is die bepaalde soorten wijn opkoopt van klanten om die later weer te verkopen.
Er zijn echter kwaadwillende klanten die nepwijn verkopen om geld te verdienen. In dit geval moet de winkelier het verschil kunnen zien tussen neppe en authentieke wijnen.
Je kunt je voorstellen dat de vervalser in het begin veel fouten maakt bij het verkopen van nepwijn en dat het voor de winkelier makkelijk is om te zien dat de wijn niet echt is. Door deze mislukkingen blijft de vervalser verschillende technieken proberen om echte wijn te imiteren, en sommige zullen uiteindelijk slagen. Nu de vervalser weet dat bepaalde technieken door de controles van de winkelier heen kwamen, kan hij de nepwijnen verder verbeteren op basis van die technieken.
Tegelijkertijd krijgt de winkelier waarschijnlijk feedback van andere winkeliers of wijnexperts dat sommige wijnen die zij heeft niet origineel zijn. Dat betekent dat de winkelier moet verbeteren hoe ze bepaalt of een wijn nep of authentiek is. Het doel van de vervalser is wijnen te maken die niet te onderscheiden zijn van de echte, en het doel van de winkelier is nauwkeurig vast te stellen of een wijn echt is of niet.
Deze competitie heen en weer is het kernidee achter GAN’s.
Componenten van een Generative Adversarial Network
Met het voorbeeld hierboven kunnen we de architectuur van een GAN opstellen.
Er zijn twee hoofdcomponenten binnen GAN’s: de generator en de discriminator. De winkelier in het voorbeeld staat bekend als een discriminatornetwerk en is meestal een convolutioneel neuraal netwerk (aangezien GAN’s vooral voor beeldtaken worden gebruikt) dat een waarschijnlijkheid toekent dat de afbeelding echt is.
De vervalser staat bekend als het generatieve netwerk, en is doorgaans ook een convolutioneel neuraal netwerk (met deconvolutielagen). Dit netwerk neemt een ruisvector en geeft een afbeelding als output. Tijdens het trainen leert het generatieve netwerk welke delen van de afbeelding het moet verbeteren of aanpassen zodat de discriminator meer moeite heeft om zijn gegenereerde beelden van echte te onderscheiden.
Het generatieve netwerk blijft afbeeldingen produceren die qua uiterlijk dichter bij de echte beelden komen, terwijl het discriminatieve netwerk probeert de verschillen tussen echte en nepafbeeldingen te bepalen. Het uiteindelijke doel is een generatief netwerk dat afbeeldingen kan produceren die niet te onderscheiden zijn van de echte.
Een eenvoudige Generative Adversarial Network met Keras
Nu je begrijpt wat GAN’s zijn en wat hun belangrijkste componenten zijn, kunnen we een heel eenvoudige versie gaan coderen. Je gebruikt
Kerasen als je niet bekend bent met deze Python-bibliotheek, lees dan eerst deze tutorial voordat je doorgaat. Deze tutorial is gebaseerd op de GAN die hier is ontwikkeld.Het eerste wat je moet doen, is de volgende pakketten installeren via
pipkerasmatplotlibtensorflowtqdmJe gebruikt
matplotlibom te plotten,tensorflowals de Keras-backendbibliotheek entqdmom een mooie voortgangsbalk te tonen voor elke epoch (iteratie).De volgende stap is het maken van een Python-script. In dit script importeer je eerst alle modules en functies die je gaat gebruiken. Een uitleg van elk volgt wanneer ze worden gebruikt.
Nu wil je enkele variabelen instellen:
Voordat je begint met het bouwen van de discriminator en generator, moet je eerst de data verzamelen en pre-processen. Je gebruikt de populaire MNIST-dataset met afbeeldingen van enkelvoudige cijfers van 0 tot en met 9.
Let op dat
mnist.load_data()onderdeel is van Keras en het je makkelijk maakt om de MNIST-dataset in je werkomgeving te importeren.Nu kun je je generator- en discriminatornetwerken maken. Je gebruikt de Adam-optimizer voor beide netwerken. Voor zowel de generator als de discriminator maak je een neuraal netwerk met drie verborgen lagen, met als activatiefunctie de Leaky ReLU. Voeg voor de discriminator ook dropoutlagen toe om de robuustheid op ongeziene beelden te verbeteren.
Het is eindelijk tijd om de generator en discriminator samen te brengen!
Voor de volledigheid kun je een functie maken die elke 20 epochs je gegenereerde afbeeldingen opslaat. Omdat dit niet de kern van deze les is, hoef je de functie niet volledig te begrijpen.
Je hebt nu het grootste deel van je netwerk gecodeerd. Het enige wat nog rest, is dit netwerk trainen en de gemaakte afbeeldingen bekijken.
Na trainen gedurende 400 epochs kun je de gegenereerde afbeeldingen bekijken. Als je naar de beelden na de eerste epoch kijkt, zie je dat er nog geen echte structuur is; na 40 epochs beginnen de cijfers vorm te krijgen en tot slot tonen de afbeeldingen na 400 epochs duidelijke cijfers, al zijn er nog een paar onherkenbaar.
Deze code kost op een CPU ongeveer 2 minuten per epoch, en dat is de belangrijkste reden dat deze code is gekozen. Je kunt experimenteren met meer epochs en met het toevoegen van meer (en andere) lagen aan zowel de generator als de discriminator. Houd er wel rekening mee dat bij complexere en diepere architecturen de looptijd ook toeneemt als je alleen een CPU gebruikt. Laat dat je echter niet weerhouden om te experimenteren!
Conclusie
Gefeliciteerd, je bent aan het einde van deze tutorial gekomen, waarin je op een intuïtieve manier de basis van Generative Adversarial Networks (GAN’s) hebt geleerd! Ook heb je je eerste model geïmplementeerd met behulp van de Keras-bibliotheek. Wil je meer weten over deep learning met Python, overweeg dan DataCamp’s cursus Deep Learning in Python.