Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ Π΄Π° ΡΠ²ΡŠΡ€ΠΆΠ΅ΠΌ R, C++ ΠΈ Π½Π΅Π²Ρ€ΠΎΠ½Π½ΠΈ ΠΌΡ€Π΅ΠΆΠΈ

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ Π΄Π° ΡΠ²ΡŠΡ€ΠΆΠ΅ΠΌ R, C++ ΠΈ Π½Π΅Π²Ρ€ΠΎΠ½Π½ΠΈ ΠΌΡ€Π΅ΠΆΠΈ

Π—Π΄Ρ€Π°Π²Π΅ΠΉΡ‚Π΅, Π₯Π°Π±Ρ€!

ΠŸΡ€Π΅Π· СсСнта Π½Π° ΠΌΠΈΠ½Π°Π»Π°Ρ‚Π° Π³ΠΎΠ΄ΠΈΠ½Π° Π² Kaggle имашС конкурс Π·Π° классификация Π½Π° нарисувани ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠΈ Quick Draw Doodle Recognition, Π² ΠΊΠΎΠΉΡ‚ΠΎ участва Π΅ΠΊΠΈΠΏ ΠΎΡ‚ R програмисти, ΡΡŠΡΡ‚Π°Π²Π΅Π½ ΠΎΡ‚ ΠΡ€Ρ‚ΡŒΠΎΠΌ ΠšΠ»Π΅Π²Ρ†ΠΎΠ², Π€ΠΈΠ»ΠΈΠΏ Π£ΠΏΡ€Π°Ρ‚ΠΈΠ²Π΅Π»Π΅Π² ΠΈ АндрСй ΠžΠ³ΡƒΡ€Ρ†ΠΎΠ². Няма Π΄Π° описвамС ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ ΡΡŠΡΡ‚Π΅Π·Π°Π½ΠΈΠ΅Ρ‚ΠΎ, Ρ‚ΠΎΠ²Π° Π²Π΅Ρ‡Π΅ Π΅ Π½Π°ΠΏΡ€Π°Π²Π΅Π½ΠΎ Π² ΡΠΊΠΎΡ€ΠΎΡˆΠ½Π° публикация.

Π’ΠΎΠ·ΠΈ ΠΏΡŠΡ‚ Π½Π΅ успяхмС Π΄Π° спСчСлим ΠΌΠ΅Π΄Π°Π»ΠΈ, Π½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ…ΠΌΠ΅ ΠΌΠ½ΠΎΠ³ΠΎ Ρ†Π΅Π½Π΅Π½ ΠΎΠΏΠΈΡ‚, Π·Π°Ρ‚ΠΎΠ²Π° искам Π΄Π° сподСля с общността някои ΠΎΡ‚ Π½Π°ΠΉ-интСрСстнитС ΠΈ ΠΏΠΎΠ»Π΅Π·Π½ΠΈ Π½Π΅Ρ‰Π°, ΠΊΠΎΠΈΡ‚ΠΎ Ρ€Π°Π·Π³Π»Π΅Π΄Π°Ρ…ΠΌΠ΅ Π² Kaggle ΠΈ Π² Π΅ΠΆΠ΅Π΄Π½Π΅Π²Π½Π°Ρ‚Π° Ρ€Π°Π±ΠΎΡ‚Π°. Π‘Ρ€Π΅Π΄ Ρ€Π°Π·Π³Π»Π΅Π΄Π°Π½ΠΈΡ‚Π΅ Ρ‚Π΅ΠΌΠΈ: трудният ΠΆΠΈΠ²ΠΎΡ‚ Π±Π΅Π· OpenCV, парсинг Π½Π° JSON (Π² Ρ‚Π΅Π·ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΈ сС Ρ€Π°Π·Π³Π»Π΅ΠΆΠ΄Π°Ρ‚ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ Π½Π° C++ ΠΊΠΎΠ΄ Π² скриптовС ΠΈΠ»ΠΈ ΠΏΠ°ΠΊΠ΅Ρ‚ΠΈ Π½Π° R Ρ‡Ρ€Π΅Π· Rcpp), ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΠ·ΠΈΡ€Π°Π½Π΅ Π½Π° скриптовС ΠΈ контСйнСризация Π½Π° ΠΊΡ€Π°ΠΉΠ½ΠΎΡ‚ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅. ЦСлият ΠΊΠΎΠ΄ ΠΎΡ‚ публикацията Π² ΡƒΠ΄ΠΎΠ±Π΅Π½ Π·Π° изпълнСниС Π²ΠΈΠ΄ Π΅ Π½Π°Π»ΠΈΡ‡Π΅Π½ Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ.

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

  1. Π•Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΎ Π·Π°Ρ€Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° Π΄Π°Π½Π½ΠΈ ΠΎΡ‚ CSV Π² Π±Π°Π·Π° MonetDB
  2. ΠŸΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° Π½Π° ΠΏΠ°Ρ€Ρ‚ΠΈΠ΄ΠΈ
  3. Π˜Ρ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ΠΈ Π·Π° ΠΈΠ·Π²Π»ΠΈΡ‡Π°Π½Π΅ Π½Π° ΠΏΠ°Ρ€Ρ‚ΠΈΠ΄ΠΈ ΠΎΡ‚ Π‘Π”
  4. Π˜Π·Π±ΠΎΡ€ Π½Π° Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π° Π½Π° ΠΌΠΎΠ΄Π΅Π»Π°
  5. ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΠ·ΠΈΡ€Π°Π½Π΅ Π½Π° скриптовС
  6. ΠšΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ΠΈΠ·Π°Ρ†ΠΈΡ Π½Π° скриптовС
  7. ИзползванС на няколко GPU в облака Google Cloud
  8. ВмСсто Π·Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

1. Π•Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΎ Π·Π°Ρ€Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° Π΄Π°Π½Π½ΠΈ ΠΎΡ‚ CSV Π² Π±Π°Π·Π° MonetDB

Π”Π°Π½Π½ΠΈΡ‚Π΅ Π² Ρ‚ΠΎΠ²Π° ΡΡŠΡΡ‚Π΅Π·Π°Π½ΠΈΠ΅ сС прСдоставят Π½Π΅ във Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° Π½Π° Π³ΠΎΡ‚ΠΎΠ²ΠΈ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠΈ, Π° Π² 340 CSV Ρ„Π°ΠΉΠ»Π° (ΠΏΠΎ Π΅Π΄ΠΈΠ½ Ρ„Π°ΠΉΠ» Π½Π° всСки клас), ΡΡŠΠ΄ΡŠΡ€ΠΆΠ°Ρ‰ΠΈ JSON с ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈ Π½Π° Ρ‚ΠΎΡ‡ΠΊΠΈ. Π‘Π²ΡŠΡ€Π·Π²Π°ΠΉΠΊΠΈ Ρ‚Π΅Π·ΠΈ Ρ‚ΠΎΡ‡ΠΊΠΈ с Π»ΠΈΠ½ΠΈΠΈ, ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ ΠΊΡ€Π°ΠΉΠ½ΠΎ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ с Ρ€Π°Π·ΠΌΠ΅Ρ€ 256x256 пиксСла. Π‘ΡŠΡ‰ΠΎ Ρ‚Π°ΠΊΠ° Π·Π° всяка записа посочва Π΅Ρ‚ΠΈΠΊΠ΅Ρ‚, Π΄Π°Π»ΠΈ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ°Ρ‚Π° Π΅ Π±ΠΈΠ»Π° ΠΊΠΎΡ€Π΅ΠΊΡ‚Π½ΠΎ Ρ€Π°Π·ΠΏΠΎΠ·Π½Π°Ρ‚Π° ΠΎΡ‚ използвания ΠΏΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π½Π° ΡΡŠΠ±ΠΈΡ€Π°Π½Π΅ Π½Π° Π΄Π°Π½Π½ΠΈ класификатор, Π΄Π²ΡƒΠ±ΡƒΠΊΠ²Π΅Π½ ΠΊΠΎΠ΄ Π½Π° страната Π½Π° ΠΏΡ€Π΅Π±ΠΈΠ²Π°Π²Π°Π½Π΅ Π½Π° Π°Π²Ρ‚ΠΎΡ€Π° Π½Π° рисунката, ΡƒΠ½ΠΈΠΊΠ°Π»Π΅Π½ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€, Π²Ρ€Π΅ΠΌΠ΅Π²ΠΈ Π΅Ρ‚ΠΈΠΊΠ΅Ρ‚ ΠΈ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Π½Π° класа, ΠΊΠΎΠ΅Ρ‚ΠΎ съвпада с ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° Ρ„Π°ΠΉΠ»Π°. ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€Π°Π½Π°Ρ‚Π° вСрсия Π½Π° ΠΈΠ·Ρ…ΠΎΠ΄Π½ΠΈΡ‚Π΅ Π΄Π°Π½Π½ΠΈ Π΅ с ΠΎΠ±Π΅ΠΌ 7.4 Π“Π± Π² Π°Ρ€Ρ…ΠΈΠ² ΠΈ ΠΏΡ€ΠΈΠ±Π»ΠΈΠ·ΠΈΡ‚Π΅Π»Π½ΠΎ 20 Π“Π± слСд Ρ€Π°Π·Π°Ρ€Ρ…ΠΈΠ²ΠΈΡ€Π°Π½Π΅, ΠΏΡŠΠ»Π½ΠΈΡ‚Π΅ Π΄Π°Π½Π½ΠΈ слСд Ρ€Π°Π·Π°Ρ€Ρ…ΠΈΠ²ΠΈΡ€Π°Π½Π΅ Π·Π°Π΅ΠΌΠ°Ρ‚ 240 Π“Π±. ΠžΡ€Π³Π°Π½ΠΈΠ·Π°Ρ‚ΠΎΡ€ΠΈΡ‚Π΅ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€Π°Ρ…Π°, Ρ‡Π΅ ΠΈ Π΄Π²Π΅Ρ‚Π΅ вСрсии Π²ΡŠΠ·ΠΏΡ€ΠΎΠΈΠ·Π²Π΅ΠΆΠ΄Π°Ρ‚ Π΅Π΄Π½ΠΈ ΠΈ ΡΡŠΡ‰ΠΈ рисунки, Ρ‚.Π΅. ΠΏΡŠΠ»Π½Π°Ρ‚Π° вСрсия Π΅ излишна. Π’ΡŠΠ² всСки случай, ΡΡŠΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½Π° 50 ΠΌΠΈΠ»ΠΈΠΎΠ½Π° ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠΈ Π² Π³Ρ€Π°Ρ„ΠΈΡ‡Π½ΠΈ Ρ„Π°ΠΉΠ»ΠΎΠ²Π΅ ΠΈΠ»ΠΈ ΠΏΠΎΠ΄ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° Π½Π° масиви Π²Π΅Π΄Π½Π°Π³Π° бСшС ΠΏΡ€ΠΈΠ·Π½Π°Ρ‚ΠΎ Π·Π° Π½Π΅Ρ€Π΅Π½Ρ‚Π°Π±ΠΈΠ»Π½ΠΎ ΠΈ Ρ€Π΅ΡˆΠΈΡ…ΠΌΠ΅ Π΄Π° слССм всички CSV Ρ„Π°ΠΉΠ»ΠΎΠ²Π΅ ΠΎΡ‚ Π°Ρ€Ρ…ΠΈΠ²Π° train_simplified.zip Π² Π±Π°Π·Π° Π΄Π°Π½Π½ΠΈ с послСдваща гСнСрация Π½Π° ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠΈ с нуТния Ρ€Π°Π·ΠΌΠ΅Ρ€ "Π½Π° Π»Π΅Ρ‚ΠΎ" Π·Π° всяка ΠΏΠ°Ρ€Ρ‚ΠΈΠ΄Π°.

Π—Π° Π‘Π£Π‘Π” бСшС ΠΈΠ·Π±Ρ€Π°Π½Π° Π΄ΠΎΠ±Ρ€Π΅ ΡƒΡ‚Π²ΡŠΡ€Π΄Π΅Π½Π°Ρ‚Π° MonetDB, Π° ΠΈΠΌΠ΅Π½Π½ΠΎ рСализацията Π·Π° R ΠΏΠΎΠ΄ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π° Π½Π° ΠΏΠ°ΠΊΠ΅Ρ‚ MonetDBLite. ΠŸΠ°ΠΊΠ΅Ρ‚ΡŠΡ‚ Π²ΠΊΠ»ΡŽΡ‡Π²Π° Π² сСбС си Π²Π³Ρ€Π°Π΄Π΅Π½Π°Ρ‚Π° вСрсия Π½Π° ΡΡŠΡ€Π²ΡŠΡ€Π° Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ ΠΈ позволява стартиранС Π½Π° ΡΡŠΡ€Π²ΡŠΡ€Π° Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΎ ΠΎΡ‚ R сСсия ΠΈ Ρ€Π°Π±ΠΎΡ‚Π° с Π½Π΅Π³ΠΎ Ρ‚Π°ΠΌ. Π‘ΡŠΠ·Π΄Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ ΠΈ ΡΠ²ΡŠΡ€Π·Π²Π°Π½Π΅Ρ‚ΠΎ с нСя сС ΠΈΠ·Π²ΡŠΡ€ΡˆΠ²Π° с Π΅Π΄Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π°:

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

НуТно Π΄Π° създадСм Π΄Π²Π΅ Ρ‚Π°Π±Π»ΠΈΡ†ΠΈ: Π΅Π΄Π½Π° Π·Π° всички Π΄Π°Π½Π½ΠΈ ΠΈ Π΄Ρ€ΡƒΠ³Π° Π·Π° слуТСбна информация Π·Π° ΠΊΠ°Ρ‡Π΅Π½ΠΈΡ‚Π΅ Ρ„Π°ΠΉΠ»ΠΎΠ²Π΅ (Ρ‰Π΅ Π΅ ΠΏΠΎΠ»Π΅Π·Π½Π°, Π°ΠΊΠΎ Π½Π΅Ρ‰ΠΎ Π½Π΅ Ρ‚Ρ€ΡŠΠ³Π½Π΅ ΠΈ ΠΏΡ€ΠΎΡ†Π΅ΡΡŠΡ‚ трябва Π΄Π° бъдС възобновСн слСд ΠΊΠ°Ρ‡Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° няколко Ρ„Π°ΠΉΠ»Π°):

БъздаванС Π½Π° Ρ‚Π°Π±Π»ΠΈΡ†ΠΈ

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

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

Най-Π±ΡŠΡ€Π·ΠΈΡΡ‚ Π½Π°Ρ‡ΠΈΠ½ Π·Π° ΠΊΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° Π΄Π°Π½Π½ΠΈ Π² Π‘Π” сС ΠΎΠΊΠ°Π·Π° Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΎΡ‚ΠΎ ΠΊΠΎΠΏΠΈΡ€Π°Π½Π΅ Π½Π° CSV Ρ„Π°ΠΉΠ»ΠΎΠ²Π΅ с SQL β€” ΠΊΠΎΠΌΠ°Π½Π΄Π°Ρ‚Π° COPY OFFSET 2 INTO tablename FROM path USING DELIMITERS ',','n','"' NULL AS '' BEST EFFORT, ΠΊΡŠΠ΄Π΅Ρ‚ΠΎ tablename β€” ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° ΠΈ ΠΏΡŠΡ‚ β€” ΠΏΡŠΡ‚Ρ към Ρ„Π°ΠΉΠ»Π°. ΠŸΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π° с Π°Ρ€Ρ…ΠΈΠ²Π° Π±eшС ΠΎΡ‚ΠΊΡ€ΠΈΡ‚ΠΎ, Ρ‡Π΅ Π²Π³Ρ€Π°Π΄Π΅Π½Π°Ρ‚Π° рСализация 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("Π“Π΅Π½Π΅Ρ€ΠΈΡ€Π°Π½Π΅ Π½Π° Π΅Ρ‚ΠΈΠΊΠ΅Ρ‚ΠΈ")
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("Π“Π΅Π½Π΅Ρ€ΠΈΡ€Π°Π½Π΅ Π½Π° Π½ΠΎΠΌΠ΅Ρ€ Π½Π° Ρ€Π΅Π΄ΠΎΠ²Π΅")
invisible(DBI::dbExecute(con, "ALTER TABLE doodles ADD id serial"))
invisible(DBI::dbExecute(con, "CREATE ORDERED INDEX doodles_id_ord_idx ON doodles(id)"))

Π—Π° Π΄Π° Ρ€Π°Π·Ρ€Π΅ΡˆΠΈΠΌ Π·Π°Π΄Π°Ρ‡Π°Ρ‚Π° ΠΏΠΎ Ρ„ΠΎΡ€ΠΌΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΠΏΠ°Ρ€Ρ‚ΠΈΠ΄Π°Ρ‚Π° "Π½Π° Π»Π΅Ρ‚Ρƒ", бСшС Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π΄Π° постигнСм максимална скорост Π½Π° ΠΈΠ·Π²Π»ΠΈΡ‡Π°Π½Π΅ Π½Π° случайни Ρ€Π΅Π΄ΠΎΠ²Π΅ ΠΎΡ‚ Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° doodles. Π—Π° Ρ‚Π°Π·ΠΈ Ρ†Π΅Π» ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ…ΠΌΠ΅ 3 Ρ‚Ρ€ΠΈΠΊΠ°. ΠŸΡŠΡ€Π²ΠΈΡΡ‚ бСшС Π΄Π° Π½Π°ΠΌΠ°Π»ΠΈΠΌ размСрността Π½Π° Ρ‚ΠΈΠΏΠ°, Π² ΠΊΠΎΠΉΡ‚ΠΎ сС ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π° ID-Ρ‚ΠΎ Π½Π° Π½Π°Π±Π»ΡŽΠ΄Π΅Π½ΠΈΠ΅Ρ‚ΠΎ. Π’ изходния Π½Π°Π±ΠΎΡ€ ΠΎΡ‚ Π΄Π°Π½Π½ΠΈ Π·Π° ΡΡŠΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ Π½Π° ID Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌ Ρ‚ΠΈΠΏ bigint, Π½ΠΎ количСството наблюдСния позволява Π΄Π° вмСстим Ρ‚Π΅Ρ…Π½ΠΈΡ‚Π΅ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΈ, Ρ€Π°Π²Π½ΠΈ Π½Π° рСдовия Π½ΠΎΠΌΠ΅Ρ€, Π² Ρ‚ΠΈΠΏ int. Π’ΡŠΡ€ΡΠ΅Π½Π΅Ρ‚ΠΎ Π² Ρ‚ΠΎΠ·ΠΈ случай става Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»Π½ΠΎ ΠΏΠΎ-Π±ΡŠΡ€Π·ΠΎ. Вторият Ρ‚Ρ€ΠΈΠΊ бСшС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° ORDERED INDEX β€” Π΄ΠΎ Ρ‚ΠΎΠ²Π° Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ стигнахмС Π΅ΠΌΠΏΠΈΡ€ΠΈΡ‡Π½ΠΎ, ΠΎΠΏΠΈΡ‚Π²Π°ΠΉΠΊΠΈ Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈΡ‚Π΅ Π½Π°Π»ΠΈΡ‡Π½ΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΈ. ВрСтият сС ΡΡŠΡΡ‚ΠΎΠ΅ΡˆΠ΅ Π² ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈΠ·ΠΈΡ€Π°Π½ΠΈ заявки. Π‘ΡŠΡ‰Π½ΠΎΡΡ‚Ρ‚Π° Π½Π° ΠΌΠ΅Ρ‚ΠΎΠ΄Π° сС ΡΡŠΡΡ‚ΠΎΠΈ Π² Π΅Π΄Π½ΠΎΠΊΡ€Π°Ρ‚Π½ΠΎΡ‚ΠΎ изпълнСниС Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π°Ρ‚Π° PREPARE с ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° ΠΏΠΎΠ΄Π³ΠΎΡ‚Π²Π΅Π½ΠΎ изразяванС ΠΏΡ€ΠΈ създаванС Π½Π° ΠΊΡƒΠΏΡ‡ΠΈΠ½Π° Π΅Π΄Π½ΠΎΡ‚ΠΈΠΏΠ½ΠΈ заявки, Π½ΠΎ Π² дСйствитСлност ΠΏΠΎΠ΄ΠΎΠ±Ρ€Π΅Π½ΠΈΠ΅Ρ‚ΠΎ Π΅ Π² Ρ€Π°ΠΌΠΊΠΈΡ‚Π΅ Π½Π° статистичСската Π³Ρ€Π΅ΡˆΠΊΠ°. SELECT Π’ΠΎΠ·ΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ позволява ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° Π΄Π°Π½Π½ΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ 450 MB RAM. Π’ΠΎΠ²Π° ΠΎΠ·Π½Π°Ρ‡Π°Π²Π°, Ρ‡Π΅ описаният ΠΌΠ΅Ρ‚ΠΎΠ΄ Π΄Π°Π²Π° Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ Π·Π° Ρ€Π°Π±ΠΎΡ‚Π° с Π½Π°Π±ΠΎΡ€ΠΈ ΠΎΡ‚ Π΄Π°Π½Π½ΠΈ с Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΈ Π² дСсСтки Π³ΠΈΠ³Π°Π±Π°ΠΉΡ‚ΠΈ Π½Π° ΠΏΠΎΡ‡Ρ‚ΠΈ всякакво Π±ΡŽΠ΄ΠΆΠ΅Ρ‚Π½ΠΎ ΠΎΠ±ΠΎΡ€ΡƒΠ΄Π²Π°Π½Π΅, Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»Π½ΠΎ някои ΠΌΠΈΠ½ΠΈ ΠΊΠΎΠΌΠΏΡŽΡ‚Ρ€ΠΈ, ΠΊΠΎΠ΅Ρ‚ΠΎ Π΅ доста впСчатляващо.

ΠžΡΡ‚Π°Π²Π° Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ измСрвания Π½Π° скоростта Π½Π° ΠΈΠ·Π²Π»ΠΈΡ‡Π°Π½Π΅ Π½Π° (случайни) Π΄Π°Π½Π½ΠΈ ΠΈ Π΄Π° ΠΎΡ†Π΅Π½ΠΈΠΌ ΠΌΠ°Ρ‰Π°Π±ΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ ΠΏΡ€ΠΈ ΠΈΠ·Π²Π»ΠΈΡ‡Π°Π½Π΅ Π½Π° ΠΏΠ°Ρ€Ρ‚ΠΈΠ΄ΠΈ с Ρ€Π°Π·Π»ΠΈΡ‡Π΅Π½ Ρ€Π°Π·ΠΌΠ΅Ρ€:

Π‘Π΅Π½Ρ‡ΠΌΠ°Ρ€ΠΊ Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ

library(ggplot2)set.seed(0) # Π‘Π²ΡŠΡ€Π·Π²Π°Π½Π΅ с Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ con <- DBI::dbConnect(MonetDBLite::MonetDBLite(), Sys.getenv("DBDIR"))# Ѐункция Π·Π° ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° Π½Π° заявка Π½Π° страната Π½Π° ΡΡŠΡ€Π²ΡŠΡ€Π° prep_sql <- function(batch_size) { sql <- sprintf("PREPARE SELECT id FROM doodles WHERE id IN (%s)", paste(rep("?", batch_size), collapse = ",")) res <- DBI::dbSendQuery(con, sql) return(res) }# Ѐункция Π·Π° ΠΈΠ·Π²Π»ΠΈΡ‡Π°Π½Π΅ Π½Π° Π΄Π°Π½Π½ΠΈ fetch_data <- function(rs, batch_size) { ids <- sample(seq_len(n), batch_size) res <- DBI::dbFetch(DBI::dbBind(rs, as.list(ids))) return(res) }# ΠŸΡ€ΠΎΠ²Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° ΠΈΠ·ΠΌΠ΅Ρ€Π²Π°Π½Π΅ res_bench <- bench::press( batch_size = 2^(4:10), { rs <- prep_sql(batch_size) bench::mark( fetch_data(rs, batch_size), min_iterations = 50L ) } ) # ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈ Π½Π° Π±Π΅Π½Ρ‡ΠΌΠ°Ρ€ΠΊΠ° cols <- c("batch_size", "min", "median", "max", "itr/sec", "total_time", "n_itr") res_bench[, cols]# batch_size min median max `itr/sec` total_time n_itr # # 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 49ggplot(res_bench, aes(x = factor(batch_size), y = median, group = 1)) + geom_point() + geom_line() + ylab("ΠΌΠ΅Π΄ΠΈΠΉΠ½ΠΎ Π²Ρ€Π΅ΠΌΠ΅, с") + theme_minimal()DBI::dbDisconnect(con, shutdown = TRUE)

2. ΠŸΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° Π½Π° ΠΏΠ°Ρ€Ρ‚ΠΈΠ΄ΠΈ

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ Π΄Π° ΡΠ²ΡŠΡ€ΠΆΠ΅ΠΌ R, C++ ΠΈ Π½Π΅Π²Ρ€ΠΎΠ½Π½ΠΈ ΠΌΡ€Π΅ΠΆΠΈ

ЦСлият процСс Π½Π° ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° Π½Π° ΠΏΠ°Ρ€Ρ‚ΠΈΠ΄ΠΈ Π²ΠΊΠ»ΡŽΡ‡Π²Π° слСднитС Π΅Ρ‚Π°ΠΏΠΈ:

ΠŸΠ°Ρ€ΡΠ²Π°Π½Π΅ Π½Π° няколко JSON ΠΎΠ±Π΅ΠΊΡ‚Π°, ΡΡŠΠ΄ΡŠΡ€ΠΆΠ°Ρ‰ΠΈ Π²Π΅ΠΊΡ‚ΠΎΡ€ΠΈ ΠΎΡ‚ Π½ΠΈΠ·ΠΎΠ²Π΅ с ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈ Π½Π° Ρ‚ΠΎΡ‡ΠΊΠΈ.

  1. РисуванС Π½Π° Ρ†Π²Π΅Ρ‚Π½ΠΈ Π»ΠΈΠ½ΠΈΠΈ ΠΏΠΎ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ΠΈΡ‚Π΅ Π½Π° Ρ‚ΠΎΡ‡ΠΊΠΈΡ‚Π΅ Π½Π° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ с Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ 256Γ—256 ΠΈΠ»ΠΈ 128Γ—128).
  2. ΠŸΡ€Π΅Π²Ρ€ΡŠΡ‰Π°Π½Π΅ Π½Π° ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΡ‚Π΅ изобраТСния Π² Ρ‚Π΅Π½Π·ΠΎΡ€.
  3. Π’ Ρ€Π°ΠΌΠΊΠΈΡ‚Π΅ Π½Π° ΡΡŠΡΡ‚Π΅Π·Π°Π½ΠΈΠ΅ срСд kernel-ΠΈΡ‚Π΅ Π½Π° Python Π·Π°Π΄Π°Ρ‡Π°Ρ‚Π° ΠΏΡ€Π΅Π΄ΠΈΠΌΠ½ΠΎ сС Ρ€Π΅ΡˆΠ°Π²Π°ΡˆΠ΅ с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π°

. Π•Π΄ΠΈΠ½ ΠΎΡ‚ Π½Π°ΠΉ-проститС ΠΈ ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½ΠΈ Π°Π½Π°Π»ΠΎΠ·ΠΈ Π½Π° R Ρ‰Π΅ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΏΠΎ слСдния Π½Π°Ρ‡ΠΈΠ½: OpenCVРСализация Π½Π° ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΡƒΠ²Π°Π½Π΅Ρ‚ΠΎ Π½Π° JSON Π² Ρ‚Π΅Π½Π·ΠΎΡ€ Π½Π° 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, Π° Ρ‚Π°ΠΊΠΆΠ΅ ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒ 3D Ρ‚Π΅Π½Π·ΠΎΡ€Ρ‹ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π² 4D Ρ‚Π΅Π½Π·ΠΎΡ€ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎΠΉ размСрности (собствСнно Π±Π°Ρ‚Ρ‡).

ΠœΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»ΠΈ Π·Π° ΠΈΠ·ΡƒΡ‡Π°Π²Π°Π½Π΅ Π½Π° Rcpp, xtensor ΠΈ RcppThread

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

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

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

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

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

Π—Π° ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡ€Π°Π½Π΅ Π½Π° Ρ„Π°ΠΉΠ»ΠΎΠ²Π΅Ρ‚Π΅, ΠΊΠΎΠΈΡ‚ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚ систСмни Ρ„Π°ΠΉΠ»ΠΎΠ²Π΅ ΠΈ Π΄ΠΈΠ½Π°ΠΌΠΈΡ‡Π½Π° Π»ΠΈΠ½ΠΊΠΎΠ²ΠΊΠ° с инсталиранитС Π² систСмата Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ…ΠΌΠ΅ ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌΠ° Π½Π° ΠΏΠ»ΡŠΠ³ΠΈΠ½ΠΈΡ‚Π΅, Ρ€Π΅Π°Π»ΠΈΠ·ΠΈΡ€Π°Π½ Π² ΠΏΠ°ΠΊΠ΅Ρ‚Π° Rcpp. Π—Π° Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎ Π½Π°ΠΌΠΈΡ€Π°Π½Π΅ Π½Π° ΠΏΡŠΡ‚ΠΈΡ‰Π° ΠΈ Ρ„Π»Π°Π³ΠΎΠ²Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ…ΠΌΠ΅ популярната линукс ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Π° pkg-config.

РСализация Π½Π° 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 конфигурация Π·Π° pkg-config Π½Π΅ Π΅ Π½Π°ΠΌΠ΅Ρ€Π΅Π½Π°", 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 Π² Ρ‚Π΅Π½Π·ΠΎΡ€ Π½Π° C++

// [[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 ΠΈ 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
#                                  
# 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
#                  & & &         &
#  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("ΠΌΠ΅Π΄ΠΈΠΉΠ½ΠΎ Π²Ρ€Π΅ΠΌΠ΅, с") +
  theme_minimal() +
  scale_color_discrete(name = "", labels = c("cpp", "r")) +
  theme(legend.position = "bottom") 

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ Π΄Π° ΡΠ²ΡŠΡ€ΠΆΠ΅ΠΌ R, C++ ΠΈ Π½Π΅Π²Ρ€ΠΎΠ½Π½ΠΈ ΠΌΡ€Π΅ΠΆΠΈ

ΠšΠ°ΠΊΡ‚ΠΎ Π²ΠΈΠΆΠ΄Π°ΠΌΠ΅, ΡƒΠ²Π΅Π»ΠΈΡ‡Π΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½Π° скоростта сС ΠΎΠΊΠ°Π·Π° ΠΌΠ½ΠΎΠ³ΠΎ Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»Π½ΠΎ, ΠΈ постиганСто Π½Π° ΠΊΠΎΠ΄ Π½Π° C++ Ρ‡Ρ€Π΅Π· ΠΏΠ°Ρ€Π°Π»Π΅Π»ΠΈΠ·ΠΈΡ€Π°Π½Π΅ Π½Π° ΠΊΠΎΠ΄Π° Π½Π° R Π½Π΅ прСдставлява Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚.

3. Π˜Ρ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ΠΈ Π·Π° ΠΈΠ·Π²Π»ΠΈΡ‡Π°Π½Π΅ Π½Π° Π±Π°Ρ‚Ρ‡ΠΎΠ²Π΅ ΠΎΡ‚ Π‘Π”

R ΠΈΠΌΠ° заслуТСна рСпутация Π½Π° Π΅Π·ΠΈΠΊ Π·Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° Π΄Π°Π½Π½ΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ Π²Π»ΠΈΠ·Π°Ρ‚ Π² ΠžΠ—Π£, Π΄ΠΎΠΊΠ°Ρ‚ΠΎ Π·Π° Python Π΅ ΠΏΠΎ-Ρ…Π°Ρ€Π°ΠΊΡ‚Π΅Ρ€Π½Π° ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΈΠ²Π½Π°Ρ‚Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° Π΄Π°Π½Π½ΠΈ, която позволява лСсно ΠΈ Π±Π΅Π·ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ½ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΈΡ€Π°Π½Π΅ Π½Π° out-of-core изчислСния (изчислСния с ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° външна ΠΏΠ°ΠΌΠ΅Ρ‚). ΠšΠ»Π°ΡΠΈΡ‡Π΅ΡΠΊΠΈΡΡ‚ ΠΈ Π°ΠΊΡ‚ΡƒΠ°Π»Π΅Π½ Π·Π° нас Π² контСкста Π½Π° описаната Π·Π°Π΄Π°Ρ‡Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π·Π° Ρ‚Π°ΠΊΠΈΠ²Π° изчислСния са Π΄ΡŠΠ»Π±ΠΎΠΊΠΈΡ‚Π΅ Π½Π΅Π²Ρ€ΠΎΠ½Π½ΠΈ ΠΌΡ€Π΅ΠΆΠΈ, ΠΎΠ±ΡƒΡ‡Π°Π²Π°Π½ΠΈ с ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π½Π° Π³Ρ€Π°Π΄ΠΈΠ΅Π½Ρ‚Π½ΠΎΡ‚ΠΎ спусканС с аппроксимация Π½Π° Π³Ρ€Π°Π΄ΠΈΠ΅Π½Ρ‚Π° Π½Π° всяка ΡΡ‚ΡŠΠΏΠΊΠ° с малък Π½Π°Π±ΠΎΡ€ ΠΎΡ‚ наблюдСния, ΠΈΠ»ΠΈ ΠΌΠΈΠ½ΠΈ-Π±Π°Ρ‚Ρ‡.

Π€Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΈΡ‚Π΅ Π·Π° дълбоко ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅, написани Π½Π° Python, ΠΈΠΌΠ°Ρ‚ спСциални класовС, Ρ€Π΅Π°Π»ΠΈΠ·ΠΈΡ€Π°Ρ‰ΠΈ ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ΠΈ Π·Π° Π΄Π°Π½Π½ΠΈ: Ρ‚Π°Π±Π»ΠΈΡ†ΠΈ, изобраТСния Π² ΠΏΠ°ΠΏΠΊΠΈ, Π±ΠΈΠ½Π°Ρ€Π½ΠΈ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈ ΠΈ Ρ‚.Π½. ΠœΠΎΠ³Π°Ρ‚ Π΄Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚ Π³ΠΎΡ‚ΠΎΠ²ΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΈ ΠΈΠ»ΠΈ Π΄Π° сС ΠΏΠΈΡˆΠ°Ρ‚ собствСни Π·Π° спСцифични Π·Π°Π΄Π°Ρ‡ΠΈ. Π’ R ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° сС възползвамС ΠΎΡ‚ всички Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΠΈ Π½Π° Python Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°Ρ‚Π° 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 ΡΡŠΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²Π° Π½Π° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ с Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΈ 256x256 пиксСла, scale = 0.5 β€” 128x128 пиксСла), ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π·Π° цвСтността (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
#                    
# 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("ΠΌΠ΅Π΄ΠΈΠΉΠ½ΠΎ Π²Ρ€Π΅ΠΌΠ΅, с") +
    theme_minimal()

DBI::dbDisconnect(con, shutdown = TRUE)

Quick Draw Doodle Recognition: ΠΊΠ°ΠΊ Π΄Π° ΡΠ²ΡŠΡ€ΠΆΠ΅ΠΌ R, C++ ΠΈ Π½Π΅Π²Ρ€ΠΎΠ½Π½ΠΈ ΠΌΡ€Π΅ΠΆΠΈ

Ако ΠΈΠΌΠ° Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ‡Π½ΠΎ ΠΎΠ±Π΅ΠΌ Π½Π° RAM, ΠΌΠΎΠΆΠ΅ Π΄Π° сС ускори Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚Π° Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ, ΠΊΠ°Ρ‚ΠΎ сС прСмСсти Π² самата RAM (Π·Π° Π½Π°ΡˆΠ°Ρ‚Π° Π·Π°Π΄Π°Ρ‡Π° 32 Π“Π‘ са Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ‡Π½ΠΈ). По ΠΏΠΎΠ΄Ρ€Π°Π·Π±ΠΈΡ€Π°Π½Π΅ Π² Linux сС ΠΌΠΎΠ½Ρ‚ΠΈΡ€Π° дял /dev/shm, Π·Π°Π΅ΠΌΠ°Ρ‰ Π΄ΠΎ ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½Π°Ρ‚Π° ΠΎΡ‚ ΠΎΠ±Π΅ΠΌΠ° Π½Π° RAM. МоТС Π΄Π° сС опрСдСлят ΠΈ ΠΏΠΎΠ²Π΅Ρ‡Π΅, ΠΊΠ°Ρ‚ΠΎ сС Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€Π° /etc/fstab, Π·Π° Π΄Π° ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ запис ΠΎΡ‚ Π²ΠΈΠ΄Π° tmpfs /dev/shm tmpfs defaults,size=25g 0 0. ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ пСрСзагруТаСмся ΠΈ провСряСм Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚, Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΠ² ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ df -h.

Π˜Ρ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ΡŠΡ‚ Π·Π° тСстови Π΄Π°Π½Π½ΠΈ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎ-просто, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ цСлият тСстови Π½Π°Π±ΠΎΡ€ ΠΎΡ‚ Π΄Π°Π½Π½ΠΈ сС ΠΏΠΎΠ±ΠΈΡ€Π° Π² ΠžΠ—Π£:

Π˜Ρ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ Π·Π° тСстови Π΄Π°Π½Π½ΠΈ

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

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

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

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

4. Π˜Π·Π±ΠΎΡ€ Π½Π° Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π° Π½Π° ΠΌΠΎΠ΄Π΅Π»Π°

ΠŸΡŠΡ€Π²Π°Ρ‚Π° ΠΎΡ‚ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½ΠΈΡ‚Π΅ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€ΠΈ бСшС 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 %
  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 %
    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 % 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("МодСл ", shQuote(name), " Π½Π΅ Π΅ Π½Π°ΠΌΠ΅Ρ€Π΅Π½.", 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 ΠΏΠΎ слСдния Π½Π°Ρ‡ΠΈΠ½:

Π΄ΠΎΠΊ <- '
Π£ΠΏΠΎΡ‚Ρ€Π΅Π±Π°:
  train_nn.R --help
  train_nn.R --list-models
  train_nn.R [ΠΎΠΏΡ†ΠΈΠΈ]

ΠžΠΏΡ†ΠΈΠΈ:
  -h --help                   ΠŸΠΎΠΊΠ°Π·Π°Ρ‚ΠΈ Ρ†Π΅ повідомлСння.
  -l --list-models            ΠŸΠ΅Ρ€Π΅Π»Ρ–ΠΊ доступних ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ.
  -m --model=<модСль>          Π†ΠΌ'я Π½Π΅ΠΉΡ€ΠΎΠ½Π½ΠΎΡ— ΠΌΠ΅Ρ€Π΅ΠΆΡ– [Π·Π° замовчуванням: mobilenet_v2].
  -b --batch-size=<Ρ€ΠΎΠ·ΠΌΡ–Ρ€>      Π ΠΎΠ·ΠΌΡ–Ρ€ ΠΏΠ°ΠΊΠ΅Ρ‚Ρƒ [Π·Π° замовчуванням: 32].
  -s --scale-factor=<ΡΠΏΡ–Π²Π²Ρ–Π΄Π½ΠΎΡˆΠ΅Π½Π½Ρ>   Π€Π°ΠΊΡ‚ΠΎΡ€ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΡƒΠ²Π°Π½Π½Ρ [Π·Π° замовчуванням: 0.5].
  -c --color                  Використовувати ΠΊΠΎΠ»ΡŒΠΎΡ€ΠΎΠ²Ρ– Π»Ρ–Π½Ρ–Ρ— [Π·Π° замовчуванням: FALSE].
  -d --db-dir=<ΡˆΠ»ΡΡ…>          Шлях Π΄ΠΎ ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³Ρƒ Π±Π°Π·ΠΈ Π΄Π°Π½ΠΈΡ… [Π·Π° замовчуванням: Sys.getenv("db_dir")].
  -r --validate-ratio=<ΡΠΏΡ–Π²Π²Ρ–Π΄Π½ΠΎΡˆΠ΅Π½Π½Ρ> Π’Π°Π»Ρ–Π΄Π°Ρ†Ρ–ΠΉΠ½Π΅ ΡΠΏΡ–Π²Π²Ρ–Π΄Π½ΠΎΡˆΠ΅Π½Π½Ρ Π²ΠΈΠ±Ρ–Ρ€ΠΊΠΈ [Π·Π° замовчуванням: 0.995].
  -n --n-gpu=<число>         ΠšΡ–Π»ΡŒΠΊΡ–ΡΡ‚ΡŒ Π³Ρ€Π°Ρ„Ρ–Ρ‡Π½ΠΈΡ… процСсорів [Π·Π° замовчуванням: 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 Ρ– Π±Ρ–Π±Π»Ρ–ΠΎΡ‚Π΅ΠΊ Python β€” Π΄ΠΎΡΠΈΡ‚ΡŒ ΠΎΠ±'Ρ”ΠΌΠ½Π° частина ΠΎΠ±Ρ€Π°Π·Ρƒ, Ρ– Π²ΠΈΡ€Ρ–ΡˆΠΈΠ»ΠΈ взяти Π·Π° основу ΠΎΡ„Ρ–Ρ†Ρ–ΠΉΠ½ΠΈΠΉ ΠΎΠ±Ρ€Π°Π· tensorflow/tensorflow:1.12.0-gpu, додавши Ρ‚ΡƒΠ΄ΠΈ Π½Π΅ΠΎΠ±Ρ…Ρ–Π΄Π½Ρ– R-ΠΏΠ°ΠΊΠ΅Ρ‚ΠΈ.

Π€Ρ–Π½Π°Π»ΡŒΠ½ΠΈΠΉ Π΄ΠΎΠΊΠ΅Ρ€-Ρ„Π°ΠΉΠ» вийшов Ρ‚Π°ΠΊΠΈΠΌ:

Dockerfile

Π˜Π— tensorflow/tensorflow:1.12.0-gpu

ДЕРЖАВЕЛЬ Artem Klevtsov 

ΠžΠ±ΠΎΠ»ΠΎΡ‡ΠΊΠ° ["/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. ΠŸΠΎΡ€Π°Π΄ΠΈ Тадност бСшС ΠΏΠΎΡ€ΡŠΡ‡Π°Π½ инстанс с 4xV100 с SSD ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ RAM, ΠΈ Ρ‚ΠΎΠ²Π° сС ΠΎΠΊΠ°Π·Π° голяма Π³Ρ€Π΅ΡˆΠΊΠ°. ΠŸΠ°Ρ€ΠΈΡ‚Π΅ Ρ‚Π°ΠΊΠ°Π²Π° машина Π±ΡŠΡ€Π·ΠΎ ΠΈΠ·Ρ€Π°Π·Ρ…ΠΎΠ΄Π²Π°, ΠΏΡ€ΠΈ СкспСримСнти Π±Π΅Π· ΠΎΡ‚Ρ€Π°Π±ΠΎΡ‚Π΅Π½ ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½ ΠΌΠΎΠΆΠ΅ Π΄Π° сС ΡΠ²ΡŠΡ€ΡˆΠΈΡ‚Π΅. Π—Π° ΡƒΡ‡Π΅Π±Π½ΠΈ Ρ†Π΅Π»ΠΈ Π΅ ΠΏΠΎ-Π΄ΠΎΠ±Ρ€Π΅ Π΄Π° сС Π²Π·Π΅ΠΌΠ°Ρ‚ K80. ΠžΠ±Π°Ρ‡Π΅ голямото количСство RAM сС ΠΎΠΊΠ°Π·Π° ΠΏΠΎΠ»Π΅Π·Π½ΠΎ β€” облачният SSD Π½Π΅ Π²ΠΏΠ΅Ρ‡Π°Ρ‚Π»ΠΈ с Π±ΡŠΡ€Π·ΠΎΠ΄Π΅ΠΉΡΡ‚Π²ΠΈΠ΅Ρ‚ΠΎ си, Π·Π°Ρ‚ΠΎΠ²Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ ΠΏΡ€ΠΈ всяко стартиранС Π½Π° инстанса прСнасяхмС Π½Π° dev/shm.

Най-голям интСрСс прСдставлява Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ΡŠΡ‚ ΠΎΡ‚ ΠΊΠΎΠ΄Π°, отговарящ Π·Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° няколко GPU. ΠŸΡŠΡ€Π²ΠΎ, ΠΌΠΎΠ΄Π΅Π»ΡŠΡ‚ сС създава Π½Π° CPU, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΠΊΠΈ ΠΌΠ΅Π½ΠΈΠ΄ΠΆΡŠΡ€ Π½Π° контСкста, съвсСм ΠΊΠ°ΠΊΡ‚ΠΎ Π² Python:

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 ΠΈ дискрСтни скорости Π½Π° ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅ (cosine annealing ΠΏΠΎ Π½Π°ΡˆΠ°Ρ‚Π° ΠΌΠΎΠ»Π±Π° бСшС Ρ€Π΅Π°Π»ΠΈΠ·ΠΈΡ€Π°Π½, благодаря skeydan).

Какво ΠΏΠΎΠ»Π΅Π·Π½ΠΎ успяхмС Π΄Π° ΠΈΠ·Π²Π»Π΅Ρ‡Π΅ΠΌ ΠΎΡ‚ Ρ‚ΠΎΠ²Π° ΡΡŠΡΡ‚Π΅Π·Π°Π½ΠΈΠ΅:

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

Π’ Π·Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅, Ρ‚ΠΎΠ·ΠΈ ΠΎΠΏΠΈΡ‚ бСшС ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎΠ»Π΅Π·Π΅Π½ ΠΈ ΠΏΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠ°Π²Π°ΠΌΠ΅ Π΄Π° Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ Π²ΡŠΡ€Ρ…Ρƒ Ρ€Π΅ΡˆΠ°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° някои ΠΎΡ‚ ΠΎΠ·Π²ΡƒΡ‡Π΅Π½ΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ.

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

ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС с Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ πŸ”₯ ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС с Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ | ProHoster