Entraînez votre premier modèle GAN! -Parlons de Gans

Partager sur Facebook
Partager sur Twitter
Partager sur lié
Partager sur télégramme
Partager sur WhatsApp

Contenu

Cet article a été publié dans le cadre du Blogathon sur la science des données

“Generative Adversarial Networks est l'idée la plus intéressante de ces dix dernières années en Machine Learning” – Yann LeCun

introduction

compréhension mathématique et pratique de celle-ci, mais avant ça, si vous voulez jeter un oeil aux bases du GAN, vous pouvez continuer avec le lien suivant:

https://www.analyticsvidhya.com/blog/2021/04/lets-talk-about-gans/

La plupart des géants de la technologie (comme Google, Microsoft, Amazone, etc.) travaillent dur pour appliquer les GAN à une utilisation pratique, certains de ces cas d'utilisation sont:

  1. Adobe: utiliser GAN pour votre Photoshop de nouvelle génération.
  2. Google: utiliser GAN pour la génération de texte.
  3. IBM: utilisation du GAN pour l'augmentation des données (pour générer des images synthétiques pour entraîner vos modèles de classification).
  4. Chat instantané / TIC Tac: pour créer plusieurs filtres d'image (que vous avez peut-être déjà vu).
  5. Disney: utilisation du GAN pour une super résolution (amélioration de la qualité vidéo) pour vos films.

La particularité des GAN est que ces entreprises dépendent d'eux pour leur avenir., vous ne pensez pas?

Ensuite, Qu'est-ce qui vous empêche d'acquérir la connaissance de cette technologie épique? Je te répondrai, tout, vous avez juste besoin d'un avantage et cet article serait. Discutons d'abord des mathématiques derrière Générateur et Discriminateur.

Fonctionnement mathématique du discriminateur:

Le seul but du discriminateur est de classer les images réelles et fausses. Pour le classement, utilise un réseau de neurones convolutifs (CNN) traditionnel avec une fonction de coût spécifique. Le processus de formation des discriminateurs fonctionne comme suit:

88437discriminateur-2252383
La source: deeplearning.ai

Où X et Y sont respectivement des caractéristiques d'entrée et des étiquettes, la sortie est représentée par (??) et les paramètres du réseau sont représentés par (??).

Les GAN d'entraînement ont besoin d'un ensemble d'images d'entraînement et de leurs étiquettes respectives, ces images en tant que fonction d'entrée vont à CNN, avec un ensemble de paramètres initialisés. Ce CNN génère une sortie en multipliant la matrice de poids (W) avec caractéristiques d'entrée (X) et en ajoutant un biais (B) dessus et en la convertissant en une matrice non linéaire en la passant à une fonction d'activation.

Cette sortie est appelée sortie prédite., puis la perte est calculée sur la base des paramètres de poids qui sont ajustés dans le réseau pour minimiser la perte.

Fonctionnement mathématique du générateur:

Le but du générateur est de générer une fausse image à partir de la distribution donnée (ensemble d'images), il le fait avec la procédure suivante:

42922générateur-1051664
La source: deeplearning.ai

Un ensemble de vecteurs d'entrée est passé (bruit aléatoire) via le réseau de neurones du générateur, qui crée une toute nouvelle image en multipliant la matrice de poids du générateur avec le bruit d'entrée.

Cette image générée sert d'entrée au discriminateur qui est entraîné à classer les images fausses et réelles.. Ensuite, la perte est calculée pour les images générées, en fonction des paramètres mis à jour pour le générateur jusqu'à ce que nous obtenions une bonne précision.

Une fois que nous sommes satisfaits de la précision du générateur, Nous sauvegardons les poids du générateur et éliminons le discriminateur du réseau, et nous utilisons cette matrice de poids pour générer plus de nouvelles images en lui passant une matrice de bruit aléatoire différente à chaque fois.

Perte d'entropie croisée binaire pour GAN:

Pour optimiser les paramètres GAN, nous avons besoin d'une fonction de coût qui indique au réseau combien il doit s'améliorer simplement en calculant la différence entre la valeur réelle et la valeur prévue. La fonction de perte utilisée dans les GAN est appelée entropie croisée binaire et est représentée par:

76148capture d
La source: deeplearning.ai

Où m est la taille du lot, Oui(je) est la valeur réelle de la balise, h est la valeur prédite de l'étiquette, X(je) est la caractéristique d'entrée et représente le paramètre.

Divisons cette fonction de coût en sous-parties pour mieux comprendre. La formule donnée est la combinaison de deux termes où l'un est utilisé lorsqu'il est efficace lorsque l'étiquette est “0” et l'autre est important lorsque l'étiquette est “1”. Le premier terme est:

14807capture d
La source: deeplearning.ai

si la valeur réelle est “1” et la valeur prédite est “~ 0” dans ce cas, depuis le journal (~ 0) tend vers l'infini négatif ou très haut, et si la valeur prédite est également “~ 1”, puis le journal ( ~ 1) serait proche de “0” ou très moins, donc ce terme aide à calculer la perte pour les valeurs d'étiquette “1”.

53011capture d
La source: deeplearning.ai

Si la valeur réelle est “0” et la valeur prédite est “~ 1”, puis connectez-vous (1- (~ 1)) se traduirait par un infini négatif ou très élevé, et si la valeur prédite est “~ 0”, alors le terme produirait des résultats "~ 0" ou beaucoup moins de perte, ce terme est donc utilisé pour les valeurs réelles des balises "0".

N'importe lequel des termes de perte renverrait des valeurs négatives au cas où la prédiction serait fausse, la combinaison de ces termes est appelée Entropie (perte logarithmique). Mais comme c'est négatif, pour le rendre supérieur à "1", nous appliquons un signe négatif (peut être vu dans la formule principale), appliquer ce signe négatif est ce que cela fait Entropie croisée (perte logarithmique négative).

Entraînons le premier modèle GAN:

Nous allons créer un modèle GAN qui pourrait générer des chiffres manuscrits à partir de la distribution de données MNIST à l'aide du module PyTorch.

Premier, importons les modules requis:

%matplotlib en ligne
importer numpy en tant que np
torche d'importation
importer matplotlib.pyplot en tant que plt

Ensuite, nous lisions les données du sous-module fourni par PyTorch appelé ensembles de données.

# nombre de sous-processus à utiliser pour le chargement des données
nombre_travailleurs = 0
# combien d'échantillons par lot à charger
taille_bat = 64
# convertir les données en torche.FloatTensor
transform = transforms.ToTensor()
# obtenir les ensembles de données d'entraînement
train_data = datasets.MNIST(racine="Les données", train=Vrai,
                                   download=Vrai, transformer=transformer)
# préparer le chargeur de données
train_loader = torch.utils.data.DataLoader(train_données, batch_size=bat_size,
                                           num_workers=num_workers)

Visualiser les données

Puisque nous créerions notre modèle dans le framework PyTorch qui utilise des tenseurs, nous transformerions nos données en tendeurs de torche. Si vous voulez voir les données, vous pouvez continuer et utiliser l'extrait de code suivant:

# obtenir un lot d'images d'entraînement
dataiter = iter(train_loader)
images, labels = dataiter.next()
images = images.numpy()
# obtenir une image du lot
img = np.squeeze(images[0])
fig = plt.figure(taille de la figue = (3,3)) 
ax = fig.add_subplot(111)
ax.imshow(img, cmap='gris')
64254capture d
La source: Udacity.com

Discriminé

Il est maintenant temps de définir le réseau Discriminator, qui est la combinaison de plusieurs couches de CNN.

importer torch.nn en tant que nn
importer torch.nn.fonctionnel comme F
discriminateur de classe(nn.Module):
    def __init__(soi, taille_entrée, caché_dim, taille_sortie):
        super(Discriminateur, soi).__init__()
        # définir des calques linéaires masqués
        self.fc1 = nn.Linéaire(taille_entrée, caché_dim*4)
        self.fc2 = nn.Linéaire(caché_dim*4, caché_dim*2)
        self.fc3 = nn.Linéaire(caché_dim*2, caché_dim)
        # couche finale entièrement connectée
        self.fc4 = nn.Linéaire(caché_dim, taille_sortie)
        # couche d'abandon
        self.dropout = nn.Dropout(0.3)
    def vers l'avant(soi, X):
        # une image aplatie
        x = x.vue(-1, 28*28)
        # toutes les couches cachées
        x = F.leaky_relu(soi.fc1(X), 0.2) # (saisir, pente_négative=0.2)
        x = self.dropout(X)
        x = F.leaky_relu(self.fc2(X), 0.2)
        x = self.dropout(X)
        x = F.leaky_relu(soi.fc3(X), 0.2)
        x = self.dropout(X)
        # couche finale
        out = self.fc4(X)
        revenir

Le code ci-dessus suit l'architecture Python traditionnelle orientée objet. fc1, fc2, fc3, fc3 sont les couches entièrement connectées. Lorsque nous passons nos entités d'entrée, passe par toutes ces couches à partir de fc1, et enfin, nous avons une couche d'abandon qui est utilisée pour résoudre le problème de surapprentissage.

Dans le même code, vous verrez une fonction appelée forward (soi, X), cette fonction est la mise en œuvre du mécanisme réel de propagation vers l'avant où chaque couche (fc1, fc2, FC3 et FC4) est suivi d'une fonction de déclenchement (sautant_relu ) pour convertir la sortie du liner en non linéaire.

Modèle de générateur

Après cela, nous allons vérifier le segment Générateur de GAN:

Générateur de classe(nn.Module):
    def __init__(soi, taille_entrée, caché_dim, taille_sortie):
        super(Générateur, soi).__init__()
        # définir des calques linéaires masqués
        self.fc1 = nn.Linéaire(taille_entrée, caché_dim)
        self.fc2 = nn.Linéaire(caché_dim, caché_dim*2)
        self.fc3 = nn.Linéaire(caché_dim*2, caché_dim*4)
        # couche finale entièrement connectée
        self.fc4 = nn.Linéaire(caché_dim*4, taille_sortie)
        # couche d'abandon
        self.dropout = nn.Dropout(0.3)
    def vers l'avant(soi, X):
        # toutes les couches cachées
        x = F.leaky_relu(soi.fc1(X), 0.2) # (saisir, pente_négative=0.2)
        x = self.dropout(X)
        x = F.leaky_relu(self.fc2(X), 0.2)
        x = self.dropout(X)
        x = F.leaky_relu(soi.fc3(X), 0.2)
        x = self.dropout(X)
        # couche finale avec tanh appliqué
        out = F.tanh(soi.fc4(X))
        revenir

Le réseau de générateurs est également construit à partir des couches entièrement connectées, Fonctions de déclenchement et de décrochage de Leaky Relu. La seule chose qui le rend différent du discriminateur est qu'il sort en fonction du paramètre output_size (quelle est la taille de l'image à générer).

Réglage des hyperparamètres

Les hyperparamètres que nous allons utiliser pour entraîner les GAN sont:

# Hyperparamètres discriminants
# Taille de l'image d'entrée vers le discriminateur (28*28)
taille_entrée = 784
# Taille de la sortie du discriminateur (vrai ou faux)
d_output_size = 1
# Taille de la dernière couche cachée dans le discriminateur
d_hidden_size = 32
# Générateur d'hyperparamètres
# Taille du vecteur latent à donner au générateur
z_taille = 100
# Taille de la sortie du discriminateur (image générée)
g_output_size = 784
# Taille de la première couche cachée dans le générateur
g_hidden_size = 32

Créer une instance des modèles

Et finalement, l'ensemble du réseau ressemblerait à ceci:

# discriminateur et générateur d'instantanés
D = discriminateur(taille_entrée, d_hidden_size, d_output_size)
G = Générateur(z_taille, g_hidden_size, g_output_size)
# vérifiez qu'ils sont comme vous l'attendez
imprimer(ré)
imprimer( )
imprimer(g)

99398capture d
La source: Udacity.com

Calculer les pertes

Nous avons défini le Générateur et le Discriminateur maintenant il est temps de définir vos pertes afin que ces réseaux s'améliorent avec le temps. Pour le GAN nous aurions deux pertes réelles de fonction de perte et une fausse perte qui serait définie comme ceci:

# Calculer les pertes
def real_loss(D_out, lisse=Faux):
    batch_size = D_out.size(0)
    # lissage des étiquettes
    si lisse:
        # lisse, vraies étiquettes = 0.9
        labels = torch.ones(taille du lot)*0.9
    autre:
        labels = torch.ones(taille du lot) # vraies étiquettes = 1
    # perte numériquement stable
    critère = nn.BCEWithLogitsLoss()
    # calculer la perte
    perte = critère(D_out.squeeze(), Étiquettes)
    perte de retour
def fake_loss(D_out):
    batch_size = D_out.size(0)
    labels = torche.zéros(taille du lot) # fausses étiquettes = 0
    critère = nn.BCEWithLogitsLoss()
    # calculer la perte
    perte = critère(D_out.squeeze(), Étiquettes)
    perte de retour

Optimiseurs

Une fois les pertes définies, nous choisirions un optimiseur approprié pour la formation:

importer torch.optim en tant qu'optim
# Optimiseurs
lr = 0.002
# Créer des optimiseurs pour le discriminateur et le générateur
d_optimizer = optim.Adam(D.paramètres(), g / D)
g_optimizer = optim.Adam(G.paramètres(), g / D)

Formation de modèle

Puisque nous avons défini Générateur et Discriminateur, les réseaux, sa perte fonctionne comme des optimiseurs, maintenant nous utiliserions les temps et d'autres caractéristiques pour former l'ensemble du réseau.

importer des cornichons au format pkl
# hyperparamètres d'entraînement
nombre_époques = 100
# garder une trace de la perte et généré, "faux" échantillons
échantillons = []
pertes = []
print_every = 400
# Obtenez des données fixes pour l'échantillonnage. Ce sont des images qui se tiennent
# constant tout au long de la formation, et nous permettent d'inspecter les performances du modèle
sample_size=16
fixed_z = np.random.uniform(-1, 1, taille =(taille de l'échantillon, z_taille))
fixed_z = torch.from_numpy(fix_z).flotter()
# former le réseau
D.train()
G.train()
pour l'époque dans la gamme(nombre_époques):
    pour batch_i, (images_réelles, _) en énumérer(train_loader):
        batch_size = real_images.size(0)
        ## Étape de remise à l'échelle importante ## 
        images_réelles = images_réelles*2 - 1  # redimensionner les images d'entrée à partir de [0,1) à [-1, 1)
        # =============================================
        #            FORMER LE DISCRIMINATEUR
        # =============================================
        d_optimizer.zero_grad()
        # 1. Entraînez-vous avec de vraies images
        # Calculer les pertes du discriminateur sur des images réelles 
        # lisser les vraies étiquettes
        D_réel = D(images_réelles)
        d_real_loss = real_loss(D_réel, lisse=Vrai)
        # 2. Entraînez-vous avec de fausses images
        # Générer de fausses images
        # les dégradés n'ont pas à couler pendant cette étape
        avec torche.no_grad():
            z = np.aléatoire.uniforme(-1, 1, taille =(taille du lot, z_taille))
            z = torche.from_numpy(Avec).flotter()
            fake_images = G(Avec)
        # Calculer les pertes du discriminateur sur les fausses images
        D_faux = D(fake_images)
        d_fake_loss = fake_loss(D_faux)
        # additionner les pertes et effectuer le backprop
        d_loss = d_real_loss + d_fake_loss
        d_loss.backward()
        d_optimizer.step()
        # =========================================
        #            FORMER LE GÉNÉRATEUR
        # =========================================
        g_optimizer.zero_grad()
        # 1. Entraînez-vous avec de fausses images et des étiquettes retournées
        # Générer de fausses images
        z = np.aléatoire.uniforme(-1, 1, taille =(taille du lot, z_taille))
        z = torche.from_numpy(Avec).flotter()
        fake_images = G(Avec)
        # Calculer les pertes du discriminateur sur les fausses images 
        # utiliser des étiquettes inversées!
        D_faux = D(fake_images)
        g_loss = real_loss(D_faux) # utiliser la vraie perte pour retourner les étiquettes
        # effectuer un backprop
        g_loss.backward()
        g_optimizer.step()
        # Imprimer des statistiques de pertes
        si batch_i % print_every == 0:
            # discriminateur d'impression et perte de générateur
            imprimer('Époque [{:5ré}/{:5ré}] | d_perte: {:6.4F} | brillant: {:6.4F}'.format(
                    époque+1, nombre_époques, d_perte.item(), g_loss.item()))
    ## APRÈS CHAQUE ÉPOQUE##
    # ajouter la perte du discriminateur et la perte du générateur
    pertes.append((d_perte.item(), g_loss.item()))
    # générer et enregistrer un échantillon, fausses images
    G.eval() # mode eval pour générer des échantillons
    échantillons_z = G(fix_z)
    samples.append(échantillons_z)
    G.train() # retour en mode train
# Enregistrer des exemples de générateur d'entraînement
avec ouvert('train_échantillons.pkl', 'wb') comme f:
    pkl.dump(échantillons, F)

Une fois que vous exécutez l'extrait de code ci-dessus, le processus de formation commencerait ainsi:

54477capture d
La source: Udacity.com

Générer des images

Finalement, lorsque le modèle est formé, vous pouvez utiliser le générateur entraîné pour produire les nouvelles images manuscrites.

# généré aléatoirement, nouveaux vecteurs latents
sample_size=16
rand_z = np.random.uniform(-1, 1, taille =(taille de l'échantillon, z_taille))
rand_z = torche.from_numpy(rand_z).flotter()
G.eval() # mode d'évaluation
# échantillons générés
rand_images = G(rand_z)
# 0 indique le premier ensemble d'échantillons dans la liste transmise
# et nous n'avons qu'un seul lot d'échantillons, ici
voir_échantillons(0, [rand_images])

La sortie générée avec le code suivant aimerait quelque chose comme ceci:

77359capture d
La source: Udacity.com

Ensuite, maintenant que vous avez votre propre modèle GAN formé, vous pouvez utiliser ce modèle pour vous entraîner sur un autre ensemble d'images, produire de nouvelles images invisibles.

Les références:

1. Apprentissage en profondeur d'Udacity: https://www.udacity.com/

2. Apprentissage profond de l'intelligence artificielle: https://www.deeplearning.ai/

Merci d'avoir lu cet article. Si vous avez appris quelque chose de nouveau, n'hésitez pas à commenter, à la prochaine ! !!! ❤️

Les médias présentés dans cet article ne sont pas la propriété de DataPeaker et sont utilisés à la discrétion de l'auteur.

Abonnez-vous à notre newsletter

Nous ne vous enverrons pas de courrier SPAM. Nous le détestons autant que vous.