はじめに
機械学習モデル等を用いた因果推論手法であるDouble Machine Learningについて、表面的には理解していましたが、詳しい部分まで知らなかったためいくつかのドキュメントを読みました。
簡単にまとめたいと思います。
ベースはMSのEconMLのページを参考にしています。
Double Machine Learning
特徴
- 二段階のモデル推定を行う
- 推定に用いる手法やモデルは柔軟に設定することができる
- ネイマン直交条件を満たすスコア関数の零点を求めることで処置結果やその信頼区間を推定できる
- モデルが誤推定されていなければ推定結果は√n-consistent になる
モデル
Double Machine Learningでは、部分線形モデルを考えることになる。 部分線形モデルは線形モデルより表現力が高い。
ここでそれぞれの変数は、アウトカム,
※ 線形モデル
Double Machine Learningでは、データ生成プロセスに関して次の構造方程式の仮定しています。
このモデルを持ちいてmarginal CATE : を推定することを目指している。
1段階目の推定では条件付き期待関数を推定する。 このモデルには任意のノンパラメトリックモデル・機械学習モデルを用いることができる。
2段階目では、を推定する。 構造方程式をとの残差同士の回帰の形に書き直す。 これにより式からを削除することができる。
それぞれの残差は次のように計算できる。
残差同士の回帰は以下のようになる。
最終的には次の目的関数を満たす問題に帰着することができる。
EconMLでは、上記の目的関数に正則化項を加えて推定を行うこともできる。
部分線形モデルについて
部分線形モデルにおけるnaibeな推定量では、√n-consistentではなくなるため、推定精度が良くない。
要因としてはの推定精度が関係している。
機械学習モデルの一般的な収束レートは、n1/4-consistent()である。 この時のの収束レートは()となってしまい、うまく推定することができない。
このバイアスをRegularization Biasと呼ばれている。
対策として操作変数法の考え方を用いたモーメント条件を考えると、推定量の収束レートはの推定精度の掛け算項が支配的になっている。
MLモデルの一般的な収束レートはn1/4-consistent()であるため、その掛け算であるため収束レートは√n-consistent()となる。
これを達成する方法が上記に記したYとTの残差同士の回帰であり、Robinsonの手法と呼ばれている。
EconMLでの実行
CATEを推定するためのPythonライブラリ:EconMLをMicrosoftが提供しているです。
様々な因果推論+機械学習のモデルが簡単に実行することができる。
先に用いるサンプルデータを生成する。 基本的にはEconMLのサンプルコードをベースにさせてもらっている。
基本的な使い方
提供されているモデルのタイプは大まかに3タイプある模様。 いくつかのパターンの挙動を確認する。
LinearDML 線形の因果効果を仮定する 任意のscikit-learn線形推定器等を最終段階として定義することができる方法
CausalForestDML Causal Forest を最終モデルとして使用する方法
NonParamDML 最終的な因果効果の推定にモデルを仮定しない方法
import numpy as np from econml.dml import DML, LinearDML, CausalForestDML from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
それぞれのモデルの場合の処理を行う。
はじめにLinearDML。
結果変数を推定するモデルと処置を推定するモデルを指定する必要がある。 今回はRandomForestRegressorを利用。 因果効果は線形であると考えることになる。
est1 = LinearDML(model_y = RandomForestRegressor(), model_t = RandomForestRegressor()) est1.fit(Y, T, X = X, W = W) # モデルの学習 te_pred1 = est1.effect(X_test) # CATEの推定 te_lower1, te_upper1 = est1.effect_interval(X_test, alpha=0.01)
非線形の因果効果を推定する場合は、以下のように2段階目の推定に多項式モデルを仮定する。
# CausalForestDML est3 = CausalForestDML(model_y=RandomForestRegressor(), model_t=RandomForestClassifier(min_samples_leaf=10), discrete_treatment=True, n_estimators=200, min_impurity_decrease=0.001, verbose=0, cv=6) est3.tune(Y, T, X=X, W=W) est3.fit(Y, T, X=X, W=W) te_pred3 = est3.effect(X_test) te_lower3, te_upper3 = est3.effect_interval(X_test, alpha=0.01) # 1-α %の信頼区間を出す
またCausalForestDMLでも非線形の因果効果を推定することができる。
# CausalForestDML est3 = CausalForestDML(model_y=RandomForestRegressor(), model_t=RandomForestClassifier(min_samples_leaf=10), discrete_treatment=True, n_estimators=200, min_impurity_decrease=0.001, verbose=0, cv=6) est3.tune(Y, T, X=X, W=W) est3.fit(Y, T, X=X, W=W) te_pred3 = est3.effect(X_test) te_lower3, te_upper3 = est3.effect_interval(X_test, alpha=0.01)
NonParamDMLでは最終的な因果効果の推定にモデルを仮定しておらず、自由に推定モデルを設定することができる。
model_final引数にsklearnのモデルを設定する。
est4 = NonParamDML(model_y = GradientBoostingRegressor(), model_t = GradientBoostingRegressor(), model_final = GradientBoostingRegressor()) est4.fit(Y, T, X=X, W=W) te_pred4 = est4.effect(X_test)
CausalForestDMLと同様にCATEを推定することができているが、分散がかなり大きいように見える。
推定モデルのサマリを確認する
est1.summary()
est3.summary()
2段階目のモテリングにおける交差検証で算出された誤差も確認することができる。
print("Linear Model score:",round(est1.score_,4)) print("Poly Model score:",round(est2.score_,4)) print("DML Model score:",round(est3.score_,4)) print("Non Model score:",round(est4.score_,4))
Linear Model score: 1.3845 Poly Model score: 1.0754 DML Model score: 0.936 Non Model score: 0.9862
そのほかの設定
SHAPによる寄与の算出
因果効果を推定する2段階目のモデルに対してSHAPを適用することで、因果効果の推定における各特徴量の寄与を推定することができる。
import shap shap_values = est3.shap_values(X) shap.plots.beeswarm(shap_values['Y0']['T0_1'])
単一な決定木による説明の獲得
CATEの説明をよりわかりやすくするために、獲得した因果効果を単一な決定木により表現する。 これにより複雑な因果効果の場合分けを比較的人間が理解できる結果に変換してくれる。
from econml.cate_interpreter import SingleTreeCateInterpreter plt.figure(figsize=(5, 8)) intrp = SingleTreeCateInterpreter(include_model_uncertainty=True, max_depth=2, min_samples_leaf=10) intrp.interpret(est3, X_test) plt.figure(figsize=(25, 5)) intrp.plot(feature_names=["X1","X2","X3","X4"], fontsize=12)
パイプラインの利用
モデルの設定で特徴量変換を設定することができる。 定義した変換処理をfeaturizerに渡すだけで良い。
from sklearn.ensemble import RandomForestRegressor class LogFeatures(object): def fit(self, X, y=None): return self def transform(self, X): return np.concatenate((X, np.log(1+X)), axis=1) def fit_transform(self, X, y=None): return self.fit(X).transform(X) est = LinearDML(model_y=RandomForestRegressor(), model_t=RandomForestRegressor(), featurizer=LogFeatures()) est.fit(y, T, X=X, W=W)
featurizerにはパイプラインも設定することもできる。
from sklearn.pipeline import Pipeline from sklearn.preprocessing import PolynomialFeatures est = LinearDML(model_y=RandomForestRegressor(), model_t=RandomForestRegressor(), featurizer=Pipeline([('log', LogFeatures()), ('poly', PolynomialFeatures(degree=2))]))
固定効果の導入
from sklearn.preprocessing import OneHotEncoder X_oh = OneHotEncoder(sparse_output=False).fit_transform(X)[:, 1:] est = LinearDML(model_y=RandomForestRegressor(), model_t=RandomForestRegressor()) est.fit(y, T, X=X_oh, W=W)
複数の処置がある場合
est = LinearDML()
est.fit(y, np.concatenate((T0, T1), axis=1), X=X, W=W)
なお、複数の処理と複数の結果変数がある場合も対応することができる。
適用例
こちらのノートブックでより具体的なサンプルデータにおける分析の流れを説明している。 (サンプルデータの生成についてはここでは割愛) github.com
ここでは所得に応じた需要の価格弾力性を推定することを目指す。
用いるのは以下のようなモデル。
ここで、は需要の自然対数、 所得 に基づいた需要の価格弾力性、は価格の自然対数、 は、所得 とユーザー特性 に基づいた需要に対する他の要因の関数、 は に基づいた価格決定に対する要因の関数。
多項式回帰を仮定したパラメトリック推定を行う。 実行するには以下のとおり。
est = LinearDML(model_y = GradientBoostingRegressor(), model_t = GradientBoostingRegressor(), featurizer=PolynomialFeatures(degree=5, include_bias=False)) est.fit(Y, T, X = X, W = W) te_pred = est.effect(X_test) te_pred_interval = est.effect_interval(X_test, alpha=0.01) # 1-α %の信頼区間を出す
推定されたモデルは以下のようになっている。
Coefficient Results point_estimate stderr zstat pvalue ci_lower ci_upper income -50.646 7.043 -7.191 0.0 -64.45 -36.842 income^2 83.662 9.41 8.891 0.0 65.219 102.106 income^3 -43.952 5.3 -8.293 0.0 -54.34 -33.565 income^4 9.301 1.28 7.265 0.0 6.792 11.81 income^5 -0.683 0.109 -6.289 0.0 -0.896 -0.47 CATE Intercept Results point_estimate stderr zstat pvalue ci_lower ci_upper cate_intercept -8.698 1.751 -4.967 0.0 -12.131 -5.266
次にCausal Forestを用いたノンパラメトリックな推定を行う。 実行するには以下のとおり。
est = CausalForestDML(
model_y=GradientBoostingRegressor(), model_t=GradientBoostingRegressor()
)
est.fit(log_Y, log_T, X=X, W=W, inference="blb")
te_pred = est.effect(X_test)
te_pred_interval = est.effect_interval(X_test)
次に収益を最大化する施策割り当て方策(ポリシー)を考えたい。
EconMLライブラリでは、シンプルな説明可能なポリシーを生成するためにSingleTreePolicyInterpreterが提供されている。 以下のように実行すると介入費用と介入効果の関係から、ターゲットにするべき対象を判別するルールを生成できる。
intrp = SingleTreePolicyInterpreter(risk_level=0.05, max_depth=2, min_samples_leaf=1, min_impurity_decrease=0.001) intrp.interpret(est, X_test, sample_treatment_costs=-1) plt.figure(figsize=(25, 5)) intrp.plot(feature_names=X.columns, treatment_names=["Discount", "No-Discount"], fontsize=12)