- PVSM.RU - https://www.pvsm.ru -
Всем привет! Меня зовут Мельников Виктор, я работаю Junior Data Scientist в хабе Розничного Бизнеса Департамента Продвинутой Аналитики в Альфа-Банке. В этой статье я расскажу про AUF — Open Source библиотеку Альфа-Банка.
Её главная задача — автоматическое решение задач uplift-моделирования.
Позволяет ускорять разработку в десятки раз и убирает рутину, избавляя от привычного fit-predict. Приятным бонусом идёт полный отчёт по качеству модели, понятный как DS, так и бизнесу.
Дисклеймер: эта статья не про uplift-моделирование, а про библиотеку, которая решает задачу uplift-моделирования. Если хотите почитать про общую постановку задачи, посмотреть на математику, лежащую в основе uplift, в конце статьи оставил раздел с ссылками на полезные материалы.
Введение
Как обычно решают uplift-задачи?
Преимущества AUF
Как использовать AUF?
Скачиваем данные.
Установка библиотеки.
Класс UpliftPipeline.
Загрузка выборки.
Метод run().
Отбор признаков.
Обучение моделей.
Поиск лучшей модели.
Визуализация качества модели.
Интерпретация модели.
Источники и ссылки.
Благодарности.
Рассмотрим на примере молока в любом продуктовом магазине. Допустим, у него подходит срок годности и на него дают скидку. Купили бы вы это молоко, если бы один литр молока подешевел на 5 рублей? А если на 10? А если бы оно подешевело в 2 раза?
Если с последним почти, наверное, многие согласились бы (конечно, если срок годности ещё только подходит, а не машет рукой вслед), то с первыми двумя вопросами всё не так просто.
Другой пример: что, если клиент интересуется некоторым продуктом компании (например, подпиской Альфа-Смарт), но почему-то её не подключает? В таком случае можно отправить ему коммуникацию в виде СМС или push-уведомления с описанием преимуществ, которые он получит от подписки. Возможно, после такого кто-то соберётся с духом и оформит её.
Подобные задачи есть и в ритейле, и в телекоме, и даже в медицине. В них нужно оценить изменение поведения клиента после некоторого воздействия на него. Бизнес таким образом сможет повысить конверсию в продажи через более эффективные маркетинговые кампании. Такие задачи решаются методами uplift-моделирования, которые и реализованы в нашей Python-библиотеке AUF.
Data Scientist, не знакомый с uplift-моделированием, чтобы не изобретать велосипед с нуля, сначала пойдёт гуглить существующие решения.
Так он, скорее всего, найдет библиотеки:
scikit-uplift [1],
causalml [2],
EconML [3],
Pylift [4].
Каждый из инструментов содержит строительные блоки, из которых можно собрать итоговое решение. Однако от DS потребуется собрать это всё воедино и при этом не посадить багу в код.
Отсюда возникла идея автоматизировать процесс создания uplift-моделей. Так появилась библиотека AUF — Automatic Uplift Framework.
AUF означает Automatic Uplift Framework — то есть AUF берёт на себя всю рутину по обучению моделей. Просто загрузи выборку в пайплайн, и на выходе тебя будет ждать шикарная модель (прям как с обложки журнала)!
Структура библиотеки для ознакомления.
Если по-серьёзному, то обычно на разработку всего одной uplift-модели уходит порядка 1-2 недель. Но часто срок растягивается из-за необходимости проверить новые гипотезы бизнеса. С помощью AUF каждую такую итерацию можно ускорить с пары недель до нескольких часов. Ускорение в десятки раз на лицо!
В этом разделе мы рассмотрим использование AUF на примере реальных данных из соревнования Мегафон.
Для этого нам понадобится модуль datasets из библиотеки sklift (полное название scikit-uplift). В нём содержатся датасеты основных соревнований по uplift.
import pandas as pd
from sklift import datasets
data, target, treatment = datasets.fetch_megafon(
download_if_missing=True,
return_X_y_t=True
)
df = pd.concat([data, target, treatment], axis=1)
# зададим явно колонку с айдишником наблюдения для работы паплайна
df["id"] = list(range(df.shape[0]))
print(df.shape)
df.head(2)
В результате выполнения данного кода выводятся размеры таблицы с данными и её две первые строки.
Обратим внимание на названия последних колонок:
Таргет называется «conversion», а тритмент – «treatment_group» (что это такое, описано ниже в разделе «загрузка выборки»). Теперь, когда выборка скачана к нам в окружение, можно приступать к установке AUF.
Всё почти так же, как с привычными нам библиотеками. Но перед установкой библиотеки нужно установить Matplotlib версии 3.5.3 и перезапустить ядро для корректной отрисовки графиков.
Команды для начала работы с AUF:
(при запуске ноутбука на kaggle нужно переустановить matplotlib)
pip uninstall matplotlib -y
pip install matplotlib==3.5.3
(перезапустить ядро, restart kernel)
pip install auf
Это ядро всей библиотеки. Вся работа на 99% ведётся именно через методы данного класса.
Из названия понятно, что этот класс имеет методы для всех основных шагов любого пайплайна машинного обучения: загрузки выборки, отбора признаков, обучения моделей и визуализации качества. Далее мы рассмотрим краткие примеры использования этих методов.
Создать экземпляр данного класса можно с помощью простого кода:
from auf.pipeline import UpliftPipeline
pipeline = UpliftPipeline(print_doc=False, task_name_mlflow=None, run_id=None)
Аргумент print_doc
отвечает за печать документации класса в выводе ячейки (False означает, что мы решили её пока не печатать). Аргументы task_name_mlflow
и run_id
нужны только при использовании mlflow. То есть при желании вы можете логировать все промежуточные результаты выполнения пайплайна в MLflow. Это даёт возможность спустя время вернуться к какому-то эксперименту и сравнить разные версии моделей.
Здесь мы считаем, что у вас уже есть готовая выборка в виде pandas.DataFrame с айдишниками, таргетом, тритментом и всеми доступными фичами:
Айдишник (id) — уникальный идентификатор записи/наблюдения.
Таргет (target) — бинарная целевая переменная.
Тритмент (treatment) — группа воздействия на клиента.
Фичи — признаковое описание клиента.
Можно использовать унифицированные названия для колонок с таргетом, тритментом и айдишником: target, treatment, id. Аналогично и с названиями групп воздействия на клиента: 0 — контрольная, 1 — целевая. Но при надобности можете передать свои в виде двух словариков (см. пример ниже).
Также есть возможность задать колонку со своим разбиением на train/val/test выборки (унифицированное название колонки — segm) в вашем датафрейме с данными или отдать это на откуп AUF.
base_cols_mapper = {
“id”: “id”,
“treatment”: “treatment_group”,
“target”: “conversion”,
“segm”: “segm”
}
treatment_groups_mapper = {
“control”: 0,
“treatment”: 1
}
pipeline.load_sample(
df,
base_cols_mapper,
treatment_groups_mapper
)
В выводе ячейки с этим кодом увидим:
№1. Результаты проверки гипотезы об отсутствии эффекта от воздействия (то есть о равенстве конверсий в целевой и контрольной группах).
№2. Табличку со средними конверсиями в целевой и контрольной группах в разрезе train/val/test выборок.
Видно, что конверсия в целевой группе статистически значимо выше конверсии в контроле. Это подтверждается околонулевым p-value (для наглядности мы выводим всегда только первые три цифры после запятой). Также видно, что «единичек» (таргетов, равных 1) достаточно много в каждой группе, это позволит построить более точную модель.
У класса UpliftPipeline
есть метод, в котором соединены все промежуточные шаги, кроме load_sample
. Это метод run()
, который позволит всего в одну строку получить готовую модель. Все параметры запуска можно указать прямо в аргументах метода run()
. При желании их можно выставить самим, либо использовать наши дефолтные значения.
В случае, если выборка содержит лишь колонки с унифицированными именами и фичами, получить модель и признаки можно совсем просто:
from auf.pipline import UpliftPipeline
pipeline = UpliftPipeline(print_doc=False)
pipeline.load_sample(df)
model, features, model_result = pipeline.run()
Вывод данной ячейки будет состоять из выводов промежуточных методов, которые мы рассмотрим по отдельности прямо сейчас. Так будет проще понять, что происходит внутри нашего пайплайна.
Отбор признаков мы разделили на 4 главных этапа:
Удаление «пустых» признаков.
Очистка выборки от «ликов».
Отбор «кандидатов» в признаки для uplift-модели.
Ранжирование кандидатов различными способами.
У каждого этапа своя цель:
Удалить признаки со всеми пропусками и 1 уникальным значением.
Удалить признаки, которые нарушают гомогенность выборки.
Ускорить обучение модели и убрать шум из данных.
Отобрать разными способами ТОП признаков для обучения моделей.
Рассмотрим этап 2 подробнее, поскольку он очень важен.
Гомогенность выборки — это свойство, которое необходимо для построения uplift-модели. Оно означает, что назначение воздействия было рандомным (случайным). В таких данных нет информации, которая позволила бы определить группу, к которой был отнесён клиент (целевая или контрольная). Это позволит при моделировании оценить чистый эффект от нашего конкретного воздействия (например, скидки или звонка/push/смс).
На практике эту проверку можно совершить так: взять бинарный флаг воздействия и попробовать его предсказать для каждого клиента на основе наших данных. Использовать можно любые классические модели для бинарной классификации, но мы обычно используем CatBoost.
Также мы модифицировали этот подход: вместо обучения большой модели один раз мы обучаем несколько раз модели поменьше. Обучив одну модель, мы выкидываем ТОП-1 фичу по важности и повторяем процедуру, пока можем предсказать тритмент с качеством выше порога. Так мы находим потенциальные «лики» тритмента.
Теперь посмотрим, как это можно сделать через методы класса UpliftPipeline
. Первый этап вшит в метод для загрузки выборки из-за его очевидной необходимости. Этапы 2-4 с дефолтными настройками можно запустить так:
pipeline.check_treatment_leaks()
pipeline.preselect_features_candidates()
pipeline.rank_features_candidates()
Метод check_treatment_leaks
выводит информацию о найденных «ликах». Он показывает их число, а также для ТОП-5 «ликов» он выводит ROC-AUC при предсказании тритмента с использованием данной фичи. Важно, что под капотом для предсказания тритмента используется небольшая моделька (бустинг из 10–20 деревьев глубины 2–3). И если рядом с фичей указан ROC-AUC 0,8, то это значит, что к этой фиче нужно особо внимательно присмотреться. Возможно, это «лик».
Но в нашем примере с соревнования Магафон такой проблемы не оказалось, повезло!
Они нужны для:
проверки пропусков и отсечения слишком редких значений категориальных переменных;
проверки корреляции у оставшихся признаков;
вывода числа признаков, удалённых на каждом этапе;
вывода признаков, удалённых по конкретной причине;
анализа наиболее «ликованных» признаков;
кастомного удаления признаков при необходимости (например, из-за бизнес-логики).
pipeline.check_feature_values()
pipeline.check_correlated_features()
pipeline.show_removed_features_with_reasons()
pipeline.get_removed_features_by_reason(“too correlated”)
pipeline.plot_treatment_leaks(top_k=5)
pipeline.remove_features(features=[“feature1”, “feature2”])
В нашей библиотеке доступны все основные типы uplift-моделей:
S-learner.
T-learner.
X-learner.
Uplift Tree.
Uplift Random Forest.
После ранжирования «кандидатов» каждый тип моделей будет обучен на разных количествах топ-признаков по каждому способу ранжирования. При этом структура модели (её гиперпараметры) будут подобраны для каждого набора признаков и класса модели через Optuna. В качестве метрики для оптимизации можно выбрать метрику из списка доступных в нашей библиотеке. О метриках речь подробнее пойдёт ниже.
Пример кода для обучения моделей:
from sklift.metrics import qini_auc_score
pipeline.train_models(
classes=["TwoModels"], # классы моделей для обучения
feature_nums=[5, 15], # количества ТОП-признаков для обучения моделей
use_default_params=False, # использовать ли дефолтные параметры моделей
metric=qini_auc_score, # метрика для оптимизации в Optuna
timeout_estimator=10 # время обучения 1 модели в секундах
)
Вывод данного метода выглядит на данный момент довольно просто: для каждой модели указывается статус «начало обучения» и «конец обучения».
После обучения моделей у нас есть множество троек вида (модель, метод ранжирования признаков, число ТОП-признаков). Далее нужно найти лучшую из них — ту, которая в среднем даёт лучшее качество. Для поиска лучшей модели используется оценка медианного значения оптимизируемой в Optuna метрики. Такую оценку легко получить через бутстрап. После этого выбирается тройка с наибольшим медианным значением метрики.
Пример кода для поиска лучшей модели:
model_class_name, ranker_method, model_result = pipeline.get_result(
metric = qini_auc_score,
n_max_features = 15,
rating = 0
)
Разберём возвращаемые значения.
Первые два поля означают название класса лучшей модели и название метода ранжирования фич из лучшей модели соответственно.
Также возвращается model_result
— специальная структура, в которой хранится вся информация про модель. В ней хранится сама модель, а также нужные ей признаки и некоторые служебные поля.
Обычно даже для простой модели бинарной классификации недостаточно посмотреть на одну метрику или один график. Например, классические метрики, такие как ROC-AUC, Precision или Recall не покажут всей картины. Нужно смотреть на графики по типу ROC-кривой или PR-кривой. Из них можно извлечь заметно больше информации.
Оценить качество uplift-модели ещё сложнее.
Поэтому в нашей библиотеке мы реализовали не только основные метрики и графики качества uplift-моделей. Также мы включили графики анализа важностей признаков и некоторые кастомные метрики со своей интуицией. Полный перечень метрик представлен в модуле auf.metrics.
Здесь нам понадобятся два метода.
Первый — show_metrics_table
для вычисления датафрейма с метриками по всем обученным моделям.
Второй – plot_results
для вывода краткой статистики по лучшей модели и отрисовки графиков.
Пример кода:
full_metrics_df = pipeline.show_metrics_table(
metrics_names=[
"uplift@10",
"uplift_rel@10",
"uplift@15",
"uplift_rel@15",
"uplift@20",
"uplift_rel@20",
"qini_auc",
"qini_auc_20"
]
)
self.plot_results(
full_metrics_df,
model_class_name,
ranker_method,
best_result,
n_uplift_bins=10
)
Это, пожалуй, самый информативный метод класса UpliftPipeline
. Он позволяет под разными углами взглянуть на качество получившейся модели. Рассмотрим его вывод ниже.
Сначала идёт табличка с результатами лучшей модели, которые показаны в виде основных, на наш взгляд, метрик. Это даёт возможность сразу понять, может ли модель принести какой-то эффект. Значения метрик показаны по train/val/test выборкам, чтобы можно было оценить степень переобучения.
Далее идёт другая табличка. В ней показана информация по бакетам предсказанного uplift в порядке убывания (в бакете 0-10 клиенты с самым высоким uplift). Какие есть поля в таблице:
percentile — бакет предсказанного uplift;
n_treatment — число клиентов из целевой группы в данном бакете;
n_control — число клиентов из контрольной группы в данном бакете;
response_rate_treatment — средняя реальная конверсия у клиентов из целевой группы в данном бакете;
response_rate_control — средняя реальная конверсия у клиентов из контрольной группы в данном бакете;
uplift — разница response_rate_treatment и response_rate_control;
rel_uplift = response_rate_treatment / response_rate_control - 1.
Поля uplift и uplift_rel показывают разницу конверсий в каждом бакете в целевой и контрольной группах. Первое поле показывают абсолютную разницу (простая разность конверсий). Второе поле показывает относительную разницу конверсий (делим конверсию в целевой группе на конверсию в контрольной и вычитаем 1). Это позволяет понять, сколько реально дополнительных продаж мы совершили, которые бы не получилось совершить без воздействия.
Частая проблема моделей склонности в том, что uplift_rel у них низкий, хотя uplift может быть высоким. Именно поэтому они не подходят для решения uplift-задач: например, мы так можем дать скидку тем клиентам, кто купил бы наш продукт и без неё.
Далее идут графики качества для train/val/test выборок. Они идут парами — слева диаграмма uplift_by_percentile, а справа qini кривая. Первый график показывает ту же информацию по бакетам, что и в табличке выше. Зелёные столбики показывают конверсии в бакетах по клиентам из целевой группы, а жёлтые — из контрольной.
Второй график показывает накопительным образом число дополнительных продаж/сделок/целевых событий в зависимости от количества клиентов с наибольшим uplift, которым решили отправить воздействие.
Кроме оценки качества модели, также важно понять, насколько она получилась адекватной. Например, логично ожидать, что в модель оценки вероятности дефолта при выплате ипотеки зайдёт признак типа «количество просрочек платежей по ипотеке». Это можно понять с помощью визуализации важности признаков, зашедших в финальную модель.
Также может быть полезно посмотреть на портрет чувствительного клиента (с высоким uplift) в виде решающего дерева. Часто такая визуализация помогает понять, насколько модель получилась адекватной, и позволяет представить бизнесу некоторую интерпретацию, по которой они смогут понять, насколько модель применима в конкретном бизнес-процессе.
Вывести их можно с помощью кода:
pipeline.plot_feature_importances(
model_result=model_result
)
plot_portrait_tree(
x=df[model_result.features],
uplift= model_result.estimator.predict(df[df.segm=='val'][ model_result.features]),
max_depth=2
)
Примеры графиков приведены ниже.
Для более подробного анализа любого числового признака из нашего датафрейма в разрезе бакетов его значений существует функция plot_uplift_by_feature_bins
. Она позволяет проверить отличие средних конверсий в каждом бакете целевой и контрольной групп. Как и ранее, рисуется гистограмма количества клиентов в каждом бакете и каждой группе. Это позволяет оценить, насколько случайно было назначено воздействие, то есть проверить гомогенность выборки, о которой говорилось ранее.
Пример кода:
for f in model_result.features:
plot_uplift_by_feature_bins(
df[f],
df[base_cols_mapper["treatment"]],
df[base_cols_mapper["target"]],
f"{f}",
amount_of_bins= 4
)
Пример использования AUF на данных Мегафон (ссылка [5]).
Цикл статей от МТС по аплифт моделированию: раз [6], два [7], три [8].
Документации библиотек: scikit-uplift [1], CausalML [2], EconML [3], Pylift [4].
Спасибо тем, кто также работал над статьёй и помогал с редактурой: Дмитрию Светлову (TeamLead DS), Даниилу Лескевичу (Senior DS) и Арине Неволиной (Junior DS).
Автор: dcamvik2020
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/415019
Ссылки в тексте:
[1] scikit-uplift: https://www.uplift-modeling.com/en/latest/index.html
[2] causalml: https://causalml.readthedocs.io/en/latest/about.html
[3] EconML: https://econml.azurewebsites.net/
[4] Pylift: https://pylift.readthedocs.io/en/latest/
[5] ссылка: https://www.kaggle.com/code/melvik2020/auf-example-megafon
[6] раз: https://habr.com/ru/companies/ru_mts/articles/485980/
[7] два: https://habr.com/ru/companies/ru_mts/articles/485976/
[8] три: https://habr.com/ru/companies/ru_mts/articles/538934/
[9] Источник: https://habr.com/ru/companies/alfa/articles/895002/?utm_campaign=895002&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.