Quick Draw Doodle Recognition: R، C++ ۽ نيورل نيٽ ورڪن سان دوستي ڪيئن ڪجي

Quick Draw Doodle Recognition: R، C++ ۽ نيورل نيٽ ورڪن سان دوستي ڪيئن ڪجي

اي حبر!

آخري زوال ۾، ڪيگل هٿ سان ٺهيل تصويرن کي درجه بندي ڪرڻ لاءِ هڪ مقابلي جي ميزباني ڪئي، Quick Draw Doodle Recognition، جنهن ۾، ٻين جي وچ ۾، R-scientists جي هڪ ٽيم حصو ورتو: Artem Klevtsova, فليپا مئنيجر и آندري Ogurtsov. اسان مقابلي کي تفصيل سان بيان نه ڪنداسين؛ جيڪو اڳ ۾ ئي ٿي چڪو آهي تازو اشاعت.

هن ڀيري ميڊل فارمنگ سان ڪم نه ڪيو ويو، پر تمام گهڻو قيمتي تجربو حاصل ڪيو ويو، تنهنڪري مان ڪميونٽي کي ڪيگل تي ۽ روزمره جي ڪم ۾ تمام دلچسپ ۽ مفيد شين بابت ٻڌائڻ چاهيان ٿو. بحث ڪيل عنوانن مان: ڏکيو زندگي بغير OpenCV, JSON parsing (اهي مثال C++ ڪوڊ جي انضمام کي جانچيندا آهن آر ۾ اسڪرپٽس يا پيڪيجز ۾ آر سي پي پي)، اسڪرپٽ جي پيٽرولائيزيشن ۽ حتمي حل جي ڊاکرائيزيشن. ميسيج مان سمورو ڪوڊ هڪ فارم ۾ موجود آهي جيڪو عمل لاءِ موزون آهي ذخيرو.

مضمون:

  1. CSV مان ڊيٽا کي موثر طريقي سان لوڊ ڪريو MonetDB ۾
  2. بيچ تيار ڪرڻ
  3. ڊيٽابيس مان بيچز کي لوڊ ڪرڻ لاءِ آئٽرٽر
  4. هڪ ماڊل آرڪيٽيڪچر چونڊيو
  5. اسڪرپٽ parameterization
  6. اسڪرپٽ جي Dockerization
  7. Google Cloud تي گھڻن GPUs استعمال ڪندي
  8. سوچيم ته هڪ ٿڪل جي

1. موثر طريقي سان CSV مان ڊيٽا کي MonetDB ڊيٽابيس ۾ لوڊ ڪريو

هن مقابلي ۾ ڊيٽا تيار ڪيل تصويرن جي صورت ۾ نه، پر 340 CSV فائلن جي صورت ۾ مهيا ڪئي وئي آهي (هر ڪلاس لاءِ هڪ فائل) جنهن ۾ JSONs شامل آهن پوائنٽ ڪوآرڊينيٽ. انهن پوائنٽن کي لائنن سان ڳنڍڻ سان، اسان 256x256 پکسلز جي ماپ جي آخري تصوير حاصل ڪندا آهيون. انهي سان گڏ هر رڪارڊ لاءِ هڪ ليبل هوندو آهي جيڪو ظاهر ڪري ٿو ته ڇا تصوير صحيح طور تي استعمال ڪيل درجه بندي ڪندڙ طرفان استعمال ڪئي وئي هئي جڏهن ڊيٽا سيٽ گڏ ڪئي وئي هئي، تصوير جي ليکڪ جي رهائش واري ملڪ جو هڪ ٻه اکر ڪوڊ، هڪ منفرد سڃاڻپ ڪندڙ، ٽائم اسٽيمپ ۽ هڪ طبقي جو نالو جيڪو فائل جي نالي سان ملندو آهي. اصل ڊيٽا جو هڪ آسان ورزن آرڪائيو ۾ 7.4 GB جو وزن آهي ۽ پيڪنگ ڪرڻ کان پوءِ تقريبن 20 GB، ان پيڪ ڪرڻ کان پوءِ مڪمل ڊيٽا 240 GB تائين ٿي وڃي ٿي. منتظمين کي يقيني بڻايو ويو ته ٻنهي نسخن ساڳئي ڊرائنگ کي ٻيهر پيش ڪيو، مطلب ته مڪمل نسخو بيڪار هو. ڪنهن به صورت ۾، 50 ملين تصويرن کي گرافڪ فائلن ۾ محفوظ ڪرڻ يا صفن جي صورت ۾ فوري طور تي غير منافع بخش سمجهيو ويو، ۽ اسان آرڪائيو مان سڀني CSV فائلن کي ضم ڪرڻ جو فيصلو ڪيو. train_simplified.zip هر بيچ لاءِ گهربل سائيز جي تصويرن جي ايندڙ نسل سان گڏ ڊيٽابيس ۾ ”فلائي تي“.

هڪ چڱي طرح ثابت نظام چونڊيو ويو DBMS طور مونٽ ڊي بي، يعني هڪ پيڪيج جي طور تي آر لاءِ هڪ عمل درآمد MonetDBLite. پيڪيج ۾ ڊيٽابيس سرور جو هڪ ايمبيڊڊ ورزن شامل آهي ۽ توهان کي اجازت ڏئي ٿو ته سرور کي سڌو سنئون آر سيشن مان چونڊيو ۽ ان سان گڏ ڪم ڪريو. ڊيٽابيس ٺاهڻ ۽ ان سان ڳنڍڻ هڪ حڪم سان ڪيو ويو آهي:

con <- DBI::dbConnect(drv = MonetDBLite::MonetDBLite(), Sys.getenv("DBDIR"))

اسان کي ٻه ٽيبل ٺاهڻا پوندا: هڪ سموري ڊيٽا لاءِ، ٻيو ڊائون لوڊ ڪيل فائلن بابت سروس جي معلومات لاءِ (مفيد جيڪڏهن ڪجهه غلط ٿي وڃي ۽ عمل کي ڪيترن ئي فائلن کي ڊائون لوڊ ڪرڻ کان پوءِ ٻيهر شروع ڪرڻو پوندو):

ٽيبل ٺاهڻ

if (!DBI::dbExistsTable(con, "doodles")) {
  DBI::dbCreateTable(
    con = con,
    name = "doodles",
    fields = c(
      "countrycode" = "char(2)",
      "drawing" = "text",
      "key_id" = "bigint",
      "recognized" = "bool",
      "timestamp" = "timestamp",
      "word" = "text"
    )
  )
}

if (!DBI::dbExistsTable(con, "upload_log")) {
  DBI::dbCreateTable(
    con = con,
    name = "upload_log",
    fields = c(
      "id" = "serial",
      "file_name" = "text UNIQUE",
      "uploaded" = "bool DEFAULT false"
    )
  )
}

ڊيٽابيس ۾ ڊيٽا کي لوڊ ڪرڻ جو تيز ترين طريقو SQL - ڪمانڊ استعمال ڪندي CSV فائلن کي سڌو نقل ڪرڻ هو COPY OFFSET 2 INTO tablename FROM path USING DELIMITERS ',','n','"' NULL AS '' BEST EFFORTڪٿي tablename - ٽيبل جو نالو ۽ path - فائل ڏانهن رستو. آرڪائيو سان ڪم ڪرڻ دوران، اهو دريافت ڪيو ويو ته تعمير ٿيل عمل درآمد unzip آر ۾ آرڪائيو مان ڪيترن ئي فائلن سان صحيح ڪم نٿو ڪري، تنهنڪري اسان سسٽم استعمال ڪيو unzip (پيراميٽر استعمال ڪندي getOption("unzip")).

ڊيٽابيس ۾ لکڻ لاءِ فنڪشن

#' @title Извлечение и загрузка файлов
#'
#' @description
#' Извлечение CSV-файлов из ZIP-архива и загрузка их в базу данных
#'
#' @param con Объект подключения к базе данных (класс `MonetDBEmbeddedConnection`).
#' @param tablename Название таблицы в базе данных.
#' @oaram zipfile Путь к ZIP-архиву.
#' @oaram filename Имя файла внури ZIP-архива.
#' @param preprocess Функция предобработки, которая будет применена извлечённому файлу.
#'   Должна принимать один аргумент `data` (объект `data.table`).
#'
#' @return `TRUE`.
#'
upload_file <- function(con, tablename, zipfile, filename, preprocess = NULL) {
  # Проверка аргументов
  checkmate::assert_class(con, "MonetDBEmbeddedConnection")
  checkmate::assert_string(tablename)
  checkmate::assert_string(filename)
  checkmate::assert_true(DBI::dbExistsTable(con, tablename))
  checkmate::assert_file_exists(zipfile, access = "r", extension = "zip")
  checkmate::assert_function(preprocess, args = c("data"), null.ok = TRUE)

  # Извлечение файла
  path <- file.path(tempdir(), filename)
  unzip(zipfile, files = filename, exdir = tempdir(), 
        junkpaths = TRUE, unzip = getOption("unzip"))
  on.exit(unlink(file.path(path)))

  # Применяем функция предобработки
  if (!is.null(preprocess)) {
    .data <- data.table::fread(file = path)
    .data <- preprocess(data = .data)
    data.table::fwrite(x = .data, file = path, append = FALSE)
    rm(.data)
  }

  # Запрос к БД на импорт CSV
  sql <- sprintf(
    "COPY OFFSET 2 INTO %s FROM '%s' USING DELIMITERS ',','n','"' NULL AS '' BEST EFFORT",
    tablename, path
  )
  # Выполнение запроса к БД
  DBI::dbExecute(con, sql)

  # Добавление записи об успешной загрузке в служебную таблицу
  DBI::dbExecute(con, sprintf("INSERT INTO upload_log(file_name, uploaded) VALUES('%s', true)",
                              filename))

  return(invisible(TRUE))
}

جيڪڏهن توهان کي ڊيٽابيس ۾ لکڻ کان پهريان ٽيبل کي تبديل ڪرڻ جي ضرورت آهي، اهو دليل ۾ پاس ڪرڻ ڪافي آهي preprocess فنڪشن جيڪو ڊيٽا کي تبديل ڪندو.

ڊيٽابيس ۾ ڊيٽا کي ترتيب وار لوڊ ڪرڻ لاءِ ڪوڊ:

ڊيٽابيس ۾ ڊيٽا لکڻ

# Список файлов для записи
files <- unzip(zipfile, list = TRUE)$Name

# Список исключений, если часть файлов уже была загружена
to_skip <- DBI::dbGetQuery(con, "SELECT file_name FROM upload_log")[[1L]]
files <- setdiff(files, to_skip)

if (length(files) > 0L) {
  # Запускаем таймер
  tictoc::tic()
  # Прогресс бар
  pb <- txtProgressBar(min = 0L, max = length(files), style = 3)
  for (i in seq_along(files)) {
    upload_file(con = con, tablename = "doodles", 
                zipfile = zipfile, filename = files[i])
    setTxtProgressBar(pb, i)
  }
  close(pb)
  # Останавливаем таймер
  tictoc::toc()
}

# 526.141 sec elapsed - копирование SSD->SSD
# 558.879 sec elapsed - копирование USB->SSD

استعمال ٿيل ڊرائيو جي رفتار جي خاصيتن جي لحاظ سان ڊيٽا لوڊ ڪرڻ جو وقت مختلف ٿي سگھي ٿو. اسان جي حالت ۾، پڙهڻ ۽ لکڻ هڪ SSD اندر يا هڪ فليش ڊرائيو (ذريعو فائل) کان هڪ SSD (DB) تائين 10 منٽ کان گهٽ وقت وٺندو آهي.

انٽيجر ڪلاس جي ليبل ۽ انڊيڪس ڪالمن سان ڪالم ٺاھڻ ۾ ڪجھ وڌيڪ سيڪنڊ لڳن ٿا (ORDERED INDEX) ليڪ نمبرن سان جنهن جي ذريعي مشاهدو نموني ڪيو ويندو جڏهن بيچ ٺاهيندي:

اضافي ڪالمن ۽ انڊيڪس ٺاهڻ

message("Generate lables")
invisible(DBI::dbExecute(con, "ALTER TABLE doodles ADD label_int int"))
invisible(DBI::dbExecute(con, "UPDATE doodles SET label_int = dense_rank() OVER (ORDER BY word) - 1"))

message("Generate row numbers")
invisible(DBI::dbExecute(con, "ALTER TABLE doodles ADD id serial"))
invisible(DBI::dbExecute(con, "CREATE ORDERED INDEX doodles_id_ord_idx ON doodles(id)"))

اڏام تي بيچ ٺاهڻ جي مسئلي کي حل ڪرڻ لاء، اسان کي ميز مان بي ترتيب واري قطار کي ڪڍڻ جي وڌ ۾ وڌ رفتار حاصل ڪرڻ جي ضرورت آهي. doodles. ان لاءِ اسان 3 ترڪيبون استعمال ڪيون. پهرين قسم جي طول و عرض کي گهٽائڻ هو جيڪو ذخيرو ID کي محفوظ ڪري ٿو. اصل ڊيٽا سيٽ ۾، ID کي ذخيرو ڪرڻ لاء گهربل قسم آهي bigint, پر مشاهدن جو تعداد ان کي ممڪن بڻائي ٿو ته انهن جي سڃاڻپ ڪندڙ، ترتيب واري نمبر جي برابر، قسم ۾ int. هن معاملي ۾ ڳولا تمام تيز آهي. ٻيو چال استعمال ڪرڻ هو ORDERED INDEX - اسان هن فيصلي تي تجرباتي طور تي آيا آهيون، سڀني دستيابن مان گذري چڪا آهيون اختيارات. ٽيون هو parameterized سوالن کي استعمال ڪرڻ لاء. طريقو جو بنياد هڪ ڀيرو حڪم تي عمل ڪرڻ آهي PREPARE تيار ڪيل اظهار جي بعد ۾ استعمال سان جڏهن هڪ ئي قسم جي سوالن جو هڪ گروپ ٺاهيو، پر حقيقت ۾ هڪ سادي جي مقابلي ۾ هڪ فائدو آهي. SELECT انگن اکرن جي غلطي جي حد اندر ٿي ويو.

ڊيٽا کي اپ لوڊ ڪرڻ جو عمل 450 MB کان وڌيڪ ريم استعمال نٿو ڪري. اهو آهي، بيان ڪيل طريقي سان توهان کي تقريبا ڪنهن به بجيٽ هارڊويئر تي ڏهه گيگا بائيٽ وزن واري ڊيٽا سيٽ کي منتقل ڪرڻ جي اجازت ڏئي ٿي، بشمول ڪجهه سنگل بورڊ ڊوائيسز، جيڪو تمام سٺو آهي.

باقي اهو آهي ته حاصل ڪرڻ جي رفتار کي ماپڻ (بي ترتيب) ڊيٽا ۽ اسڪيلنگ جو اندازو لڳايو جڏهن مختلف سائزن جي بيچ کي نموني ڏيڻ:

ڊيٽابيس جي معيار

library(ggplot2)

set.seed(0)
# Подключение к базе данных
con <- DBI::dbConnect(MonetDBLite::MonetDBLite(), Sys.getenv("DBDIR"))

# Функция для подготовки запроса на стороне сервера
prep_sql <- function(batch_size) {
  sql <- sprintf("PREPARE SELECT id FROM doodles WHERE id IN (%s)",
                 paste(rep("?", batch_size), collapse = ","))
  res <- DBI::dbSendQuery(con, sql)
  return(res)
}

# Функция для извлечения данных
fetch_data <- function(rs, batch_size) {
  ids <- sample(seq_len(n), batch_size)
  res <- DBI::dbFetch(DBI::dbBind(rs, as.list(ids)))
  return(res)
}

# Проведение замера
res_bench <- bench::press(
  batch_size = 2^(4:10),
  {
    rs <- prep_sql(batch_size)
    bench::mark(
      fetch_data(rs, batch_size),
      min_iterations = 50L
    )
  }
)
# Параметры бенчмарка
cols <- c("batch_size", "min", "median", "max", "itr/sec", "total_time", "n_itr")
res_bench[, cols]

#   batch_size      min   median      max `itr/sec` total_time n_itr
#        <dbl> <bch:tm> <bch:tm> <bch:tm>     <dbl>   <bch:tm> <int>
# 1         16   23.6ms  54.02ms  93.43ms     18.8        2.6s    49
# 2         32     38ms  84.83ms 151.55ms     11.4       4.29s    49
# 3         64   63.3ms 175.54ms 248.94ms     5.85       8.54s    50
# 4        128   83.2ms 341.52ms 496.24ms     3.00      16.69s    50
# 5        256  232.8ms 653.21ms 847.44ms     1.58      31.66s    50
# 6        512  784.6ms    1.41s    1.98s     0.740       1.1m    49
# 7       1024  681.7ms    2.72s    4.06s     0.377      2.16m    49

ggplot(res_bench, aes(x = factor(batch_size), y = median, group = 1)) +
  geom_point() +
  geom_line() +
  ylab("median time, s") +
  theme_minimal()

DBI::dbDisconnect(con, shutdown = TRUE)

Quick Draw Doodle Recognition: R، C++ ۽ نيورل نيٽ ورڪن سان دوستي ڪيئن ڪجي

2. بيچ تيار ڪرڻ

سڄي بيچ جي تياري جي عمل هيٺ ڏنل قدمن تي مشتمل آهي:

  1. ڪيترن ئي JSONs کي پارس ڪري رهيو آهي جنهن ۾ اسٽرنگ جا ویکٹر شامل آهن پوائنٽن جي همراهن سان.
  2. گھربل سائيز جي تصوير تي پوائنٽن جي ڪوآرڊينيٽ جي بنياد تي رنگين لائينون ٺاھيو (مثال طور، 256 × 256 يا 128 × 128).
  3. نتيجو تصويرن کي ٽينسر ۾ تبديل ڪرڻ.

Python kernels جي مقابلي جي حصي جي طور تي، مسئلو حل ڪيو ويو بنيادي طور تي استعمال ڪندي OpenCV. آر ۾ هڪ آسان ۽ سڀ کان وڌيڪ واضح اينالاگ هن طرح نظر ايندو:

آر ۾ JSON کي ٽينسر جي تبديلي کي لاڳو ڪرڻ

r_process_json_str <- function(json, line.width = 3, 
                               color = TRUE, scale = 1) {
  # Парсинг JSON
  coords <- jsonlite::fromJSON(json, simplifyMatrix = FALSE)
  tmp <- tempfile()
  # Удаляем временный файл по завершению функции
  on.exit(unlink(tmp))
  png(filename = tmp, width = 256 * scale, height = 256 * scale, pointsize = 1)
  # Пустой график
  plot.new()
  # Размер окна графика
  plot.window(xlim = c(256 * scale, 0), ylim = c(256 * scale, 0))
  # Цвета линий
  cols <- if (color) rainbow(length(coords)) else "#000000"
  for (i in seq_along(coords)) {
    lines(x = coords[[i]][[1]] * scale, y = coords[[i]][[2]] * scale, 
          col = cols[i], lwd = line.width)
  }
  dev.off()
  # Преобразование изображения в 3-х мерный массив
  res <- png::readPNG(tmp)
  return(res)
}

r_process_json_vector <- function(x, ...) {
  res <- lapply(x, r_process_json_str, ...)
  # Объединение 3-х мерных массивов картинок в 4-х мерный в тензор
  res <- do.call(abind::abind, c(res, along = 0))
  return(res)
}

ڊرائنگ معياري R اوزار استعمال ڪندي ڪئي وئي آهي ۽ محفوظ ٿيل عارضي PNG ۾ محفوظ ڪئي وئي رام ۾ (لينڪس تي، عارضي آر ڊائريڪٽري ڊاريڪٽري ۾ واقع آهن. /tmp، رام ۾ نصب ٿيل). هن فائل کي پوءِ 0 کان 1 تائين جي انگن سان گڏ هڪ ٽي-dimensional صف جي طور تي پڙهيو ويندو آهي. اهو ضروري آهي ڇاڪاڻ ته هڪ وڌيڪ روايتي BMP هيڪس رنگن ڪوڊس سان خام صف ۾ پڙهي ويندي.

اچو ته نتيجو جاچيون:

zip_file <- file.path("data", "train_simplified.zip")
csv_file <- "cat.csv"
unzip(zip_file, files = csv_file, exdir = tempdir(), 
      junkpaths = TRUE, unzip = getOption("unzip"))
tmp_data <- data.table::fread(file.path(tempdir(), csv_file), sep = ",", 
                              select = "drawing", nrows = 10000)
arr <- r_process_json_str(tmp_data[4, drawing])
dim(arr)
# [1] 256 256   3
plot(magick::image_read(arr))

Quick Draw Doodle Recognition: R، C++ ۽ نيورل نيٽ ورڪن سان دوستي ڪيئن ڪجي

بيچ پاڻ کي ھيٺ ڏنل ٺاھيو ويندو:

res <- r_process_json_vector(tmp_data[1:4, drawing], scale = 0.5)
str(res)
 # num [1:4, 1:128, 1:128, 1:3] 1 1 1 1 1 1 1 1 1 1 ...
 # - attr(*, "dimnames")=List of 4
 #  ..$ : NULL
 #  ..$ : NULL
 #  ..$ : NULL
 #  ..$ : NULL

اهو عمل اسان لاءِ غير مناسب لڳي رهيو آهي، ڇاڪاڻ ته وڏين بيچز جي ٺهڻ ۾ تمام گهڻو وقت لڳندو آهي، ۽ اسان هڪ طاقتور لائبريري استعمال ڪندي پنهنجن ساٿين جي تجربي مان فائدو وٺڻ جو فيصلو ڪيو. OpenCV. ان وقت آر لاءِ ڪو به تيار ٿيل پيڪيج موجود نه هو (هاڻي ڪو به ناهي)، تنهنڪري گهربل ڪارڪردگي جو گهٽ ۾ گهٽ عمل C++ ۾ لکيو ويو هو R ڪوڊ ۾ انضمام سان. آر سي پي پي.

مسئلو حل ڪرڻ لاء، هيٺيون پيڪيجز ۽ لائبريريون استعمال ڪيون ويون آهن:

  1. OpenCV تصويرن ۽ ڊرائنگ لائنن سان ڪم ڪرڻ لاءِ. استعمال ٿيل اڳ-انسٽال ٿيل سسٽم لائبريريون ۽ هيڊر فائلون، گڏو گڏ متحرڪ لنڪنگ.

  2. xtensor multidimensional arrays ۽ tensors سان ڪم ڪرڻ لاءِ. اسان ساڳئي نالي جي R پيڪيج ۾ شامل هيڊر فائلون استعمال ڪيون. لائبريري توهان کي ڪم ڪرڻ جي اجازت ڏئي ٿي گھڻائي واري صفن سان، ٻئي قطار ميجر ۽ ڪالمن جي وڏي ترتيب ۾.

  3. ndjson JSON پارس ڪرڻ لاءِ. هن لائبريري ۾ استعمال ڪيو ويندو آهي xtensor خودڪار طور تي جيڪڏهن اهو پروجيڪٽ ۾ موجود آهي.

  4. RcppThread JSON کان ویکٹر جي ملٽي ٿريڊ پروسيسنگ کي منظم ڪرڻ لاءِ. استعمال ڪيو هيڊر فائلون هن پيڪيج پاران مهيا ڪيل. کان وڌيڪ مشهور آر سي پي پي پارلل پيڪيج، ٻين شين جي وچ ۾، هڪ تعمير ٿيل لوپ مداخلت ميڪانيزم آهي.

اهو ان ڳالهه جي قابل آهي xtensor هڪ ديوتا ثابت ٿيو: حقيقت اها آهي ته ان ۾ وسيع ڪارڪردگي ۽ اعلي ڪارڪردگي آهي، ان جي ڊولپرز ڪافي جوابدار ٿي چڪا آهن ۽ فوري طور تي ۽ تفصيل سان سوالن جو جواب ڏنو. انهن جي مدد سان، اهو ممڪن هو ته OpenCV ميٽرڪس جي تبديلين کي xtensor ٽينسر ۾ لاڳو ڪرڻ، انهي سان گڏ 3-dimensional تصويري ٽينسر کي گڏ ڪرڻ جو هڪ طريقو صحيح طول و عرض جي 4-dimensional ٽينسر ۾ (بيچ پاڻ).

Rcpp، xtensor ۽ RcppThread سکڻ لاءِ مواد

https://thecoatlessprofessor.com/programming/unofficial-rcpp-api-documentation

https://docs.opencv.org/4.0.1/d7/dbd/group__imgproc.html

https://xtensor.readthedocs.io/en/latest/

https://xtensor.readthedocs.io/en/latest/file_loading.html#loading-json-data-into-xtensor

https://cran.r-project.org/web/packages/RcppThread/vignettes/RcppThread-vignette.pdf

فائلن کي گڏ ڪرڻ لاءِ جيڪي سسٽم فائلون استعمال ڪن ٿيون ۽ سسٽم تي نصب ٿيل لائبريرين سان متحرڪ ڳنڍڻ، اسان استعمال ڪيو پلگ ان ميڪانيزم کي لاڳو ڪيل پيڪيج ۾ آر سي پي پي. پاڻمرادو رستا ۽ جھنڊا ڳولڻ لاءِ، اسان استعمال ڪيو ھڪ مشهور لينڪس يوٽيلٽي pkg-ترتيب.

OpenCV لائبريري استعمال ڪرڻ لاءِ Rcpp پلگ ان جو نفاذ

Rcpp::registerPlugin("opencv", function() {
  # Возможные названия пакета
  pkg_config_name <- c("opencv", "opencv4")
  # Бинарный файл утилиты pkg-config
  pkg_config_bin <- Sys.which("pkg-config")
  # Проврека наличия утилиты в системе
  checkmate::assert_file_exists(pkg_config_bin, access = "x")
  # Проверка наличия файла настроек OpenCV для pkg-config
  check <- sapply(pkg_config_name, 
                  function(pkg) system(paste(pkg_config_bin, pkg)))
  if (all(check != 0)) {
    stop("OpenCV config for the pkg-config not found", call. = FALSE)
  }

  pkg_config_name <- pkg_config_name[check == 0]
  list(env = list(
    PKG_CXXFLAGS = system(paste(pkg_config_bin, "--cflags", pkg_config_name), 
                          intern = TRUE),
    PKG_LIBS = system(paste(pkg_config_bin, "--libs", pkg_config_name), 
                      intern = TRUE)
  ))
})

پلگ ان جي آپريشن جي نتيجي ۾، ھيٺين قدرن کي ترتيب ڏيڻ واري عمل دوران متبادل بڻايو ويندو:

Rcpp:::.plugins$opencv()$env

# $PKG_CXXFLAGS
# [1] "-I/usr/include/opencv"
#
# $PKG_LIBS
# [1] "-lopencv_shape -lopencv_stitching -lopencv_superres -lopencv_videostab -lopencv_aruco -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_datasets -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hdf -lopencv_line_descriptor -lopencv_optflow -lopencv_video -lopencv_plot -lopencv_reg -lopencv_saliency -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_rgbd -lopencv_viz -lopencv_surface_matching -lopencv_text -lopencv_ximgproc -lopencv_calib3d -lopencv_features2d -lopencv_flann -lopencv_xobjdetect -lopencv_objdetect -lopencv_ml -lopencv_xphoto -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_photo -lopencv_imgproc -lopencv_core"

JSON کي پارس ڪرڻ ۽ ماڊل ڏانهن منتقلي لاءِ بيچ پيدا ڪرڻ لاءِ لاڳو ڪندڙ ڪوڊ اسپائلر هيٺ ڏنل آهي. پهرين، هيڊر فائلن کي ڳولڻ لاءِ مقامي پروجيڪٽ ڊاريڪٽري شامل ڪريو (جي ضرورت آهي ndjson):

Sys.setenv("PKG_CXXFLAGS" = paste0("-I", normalizePath(file.path("src"))))

C++ ۾ ٽينسر جي تبديليءَ لاءِ JSON جو نفاذ

// [[Rcpp::plugins(cpp14)]]
// [[Rcpp::plugins(opencv)]]
// [[Rcpp::depends(xtensor)]]
// [[Rcpp::depends(RcppThread)]]

#include <xtensor/xjson.hpp>
#include <xtensor/xadapt.hpp>
#include <xtensor/xview.hpp>
#include <xtensor-r/rtensor.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <Rcpp.h>
#include <RcppThread.h>

// Синонимы для типов
using RcppThread::parallelFor;
using json = nlohmann::json;
using points = xt::xtensor<double,2>;     // Извлечённые из JSON координаты точек
using strokes = std::vector<points>;      // Извлечённые из JSON координаты точек
using xtensor3d = xt::xtensor<double, 3>; // Тензор для хранения матрицы изоображения
using xtensor4d = xt::xtensor<double, 4>; // Тензор для хранения множества изображений
using rtensor3d = xt::rtensor<double, 3>; // Обёртка для экспорта в R
using rtensor4d = xt::rtensor<double, 4>; // Обёртка для экспорта в R

// Статические константы
// Размер изображения в пикселях
const static int SIZE = 256;
// Тип линии
// См. https://en.wikipedia.org/wiki/Pixel_connectivity#2-dimensional
const static int LINE_TYPE = cv::LINE_4;
// Толщина линии в пикселях
const static int LINE_WIDTH = 3;
// Алгоритм ресайза
// https://docs.opencv.org/3.1.0/da/d54/group__imgproc__transform.html#ga5bb5a1fea74ea38e1a5445ca803ff121
const static int RESIZE_TYPE = cv::INTER_LINEAR;

// Шаблон для конвертирования OpenCV-матрицы в тензор
template <typename T, int NCH, typename XT=xt::xtensor<T,3,xt::layout_type::column_major>>
XT to_xt(const cv::Mat_<cv::Vec<T, NCH>>& src) {
  // Размерность целевого тензора
  std::vector<int> shape = {src.rows, src.cols, NCH};
  // Общее количество элементов в массиве
  size_t size = src.total() * NCH;
  // Преобразование cv::Mat в xt::xtensor
  XT res = xt::adapt((T*) src.data, size, xt::no_ownership(), shape);
  return res;
}

// Преобразование JSON в список координат точек
strokes parse_json(const std::string& x) {
  auto j = json::parse(x);
  // Результат парсинга должен быть массивом
  if (!j.is_array()) {
    throw std::runtime_error("'x' must be JSON array.");
  }
  strokes res;
  res.reserve(j.size());
  for (const auto& a: j) {
    // Каждый элемент массива должен быть 2-мерным массивом
    if (!a.is_array() || a.size() != 2) {
      throw std::runtime_error("'x' must include only 2d arrays.");
    }
    // Извлечение вектора точек
    auto p = a.get<points>();
    res.push_back(p);
  }
  return res;
}

// Отрисовка линий
// Цвета HSV
cv::Mat ocv_draw_lines(const strokes& x, bool color = true) {
  // Исходный тип матрицы
  auto stype = color ? CV_8UC3 : CV_8UC1;
  // Итоговый тип матрицы
  auto dtype = color ? CV_32FC3 : CV_32FC1;
  auto bg = color ? cv::Scalar(0, 0, 255) : cv::Scalar(255);
  auto col = color ? cv::Scalar(0, 255, 220) : cv::Scalar(0);
  cv::Mat img = cv::Mat(SIZE, SIZE, stype, bg);
  // Количество линий
  size_t n = x.size();
  for (const auto& s: x) {
    // Количество точек в линии
    size_t n_points = s.shape()[1];
    for (size_t i = 0; i < n_points - 1; ++i) {
      // Точка начала штриха
      cv::Point from(s(0, i), s(1, i));
      // Точка окончания штриха
      cv::Point to(s(0, i + 1), s(1, i + 1));
      // Отрисовка линии
      cv::line(img, from, to, col, LINE_WIDTH, LINE_TYPE);
    }
    if (color) {
      // Меняем цвет линии
      col[0] += 180 / n;
    }
  }
  if (color) {
    // Меняем цветовое представление на RGB
    cv::cvtColor(img, img, cv::COLOR_HSV2RGB);
  }
  // Меняем формат представления на float32 с диапазоном [0, 1]
  img.convertTo(img, dtype, 1 / 255.0);
  return img;
}

// Обработка JSON и получение тензора с данными изображения
xtensor3d process(const std::string& x, double scale = 1.0, bool color = true) {
  auto p = parse_json(x);
  auto img = ocv_draw_lines(p, color);
  if (scale != 1) {
    cv::Mat out;
    cv::resize(img, out, cv::Size(), scale, scale, RESIZE_TYPE);
    cv::swap(img, out);
    out.release();
  }
  xtensor3d arr = color ? to_xt<double,3>(img) : to_xt<double,1>(img);
  return arr;
}

// [[Rcpp::export]]
rtensor3d cpp_process_json_str(const std::string& x, 
                               double scale = 1.0, 
                               bool color = true) {
  xtensor3d res = process(x, scale, color);
  return res;
}

// [[Rcpp::export]]
rtensor4d cpp_process_json_vector(const std::vector<std::string>& x, 
                                  double scale = 1.0, 
                                  bool color = false) {
  size_t n = x.size();
  size_t dim = floor(SIZE * scale);
  size_t channels = color ? 3 : 1;
  xtensor4d res({n, dim, dim, channels});
  parallelFor(0, n, [&x, &res, scale, color](int i) {
    xtensor3d tmp = process(x[i], scale, color);
    auto view = xt::view(res, i, xt::all(), xt::all(), xt::all());
    view = tmp;
  });
  return res;
}

هن ڪوڊ کي فائل ۾ رکڻ گهرجي src/cv_xt.cpp ۽ حڪم سان گڏ ڪريو Rcpp::sourceCpp(file = "src/cv_xt.cpp", env = .GlobalEnv)؛ ڪم لاء پڻ گهربل nlohmann/json.hpp کان ريٽائرمينٽ. ڪوڊ ڪيترن ئي ڪمن ۾ ورهايل آهي:

  • to_xt - تصويري ميٽرڪس کي تبديل ڪرڻ لاءِ ٺهيل فنڪشن (cv::Mat) ٽينسر ڏانهن xt::xtensor;

  • parse_json - فنڪشن JSON اسٽرنگ کي پارس ڪري ٿو، پوائنٽن جي همراهن کي ڪڍي ٿو، انهن کي ویکٹر ۾ پيڪنگ؛

  • ocv_draw_lines - پوائنٽن جي نتيجي ۾ ویکٹر مان، گھڻن رنگن جون لائينون ٺاھيو؛

  • process - مٿين ڪمن کي گڏ ڪري ٿو ۽ نتيجو واري تصوير کي ماپڻ جي صلاحيت پڻ شامل ڪري ٿو؛

  • cpp_process_json_str - فنڪشن مٿان چادر process، جيڪو نتيجو برآمد ڪري ٿو آر-آبجیکٹ (گهڻائي عمدي صف)؛

  • cpp_process_json_vector - فنڪشن مٿان چادر cpp_process_json_str، جيڪو توهان کي اجازت ڏئي ٿو هڪ اسٽرنگ ویکٹر کي ملٽي ٿريڊ موڊ ۾ پروسيس ڪري.

گھڻن رنگن جون لائينون ٺاھڻ لاء، HSV رنگ ماڊل استعمال ڪيو ويو، بعد ۾ آر بي بي ۾ تبديلي. اچو ته نتيجو جاچيون:

arr <- cpp_process_json_str(tmp_data[4, drawing])
dim(arr)
# [1] 256 256   3
plot(magick::image_read(arr))

Quick Draw Doodle Recognition: R، C++ ۽ نيورل نيٽ ورڪن سان دوستي ڪيئن ڪجي
R ۽ C++ ۾ عمل درآمد جي رفتار جو مقابلو

res_bench <- bench::mark(
  r_process_json_str(tmp_data[4, drawing], scale = 0.5),
  cpp_process_json_str(tmp_data[4, drawing], scale = 0.5),
  check = FALSE,
  min_iterations = 100
)
# Параметры бенчмарка
cols <- c("expression", "min", "median", "max", "itr/sec", "total_time", "n_itr")
res_bench[, cols]

#   expression                min     median       max `itr/sec` total_time  n_itr
#   <chr>                <bch:tm>   <bch:tm>  <bch:tm>     <dbl>   <bch:tm>  <int>
# 1 r_process_json_str     3.49ms     3.55ms    4.47ms      273.      490ms    134
# 2 cpp_process_json_str   1.94ms     2.02ms    5.32ms      489.      497ms    243

library(ggplot2)
# Проведение замера
res_bench <- bench::press(
  batch_size = 2^(4:10),
  {
    .data <- tmp_data[sample(seq_len(.N), batch_size), drawing]
    bench::mark(
      r_process_json_vector(.data, scale = 0.5),
      cpp_process_json_vector(.data,  scale = 0.5),
      min_iterations = 50,
      check = FALSE
    )
  }
)

res_bench[, cols]

#    expression   batch_size      min   median      max `itr/sec` total_time n_itr
#    <chr>             <dbl> <bch:tm> <bch:tm> <bch:tm>     <dbl>   <bch:tm> <int>
#  1 r                   16   50.61ms  53.34ms  54.82ms    19.1     471.13ms     9
#  2 cpp                 16    4.46ms   5.39ms   7.78ms   192.      474.09ms    91
#  3 r                   32   105.7ms 109.74ms 212.26ms     7.69        6.5s    50
#  4 cpp                 32    7.76ms  10.97ms  15.23ms    95.6     522.78ms    50
#  5 r                   64  211.41ms 226.18ms 332.65ms     3.85      12.99s    50
#  6 cpp                 64   25.09ms  27.34ms  32.04ms    36.0        1.39s    50
#  7 r                  128   534.5ms 627.92ms 659.08ms     1.61      31.03s    50
#  8 cpp                128   56.37ms  58.46ms  66.03ms    16.9        2.95s    50
#  9 r                  256     1.15s    1.18s    1.29s     0.851     58.78s    50
# 10 cpp                256  114.97ms 117.39ms 130.09ms     8.45       5.92s    50
# 11 r                  512     2.09s    2.15s    2.32s     0.463       1.8m    50
# 12 cpp                512  230.81ms  235.6ms 261.99ms     4.18      11.97s    50
# 13 r                 1024        4s    4.22s     4.4s     0.238       3.5m    50
# 14 cpp               1024  410.48ms 431.43ms 462.44ms     2.33      21.45s    50

ggplot(res_bench, aes(x = factor(batch_size), y = median, 
                      group =  expression, color = expression)) +
  geom_point() +
  geom_line() +
  ylab("median time, s") +
  theme_minimal() +
  scale_color_discrete(name = "", labels = c("cpp", "r")) +
  theme(legend.position = "bottom") 

Quick Draw Doodle Recognition: R، C++ ۽ نيورل نيٽ ورڪن سان دوستي ڪيئن ڪجي

جئين توهان ڏسي سگهو ٿا، رفتار جي واڌ تمام اهم ٿي چڪي آهي، ۽ آر ڪوڊ کي متوازي ڪندي C ++ ڪوڊ سان پڪڙڻ ممڪن ناهي.

3. ڊيٽابيس مان بيچز کي لوڊ ڪرڻ لاء آئٽرٽر

R وٽ ڊيٽا جي پروسيسنگ لاءِ سٺي شهرت رکي ٿي جيڪا RAM ۾ ٺهي ٿي، جڏهن ته Python وڌيڪ نمايان ڊيٽا پروسيسنگ جي ڪري آهي، جيڪا توهان کي آساني سان ۽ قدرتي طور تي آئوٽ آف ڪور حسابن تي عمل ڪرڻ جي اجازت ڏئي ٿي (بيروني ميموري استعمال ڪندي حساب). بيان ڪيل مسئلي جي حوالي سان اسان لاءِ هڪ ڪلاسيڪل ۽ لاڳاپيل مثال گہرا نيورل نيٽ ورڪ آهن جيڪي گريڊيئنٽ ڊيسينٽ طريقي سان تربيت ڪيا ويندا آهن هر قدم تي گريڊيئنٽ جي لڳ ڀڳ مشاهدي جو هڪ ننڍڙو حصو استعمال ڪندي، يا ميني بيچ.

پٿون ۾ لکيل ڊيپ لرننگ فريم ورڪ ۾ خاص ڪلاس آھن جيڪي ڊيٽا جي بنياد تي آئٽرٽر کي لاڳو ڪندا آھن: ٽيبل، فولڊر ۾ تصويرون، بائنري فارميٽ وغيره. توھان استعمال ڪري سگھوٿا تيار ڪيل اختيارات يا مخصوص ڪمن لاءِ پنھنجو پاڻ لکندا. R ۾ اسان پائٿون لائبريري جي سڀني خصوصيتن مان فائدو وٺي سگهون ٿا keras ساڳئي نالي جي پيڪيج کي استعمال ڪندي ان جي مختلف پس منظرن سان، جيڪو بدلي ۾ پيڪيج جي چوٽي تي ڪم ڪري ٿو جواب ڏيڻ. بعد ۾ هڪ الڳ ڊگهو مضمون آهي؛ اهو نه صرف توهان کي R مان پٿون ڪوڊ هلائڻ جي اجازت ڏئي ٿو، پر توهان کي آر ۽ پٿون سيشن جي وچ ۾ شيون منتقل ڪرڻ جي اجازت ڏئي ٿو، خودڪار طريقي سان سڀني ضروري قسم جي تبديلين کي انجام ڏئي ٿو.

مونٽ ڊي بي لائيٽ استعمال ڪندي سموري ڊيٽا کي RAM ۾ محفوظ ڪرڻ جي ضرورت کان نجات حاصل ڪئي، سمورو ”نيرل نيٽ ورڪ“ ڪم اصل ڪوڊ ذريعي پٿون ۾ ڪيو ويندو، اسان کي صرف ڊيٽا مٿان هڪ ٽريٽر لکڻو آهي، ڇو ته هتي ڪجهه به تيار ناهي. اهڙي صورتحال لاءِ آر يا پٿون ۾. ان لاءِ بنيادي طور تي صرف ٻه گهرجون آهن: ان کي لازمي طور تي بيچ واپس ڪرڻ گهرجي هڪ لامحدود لوپ ۾ ۽ ان جي حالت کي ٻيهر ورجائڻ جي وچ ۾ محفوظ ڪرڻ گهرجي (بعد ۾ آر کي بندش استعمال ڪندي آسان طريقي سان لاڳو ڪيو ويو آهي). اڳي، ان کي واضح طور تي R arrays کي تبديل ڪرڻ جي ضرورت هئي numpy arrays ۾ iterator اندر، پر پيڪيج جو موجوده نسخو keras پاڻ ڪندو آهي.

ٽريننگ ۽ تصديق واري ڊيٽا لاءِ آئٽرٽر هن ريت نڪتو:

ٽريننگ ۽ تصديق واري ڊيٽا لاءِ آئيٽرٽر

train_generator <- function(db_connection = con,
                            samples_index,
                            num_classes = 340,
                            batch_size = 32,
                            scale = 1,
                            color = FALSE,
                            imagenet_preproc = FALSE) {
  # Проверка аргументов
  checkmate::assert_class(con, "DBIConnection")
  checkmate::assert_integerish(samples_index)
  checkmate::assert_count(num_classes)
  checkmate::assert_count(batch_size)
  checkmate::assert_number(scale, lower = 0.001, upper = 5)
  checkmate::assert_flag(color)
  checkmate::assert_flag(imagenet_preproc)

  # Перемешиваем, чтобы брать и удалять использованные индексы батчей по порядку
  dt <- data.table::data.table(id = sample(samples_index))
  # Проставляем номера батчей
  dt[, batch := (.I - 1L) %/% batch_size + 1L]
  # Оставляем только полные батчи и индексируем
  dt <- dt[, if (.N == batch_size) .SD, keyby = batch]
  # Устанавливаем счётчик
  i <- 1
  # Количество батчей
  max_i <- dt[, max(batch)]

  # Подготовка выражения для выгрузки
  sql <- sprintf(
    "PREPARE SELECT drawing, label_int FROM doodles WHERE id IN (%s)",
    paste(rep("?", batch_size), collapse = ",")
  )
  res <- DBI::dbSendQuery(con, sql)

  # Аналог keras::to_categorical
  to_categorical <- function(x, num) {
    n <- length(x)
    m <- numeric(n * num)
    m[x * n + seq_len(n)] <- 1
    dim(m) <- c(n, num)
    return(m)
  }

  # Замыкание
  function() {
    # Начинаем новую эпоху
    if (i > max_i) {
      dt[, id := sample(id)]
      data.table::setkey(dt, batch)
      # Сбрасываем счётчик
      i <<- 1
      max_i <<- dt[, max(batch)]
    }

    # ID для выгрузки данных
    batch_ind <- dt[batch == i, id]
    # Выгрузка данных
    batch <- DBI::dbFetch(DBI::dbBind(res, as.list(batch_ind)), n = -1)

    # Увеличиваем счётчик
    i <<- i + 1

    # Парсинг JSON и подготовка массива
    batch_x <- cpp_process_json_vector(batch$drawing, scale = scale, color = color)
    if (imagenet_preproc) {
      # Шкалирование c интервала [0, 1] на интервал [-1, 1]
      batch_x <- (batch_x - 0.5) * 2
    }

    batch_y <- to_categorical(batch$label_int, num_classes)
    result <- list(batch_x, batch_y)
    return(result)
  }
}

فنڪشن ڊيٽابيس جي ڪنيڪشن سان هڪ متغير کي ان پٽ طور وٺندو آهي، استعمال ٿيل لائينن جو تعداد، طبقن جو تعداد، بيچ سائيز، اسڪيل (scale = 1 256x256 پکسلز جون تصويرون رينڊرنگ سان ملن ٿيون، scale = 0.5 - 128x128 پکسلز)، رنگ اشارو (color = FALSE استعمال ڪرڻ وقت گراسڪيل ۾ رينڊرنگ بيان ڪري ٿو color = TRUE ھر اسٽروڪ کي نئين رنگ ۾ ٺاھيو ويو آھي) ۽ تصويري نيٽ تي اڳي تربيت ڪيل نيٽ ورڪن لاءِ اڳواٽ پروسيسنگ اشارو. بعد ۾ ضرورت آهي پکسل جي قيمتن کي اسڪيل ڪرڻ لاءِ وقفو [0, 1] کان وقفي تائين [-1, 1] تائين، جيڪو استعمال ڪيو ويو جڏهن فراهم ڪيل تربيت keras ماڊل

خارجي فنڪشن تي مشتمل آهي دليل جي قسم جي چڪاس، هڪ ٽيبل data.table مان بي ترتيب مليل لائن نمبرن سان samples_index ۽ بيچ نمبر، ڪائونٽر ۽ بيچ جو وڌ ۾ وڌ تعداد، گڏو گڏ ڊيٽابيس مان ڊيٽا ڪڍڻ لاءِ SQL ايڪسپريشن. اضافي طور تي، اسان اندر اندر فنڪشن جو هڪ تيز اينالاگ بيان ڪيو آهي keras::to_categorical(). اسان ٽريننگ لاءِ لڳ ڀڳ سموري ڊيٽا استعمال ڪئي، اڌ سيڪڙو تصديق لاءِ ڇڏي ڏنو، تنهن ڪري دور جي ماپ محدود هئي steps_per_epoch جڏهن سڏيو وڃي ٿو keras::fit_generator()، ۽ حالت if (i > max_i) صرف تصديق ڪرڻ واري لاءِ ڪم ڪيو.

اندروني فنڪشن ۾، قطار جي انگن اکرن کي ايندڙ بيچ لاء حاصل ڪيو ويو آهي، رڪارڊ ڊيٽابيس مان لوڊ ٿيل آهن بيچ ڪائونٽر وڌائڻ سان، JSON پارسنگ (فنڪشن cpp_process_json_vector()، C++ ۾ لکيل آهي) ۽ تصويرن سان ملندڙ صفون ٺاهڻ. پوءِ ڪلاس ليبلز سان گڏ ھڪڙو گرم ویکٹر ٺاھيا ويندا آھن، پکسل ويلز سان گڏ صفون ۽ ليبل ھڪڙي فهرست ۾ گڏ ڪيا ويندا آھن، جيڪو موٽڻ جي قيمت آھي. ڪم کي تيز ڪرڻ لاء، اسان جدولن ۾ انڊيڪس جي پيدائش کي استعمال ڪيو data.table ۽ لنڪ ذريعي تبديلي - ان پئڪيج جي بغير "چپس" ڊيٽا. ٽيبل آر ۾ ڊيٽا جي ڪنهن به وڏي مقدار سان مؤثر طريقي سان ڪم ڪرڻ جو تصور ڪرڻ ڏاڍو ڏکيو آهي.

ڪور i5 ليپ ٽاپ تي رفتار جي ماپ جا نتيجا هن ريت آهن:

آئيٽرٽر بينچ مارڪ

library(Rcpp)
library(keras)
library(ggplot2)

source("utils/rcpp.R")
source("utils/keras_iterator.R")

con <- DBI::dbConnect(drv = MonetDBLite::MonetDBLite(), Sys.getenv("DBDIR"))

ind <- seq_len(DBI::dbGetQuery(con, "SELECT count(*) FROM doodles")[[1L]])
num_classes <- DBI::dbGetQuery(con, "SELECT max(label_int) + 1 FROM doodles")[[1L]]

# Индексы для обучающей выборки
train_ind <- sample(ind, floor(length(ind) * 0.995))
# Индексы для проверочной выборки
val_ind <- ind[-train_ind]
rm(ind)
# Коэффициент масштаба
scale <- 0.5

# Проведение замера
res_bench <- bench::press(
  batch_size = 2^(4:10),
  {
    it1 <- train_generator(
      db_connection = con,
      samples_index = train_ind,
      num_classes = num_classes,
      batch_size = batch_size,
      scale = scale
    )
    bench::mark(
      it1(),
      min_iterations = 50L
    )
  }
)
# Параметры бенчмарка
cols <- c("batch_size", "min", "median", "max", "itr/sec", "total_time", "n_itr")
res_bench[, cols]

#   batch_size      min   median      max `itr/sec` total_time n_itr
#        <dbl> <bch:tm> <bch:tm> <bch:tm>     <dbl>   <bch:tm> <int>
# 1         16     25ms  64.36ms   92.2ms     15.9       3.09s    49
# 2         32   48.4ms 118.13ms 197.24ms     8.17       5.88s    48
# 3         64   69.3ms 117.93ms 181.14ms     8.57       5.83s    50
# 4        128  157.2ms 240.74ms 503.87ms     3.85      12.71s    49
# 5        256  359.3ms 613.52ms 988.73ms     1.54       30.5s    47
# 6        512  884.7ms    1.53s    2.07s     0.674      1.11m    45
# 7       1024     2.7s    3.83s    5.47s     0.261      2.81m    44

ggplot(res_bench, aes(x = factor(batch_size), y = median, group = 1)) +
    geom_point() +
    geom_line() +
    ylab("median time, s") +
    theme_minimal()

DBI::dbDisconnect(con, shutdown = TRUE)

Quick Draw Doodle Recognition: R، C++ ۽ نيورل نيٽ ورڪن سان دوستي ڪيئن ڪجي

جيڪڏهن توهان وٽ ڪافي مقدار ۾ رام آهي، ته توهان ڊيٽابيس جي آپريشن کي تيز ڪري سگهو ٿا ان کي ساڳئي ريم ڏانهن منتقل ڪندي (اسان جي ڪم لاء 32 GB ڪافي آهي). لينڪس ۾، ورهاڱي ڊفالٽ طور تي نصب ٿيل آهي /dev/shm، RAM جي اڌ جي گنجائش تي قبضو. توھان ايڊٽ ڪندي وڌيڪ نمايان ڪري سگھو ٿا /etc/fstabهڪ رڪارڊ حاصل ڪرڻ لاء tmpfs /dev/shm tmpfs defaults,size=25g 0 0. ريبوٽ ڪرڻ جي پڪ ڪريو ۽ حڪم هلائڻ سان نتيجو چيڪ ڪريو df -h.

ٽيسٽ ڊيٽا لاءِ آئٽرٽر تمام سادو نظر اچي ٿو، ڇاڪاڻ ته ٽيسٽ ڊيٽا سيٽ مڪمل طور تي رام ۾ اچي ٿو:

ٽيسٽ ڊيٽا لاءِ آئٽرٽر

test_generator <- function(dt,
                           batch_size = 32,
                           scale = 1,
                           color = FALSE,
                           imagenet_preproc = FALSE) {

  # Проверка аргументов
  checkmate::assert_data_table(dt)
  checkmate::assert_count(batch_size)
  checkmate::assert_number(scale, lower = 0.001, upper = 5)
  checkmate::assert_flag(color)
  checkmate::assert_flag(imagenet_preproc)

  # Проставляем номера батчей
  dt[, batch := (.I - 1L) %/% batch_size + 1L]
  data.table::setkey(dt, batch)
  i <- 1
  max_i <- dt[, max(batch)]

  # Замыкание
  function() {
    batch_x <- cpp_process_json_vector(dt[batch == i, drawing], 
                                       scale = scale, color = color)
    if (imagenet_preproc) {
      # Шкалирование c интервала [0, 1] на интервал [-1, 1]
      batch_x <- (batch_x - 0.5) * 2
    }
    result <- list(batch_x)
    i <<- i + 1
    return(result)
  }
}

4. ماڊل آرڪيٽيڪچر جو انتخاب

استعمال ٿيل پهريون فن تعمير هو موبائل نيٽ v1، جن جي خاصيتن ۾ بحث ڪيو ويو آهي هي پيغام. اهو معياري طور تي شامل آهي keras ۽، مطابق، آر لاءِ ساڳئي نالي جي پيڪيج ۾ موجود آهي. پر جڏهن ان کي سنگل چينل تصويرن سان استعمال ڪرڻ جي ڪوشش ڪئي وئي، هڪ عجيب ڳالهه سامهون آئي: ان پٽ ٽينسر کي هميشه طول و عرض هجڻ گهرجي. (batch, height, width, 3)، اهو آهي، چينلن جو تعداد تبديل نه ٿو ڪري سگھجي. Python ۾ اهڙي ڪا به حد ناهي، تنهنڪري اسان جلدي ڪئي ۽ هن آرڪيٽيڪچر جي پنهنجي عمل کي لکيو، اصل مضمون جي پٺيان (بغير ڊراپ آئوٽ جيڪو ڪيراس ورزن ۾ آهي):

Mobilenet v1 فن تعمير

library(keras)

top_3_categorical_accuracy <- custom_metric(
    name = "top_3_categorical_accuracy",
    metric_fn = function(y_true, y_pred) {
         metric_top_k_categorical_accuracy(y_true, y_pred, k = 3)
    }
)

layer_sep_conv_bn <- function(object, 
                              filters,
                              alpha = 1,
                              depth_multiplier = 1,
                              strides = c(2, 2)) {

  # NB! depth_multiplier !=  resolution multiplier
  # https://github.com/keras-team/keras/issues/10349

  layer_depthwise_conv_2d(
    object = object,
    kernel_size = c(3, 3), 
    strides = strides,
    padding = "same",
    depth_multiplier = depth_multiplier
  ) %>%
  layer_batch_normalization() %>% 
  layer_activation_relu() %>%
  layer_conv_2d(
    filters = filters * alpha,
    kernel_size = c(1, 1), 
    strides = c(1, 1)
  ) %>%
  layer_batch_normalization() %>% 
  layer_activation_relu() 
}

get_mobilenet_v1 <- function(input_shape = c(224, 224, 1),
                             num_classes = 340,
                             alpha = 1,
                             depth_multiplier = 1,
                             optimizer = optimizer_adam(lr = 0.002),
                             loss = "categorical_crossentropy",
                             metrics = c("categorical_crossentropy",
                                         top_3_categorical_accuracy)) {

  inputs <- layer_input(shape = input_shape)

  outputs <- inputs %>%
    layer_conv_2d(filters = 32, kernel_size = c(3, 3), strides = c(2, 2), padding = "same") %>%
    layer_batch_normalization() %>% 
    layer_activation_relu() %>%
    layer_sep_conv_bn(filters = 64, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 128, strides = c(2, 2)) %>%
    layer_sep_conv_bn(filters = 128, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 256, strides = c(2, 2)) %>%
    layer_sep_conv_bn(filters = 256, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(2, 2)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 1024, strides = c(2, 2)) %>%
    layer_sep_conv_bn(filters = 1024, strides = c(1, 1)) %>%
    layer_global_average_pooling_2d() %>%
    layer_dense(units = num_classes) %>%
    layer_activation_softmax()

    model <- keras_model(
      inputs = inputs,
      outputs = outputs
    )

    model %>% compile(
      optimizer = optimizer,
      loss = loss,
      metrics = metrics
    )

    return(model)
}

هن طريقي جي نقصان پڌرو آهي. مان ڪيترن ئي ماڊلز کي جانچڻ چاهيان ٿو، پر ان جي برعڪس، مان هر فن تعمير کي دستي طور تي ٻيهر لکڻ نٿو چاهيان. اسان تصويرنيٽ تي اڳ-تربيت ٿيل ماڊل جي وزن کي استعمال ڪرڻ جي موقعي کان پڻ محروم ٿي ويا. هميشه وانگر، دستاويز جي مطالعي ۾ مدد ڪئي. فنڪشن get_config() توهان کي ماڊل جي وضاحت حاصل ڪرڻ جي اجازت ڏئي ٿي جيڪا ترميم لاءِ موزون آهي (base_model_conf$layers - هڪ باقاعده آر لسٽ)، ۽ فنڪشن from_config() هڪ ماڊل اعتراض کي ريورس ڪنورشن انجام ڏئي ٿو:

base_model_conf <- get_config(base_model)
base_model_conf$layers[[1]]$config$batch_input_shape[[4]] <- 1L
base_model <- from_config(base_model_conf)

هاڻي اهو ڏکيو ناهي ته هڪ آفاقي فنڪشن لکڻ لاء ڪنهن به فراهم ڪيل حاصل ڪرڻ لاء keras تصويري نيٽ تي تربيت يافته وزن سان يا بغير ماڊل:

تيار ٿيل تعميرات کي لوڊ ڪرڻ لاء فنڪشن

get_model <- function(name = "mobilenet_v2",
                      input_shape = NULL,
                      weights = "imagenet",
                      pooling = "avg",
                      num_classes = NULL,
                      optimizer = keras::optimizer_adam(lr = 0.002),
                      loss = "categorical_crossentropy",
                      metrics = NULL,
                      color = TRUE,
                      compile = FALSE) {
  # Проверка аргументов
  checkmate::assert_string(name)
  checkmate::assert_integerish(input_shape, lower = 1, upper = 256, len = 3)
  checkmate::assert_count(num_classes)
  checkmate::assert_flag(color)
  checkmate::assert_flag(compile)

  # Получаем объект из пакета keras
  model_fun <- get0(paste0("application_", name), envir = asNamespace("keras"))
  # Проверка наличия объекта в пакете
  if (is.null(model_fun)) {
    stop("Model ", shQuote(name), " not found.", call. = FALSE)
  }

  base_model <- model_fun(
    input_shape = input_shape,
    include_top = FALSE,
    weights = weights,
    pooling = pooling
  )

  # Если изображение не цветное, меняем размерность входа
  if (!color) {
    base_model_conf <- keras::get_config(base_model)
    base_model_conf$layers[[1]]$config$batch_input_shape[[4]] <- 1L
    base_model <- keras::from_config(base_model_conf)
  }

  predictions <- keras::get_layer(base_model, "global_average_pooling2d_1")$output
  predictions <- keras::layer_dense(predictions, units = num_classes, activation = "softmax")
  model <- keras::keras_model(
    inputs = base_model$input,
    outputs = predictions
  )

  if (compile) {
    keras::compile(
      object = model,
      optimizer = optimizer,
      loss = loss,
      metrics = metrics
    )
  }

  return(model)
}

جڏهن سنگل چينل تصويرون استعمال ڪندي، ڪو به اڳواٽ وزن استعمال نه ڪيو ويندو آهي. اهو طئي ٿي سگهي ٿو: فنڪشن استعمال ڪندي get_weights() R arrays جي فهرست جي صورت ۾ ماڊل وزن حاصل ڪريو، ھن لسٽ جي پھرين عنصر جي طول و عرض کي تبديل ڪريو (ھڪڙو رنگ چينل کڻڻ يا سڀني ٽن جي اوسط سان)، ۽ پوء وزن کي واپس ماڊل ۾ فنڪشن سان لوڊ ڪريو. set_weights(). اسان ڪڏهن به هن ڪارڪردگي کي شامل نه ڪيو، ڇاڪاڻ ته هن اسٽيج تي اهو اڳ ۾ ئي واضح هو ته اهو رنگ تصويرن سان ڪم ڪرڻ لاء وڌيڪ پيداوار هو.

اسان موبائل نيٽ ورزن 1 ۽ 2، گڏوگڏ resnet34 استعمال ڪندي اڪثر تجربا ڪيا. وڌيڪ جديد فن تعمير جهڙوڪ SE-ResNeXt هن مقابلي ۾ سٺي نموني ڪئي. بدقسمتي سان، اسان جي اختيار ۾ تيار ڪيل عمل نه هئا، ۽ اسان پنهنجو پاڻ کي نه لکيو (پر اسين ضرور لکنداسين).

5. اسڪرپٽ جي پيٽرولائيزيشن

سهولت لاءِ، ٽريننگ شروع ڪرڻ لاءِ سڀ ڪوڊ هڪ واحد اسڪرپٽ جي طور تي ٺهيل هئا، استعمال ڪندي پيرا ميٽرز docopt جيئن ته:

doc <- '
Usage:
  train_nn.R --help
  train_nn.R --list-models
  train_nn.R [options]

Options:
  -h --help                   Show this message.
  -l --list-models            List available models.
  -m --model=<model>          Neural network model name [default: mobilenet_v2].
  -b --batch-size=<size>      Batch size [default: 32].
  -s --scale-factor=<ratio>   Scale factor [default: 0.5].
  -c --color                  Use color lines [default: FALSE].
  -d --db-dir=<path>          Path to database directory [default: Sys.getenv("db_dir")].
  -r --validate-ratio=<ratio> Validate sample ratio [default: 0.995].
  -n --n-gpu=<number>         Number of GPUs [default: 1].
'
args <- docopt::docopt(doc)

پيڪ docopt عمل درآمد جي نمائندگي ڪري ٿو http://docopt.org/ آر لاءِ. ان جي مدد سان، اسڪرپٽ شروع ڪيا ويا آهن سادو حڪمن سان Rscript bin/train_nn.R -m resnet50 -c -d /home/andrey/doodle_db يا ./bin/train_nn.R -m resnet50 -c -d /home/andrey/doodle_db، جيڪڏهن فائل train_nn.R قابل عمل آهي (هي حڪم ماڊل کي تربيت ڏيڻ شروع ڪندو resnet50 ٽن رنگن جي تصويرن تي 128x128 پکسلز جي ماپ، ڊيٽابيس کي فولڊر ۾ واقع هجڻ گهرجي /home/andrey/doodle_db). توھان شامل ڪري سگھوٿا سکيا جي رفتار، اصلاحي قسم، ۽ ڪي ٻيا حسب ضرورت پيرا ميٽر لسٽ ۾. اشاعت جي تياري جي عمل ۾، اهو ظاهر ٿيو ته فن تعمير mobilenet_v2 موجوده نسخي کان keras آر جي استعمال ۾ نه ٿي سگهي آر پيڪيج ۾ اڪائونٽ ۾ نه ڪيل تبديلين جي ڪري، اسان ان کي درست ڪرڻ لاء انتظار ڪري رهيا آهيون.

هن طريقي سان RStudio ۾ اسڪرپٽ جي وڌيڪ روايتي لانچ جي مقابلي ۾ مختلف ماڊلز سان تجربن کي تيز ڪرڻ ممڪن بڻايو (اسان پيڪيج کي هڪ ممڪن متبادل طور نوٽ ڪيو. tfruns). پر بنيادي فائدو اهو آهي ته آساني سان ڊڪر ۾ اسڪرپٽس جي لانچ کي منظم ڪرڻ جي صلاحيت يا صرف سرور تي، ان لاءِ RStudio انسٽال ڪرڻ کان سواءِ.

6. لکت جي Dockerization

اسان Docker استعمال ڪيو ماحول جي پورائيبلٽي کي يقيني بڻائڻ لاءِ ٽيم جي ميمبرن جي وچ ۾ ٽريننگ ماڊلز ۽ بادل ۾ تيزيءَ سان ترتيب ڏيڻ لاءِ. توهان هن اوزار سان واقف ٿيڻ شروع ڪري سگهو ٿا، جيڪو هڪ آر پروگرامر لاء نسبتا غير معمولي آهي هي اشاعتن جو سلسلو يا وڊيو ڪورس.

Docker توهان کي اجازت ڏئي ٿو ته توهان ٻنهي کي شروع کان پنهنجون پنهنجون تصويرون ٺاهي ۽ ٻيون تصويرون استعمال ڪري پنهنجو پاڻ ٺاهڻ جي بنياد طور. جڏهن موجود اختيارن جو تجزيو ڪندي، اسان ان نتيجي تي پهتاسين ته NVIDIA، CUDA + cuDNN ڊرائيورز ۽ پٿون لائبريرين کي انسٽال ڪرڻ تصوير جو هڪ تمام وڏو حصو آهي، ۽ اسان فيصلو ڪيو ته سرڪاري تصوير کي بنياد طور وٺي. tensorflow/tensorflow:1.12.0-gpu، اتي ضروري آر پيڪيجز شامل ڪريو.

فائنل ڊاکر فائل هن طرح ڏٺو:

Dockerfile

FROM tensorflow/tensorflow:1.12.0-gpu

MAINTAINER Artem Klevtsov <[email protected]>

SHELL ["/bin/bash", "-c"]

ARG LOCALE="en_US.UTF-8"
ARG APT_PKG="libopencv-dev r-base r-base-dev littler"
ARG R_BIN_PKG="futile.logger checkmate data.table rcpp rapidjsonr dbi keras jsonlite curl digest remotes"
ARG R_SRC_PKG="xtensor RcppThread docopt MonetDBLite"
ARG PY_PIP_PKG="keras"
ARG DIRS="/db /app /app/data /app/models /app/logs"

RUN source /etc/os-release && 
    echo "deb https://cloud.r-project.org/bin/linux/ubuntu ${UBUNTU_CODENAME}-cran35/" > /etc/apt/sources.list.d/cran35.list && 
    apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E084DAB9 && 
    add-apt-repository -y ppa:marutter/c2d4u3.5 && 
    add-apt-repository -y ppa:timsc/opencv-3.4 && 
    apt-get update && 
    apt-get install -y locales && 
    locale-gen ${LOCALE} && 
    apt-get install -y --no-install-recommends ${APT_PKG} && 
    ln -s /usr/lib/R/site-library/littler/examples/install.r /usr/local/bin/install.r && 
    ln -s /usr/lib/R/site-library/littler/examples/install2.r /usr/local/bin/install2.r && 
    ln -s /usr/lib/R/site-library/littler/examples/installGithub.r /usr/local/bin/installGithub.r && 
    echo 'options(Ncpus = parallel::detectCores())' >> /etc/R/Rprofile.site && 
    echo 'options(repos = c(CRAN = "https://cloud.r-project.org"))' >> /etc/R/Rprofile.site && 
    apt-get install -y $(printf "r-cran-%s " ${R_BIN_PKG}) && 
    install.r ${R_SRC_PKG} && 
    pip install ${PY_PIP_PKG} && 
    mkdir -p ${DIRS} && 
    chmod 777 ${DIRS} && 
    rm -rf /tmp/downloaded_packages/ /tmp/*.rds && 
    rm -rf /var/lib/apt/lists/*

COPY utils /app/utils
COPY src /app/src
COPY tests /app/tests
COPY bin/*.R /app/

ENV DBDIR="/db"
ENV CUDA_HOME="/usr/local/cuda"
ENV PATH="/app:${PATH}"

WORKDIR /app

VOLUME /db
VOLUME /app

CMD bash

سهولت لاءِ، استعمال ٿيل پيڪيجز متغير ۾ رکيا ويا. لکت واري لکت جو وڏو حصو اسيمبليءَ دوران ڪنٽينر اندر نقل ڪيو ويندو آهي. اسان پڻ ڪمانڊ شيل کي تبديل ڪيو /bin/bash مواد جي استعمال جي آسانيء لاء /etc/os-release. هن ڪوڊ ۾ OS ورجن جي وضاحت ڪرڻ جي ضرورت کان پاسو ڪيو.

اضافي طور تي، هڪ ننڍڙو بش اسڪرپٽ لکيو ويو آهي جيڪو توهان کي مختلف حڪمن سان ڪنٽينر لانچ ڪرڻ جي اجازت ڏئي ٿو. مثال طور، اهي نيورل نيٽ ورڪ جي تربيت لاءِ لکتون ٿي سگهن ٿيون جيڪي اڳ ۾ ڪنٽينر جي اندر رکيل هيون، يا ڪنٽينر جي آپريشن کي ڊيبگ ڪرڻ ۽ نگراني ڪرڻ لاءِ ڪمانڊ شيل:

ڪنٽينر کي لانچ ڪرڻ لاءِ اسڪرپٽ

#!/bin/sh

DBDIR=${PWD}/db
LOGSDIR=${PWD}/logs
MODELDIR=${PWD}/models
DATADIR=${PWD}/data
ARGS="--runtime=nvidia --rm -v ${DBDIR}:/db -v ${LOGSDIR}:/app/logs -v ${MODELDIR}:/app/models -v ${DATADIR}:/app/data"

if [ -z "$1" ]; then
    CMD="Rscript /app/train_nn.R"
elif [ "$1" = "bash" ]; then
    ARGS="${ARGS} -ti"
else
    CMD="Rscript /app/train_nn.R $@"
fi

docker run ${ARGS} doodles-tf ${CMD}

جيڪڏهن هي بش اسڪرپٽ بغير پيرا ميٽرن جي هلندي، اسڪرپٽ کي ڪنٽينر اندر سڏيو ويندو train_nn.R ڊفالٽ قدرن سان؛ جيڪڏهن پهريون پوزيشن دليل آهي "bash"، پوء ڪنٽينر ڪمانڊ شيل سان مداخلت سان شروع ڪندو. ٻين سڀني حالتن ۾، پوزيشن دليلن جا قدر متبادل آهن: CMD="Rscript /app/train_nn.R $@".

اها ڳالهه نوٽ ڪرڻ جي قابل آهي ته ڊائريڪٽري ماخذ ڊيٽا ۽ ڊيٽابيس سان گڏو گڏ، گڏو گڏ تربيتي ماڊلز کي محفوظ ڪرڻ لاء ڊائريڪٽري، ميزبان نظام کان ڪنٽينر اندر نصب ٿيل آهن، جيڪا توهان کي اجازت ڏئي ٿي لکت جي نتيجن تائين رسائي حاصل ڪرڻ جي بغير غير ضروري ڦيرڦار جي.

7. گوگل ڪلائوڊ تي گھڻن GPUs استعمال ڪرڻ

مقابلي جي خاصيتن مان هڪ تمام گهڻو شور وارو ڊيٽا هو (ڏسو عنوان تصوير، ODS slack مان @Leigh.plt کان قرض ورتو ويو). وڏيون بيچ هن کي وڙهڻ ۾ مدد ڪن ٿيون، ۽ 1 GPU سان هڪ PC تي تجربن کان پوءِ، اسان فيصلو ڪيو ته ڪلائوڊ ۾ ڪيترن ئي GPUs تي ٽريننگ ماڊلز کي ماسٽر ڪرڻ جو. استعمال ٿيل GoogleCloud (بنياديات لاء سٺو گائيڊ) دستياب ترتيبن جي وڏي چونڊ جي ڪري، مناسب قيمتون ۽ $300 بونس. لالچ کان ٻاهر، مون آرڊر ڪيو 4xV100 مثال هڪ SSD ۽ هڪ ٽين ريم سان، ۽ اها هڪ وڏي غلطي هئي. اهڙي مشين جلدي پئسا کائي ٿي؛ توهان بغير ثابت ٿيل پائپ لائن جي ڀڃڪڙي تجربو ڪري سگهو ٿا. تعليمي مقصدن لاء، ان کي وٺڻ لاء ڀلو آھي K80. پر رام جي وڏي مقدار هٿ ۾ آئي - بادل ايس ايس ڊي پنهنجي ڪارڪردگي سان متاثر نه ڪيو، تنهنڪري ڊيٽابيس کي منتقل ڪيو ويو. dev/shm.

تمام گهڻي دلچسپي جو ڪوڊ ٽڪرو آهي جيڪو ڪيترن ئي GPU استعمال ڪرڻ لاء ذميوار آهي. پهريون، ماڊل ٺاهي وئي آهي سي پي يو تي هڪ حوالي سان مينيجر استعمال ڪندي، جهڙوڪ پٿون ۾:

with(tensorflow::tf$device("/cpu:0"), {
  model_cpu <- get_model(
    name = model_name,
    input_shape = input_shape,
    weights = weights,
    metrics =(top_3_categorical_accuracy,
    compile = FALSE
  )
})

ان کان پوء اڻڄاتل (اهو اهم آهي) ماڊل کي نقل ڪيو ويو آهي ڏنل تعداد ۾ موجود GPUs، ۽ صرف ان کان پوء اهو مرتب ڪيو ويو آهي:

model <- keras::multi_gpu_model(model_cpu, gpus = n_gpu)
keras::compile(
  object = model,
  optimizer = keras::optimizer_adam(lr = 0.0004),
  loss = "categorical_crossentropy",
  metrics = c(top_3_categorical_accuracy)
)

آخري پرت کي منجمد ڪرڻ جي کلاسک ٽيڪنڪ، آخري پرت جي تربيت، ڪيترن ئي GPUs لاءِ پوري ماڊل کي منجمد ڪرڻ ۽ ٻيهر تربيت ڏيڻ تي عمل نه ٿي سگهيو.

ٽريننگ بغير استعمال جي نگراني ڪئي وئي. ٽينسر بورڊهر دور کان پوءِ معلوماتي نالن سان گڏ رڪارڊنگ لاگز ۽ ماڊلز کي محفوظ ڪرڻ تائين پاڻ کي محدود ڪرڻ:

ڪال بڪ

# Шаблон имени файла лога
log_file_tmpl <- file.path("logs", sprintf(
  "%s_%d_%dch_%s.csv",
  model_name,
  dim_size,
  channels,
  format(Sys.time(), "%Y%m%d%H%M%OS")
))
# Шаблон имени файла модели
model_file_tmpl <- file.path("models", sprintf(
  "%s_%d_%dch_{epoch:02d}_{val_loss:.2f}.h5",
  model_name,
  dim_size,
  channels
))

callbacks_list <- list(
  keras::callback_csv_logger(
    filename = log_file_tmpl
  ),
  keras::callback_early_stopping(
    monitor = "val_loss",
    min_delta = 1e-4,
    patience = 8,
    verbose = 1,
    mode = "min"
  ),
  keras::callback_reduce_lr_on_plateau(
    monitor = "val_loss",
    factor = 0.5, # уменьшаем lr в 2 раза
    patience = 4,
    verbose = 1,
    min_delta = 1e-4,
    mode = "min"
  ),
  keras::callback_model_checkpoint(
    filepath = model_file_tmpl,
    monitor = "val_loss",
    save_best_only = FALSE,
    save_weights_only = FALSE,
    mode = "min"
  )
)

8. نتيجي جي بدران

ڪجھ مسئلا جيڪي اسان جي سامھون آھن اڃا تائين ختم نه ٿي سگھيا آھن:

  • в keras خود بخود بهترين سکيا جي شرح (اينالاگ lr_finder لائبريري ۾ تيز.اي؛ ڪجھ ڪوشش سان، اھو ممڪن آھي ته ٽئين پارٽي جي عملن کي پورٽ ڪرڻ لاءِ آر، مثال طور، هي;
  • پوئين نقطي جي نتيجي ۾، ڪيترن ئي GPUs کي استعمال ڪندي صحيح تربيت جي رفتار کي چونڊڻ ممڪن نه هو؛
  • جديد نيورل نيٽ ورڪ آرڪيٽيڪچر جي کوٽ آهي، خاص طور تي جيڪي اڳ ۾ تربيت يافته تصويري نيٽ ورڪ تي.
  • ڪو به هڪ سائيڪل پاليسي ۽ تبعيض واري سکيا جي شرح (ڪوزائن اينيلنگ اسان جي درخواست تي هئي لاڳو ڪيو ويو، توهان جي مهرباني اسڪيڊان).

هن مقابلي مان ڪهڙيون مفيد شيون سکيون ويون:

  • نسبتا گھٽ پاور هارڊويئر تي، توهان ڪم ڪري سگهو ٿا مهذب (ڪيترائي ڀيرا ريم جي سائيز) ڊيٽا جي مقدار جي بغير درد جي. پلاسٽڪ جي ٿيلھي ڊيٽا. ٽيبل جدولن جي جاءِ تي تبديليءَ جي ڪري ميموري بچائي ٿي، جيڪا انھن کي نقل ڪرڻ کان پاسو ڪري ٿي، ۽ جڏھن صحيح طريقي سان استعمال ٿئي ٿي، ته ان جون صلاحيتون تقريبن ھميشه سڀني اوزارن جي وچ ۾ تمام تيز رفتار ڏيکارين ٿيون، جيڪي اسان کي ٻولين جي لکت لاءِ سڃاتل آھن. ڊيٽابيس ۾ ڊيٽا محفوظ ڪرڻ توهان کي اجازت ڏئي ٿي، ڪيترن ئي ڪيسن ۾، سڄي ڊيٽا سيٽ کي رام ۾ نچوض ڪرڻ جي ضرورت جي باري ۾ سوچڻ جي ضرورت ناهي.
  • آر ۾ سست افعال کي پيڪيج استعمال ڪندي C++ ۾ تيز رفتار سان تبديل ڪري سگھجي ٿو آر سي پي پي. جيڪڏهن استعمال ڪرڻ کان علاوه RcppThread يا آر سي پي پي پارلل، اسان حاصل ڪريون ٿا ڪراس-پليٽفارم ملٽي-ٽيڊڊ پليپشنز، تنهنڪري ڪوڊ کي آر سطح تي متوازي ڪرڻ جي ڪا ضرورت ناهي.
  • پيڪيج آر سي پي پي C++ جي سنجيده ڄاڻ کان سواءِ استعمال ڪري سگھجي ٿو، گھربل گھٽ ۾ گھٽ بيان ڪيل آھي هتي. هيڊر فائلون ڪيترن ئي سڪل سي-لائبريري لاءِ xtensor CRAN تي دستياب آهي، يعني، هڪ انفراسٽرڪچر ٺاهي رهيو آهي منصوبن تي عمل درآمد لاءِ جيڪو تيار ڪيل اعليٰ ڪارڪردگي C++ ڪوڊ کي R ۾ ضم ڪري ٿو. اضافي سهولت نحو کي نمايان ڪرڻ ۽ RStudio ۾ هڪ جامد C ++ ڪوڊ تجزيه ڪندڙ آهي.
  • docopt توهان کي اجازت ڏئي ٿو ته پاڻمرادو اسڪرپٽس کي پيرا ميٽرن سان هلائڻ لاءِ. اهو ريموٽ سرور تي استعمال لاءِ آسان آهي، بشمول. ڊڪر جي هيٺان. RStudio ۾، اعصابي نيٽ ورڪن جي تربيت سان ڪيترن ئي ڪلاڪن جا تجربا ڪرڻ مشڪل آهي، ۽ خود سرور تي IDE انسٽال ڪرڻ هميشه جائز نه هوندو آهي.
  • ڊاڪر ڪوڊ پورٽيبلٽي ۽ ڊولپرز جي وچ ۾ نتيجن جي ٻيهر پيداوار کي يقيني بڻائي ٿو OS ۽ لائبريرين جي مختلف نسخن سان، انهي سان گڏ سرور تي عملدرآمد جي آسانيءَ سان. توھان شروع ڪري سگھوٿا پوري ٽريننگ پائپ لائن صرف ھڪڙي حڪم سان.
  • گوگل ڪلائوڊ قيمتي هارڊويئر تي تجربو ڪرڻ لاءِ هڪ بجيٽ-دوست طريقو آهي، پر توهان کي احتياط سان ترتيبن کي چونڊڻ جي ضرورت آهي.
  • انفرادي ڪوڊ جي ٽڪرن جي رفتار کي ماپڻ تمام مفيد آهي، خاص طور تي جڏهن R ۽ C++ کي گڏ ڪرڻ، ۽ پيڪيج سان. بينچ - پڻ تمام آسان.

مجموعي طور تي هي تجربو تمام گهڻو ثواب وارو هو ۽ اسان ڪجهه مسئلن کي حل ڪرڻ لاء ڪم جاري رکون ٿا.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو