Privilège sûr, c’est diligent lorsque j’ai une idéal d’un section, et un lacis de neurones peut me attestation de lesquelles typique d’section il s’agit. De calculé surtout efficace, il pourrait y capital hétérogènes objets saillants comme cette idéal, et ceci me dit ce qu’ils sont et où ils se trouvent. Cette dernière tâche (accueillie découverte d’section) semble surtout prototypique des applications contemporaines de l’IA qui sont à la coup psychologiquement fascinantes et éthiquement discutables. C’est singulier alors le partie de ce post : Mérité division d’images a à satiété d’applications indiscutablement utiles. Par original, c’est une modalité sine qua non en remède, en neurosciences, en génétique et comme d’différents sciences de la vie.

Après, qu’est-ce que la division d’idéal, techniquement, et hein pouvons-nous civiliser un lacis de neurones vers le commettre ?

La division d’images en commun

Supposons que moi-même ayons une idéal alors un totalisé de chats. Comme catégorisation, la chapitre est “qu’est-ce que c’est?” et la bulle que moi-même voulons percevoir est : “matou”. Comme découverte d’sectionmoi-même demandons à frais “qu’est-ce que c’est”, simplement actuellement que “à laquelle” est tacitement au multiple, et moi-même attendons une bulle également “il y a un matou, un matou et un matou, et ils sont ici, ici et ici” (imaginez le lacis marquant, au médiation du volute boîtes englobantes, c’est-à-dire des rectangles tout autour des objets détectés). Comme divisionmoi-même en voulons surtout : moi-même voulons que toute l’idéal O.K. recouverte de “boîtes” – qui ne sont surtout des boîtes, simplement des unions de “boîtes” de la hauteur d’un point – ou différemment : Nous-même voulons que le lacis écriteau tout point de l’idéal.

Voilà un original tendu du écrit lesquels moi-même allons babiller comme un minute. Sur la balourd se trouve l’idéal d’accès (cellules HeLa), puis la adage position, et la étranger est le tromperie de division enseigné.


Exemple de segmentation de Ronneberger et al.  2015.

Armes 1 : Prototype de division de Ronneberger et al. 2015.

Techniquement, une joliesse est faite compris division de espèce et division des instances. Comme la division de espèce, en se référant à l’original du « totalisé de chats », il existe un duo de affiches possibles : tout point est O.K. « matou », O.K. « pas de matou ». La division des instances est surtout acerbe : Ici, tout matou reçoit sa apte écriteau. (Sinon dit en marcheur, ainsi ceci devrait-il dépendre surtout acerbe ? En pieux une conscience de typique compréhensif, ce ne serait pas le cas – si j’ai le image d’un matou, au emplacement de explicitement “tendre”, je “accomplis” qu’il y a un duo de chats, pas 1. Purement subséquent ce sur à laquelle repose le surtout un lacis de neurones particulier – texture, coloris, parties isolées – ces offices peuvent reporter en masse en empêchement.)

L’charpente lacis utilisée comme cet entrefilet est adéquate vers division de espèce offices et devrait s’apposer à un seigneur afflux d’applications oeuvres, scientifiques et non scientifiques. En éloquent d’charpente lacis, à à laquelle devrait-elle correspondre ?

Prodrome de U-Net

Liste délié de à elles grâce comme la catégorisation d’images, ne peut-on pas explicitement tendre une charpente coutumier également Fécondation V(n), ResNet, ResSuivant … , peu importe? Le embarras est que à nous tâche à réaliser – ranger tout point – ne correspond pas si diligent à l’aperçu coutumier d’un CNN. Comme les convnets, l’aperçu est d’apposer des travail successives de convolution et de pooling vers fonder des feature maps de granularité décroissante, vers bref apparaître à un palier obscur où l’on se contente de attestation : « ouais, un matou ». La rançon présentant, on perd des informations détaillées : Vers le hiérarchie comble, peu importe que les cinq pixels de la emplacement en dominant à balourd soient noirs ou purs.

En manoeuvre, les architectures classiques utilisent le (max) pooling ou les convolutions alors stride > 1 vers concevoir ces abstractions successives – alléchant inévitablement une réduction de la audace spatiale. Après, hein pouvons-nous tendre un convnet globalité en préservant les informations détaillées ? Comme à elles entrefilet de 2015 U-Net : Réseaux convolutifs vers la division d’images biomédicales (Ronneberger, Fischer et Brox 2015), Olaf Ronneberger et al. est survenu alors ce qui, quatre ans surtout tard, en 2019, est assidûment l’approximatif la surtout apprécié. (Ce qui veut attestation tout truc, quatre ans, c’est immense, comme l’dégrossissage en grosseur.)

L’aperçu est étonnamment faible. Après que les étapes successives d’codification (convolution / max pooling), également d’insensibilisation, réduisent la audace, le décodage prochain – moi-même endettons apparaître à une incartade de hauteur reproduit à l’accès, car moi-même voulons ranger tout point ! – ne suréchantillonne pas explicitement à sauvagement de la catégorie la surtout compressée. Au emplacement de ceci, alors du suréchantillonnage, à tout règne, moi-même alimentons les informations de la catégorie informatrice, en audace, comme la clôture de atrophie des effectifs.

Vers U-Net, une idéal en dit réellement surtout que à satiété de mots :


Architecture U-Net de Ronneberger et al.  2015.

Armes 2 : Structure U-Net de Ronneberger et al. 2015.

A tout règne de suréchantillonnage, moi-même dominer la incartade de la catégorie précédente alors icelle de son semblable comme l’mezzanine de entassement. La incartade conclusion est un tromperie de hauteur l’idéal propre, posséder par convolution 1×1 ; aucune catégorie pâteux conclusion n’est requise, à la activité la catégorie de incartade est adapté une catégorie convolutive alors un séparé amortisseur.

Entraînons actuellement un U-Net. Nous-même allons tendre le unet produit qui toi-même permet de bâtir un modèle adéquat en une propre cordon :

remotes::install_github("r-tensorflow/unet")
library(unet)

# takes additional parameters, including number of downsizing blocks, 
# number of filters to start with, and number of classes to identify
# see ?unet for more information
model <- unet(input_shape = c(128, 128, 3))

Nous-même avons alors un modèle, et il semble que moi-même voudrons lui adjuger des images RVB 128×128. Retenant, hein obtient-on ces images ?

Les occasion

Vers expliquer hein les applications se présentent même en abord du fief de la préparatif médicale, moi-même utiliserons également original le Kaggle Carvana Métaphore Masking Tournoi. La tâche consiste à bâtir un tromperie de division séparant les voitures de l’écarté. Vers à nous impartial nouveau, moi-même avons mais désir cantine.zip et train_mask.zip à sauvagement de l’dépôt buissonnante en téléchargement. Comme ce qui suit, moi-même supposons que iceux ont été recueil comme un sous-répertoire conscrit data-raw.

Voyons d’apparence divers images et à eux masques de division associés.

Les photos sont des JPEG à distant RVB, lors que les masques sont des GIF en salsifis et pâle.

Nous-même divisons les occasion en un association d’exemple et un association de homologation. Nous-même utiliserons celui-ci vers pister les performances de développement contre la équipe.

data <- tibble(
  img = list.files(here::here("data-raw/cantine"), full.names = TRUE),
  mask = list.files(here::here("data-raw/train_masks"), full.names = TRUE)
)

data <- initial_split(data, prop = 0.8)

Vers adresser les occasion au lacis, moi-même utiliserons tfdatasets. Intégrité le prétraitement se terminera comme un oléoduc faible, simplement moi-même allons d’apparence grimper en certificat les labeurs requises règne par règne.

Gazoduc de prétraitement

La dédicace règne consiste à deviner comme les images, en utilisant les épreuves appropriées comme tf$idéal.

training_dataset <- jogging(data) %>%  
  tensor_slices_dataset() %>% 
  dataset_map(~.x %>% list_modify(
    # decode_jpeg yields a 3d tensor of shape (1280, 1918, 3)
    img = tf$idéal$decode_jpeg(tf$io$read_file(.x$img)),
    # decode_gif yields a 4d tensor of shape (1, 1280, 1918, 3),
    # so we remove the unneeded batch largeur and all but one 
    # of the 3 (identical) channels
    mask = tf$idéal$decode_gif(tf$io$read_file(.x$mask))(1,,,)(,,1,drop=FALSE)
  ))

Donc de la élévation d’un oléoduc de prétraitement, il est formidablement notable de noter les résultats intermédiaires. C’est prévenant à commettre en utilisant reticulate::as_iterator sur le jeu de occasion :

$img
tf.Tensor(
(((243 244 239)
  (243 244 239)
  (243 244 239)
  ...
 ...
  ...
  (175 179 178)
  (175 179 178)
  (175 179 178))), shape=(1280, 1918, 3), dtype=uint8)

$mask
tf.Tensor(
(((0)
  (0)
  (0)
  ...
 ...
  ...
  (0)
  (0)
  (0))), shape=(1280, 1918, 1), dtype=uint8)

Comme que le uint8 Le typique de occasion rend les capacités RVB serviables à deviner vers les humains, le lacis va s’espérer à des nombres à virgule double. Le légalité consécutif convertit son accès et, en artificiel, met les capacités à l’recueil de l’espace (0,1) :

training_dataset <- training_dataset %>% 
  dataset_map(~.x %>% list_modify(
    img = tf$idéal$convert_image_dtype(.x$img, dtype = tf$float32),
    mask = tf$idéal$convert_image_dtype(.x$mask, dtype = tf$float32)
  ))

Vers raccourcir les coûts de détermination, moi-même redimensionnons les images à la hauteur 128x128. Ceci modifiera le profit d’tournure et alors déformera les images, simplement ce n’est pas un embarras alors l’association de occasion donné.

training_dataset <- training_dataset %>% 
  dataset_map(~.x %>% list_modify(
    img = tf$idéal$resize(.x$img, size = shape(128, 128)),
    mask = tf$idéal$resize(.x$mask, size = shape(128, 128))
  ))

Retenant, il est diligent révélé que comme l’dégrossissage en grosseur, l’agrandissement des occasion est tranchante. Vers la division, il y a une truc à observer, qui est de classicisme si une métamorphose doit pareillement dépendre appliquée au tromperie – ce serait le cas, par original, vers les rotations ou le amélioration. Ici, les résultats seront modérément altruistes en appliquant textuellement les transformations qui préservent les positions :

random_bsh <- function(img) {
  img %>% 
    tf$idéal$random_brightness(max_delta = 0.3) %>% 
    tf$idéal$random_contrast(lower = 0.5, upper = 0.7) %>% 
    tf$idéal$random_saturation(lower = 0.5, upper = 0.7) %>% 
    # make sure we still are between 0 and 1
    tf$clip_by_value(0, 1) 
}

training_dataset <- training_dataset %>% 
  dataset_map(~.x %>% list_modify(
    img = random_bsh(.x$img)
  ))

Davantage une coup, moi-même pouvons tendre as_iterator vers discerner ce que ces transformations font à nos images :

Voilà le oléoduc de prétraitement réalisé.

create_dataset <- function(data, cantine, batch_size = 32L) {
  
  dataset <- data %>% 
    tensor_slices_dataset() %>% 
    dataset_map(~.x %>% list_modify(
      img = tf$idéal$decode_jpeg(tf$io$read_file(.x$img)),
      mask = tf$idéal$decode_gif(tf$io$read_file(.x$mask))(1,,,)(,,1,drop=FALSE)
    )) %>% 
    dataset_map(~.x %>% list_modify(
      img = tf$idéal$convert_image_dtype(.x$img, dtype = tf$float32),
      mask = tf$idéal$convert_image_dtype(.x$mask, dtype = tf$float32)
    )) %>% 
    dataset_map(~.x %>% list_modify(
      img = tf$idéal$resize(.x$img, size = shape(128, 128)),
      mask = tf$idéal$resize(.x$mask, size = shape(128, 128))
    ))
  
  # data agrandissement performed on jogging set only
  if (cantine) {
    dataset <- dataset %>% 
      dataset_map(~.x %>% list_modify(
        img = random_bsh(.x$img)
      )) 
  }
  
  # shuffling on jogging set only
  if (cantine) {
    dataset <- dataset %>% 
      dataset_shuffle(buffer_size = batch_size*128)
  }
  
  # cantine in batches; batch size might need to be adapted depending on
  # available memory
  dataset <- dataset %>% 
    dataset_batch(batch_size)
  
  dataset %>% 
    # produit needs to be unnamed
    dataset_map(unname) 
}

La équipe et la ouvrage d’ensembles de critère ne sont surtout qu’une chapitre de un duo de avertisseurs de place.

training_dataset <- create_dataset(jogging(data), cantine = TRUE)
validation_dataset <- create_dataset(testing(data), cantine = FALSE)

Et moi-même totaux prêts à civiliser le modèle.

Établir le modèle

Nous-même avons déjà montré hein bâtir le modèle, simplement répétons-le ici et vérifions l’charpente du modèle :

model <- unet(input_shape = c(128, 128, 3))
summary(model)
Model: "model"
______________________________________________________________________________________________
Layer (typique)                   Produit Shape        Param #    Connected to                    
==============================================================================================
input_1 (InputLayer)           ((None, 128, 128, 3 0                                          
______________________________________________________________________________________________
conv2d (Conv2D)                (None, 128, 128, 64 1792       input_1(0)(0)                   
______________________________________________________________________________________________
conv2d_1 (Conv2D)              (None, 128, 128, 64 36928      conv2d(0)(0)                    
______________________________________________________________________________________________
max_pooling2d (MaxPooling2D)   (None, 64, 64, 64)  0          conv2d_1(0)(0)                  
______________________________________________________________________________________________
conv2d_2 (Conv2D)              (None, 64, 64, 128) 73856      max_pooling2d(0)(0)             
______________________________________________________________________________________________
conv2d_3 (Conv2D)              (None, 64, 64, 128) 147584     conv2d_2(0)(0)                  
______________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D) (None, 32, 32, 128) 0          conv2d_3(0)(0)                  
______________________________________________________________________________________________
conv2d_4 (Conv2D)              (None, 32, 32, 256) 295168     max_pooling2d_1(0)(0)           
______________________________________________________________________________________________
conv2d_5 (Conv2D)              (None, 32, 32, 256) 590080     conv2d_4(0)(0)                  
______________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D) (None, 16, 16, 256) 0          conv2d_5(0)(0)                  
______________________________________________________________________________________________
conv2d_6 (Conv2D)              (None, 16, 16, 512) 1180160    max_pooling2d_2(0)(0)           
______________________________________________________________________________________________
conv2d_7 (Conv2D)              (None, 16, 16, 512) 2359808    conv2d_6(0)(0)                  
______________________________________________________________________________________________
max_pooling2d_3 (MaxPooling2D) (None, 8, 8, 512)   0          conv2d_7(0)(0)                  
______________________________________________________________________________________________
dropout (Dropout)              (None, 8, 8, 512)   0          max_pooling2d_3(0)(0)           
______________________________________________________________________________________________
conv2d_8 (Conv2D)              (None, 8, 8, 1024)  4719616    dropout(0)(0)                   
______________________________________________________________________________________________
conv2d_9 (Conv2D)              (None, 8, 8, 1024)  9438208    conv2d_8(0)(0)                  
______________________________________________________________________________________________
conv2d_transpose (Conv2DTransp (None, 16, 16, 512) 2097664    conv2d_9(0)(0)                  
______________________________________________________________________________________________
concatenate (Concatenate)      (None, 16, 16, 1024 0          conv2d_7(0)(0)                  
                                                              conv2d_transpose(0)(0)          
______________________________________________________________________________________________
conv2d_10 (Conv2D)             (None, 16, 16, 512) 4719104    concatenate(0)(0)               
______________________________________________________________________________________________
conv2d_11 (Conv2D)             (None, 16, 16, 512) 2359808    conv2d_10(0)(0)                 
______________________________________________________________________________________________
conv2d_transpose_1 (Conv2DTran (None, 32, 32, 256) 524544     conv2d_11(0)(0)                 
______________________________________________________________________________________________
concatenate_1 (Concatenate)    (None, 32, 32, 512) 0          conv2d_5(0)(0)                  
                                                              conv2d_transpose_1(0)(0)        
______________________________________________________________________________________________
conv2d_12 (Conv2D)             (None, 32, 32, 256) 1179904    concatenate_1(0)(0)             
______________________________________________________________________________________________
conv2d_13 (Conv2D)             (None, 32, 32, 256) 590080     conv2d_12(0)(0)                 
______________________________________________________________________________________________
conv2d_transpose_2 (Conv2DTran (None, 64, 64, 128) 131200     conv2d_13(0)(0)                 
______________________________________________________________________________________________
concatenate_2 (Concatenate)    (None, 64, 64, 256) 0          conv2d_3(0)(0)                  
                                                              conv2d_transpose_2(0)(0)        
______________________________________________________________________________________________
conv2d_14 (Conv2D)             (None, 64, 64, 128) 295040     concatenate_2(0)(0)             
______________________________________________________________________________________________
conv2d_15 (Conv2D)             (None, 64, 64, 128) 147584     conv2d_14(0)(0)                 
______________________________________________________________________________________________
conv2d_transpose_3 (Conv2DTran (None, 128, 128, 64 32832      conv2d_15(0)(0)                 
______________________________________________________________________________________________
concatenate_3 (Concatenate)    (None, 128, 128, 12 0          conv2d_1(0)(0)                  
                                                              conv2d_transpose_3(0)(0)        
______________________________________________________________________________________________
conv2d_16 (Conv2D)             (None, 128, 128, 64 73792      concatenate_3(0)(0)             
______________________________________________________________________________________________
conv2d_17 (Conv2D)             (None, 128, 128, 64 36928      conv2d_16(0)(0)                 
______________________________________________________________________________________________
conv2d_18 (Conv2D)             (None, 128, 128, 1) 65         conv2d_17(0)(0)                 
==============================================================================================
Intégral params: 31,031,745
Trainable params: 31,031,745
Non-trainable params: 0
______________________________________________________________________________________________

La pilastre “habitué de incartade” étal arithmétiquement la habitué en U attendue : la hauteur et la altitude diminuent d’apparence, jusqu’à ce que moi-même atteignions une audace minimale de 8x8; ils remontent puis, jusqu’à ce que moi-même ayons accosté la audace d’introduction. Comme le même instant, le afflux de filtres bouffi d’apparence, avec redescend, jusqu’à ce que comme la catégorie de incartade, moi-même ayons un séparé amortisseur. Toi-même pouvez pareillement discerner le concatenate travail ajoutant des informations qui viennent « du bas » aux informations qui viennent « transversalement ».

Quoi devrait dépendre la place de dépense ici? Nous-même étiquetons tout point, alors tout point contribue à la dépense. Nous-même avons un embarras numérique – tout point peut dépendre “écart” ou “écarté” – moi-même voulons alors que tout incartade O.K. limitrophe de 0 ou 1. Ceci rend binar_crossentropy la place de dépense adéquate.

Suspendant l’exemple, moi-même gardons une seing de la sobriété de la catégorisation de la sorte que du ratio de dé, la poésie d’estimation utilisée comme la défi. Le ratio de dé est un médiation de rapprocher la pourcentage de classements probes :

dice <- custom_metric("dice", function(y_true, y_pred, smooth = 1.0) {
  y_true_f <- k_flatten(y_true)
  y_pred_f <- k_flatten(y_pred)
  coupure <- k_sum(y_true_f * y_pred_f)
  (2 * coupure + smooth) / (k_sum(y_true_f) + k_sum(y_pred_f) + smooth)
})

model %>% compile(
  optimizer = optimizer_rmsprop(lr = 1e-5),
  loss = "binary_crossentropy",
  metrics = list(dice, metric_binary_accuracy)
)

L’guenille du modèle prend un clair instant – quel nombre, diligent sûr, dépendra de votre positif. Purement l’surveillance susceptible ses fruits : ensuite cinq époques, moi-même avons évident un ratio de dé d’quasi 0,87 sur l’association de homologation et une sobriété d’quasi 0,95.

Prédictions

Privilège sûr, ce qui moi-même ligoté en fin de gain, ce sont les prédictions. Voyons divers masques générés vers les éléments du jeu de homologation :

batch <- validation_dataset %>% as_iterator() %>% iter_next()
predictions <- predict(model, batch)

images <- tibble(
  idéal = batch((1)) %>% array_branch(1),
  predicted_mask = predictions(,,,1) %>% array_branch(1),
  mask = batch((2))(,,,1)  %>% array_branch(1)
) %>% 
  sample_n(2) %>% 
  map_depth(2, function(x) {
    as.raster(x) %>% magick::image_read()
  }) %>% 
  map(~do.call(c, .x))


out <- magick::image_append(c(
  magick::image_append(images$mask, stack = TRUE),
  magick::image_append(images$idéal, stack = TRUE), 
  magick::image_append(images$predicted_mask, stack = TRUE)
  )
)

plot(out)

De gauche à droite : vérité terrain, image d'entrée et masque prédit de U-Net.

Armes 3 : De balourd à cordiale : adage position, idéal d’accès et tromperie promis de U-Net.

Situation

S’il y avait un défi vers la paie la surtout digue d’confort et de nitescence architecturale, U-Net serait immanquablement un participant. Rien à satiété de ajustement, il est conciliable d’conserver des résultats décents. Si toi-même êtes en grosseur d’tendre ce modèle comme votre couches, ou si toi-même rencontrez des problèmes vers l’tendre, faites-le moi-même classicisme ! Miséricorde d’capital lu!

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

By nsmaat