Привет, хабр!
Меня зовут Марк Порошин вместе с моим коллегой Артемом Шнайдером в DV Group мы занимаемся Data Science. Сейчас мы активно развиваем собственную платформу клиентских данных (CDP) DV Platform. Коротко расскажу, зачем вообще она нужна. Платформа обрабатывает данные из маркетплейсов и позволяет создавать и передавать сегменты пользователей, которые с наибольшей вероятностью совершат покупки конкретной категории или товара. Это позволяет оптимизировать маркетинговые бюджеты и увеличивать онлайн-продажи брендов.
При работе с данными о покупках пользователей всегда необходимо анализировать их предпочтения и поведение, аналитикам строить отчеты и совершать другие манипуляции с данными, чтобы корректно их оценить. При большом объеме наименований товаров тяжело производить различные агрегации данными, например, посчитать общую выручку в категории «Красота и здоровье». Чтобы корректно анализировать данные, нужно правильно классифицировать товары в этом может помочь ML-модель, о которой мы сегодня и поговорим.
Подготовка данных
Мы в DV Group работаем с данными из различных источников, в том числе с чеками популярных российских маркетплейсов. Прежде чем приступать к работе над моделью категоризации, мы проанализировали существующие каталоги маркетплейсов и составили свой собственный набор категорий, который будет удобен для работы в будущем. За основу взяли каталог известного маркетлпейса так как он больше всех подходил под наши потребности, конечно, его пришлось немного переработать. В итоге у нас получилось дерево категорий, где примерно пять тысяч наименований на нижнем уровне, выглядит это следующим образом:
Автотовары->Автоаксессуары и принадлежности->Аварийные принадлежности->Автомобильные фонари->Автотовары->Автозапчасти->Тормозная система->Система ABS
…
Следующий этап – сбор и нормализация данных. Для этого собрали датасет вида <product_name>: (<cat1>, <cat2>, <cat3>, <cat4>), в соответствии с составленной категоризацией. За основу взяли открытые данные больших маркетплейсов и агрегировали их в единый каталог. Собрали датасет порядка 15 миллионов товаров с проставленными категориями. Дальше мы использовали эти данные в качестве обучающей выборки.
Первичный анализ данных
Для начала мы решили построить элементарные статистики о данных, размер каждой категории, распределение данных по категориям и тд. Сразу столкнулись с проблемой: данные сильно не сбалансированы т.е., например, в категории «Овощи и фрукты» у нас данных практически нет, поскольку их не очень активно продают онлайн. Однако появилась уверенность в том, что футболки мы сможем определять корректно, так как выборка исчислялась миллионами товаров. А еще мы поняли, что в данных присутствуют как опечатки в наименовании товаров, так и ошибки в категоризации, но в данном случае была надежда на то, что статистика все рассудит =).
Построение baseline модели
Хотелось быстро получить результат, чтобы сложилось первое впечатление о данных, и о проблемах, с которыми предстоит столкнуться.
Во-первых, необходимо было решить: какую именно архитектуру выбрать. Можно сделать одну модель, которая будет предсказывать сразу одну из категорий последнего (4-го) уровня, или же построить модель на каждый уровень категоризации.
Достаточно быстро стало понятно, что второй вариант нам подойдет больше и на это есть несколько причин:
Чтобы изменить структуру каталога или получить дополнительные данные не нужно будет переобучать модель полностью, достаточно будет только поменять часть моделей;
Для разных категорий можно применять различные алгоритмы машинного обучения и подготовки данных. Например, для категории «Аптека» ни word2vec, ни tfidf не дают удовлетворительных результатов;
Такую модель проще реализовать с точки зрения ресурсов, поскольку на всех данных нужно обучить только «рутовую» модель, у которой будет сильно меньше выходов.
Недостаток такого подхода – сложность внедрения решения в систему и бОльшее количество мест для ошибок и багов, куда же без них:)
Самое важное при решении NLP(Natural Language Processing) задач – переход от текстового представления данных к векторному. Существует несколько мейнстримных подходов:
Использование предобученной модели word2vec/glove/fasttext к токенам текста для получения эмбеддингов слов с последующей суммаризацией (и, возможно, нормализацией);
Применение предобученных моделей doc2vec/bert к экземпляру выборки;
Использование tfidf.
Вариант обучения собственных моделей из первых двух пунктов или дообучения готовых мы не рассматривали так как время на решение задачи было сильно ограничено.
После получения эмбеддингов, оставалось выбрать алгоритм машинного обучения и настроить гиперпараметры алгоритма. Наиболее популярными являются: байес, SVM, случайный лес или же градиентный бустинг.
В качестве архитектуры «рутовой» модели мы выбрали связку word2vec и xgboost, чтобы реализация была быстрее и меньше нагрузка на RAM. В моделях нижних уровней в текущей реализации используется tfidf + LinearSVC.
При данном подходе мы столкнулись с рядом сложностей, расскажу о них, и как мы их решали ниже:
Большое количество слов отсутствуют в модели word2vec. Чтобы решить проблему, составили словарь «неизвестных слов», для которых в качестве вектора проставили средний по категории вектор;
Низкое качество полученной модели – нужно использовать другое представление данных и алгоритм :)
Обучение бустинга на «маленьких» категориях – заменить более простыми алгоритмами, например SVM;
Для части категории нам не удалось получить даже минимальный удовлетворительный результат, среди них: «Книги», «Музыка», «Кино» и «Аптека». Связано это с тем, что в названии товара не содержится ключевых слов для определения подкатегорий. Для этих категорий мы строили отдельные модели, если интересно, пишите в комментариях, расскажу как мы это сделали в отдельной статье.
При анализе боевых данных было обнаружено большое количество экземпляров позиций в чеке, которые не относятся к товарам и не несут в себе никакой полезной информации, поэтому было решено добавить этап фильтрации «трешовых» товаров.
Итоговое качество модели получается как произведение 4 моделей для каждого уровня, поэтому, если каждая модель имеет качество на уровне 85% общая точность предсказания итоговой категории получается 52%. Очень важно бороться за каждый процент качества в корневой модели. Мы добились точности корневой модели на уровне 75% (совокупная точность по всем классам), у моделей остальных уровней точность была в среднем порядка 85%, что дает нам в итоге результат 0.46%.
Еще одной выявленной проблемой стали различные генеральные совокупности обучающих и боевых данных. Связано это с тем, что у нас было мало данных по некоторых категориям, например, продукты питания, в которых основная часть данных была в категории «специи», при этом в чеках они занимают значительную часть, конечно, это приводит к сильному ухудшению качества. Например, груши, картошка и репчатый лук, по мнению модели были приправами. При проверке модели на реальных данных из чеков, качество модели падало до 10%.
Рекомендации, как сделать лучше и не потратить много времени
Корневая модель
Следующая итерация – более качественная подготовка данных. Удаление stopwords, специальных символов и других малоинформативных токенов из исходных текстов названий товаров. После этого к полученным токенам мы применили tfidf для получения математической модели данных и добавили полносвязанную нейронную сеть с двумя скрытыми слоями. Без особых усилий и настройки параметров модели это дало прирост качества до 86% на выборке полученной от аналитиков, при этом на тестовой выборке, собранной из каталогов ритейлеров точность достигла 93%.
Анализ полученного отчета качества модели показал наличие проседающих категорий и несбалансированность тестовой выборки, а значит, ее невозможно использовать в продакшне.
Тогда мы приняли решение выбрать только те категории (precision & recall), в которых превышает 0.85 и отдать данные аналитикам для построения отчетов.
Тянулись дни и недели на подготовку данных, подбор оптимального размера словаря tfidf, количества скрытых слоев нейронной сети, активирующих функций. В итоге удалось получить следующие результаты:
И, вуаля, получилась итоговая архитектура:
def create_model(num_labels, vocab_size):
model = Sequential()
model.add(Dense(256, input_shape=(vocab_size,)))
model.add(Activation('relu'))
model.add(Dropout(0.3))
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dropout(0.3))
model.add(Dense(num_labels))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy',
optimizer=keras.optimizers.Adam(learning_rate=0.0003),
metrics=['accuracy'])
return model
Дочерние модели
При обучении дочерних моделей мы так же отошли от использования word2vec к tfidf, что дало значительных прирост качества моделей.
Понятное дело, чем глубже мы погружаемся по уровням категорий, тем меньше становится обучающая выборки, но при этом сужается и пространство признаков. Поэтому для категорий с маленьким набором семплов мы применяли преимущественно SVM и наивный байес. Для больших категорий, в основном второго уровня, мы использовали градиентный бустинг из библиотеки xgboost. Тут стоит отметить, что были попытки использовать catboost или lightgbm, но как бы мы ни старались подобрать гиперпараметры они давали чуть меньшую точность предсказания, хотя и обучались на порядок быстрее.
Почему так сложно классифицировать книги
Некоторые категории потребовали от нас дополнительной работы, например, книги. Начнем с того, что фасетная классификация как книжных магазинов, так и взятая нами за основу, содержит трудно различимые категории верхнего уровня, например, “Букинистика” и “Художественная литература”. Понять, что речь идет об антикварных изданиях зачастую просто невозможно. А еще некоторые наименования могут относиться сразу к нескольким различным категориям нижнего уровня. Например, книгу “Самый богатый человек в Вавилоне | Клейсон Джордж Самюэль” можно встретить в разделе нехудожественной литературы в категориях Саморазвитие и Психология.
Мы приняли решение попробовать классифицировать товары по нескольким меткам (Multi-label Classification). Но при таком подходе мы получаем большое количество иногда едва различимых меток (у нас было 308), и результаты классификации совсем не радуют. Точность модели Multilabel kNN в связке с Tf-Idf составила всего 19%.
Однако, кажется, что мы все еще можем определять категории верхнего уровня, поэтому мы немного доработали наш каталог классов. В результате остались следующие категории:
Бизнес-литература;
Детям и родителям;
Комикс. Манга. Графический роман;
Литература на иностранных языках;
Нехудожественная литература;
Учебная литература;
Художественная литература.
Модель линейного SVM показала точность 72%., на данный момент – это хороший показатель для бизнеса. Еще один дополнительный этап работ – определить автора, то есть задача NER (Named Entity Recognition). Обучать свою модель мы не стали, а воспользовались предобученной моделью от DeepPavlov на основе Bert (ner_rus_bert). Точность распознавания составила порядка 93%.
В рамках работы получилось решить еще две задачи: детекция аудиокниг и определение ценовой категории, благо здесь никаких сложностей не возникло.
В первом случае мы справились простой логистической регрессией на небольшой обучающей выборке из наименований, по которым можно однозначно понять, цифровой товар это товар или нет (например, “Русские сказки (аудиокнига на 2-х CD-MP3)”).
Во втором случае с помощью KMeans определили пять ценовых диапазонов, используя наименование товара и примерную рыночную стоимость.
В качестве вывода
Нам удалось решить бизнес-задачу категоризации и облегчить жизнь аналитиков. Часть категорий, конечно, пришлось рассматривать отдельно и применять к ним другие подходы. Вот несколько ключевых моментов, которые нам сильно помогли в ходе разработки данного решения:
Стоит обращать внимание на токены “кг”, “шт” и прочие;
Чуть ли не самой большой проблемой стало внедрение моделей в систему, поэтому желательно продумывать это заранее;
У нас по итогу получилось около 800 моделей и мы очень жалеем, что во время обучения не уделили внимание структурированию версий и грамотному сбору статистики по каждой модели;
Не удастся выработать общий подход при решении такой комплексной задачи, все равно придется прорабатывать почти каждую модель по отдельности;
Все время держите контакт с представителями бизнеса, чтобы не тратить время на менее приоритетные задачи, в нашем случае модели категоризации.
Это всего лишь небольшая часть работы, которую мы сделали в рамках классификации товаров. Напомню, что наш основной вектор деятельности – диджитал-маркетинг, мы помогаем крупным брендам расти в e-commerce.
P.S Если у вас есть идеи, как можно было сделать проще и легче, предлагайте их в комментарии, будет интересно почитать :)