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:
- Adobe: utiliser GAN pour votre Photoshop de nouvelle génération.
- Google: utiliser GAN pour la génération de texte.
- 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).
- Chat instantané / TIC Tac: pour créer plusieurs filtres d'image (que vous avez peut-être déjà vu).
- 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:
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:
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:
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:
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”.
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')
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)
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:
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:
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.