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

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

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

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

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

Описание данных

Набор данных содержит следующую информацию и может быть загружен с Kaggle, и мы будем использовать train.csv.

Попробуем сделать названия столбцов более интуитивно понятными.

Предварительная обработка данных

import pandas as pd
df = pd.read_csv('train.csv')
df.shape

(9917530, 54)

Это большой набор данных с почти 10 миллионами наблюдений и 54 особенностями. Итак, я ищу способ сделать это более управляемым.

import matplotlib.pyplot as plt
n, bins, patches = plt.hist(df.prop_country_id, 100, density = 1, facecolor='blue', alpha=0.75)
plt.xlabel('Property country Id')
plt.title('Histogram of prop_country_id')
plt.show();

df.groupby('prop_country_id').size().nlargest(5)

n, bins, patches = plt.hist(df.visitor_location_country_id, 100, density = 1, facecolor='blue', alpha=0.75)
plt.xlabel('Visitor location country Id')
plt.title('Histogram of visitor_location_country_id')
plt.show();

df.groupby('visitor_location_country_id').size().nlargest(5)

Данные анонимны, поэтому определить точную страну или город, в который планирует поездку, невозможно. Однако очевидно, что самой большой страной (обозначенной 219) являются Соединенные Штаты. На самую большую страну приходится 61% всех наблюдений. Из них 58% поисков выполняют потребители, также проживающие в этой стране, что позволяет предположить, что страна имеет большую территорию, на которую приходится значительная часть внутренних поездок. Валюта цены также предполагала, что крупнейшей страной являются Соединенные Штаты.

Поэтому для повышения эффективности вычислений мы собираемся обучать независимые модели на посетителях из США. Этот метод значительно сокращает время на обучение.

us = df.loc[df['visitor_location_country_id'] == 219]
us = us.sample(frac=0.6, random_state=99)
del us['visitor_location_country_id']

Ограниченные вычислительной мощностью, мы случайным образом берем 60% набора данных США. Затем удалите столбец «visitor_location_country_id».

us.isnull().sum()

Как видите, по многим функциям у нас много недостающих данных. Мы собираемся отбросить функции, которые имеют более 90% значений NaN, а также «date_time», «srch_id» и «prop_id» и присвоить три функции, которые содержат менее 30% значения NaN, а именно: «prop_review_score» , «Prop_location_score2» и «orig_destination_distance».

cols_to_drop = ['date_time', 'visitor_hist_starrating', 'visitor_hist_adr_usd', 'srch_query_affinity_score', 'comp1_rate', 'comp1_inv', 'comp1_rate_percent_diff', 'comp2_rate_percent_diff', 'comp3_rate_percent_diff', 'comp4_rate_percent_diff', 'comp5_rate_percent_diff', 'comp6_rate_percent_diff', 'comp7_rate_percent_diff', 'comp8_rate_percent_diff', 'comp2_rate', 'comp3_rate', 'comp4_rate', 'comp5_rate', 'comp6_rate', 'comp7_rate', 'comp8_rate', 'comp2_inv', 'comp3_inv', 'comp4_inv', 'comp5_inv', 'comp6_inv', 'comp7_inv', 'comp8_inv', 'gross_bookings_usd', 'srch_id', 'prop_id']
us.drop(cols_to_drop, axis=1, inplace=True)

Внести «prop_review_score» в произвольном порядке

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

Вменять «prop_location_score2» в среднее значение

us['prop_location_score2'].fillna((us['prop_location_score2'].mean()), inplace=True)

Вписать «исходное_предел_дистанции» и медианное значение

us['orig_destination_distance'].fillna((us['orig_destination_distance'].median()), inplace=True)

Мы закончили вменение!

EDA

us.shape

(3467283, 22)

После базовой очистки данных наш набор данных для США содержит более 3,4 миллиона наблюдений и 22 особенности. Давайте изучим эти особенности.

Нажмите и закажите

Наша целевая переменная - «click_bool», а не «booking_bool». Потому что там, где есть бронирование, должен быть клик, и мы хотим оптимизировать клики.

import matplotlib.pyplot as plt
import seaborn as sns
sns.countplot(x='booking_bool',data=us, palette='hls')
plt.show();
us['booking_bool'].value_counts()

sns.countplot(x='click_bool',data=us, palette='hls')
plt.show();
us['click_bool'].value_counts()

Из-за особенностей туристического онлайн-бизнеса, как уровень бронирования (2,8%), так и рейтинг кликов (4,3%) чрезвычайно низки, количество показов без кликов подавляющее, класс очень несбалансирован.

Продолжительность поиска

n, bins, patches = plt.hist(us.srch_length_of_stay, 50, density = 1, facecolor='blue', alpha=0.75)
plt.xlabel('Search length of stay')
plt.title('Histogram of search_length_of_stay')
plt.axis([0, 30, 0, 0.65])
plt.show();

us.groupby('srch_length_of_stay').size().nlargest(5)

Больше всего ищут length_of_stay: 1 день, затем 2, 3,… Ничего особенного.

Количество взрослых в поиске

n, bins, patches = plt.hist(us.srch_adults_count, 20, density = 1, facecolor='blue', alpha=0.75)
plt.xlabel('Search adults count')
plt.title('Histogram of search_adults_count')
plt.show();

df.groupby('srch_adults_count').size().nlargest(5)

Чаще всего при поиске взрослых учитывается 2 взрослых, затем 1 взрослый, имеет смысл.

Рейтинг объекта в звездах

n, bins, patches = plt.hist(us.prop_starrating, 20, density = 1, facecolor='blue', alpha=0.75)
plt.xlabel('Property star rating')
plt.title('Histogram of prop_star_rating')
plt.show();

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

Собственность - это бренд или нет

us.groupby('prop_brand_bool').size()

Более 73% объектов являются объектами брендов. Это имеет смысл, поскольку мы говорим об отелях США и путешественниках из США.

Остаться в субботу или нет

us.groupby('srch_saturday_night_bool').size()

Цена в долларах

sns.set(style="ticks", palette="pastel")
ax = sns.boxplot(x="click_bool", y="price_usd", hue="click_bool", data=us)
ax.set_ylim([0, 200]);

us.groupby('click_bool')['price_usd'].describe()

В среднем цена price_usd, на которую был получен клик, всегда ниже, чем у не полученного клика.

Балансировка классов

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

Модельное обучение и оценка

Прогнозирование кликов с помощью ансамблевых моделей

Прогнозирование кликов с помощью наивных байесовских моделей

Прогнозирование кликов с помощью нейронной сети

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

Блокнот Jupyter можно найти на Github. Хороших выходных!