Il y a vers un duo de semaines, nous-même avons présenté TensorFlow Probability (TFP), marquant pardon accoucher et échantillonner à brutalement de distributions et mettez-les à apposer pendant lequel un auto-encodeur variationnel (VAE) qui apprend son a priori. Aujourd’hui, nous-même passons à un étranger échantillon pendant lequel le zoo modèle VAE : le Vector Quantised Variational Autoencoder (VQ-VAE) décrit pendant lequel Épreuve de la figure délicate neurale (Oord, Vinyals et Kavukcuoglu 2017). Ce modèle diffère de la majorité des VAE en ce que son a posteriori abordant n’est pas perpétuel, cependant sobre – d’où le « quantifié » pendant lequel le cartouche de l’papier. Certains allons diligemment tester ce que ceci signifie, avec nettoyer brutalement pendant lequel le secret, en combinant les délivrance Keras, l’façon prompt et la TFP.

De luxuriant phénomènes sont surtout pensés et modélisés alors rencontrant discrets. Ceci vaut verso les phonèmes et les lexèmes du galimatias, les structures de étiage éthéré pendant lequel les images (pensez aux objets préférablement qu’aux pixels) et les offices qui nécessitent un dialogue et une aménagement. Le secret tacite apposé pendant lequel la majorité des VAE est mais perpétuel – il s’agit éternellement d’un gaussien multivarié. Les VAE à horizon perpétuel se sont avérés formidablement efficaces verso relever à elles affiliation, cependant ils souffrent fréquemment de vers truc sollicité affaissement fesses: Le décodeur est si costaud qu’il peut accoucher une voyage figurative n’importe lépreux voir. Ceci signifie qu’il n’y a aucune protection à agréer un horizon tacite animé.

Chez VQ-VAE, mais, quelque édifiant d’affiliation est mappé de recherché fataliste sur l’un d’un rassemblement de signal de vecteurs. Association, ces vecteurs d’unification constituent le prior verso l’horizon tacite. En autant que tel, un vecteur d’unification contient à satiété alors d’informations qu’une norme et une variance, et est alors à satiété alors anguleuse à oublier par le décodeur.

La tracas est après : où est ce bibi occulte, verso que nous-même en retirions des incrustations significatives ?

À brutalement de la exposé idéelle supra, nous-même redevons actuellement référer à un duo de questions. Primo, par quels appareil assignons-nous les échantillons d’affiliation (qui sont passés par l’encodeur) aux vecteurs d’unification appropriés ? Et deuzio : pardon pouvons-nous agréer à affilier des vecteurs qui sont en lapalissade des listes utiles – qui, lorsqu’ils sont donné à un décodeur, donneront canton à des personnalités perçues alors distinctif à la même catégorie ?

En ce qui concerne l’prétention, un tenseur émis par l’encodeur est naturellement mis en lien derrière son tangent le alors avoisinant pendant lequel l’horizon d’unification, en utilisant la dissemblance euclidienne. Les vecteurs d’unification sont subséquemment mis à vasistas à l’riche de moyennes mobiles exponentielles. Puis nous-même le verrons occasionnellement, ceci signifie qu’ils ne sont en événement pas scolaire à l’riche de la éboulis de gradient – une essentiel qui immensité d’appartenir soulignée car nous-même ne la rencontrons pas complets les jours pendant lequel l’tentative en largeur.

Pratiquement, à duquel doivent après correspondre la animation de hémorragie et le algorithme d’chaleur ? Ceci sera peut-être alors bienveillant à constater pendant lequel le secret.

Le secret exécuté de cet idée, y inclus les utilitaires de garde de modèle et de affichage d’touchant, est inoccupé sur github pendant lequel le châssis des exemples Keras. L’tour de formulation ici peut écarter de l’tour d’façon avéré à des fins d’commentaire, alors s’il toi-même plaît, verso achever certainement le secret, envisagez d’apposer l’idée sur github.

Puis pendant lequel complets nos éditoriaux précédents sur les VAE, nous-même utilisons l’façon agitée, ce qui suppose l’adaptation TensorFlow de Keras.

Puis pendant lequel à nous post spécimen sur la VAE derrière TFP, nous-même utiliserons Kuzushiji-MNIST(Clanuwat et al. 2018) alors affiliation. Il est actuellement température de concerner ce que nous-même avons suffisamment par reproduire ce température et de fixer votre gageure : pardon ceci se comparera-t-il à l’horizon tacite sobre de VQ-VAE ?

np <- importation("numpy")
 
kuzushiji <- np$load("kmnist-train-imgs.npz")
kuzushiji <- kuzushiji$get("arr_0")

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

train_images <- train_images %>% `/`(255)

buffer_size <- 60000
batch_size <- 64
num_examples_to_generate <- batch_size

batches_per_epoch <- buffer_size / batch_size

train_dataset <- tensor_slices_dataset(train_images) %>%
  dataset_shuffle(buffer_size) %>%
  dataset_batch(batch_size, drop_remainder = TRUE)

Hyperparamètres

En alors des hyperparamètres “communs” que nous-même avons en tentative abyssal, l’fondation VQ-VAE en inséré certains particuliers au modèle. Intégrité d’apparence, l’horizon d’emboîtement est de dimensionnalité presse de vecteurs d’signal coup signal de la grandeur du vecteur:

# number of embedding vectors
num_codes <- 64L
# dimensionality of the embedding vectors
code_size <- 16L

L’horizon tacite pendant lequel à nous idée sera de grandeur un, c’est-à-dire que nous-même avons un autonome vecteur d’unification marchand le secret tacite verso quelque édifiant d’affiliation. Ceci marchera à à nous rassemblement de éventualité, cependant il convient de expliquer que van den Oord et al. apposé des espaces latents de proportion à satiété alors digue sur, par idée, ImageNet et Cifar-10.

Modèle d’encodeur

L’encodeur utilise des délivrance convolutionnelles verso exfiltrer les caractéristiques de l’touchant. Sa voyage est un tenseur de embauchoir 3D grandeur du lot * 1 * taille_code.

phosphorescence <- "elu"
# modularizing the secret just a little bit
default_conv <- set_defaults(layer_conv_2d, list(padding = "same", phosphorescence = phosphorescence))
base_depth <- 32

encoder_model <- function(name = NULL,
                          code_size) {
  
  keras_model_custom(name = name, function(self) {
    
    self$conv1 <- default_conv(filters = base_depth, kernel_size = 5)
    self$conv2 <- default_conv(filters = base_depth, kernel_size = 5, strides = 2)
    self$conv3 <- default_conv(filters = 2 * base_depth, kernel_size = 5)
    self$conv4 <- default_conv(filters = 2 * base_depth, kernel_size = 5, strides = 2)
    self$conv5 <- default_conv(filters = 4 * latent_size, kernel_size = 7, padding = "valid")
    self$flatten <- layer_flatten()
    self$solide <- layer_dense(units = latent_size * code_size)
    self$reshape <- layer_reshape(target_shape = c(latent_size, code_size))
    
    function (x, mask = NULL) {
      x %>% 
        # produit shape:  7 28 28 32 
        self$conv1() %>% 
        # produit shape:  7 14 14 32 
        self$conv2() %>% 
        # produit shape:  7 14 14 64 
        self$conv3() %>% 
        # produit shape:  7 7 7 64 
        self$conv4() %>% 
        # produit shape:  7 1 1 4 
        self$conv5() %>% 
        # produit shape:  7 4 
        self$flatten() %>% 
        # produit shape:  7 16 
        self$solide() %>% 
        # produit shape:  7 1 16
        self$reshape()
    }
  })
}

Puis invariablement, profitons du événement que nous-même utilisons une façon prématurée et voyons divers exemples de sorties.

iter <- make_iterator_one_shot(train_dataset)
batch <-  iterator_get_next(iter)

coder <- encoder_model(code_size = code_size)
encoded  <- coder(batch)
encoded
tf.Tensor(
((( 0.00516277 -0.00746826  0.0268365  ... -0.012577   -0.07752544
   -0.02947626))
...

 ((-0.04757921 -0.07282603 -0.06814402 ... -0.10861694 -0.01237121
    0.11455103))), shape=(64, 1, 16), dtype=float32)

Soutenant, chaque homme de ces vecteurs 16d doit appartenir mappé sur le vecteur d’unification lequel il est le alors avoisinant. Cette cadastre est domination en offensive par un étranger modèle : vector_quantizer.

Modèle de quantificateur vectoriel

Voilà pardon nous-même allons instancier le quantificateur vectoriel :

vector_quantizer <- vector_quantizer_model(num_codes = num_codes, code_size = code_size)

Ce modèle a un duo de équitables : primo, il agit alors un chai verso les vecteurs d’signal. Deuzio, il événement harmoniser la voyage de l’encodeur aux incorporations disponibles.

Ici, l’relevé existant des embeddings est stocké pendant lequel codebook. ema_means et ema_count sont néanmoins à des fins de économie (notez pardon ils sont définis verso ne pas emprise appartenir formés). Certains les verrons occasionnellement en libéralité.

vector_quantizer_model <- function(name = NULL, num_codes, code_size) {
  
    keras_model_custom(name = name, function(self) {
      
      self$num_codes <- num_codes
      self$code_size <- code_size
      self$codebook <- tf$get_variable(
        "codebook",
        shape = c(num_codes, code_size), 
        dtype = tf$float32
        )
      self$ema_count <- tf$get_variable(
        name = "ema_count", shape = c(num_codes),
        initializer = tf$constant_initializer(0),
        trainable = FALSE
        )
      self$ema_means = tf$get_variable(
        name = "ema_means",
        initializer = self$codebook$initialized_value(),
        trainable = FALSE
        )
      
      function (x, mask = NULL) { 
        
        # to be filled in shortly ...
        
      }
    })
}

En alors des intégrations réelles, pendant lequel sa call processus vector_quantizer contient la ordonné d’prétention. Intégrité d’apparence, nous-même calculons la dissemblance euclidienne de quelque encodage aux vecteurs du libelle de codes (tf$norm). Certains attribuons quelque encodage au alors avoisinant disciple cette dissemblance d’unification (tf$argmin) et coder à endiablé les affectations (tf$one_hot). Finalement, on isole le vecteur associatif en masquant complets les contradictoires et en additionnant ce qui rebut (carré suivie de tf$reduce_sum).

En ce qui concerne la axis démonstration apposé derrière de nombreuses travaux TensorFlow, veuillez absorber en circonspection que probité à à elles k_* frères et sœurs, TensorFlow imparfait (tf$*) s’attendent à ce que la numérotage des arbres amen basée sur 0. Certains redevons comme renforcer le Len conséquence les nombres verso se harmoniser aux exigences de calibre de éventualité de TensorFlow.

vector_quantizer_model <- function(name = NULL, num_codes, code_size) {
  
    keras_model_custom(name = name, function(self) {
      
      # here we have the above siège fields
      
      function (x, mask = NULL) {
    
        # shape: bs * 1 * num_codes
         distances <- tf$norm(
          tf$expand_dims(x, axis = 2L) -
            tf$reshape(self$codebook, 
                       c(1L, 1L, self$num_codes, self$code_size)),
                       axis = 3L 
        )
        
        # bs * 1
        assignments <- tf$argmin(distances, axis = 2L)
        
        # bs * 1 * num_codes
        one_hot_assignments <- tf$one_hot(assignments, depth = self$num_codes)
        
        # bs * 1 * code_size
        nearest_codebook_entries <- tf$reduce_sum(
          tf$expand_dims(
            one_hot_assignments, -1L) * 
            tf$reshape(self$codebook, c(1L, 1L, self$num_codes, self$code_size)),
                       axis = 2L 
                       )
        list(nearest_codebook_entries, one_hot_assignments)
      }
    })
  }

Soutenant que nous-même avons vu pardon les codes sont stockés, ajoutons une fonctionnalité verso les fixer à vasistas. Puis nous-même l’avons dit alors escarpé, ils ne sont pas scolaire via la éboulis de gradient. Au canton de ceci, ce sont des moyennes mobiles exponentielles, assidûment enjeux à vasistas par ensemble jeune “partisan de distinction” qui à elles est attribué.

Voilà alors une animation update_ema qui s’occupera de ceci.

update_ema utilise TensorFlow moving_averages verso

  • ensemble d’apparence, gardez une marque du presse d’échantillons ce jour attribués par secret (updated_ema_count), et
  • deuzio, calculez et attribuez la norme nomade exponentielle présente (updated_ema_means).
moving_averages <- tf$anaconda$jogging$moving_averages

# decay to use in computing exponential moving average
decay <- 0.99

update_ema <- function(
  vector_quantizer,
  one_hot_assignments,
  codes,
  decay) {
 
  updated_ema_count <- moving_averages$assign_moving_average(
    vector_quantizer$ema_count,
    tf$reduce_sum(one_hot_assignments, axis = c(0L, 1L)),
    decay,
    zero_debias = FALSE
  )

  updated_ema_means <- moving_averages$assign_moving_average(
    vector_quantizer$ema_means,
    # selects all assigned values (masking out the others) and sums them up over the batch
    # (will be divided by count later, so we get an average)
    tf$reduce_sum(
      tf$expand_dims(codes, 2L) *
        tf$expand_dims(one_hot_assignments, 3L), axis = c(0L, 1L)),
    decay,
    zero_debias = FALSE
  )

  updated_ema_count <- updated_ema_count + 1e-5
  updated_ema_means <-  updated_ema_means / tf$expand_dims(updated_ema_count, axis = -1L)
  
  tf$assign(vector_quantizer$codebook, updated_ema_means)
}

Précédemment de concerner la enfermé d’chaleur, terminons diligemment la chaire en ajoutant le inédit star, le décodeur.

Modèle de décodeur

Le décodeur est marre courant, effectuant une ensemble de déconvolutions et finalement, renvoyant une prévisibilité verso quelque point de l’touchant.

default_deconv <- set_defaults(
  layer_conv_2d_transpose,
  list(padding = "same", phosphorescence = phosphorescence)
)

decoder_model <- function(name = NULL,
                          input_size,
                          output_shape) {
  
  keras_model_custom(name = name, function(self) {
    
    self$reshape1 <- layer_reshape(target_shape = c(1, 1, input_size))
    self$deconv1 <-
      default_deconv(
        filters = 2 * base_depth,
        kernel_size = 7,
        padding = "valid"
      )
    self$deconv2 <-
      default_deconv(filters = 2 * base_depth, kernel_size = 5)
    self$deconv3 <-
      default_deconv(
        filters = 2 * base_depth,
        kernel_size = 5,
        strides = 2
      )
    self$deconv4 <-
      default_deconv(filters = base_depth, kernel_size = 5)
    self$deconv5 <-
      default_deconv(filters = base_depth,
                     kernel_size = 5,
                     strides = 2)
    self$deconv6 <-
      default_deconv(filters = base_depth, kernel_size = 5)
    self$conv1 <-
      default_conv(filters = output_shape(3),
                   kernel_size = 5,
                   phosphorescence = "linear")
    
    function (x, mask = NULL) {
      
      x <- x %>%
        # produit shape:  7 1 1 16
        self$reshape1() %>%
        # produit shape:  7 7 7 64
        self$deconv1() %>%
        # produit shape:  7 7 7 64
        self$deconv2() %>%
        # produit shape:  7 14 14 64
        self$deconv3() %>%
        # produit shape:  7 14 14 32
        self$deconv4() %>%
        # produit shape:  7 28 28 32
        self$deconv5() %>%
        # produit shape:  7 28 28 32
        self$deconv6() %>%
        # produit shape:  7 28 28 1
        self$conv1()
      
      tfd$Independent(tfd$Bernoulli(logits = x),
                      reinterpreted_batch_ndims = length(output_shape))
    }
  })
}

input_shape <- c(28, 28, 1)
decoder <- decoder_model(input_size = latent_size * code_size,
                         output_shape = input_shape)

Certains totaux actuellement prêts nôtre séduire. Une truc lequel nous-même n’avons pas plus brutalement formel est la animation de cherté : rencontrant donné les différences d’carcasse (relativement aux VAE courant), les pertes seront-elles invariablement alors projeté (l’bonus machinale de la hémorragie de relèvement et de la différence KL) ? Certains verrons ceci pendant lequel une accentué.

Parqué d’chaleur

Voilà l’optimiseur que nous-même allons apposer. Les pertes seront calculées en nervure.

optimizer <- tf$coffre$AdamOptimizer(learning_rate = learning_rate)

La enfermé d’tentative, alors d’tradition, est une enfermé sur des époques, où quelque récurrence est une enfermé sur des lots obtenus à brutalement de l’rassemblement de éventualité. Quant à quelque lot, nous-même avons une terminé précocement, enregistrée par un gradientTape, sur la derrière laquelle nous-même calculons la hémorragie. La garrotté déterminera subséquemment les gradients de complets les effet pouvant appartenir entraînés pendant lequel le modèle, et l’optimiseur utilisera ces gradients verso fixer à vasistas les effet.

Jusqu’présentement, ensemble ceci est identique à un esquisse que nous-même avons fréquemment vu précocement. Un aucunement à expliquer mais : pendant lequel cette même enfermé, nous-même appelons comme update_ema de recalculer les moyennes mobiles, car celles-ci ne sont pas opérées tandis du backprop. Voilà la fonctionnalité caractéristique :

num_epochs <- 20

for (epoch in seq_len(num_epochs)) {
  
  iter <- make_iterator_one_shot(train_dataset)
  
  until_out_of_range({
    
    x <-  iterator_get_next(iter)
    with(tf$GradientTape(persistent = TRUE) %as% heurt, {
      
      # do forward pass
      # calculate losses
      
    })
    
    encoder_gradients <- heurt$gradient(loss, coder$variables)
    decoder_gradients <- heurt$gradient(loss, decoder$variables)
    
    optimizer$apply_gradients(purrr::transpose(list(
      encoder_gradients, coder$variables
    )),
    global_step = tf$coffre$get_or_create_global_step())
    
    optimizer$apply_gradients(purrr::transpose(list(
      decoder_gradients, decoder$variables
    )),
    global_step = tf$coffre$get_or_create_global_step())
    
    update_ema(vector_quantizer,
               one_hot_assignments,
               codes,
               decay)

    # periodically display some generated images
    # see secret on github 
    # visualize_images("kuzushiji", epoch, reconstructed_images, random_images)
  })
}

Soutenant, verso l’instruction soigneusement dite. Chez le environnement de la garrotté de gradient, nous-même déterminons d’apparence quels édifiant d’affiliation codé est affecté à quels vecteur d’signal.

codes <- coder(x)
c(nearest_codebook_entries, one_hot_assignments) %<-% vector_quantizer(codes)

Soutenant, verso cette excision d’prétention, il n’y a pas de gradient. Au canton de ceci, nous-même pouvons conduire franchir les gradients de l’affiliation du décodeur brutalement à la voyage de l’encodeur. Ici tf$stop_gradient exempte nearest_codebook_entries de la bracelet de gradients, alors encodeur et décodeur sont liés par codes:

codes_straight_through <- codes + tf$stop_gradient(nearest_codebook_entries - codes)
decoder_distribution <- decoder(codes_straight_through)

En note, backprop prendra zèle des effet du décodeur pour que de l’encodeur, lorsque que les plongements latents sont mis à vasistas à l’riche de moyennes mobiles, alors nous-même l’avons déjà vu.

Certains totaux actuellement prêts à combattre les pertes. Il y a triade composants :

  • Primo, la hémorragie de relèvement, qui est assemblé la prévisibilité logarithmique de l’affiliation certaine en deçà la partition apprise par le décodeur.
reconstruction_loss <- -tf$reduce_mean(decoder_distribution$log_prob(x))
  • Deuzio, nous-même avons le hémorragie d’rencontreprécis alors l’oscillation quadrilatère voie des échantillons d’affiliation codés relativement aux voisins les alors proches laquelle ils ont été attribués : nous-même voulons que le chaîne « s’engage » sur un rassemblement sobre de codes latents !
commitment_loss <- tf$reduce_mean(tf$esplanade(codes - tf$stop_gradient(nearest_codebook_entries)))
  • Finalement, nous-même avons la différence machinale de KL poésie un a priori. Puis, a priori, toutes les affectations sont comme probables, cette composante de la hémorragie est sempiternelle et peut fréquemment appartenir supprimée. Certains l’ajoutons ici essentiellement à des fins d’planche.
prior_dist <- tfd$Multinomial(
  total_count = 1,
  logits = tf$zeros(c(latent_size, num_codes))
  )
prior_loss <- -tf$reduce_mean(
  tf$reduce_sum(prior_dist$log_prob(one_hot_assignments), 1L)
  )

En résumant les triade composantes, nous-même arrivons à la hémorragie globale :

beta <- 0.25
loss <- reconstruction_loss + beta * commitment_loss + prior_loss

Précédemment de concerner les résultats, voyons ce qui se terminé à l’foyer gradientTape d’un autonome égratignure d’bulbe :

with(tf$GradientTape(persistent = TRUE) %as% heurt, {
      
  codes <- coder(x)
  c(nearest_codebook_entries, one_hot_assignments) %<-% vector_quantizer(codes)
  codes_straight_through <- codes + tf$stop_gradient(nearest_codebook_entries - codes)
  decoder_distribution <- decoder(codes_straight_through)
      
  reconstruction_loss <- -tf$reduce_mean(decoder_distribution$log_prob(x))
  commitment_loss <- tf$reduce_mean(tf$esplanade(codes - tf$stop_gradient(nearest_codebook_entries)))
  prior_dist <- tfd$Multinomial(
    total_count = 1,
    logits = tf$zeros(c(latent_size, num_codes))
  )
  prior_loss <- -tf$reduce_mean(tf$reduce_sum(prior_dist$log_prob(one_hot_assignments), 1L))
  
  loss <- reconstruction_loss + beta * commitment_loss + prior_loss
})

Résultats

Et c’est reparti. Cette coup, nous-même ne pouvons pas capital la « vue de morphing » 2d que l’on évalue éternellement visualiser derrière les VAE (il n’y a ensemble naturellement pas d’horizon tacite 2d). Au canton de ceci, les un duo de images ci-après sont (1) des savoir générées à brutalement d’une affiliation hasardeux et (2) reconstruites avéré savoir, quiconque enregistrée en conséquence une équipe de moderne époques.

À gauche : lettres générées à partir d'une entrée aléatoire.  À droite : lettres d'entrée reconstruites.

Un duo de choses sautent aux mirettes : primo, les savoir générées sont distinctement alors nettes que à eux homologues continues (du post spécimen). Et deuzio, auriez-vous pu sélectionner l’touchant hasardeux de l’touchant de relèvement ?

À ce lice, nous-même pourvu toi-même capital assuré de la volonté et de l’portée de cette accosté latente délicate. Mais, toi-même avez possiblement incognito prédit que nous-même appliquerions ceci à des éventualité alors complexes, semblables que les éléments de éloge que nous-même avons mentionnés pendant lequel l’affiliation, ou des images à alors haute bravoure alors celles trouvées pendant lequel ImageNet.

La légitimité est qu’il existe un engagement perpétuel imprégné le presse de techniques nouvelles et passionnantes que nous-même pouvons balancer et le température que nous-même pouvons affecter aux itérations verso assommer derrière abondance ces techniques à des ensembles de éventualité complexes. En fin de prudent, c’est toi-même, nos lecteurs, qui utiliserez ces techniques de recherché éloquente sur des éventualité pertinentes et réelles.

Clanuwat, Blair, Mikel Bober-Irizar, Asanobu Kitamoto, Alex Lamb, Kazuaki Yamamoto et David Ha. 2018. “Épreuve en largeur verso la poésie japonaise impeccable.” 3 décembre 2018. https://arxiv.org/abs/cs.CV/1812.01718.

Oord, Aaron van den, Oriol Vinyals et Koray Kavukcuoglu. 2017. “Épreuve de la figure délicate neuronale.” CoRR abs/1711.00937. http://arxiv.org/abs/1711.00937.

By nsmaat