рдХреНрд╡рд┐рдХ рдбреНрд░реЙ рдбреВрдбрд▓ рдУрд│рдЦ: R, C++ рдЖрдгрд┐ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╢реА рдореИрддреНрд░реА рдХрд╢реА рдХрд░рд╛рд╡реА

рдХреНрд╡рд┐рдХ рдбреНрд░реЙ рдбреВрдбрд▓ рдУрд│рдЦ: R, C++ рдЖрдгрд┐ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╢реА рдореИрддреНрд░реА рдХрд╢реА рдХрд░рд╛рд╡реА

рдЕрд╣реЛ рд╣рд╛рдмреНрд░!

рд╢реЗрд╡рдЯрдЪреНрдпрд╛ рдЧрдбреА рдмрд╛рдж рд╣реЛрдгреНрдпрд╛рдЪрд╛ рдХреНрд░рдо, Kaggle рдиреЗ рд╣рд╛рддрд╛рдиреЗ рдХрд╛рдврд▓реЗрд▓реНрдпрд╛ рдЪрд┐рддреНрд░рд╛рдВрдЪреЗ рд╡рд░реНрдЧреАрдХрд░рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдПрдХ рд╕реНрдкрд░реНрдзрд╛ рдЖрдпреЛрдЬрд┐рдд рдХреЗрд▓реА, рдХреНрд╡рд┐рдХ рдбреНрд░реЙ рдбреВрдбрд▓ рд░рд┐рдХрдЧреНрдирд┐рд╢рди, рдЬреНрдпрд╛рдордзреНрдпреЗ, рдЗрддрд░рд╛рдВрд╕рд╣, R-рд╢рд╛рд╕реНрддреНрд░рдЬреНрдЮрд╛рдВрдЪреНрдпрд╛ рдЯреАрдордиреЗ рднрд╛рдЧ рдШреЗрддрд▓рд╛: рдЖрд░реНрдЯреЗрдо рдХреНрд▓реЗрд╡реНрд╣рддреНрд╕реЛрд╡рд╛, рдлрд┐рд▓рд┐рдкрд╛ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ ╨╕ рдЖрдВрджреНрд░реЗ рдУрдЧреБрд░реНрддреНрд╕реЛрд╡реНрд╣. рдЖрдореНрд╣реА рд╕реНрдкрд░реНрдзреЗрдЪреЗ рддрдкрд╢реАрд▓рд╡рд╛рд░ рд╡рд░реНрдгрди рдХрд░рдгрд╛рд░ рдирд╛рд╣реА; рддреЗ рдЖрдзреАрдЪ рдХреЗрд▓реЗ рдЧреЗрд▓реЗ рдЖрд╣реЗ рдЕрд▓реАрдХрдбреАрд▓ рдкреНрд░рдХрд╛рд╢рди.

рдпрд╛рд╡реЗрд│реА рдореЗрдбрд▓ рдлрд╛рд░реНрдорд┐рдВрдЧрдиреЗ рдХрд╛рдо рдХреЗрд▓реЗ рдирд╛рд╣реА, рдкрд░рдВрддреБ рдЦреВрдк рдореМрд▓реНрдпрд╡рд╛рди рдЕрдиреБрднрд╡ рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓рд╛, рдореНрд╣рдгреВрди рдореА рд╕рдорд╛рдЬрд╛рд▓рд╛ рдХрд╛рдЧрд▓реЗрд╡рд░реАрд▓ рдЖрдгрд┐ рджреИрдирдВрджрд┐рди рдХрд╛рдорд╛рддреАрд▓ рдЕрдиреЗрдХ рдордиреЛрд░рдВрдЬрдХ рдЖрдгрд┐ рдЙрдкрдпреБрдХреНрдд рдЧреЛрд╖реНрдЯреАрдВрдмрджреНрджрд▓ рд╕рд╛рдВрдЧреВ рдЗрдЪреНрдЫрд┐рддреЛ. рдЪрд░реНрдЪрд╛ рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╡рд┐рд╖рдпрд╛рдВрдкреИрдХреА: рдХрдареАрдг рдЬреАрд╡рдирд╛рд╢рд┐рд╡рд╛рдп рдУрдкрдирд╕реАрд╡реНрд╣реАJSON рдкрд╛рд░реНрд╕рд┐рдВрдЧ Rcpp), рд╕реНрдХреНрд░рд┐рдкреНрдЯреНрд╕рдЪреЗ рдкреЕрд░рд╛рдореАрдЯрд░рд╛рдпрдЭреЗрд╢рди рдЖрдгрд┐ рдЕрдВрддрд┐рдо рд╕рдорд╛рдзрд╛рдирд╛рдЪреЗ рдбреЙрдХрд░рд╛рдпрдЭреЗрд╢рди. рд╕рдВрджреЗрд╢рд╛рддреАрд▓ рд╕рд░реНрд╡ рдХреЛрдб рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрд╕рд╛рдареА рдпреЛрдЧреНрдп рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдлреЙрд░реНрдордордзреНрдпреЗ рдЙрдкрд▓рдмреНрдз рдЖрд╣реЗрдд рднрд╛рдВрдбрд╛рд░.

рд╕рд╛рдордЧреНрд░реА:

  1. CSV рд╡рд░реВрди MonetDB рдордзреНрдпреЗ рдбреЗрдЯрд╛ рдкреНрд░рднрд╛рд╡реАрдкрдгреЗ рд▓реЛрдб рдХрд░рд╛
  2. рдмреЕрдЪ рддрдпрд╛рд░ рдХрд░рдд рдЖрд╣реЗ
  3. рдбреЗрдЯрд╛рдмреЗрд╕рдордзреВрди рдмреЕрдЪ рдЕрдирд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдкреБрдирд░рд╛рд╡реГрддреНрддреА рдХрд░рдгрд╛рд░реЗ
  4. рдореЙрдбреЗрд▓ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдирд┐рд╡рдбрдгреЗ
  5. рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдкреЕрд░рд╛рдореАрдЯрд░рд╛рдпрдЭреЗрд╢рди
  6. рд╕реНрдХреНрд░рд┐рдкреНрдЯрдЪреЗ рдбреЙрдХрд░рд╛рдпрдЭреЗрд╢рди
  7. Google Cloud рд╡рд░ рдПрдХрд╛рдзрд┐рдХ GPU рд╡рд╛рдкрд░рдгреЗ
  8. рддреНрдпрд╛рдРрд╡рдЬреА рдПрдХ рдирд┐рд╖реНрдХрд░реНрд╖

1. рдореЛрдиреЗрдЯрдбреАрдмреА рдбреЗрдЯрд╛рдмреЗрд╕рдордзреНрдпреЗ CSV рд╡рд░реВрди рдХрд╛рд░реНрдпрдХреНрд╖рдорддреЗрдиреЗ рдбреЗрдЯрд╛ рд▓реЛрдб рдХрд░рд╛

рдпрд╛ рд╕реНрдкрд░реНрдзреЗрддреАрд▓ рдбреЗрдЯрд╛ рддрдпрд╛рд░ рдкреНрд░рддрд┐рдорд╛рдВрдЪреНрдпрд╛ рд╕реНрд╡рд░реВрдкрд╛рдд рдкреНрд░рджрд╛рди рдХреЗрд▓рд╛ рдЬрд╛рдд рдирд╛рд╣реА, рдкрд░рдВрддреБ 340 CSV рдлрд╛рдпрд▓реАрдВрдЪреНрдпрд╛ рд╕реНрд╡рд░реВрдкрд╛рдд (рдкреНрд░рддреНрдпреЗрдХ рд╡рд░реНрдЧрд╛рд╕рд╛рдареА рдПрдХ рдлрд╛рдИрд▓) рдЬреНрдпрд╛рдордзреНрдпреЗ рдкреЙрдЗрдВрдЯ рдХреЛрдСрд░реНрдбрд┐рдиреЗрдЯреНрд╕рд╕рд╣ JSON рдЖрд╣реЗрдд. рдпрд╛ рдмрд┐рдВрджреВрдВрдирд╛ рд░реЗрд╖рд╛рдВрд╕рд╣ рдЬреЛрдбреВрди, тАЛтАЛрдЖрдореНрд╣рд╛рд▓рд╛ 256x256 рдкрд┐рдХреНрд╕реЗрд▓ рдореЛрдЬрдгрд╛рд░реА рдЕрдВрддрд┐рдо рдкреНрд░рддрд┐рдорд╛ рдорд┐рд│рддреЗ. рддрд╕реЗрдЪ рдкреНрд░рддреНрдпреЗрдХ рд░реЗрдХреЙрд░реНрдбрд╕рд╛рдареА рдбреЗрдЯрд╛рд╕реЗрдЯ рд╕рдВрдХрд▓рд┐рдд рдХрд░рддрд╛рдирд╛ рд╡рд╛рдкрд░рд▓реЗрд▓реНрдпрд╛ рдХреНрд▓рд╛рд╕рд┐рдлрд╛рдпрд░рджреНрд╡рд╛рд░реЗ рдЪрд┐рддреНрд░ рдпреЛрдЧреНрдпрд░рд┐рддреНрдпрд╛ рдУрд│рдЦрд▓реЗ рдЧреЗрд▓реЗ рдХреА рдирд╛рд╣реА рд╣реЗ рджрд░реНрд╢рд╡рдгрд╛рд░реЗ рд▓реЗрдмрд▓ рдЕрд╕рддреЗ, рдЪрд┐рддреНрд░рд╛рдЪреНрдпрд╛ рд▓реЗрдЦрдХрд╛рдЪреНрдпрд╛ рд░рд╛рд╣рддреНрдпрд╛ рджреЗрд╢рд╛рдЪрд╛ рджреЛрди-рдЕрдХреНрд╖рд░реА рдХреЛрдб, рдПрдХ рдЕрджреНрд╡рд┐рддреАрдп рдУрд│рдЦрдХрд░реНрддрд╛, рдПрдХ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк. рдЖрдгрд┐ рдлрд╛рдИрд▓ рдирд╛рд╡рд╛рд╢реА рдЬреБрд│рдгрд╛рд░реЗ рд╡рд░реНрдЧ рдирд╛рд╡. рдореВрд│ рдбреЗрдЯрд╛рдЪреА рд╕рд░рд▓реАрдХреГрдд рдЖрд╡реГрддреНрддреА рд╕рдВрдЧреНрд░рд╣рдгрд╛рдд 7.4 GB рдЕрд╕рддреЗ рдЖрдгрд┐ рдЕрдирдкреЕрдХ рдХреЗрд▓реНрдпрд╛рдирдВрддрд░ рдЕрдВрджрд╛рдЬреЗ 20 GB рдЕрд╕рддреЗ, рдЕрдирдкреЕрдХ рдХреЗрд▓реНрдпрд╛рдирдВрддрд░ рдкреВрд░реНрдг рдбреЗрдЯрд╛ 240 GB рдШреЗрддреЗ. рдЖрдпреЛрдЬрдХрд╛рдВрдиреА рд╣реЗ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХреЗрд▓реЗ рдХреА рджреЛрдиреНрд╣реА рдЖрд╡реГрддреНрддреНрдпрд╛рдВрдиреА рд╕рдорд╛рди рд░реЗрдЦрд╛рдЪрд┐рддреНрд░реЗ рдкреБрдирд░реБрддреНрдкрд╛рджрд┐рдд рдХреЗрд▓реА рдЖрд╣реЗрдд, рдпрд╛рдЪрд╛ рдЕрд░реНрде рдкреВрд░реНрдг рдЖрд╡реГрддреНрддреА рдирд┐рд░рд░реНрдердХ рдЖрд╣реЗ. рдХреЛрдгрддреНрдпрд╛рд╣реА рдкрд░рд┐рд╕реНрдерд┐рддреАрдд, 50 рджрд╢рд▓рдХреНрд╖ рдкреНрд░рддрд┐рдорд╛ рдЧреНрд░рд╛рдлрд┐рдХ рдлрд╛рдЗрд▓реНрд╕рдордзреНрдпреЗ рдХрд┐рдВрд╡рд╛ рдЕреЕрд░реЗрдЪреНрдпрд╛ рд╕реНрд╡рд░реВрдкрд╛рдд рд╕рдВрдЧреНрд░рд╣рд┐рдд рдХрд░рдгреЗ рддрд╛рдмрдбрддреЛрдм рдлрд╛рдпрджреЗрд╢реАрд░ рдорд╛рдирд▓реЗ рдЬрд╛рдд рдирд╛рд╣реА рдЖрдгрд┐ рдЖрдореНрд╣реА рд╕рдВрдЧреНрд░рд╣рдгрд╛рддреАрд▓ рд╕рд░реНрд╡ CSV рдлрд╛рдЗрд▓реНрд╕ рд╡рд┐рд▓реАрди рдХрд░рдгреНрдпрд╛рдЪрд╛ рдирд┐рд░реНрдгрдп рдШреЗрддрд▓рд╛. train_simplified.zip рдкреНрд░рддреНрдпреЗрдХ рдмреЕрдЪрд╕рд╛рдареА рдЖрд╡рд╢реНрдпрдХ рдЖрдХрд╛рд░рд╛рдЪреНрдпрд╛ рдкреНрд░рддрд┐рдорд╛рдВрдЪреНрдпрд╛ рдкреБрдвреАрд▓ рдкрд┐рдвреАрд╕рд╣ рдбреЗрдЯрд╛рдмреЗрд╕рдордзреНрдпреЗ "рдлреНрд▓рд╛рдпрд╡рд░"

DBMS рдореНрд╣рдгреВрди рдПрдХ рдЪрд╛рдВрдЧрд▓реА рд╕рд┐рджреНрдз рдкреНрд░рдгрд╛рд▓реА рдирд┐рд╡рдбрд▓реА рдЧреЗрд▓реА рдореЛрдиреЗрдЯрдбреАрдмреА, рдореНрд╣рдгрдЬреЗ рдкреЕрдХреЗрдЬ рдореНрд╣рдгреВрди R рд╕рд╛рдареА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА MonetDBLite. рдкреЕрдХреЗрдЬрдордзреНрдпреЗ рдбреЗрдЯрд╛рдмреЗрд╕ рд╕рд░реНрд╡реНрд╣рд░рдЪреА рдПрдореНрдмреЗрдбреЗрдб рдЖрд╡реГрддреНрддреА рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдЖрд╣реЗ рдЖрдгрд┐ рддреБрдореНрд╣рд╛рд▓рд╛ рдереЗрдЯ рдЖрд░ рд╕реЗрд╢рдирдордзреВрди рд╕рд░реНрд╡реНрд╣рд░ рдЙрдЪрд▓рдгреНрдпрд╛рдЪреА рдЖрдгрд┐ рддреНрдпрд╛рдЪреНрдпрд╛рд╕реЛрдмрдд рдХрд╛рдо рдХрд░рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЗ. рдбреЗрдЯрд╛рдмреЗрд╕ рддрдпрд╛рд░ рдХрд░рдгреЗ рдЖрдгрд┐ рддреНрдпрд╛рд╕ рдХрдиреЗрдХреНрдЯ рдХрд░рдгреЗ рдПрдХрд╛ рдЖрджреЗрд╢рд╛рджреНрд╡рд╛рд░реЗ рдХреЗрд▓реЗ рдЬрд╛рддреЗ:

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

рдЖрдореНрд╣рд╛рд▓рд╛ рджреЛрди рд╕рд╛рд░рдгреНрдпрд╛ рддрдпрд╛рд░ рдХрд░рд╛рд╡реНрдпрд╛ рд▓рд╛рдЧрддреАрд▓: рдПрдХ рд╕рд░реНрд╡ рдбреЗрдЯрд╛рд╕рд╛рдареА, рджреБрд╕рд░рд╛ рдбрд╛рдЙрдирд▓реЛрдб рдХреЗрд▓реЗрд▓реНрдпрд╛ рдлрд╛рдпрд▓реАрдВрдмрджреНрджрд▓ рд╕реЗрд╡рд╛ рдорд╛рд╣рд┐рддреАрд╕рд╛рдареА (рдХрд╛рд╣реА рдЪреВрдХ рдЭрд╛рд▓реНрдпрд╛рд╕ рдЙрдкрдпреБрдХреНрдд рдЖрдгрд┐ рдЕрдиреЗрдХ рдлрд╛рдпрд▓реА рдбрд╛рдЙрдирд▓реЛрдб рдХреЗрд▓реНрдпрд╛рдирдВрддрд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдкреБрдиреНрд╣рд╛ рд╕реБрд░реВ рдХрд░рд╛рд╡реА рд▓рд╛рдЧреЗрд▓):

рдЯреЗрдмрд▓ рддрдпрд╛рд░ рдХрд░рдгреЗ

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

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

рдбреЗрдЯрд╛рдмреЗрд╕рдордзреНрдпреЗ рдбреЗрдЯрд╛ рд▓реЛрдб рдХрд░рдгреНрдпрд╛рдЪрд╛ рд╕рд░реНрд╡рд╛рдд рдЬрд▓рдж рдорд╛рд░реНрдЧ рдореНрд╣рдгрдЬреЗ SQL - рдХрдорд╛рдВрдб рд╡рд╛рдкрд░реВрди рдереЗрдЯ CSV рдлрд╛рдЗрд▓ рдХреЙрдкреА рдХрд░рдгреЗ COPY OFFSET 2 INTO tablename FROM path USING DELIMITERS ',','n','"' NULL AS '' BEST EFFORTрдХреБрдареЗ tablename - рдЯреЗрдмрд▓рдЪреЗ рдирд╛рд╡ рдЖрдгрд┐ path - рдлрд╛рдИрд▓рдЪрд╛ рдорд╛рд░реНрдЧ. рдЖрд░реНрдХрд╛рдЗрд╡реНрд╣рд╕рд╣ рдХрд╛рдо рдХрд░рддрд╛рдирд╛, рдЕрдВрдЧрднреВрдд рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрдЪрд╛ рд╢реЛрдз рд▓рд╛рдЧрд▓рд╛ unzip R рдордзреНрдпреЗ рд╕рдВрдЧреНрд░рд╣рдгрд╛рддреАрд▓ рдЕрдиреЗрдХ рдлрд╛рдЗрд▓реНрд╕рд╕рд╣ рдпреЛрдЧреНрдпрд░рд┐рддреНрдпрд╛ рдХрд╛рд░реНрдп рдХрд░рдд рдирд╛рд╣реА, рдореНрд╣рдгреВрди рдЖрдореНрд╣реА рд╕рд┐рд╕реНрдЯрдо рд╡рд╛рдкрд░рд▓реА unzip (рдкреЕрд░рд╛рдореАрдЯрд░ рд╡рд╛рдкрд░реВрди getOption("unzip")).

рдбреЗрдЯрд╛рдмреЗрд╕рд╡рд░ рд▓рд┐рд╣рд┐рдгреНрдпрд╛рдЪреЗ рдХрд╛рд░реНрдп

#' @title ╨Ш╨╖╨▓╨╗╨╡╤З╨╡╨╜╨╕╨╡ ╨╕ ╨╖╨░╨│╤А╤Г╨╖╨║╨░ ╤Д╨░╨╣╨╗╨╛╨▓
#'
#' @description
#' ╨Ш╨╖╨▓╨╗╨╡╤З╨╡╨╜╨╕╨╡ CSV-╤Д╨░╨╣╨╗╨╛╨▓ ╨╕╨╖ ZIP-╨░╤А╤Е╨╕╨▓╨░ ╨╕ ╨╖╨░╨│╤А╤Г╨╖╨║╨░ ╨╕╤Е ╨▓ ╨▒╨░╨╖╤Г ╨┤╨░╨╜╨╜╤Л╤Е
#'
#' @param con ╨Ю╨▒╤К╨╡╨║╤В ╨┐╨╛╨┤╨║╨╗╤О╤З╨╡╨╜╨╕╤П ╨║ ╨▒╨░╨╖╨╡ ╨┤╨░╨╜╨╜╤Л╤Е (╨║╨╗╨░╤Б╤Б `MonetDBEmbeddedConnection`).
#' @param tablename ╨Э╨░╨╖╨▓╨░╨╜╨╕╨╡ ╤В╨░╨▒╨╗╨╕╤Ж╤Л ╨▓ ╨▒╨░╨╖╨╡ ╨┤╨░╨╜╨╜╤Л╤Е.
#' @oaram zipfile ╨Я╤Г╤В╤М ╨║ ZIP-╨░╤А╤Е╨╕╨▓╤Г.
#' @oaram filename ╨Ш╨╝╤П ╤Д╨░╨╣╨╗╨░ ╨▓╨╜╤Г╤А╨╕ ZIP-╨░╤А╤Е╨╕╨▓╨░.
#' @param preprocess ╨д╤Г╨╜╨║╤Ж╨╕╤П ╨┐╤А╨╡╨┤╨╛╨▒╤А╨░╨▒╨╛╤В╨║╨╕, ╨║╨╛╤В╨╛╤А╨░╤П ╨▒╤Г╨┤╨╡╤В ╨┐╤А╨╕╨╝╨╡╨╜╨╡╨╜╨░ ╨╕╨╖╨▓╨╗╨╡╤З╤С╨╜╨╜╨╛╨╝╤Г ╤Д╨░╨╣╨╗╤Г.
#'   ╨Ф╨╛╨╗╨╢╨╜╨░ ╨┐╤А╨╕╨╜╨╕╨╝╨░╤В╤М ╨╛╨┤╨╕╨╜ ╨░╤А╨│╤Г╨╝╨╡╨╜╤В `data` (╨╛╨▒╤К╨╡╨║╤В `data.table`).
#'
#' @return `TRUE`.
#'
upload_file <- function(con, tablename, zipfile, filename, preprocess = NULL) {
  # ╨Я╤А╨╛╨▓╨╡╤А╨║╨░ ╨░╤А╨│╤Г╨╝╨╡╨╜╤В╨╛╨▓
  checkmate::assert_class(con, "MonetDBEmbeddedConnection")
  checkmate::assert_string(tablename)
  checkmate::assert_string(filename)
  checkmate::assert_true(DBI::dbExistsTable(con, tablename))
  checkmate::assert_file_exists(zipfile, access = "r", extension = "zip")
  checkmate::assert_function(preprocess, args = c("data"), null.ok = TRUE)

  # ╨Ш╨╖╨▓╨╗╨╡╤З╨╡╨╜╨╕╨╡ ╤Д╨░╨╣╨╗╨░
  path <- file.path(tempdir(), filename)
  unzip(zipfile, files = filename, exdir = tempdir(), 
        junkpaths = TRUE, unzip = getOption("unzip"))
  on.exit(unlink(file.path(path)))

  # ╨Я╤А╨╕╨╝╨╡╨╜╤П╨╡╨╝ ╤Д╤Г╨╜╨║╤Ж╨╕╤П ╨┐╤А╨╡╨┤╨╛╨▒╤А╨░╨▒╨╛╤В╨║╨╕
  if (!is.null(preprocess)) {
    .data <- data.table::fread(file = path)
    .data <- preprocess(data = .data)
    data.table::fwrite(x = .data, file = path, append = FALSE)
    rm(.data)
  }

  # ╨Ч╨░╨┐╤А╨╛╤Б ╨║ ╨С╨Ф ╨╜╨░ ╨╕╨╝╨┐╨╛╤А╤В CSV
  sql <- sprintf(
    "COPY OFFSET 2 INTO %s FROM '%s' USING DELIMITERS ',','n','"' NULL AS '' BEST EFFORT",
    tablename, path
  )
  # ╨Т╤Л╨┐╨╛╨╗╨╜╨╡╨╜╨╕╨╡ ╨╖╨░╨┐╤А╨╛╤Б╨░ ╨║ ╨С╨Ф
  DBI::dbExecute(con, sql)

  # ╨Ф╨╛╨▒╨░╨▓╨╗╨╡╨╜╨╕╨╡ ╨╖╨░╨┐╨╕╤Б╨╕ ╨╛╨▒ ╤Г╤Б╨┐╨╡╤И╨╜╨╛╨╣ ╨╖╨░╨│╤А╤Г╨╖╨║╨╡ ╨▓ ╤Б╨╗╤Г╨╢╨╡╨▒╨╜╤Г╤О ╤В╨░╨▒╨╗╨╕╤Ж╤Г
  DBI::dbExecute(con, sprintf("INSERT INTO upload_log(file_name, uploaded) VALUES('%s', true)",
                              filename))

  return(invisible(TRUE))
}

рдбреЗрдЯрд╛рдмреЗрд╕рд╡рд░ рд▓рд┐рд╣рд┐рдгреНрдпрд╛рдкреВрд░реНрд╡реА рдЯреЗрдмрд▓рдЪреЗ рд░реВрдкрд╛рдВрддрд░ рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЕрд╕рд▓реНрдпрд╛рд╕, рдпреБрдХреНрддрд┐рд╡рд╛рджрд╛рдд рдкрд╛рд╕ рдХрд░рдгреЗ рдкреБрд░реЗрд╕реЗ рдЖрд╣реЗ preprocess рдХрд╛рд░реНрдп рдЬреЗ рдбреЗрдЯрд╛рдЪреЗ рд░реВрдкрд╛рдВрддрд░ рдХрд░реЗрд▓.

рдбреЗрдЯрд╛рдмреЗрд╕рдордзреНрдпреЗ рдбреЗрдЯрд╛ рдЕрдиреБрдХреНрд░рдореЗ рд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдХреЛрдб:

рдбреЗрдЯрд╛рдмреЗрд╕рдордзреНрдпреЗ рдбреЗрдЯрд╛ рд▓рд┐рд╣рд┐рдд рдЖрд╣реЗ

# ╨б╨┐╨╕╤Б╨╛╨║ ╤Д╨░╨╣╨╗╨╛╨▓ ╨┤╨╗╤П ╨╖╨░╨┐╨╕╤Б╨╕
files <- unzip(zipfile, list = TRUE)$Name

# ╨б╨┐╨╕╤Б╨╛╨║ ╨╕╤Б╨║╨╗╤О╤З╨╡╨╜╨╕╨╣, ╨╡╤Б╨╗╨╕ ╤З╨░╤Б╤В╤М ╤Д╨░╨╣╨╗╨╛╨▓ ╤Г╨╢╨╡ ╨▒╤Л╨╗╨░ ╨╖╨░╨│╤А╤Г╨╢╨╡╨╜╨░
to_skip <- DBI::dbGetQuery(con, "SELECT file_name FROM upload_log")[[1L]]
files <- setdiff(files, to_skip)

if (length(files) > 0L) {
  # ╨Ч╨░╨┐╤Г╤Б╨║╨░╨╡╨╝ ╤В╨░╨╣╨╝╨╡╤А
  tictoc::tic()
  # ╨Я╤А╨╛╨│╤А╨╡╤Б╤Б ╨▒╨░╤А
  pb <- txtProgressBar(min = 0L, max = length(files), style = 3)
  for (i in seq_along(files)) {
    upload_file(con = con, tablename = "doodles", 
                zipfile = zipfile, filename = files[i])
    setTxtProgressBar(pb, i)
  }
  close(pb)
  # ╨Ю╤Б╤В╨░╨╜╨░╨▓╨╗╨╕╨▓╨░╨╡╨╝ ╤В╨░╨╣╨╝╨╡╤А
  tictoc::toc()
}

# 526.141 sec elapsed - ╨║╨╛╨┐╨╕╤А╨╛╨▓╨░╨╜╨╕╨╡ SSD->SSD
# 558.879 sec elapsed - ╨║╨╛╨┐╨╕╤А╨╛╨▓╨░╨╜╨╕╨╡ USB->SSD

рд╡рд╛рдкрд░рд▓реЗрд▓реНрдпрд╛ рдбреНрд░рд╛рдЗрд╡реНрд╣рдЪреНрдпрд╛ рдЧрддреА рд╡реИрд╢рд┐рд╖реНрдЯреНрдпрд╛рдВрд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдбреЗрдЯрд╛ рд▓реЛрдбрд┐рдВрдЧ рд╡реЗрд│ рдмрджрд▓реВ рд╢рдХрддреЛ. рдЖрдордЪреНрдпрд╛ рдмрд╛рдмрддреАрдд, рдПрдХрд╛ SSD рдордзреНрдпреЗ рдХрд┐рдВрд╡рд╛ рдлреНрд▓реЕрд╢ рдбреНрд░рд╛рдЗрд╡реНрд╣ (рд╕реНрд░реЛрдд рдлрд╛рдЗрд▓) рд╡рд░реВрди SSD (DB) рдордзреНрдпреЗ рд╡рд╛рдЪрди рдЖрдгрд┐ рд▓рд┐рд╣рд┐рдгреНрдпрд╛рд╕рд╛рдареА 10 рдорд┐рдирд┐рдЯрд╛рдВрдкреЗрдХреНрд╖рд╛ рдХрдореА рд╡реЗрд│ рд▓рд╛рдЧрддреЛ.

рдкреВрд░реНрдгрд╛рдВрдХ рд╡рд░реНрдЧ рд▓реЗрдмрд▓ рдЖрдгрд┐ рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рд╕реНрддрдВрднрд╛рд╕рд╣ рд╕реНрддрдВрдн рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрдЦреА рдХрд╛рд╣реА рд╕реЗрдХрдВрдж рд▓рд╛рдЧрддрд╛рдд (ORDERED INDEX) рд░реЗрд╖рд╛ рдХреНрд░рдорд╛рдВрдХрд╛рдВрд╕рд╣ рдЬреНрдпрд╛рджреНрд╡рд╛рд░реЗ рдмреЕрдЪреЗрд╕ рддрдпрд╛рд░ рдХрд░рддрд╛рдирд╛ рдирд┐рд░реАрдХреНрд╖рдгреЗ рдирдореБрдирд╛ рдХреЗрд▓реА рдЬрд╛рддреАрд▓:

рдЕрддрд┐рд░рд┐рдХреНрдд рд╕реНрддрдВрдн рдЖрдгрд┐ рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рддрдпрд╛рд░ рдХрд░рдгреЗ

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

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

рдлреНрд▓рд╛рдпрд╡рд░ рдмреЕрдЪ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рдЪреНрдпрд╛ рд╕рдорд╕реНрдпреЗрдЪреЗ рдирд┐рд░рд╛рдХрд░рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣рд╛рд▓рд╛ рдЯреЗрдмрд▓рдордзреВрди рдпрд╛рджреГрдЪреНрдЫрд┐рдХ рдкрдВрдХреНрддреА рдХрд╛рдврдгреНрдпрд╛рдЪреА рдХрдорд╛рд▓ рдЧрддреА рдкреНрд░рд╛рдкреНрдд рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ. doodles. рдпрд╛рд╕рд╛рдареА рдЖрдореНрд╣реА 3 рдпреБрдХреНрддреНрдпрд╛ рд╡рд╛рдкрд░рд▓реНрдпрд╛. рдкрд╣рд┐рд▓реЗ рдореНрд╣рдгрдЬреЗ рдирд┐рд░реАрдХреНрд╖рдг рдЖрдпрдбреА рд╕рдВрдЪрдпрд┐рдд рдХрд░рдгрд╛рд░реНтАНрдпрд╛ рдкреНрд░рдХрд╛рд░рд╛рдЪреА рдЖрдпрд╛рдореАрддрд╛ рдХрдореА рдХрд░рдгреЗ. рдореВрд│ рдбреЗрдЯрд╛ рд╕реЗрдЯрдордзреНрдпреЗ, рдЖрдпрдбреА рд╕рдВрдЪрдпрд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрд╡рд╢реНрдпрдХ рдкреНрд░рдХрд╛рд░ рдЖрд╣реЗ bigint, рдкрд░рдВрддреБ рдирд┐рд░реАрдХреНрд╖рдгрд╛рдВрдЪреНрдпрд╛ рд╕рдВрдЦреНрдпреЗрдореБрд│реЗ рддреНрдпрд╛рдВрдЪреЗ рдЕрднрд┐рдЬреНрдЮрд╛рдкрдХ, рдХреНрд░рдорд╕рдВрдЦреНрдпреЗрдЪреНрдпрд╛ рд╕рдорд╛рди, рдкреНрд░рдХрд╛рд░рд╛рдд рдмрд╕рд╡рдгреЗ рд╢рдХреНрдп рд╣реЛрддреЗ int. рдпрд╛ рдкреНрд░рдХрд░рдгрд╛рдд рд╢реЛрдз рдЕрдзрд┐рдХ рд╡реЗрдЧрд╡рд╛рди рдЖрд╣реЗ. рджреБрд╕рд░реА рдпреБрдХреНрддреА рд╡рд╛рдкрд░рд╛рдпрдЪреА рд╣реЛрддреА ORDERED INDEX тАФ рд╕рд░реНрд╡ рдЙрдкрд▓рдмреНрдз рдЧреЛрд╖реНрдЯреАрдВрдЪрд╛ рдЕрднреНрдпрд╛рд╕ рдХрд░реВрди рдЖрдореНрд╣реА рд╣рд╛ рдирд┐рд░реНрдгрдп рдЕрдиреБрднрд╡рд╛рдиреЗ рдШреЗрддрд▓рд╛ рдкрд░реНрдпрд╛рдп. рддрд┐рд╕рд░реЗ рдкреЕрд░рд╛рдореАрдЯрд░рд╛рдЗрдЬреНрдб рдХреНрд╡реЗрд░реА рд╡рд╛рдкрд░рдгреЗ рд╣реЛрддреЗ. рдкрджреНрдзрддреАрдЪреЗ рд╕рд╛рд░ рдореНрд╣рдгрдЬреЗ рдХрдорд╛рдВрдб рдПрдХрджрд╛рдЪ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рдгреЗ PREPARE рддреНрдпрд╛рдЪ рдкреНрд░рдХрд╛рд░рдЪреНрдпрд╛ рдХреНрд╡реЗрд░реАрдВрдЪрд╛ рд╕рдореВрд╣ рддрдпрд╛рд░ рдХрд░рддрд╛рдирд╛ рддрдпрд╛рд░ рдХреЗрд▓реЗрд▓реНрдпрд╛ рдЕрднрд┐рд╡реНрдпрдХреНрддреАрдЪреНрдпрд╛ рдирдВрддрд░рдЪреНрдпрд╛ рд╡рд╛рдкрд░рд╛рд╕рд╣, рдкрд░рдВрддреБ рдкреНрд░рддреНрдпрдХреНрд╖рд╛рдд рд╕рд╛рдзреНрдпрд╛ рдкреНрд░рд╢реНрдирд╛рдВрдЪреНрдпрд╛ рддреБрд▓рдиреЗрдд рдПрдХ рдлрд╛рдпрджрд╛ рдЖрд╣реЗ SELECT рд╕рд╛рдВрдЦреНрдпрд┐рдХреАрдп рддреНрд░реБрдЯреАрдЪреНрдпрд╛ рдорд░реНрдпрд╛рджреЗрдд рдЕрд╕рд▓реНрдпрд╛рдЪреЗ рджрд┐рд╕реВрди рдЖрд▓реЗ.

рдбреЗрдЯрд╛ рдЕрдкрд▓реЛрдб рдХрд░рдгреНрдпрд╛рдЪреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ 450 MB рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд RAM рд╡рд╛рдкрд░рдд рдирд╛рд╣реА. рдореНрд╣рдгрдЬреЗрдЪ, рд╡рд░реНрдгрди рдХреЗрд▓реЗрд▓рд╛ рджреГрд╖реНрдЯреАрдХреЛрди рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рд╣реА рд╕рд┐рдВрдЧрд▓-рдмреЛрд░реНрдб рдбрд┐рд╡реНрд╣рд╛рдЗрд╕реЗрд╕рд╕рд╣ рдЬрд╡рд│рдЬрд╡рд│ рдХреЛрдгрддреНрдпрд╛рд╣реА рдмрдЬреЗрдЯ рд╣рд╛рд░реНрдбрд╡реЗрдЕрд░рд╡рд░ рджрд╣рд╛рдкрдЯ рдЧреАрдЧрд╛рдмрд╛рдЗрдЯ рд╡рдЬрдирд╛рдЪрд╛ рдбреЗрдЯрд╛рд╕реЗрдЯ рд╣рд▓рд╡рд┐рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЛ, рдЬреЗ рдЦреВрдкрдЪ рдЫрд╛рди рдЖрд╣реЗ.

рдЬреЗ рдХрд╛рд╣реА рдЙрд░рддреЗ рддреЗ рдореНрд╣рдгрдЬреЗ (рдпрд╛рджреГрдЪреНрдЫрд┐рдХ) рдбреЗрдЯрд╛ рдкреБрдирд░реНрдкреНрд░рд╛рдкреНрдд рдХрд░рдгреНрдпрд╛рдЪреНрдпрд╛ рдЧрддреАрдЪреЗ рдореЛрдЬрдорд╛рдк рдХрд░рдгреЗ рдЖрдгрд┐ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рдЖрдХрд╛рд░рд╛рдВрдЪреНрдпрд╛ рдмреЕрдЪрдЪреЗ рд╕реЕрдореНрдкрд▓рд┐рдВрдЧ рдХрд░рддрд╛рдирд╛ рд╕реНрдХреЗрд▓рд┐рдВрдЧрдЪреЗ рдореВрд▓реНрдпрд╛рдВрдХрди рдХрд░рдгреЗ:

рдбреЗрдЯрд╛рдмреЗрд╕ рдмреЗрдВрдЪрдорд╛рд░реНрдХ

library(ggplot2)

set.seed(0)
# ╨Я╨╛╨┤╨║╨╗╤О╤З╨╡╨╜╨╕╨╡ ╨║ ╨▒╨░╨╖╨╡ ╨┤╨░╨╜╨╜╤Л╤Е
con <- DBI::dbConnect(MonetDBLite::MonetDBLite(), Sys.getenv("DBDIR"))

# ╨д╤Г╨╜╨║╤Ж╨╕╤П ╨┤╨╗╤П ╨┐╨╛╨┤╨│╨╛╤В╨╛╨▓╨║╨╕ ╨╖╨░╨┐╤А╨╛╤Б╨░ ╨╜╨░ ╤Б╤В╨╛╤А╨╛╨╜╨╡ ╤Б╨╡╤А╨▓╨╡╤А╨░
prep_sql <- function(batch_size) {
  sql <- sprintf("PREPARE SELECT id FROM doodles WHERE id IN (%s)",
                 paste(rep("?", batch_size), collapse = ","))
  res <- DBI::dbSendQuery(con, sql)
  return(res)
}

# ╨д╤Г╨╜╨║╤Ж╨╕╤П ╨┤╨╗╤П ╨╕╨╖╨▓╨╗╨╡╤З╨╡╨╜╨╕╤П ╨┤╨░╨╜╨╜╤Л╤Е
fetch_data <- function(rs, batch_size) {
  ids <- sample(seq_len(n), batch_size)
  res <- DBI::dbFetch(DBI::dbBind(rs, as.list(ids)))
  return(res)
}

# ╨Я╤А╨╛╨▓╨╡╨┤╨╡╨╜╨╕╨╡ ╨╖╨░╨╝╨╡╤А╨░
res_bench <- bench::press(
  batch_size = 2^(4:10),
  {
    rs <- prep_sql(batch_size)
    bench::mark(
      fetch_data(rs, batch_size),
      min_iterations = 50L
    )
  }
)
# ╨Я╨░╤А╨░╨╝╨╡╤В╤А╤Л ╨▒╨╡╨╜╤З╨╝╨░╤А╨║╨░
cols <- c("batch_size", "min", "median", "max", "itr/sec", "total_time", "n_itr")
res_bench[, cols]

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

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

DBI::dbDisconnect(con, shutdown = TRUE)

рдХреНрд╡рд┐рдХ рдбреНрд░реЙ рдбреВрдбрд▓ рдУрд│рдЦ: R, C++ рдЖрдгрд┐ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╢реА рдореИрддреНрд░реА рдХрд╢реА рдХрд░рд╛рд╡реА

2. рдмреЕрдЪ рддрдпрд╛рд░ рдХрд░рдгреЗ

рд╕рдВрдкреВрд░реНрдг рдмреЕрдЪ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рдЪреНрдпрд╛ рдкреНрд░рдХреНрд░рд┐рдпреЗрдд рдЦрд╛рд▓реАрд▓ рдЪрд░рдгрд╛рдВрдЪрд╛ рд╕рдорд╛рд╡реЗрд╢ рдЖрд╣реЗ:

  1. рдкреЙрдЗрдВрдЯреНрд╕рдЪреНрдпрд╛ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХрд╛рдВрд╕рд╣ рд╕реНрдЯреНрд░рд┐рдВрдЧрдЪреЗ рд╡реЗрдХреНрдЯрд░ рдЕрд╕рд▓реЗрд▓реЗ рдЕрдиреЗрдХ JSON рдкрд╛рд░реНрд╕ рдХрд░рдгреЗ.
  2. рдЖрд╡рд╢реНрдпрдХ рдЖрдХрд╛рд░рд╛рдЪреНрдпрд╛ рдкреНрд░рддрд┐рдореЗрд╡рд░реАрд▓ рдмрд┐рдВрджреВрдВрдЪреНрдпрд╛ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХрд╛рдВрд╡рд░ рдЖрдзрд╛рд░рд┐рдд рд░рдВрдЧреАрдд рд░реЗрд╖рд╛ рдХрд╛рдврдгреЗ (рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, 256├Ч256 рдХрд┐рдВрд╡рд╛ 128├Ч128).
  3. рдкрд░рд┐рдгрд╛рдореА рдкреНрд░рддрд┐рдорд╛ рдЯреЗрдиреНрд╕рд░рдордзреНрдпреЗ рд░реВрдкрд╛рдВрддрд░рд┐рдд рдХрд░рдгреЗ.

рдкрд╛рдпрдерди рдХрд░реНрдирд▓рдордзреАрд▓ рд╕реНрдкрд░реНрдзреЗрдЪрд╛ рднрд╛рдЧ рдореНрд╣рдгреВрди, рд╕рдорд╕реНрдпрд╛ рдкреНрд░рд╛рдореБрдЦреНрдпрд╛рдиреЗ рд╡рд╛рдкрд░реВрди рд╕реЛрдбрд╡рд▓реА рдЧреЗрд▓реА рдУрдкрдирд╕реАрд╡реНрд╣реА. R рдордзреАрд▓ рд╕рд░реНрд╡рд╛рдд рд╕реЛрдкреНрдпрд╛ рдЖрдгрд┐ рд╕рд░реНрд╡рд╛рдд рд╕реНрдкрд╖реНрдЯ рдЕреЕрдирд╛рд▓реЙрдЧреНрд╕рдкреИрдХреА рдПрдХ рдЕрд╕реЗ рджрд┐рд╕реЗрд▓:

R рдордзреНрдпреЗ JSON рддреЗ Tensor рд░реВрдкрд╛рдВрддрд░рдг рд▓рд╛рдЧреВ рдХрд░рдгреЗ

r_process_json_str <- function(json, line.width = 3, 
                               color = TRUE, scale = 1) {
  # ╨Я╨░╤А╤Б╨╕╨╜╨│ JSON
  coords <- jsonlite::fromJSON(json, simplifyMatrix = FALSE)
  tmp <- tempfile()
  # ╨г╨┤╨░╨╗╤П╨╡╨╝ ╨▓╤А╨╡╨╝╨╡╨╜╨╜╤Л╨╣ ╤Д╨░╨╣╨╗ ╨┐╨╛ ╨╖╨░╨▓╨╡╤А╤И╨╡╨╜╨╕╤О ╤Д╤Г╨╜╨║╤Ж╨╕╨╕
  on.exit(unlink(tmp))
  png(filename = tmp, width = 256 * scale, height = 256 * scale, pointsize = 1)
  # ╨Я╤Г╤Б╤В╨╛╨╣ ╨│╤А╨░╤Д╨╕╨║
  plot.new()
  # ╨а╨░╨╖╨╝╨╡╤А ╨╛╨║╨╜╨░ ╨│╤А╨░╤Д╨╕╨║╨░
  plot.window(xlim = c(256 * scale, 0), ylim = c(256 * scale, 0))
  # ╨ж╨▓╨╡╤В╨░ ╨╗╨╕╨╜╨╕╨╣
  cols <- if (color) rainbow(length(coords)) else "#000000"
  for (i in seq_along(coords)) {
    lines(x = coords[[i]][[1]] * scale, y = coords[[i]][[2]] * scale, 
          col = cols[i], lwd = line.width)
  }
  dev.off()
  # ╨Я╤А╨╡╨╛╨▒╤А╨░╨╖╨╛╨▓╨░╨╜╨╕╨╡ ╨╕╨╖╨╛╨▒╤А╨░╨╢╨╡╨╜╨╕╤П ╨▓ 3-╤Е ╨╝╨╡╤А╨╜╤Л╨╣ ╨╝╨░╤Б╤Б╨╕╨▓
  res <- png::readPNG(tmp)
  return(res)
}

r_process_json_vector <- function(x, ...) {
  res <- lapply(x, r_process_json_str, ...)
  # ╨Ю╨▒╤К╨╡╨┤╨╕╨╜╨╡╨╜╨╕╨╡ 3-╤Е ╨╝╨╡╤А╨╜╤Л╤Е ╨╝╨░╤Б╤Б╨╕╨▓╨╛╨▓ ╨║╨░╤А╤В╨╕╨╜╨╛╨║ ╨▓ 4-╤Е ╨╝╨╡╤А╨╜╤Л╨╣ ╨▓ ╤В╨╡╨╜╨╖╨╛╤А
  res <- do.call(abind::abind, c(res, along = 0))
  return(res)
}

рд░реЗрдЦрд╛рдВрдХрди рдорд╛рдирдХ R рдЯреВрд▓реНрд╕ рд╡рд╛рдкрд░реВрди рдХреЗрд▓реЗ рдЬрд╛рддреЗ рдЖрдгрд┐ RAM рдордзреНрдпреЗ рд╕рдВрдЧреНрд░рд╣рд┐рдд рддрд╛рддреНрдкреБрд░рддреНрдпрд╛ PNG рдордзреНрдпреЗ рдЬрддрди рдХреЗрд▓реЗ рдЬрд╛рддреЗ (Linux рд╡рд░, рддрд╛рддреНрдкреБрд░рддреНрдпрд╛ R рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдирд┐рд░реНрджреЗрд╢рд┐рдХреЗрдд рдЕрд╕рддрд╛рдд. /tmp, RAM рдордзреНрдпреЗ рдЖрд░реЛрд╣рд┐рдд). рд╣реА рдлрд╛рдЗрд▓ рдирдВрддрд░ 0 рддреЗ 1 рдкрд░реНрдпрдВрддрдЪреНрдпрд╛ рд╕рдВрдЦреНрдпреЗрд╕рд╣ рддреНрд░рд┐рдорд┐рддреАрдп рдЕреЕрд░реЗ рдореНрд╣рдгреВрди рд╡рд╛рдЪрд▓реА рдЬрд╛рддреЗ. рд╣реЗ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ рдХрд╛рд░рдг рдЕрдзрд┐рдХ рдкрд╛рд░рдВрдкрд╛рд░рд┐рдХ BMP рд╣реЗрдХреНрд╕ рдХрд▓рд░ рдХреЛрдбрд╕рд╣ рд░реЙ рдЕреЕрд░реЗрдордзреНрдпреЗ рд╡рд╛рдЪрд▓реЗ рдЬрд╛рдИрд▓.

рдЪрд▓рд╛ рдирд┐рдХрд╛рд▓ рддрдкрд╛рд╕реВрдпрд╛:

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

рдХреНрд╡рд┐рдХ рдбреНрд░реЙ рдбреВрдбрд▓ рдУрд│рдЦ: 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

рд╣реА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдЖрдореНрд╣рд╛рд▓рд╛ рд╕рд░реНрд╡реЛрддреНрдХреГрд╖реНрдЯ рд╡рд╛рдЯрд▓реА, рдХрд╛рд░рдг рдореЛрдареНрдпрд╛ рддреБрдХрдбреНрдпрд╛ рддрдпрд╛рд░ рд╣реЛрдгреНрдпрд╛рд╕ рдЦреВрдк рд╡реЗрд│ рд▓рд╛рдЧрддреЛ рдЖрдгрд┐ рдЖрдореНрд╣реА рдПрдХрд╛ рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рд▓рд╛рдпрдмреНрд░рд░реАрдЪрд╛ рд╡рд╛рдкрд░ рдХрд░реВрди рдЖрдордЪреНрдпрд╛ рд╕рд╣рдХрд╛рд▒реНрдпрд╛рдВрдЪреНрдпрд╛ рдЕрдиреБрднрд╡рд╛рдЪрд╛ рдлрд╛рдпрджрд╛ рдШреЗрдгреНрдпрд╛рдЪреЗ рдард░рд╡рд▓реЗ. рдУрдкрдирд╕реАрд╡реНрд╣реА. рддреНрдпрд╛рд╡реЗрд│реА R рд╕рд╛рдареА рдХреЛрдгрддреЗрд╣реА рд░реЗрдбреАрдореЗрдб рдкреЕрдХреЗрдЬ рдирд╡реНрд╣рддреЗ (рдЖрддрд╛ рдХреЛрдгрддреЗрд╣реА рдирд╛рд╣реА), рддреНрдпрд╛рдореБрд│реЗ рдЖрд╡рд╢реНрдпрдХ рдХрд╛рд░реНрдпрдХреНрд╖рдорддреЗрдЪреА рдХрд┐рдорд╛рди рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА C++ рдордзреНрдпреЗ R рдХреЛрдб рд╡рд╛рдкрд░реВрди рдПрдХрддреНрд░реАрдХрд░рдгрд╛рд╕рд╣ рд▓рд┐рд╣рд┐рд▓реА рдЧреЗрд▓реА. Rcpp.

рд╕рдорд╕реНрдпреЗрдЪреЗ рдирд┐рд░рд╛рдХрд░рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЦрд╛рд▓реАрд▓ рдкреЕрдХреЗрдЬреЗрд╕ рдЖрдгрд┐ рд▓рд╛рдпрдмреНрд░рд░реА рд╡рд╛рдкрд░рд▓реНрдпрд╛ рдЧреЗрд▓реНрдпрд╛:

  1. рдУрдкрдирд╕реАрд╡реНрд╣реА рдкреНрд░рддрд┐рдорд╛ рдЖрдгрд┐ рд░реЗрдЦрд╛рдЪрд┐рддреНрд░реЗ рд╕рд╣ рдХрд╛рд░реНрдп рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА. рдкреНрд░реА-рдЗрдВрд╕реНрдЯреЙрд▓ рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╕рд┐рд╕реНрдЯрдо рд▓рд╛рдпрдмреНрд░рд░реА рдЖрдгрд┐ рд╣реЗрдбрд░ рдлрд╛рдЗрд▓реНрд╕, рддрд╕реЗрдЪ рдбрд╛рдпрдиреЕрдорд┐рдХ рд▓рд┐рдВрдХрд┐рдВрдЧ рд╡рд╛рдкрд░рд▓реЗ.

  2. xtensor рдмрд╣реБрдЖрдпрд╛рдореА рдЕреЕрд░реЗ рдЖрдгрд┐ рдЯреЗрдиреНрд╕рд░реНрд╕рд╕рд╣ рдХрд╛рдо рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА. рдЖрдореНрд╣реА рддреНрдпрд╛рдЪ рдирд╛рд╡рд╛рдЪреНрдпрд╛ R рдкреЕрдХреЗрдЬрдордзреНрдпреЗ рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рд╣реЗрдбрд░ рдлрд╛рдЗрд▓реНрд╕ рд╡рд╛рдкрд░рд▓реНрдпрд╛. рд▓рд╛рдпрдмреНрд░рд░реА рддреБрдореНрд╣рд╛рд▓рд╛ рдмрд╣реБрдЖрдпрд╛рдореА рдЕреЕрд░реЗрд╕рд╣ рдХрд╛рд░реНрдп рдХрд░рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЗ, рджреЛрдиреНрд╣реА рдкреНрд░рдореБрдЦ рдкрдВрдХреНрддреА рдЖрдгрд┐ рд╕реНрддрдВрдн рдкреНрд░рдореБрдЦ рдХреНрд░рдорд╛рдиреЗ.

  3. ndjson JSON рдкрд╛рд░реНрд╕ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА. рдордзреНрдпреЗ рдпрд╛ рдЧреНрд░рдВрдерд╛рд▓рдпрд╛рдЪрд╛ рд╡рд╛рдкрд░ рдХреЗрд▓рд╛ рдЬрд╛рддреЛ xtensor рддреЗ рдкреНрд░рдХрд▓реНрдкрд╛рдд рдЙрдкрд╕реНрдерд┐рдд рдЕрд╕рд▓реНрдпрд╛рд╕ рдЖрдкреЛрдЖрдк.

  4. RcppThread JSON рдХрдбреВрди рд╡реЗрдХреНрдЯрд░рдЪреА рдорд▓реНрдЯреА-рдереНрд░реЗрдб рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧ рдЖрдпреЛрдЬрд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА. рдпрд╛ рдкреЕрдХреЗрдЬрджреНрд╡рд╛рд░реЗ рдкреНрд░рджрд╛рди рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╢реАрд░реНрд╖рд▓реЗрдЦ рдлрд╛рдпрд▓реА рд╡рд╛рдкрд░рд▓реНрдпрд╛. рдЕрдзрд┐рдХ рд▓реЛрдХрдкреНрд░рд┐рдп рдкрд╛рд╕реВрди Rcpp рд╕рдорд╛рдВрддрд░ рдкреЕрдХреЗрдЬрдордзреНрдпреЗ, рдЗрддрд░ рдЧреЛрд╖реНрдЯреАрдВрдмрд░реЛрдмрд░рдЪ, рдЕрдВрдЧрднреВрдд рд▓реВрдк рдЗрдВрдЯрд░рдкреНрдЯ рдпрдВрддреНрд░рдгрд╛ рдЖрд╣реЗ.

рд╣реЗ рд▓рдХреНрд╖рд╛рдд рдШреЗрдгреНрдпрд╛рд╕рд╛рд░рдЦреЗ рдЖрд╣реЗ 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. рдорд╛рд░реНрдЧ рдЖрдгрд┐ рдзреНрд╡рдЬ рд╕реНрд╡рдпрдВрдЪрд▓рд┐рддрдкрдгреЗ рд╢реЛрдзрдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣реА рдПрдХ рд▓реЛрдХрдкреНрд░рд┐рдп рд▓рд┐рдирдХреНрд╕ рдЙрдкрдпреБрдХреНрддрддрд╛ рд╡рд╛рдкрд░рд▓реА pkg-config.

OpenCV рд▓рд╛рдпрдмреНрд░рд░реА рд╡рд╛рдкрд░рдгреНрдпрд╛рд╕рд╛рдареА Rcpp рдкреНрд▓рдЧрдЗрдирдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА

Rcpp::registerPlugin("opencv", function() {
  # ╨Т╨╛╨╖╨╝╨╛╨╢╨╜╤Л╨╡ ╨╜╨░╨╖╨▓╨░╨╜╨╕╤П ╨┐╨░╨║╨╡╤В╨░
  pkg_config_name <- c("opencv", "opencv4")
  # ╨С╨╕╨╜╨░╤А╨╜╤Л╨╣ ╤Д╨░╨╣╨╗ ╤Г╤В╨╕╨╗╨╕╤В╤Л pkg-config
  pkg_config_bin <- Sys.which("pkg-config")
  # ╨Я╤А╨╛╨▓╤А╨╡╨║╨░ ╨╜╨░╨╗╨╕╤З╨╕╤П ╤Г╤В╨╕╨╗╨╕╤В╤Л ╨▓ ╤Б╨╕╤Б╤В╨╡╨╝╨╡
  checkmate::assert_file_exists(pkg_config_bin, access = "x")
  # ╨Я╤А╨╛╨▓╨╡╤А╨║╨░ ╨╜╨░╨╗╨╕╤З╨╕╤П ╤Д╨░╨╣╨╗╨░ ╨╜╨░╤Б╤В╤А╨╛╨╡╨║ OpenCV ╨┤╨╗╤П pkg-config
  check <- sapply(pkg_config_name, 
                  function(pkg) system(paste(pkg_config_bin, pkg)))
  if (all(check != 0)) {
    stop("OpenCV config for the pkg-config not found", call. = FALSE)
  }

  pkg_config_name <- pkg_config_name[check == 0]
  list(env = list(
    PKG_CXXFLAGS = system(paste(pkg_config_bin, "--cflags", pkg_config_name), 
                          intern = TRUE),
    PKG_LIBS = system(paste(pkg_config_bin, "--libs", pkg_config_name), 
                      intern = TRUE)
  ))
})

рдкреНрд▓рдЧрдЗрдирдЪреНрдпрд╛ рдСрдкрд░реЗрд╢рдирдЪреНрдпрд╛ рдкрд░рд┐рдгрд╛рдореА, рд╕рдВрдХрд▓рди рдкреНрд░рдХреНрд░рд┐рдпреЗрджрд░рдореНрдпрд╛рди рдЦрд╛рд▓реАрд▓ рдореВрд▓реНрдпреЗ рдмрджрд▓рд▓реА рдЬрд╛рддреАрд▓:

Rcpp:::.plugins$opencv()$env

# $PKG_CXXFLAGS
# [1] "-I/usr/include/opencv"
#
# $PKG_LIBS
# [1] "-lopencv_shape -lopencv_stitching -lopencv_superres -lopencv_videostab -lopencv_aruco -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_datasets -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hdf -lopencv_line_descriptor -lopencv_optflow -lopencv_video -lopencv_plot -lopencv_reg -lopencv_saliency -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_rgbd -lopencv_viz -lopencv_surface_matching -lopencv_text -lopencv_ximgproc -lopencv_calib3d -lopencv_features2d -lopencv_flann -lopencv_xobjdetect -lopencv_objdetect -lopencv_ml -lopencv_xphoto -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_photo -lopencv_imgproc -lopencv_core"

JSON рдкрд╛рд░реНрд╕ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрд┐ рдореЙрдбреЗрд▓рдордзреНрдпреЗ рдЯреНрд░рд╛рдиреНрд╕рдорд┐рд╢рдирд╕рд╛рдареА рдмреЕрдЪ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдХреЛрдб рд╕реНрдкреЙрдпрд▓рд░ рдЕрдВрддрд░реНрдЧрдд рджрд┐рд▓реЗрд▓рд╛ рдЖрд╣реЗ. рдкреНрд░рдердо, рд╢реАрд░реНрд╖рд▓реЗрдЦ рдлрд╛рдпрд▓реА рд╢реЛрдзрдгреНрдпрд╛рд╕рд╛рдареА рд╕реНрдерд╛рдирд┐рдХ рдкреНрд░рдХрд▓реНрдк рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдЬреЛрдбрд╛ (ndjson рд╕рд╛рдареА рдЖрд╡рд╢реНрдпрдХ):

Sys.setenv("PKG_CXXFLAGS" = paste0("-I", normalizePath(file.path("src"))))

C++ рдордзреНрдпреЗ JSON рддреЗ рдЯреЗрдиреНрд╕рд░ рд░реВрдкрд╛рдВрддрд░рдгрд╛рдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА

// [[Rcpp::plugins(cpp14)]]
// [[Rcpp::plugins(opencv)]]
// [[Rcpp::depends(xtensor)]]
// [[Rcpp::depends(RcppThread)]]

#include <xtensor/xjson.hpp>
#include <xtensor/xadapt.hpp>
#include <xtensor/xview.hpp>
#include <xtensor-r/rtensor.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <Rcpp.h>
#include <RcppThread.h>

// ╨б╨╕╨╜╨╛╨╜╨╕╨╝╤Л ╨┤╨╗╤П ╤В╨╕╨┐╨╛╨▓
using RcppThread::parallelFor;
using json = nlohmann::json;
using points = xt::xtensor<double,2>;     // ╨Ш╨╖╨▓╨╗╨╡╤З╤С╨╜╨╜╤Л╨╡ ╨╕╨╖ JSON ╨║╨╛╨╛╤А╨┤╨╕╨╜╨░╤В╤Л ╤В╨╛╤З╨╡╨║
using strokes = std::vector<points>;      // ╨Ш╨╖╨▓╨╗╨╡╤З╤С╨╜╨╜╤Л╨╡ ╨╕╨╖ JSON ╨║╨╛╨╛╤А╨┤╨╕╨╜╨░╤В╤Л ╤В╨╛╤З╨╡╨║
using xtensor3d = xt::xtensor<double, 3>; // ╨в╨╡╨╜╨╖╨╛╤А ╨┤╨╗╤П ╤Е╤А╨░╨╜╨╡╨╜╨╕╤П ╨╝╨░╤В╤А╨╕╤Ж╤Л ╨╕╨╖╨╛╨╛╨▒╤А╨░╨╢╨╡╨╜╨╕╤П
using xtensor4d = xt::xtensor<double, 4>; // ╨в╨╡╨╜╨╖╨╛╤А ╨┤╨╗╤П ╤Е╤А╨░╨╜╨╡╨╜╨╕╤П ╨╝╨╜╨╛╨╢╨╡╤Б╤В╨▓╨░ ╨╕╨╖╨╛╨▒╤А╨░╨╢╨╡╨╜╨╕╨╣
using rtensor3d = xt::rtensor<double, 3>; // ╨Ю╨▒╤С╤А╤В╨║╨░ ╨┤╨╗╤П ╤Н╨║╤Б╨┐╨╛╤А╤В╨░ ╨▓ R
using rtensor4d = xt::rtensor<double, 4>; // ╨Ю╨▒╤С╤А╤В╨║╨░ ╨┤╨╗╤П ╤Н╨║╤Б╨┐╨╛╤А╤В╨░ ╨▓ R

// ╨б╤В╨░╤В╨╕╤З╨╡╤Б╨║╨╕╨╡ ╨║╨╛╨╜╤Б╤В╨░╨╜╤В╤Л
// ╨а╨░╨╖╨╝╨╡╤А ╨╕╨╖╨╛╨▒╤А╨░╨╢╨╡╨╜╨╕╤П ╨▓ ╨┐╨╕╨║╤Б╨╡╨╗╤П╤Е
const static int SIZE = 256;
// ╨в╨╕╨┐ ╨╗╨╕╨╜╨╕╨╕
// ╨б╨╝. https://en.wikipedia.org/wiki/Pixel_connectivity#2-dimensional
const static int LINE_TYPE = cv::LINE_4;
// ╨в╨╛╨╗╤Й╨╕╨╜╨░ ╨╗╨╕╨╜╨╕╨╕ ╨▓ ╨┐╨╕╨║╤Б╨╡╨╗╤П╤Е
const static int LINE_WIDTH = 3;
// ╨Р╨╗╨│╨╛╤А╨╕╤В╨╝ ╤А╨╡╤Б╨░╨╣╨╖╨░
// https://docs.opencv.org/3.1.0/da/d54/group__imgproc__transform.html#ga5bb5a1fea74ea38e1a5445ca803ff121
const static int RESIZE_TYPE = cv::INTER_LINEAR;

// ╨и╨░╨▒╨╗╨╛╨╜ ╨┤╨╗╤П ╨║╨╛╨╜╨▓╨╡╤А╤В╨╕╤А╨╛╨▓╨░╨╜╨╕╤П OpenCV-╨╝╨░╤В╤А╨╕╤Ж╤Л ╨▓ ╤В╨╡╨╜╨╖╨╛╤А
template <typename T, int NCH, typename XT=xt::xtensor<T,3,xt::layout_type::column_major>>
XT to_xt(const cv::Mat_<cv::Vec<T, NCH>>& src) {
  // ╨а╨░╨╖╨╝╨╡╤А╨╜╨╛╤Б╤В╤М ╤Ж╨╡╨╗╨╡╨▓╨╛╨│╨╛ ╤В╨╡╨╜╨╖╨╛╤А╨░
  std::vector<int> shape = {src.rows, src.cols, NCH};
  // ╨Ю╨▒╤Й╨╡╨╡ ╨║╨╛╨╗╨╕╤З╨╡╤Б╤В╨▓╨╛ ╤Н╨╗╨╡╨╝╨╡╨╜╤В╨╛╨▓ ╨▓ ╨╝╨░╤Б╤Б╨╕╨▓╨╡
  size_t size = src.total() * NCH;
  // ╨Я╤А╨╡╨╛╨▒╤А╨░╨╖╨╛╨▓╨░╨╜╨╕╨╡ cv::Mat ╨▓ xt::xtensor
  XT res = xt::adapt((T*) src.data, size, xt::no_ownership(), shape);
  return res;
}

// ╨Я╤А╨╡╨╛╨▒╤А╨░╨╖╨╛╨▓╨░╨╜╨╕╨╡ JSON ╨▓ ╤Б╨┐╨╕╤Б╨╛╨║ ╨║╨╛╨╛╤А╨┤╨╕╨╜╨░╤В ╤В╨╛╤З╨╡╨║
strokes parse_json(const std::string& x) {
  auto j = json::parse(x);
  // ╨а╨╡╨╖╤Г╨╗╤М╤В╨░╤В ╨┐╨░╤А╤Б╨╕╨╜╨│╨░ ╨┤╨╛╨╗╨╢╨╡╨╜ ╨▒╤Л╤В╤М ╨╝╨░╤Б╤Б╨╕╨▓╨╛╨╝
  if (!j.is_array()) {
    throw std::runtime_error("'x' must be JSON array.");
  }
  strokes res;
  res.reserve(j.size());
  for (const auto& a: j) {
    // ╨Ъ╨░╨╢╨┤╤Л╨╣ ╤Н╨╗╨╡╨╝╨╡╨╜╤В ╨╝╨░╤Б╤Б╨╕╨▓╨░ ╨┤╨╛╨╗╨╢╨╡╨╜ ╨▒╤Л╤В╤М 2-╨╝╨╡╤А╨╜╤Л╨╝ ╨╝╨░╤Б╤Б╨╕╨▓╨╛╨╝
    if (!a.is_array() || a.size() != 2) {
      throw std::runtime_error("'x' must include only 2d arrays.");
    }
    // ╨Ш╨╖╨▓╨╗╨╡╤З╨╡╨╜╨╕╨╡ ╨▓╨╡╨║╤В╨╛╤А╨░ ╤В╨╛╤З╨╡╨║
    auto p = a.get<points>();
    res.push_back(p);
  }
  return res;
}

// ╨Ю╤В╤А╨╕╤Б╨╛╨▓╨║╨░ ╨╗╨╕╨╜╨╕╨╣
// ╨ж╨▓╨╡╤В╨░ HSV
cv::Mat ocv_draw_lines(const strokes& x, bool color = true) {
  // ╨Ш╤Б╤Е╨╛╨┤╨╜╤Л╨╣ ╤В╨╕╨┐ ╨╝╨░╤В╤А╨╕╤Ж╤Л
  auto stype = color ? CV_8UC3 : CV_8UC1;
  // ╨Ш╤В╨╛╨│╨╛╨▓╤Л╨╣ ╤В╨╕╨┐ ╨╝╨░╤В╤А╨╕╤Ж╤Л
  auto dtype = color ? CV_32FC3 : CV_32FC1;
  auto bg = color ? cv::Scalar(0, 0, 255) : cv::Scalar(255);
  auto col = color ? cv::Scalar(0, 255, 220) : cv::Scalar(0);
  cv::Mat img = cv::Mat(SIZE, SIZE, stype, bg);
  // ╨Ъ╨╛╨╗╨╕╤З╨╡╤Б╤В╨▓╨╛ ╨╗╨╕╨╜╨╕╨╣
  size_t n = x.size();
  for (const auto& s: x) {
    // ╨Ъ╨╛╨╗╨╕╤З╨╡╤Б╤В╨▓╨╛ ╤В╨╛╤З╨╡╨║ ╨▓ ╨╗╨╕╨╜╨╕╨╕
    size_t n_points = s.shape()[1];
    for (size_t i = 0; i < n_points - 1; ++i) {
      // ╨в╨╛╤З╨║╨░ ╨╜╨░╤З╨░╨╗╨░ ╤И╤В╤А╨╕╤Е╨░
      cv::Point from(s(0, i), s(1, i));
      // ╨в╨╛╤З╨║╨░ ╨╛╨║╨╛╨╜╤З╨░╨╜╨╕╤П ╤И╤В╤А╨╕╤Е╨░
      cv::Point to(s(0, i + 1), s(1, i + 1));
      // ╨Ю╤В╤А╨╕╤Б╨╛╨▓╨║╨░ ╨╗╨╕╨╜╨╕╨╕
      cv::line(img, from, to, col, LINE_WIDTH, LINE_TYPE);
    }
    if (color) {
      // ╨Ь╨╡╨╜╤П╨╡╨╝ ╤Ж╨▓╨╡╤В ╨╗╨╕╨╜╨╕╨╕
      col[0] += 180 / n;
    }
  }
  if (color) {
    // ╨Ь╨╡╨╜╤П╨╡╨╝ ╤Ж╨▓╨╡╤В╨╛╨▓╨╛╨╡ ╨┐╤А╨╡╨┤╤Б╤В╨░╨▓╨╗╨╡╨╜╨╕╨╡ ╨╜╨░ RGB
    cv::cvtColor(img, img, cv::COLOR_HSV2RGB);
  }
  // ╨Ь╨╡╨╜╤П╨╡╨╝ ╤Д╨╛╤А╨╝╨░╤В ╨┐╤А╨╡╨┤╤Б╤В╨░╨▓╨╗╨╡╨╜╨╕╤П ╨╜╨░ float32 ╤Б ╨┤╨╕╨░╨┐╨░╨╖╨╛╨╜╨╛╨╝ [0, 1]
  img.convertTo(img, dtype, 1 / 255.0);
  return img;
}

// ╨Ю╨▒╤А╨░╨▒╨╛╤В╨║╨░ JSON ╨╕ ╨┐╨╛╨╗╤Г╤З╨╡╨╜╨╕╨╡ ╤В╨╡╨╜╨╖╨╛╤А╨░ ╤Б ╨┤╨░╨╜╨╜╤Л╨╝╨╕ ╨╕╨╖╨╛╨▒╤А╨░╨╢╨╡╨╜╨╕╤П
xtensor3d process(const std::string& x, double scale = 1.0, bool color = true) {
  auto p = parse_json(x);
  auto img = ocv_draw_lines(p, color);
  if (scale != 1) {
    cv::Mat out;
    cv::resize(img, out, cv::Size(), scale, scale, RESIZE_TYPE);
    cv::swap(img, out);
    out.release();
  }
  xtensor3d arr = color ? to_xt<double,3>(img) : to_xt<double,1>(img);
  return arr;
}

// [[Rcpp::export]]
rtensor3d cpp_process_json_str(const std::string& x, 
                               double scale = 1.0, 
                               bool color = true) {
  xtensor3d res = process(x, scale, color);
  return res;
}

// [[Rcpp::export]]
rtensor4d cpp_process_json_vector(const std::vector<std::string>& x, 
                                  double scale = 1.0, 
                                  bool color = false) {
  size_t n = x.size();
  size_t dim = floor(SIZE * scale);
  size_t channels = color ? 3 : 1;
  xtensor4d res({n, dim, dim, channels});
  parallelFor(0, n, [&x, &res, scale, color](int i) {
    xtensor3d tmp = process(x[i], scale, color);
    auto view = xt::view(res, i, xt::all(), xt::all(), xt::all());
    view = tmp;
  });
  return res;
}

рд╣рд╛ рдХреЛрдб рдлрд╛рдИрд▓рдордзреНрдпреЗ рдареЗрд╡рд╛рд╡рд╛ src/cv_xt.cpp рдЖрдгрд┐ рдХрдорд╛рдВрдбрд╕рд╣ рд╕рдВрдХрд▓рд┐рдд рдХрд░рд╛ Rcpp::sourceCpp(file = "src/cv_xt.cpp", env = .GlobalEnv); рдХрд╛рдорд╛рд╕рд╛рдареА рджреЗрдЦреАрд▓ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ nlohmann/json.hpp рдкрд╛рд╕реВрди REPOZITORIA. рдХреЛрдб рдЕрдиреЗрдХ рдлрдВрдХреНрд╢рдиреНрд╕рдордзреНрдпреЗ рд╡рд┐рднрд╛рдЧрд▓реЗрд▓рд╛ рдЖрд╣реЗ:

  • to_xt - рдЗрдореЗрдЬ рдореЕрдЯреНрд░рд┐рдХреНрд╕рдЪреЗ рд░реВрдкрд╛рдВрддрд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЯреЗрдореНрдкрд▓реЗрдЯ рдХреЗрд▓реЗрд▓реЗ рдХрд╛рд░реНрдп (cv::Mat) рдЯреЗрдиреНрд╕рд░рд▓рд╛ xt::xtensor;

  • parse_json тАФ рдлрдВрдХреНрд╢рди JSON рд╕реНрдЯреНрд░рд┐рдВрдЧ рдкрд╛рд░реНрд╕ рдХрд░рддреЗ, рдкреЙрдЗрдВрдЯреНрд╕рдЪреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХрд╛рдврддреЗ, рддреНрдпрд╛рдВрдирд╛ рд╡реЗрдХреНрдЯрд░рдордзреНрдпреЗ рдкреЕрдХ рдХрд░рддреЗ;

  • ocv_draw_lines тАФ рдмрд┐рдВрджреВрдВрдЪреНрдпрд╛ рдкрд░рд┐рдгрд╛рдореА рд╡реЗрдХреНрдЯрд░рдордзреВрди, рдмрд╣реБ-рд░рдВрдЧреАрдд рд░реЗрд╖рд╛ рдХрд╛рдврддреЛ;

  • process тАФ рд╡рд░реАрд▓ рдХрд╛рд░реНрдпреЗ рдПрдХрддреНрд░рд┐рдд рдХрд░рддреЗ рдЖрдгрд┐ рдкрд░рд┐рдгрд╛рдореА рдкреНрд░рддрд┐рдорд╛ рдореЛрдЬрдгреНрдпрд╛рдЪреА рдХреНрд╖рдорддрд╛ рджреЗрдЦреАрд▓ рдЬреЛрдбрддреЗ;

  • cpp_process_json_str - рдлрдВрдХреНрд╢рдирд╡рд░ рдЖрд╡рд░рдг process, рдЬреЗ рдкрд░рд┐рдгрд╛рдо рдЖрд░-рдСрдмреНрдЬреЗрдХреНрдЯ (рдмрд╣реБрдЖрдпрд╛рдореА рдЕреЕрд░реЗ) рд╡рд░ рдирд┐рд░реНрдпрд╛рдд рдХрд░рддреЗ;

  • cpp_process_json_vector - рдлрдВрдХреНрд╢рдирд╡рд░ рдЖрд╡рд░рдг cpp_process_json_str, рдЬреЗ рддреБрдореНрд╣рд╛рд▓рд╛ рдорд▓реНрдЯреА-рдереНрд░реЗрдбреЗрдб рдореЛрдбрдордзреНрдпреЗ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╡реЗрдХреНрдЯрд░рд╡рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд░рдгреНрдпрд╛рд╕ рдЕрдиреБрдорддреА рджреЗрддреЗ.

рдмрд╣реБ-рд░рдВрдЧреАрдд рд░реЗрд╖рд╛ рдХрд╛рдврдгреНрдпрд╛рд╕рд╛рдареА, HSV рдХрд▓рд░ рдореЙрдбреЗрд▓ рд╡рд╛рдкрд░рдгреНрдпрд╛рдд рдЖрд▓реЗ, рддреНрдпрд╛рдирдВрддрд░ RGB рдордзреНрдпреЗ рд░реВрдкрд╛рдВрддрд░ рдЭрд╛рд▓реЗ. рдЪрд▓рд╛ рдирд┐рдХрд╛рд▓ рддрдкрд╛рд╕реВрдпрд╛:

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

рдХреНрд╡рд┐рдХ рдбреНрд░реЙ рдбреВрдбрд▓ рдУрд│рдЦ: R, C++ рдЖрдгрд┐ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╢реА рдореИрддреНрд░реА рдХрд╢реА рдХрд░рд╛рд╡реА
R рдЖрдгрд┐ C++ рдордзреНрдпреЗ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрдЪреНрдпрд╛ рдЧрддреАрдЪреА рддреБрд▓рдирд╛

res_bench <- bench::mark(
  r_process_json_str(tmp_data[4, drawing], scale = 0.5),
  cpp_process_json_str(tmp_data[4, drawing], scale = 0.5),
  check = FALSE,
  min_iterations = 100
)
# ╨Я╨░╤А╨░╨╝╨╡╤В╤А╤Л ╨▒╨╡╨╜╤З╨╝╨░╤А╨║╨░
cols <- c("expression", "min", "median", "max", "itr/sec", "total_time", "n_itr")
res_bench[, cols]

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

library(ggplot2)
# ╨Я╤А╨╛╨▓╨╡╨┤╨╡╨╜╨╕╨╡ ╨╖╨░╨╝╨╡╤А╨░
res_bench <- bench::press(
  batch_size = 2^(4:10),
  {
    .data <- tmp_data[sample(seq_len(.N), batch_size), drawing]
    bench::mark(
      r_process_json_vector(.data, scale = 0.5),
      cpp_process_json_vector(.data,  scale = 0.5),
      min_iterations = 50,
      check = FALSE
    )
  }
)

res_bench[, cols]

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

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

рдХреНрд╡рд┐рдХ рдбреНрд░реЙ рдбреВрдбрд▓ рдУрд│рдЦ: R, C++ рдЖрдгрд┐ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╢реА рдореИрддреНрд░реА рдХрд╢реА рдХрд░рд╛рд╡реА

рдЬрд╕реЗ рддреБрдореНрд╣реА рдмрдШреВ рд╢рдХрддрд╛, рд╡реЗрдЧ рд╡рд╛рдврдгреЗ рдЦреВрдк рд▓рдХреНрд╖рдгреАрдп рдЕрд╕рд▓реНрдпрд╛рдЪреЗ рджрд┐рд╕реВрди рдЖрд▓реЗ рдЖрдгрд┐ R рдХреЛрдбрд▓рд╛ рд╕рдорд╛рдВрддрд░ рдХрд░реВрди C++ рдХреЛрдб рдкрдХрдбрдгреЗ рд╢рдХреНрдп рдирд╛рд╣реА.

3. рдбреЗрдЯрд╛рдмреЗрд╕рдордзреВрди рдмреЕрдЪ рдЕрдирд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдкреБрдирд░рд╛рд╡реГрддреНрддреА рдХрд░рдгрд╛рд░реЗ

RAM рдордзреНрдпреЗ рдмрд╕рдгрд╛рд▒реНрдпрд╛ рдбреЗрдЯрд╛рд╡рд░ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА R рд▓рд╛ рдпреЛрдЧреНрдп рдкреНрд░рддрд┐рд╖реНрдард╛ рдЖрд╣реЗ, рддрд░ Python рд╣реЗ рдкреБрдирд░рд╛рд╡реГрддреНрддреА рдбреЗрдЯрд╛ рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧрджреНрд╡рд╛рд░реЗ рд╡реИрд╢рд┐рд╖реНрдЯреНрдпреАрдХреГрдд рдЖрд╣реЗ, рдЬреНрдпрд╛рдореБрд│реЗ рддреБрдореНрд╣рд╛рд▓рд╛ рдЖрдЙрдЯ-рдСрдл-рдХреЛрд░ рдХреЕрд▓реНрдХреНрдпреБрд▓реЗрд╢рди (рдмрд╛рд╣реНрдп рдореЗрдорд░реА рд╡рд╛рдкрд░реВрди рдЧрдгрдирд╛) рд╕рд╣рдЬ рдЖрдгрд┐ рдиреИрд╕рд░реНрдЧрд┐рдХрд░рд┐рддреНрдпрд╛ рдЕрдВрдорд▓рд╛рдд рдЖрдгрддрд╛ рдпреЗрддреЗ. рд╡рд░реНрдгрди рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╕рдорд╕реНрдпреЗрдЪреНрдпрд╛ рд╕рдВрджрд░реНрднрд╛рдд рдЖрдордЪреНрдпрд╛рд╕рд╛рдареА рдПрдХ рдЙрддреНрдХреГрд╖реНрдЯ рдЖрдгрд┐ рд╕рдорд░реНрдкрдХ рдЙрджрд╛рд╣рд░рдг рдореНрд╣рдгрдЬреЗ рдбреАрдк рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХреНрд╕ рд╣реЗ рдЧреНрд░реЗрдбрд┐рдпрдВрдЯ рдбрд┐рд╕реЗрдВрдЯ рдкрджреНрдзрддреАрджреНрд╡рд╛рд░реЗ рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдХреЗрд▓реЗ рдЬрд╛рддреЗ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ рдЯрдкреНрдкреНрдпрд╛рд╡рд░ рдирд┐рд░реАрдХреНрд╖рдгрд╛рдЪрд╛ рдПрдХ рдЫреЛрдЯрд╛рд╕рд╛ рднрд╛рдЧ рдХрд┐рдВрд╡рд╛ рдорд┐рдиреА-рдмреЕрдЪ рд╡рд╛рдкрд░реВрди рдЧреНрд░реЗрдбрд┐рдпрдВрдЯрдЪрд╛ рдЕрдВрджрд╛рдЬ рдШреЗрддреЛ.

рдкрд╛рдпрдердирдордзреНрдпреЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ рдбреАрдк рд▓рд░реНрдирд┐рдВрдЧ рдлреНрд░реЗрдорд╡рд░реНрдХрдордзреНрдпреЗ рд╡рд┐рд╢реЗрд╖ рд╡рд░реНрдЧ рдЖрд╣реЗрдд рдЬреЗ рдбреЗрдЯрд╛рдЪреНрдпрд╛ рдЖрдзрд╛рд░рд╛рд╡рд░ рдкреБрдирд░рд╛рд╡реГрддреНрддреА рдХрд░рдгрд╛рд░реЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рддрд╛рдд: рдЯреЗрдмрд▓реНрд╕, рдлреЛрд▓реНрдбрд░реНрд╕рдордзреАрд▓ рдЪрд┐рддреНрд░реЗ, рдмрд╛рдпрдирд░реА рдлреЙрд░рдореЕрдЯ рдЗ. рддреБрдореНрд╣реА рддрдпрд╛рд░ рдкрд░реНрдпрд╛рдп рд╡рд╛рдкрд░реВ рд╢рдХрддрд╛ рдХрд┐рдВрд╡рд╛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХрд╛рд░реНрдпрд╛рдВрд╕рд╛рдареА рд╕реНрд╡рддрдГрдЪреЗ рд▓рд┐рд╣реВ рд╢рдХрддрд╛. R рдордзреНрдпреЗ рдЖрдкрдг рдкрд╛рдпрдерди рд▓рд╛рдпрдмреНрд░рд░реАрдЪреНрдпрд╛ рд╕рд░реНрд╡ рд╡реИрд╢рд┐рд╖реНрдЯреНрдпрд╛рдВрдЪрд╛ рд▓рд╛рдн рдШреЗрдК рд╢рдХрддреЛ рдХреЗрд░рд╛рд╕ рддреНрдпрд╛рдЪ рдирд╛рд╡рд╛рдЪреЗ рдкреЕрдХреЗрдЬ рд╡рд╛рдкрд░реВрди рддреНрдпрд╛рдЪреНрдпрд╛ рд╡рд┐рд╡рд┐рдз рдмреЕрдХрдПрдВрдбрд╕рд╣, рдЬреЗ рдкреЕрдХреЗрдЬрдЪреНрдпрд╛ рд╢реАрд░реНрд╖рд╕реНрдерд╛рдиреА рдХрд╛рд░реНрдп рдХрд░рддреЗ рдЬрд╛рд│реАрджрд╛рд░. рдирдВрддрд░рдЪрд╛ рд╕реНрд╡рддрдВрддреНрд░ рджреАрд░реНрдШ рд▓реЗрдЦ рдкрд╛рддреНрд░ рдЖрд╣реЗ; рд╣реЗ рддреБрдореНрд╣рд╛рд▓рд╛ рдХреЗрд╡рд│ R рд╡рд░реВрди Python рдХреЛрдб рдЪрд╛рд▓рд╡рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрдд тАЛтАЛрдирд╛рд╣реА, рддрд░ рддреБрдореНрд╣рд╛рд▓рд╛ R рдЖрдгрд┐ Python рд╕рддреНрд░рд╛рдВрджрд░рдореНрдпрд╛рди рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рд╣рд╕реНрддрд╛рдВрддрд░рд┐рдд рдХрд░рдгреНрдпрд╛рдЪреА рджреЗрдЦреАрд▓ рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЗ, рд╕рд░реНрд╡ рдЖрд╡рд╢реНрдпрдХ рдкреНрд░рдХрд╛рд░рдЪреА рд░реВрдкрд╛рдВрддрд░рдгреЗ рдЖрдкреЛрдЖрдк рдкрд╛рд░ рдкрд╛рдбрддрд╛рдд.

MonetDBLite рдЪрд╛ рд╡рд╛рдкрд░ рдХрд░реВрди рд╕рд░реНрд╡ рдбреЗрдЯрд╛ RAM рдордзреНрдпреЗ рд╕рд╛рдард╡реВрди рдареЗрд╡рдгреНрдпрд╛рдЪреА рдЧрд░рдЬ рдЖрдореНрд╣реА рджреВрд░ рдХреЗрд▓реА, рд╕рд░реНрд╡ тАЬрдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХтАЭ рдХрд╛рд░реНрдп рдкрд╛рдпрдердирдордзреАрд▓ рдореВрд│ рдХреЛрдбрджреНрд╡рд╛рд░реЗ рдХреЗрд▓реЗ рдЬрд╛рдИрд▓, рдЖрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд рдбреЗрдЯрд╛рд╡рд░ рдПрдХ рдкреБрдирд░рд╛рд╡реГрддреНрддреА рд▓рд┐рд╣рд╛рд╡реА рд▓рд╛рдЧреЗрд▓, рдХрд╛рд░рдг рдХрд╛рд╣реАрд╣реА рддрдпрд╛рд░ рдирд╛рд╣реА. R рдХрд┐рдВрд╡рд╛ Python рдордзреНрдпреЗ рдЕрд╢рд╛ рдкрд░рд┐рд╕реНрдерд┐рддреАрд╕рд╛рдареА. рддреНрдпрд╛рд╕рд╛рдареА рдореВрд▓рдд: рджреЛрдирдЪ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗрдд: рддреНрдпрд╛рдиреЗ рдмреЕрдЪреЗрд╕ рдПрдХрд╛ рдЕрдВрддрд╣реАрди рд▓реВрдкрдордзреНрдпреЗ рдкрд░рдд рдХреЗрд▓реНрдпрд╛ рдкрд╛рд╣рд┐рдЬреЗрдд рдЖрдгрд┐ рдкреБрдирд░рд╛рд╡реГрддреНрддреА рджрд░рдореНрдпрд╛рди рддреНрдпрд╛рдЪреА рд╕реНрдерд┐рддреА рдЬрддрди рдХреЗрд▓реА рдкрд╛рд╣рд┐рдЬреЗ (рдЖрд░ рдордзреАрд▓ рдирдВрддрд░рдЪреЗ рдХреНрд▓реЛрдЬрд░ рд╡рд╛рдкрд░реВрди рд╕рд░реНрд╡рд╛рдд рд╕реЛрдкреНрдпрд╛ рдкрджреНрдзрддреАрдиреЗ рд▓рд╛рдЧреВ рдХреЗрд▓реЗ рдЖрд╣реЗ). рдкреВрд░реНрд╡реА, рдЗрдЯрд░реЗрдЯрд░рдЪреНрдпрд╛ рдЖрдд рдЖрд░ рдЕреЕрд░реЗ рд╕реНрдкрд╖реНрдЯрдкрдгреЗ numpy рдЕреЕрд░реЗрдордзреНрдпреЗ рд░реВрдкрд╛рдВрддрд░рд┐рдд рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рд╣реЛрддреЗ, рдкрд░рдВрддреБ рдкреЕрдХреЗрдЬрдЪреА рд╕рдзреНрдпрд╛рдЪреА рдЖрд╡реГрддреНрддреА рдХреЗрд░рд╛рд╕ рддреЗ рд╕реНрд╡рддрдГ рдХрд░рддреЗ.

рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдЖрдгрд┐ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдбреЗрдЯрд╛рд╕рд╛рдареА рдкреБрдирд░рд╛рд╡реГрддреНрддреА рдЦрд╛рд▓реАрд▓рдкреНрд░рдорд╛рдгреЗ рдЕрд╕рд▓реНрдпрд╛рдЪреЗ рджрд┐рд╕реВрди рдЖрд▓реЗ:

рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдЖрдгрд┐ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдбреЗрдЯрд╛рд╕рд╛рдареА рдЗрдЯрд░реЗрдЯрд░

train_generator <- function(db_connection = con,
                            samples_index,
                            num_classes = 340,
                            batch_size = 32,
                            scale = 1,
                            color = FALSE,
                            imagenet_preproc = FALSE) {
  # ╨Я╤А╨╛╨▓╨╡╤А╨║╨░ ╨░╤А╨│╤Г╨╝╨╡╨╜╤В╨╛╨▓
  checkmate::assert_class(con, "DBIConnection")
  checkmate::assert_integerish(samples_index)
  checkmate::assert_count(num_classes)
  checkmate::assert_count(batch_size)
  checkmate::assert_number(scale, lower = 0.001, upper = 5)
  checkmate::assert_flag(color)
  checkmate::assert_flag(imagenet_preproc)

  # ╨Я╨╡╤А╨╡╨╝╨╡╤И╨╕╨▓╨░╨╡╨╝, ╤З╤В╨╛╨▒╤Л ╨▒╤А╨░╤В╤М ╨╕ ╤Г╨┤╨░╨╗╤П╤В╤М ╨╕╤Б╨┐╨╛╨╗╤М╨╖╨╛╨▓╨░╨╜╨╜╤Л╨╡ ╨╕╨╜╨┤╨╡╨║╤Б╤Л ╨▒╨░╤В╤З╨╡╨╣ ╨┐╨╛ ╨┐╨╛╤А╤П╨┤╨║╤Г
  dt <- data.table::data.table(id = sample(samples_index))
  # ╨Я╤А╨╛╤Б╤В╨░╨▓╨╗╤П╨╡╨╝ ╨╜╨╛╨╝╨╡╤А╨░ ╨▒╨░╤В╤З╨╡╨╣
  dt[, batch := (.I - 1L) %/% batch_size + 1L]
  # ╨Ю╤Б╤В╨░╨▓╨╗╤П╨╡╨╝ ╤В╨╛╨╗╤М╨║╨╛ ╨┐╨╛╨╗╨╜╤Л╨╡ ╨▒╨░╤В╤З╨╕ ╨╕ ╨╕╨╜╨┤╨╡╨║╤Б╨╕╤А╤Г╨╡╨╝
  dt <- dt[, if (.N == batch_size) .SD, keyby = batch]
  # ╨г╤Б╤В╨░╨╜╨░╨▓╨╗╨╕╨▓╨░╨╡╨╝ ╤Б╤З╤С╤В╤З╨╕╨║
  i <- 1
  # ╨Ъ╨╛╨╗╨╕╤З╨╡╤Б╤В╨▓╨╛ ╨▒╨░╤В╤З╨╡╨╣
  max_i <- dt[, max(batch)]

  # ╨Я╨╛╨┤╨│╨╛╤В╨╛╨▓╨║╨░ ╨▓╤Л╤А╨░╨╢╨╡╨╜╨╕╤П ╨┤╨╗╤П ╨▓╤Л╨│╤А╤Г╨╖╨║╨╕
  sql <- sprintf(
    "PREPARE SELECT drawing, label_int FROM doodles WHERE id IN (%s)",
    paste(rep("?", batch_size), collapse = ",")
  )
  res <- DBI::dbSendQuery(con, sql)

  # ╨Р╨╜╨░╨╗╨╛╨│ keras::to_categorical
  to_categorical <- function(x, num) {
    n <- length(x)
    m <- numeric(n * num)
    m[x * n + seq_len(n)] <- 1
    dim(m) <- c(n, num)
    return(m)
  }

  # ╨Ч╨░╨╝╤Л╨║╨░╨╜╨╕╨╡
  function() {
    # ╨Э╨░╤З╨╕╨╜╨░╨╡╨╝ ╨╜╨╛╨▓╤Г╤О ╤Н╨┐╨╛╤Е╤Г
    if (i > max_i) {
      dt[, id := sample(id)]
      data.table::setkey(dt, batch)
      # ╨б╨▒╤А╨░╤Б╤Л╨▓╨░╨╡╨╝ ╤Б╤З╤С╤В╤З╨╕╨║
      i <<- 1
      max_i <<- dt[, max(batch)]
    }

    # ID ╨┤╨╗╤П ╨▓╤Л╨│╤А╤Г╨╖╨║╨╕ ╨┤╨░╨╜╨╜╤Л╤Е
    batch_ind <- dt[batch == i, id]
    # ╨Т╤Л╨│╤А╤Г╨╖╨║╨░ ╨┤╨░╨╜╨╜╤Л╤Е
    batch <- DBI::dbFetch(DBI::dbBind(res, as.list(batch_ind)), n = -1)

    # ╨г╨▓╨╡╨╗╨╕╤З╨╕╨▓╨░╨╡╨╝ ╤Б╤З╤С╤В╤З╨╕╨║
    i <<- i + 1

    # ╨Я╨░╤А╤Б╨╕╨╜╨│ JSON ╨╕ ╨┐╨╛╨┤╨│╨╛╤В╨╛╨▓╨║╨░ ╨╝╨░╤Б╤Б╨╕╨▓╨░
    batch_x <- cpp_process_json_vector(batch$drawing, scale = scale, color = color)
    if (imagenet_preproc) {
      # ╨и╨║╨░╨╗╨╕╤А╨╛╨▓╨░╨╜╨╕╨╡ c ╨╕╨╜╤В╨╡╤А╨▓╨░╨╗╨░ [0, 1] ╨╜╨░ ╨╕╨╜╤В╨╡╤А╨▓╨░╨╗ [-1, 1]
      batch_x <- (batch_x - 0.5) * 2
    }

    batch_y <- to_categorical(batch$label_int, num_classes)
    result <- list(batch_x, batch_y)
    return(result)
  }
}

рдлрдВрдХреНрд╢рди рдбреЗрдЯрд╛рдмреЗрд╕рд╢реА рдЬреЛрдбрдгреАрд╕рд╣ рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓ рдЗрдирдкреБрдЯ рдореНрд╣рдгреВрди рдШреЗрддреЗ, рд╡рд╛рдкрд░рд▓реЗрд▓реНрдпрд╛ рдУрд│реАрдВрдЪреА рд╕рдВрдЦреНрдпрд╛, рд╡рд░реНрдЧрд╛рдВрдЪреА рд╕рдВрдЦреНрдпрд╛, рдмреЕрдЪ рдЖрдХрд╛рд░, рд╕реНрдХреЗрд▓ (scale = 1 256x256 рдкрд┐рдХреНрд╕реЗрд▓рдЪреНрдпрд╛ рд░реЗрдВрдбрд░рд┐рдВрдЧ рдЗрдореЗрдЬрд╢реА рд╕рдВрдмрдВрдзрд┐рдд рдЖрд╣реЗ, scale = 0.5 тАФ резреиреоxрезреирео рдкрд┐рдХреНрд╕реЗрд▓), рд░рдВрдЧ рд╕реВрдЪрдХ (color = FALSE рд╡рд╛рдкрд░рд▓реНрдпрд╛рд╡рд░ рдЧреНрд░реЗрд╕реНрдХреЗрд▓рдордзреНрдпреЗ рдкреНрд░рд╕реНрддреБрддреАрдХрд░рдг рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рддреЗ color = TRUE рдкреНрд░рддреНрдпреЗрдХ рд╕реНрдЯреНрд░реЛрдХ рдирд╡реАрди рд░рдВрдЧрд╛рдд рдХрд╛рдврд▓рд╛ рдЬрд╛рддреЛ) рдЖрдгрд┐ рдЗрдореЗрдЬрдиреЗрдЯрд╡рд░ рдкреВрд░реНрд╡-рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдиреЗрдЯрд╡рд░реНрдХрд╕рд╛рдареА рдкреНрд░реА-рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧ рдЗрдВрдбрд┐рдХреЗрдЯрд░. рдордзреНрдпрд╛рдВрддрд░ [0, 1] рдкрд╛рд╕реВрди рдордзреНрдпрд╛рдВрддрд░ [-1, 1] рдкрд░реНрдпрдВрдд рдкрд┐рдХреНрд╕реЗрд▓ рдореВрд▓реНрдпреЗ рдореЛрдЬрдгреНрдпрд╛рд╕рд╛рдареА рдирдВрддрд░рдЪреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ, рдЬреЗ рдкреБрд░рд╡рдард╛ рдХреЗрд▓реЗрд▓реНрдпрд╛рдВрдирд╛ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рджреЗрддрд╛рдирд╛ рд╡рд╛рдкрд░рд▓реЗ рд╣реЛрддреЗ рдХреЗрд░рд╛рд╕ рдореЙрдбреЗрд▓

рдмрд╛рд╣реНрдп рдлрдВрдХреНрд╢рдирдордзреНрдпреЗ рдпреБрдХреНрддрд┐рд╡рд╛рдж рдкреНрд░рдХрд╛рд░ рддрдкрд╛рд╕рдгреЗ, рдПрдХ рд╕рд╛рд░рдгреА рдЕрд╕рддреЗ data.table рдкрд╛рд╕реВрди рдпрд╛рджреГрдЪреНрдЫрд┐рдХрдкрдгреЗ рдорд┐рд╢реНрд░рд┐рдд рд░реЗрдЦрд╛ рдХреНрд░рдорд╛рдВрдХрд╛рдВрд╕рд╣ samples_index рдЖрдгрд┐ рдмреЕрдЪ рдХреНрд░рдорд╛рдВрдХ, рдХрд╛рдЙрдВрдЯрд░ рдЖрдгрд┐ рдмреЕрдЪрдЪреА рдХрдорд╛рд▓ рд╕рдВрдЦреНрдпрд╛, рддрд╕реЗрдЪ рдбреЗрдЯрд╛рдмреЗрд╕рдордзреВрди рдбреЗрдЯрд╛ рдЕрдирд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА SQL рдЕрднрд┐рд╡реНрдпрдХреНрддреА. рдпрд╛рд╡реНрдпрддрд┐рд░рд┐рдХреНрдд, рдЖрдореНрд╣реА рдЖрдд рдлрдВрдХреНрд╢рдирдЪреЗ рд╡реЗрдЧрд╡рд╛рди рдЕреЕрдирд╛рд▓реЙрдЧ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХреЗрд▓реЗ keras::to_categorical(). рдЖрдореНрд╣реА рдЬрд╡рд│рдЬрд╡рд│ рд╕рд░реНрд╡ рдбреЗрдЯрд╛ рдкреНрд░рд╢рд┐рдХреНрд╖рдгрд╛рд╕рд╛рдареА рд╡рд╛рдкрд░рд▓рд╛, рдкреНрд░рдорд╛рдгреАрдХрд░рдгрд╛рд╕рд╛рдареА рдЕрд░реНрдзрд╛ рдЯрдХреНрдХрд╛ рд╕реЛрдбрд▓рд╛, рдореНрд╣рдгреВрди рдпреБрдЧрд╛рдЪрд╛ рдЖрдХрд╛рд░ рдкреЕрд░рд╛рдореАрдЯрд░рджреНрд╡рд╛рд░реЗ рдорд░реНрдпрд╛рджрд┐рдд рд╣реЛрддрд╛ steps_per_epoch рдХреЙрд▓ рдХреЗрд▓реНрдпрд╛рд╡рд░ keras::fit_generator(), рдЖрдгрд┐ рд╕реНрдерд┐рддреА if (i > max_i) рдлрдХреНрдд рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдкреБрдирд░рд╛рд╡реГрддреНрддреАрд╕рд╛рдареА рдХрд╛рдо рдХреЗрд▓реЗ.

рдЕрдВрддрд░реНрдЧрдд рдХрд╛рд░реНрдпрд╛рдордзреНрдпреЗ, рдкреБрдвреАрд▓ рдмреЕрдЪрд╕рд╛рдареА рдкрдВрдХреНрддреА рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рдкреБрдирд░реНрдкреНрд░рд╛рдкреНрдд рдХреЗрд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд, рдмреЕрдЪ рдХрд╛рдЙрдВрдЯрд░ рд╡рд╛рдврд╡реВрди рдбреЗрдЯрд╛рдмреЗрд╕рдордзреВрди рд░реЗрдХреЙрд░реНрдб рдЕрдирд▓реЛрдб рдХреЗрд▓реЗ рдЬрд╛рддрд╛рдд, JSON рдкрд╛рд░реНрд╕рд┐рдВрдЧ (рдлрдВрдХреНрд╢рди cpp_process_json_vector(), C++ рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ рдЖрд╣реЗ рдЖрдгрд┐ рдЪрд┐рддреНрд░рд╛рдВрд╢реА рд╕рдВрдмрдВрдзрд┐рдд рдЕреЕрд░реЗ рддрдпрд╛рд░ рдХрд░рдгреЗ. рдирдВрддрд░ рдХреНрд▓рд╛рд╕ рд▓реЗрдмрд▓реНрд╕рд╕рд╣ рдПрдХ-рд╣реЙрдЯ рд╡реЗрдХреНрдЯрд░ рддрдпрд╛рд░ рдХреЗрд▓реЗ рдЬрд╛рддрд╛рдд, рдкрд┐рдХреНрд╕реЗрд▓ рдореВрд▓реНрдпрд╛рдВрд╕рд╣ рдЕреЕрд░реЗ рдЖрдгрд┐ рд▓реЗрдмрд▓реЗ рдПрдХрд╛ рд╕реВрдЪреАрдордзреНрдпреЗ рдПрдХрддреНрд░ рдХреЗрд▓реА рдЬрд╛рддрд╛рдд, рдЬреА рд░рд┐рдЯрд░реНрди рд╡реНрд╣реЕрд▓реНрдпреВ рдЖрд╣реЗ. рдХрд╛рдорд╛рдЪреА рдЧрддреА рд╡рд╛рдврд╡рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣реА рдЯреЗрдмрд▓рдордзреНрдпреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рддрдпрд╛рд░ рдХрд░рдгреЗ рд╡рд╛рдкрд░рд▓реЗ data.table рдЖрдгрд┐ рджреБрд╡реНрдпрд╛рджреНрд╡рд╛рд░реЗ рдмрджрд▓ - рдпрд╛ рдкреЕрдХреЗрдЬрд╢рд┐рд╡рд╛рдп "рдЪрд┐рдкреНрд╕" рдбреЗрдЯрд╛.рдЯреЗрдмрд▓ рдЖрд░ рдордзреАрд▓ рдХреЛрдгрддреНрдпрд╛рд╣реА рдорд╣рддреНрддреНрд╡рдкреВрд░реНрдг рдбреЗрдЯрд╛рд╕рд╣ рдкреНрд░рднрд╛рд╡реАрдкрдгреЗ рдХрд╛рд░реНрдп рдХрд░рдгреНрдпрд╛рдЪреА рдХрд▓реНрдкрдирд╛ рдХрд░рдгреЗ рдЦреВрдк рдХрдареАрдг рдЖрд╣реЗ.

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)

рдХреНрд╡рд┐рдХ рдбреНрд░реЙ рдбреВрдбрд▓ рдУрд│рдЦ: R, C++ рдЖрдгрд┐ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╢реА рдореИрддреНрд░реА рдХрд╢реА рдХрд░рд╛рд╡реА

рдЖрдкрд▓реНрдпрд╛рдХрдбреЗ рдкреБрд░реЗрд╢реА рд░реЕрдо рдЕрд╕рд▓реНрдпрд╛рд╕, рдЖрдкрдг рддреНрдпрд╛рдЪ рд░реЕрдордордзреНрдпреЗ (рдЖрдордЪреНрдпрд╛ рдХрд╛рд░реНрдпрд╛рд╕рд╛рдареА 32 рдЬреАрдмреА рдкреБрд░реЗрд╕реЗ рдЖрд╣реЗ) рд╣рд╕реНрддрд╛рдВрддрд░рд┐рдд рдХрд░реВрди рдбреЗрдЯрд╛рдмреЗрд╕рдЪреНрдпрд╛ рдСрдкрд░реЗрд╢рдирд▓рд╛ рдЧрдВрднреАрд░рдкрдгреЗ рдЧрддреА рджреЗрдК рд╢рдХрддрд╛. рд▓рд┐рдирдХреНрд╕рдордзреНрдпреЗ, рдбрд┐рдлреЙрд▓реНрдЯрдиреБрд╕рд╛рд░ рд╡рд┐рднрд╛рдЬрди рдорд╛рдЙрдВрдЯ рдХреЗрд▓реЗ рдЬрд╛рддреЗ /dev/shm, RAM рдХреНрд╖рдорддреЗрдЪреНрдпрд╛ рдЕрд░реНрдзреНрдпрд╛ рдкрд░реНрдпрдВрдд рд╡реНрдпрд╛рдкрдд рдЖрд╣реЗ. рддреБрдореНрд╣реА рд╕рдВрдкрд╛рджрди рдХрд░реВрди рдЕрдзрд┐рдХ рд╣рд╛рдпрд▓рд╛рдЗрдЯ рдХрд░реВ рд╢рдХрддрд╛ /etc/fstabрд╕рд╛рд░рдЦреЗ рд░реЗрдХреЙрд░реНрдб рдорд┐рд│рд╡рд┐рдгреНрдпрд╛рд╕рд╛рдареА tmpfs /dev/shm tmpfs defaults,size=25g 0 0. рд░реАрдмреВрдЯ рдХрд░рдгреНрдпрд╛рдЪреЗ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рд╛ рдЖрдгрд┐ рдХрдорд╛рдВрдб рдЪрд╛рд▓рд╡реВрди рдкрд░рд┐рдгрд╛рдо рддрдкрд╛рд╕рд╛ df -h.

рдЪрд╛рдЪрдгреА рдбреЗрдЯрд╛рд╕рд╛рдареА рдкреБрдирд░рд╛рд╡реГрддреНрддреА рдЕрдзрд┐рдХ рд╕реЛрдкреА рджрд┐рд╕рддреЗ, рдХрд╛рд░рдг рдЪрд╛рдЪрдгреА рдбреЗрдЯрд╛рд╕реЗрдЯ рдкреВрд░реНрдгрдкрдгреЗ RAM рдордзреНрдпреЗ рдмрд╕рддреЛ:

рдЪрд╛рдЪрдгреА рдбреЗрдЯрд╛рд╕рд╛рдареА рдЗрдЯрд░реЗрдЯрд░

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

  # ╨Я╤А╨╛╨▓╨╡╤А╨║╨░ ╨░╤А╨│╤Г╨╝╨╡╨╜╤В╨╛╨▓
  checkmate::assert_data_table(dt)
  checkmate::assert_count(batch_size)
  checkmate::assert_number(scale, lower = 0.001, upper = 5)
  checkmate::assert_flag(color)
  checkmate::assert_flag(imagenet_preproc)

  # ╨Я╤А╨╛╤Б╤В╨░╨▓╨╗╤П╨╡╨╝ ╨╜╨╛╨╝╨╡╤А╨░ ╨▒╨░╤В╤З╨╡╨╣
  dt[, batch := (.I - 1L) %/% batch_size + 1L]
  data.table::setkey(dt, batch)
  i <- 1
  max_i <- dt[, max(batch)]

  # ╨Ч╨░╨╝╤Л╨║╨░╨╜╨╕╨╡
  function() {
    batch_x <- cpp_process_json_vector(dt[batch == i, drawing], 
                                       scale = scale, color = color)
    if (imagenet_preproc) {
      # ╨и╨║╨░╨╗╨╕╤А╨╛╨▓╨░╨╜╨╕╨╡ c ╨╕╨╜╤В╨╡╤А╨▓╨░╨╗╨░ [0, 1] ╨╜╨░ ╨╕╨╜╤В╨╡╤А╨▓╨░╨╗ [-1, 1]
      batch_x <- (batch_x - 0.5) * 2
    }
    result <- list(batch_x)
    i <<- i + 1
    return(result)
  }
}

4. рдореЙрдбреЗрд▓ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░рдЪреА рдирд┐рд╡рдб

рд╡рд╛рдкрд░рд▓реЗрд▓реА рдкрд╣рд┐рд▓реА рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рд╣реЛрддреА рдореЛрдмрд╛рдИрд▓рдиреЗрдЯ v1, рдЬреНрдпрд╛рдЪреНрдпрд╛ рд╡реИрд╢рд┐рд╖реНрдЯреНрдпрд╛рдВрд╡рд░ рдЪрд░реНрдЪрд╛ рдХреЗрд▓реА рдЖрд╣реЗ рд╣реЗ рд╕рдВрджреЗрд╢ рд╣реЗ рдорд╛рдирдХ рдореНрд╣рдгреВрди рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдХреЗрд▓реЗ рдЖрд╣реЗ рдХреЗрд░рд╛рд╕ рдЖрдгрд┐, рддреНрдпрд╛рдиреБрд╕рд╛рд░, R рд╕рд╛рдареА рддреНрдпрд╛рдЪ рдирд╛рд╡рд╛рдЪреНрдпрд╛ рдкреЕрдХреЗрдЬрдордзреНрдпреЗ рдЙрдкрд▓рдмреНрдз рдЖрд╣реЗ. рдкрд░рдВрддреБ рдПрдХрд▓-рдЪреЕрдиреЗрд▓ рдкреНрд░рддрд┐рдорд╛рдВрд╕рд╣ рд╡рд╛рдкрд░рдгреНрдпрд╛рдЪрд╛ рдкреНрд░рдпрддреНрди рдХрд░рддрд╛рдирд╛, рдПрдХ рд╡рд┐рдЪрд┐рддреНрд░ рдЧреЛрд╖реНрдЯ рдмрд╛рд╣реЗрд░ рдЖрд▓реА: рдЗрдирдкреБрдЯ рдЯреЗрдиреНрд╕рд░рдордзреНрдпреЗ рдиреЗрд╣рдореА рдкрд░рд┐рдорд╛рдг рдЕрд╕рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ (batch, height, width, 3), рдореНрд╣рдгрдЬреЗ, рдЪреЕрдиреЗрд▓рдЪреА рд╕рдВрдЦреНрдпрд╛ рдмрджрд▓рд▓реА рдЬрд╛рдК рд╢рдХрдд рдирд╛рд╣реА. рдкрд╛рдпрдердирдордзреНрдпреЗ рдЕрд╢реА рдХреЛрдгрддреАрд╣реА рдорд░реНрдпрд╛рджрд╛ рдирд╛рд╣реА, рдореНрд╣рдгреВрди рдЖрдореНрд╣реА рдореВрд│ рд▓реЗрдЦрд╛рдЪреЗ рдЕрдиреБрд╕рд░рдг рдХрд░реВрди рдпрд╛ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░рдЪреА рд╕реНрд╡рддрдГрдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдХреЗрд▓реА рдЖрдгрд┐ рд▓рд┐рд╣рд┐рд▓реА (рдХреЗрд░рд╛ рдЖрд╡реГрддреНрддреАрдордзреНрдпреЗ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдбреНрд░реЙрдкрдЖрдЙрдЯрд╢рд┐рд╡рд╛рдп):

рдореЛрдмрд╛рдЗрд▓рдиреЗрдЯ v1 рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░

library(keras)

top_3_categorical_accuracy <- custom_metric(
    name = "top_3_categorical_accuracy",
    metric_fn = function(y_true, y_pred) {
         metric_top_k_categorical_accuracy(y_true, y_pred, k = 3)
    }
)

layer_sep_conv_bn <- function(object, 
                              filters,
                              alpha = 1,
                              depth_multiplier = 1,
                              strides = c(2, 2)) {

  # NB! depth_multiplier !=  resolution multiplier
  # https://github.com/keras-team/keras/issues/10349

  layer_depthwise_conv_2d(
    object = object,
    kernel_size = c(3, 3), 
    strides = strides,
    padding = "same",
    depth_multiplier = depth_multiplier
  ) %>%
  layer_batch_normalization() %>% 
  layer_activation_relu() %>%
  layer_conv_2d(
    filters = filters * alpha,
    kernel_size = c(1, 1), 
    strides = c(1, 1)
  ) %>%
  layer_batch_normalization() %>% 
  layer_activation_relu() 
}

get_mobilenet_v1 <- function(input_shape = c(224, 224, 1),
                             num_classes = 340,
                             alpha = 1,
                             depth_multiplier = 1,
                             optimizer = optimizer_adam(lr = 0.002),
                             loss = "categorical_crossentropy",
                             metrics = c("categorical_crossentropy",
                                         top_3_categorical_accuracy)) {

  inputs <- layer_input(shape = input_shape)

  outputs <- inputs %>%
    layer_conv_2d(filters = 32, kernel_size = c(3, 3), strides = c(2, 2), padding = "same") %>%
    layer_batch_normalization() %>% 
    layer_activation_relu() %>%
    layer_sep_conv_bn(filters = 64, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 128, strides = c(2, 2)) %>%
    layer_sep_conv_bn(filters = 128, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 256, strides = c(2, 2)) %>%
    layer_sep_conv_bn(filters = 256, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(2, 2)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 512, strides = c(1, 1)) %>%
    layer_sep_conv_bn(filters = 1024, strides = c(2, 2)) %>%
    layer_sep_conv_bn(filters = 1024, strides = c(1, 1)) %>%
    layer_global_average_pooling_2d() %>%
    layer_dense(units = num_classes) %>%
    layer_activation_softmax()

    model <- keras_model(
      inputs = inputs,
      outputs = outputs
    )

    model %>% compile(
      optimizer = optimizer,
      loss = loss,
      metrics = metrics
    )

    return(model)
}

рдпрд╛ рдкрджреНрдзрддреАрдЪреЗ рддреЛрдЯреЗ рд╕реНрдкрд╖реНрдЯ рдЖрд╣реЗрдд. рдорд▓рд╛ рдмрд░реНтАНрдпрд╛рдЪ рдореЙрдбреЗрд▓реНрд╕рдЪреА рдЪрд╛рдЪрдгреА рдШреНрдпрд╛рдпрдЪреА рдЖрд╣реЗ, рдкрд░рдВрддреБ рддреНрдпрд╛рдЙрд▓рдЯ, рдорд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рд╡реНрдпрдХреНрддрд┐рдЪрд▓рд┐рддрдкрдгреЗ рдкреБрдиреНрд╣рд╛ рд▓рд┐рд╣рд╛рдпрдЪреЗ рдирд╛рд╣реА. рдЗрдореЗрдЬрдиреЗрдЯрд╡рд░ рдкреНрд░реА-рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдореЙрдбреЗрд▓реНрд╕рдЪреЗ рд╡рдЬрди рд╡рд╛рдкрд░рдгреНрдпрд╛рдЪреНрдпрд╛ рд╕рдВрдзреАрдкрд╛рд╕реВрдирд╣реА рд╡рдВрдЪрд┐рдд рд░рд╛рд╣рд┐рд▓реЛ. рдиреЗрд╣рдореАрдкреНрд░рдорд╛рдгреЗ, рдХрд╛рдЧрджрдкрддреНрд░рд╛рдВрдЪрд╛ рдЕрднреНрдпрд╛рд╕ рдХреЗрд▓реНрдпрд╛рдиреЗ рдорджрдд рдЭрд╛рд▓реА. рдХрд╛рд░реНрдп get_config() рддреБрдореНрд╣рд╛рд▓рд╛ рдореЙрдбреЗрд▓рдЪреЗ рд╡рд░реНрдгрди рд╕рдВрдкрд╛рджрдирд╛рд╕рд╛рдареА рдпреЛрдЧреНрдп рдлреЙрд░реНрдордордзреНрдпреЗ рдорд┐рд│рд╡рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЗ (base_model_conf$layers - рдПрдХ рдирд┐рдпрдорд┐рдд рдЖрд░ рд╕реВрдЪреА), рдЖрдгрд┐ рдХрд╛рд░реНрдп from_config() рдореЙрдбреЗрд▓ рдСрдмреНрдЬреЗрдХреНрдЯрдордзреНрдпреЗ рдЙрд▓рдЯ рд░реВрдкрд╛рдВрддрд░рдг рдХрд░рддреЗ:

base_model_conf <- get_config(base_model)
base_model_conf$layers[[1]]$config$batch_input_shape[[4]] <- 1L
base_model <- from_config(base_model_conf)

рдЖрддрд╛ рдкреБрд░рд╡рд▓реЗрд▓реЗ рдХреЛрдгрддреЗрд╣реА рдкреНрд░рд╛рдкреНрдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╕рд╛рд░реНрд╡рддреНрд░рд┐рдХ рдХрд╛рд░реНрдп рд▓рд┐рд╣рд┐рдгреЗ рдХрдареАрдг рдирд╛рд╣реА рдХреЗрд░рд╛рд╕ рдЗрдореЗрдЬрдиреЗрдЯрд╡рд░ рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рд╡рдЬрдирд╛рд╕рд╣ рдХрд┐рдВрд╡рд╛ рддреНрдпрд╛рд╢рд┐рд╡рд╛рдп рдореЙрдбреЗрд▓:

рд░реЗрдбреАрдореЗрдб рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдХрд╛рд░реНрдп

get_model <- function(name = "mobilenet_v2",
                      input_shape = NULL,
                      weights = "imagenet",
                      pooling = "avg",
                      num_classes = NULL,
                      optimizer = keras::optimizer_adam(lr = 0.002),
                      loss = "categorical_crossentropy",
                      metrics = NULL,
                      color = TRUE,
                      compile = FALSE) {
  # ╨Я╤А╨╛╨▓╨╡╤А╨║╨░ ╨░╤А╨│╤Г╨╝╨╡╨╜╤В╨╛╨▓
  checkmate::assert_string(name)
  checkmate::assert_integerish(input_shape, lower = 1, upper = 256, len = 3)
  checkmate::assert_count(num_classes)
  checkmate::assert_flag(color)
  checkmate::assert_flag(compile)

  # ╨Я╨╛╨╗╤Г╤З╨░╨╡╨╝ ╨╛╨▒╤К╨╡╨║╤В ╨╕╨╖ ╨┐╨░╨║╨╡╤В╨░ keras
  model_fun <- get0(paste0("application_", name), envir = asNamespace("keras"))
  # ╨Я╤А╨╛╨▓╨╡╤А╨║╨░ ╨╜╨░╨╗╨╕╤З╨╕╤П ╨╛╨▒╤К╨╡╨║╤В╨░ ╨▓ ╨┐╨░╨║╨╡╤В╨╡
  if (is.null(model_fun)) {
    stop("Model ", shQuote(name), " not found.", call. = FALSE)
  }

  base_model <- model_fun(
    input_shape = input_shape,
    include_top = FALSE,
    weights = weights,
    pooling = pooling
  )

  # ╨Х╤Б╨╗╨╕ ╨╕╨╖╨╛╨▒╤А╨░╨╢╨╡╨╜╨╕╨╡ ╨╜╨╡ ╤Ж╨▓╨╡╤В╨╜╨╛╨╡, ╨╝╨╡╨╜╤П╨╡╨╝ ╤А╨░╨╖╨╝╨╡╤А╨╜╨╛╤Б╤В╤М ╨▓╤Е╨╛╨┤╨░
  if (!color) {
    base_model_conf <- keras::get_config(base_model)
    base_model_conf$layers[[1]]$config$batch_input_shape[[4]] <- 1L
    base_model <- keras::from_config(base_model_conf)
  }

  predictions <- keras::get_layer(base_model, "global_average_pooling2d_1")$output
  predictions <- keras::layer_dense(predictions, units = num_classes, activation = "softmax")
  model <- keras::keras_model(
    inputs = base_model$input,
    outputs = predictions
  )

  if (compile) {
    keras::compile(
      object = model,
      optimizer = optimizer,
      loss = loss,
      metrics = metrics
    )
  }

  return(model)
}

рдПрдХрд▓-рдЪреЕрдиреЗрд▓ рдкреНрд░рддрд┐рдорд╛ рд╡рд╛рдкрд░рддрд╛рдирд╛, рдХреЛрдгрддреЗрд╣реА рдкреВрд░реНрд╡рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рд╡рдЬрди рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рдд рдирд╛рд╣реА. рд╣реЗ рдирд┐рд╢реНрдЪрд┐рдд рдХреЗрд▓реЗ рдЬрд╛рдК рд╢рдХрддреЗ: рдлрдВрдХреНрд╢рди рд╡рд╛рдкрд░реВрди get_weights() R рдЕреЕрд░реЗрдЪреНрдпрд╛ рд╕реВрдЪреАрдЪреНрдпрд╛ рд░реВрдкрд╛рдд рдореЙрдбреЗрд▓рдЪреЗ рд╡рдЬрди рдорд┐рд│рд╡рд╛, рдпрд╛ рд╕реВрдЪреАрдЪреНрдпрд╛ рдкрд╣рд┐рд▓реНрдпрд╛ рдШрдЯрдХрд╛рдЪреЗ рдкрд░рд┐рдорд╛рдг рдмрджрд▓рд╛ (рдПрдХ рд░рдВрдЧ рдЪреЕрдиреЗрд▓ рдШреЗрдКрди рдХрд┐рдВрд╡рд╛ рддрд┐рдиреНрд╣реА рд╕рд░рд╛рд╕рд░реА рдШреЗрдКрди), рдЖрдгрд┐ рдирдВрддрд░ рдлрдВрдХреНрд╢рдирд╕рд╣ рд╡рдЬрди рдкрд░рдд рдореЙрдбреЗрд▓рдордзреНрдпреЗ рд▓реЛрдб рдХрд░рд╛. set_weights(). рдЖрдореНрд╣реА рд╣реА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрдзреАрд╣реА рдЬреЛрдбрд▓реА рдирд╛рд╣реА, рдХрд╛рд░рдг рдпрд╛ рдЯрдкреНрдкреНрдпрд╛рд╡рд░ рд╣реЗ рдЖрдзреАрдЪ рд╕реНрдкрд╖реНрдЯ рдЭрд╛рд▓реЗ рдЖрд╣реЗ рдХреА рд░рдВрдЧреАрдд рдЪрд┐рддреНрд░рд╛рдВрд╕рд╣ рдХрд╛рд░реНрдп рдХрд░рдгреЗ рдЕрдзрд┐рдХ рдЙрддреНрдкрд╛рджрдирдХреНрд╖рдо рдЖрд╣реЗ.

рдЖрдореНрд╣реА рдореЛрдмрд╛рдИрд▓рдиреЗрдЯ рдЖрд╡реГрддреНрддреНрдпрд╛ 1 рдЖрдгрд┐ 2, рддрд╕реЗрдЪ resnet34 рд╡рд╛рдкрд░реВрди рдмрд╣реБрддреЗрдХ рдкреНрд░рдпреЛрдЧ рдХреЗрд▓реЗ. SE-ResNeXt рд╕рд╛рд░рдЦреНрдпрд╛ рдЕрдзрд┐рдХ рдЖрдзреБрдирд┐рдХ рд╡рд╛рд╕реНрддреВрдВрдиреА рдпрд╛ рд╕реНрдкрд░реНрдзреЗрдд рдЪрд╛рдВрдЧрд▓реА рдХрд╛рдордЧрд┐рд░реА рдХреЗрд▓реА. рджреБрд░реНрджреИрд╡рд╛рдиреЗ, рдЖрдордЪреНрдпрд╛рдХрдбреЗ рдЖрдордЪреНрдпрд╛рдХрдбреЗ рддрдпрд╛рд░ рдХреЗрд▓реЗрд▓реА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдирд╡реНрд╣рддреА рдЖрдгрд┐ рдЖрдореНрд╣реА рд╕реНрд╡рддрдГрдЪреЗ рд▓рд┐рд╣рд┐рд▓реЗ рдирд╛рд╣реА (рдкрд░рдВрддреБ рдЖрдореНрд╣реА рдирд┐рд╢реНрдЪрд┐рддрдкрдгреЗ рд▓рд┐рд╣реВ).

5. рд╕реНрдХреНрд░рд┐рдкреНрдЯрдЪреЗ рдкреЕрд░рд╛рдореАрдЯрд░рд╛рдпрдЭреЗрд╢рди

рд╕реЛрдпреАрд╕рд╛рдареА, рдкреНрд░рд╢рд┐рдХреНрд╖рдг рд╕реБрд░реВ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╕рд░реНрд╡ рдХреЛрдб рдПрдХрдЪ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдореНрд╣рдгреВрди рдбрд┐рдЭрд╛рдЗрди рдХреЗрд▓реЗ рдЧреЗрд▓реЗ рд╣реЛрддреЗ, рд╡рд╛рдкрд░реВрди рдкреЕрд░рд╛рдореАрдЯрд░рд╛рдЗрдЬреНрдб docopt рдЦрд╛рд▓реАрд▓рдкреНрд░рдорд╛рдгреЗ:

doc <- '
Usage:
  train_nn.R --help
  train_nn.R --list-models
  train_nn.R [options]

Options:
  -h --help                   Show this message.
  -l --list-models            List available models.
  -m --model=<model>          Neural network model name [default: mobilenet_v2].
  -b --batch-size=<size>      Batch size [default: 32].
  -s --scale-factor=<ratio>   Scale factor [default: 0.5].
  -c --color                  Use color lines [default: FALSE].
  -d --db-dir=<path>          Path to database directory [default: Sys.getenv("db_dir")].
  -r --validate-ratio=<ratio> Validate sample ratio [default: 0.995].
  -n --n-gpu=<number>         Number of GPUs [default: 1].
'
args <- docopt::docopt(doc)

рдкреЕрдХреЗрдЬ docopt рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрдЪреЗ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рддреЗ http://docopt.org/ R рд╕рд╛рдареА. рддреНрдпрд╛рдЪреНрдпрд╛ рдорджрддреАрдиреЗ, рд╕реНрдХреНрд░рд┐рдкреНрдЯреНрд╕ рд╕реЛрдкреНрдпрд╛ рдХрдорд╛рдВрдбрд╕рд╣ рд▓реЙрдиреНрдЪ рдХреЗрд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд Rscript bin/train_nn.R -m resnet50 -c -d /home/andrey/doodle_db рдХрд┐рдВрд╡рд╛ ./bin/train_nn.R -m resnet50 -c -d /home/andrey/doodle_db, рдлрд╛рдЗрд▓ рдЕрд╕рд▓реНрдпрд╛рд╕ train_nn.R рдПрдХреНрдЭрд┐рдХреНрдпреБрдЯреЗрдмрд▓ рдЖрд╣реЗ (рд╣реА рдХрдорд╛рдВрдб рдореЙрдбреЗрд▓рдЪреЗ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рд╕реБрд░реВ рдХрд░реЗрд▓ resnet50 128x128 рдкрд┐рдХреНрд╕реЗрд▓реНрд╕рдЪреНрдпрд╛ рддреАрди-рд░рдВрдЧреА рдкреНрд░рддрд┐рдорд╛рдВрд╡рд░, рдбреЗрдЯрд╛рдмреЗрд╕ рдлреЛрд▓реНрдбрд░рдордзреНрдпреЗ рд╕реНрдерд┐рдд рдЕрд╕рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ /home/andrey/doodle_db). рддреБрдореНрд╣реА рд╕реВрдЪреАрдордзреНрдпреЗ рд╢рд┐рдХрдгреНрдпрд╛рдЪреА рдЧрддреА, рдСрдкреНрдЯрд┐рдорд╛рдпрдЭрд░ рдкреНрд░рдХрд╛рд░ рдЖрдгрд┐ рдЗрддрд░ рд╕рд╛рдиреБрдХреВрд▓ рдХрд░рдгреНрдпрд╛рдпреЛрдЧреНрдп рдкреЕрд░рд╛рдореАрдЯрд░реНрд╕ рдЬреЛрдбреВ рд╢рдХрддрд╛. рдкреНрд░рдХрд╛рд╢рди рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рдЪреНрдпрд╛ рдкреНрд░рдХреНрд░рд┐рдпреЗрдд, рдЕрд╕реЗ рджрд┐рд╕реВрди рдЖрд▓реЗ рдХреА рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ mobilenet_v2 рд╡рд░реНрддрдорд╛рди рдЖрд╡реГрддреНрддреА рдкрд╛рд╕реВрди рдХреЗрд░рд╛рд╕ рдЖрд░ рд╡рд╛рдкрд░рд╛рдд рдХрд░реВ рд╢рдХрдд рдирд╛рд╣реА рдЖрд░ рдкреЕрдХреЗрдЬрдордзреАрд▓ рдмрджрд▓ рд╡рд┐рдЪрд╛рд░рд╛рдд рди рдШреЗрддрд▓реНрдпрд╛рдореБрд│реЗ, рдЖрдореНрд╣реА рддреЗ рджреБрд░реБрд╕реНрдд рдХрд░рдгреНрдпрд╛рдЪреА рд╡рд╛рдЯ рдкрд╛рд╣рдд рдЖрд╣реЛрдд.

рдпрд╛ рджреГрд╖реНрдЯрд┐рдХреЛрдирд╛рдореБрд│реЗ RStudio рдордзреАрд▓ рд╕реНрдХреНрд░рд┐рдкреНрдЯреНрд╕рдЪреНрдпрд╛ рдЕрдзрд┐рдХ рдкрд╛рд░рдВрдкрд╛рд░рд┐рдХ рд▓рд╛рдБрдЪрдЪреНрдпрд╛ рддреБрд▓рдиреЗрдд рднрд┐рдиреНрди рдореЙрдбреЗрд▓реНрд╕рд╕рд╣ рдкреНрд░рдпреЛрдЧрд╛рдВрдирд╛ рд▓рдХреНрд╖рдгреАрдпрд░реАрддреНрдпрд╛ рдЧрддреА рджреЗрдгреЗ рд╢рдХреНрдп рдЭрд╛рд▓реЗ (рдЖрдореНрд╣реА рд╕рдВрднрд╛рд╡реНрдп рдкрд░реНрдпрд╛рдп рдореНрд╣рдгреВрди рдкреЕрдХреЗрдЬ рд▓рдХреНрд╖рд╛рдд рдШреЗрддреЛ. tfruns). рдкрд░рдВрддреБ рдореБрдЦреНрдп рдлрд╛рдпрджрд╛ рдореНрд╣рдгрдЬреЗ рдпрд╛рд╕рд╛рдареА рдЖрд░рд╕реНрдЯреБрдбрд┐рдУ рд╕реНрдерд╛рдкрд┐рдд рди рдХрд░рддрд╛, рдбреЙрдХрд░рдордзреНрдпреЗ рдХрд┐рдВрд╡рд╛ рдлрдХреНрдд рд╕рд░реНрд╡реНрд╣рд░рд╡рд░ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд▓реЙрдиреНрдЪ рдХрд░рдгреЗ рд╕рд╣рдЬрдкрдгреЗ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдгреНрдпрд╛рдЪреА рдХреНрд╖рдорддрд╛.

6. рд╕реНрдХреНрд░рд┐рдкреНрдЯрдЪреЗ рдбреЙрдХрд░рд╛рдпрдЭреЗрд╢рди

рдЯреАрдо рд╕рджрд╕реНрдпрд╛рдВрдордзреАрд▓ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдореЙрдбреЗрд▓реНрд╕рд╕рд╛рдареА рдЖрдгрд┐ рдХреНрд▓рд╛рдЙрдбрдордзреНрдпреЗ рдЬрд▓рдж рддреИрдирд╛рддреАрд╕рд╛рдареА рдЖрдореНрд╣реА рдкрд░реНрдпрд╛рд╡рд░рдгрд╛рдЪреА рдкреЛрд░реНрдЯреЗрдмрд┐рд▓рд┐рдЯреА рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдбреЙрдХрд░рдЪрд╛ рд╡рд╛рдкрд░ рдХреЗрд▓рд╛. рдЖрдкрдг рдпрд╛ рд╕рд╛рдзрдирд╛рд╢реА рдкрд░рд┐рдЪрд┐рдд рд╣реЛрдгреЗ рд╕реБрд░реВ рдХрд░реВ рд╢рдХрддрд╛, рдЬреЗ рдЖрд░ рдкреНрд░реЛрдЧреНрд░рд╛рдорд░рд╕рд╛рдареА рддреБрд▓рдиреЗрдиреЗ рдЕрд╕рд╛рдорд╛рдиреНрдп рдЖрд╣реЗ рд╣реЗ рдкреНрд░рдХрд╛рд╢рдирд╛рдВрдЪреА рдорд╛рд▓рд┐рдХрд╛ рдХрд┐рдВрд╡рд╛ рд╡реНрд╣рд┐рдбрд┐рдУ рдХреЛрд░реНрд╕.

рдбреЙрдХрд░ рддреБрдореНрд╣рд╛рд▓рд╛ рд╕реБрд░рд╡рд╛рддреАрдкрд╛рд╕реВрди рддреБрдордЪреНрдпрд╛ рд╕реНрд╡рддрдГрдЪреНрдпрд╛ рдкреНрд░рддрд┐рдорд╛ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рдЪреА рдЖрдгрд┐ рддреБрдордЪреНрдпрд╛ рд╕реНрд╡рддрдГрдЪреНрдпрд╛ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдзрд╛рд░ рдореНрд╣рдгреВрди рдЗрддрд░ рдкреНрд░рддрд┐рдорд╛ рд╡рд╛рдкрд░рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЛ. рдЙрдкрд▓рдмреНрдз рдкрд░реНрдпрд╛рдпрд╛рдВрдЪреЗ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░рддрд╛рдирд╛, рдЖрдореНрд╣реА рдпрд╛ рдирд┐рд╖реНрдХрд░реНрд╖рд╛рд╡рд░ рдкреЛрд╣реЛрдЪрд▓реЛ рдХреА NVIDIA, CUDA+cuDNN рдбреНрд░рд╛рдЗрд╡реНрд╣рд░реНрд╕ рдЖрдгрд┐ рдкрд╛рдпрдерди рд▓рд╛рдпрдмреНрд░рд░реА рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдгреЗ рд╣рд╛ рдкреНрд░рддрд┐рдореЗрдЪрд╛ рдПрдХ рдореЛрдард╛ рднрд╛рдЧ рдЖрд╣реЗ рдЖрдгрд┐ рдЖрдореНрд╣реА рдЕрдзрд┐рдХреГрдд рдкреНрд░рддрд┐рдорд╛ рдЖрдзрд╛рд░ рдореНрд╣рдгреВрди рдШреЗрдгреНрдпрд╛рдЪреЗ рдард░рд╡рд▓реЗ. tensorflow/tensorflow:1.12.0-gpu, рддреЗрдереЗ рдЖрд╡рд╢реНрдпрдХ R рдкреЕрдХреЗрдЬреЗрд╕ рдЬреЛрдбрдгреЗ.

рдЕрдВрддрд┐рдо рдбреЙрдХрд░ рдлрд╛рдЗрд▓ рдпрд╛рд╕рд╛рд░рдЦреА рджрд┐рд╕рд▓реА:

рдбреЙрдХрд░рдлрд╛рдЗрд▓

FROM tensorflow/tensorflow:1.12.0-gpu

MAINTAINER Artem Klevtsov <[email protected]>

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

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

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

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

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

WORKDIR /app

VOLUME /db
VOLUME /app

CMD bash

рд╕реЛрдпреАрд╕рд╛рдареА, рд╡рд╛рдкрд░рд▓реЗрд▓реЗ рдкреЕрдХреЗрдЬ рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓реНрд╕рдордзреНрдпреЗ рдареЗрд╡рд▓реЗ рд╣реЛрддреЗ; рдЕрд╕реЗрдВрдмреНрд▓реА рджрд░рдореНрдпрд╛рди рдореЛрдареНрдпрд╛ рдкреНрд░рдорд╛рдгрд╛рдд рд▓рд┐рдЦрд┐рдд рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрдВрдЯреЗрдирд░рдордзреНрдпреЗ рдХреЙрдкреА рдХреЗрд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд. рдЖрдореНрд╣реА рдХрдорд╛рдВрдб рд╢реЗрд▓ рджреЗрдЦреАрд▓ рдмрджрд▓рд▓реЗ /bin/bash рд╕рд╛рдордЧреНрд░реАрдЪрд╛ рд╡рд╛рдкрд░ рд╕реБрд▓рднрддреЗрд╕рд╛рдареА /etc/os-release. рдпрд╛рдореБрд│реЗ рдХреЛрдбрдордзреНрдпреЗ OS рдЖрд╡реГрддреНрддреА рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЯрд╛рд│рд▓реА.

рдпрд╛рд╡реНрдпрддрд┐рд░рд┐рдХреНрдд, рдПрдХ рд▓рд╣рд╛рди рдмреЕрд╢ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд▓рд┐рд╣рд┐рд▓реЗрд▓реА рдЖрд╣реЗ рдЬреА рддреБрдореНрд╣рд╛рд▓рд╛ рд╡рд┐рд╡рд┐рдз рдХрдорд╛рдВрдбрд╕рд╣ рдХрдВрдЯреЗрдирд░ рд▓реЙрдиреНрдЪ рдХрд░рдгреНрдпрд╛рд╕ рдЕрдиреБрдорддреА рджреЗрддреЗ. рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рдпрд╛ рдкреВрд░реНрд╡реА рдХрдВрдЯреЗрдирд░рдордзреНрдпреЗ рдареЗрд╡рд▓реЗрд▓реНрдпрд╛ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХреНрд╕рдЪреНрдпрд╛ рдкреНрд░рд╢рд┐рдХреНрд╖рдгрд╛рд╕рд╛рдареА рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЕрд╕реВ рд╢рдХрддрд╛рдд рдХрд┐рдВрд╡рд╛ рдХрдВрдЯреЗрдирд░рдЪреНрдпрд╛ рдСрдкрд░реЗрд╢рдирдЪреЗ рдбрд┐рдмрдЧрд┐рдВрдЧ рдЖрдгрд┐ рдирд┐рд░реАрдХреНрд╖рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдХрдорд╛рдВрдб рд╢реЗрд▓ рдЕрд╕реВ рд╢рдХрддрд╛рдд:

рдХрдВрдЯреЗрдирд░ рд▓рд╛рдБрдЪ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╕реНрдХреНрд░рд┐рдкреНрдЯ

#!/bin/sh

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

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

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

рдЬрд░ рд╣реА рдмреЕрд╢ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдкреЕрд░рд╛рдореАрдЯрд░реНрд╕рд╢рд┐рд╡рд╛рдп рдЪрд╛рд▓рд╡рд▓реА рдЕрд╕реЗрд▓, рддрд░ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХрдВрдЯреЗрдирд░рдЪреНрдпрд╛ рдЖрдд рдХреЙрд▓ рдХреЗрд▓реА рдЬрд╛рдИрд▓ train_nn.R рдбреАрдлреЙрд▓реНрдЯ рдореВрд▓реНрдпрд╛рдВрд╕рд╣; рдЬрд░ рдкрд╣рд┐рд▓рд╛ рдкреЛрдЭрд┐рд╢рдирд▓ рдЖрд░реНрдЧреНрдпреБрдореЗрдВрдЯ "рдмреЕрд╢" рдЕрд╕реЗрд▓, рддрд░ рдХрдВрдЯреЗрдирд░ рдХрдорд╛рдВрдб рд╢реЗрд▓рд╕рд╣ рд╕рдВрд╡рд╛рджрд╛рддреНрдордХрдкрдгреЗ рд╕реБрд░реВ рд╣реЛрдИрд▓. рдЗрддрд░ рд╕рд░реНрд╡ рдкреНрд░рдХрд░рдгрд╛рдВрдордзреНрдпреЗ, рд╕реНрдерд┐рддреАрддреНрдордХ рд╡рд┐рддрд░реНрдХрд╛рдВрдЪреА рдореВрд▓реНрдпреЗ рдмрджрд▓рд▓реА рдЬрд╛рддрд╛рдд: CMD="Rscript /app/train_nn.R $@".

рд╣реЗ рд▓рдХреНрд╖рд╛рдд рдШреЗрдгреНрдпрд╛рд╕рд╛рд░рдЦреЗ рдЖрд╣реЗ рдХреА рд╕реНрддреНрд░реЛрдд рдбреЗрдЯрд╛ рдЖрдгрд┐ рдбреЗрдЯрд╛рдмреЗрд╕рд╕рд╣ рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛, рддрд╕реЗрдЪ рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдореЙрдбреЗрд▓ рдЬрддрди рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛, рд╣реЛрд╕реНрдЯ рд╕рд┐рд╕реНрдЯрдордордзреВрди рдХрдВрдЯреЗрдирд░рдордзреНрдпреЗ рдЖрд░реЛрд╣рд┐рдд рдЖрд╣реЗрдд, рдЬреЗ рдЖрдкрд▓реНрдпрд╛рд▓рд╛ рдЕрдирд╛рд╡рд╢реНрдпрдХ рд╣рд╛рддрд╛рд│рдгреАрд╢рд┐рд╡рд╛рдп рд╕реНрдХреНрд░рд┐рдкреНрдЯрдЪреНрдпрд╛ рдкрд░рд┐рдгрд╛рдорд╛рдВрдордзреНрдпреЗ рдкреНрд░рд╡реЗрд╢ рдХрд░рдгреНрдпрд╛рд╕ рдЕрдиреБрдорддреА рджреЗрддреЗ.

7. Google Cloud рд╡рд░ рдПрдХрд╛рдзрд┐рдХ GPU рд╡рд╛рдкрд░рдгреЗ

рд╕реНрдкрд░реНрдзреЗрдЪреНрдпрд╛ рд╡реИрд╢рд┐рд╖реНрдЯреНрдпрд╛рдВрдкреИрдХреА рдПрдХ рдЕрддрд┐рд╢рдп рдЧреЛрдВрдЧрд╛рдЯ рдХрд░рдгрд╛рд░рд╛ рдбреЗрдЯрд╛ рд╣реЛрддрд╛ (рд╢реАрд░реНрд╖рдХ рдЪрд┐рддреНрд░ рдкрд╣рд╛, ODS рд╕реНрд▓реЕрдХ рд╡рд░реВрди @Leigh.plt рд╡рд░реВрди рдШреЗрддрд▓реЗрд▓реЗ). рдореЛрдареНрдпрд╛ рдмреЕрдЪреЗрд╕ рдпрд╛рдЪрд╛ рд╕рд╛рдордирд╛ рдХрд░рдгреНрдпрд╛рд╕ рдорджрдд рдХрд░рддрд╛рдд рдЖрдгрд┐ 1 GPU рд╕рд╣ PC рд╡рд░ рдкреНрд░рдпреЛрдЧ рдХреЗрд▓реНрдпрд╛рдирдВрддрд░, рдЖрдореНрд╣реА рдХреНрд▓рд╛рдЙрдбрдордзреАрд▓ рдЕрдиреЗрдХ GPU рд╡рд░ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдореЙрдбреЗрд▓реНрд╕рдордзреНрдпреЗ рдкреНрд░рднреБрддреНрд╡ рдорд┐рд│рд╡рдгреНрдпрд╛рдЪрд╛ рдирд┐рд░реНрдгрдп рдШреЗрддрд▓рд╛. рд╡рд╛рдкрд░рд▓реЗрд▓реЗ GoogleCloud (рдореВрд▓рднреВрдд рдЧреЛрд╖реНрдЯреАрдВрд╕рд╛рдареА рдЪрд╛рдВрдЧрд▓реЗ рдорд╛рд░реНрдЧрджрд░реНрд╢рдХ) рдЙрдкрд▓рдмреНрдз рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рдирдЪреНрдпрд╛ рдореЛрдареНрдпрд╛ рдирд┐рд╡рдбреАрдореБрд│реЗ, рд╡рд╛рдЬрд╡реА рдХрд┐рдорддреА рдЖрдгрд┐ $300 рдмреЛрдирд╕. рд▓реЛрднрд╛рдореБрд│реЗ, рдореА рдПрд╕рдПрд╕рдбреА рдЖрдгрд┐ рдПрдХ рдЯрди рд░реЕрдорд╕рд╣ 4xV100 рдЙрджрд╛рд╣рд░рдг рдСрд░реНрдбрд░ рдХреЗрд▓реЗ рдЖрдгрд┐ рд╣реА рдПрдХ рдореЛрдареА рдЪреВрдХ рд╣реЛрддреА. рдЕрд╢реА рдорд╢реАрди рддреНрд╡рд░реАрдд рдкреИрд╕реЗ рдЦрд╛рддреЗ; рдЖрдкрдг рд╕рд┐рджреНрдз рдкрд╛рдЗрдкрд▓рд╛рдЗрдирд╢рд┐рд╡рд╛рдп рдкреНрд░рдпреЛрдЧ рдХрд░реВрди рдЦрдВрдбрд┐рдд рд╣реЛрдК рд╢рдХрддрд╛. рд╢реИрдХреНрд╖рдгрд┐рдХ рд╣реЗрддреВрдВрд╕рд╛рдареА, K80 рдШреЗрдгреЗ рдЪрд╛рдВрдЧрд▓реЗ рдЖрд╣реЗ. рдкрд░рдВрддреБ рдореЛрдареНрдпрд╛ рдкреНрд░рдорд╛рдгрд╛рдд рд░реЕрдо рдЙрдкрдпреЛрдЧреА рдЖрд▓реА - рдХреНрд▓рд╛рдЙрдб рдПрд╕рдПрд╕рдбреА рддреНрдпрд╛рдЪреНрдпрд╛ рдХрд╛рд░реНрдпрдХреНрд╖рдорддреЗрдиреЗ рдкреНрд░рднрд╛рд╡рд┐рдд рдЭрд╛рд▓реЗ рдирд╛рд╣реА, рдореНрд╣рдгреВрди рдбреЗрдЯрд╛рдмреЗрд╕ рд╣рд╕реНрддрд╛рдВрддрд░рд┐рдд рдХреЗрд▓рд╛ рдЧреЗрд▓рд╛ dev/shm.

рдПрдХрд╛рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд GPU рд╡рд╛рдкрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЬрдмрд╛рдмрджрд╛рд░ рдЕрд╕рд▓реЗрд▓рд╛ рдХреЛрдб рдлреНрд░реЕрдЧрдореЗрдВрдЯ рд╣рд╛ рд╕рд░реНрд╡рд╛рдд рдЬрд╛рд╕реНрдд рд╕реНрд╡рд╛рд░рд╕реНрдп рдЖрд╣реЗ. рдкреНрд░рдердо, Python рдкреНрд░рдорд╛рдгреЗрдЪ, рд╕рдВрджрд░реНрдн рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рд╡рд╛рдкрд░реВрди 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 рд╕рд╛рдареА рд╕рдВрдкреВрд░реНрдг рдореЙрдбреЗрд▓ рдЕрдирдлреНрд░реАрдЭ рдХрд░рдгреЗ рдЖрдгрд┐ рдкреБрдиреНрд╣рд╛ рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдХрд░рдгреЗ рд╢рдХреНрдп рдЭрд╛рд▓реЗ рдирд╛рд╣реА.

рдкреНрд░рд╢рд┐рдХреНрд╖рдгрд╛рдЪрд╛ рд╡рд╛рдкрд░ рди рдХрд░рддрд╛ рдирд┐рд░реАрдХреНрд╖рдг рдХреЗрд▓реЗ рдЧреЗрд▓реЗ. рдЯреЗрдиреНрд╕рд░рдмреЛрд░реНрдб, рдиреЛрдВрджреА рд░реЗрдХреЙрд░реНрдб рдХрд░рдгреЗ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ рдпреБрдЧрд╛рдирдВрддрд░ рдорд╛рд╣рд┐рддреАрдкреВрд░реНрдг рдирд╛рд╡рд╛рдВрд╕рд╣ рдореЙрдбреЗрд▓ рдЬрддрди рдХрд░рдгреЗ рдпрд╛рд╕рд╛рдареА рд╕реНрд╡рддрдГрд▓рд╛ рдорд░реНрдпрд╛рджрд┐рдд рдХрд░рдгреЗ:

рдХреЙрд▓рдмреЕрдХ

# ╨и╨░╨▒╨╗╨╛╨╜ ╨╕╨╝╨╡╨╜╨╕ ╤Д╨░╨╣╨╗╨░ ╨╗╨╛╨│╨░
log_file_tmpl <- file.path("logs", sprintf(
  "%s_%d_%dch_%s.csv",
  model_name,
  dim_size,
  channels,
  format(Sys.time(), "%Y%m%d%H%M%OS")
))
# ╨и╨░╨▒╨╗╨╛╨╜ ╨╕╨╝╨╡╨╜╨╕ ╤Д╨░╨╣╨╗╨░ ╨╝╨╛╨┤╨╡╨╗╨╕
model_file_tmpl <- file.path("models", sprintf(
  "%s_%d_%dch_{epoch:02d}_{val_loss:.2f}.h5",
  model_name,
  dim_size,
  channels
))

callbacks_list <- list(
  keras::callback_csv_logger(
    filename = log_file_tmpl
  ),
  keras::callback_early_stopping(
    monitor = "val_loss",
    min_delta = 1e-4,
    patience = 8,
    verbose = 1,
    mode = "min"
  ),
  keras::callback_reduce_lr_on_plateau(
    monitor = "val_loss",
    factor = 0.5, # ╤Г╨╝╨╡╨╜╤М╤И╨░╨╡╨╝ lr ╨▓ 2 ╤А╨░╨╖╨░
    patience = 4,
    verbose = 1,
    min_delta = 1e-4,
    mode = "min"
  ),
  keras::callback_model_checkpoint(
    filepath = model_file_tmpl,
    monitor = "val_loss",
    save_best_only = FALSE,
    save_weights_only = FALSE,
    mode = "min"
  )
)

8. рдирд┐рд╖реНрдХрд░реНрд╖рд╛рдРрд╡рдЬреА

рдЖрдореНрд╣рд╛рд▓рд╛ рдЖрд▓реЗрд▓реНрдпрд╛ рдЕрдиреЗрдХ рд╕рдорд╕реНрдпрд╛рдВрд╡рд░ рдЕрджреНрдпрд╛рдк рдорд╛рдд рдЭрд╛рд▓реЗрд▓реА рдирд╛рд╣реА:

  • ╨▓ рдХреЗрд░рд╛рд╕ рдЗрд╖реНрдЯрддрдо рд╢рд┐рдХреНрд╖рдг рджрд░ (рдПрдирд╛рд▓реЙрдЧ lr_finder рд▓рд╛рдпрдмреНрд░рд░реАрдд рдлрд╛рд╕реНрдЯ.ai); рдХрд╛рд╣реА рдкреНрд░рдпрддреНрдирд╛рдВрдиреА, рддреГрддреАрдп-рдкрдХреНрд╖рд╛рдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА R рд╡рд░ рдкреЛрд░реНрдЯ рдХрд░рдгреЗ рд╢рдХреНрдп рдЖрд╣реЗ, рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рд╣реЗ;
  • рдорд╛рдЧреАрд▓ рдмрд┐рдВрджреВрдЪрд╛ рдкрд░рд┐рдгрд╛рдо рдореНрд╣рдгреВрди, рдЕрдиреЗрдХ GPUs рд╡рд╛рдкрд░рддрд╛рдирд╛ рдпреЛрдЧреНрдп рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдЧрддреА рдирд┐рд╡рдбрдгреЗ рд╢рдХреНрдп рдирд╡реНрд╣рддреЗ;
  • рдЖрдзреБрдирд┐рдХ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░рдЪрд╛ рдЕрднрд╛рд╡ рдЖрд╣реЗ, рд╡рд┐рд╢реЗрд╖рдд: рдЗрдореЗрдЬрдиреЗрдЯрд╡рд░ рдкреВрд░реНрд╡-рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд;
  • рдХреЛрдгрддреЗрд╣реА рдПрдХ рд╕рд╛рдпрдХрд▓ рдзреЛрд░рдг рдЖрдгрд┐ рднреЗрджрднрд╛рд╡рдкреВрд░реНрдг рд╢рд┐рдХреНрд╖рдг рджрд░ рдирд╛рд╣реА (рдХреЛрд╕рд╛рдЗрди рдЕреЕрдирд┐рд▓рд┐рдВрдЧ рдЖрдордЪреНрдпрд╛ рд╡рд┐рдирдВрддреАрдиреБрд╕рд╛рд░ рд╣реЛрддреЗ рд▓рд╛рдЧреВ рдХреЗрд▓реЗ, рдзрдиреНрдпрд╡рд╛рдж skeydan).

рдпрд╛ рд╕реНрдкрд░реНрдзреЗрддреВрди рдХреЛрдгрддреНрдпрд╛ рдЙрдкрдпреБрдХреНрдд рдЧреЛрд╖реНрдЯреА рд╢рд┐рдХрд╛рдпрд▓рд╛ рдорд┐рд│рд╛рд▓реНрдпрд╛:

  • рддреБрд▓рдиреЗрдиреЗ рдХрдореА-рдкреЙрд╡рд░ рд╣рд╛рд░реНрдбрд╡реЗрдЕрд░рд╡рд░, рддреБрдореНрд╣реА рд╡реЗрджрдирд╛ рди рдХрд░рддрд╛ рд╕рднреНрдп (рд░реЕрдордЪреНрдпрд╛ рдЖрдХрд╛рд░рд╛рдЪреНрдпрд╛ рдЕрдиреЗрдХ рдкрдЯ) рдбреЗрдЯрд╛рд╕рд╣ рдХрд╛рд░реНрдп рдХрд░реВ рд╢рдХрддрд╛. рдкреНрд▓рд╛рд╕реНрдЯрд┐рдХрдЪреА рдкрд┐рд╢рд╡реА рдбреЗрдЯрд╛.рдЯреЗрдмрд▓ рдЯреЗрдмрд▓реНрд╕рдЪреНрдпрд╛ рдЗрди-рдкреНрд▓реЗрд╕ рдлреЗрд░рдлрд╛рд░рдореБрд│реЗ рдореЗрдорд░реА рд╡рд╛рдЪрд╡рддреЗ, рдЬреЗ рддреНрдпрд╛рдВрдирд╛ рдХреЙрдкреА рдХрд░рдгреЗ рдЯрд╛рд│рддреЗ рдЖрдгрд┐ рдпреЛрдЧреНрдпрд░рд┐рддреНрдпрд╛ рд╡рд╛рдкрд░рд▓реНрдпрд╛рд╕, рддреНрдпрд╛рдЪреА рдХреНрд╖рдорддрд╛ рдЬрд╡рд│рдЬрд╡рд│ рдиреЗрд╣рдореАрдЪ рд╕реНрдХреНрд░рд┐рдкреНрдЯрд┐рдВрдЧ рднрд╛рд╖рд╛рдВрд╕рд╛рдареА рдЖрдореНрд╣рд╛рд▓рд╛ рдЬреНрдЮрд╛рдд рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рд╕рд░реНрд╡ рд╕рд╛рдзрдирд╛рдВрдордзреНрдпреЗ рд╕рд░реНрд╡рд╛рдзрд┐рдХ рдЧрддреА рджрд░реНрд╢рд╡рддреЗ. рдбреЗрдЯрд╛рдмреЗрд╕рдордзреНтАНрдпреЗ рдбреЗрдЯрд╛ рд╕реЗрд╡реНтАНрд╣ рдХреЗрд▓реНтАНрдпрд╛рдиреЗ рддреБрдореНтАНрд╣рд╛рд▓рд╛ рдмрд░реНтАНрдпрд╛рдЪ рдкреНрд░рдХрд░рдгрд╛рдВрдордзреНрдпреЗ рд╕рдВрдкреВрд░реНрдг рдбреЗрдЯрд╛рд╕реЗрдЯ рд░реЕрдордордзреНтАНрдпреЗ рдкрд┐рд│реВрди рдХрд╛рдврдгреНтАНрдпрд╛рдЪреА рдЧрд░рдЬ рдЖрд╣реЗ рдпрд╛рдЪрд╛ рдЕрдЬрд┐рдмрд╛рдд рд╡рд┐рдЪрд╛рд░ рдХрд░реВ рдирдХрд╛.
  • рдкреЕрдХреЗрдЬ рд╡рд╛рдкрд░реВрди R рдордзреАрд▓ рд╕реНрд▓реЛ рдлрдВрдХреНрд╢рдиреНрд╕ C++ рдордзреАрд▓ рд╡реЗрдЧрд╡рд╛рди рдлрдВрдХреНрд╢рдиреНрд╕рдиреЗ рдмрджрд▓рд▓реА рдЬрд╛рдК рд╢рдХрддрд╛рдд Rcpp. рд╡рд╛рдкрд░рд╛рд╡реНрдпрддрд┐рд░рд┐рдХреНрдд рдЕрд╕рд▓реНрдпрд╛рд╕ RcppThread рдХрд┐рдВрд╡рд╛ Rcpp рд╕рдорд╛рдВрддрд░, рдЖрдореНрд╣рд╛рд▓рд╛ рдХреНрд░реЙрд╕-рдкреНрд▓реЕрдЯрдлреЙрд░реНрдо рдорд▓реНрдЯреА-рдереНрд░реЗрдбреЗрдб рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдорд┐рд│рддреЗ, рддреНрдпрд╛рдореБрд│реЗ R рд╕реНрддрд░рд╛рд╡рд░ рдХреЛрдбрд▓рд╛ рд╕рдорд╛рдВрддрд░ рдХрд░рдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╛рд╣реА.
  • рдкреЕрдХреЗрдЬ Rcpp C++ рдЪреНрдпрд╛ рдЧрдВрднреАрд░ рдЬреНрдЮрд╛рдирд╛рд╢рд┐рд╡рд╛рдп рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рдК рд╢рдХрддреЗ, рдЖрд╡рд╢реНрдпрдХ рдХрд┐рдорд╛рди рдмрд╛рд╣реНрдпрд░реЗрдЦрд╛ рджрд┐рд▓реЗрд▓реА рдЖрд╣реЗ рдпреЗрдереЗ. рд╕рд╛рд░рдЦреНрдпрд╛ рдЕрдиреЗрдХ рдЫрд╛рди рд╕реА-рд▓рд╛рдпрдмреНрд░рд░реАрдВрд╕рд╛рдареА рд╣реЗрдбрд░ рдлрд╛рдЗрд▓реНрд╕ xtensor CRAN рд╡рд░ рдЙрдкрд▓рдмреНрдз рдЖрд╣реЗ, рдореНрд╣рдгрдЬреЗрдЪ рдкреНрд░рдХрд▓реНрдкрд╛рдВрдЪреНрдпрд╛ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрд╕рд╛рдареА рдПрдХ рдкрд╛рдпрд╛рднреВрдд рд╕реБрд╡рд┐рдзрд╛ рддрдпрд╛рд░ рдХреЗрд▓реА рдЬрд╛рдд рдЖрд╣реЗ рдЬреА рд░реЗрдбреАрдореЗрдб рдЙрдЪреНрдЪ-рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ C++ рдХреЛрдб R рдордзреНрдпреЗ рд╕рдорд╛рдХрд▓рд┐рдд рдХрд░рддреЗ. рдЕрддрд┐рд░рд┐рдХреНрдд рд╕реБрд╡рд┐рдзрд╛ рдореНрд╣рдгрдЬреЗ рд╕рд┐рдВрдЯреЕрдХреНрд╕ рд╣рд╛рдпрд▓рд╛рдЗрдЯрд┐рдВрдЧ рдЖрдгрд┐ RStudio рдордзреНрдпреЗ рд╕реНрдерд┐рд░ C++ рдХреЛрдб рд╡рд┐рд╢реНрд▓реЗрд╖рдХ.
  • docopt рддреБрдореНрд╣рд╛рд▓рд╛ рдкреЕрд░рд╛рдореАрдЯрд░реНрд╕рд╕рд╣ рд╕реНрд╡рдпрдВ-рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЪрд╛рд▓рд╡рд┐рдгреНрдпрд╛рд╕ рдЕрдиреБрдорддреА рджреЗрддреЗ. рд╣реЗ рд░рд┐рдореЛрдЯ рд╕рд░реНрд╡реНрд╣рд░рд╡рд░ рд╡рд╛рдкрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╕реЛрдпреАрдЪреЗ рдЖрд╣реЗ, рд╕рдорд╛рд╡реЗрд╢. рдбреЙрдХрд░ рдЕрдВрддрд░реНрдЧрдд. RStudio рдордзреНрдпреЗ, рдкреНрд░рд╢рд┐рдХреНрд╖рдг рддрдВрддреНрд░рд┐рдХрд╛ рдиреЗрдЯрд╡рд░реНрдХрд╕рд╣ рдЕрдиреЗрдХ рддрд╛рд╕ рдкреНрд░рдпреЛрдЧ рдХрд░рдгреЗ рдЧреИрд░рд╕реЛрдпреАрдЪреЗ рдЖрд╣реЗ рдЖрдгрд┐ рд╕рд░реНрд╡реНрд╣рд░рд╡рд░ IDE рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдгреЗ рдиреЗрд╣рдореАрдЪ рдиреНрдпрд╛рдпреНрдп рдирд╕рддреЗ.
  • рдбреЙрдХрд░ рдХреЛрдб рдкреЛрд░реНрдЯреЗрдмрд┐рд▓рд┐рдЯреА рдЖрдгрд┐ OS рдЖрдгрд┐ рд▓рд╛рдпрдмреНрд░рд░реАрдЪреНрдпрд╛ рд╡рд┐рд╡рд┐рдз рдЖрд╡реГрддреНрддреНрдпрд╛рдВрд╕рд╣ рд╡рд┐рдХрд╛рд╕рдХрд╛рдВрдордзреАрд▓ рдкрд░рд┐рдгрд╛рдорд╛рдВрдЪреА рдкреБрдирд░реБрддреНрдкрд╛рджрдХрддрд╛ рддрд╕реЗрдЪ рд╕рд░реНрд╡реНрд╣рд░рд╡рд░ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рд╕реБрд▓рднрддреЗрдЪреА рдЦрд╛рддреНрд░реА рджреЗрддреЗ. рддреБрдореНрд╣реА рдлрдХреНрдд рдПрдХрд╛ рдЖрджреЗрд╢рд╛рдиреЗ рд╕рдВрдкреВрд░реНрдг рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдкрд╛рдЗрдкрд▓рд╛рдЗрди рд╕реБрд░реВ рдХрд░реВ рд╢рдХрддрд╛.
  • рдорд╣рд╛рдЧрдбреНрдпрд╛ рд╣рд╛рд░реНрдбрд╡реЗрдЕрд░рд╡рд░ рдкреНрд░рдпреЛрдЧ рдХрд░рдгреНрдпрд╛рдЪрд╛ Google рдХреНрд▓рд╛рдЙрдб рд╣рд╛ рдмрдЬреЗрдЯ-рдЕрдиреБрдХреВрд▓ рдорд╛рд░реНрдЧ рдЖрд╣реЗ, рдкрд░рдВрддреБ рддреБрдореНрд╣рд╛рд▓рд╛ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рдХрд╛рд│рдЬреАрдкреВрд░реНрд╡рдХ рдирд┐рд╡рдбрдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗ.
  • рд╡реИрдпрдХреНрддрд┐рдХ рдХреЛрдбрдЪреНрдпрд╛ рддреБрдХрдбреНрдпрд╛рдВрдЪрд╛ рд╡реЗрдЧ рдореЛрдЬрдгреЗ рдЦреВрдк рдЙрдкрдпреБрдХреНрдд рдЖрд╣реЗ, рд╡рд┐рд╢реЗрд╖рдд: R рдЖрдгрд┐ C++ рдПрдХрддреНрд░ рдХрд░рддрд╛рдирд╛ рдЖрдгрд┐ рдкреЕрдХреЗрдЬрд╕рд╣ рдЦрдВрдбрдкреАрда - рджреЗрдЦреАрд▓ рдЦреВрдк рд╕реЛрдкреЗ.

рдПрдХреВрдгрдЪ рд╣рд╛ рдЕрдиреБрднрд╡ рдЦреВрдк рдлрд╛рдпрджреНрдпрд╛рдЪрд╛ рд╣реЛрддрд╛ рдЖрдгрд┐ рдЖрдореНрд╣реА рдЙрдкрд╕реНрдерд┐рдд рдХреЗрд▓реЗрд▓реНрдпрд╛ рдХрд╛рд╣реА рд╕рдорд╕реНрдпрд╛рдВрдЪреЗ рдирд┐рд░рд╛рдХрд░рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдХрд╛рд░реНрдп рдХрд░рдд рдЖрд╣реЛрдд.

рд╕реНрддреНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╛