Русский
Русский
English
Статистика
Реклама

Рекомендуем город для путешествия при помощи нейросетей с вниманием

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



Рекомендации городов в booking.com, картинка отсюда


В этой статье мы опишем наше решение задачи, которое заняло 5е место на соревновании. Наше решение основано на нейросетевом механизме внимания, а так же на основе listwise-метода обучения ранжированию LambdaRank. Наше решение выбирает правильный город в четыре рекомендованных города с вероятностью 55.5%, что есть довольно неплохой результат, учитывая что алгоритму необходимо было выбирать 4 города из практически 40000 возможных.


Эта статья расширенная версия нашей статьи, которая была принята и опубликована на воркшопе по web-туризму в рамках конференции WSDM.


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


Описание задачи


Задача, предложенная на соревновании, состояла в том, чтобы разработать рекомендательный алгоритм, который рекомендовал бы следующий город пользователю на основании уже посещенных им городов. Для разработки рекомендательного алгоритма, организаторы предложили датасет состоящий из двух частей train и test. Каждая строчка датасета содержит следующие данные:


Колонка Описание
user_id Id пользователя
checkin Дата въезда в отель
checkout Дата выезда из отеля
affiliate_id Анонимизированный Id источника трафика (Например поисковая система)
device_class Desktop/Mobile
booker_country Страна из которой было выполнено бронирование
hotel_country Страна отеля (анонимизированно)
city_id Город отеля (анонимизированно)
utrip_id Идентификатор путешествия

Пример данных из части train датасета
user_id checkin checkout city_id device_class affiliate_id booker_country hotel_country utrip_id
0 1006220 2016-04-09 2016-04-11 31114 desktop 384 Gondal Gondal 1006220_1
1 1006220 2016-04-11 2016-04-12 39641 desktop 384 Gondal Gondal 1006220_1
2 1006220 2016-04-12 2016-04-16 20232 desktop 384 Gondal Glubbdubdrib 1006220_1
3 1006220 2016-04-16 2016-04-17 24144 desktop 384 Gondal Gondal 1006220_1
4 1010293 2016-07-09 2016-07-10 5325 mobile 359 The Devilfire Empire Cobra Island 1010293_1
5 1010293 2016-07-10 2016-07-11 55 mobile 359 The Devilfire Empire Cobra Island 1010293_1
6 1010293 2016-07-12 2016-07-13 23921 mobile 359 The Devilfire Empire Cobra Island 1010293_1
7 1010293 2016-07-13 2016-07-15 65322 desktop 9924 The Devilfire Empire Cobra Island 1010293_1
8 1010293 2016-07-15 2016-07-16 23921 desktop 9924 The Devilfire Empire Cobra Island 1010293_1
9 1010293 2016-07-16 2016-07-17 20545 desktop 10573 The Devilfire Empire Cobra Island 1010293_1

В тестовой части датасета, последний город в путешествии и страна последнего города были замаскированы; остальные параметры, например дата въезда и дата выезда, доступны. Задача участников соревнования для каждого путешествия из тестовой части датасета порекомендовать 4 варианта последнего города. Участники оценивались по метрике Accuracy@4. По сути, эта метрика показыват сколько процентов рекомендаций содержали правильный город.


Переранжирующая модель на основе механизма внимания.


Метрика Accuracy@4 по своей сути является метрикой ранжирования: мы можем сгенерировать скор для всех возможных городов, отсортировать их и оценить вероятность с которой целевой город окажется среди топ-4х городов. Так как наша целевая метрика является метрикой ранжирования, мы решали задачу как задачу ранжирования.
Сама по себе метрика Accuracy@4 не очень удобна для оптимизации напрямую, тал как она учитывает выставленный скор только для четырех городов и никак не учитывает взаимный порядок между этими четыремя городами. Для того, чтобы облегчить себе задачу, мы заменили эту метрику другой популярной метрикой ранжирования, а именно метрикой NDCG@40.


Описание метрики NDCG

Метрика NDCG расшифровывается как Normalized Discounted Cumulative Gain. Эта метрика подходит для оценки порядка внтури списка документов; в нашем случае один документ это один рекомендуемый город.


Для того, чтобы посчитать NDCG нам сначала надо посчить другую метрику, Dicounted Cumulative Gain (DCG).


Пусть у нас есть список из K документов, и мы знаем, что релевантность i-го документа равна ri
Тогда, метрика DCG@K считается по формуле:


$DCG@K = \sum_{i=1}^{k}\frac{2^{r_i} - 1}{log_2(i+1)}$


Теперь мы можем определить NDCG:


$NDCG@K = \frac{DCG@K}{IDCG@K}$


где IDCG (Ideal DCG)- это DCG для того же списка документов, переупорядоченных в соответствии с релевантностью документов.


Метрика NDCG в идеальном случае всегда равна единице. Ее значение зависит только от порядка документов.


Так как метрика NDCG@K зависит не только от элементов, включенных в список рекомендаций, но и от их относительного порядка, ее значение более стабильно по сравнению с метрикой Accuracy@K. Кроме того, мы увеличили стабильность метрики за счет того, что выбрали большее значение K (40 вместо 4).


Еще одной проблемой метрик ранжирования, таких как NDCG@K или Accuracy@K, является тот факт, что их значение изменяется скачкообразно при изменении порядка элементов в списке рекомендаций. Это означает, что такие метрики не являются гладкими и дифференцируемыми и их нельзя напрямую оптимизировать классическим методом градиентного спуска.


Градиентный спуск и его вариации служат основой для оптимизации нейросетевых алгоритмов. Так как он не работает напрямую с метриками ранжирования, на практике при решении задачи ранжирования часто оптимизируют прокси-метрики, такие как log loss, в надежде, что их оптимизация приведет к улучшению качества ранжирования. Нам не очень понравилась такая идея, и мы решили все-таки попытаться оптимизировать саму метрику NDCG напрямую.


Для прямой оптимизации метрики NDCG существует метод LambdaMART[1]. Это метод, работающий на основе градиентного бустинга над деревьями принятия решений. Например, он реализован в популярных библиотеках LightGBM и XGboost и является рабочей лошадкой для решения задач ранжирования. Например, этот метод работает в Bing для ранжирования поисковых результатов, применяют в Amazon, да и Яндексовый YetiRank является близким родственником данного подхода.
В основе метода лежит идея замены градиента лосс-функции специально сконструированными значениями, называемыми лямбда-градиент, используя которые в методе градиентного спуска можно оптимизировать метрику NDCG. Мы реализовали этот метод для обучения нейросетей и применили для решения нашей задачи. В итоге, верхнеуровнево, наше решение выглядит следующим образом:


  1. Выбрать города-кандидаты для рекомендаций при помощи простых эвристических алгоритмов.
  2. Сгенерировать матрицу C фичей для кандидатов.
  3. Сгенерировать векторные представления T предыдущих городов в текущей поездке при помощи Transformer-like архитектуры.
  4. Сгенерировать векторное представление F целевого города на основе доступных фичей. Несмотря на то что мы не знаем идентификатор города, мы можем использовать такие параметры как дата въезда и выезда, тип устройства и т.д.
  5. Сгенерировать скор для кандидатов и отсортировать их в соответствии с этим скором. Для этого, мы применяем механизм внимания между кандидатами и посещенными городами. Результат этой операции скалярно умножаем на векторное представление фичей и получаем результат.

$Scores = MultiHeadAttention(C, T) \cdot F$


Теперь разберем нашу модель подробнее.


Генерация лейблов


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


1) Сортируем города из путешествия по времени
2) Несколько последних городов в путешествии объявляем "целевыми". Конкретное количество целевых городов определяем случайным образом для каждого путешествия
3) Назначаем для i-го города в целевой части скор 2-i. Идея назначать экспоненциально уменьшающийся вес базируется на том, что чем на более далекое будущее приходится посещение города, тем меньше влияние предыдущих городов и тем больше влияние других факторов, которые наша модель не может учесть.


Отбор кандидатов для ранжирования


Наша подход основан на методе оптимизации LambdaRANK.
Этот подход требует довольно тяжелых вычислений, и поэтому мы можем его применять только на ограниченном количестве кандидатов. Поэтому, нам необходимо было отобрать города-кандидаты используя более простой подход. Для того, чтобы тренировать нашу модель за разумное время мы решили ограничиться 500 городами-кандидатами для каждого путешествия из 39870 возможных. Мы использовали смесь простых моделей для того, чтобы выбрать эти 500 кандидатов.


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


Вот список простых моделей и правил, которые мы использовали для генерации 500 кандидатов:


1. Все предыдущие посещенные города из путешествия

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


2. Transition chain

Данная модель использует последовательную природу посещений городов в модели.
Предположим, что в нашем датасете всего $M$ городов, и каждое путешествие представляет из себя последовательность из $K$ городов, где $K$-й город в последовательности это наш целевой город. Мы можем создать матрицу переходов в последний город $T \in R^{M \times M}$, которую мы заполним, используя следующую процедуру:


procedure fillTransitionMatrix     T  zero matrix with size M  M      for current_trip in trips do:     last_city  current_trip [1]         prev_cities  current_trip [: 1]         for city in prev_cities do:             T [city][last_city] += 1.         end for     end forend procedure

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


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


3. BookerTripCountryTop

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


4. LastCityCountryTop

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


Используя эвристики описанные выше, мы зачастую могли полностью заполнить множество кандидатов. Однако, в некоторых ситуациях, нам не хватало городов. Например, такое могло случится, когда пользователь из редкой страны путешествует в другую редкую страну в этом случае алгоритмы TransitionChain, BookerTripCountryTop и LastCityCountryTop могут сгенерировать лишь небольшое количество городов-кандидатов с ненулевым скором. В такой ситуации мы использовали два дополнительных метода отбора кандидатов, используя следующие эвристики:


5. BookerCountryTop Самые популярные города среди путешественников из страны, совпадающей со страной текущего пользователя.


6. GlobalTop Самые популярные города в датасете.


Используя описанные шесть эвристик мы могли гарантированно заполнить массив из 500 городов-кандидатов. Наши эксперименты показали, что используя данные эвристики, вероятность включения нужного города в список кандидатов (метрика recall@500) составляет примерно 90%.


Генерируем фичи для кандидатов.


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


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

Генерируем векторе представление путешествия


Наша модель использует нейросетевую архитектуру, похожую на архитектуру Transformer[2] для того, чтобы закодироияя. Архитектура Transformer хорошо подходит для обработки текстов. Использование данной архитектуры базируется на том факте, что последовательность городов в путешествии похожа на последовательность слов в тексте.


Мы сгенерировали векторное представление каждого города в путешествии используя следующие признаки:


  • Эмбеддинг города, 32 обучаемых параметра.
  • Эмбеддинг страны пользователя, 32 обучаемых параметра.
  • Эмбеддинг страны города 32 обучаемых параметра. Данный эмбеддинг использует те же веса, что и эмбеддинг страны пользователя.
  • Эмбеддинг источника траффика(affiliate id) 5 обучаемых параметров
  • Некоторое количество фичей, разработанных нами вручную, включая:
    Количество ночей между въездом и выездом в отель.
    Захватывает ли остановка в отеле выходные? (хороший признак для того, чтобы отличать командировки)
    Совпадает ли страна города с предыдущими со страной предыдущих городов в путешествии.
    День недели и неделя в году для даты въезда и выезда.
    Совпадает ли страна пользователя и страна города?
    Год въезда. 3 параметра, one-hot encoded. В датасете представлены путешествия только за 3 года.
    Месяц въезда. 12 параметров, one-hot encoded.

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


Для того, чтобы получить более качественное семантическое представление городов в путешествии, мы закодировали их используя один полносвязный слой нейросети размерности 115. Для того, чтобы учесть позицию города в путешествии, мы так же умножили этот вектор на эмбеддинг позиции. В оригинальной архитектуре Transformer, авторы используют фиксированные эмбеддинги, основанные на функциях $sin$ и $cos$ Вместо этого, мы использовали два обучаемых эмбеддинга, представляющих собой позицию города относительно начала и конца путешествия. Мы сконкатенировали эти эмбеддинги и пропустили их через один полносвязный слой для того, чтобы получить финальный эмбеддинг города в путешествии.



Transformer-like блок. По сравнению с оригинальным блоком transformer, мы заменили сложение на умножение в residual-связи


Для того, чтобы смоделировать взаимодействия городов друг с другом, мы воспользовались архитектурой Transformer. Оригинальный блок архитектуры Transformer состоит из одного Self-Attention слоя, Dense-слоя, Residual-связи и нормализации. На хабре уже неоднократно разбирали архитектуру Transformer, поэтому мы не будем описывать ее тут. Единственная разница нашего transformer-like блока с оригинальным, заключается в том, что для residual связи мы используем не сложение, а умножение. Наши эксперименты показали, что на данном датасете, умножение позволяет обучиться быстрее и позволяет достичь более высокого результата.


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


  1. Закодировали матрицу городов-кандидатов, используя один полносвязный слой нейросети.
  2. Полученные представления городов-кандидатов мы пропустили через один Transformer-like блок. Интуитивно города кандидаты не являются независимыми и, зная какие есть другие кандидаты, нейросети будет легче подобрать правильный скор для текущего города.
  3. При помощи одного слоя MultiHeadAttention смоделировали взаимодействия с предыдущими городами из путешествия. На выходе мы получили эмбеддинги городов-кандидатов в контексте текущего путешествия.

reference link
Архитектура нашей нейросети.


Финальный скор для каждого города-кандидата мы генерировали при помощи умножения (dot product) эмбеддинга города-кандидата на закодированное представление фичей последнего города. По условиям задачи, нам не известен последний город, но известные некоторые характеристики поездки в этот город, например время въезда и выезда и источник трафика. Мы закодировали эти параметры, используя те же самые процедуры, которые мы использовали для кодирования фичей городов в путешествии. Дополнительно, мы пропустили эти фичи через один полносзвязный слой нейросети, для того чтобы получить проекцию в то же пространство, в которое мы спроектировали эмбеддинги городов-кандидатов.


Обучаем модель


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


Мы взяли имплементацию метода из библиотеки lightgbm в качестве референсной и написали собственную имплементацию, которая подходит для обучения нейросетей. Как показали наши эксперименты, использование метода LambdaRANK позволило нам намного быстрее обучать нашу нейросеть ранжировать города, по сравнению с использованием бинароной кроссэнтропией. Кроме того, метрика NDCG, которую оптимизирует LambdaRANK показала практически идеальную корелляцию с метрикой Accuracy@4, поэтому в целом обучая модель получать хороший скор NDCG, мы автоматом получали хорошее значение Accuracy@4.


Для тренировки мы использовали оптимизатор Adam с ранней остановкой после 50 эпох без улучшения метрики Accuracy@40



Метрики Accuracy@4 и NDCG@40. Видно что метрики кореллируют практически идеально. Такой выскоий уровень корреляции подсказывает что оптимизируя одну метрику, мы автоматом оптимизируем и другую


Для имплементации нашей нейросети мы использовали библиотеки Tensorflow и Keras. Тренировка заняла примерно 7 часов на GPU Nvidia RTX 3090.


Оценка качества модели и результаты


Схема валидации нашей модели была основана на отдельных путешествиях. Так как с нашей точки зрения большой разницы между частями train и test датасета не было, мы объединили эти части и использовали обе из них как для тренировки модели, так и для оценки качества. Мы случайным образом отобрали 4000 путешествий в тестовое множество (наше тестовое, не путать с частью test датасета) и еще 4000 в валидационное. Остальные путешествия из обоих датасетов мы поместили в тренировочную выборку. На этапе валидации и тестирования модели мы использовали все города кроме последнего для того, чтобы предсказать один последний город в каждом путешествии (стратегия leave one out).


Метрики


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


  • Accuracy@4 Метрика использованная организаторами соревнования для оценки моделей.
  • NDCG@40 Метрика которую мы использовали в для оптимизации нашей нейросети. Как мы показали выше, данная метрика хорошо кореллирует с метрикой Accuracy@4.
  • Leaderboard По сути, это та же самая метрика Accuracy@4, измеренная организаторами соревнования на закрытой части датасета. По условиям соревнования, мы могли померять эту метрику лишь дважды: один раз для "предварительной" модели, и один раз для финальной модели.

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


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


  • GlobalTop, LastCityCountryTop, TransitionChain простые эвристические алгоритмы которые мы использовали для отбора кандидатов.
  • TruncatedSVD Популярный подход для решения задачи построения рекомендательных систем на основе матричного разложения. Мы использовали реализацию TruncatedSVD из библиотеки sklearn.
  • SelfAttention end-to-end версия нашей модели. Архитектура данной модели похожа на нашу финальную модель. Отличие в том, что мы рассчитываем скор для всех городов, а не для топ-500 кандидатов. Из-за этого модель была более тяжеловесной, и мы не могли использовать фичи, которые мы генерировали вручную (например похожесть на предыдущие города из путешествия). По сути, при помощи Transformer-like части нашей модели мы генерировали эмбеддинг путешествия, и сравнивали с выученными эмбеддингами городов. Эту модель мы использовали для промежуточной оценки результата.
  • LambdaMART Так как мы использовали подход LambdaRANK, мы хотели попробовать сравнить нашу имплементацию с классической реализацией данного подхода на деревьях принятия решений. Мы использовали имплементацию LambdaRANK из библиотеки lightgbm. Подход базировался тольк о на вручную сгенерированных фичах (описанных в секции "генерируем фичи для кандидатов" выше).

Результаты сравнения моделей представлены в следующей таблице:


Модель Accuracy@4 NDCG@40 Leaderboard
GlobalTop 0.058 0.091 -
TransitionChain 0.440 0.429 -
SelfAttention 0.509 0.491 0.514
LambdaMART 0.514 0.485 -
RerankingAttention 0.542 0.513 0.555

Заключение


Участие в конкурсе от booking.com показало, что комбинация listwise методов ранжирования и современных нейросетевых архитектур, которая позволила нам набрать высокий скор в соревновании. Тот же самый подход может быть использован вместе с другими архитектурами нейросетей для решения большого количества задач на ранжирование из области информационного поиска, включая рекомендательные и поисковые системы.


Ссылки:
[1] Burges CJ. From ranknet to lambdarank to lambdamart: An overview
[2] Vaswani A, Shazeer N, Parmar N, Uszkoreit J, Jones L, Gomez AN, Kaiser L, Polosukhin I. Attention is all you need. arXiv preprint arXiv:1706.03762. 2017 Jun 12.


(https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.180.634&rep=rep1&type=pdf). Learning. 2010 Jun 23;11(23-581):81.

Источник: habr.com
К списку статей
Опубликовано: 24.05.2021 18:21:26
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Машинное обучение

Рекомендательные системы

Deep learning

Обучение ранжированию

Нейросети

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru