[Из песочницы] Как мы учились быстро обрабатывать сканы чеков
В этой статье я расскажу, как мы учились распознавать чеки (а точнее слипы), на какие грабли наступили и какой эффективности добились.
Начну с краткого интро. Мы зарабатываем тем, что проводим промо-акции для производителей и продавцов разнообразных потребительских товаров. Как правило, это что-то из разряда «купи товар — найди код — отправь его нам — выиграй приз». Основной наш продукт — это платформа, которая эти коды генерирует, принимает, обрабатывает, помогает коммуницировать с участниками, выплачивает бонусы и делает еще много чего интересного.
В последнее время мы стали все чаще видеть акции от коллег по цеху, где подтверждением покупки служил не промо-код, как в примере выше, а фото чека. Причем теперь явление становилось массовым. Оставаться в стороне было непозволительно.
Первым делом я зарегистрировался во всех акциях с чеками, о которых узнал (ну если честно не во всех, где-то в районе второго десятка мне надоело). Чеков у меня, как вы догадываетесь, не было и я решил вместо чека использовать фото котика на абстрактном фоне. Каково же было мое удивление, когда во всех акциях, кроме одной, моего котика приняли и допустили меня до розыгрыша призов. А кое-где даже выдали моментальный приз в виде промо-кода в онлайн-библиотеку. Честно говоря, в той одной акции котика тоже приняли, но обещали направить его на модерацию и в течение 8 (!) часов решить вопрос о моём участии в акции.
Такой вариант нам явно не подходил. Во-первых, допускать человека до розыгрыша по любому фото не хорошо. Он может сколько позволит платформа раз загрузить фото одного и того же чека, тем самым многократно повысив свои шансы на получение выигрыша. Когда одна из этих заявок выигрывает, предъявляется оригинал того самого единственного чека и забирается приз. Конечно, есть шанс выиграть дважды и выдать себя, но это мы уже увлеклись. Во-вторых, 8 часов не давать человеку обратной связи выглядит издевательски в мире, где посетитель, проведший на сайте более 15 секунд, считается целевым. В-третьих, давать приз за фото котика означает показать себя не очень компетентным организатором. Кстати, вот он.
Вывод напрашивался сам собой: нам нужно научиться распознавать чеки. Задача сложная, потому пошли к профессионалам — одной известной компании. У них по счастью оказалось решение для распознавания чеков, по несчастью не локализованное на российском рынке. В честь этого нам дали 1000 бесплатных попыток распознать чек, обещание помочь советом и пожелали удачи.
К тому моменту появился запрос от клиента. Перед нами стояла задача провести акцию для крупной сети розничных магазинов. Забегая вперед, скажу, что принимали до 1000 регистраций в день. Чтобы претендовать на призы в акции вам нужно было в определенный промежуток времени купить чего-нибудь на сумму от N руб. и обязательно оплатить покупку картой VISA. Фото полученного при покупке слипа следовало загрузить к нам на промо-сайт. Если вы признавались победителем, вы должны были предъявить слип и карту VISA на кассе и забрать приз. Одно фото — один шанс на победу. Победитель вычисляется среди всех участников, загрузивших корректные слипы, по специальной формуле. Наша задача на данном этапе: принять слип и допустить/не допустить человека до участия в розыгрыше. При этом желательно по максимуму отсечь хитрецов, которые могут пытаться подсунуть нам один слип дважды, подсунуть слип, напечатанный до начала акции и еще много всего интересного включая, но не ограничиваясь фото котика.
Многократное тестирование продукта крупной компании показало, что он определяет сумму покупки, тип карты, номер карты, время и дату печати слипа. И вроде вот оно: дубликаты отметаем (для этого вычисляя хеши распознанных параметров и самой картинки), сумму, дату, платежную систему и номер карты распознаем. Правда, распознается с ошибками… и не все.
Напомню, при выдаче приза у победителя проверялась сумма слипа и карта, точнее последние 4 цифры номера карты. Данные сверялись с реестром, который по итогам розыгрыша автоматически отправлялся в магазин нашей системой. То есть, эти данные просто обязаны быть корректными.
Нам пришлось пойти на первый компромисс: просим участника руками ввести сумму покупки и последние 4 цифры номера карты. Далее если то, что ввел человек и то, что распознала машина, совпало, а платежная система и дата печати слипа корректны, допускаем участника к розыгрышу.
Посчитали, оказалось, что допускаем мы только 71% слипов. Остальные 29% это некорректные либо некачественные изображения и корректные, но некорректно распознанные изображения в примерном соотношении 50/50.
Как быть с этими 14,5% чеков, отклоненных ошибочно? Решение пришло довольно быстро, стали отправлять на ручной аппрув в дружественный контакт-центр. Из минусов: дорого и долго. Если 71% счастливчиков получали результат в течение минуты, то этим людям пришлось сообщать об ожидании до 8 часов. Было решено попытаться нормализовать результаты распознавания в нашей системе.
Включаем аналитику: вручную сверяем данные на фото и результат распознавания. Результат распознавания прилетает следующим образом: отдельно поля «дата», «сумма» и т.д. и отдельно полный текст, то есть вообще все что было найдено на изображении. Зачастую данные, не содержащиеся в одном из первых полей, можно было отыскать в полном тексте глазами. По итогу анализа нескольких сотен слипов решили делать следующее:
1) Отличаем чек от слипа: среди всех принятых слипов ищем слип с максимальным количеством строк. Для любого отклоненного (по любой причине) документа считаем количество строк, если оно превышает рассчитанный ранее максимум, говорим человеку «Возможно, вы пытаетесь загрузить чек, а не слип. Сфотографируйте слип отдельно от чека и повторите попытку». Таким образом человек лучше понимал, что не так с его фото.
2) Если не распознана дата: пробуем искать в полном тексте фрагмент по маске «ХХ/ХХ/ХУ», где Х-любое число, а У — любой символ. При нахождении фрагмента У менять на 6 (или 7 в зависимости от года проверки), найденный фрагмент считать датой печати слипа. Да-да, косячила система в основном на последней цифре даты. Выиграли 2%.
3) Если не распознана сумма: искать в полном тексте по маске «ZХХХХ.ХХ RU», где Х-любое число, а Z — любой символ в том числе пробел или отсутствие символа. Найденный фрагмент сравнить с тем, что ввел участник. При расхождении поочередно заменить все символы 6 на 8 в найденном фрагменте и сравнить с тем что ввели. Почему-то машина часто путала именно 6 и 8, причем не 8 и 6, а именно 6 и 8. Выигрыш порядка 3%.
4) Номер карты: искать в полном тексте по маске »** XXXX», где Х-любое число. Между символами Х могут стоять пробелы либо знаки препинания, их отбрасываем. Полученное число сравниваем с введенным вручную номером карты. + 1%.
5) Карта оплаты: искать в полном тексте один из фрагментов: «Карта: V», «Карта: V», «Карта«V», «VISH». При нахождении считать карту картой VISA. + 3%.
Таким образом, мы довели количество принимаемых в течение одной минуты заявок до 80%. Увы, на этом возможности нормализации были практически исчерпаны, и мы переключились на повышение эффективности ручного распознавания (но это уже другая история).
В общем и целом, у нас получилась, насколько мне известно, первая в стране акция с реальным машинным распознаванием чеков. Результат для первого раза мне кажется неплохой, а к лету наш партнер обещал заметно повысить качество распознания, официально представив русскую версию своего сервиса.