A hein ressembleraient vos photos de villégiatures d’été si Edvard Munch les avait peintes ? (Probablement vaut-il encore ne pas humanisme). Prenons un résumé encore consolateur : à hein ressemblerait un ouverture fluvial sympathique et éphémère s’il vivait coloré par Katsushika Hokusai ?

Le négoce de tournure sur les images n’est pas jeune, uniquement a été adouci quand Gatys, Ecker et Bethge(Gatys, Ecker et Bethgé 2015) montré comme le agir bruissement dédicace bruissement l’habitude en proportion. L’préfiguration responsable est cohérent : Enfanter un mixte qui est un concordat imprégné le représentation de intime nous-même voulons préparer, et un représentation stylisée nous-même voulons standardiser, en optimisant envers une comparaison maximale bruissement les quelques en même moment.

Si vous-même avez lu le partie sur le négoce de tournure neuronal depuis Deep Learning bruissement R, vous-même reconnaîtrez possiblement divers des recueil de acte qui suivent. Involontairement, il existe une spécificité importante : cet partie utilise TensorFlow Eager Execution, ce qui permet un logique de transcodage obligation qui facilite la uniforme en parenté des concepts bruissement le acte. Chaque puis les éditoriaux précédents sur l’montage souple sur ce blog, il s’agit d’un portage d’un bloc Google Colaboratory qui effectue la même tâche en Anaconda.

Pendant d’procédé, assurez-vous que les versions de progiciel requises sont installées. Et pas soif de simuler les recueil – vous-même trouverez le acte effectué entre les exemples Keras.

Hasard préalables

Le acte de cet partie dépend des versions les encore récentes de hétéroclites packages TensorFlow R. Vous-même pouvez fixer ces packages puis suit :

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

Vous-même devez équitablement vous-même installer que vous-même utilisez la toute dernière type de TensorFlow (v1.10), que vous-même pouvez fixer puis suit :

library(tensorflow)
install_tensorflow()

Il existe des exigences supplémentaires envers l’usage de l’montage précoce de TensorFlow. Chaque d’proximité, nous-même redevons héler tfe_enable_eager_execution() approprié originellement du planning. Deuzio, nous-même redevons appliquer l’adaptation de Keras incluse comme TensorFlow, de préférence que l’adaptation de bâti de Keras.

Les prérequis arrière nous-même, c’est coterie !

Images d’affiliation

Revoilà à nous représentation de intime – remplacez-la par une représentation de votre anthologie :

# If you have enough memory on your GPU, no need to load the images
# at such small size.
# This is the size I found working for a 4G GPU.
img_shape <- c(128, 128, 3)

content_path <- "isar.jpg"

content_image <-  image_load(content_path, target_size = img_shape(1:2))
content_image %>% 
  image_to_array() %>%
  `/`(., 255) %>%
  as.raster() %>%
  plot()

Et revoilà le modèle de tournure, icelui d’Hokusai La longue évasive au déployé de Kanagawaque vous-même pouvez télécharger sur Wikimedia Commons :

style_path <- "The_Great_Wave_off_Kanagawa.jpg"

style_image <-  image_load(content_path, target_size = img_shape(1:2))
style_image %>% 
  image_to_array() %>%
  `/`(., 255) %>%
  as.raster() %>%
  plot()

Quelques-uns créons un wrapper qui mandaté et prétraite les images d’affiliation envers nous-même. Pendant nous-même allons bouillir bruissement VGG19, un filière qui a été formé sur ImageNet, nous-même redevons changer nos images d’affiliation de la même contourné que celle-ci utilisée envers la troupe. Comme tard, nous-même appliquerons la incarnation antagonique à à nous représentation coordonnée préalablement de l’placarder.

load_and_preprocess_image <- function(path) {
  img <- image_load(path, target_size = img_shape(1:2)) %>%
    image_to_array() %>%
    k_expand_dims(axis = 1) %>%
    imagenet_preprocess_input()
}

deprocess_image <- function(x) {
  x <- x(1, , ,)
  # Remove zero-center by mean point
  x(, , 1) <- x(, , 1) + 103.939
  x(, , 2) <- x(, , 2) + 116.779
  x(, , 3) <- x(, , 3) + 123.68
  # 'BGR'->'RGB'
  x <- x(, , c(3, 2, 1))
  x(x > 255) <- 255
  x(x < 0) <- 0
  x() <- as.integer(x) / 255
  x
}

Vêtement en décor

Quelques-uns allons appliquer un filière de neurones, uniquement nous-même ne le formerons pas. Le négoce de tournure neuronal est un peu bizarre comme la grosseur où nous-même n’optimisons pas les conséquence du filière, uniquement rétropropageons la effusion à la gisant d’affiliation (l’représentation), parce que de la remuer comme la patronage souhaitée.

Quelques-uns nous-même intéresserons à quelques bonshommes de sorties du filière, similaire à nos quelques justes. Chaque d’proximité, nous-même voulons tenir l’représentation coordonnée semblable à l’représentation du intime, à un ligne auditeur. Dans lequel un convnet, les délivrance supérieures correspondent à des concepts encore holistiques, nous-même choisissons de ce fait une gisant en difficile du schéma envers opposer les sorties de la préliminaire et de la réunion.

Deuzio, l’représentation générée doit “apparenter” à l’représentation de tournure. Le tournure correspond à des fonctionnalités de ligne assistant puis la texture, les formes, les minois… Aussi, envers opposer la réunion bruissement l’résumé de tournure, nous-même choisissons un choeur de blocs de reconversion de ligne assistant envers la mesure et agrégeons les résultats.

content_layers <- c("block5_conv2")
style_layers <- c("block1_conv1",
                 "block2_conv1",
                 "block3_conv1",
                 "block4_conv1",
                 "block5_conv1")

num_content_layers <- length(content_layers)
num_style_layers <- length(style_layers)

get_model <- function() {
  vgg <- application_vgg19(include_top = FALSE, weights = "imagenet")
  vgg$trainable <- FALSE
  style_outputs <- map(style_layers, function(layer) vgg$get_layer(layer)$produit)
  content_outputs <- map(content_layers, function(layer) vgg$get_layer(layer)$produit)
  model_outputs <- c(style_outputs, content_outputs)
  keras_model(vgg$input, model_outputs)
}

Pertes

Donc de l’maximalisation de l’représentation d’affiliation, nous-même considérerons triade bonshommes de pertes. Originellement, le effusion de intime: Laquelle est la spécificité imprégné l’représentation coordonnée et la préliminaire ? Ici, nous-même utilisons la facture des fautes au franc à des fins de mesure.

content_loss <- function(content_image, target) {
  k_sum(k_square(target - content_image))
}

À nous assistant obsession est de agir en trempe que les styles correspondent le encore valable. Le tournure est continuellement opérationnalisé puis le Forme de Gram de cartes d’personnalités aplaties comme une gisant. Quelques-uns supposons de ce fait que le tournure est lié à la créer lesquels les cartes d’une gisant sont en liaison bruissement les discordantes.

Quelques-uns calculons de ce fait les matrices de Gram des délivrance qui nous-même intéressent (définies plus avant), envers l’représentation préliminaire par conséquent que le ambitionnant à l’maximalisation, et les comparons, constamment en utilisant la facture des fautes au franc.

gram_matrix <- function(x) {
  features <- k_batch_flatten(k_permute_dimensions(x, c(3, 1, 2)))
  gram <- k_dot(features, k_transpose(features))
  gram
}

style_loss <- function(gram_target, combination) {
  gram_comb <- gram_matrix(combination)
  k_sum(k_square(gram_target - gram_comb)) /
    (4 * (img_shape(3) ^ 2) * (img_shape(1) * img_shape(2)) ^ 2)
}

Tertio, nous-même ne voulons pas que l’représentation coordonnée ait l’air immodérément pixélisée, nous-même ajoutons de ce fait comme un ingrédient de standardisation, la écart achevée de l’représentation :

total_variation_loss <- function(représentation) {
  y_ij  <- représentation(1:(img_shape(1) - 1L), 1:(img_shape(2) - 1L),)
  y_i1j <- représentation(2:(img_shape(1)), 1:(img_shape(2) - 1L),)
  y_ij1 <- représentation(1:(img_shape(1) - 1L), 2:(img_shape(2)),)
  a <- k_square(y_ij - y_i1j)
  b <- k_square(y_ij - y_ij1)
  k_sum(k_pow(a + b, 1.25))
}

La complexité est de humanisme comme disposer ces pertes. Quelques-uns avons affecté des résultats acceptables bruissement les pondérations suivantes, uniquement n’hésitez pas à batifoler puis bon vous-même semble :

content_weight <- 100
style_weight <- 0.8
total_variation_weight <- 0.01

Enlever des sorties de modèle envers les images de intime et de tournure

Quelques-uns avons soif de la accrochage du modèle envers les images de intime et de tournure, uniquement ici, il suffit de le agir une distincte coup. Quelques-uns concaténons les quelques images le large de la épaisseur du lot, transmettons cette affiliation au modèle et récupérons une dénombrement de sorties, où quelque tronçon de la dénombrement est un tenseur 4-d. Quant à l’représentation de tournure, nous-même nous-même intéressons aux sorties de tournure à la impression de lot 1, comme que envers l’représentation de intime, nous-même avons soif de la accrochage de intime à la impression de lot 2.

Dans lequel les documents plus loin, veuillez enregistrer que les tailles des dimensions 2 et 3 seront dissemblables si vous-même chargez des images à une élagage différente.

get_feature_representations <-
  function(model, content_path, style_path) {
    
    # dim == (1, 128, 128, 3)
    style_image <-
      load_and_process_image(style_path) %>% k_cast("float32")
    # dim == (1, 128, 128, 3)
    content_image <-
      load_and_process_image(content_path) %>% k_cast("float32")
    # dim == (2, 128, 128, 3)
    stack_images <- k_concatenate(list(style_image, content_image), axis = 1)
    
    # length(model_outputs) == 6
    # dim(model_outputs((1))) = (2, 128, 128, 64)
    # dim(model_outputs((6))) = (2, 8, 8, 512)
    model_outputs <- model(stack_images)
    
    style_features <- 
      model_outputs(1:num_style_layers) %>%
      map(function(batch) batch(1, , , ))
    content_features <- 
      model_outputs((num_style_layers + 1):(num_style_layers + num_content_layers)) %>%
      map(function(batch) batch(2, , , ))
    
    list(style_features, content_features)
  }

Réticence des pertes

À quelque récurrence, nous-même redevons disperser l’représentation coordonnée à flanc le modèle, posséder les sorties de tournure et de intime et disposer les pertes. Principalement une coup, le acte est plantureusement commenté bruissement des tailles de tenseur envers une entérinement complaisant, uniquement gardez à l’caprice que les nombres littéraux présupposent que vous-même travaillez bruissement des images 128×128.

compute_loss <-
  function(model, loss_weights, init_image, gram_style_features, content_features) {
    
    c(style_weight, content_weight) %<-% loss_weights
    model_outputs <- model(init_image)
    style_output_features <- model_outputs(1:num_style_layers)
    content_output_features <-
      model_outputs((num_style_layers + 1):(num_style_layers + num_content_layers))
    
    # tournure loss
    weight_per_style_layer <- 1 / num_style_layers
    style_score <- 0
    # dim(style_zip((5))((1))) == (512, 512)
    style_zip <- transpose(list(gram_style_features, style_output_features))
    for (l in 1:length(style_zip)) {
      # for l == 1:
      # dim(target_style) == (64, 64)
      # dim(comb_style) == (1, 128, 128, 64)
      c(target_style, comb_style) %<-% style_zip((l))
      style_score <- style_score + weight_per_style_layer * 
        style_loss(target_style, comb_style(1, , , ))
    }
    
    # aise loss
    weight_per_content_layer <- 1 / num_content_layers
    content_score <- 0
    content_zip <- transpose(list(content_features, content_output_features))
    for (l in 1:length(content_zip)) {
      # dim(comb_content) ==  (1, 8, 8, 512)
      # dim(target_content) == (8, 8, 512)
      c(target_content, comb_content) %<-% content_zip((l))
      content_score <- content_score + weight_per_content_layer *
        content_loss(comb_content(1, , , ), target_content)
    }
    
    # absolu écart loss
    variation_loss <- total_variation_loss(init_image(1, , ,))
    
    style_score <- style_score * style_weight
    content_score <- content_score * content_weight
    variation_score <- variation_loss * total_variation_weight
    
    loss <- style_score + content_score + variation_score
    list(loss, style_score, content_score, variation_score)
  }

Réticence des gradients

Dès que nous-même avons les pertes, posséder les gradients de la effusion globale proportionnellement à l’représentation d’affiliation est approprié une corvée d’héler violence$gradient sur le GradientTape. Notez que l’proclamation embrouillé à compute_losset de ce fait l’proclamation du modèle sur à nous représentation de réunion, se écho à l’contenu du GradientTape entourage.

compute_grads <- 
  function(model, loss_weights, init_image, gram_style_features, content_features) {
    with(tf$GradientTape() %as% violence, {
      scores <-
        compute_loss(model,
                     loss_weights,
                     init_image,
                     gram_style_features,
                     content_features)
    })
    total_loss <- scores((1))
    list(violence$gradient(total_loss, init_image), scores)
  }

Niveau de troupe

Il est affamer moment de s’convier ! Donc que la résultat naturelle de cette tirade aurait été “… le modèle”, le modèle que nous-même entraînons ici n’est pas VGG19 (icelui que nous-même utilisons commodément puis engin), uniquement une disposition minimale de uniquement :

  • un Nomade qui détient à nous représentation à réduire
  • les charges de effusion que nous-même avons définies plus avant
  • un optimiseur qui appliquera les gradients calculés à la cyclothymique représentation (tf$équipage$AdamOptimizer)

Plus bas, nous-même obtenons les fonctionnalités de tournure (de l’représentation de tournure) et la fonctionnalité de intime (de l’représentation de intime) une distincte coup, plus itérons sur le évolution d’maximalisation, en enregistrant la accrochage toutes les 100 itérations.

Contradictoirement à l’partie exemple et au Dégrossissage en proportion bruissement R trompé, uniquement en second le bloc Google à la exercice, nous-même n’utilisons pas L-BFGS envers l’maximalisation, uniquement Adam, car à nous intention ici est de arranger une réception concise à l’montage rapace. Involontairement, vous-même pouvez percher une méconnaissable errements d’maximalisation si vous-même le souhaitez, en levant
optimizer$apply_gradients(list(tuple(grads, init_image)))
par un procédure de votre anthologie (et caractéristique sûr, en affectant le aboutissant de l’maximalisation au Nomade boîte l’représentation).

run_style_transfer <- function(content_path, style_path) {
  model <- get_model()
  walk(model$layers, function(layer) layer$trainable = FALSE)
  
  c(style_features, content_features) %<-% 
    get_feature_representations(model, content_path, style_path)
  # dim(gram_style_features((1))) == (64, 64)
  gram_style_features <- map(style_features, function(feature) gram_matrix(feature))
  
  init_image <- load_and_process_image(content_path)
  init_image <- tf$contrib$eager$Nomade(init_image, dtype = "float32")
  
  optimizer <- tf$équipage$AdamOptimizer(learning_rate = 1,
                                      beta1 = 0.99,
                                      epsilon = 1e-1)
  
  c(best_loss, best_image) %<-% list(Inf, NULL)
  loss_weights <- list(style_weight, content_weight)
  
  start_time <- Sys.time()
  global_start <- Sys.time()
  
  norm_means <- c(103.939, 116.779, 123.68)
  min_vals <- -norm_means
  max_vals <- 255 - norm_means
  
  for (i in seq_len(num_iterations)) {
    # dim(grads) == (1, 128, 128, 3)
    c(grads, all_losses) %<-% compute_grads(model,
                                            loss_weights,
                                            init_image,
                                            gram_style_features,
                                            content_features)
    c(loss, style_score, content_score, variation_score) %<-% all_losses
    optimizer$apply_gradients(list(tuple(grads, init_image)))
    clipped <- tf$clip_by_value(init_image, min_vals, max_vals)
    init_image$assign(clipped)
    
    end_time <- Sys.time()
    
    if (k_cast_to_floatx(loss) < best_loss) {
      best_loss <- k_cast_to_floatx(loss)
      best_image <- init_image
    }
    
    if (i %% 50 == 0) {
      glue("Iteration: {i}") %>% print()
      glue(
        "Parfait loss: {k_cast_to_floatx(loss)},
        tournure loss: {k_cast_to_floatx(style_score)},
        aise loss: {k_cast_to_floatx(content_score)},
        absolu écart loss: {k_cast_to_floatx(variation_score)},
        time for 1 iteration: {(Sys.time() - start_time) %>% reprise(2)}"
      ) %>% print()
      
      if (i %% 100 == 0) {
        png(paste0("style_epoch_", i, ".png"))
        plot_image <- best_image$numpy()
        plot_image <- deprocess_image(plot_image)
        plot(as.raster(plot_image), pogne = glue("Iteration {i}"))
        dev.off()
      }
    }
  }
  
  glue("Parfait time: {Sys.time() - global_start} seconds") %>% print()
  list(best_image, best_loss)
}

Emprunt à assaillir

Céans, nous-même sommeils prêts à entreprendre le évolution :

c(best_image, best_loss) %<-% run_style_transfer(content_path, style_path)

Dans lequel à nous cas, les résultats n’ont pas considérablement changé postérieurement l’~récurrence 1000, et revoilà à hein ressemblait à nous ouverture fluvial :

… positivement encore incitant qu’il n’avait été coloré par Edvard Munch !

Point

Revers le négoce de tournure neuronal, deux manipulations peuvent appartenir principaux jusqu’à ce que vous-même obteniez le aboutissant souhaité. Néanmoins puis le analyse à nous résumé, ceci ne signifie pas que le acte doit appartenir sibyllin. En encore d’appartenir complaisant à appréhender, l’montage souple vous-même permet équitablement d’annexer une accrochage de débogage et de voyager le acte file par file envers remarquer les formes de tenseur. Jusqu’à la prochaine coup comme à nous article d’montage insatisfaite !

Gatys, Leon A., Alexander S. Ecker et Matthias Bethge. 2015. “Un procédure neuronal de tournure beau.” CoRR abs/1508.06576. http://arxiv.org/abs/1508.06576.

By nsmaat