πŸ“– πŸ‘†πŸ» Π”Π΅Π»Π°Π΅ΠΌ ΠΏΠ΅Ρ‡Π°Ρ‚Π½Ρ‹Π΅ ссылки ΠΊΠ»ΠΈΠΊΠ°Π±Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ ΡΒ ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ TensorFlow 2Β Object Detection API

 ΠŸΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ пост
2 дСкабря 2020  1211

Links Detector Cover
Links Detector Cover

πŸ“ƒ TL;DR

Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ Π½Π°Ρ‡Π½Π΅ΠΌ Ρ€Π΅ΡˆΠ°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΏΠ΅Ρ‡Π°Ρ‚Π½Ρ‹Π΅ ссылки Π² ΠΊΠ½ΠΈΠ³Π°Ρ… ΠΈΠ»ΠΈ ΠΆΡƒΡ€Π½Π°Π»Π°Ρ… ΠΊΠ»ΠΈΠΊΠ°Π±Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΊΠ°ΠΌΠ΅Ρ€Ρƒ смартфона.

Π‘ ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ TensorFlow 2 Object Detection API ΠΌΡ‹ Π½Π°ΡƒΡ‡ΠΈΠΌ TensorFlow модСль Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ ΠΈ Π³Π°Π±Π°Ρ€ΠΈΡ‚Ρ‹ строк https:// Π² изобраТСниях (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΠΊΠ°Π΄Ρ€Π΅ Π²ΠΈΠ΄Π΅ΠΎ ΠΈΠ· ΠΊΠ°ΠΌΠ΅Ρ€Ρ‹ смартфона).

ВСкст ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ссылки, располоТСнный ΠΏΠΎ ΠΏΡ€Π°Π²ΡƒΡŽ сторону ΠΎΡ‚ https://, Π±ΡƒΠ΄Π΅Ρ‚ распознан с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ Tesseract. Π Π°Π±ΠΎΡ‚Π° с Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΎΠΉ Tesseract Π½Π΅ являСтся ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚ΠΎΠΌ этой ΡΡ‚Π°Ρ‚ΡŒΠΈ, Π½ΠΎ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π½Π°ΠΉΡ‚ΠΈ ΠΏΠΎΠ»Π½Ρ‹ΠΉ исходный ΠΊΠΎΠ΄ прилоТСния Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ links-detector repository Π½Π° GitHub.

πŸš€ <strong>Π—Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ Links Detector</strong> со смартфона, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹ΠΉ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚.

πŸ“ <strong>ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ links-detector</strong> Π½Π° GitHub с ΠΏΠΎΠ»Π½Ρ‹ΠΌ исходным ΠΊΠΎΠ΄ΠΎΠΌ прилоТСния.

Π’ΠΎΡ‚ Ρ‚Π°ΠΊ Π² ΠΈΡ‚ΠΎΠ³Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π³Π»ΡΠ΄Π΅Ρ‚ΡŒ процСсс распознавания ΠΏΠ΅Ρ‡Π°Ρ‚Π½Ρ‹Ρ… ссылок:

Links Detector Demo
Links Detector Demo

⚠️ На Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ находится Π² ΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠΉ стадии ΠΈ ΠΈΠΌΠ΅Π΅Ρ‚ мноТСство Π½Π΅Π΄ΠΎΡ€Π°Π±ΠΎΡ‚ΠΎΠΊ ΠΈ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠΉ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ, Π΄ΠΎ Ρ‚Π΅Ρ… ΠΏΠΎΡ€, ΠΏΠΎΠΊΠ° Π²Ρ‹ΡˆΠ΅ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹Π΅ Π½Π΅Π΄ΠΎΡ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π½Π΅ Π±ΡƒΠ΄ΡƒΡ‚ Π»ΠΈΠΊΠ²ΠΈΠ΄ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹, Π½Π΅ ΠΎΠΆΠΈΠ΄Π°ΠΉΡ‚Π΅ ΠΎΡ‚ прилоТСния слишком ΠΌΠ½ΠΎΠ³ΠΎΠ³ΠΎ πŸ€·πŸ»β€. Π’Π°ΠΊΠΆΠ΅ стоит ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ Ρ†Π΅Π»ΡŒΡŽ Π΄Π°Π½Π½ΠΎΠΉ ΡΡ‚Π°Ρ‚ΡŒΠΈ являСтся экспСримСнтированиС с TensorFlow 2 Object Detection API, Π° Π½Π΅ созданиС production-ready прилоТСния.

Π’ случаС, Ссли Π±Π»ΠΎΠΊΠΈ с исходным ΠΊΠΎΠ΄ΠΎΠΌ Π² этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Ρ‚ΡŒΡΡ Π±Π΅Π· подсвСтки ΠΊΠΎΠ΄Π° Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ Π½Π° GitHub Π²Π΅Ρ€ΡΠΈΡŽ этой ΡΡ‚Π°Ρ‚ΡŒΠΈ

πŸ€·πŸ»β€οΈ ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°

Π― Ρ€Π°Π±ΠΎΡ‚Π°ΡŽ программистом, ΠΈ Π² свободноС ΠΎΡ‚ Ρ€Π°Π±ΠΎΡ‚Ρ‹ врСмя ΡƒΡ‡Ρƒ Machine Learning Π² качСствС Ρ…ΠΎΠ±Π±ΠΈ. Но ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π½Π΅ Π² этом.

Π― ΠΊΡƒΠΏΠΈΠ» ΠΊΠ½ΠΈΠ³Ρƒ ΠΏΠΎ ΠΌΠ°ΡˆΠΈΠ½Π½ΠΎΠΌΡƒ ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΡŽ ΠΈ, читая ΠΏΠ΅Ρ€Π²Ρ‹Π΅ Π³Π»Π°Π²Ρ‹, столкнулся с мноТСством ΠΏΠ΅Ρ‡Π°Ρ‚Π½Ρ‹Ρ… ссылок Π½Π° ΠΏΠΎΠ΄ΠΎΠ±ΠΈΠΈ https://tensorflow.org/ ΠΈΠ»ΠΈ https://some-url.com/which/may/be/even/longer?and_with_params=true.

Printed Links
Printed Links

К соТалСнию, ΠΊΠ»ΠΈΠΊΠ°Ρ‚ΡŒ ΠΏΠΎ ΠΏΠ΅Ρ‡Π°Ρ‚Π½Ρ‹ΠΌ ссылкам Π½Π΅ ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΠ»ΠΎΡΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ΠΌ (спасибо, Кэп!). Π§Ρ‚ΠΎΠ±Ρ‹ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ ссылки Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅ ΠΌΠ½Π΅ ΠΏΡ€ΠΈΡ…ΠΎΠ΄ΠΈΠ»ΠΎΡΡŒ Π½Π°Π±ΠΈΡ€Π°Ρ‚ΡŒ ΠΈΡ… посимвольно Π² адрСсной строкС, Ρ‡Ρ‚ΠΎ Π±Ρ‹Π»ΠΎ довольно ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎ. К Ρ‚ΠΎΠΌΡƒ ΠΆΠ΅ ΠΎΠΏΠ΅Ρ‡Π°Ρ‚ΠΊΠΈ Π½ΠΈΠΊΡ‚ΠΎ Π½Π΅ отмСнял.

πŸ’‘ Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠ΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅

Π― ΠΏΠΎΠ΄ΡƒΠΌΠ°Π», Π° Ρ‡Ρ‚ΠΎ Ссли, ΠΏΠΎ Π°Π½Π°Π»ΠΎΠ³ΠΈΠΈ с распознаватСлСм QR ΠΊΠΎΠ΄ΠΎΠ², ΠΌΡ‹ "Π½Π°ΡƒΡ‡ΠΈΠΌ" смартфон (1) ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡ‚ΡŒ мСстополоТСниС ΠΈ (2) Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Π²Π°Ρ‚ΡŒ ΠΏΠ΅Ρ‡Π°Ρ‚Π½Ρ‹Π΅ Π³ΠΈΠΏΠ΅Ρ€-ссылки ΠΈ Π΄Π΅Π»Π°Ρ‚ΡŒ ΠΈΡ… ΠΊΠ»ΠΈΠΊΠ°Π±Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ? Π’ Ρ‚Π°ΠΊΠΎΠΌ случаС Ρ‡ΠΈΡ‚Π°Ρ‚Π΅Π»ΡŒ Π΄Π΅Π»Π°Π» Π±Ρ‹ всСго ΠΎΠ΄ΠΈΠ½ ΠΊΠ»ΠΈΠΊ вмСсто посимвольного Π²Π²ΠΎΠ΄Π° с мноТСством Π½Π°ΠΆΠ°Ρ‚ΠΈΠΉ Π½Π° клавиши. ΠžΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½Π°Ρ ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ всСй этой ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΠ»Π°ΡΡŒ Π±Ρ‹ с O(N) Π΄ΠΎ O(1).

Π’ΠΎΡ‚ Ρ‚Π°ΠΊ Π±Ρ‹ этот процСсс выглядСл:

Links Detector Demo
Links Detector Demo

πŸ“ ВрСбования ΠΊ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡŽ

Как я ΡƒΠΆΠ΅ упомянул Π²Ρ‹ΡˆΠ΅, я Π½Π΅ экспСрт Π² машинном ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠΈ. Для мСня это большС ΠΊΠ°ΠΊ Ρ…ΠΎΠ±Π±ΠΈ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ ΠΈ Ρ†Π΅Π»ΡŒ этой ΡΡ‚Π°Ρ‚ΡŒΠΈ Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ большС Π² экспСримСнтировании ΠΈ ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ с TensorFlow 2 Object Detection API, Ρ‡Π΅ΠΌ Π² ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ΅ создания production-ready прилоТСния.

Π‘ ΡƒΡ‡Π΅Ρ‚ΠΎΠΌ Π²Ρ‹ΡˆΠ΅ΡΠΊΠ°Π·Π°Π½Π½ΠΎΠ³ΠΎ, я упростил трСбования ΠΊ Ρ„ΠΈΠ½Π°Π»ΡŒΠ½ΠΎΠΌΡƒ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡŽ ΠΈ свСл ΠΈΡ… ΠΊ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΏΡƒΠ½ΠΊΡ‚Π°ΠΌ:

  1. ΠŸΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ процСсса обнаруТСния ΠΈ распознавания Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ Π±Π»ΠΈΠ·ΠΊΠ° ΠΊ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌΡƒ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, 0.5-1 ΠΊΠ°Π΄Ρ€ΠΎΠ² Π² сСкунду Π½Π° устройствС схоТСм ΠΏΠΎ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ с iPhone X). Π­Ρ‚ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠ·Π½Π°Ρ‡Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ вСсь процСсс обнаруТСния + распознавания Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΏΡ€ΠΎΠΈΡΡ…ΠΎΠ΄ΠΈΡ‚ΡŒ Π½Π΅ Π±ΠΎΠ»Π΅Π΅ Ρ‡Π΅ΠΌ Π·Π° 2 сСкунды.
  2. Π”ΠΎΠ»ΠΆΠ½Ρ‹ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ссылки Π½Π° английском языкС.
  3. Π”ΠΎΠ»ΠΆΠ½Ρ‹ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ссылки Ρ‡Π΅Ρ€Π½ΠΎΠ³ΠΎ (Ρ‚Π΅ΠΌΠ½ΠΎ-сСрого) Ρ†Π²Π΅Ρ‚Π° Π½Π° Π±Π΅Π»ΠΎΠΌ (свСтло-сСром) Ρ„ΠΎΠ½Π΅.
  4. Π”ΠΎΠ»ΠΆΠ½Ρ‹ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ https:// ссылки (допускаСтся, Ρ‡Ρ‚ΠΎ http://, ftp://, tcp:// ΠΈ ΠΏΡ€ΠΎΡ‡ΠΈΠ΅ ссылки Π½Π΅ Π±ΡƒΠ΄ΡƒΡ‚ распознаны).

🧩 Находим Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅

ΠžΠ±Ρ‰ΠΈΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄

Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ β„–1: МодСль Π½Π° сторонС сСрвСра

Алгоритм дСйствий:

  1. ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Π²ΠΈΠ΄Π΅ΠΎ-ΠΏΠΎΡ‚ΠΎΠΊ (ΠΊΠ°Π΄Ρ€ Π·Π° ΠΊΠ°Π΄Ρ€ΠΎΠΌ) Π½Π° сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°.
  2. ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΊΠ°Π΄Ρ€ Π½Π° сСрвСр.
  3. ΠžΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΠ΅ΠΌ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ ΠΈ распознаваниС ссылок Π½Π° сСрвСрС ΠΈ отправляСм Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Ρƒ.
  4. ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅ΠΌ распознанныС ссылки Π½ΠΈ сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΈ Π΄Π΅Π»Π°Π΅ΠΌ ΠΈΡ… ΠΊΠ»ΠΈΠΊΠ°Π±Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ.

Model on the back-end
Model on the back-end

ΠŸΡ€Π΅ΠΈΠΌΡƒΡ‰Π΅ΡΡ‚Π²Π°:

  • πŸ’š Π‘ΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ обнаруТСния ΠΈ распознавания ссылок Π½Π΅ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π° ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ клиСнтского устройства. ΠŸΡ€ΠΈ ΠΆΠ΅Π»Π°Π½ΠΈΠΈ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡƒΡΠΊΠΎΡ€ΠΈΡ‚ΡŒ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ обнаруТСния ссылок ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΡƒΡ наши сСрвСра Π³ΠΎΡ€ΠΈΠ·ΠΎΠ½Ρ‚Π°Π»ΡŒΠ½ΠΎ (большС сСрвСров) ΠΈΠ»ΠΈ Π²Π΅Ρ€Ρ‚ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎ (большС ядСр ΠΈ GPUs).
  • πŸ’š МодСль ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΠΌΠ΅Ρ‚ΡŒ больший Ρ€Π°Π·ΠΌΠ΅Ρ€ (ΠΈ, Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, Π±ΠΎΠ»ΡŒΡˆΡƒΡŽ Ρ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒ), ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ отсутствуСт Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ Π΅Π΅ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Π½Π° сторону ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°. Π—Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ модСль Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ ~10Mb Π½Π° сторону ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° выглядит рСалистичным, Π½ΠΎ всС-ΠΆΠ΅ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ модСль Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ ~100Mb ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ довольно ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°Ρ‚ΠΈΡ‡Π½Ρ‹ΠΌ с Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ UX (user experience).
  • πŸ’š Π£ нас появляСтся Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ доступ ΠΊ ΠΌΠΎΠ΄Π΅Π»ΠΈ. ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ модСль "спрятана" Π·Π° ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΌ API, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠ°ΠΊΠΈΠΌ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°ΠΌ ΠΎΠ½Π° Π±ΡƒΠ΄Π΅Ρ‚ доступна.

НСдостатки:

  • πŸ’” Π‘Π»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ систСмы растСт. ВмСсто использования ΠΎΠ΄Π½ΠΎΠ³ΠΎ лишь JavaScript Π½Π° сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π±ΡƒΠ΄Π΅Ρ‚ Ρ‚Π°ΠΊ ΠΆΠ΅ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Python инфраструктуру Π½Π° сторонС сСрвСра. Нам Ρ‚Π°ΠΊ ΠΆΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠΎΠ·Π°Π±ΠΎΡ‚ΠΈΡ‚ΡŒΡΡ ΠΎΠ± автоматичСском ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠΈ сСрвиса.
  • πŸ’” Π Π°Π±ΠΎΡ‚Π° прилоТСния Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ ΠΎΡ„Ρ„Π»Π°ΠΉΠ½ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Π° ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ прилоТСния трСбуСтся доступ ΠΊ ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚Ρƒ.
  • πŸ’” ΠœΠ½ΠΎΠΆΠ΅ΡΡ‚Π²ΠΎ HTTP запросов ΠΊ сСрвису со стороны ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΌΠΎΠΆΠ΅Ρ‚ ΡΡ‚Π°Ρ‚ΡŒ слабым мСстом систСмы с Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. ΠŸΡ€Π΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠΈΠΌ, ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ ΡƒΠ»ΡƒΡ‡ΡˆΠΈΡ‚ΡŒ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ обнаруТСния ΠΈ распознавания ссылок с 1 Π΄ΠΎ 10+ ΠΊΠ°Π΄Ρ€ΠΎΠ² Π² сСкунду. Π’ Ρ‚Π°ΠΊΠΎΠΌ случаС ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Π±ΡƒΠ΄Π΅Ρ‚ ΡΠ»Π°Ρ‚ΡŒ 10+ запросов Π² сСкунду Π½Π° сСрвСр. Для 10 ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠ², Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰ΠΈΡ… ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ, это ΡƒΠΆΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠ·Π½Π°Ρ‡Π°Ρ‚ΡŒ 100+ запросов Π² сСкунду. На ΠΏΠΎΠΌΠΎΡ‰ΡŒ ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΡ€ΠΈΠΉΡ‚ΠΈ двусторонний стриминг HTTP/2 ΠΈ gRPC, Π½ΠΎ ΠΌΡ‹ снова возвращаСмся ΠΊ ΠΏΠ΅Ρ€Π²ΠΎΠΌΡƒ ΠΏΡƒΠ½ΠΊΡ‚Ρƒ, связанному с растущСй ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒΡŽ систСмы.
  • πŸ’” Π‘Ρ‚ΠΎΠΈΠΌΠΎΡΡ‚ΡŒ систСмы растСт. Π’ основном это связано с ΠΎΠΏΠ»Π°Ρ‚ΠΎΠΉ Π·Π° Π°Ρ€Π΅Π½Π΄Ρƒ сСрвСров.

Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ β„–2: МодСль Π½Π° сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°

Алгоритм дСйствий:

  1. ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Π²ΠΈΠ΄Π΅ΠΎ-ΠΏΠΎΡ‚ΠΎΠΊ (ΠΊΠ°Π΄Ρ€ Π·Π° ΠΊΠ°Π΄Ρ€ΠΎΠΌ) Π½Π° сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°.
  2. ΠžΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΠ΅ΠΌ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ ΠΈ распознаваниС ссылок Π½Π° сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° (Π±Π΅Π· ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ Π½Π° сСрвСр).
  3. ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅ΠΌ распознанныС ссылки Π½ΠΈ сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΈ Π΄Π΅Π»Π°Π΅ΠΌ ΠΈΡ… ΠΊΠ»ΠΈΠΊΠ°Π±Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ.

Model on the front-end
Model on the front-end

ΠŸΡ€Π΅ΠΈΠΌΡƒΡ‰Π΅ΡΡ‚Π²Π°:

  • πŸ’š МСнСС слоТная систСма. НСт нСобходимости Π² Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ сСрвСрной части прилоТСния ΠΈ создания API.
  • πŸ’š ΠŸΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ ΠΎΡ„Ρ„Π»Π°ΠΉΠ½. МодСль Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π° Π½Π° сторону ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΈ Π½Π΅Ρ‚ нСобходимости Π² доступС ΠΊ ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚Ρƒ (см. Progressive Web Application)
  • πŸ’š БистСма "ΠΏΠΎΡ‡Ρ‚ΠΈ" автоматичСски ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΡƒΠ΅ΠΌΠ°. ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ Π½ΠΎΠ²Ρ‹ΠΉ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ прилоТСния "ΠΏΡ€ΠΈΡ…ΠΎΠ΄ΠΈΡ‚" со своим процСссором ΠΈ Π²ΠΈΠ΄Π΅ΠΎΠΊΠ°Ρ€Ρ‚ΠΎΠΉ. Π­Ρ‚ΠΎ ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ ΠΆΠ΅ Π½Π΅ΠΏΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½ΠΎΠ΅ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ (ΠΌΡ‹ Π·Π°Ρ‚Ρ€ΠΎΠ½Π΅ΠΌ ΠΏΡ€ΠΈΡ‡ΠΈΠ½Ρ‹ Π½ΠΈΠΆΠ΅).
  • πŸ’š БистСма Π³ΠΎΡ€Π°Π·Π΄ΠΎ дСшСвлС. Нам Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π·Π°ΠΏΠ»Π°Ρ‚ΠΈΡ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π·Π° сСрвСр со статичСскими Π΄Π°Π½Π½Ρ‹ΠΌΠΈ (HTML, JS, CSS, Ρ„Π°ΠΉΠ»Ρ‹ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΈ ΠΏΡ€.). Π’ случаС с GitHub, Ρ‚Π°ΠΊΠΎΠΉ сСрвСр ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ прСдоставлСн бСсплатно.
  • πŸ’š ΠžΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΡƒΠ΅Ρ‚ (Ρ‚Π°ΠΊ ΠΆΠ΅ ΠΊΠ°ΠΊ ΠΈ сСрвСры) ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° большого количСства HTTP запросов Π² сСкунду ΠΊ сСрвСрам.

НСдостатки:

  • πŸ’” Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π³ΠΎΡ€ΠΈΠ·ΠΎΠ½Ρ‚Π°Π»ΡŒΠ½ΠΎΠ΅ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅, ΠΊΠΎΠ³Π΄Π° ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ автоматичСски ΠΈΠΌΠ΅Π΅Ρ‚ свои собствСнныС процСссоры ΠΈ Π³Ρ€Π°Ρ„ΠΈΡ‡Π΅ΡΠΊΡƒΡŽ ΠΊΠ°Ρ€Ρ‚Ρƒ. Π’Π΅Ρ€Ρ‚ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΠ΅ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΌΡ‹ Π½Π΅ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠ²Π»ΠΈΡΡ‚ΡŒ Π½Π° ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ клиСнтского устройства. Π’ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ ΠΌΡ‹ Π½Π΅ ΠΌΠΎΠΆΠ΅ΠΌ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ быстрого обнаруТСния ΠΈ распознавания ссылок для ΠΌΠ΅Π΄Π»Π΅Π½Π½Ρ‹Ρ… устройств.
  • πŸ’” НСвозмоТно ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ использованиС ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°ΠΌΠΈ. ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΌΠΎΠΆΠ΅Ρ‚ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ ΠΊ сСбС модСль ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π΅ Π³Π΄Π΅ ΠΈ ΠΊΠ°ΠΊ ΡƒΠ³ΠΎΠ΄Π½ΠΎ.
  • πŸ’” Π‘ΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ расхода Π±Π°Ρ‚Π°Ρ€Π΅ΠΈ клиСнтского устройства ΠΌΠΎΠΆΠ΅Ρ‚ ΡΡ‚Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΎΠΉ. МодСль ΠΏΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ потрСбляСт Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ рСсурсы. ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ прилоТСния ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ Π½Π΅Π΄ΠΎΠ²ΠΎΠ»ΡŒΠ½Ρ‹ Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ ΠΈΡ… iPhone становится всС Ρ‚Π΅ΠΏΠ»Π΅Π΅ ΠΈ Ρ‚Π΅ΠΏΠ»Π΅Π΅ Π²ΠΎ врСмя Ρ€Π°Π±ΠΎΡ‚Ρ‹.

Π’Ρ‹Π±ΠΈΡ€Π°Π΅ΠΌ ΠΎΠ±Ρ‰ΠΈΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄

ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Ρ†Π΅Π»ΡŽ этой ΡΡ‚Π°Ρ‚ΡŒΠΈ ΠΈ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π² Ρ†Π΅Π»ΠΎΠΌ являСтся ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅, Π° Π½Π΅ созданиС прилоТСния коммСрчСского уровня ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ Π²Ρ‚ΠΎΡ€ΠΎΠΉ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ ΠΈ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ модСль Π½Π° сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°. Π­Ρ‚ΠΎ сдСлаСт вСсь ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ ΠΌΠ΅Π½Π΅Π΅ Π·Π°Ρ‚Ρ€Π°Ρ‚Π½Ρ‹ΠΌ ΠΈ Ρƒ нас Π±ΡƒΠ΄Π΅Ρ‚ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ большС ΡΡ„ΠΎΠΊΡƒΡΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π½Π° машинном ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠΈ, Π° Π½Π΅ Π½Π° создании автоматичСски ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΡƒΠ΅ΠΌΠΎΠΉ сСрвСрной инфраструктуры.

УглубляСмся Π² Π΄Π΅Ρ‚Π°Π»ΠΈ

Π˜Ρ‚Π°ΠΊ, ΠΌΡ‹ Π²Ρ‹Π±Ρ€Π°Π»ΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ прилоТСния Π±Π΅Π· сСрвСрной части. ΠŸΡ€Π΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠΈΠΌ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ, Ρ‡Ρ‚ΠΎ Ρƒ нас Π½Π° Π²Ρ…ΠΎΠ΄Π΅ Π΅ΡΡ‚ΡŒ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ (ΠΊΠ°Π΄Ρ€) ΠΈΠ· Π²ΠΈΠ΄Π΅ΠΎ-ΠΏΠΎΡ‚ΠΎΠΊΠ° ΠΊΠ°ΠΌΠ΅Ρ€Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ выглядит Ρ‚Π°ΠΊ:

Printed Links Input
Printed Links Input

Нам Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Ρ€Π΅ΡˆΠΈΡ‚ΡŒ Π΄Π²Π΅ ΠΏΠΎΠ΄Π·Π°Π΄Π°Ρ‡ΠΈ:

  1. ΠžΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ ссылок (Π½Π°ΠΉΡ‚ΠΈ ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ ΠΈ Π³Π°Π±Π°Ρ€ΠΈΡ‚Ρ‹ ссылок Π½Π° страницС)
  2. РаспознаваниС ссылок (Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Ρ‚ΡŒ тСкст ссылок)

Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ β„–1: РСшСниС Π½Π° основС Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ Tesseract

ΠŸΠ΅Ρ€Π²Ρ‹ΠΌ ΠΈ Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½Ρ‹ΠΌ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠΌ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ΠΌ Π·Π°Π΄Π°Ρ‡ΠΈ оптичСского распознавания символов (OCR) ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ распознавания тСкста всСго изобраТСния с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ Tesseract.js. Она ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Π½Π° Π²Ρ…ΠΎΠ΄ ΠΈ Π²Ρ‹Π΄Π°Π΅Ρ‚ распознанныС ΠΏΠ°Ρ€Π°Π³Ρ€Π°Ρ„Ρ‹, тСкстовыС строки, Π±Π»ΠΎΠΊΠΈ тСкста ΠΈ слова ΠΈ вмСстС с Π³Π°Π±Π°Ρ€ΠΈΡ‚Π°ΠΌΠΈ ΠΈ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π°ΠΌΠΈ.

Recognized text with bounding boxes
Recognized text with bounding boxes

Π”Π°Π»Π΅Π΅ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠΏΡ‹Ρ‚Π°Ρ‚ΡŒΡΡ Π½Π°ΠΉΡ‚ΠΈ ссылки Π² распознанном тСкстС с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ рСгулярного выраТСния ΠΏΠΎΡ…ΠΎΠΆΠ΅Π³ΠΎ Π½Π° это (ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π½Π° TypeScript):

const URL_REG_EXP = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)/gi;

const extractLinkFromText = (text: string): string | null => {
  const urls: string[] | null = text.match(URL_REG_EXP);
  if (!urls || !urls.length) {
    return null;
  }
  return urls[0];
};

πŸ’š ΠŸΠΎΡ…ΠΎΠΆΠ΅, Ρ‡Ρ‚ΠΎ Π·Π°Π΄Π°Ρ‡Π° Ρ€Π΅ΡˆΠ΅Π½Π° довольно прямолинСйным ΠΈ простым способом:

  • ΠœΡ‹ Π·Π½Π°Π΅ΠΌ Π³Π°Π±Π°Ρ€ΠΈΡ‚Ρ‹ ΠΈ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ ссылок.
  • ΠœΡ‹ Ρ‚Π°ΠΊ ΠΆΠ΅ Π·Π½Π°Π΅ΠΌ тСкст ссылок ΠΈ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΈΡ… ΠΊΠ»ΠΈΠΊΠ°Π±Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ.

πŸ’” ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ врСмя обнаруТСния + распознавания ΠΌΠΎΠΆΠ΅Ρ‚ Π²Π°Ρ€ΡŒΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΎΡ‚ 2 Π΄ΠΎ 20+ сСкунд Π² зависимости ΠΎΡ‚ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° изобраТСния, Π΅Π³ΠΎ качСства ΠΈ "ΠΏΠΎΡ…ΠΎΠΆΠΈΡ… Π½Π° тСкст" ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ. Π’ ΠΈΡ‚ΠΎΠ³Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡ‡Π΅Π½ΡŒ слоТно Π΄ΠΎΡΡ‚ΠΈΡ‡ΡŒ Ρ‚ΠΎΠΉ Π±Π»ΠΈΠ·ΠΊΠΎΠΉ ΠΊ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌΡƒ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² 0.5-1 ΠΊΠ°Π΄Ρ€ΠΎΠ² Π² сСкунду.

πŸ’” Π’Π°ΠΊΠΆΠ΅, Ссли ΠΏΠΎΠ΄ΡƒΠΌΠ°Ρ‚ΡŒ, Ρ‚ΠΎ ΠΌΡ‹ просим Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Ρ‚ΡŒ вСсь тСкст Π½Π° ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ΅, Π΄Π°ΠΆΠ΅ Ссли Π² тСкстС совсСм Π½Π΅Ρ‚ ссылок ΠΈΠ»ΠΈ Ссли Π² тСкстС Π΅ΡΡ‚ΡŒ ΠΎΠ΄Π½Π°-Π΄Π²Π΅ ссылки, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΡΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‚, пускай, ~10% ΠΎΡ‚ всСго объСма тСкста. Π­Ρ‚ΠΎ Π·Π²ΡƒΡ‡ΠΈΡ‚ ΠΊΠ°ΠΊ нСэффСктивная Ρ‚Ρ€Π°Ρ‚Π° Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… рСсурсов.

Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ β„–2: РСшСниС Π½Π° основС Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ Tesseract ΠΈ TensorFlow (+1 модСль)

ΠœΡ‹ ΠΌΠΎΠ³Π»ΠΈ Π±Ρ‹ Π·Π°ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ Tesseract Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ быстрСС ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ Π΅Ρ‰Π΅ ΠΎΠ΄ΠΈΠ½ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ "Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ-совСтчик" ΠΏΠ΅Ρ€Π΅Π΄ Ρ‚Π΅ΠΌ, ΠΊΠ°ΠΊ ΠΏΡ€ΠΈΡΡ‚ΡƒΠΏΠΈΡ‚ΡŒ ΠΊ Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Π²Π°Π½ΠΈΡŽ ссылок. Π­Ρ‚ΠΎΡ‚ "Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ-совСтчик" Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Ρ‚ΡŒ (Π½ΠΎ Π½Π΅ Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Π²Π°Ρ‚ΡŒ) Π½Π°Ρ‡Π°Π»ΠΎ ссылок (ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ самой Π»Π΅Π²ΠΎΠΉ Π³Ρ€Π°Π½ΠΈΡ†Ρ‹ ссылки) для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ссылки Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ. Π­Ρ‚ΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ Π½Π°ΠΌ ΡƒΡΠΊΠΎΡ€ΠΈΡ‚ΡŒ Π·Π°Π΄Π°Ρ‡Ρƒ распознавания тСкста ссылок, Ссли ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΏΡ€Π°Π²ΠΈΠ»Π°ΠΌ:

  1. Если ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Π½Π΅ содСрТит Π½ΠΈ ΠΎΠ΄Π½ΠΎΠΉ ссылки ΠΌΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ распознавания тСкста Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΎΠΉ Tesseract.
  2. Если ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ содСрТит ссылки, Ρ‚ΠΎ ΠΌΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ "ΠΏΠΎΠΏΡ€ΠΎΡΠΈΡ‚ΡŒ" Tesseract Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Ρ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‚Π΅ части изобраТСния, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ содСрТат тСкст ссылок. ΠœΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ Ρ‚Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ врСмя Π½Π° распознаваниС "ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠ³ΠΎ" для нашСй Π·Π°Π΄Π°Ρ‡ΠΈ тСкста.

Π­Ρ‚ΠΎΡ‚ "Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ-совСтчик", ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ ΡΡ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ ΠΏΠ΅Ρ€Π΅Π΄ Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ Tesseract Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒΡΡ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π· Π·Π° ΠΎΠ΄Π½ΠΎ ΠΈ Ρ‚ΠΎ ΠΆΠ΅ врСмя, нСзависимо ΠΎΡ‚ качСства ΠΈ содСрТимого изобраТСния. Он Ρ‚Π°ΠΊΠΆΠ΅ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ достаточно быстрым ΠΈ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡ‚ΡŒ Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ ΠΈ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ ссылок быстрСС Ρ‡Π΅ΠΌ Π·Π° 1 сСкунду (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π½Π° iPhone X). Π’ Ρ‚Π°ΠΊΠΎΠΌ случаС ΠΌΡ‹ смоТСм ΠΏΠΎΠΏΡ‹Ρ‚Π°Ρ‚ΡŒΡΡ Π·Π°ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ нашС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ Π±Π»ΠΈΠ·ΠΊΠΎΠΌ ΠΊ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌΡƒ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ (опрСдСлСния "близости" ΠΌΡ‹ Π΄Π°Π»ΠΈ Π²Ρ‹ΡˆΠ΅).

πŸ’‘ Π˜Ρ‚Π°ΠΊ, Ρ‡Ρ‚ΠΎ Ссли ΠΌΡ‹ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡΡ Π΅Ρ‰Π΅ ΠΎΠ΄Π½ΠΈΠΌ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ (Π΅Ρ‰Π΅ ΠΎΠ΄Π½ΠΎΠΉ модСлью) обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°ΠΌ Π½Π°ΠΉΡ‚ΠΈ строки https:// Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ (каТдая защищСнная ссылка начинаСтся с https://, Π½Π΅ Ρ‚Π°ΠΊ Π»ΠΈ?). Π’ΠΎΠ³Π΄Π°, зная располоТСниС ΠΈ Π³Π°Π±Π°Ρ€ΠΈΡ‚Ρ‹ прСфиксов https:// Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ, ΠΌΡ‹ смоТСм ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π½Π° распознаваниС тСкста с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ Tesseract Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‚Π΅ части изобраТСния, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ находятся ΠΏΠΎ ΠΏΡ€Π°Π²ΡƒΡŽ сторону ΠΎΡ‚ прСфиксов https:// ΠΈ ΡΠ²Π»ΡΡŽΡ‚ΡΡ ΠΈΡ… ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ΅Π½ΠΈΠ΅ΠΌ.

ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Π½ΠΈΠΆΠ΅:

Tesseract and TensorFlow based solution
Tesseract and TensorFlow based solution

На этом ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ Tesseract Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ Π³ΠΎΡ€Π°Π·Π΄ΠΎ мСньшС Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΏΠΎ Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Π²Π°Π½ΠΈΡŽ тСкста, Ссли ΠΌΡ‹ подскаТСм Π΅ΠΌΡƒ, Π³Π΄Π΅ Π² тСкстС ΠΌΠΎΠ³ΡƒΡ‚ Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒΡΡ ссылки (ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π° количСство Π³ΠΎΠ»ΡƒΠ±Ρ‹Ρ… ΠΏΡ€ΡΠΌΠΎΡƒΠ³ΠΎΠ»ΡŒΠ½ΠΈΠΊΠΎΠ², Ρ‡Π΅ΠΌ Π½Π΅ Π΄ΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒΡΡ‚Π²ΠΎ πŸ€“).

Π˜Ρ‚Π°ΠΊ, вопрос, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΎΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ, ΠΊΠ°ΠΊΡƒΡŽ ΠΆΠ΅ модСль обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π½Π°ΠΌ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ ΠΈ ΠΊΠ°ΠΊ "Π½Π°ΡƒΡ‡ΠΈΡ‚ΡŒ" Π΅Π΅ Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ Π½Π° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ прСфиксы https://.

НаконСц-Ρ‚ΠΎ ΠΌΡ‹ ΠΏΠΎΠ΄ΠΎΠ±Ρ€Π°Π»ΠΈΡΡŒ Π±Π»ΠΈΠΆΠ΅ ΠΊ TensorFlow πŸ˜€

πŸ€– Π’Ρ‹Π±ΠΈΡ€Π°Π΅ΠΌ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΡΡ‰ΡƒΡŽ модСль обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²

Π’Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠ° Π½ΠΎΠ²ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² с нуля Π½Π΅ являСтся Ρ…ΠΎΡ€ΠΎΡˆΠΈΠΌ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠΌ Π² нашСм случаС ΠΏΠΎ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π°ΠΌ:

  • πŸ’” Π’Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠ° ΠΌΠΎΠΆΠ΅Ρ‚ Π·Π°Π½ΡΡ‚ΡŒ Π΄Π½ΠΈ/Π½Π΅Π΄Π΅Π»ΠΈ ΠΈ ΡΡ‚ΠΎΠΈΡ‚ΡŒ ΠΌΠ½ΠΎΠ³ΠΎ Π΄Π΅Π½Π΅Π³ (Π·Π° Π°Ρ€Π΅Π½Π΄Ρƒ Ρ‚Π΅Ρ…-ΠΆΠ΅ сСрвСров с GPU).
  • πŸ’” Π£ нас скорСС всСго Π½Π΅ получится ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ…, состоящий ΠΈΠ· сотСн тысяч Ρ„ΠΎΡ‚ΠΎΠ³Ρ€Π°Ρ„ΠΈΠΉ ΠΊΠ½ΠΈΠ³ ΠΈ ΠΆΡƒΡ€Π½Π°Π»ΠΎΠ² со ссылками. Π’Π΅ΠΌ-Π±ΠΎΠ»Π΅Π΅, Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ Π½ΡƒΠΆΠ½Ρ‹ Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ изобраТСния, Π½ΠΎ Π΅Ρ‰Π΅ ΠΈ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ прСфиксов https:// для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΈΠ· Π½ΠΈΡ…. Π‘ Π΄Ρ€ΡƒΠ³ΠΎΠΉ стороны ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠΏΡ‹Ρ‚Π°Ρ‚ΡŒΡΡ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚Π°ΠΊΠΎΠΉ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ…, Π½ΠΎ ΠΎΠ± этом Π½ΠΈΠΆΠ΅.

Π˜Ρ‚Π°ΠΊ, вмСсто создания Π½ΠΎΠ²ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΎΠ±ΡƒΡ‡Π°Ρ‚ΡŒ ΡƒΠΆΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΡƒΡŽ ΠΈ Π½Π°Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²Π°Π½Π½ΡƒΡŽ модСль ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹ΠΉ для Π½Π΅Π΅ класс ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² (см. transfer learning). Π’ нашСм случаС ΠΏΠΎΠ΄ "Π½ΠΎΠ²Ρ‹ΠΌ классом" ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΌΡ‹ ΠΈΠΌΠ΅Π΅ΠΌ Π² Π²ΠΈΠ΄Ρƒ изобраТСния прСфикса https://. Π’Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ ΠΈΠΌΠ΅Π΅Ρ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ прСимущСства:

  • πŸ’š Набор Π΄Π°Π½Π½Ρ‹Ρ… ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Π³ΠΎΡ€Π°Π·Π΄ΠΎ мСньшим. НСт нСобходимости ΡΠΎΠ±ΠΈΡ€Π°Ρ‚ΡŒ сотни тысяч ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ с локализациями (ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π°ΠΌΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ). ВмСсто этого ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΠ±ΠΎΠΉΡ‚ΠΈΡΡŒ сотнСй ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ ΠΈ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π»ΠΎΠΊΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ. Π­Ρ‚ΠΎ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎ Ρ‚ΠΎΠΉ ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π΅, Ρ‡Ρ‚ΠΎ модСль ΡƒΠΆΠ΅ Π½Π°Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²Π°Π½Π½Π° Π½Π° ΠΎΠ±Ρ‰Π΅ΠΌ Π½Π°Π±ΠΎΡ€Π΅ Π΄Π°Π½Π½Ρ‹Ρ… Ρ‚ΠΈΠΏΠ° COCO ΠΈ ΡƒΠΆΠ΅ ΡƒΠΌΠ΅Π΅Ρ‚ ΠΈΠ·Π²Π»Π΅ΠΊΠ°Ρ‚ΡŒ основныС характСристики изобраТСния (Π½Π°ΡƒΡ‡ΠΈΡ‚ΡŒ "пСрвокурсника" Π»ΠΈΠ½Π΅ΠΉΠ½ΠΎΠΉ Π°Π»Π³Π΅Π±Ρ€Π΅, ΠΊΠ°ΠΊ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ, Π»Π΅Π³Ρ‡Π΅, Ρ‡Π΅ΠΌ "пСрвоклассника").
  • πŸ’š ВрСмя Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ Ρ‚Π°ΠΊ ΠΆΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ Π³ΠΎΡ€Π°Π·Π΄ΠΎ мСньшим (Π½Π° GPU ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ ΠΌΠΈΠ½ΡƒΡ‚Ρ‹/часы вмСсто Π΄Π½Π΅ΠΉ/нСдСль). ВрСмя сокращаСтся Π·Π° счСт мСньшСго объСма Π΄Π°Π½Π½Ρ‹Ρ… (ΠΌΠ΅Π½ΡŒΡˆΠΈΡ… ΠΏΠ°Ρ€Ρ‚ΠΈΠΉ Π΄Π°Π½Π½Ρ‹Ρ… Π²ΠΎ врСмя Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ) ΠΈ мСньшСго количСства Ρ‚Ρ€Π΅Π½ΠΈΡ€ΡƒΠ΅ΠΌΡ‹Ρ… ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² ΠΌΠΎΠ΄Π΅Π»ΠΈ.

ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΡƒΡŽ модСль ΠΈΠ· "Π·ΠΎΠΎΠΏΠ°Ρ€ΠΊΠ°" ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ TensorFlow 2, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ прСдставляСт собой ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΡŽ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ Π½Π°Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹Ρ… Π½Π° Π½Π°Π±ΠΎΡ€Π΅ Π΄Π°Π½Π½Ρ‹Ρ… COCO 2017. На Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ эта коллСкция Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ Π² сСбя ~40 Ρ€Π°Π·Π½Ρ‹Ρ… Π²Π°Ρ€ΠΈΠ°Ρ†ΠΈΠΉ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ.

Для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ "Π½Π°ΡƒΡ‡ΠΈΡ‚ΡŒ" модСль ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹Π΅, Ρ€Π°Π½Π΅Π΅ нСизвСстныС Π΅ΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ TensorFlow 2 Object Detection API. TensorFlow Object Detection API - это Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ Π½Π° основС TensorFlow, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ позволяСт ΠΊΠΎΠ½ΡΡ‚Ρ€ΡƒΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠΎΠ΄Π΅Π»ΠΈ обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ².

Если Π²Ρ‹ ΠΏΠ΅Ρ€Π΅ΠΉΠ΄Π΅Ρ‚Π΅ ΠΏΠΎ ссылкС Π½Π° "Π·ΠΎΠΎΠΏΠ°Ρ€ΠΊ" ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ Π²Ρ‹ ΡƒΠ²ΠΈΠ΄ΠΈΡ‚Π΅, Ρ‡Ρ‚ΠΎ для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ Ρ‚Π°ΠΌ ΡƒΠΊΠ°Π·Π°Π½Π° ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ ΠΈ Ρ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒ обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ².

Model Zoo
Model Zoo

Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ взято с рСпозитория TensorFlow Model Zoo

ΠšΠΎΠ½Π΅Ρ‡Π½ΠΎ ΠΆΠ΅, для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΡΡ‰ΡƒΡŽ модСль, Π½Π°ΠΌ Π²Π°ΠΆΠ½ΠΎ Π½Π°ΠΉΡ‚ΠΈ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Ρ‹ΠΉ баланс ΠΌΠ΅ΠΆΠ΄Ρƒ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒΡŽ ΠΈ Ρ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒΡŽ обнаруТСния. Но Ρ‡Ρ‚ΠΎ Π΅Ρ‰Π΅ Π²Π°ΠΆΠ½Π΅Π΅ Π² нашСм случаС, это Ρ€Π°Π·ΠΌΠ΅Ρ€ ΠΌΠΎΠ΄Π΅Π»ΠΈ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΌΡ‹ ΠΏΠ»Π°Π½ΠΈΡ€ΡƒΠ΅ΠΌ Π·Π°Π³Ρ€ΡƒΠΆΠ°Ρ‚ΡŒ Π΅Π΅ Π½Π° сторону ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°.

Π Π°Π·ΠΌΠ΅Ρ€ Π°Ρ€Ρ…ΠΈΠ²Π° с модСлью ΠΌΠΎΠΆΠ΅Ρ‚ Π²Π°Ρ€ΡŒΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΎΡ‚ ~20Mb Π΄ΠΎ ~1Gb. Π’ΠΎΡ‚ нСсколько ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ²:

  • 1386 (Mb) centernet_hg104_1024x1024_kpts_coco17_tpu-32
  • 330 (Mb) centernet_resnet101_v1_fpn_512x512_coco17_tpu-8
  • 195 (Mb) centernet_resnet50_v1_fpn_512x512_coco17_tpu-8
  • 198 (Mb) centernet_resnet50_v1_fpn_512x512_kpts_coco17_tpu-8
  • 227 (Mb) centernet_resnet50_v2_512x512_coco17_tpu-8
  • 230 (Mb) centernet_resnet50_v2_512x512_kpts_coco17_tpu-8
  • 29 (Mb) efficientdet_d0_coco17_tpu-32
  • 49 (Mb) efficientdet_d1_coco17_tpu-32
  • 60 (Mb) efficientdet_d2_coco17_tpu-32
  • 89 (Mb) efficientdet_d3_coco17_tpu-32
  • 151 (Mb) efficientdet_d4_coco17_tpu-32
  • 244 (Mb) efficientdet_d5_coco17_tpu-32
  • 376 (Mb) efficientdet_d6_coco17_tpu-32
  • 376 (Mb) efficientdet_d7_coco17_tpu-32
  • 665 (Mb) extremenet
  • 427 (Mb) faster_rcnn_inception_resnet_v2_1024x1024_coco17_tpu-8
  • 424 (Mb) faster_rcnn_inception_resnet_v2_640x640_coco17_tpu-8
  • 337 (Mb) faster_rcnn_resnet101_v1_1024x1024_coco17_tpu-8
  • 337 (Mb) faster_rcnn_resnet101_v1_640x640_coco17_tpu-8
  • 343 (Mb) faster_rcnn_resnet101_v1_800x1333_coco17_gpu-8
  • 449 (Mb) faster_rcnn_resnet152_v1_1024x1024_coco17_tpu-8
  • 449 (Mb) faster_rcnn_resnet152_v1_640x640_coco17_tpu-8
  • 454 (Mb) faster_rcnn_resnet152_v1_800x1333_coco17_gpu-8
  • 202 (Mb) faster_rcnn_resnet50_v1_1024x1024_coco17_tpu-8
  • 202 (Mb) faster_rcnn_resnet50_v1_640x640_coco17_tpu-8
  • 207 (Mb) faster_rcnn_resnet50_v1_800x1333_coco17_gpu-8
  • 462 (Mb) mask_rcnn_inception_resnet_v2_1024x1024_coco17_gpu-8
  • 86 (Mb) ssd_mobilenet_v1_fpn_640x640_coco17_tpu-8
  • 44 (Mb) ssd_mobilenet_v2_320x320_coco17_tpu-8
  • 20 (Mb) ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8
  • 20 (Mb) ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8
  • 369 (Mb) ssd_resnet101_v1_fpn_1024x1024_coco17_tpu-8
  • 369 (Mb) ssd_resnet101_v1_fpn_640x640_coco17_tpu-8
  • 481 (Mb) ssd_resnet152_v1_fpn_1024x1024_coco17_tpu-8
  • 480 (Mb) ssd_resnet152_v1_fpn_640x640_coco17_tpu-8
  • 233 (Mb) ssd_resnet50_v1_fpn_1024x1024_coco17_tpu-8
  • 233 (Mb) ssd_resnet50_v1_fpn_640x640_coco17_tpu-8

МодСль ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8 выглядит Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ подходящСй Π² нашСм случаС:

  • πŸ’š Она ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ нСбольшая - 20Mb Π² Π°Ρ€Ρ…ΠΈΠ²Π΅.
  • πŸ’š Она достаточно быстрая - 39ms Π½Π° ΠΎΠ΄Π½ΠΎ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅.
  • πŸ’š Она ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ ΡΠ΅Ρ‚ΡŒ MobileNet v2 Π² качСствС экстрактора свойств изобраТСния (feature extractor), которая Π² свою ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Π° ΠΏΠΎΠ΄ Ρ€Π°Π±ΠΎΡ‚Ρƒ Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройствах ΠΈ обСспСчиваСт мСньший расход Π±Π°Ρ‚Π°Ρ€Π΅ΠΈ.
  • πŸ’š Она ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ всСх извСстных Π΅ΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ Π·Π° ΠΎΠ΄ΠΈΠ½ ΠΏΡ€ΠΎΡ…ΠΎΠ΄ нСзависимо ΠΎΡ‚ содСрТимого изобраТСния (отсутствуСт шаг regions proposal, Ρ‡Ρ‚ΠΎ Π΄Π΅Π»Π°Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Ρƒ сСти быстрСС).
  • πŸ’” Π’ Ρ‚ΠΎ ΠΆΠ΅ врСмя это Π½Π΅ самая точная модСль (всС являСтся компромиссом βš–οΈ)

НазваниС ΠΌΠΎΠ΄Π΅Π»ΠΈ Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ Π² сСбя Π΅Π΅ нСсколько Π²Π°ΠΆΠ½Ρ‹Ρ… характСристик, с ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌΠΈ Π²Ρ‹ ΠΏΡ€ΠΈ ΠΆΠ΅Π»Π°Π½ΠΈΠΈ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ Π΄Π΅Ρ‚Π°Π»ΡŒΠ½Π΅Π΅:

  • ΠžΠΆΠΈΠ΄Π°Π΅ΠΌΡ‹ΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ изобраТСния Π½Π° Π²Ρ…ΠΎΠ΄Π΅ - 640x640px.
  • МодСль построСна Π½Π° основС Single Shot MultiBox Detector (SSD) ΠΈ Feature Pyramid Network (FPN).
  • БвСрточная нСйронная ΡΠ΅Ρ‚ΡŒ (CNN) MobileNet v2 ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π² качСствС экстрактора свойств изобраТСния (feature extractor).
  • МодСль Π±Ρ‹Π»Π° ΠΎΠ±ΡƒΡ‡Π΅Π½Π° Π½Π° Π½Π°Π±ΠΎΡ€Π΅ Π΄Π°Π½Π½Ρ‹Ρ… COCO

πŸ›  УстанавливаСм Object Detection API

Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΡƒΡΡ‚Π°Π½Π°Π²Π»ΠΈΠ²Π°Ρ‚ΡŒ Tensorflow 2 Object Detection API Π² Π²ΠΈΠ΄Π΅ ΠΏΠ°ΠΊΠ΅Ρ‚Π° Python. Π­Ρ‚ΠΎ достаточно ΡƒΠ΄ΠΎΠ±Π½ΠΎ, Π² случаС Ссли Π²Ρ‹ экспСримСнтируСтС Π² Google Colab (ΠΏΡ€Π΅Π΄ΠΏΠΎΡ‡Ρ‚ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ) ΠΈΠ»ΠΈ Π² Jupyter. Π’ ΠΎΠ±ΠΎΠΈΡ… случаях Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ локальной инсталляции ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² ΠΈ ΠΏΡ€ΠΎΠ²ΠΎΠ΄ΠΈΡ‚ΡŒ экспСримСнты нСпосрСдствСнно Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅.

Π’Π°ΠΊΠΆΠ΅ Π΅ΡΡ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ установки Object Detection API ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ Docker, ΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π² Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ.

Если Ρƒ вас Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΡƒΡ‚ трудности Π²ΠΎ врСмя установки API ΠΈΠ»ΠΈ Π²ΠΎ врСмя создания Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ… (ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ Ρ€Π°Π·Π΄Π΅Π»Ρ‹), Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚ΡŒΡΡ ΠΊ ΡΡ‚Π°Ρ‚ΡŒΠ΅ TensorFlow 2 Object Detection API tutorial, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Π΅ΡΡ‚ΡŒ ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹Ρ… Π΄Π΅Ρ‚Π°Π»Π΅ΠΉ ΠΈ совСтов.

Для Π½Π°Ρ‡Π°Π»Π° Π΄Π°Π²Π°ΠΉΡ‚Π΅ ΠΊΠ»ΠΎΠ½ΠΈΡ€ΡƒΠ΅ΠΌ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ с API:

git clone --depth 1 https://github.com/tensorflow/models

output β†’

Cloning into 'models'...
remote: Enumerating objects: 2301, done.
remote: Counting objects: 100% (2301/2301), done.
remote: Compressing objects: 100% (2000/2000), done.
remote: Total 2301 (delta 561), reused 922 (delta 278), pack-reused 0
Receiving objects: 100% (2301/2301), 30.60 MiB | 13.90 MiB/s, done.
Resolving deltas: 100% (561/561), done.

Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ»Ρ‹-ΠΏΡ€ΠΎΡ‚ΠΎΡ‚ΠΈΠΏΡ‹ API Π² Python Ρ„ΠΎΡ€ΠΌΠ°Ρ‚, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ protoc:

cd ./models/research
protoc object_detection/protos/*.proto --python_out=.

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ шагом Π±ΡƒΠ΄Π΅Ρ‚ установка API для вСрсии TensorFlow 2 ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ pip ΠΈ Ρ„Π°ΠΉΠ» setup.py`:

cp ./object_detection/packages/tf2/setup.py .
pip install . --quiet

Если Π½Π° этом шагС Π²Ρ‹ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚Π΅ ошибки, связанныС установкой зависимых ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ², ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠΉΡ‚Π΅ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ pip install . --quiet Π²ΠΎ Π²Ρ‚ΠΎΡ€ΠΎΠΉ Ρ€Π°Π·.

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΡΡ‚ΡŒ установки Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ запустив тСст:

python object_detection/builders/model_builder_tf2_test.py

Π’ ΠΈΡ‚ΠΎΠ³Π΅ Π²Ρ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±ΡƒΠ΄Π΅Ρ‚Π΅ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ Π² консоли, Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π²Ρ€ΠΎΠ΄Π΅ этого:

[       OK ] ModelBuilderTF2Test.test_unknown_ssd_feature_extractor
----------------------------------------------------------------------
Ran 20 tests in 45.072s

OK (skipped=1)

TensorFlow Object Detection API установлСна! Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ скрипты, прСдоставляСмы этой API, для обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² изобраТСниях, Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ ΠΈΠ»ΠΈ Π΄ΠΎΡ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ.

⬇️ Π—Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ Π·Π°Ρ€Π°Π½Π΅Π΅ ΠΎΠ±ΡƒΡ‡Π΅Π½Π½ΡƒΡŽ модСль

Π”Π°Π²Π°ΠΉΡ‚Π΅ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΠΌ Ρ€Π°Π½Π΅Π΅ Π²Ρ‹Π±Ρ€Π°Π½Π½ΡƒΡŽ Π½Π°ΠΌΠΈ модСль ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8 ΠΈΠ· ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ TensorFlow ΠΈ посмотрим, ΠΊΠ°ΠΊ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π΅ для обнаруТСния ΠΎΠ±Ρ‰ΠΈΡ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², Ρ‚Π°ΠΊΠΈΡ… ΠΊΠ°ΠΊ "ΠΊΠΎΡ‚", "собака", "машина" ΠΈ ΠΏΡ€. (ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² с классами, ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹ΠΌΠΈ Π½Π°Π±ΠΎΡ€ΠΎΠΌ Π΄Π°Π½Π½Ρ‹Ρ… COCO).

ΠœΡ‹ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡΡ ΡƒΡ‚ΠΈΠ»ΠΈΡ‚ΠΎΠΉ TensorFlow get_file() для Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Π°Ρ€Ρ…ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΏΠΎ URL ΠΈ для дальнСйшСй Π΅Π΅ распаковки.

import tensorflow as tf
import pathlib

MODEL_NAME = 'ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8'
TF_MODELS_BASE_PATH = 'http://download.tensorflow.org/models/object_detection/tf2/20200711/'
CACHE_FOLDER = './cache'

def download_tf_model(model_name, cache_folder):
    model_url = TF_MODELS_BASE_PATH + model_name + '.tar.gz'
    model_dir = tf.keras.utils.get_file(
        fname=model_name, 
        origin=model_url,
        untar=True,
        cache_dir=pathlib.Path(cache_folder).absolute()
    )
    return model_dir

# Start the model download.
model_dir = download_tf_model(MODEL_NAME, CACHE_FOLDER)
print(model_dir)

output β†’

/content/cache/datasets/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8

Π’ΠΎΡ‚ ΠΊΠ°ΠΊ Π½Π° Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ выглядит структура ΠΏΠ°ΠΏΠΎΠΊ:

Cache Folder
Cache Folder

Папка checkpoint содСрТит "слСпок" ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² ΠΎΠ±ΡƒΡ‡Π΅Π½Π½ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ.

Π€Π°ΠΉΠ» pipeline.config содСрТит настройки обнаруТСния. ΠœΡ‹ Π΅Ρ‰Π΅ вСрнСмся ΠΊ этому Ρ„Π°ΠΉΠ»Ρƒ Π½ΠΈΠΆΠ΅, ΠΊΠΎΠ³Π΄Π° Π±ΡƒΠ΄Π΅ΠΌ ΠΎΠ±ΡƒΡ‡Π°Ρ‚ΡŒ Π½Π°ΡˆΡƒ модСль.

πŸ„πŸ»β€οΈ ΠžΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ

На Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ модСль способна ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Ρ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ классов, ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹Ρ… Π½Π°Π±ΠΎΡ€ΠΎΠΌ Π΄Π°Π½Π½Ρ‹Ρ… COCO (ΠΈΡ… всСго 90), Ρ‚Π°ΠΊΠΈΡ…, ΠΊΠ°ΠΊ car, bird, hot dog ΠΈ ΠΏΡ€. Π­Ρ‚ΠΈ классы Π΅Ρ‰Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π½Π°Π·Ρ‹Π²Π°Ρ‚ΡŒ ярлыками (labels).

COCO classes
COCO classes

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ изобраТСния: сайт COCO

ΠŸΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ, ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚ Π»ΠΈ модСль ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ этих классов.

Π—Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ ярлыки COCO

Object Detection API ΡƒΠΆΠ΅ содСрТит Ρ„Π°ΠΉΠ» с ΠΏΠΎΠ»Π½Ρ‹ΠΌ Π½Π°Π±ΠΎΡ€ классов (ярлыков) COCO для нашСго удобства.

import os

# Import Object Detection API helpers.
from object_detection.utils import label_map_util

# Loads the COCO labels data (class names and indices relations).
def load_coco_labels():
    # Object Detection API already has a complete set of COCO classes defined for us.
    label_map_path = os.path.join(
        'models/research/object_detection/data',
        'mscoco_complete_label_map.pbtxt'
    )
    label_map = label_map_util.load_labelmap(label_map_path)

    # Class ID to Class Name mapping.
    categories = label_map_util.convert_label_map_to_categories(
        label_map,
        max_num_classes=label_map_util.get_max_label_map_index(label_map),
        use_display_name=True
    )
    category_index = label_map_util.create_category_index(categories)

    # Class Name to Class ID mapping.
    label_map_dict = label_map_util.get_label_map_dict(label_map, use_display_name=True)

    return category_index, label_map_dict

# Load COCO labels.
coco_category_index, coco_label_map_dict = load_coco_labels()

print('coco_category_index:', coco_category_index)
print('coco_label_map_dict:', coco_label_map_dict)

output β†’

coco_category_index:
{
    1: {'id': 1, 'name': 'person'},
    2: {'id': 2, 'name': 'bicycle'},
    ...
    90: {'id': 90, 'name': 'toothbrush'},
}

coco_label_map_dict:
{
    'background': 0,
    'person': 1,
    'bicycle': 2,
    'car': 3,
    ...
    'toothbrush': 90,
}

Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ обнаруТСния

Π’ этом Ρ€Π°Π·Π΄Π΅Π»Π΅ ΠΌΡ‹ создадим Ρ‚Π°ΠΊ Π½Π°Π·Ρ‹Π²Π°Π΅ΠΌΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ обнаруТСния, которая Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½ΡƒΡŽ Π½Π°ΠΌΠΈ Ρ€Π°Π½Π΅Π΅ модСль, собствСнно, для обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ.

import tensorflow as tf

# Import Object Detection API helpers.
from object_detection.utils import config_util
from object_detection.builders import model_builder

# Generates the detection function for specific model and specific model's checkpoint
def detection_fn_from_checkpoint(config_path, checkpoint_path):
    # Build the model.
    pipeline_config = config_util.get_configs_from_pipeline_file(config_path)
    model_config = pipeline_config['model']
    model = model_builder.build(
        model_config=model_config,
        is_training=False,
    )

    # Restore checkpoints.
    ckpt = tf.compat.v2.train.Checkpoint(model=model)
    ckpt.restore(checkpoint_path).expect_partial()

    # This is a function that will do the detection.
    @tf.function
    def detect_fn(image):
        image, shapes = model.preprocess(image)
        prediction_dict = model.predict(image, shapes)
        detections = model.postprocess(prediction_dict, shapes)

        return detections, prediction_dict, tf.reshape(shapes, [-1])

    return detect_fn

inference_detect_fn = detection_fn_from_checkpoint(
    config_path=os.path.join('cache', 'datasets', MODEL_NAME, 'pipeline.config'),
    checkpoint_path=os.path.join('cache', 'datasets', MODEL_NAME, 'checkpoint', 'ckpt-0'),
)

Ѐункция inference_detect_fn ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Π½Π° Π²Ρ…ΠΎΠ΄Π΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎΠ± ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹Ρ… Π² Π½Π΅ΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°Ρ….

Π—Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ тСстовыС изобраТСния

Π”Π°Π²Π°ΠΉΡ‚Π΅ ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ Π½Π°ΠΉΡ‚ΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ Π½Π° ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ:

General Object Inference
General Object Inference

Для этого сохраним это ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Π² ΠΏΠ°ΠΏΠΊΡƒ inference/test/ нашСго ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°. Если Π²Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚Π΅ Google Colab, Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ эту ΠΏΠ°ΠΏΠΊΡƒ ΠΈ произвСсти Π·Π°Π³Ρ€ΡƒΠ·ΠΊΡƒ Ρ„Π°ΠΉΠ»Π° Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ.

Π’ΠΎΡ‚ ΠΊΠ°ΠΊ структура ΠΏΠ°ΠΏΠΎΠΊ Π΄ΠΎΠ»ΠΆΠ½Π° Π²Ρ‹Π³Π»ΡΠ΄Π΅Ρ‚ΡŒ Π½Π° Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚:

Folder structure
Folder structure

import matplotlib.pyplot as plt
%matplotlib inline

# Creating a TensorFlow dataset of just one image.
inference_ds = tf.keras.preprocessing.image_dataset_from_directory(
  directory='inference',
  image_size=(640, 640),
  batch_size=1,
  shuffle=False,
  label_mode=None
)
# Numpy version of the dataset.
inference_ds_numpy = list(inference_ds.as_numpy_iterator())

# You may preview the images in dataset like this.
plt.figure(figsize=(14, 14))
for i, image in enumerate(inference_ds_numpy):
    plt.subplot(2, 2, i + 1)
    plt.imshow(image[0].astype("uint8"))
    plt.axis("off")
plt.show()

ЗапускаСм ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ для тСстового изобраТСния

На Π΄Π°Π½Π½ΠΎΠΌ этапС ΠΌΡ‹ Π³ΠΎΡ‚ΠΎΠ²Ρ‹ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅. ΠŸΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт массива inference_ds_numpy[0] содСрТит нашС ΠΏΠ΅Ρ€Π²ΠΎΠ΅ тСстовоС ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ массива Numpy.

detections, predictions_dict, shapes = inference_detect_fn(
    inference_ds_numpy[0]
)

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€Π½ΠΎΡΡ‚ΡŒ массивов, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½Π°ΠΌ Π²Π΅Ρ€Π½ΡƒΠ»Π° функция:

boxes = detections['detection_boxes'].numpy()
scores = detections['detection_scores'].numpy()
classes = detections['detection_classes'].numpy()
num_detections = detections['num_detections'].numpy()[0]

print('boxes.shape: ', boxes.shape)
print('scores.shape: ', scores.shape)
print('classes.shape: ', classes.shape)
print('num_detections:', num_detections)

output β†’

boxes.shape:  (1, 100, 4)
scores.shape:  (1, 100)
classes.shape:  (1, 100)
num_detections: 100.0

МодСль Π²Π΅Ρ€Π½ΡƒΠ»Π° Π½Π°ΠΌ массив со 100 "обнаруТСниями". Π­Ρ‚ΠΎ Π½Π΅ ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ модСль нашла 100 ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ. Π­Ρ‚ΠΎ скорСС Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ Π½Π°ΠΌ, Ρ‡Ρ‚ΠΎ модСль ΠΈΠΌΠ΅Π΅Ρ‚ 100 ячССк ΠΈ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ максимум 100 ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ Π² ΠΎΠ΄Π½ΠΎΠΌ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ. КаТдоС "ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅" ΠΈΠΌΠ΅Π΅Ρ‚ ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ (Π²Π΅Ρ€ΠΎΡΡ‚Π½ΠΎΡΡ‚ΡŒ, score), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ ΠΎΠ± увСрСнности ΠΌΠΎΠ΄Π΅Π»ΠΈ Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ ΠΈΠΌΠ΅Π½Π½ΠΎ этот ΠΎΠ±ΡŠΠ΅ΠΊΡ‚. Π“Π°Π±Π°Ρ€ΠΈΡ‚Ρ‹ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Π½Π°ΠΉΠ΄Π΅Π½Π½ΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° хранятся Π² массивС boxes. Π Π΅ΠΉΡ‚ΠΈΠ½Π³ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ обнаруТСния хранится Π² массивС scores. Массив classes Ρ…Ρ€Π°Π½ΠΈΡ‚ ярлыки для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ "обнаруТСния".

Π”Π°Π²Π°ΠΉΡ‚Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ ΠΏΠ΅Ρ€Π²Ρ‹Π΅ 5 Ρ‚Π°ΠΊΠΈΡ… "ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠΉ":

print('First 5 boxes:')
print(boxes[0,:5])

print('First 5 scores:')
print(scores[0,:5])

print('First 5 classes:')
print(classes[0,:5])

class_names = [coco_category_index[idx + 1]['name'] for idx in classes[0]]
print('First 5 class names:')
print(class_names[:5])

output β†’

First 5 boxes:
[[0.17576033 0.84654826 0.25642633 0.88327974]
 [0.5187813  0.12410264 0.6344235  0.34545377]
 [0.5220358  0.5181462  0.6329132  0.7669856 ]
 [0.50933677 0.7045719  0.5619138  0.7446198 ]
 [0.44761637 0.51942706 0.61237675 0.75963426]]

First 5 scores:
[0.6950246 0.6343004 0.591157  0.5827219 0.5415643]

First 5 classes:
[9. 8. 8. 0. 8.]

First 5 class names:
['traffic light', 'boat', 'boat', 'person', 'boat']

МодСль Π²ΠΈΠ΄ΠΈΡ‚ свСтофор (traffic light), Ρ‚Ρ€ΠΈ Π»ΠΎΠ΄ΠΊΠΈ (boats) ΠΈ Ρ‡Π΅Π»ΠΎΠ²Π΅ΠΊΠ° (person). И ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€Π΄ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ эти ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ Π΄Π΅ΠΉΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‚ Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ.

Π’ массивС scores ΠΌΡ‹ Π²ΠΈΠ΄ΠΈΠΌ, Ρ‡Ρ‚ΠΎ модСль Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ ΡƒΠ²Π΅Ρ€Π΅Π½Π½Π° (с 70% Π²Π΅Ρ€ΠΎΡΡ‚Π½ΠΎΡΡ‚ΡŒΡŽ) Π² Π½Π°ΠΉΠ΄Π΅Π½Π½ΠΎΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π΅ класса traffic light.

ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ элСмСнт массива boxes прСдставляСт собой ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ [y1, x1, y2, x2], Π³Π΄Π΅ (x1, y1) ΠΈ (x2, y2) соотвСтствСнно ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ Π»Π΅Π²ΠΎΠ³ΠΎ Π²Π΅Ρ€Ρ…Π½Π΅Π³ΠΎ ΠΈ ΠΏΡ€Π°Π²ΠΎΠ³ΠΎ Π½ΠΈΠΆΠ½Π΅Π³ΠΎ ΡƒΠ³Π»ΠΎΠ² Π³Π°Π±Π°Ρ€ΠΈΡ‚Π½ΠΎΠ³ΠΎ ΠΏΡ€ΡΠΌΠΎΡƒΠ³ΠΎΠ»ΡŒΠ½ΠΈΠΊΠ°.

ΠŸΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ Π²ΠΈΠ·ΡƒΠ°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π³Π°Π±Π°Ρ€ΠΈΡ‚Π½Ρ‹Π΅ ΠΏΡ€ΡΠΌΠΎΡƒΠ³ΠΎΠ»ΡŒΠ½ΠΈΠΊΠΈ:

# Importing Object Detection API helpers.
from object_detection.utils import visualization_utils

# Visualizes the bounding boxes on top of the image.
def visualize_detections(image_np, detections, category_index):
    label_id_offset = 1
    image_np_with_detections = image_np.copy()

    visualization_utils.visualize_boxes_and_labels_on_image_array(
        image_np_with_detections,
        detections['detection_boxes'][0].numpy(),
        (detections['detection_classes'][0].numpy() + label_id_offset).astype(int),
        detections['detection_scores'][0].numpy(),
        category_index,
        use_normalized_coordinates=True,
        max_boxes_to_draw=200,
        min_score_thresh=.4,
        agnostic_mode=False,
    )

    plt.figure(figsize=(12, 16))
    plt.imshow(image_np_with_detections)
    plt.show()

# Visualizing the detections.
visualize_detections(
    image_np=tf.cast(inference_ds_numpy[0][0], dtype=tf.uint32).numpy(),
    detections=detections,
    category_index=coco_category_index,
)

Π’ ΠΈΡ‚ΠΎΠ³Π΅ ΠΌΡ‹ ΡƒΠ²ΠΈΠ΄ΠΈΠΌ:

Inference result
Inference result

Π’ Ρ‚ΠΎ ΠΆΠ΅ врСмя, Ссли ΠΌΡ‹ ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ Π½Π° тСкстовом ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ ΠΌΡ‹ ΡƒΠ²ΠΈΠ΄ΠΈΠΌ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅:

Inference result for text image
Inference result for text image

МодСль Π½Π΅ смогла Π½Π°ΠΉΡ‚ΠΈ Π½ΠΈΡ‡Π΅Π³ΠΎ Π² этом ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ. Π­Ρ‚ΠΎ ΠΊΠ°ΠΊ-Ρ€Π°Π· Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ собираСмся ΠΈΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ ΠΈ Ρ‡Π΅ΠΌΡƒ Ρ…ΠΎΡ‚ΠΈΠΌ Π½Π°ΡƒΡ‡ΠΈΡ‚ΡŒ Π½Π°ΡˆΡƒ модСль - Π²ΠΈΠ΄Π΅Ρ‚ΡŒ приставки https:// Π² тСкстовых изобраТСниях.

πŸ“ ΠŸΠΎΠ΄Π³ΠΎΡ‚Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… для Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ

Для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π°ΡƒΡ‡ΠΈΡ‚ΡŒ модСль ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8 ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Ρ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½Π΅ Π±Ρ‹Π»ΠΈ описаны Π² Π½Π°Π±ΠΎΡ€Π΅ Π΄Π°Π½Π½Ρ‹Ρ… COCO Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΈΡ‚ΡŒ свой Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ "Π΄ΠΎΡƒΡ‡ΠΈΡ‚ΡŒ" модСль Π½Π° Π½Π΅ΠΌ.

Наборы Π΄Π°Π½Π½Ρ‹Ρ… для Π·Π°Π΄Π°Ρ‡ΠΈ обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² состоят ΠΈΠ· Π΄Π²ΡƒΡ… ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ²:

  1. БобствСнно само ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΏΠ΅Ρ‡Π°Ρ‚Π½ΠΎΠΉ странички ΠΊΠ½ΠΈΠ³ΠΈ ΠΈΠ»ΠΈ ΠΆΡƒΡ€Π½Π°Π»Π°)
  2. Π“Π°Π±Π°Ρ€ΠΈΡ‚Π½Ρ‹Π΅ ΠΏΡ€ΡΠΌΠΎΡƒΠ³ΠΎΠ»ΡŒΠ½ΠΈΠΊΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‚ Π³Π΄Π΅ ΠΈΠΌΠ΅Π½Π½ΠΎ Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ располоТСны ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹.

Bounding Boxes
Bounding Boxes

Π’ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π²Ρ‹ΡˆΠ΅ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ Π»Π΅Π²ΠΎΠ³ΠΎ Π²Π΅Ρ€Ρ…Π½Π΅Π³ΠΎ ΠΈ ΠΏΡ€Π°Π²ΠΎΠ³ΠΎ Π½ΠΈΠΆΠ½Π΅Π³ΠΎ ΡƒΠ³Π»ΠΎΠ² ΠΈΠΌΠ΅ΡŽΡ‚ Π°Π±ΡΠΎΠ»ΡŽΡ‚Π½Ρ‹Π΅ значСния (Π² пиксСлях). Π’Π°ΠΊΠΆΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‚ Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½Ρ‹Π΅ способы записи ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² Ρ‚Π°ΠΊΠΈΡ… Π³Π°Π±Π°Ρ€ΠΈΡ‚Π½Ρ‹Ρ… ΠΏΡ€ΡΠΌΠΎΡƒΠ³ΠΎΠ»ΡŒΠ½ΠΈΠΊΠΎΠ². НапримСр, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΏΡ€ΡΠΌΠΎΡƒΠ³ΠΎΠ»ΡŒΠ½ΠΈΠΊ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π΅Π³ΠΎ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ Ρ†Π΅Π½Ρ‚Ρ€Π°, Π° Ρ‚Π°ΠΊ ΠΆΠ΅ ΡˆΠΈΡ€ΠΈΠ½Ρ‹ ΠΈ высоты. ΠœΡ‹ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ значСния ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ (ΠΏΡ€ΠΎΡ†Π΅Π½Ρ‚ ΠΎΡ‚ ΡˆΠΈΡ€ΠΈΠ½Ρ‹ ΠΈΠ»ΠΈ высоты изобраТСния). Но Π² Ρ†Π΅Π»ΠΎΠΌ, Π΄ΡƒΠΌΠ°ΡŽ идСя понятна: модСль Π΄ΠΎΠ»ΠΆΠ½Π° Π·Π½Π°Ρ‚ΡŒ Π³Π΄Π΅ ΠΈΠΌΠ΅Π½Π½ΠΎ Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ находится Ρ‚ΠΎΡ‚ ΠΈΠ»ΠΈ ΠΈΠ½ΠΎΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚.

Вопрос Π² Ρ‚ΠΎΠΌ, Π³Π΄Π΅ ΠΆΠ΅ Π½Π°ΠΌ Π²Π·ΡΡ‚ΡŒ Ρ‚Π°ΠΊΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Π΅ для Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ. Π£ нас Π΅ΡΡ‚ΡŒ Ρ‚Ρ€ΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°:

  1. Π’ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΈΠΌΠ΅ΡŽΡ‰ΠΈΠΌΡΡ Π½Π°Π±ΠΎΡ€ΠΎΠΌ Π΄Π°Π½Π½Ρ‹Ρ….
  2. Π‘Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹ΠΉ искусствСнный Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ….
  3. Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ ΠΏΡƒΡ‚Π΅ΠΌ фотографирования ΠΈΠ»ΠΈ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Ρ€Π΅Π°Π»ΡŒΠ½Ρ‹Ρ… ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ с тСкстом ΠΈ https:// ссылками ΠΈ дальнСйшСй Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠ΅ΠΉ (ΡƒΠΊΠ°Π·Π°Π½ΠΈΠ΅ΠΌ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²) ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ изобраТСния Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ.

Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ β„–1: ИспользованиС ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… Π½Π°Π±ΠΎΡ€ΠΎΠ² Π΄Π°Π½Π½Ρ‹Ρ…

Π•ΡΡ‚ΡŒ мноТСство общСдоступных Π½Π°Π±ΠΎΡ€ΠΎΠ² Π΄Π°Π½Π½Ρ‹Ρ…. ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌΠΈ рСсурсами для поиска подходящСго Π½Π°Π±ΠΎΡ€Π°:

πŸ’š Если Ρƒ вас получится Π½Π°ΠΉΡ‚ΠΈ подходящий Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… с Π»ΠΈΡ†Π΅Π½Π·ΠΈΠ΅ΠΉ, ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‰Π΅ΠΉ Π΅Π³ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ, Ρ‚ΠΎ это, ΠΏΠΎΠΆΠ°Π»ΡƒΠΉ, Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ быстрый способ Π½Π°Ρ‡Π°Ρ‚ΡŒ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΡƒ ΠΌΠΎΠ΄Π΅Π»ΠΈ.

πŸ’” Но ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ ΠΌΠ½Π΅ Π½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π½Π°ΠΉΡ‚ΠΈ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ…, содСрТащий изобраТСния ΠΊΠ½ΠΈΠ³ со ссылками ΠΈ ΠΈΡ… ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π°ΠΌΠΈ.

Π­Ρ‚ΠΎΡ‚ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ Π½Π°ΠΌ прийдСтся ΠΏΡ€ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ.

Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ β„–2: Π“Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ искусствСнного Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ…

Π‘ΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‚ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ keras_ocr), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³Π»ΠΈ Π±Ρ‹ Π½Π°ΠΌ ΠΏΠΎΠΌΠΎΡ‡ΡŒ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ случайный тСкст, ΠΏΠΎΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ Π² Π½Π΅Π³ΠΎ ссылку ΠΈ ΠΎΡ‚Ρ€ΠΈΡΠΎΠ²Π°Ρ‚ΡŒ тСкст Π½Π° Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Ρ… Ρ„ΠΎΠ½Π°Ρ… ΠΈ с Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹ΠΌΠΈ искаТСниями.

πŸ’š ΠŸΡ€Π΅ΠΈΠΌΡƒΡ‰Π΅ΡΡ‚Π²ΠΎ Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π° Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ ΠΎΠ½ Π΄Π°Π΅Ρ‚ Π½Π°ΠΌ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ экзСмпляры Π΄Π°Π½Π½Ρ‹Ρ… с Ρ€Π°Π·Π½Ρ‹ΠΌΠΈ ΡˆΡ€ΠΈΡ„Ρ‚Π°ΠΌΠΈ, Π»ΠΈΠ³Π°Ρ‚ΡƒΡ€Π°ΠΌΠΈ, Ρ†Π²Π΅Ρ‚Π°ΠΌΠΈ тСкста ΠΈ Ρ„ΠΎΠ½Π°. Π­Ρ‚ΠΎ ΠΏΠΎΠΌΠΎΠ³Π»ΠΎ Π±Ρ‹ Π½Π°ΠΌ ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ пСрСучСнности ΠΌΠΎΠ΄Π΅Π»ΠΈ. МодСль ΠΌΠΎΠ³Π»Π°-Π±Ρ‹ Π»Π΅Π³ΠΊΠΎ ΠΎΠ±ΠΎΠ±Ρ‰Π°Ρ‚ΡŒ свои "знания" Π² случаС с изобраТСниями, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΎΠ½Π° Π½Π΅ Π²ΠΈΠ΄Π΅Π»Π° Ρ€Π°Π½Π΅Π΅.

πŸ’š Π­Ρ‚ΠΎΡ‚ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ Π΄Π°Π΅Ρ‚ Π½Π°ΠΌ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ€Π°Π·Π½Ρ‹Π΅ Ρ‚ΠΈΠΏΡ‹ ссылок, Ρ‚Π°ΠΊΠΈΡ… ΠΊΠ°ΠΊ: http://, http://, ftp://, tcp:// ΠΈ ΠΏΡ€. Π’Π΅Π΄ΡŒ Π½Π°ΠΉΡ‚ΠΈ мноТСство Ρ€Π΅Π°Π»ΡŒΠ½Ρ‹Ρ… ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ с Ρ€Π°Π·Π½Ρ‹ΠΌΠΈ Ρ‚ΠΈΠΏΠ°ΠΌΠΈ ссылок ΠΌΠΎΠ³Π»ΠΎ Π±Ρ‹ ΡΡ‚Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΎΠΉ.

πŸ’š Π•Ρ‰Π΅ ΠΎΠ΄Π½ΠΈΠΌ прСимущСством этого ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π° являСтся Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΡΡ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ сколько Ρ…ΠΎΡ‚ΠΈΠΌ. ΠœΡ‹ Π½Π΅ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Ρ‹ количСством страниц со ссылками Π² ΠΊΠ½ΠΈΠ³Π΅, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ Π½Π°ΠΌ ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π½Π°ΠΉΡ‚ΠΈ. Π£Π²Π΅Π»ΠΈΡ‡Π΅Π½ΠΈΠ΅ Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ… ΠΌΠΎΠΆΠ΅Ρ‚ Π² ΠΈΡ‚ΠΎΠ³Π΅ ΡƒΠ»ΡƒΡ‡ΡˆΠΈΡ‚ΡŒ Ρ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒ ΠΌΠΎΠ΄Π΅Π»ΠΈ.

πŸ’” Π‘ Π΄Ρ€ΡƒΠ³ΠΎΠΉ стороны, сущСствуСт Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π½Π΅ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎΠ³ΠΎ использования Ρ‚Π°ΠΊΠΎΠ³ΠΎ Π³Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€Π°, Ρ‡Ρ‚ΠΎ Π² ΠΈΡ‚ΠΎΠ³Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ привСсти ΠΊ Π½Π°Π±ΠΎΡ€Ρƒ
Π΄Π°Π½Π½Ρ‹Ρ…, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ сущСствСнно ΠΎΡ‚Π»ΠΈΡ‡Π°Ρ‚ΡŒΡΡ ΠΎΡ‚ Ρ€Π΅Π°Π»ΡŒΠ½Ρ‹Ρ… ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ. НапримСр, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΡˆΠΈΠ±ΠΎΡ‡Π½ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π½Π΅ΠΏΡ€Π°Π²Π΄ΠΎΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Π΅ ΠΈΠ·Π³ΠΈΠ±Ρ‹ страниц (Π²ΠΎΠ»Π½Π° вмСсто Π΄ΡƒΠ³ΠΈ) ΠΈΠ»ΠΈ Π½Π΅ΠΏΡ€Π°Π²Π΄ΠΎΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Π΅ Ρ„ΠΎΠ½Ρ‹. МодСль Π² Ρ‚Π°ΠΊΠΎΠΌ ΠΌΠΎΠΆΠ΅Ρ‚ Π½Π΅ ΠΎΠ±ΠΎΠ±Ρ‰ΠΈΡ‚ΡŒ свои "знания" Π½Π° изобраТСния ΠΈΠ· Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ ΠΌΠΈΡ€Π°.

Π­Ρ‚ΠΎΡ‚ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ ΠΌΠ½Π΅ каТСтся ΠΎΡ‡Π΅Π½ΡŒ ΠΌΠ½ΠΎΠ³ΠΎΠΎΠ±Π΅Ρ‰Π°ΡŽΡ‰ΠΈΠΌ. Он ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠΌΠΎΡ‡ΡŒ Π½Π°ΠΌ ΠΏΡ€Π΅ΠΎΠ΄ΠΎΠ»Π΅Ρ‚ΡŒ мноТСство нСдостатков ΠΌΠΎΠ΄Π΅Π»ΠΈ (ΠΎ Π½ΠΈΡ… ΠΌΡ‹ упомянСм Π½ΠΈΠΆΠ΅ Π² ΡΡ‚Π°Ρ‚ΡŒΠ΅). Π― ΠΏΠΎΠΊΠ° Π΅Ρ‰Π΅ Π½Π΅ ΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Π» ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ этот ΠΏΠΎΠ΄Ρ…ΠΎΠ΄, Π½ΠΎ, Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, это Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚ΠΎΠΌ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΠΉ ΡΡ‚Π°Ρ‚ΡŒΠΈ.

Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ β„–3: Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ… Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ

НаиболСС прямолинСйный способ - это Π²Π·ΡΡ‚ΡŒ ΠΊΠ½ΠΈΠ³Ρƒ (ΠΈΠ»ΠΈ ΠΊΠ½ΠΈΠ³ΠΈ), ΡΡ„ΠΎΡ‚ΠΎΠ³Ρ€Π°Ρ„ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ странички, содСрТащиС ссылки ΠΈ ΠΎΠ±ΠΎΠ·Π½Π°Ρ‡ΠΈΡ‚ΡŒ Π»ΠΎΠΊΠ°Ρ†ΠΈΠΈ прСфиксов https:// для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ странички Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ.

Π₯ΠΎΡ€ΠΎΡˆΠ°Ρ Π½ΠΎΠ²ΠΎΡΡ‚ΡŒ Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ…, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π½Π°ΠΌ Π½ΡƒΠΆΠ΅Π½, ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ достаточно нСбольшим (сотни ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π±ΡƒΠ΄Π΅Ρ‚ достаточно). Π­Ρ‚ΠΎ обусловлСно Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ Π½Π΅ собираСмся Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ модСль с нуля. ВмСсто этого ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ "Π΄ΠΎΡƒΡ‡ΠΈΠ²Π°Ρ‚ΡŒ" ΡƒΠΆΠ΅ ΠΎΠ±ΡƒΡ‡Π΅Π½Π½ΡƒΡŽ модСль (см. transfer learning ΠΈ few-shot learning).

πŸ’š Π’ Π΄Π°Π½Π½ΠΎΠΌ случаС Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… Π±ΡƒΠ΄Π΅Ρ‚ максимально ΠΏΡ€ΠΈΠ±Π»ΠΈΠΆΠ΅Π½ ΠΊ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌΡƒ ΠΌΠΈΡ€Ρƒ. ΠœΡ‹ Π² Π±ΡƒΠΊΠ²Π°Π»ΡŒΠ½ΠΎΠΌ смыслС возьмСм ΠΊΠ½ΠΈΠ³Ρƒ, сфотографируСм странички с Ρ€Π΅Π°Π»ΡŒΠ½Ρ‹ΠΌΠΈ ΡˆΡ€ΠΈΡ„Ρ‚Π°ΠΌΠΈ, ΠΈΠ·Π³ΠΈΠ±Π°ΠΌΠΈ, тСнями ΠΈ Ρ†Π²Π΅Ρ‚Π°ΠΌΠΈ.

πŸ’” Π‘ Π΄Ρ€ΡƒΠ³ΠΎΠΉ стороны, Π΄Π°ΠΆΠ΅ с ΡƒΡ‡Π΅Ρ‚ΠΎΠΌ Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ Π½ΡƒΠΆΠ½Ρ‹ всСго сотни страничСк, Ρ€Π°Π±ΠΎΡ‚Π° ΠΏΠΎ сбору Ρ‚Π°ΠΊΠΈΡ… страничСк ΠΈ ΠΈΡ… дальнСйшСй Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ Π·Π°Π½ΡΡ‚ΡŒ достаточно ΠΌΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ.

πŸ’” ВяТСло Π½Π°ΠΉΡ‚ΠΈ Ρ€Π°Π·Π½Ρ‹Π΅ ΠΊΠ½ΠΈΠ³ΠΈ ΠΈ ΠΆΡƒΡ€Π½Π°Π»Ρ‹ с Ρ€Π°Π·Π½Ρ‹ΠΌΠΈ ΡˆΡ€ΠΈΡ„Ρ‚Π°ΠΌΠΈ, Ρ‚ΠΈΠΏΠ°ΠΌΠΈ ссылок, с Ρ€Π°Π·Π½Ρ‹ΠΌΠΈ Ρ„ΠΎΠ½Π°ΠΌΠΈ ΠΈ Π»ΠΈΠ³Π°Ρ‚ΡƒΡ€Π°ΠΌΠΈ. Π’ ΠΈΡ‚ΠΎΠ³Π΅ Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ… Π±ΡƒΠ΄Π΅Ρ‚ достаточно ΡƒΠ·ΠΊΠΎΠ½Π°ΠΏΡ€Π°Π²Π»Π΅Π½Π½Ρ‹ΠΌ (Ρƒ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±ΡƒΠ΄ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ ΠΊΠ½ΠΈΠ³ΠΈ со ΡˆΡ€ΠΈΡ„Ρ‚Π°ΠΌΠΈ ΠΈ Ρ„ΠΎΠ½Π°ΠΌΠΈ ΠΏΠΎΡ…ΠΎΠΆΠΈΠΌΠΈ Π½Π° ваши).

ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Ρ†Π΅Π»ΡŒΡŽ этой ΡΡ‚Π°Ρ‚ΡŒΠΈ, ΠΊΠ°ΠΊ Π±Ρ‹Π»ΠΎ упомянуто Π²Ρ‹ΡˆΠ΅, Π½Π΅ являСтся созданиС ΠΌΠΎΠ΄Π΅Π»ΠΈ, которая Π΄ΠΎΠ»ΠΆΠ½Π° Π²Ρ‹ΠΈΠ³Ρ€Π°Ρ‚ΡŒ сорСвнованиС ΠΏΠΎ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΡŽ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠΉΡ‚ΠΈ ΠΏΠΎ ΠΏΡƒΡ‚ΠΈ создания ΠΌΠΎΠ΄Π΅Π»ΠΈ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ.

ΠžΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌ Ρ„ΠΎΡ‚ΠΎ для Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ…

Π― сфотографировал 125 страничСк ΠΎΠ΄Π½ΠΎΠΉ ΠΊΠ½ΠΈΠ³ΠΈ, Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… нашСл https:// ссылки.

Raw Dataset
Raw Dataset

ВсС изобраТСния Π±Ρ‹Π»ΠΈ ΠΏΠΎΠΌΠ΅Ρ‰Π΅Π½Ρ‹ Π² ΠΏΠ°ΠΏΠΊΡƒ dataset/printed_links/raw.

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ шаг - ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ. Π”Π°Π²Π°ΠΉΡ‚Π΅ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΠΌ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ прСобразования:

  • ИзмСним Ρ€Π°Π·ΠΌΠ΅Ρ€ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ изобраТСния Ρ‚Π°ΠΊ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΡ… ΡˆΠΈΡ€ΠΈΠ½Π° составила 1024px (ΠΈΠ·Π½Π°Ρ‡Π°Π»ΡŒΠ½ΠΎ изобраТСния Π±Ρ‹Π»ΠΈ чСрСсчур большими с ΡˆΠΈΡ€ΠΈΠ½ΠΎΠΉ Π² 3024px)
  • ΠžΠ±Ρ€Π΅ΠΆΠ΅ΠΌ ΠΊΠ°ΠΆΠ΄ΠΎΠ΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Ρ‚Π°ΠΊ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΠ½ΠΎ стало ΠΊΠ²Π°Π΄Ρ€Π°Ρ‚Π½Ρ‹ΠΌ (это Π΄Π΅Π»Π°Ρ‚ΡŒ Π½Π΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ, ΠΌΠΎΠΆΠ½ΠΎ просто ΡΠΆΠ°Ρ‚ΡŒ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Π΄ΠΎ ΠΊΠ²Π°Π΄Ρ€Π°Ρ‚Π½Ρ‹Ρ… ΠΏΡ€ΠΎΠΏΠΎΡ€Ρ†ΠΈΠΉ, Π½Π΅ обрСзая Π΅Π³ΠΎ, Π½ΠΎ я Ρ…ΠΎΡ‚Π΅Π» ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ СстСствСнныС ΠΏΡ€ΠΎΠΏΠΎΡ€Ρ†ΠΈΠΈ прСфиксов https: ΠΏΠ΅Ρ€Π΅Π΄ ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅ΠΌ).
  • Π Π°Π·Π²Π΅Ρ€Π½Π΅ΠΌ ΠΊΠ°ΠΆΠ΄ΠΎΠ΅ изобраТСния Π΄ΠΎ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎΠΉ ΠΎΡ€ΠΈΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ, ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΠ² ΠΌΠ΅Ρ‚Π°Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠ· Ρ‚Π΅Π³Π° exif.
  • Π‘Π΄Π΅Π»Π°Π΅ΠΌ ΠΊΠ°ΠΆΠ΄ΠΎΠ΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Ρ‡Π΅Ρ€Π½ΠΎ-Π±Π΅Π»Ρ‹ΠΌ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΌΡ‹ Π½Π΅ Ρ…ΠΎΡ‚ΠΈΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ модСль Π±Ρ€Π°Π»Π° Π²ΠΎ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Ρ†Π²Π΅Ρ‚.
  • Π£Π²Π΅Π»ΠΈΡ‡ΠΈΠΌ ΡΡ€ΠΊΠΎΡΡ‚ΡŒ
  • Π£Π²Π΅Π»ΠΈΡ‡ΠΈΠΌ контраст
  • Π£Π²Π΅Π»ΠΈΡ‡ΠΈΠΌ Ρ€Π΅Π·ΠΊΠΎΡΡ‚ΡŒ

Π‘Ρ‚ΠΎΠΈΡ‚ΡŒ ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ Π² Π±ΡƒΠ΄ΡƒΡ‰Π΅ΠΌ, ΠΌΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡ‚ΡŒ эти ΠΆΠ΅ манипуляции Π½Π°Π΄ изобраТСниями ΠΏΠ΅Ρ€Π΅Π΄ Ρ‚Π΅ΠΌ, ΠΊΠ°ΠΊ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ ΠΈΡ… Π½Π° Π²Ρ…ΠΎΠ΄ нашСй ΠΌΠΎΠ΄Π΅Π»ΠΈ (Ссли Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΎΡ‡Π½Ρ‹Π΅ изобраТСния Π±Ρ‹Π»ΠΈ Ρ‡Π΅Ρ€Π½ΠΎ-Π±Π΅Π»Ρ‹ΠΌΠΈ ΠΈ ΠΊΠ²Π°Π΄Ρ€Π°Ρ‚Π½Ρ‹ΠΌΠΈ, Ρ‚ΠΎ ΠΈ Ρ€Π΅Π°Π»ΡŒΠ½Ρ‹Π΅ изобраТСния, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΎΡ‚ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ Π² Π½Π°ΡˆΡƒ модСль Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Ρ‚Π°ΠΊΠΈΠΌΠΈ ΠΆΠ΅ ΠΊΠ²Π°Π΄Ρ€Π°Ρ‚Π½Ρ‹ΠΌΠΈ ΠΈ Ρ‡Π΅Ρ€Π½ΠΎ-Π±Π΅Π»Ρ‹ΠΌΠΈ).

ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ всС Π²Ρ‹ΡˆΠ΅ΠΎΠΏΠΈΡΠ°Π½Π½Ρ‹Π΅ трансформации ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ Python:

import os
import math
import shutil

from pathlib import Path
from PIL import Image, ImageOps, ImageEnhance

# Resize an image.
def preprocess_resize(target_width):
    def preprocess(image: Image.Image, log) -> Image.Image:
        (width, height) = image.size
        ratio = width / height

        if width > target_width:
            target_height = math.floor(target_width / ratio)
            log(f'Resizing: To size {target_width}x{target_height}')
            image = image.resize((target_width, target_height))
        else:
            log('Resizing: Image already resized, skipping...')

        return image
    return preprocess

# Crop an image.
def preprocess_crop_square():
    def preprocess(image: Image.Image, log) -> Image.Image:
        (width, height) = image.size

        left = 0
        top = 0
        right = width
        bottom = height

        crop_size = min(width, height)

        if width >= height:
            # Horizontal image.
            log(f'Squre cropping: Horizontal {crop_size}x{crop_size}')
            left = width // 2 - crop_size // 2
            right = left + crop_size
        else:
            # Vetyical image.
            log(f'Squre cropping: Vertical {crop_size}x{crop_size}')
            top = height // 2 - crop_size // 2
            bottom = top + crop_size

        image = image.crop((left, top, right, bottom))
        return image
    return preprocess

# Apply exif transpose to an image.
def preprocess_exif_transpose():
    # @see: https://pillow.readthedocs.io/en/stable/reference/ImageOps.html
    def preprocess(image: Image.Image, log) -> Image.Image:
        log('EXif transpose')
        image = ImageOps.exif_transpose(image)
        return image
    return preprocess

# Apply color transformations to the image.
def preprocess_color(brightness, contrast, color, sharpness):
    # @see: https://pillow.readthedocs.io/en/3.0.x/reference/ImageEnhance.html
    def preprocess(image: Image.Image, log) -> Image.Image:
        log('Coloring')

        enhancer = ImageEnhance.Color(image)
        image = enhancer.enhance(color)

        enhancer = ImageEnhance.Brightness(image)
        image = enhancer.enhance(brightness)

        enhancer = ImageEnhance.Contrast(image)
        image = enhancer.enhance(contrast)

        enhancer = ImageEnhance.Sharpness(image)
        image = enhancer.enhance(sharpness)

        return image
    return preprocess

# Image pre-processing pipeline.
def preprocess_pipeline(src_dir, dest_dir, preprocessors=[], files_num_limit=0, override=False):
    # Create destination folder if not exists.
    Path(dest_dir).mkdir(parents=False, exist_ok=True)

    # Get the list of files to be copied.
    src_file_names = os.listdir(src_dir)
    files_total = files_num_limit if files_num_limit > 0 else len(src_file_names)
    files_processed = 0

    # Logger function.
    def preprocessor_log(message):
        print('  ' + message)

    # Iterate through files.
    for src_file_index, src_file_name in enumerate(src_file_names):
        if files_num_limit > 0 and src_file_index >= files_num_limit:
            break

        # Copy file.        
        src_file_path = os.path.join(src_dir, src_file_name)
        dest_file_path = os.path.join(dest_dir, src_file_name)

        progress = math.floor(100 * (src_file_index + 1) / files_total)
        print(f'Image {src_file_index + 1}/{files_total} | {progress}% |  {src_file_path}')

        if not os.path.isfile(src_file_path):
            preprocessor_log('Source is not a file, skipping...\n')
            continue

        if not override and os.path.exists(dest_file_path):
            preprocessor_log('File already exists, skipping...\n')
            continue

        shutil.copy(src_file_path, dest_file_path)
        files_processed += 1

        # Preprocess file.
        image = Image.open(dest_file_path)

        for preprocessor in preprocessors:
            image = preprocessor(image, preprocessor_log)

        image.save(dest_file_path, quality=95)
        print('')

    print(f'{files_processed} out of {files_total} files have been processed')

# Launching the image preprocessing pipeline.
preprocess_pipeline(
    src_dir='dataset/printed_links/raw',
    dest_dir='dataset/printed_links/processed',
    override=True,
    # files_num_limit=1,
    preprocessors=[
        preprocess_exif_transpose(),
        preprocess_resize(target_width=1024),
        preprocess_crop_square(),
        preprocess_color(brightness=2, contrast=1.3, color=0, sharpness=1),
    ]
)

Π’ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ всС ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½Ρ‹Π΅ изобраТСния Π±ΡƒΠ΄ΡƒΡ‚ сохранСны Π² ΠΏΠ°ΠΏΠΊΠ΅ dataset/printed_links/processed.

Dataset Processed
Dataset Processed

ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Π΅ изобраТСния ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

import matplotlib.pyplot as plt
import numpy as np

def preview_images(images_dir, images_num=1, figsize=(15, 15)):
    image_names = os.listdir(images_dir)
    image_names = image_names[:images_num]

    num_cells = math.ceil(math.sqrt(images_num))
    figure = plt.figure(figsize=figsize)

    for image_index, image_name in enumerate(image_names):
        image_path = os.path.join(images_dir, image_name)
        image = Image.open(image_path)

        figure.add_subplot(num_cells, num_cells, image_index + 1)
        plt.imshow(np.asarray(image))

    plt.show()

preview_images('dataset/printed_links/processed', images_num=4, figsize=(16, 16))

Π£ΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ ΠΈ Π³Π°Π±Π°Ρ€ΠΈΡ‚Ρ‹ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² для нашСго Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ…

Для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ ΠΈ Π³Π°Π±Π°Ρ€ΠΈΡ‚Ρ‹ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² (прСфиксов https://) Π² нашСм Π½Π°Π±ΠΎΡ€Π΅ Π΄Π°Π½Π½Ρ‹Ρ… ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΎΠΉ Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ LabelImg.

Π’Π°ΠΌ понадобится ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ LabelImg локально Π½Π° ваш ΠΊΠΎΠΌΠΏΡŒΡŽΡ‚Π΅Ρ€. Π”Π΅Ρ‚Π°Π»ΡŒΠ½ΡƒΡŽ ΠΈΠ½ΡΡ‚Ρ€ΡƒΠΊΡ†ΠΈΡŽ ΠΏΠΎ установкС Π²Ρ‹ смоТСтС Π½Π°ΠΉΡ‚ΠΈ Π² Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ LabelImg

ПослС установки LabelImg, Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ ΠΈΠ· консоли, ΡƒΠΊΠ°Π·Π°Π² ΠΏΠ°ΠΏΠΊΡƒ с изобраТСниями (Π² нашСм случаС dataset/printed_links/processed), ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ Π°Π½Π½ΠΎΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ:

labelImg dataset/printed_links/processed

Π’ ΠΎΡ‚ΠΊΡ€Ρ‹Π²ΡˆΠ΅ΠΌΡΡ ΠΎΠΊΠ½Π΅ Π²Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π°Π½Π½ΠΎΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ всС изобраТСния ΠΈΠ· ΠΏΠ°ΠΏΠΊΠΈ dataset/printed_links/processed ΠΈ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ всС изобраТСния Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ XML Π² ΠΏΠ°ΠΏΠΊΡƒ dataset/printed_links/labels/xml/.

Labeling
Labeling

Labeling Process
Labeling Process

ПослС Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ процСсса аннотирования для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ изобраТСния ΠΌΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ XML Ρ„Π°ΠΉΠ» с ΠΏΠΎΠ·ΠΈΡ†ΠΈΠ΅ΠΉ ΠΈ Π³Π°Π±Π°Ρ€ΠΈΡ‚Π°ΠΌΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°:

Labels folder structure
Labels folder structure

Π Π°Π·Π±ΠΈΠ²Π°Π΅ΠΌ ΠΎΠ±Ρ‰ΠΈΠΉ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… Π½Π° Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΎΡ‡Π½Ρ‹ΠΉ ΠΈ тСстовый Π½Π°Π±ΠΎΡ€Ρ‹

Для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ пСрСучивания ΠΈΠ»ΠΈ нСдоучивания ΠΌΠΎΠ΄Π΅Π»ΠΈ, Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Ρ€Π°Π·Π±ΠΈΡ‚ΡŒ наш ΠΎΠ±Ρ‰ΠΈΠΉ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… Π½Π° Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΎΡ‡Π½Ρ‹ΠΉ ΠΈ тСстовый Π½Π°Π±ΠΎΡ€Ρ‹. ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ 80% всСх ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ для Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ ΠΈ 20% ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ для тСстирования ΠΌΠΎΠ΄Π΅Π»ΠΈ. Π—Π°Π΄Π°Ρ‡Π° тСстового Π½Π°Π±ΠΎΡ€Π° - ΠΏΠΎΠ½ΡΡ‚ΡŒ насколько наша модСль ΠΌΠΎΠΆΠ΅Ρ‚ ΠΎΠ±ΠΎΠ±Ρ‰ΠΈΡ‚ΡŒ свои "знания" Π½Π° Π΄Π°Π½Π½Ρ‹Ρ…, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΎΠ½Π° Π½Π΅ "Π²ΠΈΠ΄Π΅Π»Π°" Ρ€Π°Π½ΡŒΡˆΠ΅.

Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ Ρ€Π°Π·Π±ΠΈΠ²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ»Ρ‹ ΠΏΡƒΡ‚Π΅ΠΌ ΠΈΡ… ΠΏΠ΅Ρ€Π΅ΠΌΠ΅ΡˆΠΈΠ²Π°Π½ΠΈΡ ΠΈ копирования Π² Ρ€Π°Π·Π½Ρ‹Π΅ ΠΏΠ°ΠΏΠΊΠΈ (Π² ΠΏΠ°ΠΏΠΊΠΈ test ΠΈ train). Π‘Ρ‚ΠΎΠΈΡ‚ ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ Ρ‚Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄, Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, Π½Π΅ являСтся ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΌ. ВмСсто физичСского размСщСния Ρ„Π°ΠΉΠ»ΠΎΠ² Π² Ρ€Π°Π·Π½Ρ‹Ρ… ΠΏΠ°ΠΏΠΊΠ°Ρ… ΠΌΡ‹ Ρ‚Π°ΠΊ ΠΆΠ΅ ΠΌΠΎΠΆΠ΅ΠΌ Ρ€Π°Π·Π±ΠΈΠ²Π°Ρ‚ΡŒ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… Π½Π° ΠΏΠΎΠ΄Π³Ρ€ΡƒΠΏΠΏΡ‹ Π½Π° Π»Π΅Ρ‚Ρƒ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ tf.data.Dataset.

import re
import random

def partition_dataset(
    images_dir,
    xml_labels_dir,
    train_dir,
    test_dir,
    val_dir,
    train_ratio,
    test_ratio,
    val_ratio,
    copy_xml
):    
    if not os.path.exists(train_dir):
        os.makedirs(train_dir)

    if not os.path.exists(test_dir):
        os.makedirs(test_dir)

    if not os.path.exists(val_dir):
        os.makedirs(val_dir)

    images = [f for f in os.listdir(images_dir)
              if re.search(r'([a-zA-Z0-9\s_\\.\-\(\):])+(.jpg|.jpeg|.png)$', f, re.IGNORECASE)]

    num_images = len(images)

    num_train_images = math.ceil(train_ratio * num_images)
    num_test_images = math.ceil(test_ratio * num_images)
    num_val_images = math.ceil(val_ratio * num_images)

    print('Intended split')
    print(f'  train: {num_train_images}/{num_images} images')
    print(f'  test: {num_test_images}/{num_images} images')
    print(f'  val: {num_val_images}/{num_images} images')

    actual_num_train_images = 0
    actual_num_test_images = 0
    actual_num_val_images = 0

    def copy_random_images(num_images, dest_dir):
        copied_num = 0

        if not num_images:
            return copied_num

        for i in range(num_images):
            if not len(images):
                break

            idx = random.randint(0, len(images)-1)
            filename = images[idx]
            shutil.copyfile(os.path.join(images_dir, filename), os.path.join(dest_dir, filename))

            if copy_xml:
                xml_filename = os.path.splitext(filename)[0]+'.xml'
                shutil.copyfile(os.path.join(xml_labels_dir, xml_filename), os.path.join(dest_dir, xml_filename))

            images.remove(images[idx])
            copied_num += 1

        return copied_num

    actual_num_train_images = copy_random_images(num_train_images, train_dir)
    actual_num_test_images = copy_random_images(num_test_images, test_dir)
    actual_num_val_images = copy_random_images(num_val_images, val_dir)

    print('\n', 'Actual split')
    print(f'  train: {actual_num_train_images}/{num_images} images')
    print(f'  test: {actual_num_test_images}/{num_images} images')
    print(f'  val: {actual_num_val_images}/{num_images} images')

partition_dataset(
    images_dir='dataset/printed_links/processed',
    train_dir='dataset/printed_links/partitioned/train',
    test_dir='dataset/printed_links/partitioned/test',
    val_dir='dataset/printed_links/partitioned/val',
    xml_labels_dir='dataset/printed_links/labels/xml',
    train_ratio=0.8,
    test_ratio=0.2,
    val_ratio=0,
    copy_xml=True
)

ПослС разбития нашСго Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ… структура ΠΏΠ°ΠΏΠΎΠΊ Π΄ΠΎΠ»ΠΆΠ½Π° Π²Ρ‹Π³Π»ΡΠ΄Π΅Ρ‚ΡŒ Ρ‚Π°ΠΊ:

dataset/
└── printed_links
    β”œβ”€β”€ labels
    β”‚   └── xml
    β”œβ”€β”€ partitioned
    β”‚   β”œβ”€β”€ test
    β”‚   └── train
    β”‚       β”œβ”€β”€ IMG_9140.JPG
    β”‚       β”œβ”€β”€ IMG_9140.xml
    β”‚       β”œβ”€β”€ IMG_9141.JPG
    β”‚       β”œβ”€β”€ IMG_9141.xml
    β”‚       ...
    β”œβ”€β”€ processed
    └── raw

ЭкспортируСм Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ…

ПослСднСй манипуляциСй Π½Π°Π΄ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ произвСсти, Π±ΡƒΠ΄Π΅Ρ‚ конвСртация Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ TFRecord. Π€ΠΎΡ€ΠΌΠ°Ρ‚ TFRecord ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ TensorFlow для хранСния ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ записСй (Π² нашСм случаС для хранСния ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ).

Π‘Π½Π°Ρ‡Π°Π»Π° создадим Π΄Π²Π΅ ΠΏΠ°ΠΏΠΊΠΈ: ΠΎΠ΄Π½Ρƒ для хранСния Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠΉ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ CSV, Π΄Ρ€ΡƒΠ³ΡƒΡŽ для хранСния нашСй Ρ„ΠΈΠ½Π°Π»ΡŒΠ½ΠΎΠΉ вСрсии Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ TFRecord.

mkdir -p dataset/printed_links/labels/csv
mkdir -p dataset/printed_links/tfrecords

Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ»-ΠΏΡ€ΠΎΡ‚ΠΎΡ‚ΠΈΠΏ dataset/printed_links/labels/label_map.pbtxt с классами ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ наша модСль Π΄ΠΎΠ»ΠΆΠ½Π° Π½Π°ΡƒΡ‡ΠΈΡ‚ΡŒΡΡ Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Π²Π°Ρ‚ΡŒ. Π’ нашСм случаС Ρƒ нас Π±ΡƒΠ΄Π΅Ρ‚ всСго ΠΎΠ΄ΠΈΠ½ класс, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ Π½Π°Π·ΠΎΠ²Π΅ΠΌ http. Π‘ΠΎΠ΄Π΅Ρ€ΠΆΠΈΠΌΠΎΠ΅ Ρ„Π°ΠΉΠ»Π° Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ:

item {
  id: 1
  name: 'http'
}

Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ Π³ΠΎΡ‚ΠΎΠ²Ρ‹ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ TFRecord ΠΈΠ· Π½Π°Π±ΠΎΡ€Π° jpg ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ ΠΈ Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠΉ Π² xml Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅:

import os
import io
import math
import glob
import tensorflow as tf
import pandas as pd
import xml.etree.ElementTree as ET
from PIL import Image
from collections import namedtuple
from object_detection.utils import dataset_util, label_map_util

tf1 = tf.compat.v1

# Convers labels from XML format to CSV.
def xml_to_csv(path):
    xml_list = []
    for xml_file in glob.glob(path + '/*.xml'):
        tree = ET.parse(xml_file)
        root = tree.getroot()
        for member in root.findall('object'):
            value = (root.find('filename').text,
                int(root.find('size')[0].text),
                int(root.find('size')[1].text),
                member[0].text,
                int(member[4][0].text),
                int(member[4][1].text),
                int(member[4][2].text),
                int(member[4][3].text)
            )
            xml_list.append(value)
    column_name = ['filename', 'width', 'height', 'class', 'xmin', 'ymin', 'xmax', 'ymax']
    xml_df = pd.DataFrame(xml_list, columns=column_name)
    return xml_df


def class_text_to_int(row_label, label_map_dict):
    return label_map_dict[row_label]


def split(df, group):
    data = namedtuple('data', ['filename', 'object'])
    gb = df.groupby(group)
    return [data(filename, gb.get_group(x)) for filename, x in zip(gb.groups.keys(), gb.groups)]


# Creates a TFRecord.
def create_tf_example(group, path, label_map_dict):
    with tf1.gfile.GFile(os.path.join(path, '{}'.format(group.filename)), 'rb') as fid:
        encoded_jpg = fid.read()

    encoded_jpg_io = io.BytesIO(encoded_jpg)
    image = Image.open(encoded_jpg_io)
    width, height = image.size

    filename = group.filename.encode('utf8')
    image_format = b'jpg'
    xmins = []
    xmaxs = []
    ymins = []
    ymaxs = []
    classes_text = []
    classes = []

    for index, row in group.object.iterrows():
        xmins.append(row['xmin'] / width)
        xmaxs.append(row['xmax'] / width)
        ymins.append(row['ymin'] / height)
        ymaxs.append(row['ymax'] / height)
        classes_text.append(row['class'].encode('utf8'))
        classes.append(class_text_to_int(row['class'], label_map_dict))

    tf_example = tf1.train.Example(features=tf1.train.Features(feature={
        'image/height': dataset_util.int64_feature(height),
        'image/width': dataset_util.int64_feature(width),
        'image/filename': dataset_util.bytes_feature(filename),
        'image/source_id': dataset_util.bytes_feature(filename),
        'image/encoded': dataset_util.bytes_feature(encoded_jpg),
        'image/format': dataset_util.bytes_feature(image_format),
        'image/object/bbox/xmin': dataset_util.float_list_feature(xmins),
        'image/object/bbox/xmax': dataset_util.float_list_feature(xmaxs),
        'image/object/bbox/ymin': dataset_util.float_list_feature(ymins),
        'image/object/bbox/ymax': dataset_util.float_list_feature(ymaxs),
        'image/object/class/text': dataset_util.bytes_list_feature(classes_text),
        'image/object/class/label': dataset_util.int64_list_feature(classes),
    }))

    return tf_example


def dataset_to_tfrecord(
    images_dir,
    xmls_dir, 
    label_map_path,
    output_path,
    csv_path=None
):
    label_map = label_map_util.load_labelmap(label_map_path)
    label_map_dict = label_map_util.get_label_map_dict(label_map)

    tfrecord_writer = tf1.python_io.TFRecordWriter(output_path)
    images_path = os.path.join(images_dir)
    csv_examples = xml_to_csv(xmls_dir)
    grouped_examples = split(csv_examples, 'filename')

    for group in grouped_examples:
        tf_example = create_tf_example(group, images_path, label_map_dict)
        tfrecord_writer.write(tf_example.SerializeToString())

    tfrecord_writer.close()

    print('Successfully created the TFRecord file: {}'.format(output_path))

    if csv_path is not None:
        csv_examples.to_csv(csv_path, index=None)
        print('Successfully created the CSV file: {}'.format(csv_path))

# Generate a TFRecord for train dataset.
dataset_to_tfrecord(
    images_dir='dataset/printed_links/partitioned/train',
    xmls_dir='dataset/printed_links/partitioned/train',
    label_map_path='dataset/printed_links/labels/label_map.pbtxt',
    output_path='dataset/printed_links/tfrecords/train.record',
    csv_path='dataset/printed_links/labels/csv/train.csv'
)

# Generate a TFRecord for test dataset.
dataset_to_tfrecord(
    images_dir='dataset/printed_links/partitioned/test',
    xmls_dir='dataset/printed_links/partitioned/test',
    label_map_path='dataset/printed_links/labels/label_map.pbtxt',
    output_path='dataset/printed_links/tfrecords/test.record',
    csv_path='dataset/printed_links/labels/csv/test.csv'
)

Π’ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ ΠΌΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Ρ„Π°ΠΉΠ»Ρ‹ test.record ΠΈ train.record Π² ΠΏΠ°ΠΏΠΊΠ΅ dataset/printed_links/tfrecords/:

dataset/
└── printed_links
    β”œβ”€β”€ labels
    β”‚   β”œβ”€β”€ csv
    β”‚   β”œβ”€β”€ label_map.pbtxt
    β”‚   └── xml
    β”œβ”€β”€ partitioned
    β”‚   β”œβ”€β”€ test
    β”‚   β”œβ”€β”€ train
    β”‚   └── val
    β”œβ”€β”€ processed
    β”œβ”€β”€ raw
    └── tfrecords
        β”œβ”€β”€ test.record
        └── train.record

Π­Ρ‚ΠΈ Π΄Π²Π° Ρ„Π°ΠΉΠ»Π° test.record ΠΈ train.record ΡΠ²Π»ΡΡŽΡ‚ΡΡ ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎΠΉ вСрсиСй нашСго Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ…, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ для обучСния ΠΌΠΎΠ΄Π΅Π»ΠΈ ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8.

πŸ“– Π Π°Π±ΠΎΡ‚Π°Π΅ΠΌ с Π½Π°Π±ΠΎΡ€ΠΎΠΌ Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ TFRecord

Π’ этом Ρ€Π°Π·Π΄Π΅Π»Π΅ ΠΌΡ‹ посмотрим, ΠΊΠ°ΠΊΠΈΠ΅ инструмСнты для исслСдования Π½Π°Π±ΠΎΡ€ΠΎΠ² Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ TFRecord ΠΈΠΌΠ΅ΡŽΡ‚ΡΡ Π² TensorFlow 2 Object Detection API.

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ количСство экзСмпляров Π² Π½Π°Π±ΠΎΡ€Π΅ Π΄Π°Π½Π½Ρ‹Ρ…

ΠŸΠΎΡΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ количСство экзСмпляров ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

import tensorflow as tf

# Count the number of examples in the dataset.
def count_tfrecords(tfrecords_filename):
    raw_dataset = tf.data.TFRecordDataset(tfrecords_filename)
    # Keep in mind that the list() operation might be
    # a performance bottleneck for large datasets. 
    return len(list(raw_dataset))

TRAIN_RECORDS_NUM = count_tfrecords('dataset/printed_links/tfrecords/train.record')
TEST_RECORDS_NUM = count_tfrecords('dataset/printed_links/tfrecords/test.record')

print('TRAIN_RECORDS_NUM: ', TRAIN_RECORDS_NUM)
print('TEST_RECORDS_NUM:  ', TEST_RECORDS_NUM)

output β†’

TRAIN_RECORDS_NUM:  100
TEST_RECORDS_NUM:   25

Π˜Ρ‚Π°ΠΊ, ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π°ΡˆΡƒ модСль Π½Π° 100 экзСмплярах ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ Π΅Π΅ ΡΠΏΠΎΡΠΎΠ±Π½ΠΎΡΡ‚ΡŒ ΠΊ ΠΎΠ±ΠΎΠ±Ρ‰Π΅Π½ΠΈΡŽ Π½Π° 25 изобраТСниях.

ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅ΠΌ Π³Π°Π±Π°Ρ€ΠΈΡ‚Ρ‹ ΠΈ Π»ΠΎΠΊΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² изобраТСниях

ΠžΡ‚ΠΎΠ±Ρ€Π°Π·ΠΈΡ‚ΡŒ Π³Π°Π±Π°Ρ€ΠΈΡ‚Ρ‹ ΠΈ ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

import tensorflow as tf
import numpy as np
from google.protobuf import text_format
import matplotlib.pyplot as plt

# Import Object Detection API.
from object_detection.utils import visualization_utils
from object_detection.protos import string_int_label_map_pb2
from object_detection.data_decoders.tf_example_decoder import TfExampleDecoder

%matplotlib inline

# Visualize the TFRecord dataset.
def visualize_tfrecords(tfrecords_filename, label_map=None, print_num=1):
    decoder = TfExampleDecoder(
        label_map_proto_file=label_map,
        use_display_name=False
    )

    if label_map is not None:
        label_map_proto = string_int_label_map_pb2.StringIntLabelMap()

        with tf.io.gfile.GFile(label_map,'r') as f:
            text_format.Merge(f.read(), label_map_proto)
            class_dict = {}

            for entry in label_map_proto.item:
                class_dict[entry.id] = {'name': entry.name}

    raw_dataset = tf.data.TFRecordDataset(tfrecords_filename)

    for raw_record in raw_dataset.take(print_num):
        example = decoder.decode(raw_record)

        image = example['image'].numpy()
        boxes = example['groundtruth_boxes'].numpy()
        confidences = example['groundtruth_image_confidences']
        filename = example['filename']
        area = example['groundtruth_area']
        classes = example['groundtruth_classes'].numpy()
        image_classes = example['groundtruth_image_classes']
        weights = example['groundtruth_weights']

        scores = np.ones(boxes.shape[0])

        visualization_utils.visualize_boxes_and_labels_on_image_array( 
            image,                                               
            boxes,                                                     
            classes,
            scores,
            class_dict,
            max_boxes_to_draw=None,
            use_normalized_coordinates=True
        )

        plt.figure(figsize=(8, 8))
        plt.imshow(image)

    plt.show()

# Visualizing the training TFRecord dataset.
visualize_tfrecords(
    tfrecords_filename='dataset/printed_links/tfrecords/train.record',
    label_map='dataset/printed_links/labels/label_map.pbtxt',
    print_num=3
)

Π’ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ ΠΌΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ нСсколько ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ с ΠΏΡ€ΡΠΌΠΎΡƒΠ³ΠΎΠ»ΡŒΠ½Ρ‹ΠΌΠΈ Π³Π°Π±Π°Ρ€ΠΈΡ‚Π°ΠΌΠΈ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΈΠ· ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²,

TFRecord Preview
TFRecord Preview

πŸ“ˆ УстанавливаСм TensorBoard

ΠŸΠ΅Ρ€Π΅Π΄ Ρ‚Π΅ΠΌ, ΠΊΠ°ΠΊ Π½Π°Ρ‡Π°Ρ‚ΡŒ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΡƒ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ TensorBoard.

TensorBoard ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°ΠΌ Π² ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³Π΅ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΎΡ‡Π½ΠΎΠ³ΠΎ процСсса. Он ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°ΠΌ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ, Π΄Π΅ΠΉΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π»ΠΈ модСль обучаСтся ΠΈΠ»ΠΈ ΠΆΠ΅ Π½Π°ΠΌ Π»ΡƒΡ‡ΡˆΠ΅ ΠΎΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΡƒ ΠΈ ΠΏΠΎΠ΄ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ. TensorBoard Ρ‚Π°ΠΊΠΆΠ΅ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°ΠΌ ΠΊΠ°ΠΊΠΈΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ ΠΈ Π³Π΄Π΅ ΠΈΠΌΠ΅Π½Π½ΠΎ Π½Π° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ наша модСль ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Π΅Ρ‚.

TensorBoard
TensorBoard

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ изобраТСния: домашняя страница TensorBoard

ΠžΡ‚Π»ΠΈΡ‡Π½ΠΎΠΉ ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎΡΡ‚ΡŒΡŽ TensorBoard являСтся Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ Π΅Π³ΠΎ прямо Π² Google Colab. Если ΠΆΠ΅ Π²Ρ‹ экспСримСнтируСтС с модСлью локально Π² Jupyter Π½ΠΎΡƒΡ‚Π±ΡƒΠΊΠ΅, Ρ‚ΠΎ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ TensorBoard ΠΊΠ°ΠΊ Python ΠΏΠ°ΠΊΠ΅Ρ‚ ΠΈ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ Π΅Π³ΠΎ локально ΠΈΠ· консоли.

Для Π½Π°Ρ‡Π°Π»Π° создадим ΠΏΠ°ΠΏΠΊΡƒ ./logs, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Π²ΠΎ врСмя Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ Π±ΡƒΠ΄ΡƒΡ‚ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒΡΡ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ ΠΌΠΎΠ΄Π΅Π»ΠΈ.

mkdir -p logs

Π”Π°Π»Π΅Π΅, ΠΌΡ‹ Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠ΅ TensorBoard Π² Google Colab:

%load_ext tensorboard

И Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ TensorBoard ΠΈ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΏΠ°ΠΏΠΊΡƒ ./logs Π² качСствС ΠΏΠ°ΠΏΠΊΠΈ с Π»ΠΎΠ³Π°ΠΌΠΈ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ,

%tensorboard --logdir ./logs

Π’ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ Π²Ρ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ ΠΏΡƒΡΡ‚ΡƒΡŽ панСль TensorBoard:

Empty TensorBoard Panel
Empty TensorBoard Panel

ПослС Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΠΌΡ‹ Π½Π°Ρ‡Π½Π΅ΠΌ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΡƒ, ΠΌΡ‹ смоТСм Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒΡΡ ΠΊ этой ΠΏΠ°Π½Π΅Π»ΠΈ ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ насколько Ρ…ΠΎΡ€ΠΎΡˆΠΎ ΠΎΠ½Π° обучаСтся.

πŸ‹πŸ»β€οΈ Π’Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠ° ΠΌΠΎΠ΄Π΅Π»ΠΈ

НастраиваСм ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ

Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒΡΡ ΠΊ Ρ€Π°Π½Π΅Π΅ упомянутому Ρ„Π°ΠΉΠ»Ρƒ cache/datasets/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8/pipeline.config. Π’ этом Ρ„Π°ΠΉΠ»Π΅ собраны ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ для Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8.

Нам Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ» pipeline.config Π² ΠΊΠΎΡ€Π΅Π½ΡŒ нашСго ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹:

  1. НСобходимо количСство классов с 90 (количСство классов Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ… COCO) Π½Π° 1 (наш СдинствСнный класс http)
  2. НСобходимо ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΡ‚ΡŒ Ρ€Π°Π·ΠΌΠ΅Ρ€ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΎΡ‡Π½ΠΎΠ³ΠΎ ΠΏΠ°ΠΊΠ΅Ρ‚Π° (batch size) Π΄ΠΎ 8 ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Π½Π° ΠΎΠ΄ΠΈΠ½ ΠΏΠ°ΠΊΠ΅Ρ‚, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ с нСдостатком памяти.
  3. НСобходимо ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ нашСй ΠΌΠΎΠ΄Π΅Π»ΠΈ, Π³Π΄Π΅ хранятся сохранСнныС слСпки Ρ€Π°Π½Π΅Π΅ Π½Π°Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹Ρ… ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² ΠΌΠΎΠ΄Π΅Π»ΠΈ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΌΡ‹ Π½Π΅ Ρ…ΠΎΡ‚ΠΈΠΌ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π΅ с нуля.
  4. НСобходимо ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ fine_tune_checkpoint_type Π² detection.
  5. НСобходимо ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΌΠΎΠ΄Π΅Π»ΠΈ, Π³Π΄Π΅ находится ΠΊΠ°Ρ€Ρ‚Π° Π½ΠΎΠ²Ρ‹Ρ… классов ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ².
  6. НСобходимо ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΌΠΎΠ΄Π΅Π»ΠΈ, Π³Π΄Π΅ находятся Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΎΡ‡Π½Ρ‹ΠΉ ΠΈ тСстовый Π½Π°Π±ΠΎΡ€Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ….

ВсС эти измСнСния ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ Π² Ρ„Π°ΠΉΠ»Π΅ pipeline.config, Π½ΠΎ это Ρ‚Π°ΠΊ ΠΆΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΠΎ:

import tensorflow as tf
from shutil import copyfile
from google.protobuf import text_format
from object_detection.protos import pipeline_pb2

# Adjust pipeline config modification here if needed.
def modify_config(pipeline):
    # Model config.
    pipeline.model.ssd.num_classes = 1    

    # Train config.
    pipeline.train_config.batch_size = 8

    pipeline.train_config.fine_tune_checkpoint = 'cache/datasets/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8/checkpoint/ckpt-0'
    pipeline.train_config.fine_tune_checkpoint_type = 'detection'

    # Train input reader config.
    pipeline.train_input_reader.label_map_path = 'dataset/printed_links/labels/label_map.pbtxt'
    pipeline.train_input_reader.tf_record_input_reader.input_path[0] = 'dataset/printed_links/tfrecords/train.record'

    # Eval input reader config.
    pipeline.eval_input_reader[0].label_map_path = 'dataset/printed_links/labels/label_map.pbtxt'
    pipeline.eval_input_reader[0].tf_record_input_reader.input_path[0] = 'dataset/printed_links/tfrecords/test.record'

    return pipeline

def clone_pipeline_config():
    copyfile(
        'cache/datasets/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8/pipeline.config',
        'pipeline.config'
    )

def setup_pipeline(pipeline_config_path):
    clone_pipeline_config()
    pipeline = read_pipeline_config(pipeline_config_path)
    pipeline = modify_config(pipeline)
    write_pipeline_config(pipeline_config_path, pipeline)
    return pipeline

def read_pipeline_config(pipeline_config_path):
    pipeline = pipeline_pb2.TrainEvalPipelineConfig()                                                                                                                                                                                                          
    with tf.io.gfile.GFile(pipeline_config_path, "r") as f:                                                                                                                                                                                                                     
        proto_str = f.read()                                                                                                                                                                                                                                          
        text_format.Merge(proto_str, pipeline)
    return pipeline

def write_pipeline_config(pipeline_config_path, pipeline):
    config_text = text_format.MessageToString(pipeline)                                                                                                                                                                                                        
    with tf.io.gfile.GFile(pipeline_config_path, "wb") as f:                                                                                                                                                                                                                       
        f.write(config_text)

# Adjusting the pipeline configuration.
pipeline = setup_pipeline('pipeline.config')

print(pipeline)

Π’ΠΎΡ‚ ΠΎΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»ΡŒΠ½Π°Ρ вСрсия Ρ„Π°ΠΉΠ»Π° pipeline.config послС рСдактирования:

model {
  ssd {
    num_classes: 1
    image_resizer {
      fixed_shape_resizer {
        height: 640
        width: 640
      }
    }
    feature_extractor {
      type: "ssd_mobilenet_v2_fpn_keras"
      depth_multiplier: 1.0
      min_depth: 16
      conv_hyperparams {
        regularizer {
          l2_regularizer {
            weight: 3.9999998989515007e-05
          }
        }
        initializer {
          random_normal_initializer {
            mean: 0.0
            stddev: 0.009999999776482582
          }
        }
        activation: RELU_6
        batch_norm {
          decay: 0.996999979019165
          scale: true
          epsilon: 0.0010000000474974513
        }
      }
      use_depthwise: true
      override_base_feature_extractor_hyperparams: true
      fpn {
        min_level: 3
        max_level: 7
        additional_layer_depth: 128
      }
    }
    box_coder {
      faster_rcnn_box_coder {
        y_scale: 10.0
        x_scale: 10.0
        height_scale: 5.0
        width_scale: 5.0
      }
    }
    matcher {
      argmax_matcher {
        matched_threshold: 0.5
        unmatched_threshold: 0.5
        ignore_thresholds: false
        negatives_lower_than_unmatched: true
        force_match_for_each_row: true
        use_matmul_gather: true
      }
    }
    similarity_calculator {
      iou_similarity {
      }
    }
    box_predictor {
      weight_shared_convolutional_box_predictor {
        conv_hyperparams {
          regularizer {
            l2_regularizer {
              weight: 3.9999998989515007e-05
            }
          }
          initializer {
            random_normal_initializer {
              mean: 0.0
              stddev: 0.009999999776482582
            }
          }
          activation: RELU_6
          batch_norm {
            decay: 0.996999979019165
            scale: true
            epsilon: 0.0010000000474974513
          }
        }
        depth: 128
        num_layers_before_predictor: 4
        kernel_size: 3
        class_prediction_bias_init: -4.599999904632568
        share_prediction_tower: true
        use_depthwise: true
      }
    }
    anchor_generator {
      multiscale_anchor_generator {
        min_level: 3
        max_level: 7
        anchor_scale: 4.0
        aspect_ratios: 1.0
        aspect_ratios: 2.0
        aspect_ratios: 0.5
        scales_per_octave: 2
      }
    }
    post_processing {
      batch_non_max_suppression {
        score_threshold: 9.99999993922529e-09
        iou_threshold: 0.6000000238418579
        max_detections_per_class: 100
        max_total_detections: 100
        use_static_shapes: false
      }
      score_converter: SIGMOID
    }
    normalize_loss_by_num_matches: true
    loss {
      localization_loss {
        weighted_smooth_l1 {
        }
      }
      classification_loss {
        weighted_sigmoid_focal {
          gamma: 2.0
          alpha: 0.25
        }
      }
      classification_weight: 1.0
      localization_weight: 1.0
    }
    encode_background_as_zeros: true
    normalize_loc_loss_by_codesize: true
    inplace_batchnorm_update: true
    freeze_batchnorm: false
  }
}
train_config {
  batch_size: 8
  data_augmentation_options {
    random_horizontal_flip {
    }
  }
  data_augmentation_options {
    random_crop_image {
      min_object_covered: 0.0
      min_aspect_ratio: 0.75
      max_aspect_ratio: 3.0
      min_area: 0.75
      max_area: 1.0
      overlap_thresh: 0.0
    }
  }
  sync_replicas: true
  optimizer {
    momentum_optimizer {
      learning_rate {
        cosine_decay_learning_rate {
          learning_rate_base: 0.07999999821186066
          total_steps: 50000
          warmup_learning_rate: 0.026666000485420227
          warmup_steps: 1000
        }
      }
      momentum_optimizer_value: 0.8999999761581421
    }
    use_moving_average: false
  }
  fine_tune_checkpoint: "cache/datasets/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8/checkpoint/ckpt-0"
  num_steps: 50000
  startup_delay_steps: 0.0
  replicas_to_aggregate: 8
  max_number_of_boxes: 100
  unpad_groundtruth_tensors: false
  fine_tune_checkpoint_type: "detection"
  fine_tune_checkpoint_version: V2
}
train_input_reader {
  label_map_path: "dataset/printed_links/labels/label_map.pbtxt"
  tf_record_input_reader {
    input_path: "dataset/printed_links/tfrecords/train.record"
  }
}
eval_config {
  metrics_set: "coco_detection_metrics"
  use_moving_averages: false
}
eval_input_reader {
  label_map_path: "dataset/printed_links/labels/label_map.pbtxt"
  shuffle: false
  num_epochs: 1
  tf_record_input_reader {
    input_path: "dataset/printed_links/tfrecords/test.record"
  }
}

ЗапускаСм процСсс Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ

ΠœΡ‹ Π³ΠΎΡ‚ΠΎΠ²Ρ‹ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ процСсс Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ TensorFlow 2 Object Detection API. API содСрТит Ρ„Π°ΠΉΠ» model_main_tf2.py, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ содСрТит всю Π»ΠΎΠ³ΠΈΠΊΡƒ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ. Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π΅Ρ‚Π°Π»ΡŒΠ½Π΅Π΅ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ с исходным Python ΠΊΠΎΠ΄ΠΎΠΌ Ρ„Π°ΠΉΠ»Π°, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ описаны Π²Ρ…ΠΎΠ΄Π½Ρ‹Π΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ скрипта (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, num_train_steps, model_dir ΠΈ ΠΏΡ€.).

ΠœΡ‹ Π±ΡƒΠ΄Π΅ΠΌ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ модСль Π² Ρ‚Π΅Ρ‡Π΅Π½ΠΈΠ΅ 1000 ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΠΉ (эпох).

%%bash

NUM_TRAIN_STEPS=1000
CHECKPOINT_EVERY_N=1000

PIPELINE_CONFIG_PATH=pipeline.config
MODEL_DIR=./logs
SAMPLE_1_OF_N_EVAL_EXAMPLES=1

python ./models/research/object_detection/model_main_tf2.py \
  --model_dir=$MODEL_DIR \
  --num_train_steps=$NUM_TRAIN_STEPS \
  --sample_1_of_n_eval_examples=$SAMPLE_1_OF_N_EVAL_EXAMPLES \
  --pipeline_config_path=$PIPELINE_CONFIG_PATH \
  --checkpoint_every_n=$CHECKPOINT_EVERY_N \
  --alsologtostderr

Π’ΠΎ врСмя Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ (это ΠΌΠΎΠΆΠ΅Ρ‚ Π·Π°Π½ΡΡ‚ΡŒ ~10 ΠΌΠΈΠ½ΡƒΡ‚ для 1000 ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΠΉ с использованиСм GPU runtime Π² GoogleColab) Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ ΠΊΠ°ΠΊ процСсс Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ Π² TensorBoard. Ошибки localization ΠΈ classification Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΡƒΠΌΠ΅Π½ΡŒΡˆΠ°Ρ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ модСль всС Π»ΡƒΡ‡ΡˆΠ΅ ΠΈ Π»ΡƒΡ‡ΡˆΠ΅ Π»ΠΎΠΊΠ°Π»ΠΈΠ·ΡƒΠ΅Ρ‚ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ ΠΈ опрСдСляСт ΠΈΡ… класс.

Training Process
Training Process

Π’Π°ΠΊΠΆΠ΅ ΠΏΠΎ ΠΌΠ΅Ρ€Π΅ обучСния ΠΌΠΎΠ΄Π΅Π»ΠΈ Π² ΠΏΠ°ΠΏΠΊΠ΅ logs Π±ΡƒΠ΄ΡƒΡ‚ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒΡΡ Π½ΠΎΠ²Ρ‹Π΅ Ρ‡Π΅ΠΊΠΏΠΎΠΈΠ½Ρ‚Ρ‹ (слСпки) ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² ΠΌΠΎΠ΄Π΅Π»ΠΈ.

Папка logs ΠΌΠΎΠΆΠ΅Ρ‚ Π²Ρ‹Π³Π»ΡΠ΄Π΅Ρ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

logs
β”œβ”€β”€ checkpoint
β”œβ”€β”€ ckpt-1.data-00000-of-00001
β”œβ”€β”€ ckpt-1.index
└── train
    └── events.out.tfevents.1606560330.b314c371fa10.1747.1628.v2

ΠžΡ†Π΅Π½ΠΈΠ²Π°Π΅ΠΌ модСль (ΠΎΠΏΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ)

Π§Ρ‚ΠΎΠ±Ρ‹ ΠΎΡ†Π΅Π½ΠΈΡ‚ΡŒ Ρ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΌΡ‹ ΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ Π½Π° изобраТСния ΠΈΠ· тСстового Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ…. Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Ρ‚Π°ΠΊΠΎΠΉ ΠΎΡ†Π΅Π½ΠΊΠΈ обобщаСтся Π² Π²ΠΈΠ΄Π΅ ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊ, ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π½Π°Π±Π»ΡŽΠ΄Π°Ρ‚ΡŒ с Ρ‚Π΅Ρ‡Π΅Π½ΠΈΠ΅ΠΌ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ. Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π±ΠΎΠ»Π΅Π΅ Π΄Π΅Ρ‚Π°Π»ΡŒΠ½ΠΎ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ с Ρ‚Π΅ΠΌ, ΠΊΠ°ΠΊΠΈΠ΅ ΠΈΠΌΠ΅Π½Π½ΠΎ ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ здСсь.

Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ пропустим этот шаг с ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠ°ΠΌΠΈ, Π½ΠΎ ΠΌΡ‹ всС-ΠΆΠ΅ ΠΌΠΎΠΆΠ΅ΠΌ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ панСлью TensorBoard, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ, ΠΊΠ°ΠΊΠΈΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ модСль ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Π΅Ρ‚ Π½Π° тСстовом Π½Π°Π±ΠΎΡ€Π΅ Π΄Π°Π½Π½Ρ‹Ρ…:

%%bash

PIPELINE_CONFIG_PATH=pipeline.config
MODEL_DIR=logs

python ./models/research/object_detection/model_main_tf2.py \
  --model_dir=$MODEL_DIR \
  --pipeline_config_path=$PIPELINE_CONFIG_PATH \
  --checkpoint_dir=$MODEL_DIR \

ПослС запуска скрипта Π²Ρ‹ смоТСтС ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ нСсколько ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ с ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹ΠΌΠΈ Π² Π½ΠΈΡ… ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚Π°ΠΌΠΈ:

Model Evaluation
Model Evaluation

πŸ—œ ЭкспортируСм модСль

ПослС окончания Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ модСль для дальнСйшСго использования. Для экспортирования ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΌΡ‹ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡΡ скриптом exporter_main_v2.py ΠΈΠ· Object Detection API. Π­Ρ‚ΠΎΡ‚ скрипт ΠΏΠΎΠ΄Π³ΠΎΡ‚Π°Π²Π»ΠΈΠ²Π°Π΅Ρ‚ TensorFlow Π³Ρ€Π°Ρ„ Π½Π° основании Ρ‡Π΅ΠΊΠΏΠΎΠΈΠ½Ρ‚ΠΎΠ² ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΈ Π΅Π΅ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΎΡ‡Π½ΠΎΠΉ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ. ПослС выполнСния скрипта ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ ΠΏΠ°ΠΏΠΊΡƒ с Ρ‡Π΅ΠΊΠΏΠΎΠΈΠ½Ρ‚Π°ΠΌΠΈ, модСлью Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ SavedModel ΠΈ ΠΊΠΎΠΏΠΈΠ΅ΠΉ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Π° ΠΌΠΎΠ΄Π΅Π»ΠΈ.

%%bash

python ./models/research/object_detection/exporter_main_v2.py \
    --input_type=image_tensor \
    --pipeline_config_path=pipeline.config \
    --trained_checkpoint_dir=logs \
    --output_directory=exported/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8

Π’ΠΎΡ‚ Ρ‚Π°ΠΊ выглядит содСрТимоС ΠΏΠ°ΠΏΠΊΠΈ exported:

exported
└── ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8
    β”œβ”€β”€ checkpoint
    β”‚   β”œβ”€β”€ checkpoint
    β”‚   β”œβ”€β”€ ckpt-0.data-00000-of-00001
    β”‚   └── ckpt-0.index
    β”œβ”€β”€ pipeline.config
    └── saved_model
        β”œβ”€β”€ assets
        β”œβ”€β”€ saved_model.pb
        └── variables
            β”œβ”€β”€ variables.data-00000-of-00001
            └── variables.index

На этом этапС Ρƒ нас Π΅ΡΡ‚ΡŒ модСль Π² ΠΏΠ°ΠΏΠΊΠ΅ saved_model, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΌΡ‹ ΡƒΠΆΠ΅ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ для обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ².

πŸš€ ИспользованиС экспортированной ΠΌΠΎΠ΄Π΅Π»ΠΈ

Π”Π°Π²Π°ΠΉΡ‚Π΅ посмотрим, ΠΊΠ°ΠΊ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ модСль, ΡΠΊΡΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½ΡƒΡŽ Π½Π° ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ этапС.

Π’ Π½Π°Ρ‡Π°Π»Π΅ Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ-ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚Π΅Π»ΡŒ, которая Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΡΠΎΡ…Ρ€Π°Π½Π΅Π½Π½ΡƒΡŽ модСль. Π­Ρ‚Π° функция Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Ρ‚ΡŒ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Π½Π° Π²Ρ…ΠΎΠ΄ ΠΈ Π²Ρ‹Π΄Π°Π²Π°Ρ‚ΡŒ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎΠ± ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°Ρ…:

import time
import math

PATH_TO_SAVED_MODEL = 'exported/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8/saved_model'

def detection_function_from_saved_model(saved_model_path):
    print('Loading saved model...', end='')
    start_time = time.time()

    # Load saved model and build the detection function
    detect_fn = tf.saved_model.load(saved_model_path)

    end_time = time.time()
    elapsed_time = end_time - start_time

    print('Done! Took {} seconds'.format(math.ceil(elapsed_time)))

    return detect_fn

exported_detect_fn = detection_function_from_saved_model(
    PATH_TO_SAVED_MODEL
)

output β†’

Loading saved model...Done! Took 9 seconds

Для сопоставлСния ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹Ρ… классов с ΠΈΠΌΠ΅Π½Π°ΠΌΠΈ классов Π½Π°ΠΌ Ρ‚Π°ΠΊΠΆΠ΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ ΠΊΠ°Ρ€Ρ‚Ρƒ классов:

from object_detection.utils import label_map_util

category_index = label_map_util.create_category_index_from_labelmap(
    'dataset/printed_links/labels/label_map.pbtxt',
    use_display_name=True
)

print(category_index)

output β†’

{1: {'id': 1, 'name': 'http'}}

ВСстируСм Π½Π°ΡˆΡƒ модСль Π½Π° тСстовом Π½Π°Π±ΠΎΡ€Π΅ Π΄Π°Π½Π½Ρ‹Ρ….

import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np

from object_detection.utils import visualization_utils
from object_detection.data_decoders.tf_example_decoder import TfExampleDecoder

%matplotlib inline

def tensors_from_tfrecord(
    tfrecords_filename,
    tfrecords_num,
    dtype=tf.float32
):
    decoder = TfExampleDecoder()
    raw_dataset = tf.data.TFRecordDataset(tfrecords_filename)
    images = []

    for raw_record in raw_dataset.take(tfrecords_num):
        example = decoder.decode(raw_record)
        image = example['image']
        image = tf.cast(image, dtype=dtype)
        images.append(image)

    return images

def test_detection(tfrecords_filename, tfrecords_num, detect_fn):
    image_tensors = tensors_from_tfrecord(
        tfrecords_filename,
        tfrecords_num,
        dtype=tf.uint8
    )

    for image_tensor in image_tensors:   
        image_np = image_tensor.numpy()

        # The model expects a batch of images, so add an axis with `tf.newaxis`.
        input_tensor = tf.expand_dims(image_tensor, 0)

        detections = detect_fn(input_tensor)

        # All outputs are batches tensors.
        # Convert to numpy arrays, and take index [0] to remove the batch dimension.
        # We're only interested in the first num_detections.
        num_detections = int(detections.pop('num_detections'))

        detections = {key: value[0, :num_detections].numpy() for key, value in detections.items()}
        detections['num_detections'] = num_detections

        # detection_classes should be ints.
        detections['detection_classes'] = detections['detection_classes'].astype(np.int64)

        image_np_with_detections = image_np.astype(int).copy()

        visualization_utils.visualize_boxes_and_labels_on_image_array(
            image_np_with_detections,
            detections['detection_boxes'],
            detections['detection_classes'],
            detections['detection_scores'],
            category_index,
            use_normalized_coordinates=True,
            max_boxes_to_draw=100,
            min_score_thresh=.3,
            agnostic_mode=False
        )

        plt.figure(figsize=(8, 8))
        plt.imshow(image_np_with_detections)

    plt.show()


test_detection(
    tfrecords_filename='dataset/printed_links/tfrecords/test.record',
    tfrecords_num=10,
    detect_fn=exported_detect_fn
)

Π’ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π΅ Π²Ρ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ 10 ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ ΠΈΠ· тСстового Π½Π°Π±ΠΎΡ€Π° Π΄Π°Π½Π½Ρ‹Ρ… с ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹ΠΌΠΈ ΠΈ подсвСчСнными https: прСфиксами:

Testing the model on a test dataset
Testing the model on a test dataset

Π’ΠΎΡ‚ Ρ„Π°ΠΊΡ‚, Ρ‡Ρ‚ΠΎ модСль смогла ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ (Π² нашСм случаС прСфиксы https://) Π² изобраТСниях, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΎΠ½Π° Ρ€Π°Π½ΡŒΡˆΠ΅ Π½Π΅ "Π²ΠΈΠ΄Π΅Π»Π°" являСтся Ρ…ΠΎΡ€ΠΎΡˆΠΈΠΌ Π·Π½Π°ΠΊΠΎΠΌ ΠΈ, собствСнно, Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ Ρ…ΠΎΡ‚Π΅Π»ΠΈ Π΄ΠΎΡΡ‚ΠΈΠ³Π½ΡƒΡ‚ΡŒ этой Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΎΠΉ.

πŸ—œ ΠšΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ модСль Π² Π²Π΅Π±-совмСстимый Ρ„ΠΎΡ€ΠΌΠ°Ρ‚

Как Π²Ρ‹ ΠΏΠΎΠΌΠ½ΠΈΡ‚Π΅ ΠΈΠ· Π½Π°Ρ‡Π°Π»Π° Π΄Π°Π½Π½ΠΎΠΉ ΡΡ‚Π°Ρ‚ΡŒΠΈ нашСй Ρ†Π΅Π»ΡŒΡŽ Π±Ρ‹Π»Π° Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠ° ΠΌΠΎΠ΄Π΅Π»ΠΈ обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΌΡ‹ ΠΌΠΎΠ³Π»ΠΈ Π±Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅. К ΡΡ‡Π°ΡΡ‚ΡŒΡŽ, сущСствуСт JavaScript вСрсия TensorFlow - TensorFlow.js. Π’ JavaScript ΠΌΡ‹ Π½Π΅ ΠΌΠΎΠΆΠ΅ΠΌ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с сохранСнной Ρ€Π°Π½Π΅Π΅ модСлью Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ. Нам Π½ΡƒΠΆΠ½Π° Π΅Ρ‰Π΅ ΠΎΠ΄Π½Π° послСдняя конвСртация ΠΌΠΎΠ΄Π΅Π»ΠΈ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ tfjs_graph_model.

Для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²ΠΈΡ‚ΡŒ эту ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΡŽ, Π½Π°ΠΌ понадобится Python ΠΏΠ°ΠΊΠ΅Ρ‚ tensorflowjs:

pip install tensorflowjs --quiet

Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ модСль Π² Π½ΡƒΠΆΠ½Ρ‹ΠΉ Π½Π°ΠΌ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚:

%%bash

tensorflowjs_converter \
    --input_format=tf_saved_model \
    --output_format=tfjs_graph_model \
    exported/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8/saved_model \
    exported_web/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8

Папка exported_web содСрТит .json Ρ„Π°ΠΉΠ» с ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠ΅ΠΉ ΠΎΠ± Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ, Π° нСсколько Ρ„Π°ΠΉΠ»ΠΎΠ² Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ .bin содСрТат Π΅Π΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹.

exported_web
└── ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8
    β”œβ”€β”€ group1-shard1of4.bin
    β”œβ”€β”€ group1-shard2of4.bin
    β”œβ”€β”€ group1-shard3of4.bin
    β”œβ”€β”€ group1-shard4of4.bin
    └── model.json

НаконСц-Ρ‚ΠΎ ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΈ модСль, которая способна ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Ρ‚ΡŒ https:// прСфиксы Π² изобраТСниях ΠΈ которая сохранСна Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅, понятном JavaScript прилоТСниям.

Π”Π°Π²Π°ΠΉΡ‚Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ Ρ€Π°Π·ΠΌΠ΅Ρ€Ρ‹ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ создали:

import pathlib

def get_folder_size(folder_path):
    mB = 1000000
    root_dir = pathlib.Path(folder_path)
    sizeBytes = sum(f.stat().st_size for f in root_dir.glob('**/*') if f.is_file())
    return f'{sizeBytes//mB} MB'


print(f'Original model size:      {get_folder_size("cache/datasets/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8")}')
print(f'Exported model size:      {get_folder_size("exported/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8")}')
print(f'Exported WEB model size:  {get_folder_size("exported_web/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8")}')

output β†’

Original model size:      31 MB
Exported model size:      28 MB
Exported WEB model size:  13 MB

Как Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π·Π°ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, модСль, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΌΡ‹ собираСмся ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π½Π° сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° вСсит 13MB, Ρ‡Ρ‚ΠΎ Π²ΠΏΠΎΠ»Π½Π΅ допустимо ΠΈ соотвСтствуСт трСбованиям, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠ»ΠΈ Π² Π½Π°Ρ‡Π°Π»Π΅ ΡΡ‚Π°Ρ‚ΡŒΠΈ.

ПозТС Π½Π° сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° ΠΌΡ‹ смоТСм ΠΈΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ эту модСль ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

import * as tf from '@tensorflow/tfjs';
const model = await tf.loadGraphModel(modelURL);

🧭 Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ шагом Π±ΡƒΠ΄Π΅Ρ‚ рСализация ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ интСрфСйса для ΠΌΠΎΠ΄Π΅Π»ΠΈ, Ρ‡Ρ‚ΠΎ являСтся Ρ‚Π΅ΠΌΠΎΠΉ для Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΡΡ‚Π°Ρ‚ΡŒΠΈ. Но ΡƒΠΆΠ΅ сСйчас, ΠΏΡ€ΠΈ ΠΆΠ΅Π»Π°Π½ΠΈΠΈ, Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ с Ρ„ΠΈΠ½Π°Π»ΡŒΠ½Ρ‹ΠΌ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠΌ ΠΊΠΎΠ΄Π° прилоТСния Π½Π° TypeScript Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ links-detector Π½Π° GitHub.

πŸ€” Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ Π½Π°Ρ‡Π°Π»ΠΈ Ρ€Π΅ΡˆΠ°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ распознавания ΠΏΠ΅Ρ‡Π°Ρ‚Π½Ρ‹Ρ… ссылок. Π’ ΠΈΡ‚ΠΎΠ³Π΅ ΠΌΡ‹ ΠΎΠ±ΡƒΡ‡ΠΈΠ»ΠΈ модСль, ΡΠΏΠΎΡΠΎΠ±Π½ΡƒΡŽ Ρ€Π°ΡΠΏΠΎΠ·Π½Π°Π²Π°Ρ‚ΡŒ прСфиксы https:// Π² тСкстовых изобраТСниях (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π² ΠΊΠ°Π΄Ρ€Π°Ρ… Π²ΠΈΠ΄Π΅ΠΎ-ΠΏΠΎΡ‚ΠΎΠΊΠ° с ΠΊΠ°ΠΌΠ΅Ρ€Ρ‹ смартфона). ΠœΡ‹ Ρ‚Π°ΠΊΠΆΠ΅ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π»ΠΈ ΠΎΠ±ΡƒΡ‡Π΅Π½Π½ΡƒΡŽ модСль Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ tfjs_graph_model для дальнСйшСго использования Π΅Π΅ Π½Π° сторонС ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° Π² JavaScript/TypeScript ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ.

Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ πŸš€ <strong>Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ Links Detector</strong> со своСго смартфона ΠΈ ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ, ΠΊΠ°ΠΊ ΠΎΠ½ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Π΅Ρ‚ ссылки Π² вашСй ΠΊΠ½ΠΈΠ³Π΅ ΠΈΠ»ΠΈ ΠΆΡƒΡ€Π½Π°Π»Π΅.

ЀинальноС Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ выглядит ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ:

Links Detector Demo
Links Detector Demo

Π’Ρ‹ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ πŸ“ <strong>ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ с Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠ΅ΠΌ links-detector</strong> Π½Π° GitHub, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ смоТСтС Π½Π°ΠΉΡ‚ΠΈ исходный ΠΊΠΎΠ΄ клиСнтской части прилоТСния.

⚠️ На Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ находится Π² ΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠΉ стадии ΠΈ ΠΈΠΌΠ΅Π΅Ρ‚ мноТСство Π½Π΅Π΄ΠΎΡ€Π°Π±ΠΎΡ‚ΠΎΠΊ ΠΈ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠΉ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ, Π΄ΠΎ Ρ‚Π΅Ρ… ΠΏΠΎΡ€, ΠΏΠΎΠΊΠ° Π²Ρ‹ΡˆΠ΅ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹Π΅ Π½Π΅Π΄ΠΎΡ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π½Π΅ Π±ΡƒΠ΄ΡƒΡ‚ Π»ΠΈΠΊΠ²ΠΈΠ΄ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹, Π½Π΅ ΠΎΠΆΠΈΠ΄Π°ΠΉΡ‚Π΅ ΠΎΡ‚ прилоТСния слишком ΠΌΠ½ΠΎΠ³ΠΎΠ³ΠΎ πŸ€·πŸ»β€.

Π’ качСствС ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΡ… шагов ΠΏΠΎ ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡŽ точности ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅:

  • Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΎΡ‡Π½Ρ‹ΠΉ ΠΈ тСстовый Π½Π°Π±ΠΎΡ€Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… ссылками Ρ€Π°Π·Π½Ρ‹Ρ… Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΎΠ² (http://, tcp://, ftp:// ΠΈ ΠΏΡ€.)
  • Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°ΠΌΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ с Ρ‚Π΅ΠΌΠ½Ρ‹ΠΌ Ρ„ΠΎΠ½ΠΎΠΌ ΠΈ свСтлым тСкстом.
  • Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΠΎΠ΄Ρ‡Π΅Ρ€ΠΊΠ½ΡƒΡ‚Ρ‹ΠΌΠΈ ссылками.
  • Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ Π½Π°Π±ΠΎΡ€ Π΄Π°Π½Π½Ρ‹Ρ… тСкстами ΠΈ ссылками с Π΄Ρ€ΡƒΠ³ΠΈΠΌΠΈ ΡˆΡ€ΠΈΡ„Ρ‚Π°ΠΌΠΈ
  • ΠΈ ΠΏΡ€.

НСсмотря Π½Π° Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Ρ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒ ΠΌΠΎΠ΄Π΅Π»ΠΈ нСдостаточна для Ρ€Π΅Π»ΠΈΠ·Π° ΠΏΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½ΠΎΠ³ΠΎ прилоТСния, я всС-ΠΆΠ΅ надСюсь, Ρ‡Ρ‚ΠΎ эта ΡΡ‚Π°Ρ‚ΡŒΡ Π±Ρ‹Π»Π° для вас ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠΉ ΠΈ Π²Π΄ΠΎΡ…Π½ΠΎΠ²ΠΈΠ»Π° вас Π½Π° дальнСйшиС экспСримСнты с модСлями обнаруТСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ².

УспСшной Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠΈ!

9 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠ΅Π² πŸ‘‡

Классная ΠΈ полСзная ΡΡ‚Π°Ρ‚ΡŒΡ, ΠΎΠ³Ρ€ΠΎΠΌΠ½ΠΎΠ΅ спасибо! МнС нравится, Ρ‡Ρ‚ΠΎ всС рассказано ΠΎΡ‡Π΅Π½ΡŒ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ ΠΈ ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ, Π½ΠΈΠΊΠ°ΠΊΠΎΠΉ ΠΌΠ°Π³ΠΈΠΈ Π·Π° скобками Π½Π΅ остаСтся. ВсСгда Π±Ρ‹ Ρ‚Π°ΠΊ :)

  Π Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ 1 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π Π°Π΄, Ρ‡Ρ‚ΠΎ информация «зашла» :)

  Π Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ 1 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π˜Π½Ρ‚Π΅Ρ€Π΅ΡΠ½Ρ‹ΠΉ ΠΈ Π³Π»Π°Π²Π½ΠΎΠ΅ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ΠΉ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ (ΠΌΠ½ΠΎΠ³ΠΈΠ΅ ΠΏΠΎΡ…ΠΎΠΆΠΈΠ΅ ML ΠΌΠΈΠΊΡ€ΠΎ-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ встрСчал Π·Π°Π±Π°Π²Π½Ρ‹Π΅, Π½ΠΎ real-life Π·Π°Π΄Π°Ρ‡ Π½Π΅ Ρ€Π΅ΡˆΠ°ΡŽΡ‚).

Вопрос Π½Π΅ совсСм ΠΏΠΎ Ρ‚Π΅ΠΌΠ΅. Книга Π½Π΅ эта случайно? https://www.amazon.com/Hands-Machine-Learning-Scikit-Learn-TensorFlow-ebook-dp-B07XGF2G87/dp/B07XGF2G87/ref=mt_other?_encoding=UTF8&me=&qid=

Если ΠΎΠ½Π°, Ρ‚ΠΎ Π±Ρ‹Π»ΠΎ Π±Ρ‹ интСрСсно, насколько ΠΎΠ½Π° ΠΏΠΎΠ»Π΅Π·Π½Π° для Π½Π°Ρ‡ΠΈΠ½Π°ΡŽΡ‰Π΅Π³ΠΎ Π² ML ΠΈ ΠΊΠ°ΠΊΠΎΠΉ Π² Π½Π΅ΠΉ баланс Ρ‚Π΅ΠΎΡ€ΠΈΠΈ ΠΈ ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠΈ? На гудридсС ΠΏΠΈΡˆΡƒΡ‚, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠ΄Π° ΠΌΠ½ΠΎΠ³ΠΎ, Π½ΠΎ совсСм Π½Π΅Ρ‚ объяснСния Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€ Π² части ΠΏΡ€ΠΎ Π½Π΅ΠΉΡ€ΠΎΠ½Π½Ρ‹Π΅ сСти.

  Π Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ 1 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

@termith, Если ΠΈΠ½Ρ‚Π΅Ρ€Π΅ΡΡƒΡŽΡ‚ ΠΈΠΌΠ΅Π½Π½ΠΎ сСточки, Ρ‚ΠΎ Π½Π°ΡΡ‚ΠΎΡΡ‚Π΅Π»ΡŒΠ½ΠΎ Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡƒΡŽ "Π“Π»ΡƒΠ±ΠΎΠΊΠΎΠ΅ ΠžΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅" БСргСя НиколСнко. Π₯ΠΎΡ€ΠΎΡˆΠΈΠΉ баланс Ρ„ΠΎΡ€ΠΌΡƒΠ» ΠΈ объяснСний "Π½Π° ΠΏΠ°Π»ΡŒΡ†Π°Ρ…", всё обильно ΠΏΡ€ΠΈΠΏΡ€Π°Π²Π»Π΅Π½Π½ΠΎ ссылками Π½Π° ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½Ρ‹Π΅ ΡΡ‚Π°Ρ‚ΡŒΠΈ, ΠΎΡ…Π²Π°Ρ‡Π΅Π½Ρ‹ +- всС популярныС сСйчас области ΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Ρ‹, плюс ΠΊΠ½ΠΈΠ³Π° довольно свСТая. Π—Π°ΡˆΠ»Π° Π³ΠΎΡ€Π°Π·Π΄ΠΎ Π»ΡƒΡ‡ΡˆΠ΅, Ρ‡Π΅ΠΌ Ρ‚Π° ΠΆΠ΅ классичСская the Deep Learning Book

  Π Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ 1 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

@latyshev, спасибо Π·Π° Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°Ρ†ΠΈΡŽ. Π‘ΠΌΠΎΡ‚Ρ€Π΅Π» Π½Π° Π½Π΅Π΅ ΠΊΠ°ΠΊ-Ρ‚ΠΎ, Π½ΠΎ ΠΏΠΎΡ‡Π΅ΠΌΡƒ-Ρ‚ΠΎ ΠΏΡ€ΠΎΡˆΠ΅Π» ΠΌΠΈΠΌΠΎ. ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΡŽ Π΅Ρ‰Π΅ Ρ€Π°Π· :)

  Π Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ 1 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π”Π°, всС Π²Π΅Ρ€Π½ΠΎ, ΠΊΠ½ΠΈΠ³Π° ΠΈΠΌΠ΅Π½Π½ΠΎ эта - Β«Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd EditionΒ». Π― Π΄ΠΎΡˆΡ‘Π» ΠΏΠΎΠΊΠ° Π΄ΠΎ ~50-ΠΉ страницы :D ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ ΡΡƒΠ΄ΠΈΡ‚ΡŒ ΠΏΡ€ΠΎ ΠΏΠΎΠ»Π΅Π·Π½ΠΎΡΡ‚ΡŒ ΠΊΠ½ΠΈΠ³ΠΈ ΠΏΠΎΠΊΠ° Ρ€Π°Π½ΠΎ, Π½ΠΎ судя ΠΏΠΎ ΠΎΡ‚Π·Ρ‹Π²Π°ΠΌ, Π΄Π΅ΠΉΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Ρ‚Π°ΠΌ мСньшС Ρ‚Π΅ΠΎΡ€ΠΈΠΈ ΠΈ большС ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠΈ. Π’ΡƒΡ‚ ΡƒΠΆΠ΅ ΠΏΠΎΠ»Π΅Π·Π½ΠΎΡΡ‚ΡŒ зависит, Π΄ΡƒΠΌΠ°ΡŽ, ΠΎΡ‚ Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊΠΈΠ΅ Ρ†Π΅Π»ΠΈ Ρƒ читатСля: ΡƒΠ³Π»ΡƒΠ±Π»Ρ‘Π½Π½ΠΎ ΠΈΠ·ΡƒΡ‡ΠΈΡ‚ΡŒ Ρ‚Π΅ΠΌΡƒ ΠΈΠ»ΠΈ побыстрСС ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΏΠ΅Ρ€Π²Ρ‹Π΅ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Π² Colab-Π΅.

  Π Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ 1 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Бпасибо, я прямо зачитался, Π±ΡƒΠ΄Ρ‚ΠΎ Π±Ρ‹ это ΠΈ Π½Π΅ ΠœΠ› совсСм :)

  Π Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ 1 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Бпасибо πŸ‘€πŸ‘πŸ»

  Π Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ 1 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

НС Ρ‡ΠΈΡ‚Π°Π», Π½ΠΎ ΠΏΠ»ΡŽΡΠ°Π½Ρƒ. И Π² Π·Π°ΠΊΠ»Π°Π΄ΠΊΠΈ ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ ΠΆΠ΅ :D

  Π Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ 1 ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

😎

Автор поста ΠΎΡ‚ΠΊΡ€Ρ‹Π» Π΅Π³ΠΎ для большого ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚Π°, Π½ΠΎ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈ Π΄Π²ΠΈΠΆΡƒΡ…ΠΈ доступны Ρ‚ΠΎΠ»ΡŒΠΊΠΎ участникам ΠšΠ»ΡƒΠ±Π°

Π§Ρ‚ΠΎ Π²ΠΎΠΎΠ±Ρ‰Π΅ здСсь происходит?


Π’ΠΎΠΉΡ‚ΠΈ  ΠΈΠ»ΠΈ  Π’ΡΡ‚ΡƒΠΏΠΈΡ‚ΡŒ Π² ΠšΠ»ΡƒΠ±