Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ ΠΏΠΎΠ΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ R, C++ ΠΈ нСйросСтки

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ ΠΏΠΎΠ΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ R, C++ ΠΈ нСйросСтки

ΠŸΡ€ΠΈΠ²Π΅Ρ‚, Π₯Π°Π±Ρ€!

ОсСнью ΠΏΡ€ΠΎΡˆΠ»ΠΎΠ³ΠΎ Π³ΠΎΠ΄Π° Π½Π° Kaggle ΠΏΡ€ΠΎΡ…ΠΎΠ΄ΠΈΠ» конкурс ΠΏΠΎ классификации нарисованных ΠΎΡ‚ Ρ€ΡƒΠΊΠΈ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΎΠΊ Quick Draw Doodle Recognition, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ срСди ΠΏΡ€ΠΎΡ‡ΠΈΡ… поучаствовала ΠΊΠΎΠΌΠ°Π½Π΄Π° R-Ρ‰ΠΈΠΊΠΎΠ² Π² составС АртСма ΠšΠ»Π΅Π²Ρ†ΠΎΠ²Π°, Π€ΠΈΠ»ΠΈΠΏΠΏΠ° Π£ΠΏΡ€Π°Π²ΠΈΡ‚Π΅Π»Π΅Π²Π° ΠΈ АндрСя ΠžΠ³ΡƒΡ€Ρ†ΠΎΠ²Π°. ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ ΠΎΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒ сорСвнованиС Π½Π΅ Π±ΡƒΠ΄Π΅ΠΌ, это ΡƒΠΆΠ΅ сдСлано Π² Π½Π΅Π΄Π°Π²Π½Π΅ΠΉ ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ.

Π‘ Ρ„Π°Ρ€ΠΌΠΎΠΌ ΠΌΠ΅Π΄Π°Π»Π΅ΠΊ Π² этот Ρ€Π°Π· Π½Π΅ слоТилось, Π½ΠΎ Π±Ρ‹Π»ΠΎ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΎ ΠΌΠ½ΠΎΠ³ΠΎ Ρ†Π΅Π½Π½ΠΎΠ³ΠΎ ΠΎΠΏΡ‹Ρ‚Π°, поэтому ΠΎ рядС Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ интСрСсных ΠΈ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹Ρ… Π½Π° КаглС ΠΈ Π² повсСднСвной Ρ€Π°Π±ΠΎΡ‚Π΅ Π²Π΅Ρ‰Π΅ΠΉ Ρ…ΠΎΡ‚Π΅Π»ΠΎΡΡŒ Π±Ρ‹ Ρ€Π°ΡΡΠΊΠ°Π·Π°Ρ‚ΡŒ сообщСству. Π‘Ρ€Π΅Π΄ΠΈ рассмотрСнных Ρ‚Π΅ΠΌ: нСлСгкая Тизнь Π±Π΅Π· OpenCV, парсинг JSON-ΠΎΠ² (Π½Π° этих ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°Ρ… рассматриваСтся ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ ΠΊΠΎΠ΄Π° Π½Π° Π‘++ Π² скрипты ΠΈΠ»ΠΈ ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ Π½Π° R посрСдством Rcpp), парамСтризация скриптов ΠΈ докСризация ΠΈΡ‚ΠΎΠ³ΠΎΠ²ΠΎΠ³ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ. Π’Π΅ΡΡŒ ΠΊΠΎΠ΄ ΠΈΠ· сообщСния Π² ΠΏΡ€ΠΈΠ³ΠΎΠ΄Π½ΠΎΠΌ для запуска Π²ΠΈΠ΄Π΅ доступСн Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ.

Π‘ΠΎΠ΄Π΅Ρ€ΠΆΠ°Π½ΠΈΠ΅:

  1. ЭффСктивная Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π΄Π°Π½Π½Ρ‹Ρ… ΠΈΠ· CSV Π² Π±Π°Π·Ρƒ MonetDB
  2. ΠŸΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° Π±Π°Ρ‚Ρ‡Π΅ΠΉ
  3. Π˜Ρ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€Ρ‹ для Π²Ρ‹Π³Ρ€ΡƒΠ·ΠΊΠΈ Π±Π°Ρ‚Ρ‡Π΅ΠΉ ΠΈΠ· Π‘Π”
  4. Π’Ρ‹Π±ΠΎΡ€ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ‹ ΠΌΠΎΠ΄Π΅Π»ΠΈ
  5. ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΠ·Π°Ρ†ΠΈΡ скриптов
  6. ДокСризация скриптов
  7. ИспользованиС Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… GPU Π² ΠΎΠ±Π»Π°ΠΊΠ΅ Google Cloud
  8. ВмСсто Π·Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ

1. ЭффСктивная Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π΄Π°Π½Π½Ρ‹Ρ… ΠΈΠ· CSV Π² Π±Π°Π·Ρƒ MonetDB

Π”Π°Π½Π½Ρ‹Π΅ Π² этом сорСвновании ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‚ΡΡ Π½Π΅ Π² Π²ΠΈΠ΄Π΅ Π³ΠΎΡ‚ΠΎΠ²Ρ‹Ρ… ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΎΠΊ, Π° Π² Π²ΠΈΠ΄Π΅ 340 CSV-Ρ„Π°ΠΉΠ»ΠΎΠ² (ΠΏΠΎ ΠΎΠ΄Π½ΠΎΠΌΡƒ Ρ„Π°ΠΉΠ»Ρƒ Π½Π° ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ класс), содСрТащих JSON-Ρ‹ с ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π°ΠΌΠΈ Ρ‚ΠΎΡ‡Π΅ΠΊ. Π‘ΠΎΠ΅Π΄ΠΈΠ½ΠΈΠ² эти Ρ‚ΠΎΡ‡ΠΊΠΈ линиями, ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΈΡ‚ΠΎΠ³ΠΎΠ²ΠΎΠ΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ 256Ρ…256 пиксСлСй. Π’Π°ΠΊΠΆΠ΅ для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ записи приводится ΠΌΠ΅Ρ‚ΠΊΠ°, Π±Ρ‹Π»Π° Π»ΠΈ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ° ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ распознана ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹ΠΌ Π½Π° ΠΌΠΎΠΌΠ΅Π½Ρ‚ сбора датасСта классификатором, Π΄Π²ΡƒΡ…Π±ΡƒΠΊΠ²Π΅Π½Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ страны проТивания Π°Π²Ρ‚ΠΎΡ€Π° рисунка, ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€, ΠΌΠ΅Ρ‚ΠΊΠ° Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ ΠΈ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ класса, ΡΠΎΠ²ΠΏΠ°Π΄Π°ΡŽΡ‰Π΅Π΅ с ΠΈΠΌΠ΅Π½Π΅ΠΌ Ρ„Π°ΠΉΠ»Π°. УпрощСнная вСрсия исходных Π΄Π°Π½Π½Ρ‹Ρ… вСсит 7.4 Π“Π± Π² Π°Ρ€Ρ…ΠΈΠ²Π΅ ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ 20 Π“Π± послС распаковки, ΠΏΠΎΠ»Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ послС распаковки Π·Π°Π½ΠΈΠΌΠ°ΡŽΡ‚ 240 Π“Π±. ΠžΡ€Π³Π°Π½ΠΈΠ·Π°Ρ‚ΠΎΡ€Ρ‹ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π»ΠΈ, Ρ‡Ρ‚ΠΎ ΠΎΠ±Π΅ вСрсии воспроизводят ΠΎΠ΄Π½ΠΈ ΠΈ Ρ‚Π΅ ΠΆΠ΅ рисунки, Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ полная вСрсия являСтся ΠΈΠ·Π±Ρ‹Ρ‚ΠΎΡ‡Π½ΠΎΠΉ. Π’ любом случаС, Ρ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ 50 ΠΌΠ»Π½. ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΎΠΊ Π² графичСских Ρ„Π°ΠΉΠ»Π°Ρ… ΠΈΠ»ΠΈ Π² Π²ΠΈΠ΄Π΅ массивов сразу Π±Ρ‹Π»ΠΎ ΠΏΡ€ΠΈΠ·Π½Π°Π½ΠΎ Π½Π΅Ρ€Π΅Π½Ρ‚Π°Π±Π΅Π»ΡŒΠ½Ρ‹ΠΌ, ΠΈ ΠΌΡ‹ Ρ€Π΅ΡˆΠΈΠ»ΠΈ ΡΠ»ΠΈΡ‚ΡŒ всС CSV-Ρ„Π°ΠΉΠ»Ρ‹ ΠΈΠ· Π°Ρ€Ρ…ΠΈΠ²Π° train_simplified.zip Π² Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ… с ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΉ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΎΠΊ Π½ΡƒΠΆΠ½ΠΎΠ³ΠΎ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° «Π½Π° Π»Π΅Ρ‚Ρƒ» для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Π±Π°Ρ‚Ρ‡Π°.

Π’ качСствС Π‘Π£Π‘Π” Π±Ρ‹Π»Π° Π²Ρ‹Π±Ρ€Π°Π½Π° Ρ…ΠΎΡ€ΠΎΡˆΠΎ сСбя Π·Π°Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΠΎΠ²Π°Π²ΡˆΠ°Ρ MonetDB, Π° ΠΈΠΌΠ΅Π½Π½ΠΎ рСализация для R Π² Π²ΠΈΠ΄Π΅ ΠΏΠ°ΠΊΠ΅Ρ‚Π° MonetDBLite. ΠŸΠ°ΠΊΠ΅Ρ‚ Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ Π² сСбя embedded-Π²Π΅Ρ€ΡΠΈΡŽ сСрвСра Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ позволяСт ΠΏΠΎΠ΄Π½ΡΡ‚ΡŒ сСрвСр нСпосрСдствСнно ΠΈΠ· 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 Π² 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 (Π‘Π”) Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ‚ ΠΌΠ΅Π½Π΅Π΅ 10 ΠΌΠΈΠ½ΡƒΡ‚.

Π•Ρ‰Π΅ нСсколько сСкунд трСбуСтся для создания столбца с цСлочислСнной ΠΌΠ΅Ρ‚ΠΊΠΎΠΉ класса ΠΈ столбца-индСкса (ORDERED INDEX) с Π½ΠΎΠΌΠ΅Ρ€Π°ΠΌΠΈ строк, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚ΡŒΡΡ Π²Ρ‹Π±ΠΎΡ€ΠΊΠ° наблюдСний ΠΏΡ€ΠΈ создании Π±Π°Ρ‚Ρ‡Π΅ΠΉ:

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… столбцов ΠΈ индСкса

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

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

Для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π·Π°Π΄Π°Ρ‡ΠΈ формирования Π±Π°Ρ‚Ρ‡Π° Β«Π½Π° Π»Π΅Ρ‚ΡƒΒ» Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π±Ρ‹Π»ΠΎ Π΄ΠΎΠ±ΠΈΡ‚ΡŒΡΡ максимальной скорости извлСчСния случайных строк ΠΈΠ· Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ doodles. Для этого ΠΌΡ‹ использовали 3 Ρ‚Ρ€ΡŽΠΊΠ°. ΠŸΠ΅Ρ€Π²Ρ‹ΠΉ Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π»ΡΡ Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΠΊΡ€Π°Ρ‚ΠΈΡ‚ΡŒ Ρ€Π°Π·ΠΌΠ΅Ρ€Π½ΠΎΡΡ‚ΡŒ Ρ‚ΠΈΠΏΠ°, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ хранится ID наблюдСния. Π’ исходном Π½Π°Π±ΠΎΡ€Π΅ Π΄Π°Π½Π½Ρ‹Ρ… для хранСния ID трСбуСтся Ρ‚ΠΈΠΏ bigint, Π½ΠΎ количСство наблюдСний позволяСт ΡƒΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ ΠΈΡ… ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹, Ρ€Π°Π²Π½Ρ‹Π΅ порядковому Π½ΠΎΠΌΠ΅Ρ€Ρƒ, Π² Ρ‚ΠΈΠΏ int. Поиск ΠΏΡ€ΠΈ этом происходит Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ быстрСС. Π’Ρ‚ΠΎΡ€Ρ‹ΠΌ Ρ‚Ρ€ΡŽΠΊΠΎΠΌ Π±Ρ‹Π»ΠΎ использованиС ORDERED INDEX β€” ΠΊ этому Ρ€Π΅ΡˆΠ΅Π½ΠΈΡŽ ΠΏΡ€ΠΈΡˆΠ»ΠΈ эмпиричСски, ΠΏΠ΅Ρ€Π΅Π±Ρ€Π°Π² всС доступныС Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹. Π’Ρ€Π΅Ρ‚ΠΈΠΉ состоял Π² использовании ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΠ·ΠΎΠ²Π°Π½Π½Ρ‹Ρ… запросов. Π‘ΡƒΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π² ΠΎΠ΄Π½ΠΎΠΊΡ€Π°Ρ‚Π½ΠΎΠΌ выполнСния ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ PREPARE с ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ использованиСм ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²Π»Π΅Π½Π½ΠΎΠ³ΠΎ выраТСния ΠΏΡ€ΠΈ создании ΠΊΡƒΡ‡ΠΈ ΠΎΠ΄Π½ΠΎΡ‚ΠΈΠΏΠ½Ρ‹Ρ… запросов, Π½ΠΎ Π½Π° Π΄Π΅Π»Π΅ Π²Ρ‹ΠΈΠ³Ρ€Ρ‹Ρˆ ΠΏΠΎ сравнСния с простым SELECT оказался Π² Ρ€Π°ΠΉΠΎΠ½Π΅ статистичСской ΠΏΠΎΠ³Ρ€Π΅ΡˆΠ½ΠΎΡΡ‚ΠΈ.

ΠŸΡ€ΠΎΡ†Π΅ΡΡ Π·Π°Π»ΠΈΠ²ΠΊΠΈ Π΄Π°Π½Π½Ρ‹Ρ… потрСбляСт Π½Π΅ Π±ΠΎΠ»Π΅Π΅ 450 Мб ΠžΠ—Π£. Π’ΠΎ Π΅ΡΡ‚ΡŒ описанный ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ позволяСт Π²ΠΎΡ€ΠΎΡ‡Π°Ρ‚ΡŒ датасСты вСсом Π² дСсятки Π³ΠΈΠ³Π°Π±Π°ΠΉΡ‚ практичСски Π½Π° любом Π±ΡŽΠ΄ΠΆΠ΅Ρ‚Π½ΠΎΠΌ ΠΆΠ΅Π»Π΅Π·Π΅, Π²ΠΊΠ»ΡŽΡ‡Π°Ρ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΎΠ΄Π½ΠΎΠΏΠ»Π°Ρ‚Π½ΠΈΠΊΠΈ, Ρ‡Ρ‚ΠΎ довольно ΠΊΡ€ΡƒΡ‚ΠΎ.

ΠžΡΡ‚Π°Π΅Ρ‚ΡΡ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ Π·Π°ΠΌΠ΅Ρ€Ρ‹ скорости извлСчСния (случайных) Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ ΠΎΡ†Π΅Π½ΠΈΡ‚ΡŒ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΡ€ΠΈ Π²Ρ‹Π±ΠΎΡ€ΠΊΠ΅ Π±Π°Ρ‚Ρ‡Π΅ΠΉ Ρ€Π°Π·Π½ΠΎΠ³ΠΎ Ρ€Π°Π·ΠΌΠ΅Ρ€Π°:

Π‘Π΅Π½Ρ‡ΠΌΠ°Ρ€ΠΊ Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ…

library(ggplot2)

set.seed(0)
# ΠŸΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΊ Π±Π°Π·Π΅ Π΄Π°Π½Π½Ρ‹Ρ…
con <- DBI::dbConnect(MonetDBLite::MonetDBLite(), Sys.getenv("DBDIR"))

# Ѐункция для ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠΈ запроса Π½Π° сторонС сСрвСра
prep_sql <- function(batch_size) {
  sql <- sprintf("PREPARE SELECT id FROM doodles WHERE id IN (%s)",
                 paste(rep("?", batch_size), collapse = ","))
  res <- DBI::dbSendQuery(con, sql)
  return(res)
}

# Ѐункция для извлСчСния Π΄Π°Π½Π½Ρ‹Ρ…
fetch_data <- function(rs, batch_size) {
  ids <- sample(seq_len(n), batch_size)
  res <- DBI::dbFetch(DBI::dbBind(rs, as.list(ids)))
  return(res)
}

# ΠŸΡ€ΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ Π·Π°ΠΌΠ΅Ρ€Π°
res_bench <- bench::press(
  batch_size = 2^(4:10),
  {
    rs <- prep_sql(batch_size)
    bench::mark(
      fetch_data(rs, batch_size),
      min_iterations = 50L
    )
  }
)
# ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Π±Π΅Π½Ρ‡ΠΌΠ°Ρ€ΠΊΠ°
cols <- c("batch_size", "min", "median", "max", "itr/sec", "total_time", "n_itr")
res_bench[, cols]

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

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

DBI::dbDisconnect(con, shutdown = TRUE)

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ ΠΏΠΎΠ΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ R, C++ ΠΈ нСйросСтки

2. ΠŸΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° Π±Π°Ρ‚Ρ‡Π΅ΠΉ

Π’Π΅ΡΡŒ процСсс ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠΈ Π±Π°Ρ‚Ρ‡Π΅ΠΉ состоит ΠΈΠ· ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΡ… этапов:

  1. ΠŸΠ°Ρ€ΡΠΈΠ½Π³ Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… JSON-ΠΎΠ², содСрТащих Π²Π΅ΠΊΡ‚ΠΎΡ€Ρ‹ строк с ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π°ΠΌΠΈ Ρ‚ΠΎΡ‡Π΅ΠΊ.
  2. ΠžΡ‚Ρ€ΠΈΡΠΎΠ²ΠΊΠ° Ρ†Π²Π΅Ρ‚Π½Ρ‹Ρ… Π»ΠΈΠ½ΠΈΠΉ ΠΏΠΎ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π°ΠΌ Ρ‚ΠΎΡ‡Π΅ΠΊ Π½Π° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ Π½ΡƒΠΆΠ½ΠΎΠ³ΠΎ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 256×256 ΠΈΠ»ΠΈ 128×128).
  3. ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Ρ… ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π² Ρ‚Π΅Π½Π·ΠΎΡ€.

Π’ Ρ€Π°ΠΌΠΊΠ°Ρ… сорСвнования срСди kernel-ΠΎΠ² Π½Π° Python Π·Π°Π΄Π°Ρ‡Π° Ρ€Π΅ΡˆΠ°Π»Π°ΡΡŒ прСимущСствСнно срСдствами OpenCV. Один ΠΈΠ· Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ простых ΠΈ ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½Ρ‹Ρ… Π°Π½Π°Π»ΠΎΠ³ΠΎΠ² Π½Π° R Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π³Π»ΡΠ΄Π΅Ρ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

РСализация прСобразования JSON Π² Ρ‚Π΅Π½Π·ΠΎΡ€ Π½Π° R

r_process_json_str <- function(json, line.width = 3, 
                               color = TRUE, scale = 1) {
  # ΠŸΠ°Ρ€ΡΠΈΠ½Π³ JSON
  coords <- jsonlite::fromJSON(json, simplifyMatrix = FALSE)
  tmp <- tempfile()
  # УдаляСм Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ Ρ„Π°ΠΉΠ» ΠΏΠΎ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ
  on.exit(unlink(tmp))
  png(filename = tmp, width = 256 * scale, height = 256 * scale, pointsize = 1)
  # ΠŸΡƒΡΡ‚ΠΎΠΉ Π³Ρ€Π°Ρ„ΠΈΠΊ
  plot.new()
  # Π Π°Π·ΠΌΠ΅Ρ€ ΠΎΠΊΠ½Π° Π³Ρ€Π°Ρ„ΠΈΠΊΠ°
  plot.window(xlim = c(256 * scale, 0), ylim = c(256 * scale, 0))
  # Π¦Π²Π΅Ρ‚Π° Π»ΠΈΠ½ΠΈΠΉ
  cols <- if (color) rainbow(length(coords)) else "#000000"
  for (i in seq_along(coords)) {
    lines(x = coords[[i]][[1]] * scale, y = coords[[i]][[2]] * scale, 
          col = cols[i], lwd = line.width)
  }
  dev.off()
  # ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ изобраТСния Π² 3-Ρ… ΠΌΠ΅Ρ€Π½Ρ‹ΠΉ массив
  res <- png::readPNG(tmp)
  return(res)
}

r_process_json_vector <- function(x, ...) {
  res <- lapply(x, r_process_json_str, ...)
  # ОбъСдинСниС 3-Ρ… ΠΌΠ΅Ρ€Π½Ρ‹Ρ… массивов ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΎΠΊ Π² 4-Ρ… ΠΌΠ΅Ρ€Π½Ρ‹ΠΉ Π² Ρ‚Π΅Π½Π·ΠΎΡ€
  res <- do.call(abind::abind, c(res, along = 0))
  return(res)
}

РисованиС выполняСтся стандартными срСдствами R с сохранСниСм Π²ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ PNG, хранящийся Π² ΠžΠ—Π£ (Π² Linux Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ R находятся Π² ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³Π΅ /tmp, смонтированном Π² ΠžΠ—Π£). Π—Π°Ρ‚Π΅ΠΌ этот Ρ„Π°ΠΉΠ» считываСтся Π² Π²ΠΈΠ΄Π΅ Ρ‚Ρ€Ρ‘Ρ…ΠΌΠ΅Ρ€Π½ΠΎΠ³ΠΎ массива с числами Π² Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π΅ ΠΎΡ‚ 0 Π΄ΠΎ 1. Π­Ρ‚ΠΎ Π²Π°ΠΆΠ½ΠΎ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Π±ΠΎΠ»Π΅Π΅ общСпринятый BMP Π±Ρ‹Π» Π±Ρ‹ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½ Π² raw-массив с hex-ΠΊΠΎΠ΄Π°ΠΌΠΈ Ρ†Π²Π΅Ρ‚ΠΎΠ².

ΠŸΡ€ΠΎΡ‚Π΅ΡΡ‚ΠΈΡ€ΡƒΠ΅ΠΌ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚:

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

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ ΠΏΠΎΠ΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ R, C++ ΠΈ нСйросСтки

Π‘Π°ΠΌ Π±Π°Ρ‚Ρ‡ Π±ΡƒΠ΄Π΅Ρ‚ Ρ„ΠΎΡ€ΠΌΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

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

Данная рСализация Π½Π°ΠΌ показалась Π½Π΅ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½ΠΎΠΉ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Ρ„ΠΎΡ€ΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π±ΠΎΠ»ΡŒΡˆΠΈΡ… Π±Π°Ρ‚Ρ‡Π΅ΠΉ Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ‚ Π½Π΅ΠΏΡ€ΠΈΠ»ΠΈΡ‡Π½ΠΎ ΠΌΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ, ΠΈ ΠΌΡ‹ Ρ€Π΅ΡˆΠΈΠ»ΠΈ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΎΠΏΡ‹Ρ‚ΠΎΠΌ ΠΊΠΎΠ»Π»Π΅Π³, задСйствовав ΠΌΠΎΡ‰Π½ΡƒΡŽ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ OpenCV. На Ρ‚ΠΎΡ‚ ΠΌΠΎΠΌΠ΅Π½Ρ‚ Π³ΠΎΡ‚ΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠ°ΠΊΠ΅Ρ‚Π° для R Π½Π΅ Π±Ρ‹Π»ΠΎ (Π½Π΅Ρ‚ Π΅Π³ΠΎ ΠΈ сСйчас), поэтому Π±Ρ‹Π»Π° написана минимальная рСализация Ρ‚Ρ€Π΅Π±ΡƒΠ΅ΠΌΠΎΠ³ΠΎ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»Π° Π½Π° C++ с ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠ΅ΠΉ Π² ΠΊΠΎΠ΄ Π½Π° R ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ Rcpp.

Для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π·Π°Π΄Π°Ρ‡ΠΈ использовались слСдущиС ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ:

  1. OpenCV для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с изобраТСниями ΠΈ рисования Π»ΠΈΠ½ΠΈΠΉ. Использовали ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ установлСнныС систСмныС Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ ΠΈ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΡ‡Π½Ρ‹Π΅ Ρ„Π°ΠΉΠ»Ρ‹, Π° Ρ‚Π°ΠΊΠΆΠ΅ Π΄ΠΈΠ½Π°ΠΌΠΈΡ‡Π΅ΡΠΊΡƒΡŽ Π»ΠΈΠ½ΠΊΠΎΠ²ΠΊΡƒ.

  2. xtensor для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с ΠΌΠ½ΠΎΠ³ΠΎΠΌΠ΅Ρ€Π½Ρ‹ΠΌΠΈ массивами ΠΈ Ρ‚Π΅Π½Π·ΠΎΡ€Π°ΠΌΠΈ. Использовали Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΡ‡Π½Ρ‹Π΅ Ρ„Π°ΠΉΠ»Ρ‹, Π²ΠΊΠ»ΡŽΡ‡Ρ‘Π½Π½Ρ‹Π΅ Π² ΠΎΠ΄Π½ΠΎΠΈΠΌΡ‘Π½Π½Ρ‹ΠΉ R-ΠΏΠ°ΠΊΠ΅Ρ‚. Π‘ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° позволяСт Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с ΠΌΠ½ΠΎΠ³ΠΎΠΌΠ΅Ρ€Π½Ρ‹ΠΌΠΈ массивами, ΠΏΡ€ΠΈΡ‡Ρ‘ΠΌ ΠΊΠ°ΠΊ Π² row major, Ρ‚Π°ΠΊ ΠΈ Π² column major порядкС.

  3. ndjson для парсинга JSON. Данная Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² xtensor автоматичСски ΠΏΡ€ΠΈ Π΅Ρ‘ Π½Π°Π»ΠΈΡ‡ΠΈΠΈ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅.

  4. RcppThread для ΠΎΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π²Π΅ΠΊΡ‚ΠΎΡ€Π° ΠΈΠ· JSON-ΠΎΠ². Использовали Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΡ‡Π½Ρ‹Π΅ Ρ„Π°ΠΉΠ»Ρ‹, прСдоставляСмыС этим ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ. ΠžΡ‚ Π±ΠΎΠ»Π΅Π΅ популярного RcppParallel ΠΏΠ°ΠΊΠ΅Ρ‚ срСди ΠΏΡ€ΠΎΡ‡Π΅Π³ΠΎ отличаСтся встроСнным ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌΠΎΠΌ прСрывания Ρ†ΠΈΠΊΠ»Π° (interrupt).

Π‘Ρ‚ΠΎΠΈΡ‚ ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ xtensor оказался просто Π½Π°Ρ…ΠΎΠ΄ΠΊΠΎΠΉ: ΠΊΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ ΠΎΠ½ ΠΎΠ±Π»Π°Π΄Π°Π΅Ρ‚ ΠΎΠ±ΡˆΠΈΡ€Π½Ρ‹ΠΌ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΠΎΠΌ ΠΈ высокой ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ, Π΅Π³ΠΎ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ оказались довольно ΠΎΡ‚Π·Ρ‹Π²Ρ‡ΠΈΠ²Ρ‹ΠΌΠΈ ΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΈΠ²Π½ΠΎ ΠΈ ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ ΠΎΡ‚Π²Π΅Ρ‡Π°Π»ΠΈ Π½Π° Π²ΠΎΠ·Π½ΠΈΠΊΠ°ΡŽΡ‰ΠΈΠ΅ вопросы. Π‘ ΠΈΡ… ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΡƒΠ΄Π°Π»ΠΎΡΡŒ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ прСобразования ΠΌΠ°Ρ‚Ρ€ΠΈΡ† OpenCV Π² Ρ‚Π΅Π½Π·ΠΎΡ€Ρ‹ xtensor, Π° Ρ‚Π°ΠΊΠΆΠ΅ способ объСдинСния 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. Для автоматичСского нахоТдСния ΠΏΡƒΡ‚Π΅ΠΉ ΠΈ Ρ„Π»Π°Π³ΠΎΠ² использовали ΠΏΠΎΠΏΡƒΠ»ΡΡ€Π½ΡƒΡŽ linux-ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Ρƒ pkg-config.

РСализация Rcpp-ΠΏΠ»Π°Π³ΠΈΠ½Π° для использования Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ OpenCV

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

РСализация прСобразования 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))

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ ΠΏΠΎΠ΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ R, C++ ΠΈ нСйросСтки
Π‘Ρ€Π°Π²Π½Π΅Π½ΠΈΠ΅ скорости Ρ€Π°Π±ΠΎΡ‚Ρ‹ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΉ Π½Π° R ΠΈ Π‘++

res_bench <- bench::mark(
  r_process_json_str(tmp_data[4, drawing], scale = 0.5),
  cpp_process_json_str(tmp_data[4, drawing], scale = 0.5),
  check = FALSE,
  min_iterations = 100
)
# ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Π±Π΅Π½Ρ‡ΠΌΠ°Ρ€ΠΊΠ°
cols <- c("expression", "min", "median", "max", "itr/sec", "total_time", "n_itr")
res_bench[, cols]

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

library(ggplot2)
# ΠŸΡ€ΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ Π·Π°ΠΌΠ΅Ρ€Π°
res_bench <- bench::press(
  batch_size = 2^(4:10),
  {
    .data <- tmp_data[sample(seq_len(.N), batch_size), drawing]
    bench::mark(
      r_process_json_vector(.data, scale = 0.5),
      cpp_process_json_vector(.data,  scale = 0.5),
      min_iterations = 50,
      check = FALSE
    )
  }
)

res_bench[, cols]

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

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

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ ΠΏΠΎΠ΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ R, C++ ΠΈ нСйросСтки

Как Π²ΠΈΠ΄ΠΈΠΌ, прирост скорости оказался ΠΎΡ‡Π΅Π½ΡŒ Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ, ΠΈ Π΄ΠΎΠ³Π½Π°Ρ‚ΡŒ ΠΊΠΎΠ΄ Π½Π° C++ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΊΠΎΠ΄Π° Π½Π° R Π½Π΅ прСдставляСтся Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ΠΌ.

3. Π˜Ρ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€Ρ‹ для Π²Ρ‹Π³Ρ€ΡƒΠ·ΠΊΠΈ Π±Π°Ρ‚Ρ‡Π΅ΠΉ ΠΈΠ· Π‘Π”

R ΠΈΠΌΠ΅Π΅Ρ‚ Π·Π°ΡΠ»ΡƒΠΆΠ΅Π½Π½ΡƒΡŽ Ρ€Π΅ΠΏΡƒΡ‚Π°Ρ†ΠΈΡŽ языка для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π΄Π°Π½Π½Ρ‹Ρ…, ΠΏΠΎΠΌΠ΅Ρ‰Π°ΡŽΡ‰ΠΈΡ…ΡΡ Π² ΠžΠ—Π£, Π² Ρ‚ΠΎ врСмя ΠΊΠ°ΠΊ для ΠŸΠΈΡ‚ΠΎΠ½Π° Π±ΠΎΠ»Π΅Π΅ Ρ…Π°Ρ€Π°ΠΊΡ‚Π΅Ρ€Π½Π° итСративная ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π΄Π°Π½Π½Ρ‹Ρ…, ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‰Π°Ρ Π»Π΅Π³ΠΊΠΎ ΠΈ Π½Π΅ΠΏΡ€ΠΈΠ½ΡƒΠΆΠ΄Π΅Π½Π½ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Ρ‹Π²Π°Ρ‚ΡŒ out-of-core вычислСния (вычислСния с использованиСм внСшнСй памяти). ΠšΠ»Π°ΡΡΠΈΡ‡Π΅ΡΠΊΠΈΠΌ ΠΈ Π°ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½Ρ‹ΠΌ для нас Π² контСкстС описываСмой Π·Π°Π΄Π°Ρ‡ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠΌ Ρ‚Π°ΠΊΠΈΡ… вычислСний ΡΠ²Π»ΡΡŽΡ‚ΡΡ Π³Π»ΡƒΠ±ΠΎΠΊΠΈΠ΅ Π½Π΅ΠΉΡ€ΠΎΠ½Π½Ρ‹Π΅ сСти, ΠΎΠ±ΡƒΡ‡Π°Π΅ΠΌΡ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠΌ Π³Ρ€Π°Π΄ΠΈΠ΅Π½Ρ‚Π½ΠΎΠ³ΠΎ спуска с аппроксимациСй Π³Ρ€Π°Π΄ΠΈΠ΅Π½Ρ‚Π° Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠΌ шагС ΠΏΠΎ нСбольшой ΠΏΠΎΡ€Ρ†ΠΈΠΈ наблюдСний, ΠΈΠ»ΠΈ ΠΌΠΈΠ½ΠΈ-Π±Π°Ρ‚Ρ‡Ρƒ.

Π€Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΈ для Π³Π»ΡƒΠ±ΠΎΠΊΠΎΠ³ΠΎ обучСния, написанныС Π½Π° Python, ΠΈΠΌΠ΅ΡŽΡ‚ ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹Π΅ классы, Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΡŽΡ‰ΠΈΠ΅ ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€Ρ‹ ΠΏΠΎ Π΄Π°Π½Π½Ρ‹ΠΌ: Ρ‚Π°Π±Π»ΠΈΡ†Π°ΠΌΠΈ, ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ°ΠΌ Π² ΠΏΠ°ΠΏΠΊΠ°Ρ…, Π±ΠΈΠ½Π°Ρ€Π½Ρ‹ΠΌ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π°ΠΌ ΠΈ ΠΏΡ€. МоТно ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π³ΠΎΡ‚ΠΎΠ²Ρ‹Π΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹ ΠΈΠ»ΠΈ ΠΆΠ΅ ΠΏΠΈΡΠ°Ρ‚ΡŒ свои собствСнныС для спСцифичСских Π·Π°Π΄Π°Ρ‡. Π’ R ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ всСми возмоТностями питоновской Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ keras с Π΅Π³ΠΎ Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹ΠΌΠΈ Π±Π΅ΠΊΠ΅Π½Π΄Π°ΠΌΠΈ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ ΠΎΠ΄Π½ΠΎΠΈΠΌΠ΅Π½Π½ΠΎΠ³ΠΎ ΠΏΠ°ΠΊΠ΅Ρ‚Π°, Π² свою ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ²Π΅Ρ€Ρ… ΠΏΠ°ΠΊΠ΅Ρ‚Π° reticulate. ПослСдний заслуТиваСт ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΠΉ большой ΡΡ‚Π°Ρ‚ΡŒΠΈ; ΠΎΠ½ Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ позволяСт Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ ΠΊΠΎΠ΄ Π½Π° Python ΠΈΠ· R, Π½ΠΎ ΠΈ обСспСчиваСт ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Ρƒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΌΠ΅ΠΆΠ΄Ρƒ R- ΠΈ Python-сСссиями, автомагичСски выполняя всС Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ прСобразования Ρ‚ΠΈΠΏΠΎΠ².

ΠžΡ‚ нСобходимости Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ всС Π΄Π°Π½Π½Ρ‹Π΅ Π² ΠžΠ—Π£ ΠΌΡ‹ избавились Π·Π° счСт использования MonetDBLite, всю «Π½Π΅ΠΉΡ€ΠΎΡΠ΅Ρ‚Π΅Π²ΡƒΡŽ» Ρ€Π°Π±ΠΎΡ‚Ρƒ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠΎΠ΄ Π½Π° Python, Π½Π°ΠΌ остаСтся лишь Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ ΠΏΠΎ Π΄Π°Π½Π½Ρ‹ΠΌ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Π³ΠΎΡ‚ΠΎΠ²ΠΎΠ³ΠΎ для Ρ‚Π°ΠΊΠΎΠΉ ситуации Π½Π΅Ρ‚ Π½ΠΈ Π½Π° R, Π½ΠΈ Π½Π° Python. Π’Ρ€Π΅Π±ΠΎΠ²Π°Π½ΠΈΠΉ ΠΊ Π½Π΅ΠΌΡƒ ΠΏΠΎ сути всСго Π΄Π²Π°: ΠΎΠ½ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ Π±Π°Ρ‚Ρ‡ΠΈ Π² бСсконСчном Ρ†ΠΈΠΊΠ»Π΅ ΠΈ ΡΠΎΡ…Ρ€Π°Π½ΡΡ‚ΡŒ своС состояниС ΠΌΠ΅ΠΆΠ΄Ρƒ итСрациями (послСднСС Π² R ΠΏΡ€ΠΎΡΡ‚Π΅ΠΉΡˆΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ рСализуСтся ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ Π·Π°ΠΌΡ‹ΠΊΠ°Π½ΠΈΠΉ). Π Π°Π½Π΅Π΅ Ρ‚Ρ€Π΅Π±ΠΎΠ²Π°Π»ΠΎΡΡŒ Π²Π½ΡƒΡ‚Ρ€ΠΈ ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€Π° явным ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Ρ‹Π²Π°Ρ‚ΡŒ массивы R Π² numpy-массивы, Π½ΠΎ Π°ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½Π°Ρ вСрсия ΠΏΠ°ΠΊΠ΅Ρ‚Π° 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 соотвСтствуСт отрисовкС ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΎΠΊ 256Ρ…256 пиксСлСй, scale = 0.5 β€” 128Ρ…128 пиксСлСй), ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ цвСтности (color = FALSE Π·Π°Π΄Π°Π΅Ρ‚ отрисовку Π² ΠΎΡ‚Ρ‚Π΅Π½ΠΊΠ°Ρ… сСрого, ΠΏΡ€ΠΈ использовании color = TRUE ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΡˆΡ‚Ρ€ΠΈΡ… рисуСтся Π½ΠΎΠ²Ρ‹ΠΌ Ρ†Π²Π΅Ρ‚ΠΎΠΌ) ΠΈ ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ прСпроцСссинга для сСтСй, ΠΏΡ€Π΅Π΄ΠΎΠ±ΡƒΡ‡Π΅Π½Π½Ρ‹Ρ… Π½Π° imagenet-Π΅. ПослСдний Π½ΡƒΠΆΠ΅Π½ для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΡ‚ΡˆΠΊΠ°Π»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ значСния пиксСлСй с ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π»Π° [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++) ΠΈ созданиС массивов, ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ°ΠΌ. Π—Π°Ρ‚Π΅ΠΌ ΡΠΎΠ·Π΄Π°ΡŽΡ‚ΡΡ one-hot Π²Π΅ΠΊΡ‚ΠΎΡ€Ρ‹ с ΠΌΠ΅Ρ‚ΠΊΠ°ΠΌΠΈ классов, массивы со значСниями пиксСлСй ΠΈ с ΠΌΠ΅Ρ‚ΠΊΠ°ΠΌΠΈ ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΡŽΡ‚ΡΡ Π² список, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΈ являСтся Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΡ‹ΠΌ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ΠΌ. Для ускорСния Ρ€Π°Π±ΠΎΡ‚Ρ‹ использовалось созданиС индСксов Π² Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ… data.table ΠΈ модификация ΠΏΠΎ ссылкС β€” Π±Π΅Π· этих «Ρ„ΠΈΡˆΠ΅ΠΊ» ΠΏΠ°ΠΊΠ΅Ρ‚Π° data.table довольно Ρ‚Ρ€ΡƒΠ΄Π½ΠΎ ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΡΡ„Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΡƒΡŽ Ρ€Π°Π±ΠΎΡ‚Ρƒ со сколько-Π½ΠΈΠ±ΡƒΠ΄ΡŒ Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ объСмами Π΄Π°Π½Π½Ρ‹Ρ… Π² R.

Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Π·Π°ΠΌΠ΅Ρ€ΠΎΠ² скорости Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π½Π° Π½ΠΎΡƒΡ‚Π±ΡƒΡ‡Π½ΠΎΠΌ 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)

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ ΠΏΠΎΠ΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ R, C++ ΠΈ нСйросСтки

Если имССтся достаточный объСм ΠžΠ—Π£, ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΅Ρ€ΡŒΠ΅Π·Π½ΠΎ ΡƒΡΠΊΠΎΡ€ΠΈΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρƒ Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΡƒΡ‚Π΅ΠΌ Π΅Π΅ пСрСноса Π² эту ΡΠ°ΠΌΡƒΡŽ ΠžΠ—Π£ (для нашСй Π·Π°Π΄Π°Ρ‡ΠΈ Ρ…Π²Π°Ρ‚Π°Π΅Ρ‚ 32 Π“Π±). Π’ линуксС ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ монтируСтся Ρ€Π°Π·Π΄Π΅Π» /dev/shm, Π·Π°Π½ΠΈΠΌΠ°ΡŽΡ‰ΠΈΠΉ Π΄ΠΎ ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½Ρ‹ объСма ΠžΠ—Π£. МоТно Π²Ρ‹Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΈ большС, ΠΎΡ‚Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π² /etc/fstab, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»Π°ΡΡŒ запись Π²ΠΈΠ΄Π° tmpfs /dev/shm tmpfs defaults,size=25g 0 0. ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ пСрСзагруТаСмся ΠΈ провСряСм Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚, Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΠ² ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ df -h.

Π˜Ρ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ для тСстовых Π΄Π°Π½Π½Ρ‹Ρ… выглядит Π³ΠΎΡ€Π°Π·Π΄ΠΎ ΠΏΡ€ΠΎΡ‰Π΅, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ тСстовый датасСт Ρ†Π΅Π»ΠΈΠΊΠΎΠΌ помСщаСтся Π² ΠžΠ—Π£:

Π˜Ρ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ для тСстовых Π΄Π°Π½Π½Ρ‹Ρ…

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

  # ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ²
  checkmate::assert_data_table(dt)
  checkmate::assert_count(batch_size)
  checkmate::assert_number(scale, lower = 0.001, upper = 5)
  checkmate::assert_flag(color)
  checkmate::assert_flag(imagenet_preproc)

  # ΠŸΡ€ΠΎΡΡ‚Π°Π²Π»ΡΠ΅ΠΌ Π½ΠΎΠΌΠ΅Ρ€Π° Π±Π°Ρ‚Ρ‡Π΅ΠΉ
  dt[, batch := (.I - 1L) %/% batch_size + 1L]
  data.table::setkey(dt, batch)
  i <- 1
  max_i <- dt[, max(batch)]

  # Π—Π°ΠΌΡ‹ΠΊΠ°Π½ΠΈΠ΅
  function() {
    batch_x <- cpp_process_json_vector(dt[batch == i, drawing], 
                                       scale = scale, color = color)
    if (imagenet_preproc) {
      # Π¨ΠΊΠ°Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ c ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π»Π° [0, 1] Π½Π° ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π» [-1, 1]
      batch_x <- (batch_x - 0.5) * 2
    }
    result <- list(batch_x)
    i <<- i + 1
    return(result)
  }
}

4. Π’Ρ‹Π±ΠΎΡ€ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ‹ ΠΌΠΎΠ΄Π΅Π»ΠΈ

ΠŸΠ΅Ρ€Π²ΠΎΠΉ ΠΈΠ· ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½Π½Ρ‹Ρ… Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€ Π±Ρ‹Π»Π° mobilenet v1, особСнности ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Ρ€Π°Π·ΠΎΠ±Ρ€Π°Π½Ρ‹ Π² этом сообщСнии. Она присутствуСт Π² стандартной поставкС keras ΠΈ, соотвСтствСнно, доступна Π² ΠΎΠ΄Π½ΠΎΠΈΠΌΠ΅Π½Π½ΠΎΠΌ ΠΏΠ°ΠΊΠ΅Ρ‚Π΅ для R. Но ΠΏΡ€ΠΈ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π΅ с ΠΎΠ΄Π½ΠΎΠΊΠ°Π½Π°Π»ΡŒΠ½Ρ‹ΠΌΠΈ изобраТСниями Π²Ρ‹ΡΡΠ½ΠΈΠ»ΠΎΡΡŒ странноС: Π²Ρ…ΠΎΠ΄Π½ΠΎΠΉ Ρ‚Π΅Π½Π·ΠΎΡ€ Π΄ΠΎΠ»ΠΆΠ΅Π½ всСгда ΠΈΠΌΠ΅Ρ‚ΡŒ Ρ€Π°Π·ΠΌΠ΅Ρ€Π½ΠΎΡΡ‚ΡŒ (batch, height, width, 3), Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ число ΠΊΠ°Π½Π°Π»ΠΎΠ² ΠΏΠΎΠΌΠ΅Π½ΡΡ‚ΡŒ нСльзя. Π’ Python Ρ‚Π°ΠΊΠΎΠ³ΠΎ ограничСния Π½Π΅Ρ‚, поэтому ΠΌΡ‹ поспСшили ΠΈ написали свою Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ Π΄Π°Π½Π½ΠΎΠΉ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ‹, слСдуя ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½ΠΎΠΉ ΡΡ‚Π°Ρ‚ΡŒΠ΅ (Π±Π΅Π· Π΄Ρ€ΠΎΠΏΠ°ΡƒΡ‚Π°, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π΅ΡΡ‚ΡŒ Π² 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)
}

НСдостатки Ρ‚Π°ΠΊΠΎΠ³ΠΎ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π° ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½Ρ‹. МодСлСй хочСтся ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΠΌΠ½ΠΎΠ³ΠΎ, Π° ΠΏΠ΅Ρ€Π΅ΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒ ΠΊΠ°ΠΆΠ΄ΡƒΡŽ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρƒ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ, Π½Π°ΠΎΠ±ΠΎΡ€ΠΎΡ‚, Π½Π΅ хочСтся. Π’Π°ΠΊΠΆΠ΅ ΠΌΡ‹ Π±Ρ‹Π»ΠΈ Π»ΠΈΡˆΠ΅Π½Ρ‹ возмоТности ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ вСса ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ, ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΎΠ±ΡƒΡ‡Π΅Π½Π½Ρ‹Ρ… Π½Π° imagenet-Π΅. Как ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ, ΠΏΠΎΠΌΠΎΠ³Π»ΠΎ ΠΈΠ·ΡƒΡ‡Π΅Π½ΠΈΠ΅ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ. Ѐункция 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 ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ с ΠΎΠ±ΡƒΡ‡Π΅Π½Π½Ρ‹ΠΌΠΈ Π½Π° imagenet-Π΅ вСсами ΠΈΠ»ΠΈ Π±Π΅Π· Π½ΠΈΡ…:

Ѐункция для Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Π³ΠΎΡ‚ΠΎΠ²Ρ‹Ρ… Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€

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(). ΠœΡ‹ этот Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π» Ρ‚Π°ΠΊ ΠΈ Π½Π΅ Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Π½Π° этом этапС ΡƒΠΆΠ΅ Π±Ρ‹Π»ΠΎ понятно, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ΠΈΠ²Π½Π΅Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с Ρ†Π²Π΅Ρ‚Π½Ρ‹ΠΌΠΈ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ°ΠΌΠΈ.

ΠžΡΠ½ΠΎΠ²Π½ΡƒΡŽ массу экспСримСнтов ΠΌΡ‹ ΠΏΡ€ΠΎΠ΄Π΅Π»Π°Π»ΠΈ с использованиСм mobilenet вСрсии 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 Π½Π° Ρ‚Ρ€Π΅Ρ…Ρ†Π²Π΅Ρ‚Π½Ρ‹Ρ… изобраТСниях Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠ² 128Ρ…128 пиксСлСй, Π±Π°Π·Π° Π΄Π°Π½Π½Ρ‹Ρ… Π΄ΠΎΠ»ΠΆΠ½Π° Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒΡΡ Π² ΠΏΠ°ΠΏΠΊΠ΅ /home/andrey/doodle_db). Π’ список ΠΌΠΎΠΆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ обучСния, Π²ΠΈΠ΄ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ‚ΠΎΡ€Π° ΠΈ Π»ΡŽΠ±Ρ‹Π΅ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ настраиваСмыС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹. Π’ процСссС ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠΈ ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ Π²Ρ‹ΡΡΠ½ΠΈΠ»ΠΎΡΡŒ, Ρ‡Ρ‚ΠΎ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρƒ mobilenet_v2 ΠΈΠ· Π°ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΠΉ вСрсии keras Π² R ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ нСльзя ΠΈΠ·-Π·Π° Π½Π΅ΡƒΡ‡Ρ‚Π΅Π½Π½Ρ‹Ρ… Π² R-ΠΏΠ°ΠΊΠ΅Ρ‚Π΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ β€” ΠΆΠ΄Π΅ΠΌ, ΠΏΠΎΠΊΠ° пофиксят.

Π­Ρ‚ΠΎΡ‚ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΠ» Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΡƒΡΠΊΠΎΡ€ΠΈΡ‚ΡŒ экспСримСнты с Ρ€Π°Π·Π½Ρ‹ΠΌΠΈ модСлями ΠΏΠΎ ΡΡ€Π°Π²Π½Π΅Π½ΠΈΡŽ с Π±ΠΎΠ»Π΅Π΅ Ρ‚Ρ€Π°Π΄ΠΈΡ†ΠΈΠΎΠ½Π½Ρ‹ΠΌ запуском скриптов Π² RStudio (Π² качСствС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠΉ Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Ρ‹ ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΠΌ ΠΏΠ°ΠΊΠ΅Ρ‚ tfruns). Но Π³Π»Π°Π²Π½ΠΎΠ΅ прСимущСство состоит Π² возмоТности Π»Π΅Π³ΠΊΠΎ ΡƒΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ запуском скриптов Π² Π΄ΠΎΠΊΠ΅Ρ€Π΅ ΠΈΠ»ΠΈ просто Π½Π° сСрвСрС, Π½Π΅ устанавливая для этого RStudio.

6. ДокСризация скриптов

ΠœΡ‹ использовали Π΄ΠΎΠΊΠ΅Ρ€ с Ρ†Π΅Π»ΡŒΡŽ обСспСчСния пСрСносимости срСды для обучСния ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ ΠΌΠ΅ΠΆΠ΄Ρƒ Ρ‡Π»Π΅Π½Π°ΠΌΠΈ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ ΠΈ для ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΈΠ²Π½ΠΎΠ³ΠΎ развёртывания Π² ΠΎΠ±Π»Π°ΠΊΠ΅. ΠΠ°Ρ‡Π°Ρ‚ΡŒ знакомство с этим ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π½Π΅ΠΏΡ€ΠΈΠ²Ρ‹Ρ‡Π½Ρ‹ΠΌ для R-программиста инструмСнтом ΠΌΠΎΠΆΠ½ΠΎ с этой сСрии ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΉ ΠΈΠ»ΠΈ с видСокурса.

Π”ΠΎΠΊΠ΅Ρ€ позволяСт ΠΊΠ°ΠΊ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ собствСнныС ΠΎΠ±Ρ€Π°Π·Ρ‹ «с нуля», Ρ‚Π°ΠΊ ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΎΠ±Ρ€Π°Π·Ρ‹ Π² качСствС основы для создания собствСнных. ΠŸΡ€ΠΈ Π°Π½Π°Π»ΠΈΠ·Π΅ ΠΈΠΌΠ΅ΡŽΡ‰ΠΈΡ…ΡΡ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² ΠΌΡ‹ ΠΏΡ€ΠΈΡˆΠ»ΠΈ ΠΊ Π²Ρ‹Π²ΠΎΠ΄Ρƒ, Ρ‡Ρ‚ΠΎ установка Π΄Ρ€Π°ΠΉΠ²Π΅Ρ€ΠΎΠ² NVIDIA, CUDA+cuDNN ΠΈ питоновских Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ β€” довольно ΠΎΠ±ΡŠΡ‘ΠΌΠ½Π°Ρ Ρ‡Π°ΡΡ‚ΡŒ ΠΎΠ±Ρ€Π°Π·Π°, ΠΈ Ρ€Π΅ΡˆΠΈΠ»ΠΈ Π²Π·ΡΡ‚ΡŒ Π·Π° основу ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π· 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. ИспользованиС Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… GPU Π² ΠΎΠ±Π»Π°ΠΊΠ΅ Google Cloud

Одной ΠΈΠ· особСнностСй сорСвнования Π±Ρ‹Π»ΠΈ вСсьма ΡˆΡƒΠΌΠ½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ (см. Π·Π°Π³Π»Π°Π²Π½ΡƒΡŽ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΡƒ, ΠΏΠΎΠ·Π°ΠΈΠΌΡΡ‚Π²ΠΎΠ²Π°Π½Π½ΡƒΡŽ Ρƒ @Leigh.plt ΠΈΠ· ODS-слака). Π‘ΠΎΡ€ΠΎΡ‚ΡŒΡΡ с этим ΠΏΠΎΠΌΠΎΠ³Π°ΡŽΡ‚ Π±Π°Ρ‚Ρ‡ΠΈ большого Ρ€Π°Π·ΠΌΠ΅Ρ€Π°, ΠΈ ΠΌΡ‹ послС экспСримСнтов Π½Π° ПК с 1 GPU Ρ€Π΅ΡˆΠΈΠ»ΠΈ ΠΎΡΠ²ΠΎΠΈΡ‚ΡŒ ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ Π½Π° Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… GPU Π² ΠΎΠ±Π»Π°ΠΊΠ΅. Использовали GoogleCloud (Ρ…ΠΎΡ€ΠΎΡˆΠ΅Π΅ руководство ΠΏΠΎ основам Ρ€Π°Π±ΠΎΡ‚Ρ‹) ΠΈΠ·-Π·Π° большого Π²Ρ‹Π±ΠΎΡ€Π° доступных ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΉ, ΠΏΡ€ΠΈΠ΅ΠΌΠ»Π΅ΠΌΡ‹Ρ… Ρ†Π΅Π½ ΠΈ бонусных $300. ΠžΡ‚ Тадности Π±Ρ‹Π» Π·Π°ΠΊΠ°Π·Π°Π½ инстанс с 4Ρ…V100 с SSD ΠΈ ΠΊΡƒΡ‡Π΅ΠΉ ΠžΠ—Π£, ΠΈ это Π±Ρ‹Π»ΠΎ большой ошибкой. Π”Π΅Π½ΡŒΠ³ΠΈ такая машина ΠΊΡƒΡˆΠ°Π΅Ρ‚ быстро, Π½Π° экспСримСнтах Π±Π΅Π· ΠΎΡ‚Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½Π° ΠΌΠΎΠΆΠ½ΠΎ Ρ€Π°Π·ΠΎΡ€ΠΈΡ‚ΡŒΡΡ. Π‘ ΡƒΡ‡Π΅Π±Π½Ρ‹ΠΌΠΈ цСлями Π»ΡƒΡ‡ΡˆΠ΅ Π±Ρ€Π°Ρ‚ΡŒ K80. А Π²ΠΎΡ‚ большой объСм ΠžΠ—Π£ пригодился β€” ΠΎΠ±Π»Π°Ρ‡Π½Ρ‹ΠΉ SSD Π½Π΅ Π²ΠΏΠ΅Ρ‡Π°Ρ‚Π»ΠΈΠ» быстродСйcΡ‚Π²ΠΈΠ΅ΠΌ, поэтому Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΡ€ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΌ запускС инстанса пСрСносили Π½Π° 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)
)

ΠšΠ»Π°ΡΡΠΈΡ‡Π΅ΡΠΊΠΈΠΉ ΠΏΡ€ΠΈΠ΅ΠΌ с Π·Π°ΠΌΠΎΡ€ΠΎΠ·ΠΊΠΎΠΉ всСх слоСв, ΠΊΡ€ΠΎΠΌΠ΅ послСднСго, ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅ΠΌ послСднСго слоя, Ρ€Π°Π·ΠΌΠΎΡ€ΠΎΠ·ΠΊΠΎΠΉ ΠΈ Π΄ΠΎΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅ΠΌ ΠΌΠΎΠ΄Π΅Π»ΠΈ Ρ†Π΅Π»ΠΈΠΊΠΎΠΌ для Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… GPU Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ.

Π—Π° ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅ΠΌ слСдили Π±Π΅Π· использования 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 Π² Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ΅ fast.ai); ΠΏΡ€ΠΈΠ»ΠΎΠΆΠΈΠ² Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ усилия, ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π° R сторонниС Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, эту;
  • ΠΊΠ°ΠΊ слСдствиС ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅Π³ΠΎ ΠΏΡƒΠ½ΠΊΡ‚Π°, Π½Π΅ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ ΠΏΠΎΠ΄ΠΎΠ±Ρ€Π°Ρ‚ΡŒ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΡƒΡŽ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ обучСния ΠΏΡ€ΠΈ использовании Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… GPU;
  • Π½Π΅ Ρ…Π²Π°Ρ‚Π°Π΅Ρ‚ соврСмСнных Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€ нСйросСтСй, особСнно ΠΏΡ€Π΅Π΄ΠΎΠ±ΡƒΡ‡Π΅Π½Π½Ρ‹Ρ… Π½Π° imagenet-Π΅;
  • Π½Π΅Ρ‚ one cycle policy ΠΈ discriminative learning rates (сosine annealing ΠΏΠΎ нашСй ΠΏΡ€ΠΎΡΡŒΠ±Π΅ Π±Ρ‹Π» Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½, спасибо skeydan).

Π§Ρ‚ΠΎ ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠ³ΠΎ ΡƒΠ΄Π°Π»ΠΎΡΡŒ вынСсти ΠΈΠ· этого сорСвнования:

  • На ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΌΠ°Π»ΠΎΠΌΠΎΡ‰Π½ΠΎΠΌ ΠΆΠ΅Π»Π΅Π·Π΅ ΠΌΠΎΠΆΠ½ΠΎ Π±Π΅Π· Π±ΠΎΠ»ΠΈ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с ΠΏΡ€ΠΈΠ»ΠΈΡ‡Π½Ρ‹ΠΌΠΈ (ΠΊΡ€Π°Ρ‚Π½ΠΎ ΠΏΡ€Π΅Π²Ρ‹ΡˆΠ°ΡŽΡ‰ΠΈΠΌΠΈ Ρ€Π°Π·ΠΌΠ΅Ρ€ ΠžΠ—Π£) объСмами Π΄Π°Π½Π½Ρ‹Ρ…. ΠŸΠ°ΠΊΠ΅Ρ‚ data.table экономит ΠΏΠ°ΠΌΡΡ‚ΡŒ Π·Π° счСт in-place ΠΌΠΎΠ΄ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ Ρ‚Π°Π±Π»ΠΈΡ†, Ρ‡Ρ‚ΠΎ позволяСт ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ ΠΈΡ… копирования, ΠΈ ΠΏΡ€ΠΈ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎΠΌ использовании Π΅Π³ΠΎ возмоТностСй ΠΏΠΎΡ‡Ρ‚ΠΈ всСгда дСмонстрируСт Π½Π°ΠΈΠ±ΠΎΠ»ΡŒΡˆΡƒΡŽ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ срСди всСх извСстных Π½Π°ΠΌ инструмСнтов для скриптовых языков. Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Ρ… Π² Π‘Π” позволяСт Π²ΠΎ ΠΌΠ½ΠΎΠ³ΠΈΡ… случаях Π²ΠΎΠΎΠ±Ρ‰Π΅ Π½Π΅ Π΄ΡƒΠΌΠ°Ρ‚ΡŒ ΠΎ нСобходимости Π²Ρ‚ΠΈΡΠΊΠΈΠ²Π°Ρ‚ΡŒ вСсь датасСт Π² ΠžΠ—Π£.
  • ΠœΠ΅Π΄Π»Π΅Π½Π½Ρ‹Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π½Π° R ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ быстрыми Π½Π° C++ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ ΠΏΠ°ΠΊΠ΅Ρ‚Π° Rcpp. Если Π²Π΄ΠΎΠ±Π°Π²ΠΎΠΊ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ RcppThread ΠΈΠ»ΠΈ RcppParallel, ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ кроссплатформСнныС ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½Ρ‹Π΅ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ, поэтому ΠΊΠΎΠ΄ Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ R ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΠΈΡ‚ΡŒ Π½Π΅ трСбуСтся.
  • ΠŸΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ Rcpp ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π±Π΅Π· ΡΠ΅Ρ€ΡŒΠ΅Π·Π½Ρ‹Ρ… Π·Π½Π°Π½ΠΈΠΉ C++, Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹ΠΉ ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ ΠΈΠ·Π»ΠΎΠΆΠ΅Π½ здСсь. Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΡ‡Π½Ρ‹Π΅ Ρ„Π°ΠΉΠ»Ρ‹ для ряда ΠΊΡ€ΡƒΡ‚Ρ‹Ρ… ΡΠΈΡˆΠ½Ρ‹Ρ… Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ Ρ‚ΠΈΠΏΠ° xtensor доступны Π½Π° CRAN, Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ формируСтся инфраструктура для Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ², ΠΈΠ½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΡƒΡŽΡ‰ΠΈΡ… Π² R Π³ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ Π²Ρ‹ΡΠΎΠΊΠΎΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠΎΠ΄ Π½Π° C++. Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ удобство β€” подсвСтка синтаксиса ΠΈ статичСский Π°Π½Π°Π»ΠΈΠ·Π°Ρ‚ΠΎΡ€ ΠΊΠΎΠ΄Π° Π½Π° Π‘++ Π² RStudio.
  • docopt позволяСт Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ самодостаточныС скрипты с ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ. Π­Ρ‚ΠΎ ΡƒΠ΄ΠΎΠ±Π½ΠΎ для использования Π½Π° ΡƒΠ΄Π°Π»Π΅Π½Π½ΠΎΠΌ сСрвСрС, Π² Ρ‚.Ρ‡. ΠΏΠΎΠ΄ Π΄ΠΎΠΊΠ΅Ρ€ΠΎΠΌ. Π’ RStudio ΠΏΡ€ΠΎΠ²ΠΎΠ΄ΠΈΡ‚ΡŒ многочасовыС экспСримСнты с ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅ΠΌ нСйросСтСй Π½Π΅ΡƒΠ΄ΠΎΠ±Π½ΠΎ, Π΄Π° ΠΈ сама установка IDE Π½Π° сСрвСрС Π½Π΅ всСгда ΠΎΠΏΡ€Π°Π²Π΄Π°Π½Π°.
  • Π”ΠΎΠΊΠ΅Ρ€ обСспСчиваСт ΠΏΠ΅Ρ€Π΅Π½ΠΎΡΠΈΠΌΠΎΡΡ‚ΡŒ ΠΊΠΎΠ΄Π° ΠΈ Π²ΠΎΡΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ² ΠΌΠ΅ΠΆΠ΄Ρƒ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°ΠΌΠΈ с Ρ€Π°Π·Π½Ρ‹ΠΌΠΈ вСрсиями ОБ ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ, Π° Ρ‚Π°ΠΊΠΆΠ΅ простоту запуска Π½Π° сСрвСрах. Π—Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ вСсь ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½ для обучСния ΠΌΠΎΠΆΠ½ΠΎ всСго ΠΎΠ΄Π½ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ.
  • Google Cloud β€” Π±ΡŽΠ΄ΠΆΠ΅Ρ‚Π½Ρ‹ΠΉ способ ΠΏΠΎΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π° Π΄ΠΎΡ€ΠΎΠ³ΠΎΠΌ ΠΆΠ΅Π»Π΅Π·Π΅, Π½ΠΎ Π½ΡƒΠΆΠ½ΠΎ Π²Π΄ΡƒΠΌΡ‡ΠΈΠ²ΠΎ Π²Ρ‹Π±ΠΈΡ€Π°Ρ‚ΡŒ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ.
  • Π—Π°ΠΌΠ΅Ρ€ΡΡ‚ΡŒ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ΠΎΠ² ΠΊΠΎΠ΄Π° ΠΎΡ‡Π΅Π½ΡŒ ΠΏΠΎΠ»Π΅Π·Π½ΠΎ, особСнно ΠΏΡ€ΠΈ сочСтании R ΠΈ C++, Π° с ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ bench β€” Π΅Ρ‰Π΅ ΠΈ ΠΎΡ‡Π΅Π½ΡŒ Π»Π΅Π³ΠΊΠΎ.

Π’ Ρ†Π΅Π»ΠΎΠΌ этот ΠΎΠΏΡ‹Ρ‚ Π±Ρ‹Π» ΠΎΡ‡Π΅Π½ΡŒ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ΠΌ, ΠΈ ΠΌΡ‹ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π½Π°Π΄ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ΠΌ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΈΠ· ΠΎΠ·Π²ΡƒΡ‡Π΅Π½Π½Ρ‹Ρ… ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ.

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ