Собесов

khangich (Booking): автоматическое определение good value deals

ML / Data ScienceML System DesignСложнаяSenior

Условие

В Booking.com появилась задача: автоматически предлагать пользователям «хорошие сделки» (good value deals) среди отелей, в том числе тех, у которых ещё нет рейтинга. Как вы её решите?

Решение

Подход

Это композитная задача: (а) оценить «истинную цену» отеля и (б) сравнить фактическую цену с ожидаемой. Хорошая сделка = большая отрицательная разница.

1. Целевая переменная.

  • Прокси для качества — медианная цена «похожих» отелей на ту же дату/город/тип номера.
  • Альтернативно — историческая средняя цена этого же отеля (для отелей без рейтинга — мало данных).

2. Признаки отеля.

  • Локация (район, расстояние до достопримечательностей).
  • Тип (отель/апартаменты/хостел), число звёзд, количество номеров.
  • Удобства (Wi-Fi, завтрак, бассейн) — sparse one-hot.
  • Внешние сигналы: средние цены конкурентов на эту ночь, сезонность, события в городе.

3. Модель «ожидаемой цены».

  • Бейзлайн: median price по схожим отелям (collaborative filtering для cold start).
  • Основная: GBM (LightGBM/XGBoost) с регрессией на log(price).
  • Эмбеддинги соседних отелей + цена через kNN — хорошо работает для cold start.

4. Метрика хорошей сделки.

  • deal_score = (expected_price − actual_price) / expected_price.
  • Порог: deal_score > 15% → «good value». Подбирается по бизнесу.

5. Cold start (нет рейтинга, мало данных).

  • Берём ближайших соседей по локации и типу.
  • Используем content-based признаки, а не history.
  • Маркируем как «new property — рейтинг скоро появится» — пользователи реагируют лучше, чем на «безрейтинговый» без объяснений.

6. Валидация.

  • Offline: MAE / MAPE по предсказанной цене на отложенной по времени выборке.
  • Online: A/B-тест по бизнес-метрикам (CTR на deal-плашку, конверсия в бронь, контрибуция этих бронирований в общую выручку).
# Эскиз бейзлайна
import lightgbm as lgb
import numpy as np
 
# y = log(price), чтобы регрессия не страдала от длинного хвоста
model = lgb.LGBMRegressor(objective="regression", n_estimators=500)
model.fit(X_train, np.log1p(y_train), eval_set=[(X_val, np.log1p(y_val))])
 
expected = np.expm1(model.predict(X))
deal_score = (expected - actual_price) / expected
is_good_deal = deal_score > 0.15

Подводные камни

  1. Adverse selection. Если ожидаемая цена строится на конверсиях, отель с занижением может попадать в «good deals» снова и снова — модель закрепляет смещение. Регуляризуйте: ограничивайте долю «deals» в выдаче.
  2. Двойной счёт сезонности. Сравнивать с медианой города на ту же ночь — обязательно; иначе все летние Анапы будут «плохими сделками» к зимним.
  3. Налоги и комиссии. «Цена» бывает разная: со сборами, без, с завтраком. Сравниваем comparable.
  4. Cold start без content-features ломается. Для новых отелей крайне важно завести признаки локации/типа в первый же день.

Эталонный ответ

Регрессия на «ожидаемую цену» по похожим отелям/датам; deal_score = (expected − actual) / expected; порог настраивается A/B-тестом. Для отелей без истории — content-based / kNN-cold-start.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти