Вступление

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

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

Данные

В оставшейся части этого поста мы рассмотрим реальный инвентарь отелей в Нью-Йорке на период с 7 по 10 января 2019 г. И данные, и прилагаемый блокнот Jupyter можно найти здесь.

Давайте начнем с изучения данных инвентаризации отеля, загрузив их как фрейм данных Pandas и выполнив .describe() на нем.

Несколько замечаний по поводу данных:

  • названия отелей были удалены и заменены полем hotel_id, которое представляет собой целое число, присвоенное каждой строке данных
  • поле avg_rate - это минимальная средняя стоимость номера за ночь, возвращаемая отелем за время пребывания; то есть, если в отеле доступно несколько тарифов на номера, это минимальная цена.
  • поле distance указано в милях и представляет собой расстояние до места поиска, выполняемого для получения этих данных, которое находится в центре Манхэттена.
  • поля star_rating и user_rating основаны на шкале от 0 до 5 с шагом 0,5 звезды и являются мерой удобства и качества отеля.

Затем давайте проведем базовый исследовательский анализ данных, чтобы визуально изучить данные. На рисунке 1 показаны гистограммы соответствующих полей - они кажутся нормально распределенными или слегка логарифмически нормальными, что хорошо. На рисунке 2 показано, что avg_rate увеличивается по мере увеличения star_rating и user_rating - вы получаете то, за что платите!

Контентное подобие

Следующий шаг - определить, как вы хотите выводить на поверхность рекомендации, основанные на содержании. Простым подходом в этом примере является встраивание содержимого инвентаря отеля в N-мерное пространство и поиск предметов, которые наиболее похожи друг на друга. В этом случае давайте построим пространство функций как [lat, lng, avg_rate, star_rating, user_rating], поэтому пространство функций для hotel_id = 10 будет [40.714995, -74.015777, 326.4, 5, 4.5].

Теперь предположим, что клиент нажимает на конкретный отель и хочет узнать больше о типах номеров / ценах, удобствах и местоположении. Пока они изучают этот отель, мы можем представить клиенту похожие отели, чтобы они могли учесть их при совершении покупок. В Upside мы используем карусель из 4 вариантов на странице тарифов на отели, чтобы предоставить рекомендации. Это позволяет клиентам просматривать похожие варианты отелей, которые у них, возможно, не было бы возможности изучить в противном случае.

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

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

Ниже приведен код для сортировки отелей по similarity_distance привязке hotel_id.

import pandas as pd
from sklearn.metrics import euclidean_distances
from sklearn.preprocessing import StandardScaler
def get_hotel_recommendations(df: pd.DataFrame, anchor_id: int) -> pd.DataFrame:
# features used to compute the similarity
features = ['lat', 'lng', 'avg_rate', 'star_rating', 'user_rating']
# create the features - make the anchor be the first row in the dataframe
df_sorted = df.copy()
df_sorted = pd.concat([df_sorted[df_sorted['hotel_id'] == anchor_id],
df_sorted[df_sorted['hotel_id'] != anchor_id]])
df_features = df_sorted[features].copy()
df_features = normalize_features(df_features)
# compute the distances
X = df_features.values
Y = df_features.values[0].reshape(1, -1)
distances = euclidean_distances(X, Y)
df_sorted['similarity_distance'] = distances
return df_sorted.sort_values('similarity_distance').reset_index(drop=True)
def normalize_features(df: pd.DataFrame) -> pd.DataFrame:
df_norm = df.copy()
for col in df_norm.columns:
# fill any NaN's with the mean
df_norm[col] = df_norm[col].fillna(df_norm[col].mean())
df_norm[col] = StandardScaler().fit_transform(df_norm[col].values.reshape(-1, 1))
return df_norm

Здесь следует отметить несколько моментов:

  • Нормализация пространства функций очень важна, поскольку мы имеем дело с функциями, имеющими разные единицы измерения. Выбор схемы нормализации зависит от проблемы и данных - в этом случае мы используем стандартный метод оценки, потому что данные обычно распределяются, - но также может быть применено масштабирование min / max или TF-IDF (для сравнения документов). полезно для других приложений.
  • Выбор правильного расстояния или показателя сходства может иметь большое влияние на качество рекомендаций. Мы используем евклидово расстояние, потому что встраиваем географические координаты в наше пространство признаков. Использование косинусного сходства вместо этого было бы огромной проблемой для отелей с одинаковым заголовком, которые находятся далеко друг от друга, потому что они имеют одинаковый угол в пространстве [lat, lng] (привет Гилберту Уотсону за указание на это). Другие типы оценок сходства можно исследовать с помощью удобной функции Scipy cdist.
  • Важно протестировать алгоритм рекомендаций на исторических данных, чтобы выбрать лучшую схему нормализации и оценку сходства, а также настроить любые другие параметры. Например, просмотр недавних поисков и покупок клиентов и использование коэффициента отзыва / точности при k может помочь настроить гиперпараметры, чтобы найти оптимальную конфигурацию алгоритма.

Посмотрим, что произойдет, когда мы сделаем якорь hotel_id = 10:

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

Затем давайте рассмотрим второй пример, в котором мы установили привязку hotel_id = 21, что намного дешевле и имеет более низкие рейтинги, чем hotel_id = 10:

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

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

Персональные рекомендации

Основываясь на алгоритме подобия на основе контента, мы можем расширить эту методологию для создания индивидуальных рекомендаций для каждого клиента. Вместо того, чтобы использовать отель, на который кликнул клиент, в качестве привязки и находить похожие отели, мы можем использовать историю покупок для вычисления нормализованных характеристик для каждого клиента. Персонализированный якорь становится «виртуальной гостиницей», которую мы используем для сортировки инвентаря на новых рынках на основе показателя сходства. Это похоже на модель look-a-like, когда мы стараемся рекомендовать новые отели, наиболее похожие на те, что клиенты покупали ранее.

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

from typing import Dict
import pandas as pd
from sklearn.metrics import euclidean_distances
def get_personalized_hotel_recommendations(df: pd.DataFrame, user_features: Dict) -> pd.DataFrame:
# features used to compute the similarity
features = ['distance', 'avg_rate', 'star_rating', 'user_rating']
# create the features
df_features = df[features].copy()
df_features = normalize_features(df_features)
# artifically set the distance of the user anchor to be the min of the
# normalized distance
user_features['distance'] = df_features['distance'].min()
df_user = pd.DataFrame([user_features])
df_features = pd.concat([df_user, df_features], sort=False)
# compute the distances
X = df_features.values
Y = df_features.values[0].reshape(1, -1)
distances = euclidean_distances(X, Y)
df_sorted = df.copy()
df_sorted['similarity_distance'] = distances[1:]
return df_sorted.sort_values('similarity_distance').reset_index(drop=True)

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

Давайте посмотрим на индивидуальные рекомендации по отелям для 2 разных клиентов. У первого покупателя все функции равны 0 (точно в среднем), в то время как у второго покупателя все функции равны 1 (выше среднего). Как видно из двух таблиц, представленных ниже, виды очень разные, и мы показываем второму покупателю более дорогие и качественные отели. Это ожидаемо, потому что второй клиент продемонстрировал историческую склонность к покупке отелей с ценами и рейтингами выше среднего для населения.

Резюме

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

Надеюсь, это руководство вдохновит всех нас, помимо кнопок «палец вверх» / «палец вниз» (но мы с радостью возьмем ваши аплодисменты).

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