Come le GAN creano identità artificiali di celebrità?

Come le GAN creano identità artificiali di celebrità nel mondo della moda?

Introduzione

Nell’era dell’intelligenza artificiale, sta avvenendo un fenomeno straordinario: le Reti Avversariali Generative (GAN) stanno abilmente creando identità di celebrità artificiali. Questa intrigante fusione di tecnologia e creatività ha dato origine a una nuova generazione di celebrità digitali. Unisciti a noi in un viaggio affascinante attraverso il mondo delle GAN e scopri la magia dietro la creazione di personaggi celebrità artificiali che stanno conquistando il mondo virtuale. Come rendono tutto ciò possibile le GAN? Esploriamo i segreti dietro questa arte digitale.

Fonte: Hello Future

Obiettivi di apprendimento

In questo articolo impareremo:

  • Il concetto di Reti Avversariali Generative (GAN)
  • Come addestrare il generatore e il discriminatore?
  • Il processo passo-passo dell’implementazione di un modello GAN
  • Osservare come le GAN migliorano nel tempo attraverso l’addestramento avversariale

Questo articolo è stato pubblicato come parte del Data Science Blogathon.

Reti Avversariali Generative(GAN)

Una Rete Avversariale Generativa (GAN) è un modello di apprendimento profondo creato da Goodfellow. Dal nome stesso, possiamo capire lo scopo di queste GAN. Sì! Le usiamo per la generazione di oggetti. È una rete che genera qualcosa. Usiamo le GAN per generare dati sintetici. Questi dati includono immagini, testo, audio e molto altro, e sono simili ai dati del mondo reale. La GAN contiene due reti neurali. Queste reti sono chiamate generatore e discriminatore. Durante l’addestramento, queste due reti si allenano in modo tale da competere tra loro e migliorare.

Cos’è un Generatore?

Il generatore è una rete neurale responsabile della generazione. Per produrre un risultato, ha bisogno di un input. L’input che prende il generatore è un rumore casuale. Il generatore prende questo rumore casuale e cerca di generare un output simile ai dati reali. Ogni volta che riceve un feedback dal discriminatore, si migliora e genera dati migliori la volta successiva. Ad esempio, nella generazione di immagini, il generatore produrrà immagini. Man mano che migliora durante l’addestramento, inizierà con rumore casuale e progressivamente raffinerà il suo output per renderlo sempre più realistico. La prima volta potrebbe non produrre un’immagine molto simile ai dati originali. A volte può generare immagini che non assomigliano per niente a un’immagine. Man mano che l’addestramento prosegue, vengono generati dati migliori che sono sempre più accurati.

Cos’è un Discriminatore?

Il discriminatore è una rete neurale responsabile della valutazione. Per capire facilmente, possiamo considerarlo un detective. Questo riceve sia i dati reali che i dati falsi generati dal generatore. Deve distinguere i dati falsi da quelli reali. In termini semplici, si tratta di classificare i dati reali e quelli falsi. Come il generatore, man mano che l’addestramento continua, sarà in grado di distinguere meglio. Potrebbe non essere in grado di produrre i migliori risultati al primo tentativo. Ma durante l’addestramento, migliorerà sempre di più e alla fine sarà in grado di distinguere la maggior parte dei dati falsi. Come ho detto, deve agire come un detective.

Addestramento avversariale

Entrambi il generatore e il discriminatore svolgono un addestramento chiamato addestramento avversariale. Entrambi si impegnano in un addestramento competitivo, come ho già menzionato. Sappiamo che il generatore genera dati falsi che sembrano dati reali e il discriminatore cerca di distinguere i dati falsi. Nel passaggio successivo del processo di addestramento, il generatore mira a migliorarsi e genera dati falsi per ingannare il discriminatore. Di nuovo, il discriminatore rileva i dati falsi. In questo modo, durante l’addestramento, entrambi migliorano nei rispettivi compiti. Questo processo continua finché il generatore produce dati realistici e il discriminatore non può distinguerli dai dati reali. A questo punto, la GAN ha raggiunto una sorta di equilibrio e i dati generati sono molto simili ai dati reali.

Implementazione

Iniziamo importando tutte le librerie necessarie. Questo include principalmente alcuni moduli di Torch. Utilizzeremo Matplotlib per le visualizzazioni.

from __future__ import print_function%matplotlib inlineimport argparseimport osimport randomimport torchimport torch.nn as nnimport torch.nn.parallelimport torch.backends.cudnn as cudnnimport torch.optim as optimimport torch.utils.dataimport torchvision.datasets as dsetimport torchvision.transforms as transformsimport torchvision.utils as vutilsimport numpy as npimport matplotlib.pyplot as pltimport matplotlib.animation as animationfrom IPython.display import HTML

Dataset

Per l’implementazione di questo progetto, utilizzeremo il CelebFaces Attributes (CelebA) Dataset. Questo dataset è disponibile su Kaggle. Puoi scaricarlo da qui.

Link al dataset: https://www.kaggle.com/datasets/jessicali9530/celeba-dataset

Questo dataset contiene 202.599 immagini di volti di diverse celebrità. Utilizzalo per allenare e testare modelli di rilevamento del viso, in particolare quelli capaci di riconoscere specifiche caratteristiche facciali.

Configurazione Iniziale e Setup

Ora impostiamo alcuni parametri e configurazioni prima di iniziare l’implementazione effettiva. Prima di tutto, dobbiamo fornire il percorso in cui si trova il dataset. Definisci il numero di workers. Rappresenta il numero di workers per il caricamento dei dati nella data loader. Utilizziamo i workers per caricare i dati in batch in parallelo, velocizzando il processo di caricamento dei dati durante l’allenamento. Definisci la dimensione del batch e delle immagini, il numero di canali nelle immagini di addestramento, la dimensione del vettore latente, la dimensione delle mappe di caratteristiche nel generatore, la dimensione delle mappe di caratteristiche nel discriminatore, il numero di epoche, il tasso di apprendimento, l’iperparametro beta1 per gli ottimizzatori Adam. Il numero di GPU disponibili per l’allenamento. Se non è disponibile alcuna GPU, verrà utilizzata la CPU. Il codice imposta queste configurazioni e stampa il dispositivo selezionato (CPU o GPU) e il numero di GPU utilizzate per l’allenamento. Devi controllare attentamente queste impostazioni durante l’allenamento del tuo modello GAN.

# datasetdataroot = r"C:\Users\Admin\Downloads\faces\img_align_celeba"workers = 1batch_size = 16image_size = 64nc = 3# Dimensione del vettore znz = 64# Dimensione delle mappe di caratteristiche nel generatorengf = 64# Dimensione delle mappe di caratteristiche nel discriminatorendf = 64num_epochs = 5lr = 0.0001beta1 = 0.2ngpu = 1device = torch.device("cuda:0" if (torch.cuda.is_available() and ngpu > 0) else "cpu")torch.backends.cudnn.benchmark = Trueprint(device)print(ngpu)

Data Loader

Creiamo un dataset e un dataloader di PyTorch per i nostri dati di addestramento. Crea la variabile dataset utilizzando la classe dset.ImageFolder. Abbiamo creato questa classe dataset di PyTorch per caricare i dati organizzati in cartelle, richiedendo due argomenti essenziali. Abbiamo applicato una serie di trasformazioni alle immagini nel dataset, conosciute come transform.

Crea la variabile dataloader utilizzando torch.utils.data.DataLoader. Questo è responsabile del caricamento dei dati in batch. Richiede il dataset che è stato definito e la dimensione del batch. Insieme a questi, dobbiamo indicare il numero di processi worker per il caricamento dei dati e se mescolare o meno i dati. Abbiamo definito questo numero di workers in precedenza. Per assicurarsi che il modello non veda lo stesso ordine delle immagini in ogni epoca, devi mescolare i dati prima dell’allenamento.

In seguito plottiamo alcune immagini di addestramento per comprendere come sono fatte i nostri dati di addestramento.

# Creazione del datasetdataset = dset.ImageFolder(root=dataroot,                           transform=transforms.Compose([                               transforms.Resize(image_size),                               transforms.CenterCrop(image_size),                               transforms.ToTensor(),                               transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),                           ]))# Creazione del dataloaderdataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,                                         shuffle=True, num_workers=workers)# Plot di alcune immagini di addestramentoreal_batch = next(iter(dataloader))plt.figure(figsize=(8,8))plt.axis("off")plt.title("Immagini di Addestramento")plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64],            padding=2, normalize=True).cpu(),(1,2,0)))

Generazione del Rumore

Ora creiamo un po’ di rumore. Questo rumore è un input per il generatore in una GAN. La funzione noise() genera rumore casuale. Quindi definiamola. Non richiede argomenti e crea un tensore casuale di forma (nz, ngf) riempito con valori campionati da una distribuzione uniforme tra 0 e 1. Inoltre, scala il tensore casuale in modo che i valori siano compresi tra -1 e 1. Ciò avviene moltiplicando il tensore casuale per 2 e sottraendo 1. Questo tensore di rumore può essere concatenato con altri dati (ad esempio etichette o vettori latenti) e alimentato nel generatore per generare immagini.

def rumore():  return 2*torch.rand(nz, ngf, device = device) - 1

Generatore

È ora di definire il generatore. Per fare ciò, dobbiamo definire la classe del generatore. Successivamente, specificare il metodo costruttore. Accetta l’input ngpu, che indica quanti GPU sono disponibili per l’addestramento. Il parametro ngpu viene utilizzato per gestire l’addestramento multi-GPU, ma in questo codice non viene utilizzato. All’interno del costruttore, viene definita l’architettura del generatore.

I blocchi sequenziali applicano una serie di operazioni di convoluzione, normalizzazione batch e funzioni di attivazione all’input x. Quindi definiamo l’ultimo strato convoluzionale che produce l’immagine di output. Questo è self.toImage. Utilizza le mappe delle caratteristiche dai livelli precedenti per creare l’immagine desiderata a 3 canali.

Successivamente, dobbiamo definire il metodo forward. Il passaggio in avanti del generatore viene definito da questo metodo. Prende in input x. Questo x è un vettore di rumore o vettore latente e lo fa passare attraverso i livelli definiti nel costruttore. Il risultato è l’immagine generata. Inizialmente, l’input x viene trasformato dopo il passaggio attraverso il livello latente. Quindi viene elaborato attraverso tutti i livelli convoluzionali definiti. Dopo ogni livello convoluzionale, le mappe delle caratteristiche vengono concatenate con le mappe delle caratteristiche dal livello corrispondente prima dell’upsampling. Questo aiuta il generatore a catturare funzionalità di alto livello e dettagli spaziali. L’immagine generata viene ottenuta dall’output del’ultimo strato convoluzionale e viene restituita come risultato del passaggio in avanti.

# Codice del Generatoreclass Generator(nn.Module):     def __init__(self, ngpu):         super(Generator, self).__init__()         self.ngpu = ngpu         self.fromLatent = nn.Linear(ngf, (image_size*image_size//16)*ndf)         self.dlayer1 = nn.Sequential(             nn.Conv2d(ndf, ndf, 3, padding=1),             nn.BatchNorm2d(ndf),             nn.ELU(inplace=True),             nn.Conv2d(ndf, ndf, 3, padding=1),             nn.BatchNorm2d(ndf),             nn.ELU(inplace=True)         )         self.dlayer2 = nn.Sequential(             nn.Upsample(scale_factor=2),             nn.Conv2d(2 * ndf, ndf, 3, padding=1),             nn.BatchNorm2d(ndf),             nn.ELU(inplace=True),             nn.Conv2d(ndf, ndf, 3, padding=1),             nn.BatchNorm2d(ndf),             nn.ELU(inplace=True)         )         self.dlayer3 = nn.Sequential(             nn.Upsample(scale_factor=2),             nn.Conv2d(2 * ndf, ndf, 3, padding=1),             nn.BatchNorm2d(ndf),             nn.ELU(inplace=True),             nn.Conv2d(ndf, ndf, 3, padding=1),             nn.BatchNorm2d(ndf),             nn.ELU(inplace=True)         )         self.toImage = nn.Conv2d(ndf, 3, 3, padding = 1)      def forward(self, x):         x = self.fromLatent(x)         x = x.view(x.size(0), ndf, image_size//4, image_size//4)         h0 = x         x = self.dlayer1(x)         x = torch.cat([x, h0], dim=1)         x = self.dlayer2(x)         h0 = nn.functional.interpolate(h0, scale_factor=2, mode='nearest')          x = torch.cat([x, h0], dim=1)         x = self.dlayer3(x)         x = self.toImage(x)         return x

Discriminatore

Per identificare immagini reali e false, dobbiamo costruire una rete discriminante simile al generatore che abbiamo definito in precedenza. In questa istanza sarà presente una classe discriminatore. Definisci il metodo costruttore della classe discriminatore. Accetta l’input ngpu, che indica il numero di GPU disponibili per l’addestramento, proprio come il generatore.

Il elayer1 contiene tre layer convoluzionali, e ogni layer è seguito da normalizzazione batch e dalla funzione di attivazione ELU. Il self.toLatent è un layer FC che prende l’output dei layer convoluzionali e lo mappa su un tensore. Il self.fromLatent è un altro layer FC. Prende l’output del generatore e lo mappa su un tensore.

Questi blocchi sono simili a quelli del generatore, ma vengono utilizzati per la decodifica. Ciò significa generazione dell’immagine. Il self.toImage è l’ultimo layer convoluzionale che produce un’immagine di output. Crea un’immagine a 3 canali utilizzando le mappe delle caratteristiche dei layer precedenti. Il metodo forward specifica quindi il passaggio in avanti del discriminatore. Prende in input un’immagine, che può essere reale o generata, e la fa passare attraverso i layer definiti nel costruttore. Il risultato è un output simile a un’immagine.

Implementazione del codice

class Discriminatore(nn.Module):    def __init__(self, ngpu):        super(Discriminatore, self).__init__()        self.ngpu = ngpu        self.elayer1 = nn.Sequential(            nn.Conv2d(nc, ndf, 3, padding = 1),            nn.BatchNorm2d(ndf),            nn.ELU(inplace=True),            nn.Conv2d(ndf, ndf, 3, padding = 1),            nn.BatchNorm2d(ndf),            nn.ELU(inplace=True),            nn.Conv2d(ndf, 2 * ndf, 3, padding = 1),            nn.BatchNorm2d(2 * ndf),            nn.ELU(inplace=True),        )        self.elayer2 = nn.Sequential(            nn.MaxPool2d(2),            nn.Conv2d(2 * ndf, 2 * ndf, 3, padding = 1),            nn.BatchNorm2d(2 * ndf),            nn.ELU(inplace=True),            nn.Conv2d(2 * ndf, 3 * ndf, 3, padding = 1),            nn.BatchNorm2d(3 * ndf),            nn.ELU(inplace=True)        )        self.elayer3 = nn.Sequential(            nn.MaxPool2d(2),            nn.Conv2d(3 * ndf, 3 * ndf, 3, padding = 1),            nn.BatchNorm2d(3 * ndf),            nn.ELU(inplace=True),            nn.Conv2d(3 * ndf, 3 * ndf, 3, padding = 1),            nn.BatchNorm2d(3 * ndf),            nn.ELU(inplace=True)        )        self.toLatent = nn.Linear((image_size*image_size//16)*3*ndf, ngf)        self.fromLatent = nn.Linear(ngf, (image_size*image_size//16)*ndf)        self.dlayer1 = nn.Sequential(            nn.Conv2d(ndf, ndf, 3, padding=1),            nn.BatchNorm2d(ndf),            nn.ELU(inplace=True),            nn.Conv2d(ndf, ndf, 3, padding=1),            nn.BatchNorm2d(ndf),            nn.ELU(inplace=True)        )        self.dlayer2 = nn.Sequential(            nn.Upsample(scale_factor=2),            nn.Conv2d(2*ndf, ndf, 3, padding=1),            nn.BatchNorm2d(ndf),            nn.ELU(inplace=True),            nn.Conv2d(ndf, ndf, 3, padding=1),            nn.BatchNorm2d(ndf),            nn.ELU(inplace=True)        )        self.dlayer3 = nn.Sequential(            nn.Upsample(scale_factor=2),            nn.Conv2d(2*ndf, ndf, 3, padding=1),            nn.BatchNorm2d(ndf),            nn.ELU(inplace=True),            nn.Conv2d(nsdf, ndf, 3, padding=1),            nn.BatchNorm2d(ndf),            nn.ELU(inplace=True)        )        self.toImage = nn.Conv2d(ndf, 3, 3, padding = 1)            def forward(self, x):        x = self.elayer1(x)        x = self.elayer2(x)        x = self.elayer3(x)        x = x.view(x.size(0), -1)        x = self.toLatent(x)        x = self.fromLatent(x)        x = x.view(x.size(0), ndf, image_size//4, image_size//4)        h0 = x        x = self.dlayer1(x)        x = torch.cat([x, h0], dim=1)        x = self.dlayer2(x)        h0 = torch.nn.functional.interpolate(h0, scale_factor=2, mode='nearest')        x = torch.cat([x, h0], dim=1)        x = self.dlayer3(x)        x = self.toImage(x)        return x

Ora, crea una classe chiamata Generatore e passa ngpu per costruire una rete neurale generatrice. Puoi utilizzare più GPU per aumentare l’efficienza di addestramento se le hai. La rete neurale discriminatrice viene anche creata in modo simile al generatore mediante l’istanziazione di una classe chiamata Discriminatore. Inizia la funzione di perdita al prossimo passaggio.

Successivamente, dobbiamo creare un gruppo di vettori latenti chiamati fixed_noise. Questi vettori latenti sono solitamente vettori di rumore casuali, spesso estratti da una distribuzione normale. Vengono utilizzati per generare immagini false dal generatore durante l’addestramento. E successivamente dobbiamo impostare gli ottimizzatori sia per il generatore che per il discriminatore. Utilizzeremo ottimizzatori Adam per entrambi. Adam è un algoritmo di ottimizzazione popolare.

# Crea il generatorenetG = Generatore(ngpu).to(device)# Per gestire più GPUif (device.type == 'cuda') and (ngpu > 1):    netG = nn.DataParallel(netG, list(range(ngpu)))# Crea il discriminatorenD = Discriminatore(ngpu).to(device)# Per gestire più GPUif (device.type == 'cuda') and (ngpu > 1):    netD = nn.DataParallel(netD, list(range(ngpu)))    # Inizializza la funzione di perditacriterio = nn.L1Loss()fixed_noise = noise()# Imposta gli ottimizzatori Adam sia per il generatore che per il discriminatoreoptimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))    

Addestramento

È ora di addestrare il nostro modello. Prima di ciò, dobbiamo creare una classe chiamata Timer che ci aiuterà a calcolare il tempo di addestramento ad ogni passaggio. Crea alcune liste vuote necessarie e definisci alcuni parametri necessari per memorizzare i dati durante l’addestramento. Itera su un numero prefissato di epoche e lotti di dati dal caricatore di dati nel ciclo di addestramento, che è stato impostato. Il discriminatore viene addestrato con i dati falsi generati dal generatore. Durante l’addestramento sia il generatore che il discriminatore vengono aggiornati. Nel frattempo, durante l’addestramento ad ogni passaggio, tutte le statistiche verranno stampate. Raccoglie perdite e immagini generate per analisi e visualizzazione durante e dopo l’addestramento.

import timeclass Timer():    def __init__(self):        self.startTime = time.time()        self.lastTime = time.time()      def timeElapsed(self):        auxTime = self.lastTime        self.lastTime = time.time()        return self.lastTime - auxTime    def timeSinceStart(self):        self.lastTime = time.time()        return self.lastTime - self.startTime        # Ciclo di addestramentok = 0gamma = 0.4lambda_k = 0.005img_list = []G_losses = []D_losses = []iters = 0print("Ciclo di addestramento iniziato...")timer = Timer()for epoch in range(num_epochs):    # Per ogni lotto nel caricatore di dati    for i, data in enumerate(dataloader, 0):             netD.zero_grad()        # Formatta il lotto        real_cpu = data[0].to(device)        b_size = real_cpu.size(0)        # Passaggio in avanti del lotto reale tramite D        output = netD(real_cpu).view(-1)        # Calcola la perdita su tutto il lotto reale        errD_real = criterion(output, real_cpu.view(-1))        # Calcola i gradienti per D nel passaggio indietro        D_x = output.mean().item()          fake = netG(noise())        # Classifica tutto il lotto falso con D        output = netD(fake.detach()).view(-1)        # Calcola la perdita di D su tutto il lotto falso        errD_fake = criterion(output, fake.view(-1))        # Calcola i gradienti per questo lotto        D_loss = errD_real - k * errD_fake        D_loss.backward()        D_G_z1 = output.mean().item()        # Aggiungi i gradienti dai lotti reali e falsi        optimizerD.step()             netG.zero_grad()        fake = netG(noise())        output = netD(fake).view(-1)        # Calcola la perdita di G basata su questa uscita        errG = criterion(output, fake.view(-1))        # Calcola i gradienti per G        errG.backward()        D_G_z2 = output.mean().item()        # Aggiorna G        optimizerG.step()        delta = (gamma*errD_real - errG).data        k = max(min(k + lambda_k*delta, 1.0), 0.0)                # Statistiche di addestramento di output        if i % 50 == 0:            print(            '[%.4f] [%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f'                  % (timer.timeElapsed(), epoch, num_epochs, i, len(dataloader),                     D_loss.item(), errG.item(), D_x, D_G_z1, D_G_z2))                # Salva le perdite per il tracciamento successivo        G_losses.append(errG.item())        D_losses.append(D_loss.item())                if (iters % 1000 == 0) or ((epoch == num_epochs-1) and (i == len(dataloader)-1)):            with torch.no_grad():                fake = netG(fixed_noise).detach().cpu()            img_list.append(vutils.make_grid(fake, padding=2, normalize=True))                    iters += 1   

Visualizzazioni

Ora generiamo un grafico per visualizzare la perdita del generatore e del discriminatore durante l’addestramento di una GAN.

plt.figure(figsize=(10,5))plt.title("Perdita del Generatore e del Discriminatore durante l'Addestramento")plt.plot(G_losses,label="G")plt.plot(D_losses,label="D")plt.xlabel("iterazioni")plt.ylabel("Perdita")plt.legend()plt.show() 

Allo stesso modo, generiamo un grafico per il confronto tra immagini reali e immagini false generate da una GAN. Per fare ciò, dobbiamo acquisire un batch di immagini reali dal dataloader. Queste immagini reali vengono utilizzate per il confronto con le immagini generate dalla GAN. Successivamente visualizziamo le immagini reali e le immagini false generate dalla GAN. Ciò permette di confrontare visivamente la qualità delle immagini generate con i dati reali.

# Acquisisci un batch di immagini reali dal dataloaderreal_batch = next(iter(dataloader))# Visualizza le immagini realiplt.figure(figsize=(15,15))plt.subplot(1,2,1)plt.axis("off")plt.title("Immagini Reali")plt.imshow(np.transpose(vutils.make_grid(real_batch[0].to(device)[:64],            padding=5, normalize=True).cpu(),(1,2,0)))# Visualizza le immagini fittizie dall'ultima epocaplt.subplot(1,2,2)plt.axis("off")plt.title("Immagini Fittizie")plt.imshow(np.transpose(img_list[-1],(1,2,0)))plt.savefig('fake_image.png')plt.show()

Conclusione

In questo articolo abbiamo utilizzato Generative Adversarial Networks (GAN) sul dataset CelebFaces Attributes (CelebA) e generato alcuni volti di celebrità finti. Le Generative Adversarial Networks (GAN) sono una notevole innovazione tecnologica. Possono creare dati falsi che sembrano molto reali. Ciò ha diverse applicazioni ed è estremamente utile, specialmente quando sono necessari molti dati per i progetti. La tecnologia sta sviluppandosi rapidamente ed è sempre più utilizzata negli ultimi anni. Questa tecnologia è un interessante sviluppo nel campo dell’intelligenza artificiale poiché ha un futuro promettente per molte diverse applicazioni.

Punti chiave

  • Le Generative Adversarial Networks (GAN) sono una rivoluzionaria tecnologia dell’IA in grado di generare dati che assomigliano molto a dati reali.
  • È composta da due reti neurali. Una è il generatore e l’altra è il discriminatore. Queste due reti sono coinvolte in un addestramento avversario.
  • Le GAN hanno applicazioni in diversi campi, tra cui generazione di immagini, sovracampionamento, trasferimento di stile e aumento dei dati.
  • Esistono molti tipi di GAN, ognuno con i propri vantaggi, svantaggi e applicazioni.
  • Le GAN possono sollevare preoccupazioni etiche legate ai deepfake, alla generazione di contenuti falsi e alle violazioni della privacy.

Domande frequenti

Le immagini mostrate in questo articolo non sono di proprietà di Analytics Vidhya e sono utilizzate a discrezione dell’autore.