ፈጣን ስዕል Doodle እውቅና፡ ከR፣ C++ እና የነርቭ አውታረ መረቦች ጋር እንዴት ጓደኝነትን መፍጠር እንደሚቻል

ፈጣን ስዕል Doodle እውቅና፡ ከR፣ C++ እና የነርቭ አውታረ መረቦች ጋር እንዴት ጓደኝነትን መፍጠር እንደሚቻል

ሃይ ሀብር!

ባለፈው መኸር፣ ካግሌ በእጅ የተሳሉ ምስሎችን ፈጣን ስዕል ዱድል ማወቂያን ለመመደብ ውድድር አዘጋጅቷል፣ በዚህ ውስጥ፣ ከሌሎች መካከል የአር ሳይንቲስቶች ቡድን የተሳተፈበት፡- Artem Klevtsova, የፊሊፕ አስተዳዳሪ и Andrey Ogurtsov. ውድድሩን በዝርዝር አንገልጽም፤ ያ ቀደም ሲል ተከናውኗል የቅርብ ጊዜ ህትመት.

በዚህ ጊዜ በሜዳልያ እርሻ ላይ አልሰራም, ነገር ግን ብዙ ጠቃሚ ተሞክሮዎች ተገኝተዋል, ስለዚህ በካግሌ እና በዕለት ተዕለት ስራ ውስጥ ስለ ብዙ አስደሳች እና ጠቃሚ ነገሮች ለህብረተሰቡ መንገር እፈልጋለሁ. ከተወያዩት ርዕሰ ጉዳዮች መካከል: ያለ አስቸጋሪ ሕይወት OpenCVJSON መተንተን (እነዚህ ምሳሌዎች የC++ ኮድን ወደ ስክሪፕቶች ወይም ጥቅሎች R በመጠቀም መቀላቀልን ይመረምራሉ አርሲፒ.ፒ), የስክሪፕቶችን መመዘኛ እና የመጨረሻውን መፍትሄ ዶክከር ማድረግ. ለአፈፃፀም ተስማሚ በሆነ መልኩ ከመልዕክቱ የተገኘ ሁሉም ኮድ በ ውስጥ ይገኛል። ማከማቻዎች.

ይዘቶች

  1. ውሂብን ከCSV ወደ MonetDB በብቃት ይጫኑ
  2. ስብስቦችን በማዘጋጀት ላይ
  3. ከመረጃ ቋቱ ውስጥ ስብስቦችን ለማራገፍ ተደጋጋሚዎች
  4. የሞዴል አርክቴክቸር መምረጥ
  5. የስክሪፕት መለኪያ
  6. የስክሪፕቶችን ዶክከር ማድረግ
  7. በGoogle ደመና ላይ በርካታ ጂፒዩዎችን መጠቀም
  8. ከዚህ ይልቅ አንድ መደምደሚያ

1. ውሂብን ከCSV ወደ MonetDB የውሂብ ጎታ በብቃት ይጫኑ

በዚህ ውድድር ውስጥ ያለው መረጃ የሚቀርበው በተዘጋጁ ምስሎች ሳይሆን በ340 CSV ፋይሎች (ለእያንዳንዱ ክፍል አንድ ፋይል) JSONs በነጥብ መጋጠሚያዎች በያዙ መልክ ነው። እነዚህን ነጥቦች በመስመሮች በማገናኘት 256x256 ፒክሰሎች የሚለካውን የመጨረሻ ምስል እናገኛለን። እንዲሁም ለእያንዳንዱ መዝገብ ስዕሉ መረጃው በሚሰበሰብበት ጊዜ ጥቅም ላይ በሚውለው ክላሲፋየር ፣ የስዕሉ ፀሐፊ የመኖሪያ ሀገር ባለ ሁለት ፊደል ኮድ ፣ ልዩ መለያ ፣ የጊዜ ማህተም የሚያመለክት መለያ አለ ። እና ከፋይል ስም ጋር የሚዛመድ የክፍል ስም. ቀለል ያለ የዋናው መረጃ ስሪት በማህደሩ ውስጥ 7.4 ጂቢ ይመዝናል እና ከተፈታ በኋላ ወደ 20 ጂቢ ያህል ይመዝናል ፣ ከተፈታ በኋላ ያለው ሙሉ መረጃ 240 ጂቢ ይወስዳል። አዘጋጆቹ ሁለቱም ስሪቶች ተመሳሳይ ስዕሎችን መድገማቸውን አረጋግጠዋል፣ ይህም ማለት ሙሉው እትም ብዙ ጊዜ የማይፈለግ ነበር። በማንኛውም ሁኔታ 50 ሚሊዮን ምስሎችን በግራፊክ ፋይሎች ወይም በድርድር መልክ ማከማቸት ወዲያውኑ ትርፋማ እንዳልሆነ ተቆጥሮ ሁሉንም የሲኤስቪ ፋይሎች ከማህደሩ ውስጥ ለማዋሃድ ወስነናል ባቡር_ቀላል.ዚፕ ለእያንዳንዱ ባች "በበረራ ላይ" የሚፈለገው መጠን ያላቸውን ምስሎች በቀጣይ ትውልድ ወደ ዳታቤዝ ያስገቡ።

በደንብ የተረጋገጠ ስርዓት እንደ ዲቢኤምኤስ ተመርጧል ሞኔት ዲ.ቢ., ማለትም ለ R እንደ ጥቅል አተገባበር MonetDLite. ፓኬጁ የተካተተ የውሂብ ጎታ አገልጋይ ሥሪትን ያካትታል እና አገልጋዩን ከ 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"
    )
  )
}

መረጃን ወደ ዳታቤዝ ለመጫን ፈጣኑ መንገድ የ CSV ፋይሎችን SQL - ትዕዛዝን በመጠቀም መቅዳት ነው። COPY OFFSET 2 INTO tablename FROM path USING DELIMITERS ',','n','"' NULL AS '' BEST EFFORTየት tablename - የጠረጴዛ ስም እና path - ወደ ፋይሉ የሚወስደው መንገድ. ከማህደሩ ጋር አብሮ በመስራት ላይ እያለ አብሮ የተሰራው ትግበራ ተገኝቷል unzip in 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

እንደ ተጠቀመው ድራይቭ የፍጥነት ባህሪዎች ላይ በመመስረት የውሂብ የመጫኛ ጊዜ ሊለያይ ይችላል። በእኛ ሁኔታ በአንድ ኤስኤስዲ ውስጥ ወይም ከፍላሽ አንፃፊ (ምንጭ ፋይል) ወደ ኤስኤስዲ (ዲቢ) ማንበብ እና መጻፍ ከ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 ሜባ ራም አይበልጥም. ያም ማለት፣ የተገለፀው አቀራረብ በአስር ጊጋባይት የሚመዝኑ የውሂብ ስብስቦችን በማንኛውም የበጀት ሃርድዌር ላይ እንዲያንቀሳቅሱ ይፈቅድልዎታል፣ አንዳንድ ነጠላ-ቦርድ መሳሪያዎችን ጨምሮ ፣ ቆንጆ።

የቀረው ሁሉ የ(ዘፈቀደ) ውሂብን የማውጣት ፍጥነትን መለካት እና የተለያየ መጠን ያላቸውን ናሙናዎች ሲወስዱ ልኬቱን መገምገም ብቻ ነው።

የውሂብ ጎታ መለኪያ

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)

ፈጣን ስዕል Doodle እውቅና፡ ከR፣ C++ እና የነርቭ አውታረ መረቦች ጋር እንዴት ጓደኝነትን መፍጠር እንደሚቻል

2. ስብስቦችን ማዘጋጀት

አጠቃላይ የድጋፍ ዝግጅት ሂደት የሚከተሉትን ደረጃዎች ያካትታል:

  1. የሕብረቁምፊዎች ቬክተሮችን ከነጥብ መጋጠሚያዎች ጋር የያዙ በርካታ JSONዎችን መተንተን።
  2. በሚፈለገው መጠን (ለምሳሌ 256×256 ወይም 128×128) ምስል ላይ በነጥቦች መጋጠሚያዎች ላይ በመመስረት ባለቀለም መስመሮችን መሳል።
  3. የተገኙትን ምስሎች ወደ ቴንሶር መለወጥ.

በፓይዘን ኮርነሎች መካከል እንደ ውድድር አካል፣ ችግሩ በዋነኝነት የተፈታው በመጠቀም ነው። OpenCV. በ R ውስጥ በጣም ቀላል እና በጣም ግልፅ ከሆኑት አናሎጎች አንዱ ይህንን ይመስላል።

የJSON ወደ Tensor መቀየርን በአር

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

ፈጣን ስዕል Doodle እውቅና፡ ከR፣ C++ እና የነርቭ አውታረ መረቦች ጋር እንዴት ጓደኝነትን መፍጠር እንደሚቻል

ቡድኑ ራሱ እንደሚከተለው ይመሰረታል-

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

ትላልቅ ቡድኖች መፈጠር አግባብ ባልሆነ መንገድ ረጅም ጊዜ ስለሚወስድ ይህ ትግበራ በጣም ጥሩ መስሎ ይታይ ነበር፣እናም ኃይለኛ ቤተመፃህፍት በመጠቀም የባልደረቦቻችንን ልምድ ለመጠቀም ወሰንን። OpenCV. በዚያን ጊዜ ለ R የተዘጋጀ ፓኬጅ አልነበረም (አሁን የለም) ስለዚህ የሚፈለገው ተግባር አነስተኛ አተገባበር በ C ++ ውስጥ ተጽፏል ወደ R ኮድ በመጠቀም አርሲፒ.ፒ.

ችግሩን ለመፍታት የሚከተሉት ፓኬጆች እና ቤተ መጻሕፍት ጥቅም ላይ ውለዋል፡

  1. OpenCV ምስሎችን እና የስዕል መስመሮችን ለመስራት. ያገለገሉ ቀድሞ የተጫኑ የስርዓት ቤተ-ፍርግሞች እና ራስጌ ፋይሎች፣ እንዲሁም ተለዋዋጭ ማገናኘት።

  2. xtensor ከብዙ ልኬት ድርድሮች እና ቴንሰሮች ጋር ለመስራት። በተመሳሳይ ስም በ R ጥቅል ውስጥ የተካተቱትን የራስጌ ፋይሎችን ተጠቀምን። ቤተ መፃህፍቱ ከብዙ ልኬት ድርድሮች ጋር እንዲሰሩ ይፈቅድልዎታል፣ በረድፍ ዋና እና በአምድ ዋና ቅደም ተከተል።

  3. ndjson JSON ን ለመተንተን። ይህ ቤተ-መጽሐፍት በ ውስጥ ጥቅም ላይ ይውላል xtensor በፕሮጀክቱ ውስጥ ካለ በራስ-ሰር.

  4. RcppThread ከJSON የቬክተር ባለብዙ-ክር ሂደትን ለማደራጀት. በዚህ ጥቅል የቀረቡትን የራስጌ ፋይሎች ተጠቅመዋል። ከተጨማሪ ታዋቂ RcppParallel ጥቅሉ ከሌሎች ነገሮች በተጨማሪ አብሮ የተሰራ የሎፕ መቋረጥ ዘዴ አለው።

ይህን ልብ ሊባል የሚገባው xtensor አማልክት ሆኖ ተገኘ፡ ሰፊ ተግባር እና ከፍተኛ አፈፃፀም ካለው እውነታ በተጨማሪ ገንቢዎቹ በጣም ምላሽ ሰጭ ሆነው ለጥያቄዎች በፍጥነት እና በዝርዝር መለሱ። በእነሱ እርዳታ የ OpenCV ማትሪክስ ለውጦችን ወደ xtensor tensor, እንዲሁም ባለ 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

የስርዓት ፋይሎችን እና በስርዓቱ ላይ ከተጫኑ ቤተ-መጻሕፍት ጋር ተለዋዋጭ ግንኙነቶችን የሚጠቀሙ ፋይሎችን ለማሰባሰብ በጥቅሉ ውስጥ የተተገበረውን ተሰኪ ዘዴ ተጠቅመን ነበር። አርሲፒ.ፒ. ዱካዎችን እና ባንዲራዎችን በራስ ሰር ለማግኘት፣ ታዋቂ የሊኑክስ አገልግሎትን ተጠቀምን። 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 ወደ tensor ልወጣ መተግበር

// [[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) ወደ tensor xt::xtensor;

  • parse_json - ተግባሩ የ JSON ሕብረቁምፊን ይተነትናል ፣ የነጥቦችን መጋጠሚያዎች ያወጣል ፣ ወደ ቬክተር በማሸግ ፣

  • ocv_draw_lines - ከተፈጠረው የቬክተር ነጥቦች, ባለብዙ ቀለም መስመሮችን ይስላል;

  • process - ከላይ ያሉትን ተግባራት ያጣምራል እንዲሁም የተገኘውን ምስል የመጠን ችሎታን ይጨምራል;

  • cpp_process_json_str - በተግባሩ ላይ መጠቅለያ processውጤቱን ወደ R-object (multidimensional array) ወደ ውጭ የሚልክ;

  • cpp_process_json_vector - በተግባሩ ላይ መጠቅለያ cpp_process_json_str, ይህም ባለብዙ-ክር ሁነታ ውስጥ string vector ለማስኬድ ያስችልዎታል.

ባለብዙ ቀለም መስመሮችን ለመሳል, የ HSV ቀለም ሞዴል ጥቅም ላይ ውሏል, ከዚያም ወደ RGB መለወጥ. ውጤቱን እንፈትሽ፡-

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

ፈጣን ስዕል Doodle እውቅና፡ ከR፣ C++ እና የነርቭ አውታረ መረቦች ጋር እንዴት ጓደኝነትን መፍጠር እንደሚቻል
በ R እና C ++ ውስጥ የአተገባበር ፍጥነት ማወዳደር

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

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

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

res_bench[, cols]

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

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

ፈጣን ስዕል Doodle እውቅና፡ ከR፣ C++ እና የነርቭ አውታረ መረቦች ጋር እንዴት ጓደኝነትን መፍጠር እንደሚቻል

እንደምታየው የፍጥነት መጨመር በጣም አስፈላጊ ሆኖ ተገኝቷል, እና R ኮድን በማመሳሰል የ C++ ኮድ ማግኘት አይቻልም.

3. ከመረጃ ቋቱ ውስጥ ስብስቦችን ለማራገፍ ተደጋጋሚዎች

R ከ RAM ጋር የሚስማማ መረጃን በማዘጋጀት ረገድ ጥሩ ስም ያለው ሲሆን ፓይዘን በይበልጥ የሚታወቀው በተደጋጋሚ መረጃን በማዘጋጀት ሲሆን ይህም ከዋና ውጪ የሆኑ ስሌቶችን (ውጫዊ ማህደረ ትውስታን በመጠቀም ስሌቶችን) በቀላሉ እና በተፈጥሮ ተግባራዊ ለማድረግ ያስችላል። ከተገለጸው ችግር አንፃር ለእኛ የሚታወቀው እና ጠቃሚ ምሳሌ በጥልቀት ውረድ ዘዴ የሰለጠኑ ጥልቅ ነርቭ ኔትወርኮች በእያንዳንዱ ደረጃ ላይ ያለውን የግራዲየንት መጠጋጋት በትንሽ ምልከታዎች ወይም ሚኒ ባች በመጠቀም ነው።

በ Python ውስጥ የተፃፉ ጥልቅ የመማሪያ ማዕቀፎች በመረጃ ላይ ተመስርተው ተደጋጋሚዎችን የሚተገብሩ ልዩ ክፍሎች አሏቸው: ሰንጠረዦች, በአቃፊዎች ውስጥ ያሉ ስዕሎች, ሁለትዮሽ ቅርፀቶች, ወዘተ. ዝግጁ የሆኑ አማራጮችን መጠቀም ወይም ለተወሰኑ ስራዎች የራስዎን መጻፍ ይችላሉ. በ R ውስጥ ሁሉንም የ Python ቤተ-መጽሐፍት ባህሪያት መጠቀም እንችላለን keras ከተለያዩ ጀርባዎች ጋር ተመሳሳይ ስም ያለው ጥቅል በመጠቀም, እሱም በተራው በጥቅሉ ላይ ይሠራል እንደገና አነባበብ. የኋለኛው የተለየ ረጅም ጽሑፍ ይገባዋል; የ Python ኮድን ከ R እንዲያሄዱ ብቻ ሳይሆን ነገሮችን በ R እና Python ክፍለ ጊዜዎች መካከል እንዲያስተላልፉ ይፈቅድልዎታል ፣ ይህም ሁሉንም አስፈላጊ ለውጦችን በራስ-ሰር ያከናውናል።

MonetDLite ን በመጠቀም ሁሉንም መረጃዎች በ RAM ውስጥ የማከማቸት አስፈላጊነትን አስወግደናል ፣ ሁሉም “የነርቭ አውታረ መረብ” ሥራ የሚከናወነው በ Python ውስጥ ባለው ኦሪጅናል ኮድ ነው ፣ ምንም ዝግጁ የሆነ ነገር ስለሌለ በመረጃው ላይ ተደጋጋሚ መፃፍ አለብን። በ 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 እና በአገናኝ በኩል ማሻሻያ - ያለ እነዚህ ጥቅል “ቺፕስ” ዳታ በአር ውስጥ ከማንኛውም ጉልህ የሆነ የውሂብ መጠን ጋር ውጤታማ በሆነ መንገድ ለመስራት ማሰብ በጣም ከባድ ነው።

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

ፈጣን ስዕል Doodle እውቅና፡ ከR፣ C++ እና የነርቭ አውታረ መረቦች ጋር እንዴት ጓደኝነትን መፍጠር እንደሚቻል

በቂ መጠን ያለው ራም ካለዎት የውሂብ ጎታውን ወደዚህ ተመሳሳይ ራም በማዛወር ስራውን በቁም ነገር ማፋጠን ይችላሉ (32 ጂቢ ለተግባራችን በቂ ነው)። በሊኑክስ ውስጥ ክፋዩ በነባሪነት ተጭኗል /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, በ ውስጥ የተገለጹት ባህሪያት ይሄ መልእክት። እንደ መደበኛ ተካቷል keras እና በዚህ መሠረት ለ R ተመሳሳይ ስም ያለው ጥቅል ውስጥ ይገኛል. ነገር ግን በነጠላ ቻናል ምስሎች ለመጠቀም ሲሞክሩ አንድ እንግዳ ነገር ተገኘ: የግብአት ተቆጣጣሪው ሁልጊዜ መጠኑ ሊኖረው ይገባል. (batch, height, width, 3)፣ ማለትም ፣ የሰርጦች ብዛት ሊቀየር አይችልም። በፓይዘን ውስጥ እንደዚህ ያለ ገደብ የለም፣ ስለዚህ ፈጥነን የዚህን አርክቴክቸር አተገባበር የራሳችንን ፅፈናል፣ ዋናውን መጣጥፍ ተከትለን (በ keras ስሪት ውስጥ ያለ ማቋረጥ)

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. የስክሪፕቶች መለኪያ

ለምቾት ሲባል ሁሉም የሥልጠና ኮዶች እንደ ነጠላ ስክሪፕት ተዘጋጅተው ተዘጋጅተው ተዘጋጅተዋል። ዶኮፕት እንደሚከተለው ይሆናል;

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)

ጥቅል ዶኮፕት አተገባበሩን ይወክላል 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 አጠቃቀም ማድረግ አይችልም በ R ጥቅል ውስጥ ግምት ውስጥ የማይገቡ ለውጦች ምክንያት, እንዲጠግኑት እየጠበቅን ነው.

ይህ አካሄድ በ RStudio ውስጥ ካሉት የስክሪፕት ጅምር ጋር ሲነፃፀር በተለያዩ ሞዴሎች ሙከራዎችን በከፍተኛ ሁኔታ ለማፋጠን አስችሎታል (ጥቅሉን እንደ አማራጭ አማራጭ እናስተውላለን) tfruns). ነገር ግን ዋናው ጥቅሙ RStudio ን ሳይጭን በ Docker ወይም በቀላሉ በአገልጋዩ ላይ የስክሪፕቶችን ጅምር በቀላሉ የማስተዳደር ችሎታ ነው።

6. የስክሪፕቶችን ዶክከር ማድረግ

በቡድን አባላት መካከል ለስልጠና ሞዴሎች እና በደመና ውስጥ በፍጥነት ለማሰማራት የአካባቢን ተንቀሳቃሽነት ለማረጋገጥ Dockerን ተጠቀምን። ለ R ፕሮግራመር በአንፃራዊነት ያልተለመደው ከዚህ መሳሪያ ጋር መተዋወቅ ትችላላችሁ ይሄ ተከታታይ ህትመቶች ወይም የቪዲዮ ኮርስ.

Docker ሁለቱንም የእራስዎን ምስሎች ከባዶ እንዲፈጥሩ እና የራስዎን ለመፍጠር ሌሎች ምስሎችን እንዲጠቀሙ ይፈቅድልዎታል። ያሉትን አማራጮች ስንመረምር የNVDIA፣ CUDA+cuDNN ሾፌሮች እና Python ቤተመፃህፍትን መጫን የምስሉ ትልቅ ክፍል ነው ወደሚል መደምደሚያ ላይ ደርሰናል፣ እና ይፋዊውን ምስል እንደ መሰረት ለመውሰድ ወስነናል። tensorflow/tensorflow:1.12.0-gpu, እዚያ አስፈላጊዎቹን የ R ፓኬጆችን መጨመር.

የመጨረሻው ዶከር ፋይል ይህን ይመስላል።

Dockerfile

FROM tensorflow/tensorflow:1.12.0-gpu

MAINTAINER Artem Klevtsov <[email protected]>

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

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

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

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

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

WORKDIR /app

VOLUME /db
VOLUME /app

CMD bash

ለመመቻቸት, ጥቅም ላይ የዋሉ ጥቅሎች ወደ ተለዋዋጮች ተቀምጠዋል; አብዛኛዎቹ የተፃፉ ስክሪፕቶች በኮንቴይነሮች ውስጥ በሚሰበሰቡበት ጊዜ ይገለበጣሉ. የትእዛዝ ዛጎልንም ወደ ቀይረነዋል /bin/bash ለይዘት አጠቃቀም ቀላልነት /etc/os-release. ይህ በኮዱ ውስጥ የስርዓተ ክወናውን ስሪት የመግለጽ አስፈላጊነትን አስቀርቷል.

በተጨማሪም፣ የተለያዩ ትዕዛዞችን የያዘ መያዣ ለማስጀመር የሚያስችል ትንሽ የባሽ ስክሪፕት ተጽፎ ነበር። ለምሳሌ፣ እነዚህ ቀደም ሲል በመያዣው ውስጥ የተቀመጡ የነርቭ ኔትወርኮችን ለማሰልጠን ስክሪፕቶች፣ ወይም የእቃውን ሂደት ለማረም እና ለመከታተል የትእዛዝ ሼል ሊሆኑ ይችላሉ።

መያዣውን ለማስጀመር ስክሪፕት

#!/bin/sh

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

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

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

ይህ የባሽ ስክሪፕት ያለ መመዘኛዎች የሚሰራ ከሆነ ስክሪፕቱ በመያዣው ውስጥ ይጠራል train_nn.R ከነባሪ እሴቶች ጋር; የመጀመሪያው የአቀማመጥ ክርክር "bash" ከሆነ, መያዣው ከትዕዛዝ ሼል ጋር በይነተገናኝ ይጀምራል. በሌሎች በሁሉም ሁኔታዎች ፣ የአቀማመጥ ነጋሪ እሴቶች እሴቶች ተተክተዋል- CMD="Rscript /app/train_nn.R $@".

የምንጭ ውሂብ እና የውሂብ ጎታ ያላቸው ማውጫዎች እንዲሁም የሰለጠኑ ሞዴሎችን ለማዳን ማውጫው ከአስተናጋጁ ስርዓት ውስጥ በመያዣው ውስጥ ተጭነዋል ፣ ይህም የስክሪፕት ውጤቶችን ያለ አላስፈላጊ ማጭበርበሮች እንዲያገኙ የሚያስችልዎ መሆኑ ልብ ሊባል ይገባል ።

7. በጎግል ክላውድ ላይ በርካታ ጂፒዩዎችን መጠቀም

የውድድሩ አንዱ ባህሪ በጣም ጫጫታ ያለው መረጃ ነበር (የርዕስ ስዕሉን ይመልከቱ፣ ከ@Leigh.plt ከ ODS slack የተወሰዱ)። ትላልቅ ስብስቦች ይህንን ለመዋጋት ይረዳሉ, እና በፒሲ ላይ ከ 1 ጂፒዩ ጋር ከተደረጉ ሙከራዎች በኋላ, በደመና ውስጥ በበርካታ ጂፒዩዎች ላይ የስልጠና ሞዴሎችን ለመቆጣጠር ወሰንን. ያገለገለ GoogleCloud (ለመሠረታዊ ነገሮች ጥሩ መመሪያ) በተመረጡት ውቅሮች ፣ ተመጣጣኝ ዋጋዎች እና የ 300 ዶላር ጉርሻዎች ትልቅ ምርጫ ምክንያት። ከስግብግብነት የተነሳ 4xV100 ምሳሌን ከኤስኤስዲ እና ከአንድ ቶን RAM ጋር አዝዣለሁ፣ እና ያ ትልቅ ስህተት ነበር። እንዲህ ዓይነቱ ማሽን ገንዘብን በፍጥነት ይበላል ፣ ያለ የተረጋገጠ የቧንቧ መስመር መሞከር ይችላሉ ። ለትምህርት ዓላማ, K80 ን መውሰድ የተሻለ ነው. ነገር ግን ከፍተኛ መጠን ያለው ራም ጠቃሚ ሆኖ ተገኘ - ደመናው ኤስኤስዲ በአፈፃፀሙ አላስደነቀውም ፣ ስለዚህ የውሂብ ጎታው ወደ ተላልፏል dev/shm.

በጣም የሚስበው ብዙ ጂፒዩዎችን የመጠቀም ኃላፊነት ያለው የኮድ ቁርጥራጭ ነው። በመጀመሪያ፣ ሞዴሉ በሲፒዩ ላይ የተፈጠረ አውድ አስተዳዳሪን በመጠቀም ነው፣ ልክ በፓይዘን ውስጥ፡-

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

ከዚያ ያልተጠናቀረ (ይህ አስፈላጊ ነው) ሞዴሉ ለተወሰኑት ጂፒዩዎች ይገለበጣል እና ከዚያ በኋላ ብቻ ይዘጋጃል፡

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

ከመጨረሻው በስተቀር ሁሉንም ንብርብሮች የማቀዝቀዝ፣ የመጨረሻውን ንብርብር የማሰልጠን፣ ሙሉውን ሞዴል ለብዙ ጂፒዩዎች የማውጣት እና የማሰልጠን ክላሲክ ቴክኒክ ሊተገበር አልቻለም።

ስልጠና ሳይጠቀሙበት ክትትል ተደርጓል። tensorboardከእያንዳንዱ ዘመን በኋላ ምዝግብ ማስታወሻዎችን በመመዝገብ እና ሞዴሎችን በመረጃ ሰጪ ስሞች በማስቀመጥ እራሳችንን መገደብ፡-

መልሶ መደወል

# Шаблон имени файла лога
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 በቤተ መጻሕፍት ውስጥ ፈጣን.አይ); በተወሰነ ጥረት የሶስተኛ ወገን አተገባበርን ወደ አር መላክ ይቻላል ለምሳሌ፡- ይሄ;
  • በቀደመው ነጥብ ምክንያት ብዙ ጂፒዩዎችን ሲጠቀሙ ትክክለኛውን የሥልጠና ፍጥነት መምረጥ አልተቻለም።
  • በተለይም በምስልኔት ላይ ቀድሞ የሰለጠኑ ዘመናዊ የነርቭ አውታረመረብ አርክቴክቸር እጥረት አለ ።
  • ማንም የዑደት ፖሊሲ እና መድሎአዊ የመማሪያ ተመኖች (የኮሳይን ማደንዘዣ በእኛ ጥያቄ ነበር። ተተግብሯል, አመሰግናለሁ skydan).

ከዚህ ውድድር ምን ጠቃሚ ነገሮች ተምረዋል፡-

  • በአንፃራዊነት ዝቅተኛ ኃይል ባለው ሃርድዌር ላይ ያለ ህመም ጥሩ (ብዙ ጊዜ የ RAM መጠን) መጠን ባለው የውሂብ መጠን መስራት ይችላሉ። ፕላስቲክ ከረጢት ዳታ በሰንጠረዦች ውስጥ ባሉ ማስተካከያዎች ምክንያት ማህደረ ትውስታን ይቆጥባል ፣ ይህም እነሱን መቅዳት ያስወግዳል ፣ እና በትክክል ጥቅም ላይ ሲውል ፣ አቅሙ ሁል ጊዜ ማለት ይቻላል ቋንቋዎችን ለመፃፍ ከሚታወቁት ሁሉም መሳሪያዎች መካከል ከፍተኛውን ፍጥነት ያሳያል። በመረጃ ቋት ውስጥ ውሂብን መቆጠብ ፣በብዙ ሁኔታዎች ፣ ሙሉውን የውሂብ ስብስብ ወደ RAM የመጭመቅ አስፈላጊነት በጭራሽ እንዳታስቡ ያስችልዎታል።
  • በ R ውስጥ ያሉ ቀርፋፋ ተግባራት ጥቅሉን በመጠቀም በ C ++ ውስጥ በፍጥነት መተካት ይችላሉ። አርሲፒ.ፒ. ከአጠቃቀም በተጨማሪ RcppThread ወይም RcppParallel, ተሻጋሪ ባለብዙ-ክር አተገባበርን እናገኛለን, ስለዚህ በ R ደረጃ ላይ ያለውን ኮድ ትይዩ ማድረግ አያስፈልግም.
  • ጥቅል አርሲፒ.ፒ ሾለ C ++ ከፍተኛ እውቀት ሳይኖር ጥቅም ላይ ሊውል ይችላል, የሚፈለገው ዝቅተኛው ተዘርዝሯል እዚህ. እንደ አሪፍ C-ላይብረሪዎች ብዛት የራስጌ ፋይሎች xtensor በ CRAN ላይ ይገኛል፣ ማለትም፣ ዝግጁ የሆነ ከፍተኛ አፈጻጸም C++ ኮድን ወደ R የሚያዋህዱ ፕሮጀክቶችን ለማስፈጸም መሠረተ ልማት እየተዋቀረ ነው። ተጨማሪ ምቾት የአገባብ ማድመቅ እና በRStudio ውስጥ የማይንቀሳቀስ C++ ኮድ ተንታኝ ነው።
  • ዶኮፕት ከግቤቶች ጋር የራስ-የያዙ ስክሪፕቶችን እንዲያሄዱ ይፈቅድልዎታል. ይህ በሩቅ አገልጋይ ላይ ለመጠቀም ምቹ ነው, ጨምሮ. በዶክተር ሾር. በ RStudio ውስጥ የነርቭ አውታረ መረቦችን በማሰልጠን ለብዙ ሰዓታት ሙከራዎችን ማካሄድ የማይመች ነው ፣ እና IDE በአገልጋዩ ላይ መጫን ሁል ጊዜ ትክክል አይደለም።
  • ዶከር የተለያዩ የስርዓተ ክወና እና የቤተ-መጻህፍት ስሪቶች ባላቸው ገንቢዎች መካከል የኮድ ተንቀሳቃሽነት እና የውጤቶችን መባዛት እንዲሁም በአገልጋዮች ላይ የአፈፃፀም ቀላልነትን ያረጋግጣል። በአንድ ትዕዛዝ ብቻ ሙሉውን የስልጠና ቧንቧ ማስጀመር ይችላሉ.
  • ጎግል ክላውድ ውድ ሃርድዌርን ለመሞከር የበጀት ተስማሚ መንገድ ነው፣ነገር ግን ውቅሮችን በጥንቃቄ መምረጥ አለቦት።
  • የነጠላ ኮድ ቁርጥራጮችን ፍጥነት መለካት በጣም ጠቃሚ ነው፣በተለይ R እና C++ ሲጣመሩ እና ከጥቅሉ ጋር። መቀመጫ - እንዲሁም በጣም ቀላል.

በአጠቃላይ ይህ ተሞክሮ በጣም የሚክስ ነበር እናም የተነሱትን አንዳንድ ጉዳዮች ለመፍታት መስራታችንን ቀጥለናል።

ምንጭ: hab.com

አስተያየት ያክሉ