Агрегации заказов по пользователям и категориям
На собеседовании дают две таблицы: заказы и товары в заказах.
Нужно реализовать функцию build_order_features(orders, items), которая вернет словарь с тремя блоками:
category_spend: сколько денег каждый пользователь потратил в каждой категории;order_user_total_spend: для каждого заказа суммарные траты этого пользователя по всем его заказам;weekday_spend: для каждого пользователя вектор из 7 чисел, где индекс 0 соответствует понедельнику, а индекс 6 - воскресенью.
Цена строки считается как price * amount. Если в заказе несколько товаров одной категории, их нужно просуммировать.
Решение прямо на странице
Напишите код, запустите проверки и только потом открывайте разбор.
Нажмите «Запустить проверки» или Ctrl+Enter.
Показать разбор
Идея решения
Сначала джойним строки товаров с заказами через order_id, считаем стоимость строки как price * amount, затем делаем три независимые агрегации: по пользователю и категории, по суммарным тратам пользователя и по дню недели. В pandas это были бы merge, groupby, transform и pivot_table, но в тренажере задача оставлена как чистый Python, чтобы тесты были детерминированными.
Эталонный код
from collections import defaultdict
from datetime import datetime
def build_order_features(orders, items):
order_by_id = {order["order_id"]: order for order in orders}
order_spend = defaultdict(int)
category_spend = defaultdict(lambda: defaultdict(int))
weekday_spend = defaultdict(lambda: [0] * 7)
for item in items:
order = order_by_id[item["order_id"]]
user_id = order["user_id"]
value = item["price"] * item["amount"]
order_spend[item["order_id"]] += value
category_spend[user_id][item["category"]] += value
weekday = datetime.fromisoformat(order["created_at"]).weekday()
weekday_spend[user_id][weekday] += value
user_total = defaultdict(int)
for order_id, value in order_spend.items():
user_total[order_by_id[order_id]["user_id"]] += value
return {
"category_spend": {user: dict(values) for user, values in category_spend.items()},
"order_user_total_spend": {
str(order["order_id"]): user_total[order["user_id"]]
for order in orders
},
"weekday_spend": dict(weekday_spend),
}
Как поставить задачу предсказания возврата пользователя
Бизнес хочет понимать, вернется ли пользователь и стоит ли давать ему скидку. Как сформулировать ML-задачу, таргет и признаки?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Сначала фиксируем действие и горизонт: например, купит ли пользователь в следующие 7 дней. Затем строим point-in-time датасет без заглядывания в будущее: история активности, категории, частоты, давность действий, прошлые покупки и доступные пользовательские признаки.
Подробный разбор
Нужно начать не с модели, а с формулировки решения. Если бизнес хочет вернуть пользователя скидкой, таргетом может быть вероятность покупки или возвращения в ближайшую неделю после даты скоринга. Важно заранее договориться, когда модель запускается: раз в день, раз в неделю или перед конкретной коммуникацией.
Датасет собирается point-in-time: признаки берутся только из прошлого относительно даты скоринга, а таргет - из будущего окна. Признаки: давность последнего визита, число сессий, покупки и просмотры по категориям, средний чек, реакция на прошлые промо, сегмент пользователя, устройство, регион, сезонность. Базовая модель - бустинг или логистическая регрессия, дальше уже можно усложнять.
Главный риск - leakage. Нельзя брать события из той же недели, которую модель должна предсказывать, и нельзя обучаться на данных, которые в проде к моменту скоринга еще неизвестны.
Типичные ошибки
- Сразу выбирать модель, не зафиксировав окно предсказания.
- Смешивать признаки из будущего с историческими признаками.
- Оптимизировать абстрактную accuracy без учета стоимости скидки.
Как выбрать признаки и метрики для модели возврата
После первой модели нужно понять, какие признаки оставить и стала ли модель лучше. Какие offline-метрики и проверки использовать?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Сначала смотрим качество на временной валидации, затем feature importance/SHAP и slice-метрики. Для скидок обычно важны precision при заданном recall или recall при ограниченном FPR, потому что false positive тратит деньги, а false negative теряет пользователя.
Подробный разбор
Для такой задачи обычная accuracy часто бесполезна: классы могут быть несбалансированы, а ошибки стоят по-разному. Если скидка дорогая, нужен высокий precision среди тех, кому ее дадим. Если потеря пользователя дороже лишней скидки, можно фиксировать допустимый FPR и максимизировать recall.
Валидация должна быть временной: обучаемся на прошлом, проверяемся на более свежем периоде. После этого можно смотреть permutation importance, встроенную важность бустинга и SHAP, но не как абсолютную истину, а как инструмент отладки. Удалять признаки стоит осторожно: сначала проверить стабильность по периодам и сегментам, потом сравнить метрику до/после.
Дополнительно нужны slice checks: новые/старые пользователи, категории, регионы, промо-сегменты. Модель может выглядеть хорошо в среднем и ломаться на важном бизнес-сегменте.
Типичные ошибки
- Сравнивать модели на случайном split, где будущее перемешано с прошлым.
- Выкидывать признаки только потому, что SHAP маленький на одном запуске.
- Забывать про стоимость false positive при промо-скидках.
Как встроить модель возврата в продукт
Модель уже умеет предсказывать вероятность возврата. Как ее применить в продукте и где хранить признаки?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Если решение принимается раз в день или неделю, проще batch scoring: пересчитать признаки, посчитать score, записать аудиторию для CRM/push-сервиса. Online endpoint нужен только если решение зависит от свежего действия пользователя.
Подробный разбор
Нужно отделить две вещи: где считается score и где принимается бизнес-действие. Для недельной кампании достаточно batch-пайплайна: обновить признаки, применить модель, выбрать пользователей выше порога и передать список в сервис коммуникаций. Такой путь дешевле, проще мониторится и не добавляет latency в пользовательский запрос.
Признаки лучше держать рядом с ML-пайплайном или в feature store, где есть offline/online консистентность. Если push-сервису нужен только финальный флаг или score, не надо гонять через сеть большой feature vector. Если же решение должно реагировать на свежую сессию, нужен online store с TTL и endpoint, который быстро достает последние признаки.
После запуска обязательно нужен A/B тест: не только uplift по покупкам, но и стоимость скидок, отписки, жалобы, fatigue от коммуникаций и стабильность latency/ошибок пайплайна.
Типичные ошибки
- Делать online endpoint там, где достаточно разового batch scoring.
- Передавать тяжелые признаки между сервисами вместо финального score.
- Не считать стоимость промо как часть online-метрики.
Как ускорять тяжелую модель рекомендаций в рантайме
Есть трансформерная модель рекомендаций по истории пользователя. Как сделать так, чтобы она не ломала online-сервис?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Кэшировать item embeddings и последний user state, пересчитывать только изменившуюся часть истории, отделить генерацию кандидатов от реранжирования, использовать batching/quantization и держать fallback на популярное или старый top-K.
Подробный разбор
Сначала нужно понять, что именно должно быть online. Item embeddings почти всегда можно считать заранее: каталог меняется медленнее пользовательских событий. User representation можно обновлять по событию, по TTL или при входе в секцию, но не пересчитывать всю длинную историю при каждом запросе.
Практичная схема: генерация кандидатов через ANN/векторный индекс или готовые эвристики, затем легкий реранкер по ограниченному числу кандидатов. Для heavy transformer стоит хранить последний user state или хотя бы последние top-K, инвалидировать его при новых важных событиях и пересчитывать асинхронно. Если нужен sync endpoint, он должен иметь жесткий timeout и fallback.
Инференс ускоряют batching, quantization FP16/INT8, ONNX/TensorRT/Triton, precomputed item vectors, cache hit rate monitoring и autoscaling. Но оптимизация не заменяет продуктовый контракт: сколько freshness реально нужно и что показываем, если модель не успела ответить.
Типичные ошибки
- Каждый раз прогонять всю историю пользователя через трансформер.
- Не иметь fallback на случай timeout или пустых рекомендаций.
- Смешивать генерацию кандидатов и тяжелое реранжирование в один дорогой online-запрос.