- PVSM.RU - https://www.pvsm.ru -
Привет. Недавно прошло [1] соревнование от Тинькофф и McKinsey. Конкурс проходил в два этапа: первый — отборочный, в kaggle формате, т.е. отсылаешь предсказания — получаешь оценку качества предсказания; побеждает тот, у кого лучше оценка. Второй — онсайт хакатон в Москве, на который проходит топ 20 команд первого этапа. В этой статье я расскажу об отборочном этапе, где мне удалось занять первое место и выиграть макбук. Команда на лидерборде называлась "дети Лёши".
Соревнование проходило с 19 сентября до 12 октября. Я начал решать ровно за неделю до конца и решал почти фулл-тайм.
Летом в банковском приложении Тинькофф появились stories (как в Instagram). На story можно отреагировать лайком, дизлайком, скипнуть или просмотреть до конца. Задача предсказать реакцию пользователя на story.
Соревнование по большей части табличное, но в самих историях есть текст и картинки.
Прогноз реакции может принимать значение от -1 до 1 включительно — чем оно ближе к 1, тем выше вероятность получить лайк. А при значении -1 лучше убрать эту историю долой с глаз пользователя.
Для проверки точности решений используется формула, нормированная на максимально возможный результат:
Далее я подробно расскажу о каждом кусочке данных, как я его обрабатывал и какие признаки (далее фичи) извлекал.
что есть изначально:
как фичи используем всё вышеперечисленное кроме job_title, т.к. предполагаем, что job_position_cd нормально описывает должность человека.
что есть изначально:
MCC — Merchant category code. Это стандартизованный код услуги, которую предоставляет получатель. Эта информация открыта, вот расшифровка [13]. Эти коды можно удобно разбить на категории, например: entertainment, hotels и т.п.
Для каждого customer_id сопоставим следующие фичи:
Всего историй у нас 959.
что есть изначально:
выглядит json подобным образом:
Это такое дерево элементов, где каждый элемент описывается ключами: ['guid', 'type', 'description', 'properties', 'content']. В 'content' лежит список дочерних элементов. История состоит из страниц. На страницу накиданы фон, текст, картинки. Конструктора историй у нас не было, а самому отрисовать всё это достаточно сложно и не факт, что значительно поможет в дальнейшем.
Регулярками вытащим весь текст и соответствующий размер шрифта. Извлечём следующие фичи:
def get_text_amount(all_text, font_sizes):
assert len(all_text) == len(font_sizes)
lengths = np.array(list(map(len, all_text)))
sizes = (np.array(font_sizes) / 100)**2
return (lengths * sizes).sum()
что есть изначально:
Обработаем время и как фичи добавим:
Далее ещё добавится группа фичей исходя из данных по реакциям, а пока что идём в бой с этим арсеналом фичей делать бейзлайн.
Лучший подход, который использовал весь топ, следующий: cведём задачу к многоклассовой классификации, т.е. предсказываем вероятность каждой реакции. Считаем матожидание оценки для данной истории :
Бинаризуем :
— наш ответ для объекта , который может принимать значение
С самого начала и до конца я использовал CatBoost. Обусловлено это тем, что CatBoost из коробки строит полезные статистики для категориальных фичей. А статистика по пользователю — то, насколько он склонен к каким реакциям, и статистика по истории — то, как чаще всего не неё реагируют, являются самыми сильными фичами в этой задаче.
Как CatBoost работает с категориальными фичами хорошо объяснено в документации [15].
TLDR:
берём значение признака, например, один из customer_id, считаем процент случаев, когда этот customer отреагировал лайком, дизлайком, скипнул или просмотрел. Получим 4 числа. Заменяем customer_id на эти 4 числа и используем их как признаки. Делаем так для каждого customer_id.
С текущими фичами, с неоптимизированным катбустом, на публичном лидерборде я занимал на тот момент 11 место с результатом 0.31209
В какой-то момент появилась гипотеза, что приложение может показывать истории чаще или реже в зависимости от того, как пользователь отреагировал на неё ранее. Давайте тогда добавим фичи, которые будут говорить:
Конечно же, эти фичи использовать в продакшне нельзя, т.к. их банально не будет на момент применения модели, но в соревновании любые средства хороши.
Итак, сказано — сделано. Получили 0.35657 на лидерборде.
Перебирал параметры я с помощью байесовской оптимизации [16]
Из интересного можно упомянуть параметр max_ctr_complexity, который отвечает за максимальное количество категориальных фичей, которые могут быть скомбинированы. Пример под спойлером.
Assume that the objects in the training set belong to two categorical features: the musical genre (“rock”, “indie”) and the musical style (“dance”, “classical”). These features can occur in different combinations. CatBoost can create a new feature that is a combination of those listed (“dance rock”, “classic rock”, “dance indie”, or “indie classical”).
CatBoost можно обучать на GPU, это заметно ускоряет обучение, но также вводит много ограничений, особенно касательно категориальных фичей. В этой задаче обучение на GPU давало результат сильно хуже, чем на CPU.
Важность фичей по мнению CatBoost. Во многом названия фичей говорят сами за себя, но некоторые, не самые очевидные, из топа, я поясню:
Давайте посмотрим на распределение реакций с течением времени:
Т.е. в какой то момент распределение по реакциям сильно меняется.
Далее хочется получить какое-нибудь подтверждение, что на тесте распределение такое же, как и в конце тренировочной выборки. Зашлём как предсказание все единички, получим результат 0.00237. Предскажем все единички на последней части трейна — получим около 0.009, на первой части — около -0.22. Значит распределение на тесте скорее всего такое же, как в конце трейна и точно не похоже на основную часть. Отсюда рождается гипотеза, что если подправить распределение в наших предсказаниях, то результат на лидерборде сильно улучшится, т.к. распределения на трейне и на тесте отличаются.
На последнем шаге получения итоговых предсказаний добавим трешхолд:
В последней модели у меня было что-то около 66% единичек, если бинаризовать с трешхолдом равным 0. Оказалось, что действительно, уменьшение количества +1 давало сильный прирост качества. Оценивались только последние 3 посылки, поэтому я заслал предсказания лучшей модели с разными трешхолдами так, чтобы процент плюс единичек был примерно 62, 58 и 54.
По итогу на публичном лидерборде мой лучший результат был 0.37970.
Как обычно принято в соревнованиях по машинному обучению, когда отправляешь предсказания в систему, результат оценивается только по части всей тестовой выборки. Обычно около 30%. Результаты для этой части отражаются на публичном лидерборде. По оставшейся части теста оценивается итоговый результат, который отображается после окончания соревнования на приватном лидерборде.
В конце соревнования на публичном лидерборде положение было таким:
На приватном лидерборде, по которому считались итоговые результаты, мне повезло и ребята по какой-то причине с первого места упали на 4ое. Вот итоговое положение.
В общем случае если время есть, но его не учитывать при построении Mean Target Encoding'а, то модель будет использовать информацию о правильных ответах из будущего и может переобучиться под это. В данной задаче, видимо, это не имело большого эффекта и пробежаться несколько раз по разным перестановкам было важнее.
Соревнование получилось интересным, поскольку объединяло много компонентов, таких как табличные данные, тексты и картинки. Было много пространства для исследований, много с чем ещё можно было бы экспериментировать. В общем, скучать не пришлось.
Спасибо организаторам конкурса!
Весь код выложен на гитхабе [18].
Автор: Евгений
Источник [19]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/338132
Ссылки в тексте:
[1] прошло: https://vc.ru/data-like
[2] Метрика: #metrics
[3] Данные и Feature engineering: #fe
[4] Какую задачу мы решаем и как формировать предсказание?: #prediction
[5] Модель: #model
[6] Киллер-фичи: #killer
[7] Оптимизация модели: #optimization
[8] Интересные наблюдения: #interesting
[9] Трешхолд предсказаний: #threshold
[10] Результаты соревнования: #results
[11] Что не сработало: #nonworking
[12] Выводы: #conclusion
[13] расшифровка: https://www.web-payment-software.com/online-merchant-accounts/mcc-codes/
[14] dostoevsky: https://pypi.org/project/dostoevsky/
[15] документации: https://catboost.ai/docs/concepts/algorithm-main-stages_cat-to-numberic.html
[16] байесовской оптимизации: https://github.com/fmfn/BayesianOptimization
[17] Image: https://habrastorage.org/webt/d6/yo/pu/d6yopub7m8jlc9fs3gpoaruqcgu.png
[18] гитхабе: https://github.com/EugeneZabrotsky/data_like
[19] Источник: https://habr.com/ru/post/475182/?utm_source=habrahabr&utm_medium=rss&utm_campaign=475182
Нажмите здесь для печати.