விரைவு வரைதல் டூடுல் அங்கீகாரம்: ஆர், சி++ மற்றும் நரம்பியல் நெட்வொர்க்குடன் நட்பு கொள்வது எப்படி

விரைவு வரைதல் டூடுல் அங்கீகாரம்: ஆர், சி++ மற்றும் நரம்பியல் நெட்வொர்க்குடன் நட்பு கொள்வது எப்படி

ஹே ஹப்ர்!

கடந்த இலையுதிர்காலத்தில், கையால் வரையப்பட்ட படங்களை வகைப்படுத்துவதற்கான ஒரு போட்டியை Kaggle நடத்தினார், Quick Draw Doodle Recognition, இதில், மற்றவற்றுடன், R-விஞ்ஞானிகளின் குழு பங்கேற்றது: Artem Klevtsova, பிலிப்பா மேலாளர் и ஆண்ட்ரி ஓகுர்ட்சோவ். போட்டியை நாங்கள் விரிவாக விவரிக்க மாட்டோம்; அது ஏற்கனவே செய்யப்பட்டுள்ளது சமீபத்திய வெளியீடு.

இந்த முறை இது பதக்க விவசாயத்தில் வேலை செய்யவில்லை, ஆனால் நிறைய மதிப்புமிக்க அனுபவம் கிடைத்தது, எனவே காக்லே மற்றும் அன்றாட வேலைகளில் மிகவும் சுவாரஸ்யமான மற்றும் பயனுள்ள பல விஷயங்களைப் பற்றி சமூகத்திற்குச் சொல்ல விரும்புகிறேன். விவாதிக்கப்பட்ட தலைப்புகளில்: இல்லாமல் கடினமான வாழ்க்கை ஓபன்சிவி, JSON பாகுபடுத்துதல் (இந்த எடுத்துக்காட்டுகள், R இல் உள்ள ஸ்கிரிப்டுகள் அல்லது தொகுப்புகளில் C++ குறியீட்டை ஒருங்கிணைப்பதை ஆய்வு செய்கின்றன. Rcpp), ஸ்கிரிப்ட்களின் அளவுருக்கள் மற்றும் இறுதி தீர்வின் டாக்கரைசேஷன். செயல்பாட்டிற்கு ஏற்ற படிவத்தில் செய்தியிலிருந்து அனைத்து குறியீடுகளும் கிடைக்கும் களஞ்சியங்கள்.

பொருளடக்கம்:

  1. CSV இலிருந்து MonetDB இல் தரவை திறம்பட ஏற்றவும்
  2. தொகுப்புகளை தயார் செய்தல்
  3. தரவுத்தளத்தில் இருந்து தொகுப்புகளை இறக்குவதற்கான இட்ரேட்டர்கள்
  4. ஒரு மாதிரி கட்டிடக்கலை தேர்வு
  5. ஸ்கிரிப்ட் அளவுருவாக்கம்
  6. ஸ்கிரிப்ட்களின் டாக்கரைசேஷன்
  7. Google Cloud இல் பல GPUகளைப் பயன்படுத்துதல்
  8. அதற்கு பதிலாக, ஒரு முடிவுக்கும்

1. MonetDB தரவுத்தளத்தில் CSV இலிருந்து தரவை திறம்பட ஏற்றவும்

இந்தப் போட்டியில் உள்ள தரவு ஆயத்தப் படங்களின் வடிவத்தில் வழங்கப்படாமல், புள்ளி ஆயத்தொலைவுகளுடன் JSON களைக் கொண்ட 340 CSV கோப்புகள் (ஒவ்வொரு வகுப்பிற்கும் ஒரு கோப்பு) வடிவத்தில் வழங்கப்படுகிறது. இந்த புள்ளிகளை கோடுகளுடன் இணைப்பதன் மூலம், 256x256 பிக்சல்கள் அளவுள்ள இறுதிப் படத்தைப் பெறுகிறோம். ஒவ்வொரு பதிவிற்கும் தரவுத்தொகுப்பு சேகரிக்கப்பட்ட நேரத்தில் பயன்படுத்தப்பட்ட வகைப்படுத்தி மூலம் படம் சரியாக அங்கீகரிக்கப்பட்டதா என்பதைக் குறிக்கும் ஒரு லேபிள் உள்ளது, படத்தை எழுதியவர் வசிக்கும் நாட்டின் இரு எழுத்துக் குறியீடு, தனிப்பட்ட அடையாளங்காட்டி, நேர முத்திரை. மற்றும் கோப்பு பெயருடன் பொருந்தக்கூடிய ஒரு வகுப்பு பெயர். அசல் தரவின் எளிமைப்படுத்தப்பட்ட பதிப்பானது, காப்பகத்தில் 7.4 ஜிபி எடையுள்ளதாகவும், அன்பேக் செய்த பிறகு தோராயமாக 20 ஜிபி வரை இருக்கும். இரண்டு பதிப்புகளும் ஒரே மாதிரியான வரைபடங்களை மீண்டும் உருவாக்குவதை அமைப்பாளர்கள் உறுதி செய்தனர், அதாவது முழு பதிப்பு தேவையற்றது. எப்படியிருந்தாலும், 240 மில்லியன் படங்களை கிராஃபிக் கோப்புகள் அல்லது வரிசைகளின் வடிவத்தில் சேமிப்பது உடனடியாக லாபமற்றதாகக் கருதப்பட்டது, மேலும் காப்பகத்திலிருந்து அனைத்து CSV கோப்புகளையும் ஒன்றிணைக்க முடிவு செய்தோம். ரயில்_எளிமைப்படுத்தப்பட்டது.ஜிப் ஒவ்வொரு தொகுதிக்கும் தேவையான அளவு "ஆன் தி ஃப்ளை" படங்களின் அடுத்தடுத்த தலைமுறையுடன் தரவுத்தளத்தில்.

நன்கு நிரூபிக்கப்பட்ட அமைப்பு DBMS ஆக தேர்ந்தெடுக்கப்பட்டது MonetDB, அதாவது 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 தந்திரங்களைப் பயன்படுத்தினோம். முதலாவது, கண்காணிப்பு ஐடியை சேமிக்கும் வகையின் பரிமாணத்தைக் குறைப்பது. அசல் தரவுத் தொகுப்பில், ஐடியைச் சேமிக்கத் தேவையான வகை 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 இல் சேமிக்கப்படுகிறது (லினக்ஸில், தற்காலிக 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 இலிருந்து வெக்டரின் மல்டி-த்ரெட் செயலாக்கத்தை ஒழுங்கமைக்க. இந்த தொகுப்பு வழங்கிய தலைப்பு கோப்புகளைப் பயன்படுத்தியது. மிகவும் பிரபலமாக இருந்து RcppParallel தொகுப்பு, மற்றவற்றுடன், உள்ளமைக்கப்பட்ட லூப் குறுக்கீடு பொறிமுறையைக் கொண்டுள்ளது.

அது குறிப்பிடத்தக்கது எக்ஸ்டென்சர் ஒரு தெய்வீக வரம்பாக மாறியது: இது விரிவான செயல்பாடு மற்றும் உயர் செயல்திறனைக் கொண்டிருப்பதுடன், அதன் டெவலப்பர்கள் மிகவும் பதிலளிக்கக்கூடியவர்களாகவும், கேள்விகளுக்கு உடனடியாகவும் விரிவாகவும் பதிலளித்தனர். அவர்களின் உதவியுடன், ஓபன்சிவி மெட்ரிக்குகளை எக்ஸ்டென்சர் டென்சர்களாக மாற்றுவதையும், 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-கட்டமைப்பு.

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 இல் நாம் பைதான் நூலகத்தின் அனைத்து அம்சங்களையும் பயன்படுத்திக் கொள்ளலாம் keras அதே பெயரின் தொகுப்பைப் பயன்படுத்தி அதன் பல்வேறு பின்தளங்களுடன், இது தொகுப்பின் மேல் வேலை செய்கிறது வலைப்பின்னல். பிந்தையது ஒரு தனி நீண்ட கட்டுரைக்கு தகுதியானது; இது R இலிருந்து பைதான் குறியீட்டை இயக்க உங்களை அனுமதிப்பது மட்டுமல்லாமல், R மற்றும் Python அமர்வுகளுக்கு இடையில் பொருட்களை மாற்றவும் உங்களை அனுமதிக்கிறது, தேவையான அனைத்து வகை மாற்றங்களையும் தானாகவே செய்கிறது.

MonetDBLite ஐப் பயன்படுத்தி எல்லா தரவையும் RAM இல் சேமிக்க வேண்டிய அவசியத்திலிருந்து நாங்கள் விடுபட்டோம், அனைத்து “நரம்பியல் நெட்வொர்க்” வேலைகளும் பைத்தானில் உள்ள அசல் குறியீட்டால் செய்யப்படும், எதுவும் தயாராக இல்லாததால், தரவுகளின் மேல் ஒரு மறு செய்கையை எழுத வேண்டும். R அல்லது Python இல் அத்தகைய சூழ்நிலைக்கு. இதற்கு அடிப்படையில் இரண்டு தேவைகள் மட்டுமே உள்ளன: இது முடிவில்லாத சுழற்சியில் தொகுதிகளைத் திருப்பி, மறு செய்கைகளுக்கு இடையில் அதன் நிலையைச் சேமிக்க வேண்டும் (R இல் பிந்தையது மூடல்களைப் பயன்படுத்தி எளிமையான முறையில் செயல்படுத்தப்படுகிறது). முன்னதாக, R வரிசைகளை இடிரேட்டருக்குள் நம்பி வரிசைகளாக வெளிப்படையாக மாற்ற வேண்டியிருந்தது, ஆனால் தொகுப்பின் தற்போதைய பதிப்பு 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 மற்றும் இணைப்பு வழியாக மாற்றம் - இந்த தொகுப்பு "சிப்ஸ்" இல்லாமல் தரவு. அட்டவணை 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)

விரைவு வரைதல் டூடுல் அங்கீகாரம்: ஆர், சி++ மற்றும் நரம்பியல் நெட்வொர்க்குடன் நட்பு கொள்வது எப்படி

உங்களிடம் போதுமான அளவு ரேம் இருந்தால், அதே ரேமுக்கு மாற்றுவதன் மூலம் தரவுத்தளத்தின் செயல்பாட்டை நீங்கள் தீவிரமாக விரைவுபடுத்தலாம் (எங்கள் பணிக்கு 32 ஜிபி போதுமானது). லினக்ஸில், பகிர்வு முன்னிருப்பாக ஏற்றப்படும் /dev/shm, பாதி ரேம் கொள்ளளவு வரை ஆக்கிரமித்துள்ளது. எடிட்டிங் செய்வதன் மூலம் நீங்கள் பலவற்றை முன்னிலைப்படுத்தலாம் /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, இதில் உள்ள அம்சங்கள் விவாதிக்கப்படுகின்றன இந்த செய்தி. இது தரநிலையாக சேர்க்கப்பட்டுள்ளது keras மற்றும், அதன்படி, 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)

இப்போது வழங்கப்பட்ட எந்தவொரு பொருளையும் பெற உலகளாவிய செயல்பாட்டை எழுதுவது கடினம் அல்ல 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 அணிவரிசைகளின் பட்டியலின் வடிவத்தில் மாதிரி எடைகளைப் பெறவும், இந்த பட்டியலின் முதல் உறுப்புகளின் பரிமாணத்தை மாற்றவும் (ஒரு வண்ண சேனலை எடுத்து அல்லது மூன்றையும் சராசரியாகக் கொண்டு), பின்னர் செயல்பாட்டின் மூலம் எடைகளை மீண்டும் மாதிரியில் ஏற்றவும் 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 தற்போதைய பதிப்பில் இருந்து keras ஆர் பயன்பாட்டில் முடியாது R தொகுப்பில் கணக்கில் எடுத்துக்கொள்ளப்படாத மாற்றங்கள் காரணமாக, அவர்கள் அதை சரிசெய்வதற்காக நாங்கள் காத்திருக்கிறோம்.

இந்த அணுகுமுறையானது RStudio இல் மிகவும் பாரம்பரியமான ஸ்கிரிப்ட் வெளியீட்டுடன் ஒப்பிடும்போது வெவ்வேறு மாதிரிகள் கொண்ட சோதனைகளை கணிசமாக விரைவுபடுத்துவதை சாத்தியமாக்கியது (பேக்கேஜை சாத்தியமான மாற்றாக நாங்கள் கவனிக்கிறோம் tfrns) ஆனால் RStudio ஐ நிறுவாமல், டோக்கரில் அல்லது சர்வரில் ஸ்கிரிப்ட்களின் வெளியீட்டை எளிதாக நிர்வகிக்கும் திறன் முக்கிய நன்மை.

6. ஸ்கிரிப்ட்களின் டாக்கரைசேஷன்

குழு உறுப்பினர்களுக்கிடையேயான பயிற்சி மாதிரிகள் மற்றும் மேகக்கணியில் விரைவான வரிசைப்படுத்தல் ஆகியவற்றிற்காக சூழலின் பெயர்வுத்திறனை உறுதிப்படுத்த டோக்கரைப் பயன்படுத்தினோம். ஆர் புரோகிராமருக்கு ஒப்பீட்டளவில் அசாதாரணமான இந்தக் கருவியை நீங்கள் அறிந்துகொள்ளத் தொடங்கலாம் இந்த தொடர் வெளியீடுகள் அல்லது வீடியோ பாடநெறி.

புதிதாக உங்கள் சொந்த படங்களை உருவாக்கவும், உங்கள் சொந்த படங்களை உருவாக்குவதற்கான அடிப்படையாக மற்ற படங்களை பயன்படுத்தவும் டோக்கர் உங்களை அனுமதிக்கிறது. கிடைக்கக்கூடிய விருப்பங்களை பகுப்பாய்வு செய்யும் போது, ​​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 Cloud இல் பல GPUகளைப் பயன்படுத்துதல்

போட்டியின் அம்சங்களில் ஒன்று மிகவும் சத்தமில்லாத தரவு (தலைப்புப் படத்தைப் பார்க்கவும், ODS ஸ்லாக்கிலிருந்து @Leigh.plt இலிருந்து கடன் வாங்கப்பட்டது). பெரிய தொகுதிகள் இதை எதிர்த்துப் போராட உதவுகின்றன, மேலும் 1 GPU கொண்ட கணினியில் சோதனைகளுக்குப் பிறகு, கிளவுட்டில் உள்ள பல GPUகளில் பயிற்சி மாடல்களில் தேர்ச்சி பெற முடிவு செய்தோம். பயன்படுத்திய GoogleCloud (அடிப்படைகளுக்கு நல்ல வழிகாட்டி) கிடைக்கக்கூடிய உள்ளமைவுகளின் பெரிய தேர்வு, நியாயமான விலைகள் மற்றும் $300 போனஸ் காரணமாக. பேராசையின் காரணமாக, நான் ஒரு SSD மற்றும் ஒரு டன் ரேம் கொண்ட 4xV100 நிகழ்வை ஆர்டர் செய்தேன், அது ஒரு பெரிய தவறு. அத்தகைய இயந்திரம் விரைவாக பணத்தை தின்றுவிடும்; நிரூபிக்கப்பட்ட குழாய் இல்லாமல் நீங்கள் சோதனைக்கு செல்லலாம். கல்வி நோக்கங்களுக்காக, K80 ஐ எடுத்துக்கொள்வது நல்லது. ஆனால் பெரிய அளவிலான ரேம் கைக்கு வந்தது - கிளவுட் 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. ஒரு முடிவுக்கு பதிலாக

நாங்கள் சந்தித்த பல பிரச்சனைகள் இன்னும் தீர்க்கப்படவில்லை:

  • в keras உகந்த கற்றல் வீதத்தைத் தானாகத் தேடுவதற்கு ஆயத்த செயல்பாடு எதுவும் இல்லை (அனலாக் lr_finder நூலகத்தில் வேகமாக. ஐ); சில முயற்சிகளால், மூன்றாம் தரப்பு செயலாக்கங்களை R க்கு போர்ட் செய்ய முடியும், எடுத்துக்காட்டாக, இந்த;
  • முந்தைய புள்ளியின் விளைவாக, பல GPUகளைப் பயன்படுத்தும் போது சரியான பயிற்சி வேகத்தைத் தேர்ந்தெடுக்க முடியவில்லை;
  • நவீன நரம்பியல் நெட்வொர்க் கட்டமைப்புகளின் பற்றாக்குறை உள்ளது, குறிப்பாக இமேஜ்நெட்டில் முன் பயிற்சி பெற்றவை;
  • யாரும் சுழற்சிக் கொள்கை மற்றும் பாரபட்சமான கற்றல் விகிதங்கள் (கோசைன் அனீலிங் எங்கள் வேண்டுகோளின்படி இருந்தது செயல்படுத்தப்பட்டது, நன்றி ஸ்கேடான்).

இந்தப் போட்டியிலிருந்து என்ன பயனுள்ள விஷயங்கள் கற்றுக்கொண்டன:

  • ஒப்பீட்டளவில் குறைந்த சக்தி கொண்ட வன்பொருளில், நீங்கள் வலியின்றி ஒழுக்கமான (ரேம் அளவை விட பல மடங்கு) தரவு அளவுகளுடன் வேலை செய்யலாம். நெகிழி பை தரவு. அட்டவணை அட்டவணைகளின் இடமாற்றம் காரணமாக நினைவகத்தைச் சேமிக்கிறது, இது அவற்றை நகலெடுப்பதைத் தவிர்க்கிறது, மேலும் சரியாகப் பயன்படுத்தும்போது, ​​​​அதன் திறன்கள் எப்போதும் ஸ்கிரிப்டிங் மொழிகளுக்கு நமக்குத் தெரிந்த அனைத்து கருவிகளிலும் அதிக வேகத்தை நிரூபிக்கின்றன. தரவுத்தளத்தில் தரவைச் சேமிப்பது, பல சந்தர்ப்பங்களில், முழு தரவுத்தொகுப்பையும் RAM இல் அழுத்துவதன் அவசியத்தைப் பற்றி சிந்திக்காமல் இருக்க உங்களை அனுமதிக்கிறது.
  • R இல் உள்ள மெதுவான செயல்பாடுகளை C++ இல் உள்ள வேகமான செயல்பாடுகளுடன் தொகுப்பைப் பயன்படுத்தி மாற்றலாம் Rcpp. கூடுதலாக பயன்படுத்தினால் RcppThread அல்லது RcppParallel, க்ராஸ்-பிளாட்ஃபார்ம் மல்டி-த்ரெட் செயலாக்கங்களைப் பெறுகிறோம், எனவே R மட்டத்தில் குறியீட்டை இணையாக்க வேண்டிய அவசியமில்லை.
  • தொகுப்பு Rcpp C++ பற்றிய தீவிர அறிவு இல்லாமல் பயன்படுத்தலாம், தேவையான குறைந்தபட்சம் கோடிட்டுக் காட்டப்பட்டுள்ளது இங்கே. போன்ற பல அருமையான சி-லைப்ரரிகளுக்கான தலைப்பு கோப்புகள் எக்ஸ்டென்சர் CRAN இல் கிடைக்கிறது, அதாவது, ஆயத்த உயர் செயல்திறன் கொண்ட C++ குறியீட்டை R உடன் ஒருங்கிணைக்கும் திட்டங்களைச் செயல்படுத்துவதற்கான உள்கட்டமைப்பு உருவாக்கப்படுகிறது. RStudio இல் தொடரியல் சிறப்பம்சங்கள் மற்றும் நிலையான C++ குறியீடு பகுப்பாய்வி ஆகியவை கூடுதல் வசதி.
  • docopt அளவுருக்களுடன் சுய-கட்டுமான ஸ்கிரிப்ட்களை இயக்க உங்களை அனுமதிக்கிறது. ரிமோட் சர்வரில் பயன்படுத்த இது வசதியானது, உட்பட. டாக்கரின் கீழ். RStudio இல், நரம்பியல் நெட்வொர்க்குகளைப் பயிற்றுவிப்பதன் மூலம் பல மணிநேர சோதனைகளை நடத்துவது சிரமமாக உள்ளது, மேலும் சேவையகத்தில் IDE ஐ நிறுவுவது எப்போதும் நியாயப்படுத்தப்படாது.
  • OS மற்றும் லைப்ரரிகளின் வெவ்வேறு பதிப்புகளைக் கொண்ட டெவலப்பர்களிடையே குறியீடு பெயர்வுத்திறன் மற்றும் முடிவுகளின் மறுஉருவாக்கம், அத்துடன் சேவையகங்களில் எளிதாக செயல்படுத்துதல் ஆகியவற்றை டோக்கர் உறுதி செய்கிறது. ஒரே ஒரு கட்டளை மூலம் முழு பயிற்சி பைப்லைனையும் தொடங்கலாம்.
  • கூகிள் கிளவுட் என்பது விலையுயர்ந்த வன்பொருளில் பரிசோதனை செய்வதற்கான பட்ஜெட்டுக்கு ஏற்ற வழியாகும், ஆனால் நீங்கள் உள்ளமைவுகளை கவனமாக தேர்வு செய்ய வேண்டும்.
  • தனிப்பட்ட குறியீடு துண்டுகளின் வேகத்தை அளவிடுவது மிகவும் பயனுள்ளதாக இருக்கும், குறிப்பாக R மற்றும் C++ மற்றும் தொகுப்புடன் இணைக்கும்போது பெஞ்ச் - மிகவும் எளிதானது.

ஒட்டுமொத்தமாக இந்த அனுபவம் மிகவும் பலனளித்தது மற்றும் எழுப்பப்பட்ட சில சிக்கல்களைத் தீர்க்க நாங்கள் தொடர்ந்து பணியாற்றி வருகிறோம்.

ஆதாரம்: www.habr.com

கருத்தைச் சேர்