ತ್ವರಿತ ಡ್ರಾ ಡೂಡಲ್ ಗುರುತಿಸುವಿಕೆ: ಆರ್, ಸಿ++ ಮತ್ತು ನ್ಯೂರಲ್ ನೆಟ್‌ವರ್ಕ್‌ಗಳೊಂದಿಗೆ ಸ್ನೇಹಿತರಾಗುವುದು ಹೇಗೆ

ತ್ವರಿತ ಡ್ರಾ ಡೂಡಲ್ ಗುರುತಿಸುವಿಕೆ: ಆರ್, ಸಿ++ ಮತ್ತು ನ್ಯೂರಲ್ ನೆಟ್‌ವರ್ಕ್‌ಗಳೊಂದಿಗೆ ಸ್ನೇಹಿತರಾಗುವುದು ಹೇಗೆ

ಹಲೋ, ಹಬ್ರ್!

ಕಳೆದ ಶರತ್ಕಾಲದಲ್ಲಿ, ಕೈಯಿಂದ ಚಿತ್ರಿಸಿದ ಚಿತ್ರಗಳನ್ನು ವರ್ಗೀಕರಿಸಲು ಕಾಗ್ಲೆ ಸ್ಪರ್ಧೆಯನ್ನು ಆಯೋಜಿಸಿದರು, ಕ್ವಿಕ್ ಡ್ರಾ ಡೂಡಲ್ ರೆಕಗ್ನಿಷನ್, ಇದರಲ್ಲಿ ಇತರರಲ್ಲಿ, ಆರ್-ವಿಜ್ಞಾನಿಗಳ ತಂಡವು ಭಾಗವಹಿಸಿತು: ಆರ್ಟೆಮ್ ಕ್ಲೆವ್ಟ್ಸೊವಾ, ಫಿಲಿಪ್ಪಾ ಮ್ಯಾನೇಜರ್ и ಆಂಡ್ರೆ ಒಗುರ್ಟ್ಸೊವ್. ನಾವು ಸ್ಪರ್ಧೆಯನ್ನು ವಿವರವಾಗಿ ವಿವರಿಸುವುದಿಲ್ಲ; ಅದನ್ನು ಈಗಾಗಲೇ ಮಾಡಲಾಗಿದೆ ಇತ್ತೀಚಿನ ಪ್ರಕಟಣೆ.

ಈ ಬಾರಿ ಇದು ಪದಕ ಕೃಷಿಯೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲಿಲ್ಲ, ಆದರೆ ಬಹಳಷ್ಟು ಅಮೂಲ್ಯವಾದ ಅನುಭವವನ್ನು ಪಡೆಯಲಾಗಿದೆ, ಆದ್ದರಿಂದ ನಾನು ಕಾಗ್ಲೆ ಮತ್ತು ದೈನಂದಿನ ಕೆಲಸದಲ್ಲಿ ಹಲವಾರು ಆಸಕ್ತಿದಾಯಕ ಮತ್ತು ಉಪಯುಕ್ತ ವಿಷಯಗಳ ಬಗ್ಗೆ ಸಮುದಾಯಕ್ಕೆ ಹೇಳಲು ಬಯಸುತ್ತೇನೆ. ಚರ್ಚಿಸಿದ ವಿಷಯಗಳ ಪೈಕಿ: ಕಷ್ಟದ ಜೀವನ ಓಪನ್‌ಸಿವಿ, JSON ಪಾರ್ಸಿಂಗ್ (ಈ ಉದಾಹರಣೆಗಳು C++ ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳು ಅಥವಾ ಪ್ಯಾಕೇಜ್‌ಗಳಿಗೆ R ಬಳಸಿಕೊಂಡು ಏಕೀಕರಣವನ್ನು ಪರೀಕ್ಷಿಸುತ್ತವೆ Rcpp), ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳ ನಿಯತಾಂಕೀಕರಣ ಮತ್ತು ಅಂತಿಮ ಪರಿಹಾರದ ಡಾಕರೈಸೇಶನ್. ಕಾರ್ಯಗತಗೊಳಿಸಲು ಸೂಕ್ತವಾದ ರೂಪದಲ್ಲಿ ಸಂದೇಶದಿಂದ ಎಲ್ಲಾ ಕೋಡ್ ಲಭ್ಯವಿದೆ ಭಂಡಾರಗಳು.

ಪರಿವಿಡಿ:

  1. CSV ನಿಂದ MonetDB ಗೆ ಡೇಟಾವನ್ನು ಪರಿಣಾಮಕಾರಿಯಾಗಿ ಲೋಡ್ ಮಾಡಿ
  2. ಬ್ಯಾಚ್‌ಗಳನ್ನು ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ
  3. ಡೇಟಾಬೇಸ್‌ನಿಂದ ಬ್ಯಾಚ್‌ಗಳನ್ನು ಇಳಿಸಲು ಪುನರಾವರ್ತಕಗಳು
  4. ಮಾದರಿ ಆರ್ಕಿಟೆಕ್ಚರ್ ಆಯ್ಕೆ
  5. ಸ್ಕ್ರಿಪ್ಟ್ ನಿಯತಾಂಕೀಕರಣ
  6. ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳ ಡಾಕರೈಸೇಶನ್
  7. Google ಮೇಘದಲ್ಲಿ ಬಹು GPU ಗಳನ್ನು ಬಳಸುವುದು
  8. ಬದಲಿಗೆ ತೀರ್ಮಾನದ

1. MonetDB ಡೇಟಾಬೇಸ್‌ಗೆ CSV ಯಿಂದ ಡೇಟಾವನ್ನು ಪರಿಣಾಮಕಾರಿಯಾಗಿ ಲೋಡ್ ಮಾಡಿ

ಈ ಸ್ಪರ್ಧೆಯಲ್ಲಿ ಡೇಟಾವನ್ನು ಸಿದ್ಧಪಡಿಸಿದ ಚಿತ್ರಗಳ ರೂಪದಲ್ಲಿ ಒದಗಿಸಲಾಗಿಲ್ಲ, ಆದರೆ ಪಾಯಿಂಟ್ ನಿರ್ದೇಶಾಂಕಗಳೊಂದಿಗೆ JSON ಗಳನ್ನು ಹೊಂದಿರುವ 340 CSV ಫೈಲ್‌ಗಳ ರೂಪದಲ್ಲಿ (ಪ್ರತಿ ವರ್ಗಕ್ಕೆ ಒಂದು ಫೈಲ್) ಒದಗಿಸಲಾಗಿದೆ. ಈ ಬಿಂದುಗಳನ್ನು ರೇಖೆಗಳೊಂದಿಗೆ ಸಂಪರ್ಕಿಸುವ ಮೂಲಕ, ನಾವು 256x256 ಪಿಕ್ಸೆಲ್‌ಗಳನ್ನು ಅಳತೆ ಮಾಡುವ ಅಂತಿಮ ಚಿತ್ರವನ್ನು ಪಡೆಯುತ್ತೇವೆ. ಪ್ರತಿ ರೆಕಾರ್ಡ್‌ಗೆ ಡೇಟಾಸೆಟ್ ಅನ್ನು ಸಂಗ್ರಹಿಸುವ ಸಮಯದಲ್ಲಿ ಬಳಸಿದ ವರ್ಗೀಕರಣದಿಂದ ಚಿತ್ರವನ್ನು ಸರಿಯಾಗಿ ಗುರುತಿಸಲಾಗಿದೆಯೇ ಎಂದು ಸೂಚಿಸುವ ಲೇಬಲ್ ಇರುತ್ತದೆ, ಚಿತ್ರದ ಲೇಖಕರ ವಾಸಸ್ಥಳದ ಎರಡು ಅಕ್ಷರಗಳ ಕೋಡ್, ವಿಶಿಷ್ಟ ಗುರುತಿಸುವಿಕೆ, ಟೈಮ್‌ಸ್ಟ್ಯಾಂಪ್ ಮತ್ತು ಫೈಲ್ ಹೆಸರಿಗೆ ಹೊಂದಿಕೆಯಾಗುವ ವರ್ಗದ ಹೆಸರು. ಮೂಲ ಡೇಟಾದ ಸರಳೀಕೃತ ಆವೃತ್ತಿಯು ಆರ್ಕೈವ್‌ನಲ್ಲಿ 7.4 GB ತೂಗುತ್ತದೆ ಮತ್ತು ಅನ್‌ಪ್ಯಾಕ್ ಮಾಡಿದ ನಂತರ ಸರಿಸುಮಾರು 20 GB, ಅನ್‌ಪ್ಯಾಕ್ ಮಾಡಿದ ನಂತರ ಪೂರ್ಣ ಡೇಟಾ 240 GB ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ. ಎರಡೂ ಆವೃತ್ತಿಗಳು ಒಂದೇ ರೇಖಾಚಿತ್ರಗಳನ್ನು ಪುನರುತ್ಪಾದಿಸುತ್ತವೆ ಎಂದು ಸಂಘಟಕರು ಖಚಿತಪಡಿಸಿದರು, ಅಂದರೆ ಪೂರ್ಣ ಆವೃತ್ತಿಯು ಅನಗತ್ಯವಾಗಿದೆ. ಯಾವುದೇ ಸಂದರ್ಭದಲ್ಲಿ, ಗ್ರಾಫಿಕ್ ಫೈಲ್‌ಗಳಲ್ಲಿ ಅಥವಾ ಅರೇಗಳ ರೂಪದಲ್ಲಿ 50 ಮಿಲಿಯನ್ ಚಿತ್ರಗಳನ್ನು ಸಂಗ್ರಹಿಸುವುದು ತಕ್ಷಣವೇ ಲಾಭದಾಯಕವಲ್ಲ ಎಂದು ಪರಿಗಣಿಸಲಾಗಿದೆ ಮತ್ತು ಆರ್ಕೈವ್‌ನಿಂದ ಎಲ್ಲಾ CSV ಫೈಲ್‌ಗಳನ್ನು ವಿಲೀನಗೊಳಿಸಲು ನಾವು ನಿರ್ಧರಿಸಿದ್ದೇವೆ train_simplified.zip ಪ್ರತಿ ಬ್ಯಾಚ್‌ಗೆ "ಫ್ಲೈನಲ್ಲಿ" ಅಗತ್ಯವಿರುವ ಗಾತ್ರದ ನಂತರದ ಪೀಳಿಗೆಯ ಚಿತ್ರಗಳೊಂದಿಗೆ ಡೇಟಾಬೇಸ್‌ಗೆ.

ಉತ್ತಮವಾಗಿ ಸಾಬೀತಾಗಿರುವ ವ್ಯವಸ್ಥೆಯನ್ನು DBMS ಎಂದು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ ಮೊನೆಟ್ ಡಿಬಿ, ಅಂದರೆ R ಗಾಗಿ ಒಂದು ಪ್ಯಾಕೇಜ್ ಆಗಿ ಅನುಷ್ಠಾನ MonetDBLite. ಪ್ಯಾಕೇಜ್ ಡೇಟಾಬೇಸ್ ಸರ್ವರ್‌ನ ಎಂಬೆಡೆಡ್ ಆವೃತ್ತಿಯನ್ನು ಒಳಗೊಂಡಿದೆ ಮತ್ತು R ಸೆಷನ್‌ನಿಂದ ನೇರವಾಗಿ ಸರ್ವರ್ ಅನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ಮತ್ತು ಅದರೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಡೇಟಾಬೇಸ್ ಅನ್ನು ರಚಿಸುವುದು ಮತ್ತು ಅದಕ್ಕೆ ಸಂಪರ್ಕಿಸುವುದು ಒಂದು ಆಜ್ಞೆಯೊಂದಿಗೆ ನಿರ್ವಹಿಸಲ್ಪಡುತ್ತದೆ:

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 ಆರ್ಕೈವ್‌ನಿಂದ ಹಲವಾರು ಫೈಲ್‌ಗಳೊಂದಿಗೆ R ನಲ್ಲಿ ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುವುದಿಲ್ಲ, ಆದ್ದರಿಂದ ನಾವು ಸಿಸ್ಟಮ್ ಅನ್ನು ಬಳಸಿದ್ದೇವೆ 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 - ಲಭ್ಯವಿರುವ ಎಲ್ಲದರ ಮೂಲಕ ನಾವು ಪ್ರಾಯೋಗಿಕವಾಗಿ ಈ ನಿರ್ಧಾರಕ್ಕೆ ಬಂದಿದ್ದೇವೆ ಆಯ್ಕೆಗಳು. ಮೂರನೆಯದು ಪ್ಯಾರಾಮೀಟರ್ ಮಾಡಿದ ಪ್ರಶ್ನೆಗಳನ್ನು ಬಳಸುವುದು. ಒಮ್ಮೆ ಆಜ್ಞೆಯನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುವುದು ವಿಧಾನದ ಮೂಲತತ್ವವಾಗಿದೆ PREPARE ಅದೇ ಪ್ರಕಾರದ ಪ್ರಶ್ನೆಗಳ ಗುಂಪನ್ನು ರಚಿಸುವಾಗ ಸಿದ್ಧಪಡಿಸಿದ ಅಭಿವ್ಯಕ್ತಿಯ ನಂತರದ ಬಳಕೆಯೊಂದಿಗೆ, ಆದರೆ ವಾಸ್ತವವಾಗಿ ಸರಳವಾದದಕ್ಕೆ ಹೋಲಿಸಿದರೆ ಒಂದು ಪ್ರಯೋಜನವಿದೆ SELECT ಅಂಕಿಅಂಶ ದೋಷದ ವ್ಯಾಪ್ತಿಯೊಳಗೆ ಹೊರಹೊಮ್ಮಿದೆ.

ಡೇಟಾವನ್ನು ಅಪ್‌ಲೋಡ್ ಮಾಡುವ ಪ್ರಕ್ರಿಯೆಯು 450 MB ಗಿಂತ ಹೆಚ್ಚು RAM ಅನ್ನು ಬಳಸುವುದಿಲ್ಲ. ಅಂದರೆ, ವಿವರಿಸಿದ ವಿಧಾನವು ಹತ್ತಾರು ಗಿಗಾಬೈಟ್‌ಗಳ ತೂಕದ ಡೇಟಾಸೆಟ್‌ಗಳನ್ನು ಯಾವುದೇ ಬಜೆಟ್ ಹಾರ್ಡ್‌ವೇರ್‌ನಲ್ಲಿ ಸರಿಸಲು ಅನುಮತಿಸುತ್ತದೆ, ಕೆಲವು ಸಿಂಗಲ್-ಬೋರ್ಡ್ ಸಾಧನಗಳು ಸೇರಿದಂತೆ, ಇದು ತುಂಬಾ ತಂಪಾಗಿದೆ.

(ಯಾದೃಚ್ಛಿಕ) ಡೇಟಾವನ್ನು ಮರುಪಡೆಯುವ ವೇಗವನ್ನು ಅಳೆಯುವುದು ಮತ್ತು ವಿವಿಧ ಗಾತ್ರಗಳ ಬ್ಯಾಚ್‌ಗಳನ್ನು ಮಾದರಿ ಮಾಡುವಾಗ ಸ್ಕೇಲಿಂಗ್ ಅನ್ನು ಮೌಲ್ಯಮಾಪನ ಮಾಡುವುದು ಮಾತ್ರ ಉಳಿದಿದೆ:

ಡೇಟಾಬೇಸ್ ಮಾನದಂಡ

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)

ತ್ವರಿತ ಡ್ರಾ ಡೂಡಲ್ ಗುರುತಿಸುವಿಕೆ: ಆರ್, ಸಿ++ ಮತ್ತು ನ್ಯೂರಲ್ ನೆಟ್‌ವರ್ಕ್‌ಗಳೊಂದಿಗೆ ಸ್ನೇಹಿತರಾಗುವುದು ಹೇಗೆ

2. ಬ್ಯಾಚ್‌ಗಳನ್ನು ಸಿದ್ಧಪಡಿಸುವುದು

ಸಂಪೂರ್ಣ ಬ್ಯಾಚ್ ತಯಾರಿಕೆಯ ಪ್ರಕ್ರಿಯೆಯು ಈ ಕೆಳಗಿನ ಹಂತಗಳನ್ನು ಒಳಗೊಂಡಿದೆ:

  1. ಬಿಂದುಗಳ ನಿರ್ದೇಶಾಂಕಗಳೊಂದಿಗೆ ತಂತಿಗಳ ವೆಕ್ಟರ್‌ಗಳನ್ನು ಹೊಂದಿರುವ ಹಲವಾರು JSON ಗಳನ್ನು ಪಾರ್ಸಿಂಗ್ ಮಾಡುವುದು.
  2. ಅಗತ್ಯವಿರುವ ಗಾತ್ರದ ಚಿತ್ರದ ಮೇಲೆ ಬಿಂದುಗಳ ನಿರ್ದೇಶಾಂಕಗಳ ಆಧಾರದ ಮೇಲೆ ಬಣ್ಣದ ರೇಖೆಗಳನ್ನು ಚಿತ್ರಿಸುವುದು (ಉದಾಹರಣೆಗೆ, 256×256 ಅಥವಾ 128×128).
  3. ಫಲಿತಾಂಶದ ಚಿತ್ರಗಳನ್ನು ಟೆನ್ಸರ್ ಆಗಿ ಪರಿವರ್ತಿಸುವುದು.

ಪೈಥಾನ್ ಕರ್ನಲ್‌ಗಳ ನಡುವಿನ ಸ್ಪರ್ಧೆಯ ಭಾಗವಾಗಿ, ಸಮಸ್ಯೆಯನ್ನು ಪ್ರಾಥಮಿಕವಾಗಿ ಬಳಸಿಕೊಂಡು ಪರಿಹರಿಸಲಾಗಿದೆ ಓಪನ್‌ಸಿವಿ. R ನಲ್ಲಿನ ಸರಳ ಮತ್ತು ಅತ್ಯಂತ ಸ್ಪಷ್ಟವಾದ ಅನಲಾಗ್‌ಗಳಲ್ಲಿ ಒಂದು ಈ ರೀತಿ ಕಾಣುತ್ತದೆ:

R ನಲ್ಲಿ 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 ಉಪಕರಣಗಳನ್ನು ಬಳಸಿ ನಿರ್ವಹಿಸಲಾಗುತ್ತದೆ ಮತ್ತು RAM ನಲ್ಲಿ ಸಂಗ್ರಹವಾಗಿರುವ ತಾತ್ಕಾಲಿಕ PNG ಗೆ ಉಳಿಸಲಾಗುತ್ತದೆ (Linux ನಲ್ಲಿ, ತಾತ್ಕಾಲಿಕ R ಡೈರೆಕ್ಟರಿಗಳು ಡೈರೆಕ್ಟರಿಯಲ್ಲಿವೆ /tmp, RAM ನಲ್ಲಿ ಅಳವಡಿಸಲಾಗಿದೆ). ಈ ಫೈಲ್ ಅನ್ನು ನಂತರ 0 ರಿಂದ 1 ರವರೆಗಿನ ಸಂಖ್ಯೆಗಳೊಂದಿಗೆ ಮೂರು ಆಯಾಮದ ಸರಣಿಯಾಗಿ ಓದಲಾಗುತ್ತದೆ. ಇದು ಮುಖ್ಯವಾದುದು ಏಕೆಂದರೆ ಹೆಚ್ಚು ಸಾಂಪ್ರದಾಯಿಕ 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))

ತ್ವರಿತ ಡ್ರಾ ಡೂಡಲ್ ಗುರುತಿಸುವಿಕೆ: ಆರ್, ಸಿ++ ಮತ್ತು ನ್ಯೂರಲ್ ನೆಟ್‌ವರ್ಕ್‌ಗಳೊಂದಿಗೆ ಸ್ನೇಹಿತರಾಗುವುದು ಹೇಗೆ

ಬ್ಯಾಚ್ ಅನ್ನು ಈ ಕೆಳಗಿನಂತೆ ರಚಿಸಲಾಗುತ್ತದೆ:

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

ದೊಡ್ಡ ಬ್ಯಾಚ್‌ಗಳ ರಚನೆಯು ಅಸಭ್ಯವಾಗಿ ಬಹಳ ಸಮಯ ತೆಗೆದುಕೊಳ್ಳುವುದರಿಂದ ಈ ಅನುಷ್ಠಾನವು ನಮಗೆ ಸೂಕ್ತವೆಂದು ತೋರುತ್ತದೆ, ಮತ್ತು ಶಕ್ತಿಯುತ ಗ್ರಂಥಾಲಯವನ್ನು ಬಳಸಿಕೊಂಡು ನಮ್ಮ ಸಹೋದ್ಯೋಗಿಗಳ ಅನುಭವದ ಲಾಭವನ್ನು ಪಡೆಯಲು ನಾವು ನಿರ್ಧರಿಸಿದ್ದೇವೆ. ಓಪನ್‌ಸಿವಿ. ಆ ಸಮಯದಲ್ಲಿ R ಗಾಗಿ ಯಾವುದೇ ರೆಡಿಮೇಡ್ ಪ್ಯಾಕೇಜ್ ಇರಲಿಲ್ಲ (ಈಗ ಯಾವುದೂ ಇಲ್ಲ), ಆದ್ದರಿಂದ ಅಗತ್ಯವಿರುವ ಕ್ರಿಯಾತ್ಮಕತೆಯ ಕನಿಷ್ಠ ಅನುಷ್ಠಾನವನ್ನು C ++ ನಲ್ಲಿ R ಕೋಡ್‌ಗೆ ಏಕೀಕರಣದೊಂದಿಗೆ ಬರೆಯಲಾಗಿದೆ Rcpp.

ಸಮಸ್ಯೆಯನ್ನು ಪರಿಹರಿಸಲು, ಈ ಕೆಳಗಿನ ಪ್ಯಾಕೇಜುಗಳು ಮತ್ತು ಲೈಬ್ರರಿಗಳನ್ನು ಬಳಸಲಾಗಿದೆ:

  1. ಓಪನ್‌ಸಿವಿ ಚಿತ್ರಗಳು ಮತ್ತು ರೇಖಾಚಿತ್ರ ರೇಖೆಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು. ಪೂರ್ವ-ಸ್ಥಾಪಿತ ಸಿಸ್ಟಮ್ ಲೈಬ್ರರಿಗಳು ಮತ್ತು ಹೆಡರ್ ಫೈಲ್‌ಗಳು, ಹಾಗೆಯೇ ಡೈನಾಮಿಕ್ ಲಿಂಕ್ ಅನ್ನು ಬಳಸಲಾಗಿದೆ.

  2. ಎಕ್ಸ್ಟೆನ್ಸರ್ ಬಹು ಆಯಾಮದ ಅರೇಗಳು ಮತ್ತು ಟೆನ್ಸರ್‌ಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು. ಅದೇ ಹೆಸರಿನ R ಪ್ಯಾಕೇಜ್‌ನಲ್ಲಿ ಸೇರಿಸಲಾದ ಹೆಡರ್ ಫೈಲ್‌ಗಳನ್ನು ನಾವು ಬಳಸಿದ್ದೇವೆ. ಲೈಬ್ರರಿಯು ಬಹುಆಯಾಮದ ಅರೇಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಸಾಲು ಮೇಜರ್ ಮತ್ತು ಕಾಲಮ್ ಪ್ರಮುಖ ಕ್ರಮದಲ್ಲಿ.

  3. ndjson JSON ಅನ್ನು ಪಾರ್ಸಿಂಗ್ ಮಾಡಲು. ಈ ಗ್ರಂಥಾಲಯವನ್ನು ಬಳಸಲಾಗುತ್ತದೆ ಎಕ್ಸ್ಟೆನ್ಸರ್ ಅದು ಯೋಜನೆಯಲ್ಲಿ ಇದ್ದರೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ.

  4. RcppThread JSON ನಿಂದ ವೆಕ್ಟರ್‌ನ ಬಹು-ಥ್ರೆಡ್ ಸಂಸ್ಕರಣೆಯನ್ನು ಸಂಘಟಿಸಲು. ಈ ಪ್ಯಾಕೇಜ್ ಒದಗಿಸಿದ ಹೆಡರ್ ಫೈಲ್‌ಗಳನ್ನು ಬಳಸಲಾಗಿದೆ. ಹೆಚ್ಚು ಜನಪ್ರಿಯತೆಯಿಂದ Rcpp ಸಮಾನಾಂತರ ಪ್ಯಾಕೇಜ್, ಇತರ ವಿಷಯಗಳ ಜೊತೆಗೆ, ಅಂತರ್ನಿರ್ಮಿತ ಲೂಪ್ ಅಡಚಣೆ ಯಾಂತ್ರಿಕತೆಯನ್ನು ಹೊಂದಿದೆ.

ಇದು ಗಮನಕ್ಕೆ ಯೋಗ್ಯವಾಗಿದೆ ಎಕ್ಸ್ಟೆನ್ಸರ್ ಒಂದು ದೈವದತ್ತವಾಗಿ ಹೊರಹೊಮ್ಮಿತು: ಇದು ವ್ಯಾಪಕವಾದ ಕಾರ್ಯವನ್ನು ಮತ್ತು ಹೆಚ್ಚಿನ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಹೊಂದಿದೆ ಎಂಬ ಅಂಶದ ಜೊತೆಗೆ, ಅದರ ಅಭಿವರ್ಧಕರು ಸಾಕಷ್ಟು ಸ್ಪಂದಿಸುತ್ತಾರೆ ಮತ್ತು ಪ್ರಶ್ನೆಗಳಿಗೆ ತ್ವರಿತವಾಗಿ ಮತ್ತು ವಿವರವಾಗಿ ಉತ್ತರಿಸಿದರು. ಅವರ ಸಹಾಯದಿಂದ, ಓಪನ್‌ಸಿವಿ ಮ್ಯಾಟ್ರಿಕ್ಸ್‌ಗಳ ರೂಪಾಂತರಗಳನ್ನು ಎಕ್ಸ್‌ಟೆನ್ಸರ್ ಟೆನ್ಸರ್‌ಗಳಾಗಿ ಕಾರ್ಯಗತಗೊಳಿಸಲು ಸಾಧ್ಯವಾಯಿತು, ಜೊತೆಗೆ 3-ಆಯಾಮದ ಇಮೇಜ್ ಟೆನ್ಸರ್‌ಗಳನ್ನು ಸರಿಯಾದ ಆಯಾಮದ 4-ಆಯಾಮದ ಟೆನ್ಸರ್‌ಗೆ ಸಂಯೋಜಿಸುವ ಮಾರ್ಗವಾಗಿದೆ (ಬ್ಯಾಚ್ ಸ್ವತಃ).

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

ಸಿಸ್ಟಮ್ ಫೈಲ್‌ಗಳನ್ನು ಬಳಸುವ ಫೈಲ್‌ಗಳನ್ನು ಕಂಪೈಲ್ ಮಾಡಲು ಮತ್ತು ಸಿಸ್ಟಮ್‌ನಲ್ಲಿ ಸ್ಥಾಪಿಸಲಾದ ಲೈಬ್ರರಿಗಳೊಂದಿಗೆ ಡೈನಾಮಿಕ್ ಲಿಂಕ್ ಮಾಡಲು, ನಾವು ಪ್ಯಾಕೇಜ್‌ನಲ್ಲಿ ಅಳವಡಿಸಲಾಗಿರುವ ಪ್ಲಗಿನ್ ಕಾರ್ಯವಿಧಾನವನ್ನು ಬಳಸಿದ್ದೇವೆ Rcpp. ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಮಾರ್ಗಗಳು ಮತ್ತು ಫ್ಲ್ಯಾಗ್‌ಗಳನ್ನು ಹುಡುಕಲು, ನಾವು ಜನಪ್ರಿಯ ಲಿನಕ್ಸ್ ಉಪಯುಕ್ತತೆಯನ್ನು ಬಳಸಿದ್ದೇವೆ pkg-config.

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, ಇದು ಫಲಿತಾಂಶವನ್ನು R-ಆಬ್ಜೆಕ್ಟ್‌ಗೆ ರಫ್ತು ಮಾಡುತ್ತದೆ (ಬಹು ಆಯಾಮದ ಅರೇ);

  • cpp_process_json_vector - ಕಾರ್ಯದ ಮೇಲೆ ಹೊದಿಕೆ cpp_process_json_str, ಇದು ಬಹು-ಥ್ರೆಡ್ ಮೋಡ್‌ನಲ್ಲಿ ಸ್ಟ್ರಿಂಗ್ ವೆಕ್ಟರ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ.

ಬಹು-ಬಣ್ಣದ ರೇಖೆಗಳನ್ನು ಸೆಳೆಯಲು, HSV ಬಣ್ಣದ ಮಾದರಿಯನ್ನು ಬಳಸಲಾಯಿತು, ನಂತರ RGB ಗೆ ಪರಿವರ್ತಿಸಲಾಯಿತು. ಫಲಿತಾಂಶವನ್ನು ಪರೀಕ್ಷಿಸೋಣ:

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

ತ್ವರಿತ ಡ್ರಾ ಡೂಡಲ್ ಗುರುತಿಸುವಿಕೆ: ಆರ್, ಸಿ++ ಮತ್ತು ನ್ಯೂರಲ್ ನೆಟ್‌ವರ್ಕ್‌ಗಳೊಂದಿಗೆ ಸ್ನೇಹಿತರಾಗುವುದು ಹೇಗೆ
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") 

ತ್ವರಿತ ಡ್ರಾ ಡೂಡಲ್ ಗುರುತಿಸುವಿಕೆ: ಆರ್, ಸಿ++ ಮತ್ತು ನ್ಯೂರಲ್ ನೆಟ್‌ವರ್ಕ್‌ಗಳೊಂದಿಗೆ ಸ್ನೇಹಿತರಾಗುವುದು ಹೇಗೆ

ನೀವು ನೋಡುವಂತೆ, ವೇಗದ ಹೆಚ್ಚಳವು ಬಹಳ ಮಹತ್ವದ್ದಾಗಿದೆ ಮತ್ತು R ಕೋಡ್ ಅನ್ನು ಸಮಾನಾಂತರಗೊಳಿಸುವ ಮೂಲಕ C ++ ಕೋಡ್ ಅನ್ನು ಹಿಡಿಯಲು ಸಾಧ್ಯವಿಲ್ಲ.

3. ಡೇಟಾಬೇಸ್‌ನಿಂದ ಬ್ಯಾಚ್‌ಗಳನ್ನು ಇಳಿಸಲು ಪುನರಾವರ್ತಕಗಳು

RAM ನಲ್ಲಿ ಹೊಂದಿಕೊಳ್ಳುವ ದತ್ತಾಂಶ ಸಂಸ್ಕರಣೆಗೆ ಅರ್ಹವಾದ ಖ್ಯಾತಿಯನ್ನು ಹೊಂದಿದೆ, ಆದರೆ ಪೈಥಾನ್ ಪುನರಾವರ್ತಿತ ಡೇಟಾ ಸಂಸ್ಕರಣೆಯಿಂದ ಹೆಚ್ಚು ಗುಣಲಕ್ಷಣಗಳನ್ನು ಹೊಂದಿದೆ, ಇದು ನಿಮಗೆ ಸುಲಭವಾಗಿ ಮತ್ತು ನೈಸರ್ಗಿಕವಾಗಿ ಔಟ್-ಆಫ್-ಕೋರ್ ಲೆಕ್ಕಾಚಾರಗಳನ್ನು (ಬಾಹ್ಯ ಮೆಮೊರಿಯನ್ನು ಬಳಸಿಕೊಂಡು ಲೆಕ್ಕಾಚಾರಗಳು) ಕಾರ್ಯಗತಗೊಳಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ. ವಿವರಿಸಿದ ಸಮಸ್ಯೆಯ ಸಂದರ್ಭದಲ್ಲಿ ನಮಗೆ ಒಂದು ಶ್ರೇಷ್ಠ ಮತ್ತು ಸಂಬಂಧಿತ ಉದಾಹರಣೆಯೆಂದರೆ ಆಳವಾದ ನರಮಂಡಲದ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಗ್ರೇಡಿಯಂಟ್ ಮೂಲದ ವಿಧಾನದಿಂದ ತರಬೇತಿ ಪಡೆದಿದ್ದು, ಪ್ರತಿ ಹಂತದಲ್ಲೂ ಗ್ರೇಡಿಯಂಟ್‌ನ ಅಂದಾಜಿನ ಸಣ್ಣ ಭಾಗವನ್ನು ಅವಲೋಕನಗಳು ಅಥವಾ ಮಿನಿ-ಬ್ಯಾಚ್ ಬಳಸಿ.

ಪೈಥಾನ್‌ನಲ್ಲಿ ಬರೆಯಲಾದ ಆಳವಾದ ಕಲಿಕೆಯ ಚೌಕಟ್ಟುಗಳು ಡೇಟಾದ ಆಧಾರದ ಮೇಲೆ ಪುನರಾವರ್ತಕಗಳನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುವ ವಿಶೇಷ ತರಗತಿಗಳನ್ನು ಹೊಂದಿವೆ: ಕೋಷ್ಟಕಗಳು, ಫೋಲ್ಡರ್‌ಗಳಲ್ಲಿನ ಚಿತ್ರಗಳು, ಬೈನರಿ ಸ್ವರೂಪಗಳು, ಇತ್ಯಾದಿ. ನೀವು ಸಿದ್ಧ-ಸಿದ್ಧ ಆಯ್ಕೆಗಳನ್ನು ಬಳಸಬಹುದು ಅಥವಾ ನಿರ್ದಿಷ್ಟ ಕಾರ್ಯಗಳಿಗಾಗಿ ನಿಮ್ಮದೇ ಆದದನ್ನು ಬರೆಯಬಹುದು. R ನಲ್ಲಿ ನಾವು ಪೈಥಾನ್ ಲೈಬ್ರರಿಯ ಎಲ್ಲಾ ವೈಶಿಷ್ಟ್ಯಗಳ ಲಾಭವನ್ನು ಪಡೆಯಬಹುದು ಕೆರಾಸ್ ಅದೇ ಹೆಸರಿನ ಪ್ಯಾಕೇಜ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು ಅದರ ವಿವಿಧ ಬ್ಯಾಕೆಂಡ್‌ಗಳೊಂದಿಗೆ, ಇದು ಪ್ಯಾಕೇಜ್‌ನ ಮೇಲ್ಭಾಗದಲ್ಲಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ರೆಟಿಕ್ಯುಲೇಟ್. ಎರಡನೆಯದು ಪ್ರತ್ಯೇಕ ದೀರ್ಘ ಲೇಖನಕ್ಕೆ ಅರ್ಹವಾಗಿದೆ; ಇದು R ನಿಂದ ಪೈಥಾನ್ ಕೋಡ್ ಅನ್ನು ಚಲಾಯಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಆದರೆ R ಮತ್ತು ಪೈಥಾನ್ ಸೆಷನ್‌ಗಳ ನಡುವೆ ವಸ್ತುಗಳನ್ನು ವರ್ಗಾಯಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಎಲ್ಲಾ ಅಗತ್ಯ ರೀತಿಯ ಪರಿವರ್ತನೆಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿರ್ವಹಿಸುತ್ತದೆ.

MonetDBLite ಅನ್ನು ಬಳಸಿಕೊಂಡು RAM ನಲ್ಲಿ ಎಲ್ಲಾ ಡೇಟಾವನ್ನು ಸಂಗ್ರಹಿಸುವ ಅಗತ್ಯವನ್ನು ನಾವು ತೊಡೆದುಹಾಕಿದ್ದೇವೆ, ಎಲ್ಲಾ "ನ್ಯೂರಲ್ ನೆಟ್‌ವರ್ಕ್" ಕೆಲಸಗಳನ್ನು ಪೈಥಾನ್‌ನಲ್ಲಿನ ಮೂಲ ಕೋಡ್‌ನಿಂದ ನಿರ್ವಹಿಸಲಾಗುತ್ತದೆ, ನಾವು ಡೇಟಾದ ಮೇಲೆ ಪುನರಾವರ್ತಕವನ್ನು ಬರೆಯಬೇಕಾಗಿದೆ, ಏಕೆಂದರೆ ಏನೂ ಸಿದ್ಧವಾಗಿಲ್ಲ R ಅಥವಾ ಪೈಥಾನ್‌ನಲ್ಲಿ ಅಂತಹ ಪರಿಸ್ಥಿತಿಗಾಗಿ. ಇದಕ್ಕೆ ಮೂಲಭೂತವಾಗಿ ಕೇವಲ ಎರಡು ಅವಶ್ಯಕತೆಗಳಿವೆ: ಇದು ಅಂತ್ಯವಿಲ್ಲದ ಲೂಪ್‌ನಲ್ಲಿ ಬ್ಯಾಚ್‌ಗಳನ್ನು ಹಿಂತಿರುಗಿಸಬೇಕು ಮತ್ತು ಪುನರಾವರ್ತನೆಗಳ ನಡುವೆ ಅದರ ಸ್ಥಿತಿಯನ್ನು ಉಳಿಸಬೇಕು (ಆರ್‌ನಲ್ಲಿ ಎರಡನೆಯದನ್ನು ಮುಚ್ಚುವಿಕೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಸರಳ ರೀತಿಯಲ್ಲಿ ಕಾರ್ಯಗತಗೊಳಿಸಲಾಗುತ್ತದೆ). ಹಿಂದೆ, ಪುನರಾವರ್ತನೆಯೊಳಗೆ R ಅರೇಗಳನ್ನು numpy ಅರೇಗಳಾಗಿ ಸ್ಪಷ್ಟವಾಗಿ ಪರಿವರ್ತಿಸುವ ಅಗತ್ಯವಿತ್ತು, ಆದರೆ ಪ್ಯಾಕೇಜ್‌ನ ಪ್ರಸ್ತುತ ಆವೃತ್ತಿ ಕೆರಾಸ್ ಅದನ್ನು ಸ್ವತಃ ಮಾಡುತ್ತದೆ.

ತರಬೇತಿ ಮತ್ತು ಊರ್ಜಿತಗೊಳಿಸುವಿಕೆಯ ಡೇಟಾಕ್ಕಾಗಿ ಪುನರಾವರ್ತಕವು ಈ ಕೆಳಗಿನಂತೆ ಹೊರಹೊಮ್ಮಿತು:

ತರಬೇತಿ ಮತ್ತು ದೃಢೀಕರಣ ಡೇಟಾಕ್ಕಾಗಿ ಪುನರಾವರ್ತಕ

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] ವರೆಗೆ ಪಿಕ್ಸೆಲ್ ಮೌಲ್ಯಗಳನ್ನು ಅಳೆಯಲು ಎರಡನೆಯದು ಅಗತ್ಯವಿದೆ, ಇದನ್ನು ಸರಬರಾಜು ಮಾಡಿದವರಿಗೆ ತರಬೇತಿ ನೀಡುವಾಗ ಬಳಸಲಾಗುತ್ತಿತ್ತು ಕೆರಾಸ್ ಮಾದರಿಗಳು.

ಬಾಹ್ಯ ಕಾರ್ಯವು ಆರ್ಗ್ಯುಮೆಂಟ್ ಪ್ರಕಾರ ಪರಿಶೀಲನೆ, ಟೇಬಲ್ ಅನ್ನು ಒಳಗೊಂಡಿದೆ 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 ಮತ್ತು ಲಿಂಕ್ ಮೂಲಕ ಮಾರ್ಪಾಡು - ಈ ಪ್ಯಾಕೇಜ್ "ಚಿಪ್ಸ್" ಇಲ್ಲದೆ ಡೇಟಾ. ಟೇಬಲ್ R ನಲ್ಲಿ ಯಾವುದೇ ಗಮನಾರ್ಹ ಪ್ರಮಾಣದ ಡೇಟಾದೊಂದಿಗೆ ಪರಿಣಾಮಕಾರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುವುದನ್ನು ಕಲ್ಪಿಸುವುದು ತುಂಬಾ ಕಷ್ಟ.

ಕೋರ್ 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)

ತ್ವರಿತ ಡ್ರಾ ಡೂಡಲ್ ಗುರುತಿಸುವಿಕೆ: ಆರ್, ಸಿ++ ಮತ್ತು ನ್ಯೂರಲ್ ನೆಟ್‌ವರ್ಕ್‌ಗಳೊಂದಿಗೆ ಸ್ನೇಹಿತರಾಗುವುದು ಹೇಗೆ

ನೀವು ಸಾಕಷ್ಟು ಪ್ರಮಾಣದ RAM ಅನ್ನು ಹೊಂದಿದ್ದರೆ, ಅದೇ RAM ಗೆ ವರ್ಗಾಯಿಸುವ ಮೂಲಕ ನೀವು ಡೇಟಾಬೇಸ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ಗಂಭೀರವಾಗಿ ವೇಗಗೊಳಿಸಬಹುದು (ನಮ್ಮ ಕಾರ್ಯಕ್ಕೆ 32 GB ಸಾಕು). Linux ನಲ್ಲಿ, ವಿಭಾಗವನ್ನು ಪೂರ್ವನಿಯೋಜಿತವಾಗಿ ಜೋಡಿಸಲಾಗಿದೆ /dev/shm, ಅರ್ಧದಷ್ಟು RAM ಸಾಮರ್ಥ್ಯದವರೆಗೆ ಆಕ್ರಮಿಸಿಕೊಂಡಿದೆ. ಸಂಪಾದಿಸುವ ಮೂಲಕ ನೀವು ಹೆಚ್ಚಿನದನ್ನು ಹೈಲೈಟ್ ಮಾಡಬಹುದು /etc/fstabಹಾಗೆ ದಾಖಲೆ ಪಡೆಯಲು tmpfs /dev/shm tmpfs defaults,size=25g 0 0. ಆಜ್ಞೆಯನ್ನು ಚಲಾಯಿಸುವ ಮೂಲಕ ರೀಬೂಟ್ ಮಾಡಲು ಮತ್ತು ಫಲಿತಾಂಶವನ್ನು ಪರೀಕ್ಷಿಸಲು ಮರೆಯದಿರಿ df -h.

ಪರೀಕ್ಷಾ ಡೇಟಾದ ಪುನರಾವರ್ತಕವು ಹೆಚ್ಚು ಸರಳವಾಗಿ ಕಾಣುತ್ತದೆ, ಏಕೆಂದರೆ ಪರೀಕ್ಷಾ ಡೇಟಾಸೆಟ್ ಸಂಪೂರ್ಣವಾಗಿ RAM ಗೆ ಹೊಂದಿಕೊಳ್ಳುತ್ತದೆ:

ಪರೀಕ್ಷಾ ಡೇಟಾಕ್ಕಾಗಿ ಪುನರಾವರ್ತಕ

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, ಇದರ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಚರ್ಚಿಸಲಾಗಿದೆ ಇದು ಸಂದೇಶ. ಇದನ್ನು ಪ್ರಮಾಣಿತವಾಗಿ ಸೇರಿಸಲಾಗಿದೆ ಕೆರಾಸ್ ಮತ್ತು, ಅದರ ಪ್ರಕಾರ, R ಗಾಗಿ ಅದೇ ಹೆಸರಿನ ಪ್ಯಾಕೇಜ್‌ನಲ್ಲಿ ಲಭ್ಯವಿದೆ. ಆದರೆ ಅದನ್ನು ಏಕ-ಚಾನೆಲ್ ಚಿತ್ರಗಳೊಂದಿಗೆ ಬಳಸಲು ಪ್ರಯತ್ನಿಸುವಾಗ, ವಿಚಿತ್ರವಾದ ವಿಷಯವು ಹೊರಹೊಮ್ಮಿತು: ಇನ್‌ಪುಟ್ ಟೆನ್ಸರ್ ಯಾವಾಗಲೂ ಆಯಾಮವನ್ನು ಹೊಂದಿರಬೇಕು (batch, height, width, 3), ಅಂದರೆ, ಚಾನಲ್‌ಗಳ ಸಂಖ್ಯೆಯನ್ನು ಬದಲಾಯಿಸಲಾಗುವುದಿಲ್ಲ. ಪೈಥಾನ್‌ನಲ್ಲಿ ಅಂತಹ ಯಾವುದೇ ಮಿತಿಯಿಲ್ಲ, ಆದ್ದರಿಂದ ನಾವು ಮೂಲ ಲೇಖನವನ್ನು ಅನುಸರಿಸಿ (ಕೆರಾಸ್ ಆವೃತ್ತಿಯಲ್ಲಿರುವ ಡ್ರಾಪ್‌ಔಟ್ ಇಲ್ಲದೆ) ಈ ವಾಸ್ತುಶಿಲ್ಪದ ನಮ್ಮ ಸ್ವಂತ ಅನುಷ್ಠಾನವನ್ನು ಧಾವಿಸಿ ಬರೆದಿದ್ದೇವೆ:

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 - ಸಾಮಾನ್ಯ R ಪಟ್ಟಿ), ಮತ್ತು ಕಾರ್ಯ 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)

ಈಗ ಸರಬರಾಜು ಮಾಡಿದ ಯಾವುದನ್ನಾದರೂ ಪಡೆಯಲು ಸಾರ್ವತ್ರಿಕ ಕಾರ್ಯವನ್ನು ಬರೆಯುವುದು ಕಷ್ಟವೇನಲ್ಲ ಕೆರಾಸ್ ಇಮೇಜ್‌ನೆಟ್‌ನಲ್ಲಿ ತರಬೇತಿ ಪಡೆದ ತೂಕವನ್ನು ಹೊಂದಿರುವ ಅಥವಾ ಇಲ್ಲದ ಮಾದರಿಗಳು:

ರೆಡಿಮೇಡ್ ಆರ್ಕಿಟೆಕ್ಚರ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡುವ ಕಾರ್ಯ

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 ಅರೇಗಳ ಪಟ್ಟಿಯ ರೂಪದಲ್ಲಿ ಮಾದರಿ ತೂಕವನ್ನು ಪಡೆಯಿರಿ, ಈ ಪಟ್ಟಿಯ ಮೊದಲ ಅಂಶದ ಆಯಾಮವನ್ನು ಬದಲಾಯಿಸಿ (ಒಂದು ಬಣ್ಣದ ಚಾನಲ್ ಅನ್ನು ತೆಗೆದುಕೊಳ್ಳುವ ಮೂಲಕ ಅಥವಾ ಎಲ್ಲಾ ಮೂರನ್ನೂ ಸರಾಸರಿ ಮಾಡುವ ಮೂಲಕ), ತದನಂತರ ತೂಕವನ್ನು ಕಾರ್ಯದೊಂದಿಗೆ ಮಾದರಿಗೆ ಹಿಂತಿರುಗಿಸಿ 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/ R. ಗಾಗಿ ಅದರ ಸಹಾಯದಿಂದ, ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳನ್ನು ಸರಳ ಆಜ್ಞೆಗಳೊಂದಿಗೆ ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತದೆ 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 ಪ್ರಸ್ತುತ ಆವೃತ್ತಿಯಿಂದ ಕೆರಾಸ್ ಆರ್ ಬಳಕೆಯಲ್ಲಿ ಸಾಧ್ಯವಿಲ್ಲ R ಪ್ಯಾಕೇಜ್‌ನಲ್ಲಿ ಬದಲಾವಣೆಗಳನ್ನು ಗಣನೆಗೆ ತೆಗೆದುಕೊಳ್ಳದ ಕಾರಣ, ಅದನ್ನು ಸರಿಪಡಿಸಲು ನಾವು ಕಾಯುತ್ತಿದ್ದೇವೆ.

RStudio ನಲ್ಲಿನ ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳ ಸಾಂಪ್ರದಾಯಿಕ ಉಡಾವಣೆಗೆ ಹೋಲಿಸಿದರೆ ವಿಭಿನ್ನ ಮಾದರಿಗಳೊಂದಿಗೆ ಪ್ರಯೋಗಗಳನ್ನು ಗಮನಾರ್ಹವಾಗಿ ವೇಗಗೊಳಿಸಲು ಈ ವಿಧಾನವು ಸಾಧ್ಯವಾಗಿಸಿತು (ಸಾಧ್ಯವಾದ ಪರ್ಯಾಯವಾಗಿ ನಾವು ಪ್ಯಾಕೇಜ್ ಅನ್ನು ಗಮನಿಸುತ್ತೇವೆ tfruns) ಆದರೆ ಇದಕ್ಕಾಗಿ RStudio ಅನ್ನು ಸ್ಥಾಪಿಸದೆಯೇ ಡಾಕರ್‌ನಲ್ಲಿ ಅಥವಾ ಸರಳವಾಗಿ ಸರ್ವರ್‌ನಲ್ಲಿ ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳ ಉಡಾವಣೆಯನ್ನು ಸುಲಭವಾಗಿ ನಿರ್ವಹಿಸುವ ಸಾಮರ್ಥ್ಯವು ಮುಖ್ಯ ಪ್ರಯೋಜನವಾಗಿದೆ.

6. ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳ ಡಾಕರೈಸೇಶನ್

ತಂಡದ ಸದಸ್ಯರ ನಡುವಿನ ತರಬೇತಿ ಮಾದರಿಗಳಿಗೆ ಮತ್ತು ಕ್ಲೌಡ್‌ನಲ್ಲಿ ತ್ವರಿತ ನಿಯೋಜನೆಗಾಗಿ ಪರಿಸರದ ಪೋರ್ಟಬಿಲಿಟಿಯನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು ನಾವು ಡಾಕರ್ ಅನ್ನು ಬಳಸಿದ್ದೇವೆ. R ಪ್ರೋಗ್ರಾಮರ್‌ಗೆ ತುಲನಾತ್ಮಕವಾಗಿ ಅಸಾಮಾನ್ಯವಾದ ಈ ಉಪಕರಣದೊಂದಿಗೆ ನೀವು ಪರಿಚಯ ಮಾಡಿಕೊಳ್ಳಲು ಪ್ರಾರಂಭಿಸಬಹುದು ಇದು ಪ್ರಕಟಣೆಗಳ ಸರಣಿ ಅಥವಾ ವೀಡಿಯೊ ಕೋರ್ಸ್.

ಮೊದಲಿನಿಂದಲೂ ನಿಮ್ಮ ಸ್ವಂತ ಚಿತ್ರಗಳನ್ನು ರಚಿಸಲು ಮತ್ತು ನಿಮ್ಮದೇ ಆದ ಚಿತ್ರಗಳನ್ನು ರಚಿಸಲು ಆಧಾರವಾಗಿ ಇತರ ಚಿತ್ರಗಳನ್ನು ಬಳಸಲು ಡಾಕರ್ ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಲಭ್ಯವಿರುವ ಆಯ್ಕೆಗಳನ್ನು ವಿಶ್ಲೇಷಿಸುವಾಗ, NVIDIA, CUDA+cuDNN ಡ್ರೈವರ್‌ಗಳು ಮತ್ತು ಪೈಥಾನ್ ಲೈಬ್ರರಿಗಳನ್ನು ಸ್ಥಾಪಿಸುವುದು ಚಿತ್ರದ ಸಾಕಷ್ಟು ದೊಡ್ಡ ಭಾಗವಾಗಿದೆ ಎಂದು ನಾವು ತೀರ್ಮಾನಕ್ಕೆ ಬಂದಿದ್ದೇವೆ ಮತ್ತು ಅಧಿಕೃತ ಚಿತ್ರವನ್ನು ಆಧಾರವಾಗಿ ತೆಗೆದುಕೊಳ್ಳಲು ನಾವು ನಿರ್ಧರಿಸಿದ್ದೇವೆ. tensorflow/tensorflow:1.12.0-gpu, ಅಲ್ಲಿ ಅಗತ್ಯ R ಪ್ಯಾಕೇಜ್‌ಗಳನ್ನು ಸೇರಿಸಲಾಗುತ್ತಿದೆ.

ಅಂತಿಮ ಡಾಕರ್ ಫೈಲ್ ಈ ರೀತಿ ಕಾಣುತ್ತದೆ:

ಡಾಕರ್‌ಫೈಲ್

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 ಡೀಫಾಲ್ಟ್ ಮೌಲ್ಯಗಳೊಂದಿಗೆ; ಮೊದಲ ಸ್ಥಾನಿಕ ವಾದವು "ಬಾಷ್" ಆಗಿದ್ದರೆ, ನಂತರ ಕಂಟೇನರ್ ಕಮಾಂಡ್ ಶೆಲ್‌ನೊಂದಿಗೆ ಸಂವಾದಾತ್ಮಕವಾಗಿ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ. ಎಲ್ಲಾ ಇತರ ಸಂದರ್ಭಗಳಲ್ಲಿ, ಸ್ಥಾನಿಕ ವಾದಗಳ ಮೌಲ್ಯಗಳನ್ನು ಬದಲಿಸಲಾಗುತ್ತದೆ: CMD="Rscript /app/train_nn.R $@".

ಮೂಲ ಡೇಟಾ ಮತ್ತು ಡೇಟಾಬೇಸ್ ಹೊಂದಿರುವ ಡೈರೆಕ್ಟರಿಗಳು, ಹಾಗೆಯೇ ತರಬೇತಿ ಪಡೆದ ಮಾದರಿಗಳನ್ನು ಉಳಿಸುವ ಡೈರೆಕ್ಟರಿಯನ್ನು ಹೋಸ್ಟ್ ಸಿಸ್ಟಮ್‌ನಿಂದ ಕಂಟೇನರ್‌ನೊಳಗೆ ಜೋಡಿಸಲಾಗಿದೆ, ಇದು ಅನಗತ್ಯ ಬದಲಾವಣೆಗಳಿಲ್ಲದೆ ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳ ಫಲಿತಾಂಶಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ನಿಮಗೆ ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ.

7. Google ಕ್ಲೌಡ್‌ನಲ್ಲಿ ಬಹು GPU ಗಳನ್ನು ಬಳಸುವುದು

ಸ್ಪರ್ಧೆಯ ವೈಶಿಷ್ಟ್ಯಗಳಲ್ಲಿ ಒಂದು ಅತ್ಯಂತ ಗದ್ದಲದ ಡೇಟಾ (ಶೀರ್ಷಿಕೆ ಚಿತ್ರವನ್ನು ನೋಡಿ, ODS ಸ್ಲಾಕ್‌ನಿಂದ @Leigh.plt ನಿಂದ ಎರವಲು ಪಡೆಯಲಾಗಿದೆ). ದೊಡ್ಡ ಬ್ಯಾಚ್‌ಗಳು ಇದನ್ನು ಎದುರಿಸಲು ಸಹಾಯ ಮಾಡುತ್ತವೆ ಮತ್ತು 1 GPU ನೊಂದಿಗೆ PC ಯಲ್ಲಿ ಪ್ರಯೋಗಗಳ ನಂತರ, ನಾವು ಕ್ಲೌಡ್‌ನಲ್ಲಿ ಹಲವಾರು GPU ಗಳಲ್ಲಿ ತರಬೇತಿ ಮಾದರಿಗಳನ್ನು ಕರಗತ ಮಾಡಿಕೊಳ್ಳಲು ನಿರ್ಧರಿಸಿದ್ದೇವೆ. ಉಪಯೋಗಿಸಿದ GoogleCloud (ಮೂಲಭೂತ ವಿಷಯಗಳಿಗೆ ಉತ್ತಮ ಮಾರ್ಗದರ್ಶಿ) ಲಭ್ಯವಿರುವ ಕಾನ್ಫಿಗರೇಶನ್‌ಗಳ ದೊಡ್ಡ ಆಯ್ಕೆ, ಸಮಂಜಸವಾದ ಬೆಲೆಗಳು ಮತ್ತು $300 ಬೋನಸ್ ಕಾರಣ. ದುರಾಶೆಯಿಂದ, ನಾನು SSD ಮತ್ತು ಟನ್ RAM ನೊಂದಿಗೆ 4xV100 ನಿದರ್ಶನವನ್ನು ಆದೇಶಿಸಿದೆ ಮತ್ತು ಅದು ದೊಡ್ಡ ತಪ್ಪು. ಅಂತಹ ಯಂತ್ರವು ಹಣವನ್ನು ತ್ವರಿತವಾಗಿ ತಿನ್ನುತ್ತದೆ; ಸಾಬೀತಾದ ಪೈಪ್‌ಲೈನ್ ಇಲ್ಲದೆ ನೀವು ಪ್ರಯೋಗವನ್ನು ಮುರಿದು ಹೋಗಬಹುದು. ಶೈಕ್ಷಣಿಕ ಉದ್ದೇಶಗಳಿಗಾಗಿ, ಕೆ 80 ಅನ್ನು ತೆಗೆದುಕೊಳ್ಳುವುದು ಉತ್ತಮ. ಆದರೆ ಹೆಚ್ಚಿನ ಪ್ರಮಾಣದ RAM ಸೂಕ್ತವಾಗಿ ಬಂದಿತು - ಕ್ಲೌಡ್ SSD ಅದರ ಕಾರ್ಯಕ್ಷಮತೆಯಿಂದ ಪ್ರಭಾವಿತವಾಗಲಿಲ್ಲ, ಆದ್ದರಿಂದ ಡೇಟಾಬೇಸ್ ಅನ್ನು ವರ್ಗಾಯಿಸಲಾಯಿತು dev/shm.

ಬಹು GPU ಗಳನ್ನು ಬಳಸುವ ಜವಾಬ್ದಾರಿಯುತ ಕೋಡ್ ತುಣುಕು ಹೆಚ್ಚಿನ ಆಸಕ್ತಿಯಾಗಿದೆ. ಮೊದಲಿಗೆ, ಪೈಥಾನ್‌ನಲ್ಲಿರುವಂತೆ ಸನ್ನಿವೇಶ ನಿರ್ವಾಹಕವನ್ನು ಬಳಸಿಕೊಂಡು CPU ನಲ್ಲಿ ಮಾದರಿಯನ್ನು ರಚಿಸಲಾಗಿದೆ:

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
  )
})

ನಂತರ ಕಂಪೈಲ್ ಮಾಡದ (ಇದು ಮುಖ್ಯವಾದ) ಮಾದರಿಯನ್ನು ಲಭ್ಯವಿರುವ GPU ಗಳ ನಿರ್ದಿಷ್ಟ ಸಂಖ್ಯೆಗೆ ನಕಲಿಸಲಾಗುತ್ತದೆ ಮತ್ತು ಅದರ ನಂತರ ಮಾತ್ರ ಅದನ್ನು ಸಂಕಲಿಸಲಾಗುತ್ತದೆ:

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)
)

ಕೊನೆಯದನ್ನು ಹೊರತುಪಡಿಸಿ ಎಲ್ಲಾ ಲೇಯರ್‌ಗಳನ್ನು ಫ್ರೀಜ್ ಮಾಡುವ ಕ್ಲಾಸಿಕ್ ತಂತ್ರ, ಕೊನೆಯ ಲೇಯರ್‌ಗೆ ತರಬೇತಿ ನೀಡುವುದು, ಹಲವಾರು ಜಿಪಿಯುಗಳಿಗೆ ಸಂಪೂರ್ಣ ಮಾದರಿಯನ್ನು ಫ್ರೀಜ್ ಮಾಡುವುದು ಮತ್ತು ಮರುತರಬೇತಿ ಮಾಡುವುದು ಕಾರ್ಯಗತಗೊಳಿಸಲಾಗಲಿಲ್ಲ.

ತರಬೇತಿ ಬಳಕೆಯಾಗದೆ ನಿಗಾ ವಹಿಸಲಾಗಿದೆ. ಟೆನ್ಸಾರ್ಬೋರ್ಡ್, ಲಾಗ್‌ಗಳನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಲು ಮತ್ತು ಪ್ರತಿ ಯುಗದ ನಂತರ ಮಾಹಿತಿಯುಕ್ತ ಹೆಸರುಗಳೊಂದಿಗೆ ಮಾದರಿಗಳನ್ನು ಉಳಿಸಲು ನಮ್ಮನ್ನು ನಾವು ಸೀಮಿತಗೊಳಿಸಿಕೊಳ್ಳುತ್ತೇವೆ:

ಕಾಲ್‌ಬ್ಯಾಕ್‌ಗಳು

# Шаблон имени файла лога
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. ಒಂದು ತೀರ್ಮಾನಕ್ಕೆ ಬದಲಾಗಿ

ನಾವು ಎದುರಿಸಿದ ಹಲವಾರು ಸಮಸ್ಯೆಗಳನ್ನು ಇನ್ನೂ ನಿವಾರಿಸಲಾಗಿಲ್ಲ:

  • в ಕೆರಾಸ್ ಸೂಕ್ತವಾದ ಕಲಿಕೆಯ ದರವನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಹುಡುಕಲು ಯಾವುದೇ ಸಿದ್ಧ ಕಾರ್ಯವಿಲ್ಲ (ಅನಲಾಗ್ lr_finder ಗ್ರಂಥಾಲಯದಲ್ಲಿ fast.ai); ಕೆಲವು ಪ್ರಯತ್ನಗಳೊಂದಿಗೆ, ಮೂರನೇ ವ್ಯಕ್ತಿಯ ಅನುಷ್ಠಾನಗಳನ್ನು R ಗೆ ಪೋರ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿದೆ, ಉದಾಹರಣೆಗೆ, ಇದು;
  • ಹಿಂದಿನ ಬಿಂದುವಿನ ಪರಿಣಾಮವಾಗಿ, ಹಲವಾರು ಜಿಪಿಯುಗಳನ್ನು ಬಳಸುವಾಗ ಸರಿಯಾದ ತರಬೇತಿ ವೇಗವನ್ನು ಆಯ್ಕೆ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ;
  • ಆಧುನಿಕ ನ್ಯೂರಲ್ ನೆಟ್‌ವರ್ಕ್ ಆರ್ಕಿಟೆಕ್ಚರ್‌ಗಳ ಕೊರತೆಯಿದೆ, ವಿಶೇಷವಾಗಿ ಇಮೇಜ್‌ನೆಟ್‌ನಲ್ಲಿ ಪೂರ್ವ-ತರಬೇತಿ ಪಡೆದವರು;
  • ಯಾವುದೇ ಸೈಕಲ್ ನೀತಿ ಮತ್ತು ತಾರತಮ್ಯದ ಕಲಿಕೆಯ ದರಗಳು (ಕೊಸೈನ್ ಅನೆಲಿಂಗ್ ನಮ್ಮ ಕೋರಿಕೆಯ ಮೇರೆಗೆ ಅಳವಡಿಸಲಾಗಿದೆ, ಧನ್ಯವಾದಗಳು ಸ್ಕೈಡಾನ್).

ಈ ಸ್ಪರ್ಧೆಯಿಂದ ಯಾವ ಉಪಯುಕ್ತ ವಿಷಯಗಳನ್ನು ಕಲಿತರು:

  • ತುಲನಾತ್ಮಕವಾಗಿ ಕಡಿಮೆ-ಶಕ್ತಿಯ ಹಾರ್ಡ್‌ವೇರ್‌ನಲ್ಲಿ, ನೀವು ನೋವು ಇಲ್ಲದೆ ಯೋಗ್ಯವಾದ (RAM ನ ಗಾತ್ರದ ಅನೇಕ ಬಾರಿ) ಡೇಟಾದ ಪರಿಮಾಣಗಳೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಬಹುದು. ಪ್ಲಾಸ್ಟಿಕ್ ಚೀಲ ಡೇಟಾ. ಟೇಬಲ್ ಕೋಷ್ಟಕಗಳ ಸ್ಥಳದಲ್ಲಿ ಮಾರ್ಪಾಡು ಮಾಡುವುದರಿಂದ ಮೆಮೊರಿಯನ್ನು ಉಳಿಸುತ್ತದೆ, ಅದು ಅವುಗಳನ್ನು ನಕಲಿಸುವುದನ್ನು ತಪ್ಪಿಸುತ್ತದೆ ಮತ್ತು ಸರಿಯಾಗಿ ಬಳಸಿದಾಗ, ಅದರ ಸಾಮರ್ಥ್ಯಗಳು ಯಾವಾಗಲೂ ಸ್ಕ್ರಿಪ್ಟಿಂಗ್ ಭಾಷೆಗಳಿಗೆ ನಮಗೆ ತಿಳಿದಿರುವ ಎಲ್ಲಾ ಸಾಧನಗಳಲ್ಲಿ ಹೆಚ್ಚಿನ ವೇಗವನ್ನು ಪ್ರದರ್ಶಿಸುತ್ತವೆ. ಡೇಟಾಬೇಸ್‌ನಲ್ಲಿ ಡೇಟಾವನ್ನು ಉಳಿಸುವುದರಿಂದ, ಹೆಚ್ಚಿನ ಸಂದರ್ಭಗಳಲ್ಲಿ, ಸಂಪೂರ್ಣ ಡೇಟಾಸೆಟ್ ಅನ್ನು RAM ಗೆ ಹಿಂಡುವ ಅಗತ್ಯತೆಯ ಬಗ್ಗೆ ಯೋಚಿಸದಿರಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ.
  • R ನಲ್ಲಿನ ನಿಧಾನ ಕಾರ್ಯಗಳನ್ನು ಪ್ಯಾಕೇಜ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು C++ ನಲ್ಲಿ ವೇಗವಾದವುಗಳೊಂದಿಗೆ ಬದಲಾಯಿಸಬಹುದು Rcpp. ಬಳಸಲು ಹೆಚ್ಚುವರಿಯಾಗಿದ್ದರೆ RcppThread ಅಥವಾ Rcpp ಸಮಾನಾಂತರ, ನಾವು ಕ್ರಾಸ್-ಪ್ಲಾಟ್‌ಫಾರ್ಮ್ ಮಲ್ಟಿ-ಥ್ರೆಡ್ ಅಳವಡಿಕೆಗಳನ್ನು ಪಡೆಯುತ್ತೇವೆ, ಆದ್ದರಿಂದ R ಮಟ್ಟದಲ್ಲಿ ಕೋಡ್ ಅನ್ನು ಸಮಾನಾಂತರಗೊಳಿಸುವ ಅಗತ್ಯವಿಲ್ಲ.
  • ಪ್ಯಾಕೇಜ್ Rcpp C ++ ನ ಗಂಭೀರ ಜ್ಞಾನವಿಲ್ಲದೆ ಬಳಸಬಹುದು, ಅಗತ್ಯವಿರುವ ಕನಿಷ್ಠವನ್ನು ವಿವರಿಸಲಾಗಿದೆ ಇಲ್ಲಿ. ಹಲವಾರು ತಂಪಾದ ಸಿ-ಲೈಬ್ರರಿಗಳಿಗೆ ಹೆಡರ್ ಫೈಲ್‌ಗಳು ಎಕ್ಸ್ಟೆನ್ಸರ್ CRAN ನಲ್ಲಿ ಲಭ್ಯವಿದೆ, ಅಂದರೆ, ರೆಡಿಮೇಡ್ ಉನ್ನತ-ಕಾರ್ಯಕ್ಷಮತೆಯ C++ ಕೋಡ್ ಅನ್ನು R ಗೆ ಸಂಯೋಜಿಸುವ ಯೋಜನೆಗಳ ಅನುಷ್ಠಾನಕ್ಕಾಗಿ ಮೂಲಸೌಕರ್ಯವನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ. ಹೆಚ್ಚುವರಿ ಅನುಕೂಲವೆಂದರೆ ಸಿಂಟ್ಯಾಕ್ಸ್ ಹೈಲೈಟ್ ಮತ್ತು RStudio ನಲ್ಲಿ ಸ್ಥಿರ C++ ಕೋಡ್ ವಿಶ್ಲೇಷಕ.
  • docopt ಪ್ಯಾರಾಮೀಟರ್‌ಗಳೊಂದಿಗೆ ಸ್ವಯಂ-ಒಳಗೊಂಡಿರುವ ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳನ್ನು ಚಲಾಯಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. ರಿಮೋಟ್ ಸರ್ವರ್‌ನಲ್ಲಿ ಬಳಸಲು ಇದು ಅನುಕೂಲಕರವಾಗಿದೆ, incl. ಡಾಕರ್ ಅಡಿಯಲ್ಲಿ. RStudio ನಲ್ಲಿ, ತರಬೇತಿ ನರಮಂಡಲದೊಂದಿಗೆ ಹಲವು ಗಂಟೆಗಳ ಪ್ರಯೋಗಗಳನ್ನು ನಡೆಸುವುದು ಅನಾನುಕೂಲವಾಗಿದೆ ಮತ್ತು ಸರ್ವರ್‌ನಲ್ಲಿ IDE ಅನ್ನು ಸ್ಥಾಪಿಸುವುದು ಯಾವಾಗಲೂ ಸಮರ್ಥಿಸುವುದಿಲ್ಲ.
  • OS ಮತ್ತು ಲೈಬ್ರರಿಗಳ ವಿಭಿನ್ನ ಆವೃತ್ತಿಗಳೊಂದಿಗೆ ಡೆವಲಪರ್‌ಗಳ ನಡುವೆ ಕೋಡ್ ಪೋರ್ಟೆಬಿಲಿಟಿ ಮತ್ತು ಫಲಿತಾಂಶಗಳ ಪುನರುತ್ಪಾದನೆಯನ್ನು ಡಾಕರ್ ಖಾತ್ರಿಗೊಳಿಸುತ್ತದೆ, ಜೊತೆಗೆ ಸರ್ವರ್‌ಗಳಲ್ಲಿ ಸುಲಭವಾಗಿ ಕಾರ್ಯಗತಗೊಳಿಸಬಹುದು. ನೀವು ಸಂಪೂರ್ಣ ತರಬೇತಿ ಪೈಪ್‌ಲೈನ್ ಅನ್ನು ಕೇವಲ ಒಂದು ಆಜ್ಞೆಯೊಂದಿಗೆ ಪ್ರಾರಂಭಿಸಬಹುದು.
  • Google ಕ್ಲೌಡ್ ದುಬಾರಿ ಹಾರ್ಡ್‌ವೇರ್‌ನಲ್ಲಿ ಪ್ರಯೋಗ ಮಾಡಲು ಬಜೆಟ್ ಸ್ನೇಹಿ ಮಾರ್ಗವಾಗಿದೆ, ಆದರೆ ನೀವು ಕಾನ್ಫಿಗರೇಶನ್‌ಗಳನ್ನು ಎಚ್ಚರಿಕೆಯಿಂದ ಆರಿಸಬೇಕಾಗುತ್ತದೆ.
  • ಪ್ರತ್ಯೇಕ ಕೋಡ್ ತುಣುಕುಗಳ ವೇಗವನ್ನು ಅಳೆಯುವುದು ತುಂಬಾ ಉಪಯುಕ್ತವಾಗಿದೆ, ವಿಶೇಷವಾಗಿ R ಮತ್ತು C++ ಅನ್ನು ಸಂಯೋಜಿಸುವಾಗ ಮತ್ತು ಪ್ಯಾಕೇಜ್‌ನೊಂದಿಗೆ ಬೆಂಚ್ - ಸಹ ತುಂಬಾ ಸುಲಭ.

ಒಟ್ಟಾರೆಯಾಗಿ ಈ ಅನುಭವವು ತುಂಬಾ ಲಾಭದಾಯಕವಾಗಿದೆ ಮತ್ತು ನಾವು ಕೆಲವು ಸಮಸ್ಯೆಗಳನ್ನು ಪರಿಹರಿಸಲು ಕೆಲಸ ಮಾಡುವುದನ್ನು ಮುಂದುವರಿಸುತ್ತೇವೆ.

ಮೂಲ: www.habr.com

ಕಾಮೆಂಟ್ ಅನ್ನು ಸೇರಿಸಿ