Классификация кассовых чеков

b1c759c5ee30e1809e561010df164509.jpg

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

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

Весной 2021-го года ВТБ организовывал соревнование на платформе Boosters с целью решения этой задачи.

Описание соревнования

Участникам был предоставлен массив данных, содержащий более семи миллионов неразмеченных чеков и более одного миллиона размеченных чеков. К сожалению, уникальных размеченных товарных позиций было порядка 50 тысяч, заявленный миллион чеков был сгенерирован на основе этой разметки. К счастью, в неразмеченных данных такая тенденция не прослеживалась и в них содержалось примерно 3 миллиона уникальных товарных позиций. В выборке содержались объекты из 90 различных классов.

Товарная позиция

Цена

Кол-во

НДС

День недели

Время

ЙогуртпитEPICAклуб/мар290

10

1

2

5

11:34

Пиво Велкопоповицкий Козел темное ж/б 3,7% 0,45л

9

1

6

3

20:54

Энерг. нап. Адреналин Раш 0,25л ж/б (ПепсиКо)

10

1

1

4

14:38

Нектар ФрутоНяня Тыквенный с мяк 0,5л

12

1

6

6

17:12

Картофель весовой

8

0.43

1

2

11:03

В дополнение к названию товарных позиций были доступны мета-признаки: ценовой бакет, количество товара, тип НДС, день недели и время покупки. Опыт подсказывает, что весовые товары сложно купить в целом количестве штук, а схема налогообложения в РФ различна для ряда товаров и услуг, поэтому дополнительные категориальные признаки могут быть полезны для решения задачи.

Организаторы соревнования выбрали метрику weighted f1 score в качестве целевой. Соревнование проходило в формате docker. Время выполнения алгоритма ограничивалось 1.5 часами. Каждый сабмит не должен был превышать 500 мегабайт. Характеристики среды исполнения: 8 vCPU, 46 GB RAM, Nvidia Tesla V100 16 GB.

Подготовка данных

Без преувеличения, текстовые данные кассовых чеков можно смело называть «грязными». Очень часто разделителем является не пробел, а точка, слэш, смена регистра, а иногда и смена языка. Некоторые магазины очень любят сокращения, другие, наоборот, для экономии обрезают часть слов. Некоторые даже экономят на мощности словаря и заменяют все похожие буквы из кириллицы на аналоги из латиницы или цифры: «к» на «k», «с» на «c» или «з» на »3». Также не всегда возможно по названию товара из чека произвести биекцию на товарную позицию, например,»Шоколадный батончик Snickers Лесной орех, 81 г» в каталоге может называться как »Шок. бат».

Алгоритм предобработки данных состоял из следующих этапов:
1. Перевод всех символов в нижний регистр.
2. Замена всех визуально-похожих символов на вариант из кириллицы.
3. Токенизация по группе регулярных выражений.
4. Замена всех последовательностей цифр на »1».

Так, например, строка »Молоко Домик.в деревне 3.2% 0.5л пл/б БЗМЖ/Россия» после предобработки превращалась в «молоко домик в деревне 1% 1л пл/б бзмж россия».

С одной стороны, размер обучающей выборки крайне мал для того, чтобы выучить вектора слов в режиме end-to-end. C другой стороны, доменный контекст в этой задаче сильно отличается от Википедии, Ленты и других популярных датасетов, на которых предобучают векторные представления слов. К счастью, после предобработки данных получалась выборка порядка 2.7 миллионов уникальных строк, что эмпирически можно считать достаточным объем для обучения векторов под специфичный домен. Пример обучения векторов FastText из туториала через CLI-интерфейс:

$./fasttext skipgram -input data/fil9 -output result/fil9 -minn 2 -maxn 5 -dim 300

Модели

Организаторами соревнования был предоставлен бейзлайн на основе логистической регрессии и мешка слов 0.63 F1. Замена логистической регрессии на SVM и добавление символьных нграмм заметно улучшили качество до 0.84 F1. В соревновании на kaggle на аналогичных данных от @dremovd значительно улучшало качество добавление контекста всего чека. В этом соревновании такой подход не зашел, так как чеки были искусственно сгенерированы.

На данный момент SOTA-решением для классификации текстов принято считать BERT-подобные архитектуры. Они показывают отличное качество за счет огромного количества обученных параметров, впитавших контекстные знания из огромных корпусов документов. К сожалению, названия товарных позиций сильно отличаются от текстов новостей и статей википедии. Более того, не существует аналогичных корпусов для домена кассовых чеков, на котором можно было бы обучить BERT. В случаях серьезного доменного отличия эмпирический опыт говорит, что рекуррентные сети справляются с решением задачи лучше.

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

image-loader.svg

В первую версию решения были добавлены дропауты и L2 регуляризации, а также подобрано число обучаемых параметров для того, чтобы сеть не переобучалась. Дополнительно был использован Cyclic Learning Rate для лучшей сходимости. В итоге такое решение получило 0.85 F1.

Следующим шагом было ансамблирование моделей. Размеченная выборка была разделена на пять фолдов, выбранная архитектура обучалась на подмножествах из четырех фолдов до тех пор, пока росло качество на оставшемся фолде. Предсказания для тестовой выборки считались как среднее от пяти моделей. Ансамблирование улучшило качество решения до 0.86 F1.

Вектора были необучаемыми параметрами сети ввиду того, что у python-пакета fasttext отсутствует совместимый с pytorch и tensorflow интерфейс для дообучения векторов в end-to-end режиме. FastText при построении векторного представления слова использует нграммы слова, а также особым образом учитывает его начало и конец. Улучшение алгоритма токенизации повысило качество решения до 0.865 F1.

Соревнование проходило в docker-формате, что приводило к необходимости отправки архива с кодом и бинарников в проверяющую систему в рамках каждого сабмита. В системе было установлено ограничение в 500 мегабайт на размер входного файла. Бинарный файл с размерностью векторов 50 едва пролезал через это ограничение.

image-loader.svg

При использовании параметров по умолчанию библиотека fasttext создает 2 миллион хэшей для хранения векторов нграмм. Их количество можно снизить, уменьшая значение параметра -bucket. В ходе экспериментов выяснилось, что оптимальным размером вектора является 150. Таким образом, при размере словаря 95k и размерности ембеддинга 150 и -butcket=2m бинарный файл весит 1.2 ГБ, а при -bucket=700k — всего 480 МБ. Это бесценное знание помогло увеличить размерность с 50 до 150. Увеличение размера векторного представление слов повысило качество до 0.88+ F1.

Пролог

К сожалению, не всегда удавалось понять четкое различие между классами при помощи естественного интеллекта. В данных было несколько классов с продуктами, скорее всего, различающих по формату магазина: супермаркет, магазин у дома, гипермаркет. Более того, в выборке был выделен класс «другое», в который асессоры относили не только «другие товары», но и сложные объекты из остальных классов. Частичная переразметка этого класса не приводила к росту метрик, так как на тестовой выборке разметка, видимо, была идентичной.

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

Материалы

Турнирная таблица

Private leaderboardPrivate leaderboard

Публичные решения

https://github.com/dremovd/data-fusion-1-baseline
https://github.com/gorodion/GoodsClassifier
https://github.com/aptmess/data_fusion_goodsification
https://github.com/pozdnyakovx/VTB_data_fusion_2021
https://github.com/pskliff/vtb-data-fusion
https://github.com/Irinas5555/Data-Fusion-Contest-Goodsification--18-place-on-public-liderboard-
https://github.com/v-pozdnyakov/data-fusion-contest

Конференция Data Fusion

© Habrahabr.ru