Как функция "Важность функций" помогла мне сделать правильные выводы по результатам тестирования A B

В этом посте я хочу сосредоточиться на использовании машинного обучения для дополнения других задач анализа данных, в частности A / B-тестирования. Предполагается, что это будет практический пост, а не теоретическое обсуждение, и я предполагаю, что вы, по крайней мере, немного знакомы с A / B-тестированием, Случайным лесом и Feature Важность.

Недавно я столкнулся с интересной проблемой, которая потребовала от меня провести A / B-тест, чтобы проверить, продаются ли автомобили на одном аукционе быстрее, чем на другом. Простое выполнение A / B-теста привело бы к выводу, что на одном типе аукциона автомобили действительно продавались быстрее, чем на другом, но оказалось, что тип аукциона не был основной движущей силой более быстрого времени продажи. Это были более низкие сопутствующие цены одного типа аукциона. Это могло иметь ужасные последствия, если бы компания, продающая эти автомобили, решила бы сосредоточиться на продаже через этот единственный тип аукциона, а не на ценообразовании в первую очередь.

Я узнал об этом, запустив случайный лес в наборе данных, а затем узнал важность функций, что помогло мне это раскрыть. На самом деле, я обычно считаю, что машинное обучение - отличный дополнительный инструмент для исследовательского анализа данных (EDA), а также для A / B-тестирования.

Данные

Давайте сначала взглянем на данные. Для A / B-теста интерес представляют sales_channel, то есть два разных типа аукционов и время продажи, которое является разницей между sold_date и buy_date.

Соревнование

Задача заключалась в том, чтобы провести A / B-тест, чтобы определить, какой тип аукциона 1 или 2 продает автомобили быстрее - если мы сможем сделать вывод, что один тип продается быстрее, чем другой, компания могла бы сосредоточиться на продаже большего количества, например, на одном аукционе.

A / B тест

Оказывается, что распределения времени продажи для обоих типов аукционов не являются «нормальными» или t-распределениями. Оба косо с длинным хвостом. Однако благодаря центральной предельной теореме мы можем преобразовать эти распределения в более нормальные распределения, произвольно выбирая из их распределений и каждый раз принимая их среднее время продажи. Если мы сделаем это 1000 раз, например, то перейдем от графика 1 (который показывает время продажи для каждого типа аукциона) к графику 2 (который показывает среднее время продажи для каждого типа аукциона).

# Do the sampling
results = [] # create an empty list into which I insert the sampled means
random_state = np.arange(0,1000) # random seeds for reproducibility
# sample with replacement using 50% of the data; do this 1000 times
# and append the mean seeling time to the list ‘results’
for i in range(1000):
 sample = df.sample(frac=0.5, replace=True, 
 random_state=random_state[i]).groupby(by=’sales_channel’)[‘selling_time’].mean()
 results.append(sample)
results = pd.DataFrame(results)

То, что не было так очевидно на графике 1, становится очень очевидным на графике 2: среднее время продажи аукциона типа 1 намного короче, чем у аукциона типа 2. Проведение A / B-теста подтверждает это с p-значением 0,00. Проведение A / B-теста здесь даже кажется излишним, поскольку дистрибутивы даже не пересекаются.

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

Что, если бы были другие характеристики / особенности, которые отличают эти два типа аукционов, помимо времени продажи? В этом наборе данных очень мало функций, поэтому мы могли бы нарисовать больше похожих распределений, вычислить корреляции, где это возможно, и так далее. Более простой и эффективный подход - использовать машинное обучение, чтобы помочь нам - это становится особенно полезным - если не необходимым - когда у нас намного больше функций.

Важность функции для спасения

У большинства алгоритмов машинного обучения есть метод расчета важности их функций, то есть того, насколько большую роль играла каждая функция в прогнозировании целевой / зависимой функции. Здесь целью будет время продажи.

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

# Set up the model and define its parameters — let’s keep it simple
rf = RandomForestRegressor(n_estimators=100, max_depth=5)
# Fit the model
rf.fit(X_train, y_train)
# Calculate the mean feature importance
importances = rf.feature_importances_
# Calculate the standard deviation of the feature importance
std = np.std([tree.feature_importances_ for tree in rf.estimators_],
 axis=0)
# Sort the features by their importance
indices = np.argsort(-importances)[::-1]
# Plot the feature importances 
plt.figure(figsize=(12,8))
plt.title(“Feature importances”)
plt.barh(range(X.shape[1]), importances[indices], 
 color=”r”, yerr=std[indices], align=”center”)
plt.yticks(range(X.shape[1]),X.columns[indices], rotation=0)
plt.ylim([len(importances)-6, len(importances)])
plt.show()

Слева вы видите важность каждой функции для прогнозирования времени продажи - чем длиннее полоса, тем важнее функция. Цена покупки - безусловно, самая важная характеристика, тогда как канал продаж (типы аукционов) практически не имеет значения.

Затем я вычислил другой тип важности функций, называемый SHAP. Способ чтения графика следующий: каждая точка - это строка / наблюдение в наборе данных. Отрицательные значения SHAP означают, что значение характеристики уменьшило значение целевого прогноза (время продажи). Синий цвет указывает на то, что значение функции было низким, а красный цвет указывает на то, что значение функции было высоким. При просмотре цены покупки мы видим, что все отрицательные значения SHAP были синими, то есть низкие цены покупки привели к предсказанию низкого времени продажи. Однако обратите внимание, что есть также несколько синих точек с положительными значениями SHAP. В таких случаях низкая цена покупки привела к противоположному предсказанию более длительного времени продажи. Это может произойти, когда объекты взаимодействуют друг с другом. Интересно, что канал продаж (типы аукционов) очень хорошо разделен: тип аукциона 1 выделен красным цветом, а тип аукциона 2 - синим.

# Plot the overall Feature Importance using SHAP
shap.initjs()
explainer = shap.TreeExplainer(cat)
shap_values = explainer.shap_values(pool_val)
shap.summary_plot(shap_values, X_val)

Прежде чем мы подведем итоги, давайте кратко рассмотрим закупочные цены по типам аукционов. Мы уже подозревали об этом, глядя на важность функции. У обоих типов аукционов очень разное ценовое распределение. Аукцион типа 1 (около 6 тысяч долларов) имеет гораздо более низкие цены, чем аукцион типа 2 (около 24 тысяч долларов).

Заключение

Простое проведение A / B-теста, чтобы увидеть, продает ли один тип аукциона автомобили быстрее, чем другой, вероятно, привело бы к неправильному выводу. Запуск Random Forest или любого другого алгоритма машинного обучения с последующим вычислением важности функции дает вам гораздо лучшее представление о том, какие функции влияют на интересующую вас цель. Я обнаружил, что использование машинного обучения в дополнение к A / B-тестам помогло мне принимать более правильные решения. . Надеюсь, тебе это тоже поможет.