🎓 レベル:発展 | 重要度:B(標準)
📎 前提:Double/Debiased Machine Learning(DML) | 識別の仮定 | 数理:勾配ブースティング(機械学習)
要点(BLUF)
- CATE(条件付き平均処置効果) は「誰に効くか」を表す。ATE を超えて効果の異質性を捉える。
- メタ学習器は、CATE 推定を既存の回帰器(base learner)を部品にした手続きに還元する。代表が S-learner / T-learner / X-learner。
- 真の を仕込んだ擬似データで、S は異質性を平坦化、T は高分散、X が最良(RMSE 0.72・スロープ 2.74)になることを数値と図で示す。
ATE から CATE へ
ここまで(Double/Debiased Machine Learning(DML))は効果が一定の ATE を推定してきた。だが現実の効果は人によって違う。それを表すのが CATE。
識別の仮定
識別の仮定の条件付き交換可能性 ・正値性 ・SUTVA の下で、CATE は観測量で書ける。
flowchart LR
X["共変量 X(交絡+効果修飾)"] --> T["処置 T"]
X --> Y["結果 Y"]
T -->|"効果 τ(x):X で変化"| Y
ここで は二役を持つ。 の経路は交絡(調整が必要)、そして処置→結果の矢の太さ を変える効果修飾。識別は交絡を塞げるかの問題、推定は の関数形をどう当てるかの問題で、両者は別物である。
3 つのメタ学習器
任意の回帰器 を部品にして、 の当て方で流派が分かれる。
- S-learner(Single):処置 を特徴量の一つとして 1 つのモデル を学習し、。実装が簡単だが、 が多数の特徴量に埋もれて効果が縮小しやすい。
- T-learner(Two):処置群・対照群で別々に を学習し、。異質性は素直に出るが、各群を独立に当てるので高分散(小さい/不均衡な群で悪化)。
- X-learner:T-learner の後に、個別効果を補完して回帰し、傾向で加重する。不均衡に強い。
加重に傾向 を使うのが要点。処置群が少数( 小)なら、 が大きくなり、大きい対照群で当てた に支えられた を重視する。これが不均衡時に効く。
コード:S/T/X-learner で τ(x) を推定し比較
真の ( で効果が変わる)を仕込み、処置割り当ては傾向 で不均衡にする。3 学習器の RMSE と異質性スロープ(真値 3.0)、平均効果(真 ATE 1.0)を比べる。
# === 共変量X0で処置効果が変わる擬似データを作り、3つのメタ学習器で τ(x) を推定 ===
import numpy as np
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.linear_model import LogisticRegression
rng = np.random.default_rng(7)
n, p = 1500, 5
X = rng.standard_normal((n, p))
e = 1.0 / (1.0 + np.exp(-(-1.2 + 0.8 * X[:, 0] + 0.8 * X[:, 1]))) # 傾向スコア(交絡+不均衡)
T = rng.binomial(1, e)
mu0 = 3 * X[:, 1] + 2 * X[:, 2] + 2 * X[:, 3] # 大きな基準応答 E[Y(0)|x]
tau_true_fn = lambda Z: 1.0 + 3.0 * Z[:, 0] # 真のCATE: X0 で変化
Y = mu0 + tau_true_fn(X) * T + rng.standard_normal(n)
print("処置を受けた割合:", round(T.mean(), 3))
def base():
return GradientBoostingRegressor(max_depth=3, n_estimators=200, random_state=0)
# --- S-learner: 1つのモデルに T も特徴量として入れる ---
s_model = base().fit(np.column_stack([X, T]), Y)
def s_tau(Z):
y1 = s_model.predict(np.column_stack([Z, np.ones(len(Z))]))
y0 = s_model.predict(np.column_stack([Z, np.zeros(len(Z))]))
return y1 - y0
# --- T-learner: 処置群・対照群で別々のモデル ---
mu1 = base().fit(X[T == 1], Y[T == 1])
mu0_hat = base().fit(X[T == 0], Y[T == 0])
def t_tau(Z):
return mu1.predict(Z) - mu0_hat.predict(Z)
# --- X-learner: 個別効果を補完してから回帰し、傾向で加重 ---
imp_treated = Y[T == 1] - mu0_hat.predict(X[T == 1]) # 処置群: 観測 − 対照予測
imp_control = mu1.predict(X[T == 0]) - Y[T == 0] # 対照群: 処置予測 − 観測
tau1 = base().fit(X[T == 1], imp_treated)
tau0 = base().fit(X[T == 0], imp_control)
ehat = LogisticRegression(max_iter=1000).fit(X, T)
def x_tau(Z):
g = ehat.predict_proba(Z)[:, 1]
return g * tau0.predict(Z) + (1 - g) * tau1.predict(Z)
# --- 独立なテスト集合で真の τ(x) との誤差と異質性スロープを比較 ---
rng_te = np.random.default_rng(999)
Xte = rng_te.standard_normal((4000, p))
tau_true = tau_true_fn(Xte)
print("\n学習器 RMSE 異質性スロープ(真値3.0) 平均効果(真ATE 1.0)")
for name, fn in [("S-learner", s_tau), ("T-learner", t_tau), ("X-learner", x_tau)]:
est = fn(Xte)
rmse = np.sqrt(np.mean((est - tau_true) ** 2))
slope = np.polyfit(Xte[:, 0], est, 1)[0]
print(f"{name:10s} {rmse:5.3f} {slope:5.3f} {est.mean():5.3f}")
出力は次のとおり。
処置を受けた割合: 0.287
学習器 RMSE 異質性スロープ(真値3.0) 平均効果(真ATE 1.0)
S-learner 1.144 1.988 1.046
T-learner 1.368 2.583 1.227
X-learner 0.722 2.743 1.007
読み取れること。
- S-learner:スロープ 1.99 と、真の異質性 3.0 を大きく平坦化。処置 が 5 つの特徴量に埋もれ、 の交互作用を取りこぼした。「効果はみんな似たようなもの」と過小評価する典型。
- T-learner:スロープ 2.58 と異質性は捉えるが RMSE 1.368 と最悪(高分散)。処置群が 29% しかなく、 がデータ不足で暴れる。
- X-learner:RMSE 0.72 で最小、スロープ 2.74 で最も真値に近く、平均効果も 1.007 と真 ATE をほぼ命中。個別効果を補完して大きい群の情報を借り、傾向で加重した成果。
コード:τ(x) の推定を図示
を動かし他の共変量を 0 に固定したスライスで、各学習器の と真の直線を重ねる。
# === τ(x) の推定を図示 ===
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.linear_model import LogisticRegression
rng = np.random.default_rng(7)
n, p = 1500, 5
X = rng.standard_normal((n, p))
e = 1.0 / (1.0 + np.exp(-(-1.2 + 0.8 * X[:, 0] + 0.8 * X[:, 1])))
T = rng.binomial(1, e)
mu0 = 3 * X[:, 1] + 2 * X[:, 2] + 2 * X[:, 3]
Y = mu0 + (1.0 + 3.0 * X[:, 0]) * T + rng.standard_normal(n)
def base():
return GradientBoostingRegressor(max_depth=3, n_estimators=200, random_state=0)
s_model = base().fit(np.column_stack([X, T]), Y)
mu1 = base().fit(X[T == 1], Y[T == 1]); mu0h = base().fit(X[T == 0], Y[T == 0])
tau1 = base().fit(X[T == 1], Y[T == 1] - mu0h.predict(X[T == 1]))
tau0 = base().fit(X[T == 0], mu1.predict(X[T == 0]) - Y[T == 0])
eh = LogisticRegression(max_iter=1000).fit(X, T)
# X0 を動かし他の共変量は0に固定したスライスで τ̂(x) を描く
grid = np.linspace(-2.5, 2.5, 100)
G = np.zeros((100, p)); G[:, 0] = grid
s_hat = s_model.predict(np.column_stack([G, np.ones(100)])) - s_model.predict(np.column_stack([G, np.zeros(100)]))
t_hat = mu1.predict(G) - mu0h.predict(G)
g = eh.predict_proba(G)[:, 1]
x_hat = g * tau0.predict(G) + (1 - g) * tau1.predict(G)
plt.figure(figsize=(8, 5))
plt.plot(grid, 1.0 + 3.0 * grid, "k--", lw=2, label="真の τ(x)=1+3·X0")
plt.plot(grid, s_hat, label="S-learner")
plt.plot(grid, t_hat, label="T-learner")
plt.plot(grid, x_hat, label="X-learner")
plt.xlabel("効果修飾子 X0"); plt.ylabel("推定された処置効果 τ(x)")
plt.title("メタ学習器による異質処置効果 τ(x) の推定")
plt.legend(); plt.tight_layout(); plt.show()
図では、S-learner の直線が真の傾きより寝て(平坦化)、T-learner は真の直線の周りで暴れ、X-learner が真の直線に最もよく沿う。異質性を見たい目的では学習器の選択が結論を変える。
直観:どれを使うか
- S-learner:効果が小さい/正則化の強い学習器だと効果が 0 方向へ縮む。効果が大きく単純で、データが潤沢なら手軽で良い。
- T-learner:群ごとに自由に当てるので異質性は素直だが分散が大きい。両群が大きく釣り合っているときに向く。
- X-learner:不均衡(片群が小さい)に強い。傾向加重で大きい群の情報を借りる。観察データの CATE 推定では既定の第一候補。
- いずれもDouble/Debiased Machine Learning(DML)の直交化と組み合わせる発展(R-learner・DR-learner)があり、 推論を CATE に持ち込める。次の因果フォレスト(Causal Forest)は honest splitting でこれを木で実現する。
⚠️ よくある誤解・落とし穴
- 「異質性が見えた」≠「本物の異質性」。 T-learner の高分散は、真の効果が一定でも をギザギザにし、偽の異質性を作る。必ず別標本での RMSE や信頼区間で確かめる。
- CATE も識別が前提。 未観測交絡があれば は因果効果にならない。メタ学習器は識別を保証しない。
- 正値性の破れに敏感。 ある で処置群(または対照群)がほぼ居なければ、その領域の は外挿になり が信用できない。
- base learner の正則化が異質性を平坦化する。 とくに S-learner で顕著。学習器とハイパラの選択が結論を左右するので、複数学習器で頑健性を見る。
- 評価が難しい。 の真値は観測できない(個体の反事実は見えない)。擬似データでの検証や、上位 vs 下位で効果差を見る方策評価(policy value)で代用する。
関連ノート
- 因果:Double/Debiased Machine Learning(DML)(直交化・R/DR-learner の土台)/因果フォレスト(Causal Forest)(木で CATE+信頼区間)/識別の仮定
- 機械学習:勾配ブースティング(base learner)/バギングとランダムフォレスト/汎化と過学習・バイアスバリアンス分解