Accéder au contenu principal

Démystifier les réseaux antagonistes génératifs (GAN)

Découvrez ce que sont les réseaux antagonistes génératifs sans entrer dans les détails mathématiques et codez un simple GAN capable de générer des chiffres !
Actualisé 19 sept. 2026  · 9 min lire

Explorer avec l’IA

ChatGPTClaudePerplexity

Dans ce tutoriel, vous allez comprendre ce que sont les réseaux antagonistes génératifs (GAN) sans plonger dans les détails mathématiques. Ensuite, vous apprendrez à coder un GAN simple capable de créer des chiffres !

Analogie

Le moyen le plus simple de comprendre les GAN consiste à utiliser une analogie parlante :

Imaginez une boutique qui achète certains types de vins à des clients pour les revendre ensuite.

analogie

Cependant, des clients malintentionnés essaient de vendre de faux vins pour gagner de l'argent. Dans ce cas, la propriétaire de la boutique doit être capable de distinguer les vins contrefaits des vins authentiques.

analogie

Au départ, le faussaire commettra sans doute beaucoup d'erreurs en tentant d'écouler du faux vin et il sera facile pour la commerçante d'identifier qu'il n'est pas authentique. Suite à ces échecs, le faussaire va multiplier les techniques pour imiter les vins originaux et certaines finiront par fonctionner. Maintenant qu'il sait que certaines méthodes passent les contrôles de la boutique, il peut perfectionner ses contrefaçons à partir de ces techniques.

Dans le même temps, la propriétaire de la boutique recevra probablement des retours d'autres commerçants ou d'experts en œnologie lui signalant que certains vins en rayon ne sont pas originaux. Elle devra donc améliorer sa façon de déterminer si un vin est faux ou authentique. L'objectif du faussaire est de créer des vins impossibles à distinguer des vrais, et celui de la commerçante est d'identifier avec précision si un vin est authentique ou non.

Ce jeu du chat et de la souris est au cœur du fonctionnement des GAN.

Composants d'un réseau antagoniste génératif

En reprenant l'exemple ci-dessus, on peut déduire l'architecture d'un GAN.

gan

Un GAN comporte deux composants majeurs : le générateur et le discriminateur. La commerçante, dans l'exemple, correspond au réseau discriminateur. Il s'agit généralement d'un réseau de neurones convolutionnel (les GAN étant surtout utilisés pour des tâches d'image) qui attribue une probabilité au fait qu'une image soit réelle.

Le faussaire correspond au réseau générateur, lui aussi généralement un réseau de neurones convolutionnel (avec des couches de déconvolution). Ce réseau prend en entrée un vecteur de bruit et produit une image. Lors de l'entraînement, le générateur apprend quelles zones de l'image améliorer ou modifier pour que le discriminateur ait plus de mal à différencier ses images synthétiques des images réelles.

Le générateur produit des images de plus en plus proches des images réelles, tandis que le discriminateur s'efforce d'identifier les différences entre vraies et fausses images. Le but ultime est d'obtenir un générateur capable de produire des images indiscernables des vraies.

Un réseau antagoniste génératif simple avec Keras

Maintenant que vous comprenez ce qu'est un GAN et ses principaux composants, passons au code d'un modèle très simple. Vous utiliserez Keras ; si vous ne connaissez pas encore cette bibliothèque Python, lisez d'abord ce tutoriel. Ce tutoriel s'inspire du GAN développé ici.

Commencez par installer les paquets suivants via pip :

  • keras
  • matplotlib
  • tensorflow
  • tqdm

Vous utiliserez matplotlib pour la visualisation, tensorflow comme moteur de Keras et tqdm pour afficher une barre de progression élégante à chaque époque (itération).

Ensuite, créez un script Python. Dans ce script, commencez par importer tous les modules et fonctions nécessaires. Chaque élément sera expliqué au moment de son utilisation.

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

Définissez maintenant quelques variables :

# 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 = 100

Avant de construire le discriminateur et le générateur, il faut d'abord rassembler et prétraiter les données. Vous utiliserez le célèbre jeu de données MNIST, qui contient des images de chiffres isolés allant de 0 à 9.

jeu de données mnist

def load_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)

Remarque : mnist.load_data() fait partie de Keras et permet d'importer très facilement le jeu de données MNIST dans votre environnement.

Vous pouvez maintenant créer les réseaux générateur et discriminateur. Vous utiliserez l'optimiseur Adam pour les deux. Pour le générateur comme pour le discriminateur, vous allez construire un réseau de neurones à trois couches cachées avec une fonction d'activation Leaky ReLU. Ajoutez également des couches de dropout côté discriminateur pour renforcer sa robustesse sur des images jamais vues.

# You will use the Adam optimizer
def get_optimizer():
    return Adam(lr=0.0002, beta_1=0.5)

def get_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

def get_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

Il est temps d'assembler le générateur et le discriminateur !

def get_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

Pour être complet, vous pouvez créer une fonction qui enregistre les images générées toutes les 20 époques. Ce n'est pas le cœur de la leçon, vous n'avez donc pas besoin de comprendre chaque détail.

# Create a wall of generated MNIST images
def plot_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 in range(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)

Vous avez maintenant codé l'essentiel du réseau. Il ne reste plus qu'à l'entraîner et à observer les images générées.

def train(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 in xrange(1, epochs+1):
        print '-'*15, 'Epoch %d' % e, '-'*15
        for _ 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 == 1 or e % 20 == 0:
            plot_generated_images(e, generator)

if __name__ == '__main__':
    train(400, 128)

Après 400 époques d'entraînement, vous pouvez visualiser les images générées. Celles produites après la première époque n'ont pas de structure perceptible ; après 40 époques, les chiffres commencent à apparaître ; enfin, après 400 époques, on observe des chiffres bien formés, même si quelques-uns restent méconnaissables.

Résultats après 1 époque Résultats après 40 époques Résultats après 400 époques
!images1 images2 images3

Ce code prend environ 2 minutes par époque sur un CPU, ce qui est la principale raison de ce choix. Vous pouvez expérimenter avec davantage d'époques et en ajoutant plus (et d'autres types) de couches au générateur et au discriminateur. Attention toutefois : avec des architectures plus profondes et complexes, le temps d'exécution augmente si vous n'utilisez qu'un CPU. Mais ne vous laissez pas arrêter pour autant !

Conclusion

Félicitations, vous êtes arrivé au bout de ce tutoriel où vous avez découvert les bases des réseaux antagonistes génératifs (GAN) de manière intuitive ! Vous avez aussi implémenté votre premier modèle avec la bibliothèque Keras. Pour aller plus loin sur le deep learning avec Python, découvrez le cours Deep Learning in Python de DataCamp.

Sujets
Apprentissage profond
Intelligence artificielle
Apprentissage automatique

Approfondissez Python et le deep learning

Cours

Introduction au Deep Learning en Python

4 h
264.6K
Initiez-vous aux réseaux neuronaux et créez vos premiers modèles avec Keras 2.0 en Python.
Afficher les détailsRight Arrow
Commencer Le Cours
Voir plusRight Arrow