UPDATE: 2024-03-16 00:49:21.393683
ここでは、Meta-LearnersのS-Learner、T-Learner、X-LearnerをRで実装してみる。PythonではEconMLパッケージが利用できるが、多分Rには今のところないはず。もしかしたらあるかも。
Meta-Learnersは、介入の効果が複雑であったり、効果が変数\(x\)ごとに異なる場合など、非線形な因果効果を推定する際に使用される。
Meta-Learnersでは、CATEを推定する。CATEはある特徴量\(x\)で条件づけた時の介入に関する因果効果の期待値のことである。\(Y^{(1)},Y^{(0)}\)は潜在的結果(potential outcomes)である。
\[ \begin{aligned} \tau(X) = E \left[ Y^{(1)} - Y^{(0)} \, | \, X \right] \end{aligned} \]
CATEを推定することのメリットとしては、こちらのブログで書かれている通りである。個人的にはUplift Modelingみたいことをしたいのだけれど、データを簡単に取れないことがあった。
CATEを推定することができれば, 嬉しいことがたくさんあります. 例えば, 因果効果がプラスであるような特徴量を持つ人だけに広告を打つことで商品の購入確率を最大化したり, 投薬計画を最適化することで生存率を改善できるかもしれません. 似たような目的を持つ分野にUplift Modelingと呼ばれるものがあります(参考1, 参考2)が, Uplift ModelingはA/Bテスト (RCT)によって収集された学習データがあることを前提とします. しかし多くの場合, A/Bテストを走らせて学習データを集めるようなことはコストの面から望ましくなく, 容易に実適用可能な技術とは言えないでしょう.
以降、S-Learner、T-Learner、X-Learnerについて内容をまとめているが、変数の説明は下記の通りである。
X
: 共変量(説明変数)t
: 介入の有無(0/1)y
: 目的変数MODEL
: 機械学習モデルここで使用するデータを読み込んでおく。また、モデルとしてランダムフォレストを利用するが、ハイパーパラメタのチューニングやバリデーションは行わない。
使用するデータはこちらよりお借りした。
options(scipen = 100000)
library(tidyverse)
library(randomForest)
df <- read_csv('~/Desktop/invest_email.csv') %>%
select(y = converted, t = em1, x1 = age, x2 = income, x3 = insurance, x4 = invested)
head(df)
## # A tibble: 6 × 6
## y t x1 x2 x3 x4
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 0 44.1 4215. 14036. 7989.
## 2 0 1 39.8 1151. 66622. 1001.
## 3 0 0 49 8047. 14120. 29480.
## 4 0 0 39.7 10558. 14798. 36373.
## 5 0 1 35.3 802. 781. 1643.
## 6 1 0 40 1672. 80489. 2785.
投資を促すメールデータで、結果変数はconverted
(投資した
vs
投資してない)。説明変数には年齢age
、所得income
、保険額insurance
、投資額invested
があり、介入はメール送信em1
である。目的は、より良い反応をしている個人にのみメールを送りたい。つまり、em1
の条件付き平均因果効果を推定したい。
\[ E[Converted(1)_i - Converted(0)_i|X_i=x] = \tau(x)_i \]
S-Learnerは下記の手順でCATEを推定する。
X,t->y
の機械学習モデルMODEL
を作成X, t=0
を使って、MODEL
から予測値y0
を得る(t=0
にデータを固定して予測)X, t=1
を使って、MODEL
から予測値y1
を得る(t=1
にデータを固定して予測)E[y1-y0]
がCATEとなる図はこちらよりお借りした。
手順および画像を参照しながらモデルを構築する。
model_s <- randomForest(y ~ x1 + x2 + x3 + x4 + t, data = df)
y1_s <- predict(model_s, df %>% mutate(t = 1), type = 'response')
y0_s <- predict(model_s, df %>% mutate(t = 0), type = 'response')
bind_cols(df, y1_s = y1_s, y0_s = y0_s, ice = y1_s - y0_s)
## # A tibble: 5,000 × 9
## y t x1 x2 x3 x4 y1_s y0_s ice
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 0 44.1 4215. 14036. 7989. 0.787 0.689 0.0978
## 2 0 1 39.8 1151. 66622. 1001. 0.226 0.306 -0.0802
## 3 0 0 49 8047. 14120. 29480. 0.553 0.548 0.00523
## 4 0 0 39.7 10558. 14798. 36373. 0.208 0.225 -0.0167
## 5 0 1 35.3 802. 781. 1643. 0.243 0.282 -0.0397
## 6 1 0 40 1672. 80489. 2785. 0.384 0.376 0.00822
## 7 0 1 36 1312. 5852. 1913. 0.278 0.252 0.0260
## 8 1 0 49.7 6059. 7438. 12591. 0.818 0.729 0.0894
## 9 0 0 38.4 2774. 44813. 1275. 0.316 0.267 0.0489
## 10 0 1 41.6 5267. 3692. 6028. 0.654 0.628 0.0261
## # ℹ 4,990 more rows
推定されたCATEは下記の通り。
# 可視化
# bind_cols(df, y1_s = y1_s, y0_s = y0_s) %>%
# ggplot(., aes(x4, y1_s - y0_s)) +
# geom_point() +
# theme_bw() +
# labs(y = 'CATE', title = 'S-Learner')
mean(y1_s - y0_s)
## [1] 0.06034141
T-Learnerは下記の手順でCATEを推定する。
t=0
のデータに対し、X->y
の機械学習モデルMODEL0
を作成(学習時にt
は使わない)t=1
のデータに対し、X->y
の機械学習モデルMODEL1
を作成(学習時にt
は使わない)X
を使って、MODEL0
から予測値y0
を得る(t
は予測には使わない)X
を使って、MODEL1
から予測値y1
を得る(t
は予測には使わない)E[y1-y0]
がCATEとなる図はこちらよりお借りした。
手順および画像を参照しながらモデルを構築する。
model0_t <- randomForest(y ~ x1 + x2 + x3 + x4, data = df %>% filter(t == 0))
model1_t <- randomForest(y ~ x1 + x2 + x3 + x4, data = df %>% filter(t == 1))
y0_t <- predict(model0_t, df, type = 'response')
y1_t <- predict(model1_t, df, type = 'response')
推定されたCATEは下記の通り。
# 可視化
# bind_cols(df, y1_t = y1_t, y0_t = y0_t) %>%
# ggplot(., aes(x, y1_t - y0_t)) +
# geom_point() +
# theme_bw() +
# labs(y = 'CATE', title = 'T-Learner')
mean(y1_t - y0_t)
## [1] 0.05789336
X-Learnerは下記の手順でCATEを推定する。傾向スコアの計算が必要だったり、構築されたモデルの推定値を利用するなど 、少し複雑な手順を踏む。
t=0
のデータに対し、X->y
の機械学習モデルMODEL0
を作成(学習時にt
は使わない)t=1
のデータに対し、X->y
の機械学習モデルMODEL1
を作成(学習時にt
は使わない)t=0
のデータに対し、X
を使って、MODEL1
から予測値y1
を得る(MODEL0
ではない)t=0
のデータに対し、y1-y
をd0
として計算d0
は介入を受けなかった個人の「介入を受けていないy
」と「もし介入を受けた時のy1
」の差分。d0
は介入を受けない個人の介入効果(ATU)。t=1
のデータに対し、X
を使って、MODEL0
から予測値y0
を得る(MODEL1
ではない)t=1
のデータに対し、y-y0
をd1
として計算d1
は介入を受けた個人の「介入を受けたy
」と「もし介入を受けなかった時のy0
」の差分。d1
は介入を受けた個人の介入効果(ATT)。t=0
のデータに対し、X->d0
の機械学習モデルMODEL00
を作成(学習時にt
は使わない)t=1
のデータに対し、X->d1
の機械学習モデルMODEL11
を作成(学習時にt
は使わない)X->t
の機械学習モデルMODEL_ps
を作成(傾向スコアps
を計算)X
を使って、MODEL00
から予測値y0m0
を得るX
を使って、MODEL11
から予測値y1m1
を得るX
を使って、MODEL-ps
から予測値p
を得る(ps)*(y0m0)+(1-ps)*(y1m1)
がCATEとなる図はこちらよりお借りした。
手順および画像を参照しながらモデルを構築する。
df0 <- df %>% filter(t == 0) # 介入を受けてない集団
df1 <- df %>% filter(t == 1) # 介入を受けた集団
# 介入を受けてないモデル
model0_x <- randomForest(y ~ x1 + x2 + x3 + x4, data = df0)
# 介入を受けたモデル
model1_x <- randomForest(y ~ x1 + x2 + x3 + x4, data = df1)
# 推定された介入効果を各個人ごとに計算
d0 <- predict(model1_x, df0, type = 'response') - df0$y
d1 <- df1$y - predict(model0_x, df1, type = 'response')
# ATTを推定するモデル
model00_x <- randomForest(d0 ~ x1 + x2 + x3 + x4, df0)
# ATUを推定するモデル
model11_x <- randomForest(d1 ~ x1 + x2 + x3 + x4, df1)
# 傾向スコアを推定するモデル
model_ps <- randomForest(t ~ x1 + x2 + x3 + x4, df)
y0m0 <- predict(model00_x, df, type = 'response')
y1m1 <- predict(model11_x, df, type = 'response')
ps <- predict(model_ps, df, type = 'response')
ice <- ps * y0m0 + (1 - ps) * y1m1
推定されたCATEは下記の通り。
# 可視化
# bind_cols(df, cate = cate) %>%
# ggplot(., aes(x, cate)) +
# geom_point() +
# theme_bw() +
# labs(y = 'CATE', title = 'X-Learner')
mean(ice)
## [1] 0.06027891
X-Learnerを使って、よく見る図を再現しておく。
df <- read_csv('~/Desktop/metalearner.csv')
df0 <- df %>% filter(t == 0)
df1 <- df %>% filter(t == 1)
model0_x <- randomForest(y ~ x, data = df0)
model1_x <- randomForest(y ~ x, data = df1)
d0 <- predict(model1_x, df0, type = 'response') - df0$y
d1 <- df1$y - predict(model0_x, df1, type = 'response')
model00_x <- randomForest(d0 ~ x, df0)
model11_x <- randomForest(d1 ~ x, df1)
model_ps <- randomForest(t ~ x, df)
y0m0 <- predict(model00_x, df, type = 'response')
y1m1 <- predict(model11_x, df, type = 'response')
ps <- predict(model_ps, df, type = 'response')
cate <- ps * y0m0 + (1 - ps) * y1m1
bind_cols(df, cate = cate) %>%
ggplot(., aes(x, cate)) +
geom_point() +
theme_bw() +
labs(y = 'CATE', title = 'X-Learner')