Приветствую всех читателей. Меня зовут Захаров Андрей. На момент написания статьи я занимаю должность старшего специалиста по анализу данных в компании СберМаркет.

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

На личном опыте я убедился, что не всегда для победы требуются сложные алгоритмы, мощное железо или большой опыт в индустрии. Иногда 5-6 строчек кода и немного смекалки достаточно, чтобы получить топ-1 решение. Я расскажу вам про не очевидные, на первый взгляд, но довольно простые решения, а также раскрою некоторые интересные моменты хакатонской кухни, которые, надеюсь, вдохновят вас на участие.

Статья будет полезна будущим участникам соревнований и data science специалистам, которые смогут применить описанные решения и практические советы в реальных задачах.

Что такое Data Science хакатон и ML соревнование

Data Science хакатон (hack + marathon) - это соревнование, в котором командам нужно за короткое время (24 - 48 часов) и, чаще, с ограниченным набором данных решить поставленную организаторами задачу. В статье будут примеры, которые можно разбить на следующие разновидности:

  1. Хакатон "с лидербордом" или короткое ML соревнование. В данном случае организаторы предоставляют набор данных для обучения модели и однородный тестовый набор, на котором нужно предоставить искомые ответы. Выигрывает та команда, которая даст лучшее качество модели и более точные предсказания на тестовом наборе данных по указанной организаторами метрике.

  2. Хакатон с презентацией решения. Если коротко, что нужно сделать: найти проблему в бизнесе компании, которая организует хакатон, решить её с помощью предоставленных данных и презентовать получившееся решение. На презентации нужно рассказать какую задачу решает команда, если изначально задача четко не поставлена, и показать как разработанная модель это делает. Иллюстрацией работы модели могут быть слайды с примерами работы или MVP (приложение, чат бот), который может быть запущен в real time и продемонстрирует решение в виде прототипа. Победа присуждается команде, которая нашла наиболее интересное и прибыльное решение для компании по мнению организаторов.

  3. Гибрид. Твоё место в рейтинге (или твой скор на ЛБ) и презентация решения могут иметь свой вес в финальном определении победителя и призеров.

Зачем участвовать в хакатонах

  1. Сильно прокачивается скилл нахождения решения или пути к решению в короткие сроки.

  2. Опыт решения Data Science задач на реальных данных: некая аппроксимация того, чем вы будете заниматься, работая в DS и ML областях.

  3. Новые знакомства: на 1-2 дня вас будут окружать единомышленники из области вашей профессии.

  4. Шанс получить приглашение на работу или стажировку от компании-организатора хакатона, чаще, минуя первые этапы собеседования.

  5. Послушать презентации победителей и узнать их решения.

  6. Часто немалые денежные призы.

  7. Ну как же без бесплатной еды на протяжении всего времени проведения хакатона. Даже после ухода в онлайн формат организаторы присылают промокоды на доставку еды.

  8. Строчка в резюме.

Наша команда

Выступаем на хакатонах командой под названием - Канапе. Некоторые бывалые "хакатонщики" вспомнят нас. Ребят, привет! Начинали нашу "хакатонскую" деятельность группой однокурсников с ФКН ВШЭ. Помимо меня, в состав команды входили:

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

  • Степан Зимин - основной генератор идей для решения бизнес задач и главный спикер команды.

  • Тамара Шангина - мастер презентаций, публичных выступлений и вычленения основных проблем бизнеса на основе данных.

Спустя некоторое время, к команде присоединились:

  • Виктория Захарова - ФКН ВШЭ, ШАД, DS и ML эĸсперт, специалист по NLP и обработке звука.

  • Максим Шевченко – универсальный боец: дизайн ĸрасочных презентаций; публичные выступления; построение ĸрутого MVP, ĸоторый производит впечатление на организаторов; ну и ĸонечно, DS и ML эĸсперт. С Максимом мы познакомились во время работы в МТС.

Кажется, статья обещает быть интересной. Начнем...


1. MVideo Data Science Haсkathon "MSmart"

  • Дата: октябрь 2017 года

  • Итоговое место: 1-ое

  • Приз: 150 000 рублей

  • Формат: Хакатон с презентацией решения

  • Продолжительность: 24 часа

Во время хакатона на выбор предлагалось решить одну из двух задач:

  1. Реализовать агрегатор отзывов о товаре.

  2. Реализовать чат-бот помощника для операторов и консультантов магазина.

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

Коротко о задаче: По каждому товару MVideo дано N отзывов о нём. Нужно извлечь самую полезную (положительную/негативную) информацию о товаре и сократить её до абзаца, чтобы пользователи сайта MVideo, не листая все отзывы, могли прочитать короткую агрегацию отзывов от других пользователей и принять решение о покупке товара.

Данные: Организаторы хакатона предоставили данные отзывов о товарах MVideo:

  • Уникальное название товара

  • Категория товара (кастрюли, смартфоны, стиральные машинки: ...)

  • Id зарегистрированного на сайте клиента MVideo, который оставил отзыв

  • Сам отзыв

  • Оценка, которую клиент поставил данному товару.

  • Дата публикации отзыва

Алгоритм: Из-за временных ограничений нужно было выбрать подход, который можно реализовать, во-первых, быстро (за 24 часа), а, во-вторых, который решал бы поставленную задачу как можно лучше. Выбор пал на следующий подход (для бОльшей наглядности, рассмотрим на примере стиральных машинок):

  1. Если клиенту что-то понравилось или не понравилось в товаре, то это должно быть что-то конкретное (какая-то составляющая продукта). На примере стиральных машин, это может быть шланг, барабан, кнопки, шум и т.д. В итоге, нашей первой задачей было понять, как вычленить основные атрибуты рассматриваемой категории товара. Как мы это сделали:

    1. Собираем N документов, где каждый документ - объединенный текст отзывов по конкретной категории. Например, по стиральным машинкам у нас было примерно 3300 отзывов. Соединим их в один текст и сделаем предобработку (лемматизация, удаление стоп-слов). Так сделаем для каждой категории. В итоге у нас N "вычещенных" текстов (документов), где каждый документ относится к определенной категории.

    2. Далее смотрим на меру tf-idf для каждого слова в каждом документе. Большой вес в tf-idf получат слова с высокой частотой в пределах конкретного документа и с низкой частотой употреблений в других документах. То, что нужно!

    3. В итоге, для категории стиральные машинки наибольший показатель меры tf-idf имели слова: шланг, барабан, машинка, отжим, люк, оборот, ... Идеально!

  2. После того, как мы вытащили основные атрибуты рассматриваемой категории товара, нужно понять, для конкретного товара в данной категории положительной или отрицательной окраской обладает конкретный атрибут в конкретной модели товара. Как мы это делали:

    1. Фиксируем один товар и выбираем только те отзывы, которые имею оценку 1 из 5 или 2 из 5 (отрицательная оценка), либо имеют оценку строго 5 из 5 (положительная оценка).

    2. Для каждого атрибута товара смотрим процент положительных и отрицательных отзывов, где встречается данное слово (например, слово шланг встречается в 75% отрицательных и в 5% положительных отзывов, в 20% отзывов слово шланг не встречается). В отрицательные качества данного товара берем слова, у которых процент участия в отрицательных отзывах выше какого-то порога (порог для каждой категории рассматривается отдельно). Аналогично для положительных качеств.

    3. В итоге, имеем список положительных атрибутов (например: стирка, оборот, управление, режим, ...) и отрицательных атрибутов (например: шланг, вид, кнопка, дверь, ...)

  3. На последнем шаге мы хотим понять, если тот или иной атрибут был отнесён к отрицательным сторонам рассматриваемого товара, то по какой причине. Пример: если атрибут шланг за предыдущие 2 пункта был выбран как отрицательная сторона, то почему? Может быть он часто ломается или он короткий или материал некачественный. Чтобы это выяснить мы реализовали следующий подход:

    1. Просматривая отзывы о товарах, мы заметили, что положительный или отрицательный отзыв о каком-либо атрибуте часто встречается в фиксированном наборе паттернов частей речи. Например, под паттерн "существительное + наречие + прилагательное" подходит "Барабан очень вместительный", под паттерн "Набор прилагательных + существительное + наречие + прилагательное" подходит "сервисные звуковые сигналы очень звонкие" и так далее.

    2. Для конкретного атрибута (например, барабан), конкретного окраса (например, положительного) ищем в положительных отзывах (5 из 5) упоминания слова барабан и его контекст. Смотрим под какой паттерн частей речи подходит контекст и вычленяем из предложения.

    3. Готово! Пример запуска алгоритма для одной из моделей стиральных машинок приведён ниже:

После небольшой обработки выхода нашего алгоритма получаем красивый результат нашей работы:

Недостатки подхода
  • Во время хакатона успели запустить и протестировать алгоритм только на стиральных машинах. Алгоритм может хуже себя вести на других категориях товаров. Было мало времени.

  • Подход отбора атрибутов с помощью tf-idf не может отобрать атрибуты, которые есть у большинства (если не у всех) товаров. Очевидно, сюда входят такие атрибуты, как "цена", "материал". Приведённые атрибуты пользователи сайта MVideo не увидят, если строить агрегацию отзывов по нашему алгоритму.

  • Одна из причин выбора категории "стиральные машинки" была достаточно большая выборка отзывов. Из-за чего мы можем не брать во внимание нейтральные отзывы (3 из 5, 4 из 5). Но другие категории могут иметь мало отзывов и на них алгоритм может работать хуже.

  • Вышепредставленный пример был реализован за 24 часа, поэтому вы можете заметить косяки. Например:

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

    • Отзыв "машинка: в общем машинка очень удачная" имеет под собой только окрас, но не указывает на конкретные положительные/отрицательные качества.

    • У отзыв "дверь: стиралок-при закрытой двери еле слышно" понятна суть, что стиральная машинка тихая, но данный фрагмент довольно "корявый".

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

Практические советы

  • Разделение зон ответственности внутри команды:

    Первые часа 3-4 в команде происходил процесс наброса идей и мозговой штурм, какой подход к задаче лучше всего подойдет. Далее, каждый из нас приступил к реализации своей части. Ближе к ночи было принято решение, что я с Колей должны доделать выбранный подход к утру, а Тамара и Степа утром начали готовить презентацию, пока мы пошли отдыхать на пуфики до начала презентаций решений.

  • Достаточность простого решения:

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


2. Mckinsey and Gett Big Data Haсkathon

  • Дата: декабрь 2017 года

  • Итоговое место: 3-е

  • Приз: 200 000 рублей

  • Формат: Гибрид

  • Продолжительность: 7 дней онлайн этапа + 36 часов очного этапа

Соревнование проходило в 2 этапа:

  1. Онлайн этап в формате короткого ML соревнования с лидербордом.

  2. Очный этап в формате хакатона с презентацией решения среди топ 20 команд на основании лидерборда предыдущего этапа.

Онлайн этап

Задача:. По данным поездки определить, возьмет ли данный водитель данный заказ или отменит его.

Данные о поездках
  • Точка отправления

  • Точка назначения

  • Точная дата создания заказа

  • Признаки водителя (пол, возраст, класс такси, ...)

Метрика: ROC AUC.

Для выхода в финальную часть достаточно быть в топ 20. Сгенерировали дополнительные признаки, настроили валидацию и заняли 4 место, потратив 2 дня.

Сгенерированные признаки
  • Расстояние точки отправления/назначения до аэропортов

  • Расстояние точки отправления/назначения до центра города

  • Временные признаки:

    • день недели

    • время суток

    • празник/нет

  • Обработанные признаки водителя (анкетные данные, текущая оценка, эконом/комфорт/бизнес, ...)

  • Другие признаки

В итоге, мы прошли в очный финал.

Очный этап

Напомню, очный этап проходил в формате "найди проблему, реши её с помощью данных".

Предоставленные данные от компании Gett
  • Данные о заказах со стороны клиента:

    • Точка старта заказа (координаты)

    • Точка назначения заказа (координаты)

    • Признаки заказа (эконом/комфорт/бизнес, цена заказа, время заказа, ...)

  • Данные о заказах со стороны водителя:

    • Местоположение водителя в момент заказа (координаты)

    • Признаки водителя (эконом/комфорт/бизнес, текущая оценка, анкетные данные водителя, ...)

  • Отзывы клиентов о водителях

Нашей командой было принято решение реализовать 2 алгоритма:

  • Алгоритм-рекомендация свободным водителям (без заказа) переместиться в точку повышенного спроса в городе в ближайшем будущем, чтобы повысить вероятность получения заказа водителем, к тому же с повышенным спросом (повышенной ценой). У нас есть история заказов клиентов, с помощью которой мы можем восстановить распределение (карту плотности, двухмерная матрица) заказов в городе в каждый час в зависимости от дня недели и признака выходного/не выходного (или праздника) дня. Также, у нас есть текущее распределение водителей в городе (карта плотности, двухмерная матрица).

    Задача: Переместить свободных водителей по карте плотности таким образом, чтобы норма матрицы (мы брали норму Фробениуса или евклидову) разница распределений клиентов и водителей уменьшилась. При этом мы штрафуем наш функционал за перемещение водителей (например, трата на бензин). В итоге наша задача оптимизации выглядит следующим образом:

F = ||C^{h,w}-D||_F+C\sum_i{d_i} \rightarrow \min_{D},\ \ где  \\ C^{h,w}-распределение \ клиентов \ в \ зависимости \ от \ часа \ и \ дня \ недели \\ D-распределение \ водителей \\ C-настраиваемая \ константа \ штрафа \ за \ перемещение  \\ d_i - расстояние \ перемещения \ i-ого \ свободного \ водителя

Задачу оптимизации решали жадным алгоритмом. Каждого отдельного свободного водителя поочередно перемещаем в сторону локального пика распределения клиентов и смотрим, уменьшается ли функционал. Решение логичное, простое, но не оптимальное по времени и с точки зрения нахождения лучшей матрицы D. Но выбор пал на данный подход в связи с ограничением по времени хакатона и "достаточности" работающего прототипа. Ниже приведена иллюстрация работа алгоритма на примере Москвы (более показательная иллюстрация показана в презентации, смотри ссылку ниже):

  • Алгоритм агрегации отзывов о водителях и интересах пассажиров. В данной задаче мы применили примерно тот же самый подход (с небольшими доработками) как и на предыдущем хакатоне по агрегации отзывов о товарах MVideo, только теперь вместо товаров у нас водители и клиенты.

Практические советы

  • Состав команды:

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

  • Разделение зон ответственности внутри команды:

    В первый день хакатона, так как у нас уже был готовый алгоритм агрегации отзывов, часть планируемых задач мы закрыли. Ближе к 9 вечера начали реализовывать алгоритм распределения водителей. Командой было принято решение, что я потрачу ночь на реализацию подхода и подготовки графиков и анимаций для презентации, а сокомандники утром начнут подготовку к выступлению и составлению презентации. В итоге к 15:00, когда нужно отдавать код и презентацию организаторам, у нас было всё готово. К вечеру Тамара и Степа уверенно презентовали наши решения.

  • Достаточность простого решения:

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

  • Используйте уже реализованное решение:

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

Упоминание о хакатоне

Ссылка на презентацию команды Канапе


3. Big Data Hackathon, MentorHack

  • Дата: февраль 2018 года

  • Итоговое место:

    • 1-е в номинации "Создание виртуального наставника"

    • 3-е в номинации "Лучшие решения хакатона"

  • Приз:

    • 150 000 рублей

    • 100 000 рублей

  • Формат: Хакатон с презентацией решения

  • Продолжительность: 48 часов

Номинация "Создание виртуального наставника"

Нашей задачей было собрать команду под конкретный проект в виде набора вакансий.

Мы собирали команду по двум основным критериям: покрытие всех необходимых скиллов и совместимость по интересам. 

Была сделана кастомная реализацию resume2vec для «умного» поиска подходящих под вакансию людей. Весь алгоритм работает примерно так: 

  1. Парсим навыки из датасета с резюме, который нам предоставили организаторы.

  2. Создаём граф, где вершины это резюме, а вес ребра - это схожесть между резюме с точки зрения набора навыков.

  3. Используя технологию node2vec, описанную в этой статье, получаем вектора для каждого резюме.

  4. Разделяем ноды (резюме) на кластера.

  5. Смотрим глазами на примеры резюме из каждого кластера и каждому кластеру даем название (например, frond-end, back-end, data science, ...)

  6. Набираем команду, исходя из описания проекта и требований к команде, из различных кластеров. По 2-4 человека из кластера.

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

                     Пример работы чат-бота
    Пример работы чат-бота
  8. Наконец, составляем команду исходя из общих интересов.

Номинация "Лучшие решения хакатона"

Организаторы хакатона дополнительно среди всех команд, среди всех задач (номинаций) мероприятия составили общий рейтинг исходя из оценок жюри и экспертов GoTo, и топ-5 команд получили по 100 000 рублей.

Итоговый рейтинг Жюри и GoTo проектов по всем номинациям
Итоговый рейтинг Жюри и GoTo проектов по всем номинациям

Практические советы

  • Состав команды

    Как было описано в предыдущих хакатонах, в команде должны быть люди, которые смогут хорошо презентовать реализованный алгоритм.

  • Разделение зон ответственности внутри команды

    Командный брейншторм для выбора подхода к реализации первые 2-3 часа. Распределение ресурса команды на отдельные зоны ответственности (презентация, выступление, алгоритма, чатбот, реализация подхода из статьи).

Статья на habr

Упоминание у ipe-lab 1

Упоминание у ipe-lab 2

Ссылка на презентацию команды Канапе


4. "СберТех" Data Science Hackathon "Cradle Rave"

  • Дата: февраль 2018 года

  • Итоговое место: 1-е

  • Приз: 250 000 рублей

  • Формат: Хакатон с презентацией решения

  • Продолжительность: 36 часов

Наша команда на хакатоне соревновалась в номинации "Большие данные" (во второй номинации "Мобильная разработка" соревновались front-end разработчики), в которой решались две задачи:

  1. Реализация чат-бота по аналитике класса офиса.

  2. Реализация алгоритма оптимизации пассажирских перевозок.

Степа с Колей работали над первой задачей, а я с Викой взяли алгоритм оптимизации перевозок.

Чат-бота по аналитике класса офиса

Частью реализации данной задачи являлась разработка ML алгоритма многоклассовой классификации по предсказанию класса офиса или офисного помещения (офис C, B, A, A+ класса). Здесь основной упор ставился на генерацию признаков.

Признаки офиса, которые больше всего дали прирост в качестве
  • Название агенства (застройщика)

  • Площадь офиса

  • Расстояние до ближайшего метро

  • Расстояние до центра города

  • Материал постройки

  • Тип недвижимости (отдельное здание, офисное помещение)

В итоге получили лучшее качество на валидации (0.88 F1 Macro).

Кроме выдачи в чат-боте по адресу показателя класса офиса, наша команда добавила в дополнительную аналитику:

  1. При отсутствии офиса по введенному адресу приводится информация о ближайшем к данному адресу офис, а так же варианты офисов в радиусе километра от введенного адреса.

  2. Поиск офиса по геолокации. Отмечаем точку на карте, затем проводится аналитика.

  3. Прикладывается фото офиса для наглядности.

  4. Возможность голосового управления.

Алгоритма оптимизации пассажирских перевозок

Задача: Есть изначальная позиция всех водителей. Имеем список поступлений новых заказов от клиентов:

  • Время оформления заказа

  • Сколько человек поедет

  • Координата старта заказа

  • Координата точки назначения

Нужно реализовать "передвижение водителей" для развоза заказов.

Наша реализация: Алгоритм меняет состояние водителей поминутно. Небольшой кусок алгоритма взят из прошлого хакатона (Mckinsey and Gett Big Data Haсkathon) по распределению свободных водителей:

  1. Если водитель свободен (без заказа), алгоритм говорит ему ехать в точку будущего пика спроса (с прошлого хакатона).

  2. При появлении заказа к нему едет ближайший свободный водитель и везет в точку назначения.

  3. Каждый водитель, который уже везет клиентов, может по пути забрать новые заказы. Работают следующие правила:

    1. Смотрим, что выгоднее: взять заказ по пути или отдать другому водителю.

    2. Если алгоритм принимает решение подобрать заказ, то перестраивается маршрут (возможно, сначала быстрее отвезти данного клиента нежели того, который уже в машине)

    3. Работает ограничение на максимум 4 клиента в одной машине.

Логи работы алгоритма
Логи работы алгоритма

По итогу, наша реализация чат-бота и алгоритма оптимизации перевозок показалась организаторам хакатона лучшей среди команд.

Практические советы

  • Используйте уже реализованное решение:

    Использовалась часть наработок алгоритма распределения водителей из предыдущего хакатана, что сэкономило время.

Упоминание на habr

Ссылка на презентацию команды Канапе


5. Tele2 Data Science Hackathon "Tele2Hack"

  • Дата: август 2018 года

  • Итоговое место: 2-е

  • Приз: 75 000 рублей

  • Формат: Хакатон с презентацией решения

  • Продолжительность: 36 часов

Задача хакатона: Создать модель (прототип), который бы помог оптимизировать работу операторов поддержки.

Данные: Табличка с признаками диалога (Дата+время, статус (диалог закончен/нет), город, ...) и, собственно, сами диалоги операторов с клиентами в формате текста.

Наша команда приняла решение реализовать 2 алгоритма:

  1. Алгоритм определения темы обращения.

  2. Алгоритм определения тональности обращения.

Алгоритм определения темы обращения

Важность алгоритма для бизнеса:

  • Есть срочные темы (не работает связь, зона покрытия), есть не срочные (благодарность, жалоба). В зависимости от темы строится приоритизация обращений (очередность обработки)

  • Некоторые темы (например, благодарность) не требуют ответа и внимания операторов, что экономит время.

  • Есть операторы - специалисты в какой-то теме (например, в SMS блоке, телефонии, интернет). В зависимости от темы, обращения можно перенаправлять на того или иного оператора.

Реализация: Использовали библиотеку BigARTM для тематического моделирования, чтобы определить тему. Так как кол-во и размер диалогов был значительным, использовали Vowpal Wabbit формат для ускорения обучения.

На выходе для каждого документа получали вероятность принадлежности к той или иной теме:

Как вариант, можно определить тему как максимальную по вероятности.

Пример обращений на тему "Интернет"
Пример обращений на тему "Интернет"

Алгоритм определения тональности обращения

В данных по обращениям для некоторых примеров была дана информация по тональности (негативное/нейтральное/позитивное обращение).

Важность алгоритма для бизнеса:

  • Срочность ответа. Если поступившее обращение очень негативное, приоритет повышается.

Реализация: Имея известные лэйблы, получим тональность обращений с неизвестными ответами, используя Label propagation algorithm в графе, где нодами являются обращения, а вес граней - похожесть обращений с точки зрения расстояния между векторными представлениями текстов. Для получения эмбеддингов использовали Tf-Idf векторизацию документа. В итоге, на выходе получаем значение от -1 (негативный) до 1 (позитивный).

Пример определения тональности
Пример определения тональности

Практические советы

  • Состав команды

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

Упоминание о хакатоне


6. Data Science Hackathon "GeoRetail"

  • Дата: апрель 2019 года

  • Итоговое место: 1-е в номинации "Лучшее решение от X5 Retail Group"

  • Приз: 30 000 рублей

  • Формат: Хакатон с презентацией решения

  • Продолжительность: 24 часа

Хакатон проходил в формате "найди проблему, реши её с помощью данных". Компания X5 предоставила следующие данные:

Данные от X5
  • Информация о действующих точках X5:

    • Наименование точки (Перекресток, Пятерочка, ...)

    • Город

    • Координаты точки

    • Тип (супермаркет, гипермаркет)

    • Торговая площадь

    • Дата открытия

    • Какой value принес магазин в первый год (некий анонимизированный показатель спроса)

    • Какой value принес магазин во второй год

  • Данные о конкурентах:

    • Город

    • Координаты точки

    • Тип (супермаркет, гипермаркет)

    • Торговая площадь

Командой было принято решение решать задачу нахождения наилучшего места открытия новой точки X5 и её размера (гипер/супермаркет).

Как решали данную задачу:

  1. Строилась карта плотности предложения: С помощью KernelDensity строилось распределение покрытия города магазинами X5 и конкурентов.

  2. Строилась карта плотности спроса: В качестве спроса бралась плотность населения районов города (так как у нас таких данных не было, мы опустили данный момент и считали, что плотность равномерна внутри границ города) с поправкой на эффективность уже имеющихся точек X5 (сколько value приносила точка за первый год), и считали, что плотность населения в районах точек с высокой эффективностью выше.

  3. В итоге, наш подход заключался в заполнении максимальной разницы между плотность спроса и плотностью предложения. Искали пики разницы и пытаемся "открыть" в данной точке магазин. После открытия новой точки забираем X предложения, если есть еще капаситет по спросу, иначе, забираем оставшийся спрос потребителей на новый магазин, а остаток делим с конкурентами:

    \begin{equation*} Value =   \begin{cases}   X & X+C<P  \\ (P-C)*X + \frac{C}{2} & X+C\geq P  \end{cases} \end{equation*}, \\ X - карта \ распространения \ Value \ от \ нового \ магазина \\ C - предложение \ (value) \ конкурентов \ и \ уже \ открытых \ магазинов \\ P - спрос \ покупателей
  4. В добавок, мы разделяем новые точки по размеру (Big, Medium, Small). Чем больше магазин, тем больше Value он принесет (бОльший охват спроса), но и затраты больше.

  5. Считаем, что магазин не имеет смысла открывать, если суммарный объём Value на карте города меньше затрат (Cost) на открытие. "Открываем" новые точки итерациями, пока расходы на открытие не превысят Value от новой точки:

Ниже представлен результат работы алгоритма на примере города Краснодар. На картинке видим плотность заполнения уже открытых магазинов X5 и конкурентов. Красными точками указаны места открытия новых точек X5.

Пример работы алгоритма
Пример работы алгоритма

Практические советы

  • Выбор решаемой задачи и презентация подхода:

    Остальные команды, как под копирку, решали следующую задачу: По информации о точке и тому, какое value магазин принёс в первый год, предсказать, какой value принесет точка на второй. На стадии демонстрации решений команды показывали jupyter ноутбуки со своими алгоритмами (достаточно интересными подходами) и по сути соревновались в метрике качества предсказаний. Выбор, может быть и не сложной, но интересной задачи и красочная презентация сделали своё дело.

  • Разделение зон ответственности внутри команды

    Часть команды готовила презентацию и выступление команды, другая часть работала над реализацией подхода.

  • Достаточность простого решения

    Решение выглядит логичным и не сложным в реализации, чего достаточно для прототипа, который можно показать на выступлении команды.

Ссылка на презентацию команды Канапе

Упоминание о хакатоне


7. Tele2 Data Science Hackathon "Tele2Hack 2.0"

  • Дата: июнь 2019 года

  • Итоговое место: 1-е

  • Приз: 100 000 рублей

  • Формат: Хакатон "с лидербордом"

  • Продолжительность: 36 часов

Берем реванш с прошлогоднего хакатона от Tele2.

Задача: По предоставленным полигонам (набор координат, которые образуют многоугольник-границы полигона на карте) нужно определить потребление трафика интернета в гигабайтах внутри данной территории.

Метрика: Качество ML алгоритма мерилось с помощью RMSLE.

Наше решение: БОльший акцент делали на извлечение признаков из открытых данных (организаторами не запрещалось). Основные признаки, которые нам получилось извлечь из открытых источников:

Признаки из открытых источников
  1. Информация о банкоматах. Чем больше банкоматов внутри полигона, тем больше живет людей в данной локации, тем больше трафик.

  2. Наличие и количество коммерческих зданий. Логика аналогичная.

  3. Информация ГИБДД об авариях за последний год в данной локации.

  4. Наличие в полигоне:

    1. Школ

    2. Поликлиник

    3. Магазинов электроники

    4. Магазинов одежды и т.д.

Так же нам получилось извлечь картинки с координатами центра полигонов с google карт с помощью Google Maps API:

С помощью VGG16 извлекли признаки из изображения, которые в итоге сильно улучшили качество на лидерборде.

В итоге архитектура нашего решения выглядит следующим образом:

Особенности хакатона: Отсутствие скрытой private выборки, качество по которой узнают только после завершения соревнования, по которой в итоге и строится финальный рейтинг. То есть, по тому показанию RMSLE, которое мы видим на лидерборде и будет строиться финальный рейтинг.

Зная информацию, что присутствует только public выборка, можно узнать точный ответ на один из объектов. Сейчас покажу, как это сделать на примере метрики MSE (Для метрики RMSLE вычисления аналогичные, только немного сложнее, MSE для наглядности):

MSE=\frac{1}{n}\sum_{i=1}^n(x_i-\hat{x}_i)^2

Если занулить все предсказания и заслать данное решение, получим:

MSE_0=\frac{1}{n}\sum_{i=1}^n(x_i-0)^2 =\frac{1}{n}\sum_{i=1}^nx_i^2

Теперь отправим все нули, кроме последнего. Последнее предсказание пусть равно a:

MSE_{\hat{x}_n=a}=\frac{1}{n}\Big[\sum_{i=1}^{n-1}x_i^2 + (x_n -a)^2\Big]=\frac{1}{n}\Big[\sum_{i=1}^{n}x_i^2-x_n^2 + (x_n -a)^2\Big]==\frac{1}{n}\Big[nMSE_0-x_n^2 + (x_n -a)^2\Big]

Из данного уравнения извлечём последний ответ:

x_n=\frac{MSE_0-MSE_{\hat{x}_n=a}+a^2}{2a}

Таким образом можно узнать любой ответ. Но! У нас стояло ограничение в 1 сабмит в час. В итоге, у нас 20 часов (остальное время хакатона уходило на сбор, подготовку презентаций, выступления участников и т.д.) = 20 сабмитов и 10 000 объектов для предсказания. Поэтому, тратить сабмиты нужно аккуратно!

Команда OilStone, которая в итоге заняла 2-е место, практически все время хакатона находилась на 1-ом с хорошим отрывом. После того, как мы вставили в нашу модель признаки из нейросети и некоторые дополнительные геоданные, качество алгоритма на локальной валидации сильно улучшилось. Нам показалось, что если мы отправим решение, то выйдем на первое место. Приняли решение проверить это без отправки лучшего решения. Сейчас покажу как. Для этого нам надо узнать один точный ответ по схеме, которая описана выше (мы узнали последний ответ), тогда, качество нашего лучшего решения =

MSE_{best}=\frac{1}{n}\Big[\sum_{i=1}^{n-1}(x_i-y_i)^2+(x_n-x_n)^2\Big]=\frac{1}{n}\sum_{i=1}^{n-1}(x_i-y_i)^2= \ ?

Теперь отправим решение, которое совпадает с лучшим, но последний ответ делаем очень большим, чтобы мы не вышли на первое место и отправим данное решение:

MSE_{10^9}=\frac{1}{n}\Big[\sum_{i=1}^{n-1}(x_i-y_i)^2+(x_n-10^9)^2\Big]=\frac{1}{n}\Big[ nMSE_{best} +(x_n-10^9)^2 \Big]

Таким образом получим показатель MSE, если бы мы его отправили:

MSE_{best}=MSE_{10^9}-\frac{(x_n-10^9)^2}{n}

Ожидания подтвердились, с помощью формулы получили, что если бы мы отправили данный сабмит, то показатель RMSLE был бы 0.61391. В итоге, у нас оставался только один сабмит в последний час. Заслали лучшее решение со скором 0.61391 и увидели данное число на лидерборде. У команды на 2-ом месте оставалось 40 минут, чтобы нас опередить, но не получилось.

Итоговый рейтинг
Итоговый рейтинг

Практические советы

  • Разделение зон ответственности внутри команды

    • Вика занималась генерацией картинок по координатам, извлечением признаков из картинок.

    • Макс занимался генерацией и чисткой признаков из открытых источников и подготовкой презентации.

    • Я с Колей строили архитектуру общей модели на базовых признаках и признаках, которые отдали Вика с Максом. Реализовали подход с определением score без отправки лучшего решения.

    Все справились со своей задачей и мы забрали золото.

Презентация команды Канапе

Упоминание о хакатоне 1

Упоминание о хакатоне 2


8. Mckinsey and Tinkoff Big Data Haсkathon

  • Дата: октябрь 2019 года

  • Итоговое место: 1-е

  • Приз: 500 000 рублей

  • Формат: Гибрид

  • Продолжительность: 7 дней онлайн этапа + 36 часов очного этапа

Соревнование проходило в 2 этапа:

  1. Онлайн этап в формате короткого ML соревнования с лидербордом.

  2. Очный этап в формате хакатона с презентацией решения среди топ 20 команд на основании лидерборда предыдущего этапа.

Онлайн этап

На первом этапе решалась задача многоклассовой классификации. Нужно определить, какое действие сделает пользователь над конкретной story в приложении Tinkoff, иначе, для каждой пары user_id - story_id дать ответ:

  • Пользователь поставит сторис dislike

  • Пользователь пропустит сторис

  • Пользователь полностью посмотрит сторис

  • Пользователь поставит like сторис

Какие организаторы нам дали данные:

Данные от организатора
  1. Train выборка:

    1. user_id - айдишник пользователя

    2. story_id - айдишник истории

    3. target - реакция пользователя на историю

  2. Информация о story_id:

    1. Текст истории

    2. Картинка истории

  3. Информация о клиенте:

    1. Пол

    2. Возраст

    3. Должность (много пропущенных значений)

    4. ...

  4. Транзакции клиентов:

    1. MCC код транзакции

    2. Сумма транзакции

Для выхода в финальную часть достаточно быть в топ 20.

Сгенерировали признаки
  • Tf-Idf векторизация обработанного (лемматизация) текста истории

  • Tf-Idf MCC кодов транзакций клиентов

  • Различные статистики (среднее, дисперсия, min, max) над суммами транзакций

  • Добавили анкетную информацию о клиентах

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

Очный этап

Напомню, очный этап проходил в формате "найди проблему, реши её с помощью данных". Дополнительных данных компания Tinkoff не предоставила.

Нашей командой было принято решение реализовать алгоритм рекомендации истории под пользователя и оценки рейтинга данной story для пользователя (шкала от 0 до 1).

Другими словами, задача - реализовать функцию:

F(story,segment,X)=score

Где для пары story_id - segment определить на сколько пользователя из определенного сегмента заинтересует конкретная история. X - признаки истории (эмбеддинги картинки и текста) и пользователей под конкретный сегмент. Для определения сегмента будем использовать информацию о MCC кодах транзакций пользователя. Сегмент для истории определяется лайками и дислайками пользователей, имеющих в транзакциях определенный сегмент по MCC кодам.

Сбор обучающей выборки происходит так:

  1. Выбираем всех пользователей, которые отреагировали на данную историю

  2. Смотрим, какие MCC коды данных пользователей

  3. Записываем в список для реакций (event) напротив сегмента: 1, если реакция положительная и -1, если отрицательная.

  4. Смотрим сумму вектора реакций. Если больше нуля, то данную историю можно рекомендовать пользователям в данном сегменте, если меньше, то нет.

Визуализация сбора обучающей выборки выглядит примерно так:

Сбор обучающей выборки
Сбор обучающей выборки

На признаках истории и клиентов определенного сегмента обучили Логистическую регрессию.

На валидации получили метрики:

  • Accuracy - 0.79

  • F1 - 0.74

К тому же, реализовали небольшой MVP, который показывает работу алгоритма и продемонстрировали на презентации:

Пример скоринга
Пример скоринга

Практические советы

  • Разговаривайте с менторами о выборе задачи

    Изначально у нас было 3 идеи для реализации. Первую компания Тинькофф уже реализовала, вторая менторам не понравилась. В итоге остановились на рекомендациях сторис.

  • Разделение зон ответственности внутри команды

    • Макс занимался подготовкой MVP и презентацией

    • Я с Колей занимались реализацией подхода

    • Степа работал над презентацией вместе с Максом и готовился к выступлению команды

  • Презентация подхода

    Данной части мы уделили бОльшую часть "человекочасов". Над презентацией (сама презентация + MVP + выступление) решения работали Степа с Максом (половина команды) + после реализации подхода под конец дедлайна мы с Колей начали помогать. Презентовали решение перед партнерами McKinsey & Company, что стало причиной смены фокуса работы в сторону выступления, а не реализации подхода.

Презентация команды Канапе

Упоминание о хакатоне


9. Yandex Realty Big Data Haсkathon "HackTheRealty"

  • Дата: сентябрь 2020 года

  • Итоговое место:

    • 1-е в номинации "Прогноз срока экспозиции объявления"

    • 2-е в номинации "Прогноз стоимости жилой недвижимости"

  • Приз:

    • 150 000 рублей

    • 100 000 рублей

  • Формат: Хакатон "с лидербордом"

  • Продолжительность: 48 часов

Номинация "Прогноз срока экспозиции объявления" (или топ-1 за 6 строчек кода).

Задача: Спрогнозировать, сколько объявление о сдаче жилья провесит на доске объявлений Yandex Realty. Ответ может принимать одно из пяти значений:

  • 1 - объявление провисит до 7 дней

  • 2 - объявление провисит от 7 до 14 дней

  • 3 - объявление провисит от 14 дней до 21 дня

  • 4 - объявление провисит от 21 дня до месяца

  • 5 - объявление провисит больше месяца

В качестве метрики качества, организаторы выбрали MAE, но под знаком суммы абсолютная ошибка меняется на экспоненту в степени абсолютного отклонения реального ответа от прогноза:

eMAE = \frac{1}{n}\sum_{i=1}^ne^{|y_i-\hat{y}_i|}

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

Проблема связана с нестандартной метрикой. Если предсказывать константу 3, то максимальная ошибка на одном примере не будет выше экспоненты в квадрате, что примерно равно 7.4. Но если предсказать 5, а на самом деле окажется 1, то ошибка будет уже 55. Поэтому далеко от константы 3 "отходить" нежелательно.

После того, как прошла половина соревнования, решением с наилучшим показателем метрики на лидерборде являлась константа 3.

Спустя некоторое время мы заметили в данных, что апартаменты сдаются сильно дольше, нежели жилые квартиры (71% апартаментов висят на доске больше месяца):

Еще немного подумав, когда могут возникать отклонения от среднего срока сдачи, догадались, что квартиры могут сдаваться быстрее в преддверии новогодних праздников. В данное время, люди могут сдавать квартиры на короткий срок (на срок праздников).

Тем самым, наше решение выглядело следующим образом:

  1. Для всех ответ 3.

  2. Если данное жильё является апартаментами, то ответ 5.

  3. Если объявление вывешено 25 или 26 декабря, то ответ 2.

Так выглядит код нашего финального решения задачи:

import pandas as pd
test = pd.read_csv('test.csv')
test['target'] = 3
test.loc[test.is_apartment, 'target'] = 5
test.loc[test['day'].isin({'2019-12-25', '2019-12-26'}), 'target'] = 2
test[['id', 'target']].to_csv('submission.csv', index=False)

Данного решения оказалось достаточно для победы.

Номинация "Прогноз стоимости жилой недвижимости"

Задача: Предсказать среднюю стоимость квадратного метра в жилом доме в каждый месяц с ноября 2019 года по март 2020. Разрешалось использовать внешние данные.

Признаки от организаторов:

  • Информация о цене за квадратный метр в прошлые месяцы

  • Геолокация строения (координаты)

  • Признаки недвижимости (этажность, год постройки, класс жилья, наличие балконов, наличие подземной парковки, является ли жилье апартаментами, ...)

Для улучшения качества модели, были добавлены внешние гео-признаки:

  • Признаки "Реформа ЖКХ"

  • Индекс московской и региональной недвижимости

  • Информация о ДТП, ЧП, ремонтах дорог (Безопасные дороги)

  • Население городов

  • Расстояния до банкоматов, центра региона, центра города

  • ...

Провели дополнительный feature engineering, отобрали самые презентативные признаки, обучили модель LightGBM. Так как будущие значения стоимости квадратного метра в доме очень сильно коррелируют с предыдущими, в качестве начальных значений, которые мы "бустили", были взяты последние известные значения среднего квадратного метра. Другими словами, параметру init_score класса lightgbm.Dataset присваиваем последние известные значения стоимости квадратного метра дома.

Практические советы

  • Начни с простого решения

    Перед "изобретением космолёта", сначала реализуйте бэйзлайн. Чаще всего, это оптимальная на рассматриваемой метрике константа (например, медиана при метрике MAE, среднее при MSE, мода при Accuracy и т.д.). Затем попытайтесь побить данное решение. В некоторых случаях (например, данный хакатон) лучшую константу сложно побить. Допустил ошибку, когда начал сразу реализовывать сложное решение с "тяжелой" моделью.

Презентация команды Канапе

Ссылка на хакатон

Видео с презентацией решений


10. Nornickel Data Science Haсkathon (или топ-1 за 5 строчек кода)

  • Дата: апрель 2021 года

  • Итоговое место: 1-е в номинации "Sick leave prediction"

  • Приз: 75 000 рублей

  • Формат: Гибрид

  • Продолжительность: 36 часов

Задача: Для каждого сотрудника (работающего в компании на момент августа 2010 г.) необходимо предсказать его уход на больничный на 12 месяцев: в период с сентября 2019 года по август 2020 года. Другими словами, для каждого работника и каждого месяца предсказать, выйдет ли он на больничный или нет.

Данные о сотрудниках, предоставленные организаторами:

  • История о фактах взятия больничных в период с 2015 по август 2019 года

  • Анкетные данные (пол, возраст, семейное положение, образование, ...)

  • Работа в Норникель (стаж/опыт работы, должность, время в пути до работы, ...)

  • Поведенческие информация (даты отпусков, дата командировок, смены/график работы, информация о переработках, ...)

Метрика качества: F1 мера

Финальная оценка при определении победителей:

  1. Score на лидерборде: Разработать ML алгоритм прогнозирования ухода сотрудника на 12 месяцев с максимальным значением F1. 50% оценки.

  2. Бизнес-ценность: Поиск закономерностей в данных. Какую ценность они могут нести бизнесу (как HR может ими пользоваться и управлять). 30% оценки.

  3. Выступление: Хороший спич и хорошая визуализация полученных результатов (алгоритма, закономерностей и т.д.). 20% оценки.

Score на лидерборде (50% оценки)

Реализовали бэйзлайн: Предсказание взятия больничного для сотрудника на определенный месяц = факту взятия больничного в тот же месяц, только год назад.

Приступили к генерации признаков. Помимо вышеописанных признаков, мы накидали еще несколько идей:

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

  • Руками разделили "вредные", "средние" и "не вредные" должности

  • Анализ причин взятия больничного по сотрудникам в прошлом. Например, флаг того, что заболевание хроническое (гипотеза на увеличения вероятности больничного в будущем), больничный из-за переломов (гипотеза на уменьшение вероятности больничного в будущем).

  • Обобщенный показатель "здоровья" сотрудника (далее share of sick). Доля месяцев, когда сотрудник находился на больничном за всю историю (с 2015 по 2019 год).

Обучили LightGBM, посмотрели feature importance признаков и заметили, что фича share of sick в значительной степени опережала по важности остальные признаки. Посмотрели лучший сплит разделения. Лучшим правилом разделения предсказания на 0 и 1 было: share of sick > 0.15.

Заслали следующее решение:

import pandas as pd
sot = pd.read_csv('sotrudniki.csv')
submit = pd.read_csv('sample_submission_check.csv')
submit = pd.merge(submit.drop('target', axis=1),
                  (sot.groupby('hash_tab_num')['sick'].mean() > 0.15)\
                          .rename('target').astype(int).reset_index(),
                  how='left', on='hash_tab_num')
submit[['hash_tab_num', 'date', 'target']].to_csv('solution.csv', index=False)

Другими словами, если доля месяцев, когда сотрудник брал больничный > 0.15, то на все 12 месяцев ответ 1, иначе на все месяцы ответ 0. Данное решение оказалось лучше на public лидерборде, чем предыдущее, следовательно, остальные признаки ухудшали качество. Данное решение давало 0.2872 F1 на private, чего уже было достаточно для выхода в топ-1 по score на лидерборде (50% оценки).

В итоге, мы ушли от модели и начали придумывать эвристики, которые могли бы поднять скор на лидерборде.

В итоге решение выглядело следующим образом:

  1. Ставим ответ 1 (на все предсказываемые 12 месяцев) для всех работников, у которых доля больничных за весь прошлый период больше 0.15

  2. Ставим ответ 1 (на все предсказываемые 12 месяцев), если у работника был больничный в последний известный месяц (август 2019 года)

  3. Ставим ответ 1 (на все предсказываемые 12 месяцев), если в последние 5 месяцев (с '2019-04-01' по '2019-08-01') работник брал больничный больше 2-х раз

  4. Если не выполняется ни один из пунктов 1-3, то ставим ответ 0 (на все предсказываемые 12 месяцев)

Финальное решение немного улучшило private F1 до 0.2886:

Финальное положение участников
Финальное положение участников

Бизнес-ценность (30% оценки)

На втором этапе (демонстрация бизнес-ценности) мы показали, какие факторы еще могут влиять на вероятность ухода на больничный, на которые руководителям нужно уделить больше внимания.

Факторы с большим влиянием на ответ
Факторы с большим влиянием на ответ

Рассказали организатором про топ-1 решение в 5 строчек, рассказали, что алгоритм на данном таргете имеет слабую предсказательную силу (на выход на больничный сотрудника сильно влияют случайные факторы, которые мы не знаем, например, сотрудник находился на сквозняке, оделся не по погоде и т.д.), и предложили немного поменять задачу прогноза ухода на больничный, которое по нашему мнению несет большее бизнес value:

  1. Будем предсказывать не уход сотрудника на больничный, а процент людей на больничном в отдельном секторе завода или занимаемых определенную должность. Таким образом, если заболеет один сотрудник, его может подменить другой, а если заболеет 20% людей определенной должности, то это уже риск остановки производства. Да и таргет выглядит более гладко:

  2. Предложили решать с помощью предоставленных данных задачи:

    1. Оттока сотрудников

    2. Рекомендации карьерного роста

    3. Построение аналитической системы мониторинга переработок

Выступление (20% оценки)

Здесь просто. Подготовили красивую, структурированную презентацию (без лишней информации на слайдах) о нашем решении и бизнес-ценности и успешно рассказали наш подход.

Практические советы

  • Начни с простого решения

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

Презентация команды Канапе

Упоминание на habr


11. Data Science Hackathon "RaifHack"

  • Дата: сентябрь 2021 года

  • Итоговое место: 2-е

  • Приз: 200 000 рублей

  • Формат: Гибрид

  • Продолжительность: 36 часов

Задача: Построить модель предсказания стоимости коммерческой недвижимости.

Оценка команд:

  1. Место на private части лидерборда (20% оценки). Распределение баллов:

    1. 10 баллов, если место на лидерборде 1,2 или 3

    2. 5 баллов, если место на лидерборде 4,5 или 6

    3. 0 баллов в иных случаях

  2. Презентация решения (80% оценки).

Метрика:

Deviation=\frac{y_{true}-y_{pred}}{y_{true}} \\ \ \\ hit=\begin{equation*}    \begin{cases}    9.9, & deviation <-0.6 \\ 1.1* (1 + \frac{deviation}{0.15})^2, & -0.6\leq deviation \leq-0.15 \\ 0, & -0.15 \leq deviation < 0.15 \\ (\frac{deviation}{0.15} - 1)^2, & 0.15 \leq deviation < 0.6 \\ 9, & deviation \geq 0.6 \end{cases} \end{equation*} \\ \ \\ Score = \frac{1}{n}\sum_{i=1}^nhit_i

Для одного отдельного коммерческого помещения hit выглядит так:

Данные, которые предоставили организаторы:

  • Цена коммерческой недвижимости (таргет)

  • Признаки здания (этажность здания, площадь ком-го помещения, тип здания (анонимный признак), год постройки, материал постройки, ...)

  • Признаки места (координаты (зашумлённые), улица (анонимный признак), регион, город, численность населения, близость к метро)

Нюанс в данных:

  • В train выборке представлены данные по стоимости 2-х типов:

    • price_type = 0 данные с предложениями о покупке коммерческой недвижимости. Далее будем называть данный тип "автоматической разметкой". (98% объёма train выборки)

    • price_type = 1 – данные по объектам, оцененными вручную. Далее будем называть данный тип "ручной разметкой".(2% объёма train выборки)

  • В test выборке представлены данные только имеющие price_type = 1.

Получилось так, что обучение алгоритма на всей выборке давало хуже показатель метрики на лидерборде, нежели использовать только ручную разметку (price_type = 1). Это связано с тем, что в тестовой выборке присутствовали сэмплы только ручной разметки. Поэтому, подавляющее большинство команд использовало только ручную разметку в обучении своих моделей.

А использование всей train выборки приводило к плохим результатам из-за того, что распределение цен из автоматической и ручной разметки оказались неоднородными:

Уникальная идея нашей команды заключалась в том, чтобы сделать выборки однородными:

  1. Сдвигаем распределение автоматической разметки так, чтобы средние значения логарифмов распределений совпадали.

  2. Сэмплируем часть объекты автоматической разметки так, чтобы распределения совпадали. Сэмплируем с использованием ядерной оценки распределения ручной разметки.

    Сэмплирование выборки
    Сэмплирование выборки
  3. Так как сэмплирование предполагает рандом, мы на каждое отдельное сэмплирование обучаем алгоритм, делаем прогноз, а затем результаты всех прогнозов усредняем для робастности ответов.

Дополнительных действий по типу генерации дополнительных признаков, признаков из внешних источников, поиск оптимальных гиперпараметров не делался. Получилось довольно компактное решение (ссылка на Git с кодом ниже).

По итогу, на private части лидерборда мы заняли 3-е место и получили максимум баллов за первую часть (10):

Сделали красивую презентацию с анимацией (файл .pptx весом больше 200 МБ), презентовали наш подход. По сумме баллов организаторы поставили нас на второе место.

Практические советы

  • Поиск нестандартных решений

    Чаще всего для улучшения качества модели помогает работа с признаками и гиперпараметрами модели. В данном случае максимальный прирост в качестве дала работа с выбором сэмплов для обучения.

  • Презентация подхода

    Презентации подхода выделялось 80% финальной оценки. Поэтому пришлось хорошо подготовится к питчингу и сделать красочную презентацию.

Презентация команды Канапе

Ссылка на хакатон

Видео с выступлением команд

Видео с награждением

Git с кодом решения


12. SIBUR Data Science Challenge 2021

  • Дата: декабрь 2021 года

  • Итоговое место: 1-е

  • Приз: 250 000 рублей

  • Формат: ML соревнование

  • Продолжительность: 2 недели

Задача: Предсказать помесячно спрос на 12 месяцев вперед на продукты SIBUR в разрезе клиента, менеджера, страны и региона.

Метрика: RMSLE

Данные: Организатор соревнования предоставил данные о продажах продуктов за прошлые периоды по дням в разрезе клиента, менеджера, страны и региона.

Решение: Перед нами задача предсказания временного ряда. Другими словами, у нас есть график значений спроса в прошлом, его надо продлить на будущий период. В задаче временные ряды были:

  1. Сильно волотильными

  2. Много нулей. Положительный временной ряд мог прерваться периодом нулевых продаж:

  3. Отсутствие годовой, месячной, недельной сезонности в продажах.

  4. Количество продаж у разных рядов могут отличатся на порядки (у одних в среднем 40 в день, у других 1000-1500).

Приступил к генерации признаков:

Сгенерированные признаки
  • Различные статистики временного ряда в прошлом:

    • По дням, неделям, месяцам

    • max, min, mean, median, std

    • Отношение суммы за последнюю неделю к сумме последнего месяца

  • Сколько прошло дней с последнего ненулевого значения

  • Признаки нулевых продаж:

    • Максимальная известная продолжительность нулевых продаж

    • Текущая продолжительность нулевых продаж

    • Доля нулевых дней к ненулевым

  • Признаки на информации о праздниках

Обучил модель регрессии с оптимизацией метрики MSE с логарифмированными таргетами (так как есть нулевые продажи, брался логарифм от продаж +1). В качестве алгоритма использовал LGBMRegressor.

После обучения модели заметил, что на валидации максимальный вклад в ошибку вносят ряды, которые имели нулевые продажи в последние месяцы, а в "будущем" сильно растут вверх. Данные случаи хочется ловить! Построил модель классификации, которая предсказывает "вероятность" того, что объем продаж в следующий месяц будет больше 25 при условии, что в предыдущий месяц продаж не было:

df = df[df.prev_month == 0]
df['target_class'] = (df['target'] > 25) * 1

После того, как я обучил модель регрессии и модель классификации, с помощью GridSearchCV подобрал "связывающие" константы и получил следующее решение:

  1. Если предыдущий месяц ненулевой, то ответ = выход модели регрессии

  2. Если предыдущий месяц нулевой и выход модели классификации > 0.2, то ответ = выход модели регрессии

  3. Если предыдущий месяц нулевой и выход модели классификации < 0.2, то ответ = выход модели регрессии * 0.5

Данный подход сильно бустанул мой score на лидерборде.

Какие подходы еще сработали

  1. Сглаживание временного ряда. В качестве значения брался не факт за день, а среднее последних 30 дней. Таким образом ряды выглядели более гладкими:

  2. На данном этапе у меня было примерно 350 признаков. Использовал permutation feature importance для отбора признаков. Оказалось, что 35% признаков ухудшали качество.

  3. В самом конце реализовал блендинг моделей. Сделал 100 предсказаний с разными random_state (seed) в параметрах модели:

for i in tqdm(range(100)):
    lgbm_params['seed'] = i
    model = lightgbm.train(params=lgbm_params, ...)
    predictions['pred_' + str(i)] = model.predict(test)
final_prediction = predictions.mean(1)

Основные стадии улучшения алгоритма (в скобках score на лидерборде):

  1. Одиночная модель регрессии ( -> 1.4403)

  2. Добавление модели классификации (1.4403 -> 1.4216)

  3. Переход от значений к скользящему окну (1.4216 -> 1.4135)

  4. Удаление мусорных признаков (1.4135 -> 1.4093)

  5. Блендинг моделей (1.4093 -> 1.4073)

Практические советы

  • Поиск нестандартных решений

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

Презентация решения

Видео с презентациями решений

Git с кодом решения


13. SIBUR Data Science Challenge 2023 (бронза линейными моделями)

  • Дата: май-июнь 2023 года

  • Итоговое место: 3-е

  • Приз: 100 000 рублей

  • Формат: ML соревнование

  • Продолжительность: 2 недели

Задача: Прогноз количества двух продуктов SIBUR в зависимости от типа сырья и параметров производственного процесса. Задача регрессии.

Данные:

  • 24 обезличенных числовых признака

  • 1 бинарный признак (далее называю gas)

  • 2 числовых таргета

Метрика: MAPE

Отправка решений: В качестве решения платформа принимала .zip архив с инференсом (файл predict.py) и моделями (model_*.pkl). Присутствовало ограничение по памяти и времени работы инференса.

Особенность соревнования: Распределение значений признаков на public/private и train выборке отличаются. Например, feature5 на train принимала значение от 0 до 10 и от 40 до 70, а на public/private от 20 до 40 и от 70 до 100. Помимо признаков, значения таргетов тоже могут отличаться.

Данную особенность я решил проверить. Заслал решение на основе модели LightGBM на предоставленных признаках и линейную регрессию на тех же признаках. Метрика на лидерборде отличалась в 20 раз в пользу линейной модели. Особенность подтвердилась, так как алгоритмы на основе деревьев решений плохо справляются с out of distribution признаками и таргетами.

Было принято решение копать в сторону линейных моделей.

Валидация: Чтобы тестировать новые подходы, нужно было настроить валидацию так, чтобы она была однородной с разделением на train и public/private выборки.

Разбил датасет выборку на train и test с помощью алгоритма кластеризации KMeans:

from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=25)
clusters = kmeans.fit_predict(df[features])
train = df.loc[(clusters % 4) != 1, :]
test = df.loc[(clusters % 4)  == 1, :]

Данный подход хорошо разделяет выборку так, что разные диапазоны значений признаков относились либо к train либо к test выборке:

Разделение на train и test на примере признака feature13
Разделение на train и test на примере признака feature13

В итоге, качество на валидации коррелировало с качеством на лидерборде.

Далее я начал тестить подходы (в скобках показатель MAPE на лидерборде):

  1. Линейная регрессия на предоставленных признаках ( -> 2.84)

  2. Линейная регрессия на полиноме третьей степени. Реализовал с помощью метода PolynomialFeatures (2.84 -> 2.34):

    model = Pipeline([('polynom', PolynomialFeatures(degree=3)),
                      ('linear regression', Ridge(alpha=5))])
  3. Далее я заметил, что качество на валидации сильно поднимается, если обучать разные модели под отдельный таргет и под отдельное значение бинарной фичи gas. На валидации подобрал гиперпараметр штрафа (alpha) в Ridge регрессии для каждой модели (2.34 -> 2.01):

    # target 1, gas=0
    model = Pipeline([('polynom', PolynomialFeatures(degree=3)),
                      ('linear regression', Ridge(alpha=1))])
    # target 1, gas=1
    model = Pipeline([('polynom', PolynomialFeatures(degree=3)),
                      ('linear regression', Ridge(alpha=5))])
    # target 2, gas=0
    model = Pipeline([('polynom', PolynomialFeatures(degree=3)),
                      ('linear regression', Ridge(alpha=0.001))])
    # target 2, gas=1
    model = Pipeline([('polynom', PolynomialFeatures(degree=3)),
                      ('linear regression', Ridge(alpha=0.01))])
  4. Следующей итерацией я решил протестить другие линейные модели в различных комбинациях моделей и гиперпараметров:

    1. Lasso регрессия

    2. SVM на полиномиальном ядре

    3. Multi-layer Perceptron

    Для модели Ridge добавил шаг нормализации с помощью StandardScaler. Получил следующий набор моделей для разных таргетов и параметра gas:

    # target 1, gas=0
    model1 = Pipeline([('polynom', PolynomialFeatures(degree=3)),
                       ('lasso', Lasso(alpha=1))])
    model2 = SVR(kernel='poly', degree=4, C=3.0)
    model = 0.5 * model1 + 0.5 * model2
    
    # target 1, gas=1
    model = Pipeline([('polynom', PolynomialFeatures(degree=4)),
                      ('ss', StandardScaler(with_std=True)),
                      ('ridge', Ridge(alpha=5))])
    
    # target 2, gas=0
    model1 = Pipeline([('polynom', PolynomialFeatures(degree=3)),
                       ('lasso', Lasso(alpha=1))])
    model2 = Pipeline([('polynom', PolynomialFeatures(degree=3)),
                       ('ss', StandardScaler(with_std=True)),
                       ('ridge', Ridge(alpha=0.01))])
    model = 0.5 * model1 + 0.5 * model2
    
    # target 2, gas=0
    model1 = SVR(kernel='poly', degree=5, C=3.0)
    model2 = Pipeline([('polynom', PolynomialFeatures(degree=3)),
                       ('ss', StandardScaler()),
                       ('lasso', Lasso(alpha=0.0001))])
    model = 0.5 * model1 + 0.5 * model2

    Решение не зашло по времени работы инференса, так как SVM довольно долго предсказывает по сравнению с линейной регрессией. На сервере у организаторов участникам было доступно 4 ядра, поэтому, с помощью Pool библиотеки multiprocessing получилось распараллелить вычисления и уложится по времени
    (2.01 -> 1.83)

  5. На валидации увеличение степени полинома до 6-ой степени в Ridge регрессии сильно улучшало качество на валидации, но решение не проходило по памяти после отправки решения, так как метод PolynomialFeatures степени 6 увеличивал количество признаков с 24 до ~190 млн. Данную проблему обошел полиномом третьей степени на базовых признаках + базовых признаках, возведенных в квадрат. Таким образом, получаем полином 6-ой степени (признаков всего ~100 тыс.), но с потерей некоторых слагаемых. (1.83 -> 1.72)

Таким образом, получилось забрать бронзу, используя только линейные модели.

Практические советы

  • Поиск нестандартных решений

    Стандартным подходом на табличных данных является обучение модели бустинга на деревьях. Бывают моменты, когда другого рода алгоритмы дают лучшее качество. Данное соревнование - именно тот случай, когда, из-за особенности разделения выборок на обучение и тест лучше себя показывают линейные модели.

Презентация решения

Ссылка на соревнование

Видео с презентациями решений

Git с кодом решения


Выводы

Практические рекомендации, которые были представлены в статье:

  1. Разделите зоны ответственности внутри команды:

    В самом начале соревнования потратьте 2-4 часа на командный брэйншторм идей и гипотез. После того, как вы создадите план действий и definition of done, разделите зоны ответственности внутри команды (презентация, MVP, алгоритм, работа с признаками, ...). Это не значит, что каждый член команды не может помогать другому в его части. Ответственность за свою часть заключается в том, что к концу соревнования вам желательно реализовать свой кусок с помощью коллег или без них. Если у вас не хватает времени на реализацию своей части или произошли форс-мажоры, попробуйте перераспределить ресурсы команды в процессе хакатона.

  2. Разговаривайте с менторами о выборе задачи:

    Совет касается хакатонов без четкой постановки задачи, хакатонов типа "найди проблему и реши её с помощью предоставленных данных". Чаще всего, менторы - сотрудники компании организатора хакатона. Они лучше знают, какие фокусы в данный момент у компании. На советы менторов по выбору решаемой задачи стоит обратить особое внимание.

  3. Обоснуйте выбор решаемой задачи:

    Ещё на этапе постановки задачи попробуйте оценить, какой вклад принесёт бизнесу ваше решение. Прямо в числах: +5% GMV, +15% CTR, -10% издержек, увеличение лояльности и т.д. Чем больше вклад алгоритма в основные метрики компании, тем лучше. Обязательно сделайте слайд с данными цифрами. Уже во время презентации будет дан ответ на вопрос, зачем внедрять ваше решение компании организатору хакатона.

  4. Начните с простого решения:

    Рекомендация скорее касается хакатонов с лидербордом, либо гибридов. Перед "изобретением космолёта", сначала реализуйте бэйзлайн. Чаще всего, это оптимальная на рассматриваемой метрике константа (например, медиана при метрике MAE, среднее при MSE, мода при Accuracy и т.д.). Затем попытайтесь побить данное решение, либо доработать. Выше описаны примеры, когда бэйзлайна с незначительными доработками оказалось достаточно для победы.

  5. Реализуйте прототип:

    Сложные подходы чувствительны к багам, и, порой, уйдет много времени на исправление ошибок. Можно просто не успеть реализовать идею к концу хакатона из-за строгого ограничения по времени и на презентации не иметь готового решения. Сделайте выбор в сторону жадных подходов с точки зрения ресурсов времени и памяти, покажите суть алгоритма, зачем он нужен, какую задачу решает. Прототип (не оптимальный алгоритм по-определению) на то и прототип, чтобы показать, в какую сторону нужно "копать", а "в бою" уже будет больше времени для специалиста Data Science на реализацию оптимального подхода.

  6. Используйте уже реализованные решения:

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

  7. Ищите нестандартные решения:

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

    1. Какие алгоритмы сработают лучше в текущей задаче (пример с out-of-distribution признаками в соревновании от SIBUR). Не просто так были придуманы kNN, LR, SVM и т.д. Каждый алгоритм уникален и имеет свои преимущества.

    2. В какую сторону нужно "копать" (выбор модели, работа с признаками, подбор выборки для обучения, ...).

    3. С какого начального решения (baseline) надо начать разработку модели.

  8. Хорошо презентуйте своё решение:

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


Комментарии (0)