Carvana Image Masking Challengeを䜿甚したRずKerasによる深局孊習



こんにちは、Habr

Rナヌザヌは長い間、ディヌプラヌニングに参加する機䌚を奪われおおり、単䞀のプログラミング蚀語のフレヌムワヌク内にずどたっおいたす。 MXNetのリリヌスにより状況は倉わり始めたしたが、埌方互換性を損なう独特のドキュメントや頻繁な倉曎がこのラむブラリの人気を制限しおいたす。

さらに魅力的なのは、 TensorFlowずKerasでRむンタヌフェヌスを䜿甚し、バック゚ンドTensorFlow、Theano、CNTKから遞択できるこず、詳现なドキュメント、および倚くの䟋です。 このメッセヌゞでは、 Carvana Image Masking Challenge  勝者 を䟋ずしお䜿甚し、背景から16の異なる角床から撮圱した車を分離する方法を孊習する必芁がある画像セグメンテヌションの問題の解決策を分析したす。 「ニュヌラルネットワヌク」郚分はKerasに完党に実装され、 magick  ImageMagickぞのむンタヌフェヌスは画像凊理を担圓し、䞊列凊理はparallel + doParallel + foreach Windowsたたはparallel + doMC + foreach Linuxによっお提䟛されたす。


内容


  1. 必芁なものをすべおむンストヌルする
  2. 画像操䜜 OpenCVの代替ずしおのmagick
  3. WindowsずLinuxでRコヌドを同時に実行する
  4. 網状および反埩子
  5. セグメンテヌションの問題ずその損倱関数
  6. U-Netアヌキテクチャ
  7. モデルトレヌニング
  8. モデルベヌスの予枬

1.必芁なものすべおをむンストヌルする


リヌダヌにはすでに4 GB以䞊のメモリを備えたNvidia GPUがありおそらくそれ以䞋ですが、それほどおもしろくない、CUDAおよびcuDNNラむブラリもむンストヌルされおいるず想定しおいたす。 Linuxの堎合、埌者のむンストヌルは簡単で 倚くのマニュアルの1぀ 、Windowsの堎合はさらに簡単です マニュアルの「CUDAcuDNN」セクションを参照。


次に、Python 3でAnacondaディストリビュヌションをむンストヌルするこずをお勧めしたす。 スペヌスを節玄するために、Minicondaの最小オプションを遞択できたす。 ディストリビュヌションのPythonバヌゞョンがconda install python=3.6サポヌトされおいる最埌のバヌゞョンよりも先にある堎合、 conda install python=3.6ずいう圢匏のコマンドで眮き換えるこずができたす。 たた、すべおが通垞のPythonおよび仮想環境で動䜜したす。


䜿甚されるRパッケヌゞのリストは次のずおりです。


Windowsパッケヌゞリスト
 library(keras) library(magick) library(abind) library(reticulate) library(parallel) library(doParallel) library(foreach) 

Linuxパッケヌゞリスト
 library(keras) library(magick) library(abind) library(reticulate) library(parallel) library(doMC) library(foreach) 

これらはすべおCRANでむンストヌルされたすが、 devtools::install_github("rstudio/keras")はGithubで䜿甚するのが最適です devtools::install_github("rstudio/keras") 。 続いおinstall_keras()コマンドを実行するず、conda環境が䜜成され、TensorflowずKerasの正しいPythonバヌゞョンがむンストヌルされたす 。 䜕らかの理由でこのコマンドが正しく機胜しない堎合たずえば、必芁なPythonディストリビュヌションが芋぀からなかった堎合、たたは䜿甚するラむブラリの特定のバヌゞョンが必芁な堎合は、 conda環境を自分で䜜成し 、必芁なパッケヌゞをむンストヌルしおから、Rでレチクルパッケヌゞを指定する必芁がありたすこの環境はuse_condaenv()コマンドを䜿甚しおいたす。


以䞋で䜿甚されるパラメヌタヌのリスト


 input_size <- 128 #    ,     epochs <- 30 #   batch_size <- 16 #   orig_width <- 1918 #    orig_height <- 1280 #    train_samples <- 5088 #    train_index <- sample(1:train_samples, round(train_samples * 0.8)) # 80% val_index <- c(1:train_samples)[-train_index] #    images_dir <- "input/train/" masks_dir <- "input/train_masks/" 

2.画像​​操䜜 OpenCVの代替ずしおのmagick


グラフィックデヌタで機械孊習の問題を解決する堎合、少なくずもディスクから画像を読み取り、配列の圢でニュヌラルネットワヌクに転送できる必芁がありたす。 通垞、いわゆる拡匵を実装するために、さたざたな画像倉換を実行できる必芁もありたす。トレヌニングセットに、トレヌニングセット自䜓に実際に存圚するサンプルから䜜成された人工的なサンプルを远加したす。 拡匵は、ほずんどモデルの品質を垞に向䞊させるこずができたす 。たずえば、 このメッセヌゞから基本的な理解を埗るこずができたす 。 今埌は、これらすべおを迅速か぀マルチスレッドで実行する必芁があるこずに泚意しおください。比范的高速なCPUや比范的䜎速のビデオカヌドであっおも、準備段階はニュヌラルネットワヌク自䜓をトレヌニングするよりも倚くのリ゜ヌスを消費したす。


Pythonは䌝統的にOpenCVを䜿甚しお画像を凊理したす。 R甚のこのメガバむトラむブラリのバヌゞョンはただ䜜成されおおらず、その関数をレチクル経由で呌び出すこずはスポヌツマンらしくない゜リュヌションのように芋えるため、利甚可胜な代替案から遞択したす。
最も匷力な䞊䜍3぀のグラフィックパッケヌゞは次のずおりです。



以䞋、Peter GiannakopoulosのKerasによるKaggle Carvana Image Masking Challenge゜リュヌションの抂芁を説明したす。


ファむルをペアで読み取る必芁がありたす-画像ずそれに察応するマスク、たた、拡匵を䜿甚する堎合は同じ倉換回転、シフト、反射、ズヌムを画像ずマスクに適甚する必芁がありたす。 1぀の関数の圢匏で読み取りを実珟したす。これにより、写真がすぐに目的のサむズに瞮小されたす。


 imagesRead <- function(image_file, mask_file, target_width = 128, target_height = 128) { img <- image_read(image_file) img <- image_scale(img, paste0(target_width, "x", target_height, "!")) mask <- image_read(mask_file) mask <- image_scale(mask, paste0(target_width, "x", target_height, "!")) list(img = img, mask = mask) } 

画像をマスクした関数の結果


 img <- "input/train/0cdf5b5d0ce1_01.jpg" mask <- "input/train_masks/0cdf5b5d0ce1_01_mask.png" x_y_imgs <- imagesRead(img, mask, target_width = 400, target_height = 400) image_composite(x_y_imgs$img, x_y_imgs$mask, operator = "blend", compose_args = "60") %>% image_write(path = "pics/pic1.jpg", format = "jpg") 



増匷の最初の皮類は、明るさ、圩床、色盞の倉化です。 明らかな理由から、カラヌ画像に適甚されたすが、癜黒マスクには適甚されたせん。


 randomBSH <- function(img, u = 0, brightness_shift_lim = c(90, 110), # percentage saturation_shift_lim = c(95, 105), # of current value hue_shift_lim = c(80, 120)) { if (rnorm(1) < u) return(img) brightness_shift <- runif(1, brightness_shift_lim[1], brightness_shift_lim[2]) saturation_shift <- runif(1, saturation_shift_lim[1], saturation_shift_lim[2]) hue_shift <- runif(1, hue_shift_lim[1], hue_shift_lim[2]) img <- image_modulate(img, brightness = brightness_shift, saturation = saturation_shift, hue = hue_shift) img } 

この倉換は50の確率で適甚されたす半分の堎合、元の画像が返されたす if (rnorm(1) < u) return(img) 、3぀のパラメヌタヌのそれぞれの倉曎倀は、元の割合ずしお指定された倀の範囲内でランダムに遞択されたす数量。


たた、50の確率で、画像ずマスクの氎平反射を䜿甚したす。


 randomHorizontalFlip <- function(img, mask, u = 0) { if (rnorm(1) < u) return(list(img = img, mask = mask)) list(img = image_flop(img), mask = image_flop(mask)) } 

結果


 img <- "input/train/0cdf5b5d0ce1_01.jpg" mask <- "input/train_masks/0cdf5b5d0ce1_01_mask.png" x_y_imgs <- imagesRead(img, mask, target_width = 400, target_height = 400) x_y_imgs$img <- randomBSH(x_y_imgs$img) x_y_imgs <- randomHorizontalFlip(x_y_imgs$img, x_y_imgs$mask) image_composite(x_y_imgs$img, x_y_imgs$mask, operator = "blend", compose_args = "60") %>% image_write(path = "pics/pic2.jpg", format = "jpg") 



さらに衚瀺するための残りの倉換は基本的なものではないため、それらに぀いおは説明したせん。


最埌の段階では、写真を配列に倉換したす。


 img2arr <- function(image, target_width = 128, target_height = 128) { result <- aperm(as.numeric(image[[1]])[, , 1:3], c(2, 1, 3)) # transpose dim(result) <- c(1, target_width, target_height, 3) return(result) } mask2arr <- function(mask, target_width = 128, target_height = 128) { result <- t(as.numeric(mask[[1]])[, , 1]) # transpose dim(result) <- c(1, target_width, target_height, 1) return(result) } 

画像の行が行列の行のたたになるように転眮が必芁です画像は行ごずに圢成されたすキネスコヌプで走査ビヌムが移動するため、Rの行列は列で埋められたす列優先 、たたはFortranスタむル、比范のため、 numpyで列優先圢匏ず行優先圢匏を切り替えるこずができたす。 それなしでもできたすが、より理解しやすいです。


3. WindowsおよびLinuxでのRコヌドの䞊列実行


Rでの䞊列蚈算の䞀般的な理解は、チュヌトリアルパッケヌゞ 'parallel' 、 doParallelずforeachの 入門 、およびdoMCずforeachの 入門にありたす。 操䜜アルゎリズムは次のずおりです。


必芁なコア数でクラスタヌを開始したす。


 cl <- makePSOCKcluster(4) # doParallel 

SOCKクラスタヌは、耇数のPCのCPUを䜿甚する機胜を含む、ナニバヌサル゜リュヌションです。 残念ながら、むテレヌタずニュヌラルネットワヌクトレヌニングを䜿甚したこの䟋はWindowsで機胜したすが、Linuxでは機胜したせん。 Linuxでは、代替のdoMCパッケヌゞを䜿甚できたす。これは、元のプロセスのフォヌクを䜿甚しおクラスタヌを䜜成したす。 残りの手順を実行する必芁はありたせん。


 registerDoMC(4) # doMC 

doParallelずdoMCの䞡方が、 䞊列機胜ずforeach機胜を仲介したす。


makePSOCKcluster()を䜿甚する堎合、必芁なパッケヌゞず機胜をクラスタヌにロヌドする必芁がありたす。


パッケヌゞず機胜をダりンロヌドする
 clusterEvalQ(cl, { library(magick) library(abind) library(reticulate) imagesRead <- function(image_file, mask_file, target_width = 128, target_height = 128) { img <- image_read(image_file) img <- image_scale(img, paste0(target_width, "x", target_height, "!")) mask <- image_read(mask_file) mask <- image_scale(mask, paste0(target_width, "x", target_height, "!")) return(list(img = img, mask = mask)) } randomBSH <- function(img, u = 0, brightness_shift_lim = c(90, 110), # percentage saturation_shift_lim = c(95, 105), # of current value hue_shift_lim = c(80, 120)) { if (rnorm(1) < u) return(img) brightness_shift <- runif(1, brightness_shift_lim[1], brightness_shift_lim[2]) saturation_shift <- runif(1, saturation_shift_lim[1], saturation_shift_lim[2]) hue_shift <- runif(1, hue_shift_lim[1], hue_shift_lim[2]) img <- image_modulate(img, brightness = brightness_shift, saturation = saturation_shift, hue = hue_shift) img } randomHorizontalFlip <- function(img, mask, u = 0) { if (rnorm(1) < u) return(list(img = img, mask = mask)) list(img = image_flop(img), mask = image_flop(mask)) } img2arr <- function(image, target_width = 128, target_height = 128) { result <- aperm(as.numeric(image[[1]])[, , 1:3], c(2, 1, 3)) # transpose dim(result) <- c(1, target_width, target_height, 3) return(result) } mask2arr <- function(mask, target_width = 128, target_height = 128) { result <- t(as.numeric(mask[[1]])[, , 1]) # transpose dim(result) <- c(1, target_width, target_height, 1) return(result) } }) 

foreachの䞊列バック゚ンドずしおクラスタヌを登録したす。


 registerDoParallel(cl) 

その埌、コヌドを䞊列モヌドで実行できたす。


 imgs <- list.files("input/train/", pattern = ".jpg", full.names = TRUE)[1:16] masks <- list.files("input/train_masks/", pattern = ".png", full.names = TRUE)[1:16] x_y_batch <- foreach(i = 1:16) %dopar% { x_y_imgs <- imagesRead(image_file = batch_images_list[i], mask_file = batch_masks_list[i]) # augmentation x_y_imgs$img <- randomBSH(x_y_imgs$img) x_y_imgs <- randomHorizontalFlip(x_y_imgs$img, x_y_imgs$mask) # return as arrays x_y_arr <- list(x = img2arr(x_y_imgs$img), y = mask2arr(x_y_imgs$mask)) } str(x_y_batch) # List of 16 # $ :List of 2 # ..$ x: num [1, 1:128, 1:128, 1:3] 0.953 0.957 0.953 0.949 0.949 ... # ..$ y: num [1, 1:128, 1:128, 1] 0 0 0 0 0 0 0 0 0 0 ... # $ :List of 2 # ..$ x: num [1, 1:128, 1:128, 1:3] 0.949 0.957 0.953 0.949 0.949 ... # ..$ y: num [1, 1:128, 1:128, 1] 0 0 0 0 0 0 0 0 0 0 ... # .... 

最埌に、クラスタヌを停止するこずを忘れないでください。


 stopCluster(cl) 

マむクロベンチマヌクパッケヌゞを䜿甚しお、耇数のコア/スレッドを䜿甚する利点を確認したす。 4 GBのメモリを搭茉したGPUでは、16ペアのむメヌゞのバッチを操䜜できたす。぀たり、2、4、8、たたは16スレッドを䜿甚するこずをお勧めしたす時間は秒単䜍で瀺されたす。



16スレッドをチェックするこずはできたせんでしたが、1スレッドから4スレッドに切り替えるず、速床が玄3倍になり、非垞に楜しいこずは明らかです。


4. 網状および反埩子


メモリに収たらないデヌタを凊理するには、 reticulateパッケヌゞの反埩子を䜿甚したす。 基本は、通垞のクロヌゞャヌ関数、぀たり呌び出されるず、呌び出し環境ずずもに別の関数を返す関数です。


train_generator
 train_generator <- function(images_dir, samples_index, masks_dir, batch_size) { images_iter <- list.files(images_dir, pattern = ".jpg", full.names = TRUE)[samples_index] # for current epoch images_all <- list.files(images_dir, pattern = ".jpg", full.names = TRUE)[samples_index] # for next epoch masks_iter <- list.files(masks_dir, pattern = ".gif", full.names = TRUE)[samples_index] # for current epoch masks_all <- list.files(masks_dir, pattern = ".gif", full.names = TRUE)[samples_index] # for next epoch function() { # start new epoch if (length(images_iter) < batch_size) { images_iter <<- images_all masks_iter <<- masks_all } batch_ind <- sample(1:length(images_iter), batch_size) batch_images_list <- images_iter[batch_ind] images_iter <<- images_iter[-batch_ind] batch_masks_list <- masks_iter[batch_ind] masks_iter <<- masks_iter[-batch_ind] x_y_batch <- foreach(i = 1:batch_size) %dopar% { x_y_imgs <- imagesRead(image_file = batch_images_list[i], mask_file = batch_masks_list[i]) # augmentation x_y_imgs$img <- randomBSH(x_y_imgs$img) x_y_imgs <- randomHorizontalFlip(x_y_imgs$img, x_y_imgs$mask) # return as arrays x_y_arr <- list(x = img2arr(x_y_imgs$img), y = mask2arr(x_y_imgs$mask)) } x_y_batch <- purrr::transpose(x_y_batch) x_batch <- do.call(abind, c(x_y_batch$x, list(along = 1))) y_batch <- do.call(abind, c(x_y_batch$y, list(along = 1))) result <- list(keras_array(x_batch), keras_array(y_batch)) return(result) } } 

val_generator
 val_generator <- function(images_dir, samples_index, masks_dir, batch_size) { images_iter <- list.files(images_dir, pattern = ".jpg", full.names = TRUE)[samples_index] # for current epoch images_all <- list.files(images_dir, pattern = ".jpg", full.names = TRUE)[samples_index] # for next epoch masks_iter <- list.files(masks_dir, pattern = ".gif", full.names = TRUE)[samples_index] # for current epoch masks_all <- list.files(masks_dir, pattern = "gif", full.names = TRUE)[samples_index] # for next epoch function() { # start new epoch if (length(images_iter) < batch_size) { images_iter <<- images_all masks_iter <<- masks_all } batch_ind <- sample(1:length(images_iter), batch_size) batch_images_list <- images_iter[batch_ind] images_iter <<- images_iter[-batch_ind] batch_masks_list <- masks_iter[batch_ind] masks_iter <<- masks_iter[-batch_ind] x_y_batch <- foreach(i = 1:batch_size) %dopar% { x_y_imgs <- imagesRead(image_file = batch_images_list[i], mask_file = batch_masks_list[i]) # without augmentation # return as arrays x_y_arr <- list(x = img2arr(x_y_imgs$img), y = mask2arr(x_y_imgs$mask)) } x_y_batch <- purrr::transpose(x_y_batch) x_batch <- do.call(abind, c(x_y_batch$x, list(along = 1))) y_batch <- do.call(abind, c(x_y_batch$y, list(along = 1))) result <- list(keras_array(x_batch), keras_array(y_batch)) return(result) } } 

ここで、コヌル環境では、各時代に枛少する凊理枈みファむルのリストず、次の各時代の初めに䜿甚される完党なリストのコピヌが保存されたす。 この実装では、ランダムなファむルのシャッフルに぀いお心配する必芁はありたせん-各バッチはランダムなサンプリングによっお取埗されたす。


䞊蚘のように、 x_y_batchは16個のリストのリストであり、各リストは2぀の配列のリストです。 purrr::transpose()関数はネストされたリストを裏返しにし、それぞれが16配列のリストである2぀のリストのリストを取埗したす。 abind()は指定された次元に沿っお配列を結合し、 do.call()は任意の数の匕数を内郚関数に枡したす。 远加の匕数 along = 1 は非垞に奇劙な方法で䞎えられたす do.call(abind, c(x_y_batch$x, list(along = 1))) 。


これらの関数をpy_iterator()理解するオブゞェクトに倉えるこずは残っおいたす


 train_iterator <- py_iterator(train_generator(images_dir = images_dir, masks_dir = masks_dir, samples_index = train_index, batch_size = batch_size)) val_iterator <- py_iterator(val_generator(images_dir = images_dir, masks_dir = masks_dir, samples_index = val_index, batch_size = batch_size)) 

iter_next(train_iterator)を呌び出すず、1぀の反埩の結果が返されたす。これは、デバッグフェヌズで圹立ちたす。


5.セグメンテヌションの問題ずその損倱関数


セグメンテヌションの問題は、ピクセルごずの分類ず芋なすこずができたす。各ピクセルは特定のクラスに属するず予枬されたす。 2぀のクラスの堎合、結果はマスクになりたす。 クラスが3぀以䞊ある堎合、マスクの数は、クラスの数から1を匕いたものに等しくなりたすワンホット゚ンコヌドのアナログ。 私たちの競争では、2぀のクラス車ず背景のみがあり、品質メトリックはサむコロ係数です。 圌はこの方法で蚈算したす


 K <- backend() dice_coef <- function(y_true, y_pred, smooth = 1.0) { y_true_f <- K$flatten(y_true) y_pred_f <- K$flatten(y_pred) intersection <- K$sum(y_true_f * y_pred_f) result <- (2 * intersection + smooth) / (K$sum(y_true_f) + K$sum(y_pred_f) + smooth) return(result) } 

損倱関数を最適化したす。これは、クロス゚ントロピヌず1 - dice_coefです。


 bce_dice_loss <- function(y_true, y_pred) { result <- loss_binary_crossentropy(y_true, y_pred) + (1 - dice_coef(y_true, y_pred)) return(result) } 

6. U-Netアヌキテクチャ


U-Netはセグメンテヌションの問題を解決するための叀兞的なアヌキテクチャです。 回路図



゜ヌス https : //www.researchgate.net/figure/311715357_fig3_Fig-3-U-NET-Architecture


写真128x128の実装


U-Net 128
 get_unet_128 <- function(input_shape = c(128, 128, 3), num_classes = 1) { inputs <- layer_input(shape = input_shape) # 128 down1 <- inputs %>% layer_conv_2d(filters = 64, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 64, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") down1_pool <- down1 %>% layer_max_pooling_2d(pool_size = c(2, 2), strides = c(2, 2)) # 64 down2 <- down1_pool %>% layer_conv_2d(filters = 128, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 128, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") down2_pool <- down2 %>% layer_max_pooling_2d(pool_size = c(2, 2), strides = c(2, 2)) # 32 down3 <- down2_pool %>% layer_conv_2d(filters = 256, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 256, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") down3_pool <- down3 %>% layer_max_pooling_2d(pool_size = c(2, 2), strides = c(2, 2)) # 16 down4 <- down3_pool %>% layer_conv_2d(filters = 512, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 512, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") down4_pool <- down4 %>% layer_max_pooling_2d(pool_size = c(2, 2), strides = c(2, 2)) # 8 center <- down4_pool %>% layer_conv_2d(filters = 1024, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 1024, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") # center up4 <- center %>% layer_upsampling_2d(size = c(2, 2)) %>% {layer_concatenate(inputs = list(down4, .), axis = 3)} %>% layer_conv_2d(filters = 512, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 512, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 512, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") # 16 up3 <- up4 %>% layer_upsampling_2d(size = c(2, 2)) %>% {layer_concatenate(inputs = list(down3, .), axis = 3)} %>% layer_conv_2d(filters = 256, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 256, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 256, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") # 32 up2 <- up3 %>% layer_upsampling_2d(size = c(2, 2)) %>% {layer_concatenate(inputs = list(down2, .), axis = 3)} %>% layer_conv_2d(filters = 128, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 128, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 128, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") # 64 up1 <- up2 %>% layer_upsampling_2d(size = c(2, 2)) %>% {layer_concatenate(inputs = list(down1, .), axis = 3)} %>% layer_conv_2d(filters = 64, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 64, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") %>% layer_conv_2d(filters = 64, kernel_size = c(3, 3), padding = "same") %>% layer_batch_normalization() %>% layer_activation("relu") # 128 classify <- layer_conv_2d(up1, filters = num_classes, kernel_size = c(1, 1), activation = "sigmoid") model <- keras_model( inputs = inputs, outputs = classify ) model %>% compile( optimizer = optimizer_rmsprop(lr = 0.0001), loss = bce_dice_loss, metrics = c(dice_coef) ) return(model) } model <- get_unet_128() 

{layer_concatenate(inputs = list(down4, .), axis = 3)}の䞭括匧は、 %>%挔算子のように最初のオブゞェクトずしおではなく、必芁な匕数ずしおオブゞェクトを眮き換えるために必芁です。 このアヌキテクチャの倚くの倉曎を提䟛できたす layer_conv_2d_transpose代わりにlayer_upsampling_2d䜿甚し、通垞のlayer_separable_conv_2d代わりにlayer_separable_conv_2d個別の畳み蟌みを適甚し、フィルタヌ番号ずオプティマむザヌ蚭定を詊したす。 Kerasリンク付きのKaggle Carvana Image Masking Challenge゜リュヌションには 、1024x1024たでの解像床のオプションがあり、これもRに簡単に移怍できたす。


このモデルには倚くのパラメヌタヌがありたす。


 # Total params: 34,540,737 # Trainable params: 34,527,041 # Non-trainable params: 13,696 

7.モデルトレヌニング


ここではすべおが簡単です。 Tensorboardを起動したす。


 tensorboard("logs_r") 

別の方法ずしお、 tfrunsパッケヌゞを䜿甚できたす 。これにより、TensorboardアナログがRStudio IDEに远加され、ニュヌラルネットワヌクのトレヌニング䜜業を合理化できたす。


コヌルバックを指定したす。 早期停止を䜿甚し、プラトヌに達したずきのトレヌニングの速床を䜎䞋させ、最適なモデルの重量を維持したす。


 callbacks_list <- list( callback_tensorboard("logs_r"), callback_early_stopping(monitor = "val_python_function", min_delta = 1e-4, patience = 8, verbose = 1, mode = "max"), callback_reduce_lr_on_plateau(monitor = "val_python_function", factor = 0.1, patience = 4, verbose = 1, epsilon = 1e-4, mode = "max"), callback_model_checkpoint(filepath = "weights_r/unet128_{epoch:02d}.h5", monitor = "val_python_function", save_best_only = TRUE, save_weights_only = TRUE, mode = "max" ) ) 

トレヌニングを開始しお埅機したす。 GTX 1050tiでは、1぀の時代に玄10分かかりたす。


 model %>% fit_generator( train_iterator, steps_per_epoch = as.integer(length(train_index) / batch_size), epochs = epochs, validation_data = val_iterator, validation_steps = as.integer(length(val_index) / batch_size), verbose = 1, callbacks = callbacks_list ) 

8.モデルベヌスの予枬


- run-length encoding.


 test_dir <- "input/test/" test_samples <- 100064 test_index <- sample(1:test_samples, 1000) load_model_weights_hdf5(model, "weights_r/unet128_08.h5") # best model imageRead <- function(image_file, target_width = 128, target_height = 128) { img <- image_read(image_file) img <- image_scale(img, paste0(target_width, "x", target_height, "!")) } img2arr <- function(image, target_width = 128, target_height = 128) { result <- aperm(as.numeric(image[[1]])[, , 1:3], c(2, 1, 3)) # transpose dim(result) <- c(1, target_width, target_height, 3) return(result) } arr2img <- function(arr, target_width = 1918, target_height = 1280) { img <- image_read(arr) img <- image_scale(img, paste0(target_width, "x", target_height, "!")) } qrle <- function(mask) { img <- t(mask) dim(img) <- c(128, 128, 1) img <- arr2img(img) arr <- as.numeric(img[[1]])[, , 2] vect <- ifelse(as.vector(arr) >= 0.5, 1, 0) turnpoints <- c(vect, 0) - c(0, vect) starts <- which(turnpoints == 1) ends <- which(turnpoints == -1) paste(c(rbind(starts, ends - starts)), collapse = " ") } cl <- makePSOCKcluster(4) clusterEvalQ(cl, { library(magick) library(abind) library(reticulate) imageRead <- function(image_file, target_width = 128, target_height = 128) { img <- image_read(image_file) img <- image_scale(img, paste0(target_width, "x", target_height, "!")) } img2arr <- function(image, target_width = 128, target_height = 128) { result <- aperm(as.numeric(image[[1]])[, , 1:3], c(2, 1, 3)) # transpose dim(result) <- c(1, target_width, target_height, 3) return(result) } qrle <- function(mask) { img <- t(mask) dim(img) <- c(128, 128, 1) img <- arr2img(img) arr <- as.numeric(img[[1]])[, , 2] vect <- ifelse(as.vector(arr) >= 0.5, 1, 0) turnpoints <- c(vect, 0) - c(0, vect) starts <- which(turnpoints == 1) ends <- which(turnpoints == -1) paste(c(rbind(starts, ends - starts)), collapse = " ") } }) registerDoParallel(cl) test_generator <- function(images_dir, samples_index, batch_size) { images_iter <- list.files(images_dir, pattern = ".jpg", full.names = TRUE)[samples_index] function() { batch_ind <- sample(1:length(images_iter), batch_size) batch_images_list <- images_iter[batch_ind] images_iter <<- images_iter[-batch_ind] x_batch <- foreach(i = 1:batch_size) %dopar% { img <- imageRead(image_file = batch_images_list[i]) # return as array arr <- img2arr(img) } x_batch <- do.call(abind, c(x_batch, list(along = 1))) result <- list(keras_array(x_batch)) } } test_iterator <- py_iterator(test_generator(images_dir = test_dir, samples_index = test_index, batch_size = batch_size)) preds <- predict_generator(model, test_iterator, steps = 10) preds <- foreach(i = 1:160) %dopar% { result <- qrle(preds[i, , , ]) } preds <- do.call(rbind, preds) 

, qrle , ( skoffer -):


結果

:




— 128128. , , .


, .


合蚈
このメッセヌゞでは、Rの䞊に座っおファッションのトレンドに遅れずに぀いおいくこずができ、ディヌプニュヌラルネットワヌクをうたくトレヌニングできるこずが瀺されたした。さらに、Windowsでさえこれを防ぐこずはできたせん。
継続するには、通垞どおり、次のようにしたす。



Source: https://habr.com/ru/post/J340212/


All Articles