L’panneau événement des noms TensorFlow 2.0 supplice anxieuse dans la prédicat prison quantité un de la neuve subdivision majeure. Qu’est-ce que ceci signifie pile les utilisateurs de R ? Même attesté pendant à nous froid étude sur la version irréfléchi neuronale, vous-même pouvez déjà user l’supplice éveil à brutalement de R, en union comme les modèles personnalisés Keras et l’API des ensembles de occasion. C’est bon de te estimer peut l’user – pourtant aussi devriez-vous ? Et pendant lequel cas ?

Chez cet étude et pendant nombreux éditoriaux à naître, nous-même voulons brandir combien une supplice éveil peut amortir le poussée de modèles à satiété comme prévenant. Le sellette de uniformisation dépendra de la tâche – et approprié à quel point comme prévenant, vous-même trouverez la neuve procédé peut autant suivre de votre vérification d’emploi de l’API fonctionnelle pile modéliser des procès-verbaux comme complexes. Même si vous-même pensez que les GAN, les architectures d’encodeur-décodeur ou le report de parole neuronal ne posaient annulé question vis-à-vis l’rattachement de l’supplice regardant, vous-même pourriez prédire que l’potentialité correspond davantage à la fabriquer lequel nous-même, les humains, imaginons psychologiquement les problèmes.

Vers cet étude, nous-même portons le acte d’un notebook froid de Google Colaboratory implémentant l’châssis DCGAN.(Radford, Metz et Chintala 2015)
Aucune familiarité préliminaire des GAN n’est requise – nous-même garderons cet étude prospect (pas de mathématiques) et nous-même nous-même concentrerons sur la fabriquer d’commencer votre loyal, en cartographiant un notion cohérent et agité en un rassemblement incroyablement abject de échelons de acte.

Même pendant le post sur la version irréfléchi comme application, nous-même endettons d’dehors protéger nombreux prérequis. Au aventure, pas appétit de retranscrire les spicilège de acte – vous-même trouverez le acte expérimenté pendant impatient_dcgan.R).

Situation préalables

Le acte de cet étude dépend des dernières versions CRAN de différentes des packages TensorFlow R. Toi-même pouvez poser ces packages dans suit :

install.packages(c("tensorflow", "keras", "tfdatasets"))

Toi-même devez autant vous-même alléguer que vous-même utilisez la toute dernière subdivision de TensorFlow (v1.10), que vous-même pouvez poser dans suit :

library(tensorflow)
install_tensorflow()

Il existe des exigences supplémentaires pile l’emploi de l’supplice bâclée de TensorFlow. Globalité d’dehors, nous-même endettons crier tfe_enable_eager_execution() approprié initialement du développement. Deuzio, nous-même endettons user l’réalisation de Keras incluse pendant TensorFlow, de préférence que l’réalisation de armature de Keras.

Certains utiliserons autant le progiciel tfdatasets pile à nous oléoduc d’approche. On se retrouve lors comme le prélude talonnant pile fixer les choses en installé :

C’est ça. Commençons.

Qu’est-ce qu’un GAN ?

GAN signifie Chaîne objecteur génératif(Goodfellow et al. 2014). Il s’agit d’une tournure de couple agents, le Dynamo et le discriminateurqui agissent les uns afin les changées (aussi, contraire). C’est génératif étant donné que l’loyal est de produire une excursion (par antinomie à, disons, une rangement ou une rétrogression).

Chez l’vérification miséricordieux, la réaction – précise ou lointaine – bajoue un vertu orthogonal. Disons que nous-même voulions mentir un annonce de institution (autant que ceux existent principalement). En croyant que nous-même puissions nous-même en extraire comme des essais vide, nous-même deviendrions de davantage en davantage en partie de plagiat comme le règne. En optimisant à nous façon, nous-même finirions riches. Ce notion d’rationalisation à brutalement de la réaction s’incarne pendant le primitif des couple agents, le Dynamo. Il reçoit ses mémoires du discriminateur, à l’contre-poil : s’il peut exagérer le discriminateur en lui choriste échafauder que le annonce existait avéré, somme va diligent ; si le discriminateur appuyé le imité, il doit commettre les choses autrement. Vers un entrecroisement de neurones, ceci signifie qu’il doit fixer à vasistas ses efficacité.

Comme le discriminateur sait-il ce qui est avéré et ce qui est imité ? Lui moyennant doit créature élevé, sur les vrais trajets (ou quelle que O.K. le modèle d’objets concernés) et les imité produits par le créateur. De la sorte, la tournure pleine est constituée de couple agents en match, l’un s’efforçant de produire de imité objets réalistes et l’disparate, de renoncer la tour. Le but de l’exercice est de commettre muter et de s’réformer à la jour, ce qui amène l’disparate à s’réformer autant.

Chez ce ordre établi, il n’y a pas de maximum loyal pile la ardeur de saignement : nous-même voulons que les couple composants apprennent et s’améliorent “en mesure”, au canton que l’un l’emporte sur l’disparate. Ceci rend l’rationalisation anguleuse. Par logique, pendant la prospect, absoudre un GAN peut correspondre comme à de l’gnose qu’à de la pouvoir, et il est habituellement raisonnement de s’augmenter sur des ouvrages et des “astuces” rapportées par d’changées.

Chez cet essence, somme dans pendant le bloc Google que nous-même portons, l’loyal est de produire des chiffres MNIST. Diligent que ceci ne semble pas créature la tâche la comme excitante que l’on puisse créer, ceci nous-même permet de nous-même approprier sur la involontaire et nous-même permet de asservir les besoins en intention et en énoncé (par comparaison) bas.

Chargeons les occasion (chorus d’exercice boîtier strictement) ensuite regardons le primitif dessinateur de à nous détresse, le créateur.

Occasion d’exercice

mnist <- dataset_mnist()
c(train_images, train_labels) %<-% mnist$cantine

train_images <- train_images %>% 
  k_expand_dims() %>%
  k_cast(dtype = "float32")

# normalize images to (-1, 1) bicause the generator uses tanh accélération
train_images <- (train_images - 127.5) / 127.5

À nous chorus d’exercice expérimenté sera vulgarisé une jour par siècle :

buffer_size <- 60000
batch_size <- 256
batches_per_epoch <- (buffer_size / batch_size) %>% reprise()

train_dataset <- tensor_slices_dataset(train_images) %>%
  dataset_shuffle(buffer_size) %>%
  dataset_batch(batch_size)

Cette approche sera ambassadrice au discriminateur strictement.

Dynamo

Le créateur et le discriminateur sont des modèles personnalisés de Keras. Inversement aux parturition personnalisées, les modèles personnalisés vous-même permettent de charpenter des modèles en autant qu’unités indépendantes, comme une raisonnement de aqueduc vis-à-vis personnalisée, une backprop et une rationalisation. La ardeur de création de modèle définit les parturition du modèle (self) veut créature affecté et renvoie la ardeur qui implémente la aqueduc vis-à-vis.

Même nous-même le verrons prochainement, le créateur reçoit des vecteurs de environnement problématique en approche. Ce vecteur est modifié en 3d (colline, grandeur, canaux) ensuite, alternativement suréchantillonné à la ébranchage de excursion requise de (28,28,3).

generator <-
  function(name = NULL) {
    keras_model_custom(name = name, function(self) {
      
      self$fc1 <- layer_dense(units = 7 * 7 * 64, use_bias = FALSE)
      self$batchnorm1 <- layer_batch_normalization()
      self$leaky_relu1 <- layer_activation_leaky_relu()
      self$conv1 <-
        layer_conv_2d_transpose(
          filters = 64,
          kernel_size = c(5, 5),
          strides = c(1, 1),
          padding = "same",
          use_bias = FALSE
        )
      self$batchnorm2 <- layer_batch_normalization()
      self$leaky_relu2 <- layer_activation_leaky_relu()
      self$conv2 <-
        layer_conv_2d_transpose(
          filters = 32,
          kernel_size = c(5, 5),
          strides = c(2, 2),
          padding = "same",
          use_bias = FALSE
        )
      self$batchnorm3 <- layer_batch_normalization()
      self$leaky_relu3 <- layer_activation_leaky_relu()
      self$conv3 <-
        layer_conv_2d_transpose(
          filters = 1,
          kernel_size = c(5, 5),
          strides = c(2, 2),
          padding = "same",
          use_bias = FALSE,
          accélération = "tanh"
        )
      
      function(inputs, mask = NULL, jogging = TRUE) {
        self$fc1(inputs) %>%
          self$batchnorm1(jogging = jogging) %>%
          self$leaky_relu1() %>%
          k_reshape(shape = c(-1, 7, 7, 64)) %>%
          self$conv1() %>%
          self$batchnorm2(jogging = jogging) %>%
          self$leaky_relu2() %>%
          self$conv2() %>%
          self$batchnorm3(jogging = jogging) %>%
          self$leaky_relu3() %>%
          self$conv3()
      }
    })
  }

Discriminateur

Le discriminateur est approprié un entrecroisement convolutif fini intelligible survenant un classement. Ici, l’emploi de “classement” au canton de “divination” est explicite : si vous-même regardez la dernière protection, elle-même est totalement connectée, de ébranchage 1 pourtant sinon l’accélération sigmoïde orthodoxe. C’est étant donné que haineusement à Keras loss_binary_crossentropyla ardeur de saignement que nous-même utiliserons ici – tf$losses$sigmoid_cross_entropy – fonctionne comme les logits bruts, pas les sorties du sigmoïde.

discriminator <-
  function(name = NULL) {
    keras_model_custom(name = name, function(self) {
      
      self$conv1 <- layer_conv_2d(
        filters = 64,
        kernel_size = c(5, 5),
        strides = c(2, 2),
        padding = "same"
      )
      self$leaky_relu1 <- layer_activation_leaky_relu()
      self$dropout <- layer_dropout(carence = 0.3)
      self$conv2 <-
        layer_conv_2d(
          filters = 128,
          kernel_size = c(5, 5),
          strides = c(2, 2),
          padding = "same"
        )
      self$leaky_relu2 <- layer_activation_leaky_relu()
      self$flatten <- layer_flatten()
      self$fc1 <- layer_dense(units = 1)
      
      function(inputs, mask = NULL, jogging = TRUE) {
        inputs %>% self$conv1() %>%
          self$leaky_relu1() %>%
          self$dropout(jogging = jogging) %>%
          self$conv2() %>%
          self$leaky_relu2() %>%
          self$flatten() %>%
          self$fc1()
      }
    })
  }

Déguisement en sortie

Devant de aigrit préluder la instruction, nous-même endettons enfanter les composants indécis d’une tournure d’vérification en dimension : le modèle (ou les modèles, pendant ce cas), la ou les impératifs de saignement et le ou les optimiseurs.

La début de modèle est approprié un interpellation de ardeur, comme un abject comme en comme :

generator <- generator()
discriminator <- discriminator()

# https://www.tensorflow.org/api_docs/anaconda/tf/contrib/eager/defun
generator$call = tf$contrib$eager$defun(generator$call)
discriminator$call = tf$contrib$eager$defun(discriminator$call)

empêcher compile une ardeur R (une jour par union différente de formes d’arguments et de droits d’objets non tensoriels)) pendant un arborescence TensorFlow, et est utilisée pile activer les gravelle. Ceci s’accompagne d’défroque secondaires et fortuitement d’un démarche brusque – veuillez sonder la meuble pile comme de comptes. Ici, nous-même vivions particulièrement dilettante de culture quels poussée nous-même pourrions comprendre donc de l’emploi de R – pendant à nous essence, ceci a expérimenté une poussée de 130 %.

Passons aux pertes. La saignement de discriminateur se compose de couple parties : identifie-t-il parfaitement les images réelles dans réelles et repère-t-il parfaitement les images fausses dans fausses. Ici real_output et generated_output contiennent les logits renvoyés par le discriminateur – c’est-à-dire son appréciation sur le aventure que les images respectives sont fausses ou réelles.

discriminator_loss <- function(real_output, generated_output) {
  real_loss <- tf$losses$sigmoid_cross_entropy(
    multi_class_labels = k_ones_like(real_output),
    logits = real_output)
  generated_loss <- tf$losses$sigmoid_cross_entropy(
    multi_class_labels = k_zeros_like(generated_output),
    logits = generated_output)
  real_loss + generated_loss
}

La saignement du créateur dépend de la fabriquer lequel le discriminateur a jugé ses créations : il espère qu’elles-mêmes seront toutes considérées dans réelles.

generator_loss <- function(generated_output) {
  tf$losses$sigmoid_cross_entropy(
    tf$ones_like(generated_output),
    generated_output)
}

Céans, nous-même endettons principalement dire des optimiseurs, un pile tout modèle.

discriminator_optimizer <- tf$cantine$AdamOptimizer(1e-4)
generator_optimizer <- tf$cantine$AdamOptimizer(1e-4)

Frisé d’exercice

Il existe couple modèles, couple impératifs de saignement et couple optimiseurs, pourtant il n’y a qu’une distincte défaut d’vérification, car les couple modèles dépendent l’un de l’disparate. La défaut de instruction sera sur des images MNIST diffusées par lots, pourtant nous-même avons infiniment appétit d’une approche pendant le créateur – un vecteur problématique de ébranchage 100, pendant ce cas.

Prenons la défaut de instruction règne par règne. Il y ambiance une défaut extérieur et une défaut civile, une sur les époques et une sur les lots. Initialement de tout siècle, nous-même créons un nouveau itérateur sur l’chorus de occasion :

for (epoch in seq_len(num_epochs)) {
  start <- Sys.time()
  total_loss_gen <- 0
  total_loss_disc <- 0
  iter <- make_iterator_one_shot(train_dataset)

Céans, pile tout lot que nous-même obtenons de l’itérateur, nous-même appelons le créateur et lui faisons produire des images à brutalement de environnement problématique. Ultérieurement, nous-même appelons le dicriminateur sur les images réelles aussi que sur les fausses images qui viennent d’créature générées. Vers le discriminateur, ses sorties relatives sont résolument introduites pendant la ardeur de saignement. Vers le créateur, sa saignement dépendra de la fabriquer lequel le discriminateur a jugé ses créations :

until_out_of_range({
  batch <- iterator_get_next(iter)
  discussion <- k_random_normal(c(batch_size, noise_dim))
  with(tf$GradientTape() %as% gen_tape, { with(tf$GradientTape() %as% disc_tape, {
    generated_images <- generator(discussion)
    disc_real_output <- discriminator(batch, jogging = TRUE)
    disc_generated_output <-
       discriminator(generated_images, jogging = TRUE)
    gen_loss <- generator_loss(disc_generated_output)
    disc_loss <- discriminator_loss(disc_real_output, disc_generated_output)
  }) })

Notez que entiers les balises de modèle se produisent à l’logement tf$GradientTape contextes. C’est aussi que les passes environs l’vis-à-vis peuvent créature enregistrées et «lues» pile vulgariser les pertes à flanc le entrecroisement.

Réussir les gradients des pertes aux variables des modèles respectifs (coup$gradient) et demandez aux optimiseurs de les administrer aux pondérations des modèles (optimizer$apply_gradients):

gradients_of_generator <-
  gen_tape$gradient(gen_loss, generator$variables)
gradients_of_discriminator <-
  disc_tape$gradient(disc_loss, discriminator$variables)
      
generator_optimizer$apply_gradients(purrr::transpose(
  list(gradients_of_generator, generator$variables)
))
discriminator_optimizer$apply_gradients(purrr::transpose(
  list(gradients_of_discriminator, discriminator$variables)
))
      
total_loss_gen <- total_loss_gen + gen_loss
total_loss_disc <- total_loss_disc + disc_loss

Ceci met fin à la défaut sur les lots. Terminez la défaut sur les époques en affichant les pertes de onde et en sauvegardant quelques-unes des illustrations du créateur :

cat("Time for epoch ", epoch, ": ", Sys.time() - start, "n")
cat("Generator loss: ", total_loss_gen$numpy() / batches_per_epoch, "n")
cat("Discriminator loss: ", total_loss_disc$numpy() / batches_per_epoch, "nn")
if (epoch %% 10 == 0)
  generate_and_save_images(generator,
                           epoch,
                           random_vector_for_generation)

Voilà à bleu la défaut de instruction, conduite pendant son chorus – même en inclusif les échelons de gain sur les rétablissement, elle-même est splendidement concise et permet une acuité éveil de ce qui se aqueduc :

cantine <- function(dataset, epochs, noise_dim) {
  for (epoch in seq_len(num_epochs)) {
    start <- Sys.time()
    total_loss_gen <- 0
    total_loss_disc <- 0
    iter <- make_iterator_one_shot(train_dataset)
    
    until_out_of_range({
      batch <- iterator_get_next(iter)
      discussion <- k_random_normal(c(batch_size, noise_dim))
      with(tf$GradientTape() %as% gen_tape, { with(tf$GradientTape() %as% disc_tape, {
        generated_images <- generator(discussion)
        disc_real_output <- discriminator(batch, jogging = TRUE)
        disc_generated_output <-
          discriminator(generated_images, jogging = TRUE)
        gen_loss <- generator_loss(disc_generated_output)
        disc_loss <-
          discriminator_loss(disc_real_output, disc_generated_output)
      }) })
      
      gradients_of_generator <-
        gen_tape$gradient(gen_loss, generator$variables)
      gradients_of_discriminator <-
        disc_tape$gradient(disc_loss, discriminator$variables)
      
      generator_optimizer$apply_gradients(purrr::transpose(
        list(gradients_of_generator, generator$variables)
      ))
      discriminator_optimizer$apply_gradients(purrr::transpose(
        list(gradients_of_discriminator, discriminator$variables)
      ))
      
      total_loss_gen <- total_loss_gen + gen_loss
      total_loss_disc <- total_loss_disc + disc_loss
      
    })
    
    cat("Time for epoch ", epoch, ": ", Sys.time() - start, "n")
    cat("Generator loss: ", total_loss_gen$numpy() / batches_per_epoch, "n")
    cat("Discriminator loss: ", total_loss_disc$numpy() / batches_per_epoch, "nn")
    if (epoch %% 10 == 0)
      generate_and_save_images(generator,
                               epoch,
                               random_vector_for_generation)
    
  }
}

Voilà la ardeur de courbette des images générées…

generate_and_save_images <- function(model, epoch, test_input) {
  predictions <- model(test_input, jogging = FALSE)
  png(paste0("images_epoch_", epoch, ".png"))
  par(mfcol = c(5, 5))
  par(mar = c(0.5, 0.5, 0.5, 0.5),
      xaxs = 'i',
      yaxs = 'i')
  for (i in 1:25) {
    img <- predictions(i, , , 1)
    img <- t(apply(img, 2, rev))
    figure(
      1:28,
      1:28,
      img * 127.5 + 127.5,
      col = gray((0:255) / 255),
      xaxt = 'n',
      yaxt = 'n'
    )
  }
  dev.off()
}

… et nous-même sommeils prêts à brutalement !

num_epochs <- 150
cantine(train_dataset, num_epochs, noise_dim)

Résultats

Voilà nombreux images générées puis un exercice de 150 époques :

Même on dit, vos résultats varieront follement évidemment !

Point

Diligent que le ajustement des GAN restera évidemment un vantardise, nous-même à condition capital pu brandir que le mappage des concepts au acte n’est pas anguleuse donc de l’emploi d’une supplice bâclée. Si vous-même avez déjà joué comme les GAN, vous-même avez probablement évident que vous-même deviez commettre follement application pile façonner les pertes de la plaisante prétentieux, coaguler les efficacité du discriminateur si boîtier, etc. Ce appétit disparaît comme une supplice fougueuse. Chez les prochains éditoriaux, nous-même montrerons d’changées exemples où son emploi facilite le poussée de modèles.

Goodfellow, Ian J., Pantalon Pouget-Abadie, Mehdi Mirza, Bang Xu, David Warde-Farley, Sherjil Ozair, Aaron C. Courville et Yoshua Bengio. 2014. « Réseaux antagonistes génératifs ». Chez Advances in Neural Examen Processing Systems 27 : Rencontres annale sur les systèmes de salaire de l’renseignement neuronale 2014, 8-13 décembre 2014, Montréal, Québec, Canada, 2672–80. http://papers.nips.cc/paper/5423-generative-adversarial-nets.

Radford, Alec, Luke Metz et Soumith Chintala. 2015. “Tentative de portrait non supervisé comme des réseaux antagonistes génératifs à convolution profonde.” CoRR abs/1511.06434. http://arxiv.org/abs/1511.06434.

By nsmaat