Этим летом довелось участвовать в конкурсе анализа данных от компании Авито. Суть задачи была в том, чтобы определить дубликаты рекламных объявлений. Для этого компанией были предоставлены гигабайты данных.
Набор данных состоял из файлов с информацией об объявлениях, файлах с локациями, категорий и более 50Гб изображений. Информация включала заголовок, сам текст, цену, различные параметры в JSON поле и пр. Метрика качества — площадь под ROC-кривой. На рисунке ниже представлена схема данных.
Для этой задачи я решил применить мощный и набирающий популярность в последнее время xgboost. Его приемуществом является скорость обучения и невысокая требовательность к данным. Однако, скармливать ему сырые изображения и данные с семантикой, например заголовок или описание бессмысленно.
Простой инжиниринг
Некоторые поля данных были мало информационны и я решил их слегка модифицировать. Добавил бинарные поля сходства для цены, широты и долготы, длины текста и заголовка, категории товара и родительской категории. Полученный на этом этапе результат был уже достаточно неплох. Метрика качества показала результат 78.
Работа с изображениями
Теперь настало время для изображений. Компания Авито предоставила огромное количество картинок. Можно добавить фичу сравнив имена или вес изображений в байтах. Для более информативного признака нужно что-то более серьезное.
Хорошим решением могло быть использование какой-либо предобученой сети, например, на ImageNet. Но к сожалению, моя видеокарта не позволяет легко и быстро решить задачу таким путем. Поэтому я выбрал другое решение, основанное на перцептивных хешах. Подробно о том, как они рассчитываются можно прочитать, например, здесь .
После расчета хешей и добавления признака их сравнения результат вырос значительно и достиг 88.
Работа с текстом
Еще из двух больших полей можно что-то выжать – это заголовок и сам текст объявления.
Естественно, я решил начать с простого. Алгоритм шанглов на коротких текстах работает очень плохо и нужно было придумать что-то другое для сравнения.
Первая мысль, которая пришла – сравнить основы слов после стемминга. Для этого я поэкспериментировал с несколькими стемерами и выбрал разработку Яндекса — mystem. Для совпадения словоформ я сформировал простой коэффициент сходства.
1 2 3 4 5 6 7 8 9 10 11 12 | def get_similarity_KE(lemmas1, lemmas2): big = lemmas2 small = lemmas1 if len(lemmas1) > len(lemmas2): big = lemmas1 small = lemmas2 inters = [i for i in small if i in big] # TODO: or conversely # no intersection if len(inters) == 0: return 0 ratio = (len(inters)/len(lemmas1) + len(inters)/len(lemmas2)) / 2.0 return ratio |
Далее нужно убрать стоп-слова, слова с латинским написанием (это чаще всего бренды). В этом помогла библиотека nltk.
Удалось обнаружить интересный стемер pymorphy2 .
Он показал неплохое качество на небольшом фрагменте, но он очень медленно работает и расчет на моей машине для всего сета выполнялся бы около недели.
В экспериментах я попробовал применить doc2vec, продолжение идеи word2vec. Чтобы долго не проводить обучение, можно скачать обученную модель корпуса русского языка. Хотя у нее есть свои недостатки, например, в объявлениях встречаются незнакомые слова.
Эксперименты с текстами дал прирост к результату около 5. Финальный результат составил 94.
Более-менее оформленный код экспериментов я оставлю здесь. Промежуточные файлы с данными можно получить выполнив соотвествующие скрипты.