Pendant un azimut, la éparpillement d’images n’est pas si différente de la triage d’images. C’est congruent qu’au endroit de catégoriser une emblème là-dedans son collection, la éparpillement aboutit à une pancarte derrière tout point. Et puis là-dedans la triage des images, les catégories d’rude dépendent de la tâche : rudimentaire recette auprès horizon, disons ; plusieurs hommes de tissus; plusieurs hommes de herbette; etc.

Le charité alinéa n’est pas le rudimentaire de ce blog à conférer de ce tireuse ; et puis intégraux les précédents, il utilise une châssis U-Net derrière venir son visée. Les caractéristiques centrales (de ce bunker, pas U-Net) sont :

  1. Il description hein procéder une précipitation de éventualité derrière une tâche de éparpillement d’emblème.

  2. Il utilise luz, torchl’limite de escarpé étiage de, derrière tenter le modèle.

  3. Il bavure JIT le modèle formé et l’enregistre derrière le déploiement sur les appareils mobiles. (JIT trouvant l’voltige classiquement employé derrière torch simulateur juste-à-temps.)

  4. Il comprend un justice de renouvellement de forme (malheureusement pas une chipotage) du modèle numéroté complet sur Android.

Et si toi-même pensez que ceci en soi n’est pas au sujet de dard, à nous tâche ici est de deviner des chats et des chiens. Duquel de surtout notable qu’une méditation adroit toi-même permettant de désigner votre minet du bergère soyeux sur léproserie il repose ?

Un chat de l'Oxford Pet Dataset (Parkhi et al. (2012)).

S’tenter en R

Moi-même commençons par concerter les éventualité.

Pré-traitement et précipitation des éventualité

Alors apprêté par torchdatasets, l’Oxford Pet Dataset propose triade variantes de éventualité cibles dans lépreux opiner : la éclat globale (minet ou dogue), la ascendance égoïste (il y en a trente-sept) et une éparpillement au étiage du point verso triade catégories : rudimentaire recette, confins et horizon. Celui-là est la énergie par dégradation ; et c’est entièrement le parangon de victime lesquels nous-même avons nécessité.

Un adresse à oxford_pet_dataset(root = dir) déclenchera le téléchargement premier :

# need torch > 0.6.1
# may have to run remotes::install_github("mlverse/torch", ref = remotes::github_pull("713")) depending on when you read this
library(torch) 
library(torchvision)
library(torchdatasets)
library(luz)

dir <- "~/.torch-datasets/oxford_pet_dataset"

ds <- oxford_pet_dataset(root = dir)

Les images (et les masques correspondants) sont de tailles singulières. Verso l’aperçu, purement, nous-même aurons nécessité qu’ils soient intégraux de la même ébarbé. Cela peut nature terminé en transmettant transform = et target_transform = arguments. Néanmoins qu’en est-il de l’précipitation des éventualité (spécialement régulièrement une parcimonieux notable à avaler) ? Imaginez que nous-même utilisions le bouleversement conjectural. Une emblème d’initiation sera retournée – ou non – escortant une réelle pronostic. Néanmoins si l’emblème est retournée, le abri a rude à l’nature quant à ! Les transformations d’initiation et de victime ne sont pas indépendantes, là-dedans ce cas.

Une issue consiste à produire un wrapper tout autour oxford_pet_dataset() qui nous-même permet de “s’prendre” au .getitem() procédé, puis cela:

pet_dataset <- torch::dataset(
  
  inherit = oxford_pet_dataset,
  
  initialize = function(..., size, normalize = TRUE, précipitation = NULL) {
    
    self$précipitation <- précipitation
    
    input_transform <- function(x) {
      x <- x %>%
        transform_to_tensor() %>%
        transform_resize(size) 
      # we'll make use of pre-trained MobileNet v2 as a feature extractor
      # => normalize in order to divertissement the répartition of images it was trained with
      if (isTRUE(normalize)) x <- x %>%
        transform_normalize(mean = c(0.485, 0.456, 0.406),
                            std = c(0.229, 0.224, 0.225))
      x
    }
    
    target_transform <- function(x) {
      x <- torch_tensor(x, dtype = torch_long())
      x <- x(newaxis,..)
      # intervention = 0 makes sure we still end up with integer classes
      x <- transform_resize(x, size, intervention = 0)
    }
    
    essence$initialize(
      ...,
      transform = input_transform,
      target_transform = target_transform
    )
    
  },
  .getitem = function(i) {
    
    élément <- essence$.getitem(i)
    if (!is.null(self$précipitation)) 
      self$précipitation(élément)
    else
      list(x = élément$x, y = élément$y(1,..))
  }
)

Entier ce que nous-même avons à commettre soutenant est de produire une empile personnalisée qui nous-même permet de résoudre lequel précipitation administrer à tout dipôle entrée-cible, avec d’hurler artisanalement les travaux de modification respectives.

Ici, nous-même retournons, en norme, une emblème sur paire, et si nous-même le faisons, nous-même retournons comme le abri. La moindre modification – orchestrant des changements aléatoires de éclat, de obstruction et de divergence – est appliquée néanmoins à l’emblème d’initiation.

précipitation <- function(élément) {
  
  vflip <- runif(1) > 0.5
  
  x <- élément$x
  y <- élément$y
  
  if (isTRUE(vflip)) {
    x <- transform_vflip(x)
    y <- transform_vflip(y)
  }
  
  x <- transform_color_jitter(x, brightness = 0.5, obstruction = 0.3, contrast = 0.3)
  
  list(x = x, y = y(1,..))
  
}

Moi-même utilisons soutenant l’nécessaire, pet_dataset()derrière instancier les ensembles d’aperçu et de affirmation, et produire les chargeurs de éventualité respectifs.

train_ds <- pet_dataset(root = dir,
                        split = "allure",
                        size = c(224, 224),
                        précipitation = précipitation)
valid_ds <- pet_dataset(root = dir,
                        split = "valid",
                        size = c(224, 224))

train_dl <- dataloader(train_ds, batch_size = 32, shuffle = TRUE)
valid_dl <- dataloader(valid_ds, batch_size = 32)

Limitation du modèle

Le modèle implémente une châssis U-Net ordinaire, verso une station d’codification (la passeport “down”), une station de décodage (la passeport “up”), et principalement, un “transition” qui passeport les caractéristiques préservées de l’station d’codification à accouchement correspondantes là-dedans l’échelonné de décodage.

Encodeur

Entier d’entour, nous-même avons l’encodeur. Il utilise un modèle pré-formé (MobileNet v2) puis arracheur de fonctionnalités.

L’encodeur divise les blocs d’défrichement de fonctionnalités de MobileNet v2 en changées étapes et administré une station postérieurement l’divergent. Les résultats respectifs sont enregistrés là-dedans une relevé.

chiffrer <- nn_module(
  
  initialize = function() {
    model <- model_mobilenet_v2(pretrained = TRUE)
    self$stages <- nn_module_list(list(
      nn_identity(),
      model$features(1:2),
      model$features(3:4),
      model$features(5:7),
      model$features(8:14),
      model$features(15:18)
    ))

    for (par in self$parameters) {
      par$requires_grad_(FALSE)
    }

  },
  forward = function(x) {
    features <- list()
    for (i in 1:length(self$stages)) {
      x <- self$stages((i))(x)
      features((length(features) + 1)) <- x
    }
    features
  }
)

Décodeur

Le décodeur est hybride de blocs configurables. Un carnet reçoit paire tenseurs d’initiation : un qui est le bout de l’méditation du carnet de décodeur imitation et un qui contient la dessin de caractéristiques produite là-dedans l’échelonné d’encodeur coïncident. Pendant le alinéa environs l’entrée, le rudimentaire est d’entour suréchantillonné et passé à défaut une non-linéarité. Le bout entremetteur est derrière adventice originellement du moindre exposé, la dessin de caractéristiques canalisées. Sur le tenseur consécutif, une convolution est appliquée, suivie d’une divergent non-linéarité.

decoder_block <- nn_module(
  
  initialize = function(in_channels, skip_channels, out_channels) {
    self$upsample <- nn_conv_transpose2d(
      in_channels = in_channels,
      out_channels = out_channels,
      kernel_size = 2,
      stride = 2
    )
    self$poussée <- nn_relu()
    self$conv <- nn_conv2d(
      in_channels = out_channels + skip_channels,
      out_channels = out_channels,
      kernel_size = 3,
      padding = "same"
    )
  },
  forward = function(x, skip) {
    x <- x %>%
      self$upsample() %>%
      self$poussée()

    input <- torch_cat(list(x, skip), dim = 2)

    input %>%
      self$conv() %>%
      self$poussée()
  }
)

Le décodeur celui-là “congruent” instancie et parcourt les blocs :

decoder <- nn_module(
  
  initialize = function(
    decoder_channels = c(256, 128, 64, 32, 16),
    encoder_channels = c(16, 24, 32, 96, 320)
  ) {

    encoder_channels <- rev(encoder_channels)
    skip_channels <- c(encoder_channels(-1), 3)
    in_channels <- c(encoder_channels(1), decoder_channels)

    depth <- length(encoder_channels)

    self$blocks <- nn_module_list()
    for (i in seq_len(depth)) {
      self$blocks$append(decoder_block(
        in_channels = in_channels(i),
        skip_channels = skip_channels(i),
        out_channels = decoder_channels(i)
      ))
    }

  },
  forward = function(features) {
    features <- rev(features)
    x <- features((1))
    for (i in seq_along(self$blocks)) {
      x <- self$blocks((i))(x, features((i+1)))
    }
    x
  }
)

Rythmé de étiage commandant

Bref, le norme de étiage commandant génère le classement de la éclat. Pendant à nous tâche, il existe triade classes de pixels. Le sous-module ouvrier de répartition peut cependant n’nature qu’une convolution dénouement, échéant triade canaux :

model <- nn_module(
  
  initialize = function() {
    self$chiffrer <- chiffrer()
    self$decoder <- decoder()
    self$produit <- nn_sequential(
      nn_conv2d(in_channels = 16,
                out_channels = 3,
                kernel_size = 3,
                padding = "same")
    )
  },
  forward = function(x) {
    x %>%
      self$chiffrer() %>%
      self$decoder() %>%
      self$produit()
  }
)

Instruction sur modèle et côte (visuelle)

Pour luzla éducation de modèle est une obstacle de paire verbes, setup() et fit(). Le pourcentage d’habitude a été résolu, derrière ce cas concis, à l’manoeuvre de luz::lr_finder(); toi-même devrez potentiellement le échanger pendant toi-même expérimenterez singulières formes d’précipitation de éventualité (et plusieurs ensembles de éventualité).

model <- model %>%
  setup(optimizer = optim_adam, loss = nn_cross_entropy_loss())

fitted <- model %>%
  set_opt_hparams(lr = 1e-3) %>%
  fit(train_dl, epochs = 10, valid_data = valid_dl)

Voilà un déterré de l’courant des performances d’aperçu là-dedans mon cas :

# Epoch 1/10
# Canter metrics: Loss: 0.504                                                           
# Valid metrics: Loss: 0.3154

# Epoch 2/10
# Canter metrics: Loss: 0.2845                                                           
# Valid metrics: Loss: 0.2549

...
...

# Epoch 9/10
# Canter metrics: Loss: 0.1368                                                           
# Valid metrics: Loss: 0.2332

# Epoch 10/10
# Canter metrics: Loss: 0.1299                                                           
# Valid metrics: Loss: 0.2511

Les chiffres ne sont que des chiffres – là-dedans lequel parcimonieux le modèle formé est-il incontestablement fort derrière sectionner les images d’élevage de congrégation ? Verso le hellénisme, nous-même générons des masques de éparpillement derrière les huit premières notes de l’collection de affirmation et les traçons en empilement sur les images. Un entremise fidèle de marquer une emblème et de échelonner un abri est ordonné par le raster emboîter.

Les intensités de pixels doivent nature comprises convaincu caducité et un, c’est ainsi là-dedans le wrapper de l’collection de éventualité, nous-même avons anecdote en nature que la progressivité puisse nature désactivée. Verso marquer les images réelles, nous-même instancions nettement un duplicata de valid_ds qui entrave les latitudes de point inchangées. (Les prédictions, en retour, devront régulièrement nature obtenues à décamper de l’collection de affirmation d’entrée.)

valid_ds_4plot <- pet_dataset(
  root = dir,
  split = "valid",
  size = c(224, 224),
  normalize = FALSE
)

Bref, les prédictions sont générées en bouclette et superposées sur les images une par une :

indices <- 1:8

preds <- predict(fitted, dataloader(dataset_subset(valid_ds, indices)))

png("pet_segmentation.png", width = 1200, height = 600, bg = "black")

par(mfcol = c(2, 4), mar = rep(2, 4))

for (i in indices) {
  
  mask <- as.array(torch_argmax(preds(i,..), 1)$to(device = "cpu"))
  mask <- raster::ratify(raster::raster(mask))
  
  img <- as.array(valid_ds_4plot(i)((1))$permute(c(2,3,1)))
  cond <- img > 0.99999
  img(cond) <- 0.99999
  img <- raster::voilier(img)
  
  # plot emblème
  raster::plotRGB(img, scale = 1, asp = 1, margins = TRUE)
  # overlay mask
  plot(mask, début = 0.4, legend = FALSE, pivots = FALSE, add = TRUE)
  
}
Masques de segmentation appris, superposés sur les images du jeu de validation.

Passons soutenant à l’agencement de ce modèle “là-dedans la complexion” (finalement, en parce que nature).

JIT-trace et agencement sur Android

Le dessin du modèle formé le convertira en un codex pouvant nature chargé là-dedans des environnements rien R, par aperçu à décamper de Serpent, C++ ou Fête.

On accède au torch modèle subjacent à l’fripe luz partie, et tracez-le – où marquer signifie l’hurler une coup, sur un parfait d’glose :

m <- fitted$model
x <- coro::collect(train_dl, 1)

traced <- jit_trace(m, x((1))$x)

Le modèle tracé peut soutenant nature numéroté derrière nature employé verso Serpent ou C++, puis cela :

traced %>% jit_save("traced_model.pt")

Néanmoins, puis nous-même savons déjà que nous-même aimerions le dérouler sur Android, nous-même utilisons préférablement la empile spécialisée jit_save_for_mobile() qui, en surtout, génère un bytecode :

# need torch > 0.6.1
jit_save_for_mobile(traced_model, "model_bytecode.pt")

Et c’est total derrière le côté R !

Verso jouer sur Android, j’ai anecdote un fashion excessif des exemples d’applications Android de PyTorch Forain, en extraordinaire icelui de éparpillement d’emblème.

Le justice de renouvellement de forme précis derrière ce conférence (qui a été employé derrière procréer l’emblème ci-après) peut nature trouvé ici : https://github.com/skeydan/ImageSegmentation. (Application, c’est ma initiale méditation Android !).

Apanage sûr, nous-même nécessitons mieux risquer de deviner le minet. Voilà le modèle, complet sur un émulateur d’chasseur là-dedans Android Logis, sur triade images (de l’Oxford Pet Dataset) sélectionnées derrière, originellement, une abondant série de difficultés, et secondement, eh propre… derrière la affabilité :

Où est mon chat ?

Miséricorde d’capital lu!

Parkhi, Omkar M., Andrea Vedaldi, Andrew Zisserman et CV Jawahar. 2012. “Les chats et les chiens.” Pendant Assemblée IEEE sur la berlue par robot et la gratitude de formes.

Ronneberger, Olaf, Philipp Fischer et Thomas Brox. 2015. “U-Net : Réseaux convolutifs derrière la éparpillement d’images biomédicales.” CoRR abs/1505.04597. http://arxiv.org/abs/1505.04597.

By nsmaat