рджреНрд░реБрдд рдбреНрд░ рдбреВрдбрд▓ рдкрд╣рд┐рдЪрд╛рди: рдХрд╕рд░реА R, C++ рд░ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╣рд░реВрд╕рдБрдЧ рд╕рд╛рдереА рдмрдирд╛рдЙрдиреЗ

рджреНрд░реБрдд рдбреНрд░ рдбреВрдбрд▓ рдкрд╣рд┐рдЪрд╛рди: рдХрд╕рд░реА R, C++ рд░ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╣рд░реВрд╕рдБрдЧ рд╕рд╛рдереА рдмрдирд╛рдЙрдиреЗ

рд╣реЗ рд╣рд╛рдмрд░!

рдкрдЫрд┐рд▓реНрд▓реЛ рдкрддрдирдорд╛, Kaggle рд▓реЗ рд╣рд╛рддрд▓реЗ рдХреЛрд░реЗрдХрд╛ рддрд╕реНрдмрд┐рд░рд╣рд░реВ, Quick Draw Doodle Recognition рдХреЛ рд╡рд░реНрдЧреАрдХрд░рдг рдЧрд░реНрди рдкреНрд░рддрд┐рдпреЛрдЧрд┐рддрд╛рдХреЛ рдЖрдпреЛрдЬрдирд╛ рдЧрд░реНрдпреЛ, рдЬрд╕рдорд╛, рдЕрд░реВрд╣рд░реВ рдордзреНрдпреЗ, R-рд╡реИрдЬреНрдЮрд╛рдирд┐рдХрд╣рд░реВрдХреЛ рдЯреЛрд▓реАрд▓реЗ рднрд╛рдЧ рд▓рд┐рдП: Artem Klevtsova, рдлрд┐рд▓рд┐рдкреНрдкрд╛ рдкреНрд░рдмрдиреНрдзрдХ ╨╕ Andrey Ogurtsovред рд╣рд╛рдореА рдкреНрд░рддрд┐рдпреЛрдЧрд┐рддрд╛рдХреЛ рд╡рд┐рд╕реНрддреГрдд рд╡рд░реНрдгрди рдЧрд░реНрджреИрдиреМрдВ; рддреНрдпреЛ рдкрд╣рд┐рд▓реЗ рдиреИ рдЧрд░рд┐рд╕рдХрд┐рдПрдХреЛ рдЫ рднрд░реНрдЦрд░рдХреЛ рдкреНрд░рдХрд╛рд╢рди.

рдпрд╕ рдкрдЯрдХ рдпреЛ рдкрджрдХ рдЦреЗрддреА рд╕рдВрдЧ рдХрд╛рдо рдЧрд░реЗрди, рддрд░ рдзреЗрд░реИ рдмрд╣реБрдореВрд▓реНрдп рдЕрдиреБрднрд╡ рдкреНрд░рд╛рдкреНрдд рднрдпреЛ, рддреНрдпрд╕реИрд▓реЗ рдо рд╕рдореБрджрд╛рдпрд▓рд╛рдИ рдХрд╛рдЧрд▓реЗ рд░ рджреИрдирд┐рдХ рдХрд╛рдордорд╛ рдзреЗрд░реИ рд░реЛрдЪрдХ рд░ рдЙрдкрдпреЛрдЧреА рдЪреАрдЬрд╣рд░реВрдХреЛ рдмрд╛рд░реЗрдорд╛ рдмрддрд╛рдЙрди рдЪрд╛рд╣рдиреНрдЫреБред рдЫрд▓рдлрд▓ рдЧрд░рд┐рдПрдХрд╛ рд╡рд┐рд╖рдпрд╣рд░реВ рдордзреНрдпреЗ: рдмрд┐рдирд╛ рдХрдард┐рди рдЬреАрд╡рди OpenCV, JSON рдкрд╛рд░реНрд╕рд┐рдЩ (рдпреА рдЙрджрд╛рд╣рд░рдгрд╣рд░реВрд▓реЗ R рдорд╛ рд▓рд┐рдкрд┐ рд╡рд╛ рдкреНрдпрд╛рдХреЗрдЬрд╣рд░реВрдорд╛ C++ рдХреЛрдбрдХреЛ рдПрдХреАрдХрд░рдг рдкрд░реАрдХреНрд╖рдг рдЧрд░реНрджрдЫ Rcpp), рд╕реНрдХреНрд░рд┐рдкреНрдЯрдХреЛ рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░рд╛рдЗрдЬреЗрд╢рди рд░ рдЕрдиреНрддрд┐рдо рд╕рдорд╛рдзрд╛рдирдХреЛ рдбрдХрд░рд╛рдЗрдЬреЗрд╢рдиред рд╕рдиреНрджреЗрд╢рдмрд╛рдЯ рд╕рдмреИ рдХреЛрдб рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрдирдХреЛ рд▓рд╛рдЧрд┐ рдЙрдкрдпреБрдХреНрдд рдлрд╛рд░рдордорд╛ рдЙрдкрд▓рдмреНрдз рдЫ рднрдгреНрдбрд╛рд░рд╣рд░реВ.

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

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

1. рдореЛрдиреЗрдЯрдбреАрдмреА рдбрд╛рдЯрд╛рдмреЗрд╕рдорд╛ CSV рдмрд╛рдЯ рдбрд╛рдЯрд╛ рдХреБрд╢рд▓рддрд╛рдкреВрд░реНрд╡рдХ рд▓реЛрдб рдЧрд░реНрдиреБрд╣реЛрд╕реН

рдпрд╕ рдкреНрд░рддрд┐рдпреЛрдЧрд┐рддрд╛рдорд╛ рдбреЗрдЯрд╛ рддрдпрд╛рд░ рдЫрд╡рд┐рд╣рд░реВрдХреЛ рд░реВрдкрдорд╛ рдкреНрд░рджрд╛рди рдЧрд░рд┐рдПрдХреЛ рдЫреИрди, рддрд░ 340 CSV рдлрд╛рдЗрд▓рд╣рд░реВрдХреЛ рд░реВрдкрдорд╛ (рдкреНрд░рддреНрдпреЗрдХ рдХрдХреНрд╖рд╛рдХреЛ рд▓рд╛рдЧрд┐ рдПрдЙрдЯрд╛ рдлрд╛рдЗрд▓) рдмрд┐рдиреНрджреБ рд╕рдордиреНрд╡рдпрд╣рд░реВ рд╕рд╣рд┐рдд JSON рд╣рд░реВред рдпреА рдмрд┐рдиреНрджреБрд╣рд░реВрд▓рд╛рдИ рд░реЗрдЦрд╛рд╣рд░реВрд╕рдБрдЧ рдЬреЛрдбреЗрд░, рд╣рд╛рдореАрд▓реЗ реирелремxреирелрем рдкрд┐рдХреНрд╕реЗрд▓ рдирд╛рдкреНрдиреЗ рдЕрдиреНрддрд┐рдо рдЫрд╡рд┐ рдкрд╛рдЙрдБрдЫреМрдБред рд╕рд╛рдереИ рдкреНрд░рддреНрдпреЗрдХ рд░реЗрдХрд░реНрдбрдХреЛ рд▓рд╛рдЧрд┐ рддреНрдпрд╣рд╛рдБ рдбреЗрдЯрд╛рд╕реЗрдЯ рд╕рдЩреНрдХрд▓рди рдЧрд░реНрджрд╛ рдкреНрд░рдпреЛрдЧ рдЧрд░рд┐рдПрдХреЛ рд╡рд░реНрдЧреАрдХрд░рдгрдХрд░реНрддрд╛рджреНрд╡рд╛рд░рд╛ рддрд╕реНрд╡реАрд░ рд╕рд╣реА рд░реВрдкрдорд╛ рдкрд╣рд┐рдЪрд╛рди рдЧрд░рд┐рдПрдХреЛ рдерд┐рдпреЛ рдХрд┐ рднрдиреЗрд░ рд╕рдВрдХреЗрдд рдЧрд░реНрдиреЗ рд▓реЗрдмрд▓ рд╣реБрдиреНрдЫ, рдЪрд┐рддреНрд░рдХреЛ рд▓реЗрдЦрдХрдХреЛ рдирд┐рд╡рд╛рд╕рдХреЛ рджреЗрд╢рдХреЛ рджреБрдИ-рдЕрдХреНрд╖рд░рдХреЛ рдХреЛрдб, рдПрдХ рдЕрджреНрд╡рд┐рддреАрдп рдкрд╣рд┐рдЪрд╛рдирдХрд░реНрддрд╛, рдЯрд╛рдЗрдорд╕реНрдЯреНрдпрд╛рдореНрдкред рд░ рдлрд╛рдЗрд▓ рдирд╛рдорд╕рдБрдЧ рдорд┐рд▓реНрдиреЗ рд╡рд░реНрдЧрдХреЛ рдирд╛рдоред рдореВрд▓ рдбрд╛рдЯрд╛рдХреЛ рд╕рд░рд▓ рд╕рдВрд╕реНрдХрд░рдгрдХреЛ рддреМрд▓ рдЕрднрд┐рд▓реЗрдЦрдорд╛ рен.рек рдЬреАрдмреА рд╣реБрдиреНрдЫ рд░ рдЕрдирдкреНрдпрд╛рдХ рдЧрд░рд┐рд╕рдХреЗрдкрдЫрд┐ рд▓рдЧрднрдЧ реиреж рдЬреАрдмреА рд╣реБрдиреНрдЫ, рдЕрдирдкреНрдпрд╛рдХ рдЧрд░реЗрдкрдЫрд┐ рдкреВрд░реНрдг рдбрд╛рдЯрд╛рд▓реЗ реирекреж рдЬреАрдмреА рд▓рд┐рдиреНрдЫред рдЖрдпреЛрдЬрдХрд╣рд░реВрд▓реЗ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдЧрд░реЗ рдХрд┐ рджреБрдмреИ рд╕рдВрд╕реНрдХрд░рдгрд╣рд░реВрд▓реЗ рдПрдЙрдЯреИ рд░реЗрдЦрд╛рдЪрд┐рддреНрд░рд╣рд░реВ рдкреБрди: рдЙрддреНрдкрд╛рджрди рдЧрд░реЗ, рдЬрд╕рдХреЛ рдЕрд░реНрде рдкреВрд░реНрдг рд╕рдВрд╕реНрдХрд░рдг рдЕрдирд╛рд╡рд╢реНрдпрдХ рдерд┐рдпреЛред рдЬреЗ рднрдП рдкрдирд┐, рдЧреНрд░рд╛рдлрд┐рдХ рдлрд╛рдЗрд▓рд╣рд░реВрдорд╛ рд╡рд╛ рдПрд░реЗрдХреЛ рд░реВрдкрдорд╛ 256 рдорд┐рд▓рд┐рдпрди рдЫрд╡рд┐рд╣рд░реВ рднрдгреНрдбрд╛рд░рдг рдЧрд░реНрдиреБрд▓рд╛рдИ рддреБрд░реБрдиреНрддреИ рд▓рд╛рднрджрд╛рдпреА рдорд╛рдирд┐рдиреНрдереНрдпреЛ, рд░ рд╣рд╛рдореАрд▓реЗ рдЕрднрд┐рд▓реЗрдЦрдмрд╛рдЯ рд╕рдмреИ CSV рдлрд╛рдЗрд▓рд╣рд░реВ рдорд░реНрдЬ рдЧрд░реНрдиреЗ рдирд┐рд░реНрдгрдп рдЧрд░реНрдпреМрдВред train_simplified.zip рдкреНрд░рддреНрдпреЗрдХ рдмреНрдпрд╛рдЪрдХреЛ рд▓рд╛рдЧрд┐ "рдЙрдбрд╛рдирдорд╛" рдЖрд╡рд╢реНрдпрдХ рдЖрдХрд╛рд░рдХреЛ рдЫрд╡рд┐рд╣рд░реВрдХреЛ рдкрдЫрд┐рд▓реНрд▓рд╛ рдкреБрд╕реНрддрд╛рдХреЛ рд╕рд╛рде рдбреЗрдЯрд╛рдмреЗрд╕рдорд╛ред

DBMS рдХреЛ рд░реВрдкрдорд╛ рдПрдХ рд░рд╛рдореНрд░реЛ рдкреНрд░рдорд╛рдгрд┐рдд рдкреНрд░рдгрд╛рд▓реА рдЫрдиреЛрдЯ рдЧрд░рд┐рдПрдХреЛ рдерд┐рдпреЛ MonetDB, рдЕрд░реНрдерд╛рддреН рдкреНрдпрд╛рдХреЗрдЬрдХреЛ рд░реВрдкрдорд╛ 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 in R рд▓реЗ рдЕрднрд┐рд▓реЗрдЦрдмрд╛рдЯ рдзреЗрд░реИ рдлрд╛рдЗрд▓рд╣рд░реВрд╕рдБрдЧ рд╕рд╣реА рд░реВрдкрдорд╛ рдХрд╛рдо рдЧрд░реНрджреИрди, рддреНрдпрд╕реИрд▓реЗ рд╣рд╛рдореАрд▓реЗ рдкреНрд░рдгрд╛рд▓реА рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдпреМрдВ unzip (рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░ рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ getOption("unzip")).

рдбрд╛рдЯрд╛рдмреЗрд╕рдорд╛ рд▓реЗрдЦреНрдирдХреЛ рд▓рд╛рдЧрд┐ рдХрд╛рд░реНрдп

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

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

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

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

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

  return(invisible(TRUE))
}

рдпрджрд┐ рддрдкрд╛рдЗрдБ рдпрд╕рд▓рд╛рдИ рдбреЗрдЯрд╛рдмреЗрд╕рдорд╛ рд▓реЗрдЦреНрдиреБ рдЕрдШрд┐ рддрд╛рд▓рд┐рдХрд╛ рдкрд░рд┐рд╡рд░реНрддрди рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫ рднрдиреЗ, рдпреЛ рддрд░реНрдХрдорд╛ рдкрд╛рд╕ рдЧрд░реНрди рдкрд░реНрдпрд╛рдкреНрдд рдЫред preprocess рдкреНрд░рдХрд╛рд░реНрдп рдЬрд╕рд▓реЗ рдбрд╛рдЯрд╛ рдкрд░рд┐рд╡рд░реНрддрди рдЧрд░реНрдиреЗрдЫред

рдбрд╛рдЯрд╛рдмреЗрд╕рдорд╛ рдХреНрд░рдорд┐рдХ рд░реВрдкрдорд╛ рдбрд╛рдЯрд╛ рд▓реЛрдб рдЧрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐ рдХреЛрдб:

рдбрд╛рдЯрд╛рдмреЗрд╕рдорд╛ рдбрд╛рдЯрд╛ рд▓реЗрдЦреНрджреИ

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

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

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

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

рдбрд╛рдЯрд╛ рд▓реЛрдбрд┐рдЩ рд╕рдордп рдкреНрд░рдпреЛрдЧ рдЧрд░рд┐рдПрдХреЛ рдбреНрд░рд╛рдЗрд╡ рдХреЛ рдЧрддрд┐ рд╡рд┐рд╢реЗрд╖рддрд╛рд╣рд░реБ рдорд╛ рдирд┐рд░реНрднрд░ рдЧрд░реНрджрдЫ рднрд┐рдиреНрди рд╣реБрди рд╕рдХреНрдЫред рд╣рд╛рдореНрд░реЛ рдЕрд╡рд╕реНрдерд╛рдорд╛, рдПрдЙрдЯрд╛ SSD рднрд┐рддреНрд░ рд╡рд╛ рдлреНрд▓реНрдпрд╛рд╕ рдбреНрд░рд╛рдЗрдн (рд╕реНрд░реЛрдд рдлрд╛рдЗрд▓) рдмрд╛рдЯ SSD (DB) рдорд╛ рдкрдвреНрди рд░ рд▓реЗрдЦреНрди резреж рдорд┐рдиреЗрдЯ рднрдиреНрджрд╛ рдХрдо рд╕рдордп рд▓рд╛рдЧреНрдЫред

рдкреВрд░реНрдгрд╛рдВрдХ рд╡рд░реНрдЧ рд▓реЗрдмрд▓ рд░ рдЕрдиреБрдХреНрд░рдордгрд┐рдХрд╛ рд╕реНрддрдореНрдн (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ред рдпрд╕рдХрд╛ рд▓рд╛рдЧрд┐ рд╣рд╛рдореАрд▓реЗ рей рд╡рдЯрд╛ рдпреБрдХреНрддрд┐рд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрдХрд╛ рдЫреМрдВред рдкрд╣рд┐рд▓реЛ рднрдиреЗрдХреЛ рдЕрд╡рд▓реЛрдХрди рдЖрдИрдбреА рднрдгреНрдбрд╛рд░рдг рдЧрд░реНрдиреЗ рдкреНрд░рдХрд╛рд░рдХреЛ рдЖрдпрд╛рдо рдШрдЯрд╛рдЙрдиреБ рдерд┐рдпреЛред рдореВрд▓ рдбреЗрдЯрд╛ рд╕реЗрдЯрдорд╛, ID рднрдгреНрдбрд╛рд░рдг рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдкреНрд░рдХрд╛рд░ рд╣реЛ bigint, рддрд░ рдЕрд╡рд▓реЛрдХрдирд╣рд░реВрдХреЛ рд╕рдВрдЦреНрдпрд╛рд▓реЗ рддрд┐рдиреАрд╣рд░реВрдХреЛ рдкрд╣рд┐рдЪрд╛рдирдХрд░реНрддрд╛рд╣рд░реВрд▓рд╛рдИ рдХреНрд░рдордмрджреНрдз рд╕рдВрдЦреНрдпрд╛рдХреЛ рдмрд░рд╛рдмрд░, рдкреНрд░рдХрд╛рд░рдорд╛ рдлрд┐рдЯ рдЧрд░реНрди рд╕рдореНрднрд╡ рдмрдирд╛рдЙрдБрдЫред intред рдпрд╕ рдорд╛рдорд▓рд╛ рдорд╛ рдЦреЛрдЬ рдзреЗрд░реИ рдЫрд┐рдЯреЛ рдЫред рджреЛрд╕реНрд░реЛ рдЪрд╛рд▓ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрди рдерд┐рдпреЛ ORDERED INDEX - рд╣рд╛рдореА рдпреЛ рдирд┐рд░реНрдгрдпрдорд╛ рдкреНрд░рд╛рдпреЛрдЧрд┐рдХ рд░реВрдкрдорд╛ рдЖрдПрдХрд╛ рдЫреМрдВ, рд╕рдмреИ рдЙрдкрд▓рдмреНрдз рдорд╛рд░реНрдлрдд рдЧрдПрд░ рд╡рд┐рдХрд▓реНрдкрд╣рд░реВред рддреЗрд╕реНрд░реЛ рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░рд╛рдЗрдЬреНрдб рдкреНрд░рд╢реНрдирд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрди рдерд┐рдпреЛред рд╡рд┐рдзрд┐рдХреЛ рд╕рд╛рд░ рднрдиреЗрдХреЛ рдЖрджреЗрд╢рд▓рд╛рдИ рдПрдХ рдкрдЯрдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЧрд░реНрдиреБ рд╣реЛ PREPARE рдПрдЙрдЯреИ рдкреНрд░рдХрд╛рд░рдХреЛ рдкреНрд░рд╢реНрдирд╣рд░реВрдХреЛ рдЧреБрдЪреНрдЫрд╛ рд╕рд┐рд░реНрдЬрдирд╛ рдЧрд░реНрджрд╛ рддрдпрд╛рд░ рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐рдХреЛ рдкрдЫрд┐рд▓реНрд▓рд╛ рдкреНрд░рдпреЛрдЧрдХреЛ рд╕рд╛рде, рддрд░ рд╡рд╛рд╕реНрддрд╡рдорд╛ рддреНрдпрд╣рд╛рдБ рдПрдХ рд╕рд╛рдзрд╛рд░рдгрд╕рдБрдЧ рддреБрд▓рдирд╛рдорд╛ рдлрд╛рдЗрджрд╛ рдЫред SELECT рд╕рд╛рдВрдЦреНрдпрд┐рдХреАрдп рддреНрд░реБрдЯрд┐рдХреЛ рджрд╛рдпрд░рд╛ рднрд┐рддреНрд░ рдмрд╛рд╣рд┐рд░ рдирд┐рд╕реНрдХрд┐рдпреЛред

рдбрд╛рдЯрд╛ рдЕрдкрд▓реЛрдб рдЧрд░реНрдиреЗ рдкреНрд░рдХреНрд░рд┐рдпрд╛рд▓реЗ RAM рдХреЛ 450 MB рднрдиреНрджрд╛ рдмрдвреА рдЦрдкрдд рдЧрд░реНрджреИрдиред рддреНрдпреЛ рд╣реЛ, рд╡рд░реНрдгрди рдЧрд░рд┐рдПрдХреЛ рджреГрд╖реНрдЯрд┐рдХреЛрдгрд▓реЗ рддрдкрд╛рдЗрдБрд▓рд╛рдИ рдХреЗрд╣рд┐ рдПрдХрд▓-рдмреЛрд░реНрдб рдЙрдкрдХрд░рдгрд╣рд░реВ рд╕рд╣рд┐рдд, рд▓рдЧрднрдЧ рдХреБрдиреИ рдкрдирд┐ рдмрдЬреЗрдЯ рд╣рд╛рд░реНрдбрд╡реЗрдпрд░рдорд╛ рджрд╢реМрдВ рдЧрд┐рдЧрд╛рдмрд╛рдЗрдЯ рддреМрд▓рдХреЛ рдбреЗрдЯрд╛рд╕реЗрдЯрд╣рд░реВ рд╕рд╛рд░реНрди рдЕрдиреБрдорддрд┐ рджрд┐рдиреНрдЫ, рдЬреБрди рдзреЗрд░реИ рд░рд╛рдореНрд░реЛ рдЫред

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

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

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. рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк рдЫрд╡рд┐рд╣рд░реВрд▓рд╛рдИ рдЯреЗрдиреНрд╕рд░рдорд╛ рд░реВрдкрд╛рдиреНрддрд░рдг рдЧрд░реНрджреИред

рдкрд╛рдЗрдерди рдХрд░реНрдиреЗрд▓рд╣рд░реВ рдмреАрдЪрдХреЛ рдкреНрд░рддрд┐рд╕реНрдкрд░реНрдзрд╛рдХреЛ рднрд╛рдЧрдХреЛ рд░реВрдкрдорд╛, рд╕рдорд╕реНрдпрд╛ рдореБрдЦреНрдп рд░реВрдкрдорд╛ рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рд╣рд▓ рдЧрд░рд┐рдПрдХреЛ рдерд┐рдпреЛ OpenCVред R рдорд╛ рд╕рдмреИ рднрдиреНрджрд╛ рд╕рд░рд▓ рд░ рд╕рдмреИрднрдиреНрджрд╛ рд╕реНрдкрд╖реНрдЯ analogues рдордзреНрдпреЗ рдПрдХ рдпрд╕реНрддреЛ рджреЗрдЦрд┐рдиреНрдЫ:

R рдорд╛ рдЯреЗрдиреНрд╕рд░ рд░реВрдкрд╛рдиреНрддрд░рдгрдорд╛ JSON рд▓рд╛рдИ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЧрд░реНрджреИ

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

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

рд░реЗрдЦрд╛рдЪрд┐рддреНрд░ рдорд╛рдирдХ R рдЙрдкрдХрд░рдгрд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рдкреНрд░рджрд░реНрд╢рди рдЧрд░рд┐рдиреНрдЫ рд░ RAM рдорд╛ рднрдгреНрдбрд╛рд░рдг рдЧрд░рд┐рдПрдХреЛ рдЕрд╕реНрдерд╛рдпреА PNG рдорд╛ рдмрдЪрдд рдЧрд░рд┐рдиреНрдЫ (рд▓рд┐рдирдХреНрд╕рдорд╛, рдЕрд╕реНрдерд╛рдпреА R рдбрд╛рдЗрд░реЗрдХреНрдЯрд░реАрд╣рд░реВ рдбрд╛рдЗрд░реЗрдХреНрдЯрд░реАрдорд╛ рдЕрд╡рд╕реНрдерд┐рдд рдЫрдиреНред /tmp, RAM рдорд╛ рдорд╛рдЙрдиреНрдЯ рдЧрд░рд┐рдПрдХреЛ)ред рдпрд╕ рдлрд╛рдЗрд▓рд▓рд╛рдИ реж рджреЗрдЦрд┐ рез рд╕рдореНрдордХрд╛ рд╕рдВрдЦреНрдпрд╛рд╣рд░реВ рд╕рд╣рд┐рддрдХреЛ рддреНрд░рд┐-рдЖрдпрд╛рдореА рдПрд░реЗрдХреЛ рд░реВрдкрдорд╛ рдкрдврд┐рдиреНрдЫред рдпреЛ рдорд╣рддреНрддреНрд╡рдкреВрд░реНрдг рдЫ рдХрд┐рдирднрдиреЗ рдЕрдзрд┐рдХ рдкрд░рдореНрдкрд░рд╛рдЧрдд 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

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

рд╕рдорд╕реНрдпрд╛ рд╕рдорд╛рдзрд╛рди рдЧрд░реНрди, рдирд┐рдореНрди рдкреНрдпрд╛рдХреЗрдЬ рд░ рдкреБрд╕реНрддрдХрд╛рд▓рдпрд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░рд┐рдпреЛ:

  1. OpenCV рдЫрд╡рд┐рд╣рд░реВ рд░ рд░реЗрдЦрд╛рдЪрд┐рддреНрд░ рд░реЗрдЦрд╛рд╣рд░реВрд╕рдБрдЧ рдХрд╛рдо рдЧрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐ред рдкреВрд░реНрд╡-рд╕реНрдерд╛рдкрд┐рдд рдкреНрд░рдгрд╛рд▓реА рдкреБрд╕реНрддрдХрд╛рд▓рдпрд╣рд░реВ рд░ рд╣реЗрдбрд░ рдлрд╛рдЗрд▓рд╣рд░реВ, рд╕рд╛рдереИ рдЧрддрд┐рд╢реАрд▓ рд▓рд┐рдЩреНрдХрд┐рдЩ рдкреНрд░рдпреЛрдЧ рдЧрд░рд┐рдпреЛред

  2. xtensor рдмрд╣реБрдЖрдпрд╛рдореА arrays рд░ tensors рд╕рдВрдЧ рдХрд╛рдо рдЧрд░реНрди рдХреЛ рд▓рд╛рдЧреАред рд╣рд╛рдореАрд▓реЗ рдПрдЙрдЯреИ рдирд╛рдордХреЛ рдЖрд░ рдкреНрдпрд╛рдХреЗрдЬрдорд╛ рд╕рдорд╛рд╡реЗрд╢ рд╣реЗрдбрд░ рдлрд╛рдЗрд▓рд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдпреМрдВред рдкреБрд╕реНрддрдХрд╛рд▓рдпрд▓реЗ рддрдкрд╛рдИрдВрд▓рд╛рдИ рдкрдЩреНрдХреНрддрд┐ рдкреНрд░рдореБрдЦ рд░ рд╕реНрддрдореНрдн рдкреНрд░рдореБрдЦ рдХреНрд░рдордорд╛, рдмрд╣реБрдЖрдпрд╛рдорд┐рдХ рдПрд░реЗрд╣рд░реВрд╕рдБрдЧ рдХрд╛рдо рдЧрд░реНрди рдЕрдиреБрдорддрд┐ рджрд┐рдиреНрдЫред

  3. ndjson JSON рдкрд╛рд░реНрд╕рд┐рдЩрдХреЛ рд▓рд╛рдЧрд┐ред рдпреЛ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдорд╛ рдкреНрд░рдпреЛрдЧ рдЧрд░рд┐рдиреНрдЫ xtensor рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдкрдорд╛ рдпрджрд┐ рдпреЛ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдорд╛ рдЙрдкрд╕реНрдерд┐рдд рдЫред

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

рдпреЛ рдЙрд▓реНрд▓реЗрдЦ рдЧрд░реНрди рд▓рд╛рдпрдХ рдЫ xtensor рдПрдХ рдЧреЙрдбрд╕реЗрдиреНрдб рд╣реБрди рдкреБрдЧреНрдпреЛ: рдпрд╕ рддрдереНрдпрдХреЛ рдЕрддрд┐рд░рд┐рдХреНрдд рдХрд┐ рдпрд╕рдорд╛ рд╡реНрдпрд╛рдкрдХ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рд░ рдЙрдЪреНрдЪ рдкреНрд░рджрд░реНрд╢рди рдЫ, рдпрд╕рдХреЛ рд╡рд┐рдХрд╛рд╕рдХрд░реНрддрд╛рд╣рд░реВ рдПрдХрджрдо рдЙрддреНрддрд░рджрд╛рдпреА рд░ рддреБрд░реБрдиреНрддреИ рд░ рд╡рд┐рд╕реНрддреГрдд рд░реВрдкрдорд╛ рдкреНрд░рд╢реНрдирд╣рд░реВрдХреЛ рдЬрд╡рд╛рдл рджрд┐рдПред рддрд┐рдиреАрд╣рд░реВрдХреЛ рдорджреНрджрддрд▓реЗ, рдУрдкрдирд╕реАрднреА рдореНрдпрд╛рдЯреНрд░рд┐рдХреНрд╕рд▓рд╛рдИ 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- рдХрдиреНрдлрд┐рдЧрд░реЗрд╕рди.

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 рдмрд╛рдЯ REPOSITORIAред рдХреЛрдб рдзреЗрд░реИ рдкреНрд░рдХрд╛рд░реНрдпрд╣рд░реВрдорд╛ рд╡рд┐рднрд╛рдЬрд┐рдд рдЫ:

  • to_xt - рдЫрд╡рд┐ рдореНрдпрд╛рдЯреНрд░рд┐рдХреНрд╕ рд░реВрдкрд╛рдиреНрддрд░рдгрдХреЛ рд▓рд╛рдЧрд┐ рдЯреЗрдореНрдкреНрд▓реЗрдЯ рдЧрд░рд┐рдПрдХреЛ рдкреНрд░рдХрд╛рд░реНрдп (cv::Mat) рдЯреЗрдиреНрд╕рд░рдорд╛ xt::xtensor;

  • parse_json - рдкреНрд░рдХрд╛рд░реНрдпрд▓реЗ JSON рд╕реНрдЯреНрд░рд┐рдЩрд▓рд╛рдИ рдкрд╛рд░реНрд╕ рдЧрд░реНрдЫ, рдмрд┐рдиреНрджреБрд╣рд░реВрдХреЛ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХрд╣рд░реВ рдирд┐рдХрд╛рд▓реНрдЫ, рддрд┐рдиреАрд╣рд░реВрд▓рд╛рдИ рднреЗрдХреНрдЯрд░рдорд╛ рдкреНрдпрд╛рдХ рдЧрд░реНрдЫ;

  • ocv_draw_lines - рдмрд┐рдиреНрджреБрд╣рд░реВрдХреЛ рдирддрд┐рдЬрд╛ рднреЗрдХреНрдЯрд░рдмрд╛рдЯ, рдмрд╣реБ-рд░рдВрдЧ рд░реЗрдЦрд╛рд╣рд░реВ рдХреЛрд░реНрдЫ;

  • process - рдорд╛рдерд┐рдХрд╛ рдкреНрд░рдХрд╛рд░реНрдпрд╣рд░реВ рд╕рдВрдпреЛрдЬрди рдЧрд░реНрджрдЫ рд░ рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк рдЫрд╡рд┐ рдорд╛рдкрди рдЧрд░реНрдиреЗ рдХреНрд╖рдорддрд╛ рдкрдирд┐ рдердкреНрдЫ;

  • cpp_process_json_str - рд╕рдорд╛рд░реЛрд╣ рдорд╛рдерд┐ рд░реНтАНрдпрд╛рдкрд░ process, рдЬрд╕рд▓реЗ рдкрд░рд┐рдгрд╛рдорд▓рд╛рдИ R-рд╡рд╕реНрддреБ (рдмрд╣реБ-рдЖрдпрд╛рдореА рдПрд░реЗ) рдорд╛ рдирд┐рд░реНрдпрд╛рдд рдЧрд░реНрджрдЫ;

  • cpp_process_json_vector - рд╕рдорд╛рд░реЛрд╣ рдорд╛рдерд┐ рд░реНтАНрдпрд╛рдкрд░ cpp_process_json_str, рдЬрд╕рд▓реЗ рддрдкрд╛рдИрдВрд▓рд╛рдИ рдмрд╣реБ-рдереНрд░реЗрдбреЗрдб рдореЛрдбрдорд╛ рд╕реНрдЯреНрд░рд┐рдЩ рднреЗрдХреНрдЯрд░ рдкреНрд░рд╢реЛрдзрди рдЧрд░реНрди рдЕрдиреБрдорддрд┐ рджрд┐рдиреНрдЫред

рдмрд╣реБ-рд░рдЩ рд░реЗрдЦрд╛рд╣рд░реВ рдХреЛрд░реНрдирдХреЛ рд▓рд╛рдЧрд┐, HSV рд░рдЩ рдореЛрдбреЗрд▓ рдкреНрд░рдпреЛрдЧ рдЧрд░рд┐рдПрдХреЛ рдерд┐рдпреЛ, рддреНрдпрд╕рдкрдЫрд┐ RGB рдорд╛ рд░реВрдкрд╛рдиреНрддрд░рдгред рдирддрд┐рдЬрд╛ рдкрд░реАрдХреНрд╖рдг рдЧрд░реМрдВ:

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

рджреНрд░реБрдд рдбреНрд░ рдбреВрдбрд▓ рдкрд╣рд┐рдЪрд╛рди: рдХрд╕рд░реА R, C++ рд░ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╣рд░реВрд╕рдБрдЧ рд╕рд╛рдереА рдмрдирд╛рдЙрдиреЗ
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. рдбрд╛рдЯрд╛рдмреЗрд╕рдмрд╛рдЯ рдмреНрдпрд╛рдЪрд╣рд░реВ рдЕрдирд▓реЛрдб рдЧрд░реНрдирдХрд╛ рд▓рд╛рдЧрд┐ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рд╣рд░реВ

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

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

рд╣рд╛рдореАрд▓реЗ MonetDBLite рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ RAM рдорд╛ рд╕рдмреИ рдбрд╛рдЯрд╛ рднрдгреНрдбрд╛рд░рдг рдЧрд░реНрдиреЗ рдЖрд╡рд╢реНрдпрдХрддрд╛рдмрд╛рдЯ рдЫреБрдЯрдХрд╛рд░рд╛ рдкрд╛рдпреМрдВ, рд╕рдмреИ "рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХ" рдХрд╛рд░реНрдпрд╣рд░реВ рдкрд╛рдЗрдердирдорд╛ рдореВрд▓ рдХреЛрдбрджреНрд╡рд╛рд░рд╛ рдкреНрд░рджрд░реНрд╢рди рдЧрд░рд┐рдиреЗрдЫ, рд╣рд╛рдореАрд▓реЗ рдбрд╛рдЯрд╛рдорд╛ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рд▓реЗрдЦреНрдиреБ рдкрд░реНрдЫ, рдХрд┐рдирдХрд┐ рддреНрдпрд╣рд╛рдБ рдХреЗрд╣рд┐ рддрдпрд╛рд░ рдЫреИрдиред R рд╡рд╛ рдкрд╛рдЗрдердирдорд╛ рдпрд╕реНрддреЛ рдЕрд╡рд╕реНрдерд╛рдХреЛ рд▓рд╛рдЧрд┐ред рддреНрдпрд╣рд╛рдБ рдЕрдирд┐рд╡рд╛рд░реНрдп рд░реВрдкрдорд╛ рдпрд╕рдХреЛ рд▓рд╛рдЧрд┐ рдХреЗрд╡рд▓ рджреБрдИ рдЖрд╡рд╢реНрдпрдХрддрд╛рд╣рд░реВ рдЫрдиреН: рдпрд╕рд▓реЗ рдЕрдирдиреНрдд рд▓реБрдкрдорд╛ рдмреНрдпрд╛рдЪрд╣рд░реВ рдлрд░реНрдХрд╛рдЙрдиреБ рдкрд░реНрдЫ рд░ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рд╣рд░реВ рдмреАрдЪрдХреЛ рд╕реНрдерд┐рддрд┐ рдмрдЪрдд рдЧрд░реНрдиреБрдкрд░реНрдЫ (R рдорд╛ рдкрдЫрд┐рд▓реНрд▓реЛ рдХреНрд▓реЛрдЬрд░рд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рд╕рд░рд▓ рддрд░рд┐рдХрд╛рдорд╛ рд▓рд╛рдЧреВ рдЧрд░рд┐рдПрдХреЛ рдЫ)ред рдкрд╣рд┐рд▓реЗ, рдпреЛ рд╕реНрдкрд╖реНрдЯ рд░реВрдкрдорд╛ рдЖрд░ рдПрд░реЗрд╣рд░реВрд▓рд╛рдИ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рднрд┐рддреНрд░ рдирдореНрдкреА рдПрд░реЗрд╣рд░реВрдорд╛ рд░реВрдкрд╛рдиреНрддрд░рдг рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдерд┐рдпреЛ, рддрд░ рдкреНрдпрд╛рдХреЗрдЬрдХреЛ рд╣рд╛рд▓рдХреЛ рд╕рдВрд╕реНрдХрд░рдг рдХреЗрд░рд╛рд╕ рдЖрдлреИ рдЧрд░реНрдЫред

рдкреНрд░рд╢рд┐рдХреНрд╖рдг рд░ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдбреЗрдЯрд╛рдХреЛ рд▓рд╛рдЧрд┐ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рднрдпреЛ:

рдкреНрд░рд╢рд┐рдХреНрд╖рдг рд░ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдбреЗрдЯрд╛рдХреЛ рд▓рд╛рдЧрд┐ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐

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

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

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

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

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

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

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

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

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

рдкреНрд░рдХрд╛рд░реНрдпрд▓реЗ рдбрд╛рдЯрд╛рдмреЗрд╕рд╕рдБрдЧ рдЬрдбрд╛рди рднрдПрдХреЛ рдЪрд░ рдЗрдирдкреБрдЯрдХреЛ рд░реВрдкрдорд╛ рд▓рд┐рдиреНрдЫ, рдкреНрд░рдпреЛрдЧ рдЧрд░рд┐рдПрдХрд╛ рд▓рд╛рдЗрдирд╣рд░реВрдХреЛ рд╕рдВрдЦреНрдпрд╛, рд╡рд░реНрдЧрд╣рд░реВрдХреЛ рд╕рдВрдЦреНрдпрд╛, рдмреНрдпрд╛рдЪ рд╕рд╛рдЗрдЬ, рд╕реНрдХреЗрд▓ (scale = 1 256x256 рдкрд┐рдХреНрд╕реЗрд▓рдХреЛ рдЫрд╡рд┐рд╣рд░реВ рд░реЗрдиреНрдбрд░рд┐рдЩрд╕рдБрдЧ рдореЗрд▓ рдЦрд╛рдиреНрдЫ, scale = 0.5 - 128x128 рдкрд┐рдХреНрд╕реЗрд▓), рд░рдВрдЧ рд╕реВрдЪрдХ (color = FALSE рдкреНрд░рдпреЛрдЧ рдЧрд░реНрджрд╛ рдЧреНрд░реЗрд╕реНрдХреЗрд▓рдорд╛ рд░реЗрдиреНрдбрд░рд┐рдЩ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдЧрд░реНрджрдЫ color = TRUE рдкреНрд░рддреНрдпреЗрдХ рд╕реНрдЯреНрд░реЛрдХ рдирдпрд╛рдБ рд░рдЩрдорд╛ рдХреЛрд░рд┐рдПрдХреЛ рдЫ) рд░ рдЗрдореЗрдЬрдиреЗрдЯрдорд╛ рдкреВрд░реНрд╡-рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдиреЗрдЯрд╡рд░реНрдХрд╣рд░реВрдХреЛ рд▓рд╛рдЧрд┐ рдПрдХ рдкреВрд░реНрд╡-рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕реВрдЪрдХред рдкрдЫрд┐рд▓реНрд▓реЛ рдЕрдиреНрддрд░рд╛рд▓ [0, 1] рдмрд╛рдЯ рдЕрдиреНрддрд░рд╛рд▓ [-1, 1] рдорд╛ рдкрд┐рдХреНрд╕реЗрд▓ рдорд╛рдирд╣рд░реВ рдорд╛рдкрди рдЧрд░реНрди рдЖрд╡рд╢реНрдпрдХ рдЫ, рдЬреБрди рдЖрдкреВрд░реНрддрд┐ рдЧрд░рд┐рдПрдХреЛ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдЧрд░реНрджрд╛ рдкреНрд░рдпреЛрдЧ рдЧрд░рд┐рдПрдХреЛ рдерд┐рдпреЛред рдХреЗрд░рд╛рд╕ рдореЛрдбреЗрд▓рд╣рд░реБред

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

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

рдХреЛрд░ i5 рд▓реНрдпрд╛рдкрдЯрдкрдорд╛ рдЧрддрд┐ рдорд╛рдкрдирдХреЛ рдирддрд┐рдЬрд╛ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рдЫрдиреН:

рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдмреЗрдиреНрдЪрдорд╛рд░реНрдХ

library(Rcpp)
library(keras)
library(ggplot2)

source("utils/rcpp.R")
source("utils/keras_iterator.R")

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

ind <- seq_len(DBI::dbGetQuery(con, "SELECT count(*) FROM doodles")[[1L]])
num_classes <- DBI::dbGetQuery(con, "SELECT max(label_int) + 1 FROM doodles")[[1L]]

# ╨Ш╨╜╨┤╨╡╨║╤Б╤Л ╨┤╨╗╤П ╨╛╨▒╤Г╤З╨░╤О╤Й╨╡╨╣ ╨▓╤Л╨▒╨╛╤А╨║╨╕
train_ind <- sample(ind, floor(length(ind) * 0.995))
# ╨Ш╨╜╨┤╨╡╨║╤Б╤Л ╨┤╨╗╤П ╨┐╤А╨╛╨▓╨╡╤А╨╛╤З╨╜╨╛╨╣ ╨▓╤Л╨▒╨╛╤А╨║╨╕
val_ind <- ind[-train_ind]
rm(ind)
# ╨Ъ╨╛╤Н╤Д╤Д╨╕╤Ж╨╕╨╡╨╜╤В ╨╝╨░╤Б╤И╤В╨░╨▒╨░
scale <- 0.5

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

#   batch_size      min   median      max `itr/sec` total_time n_itr
#        <dbl> <bch:tm> <bch:tm> <bch:tm>     <dbl>   <bch:tm> <int>
# 1         16     25ms  64.36ms   92.2ms     15.9       3.09s    49
# 2         32   48.4ms 118.13ms 197.24ms     8.17       5.88s    48
# 3         64   69.3ms 117.93ms 181.14ms     8.57       5.83s    50
# 4        128  157.2ms 240.74ms 503.87ms     3.85      12.71s    49
# 5        256  359.3ms 613.52ms 988.73ms     1.54       30.5s    47
# 6        512  884.7ms    1.53s    2.07s     0.674      1.11m    45
# 7       1024     2.7s    3.83s    5.47s     0.261      2.81m    44

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

DBI::dbDisconnect(con, shutdown = TRUE)

рджреНрд░реБрдд рдбреНрд░ рдбреВрдбрд▓ рдкрд╣рд┐рдЪрд╛рди: рдХрд╕рд░реА R, C++ рд░ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХрд╣рд░реВрд╕рдБрдЧ рд╕рд╛рдереА рдмрдирд╛рдЙрдиреЗ

рдпрджрд┐ рддрдкрд╛рдЗрдБрд╕рдБрдЧ рдкрд░реНрдпрд╛рдкреНрдд рдорд╛рддреНрд░рд╛рдорд╛ RAM рдЫ рднрдиреЗ, рддрдкрд╛рдЗрдБ рдЧрдореНрднреАрд░ рд░реВрдкрдорд╛ рдбрд╛рдЯрд╛рдмреЗрд╕рдХреЛ рд╕рдЮреНрдЪрд╛рд▓рдирд▓рд╛рдИ рд╕рдорд╛рди RAM рдорд╛ рд╕реНрдерд╛рдирд╛рдиреНрддрд░рдг рдЧрд░реЗрд░ рдЧрддрд┐ рдмрдврд╛рдЙрди рд╕рдХреНрдиреБрд╣реБрдиреНрдЫ (рд╣рд╛рдореНрд░реЛ рдХрд╛рд░реНрдпрдХреЛ рд▓рд╛рдЧрд┐ 32 GB рдкрд░реНрдпрд╛рдкреНрдд рдЫ)ред рд▓рд┐рдирдХреНрд╕рдорд╛, рд╡рд┐рднрд╛рдЬрди рдкреВрд░реНрд╡рдирд┐рд░реНрдзрд╛рд░рд┐рдд рд░реВрдкрдорд╛ рдорд╛рдЙрдиреНрдЯ рдЧрд░рд┐рдПрдХреЛ рдЫ /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. рдореЛрдбреЗрд▓ рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЛ рдЪрдпрди

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

Mobilenet v1 рд╡рд╛рд╕реНрддреБрдХрд▓рд╛

library(keras)

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

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

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

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

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

  inputs <- layer_input(shape = input_shape)

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

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

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

    return(model)
}

рдпреЛ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреЛ рдмреЗрдлрд╛рдЗрджрд╛ рд╕реНрдкрд╖реНрдЯ рдЫрдиреНред рдо рдзреЗрд░реИ рдореЛрдбреЗрд▓рд╣рд░реВ рдкрд░реАрдХреНрд╖рдг рдЧрд░реНрди рдЪрд╛рд╣рдиреНрдЫреБ, рддрд░ рдпрд╕рдХреЛ рд╡рд┐рдкрд░рд┐рдд, рдо рдкреНрд░рддреНрдпреЗрдХ рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдореНрдпрд╛рдиреБрдЕрд▓ рд░реВрдкрдорд╛ рдкреБрди: рд▓реЗрдЦреНрди рдЪрд╛рд╣рдиреНрдиред рд╣рд╛рдореА рдЗрдореЗрдЬрдиреЗрдЯрдорд╛ рдкреВрд░реНрд╡-рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдореЛрдбреЗрд▓рд╣рд░реВрдХреЛ рд╡рдЬрдирд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрдиреЗ рдЕрд╡рд╕рд░рдмрд╛рдЯ рдкрдирд┐ рд╡рдЮреНрдЪрд┐рдд рднрдпреМрдВред рд╕рд╛рдорд╛рдиреНрдп рд░реВрдкрдорд╛, рдХрд╛рдЧрдЬрд╛рдд рдЕрдзреНрдпрдпрди рдЧрд░реНрди рдорджреНрджрдд рдЧрд░реНрдпреЛред рд╕рдорд╛рд░реЛрд╣ get_config() рддрдкрд╛рдИрдВрд▓рд╛рдИ рд╕рдореНрдкрд╛рджрдирдХреЛ рд▓рд╛рдЧрд┐ рдЙрдкрдпреБрдХреНрдд рдлрд╛рд░рдордорд╛ рдореЛрдбреЗрд▓рдХреЛ рд╡рд┐рд╡рд░рдг рдкреНрд░рд╛рдкреНрдд рдЧрд░реНрди рдЕрдиреБрдорддрд┐ рджрд┐рдиреНрдЫ (base_model_conf$layers - рдирд┐рдпрдорд┐рдд рдЖрд░ рд╕реВрдЪреА), рд░ рдкреНрд░рдХрд╛рд░реНрдп 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 arrays рдХреЛ рд╕реВрдЪреАрдХреЛ рд░реВрдкрдорд╛ рдореЛрдбреЗрд▓ рд╡рдЬрдирд╣рд░реВ рдкреНрд░рд╛рдкреНрдд рдЧрд░реНрдиреБрд╣реЛрд╕реН, рдпреЛ рд╕реВрдЪреАрдХреЛ рдкрд╣рд┐рд▓реЛ рддрддреНрд╡рдХреЛ рдЖрдпрд╛рдо рдкрд░рд┐рд╡рд░реНрддрди рдЧрд░реНрдиреБрд╣реЛрд╕реН (рдПрдЙрдЯрд╛ рд░рдЩ рдЪреНрдпрд╛рдирд▓ рд▓рд┐рдПрд░ рд╡рд╛ рддреАрдирд╡рдЯреИ рдФрд╕рдд рдЧрд░реЗрд░), рд░ рддреНрдпрд╕рдкрдЫрд┐ рдкреНрд░рдХрд╛рд░реНрдпрдХреЛ рд╕рд╛рде рдореЛрдбреЗрд▓рдорд╛ рддреМрд▓рд╣рд░реВ рд▓реЛрдб рдЧрд░реНрдиреБрд╣реЛрд╕реНред set_weights()ред рд╣рд╛рдореАрд▓реЗ рдпреЛ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрд╣рд┐рд▓реНрдпреИ рдердкреЗрдХрд╛ рдЫреИрдиреМрдВ, рдХрд┐рдирднрдиреЗ рдпреЛ рдЪрд░рдгрдорд╛ рдпреЛ рдкрд╣рд┐рд▓реЗ рдиреИ рд╕реНрдкрд╖реНрдЯ рдерд┐рдпреЛ рдХрд┐ рдпреЛ рд░рдВрдЧреАрди рдЪрд┐рддреНрд░рд╣рд░реВрд╕рдБрдЧ рдХрд╛рдо рдЧрд░реНрди рдмрдвреА рдЙрддреНрдкрд╛рджрдХ рдерд┐рдпреЛред

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

5. рд▓рд┐рдкрд┐рд╣рд░реВрдХреЛ рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░рд╛рдЗрдЬреЗрд╢рди

рд╕реБрд╡рд┐рдзрд╛рдХреЛ рд▓рд╛рдЧрд┐, рдкреНрд░рд╢рд┐рдХреНрд╖рдг рд╕реБрд░реБ рдЧрд░реНрдирдХрд╛ рд▓рд╛рдЧрд┐ рд╕рдмреИ рдХреЛрдбрд╣рд░реВ рдПрдХрд▓ рд▓рд┐рдкрд┐рдХреЛ рд░реВрдкрдорд╛ рдбрд┐рдЬрд╛рдЗрди рдЧрд░рд┐рдПрдХреЛ рдерд┐рдпреЛ, рдкреНрд░рдпреЛрдЧ рдЧрд░реЗрд░ рдкреНрдпрд╛рд░рд╛рдорд┐рдЯрд░рд╛рдЗрдЬреНрдб рдХрд╛рдЧрдЬрд╛рдд рдирд┐рдореНрди рдЕрдиреБрд╕рд╛рд░:

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

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

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

рдпреЛ рджреГрд╖реНрдЯрд┐рдХреЛрдгрд▓реЗ RStudio рдорд╛ рд╕реНрдХреНрд░рд┐рдкреНрдЯрд╣рд░реВрдХреЛ рдЕрдзрд┐рдХ рдкрд░рдореНрдкрд░рд╛рдЧрдд рд╕реБрд░реБрд╡рд╛рддрдХреЛ рддреБрд▓рдирд╛рдорд╛ рд╡рд┐рднрд┐рдиреНрди рдореЛрдбреЗрд▓рд╣рд░реВрд╕рдБрдЧ рдкреНрд░рдпреЛрдЧрд╣рд░реВрд▓рд╛рдИ рдЙрд▓реНрд▓реЗрдЦрдиреАрдп рд░реВрдкрдорд╛ рдЧрддрд┐ рджрд┐рди рд╕рдореНрднрд╡ рдмрдирд╛рдпреЛ (рд╣рд╛рдореА рдкреНрдпрд╛рдХреЗрдЬрд▓рд╛рдИ рд╕рдореНрднрд╛рд╡рд┐рдд рд╡рд┐рдХрд▓реНрдкрдХреЛ рд░реВрдкрдорд╛ рдиреЛрдЯ рдЧрд░реНрдЫреМрдВред tfruns)ред рддрд░ рдореБрдЦреНрдп рдлрд╛рдЗрджрд╛ рднрдиреЗрдХреЛ рдпрд╕рдХрд╛ рд▓рд╛рдЧрд┐ RStudio рд╕реНрдерд╛рдкрдирд╛ рдирдЧрд░реА рд╕рдЬрд┐рд▓реИрд╕рдБрдЧ рдбрдХрд░рдорд╛ рд╡рд╛ рд╕рд░реНрднрд░рдорд╛ рд╕реНрдХреНрд░рд┐рдкреНрдЯрд╣рд░реВ рдкреНрд░рдХреНрд╖реЗрдкрдг рдЧрд░реНрдиреЗ рдХреНрд╖рдорддрд╛ рд╣реЛред

6. рд▓рд┐рдкрд┐рд╣рд░реВрдХреЛ рдбрдХрд░реАрдХрд░рдг

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

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

рдЕрдиреНрддрд┐рдо рдбрдХрд░ рдлрд╛рдЗрд▓ рдпрд╕реНрддреЛ рджреЗрдЦрд┐рдиреНрдереНрдпреЛ:

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

FROM tensorflow/tensorflow:1.12.0-gpu

MAINTAINER Artem Klevtsov <[email protected]>

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

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

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

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

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

WORKDIR /app

VOLUME /db
VOLUME /app

CMD bash

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

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

рдХрдиреНрдЯреЗрдирд░ рд╕реБрд░реБ рдЧрд░реНрди рд▓рд┐рдкрд┐

#!/bin/sh

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

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

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

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

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

7. Google Cloud рдорд╛ рдзреЗрд░реИ GPU рд╣рд░реВ рдкреНрд░рдпреЛрдЧ рдЧрд░реНрджреИ

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

рд╕рдмреИрднрдиреНрджрд╛ рдареВрд▓реЛ рдЪрд╛рд╕реЛ рдзреЗрд░реИ GPUs рдкреНрд░рдпреЛрдЧ рдЧрд░реНрди рдЬрд┐рдореНрдореЗрд╡рд╛рд░ рдХреЛрдб рдЯреБрдХреНрд░рд╛ рд╣реЛред рдкрд╣рд┐рд▓реЗ, рдореЛрдбреЗрд▓ 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
  )
})

рддреНрдпрд╕рдкрдЫрд┐ uncompiled (рдпреЛ рдорд╣рддреНрддреНрд╡рдкреВрд░реНрдг рдЫ) рдореЛрдбреЗрд▓ рдЙрдкрд▓рдмреНрдз 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 рдкреБрд╕реНрддрдХрд╛рд▓рдп рдорд╛ рдЫрд┐рдЯреЛредрдЖрдИ); рдХреЗрд╣рд┐ рдкреНрд░рдпрд╛рд╕ рд╕рдВрдЧ, рдпреЛ R рдорд╛ рддреЗрд╕реНрд░реЛ-рдкрдХреНрд╖ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдкреЛрд░реНрдЯ рдЧрд░реНрди рд╕рдореНрднрд╡ рдЫ, рдЙрджрд╛рд╣рд░рдг рдХреЛ рд▓рд╛рдЧреА, рдпреЛ;
  • рдЕрдШрд┐рд▓реНрд▓реЛ рдмрд┐рдиреНрджреБрдХреЛ рдкрд░рд┐рдгрд╛рдордХреЛ рд░реВрдкрдорд╛, рдзреЗрд░реИ GPUs рдкреНрд░рдпреЛрдЧ рдЧрд░реНрджрд╛ рд╕рд╣реА рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдЧрддрд┐ рдЪрдпрди рдЧрд░реНрди рд╕рдореНрднрд╡ рдерд┐рдПрди;
  • рддреНрдпрд╣рд╛рдБ рдЖрдзреБрдирд┐рдХ рдиреНрдпреВрд░рд▓ рдиреЗрдЯрд╡рд░реНрдХ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░рдХреЛ рдЕрднрд╛рд╡ рдЫ, рд╡рд┐рд╢реЗрд╖ рдЧрд░реА рдЗрдореЗрдЬрдиреЗрдЯрдорд╛ рдкреВрд░реНрд╡ рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рддрд╣рд░реВ;
  • рдХреБрдиреИ рдПрдХ рдЪрдХреНрд░ рдиреАрддрд┐ рд░ рднреЗрджрднрд╛рд╡рдкреВрд░реНрдг рд╢рд┐рдХреНрд╖рд╛ рджрд░рд╣рд░реВ (рдХреЛрд╕рд╛рдЗрди рдПрдирд┐рд▓рд┐рдЩ рд╣рд╛рдореНрд░реЛ рдЕрдиреБрд░реЛрдзрдорд╛ рдерд┐рдпреЛред рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЧрд░рд┐рдпреЛ, рдзрдиреНрдпрд╡рд╛рдж skeydan).

рдпрд╕ рдкреНрд░рддрд┐рдпреЛрдЧрд┐рддрд╛рдмрд╛рдЯ рдХреЗ рдХреЗ рдЙрдкрдпреЛрдЧреА рдХреБрд░рд╛рд╣рд░реВ рд╕рд┐рдХрд┐рдпреЛ:

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

рд╕рдордЧреНрд░рдорд╛ рдпреЛ рдЕрдиреБрднрд╡ рдзреЗрд░реИ рдЗрдирд╛рдорджрд╛рдпреА рдерд┐рдпреЛ рд░ рд╣рд╛рдореАрд▓реЗ рдЙрдард╛рдПрдХрд╛ рдХреЗрд╣реА рд╕рдорд╕реНрдпрд╛рд╣рд░реВ рд╕рдорд╛рдзрд╛рди рдЧрд░реНрди рдХрд╛рдо рдЬрд╛рд░реА рд░рд╛рдЦреНрдЫреМрдВред

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

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдердкреНрди