Машинное обучение @ DKatalis: создание синтетических данных с помощью Photoshop и Python для большого блага!

Машинное обучение - дело дорогое. Обучение моделей стоит денег и даже больше, когда задействованы графические процессоры. Однако, как большинство компаний, углубившись в изучение любого нетривиального машинного обучения, обнаружит, именно данные составляют большую часть затрат.

В этом посте я подробно расскажу, как я использовал Photoshop и Python для создания тысяч (более 10 тысяч) синтетических идентификационных карт Индонезии для обучения модели глубокого обучения OCR-сегментации и распознавания.

Проблемная область

Это пример индонезийской идентификационной карты, также известной как Kartu Tanda Penduduk, или сокращенно KTP.

В рамках процесса подключения к приложению Jago Bank мы хотим разрешить пользователям загружать изображения своих KTP, а на следующем этапе сразу же предварительно заполнить важную информацию, такую ​​как NIK (идентификационный номер), имя , и адрес.

Теперь переход от случайных изображений KTP к предварительно заполненным полям требует значительных усилий в области науки о данных и инженерии, но это уже другая статья в блоге. В этом посте я хочу рассмотреть проблему сбора данных.

Шаг 0: получение и маркировка данных

В зависимости от варианта использования сбор данных может быть чрезвычайно сложным. Рассмотрим на мгновение этот точный вариант использования и подумайте, как бы вы собирались получать изображения карт KTP для обучения своей модели машинного обучения OCR. Вы можете просматривать YouTube и Pinterest сколько угодно, но вы не сможете собрать достаточно данных для осмысленного обучения какой-либо модели.

Если вам посчастливилось иметь клиентов (как и нам!), Тогда у вас будут некоторые данные для начала. Но тогда кто будет делать аннотации? Теперь, если у вас есть люди в компании, желающие делать аннотации (как это делаем мы!), То вам следует поблагодарить своих счастливых звезд.

Вот что мы требуем от наших аннотаторов:

Помимо рисования ограничивающих рамок, они также необходимы для заполнения текстовых значений.

Так здорово, допустим, у вас есть данные, и у вас есть люди, помогающие с аннотациями, что вы собираетесь делать тем временем, пока вы ждете, пока аннотированные данные поступят? Это может быть аналог Data Scientist:

Подделка, чтобы сделать это

Иногда требуется интуиция и творческий подход, чтобы наткнуться на неожиданное решение. Когда я искал изображения образцов карт KTP (я хотел найти примеры многострочных адресов в качестве крайнего случая), я наткнулся на этот сайт, на котором были эти два изображения:

Более того, он шел с шаблоном PSD! (Это тот момент, когда я обрадовался визжащим звукам ...)

Это круто, потому что:

  1. Мне не нужно выяснять, какие шрифты мне нужны (OCR A и Arial, если вам интересно)
  2. Каждое поле было выстроено правильно
  3. Самое главное, что карта выглядела полностью законной!

Итак, теперь мы могли в Photoshop редактировать любые текстовые поля, сколько душе угодно, и даже заменять удостоверение личности с фотографией, если захотим. Но как мы могли автоматизировать все это? Я хочу, чтобы всю тяжелую работу выполняла моя машина, а не я. Другими словами, как мне скрипт Photoshop делать мои ставки?

Автоматизация Photoshop с помощью Python

Немного покопавшись, мы получили следующий камень: https://github.com/loonghao/photoshop-python-api. Вот образец из README:

import photoshop.api as ps
app = ps.Application()
doc = app.documents.add()
new_doc = doc.artLayers.add()
text_color = ps.SolidColor()
text_color.rgb.green = 255
new_text_layer = new_doc
new_text_layer.kind = ps.LayerKind.TextLayer
new_text_layer.textItem.contents = 'Hello, World!'
new_text_layer.textItem.position = [160, 167]
new_text_layer.textItem.size = 40
new_text_layer.textItem.color = text_color
options = ps.JPEGSaveOptions(quality=5)
# # save to jpg
jpg = 'd:/hello_world.jpg'
doc.saveAs(jpg, options, asCopy=True)
app.doJavaScript(f'alert("save to jpg: {jpg}")')

При выполнении сценария Photoshop выполняет следующие действия:

Надеюсь, вы начинаете видеть здесь возможности. Мы можем управлять Photoshop через Python, чтобы отредактировать каждый текстовый слой выбранным нами текстом, а также заменить идентификатор фотографии. Позже мы подробнее рассмотрим код, но перед этим нам нужно еще кое-что выяснить: Что мы должны заполнить в текстовых полях?

Некоторые поля просты. Пол, группа крови, национальность, даты, НИК легко рандомизировать. Однако как насчет таких вещей, как имена и адреса? Одно из полей - род занятий!

Подделка значений текста с помощью Faker

Если вы когда-либо занимались разработкой через тестирование, вы наверняка встречали Faker. Он доступен на разных языках и, конечно же, на Python:

from faker import Faker
fake = Faker()
fake.name()
# 'Lucy Cechtelar'
fake.address()
# '426 Jordy Lodge
#  Cartwrightshire, SC 88120-6700'

Но ни Lucy Cechtelar, ни Cartwrightshire не звучит очень индонезийским, не так ли? Вот что я считаю супероружием Faker: локализация! Проверь это:

>>> faker = Faker("id_ID") # <-- Indonesian Localisation applied here!
>>> faker.name()
'Restu Irawan, S.I.Kom'
>>> faker.address()
'Gg. Peta No. 2\nMedan, Sumatera Selatan 99756'

Я сделал еще несколько попыток, и вот что придумал Faker:

>>> faker.name()
'Puti Icha Susanti, S.Sos'
>>> faker.name()
'Dt. Marwata Maheswara, M.M.'
>>> faker.name()
'Tgk. Aisyah Safitri, S.Ked'

Помните, что в KTP есть поле для профессии? Faker получил вашу поддержку:

>>> faker.job()
'Event organiser'
>>> faker.job()
'Armed forces training and education officer'
>>> faker.job()
'Nurse, mental health'

Это не на бахасе, но в этом случае я Faker немного расслаблюсь. Есть даже индонезийские города:

>>> faker.city()
'Kediri'
>>> faker.city()
'Binjai'
>>> faker.city()
'Surabaya'

Хорошо. У нас есть текстовое наполнение. Что насчет удостоверения личности с фотографией?

Этого человека не должно быть!

Если вы не испытали на себе чудо https://thispersondoesnotexist.com/, вы упускаете возможность. Зайдите по ссылке, обновите пару раз. Давай, я подожду. Этот сайт, созданный GAN, представляет собой кладезь поддельных человеческих лиц.

И, конечно же, для этого есть пакет Python: https://github.com/David-Lor/ThisPersonDoesNotExistAPI.

Итак, как выглядят полученные результаты?

Я рада, что вы спросили! Вот пример:

Вы можете возразить, что почти никто из присутствующих здесь не выглядит индонезийцем. Однако напомним, что наша проблема с машинным обучением связана с распознаванием текста. Пока карты выглядят иначе, ничего страшного.

Итак, теперь, когда у нас есть синтетические данные, мы можем перейти к следующей части головоломки: созданию синтетических меток.

Создание ограничивающих рамок

Оказалось, это было немного сложно. Вы могли бы подумать, например, что вы можете вычислить количество символов и взять оттуда смещение. К сожалению, это будет работать только для поля NIK по той простой причине, что только поле NIK использует шрифт фиксированной ширины. Поскольку NIK имеет фиксированное количество символов, ограничивающую рамку менять не нужно!

Для остальных полей они бывают разной длины. Так что делать? После того, как мы немного почесали голову, пришел ответ! Первое наблюдение заключается в том, что левые координаты ограничивающих прямоугольников всегда фиксированы (отмечены красными линиями):

Это означает, что все, что нам нужно сделать, это увеличить ограничивающие прямоугольники в направлении оси x. Другими словами, нам просто нужно настроить ширину ограничивающих рамок. Однако этот прием не будет работать для полей прямо вверху карты, так как они расположены примерно посередине карты. Учитывая, что Arial не является шрифтом фиксированной ширины, как мы можем затем установить размер ограничивающей рамки?

Вот подсказка:

Если бы мы нарисовали отдельные ограничивающие прямоугольники на всех полях всего один раз, сможем ли мы выяснить, какой должна быть ширина ограничивающих прямоугольников в следующей сгенерированной карточке? Мы можем точно! Все, что вам нужно, это немного математики. Каждое текстовое поле уже внутренне представлено ограничивающим прямоугольником (мы не используем его напрямую, потому что оно включает метку (например, «Nama»)).

Все, что нам нужно сделать, это выяснить, насколько шире увеличился / уменьшился каждый ограничивающий прямоугольник, и применить тот же процент к зеленому ограничивающему прямоугольнику. Самое приятное то, что мы можем применить то же вычисление ширины результирующей ограничительной рамки к двум верхним полям.

Генерируемый файл аннотации совместим с теми, которые используются для обучения. Вот пример сгенерированного файла (некоторые другие поля опущены):

<annotations>
 <image name="1124436213898514.png">
 <polygon name="NAME" points="22.0,175.0;490.0,175.0;490.0,205.0;22.0,205.0">
 <attribute name="value">Nama : RAFI ISWAHYUDI</attribute>
 </polygon>
 <polygon name="NIK" points="21.0,119.0;739.0,119.0;739.0,161.0;21.0,161.0">
 <attribute name="value">NIK : 1124436213898514</attribute>
 </polygon>
 </image>
</annotations>

Большая картинка

Надеюсь, теперь вы начинаете видеть, как все элементы подходят друг другу. Python будет управлять Photoshop, заполняя текстовые значения Faker или простыми функциями рандомизатора и заполняя идентификаторы фотографий изображениями с https://thispersondoesnotexist.com/.

Вот самое важное при создании синтетических данных: аннотации (почти) всегда будут на 100% правильными! Это потому, что вы всегда знаете, какие фальшивые значения использовались. Помимо необходимости получать точные аннотации

На данный момент у нас есть все необходимое для создания 100% правильных аннотаций. У нас есть не только текстовые значения, но мы также можем вычислить координаты ограничивающего прямоугольника. Итак, Python и Photoshop усердно работают для нас во всей красе:

Увеличение изображения

Несмотря на то, что мы можем генерировать тысячи изображений KTP с невероятной самоотдачей, если вы посмотрите на все изображения издалека, все они выглядят очень похожими. Для алгоритма глубокого обучения это будет означать, что данные, скорее всего, будут чрезмерно соответствовать, и это не очень хорошо, особенно когда реальные данные никогда не бывают такими аккуратными.

Имея это в виду, никто не мешает вам выполнять аугментацию изображений на синтетических данных. Здесь я нанес точки, где должны быть ограничивающие рамки, просто чтобы показать, что преобразования также применяются к координатам. Если вы прищурите фон, то увидите, что я смешал случайные фоны, чтобы создать больше шума.

Вывод

Наличие синтетических данных было чрезвычайно полезно для разработки и тестирования модели и запуска в ожидании поступления реальных данных. Генерация каждого изображения занимает от 8 до 16 секунд (есть HTTP-вызов для получения идентификатора фотографии, который также необходимо учитывать. ).

Однако это не проблема, потому что я просто запустил скрипт на ночь и позволил Photoshop сделать свое дело. За неделю я собрал более 10 тысяч синтетических изображений с идеальными аннотациями:

В целом результатом я очень доволен. Это один из тех редких моментов, когда разные части программного обеспечения объединяются для решения интересной проблемы.

И, эй, если тебе случится найти это интересным (ты чудак), например, работа с Kubernetes, споры о конвейерах машинного обучения и бессмысленные разговоры со специалистами по данным, то может быть, ты бы отлично вписался в команду!