Mímisbrunnr知恵の泉

← 因果推論 一覧

🎓 レベル:発展 | 重要度:B(標準)

📎 前提:バックドア基準と識別 | 識別の仮定 | 逆確率重み付けIPW

要点(BLUF)


1. 概念:2つの推定量を「補正項」で繋ぐ

これまで交絡を消す道具を2つ見た。

AIPW は両者を融合する。E[Y(1)]E[Y(1)] の推定量は

μ^1=1ni=1n[m^1(Ci)+Xi(Yim^1(Ci))e^(Ci)]\hat\mu_1 = \frac{1}{n}\sum_{i=1}^{n}\left[\,\hat m_1(C_i) + \frac{X_i\big(Y_i-\hat m_1(C_i)\big)}{\hat e(C_i)}\,\right]

第1項は回帰予測そのもの。第2項は残差 Ym^1Y-\hat m_1 を IPW で重み付けした補正項E[Y(0)]E[Y(0)] も対称に定義し、ATE^=μ^1μ^0\widehat{\text{ATE}}=\hat\mu_1-\hat\mu_0

flowchart TB
    O["アウトカムモデル m(C) が正しい"] --> A["AIPW 推定量"]
    P["傾向スコア e(C) が正しい"] --> A
    A --> R["どちらか一方が正しければ ATE に一致(二重頑健)"]

2. 識別:なぜ二重頑健になるか(導出)

m1(C)=E[Y(1)C]m_1(C)=E[Y(1)\mid C] を真のアウトカム回帰、e(C)e(C) を真の傾向スコアとし、推定値 m^1,e^\hat m_1,\hat e は誤っているかもしれないとする。AIPW の被加算項を CC で条件づけて期待値を取る。交換可能性 Y(1)XCY(1)\perp X\mid C より E[XC]=e(C)E[X\mid C]=e(C)E[YX=1,C]=m1(C)E[Y\mid X{=}1,C]=m_1(C) なので、

E ⁣[m^1(C)+X(Ym^1(C))e^(C)|C]=m^1(C)+e(C)e^(C)(m1(C)m^1(C))E\!\left[\hat m_1(C) + \frac{X\big(Y-\hat m_1(C)\big)}{\hat e(C)} \,\middle|\, C\right] = \hat m_1(C) + \frac{e(C)}{\hat e(C)}\big(m_1(C)-\hat m_1(C)\big)

右辺を m1(C)m_1(C) 中心に整理すると、

=m1(C)+(1e(C)e^(C))(m^1(C)m1(C))= m_1(C) + \left(1 - \frac{e(C)}{\hat e(C)}\right)\big(\hat m_1(C)-m_1(C)\big)

第2項がバイアス項である。これが消える条件は2通り。

どちらか一方が成り立てば CC 条件付き期待値が真の m1(C)m_1(C) に一致し、外側で平均すれば E[Y(1)]E[Y(1)] になる。これが二重頑健性の正体だ。積 (1e/e^)(m^1m1)(1-e/\hat e)(\hat m_1-m_1)2つの誤差の掛け算なので、片方がゼロなら全体がゼロになる――この構造が鍵である。

❓ 確認:二重頑健は「両方間違っていてもよい」という意味だろうか? 違う。少なくとも片方は正しいことが要る。両方の誤差が残ると、その積がバイアスとして残る(後述の (d))。


3. 実証:片方が誤っても AIPW は当てる

真の傾向スコアとアウトカムがどちらも交互作用(非線形)を含むデータを作る(ATE=2.0\text{ATE}=2.0)。「正しいモデル」は交互作用込み、「誤特定モデル」は線形のみ、として切り替える。回帰調整・IPW・AIPW を4つの状況で比較する。

import warnings
import numpy as np
from sklearn.linear_model import LogisticRegression, LinearRegression

warnings.filterwarnings("ignore")


# === 二重頑健: アウトカムか傾向スコアの片方が誤っても AIPW は ATE を当てる ===
def make_data(seed):
    rng = np.random.default_rng(seed)
    n = 8000
    X1 = rng.normal(0, 1, size=n)
    X2 = rng.normal(0, 1, size=n)
    # 真の傾向スコア・アウトカムはどちらも交互作用(非線形)を含む
    logit_true = 0.7 * X1 + 0.7 * X2 + 0.6 * X1 * X2
    e_true = 1.0 / (1.0 + np.exp(-logit_true))
    T = rng.binomial(1, e_true)
    ATE_true = 2.0
    baseline = 1.0 + 2.0 * X1 + 2.0 * X2 + 2.0 * X1 * X2
    Y = baseline + ATE_true * T + rng.normal(0, 1, size=n)
    return X1, X2, T, Y


def features(X1, X2, correct):
    # correct=True: 交互作用込み(正しい)。False: 線形のみ(誤特定)
    if correct:
        return np.column_stack([X1, X2, X1 * X2])
    return np.column_stack([X1, X2])


def estimate(seed, outcome_correct, ps_correct):
    X1, X2, T, Y = make_data(seed)
    Z_out = features(X1, X2, outcome_correct)
    Z_ps = features(X1, X2, ps_correct)

    # アウトカムモデル(処置群・対照群で別々に回帰し標準化)
    mu1 = LinearRegression().fit(Z_out[T == 1], Y[T == 1]).predict(Z_out)
    mu0 = LinearRegression().fit(Z_out[T == 0], Y[T == 0]).predict(Z_out)
    reg = (mu1 - mu0).mean()

    # 傾向スコアモデル
    e = LogisticRegression(max_iter=1000).fit(Z_ps, T).predict_proba(Z_ps)[:, 1]
    e = np.clip(e, 0.01, 0.99)

    # 安定化(Hajek)IPW
    w1, w0 = T / e, (1 - T) / (1 - e)
    ipw = (w1 * Y).sum() / w1.sum() - (w0 * Y).sum() / w0.sum()

    # AIPW(二重頑健): 回帰予測 + IPW による残差補正
    psi1 = mu1 + T * (Y - mu1) / e
    psi0 = mu0 + (1 - T) * (Y - mu0) / (1 - e)
    aipw = (psi1 - psi0).mean()
    return reg, ipw, aipw


def summarize(label, outcome_correct, ps_correct):
    results = np.array([estimate(s, outcome_correct, ps_correct) for s in range(100)])
    reg, ipw, aipw = results.mean(axis=0)
    print(f"{label}")
    print(f"   回帰調整 {reg:5.3f} | IPW {ipw:5.3f} | AIPW {aipw:5.3f}")


print("真の ATE = 2.000   (100回反復の平均)\n")
summarize("(a) アウトカム誤特定・傾向スコア正しい:", outcome_correct=False, ps_correct=True)
summarize("(b) 傾向スコア誤特定・アウトカム正しい:", outcome_correct=True, ps_correct=False)
summarize("(c) 両方とも正しい            :", outcome_correct=True, ps_correct=True)
summarize("(d) 両方とも誤特定            :", outcome_correct=False, ps_correct=False)

実行結果は次の通り。

真の ATE = 2.000   (100回反復の平均)

(a) アウトカム誤特定・傾向スコア正しい:
   回帰調整 2.910 | IPW 2.036 | AIPW 2.023
(b) 傾向スコア誤特定・アウトカム正しい:
   回帰調整 1.997 | IPW 2.964 | AIPW 1.996
(c) 両方とも正しい            :
   回帰調整 1.997 | IPW 2.036 | AIPW 1.997
(d) 両方とも誤特定            :
   回帰調整 2.910 | IPW 2.964 | AIPW 3.317

出力の意味――ここが二重頑健の核心だ。

(a) と (b) を見比べてほしい。回帰調整は (a) で外し、IPW は (b) で外すのに、AIPW だけが (a) でも (b) でも真値を当てている。片方のモデルが誤ってももう片方が補正する――これが「2回チャンスがある」推定量の威力だ。


4. なぜ強力なのか/何が限界か(直観)

二重頑健性のうれしさは、モデル選択の失敗に対する保険である。観察データでは、結果の関数形も処置の確率も真の形は分からない。AIPW なら「2つのモデルのうち少なくとも1つを当てればよい」ので、当てる確率が上がる。

数理的には、バイアスが2つの誤差の (1e/e^)(m^m)(1-e/\hat e)(\hat m-m) になることが効いている。各モデルの誤差が小さければ、その積はさらに小さい(二次のバイアス)。この性質は、mmee を機械学習で柔軟に推定したい場合に決定的になる。MLは単体では正則化バイアスを持ち込むが(MLをそのまま使うと因果を誤る理由)、AIPW 型の補正とクロスフィッティングを組み合わせると、そのバイアスが二次に抑えられる。これを定式化したのがDouble/Debiased Machine Learning(DML)であり、AIPW はその古典的な原型にあたる。

限界は明確だ。(d) が示すように、両方のモデルが誤れば二重頑健性は無力。また AIPW は IPW を内包するため、正値性が崩れれば同じく分散が膨らむ。二重頑健は「関数形の誤特定」への保険であって、未観測交絡や正値性違反(識別の仮定)を救うものではない。


⚠️ よくある誤解・落とし穴


関連ノート