пятница, 25 августа 2017 г.

Соревнование Invasive Species Monitoring: решение на R с использованием Keras

В недавнем соревновании Invasive Species Monitoring удалось опробовать пакет keras, позволяющий использовать в R одноименную библиотеку. Был на 11 месте, затем съехал на 19 (на публичном LB), а итоговые результаты не показывают до сих пор. 
Мое лучшее решение представляло собой простое усреднение предсказаний одной модели после двух разных эпох и еще одной модели после лучшей эпохи. Модели отличались форматом изображений: в первой картинки были с правильным соотношением сторон - 300х400 (высота на ширину), а во второй - с неправильным (400х300). Файнтюнил InceptionV3, хотя у участников и с Resnet все очень хорошо получалось. 
В топовом решении использовалась перекрестная проверка вместо маленькой проверочной выборки в моем случае, а также картинки значительно большего размера; я остановился на 400х300 ввиду наличия GPU всего c 4Гб памяти, а автор https://www.kaggle.com/jamesrequa/keras-k-fold-inception-v3-1st-place-lb-0-99770 взял 800х800. Размер картинки в данном случае решает: борьба шла за доли процента AUC, а по своим моделям я видел, что самые "сомнительные" предсказания были как раз для тех изображений, где цветочки получились совсем маленького размера. Потенциально качество модели на относительно мелких изображениях можно было бы улучшить, добавив в список преобразований при аугментации еще и увеличение (zoom_range в image_data_generator()).
0.99 with R and Keras (Inception V3 fine-tune)

setwd("G:/KAGGLE/invasive")
library(keras)

# Class labels
labels <- read.table("data/train_labels.csv",
                     header = TRUE, 
                     sep = ",")
labels$invasive_f <- factor(labels$invasive, 
                            levels = c(0, 1),
                            labels = c("non_invasive", "invasive"))

# Train data
files <- list.files("data/train")
old_names <- sapply(files, 
                    strsplit, 
                    split = ".", 
                    fixed = TRUE)
max_length <- max(sapply(old_names, 
                         function(x) nchar(x[[1]])))
zeros <- max_length - sapply(old_names, 
                             function(x) nchar(x[[1]]))
zeros <- sapply(zeros, 
                function(x) paste(rep(0, x), collapse = ""))
new_names <- Map(function(x, y) paste0("data/train/", y, x[1], ".jpg"),
                 x = old_names, 
                 y = zeros)

files <- list.files("data/train", 
                    full.names = TRUE)
Map(function(x, y) file.rename(from = x, to = y), 
    files, 
    new_names)

lapply(as.character(unique(labels$invasive_f)), 
       function(x) dir.create(path = paste0("./data/train/", x)))

files <- list.files("data/train", 
                    pattern = "*.jpg")
new_names <- paste0("data/train/", 
                    labels$invasive_f, 
                    "/", 
                    files)
files <- list.files("data/train", 
                    full.names = TRUE, 
                    pattern = "*.jpg")
Map(function(x, y) file.rename(from = x, to = y), 
    files, 
    new_names)

# Test data
files <- list.files("data/test")
old_names <- sapply(files, 
                    strsplit, 
                    split = ".", 
                    fixed = TRUE)
max_length <- max(sapply(old_names, 
                         function(x) nchar(x[[1]])))
zeros <- max_length - sapply(old_names, 
                             function(x) nchar(x[[1]]))
zeros <- sapply(zeros, 
                function(x) paste(rep(0, x), collapse = ""))
new_names <- Map(function(x, y) paste0("data/test/", y, x[1], ".jpg"),
                 x = old_names, 
                 y = zeros)
files <- list.files("data/test", 
                    full.names = TRUE)
Map(function(x, y) file.rename(from = x, to = y), 
    files, 
    new_names)

train_directory <- "data/train/"
validation_directory <- "data/validation/"
test_directory <- "data/test/"

img_height <- 300
img_width <- 400
batch_size <- 16
epochs <- 40
train_samples = 2136
validation_samples = 159 
test_samples = 1531

datagen <- image_data_generator(
    rotation_range = 20,
    width_shift_range = 0.2,
    height_shift_range = 0.2,
    horizontal_flip = TRUE
  )

train_generator <- flow_images_from_directory(
    train_directory, 
    generator = datagen,
    target_size = c(img_height, img_width),
    color_mode = "rgb",
    class_mode = "binary", 
    batch_size = batch_size, 
    shuffle = TRUE,
    seed = 123)

validation_generator <- flow_images_from_directory(
    validation_directory, 
    generator = datagen,
    target_size = c(img_height, img_width), 
    color_mode = "rgb", 
    classes = NULL,
    class_mode = "binary", 
    batch_size = batch_size, 
    shuffle = TRUE,
    seed = 123)

test_generator <- flow_images_from_directory(
    test_directory, 
    generator = image_data_generator(),
    target_size = c(img_height, img_width), 
    color_mode = "rgb", 
    class_mode = "binary", 
    batch_size = 1,
    shuffle = FALSE) 

base_model <- application_inception_v3(weights = "imagenet", 
                                       include_top = FALSE,
                                       input_shape = c(img_height, img_width, 3))

# Custom layers
predictions <- base_model$output %>% 
  layer_global_average_pooling_2d() %>% 
  layer_dense(units = 1024, activation = "relu") %>% 
  layer_dense(units = 1, activation = "sigmoid")

model <- keras_model(inputs = base_model$input, 
                     outputs = predictions)

model %>% compile(
  loss = "binary_crossentropy",
  optimizer = optimizer_sgd(lr = 0.0001, 
                            momentum = 0.9, 
                            decay = 1e-5),
  metrics = "accuracy"
)

tensorboard("logs/inception3")

model %>% fit_generator(
  train_generator,
  steps_per_epoch = as.integer(train_samples / batch_size), 
  epochs = epochs, 
  validation_data = validation_generator,
  validation_steps = as.integer(validation_samples / batch_size),
  verbose = 1,
  
  callbacks = list(
      callback_model_checkpoint(
          "models/inception3/inception3_{epoch:02d}_{val_acc:.2f}.h5",
          save_weights_only = TRUE),
      callback_tensorboard("logs/inception3"),
      callback_csv_logger("logs/inception3/log_inception3.csv", separator = ";")
  )
)

save_model_hdf5(model, 
                "models/inception3_40epochs.h5")

base_model <- application_inception_v3(weights = "imagenet", 
                                       include_top = FALSE,
                                       input_shape = c (img_height, img_width, 3))

predictions <- base_model$output %>% 
  layer_global_average_pooling_2d() %>% 
  layer_dense(units = 1024, activation = "relu") %>% 
  layer_dense(units = 1, activation = "sigmoid")

model <- keras_model(inputs = base_model$input, 
                     outputs = predictions)

load_model_weights_hdf5(model, 
                        "models/inception3/XXXXXXX.h5") # use your best epoch here

model %>% compile(
  loss = "binary_crossentropy",
  optimizer = optimizer_sgd(lr = 0.0001, 
                            momentum = 0.9, 
                            decay = 1e-5),
  metrics = "accuracy"
)

preds <- predict_generator(model, 
                           test_generator, 
                           steps = test_samples)
preds <- data.frame(name = 1:test_samples,
                    invasive = 1 - preds)
write.table(preds, 
            "submit.csv", sep = ",", 
            dec = ".", 
            quote = FALSE, 
            row.names = FALSE)

Комментариев нет:

Отправить комментарий