В последнее время мы много пишем о конкурсах по машинному обучению, в основном рассматривая их с точки зрения участников. Но организовать и правильно провести соревнование — тоже сложная задача. Компании учатся на своих ошибках и в следующие разы меняют структуру конкурсов. Например, RecSys Challenge 2017 с учётом опыта прошлых лет провели в два последовательных этапа. Андрей Остапец из компании Avito рассказывает об этих этапах, о различных признаках, основанных на истории поведения пользователей, и о том, всегда ли нужно использовать сложные модели для решения задачи. Команда Андрея заняла в RecSys Challenge седьмое место.
— Всем привет! Меня зовут Андрей, я работаю в Avito, и сегодня я расскажу про участие нашей команды в конкурсе RecSys Challenge 2017.
Сначала расскажу про сам конкурс и про то, какая задача решалась. Дальше расскажу про решение нашей команды и в конце — про получившиеся результаты.
RecSys Challenge — конкурс, который проводится в рамках конференции RecSys. Это крупная конференция, в нынешнем году она будет проводиться в 11-й раз. На конференции достаточно много научных докладов, но с самого начала у организаторов была традиция проводить конкурс по рекомендациям. Каждый год какая-то компания организует конкурс, различные команды участвуют. В итоге все приезжают на воркшопы и рассказывают про свои решения. В этом есть плюс по сравнению с Kaggle: можно рассказать про свой алгоритм, опубликовать статью в отдельном журнале, посвященном воркшопу конференции, узнать какие-то подробности у организаторов конкурса и посмотреть, какие решения использовали другие участники.
В прошлом году, как и в этом, конкурс проводила компания XING. Это немецкий аналог LinkedIn, который посвящен рекомендациям работы. В прошлом году команда Avito тоже участвовала в RecSys Challenge, мы заняли седьмое место и рассказали об этом на тренировке в ШАДе. Вот ссылка на наш рассказ.
В прошлом году нам дали достаточно стандартную задачу. Были даны взаимодействия за год, и требовалось предсказать, на какие вакансии будут кликать пользователи сайта. Мы получили достаточно неплохие результаты, использовали факторизационные машины, все было достаточно хорошо, но было и несколько проблем для организаторов.
В первую очередь, хорошо работали те алгоритмы, которые выбирали лучшие вакансии среди рекомендованных платформой XING. Известный факт: рекомендации оказывают влияние на действия пользователей. Таким образом, хорошо работали алгоритмы, которые пытались предсказать, что будет показывать платформа, и исходя из этого ранжировали вакансии. С одной стороны, это неплохо, но если попытаться заинклементить такой алгоритм в продакшен, то столкнешься с проблемой, когда алгоритм по сути зависит от другого алгоритма.
Вторая проблема заключалась в том, что пользователи часто совершали повторные клики. Примерно 30% кликов были повторными. И это была довольно сильная фича. Если пользователь уже когда-то кликал на данную вакансию — порекомендуй ее снова. Это, может быть, не такая большая проблема, но вряд ли пользователи в рекомендациях хотят видеть только те айтемы, на которые они уже кликали. Скорее всего, они хотят видеть что-то новое.
В 2017 году XING исправили эти проблемы. Чтобы решить первую проблему, они организовали соревнование из двух этапов. Офлайн — все стандартно, дана обучающая выборка, тестовая выборка, которая является исторической, надо отправлять решения и пытаться угадать, как пользователи кликали. Все это — на исторических данных.
Вторая стадия — онлайн-этап, когда каждой команде давался отдельный участок трафика, на котором показывались только рекомендации команды. Таким образом, весь трафик полностью зависел от команды и все клики зависили от рекомендаций, которые давала команда.
Чтобы решить проблему повторных кликов, организаторы сфокусировались на задаче холодного старта относительно вакансий. Все вакансии, которые выкладывались, были новыми. По ним не было истории кликов, опираться можно было только на контент вакансии.
Нужно было определять пользователей, которые могут быть заинтересованы в получении пуша для онлайн-стадии. После того, как команда отправляла решение, пользователям, если у них установлено приложение, приходил пуш. Они могли кликнуть на эту вакансию.
Нужно было определять тех пользователей, которые являются подходящими кандидатами на указанную вакансию. Об этом чуть позднее.
Для двух этапов использовались практически одни и те же метрики и наборы данных. Офлайн-этап служил лишь как отбор команд во вторую стадию конкурса. Те команды, которые занимали топ-места, проходили во вторую стадию, и им уже выделялся участок трафика, на котором они могли посылать рекомендации. Вторая стадия длилась пять недель. Каждый день надо было отправлять рекомендации. Финальный результат — сумма за две лучшие недели.
Как использовать рекомендации?
С офлайн-стадией все понятно, это исторические данные. Отправляешь файл с решением — сразу получаешь результат. Единственное ограничение: для вакансий можно было рекомендовать не более ста пользователей. Для онлайн-стадии все интереснее. Организаторы выкладывали целевым пользователям целевые вакансии, давалось 24 часа, чтобы подготовить файл сабмита, который включал этих пользователей и эти вакансии. Для каждой вакансии можно было предоставить список не более чем из 255 пользователей. Это сделано, чтобы какая-то вакансия не получала перекос, когда все рекомендации идут на эту вакансию. Пользователь мог получить не более одного пуша в сутки, чтобы не нагружать пользователей, чтобы они не уставали от рекомендаций.
Было четыре различных способа доставки рекомендаций. Первый — лист рекомендаций. Это десктопная версия. Если у пользователя была установлена мобильная версия, то ему приходил пуш, он переходил по ссылке и сразу видел вакансии, которые ему рекомендованы. При наличии десктопной версии была ссылка Jobs. Когда приходила новая рекомендация, все это обновлялось на сайте, и пользователь мог увидеть новую вакансию.
Дальше рекомендации показывались на домашней странице. Когда пользователь заходит на сайт, ему показываются новости, среди которых могла быть показана и рекомендация.
Третий вариант — электронная почта. Если организаторы видели, что пользователь не заходил на сайт, они отправляли ему письмо, где говорили, что вам рекомендована новая вакансия. Если вам интересно — переходите по ссылке.
Четвертая часть довольно любопытная. Это лист рекомендаций для рекрутера. XING нацелен не только на пользователей, но и на рекрутеров, поэтому для каждой вакансии есть отдельная страничка, где рекрутер может увидеть те рекомендации, которые ему дает XING. В них — те пользователи, которые по мнению XING подходят под данную вакансию. Здесь указана для каждого человека его текущая работа, предыдущая работа и возможность связаться с человеком, если рекрутер посчитает, что он подходит под данную вакансию.
На слайде — данные, которые были предоставлены участникам.
Для пользователей данные в основном были взяты из анкеты. Из интересного: организаторы предоставляли ключевые слова из текущей или, возможно, предыдущей работы пользователя, но они сами выполняли обработку текстов, и данные были предоставлены в виде списка натуральных чисел. Это сокращало диапазон работы с текстами. Можно было много всего применять, но здесь мы не знали оригинальные слова и не знали, являлись ли эти числа оригинальными словами или же биграммами, триграммами и т. д.
Также был предоставлен текущий уровень карьеры — натуральное число от 1 до 6. Чем выше, тем выше уровень карьеры. Дисциплина, индустрия, география и т. д.
Для офлайн-стадии была предоставлена интересная фича — интерес к смене работы. Но ее уже не было во второй стадии. Пользователь мог быть премиум, он мог платить XING деньги. Это повлияет на функционал и качество.
Для вакансий были предоставлены схожие признаки. Отличие в том, что ключевые слова брались из заголовка вакансии и из описания компании. Все остальные признаки схожи. Также вакансия могла быть проплаченной или бесплатной.
Файл взаимодействия включал всего четыре поля: какой пользователь на какую вакансию в какой момент кликнул и каким был тип взаимодействия. Всего было шесть типов. Первое — показ: пользователю показывалась данная вакансия. Второе — опция добавления в закладки: пользователь добавил вакансию в закладки, чтобы вернуться позднее. Третье — он нажал на кнопку «связаться с рекрутером». Четвертое — он удалил вакансию, сказал, что она мне больше не интересна, не хочу ее больше видеть. Последнее — интерес рекрутера: рекрутер перешел со своей страницы на профиль данного пользователя.
Для небольшой статистики показано распределение тренировочной выборки в онлайн-стадии. В обучении был миллион пользователей, 750 тыс. вакансий, порядка 90 млн показов, около 4 млн взаимодействий. То есть пользователи кликают не так часто, причем бóльшая часть взаимодействий — клики. На втором месте — удаление рекомендации. Пользователи довольно часто удаляют рекомендации и говорят, что не хотят их больше видеть. Остальных типов было меньше.
Какой функционал качества использовался? Он был довольно необычным. Участники должны были для каждой вакансии предоставить список рекомендаций. Итоговая сумма, которую получало решение, считалась по всем вакансиям. И оценка для каждой вакансии разбивалась на две части: user success, определяющая, насколько это успешно по пользователям, и item success — был ли этот список рекомендаций успешен для данной вакансии.
User success считался просто. Если какой-то пользователь кликнул на вакансию, получался 1 балл. Если нажал кнопку «Добавить в закладки» или «Ответить» — 5 баллов, причем если он выполнял оба действия, все равно прибавлялось только 5 баллов. Если к нему проявил интерес рекрутер, к оценке добавлялось 20 очков. Если пользователь удалил вакансию — минус 10 очков. Причем вся эта сумма умножалась на два, если пользователь являлся премиум и платил XING деньги.
С item success все немного интереснее. Для каждой вакансии был предоставлен список пользователей, и если хотя бы для одного пользователя рекомендация была успешной, то в случае, если item платный, получалось 50 очков, а если бесплатный — 25. Ну а если для вакансии не было ни одного успешного кандидата, ни один человек не кликнул и не получил сумму больше нуля, то к оценке добавлялся 0, мы не получали ничего.
Сразу может возникнуть вопрос: какую максимальную оценку можно было получить за одну пару юзер-вакансия, исходя из этого функционала? 102. Кликнул на вакансию — плюс 1, добавил в закладки — плюс 5, рекрутер проявил интерес — плюс 20, пользователь премиум — умножаем на два. Получаем 52, и этот пользователь является успешной рекомендацией для данной вакансии. Если item платный, получаем еще 50 очков, в сумме 102.
Очевидно, минимум за рекомендацию — всего 1 очко. Это если пользователь просто кликнул на вакансию и есть item, который уже является успешным для данной вакансии.
Какие вызовы со стороны организаторов?
Баланс между интересами пользователей и рекрутеров основан на функционале качества. Клик пользователя ничего не значит, гораздо выгоднее, если к нему проявил интерес рекрутер.
Второй вызов — баланс между релевантностью и заработком. Как было показано, score сильно зависел от того, является ли пользователь и item премиум, получает ли платформа XING с них деньги.
Третье — проблема «холодного старта». Все вакансии не имели истории, и это более сложная задача, поскольку мы могли использовать только контент объявления.
Четвертое — умная посылка пушей. Надо определить, какому пользователю посылать пуш, какому нет, и в какое время. Об этом позднее.
Сначала организаторы, как и во многих конкурсах, предоставили baseline. Он был крайне простой, но показывал хороший результат. В нем было всего шесть признаков, причем пять из них были бинарными: количество совпадений по тайтлам юзера и вакансии, натуральное число, и дальше простой матчинг дисциплины, уровня карьеры, отрасли, страны и региона. Для обучения использовалось только взаимодействие. Если пользователь удалил вакансию, целевая переменная получалась равной нулю, а если пользователь произвел положительное действие — единице. Это обучалось на XGBoost и давало результат 10 000 на лидерборде. Довольно хорошо.
Если просто для всех вакансий на офлайн-стадии рекомендуем топ-100 самых активных пользователей, получаем результат 519. XGBoost дает намного больше.
Какой был объем выборки? Всего 300 млн взаимодействий, 75 млн целевых пользователей, для которых надо было построить рекомендации, и примерно 50 тыс. целевых вакансий.
Одна из особенностей датасета, которую можно было сразу увидеть: взаимодействие содержит огромное количество полных дубликатов. Если их почистить, уже получаем 220 млн взаимодействий, что позволило ускорить алгоритм. Обучаемся только на взаимодействиях. Взаимодействий меньше — всего 6 млн. Получаем обучающую выборку из 6 млн.
Чтобы ускорить процесс, предсказание для пары делалось только в том случае, если есть пересечение по заголовкам. В итоге выборка, для которой XGBoost должен построить предсказание, — примерно 50 млн. Когда я пытался воспроизвести этот baseline, я столкнулся с проблемой, что на сервере не работал XGBoost, и я просто переписал на Н2О, но на решение это не оказало существенного влияния.
Казалось бы, логичная вещь — просто добавить в этот baseline новых фичей. Пять бинарных фичей дают хороший результат, так давайте просто накидаем фичей и получим результат лучше.
После того, как я добавил еще 19 и получилось 25, действительно удалось чуть-чуть улучшить результат. Он стал примерно 11 тыс., но дальнейшие улучшения не получались. Причем в конце были различные эксперименты: по-другому составить обучающую выборку, давать разным событиям разный вес, просто добавлять им просмотры с небольшим весом, который больше, чем вес удаления, но меньше, чем у положительного действия. Но все это приводило к тому, что score падал, пока наконец не получился результат минус 5000. Плохо, в основном рекомендуем то, что пользователи удаляют.
Почему так получилось? Скорее всего, есть две проблемы. Первая — я не смог настроить негативный сэмплинг. Когда я просто накидывал просмотры, алгоритм начинал отдавать много предсказаний про простые просмотры.
Вторая проблема — я оптимизировал среднеквадратичное отклонение в XGBoost, и наверное, это плохо. Функционал заточен на деньги, а мы оптимизируем RMSE. Но с этим надо было что-то делать.
Очевидно, признаки, которые добавлялись, логичны и должны улучшать score. Поэтому я решил использовать какой-нибудь простой алгоритм, чтобы проверить качество признаков. Для вычисления оценки для пары пользователь-вакансия я просто использовал суммы трех типов, которые тоже разумны. Близость по профилю пользователя к профилю вакансии —смотрим только на соответствие контента между ними.
Дальше строим различные интересы к параметрам, исходя из истории пользователя. Никак не используем профиль пользователя, смотрим только на то, что он кликал и что удалял.
Третья часть, которая была актуальна для офлайн-стадии: для некоторых айтемов и пользователей были указаны предыдущие взаимодействия. Их было мало, но все равно они позволяли немного поднять score.
Логично, что если есть 50 тыс. пользователей и 50 тыс. вакансий, то все их проскорить невозможно. Надо отбирать какое-то множество подходящих. Чтобы уменьшить объем предсказаний, использовалась следующая эвристика: для пары вычисляем оценку только в том случае, если есть какое-то пересечение по заголовкам вакансии и профиля пользователя, либо если пользователь просматривал объявление, которое пересекается с вакансией. Например, у пользователя указано, что он является аналитиком, и он когда-то совершил всего один клик на data scientist. Потенциальные рекомендации — это вакансии, где есть слова «аналитик», «data scientist» и всё. Очевидно, это не самая лучшая эвристика, но она позволяет существенно сократить объем той выборки, для которой надо строить предсказания.
Как считались различные близости? В качестве примера рассмотрим близость ключевых слов к заголовку вакансии. Основная идея в том, что vs можем использовать некий аналог IDF по трем словарям, то есть у нас есть слова из профиля пользователя и из заголовка айтема, а также теги айтема. Для них вычислялся аналог IDF. Мы просто берем логарифм от числа уникальных токенов на встречаемость токенов, и чем реже встречается токен, тем больший вес он получает. Когда мы считаем близость по профилю, мы просто это нормируем на количество уникальных ключевых слов пользователя. Чем меньше у него ключевых слов, тем больший вес получит данная сумма.
Под каждый тип близости мы подбираем свой вес W. В итоге финальная оценка для пары пользователь-вакансия вычисляется как сумма по всем перечислениям ключевых слов пользователя из заголовка вакансии как раз с данными весами. Такие суммы вычислялись для всех возможных вариантов, указанных в профиле пользователя и в вакансии. Мы уже получаем множество признаков.
Другой вариант подсчета для текста — полное вложение ключевых слов, ключевые слова из заголовка вакансии. Это тоже выглядит логично, если у пользователя, например, указано «аналитик», а вакансия — «аналитик в банк». Наверное, это тоже сигнал. Но здесь особенность в том, что неизвестно, как организаторы получали эти ключевые слова, удаляли ли они стоп-слова или нет. Поэтому использовался достаточно стандартный подход, и он сработал довольно неплохо.
Как считались близости по другим признакам? По карьерному уровню все было довольно просто: он был от 1 до 6, поэтому по профилю мы можем просто рассмотреть разность. Если разность маленькая — увеличиваем вес, если большая — уменьшаем. Забавно, что пользователи, у которых было указано, что они студенты, тоже иногда смотрели вакансии, где требовались директора фирм. Очевидно, что они под эти вакансии не подходят, но какой-то интерес был. Непонятно, почему. Как следствие — в данном случае нельзя ставить нулевой вес. Мы просто использовали понижение веса.
Индустрия и дисциплина служили категориальными признаками, которые были анонимизированы. Тут особо ничего не придумаешь. Использовалась довольно простая эвристика: при точном совпадении оценка умножалась на вес, который больше 1, а иначе немного понижалась.
По взаимодействиям мы можем не смотреть на профиль пользователя, а на те вакансии, которые он кликал. Тогда мы получаем не единое число, которое соответствует пользователю, а целое распределение. Нам надо померить близость к единственному числу, указанному в вакансии. Здесь просто считалась различная близость, и находились такие ключевые правила. Например, если у пользователя богатая история, он много кликает, но на данный карьерный уровень кликает крайне редко, то оценка понижалась. Если кликает часто, 95% приходится на данный карьерный уровень, то повышалась. При этом все нормируется на общее число кликов пользователя.
Использовалось и еще несколько ранкеров. Первый был основан на предыдущих кликах пользователя. Хороший признак, который серьезно позволял улучшить score, — это когда пользователь кликал на вакансии с таким же заголовком: уровень карьеры, индустрии, дисциплины. Пользователю интересна вакансия с таким же заголовком и другими параметрами. Он кликал не на данную вакансию, а, когда-то в прошлом, — на схожую.
Также для пользователя считалось отношение негативных и позитивных действий, что тоже логично: если пользователь часто удаляет вакансии, то не нужно ему ничего рекомендовать. Лучше рекомендовать тем, которые чаще кликают. Для офлайн-этапа использовалась последняя активность пользователя, и были попытки оптимизировать сам функционал задачи, поскольку он выглядел необычно. Использовалось то, является ли пользователь премиум-пользователем и является ли вакансия VIP. В таких случаях общая оценка повышалась.
Как производилась локальная валидация? С ней все было довольно интересно. Во-первых, все время, указанное в событии, было анонимизировано, то есть нельзя было использовать точное значение. Но организаторы гарантировали сохранение порядка действий. Если в данных одна вакансия находилась позже другой, то и по факту они были выложены на сайт одна позже другой.
Чтобы делать валидацию быстро, был просто подобран P последних дней так, чтобы примерное количество пользователей в вакансии и валидации совпадало с тестом. И все целевые пользователи вакансии — это те, кто получил или совершил хотя бы одно действие.
Минусы в валидации. В последних днях не было события с типом «интерес рекрутера», непонятно, как валидироваться по этому событию. Но по факту получилось интересно: на деле этого события не было в онлайн-этапе. Либо организаторы ошиблись в каких-то данных, либо у них поехала статистика. Самое дорогое действие, которое приносило больше всего очков по user success, с какого-то момента перестало появляться на сайте. Либо статистика упала, либо пользователи не привлекали рекрутеров. И совпадение такой валидации с публичным лидербордом было примерно в 70% случаев. Поэтому валидация шла исходя из улучшения на локальной валидации, по ссылке на публичный лидерборд. Если и там, и там совпадало, то алгоритм обновлялся. Если нет — искалось что-то новое.
На онлайн-этапе, где каждый день выдавались новые данные, а вначале была выдана тренировочная выборка, валидация проходила просто на всех событиях, которые получились с момента начала этапа. Валидация с каждым днем увеличивалась.
Получившиеся улучшения. Использование такого алгоритма позволило повысить скорость примерно до 20 тыс., а в дальнейшем удалось дойти до 32 тыс. Причем score не всегда улучшался, потому что локальная валидация не всегда отражала действительный порядок и улучшения.
Краткий обзор ключевых шагов. Простая модель, которая работала, за 30 минут давала результат 19 тыс., причем после отправки файла с решением наша команда оказалась на четвертом месте.
Дальше из ключевых вещей, которые помогли, — как раз добавление заголовка, полное совпадение по заголовку, уровню карьеры, индустрии и дисциплины, и переподбор весов. Это позволило довольно серьезно улучшить результат. Еще одно довольно серьезное улучшение, которое было сделано в конце, — добавление IDF. После этого наша команда оказалась на шестом месте, и такого результата удалось добиться через две недели после начала конкурса. Потом у меня идеи исчезли, больше решений мы не отправляли, а конкурс длился еще месяц.
Во вторую стадию проходило топ-20 команд. Наша команда ничего не отправляла, потихоньку падала, но в итоге оказалась на 18 месте, и мы пробились в онлайн-стадию.
На онлайн-этапе общее решение проходило по такой схеме: за несколько дней до начала конкурса XING выдавал тренировочную выборку, а после начала конкурса каждый день проходила следующая процедура. XING автоматически выкладывал таргет пользователей и вакансий, для которых нужно построить рекомендации, и предыдущее взаимодействие за 24 последних часа. После этого давалось 24 часа на построение рекомендаций. Если команда отправляет результат, то рекомендации сразу показывают пользователям. Если она не успевает уложиться в 24 часа, то пользователям ничего не показывается.
После этого в течение семи дней меряется фидбек пользователей. Если пользователь кликнул на данную вакансию в течение 7 дней, то команда получает результат, какие-то очки, а если он кликнул на вакансию через 8 дней — команда ничего не получает. Так длится до окончания конкурса.
Наш алгоритм чуть менялся под конкурс, но не особо сильно. По результатам первой недели мы заняли 11 место. Это было связано с тем, что первые два дня пришлись на майские праздники и мы ничего не отправляли, а затем, несмотря на то, что форматы данных должны были совпадать, они немного отличались. В первые три дня после отправки мы получали неправильные рекомендации, на сайт они засылалось в маленьком количестве, score был крайне низкий.
В последние два дня это удалось поправить, так что score поднялсяю. Во вторую неделю был некий оптимизм.
На второй неделе все стало лучше. Мой алгоритм занял четвертое место, причем до последних дней была борьба: наша команда оказывалась то на втором, то на третьем месте, и отрыв от 10 места был достаточно серьезным, что позволяло смотреть в будущее с воодушевлением.
На третьей неделе, несмотря на то, что никаких изменений не было, score упал. На второй было 4800, на третьей стало 3800, причем алгоритм никак не менялся. Возможно, это связано с тем, что кусок трафика, который давался каждой команде, включал в себя примерно 50 тыс. пользователей, и каждый день нужно было строить рекомендации примерно для 10 тыс. вакансий. Уажется, что 50 тыс. пользователей — много. Все равно их поведение случайно, и в один день можно получить больше, а в другой — меньше.
Мы решали это командой Avito, но договорились идти параллельными путями: каждый делал свое решение. Мое решение с самого начала было лучше, но за две недели до конца удалось настроить линейную комбинацию решений. Василий Лексин построил решение на основе LCE, он об этом рассказывал неделю назад на Avito Data Science meetup по рекомендациям — можно перейти по ссылке и послушать.
Мы посмотрели на локальную валидацию, и у нас получилось улучшение на 8%, что хорошо. Сам алгоритм Василия показывал результат примерно на треть хуже, но в линейной комбинации мы получили улучшение.
Мы попытались отправлять алгоритм уже на основе линейной комбинации, но score не вырос.
При этом забавная вещь: начиная с четвертой недели у всех команд score упал в несколько раз. Если раньше победители набирали порядка 5 тыс. за неделю, то начиная с четвертой недели они набирали максимум 2 тыс. На форуме у организаторов спросили, почему это произошло, и они ответили, что, скорее всего, это естественный процесс. Пользователям надоедают рекомендации. Ответ может быть логичным, но как-то странно, что три недели все было нормально, а на четвертой неделе score упал. Возможно, если в течение 21 дня на телефон пользователям ежедневно приходит пуш, им действительно начинает надоедать и они отключают пуши. Но то, что существует такая точка, бифуркация, когда score у всех упал, кажется не очень логичным.
В итоге, несмотря на то, что мы получили улучшение по локальной валидации, по ранкам мы остались примерно на том же уровне и даже немного упали. Непонятно, связано ли это с тем, что другие команды улучшили свои результаты, или с тем, что у всех упал общий score. Являются ли эти score адекватными — вопрос.
На пятой неделе score упал еще сильнее, вообще до 300 очков. Как я говорил, финальные результаты считались по двум лучшим неделям, поэтому здесь приведена статистика всех команд.
По сумме двух недель у нас получился результат 8700 очков. У победителя довольно серьезный отрыв — почти 11 тыс. очков. Но заметно, что все команды со второго по седьмое место находятся очень близко. Второе место — 9700, у нас 8700, результаты довольно близкие. И отрыв от восьмого места в 1700 очков тоже является достаточно значительным. При этом надо понимать, что последние две недели, когда, скорее всего, все участники улучшали свои алгоритмы, пропали, и по факту можно было использовать только первые три недели. Допустим, для первой недели мы запоздали с посылкой решения и тоже получили низкий score. Являются ли эти результаты адекватными? Мы могли оказаться и выше, и ниже.
Из интересного: все шесть команд, которые оказались выше нас, на офлайн-стадии занимали места с первого по восьмое, а то, что я описал, было на 18 месте. Мы использовали его без изменений, и нас это подняло с 18 места на седьмое.
Забавно, что и в прошлом, и в этом году мы заняли седьмое место, хотя задача и подход были совсем другие.
Что не получилось?
Первое — не получилось учитывать особенности функционала задачи. Здесь очевидно, что рекомендация может приносить и 1 очко, и 50. Хотелось бы после того, как был получен список рекомендаций по релевантности, как-то его переранжировать, чтобы, скорее всего, улучшить функционал. В текущем решении были небольшие попытки — повышался score у премиум-айтемов. Но на функционал это влияло не очень сильно.
Второе — использование сложной модели. Все, что я описал, — это различные фичи, которым давался положительный или отрицательный вес. По-хорошему, надо использовать какой-то бустинг, куда закладывать все эти фичи. Вероятнее всего, score можно улучшить. Это связано с использованием простой модели. Не получилось использовать сложную модель, и лучше получить хоть что-то, чем крайне низкий score.
Третье — не удалось получать обратную связь по изменению качества работы модели на онлайн-этапе. Организаторы давали все взаимодействия за предыдущий день. Были идеи использовать некое А/В-тестирование: дается 50 тыс. пользователей, мы разбиваем их на две части, на одной запускаем один алгоритм, на другой — другой, после этого получаем обратную связь по тому, как это действует на сайте, и выборка в 25 тыс. должна показывать результаты. И уже исходя из этого использовать подстройку алгоритма. Но по факту мы пытались несколько раз воспроизвести score на лидерборде, и он отличался от того, который строился по взаимодействиям от организаторов. Так что мы решили, что невозможно им доверять, и поэтому такое тестирование не получилось.
Использование по всей выборке, когда в один день отправлялся бы один алгоритм, а в другой день — другой, не получалось, исходя из того, что score в лидерборде обновлялся за 7 предыдущих дней. Если ты поменял параметр в алгоритме, то точное улучшение ты увидишь через 7 дней. Это долго: конкурс длился всего пять недель.
И четвертое — у организаторов не получилось провести конкурс без накладок. Первая накладка в том, что одно действие — интерес рекрутера — с какого-то момент вообще перестало появляться во всех тестовых выборках. После четвертой недели score у всех упали, и этому нет объяснения. Но организаторы объявили финальные результаты, и они основаны, по сути, на сумме за вторую и третью недели.
Автор: Яндекс