2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

EconMLケーススタディ - レコメンデーション A/Bテスト: 不完全コンプライアンスを伴う実験

Last updated at Posted at 2020-05-05

この投稿はこのノートブックの和訳です。

レコメンデーション A/Bテスト: 不完全コンプライアンスを伴う実験

オンラインビジネスの世界では、自社のウェブサイトの新機能やサービスをテストし、下流の収益に与える影響を知りたいと考えています。さらに、どのようなユーザーが新バージョンに最もよく反応するかを知りたいと考えています。このようなユーザーに特化した効果を heterogeneous treatment effect(異質処置効果) と呼びます。

理想的なのは、ウェブサイトの新旧バージョン間でA/Bテストを実行することです。しかし、企業は顧客に新しい提供価値を取ることを強制することはできないので、直接のA/Bテストはうまくいかないかもしれません。この方法で効果を測定することは、新しい提供価値に触れたすべての顧客がそれを受け入れるわけではないため、誤解を招くでしょう。

また、既存のデータを直接見ることはできません。最新のウェブサイト機能を使用しているユーザーは、ウェブサイトに非常に興味を持っている可能性が高く、その結果、最初に会社の製品に多くの時間を費やすことになります。このような方法で効果を推定するのは楽観的すぎるでしょう。

このカスタマー・シナリオ・ウォークスルーでは、EconMLライブラリのツールがどのようにして直接A/Bテストを使用し、これらの欠点を軽減することができるのかを紹介します。

要約

  1. 背景
  2. データ
  3. EconMLによる因果関係の取得
  4. EconMLによる処置効果の理解
  5. EconMLを用いた方針決定
  6. 結論

背景

このシナリオでは、とある旅行会社のウェブサイトにおいて、会員制プログラムに参加することで、ユーザーがウェブサイトを利用する時間をより長くし、より多くの商品を購入するようにすることができるかどうかを知りたいと考えています。

ウェブサイトはユーザーに強制的に会員になってもらうことができないため、直接A/Bテストを行うことはできません。また、旅行会社は、会員になることを選択した顧客は、他のユーザーよりもすでにエンゲージメントが高い可能性が高いため、会員と非会員を比較する既存のデータを直接見ることができません。

解決策: この旅行会社は、以前にも、新しいより迅速なサインアップ・プロセスの価値をテストするための実験を行っていました。EconMLのIV推定器は、会員になる可能性の確率にランダムな変動を発生させる手段として、この実験的な会員への後押しを利用することができます。これはintent-to-treat設定として知られています。この意図は、ランダムなグループのユーザに「処置」(より簡単なサインアッププロセスへのアクセス)を与えることですが、すべてのユーザが実際に処置を受けるわけではありません。

EconMLの IntentToTreatDRIV 推定モデルは、簡単なサインアップを提供されたすべての顧客が会員になったわけではないという事実を利用して、簡単なサインアップを受けた効果ではなく、会員になることの効果を学習します。

# Some imports to get us started
# Utilities
import os
import urllib.request
import numpy as np
import pandas as pd

# Generic ML imports
import lightgbm as lgb
from sklearn.preprocessing import PolynomialFeatures

# EconML imports
from econml.ortho_iv import LinearIntentToTreatDRIV
from econml.cate_interpreter import SingleTreeCateInterpreter, \
                                    SingleTreePolicyInterpreter

import matplotlib.pyplot as plt
%matplotlib inline

データ

データ*は以下のもので構成されています。

  • 実験の28日前に収集された特徴(接尾辞 _pre で示される)
  • 実験変数(簡単なサインアップにユーザーがさらされたかどうか -> 操作変数、利用者が会員になったかどうか -> 処置)
  • 実験後28日以内に収集された変数(接尾辞 _post で示される)

特徴量名 | 詳細
:--- |: ---
days_visited_exp_pre |#ユーザーがアトラクションページを訪問した日数
days_visited_free_pre | #ユーザーが無料のチャンネル(例:ドメインダイレクト)でウェブサイトを訪問した日数
days_visited_fs_pre | #ユーザーがフライトページを訪問した日数
days_visited_hs_pre | #ユーザーがホテルのページを訪問した日数
days_visited_rs_pre | #ユーザーがレストランのページを訪問した日数
days_visited_vrs_pre | #ユーザーがバケーションレンタルのページを訪問した日数
locale_en_US | ユーザーが米国からウェブサイトにアクセスしているかどうか
os_type | ユーザーのオペレーティングシステム (windows, osx, その他)
revenue_pre | pre-period期間にユーザーがWebサイトを利用した金額
easier_signup | ユーザーがより簡単なサインアッププロセスにさらされたかどうか
became_member | ユーザがメンバーになったかどうか
days_visited_post | #実験後28日以内にユーザーがサイトを訪れた日数

*旅行会社の利用者のプライバシーを保護するため、本シナリオで使用するデータは合成的に生成されたものであり、特徴量の分布は実際の分布とは一致しません。ただし、特徴量名はその名称と意味を保持しています。

# Import the sample AB data
file_url = "https://msalicedatapublic.blob.core.windows.net/datasets/RecommendationAB/ab_sample.csv"   
ab_data = pd.read_csv(file_url)
# Data sample
ab_data.head()
days_visited_exp_pre days_visited_free_pre days_visited_fs_pre days_visited_hs_pre days_visited_rs_pre days_visited_vrs_pre locale_en_US revenue_pre os_type_osx os_type_windows easier_signup became_member days_visited_post
0 1 9 7 25 6 3 1 0.01 0 1 0 0 1
1 10 25 27 10 27 27 0 2.26 0 0 0 0 15
2 18 14 8 4 5 2 1 0.03 0 1 0 0 17
3 17 0 23 2 3 1 1 418.77 0 1 0 0 6
4 24 9 22 2 3 18 1 1.54 0 0 0 0 12
# Define estimator inputs
Z = ab_data['easier_signup'] # nudge, or instrument
T = ab_data['became_member'] # intervention, or treatment
Y = ab_data['days_visited_post'] # outcome of interest
X_data = ab_data.drop(columns=['easier_signup', 'became_member', 'days_visited_post']) # features

データは、次の処置効果関数を用いて生成されました。

$$
\text{treatment_effect} = 0.2 + 0.3 \cdot \text{days_visited_free_pre} - 0.2 \cdot \text{days_visited_hs_pre} + \text{os_type_osx}
$$

これは、実験前にWebサイトにアクセスしたユーザーやiPhoneを使用しているユーザーはメンバーシッププログラムの恩恵を受ける傾向があるのに対し、ホテルのページにアクセスしたユーザーはメンバーシップによって害を受ける傾向があるという解釈ができます。 このような関係性をデータから学ぼうとしています。

# Define underlying treatment effect function 
TE_fn = lambda X: (0.2 + 0.3 * X['days_visited_free_pre'] - 0.2 * X['days_visited_hs_pre'] + X['os_type_osx']).values
true_TE = TE_fn(X_data)

# Define the true coefficients to compare with
true_coefs = np.zeros(X_data.shape[1])
true_coefs[[1, 3, -2]] = [0.3, -0.2, 1]

EconMLによる因果関係の取得

処置効果の線形射影を学習するには、LinearIntentToTreatDRIV EconML 推定器を用います。より柔軟な処置効果関数を求めるには、代わりに IntentToTreatDRIV 推定器を使用します。

モデルは、いくつかのニュアンスモデル(つまり、我々が実際には気にしていないが分析には重要なモデル)を定義する必要があります:結果 $Y$ が特徴量 $X$ にどのように依存するかのモデル(model_Y_X)と、処置 $T$ が手段 $Z$ と特徴量 $X$ にどのように依存するかのモデル(model_T_XZ)です。これらのモデルには事前分布がないため、一般的なブースティング決定木推定器を使用してモデルを学習します。

# Define nuissance estimators
lgb_T_XZ_params = {
    'objective' : 'binary',
    'metric' : 'auc',
    'learning_rate': 0.1,
    'num_leaves' : 30,
    'max_depth' : 5
}

lgb_Y_X_params = {
    'metric' : 'rmse',
    'learning_rate': 0.1,
    'num_leaves' : 30,
    'max_depth' : 5
}
model_T_XZ = lgb.LGBMClassifier(**lgb_T_XZ_params)
model_Y_X = lgb.LGBMRegressor(**lgb_Y_X_params)
flexible_model_effect = lgb.LGBMRegressor(**lgb_Y_X_params)
# Train EconML model
model = LinearIntentToTreatDRIV(
    model_Y_X = model_Y_X,
    model_T_XZ = model_T_XZ,
    flexible_model_effect = flexible_model_effect,
    featurizer = PolynomialFeatures(degree=1, include_bias=False)
)
model.fit(Y.values, T, Z, X_data.values, inference="statsmodels")
<econml.ortho_iv.LinearIntentToTreatDRIV at 0x1a1152e3588>
# Compare learned coefficients with true model coefficients
coef_indices = np.arange(model.coef_.shape[0])
# Calculate error bars
coef_error = np.asarray(model.coef__interval()) # 90% confidence interval for coefficients
coef_error[0, :] = model.coef_ - coef_error[0, :]
coef_error[1, :] = coef_error[1, :] - model.coef_
plt.errorbar(coef_indices, model.coef_, coef_error, fmt="o", label="Learned coefficients\nand 90% confidence interval")
plt.scatter(coef_indices, true_coefs, color='C1', label="True coefficients")
plt.xticks(coef_indices, X_data.columns, rotation='vertical')
plt.legend()
plt.show()

output_14_0.png

係数の推定値が線形の処置効果関数の真の係数にかなり近いことがわかります。

また、点推定値、p値、信頼区間を得るために model.summary 関数を使うこともできます。下の表から、days_visited_free_pre, days_visited_hs_pre, os_type_osx 特徴量だけが、処置効果について統計的に有意であることがわかります(信頼区間は $0$ を含まず、p値 < 0.05)。

model.summary(feat_name=X_data.columns)
Coefficient Results
point_estimate stderr zstat pvalue ci_lower ci_upper
days_visited_exp_pre 0.001 0.007 0.157 0.875 -0.01 0.012
days_visited_free_pre 0.285 0.007 38.361 0.0 0.273 0.297
days_visited_fs_pre -0.009 0.007 -1.257 0.209 -0.02 0.003
days_visited_hs_pre -0.191 0.007 -28.274 0.0 -0.202 -0.18
days_visited_rs_pre -0.0 0.007 -0.051 0.959 -0.011 0.011
days_visited_vrs_pre -0.001 0.007 -0.143 0.887 -0.012 0.01
locale_en_US -0.043 0.113 -0.381 0.703 -0.229 0.143
revenue_pre -0.0 0.0 -1.551 0.121 -0.0 0.0
os_type_osx 0.964 0.139 6.949 0.0 0.736 1.192
os_type_windows 0.034 0.138 0.244 0.807 -0.194 0.262
Intercept Results
point_estimate stderr zstat pvalue ci_lower ci_upper
intercept 0.539 0.27 1.996 0.046 0.095 0.983
test_customers = X_data.iloc[:1000]
true_customer_TE = TE_fn(test_customers)
model_customer_TE = model.effect(test_customers)
# How close are the predicted treatment effect to the true treatment effects for 1000 users?
plt.scatter(true_customer_TE, model.effect(test_customers), label="Predicted vs True treatment effect")
plt.xlabel("True treatment effect")
plt.ylabel("Predicted treatment effect")
plt.legend()
plt.show()

output_18_0.png

EconMLによる処置効果の理解

EconMLには、処置効果をよりよく理解するための解釈ツールが含まれています。 処置効果は複雑になる可能性がありますが、多くの場合、積極的に反応するユーザー、中立を維持するユーザー、提案された変更に否定的に反応するユーザーを区別できる単純なルールに私たちは関心があります。

EconML SingleTreeCateInterpreterは、EconML推定器のいずれかによって出力された処置効果について単一の決定木をトレーニングすることにより、相互運用性を提供します。 下の図では、メンバーシッププログラムに否定的な反応を示す濃い赤のユーザーと、肯定的な反応を示す濃い緑のユーザーを示しています。

intrp = SingleTreeCateInterpreter(include_model_uncertainty=True, max_depth=2, min_samples_leaf=10)
intrp.interpret(model, test_customers)
plt.figure(figsize=(25, 5))
intrp.plot(feature_names=X_data.columns, fontsize=12)

output_20_0.png

EconMLを用いた方針決定

介入には通常コストがかかります:ユーザーに会員になってもらうにはコストがかかります(割引を提供するなど)。したがって、ユーザーのエンゲージメントを高めることで利益を最大化するためには、どのような顧客をターゲットにすればよいかを知りたいと思います。これが処置方針です。

EconMLライブラリには、SingleTreePolicyInterpreterのような方針解釈ツールが用意されており、処置コストと処置効果を考慮して、どの顧客をターゲットにして利益を最大化するかという簡単なルールを学習することができます。

intrp = SingleTreePolicyInterpreter(risk_level=0.05, max_depth=2, min_samples_leaf=10)
intrp.interpret(model, test_customers, sample_treatment_costs=0.2)
plt.figure(figsize=(25, 5))
intrp.plot(feature_names=X_data.columns, fontsize=12)

output_22_0.png

結論

このノートでは、EconMLを使って、以下のようなことができることを実証しました。

  • 一見不可能に見えるシナリオにおいて、有効な因果関係の洞察を得る
  • 結果として得られる個人レベルの処置効果を計算する
  • 学んだ効果を軸に方針を構築する

EconMLがあなたのためにできることの詳細については、ウェブサイトGitHubページドキュメントを参照してください。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?