GENERATIVE ADVERSARIAL NETWORK - CREARE NUOVE IMMAGINI
Il Generative Adversarial Network è un modello di Deep Learning che ci permette di creare delle immagini che non esistevano inserendo in input un elenco di immagini reali.
L'algoritmo è composta da 2 modelli, il primo il Generatore crea delle immagini nuove mentre il secondo il Discriminatore valuta se l'immagine creata è reale o falsa.
Essendo entrambi reti neurali apprendono con la backpropagation in ogni iterazione aggiornando i weights ed i bias.
Definiamo anche il latent space, un 100-dimensional hypersphere ovvero una sfera a 4 dimensioni.
Il generatore ad ogni iterazione definisce nel latent space nuovi punti che rappresentano una nuova immagine.
Come vedremo ad ogni iterazione prenderemo dei numeri random per generare una nuova immagine nel latent space. Sotto un semplice esempio in python in Colab.


Vediamo il codice per operare con TPU
import os
assert os.environ['COLAB_TPU_ADDR'], 'Make sure to select TPU from Edit > Notebook settings > Hardware accelerator'
!pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.7-cp36-cp36m-linux_x86_64.whl import torch
import torch_xla
import torch_xla.core.xla_model as xm
device = xm.xla_device()
device2 = xm.xla_device(n=2, devkind='TPU')
import delle librerie
import torchvision
import torch.nn as nn
import torch.nn.functional as F
import torchvision.datasets as dset
from torchvision import transforms
from torchvision.datasets import ImageFolder
from torchvision.utils import save_image
from torchvision.utils import make_grid
from torch.autograd import Variable
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
%matplotlib inline
definizione del modello G - generator
class Generator(nn.Module):
def __init__(self):
super(Generator, self).__init__()
self.main = nn.Sequential(
nn.ConvTranspose2d(
100, 512, kernel_size=4,
stride=1, padding=0),
nn.BatchNorm2d(512),
nn.ReLU(True),
nn.ConvTranspose2d(
512, 256, kernel_size=4,
stride=2, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(True),
nn.ConvTranspose2d(
256, 128, kernel_size=4,
stride=2, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(True),
nn.ConvTranspose2d(
128, 64, kernel_size=4,
stride=2, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(True),
nn.ConvTranspose2d(
64, color, kernel_size=4,
stride=2, padding=1),
nn.Tanh()
)

def (self, input):
return self.main(input)
definizione del modello D - discriminator
lass Discriminator(nn.Module):
def (self):
super(Discriminator, self).__init__()
self.main = nn.Sequential(
nn.Conv2d(
color, 64, kernel_size=4,
stride=2, padding=1),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(
64, 128, kernel_size=4,
stride=2, padding=1),
nn.BatchNorm2d(128),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(
128, 256, kernel_size=4,
stride=2, padding=1),
nn.BatchNorm2d(256),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(
256, 512, kernel_size=4,
stride=2, padding=1),
nn.BatchNorm2d(512),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(
512, 1, kernel_size=4,
stride=1, padding=0),
nn.Sigmoid()
)

def forward(self, input):
return self.main(input)
definizione parametri, loss function ed optimizer
torch.manual_seed(125)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
lr = 0.0002
batch_size = 16
num_epochs = 700

lossF = nn.BCELoss()

optimizerD = torch.optim.Adam(discriminator.parameters(), lr=lr)
optimizerG = torch.optim.Adam(generator.parameters(), lr=lr)
download dataset celebrità lfw
lfw_url = 'http://vis-www.cs.umass.edu/lfw/lfw-deepfunneled.tgz'
root = 'lfw_dataset'
if not os.path.exists(root):
os.makedirs(root)
data_file = utils.download(lfw_url)
with tarfile.open(data_file) as tar:
tar.extractall(path=root)

dest = 'lfw'

if not os.path.exists(dest):
os.makedirs(dest)

for path, _, files in os.walk(root):
for file in files:
if file :
shutil.copy(path + '/'+ file, '/content/lfw')

root = '/content/'
my_dataset = dset.ImageFolder(root= root,
transform=transforms.Compose([
transforms.Resize(64),
transforms.CenterCrop(64),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
]))

train_data = DataLoader(my_dataset, batch_size=batch_size,
shuffle=True, num_workers=4, drop_last= True)
visualizzazione immagini che verranno inserite nel modello nella fase di training
for i in range(batch_size):
ax = plt.subplot(4, 4, i + 1)
plt.imshow(data_x[i].reshape(28, 28), cmap="gray_r")
plt.xticks([])
plt.yticks([])

istanza dell'oggetto generator e discriminator ed assegnazione degli optimizer per i 2 modelli inoltre si definiscono le label 1 per immagini reali e 0 per le immagini fake.
discriminator = Discriminator().to(device=device)
generator = Generator().to(device=device)

optimizerD = torch.optim.Adam(discriminator.parameters(), lr=lr)
optimizerG = torch.optim.Adam(generator.parameters(), lr=lr)

label = torch.full((batch_size,), 1.0, dtype=torch.float, device=device)
fake_label = torch.full((batch_size,), 0.0, dtype=torch.float, device=device)

training the GAN model
for epoch in range(num_epochs):
for n_train, x_train in enumerate(train_data):
latent_space = torch.randn(batch_size, 100, 1, 1, device=device)
# DISCRIMINATOR con real e fake images (maximize log(D(x)) + log(1 - D(G(z))))
## Train real images
discriminator.zero_grad()
real_data = x_train[0].to(device)
real_dis = discriminator(real_data).view(-1)
lossD_real = lossF(real_dis, label)
lossD_real.backward()
## Train fake images
fake = generator(latent_space)
fake_dis = discriminator(fake.detach()).view(-1)
lossD_fake = lossF(fake_dis, fake_label)
lossD_fake.backward()
lossD = lossD_real + lossD_fake
optimizerD.step()


# GENERATOR con real e fake images (maximize log(D(x)) + log(1 - D(G(z)))) maximize log(D(G(z)))
generator.zero_grad()
dis_fake = discriminator(fake).view(-1)
lossG = lossF(dis_fake, label)
lossG.backward()
optimizerG.step()
history_lossG.append(lossG.item())
history_lossD.append(lossD.item())
if n_train + 1 == batch_size :
print(f"Epoch: {epoch} Loss Discriminator : {lossD: 3f}", f"Loss Generator : {lossG: 3f}")