В прошедшие выходные (14-15 июля) в офисе Mail.Ru Group прошел очередной хакатон SmartMailHack. Нам предложили «придумать фичу, которая позволит получить доступ к данным из Почты и более эффективно взаимодействовать с ними».
Описание данных, идеи и решения
Мы получили тестовый ящик с более чем 1500 сообщениями, а также полный доступ к нему через API. Организаторы предоставили большой, развернутый мануал по его использованию (книжка на 547 страниц). С помощью токена и простых JSON-запросов, мы могли получать всю необходимую информацию о почте: письма, имена отправителей, различные характеристики.
Обсудив, что у каждого из нас в почте порядка нескольких тысяч непрочитанных писем из рассылок, мы решили разобраться с этой проблемой. В нашем случае релевантность письма перестала определяться временем его появления в почтовом ящике. И если исходить из того, что будут открыты далеко не все письма, тогда лучше показать пользователю только те из них, которые он, вероятно, откроет. А всё остальное можно убрать куда подальше. Так что мы решили сделать сортировку для почты.
Сортировать письма предполагалось по категориям, а категории размещать внутри плиток (привет, Trello). Верхний ряд плиток объединял по смыслу письма от разных отправителей. Здесь могут быть «Поездки», «Регистрации», «Переписка с Васей», «События», «Финансы» и так далее, всего около 10 категорий. Вторым рядом шли плитки с самыми крутыми предложениями от компаний. Мы искали самые релевантные промокоды, самые скидочные акции, самые ценные предложения, и показывали их здесь, сгруппированными по компаниям. Затем шли все остальные письма, распределенные по компаниям-отправителям, а эти отправители, в свою очередь, были раскиданы по категориям («Еда», «Косметика», «Электроника» и другие). Причем категории тоже ранжировались по релевантности писем, и только письма, преодолевшие определенный порог релевантности, показывались внутри. Подкрепив идею словами «Найти нужное и избавиться от лишнего», мы полезли в ML.
Машинное обучение
Мы решили построить три модели:
- классификатор из более чем 30 категорий, которые мы назначили базовыми для всех пользователей;
- кластеризация и выделение новых категорий на основе предпочтений пользователя;
- ранжирование писем в пределах категории, от наиболее релевантных к наименее.
Признаки
Кажется, что данный пункт должен быть описан персонально для каждой задачи. Однако мы сгенерировали один общий датасет признаков и обучали на нём все модели. Времени для тщательного отбора не было.
Была куча бинарных признаков, которые выгружаются с помощью API. Однако, большинство из них оказались сгенерированы на текстах:
- tf-idf на коллекции документов;
- эмбеддинги полученные с Word2Vec;
- поведенческие признаки, такие как:
— количество прочитанных сообщений за последние окна (1, 2, 5 недель назад);
— число сообщений от данной.
Задача классификации
Мы разметили руками 1000 писем для обучения. Оказалось, это не такая медленная и нудная работа как может показаться сперва. Если использовать адреса и заголовки, можно заметно ускорить работу. Например, Lamoda почти всегда присылает письма в категорию «Одежда».
Далее мы обучаем LightGBM на всем множестве признаков и получили качество 0.913 accuracy и 0.892 f1 меры, что мы определили очень неплохим результатом на уровне base line. Это показывает что письма можно классифицировать очень неплохо.
Задача ранжирования
В качестве целевой переменной мы использовали бинарный флаг 0/1 — было ли прочитано письмо пользователем. Далее ранжировали по вероятности, предсказанной моделью, поскольку именно это отражает то, насколько модель уверена в том прочитает человек сообщение или нет.
Здесь мы так же обучили LightGBM на всем множестве признаков и получили качество около 0.816 auc-roc.
Кластеризация и выделение новых категорий
Помимо основных категорий у нас есть категория «Другое». Из нее можно выделять новые темы.
Мы обучили стандартный DBSCAN на письмах из этой группы, а дальше выбрали те кластеры, в которых было много сообщений (threshold можно оптимизировать, но он был зафиксирован случайно). На коллекции документов кластера можно натравить, например, тематическое моделирование, получить наиболее релевантную тему для данного кластера и выделить его в отдельную группу. Отвалидировать этот алгоритм времени не хватило.
Итак, входящие письма пропускаются через классификатор, если попадают в категорию «Другое» — кластеризуются, пытаются выделиться новые темы, а далее происходит ранжирование. Отправляется запрос на бэкенд, который всё агрегирует, и рендерится фронтенд.
Оставшиеся идеи
- улучшение моделей машинного обучения;
- сбор данных с большего числа пользователей для более качественного предсказания поведения каждого из них;
- тщательная валидация новых появляющихся категорий;
- использование картинок как признаков, например, выделение эмбеддингов с предобученных нейросетей.