Lecture 11:予測から意思決定へ:ターゲティングと離散的施策最適化

計量経済学II

作者

Kei Ikegami

重要この講義で押さえたいこと
  1. 「解約リスクが高い顧客に施策を打つ」という直感が、しばしば間違っていることを、Ascarza (2018) のRCTの教訓として理解する。解約リスクの高さ(predicted churn)と、施策への感応度(treatment effect)はまったく別の量である。
  2. 二値の潜在アウトカムが作る説得可能(persuadables)・鉄板(sure things)・見込みなし(lost causes)・寝た子を起こす(sleeping dogs)の4類型と、観測可能な集団平均であるCATEを区別する。
  3. CATE(条件付き平均処置効果)\(\tau(x)\) を、RCTデータからT-learnerとcausal forestで推定する方法を身につけ、過学習を防ぐための sample splitting / honesty の直感を持つ。
  4. Qini曲線(upliftカーブ)を自分の手で実装し、「churnスコア順に配る」ことが「ランダムに配る」ことより悪い場合すらあることを、シミュレーションで確認する。
  5. IPW(逆確率重み付け)による政策価値評価を使えば、新しい配分ルールを、過去のRCTログから再実験せずに評価できることを理解し、実装できるようになる。
  6. 予算制約下での配分(top-k、閾値ルール)、policy learningの考え方(Kitagawa and Tetenov 2018)、観察データでCATEを推定する際のDML(Chernozhukov et al. 2018)の位置づけを概念として押さえる。

前回とのつながり・今回のゴール

第9回では、同じ価格・広告・商品属性に対して消費者ごとに反応が異なるという「異質性」を階層モデルで扱い、小さいセグメントの効果推定がどれだけ暴れやすいか(shrinkageがなぜ必要か)を学んだ。第10回では、購買履歴データからBG/NBDのような統計モデルを使って、個人ごとの将来購買・離脱確率・顧客生涯価値(CLV)を予測する方法を学んだ。そして第10回の最後に、極めて重要な警告を出した。CLVは予測であって因果ではない、というものだ。CLVが高い(あるいは解約しそうな)顧客のリストは作れるが、そのリストの「正しい使い方」はまだ何も説明していなかった。

今回はその宿題を完全に回収する回である。第9-10回で手に入れた「顧客ごとの価値・購買性向の予測」と、これから学ぶ「施策の因果効果」を組み合わせて、誰にどの施策を配るべきかという意思決定問題を扱う。これは第1回で最初に立てた「予測 vs 因果 vs 反実仮想」という対比の、最終的な実務的回収でもある。予測モデルがどれだけ優れていても、それをそのまま意思決定ルールに使うと、大失敗することがある。今回はその失敗のメカニズムを正面から解剖し、正しい代替手段(uplift modeling、policy learning、IPWによる政策評価)を身につける。

また、今回は第7回で学んだRCT(A/Bテスト)のデータを、これまでで最も徹底的に使い倒す回でもある。第7回では実験の設計(ランダム化推論、検出力・MDE、ITT/TOT、interference/SUTVA違反など)を学んだが、今回はその実験のログデータをターゲティング・ポリシーの評価に転用するという、実務的にきわめて価値の高い技術を学ぶ。

次回(第12回)はゲスト講義(企業エコノミストによる実務講義)で、今回学んだ内容が実際の企業でどう使われているかを直接聞く回になる。そして第13回からは、このコースの最後のパートである「動学」(動学的離散選択、dynamic pricing)に入っていく。

1. Ascarzaの教訓:解約リスクが高い人に施策を打つ、は正しいか

誰もがやりそうな発想

第10回でP(alive)(生存確率)やCLVのランキングを作った。次に自然に浮かぶ発想は、「解約しそうな顧客(P(alive)が低い、あるいはchurnスコアが高い顧客)に、引き留め施策(クーポン、電話サポート、割引オファー)を優先的に打とう」というものだ。これは実務でもっともよく見る打ち手であり、直感的にも「困っている人を助ける」という発想と親和性が高い。

ところが、Ascarza (2018) は、この発想が間違っている可能性があることを、実際の通信会社とメディア企業のRCTデータを使って示した。論文のタイトルがそのものずばり “Retention Futility”(解約引き留めの徒労)である。

何が起きているのか。解約リスクが高いこと(\(P(\text{churn} \mid X)\) が高いこと)と、引き留め施策への感応度(\(\tau(x)\) が大きいこと)は、まったく別の量である。 解約リスクが非常に高い顧客というのは、多くの場合「もう決めている」顧客であり、クーポンを1枚渡したところで気持ちは変わらない。逆に、解約するかどうかまだ迷っている中間層の顧客にこそ、施策が効く余地がある。

警告この分析、どこがまずい?

「churn予測モデルを作って、リスク上位X%に施策を打つ」というプロジェクトが社内で承認されたとする。この設計のどこに危うさがあるか、一度立ち止まって考えてほしい。

churn予測モデルが答えているのは「この人は将来離脱しますか」という予測の問いである。しかし意思決定に必要なのは「この人に施策を打てば離脱を防げますか」という因果の問いである。この2つの問いに答えるモデルは、原理的に別物であり、同じデータ・同じ予測モデルから自動的に後者が手に入るわけではない。これが今回の講義全体を貫く中心的なメッセージであり、predict-then-actの罠と呼ぶことにする。

predict-then-actの罠

この失敗パターンを、本講義では便宜上 predict-then-act(予測してから行動する)の罠と呼ぶ(特定の推定法の標準名ではない)。この単純な意思決定フローは、次のような暗黙の前提を置いている。

\[ \text{予測スコアが高い} \implies \text{施策の効果も大きい} \]

この前提が成り立つ保証はどこにもない。むしろ、解約リスクのように「その人の状態」を表す予測スコアと、「施策への反応」という別の量が相関するかどうかは、経験的に確かめるべき問題であり、しばしば無関係、時には逆相関する。この講義の後半で、まさにこの逆相関が起きるシミュレーションを作り、Ascarzaの結果を自分の手で追体験する。

ノートビジネスの現場で:「解約予測モデルできました」レビュー会議

データサイエンスチームが「解約予測AUCが0.85のモデルができました。このスコアの上位20%に集中的にリテンション施策を打ちましょう」と提案してきたとする。この提案を鵜呑みにせず、会議で聞くべき質問リストを整理しておこう。

  • 「このスコア、過去にリテンション施策を打った人・打たなかった人でランダム化した実験データで検証しましたか?」(=スコアと介入効果の相関を検証したか)
  • 「スコア上位の人に施策を打った場合と打たなかった場合を比較するホールドアウト(一部ランダムに施策を打たない群)は用意しますか?」(=評価可能な設計になっているか)
  • 「このスコアが高い人は、そもそも施策なしでも引き留められる(sure things)か、施策を打っても引き留められない(lost causes)人ではないですか?」
  • 「過去に類似の施策をランダム化して評価したデータはありますか? それを使って uplift モデルを作れないですか?」

この質問リストに答えられないプロジェクトは、predict-then-actの罠に陥っている可能性が高い。

2. 顧客の4類型:persuadables, sure things, lost causes, sleeping dogs

uplift modelingの文献でよく使われる、顧客を4つに分類する古典的な整理を導入する。ある施策(クーポン、電話、メールなど)を受け取った場合と受け取らなかった場合で、顧客の行動(ここでは「解約するかどうか」)がどう変わるかによって、顧客を次の4つに分ける。

タイプ 施策なしでの行動 施策ありでの行動 施策の効果
説得可能(persuadables) 解約する 解約しない 施策が効く(プラス)
鉄板(sure things) 解約しない 解約しない 効果なし(もともと残る)
見込みなし(lost causes) 解約する 解約する 効果なし(もともと去る)
寝た子を起こす(sleeping dogs) 解約しない 解約する 施策が逆効果(マイナス)

継続者数を増やすのは persuadables であり、sleeping dogs では逆に減る。sure things と lost causes の個人処置効果は0である。ただし、これはアウトカムの効果の分解であり、利益にはクーポンコストや継続の価値も必要である。コストが正なら、効果0の二類型への配布も純利益を下げる。

警告4類型とCATEは別物

4類型は各個人の組 \((Y_i(0),Y_i(1))\) で定まる principal stratum である。しかし現実には同じ人の \(Y_i(0)\)\(Y_i(1)\) を同時には観測できないため、通常は個人を一類型に特定できない。一方、CATEは \(X=x\) の集団における平均である。二値アウトカムなら

\[ \tau(x)=P(\text{persuadable}\mid X=x)-P(\text{sleeping dog}\mid X=x). \]

したがって \(\tau(x)<0\) は「その集団の大半がsleeping dogs」を意味しない。sleeping dogs の確率が persuadables の確率を上回る、という平均的な差を意味する。

churnスコア(解約リスクの予測値)でリスト上位を狙うと、実際には persuadables だけでなく、sure things、lost causes、sleeping dogs も混ざりうる。スコアは無施策時の解約を予測しているだけで、施策への反応を分類してはいない。これが「churnスコア上位を狙う」という発想が失敗しうる理由である。

負の効果が生じうるメカニズム

施策が逆効果になる経路として、例えば次の仮説が考えられる。

  • 解約引き留めの電話:普段はサービスをそれほど意識せずに使っている顧客に「解約をお考えではないですか」という電話をかけると、かえって「そういえば解約という選択肢もあったな」と顧客に思い出させてしまい、解約率が上がることがある。
  • 退会確認メールでの逆効果キャンペーン:休眠顧客の掘り起こしキャンペーンで送ったメールが、逆に「このサービス、まだ契約していたのか。この機会に解約しよう」というきっかけを与えてしまうことがある。
  • 値引きオファーの逆効果:不満を持っていない顧客に「特別値引き」のオファーを送ると、「そもそも値引きが必要なほど、このサービスは値段に見合わない価値なのか」と顧客に疑念を持たせてしまうことがある。

これらは直感を与える仮説的メカニズムであり、個別事例だけから sleeping dogs の存在や割合を断定してはいけない。確認にはランダム化実験と十分な標本が必要である。後半では、実際のカタログ郵送RCTを用いた Hitsch, Misra, and Zhang (2024) の政策評価を紹介する。

ノートビジネスの現場で:ベンダーの「AIターゲティング」評価チェックリスト

外部ベンダーから「AIを使った高精度ターゲティングツール」を売り込まれたとき、契約前に確認すべきチェックリストを持っておこう。

  • そのツールの「効果」は、ランダム化されたholdout群との比較で評価されているか。単に「ターゲットにした群」と「しなかった群」を比較しただけなら、両群の性質の違い(選択バイアス)が効果に紛れ込んでいる可能性が高い。
  • ツールが出力するのは「反応しやすさ(予測スコア)」なのか、それとも「施策への感応度(uplift)」なのか。ベンダーの説明資料でこの2つの言葉が区別されずに使われている場合は要注意である。
  • 過去の導入事例で、施策を打った顧客群の中に sleeping dogs(施策が逆効果だった層)がいなかったかどうかの分析があるか。

3. CATEの推定:T-learnerとcausal forest

ここから先は、機械学習の道具を因果推論に応用する話になる。Athey and Imbens (2019) は、経済学者が知っておくべき機械学習の手法(決定木、ランダムフォレスト、正則化など)を整理したレビュー論文の中で、「機械学習は予測に強いが、そのままでは因果推論の道具にはならない。ただし、予測のための柔軟な関数近似という機械学習の強みを、sample splittingなどの工夫と組み合わせれば、異質処置効果の推定という因果推論の問題にも応用できる」という見取り図を示している。これから見るT-learnerとcausal forestは、まさにその見取り図の具体例である。

定義とRCTでの識別

第9回で使った記法を思い出す。処置 \(D_i \in \{0, 1\}\)、潜在アウトカム \(Y_i(1), Y_i(0)\)条件付き平均処置効果(conditional average treatment effect, CATE)

\[ \tau(x) = E[Y_i(1) - Y_i(0) \mid X_i = x] \]

と定義する。第7回で学んだように、RCTデータ(\(D_i\) がランダムに割り当てられている)であれば、\(\tau(x)\) は「\(X=x\) の近傍にいる人々の、処置群と対照群の平均の差」として識別できる。

\[ \tau(x) = E[Y_i \mid X_i = x, D_i = 1] - E[Y_i \mid X_i = x, D_i = 0] \]

問題は、\(x\) が連続変数や高次元の共変量ベクトルを含む場合、「\(X=x\) の近傍」を厳密に取ると、その近傍にいるサンプルサイズが小さくなり、平均の差の推定が不安定になることだ。ここで第9回の教訓(小さいセグメントの効果推定は暴れる)がそのまま関係してくる。CATE推定は、まさにこの問題への機械学習的な処方箋を必要とする。

まずシミュレーションのDGPを作る

抽象的な話が続いたので、具体的な状況設定を作って手を動かそう。ある通信キャリアの解約引き留めクーポンRCTを考える。契約者20,000人に対して、月額料金の一部を割り引くクーポンを50%の確率でランダムに配布し(\(D_i\))、翌月に解約したかどうか(\(\text{churn}_i\))を観測したとする。

観測される共変量 \(X\) は次の5つとする。

  • tenure_months:契約継続月数
  • usage_hours:週あたりの利用時間
  • billing_trouble:過去に請求トラブル(料金の問い合わせ・クレーム)があったかどうか
  • price_sensitivity:価格感応度を示す潜在変数(アンケートや行動データから推定されたスコアと考える)
  • num_devices:契約に紐づくデバイス数

DGPは次のように設計する。まず、クーポンなしでの解約確率 \(P(\text{churn}=1 \mid X, D=0)\) を、ロジスティックな形で定める(利用時間が長いほど・契約が長いほど解約しにくく、請求トラブルがあるほど解約しやすい)。そのうえで、クーポンの効果 \(\tau(x)\)(クーポンが解約確率を何ポイント下げるか)を、次のように非単調な形で作る。

  • 解約リスクが中位の層(persuadablesが多いと想定される層)で効果が大きい。
  • 解約リスクが非常に高い層では効果がほぼゼロ(もう決めている、lost causes寄り)。
  • 請求トラブルがあった層では、平均的な効果がになりやすい。これはその層でsleeping dogsの確率がpersuadablesの確率を上回る設定であり、層の全員がsleeping dogsという意味ではない。
n <- 20000

tenure_months <- runif(n, 1, 60)
usage_hours <- rgamma(n, shape = 2.0, scale = 3.0)
price_sensitivity <- rnorm(n, 0, 1)
num_devices <- sample(1:4, n, replace = TRUE)

# 請求トラブルは、価格感応度・利用時間とは別の軸として生成する
# (解約リスクの高さとほどよく相関しつつ、決定的に一致させないため)
logit_billing <- -1.6 + 0.25 * price_sensitivity - 0.03 * usage_hours
p_billing <- 1 / (1 + exp(-logit_billing))
billing_trouble <- rbinom(n, 1, pmin(pmax(p_billing, 0.02), 0.75))

# クーポンなしでの解約確率 P(churn=1 | X, D=0)
logit_churn0 <- -1.0 - 0.05 * usage_hours - 0.012 * tenure_months +
  0.9 * billing_trouble + 0.35 * price_sensitivity
p_churn0 <- 1 / (1 + exp(-logit_churn0))

# 解約リスクスコア(企業が実際に構築するようなスコア。真のp_churn0にノイズが乗ったもの)
risk_score <- pmin(pmax(p_churn0 + rnorm(n, 0, 0.02), 0.001), 0.999)

# クーポンのランダム割り当て(50%)
D <- rbinom(n, 1, 0.5)

# 潜在アウトカムの確率を作るための効果index
mid_risk_bump <- exp(-0.5 * ((p_churn0 - 0.25) / 0.09)^2)
tau_index <- 0.16 * mid_risk_bump -
  0.22 * billing_trouble -
  0.05 * (p_churn0 > 0.5)

# 確率を切り詰めた後の差こそが、継続確率スケールの真のCATE
p_churn0_clipped <- pmin(pmax(p_churn0, 0.001), 0.999)
p_churn1 <- pmin(pmax(p_churn0_clipped - tau_index, 0.001), 0.999)
tau_true <- p_churn0_clipped - p_churn1

churn_potential_0 <- rbinom(n, 1, p_churn0_clipped)
churn_potential_1 <- rbinom(n, 1, p_churn1)
stay_potential_0 <- 1 - churn_potential_0
stay_potential_1 <- 1 - churn_potential_1
principal_stratum <- case_when(
  stay_potential_0 == 0 & stay_potential_1 == 1 ~ "persuadable",
  stay_potential_0 == 1 & stay_potential_1 == 1 ~ "sure thing",
  stay_potential_0 == 0 & stay_potential_1 == 0 ~ "lost cause",
  stay_potential_0 == 1 & stay_potential_1 == 0 ~ "sleeping dog"
)
churn_obs <- ifelse(D == 1, churn_potential_1, churn_potential_0)
stay_obs <- 1 - churn_obs   # Y = 継続したかどうか(1=継続)。以下ではこちらを主に使う

# 学習用60%と、モデルやルールの構築に一切使わない最終評価用40%を先に固定
set.seed(202611)
train_idx <- sample(seq_len(n), size = floor(0.60 * n), replace = FALSE)
sample_role <- ifelse(seq_len(n) %in% train_idx, "train", "test")

coupon_df <- data.frame(
  tenure_months = tenure_months,
  usage_hours = usage_hours,
  billing_trouble = billing_trouble,
  price_sensitivity = price_sensitivity,
  num_devices = num_devices,
  risk_score = risk_score,
  D = D,
  churn = churn_obs,
  stay = stay_obs,
  stay_potential_0 = stay_potential_0,
  stay_potential_1 = stay_potential_1,
  principal_stratum = principal_stratum,
  tau_true = tau_true,
  p_churn0 = p_churn0_clipped,
  sample_role = sample_role
)

train_df <- coupon_df %>% filter(sample_role == "train")
test_df <- coupon_df %>% filter(sample_role == "test")

head(coupon_df)
  tenure_months usage_hours billing_trouble price_sensitivity num_devices
1     51.959920   9.7871155               0         0.7747394           4
2      4.820909  10.4073608               1         0.2416795           3
3     52.302443   4.8611489               0        -1.1610065           2
4      9.153096   7.8042770               0        -0.9054767           4
5     45.434875   0.5849181               0        -0.4500357           1
6     14.422976   2.6894053               0         0.8619469           2
  risk_score D churn stay stay_potential_0 stay_potential_1 principal_stratum
1 0.14046808 1     0    1                1                1        sure thing
2 0.33767818 0     1    0                0                1       persuadable
3 0.06791045 1     0    1                1                1        sure thing
4 0.15837206 1     0    1                1                1        sure thing
5 0.15594658 0     0    1                1                1        sure thing
6 0.29432414 0     1    0                0                1       persuadable
     tau_true   p_churn0 sample_role
1  0.07259155 0.13684847       train
2 -0.13982752 0.35580214       train
3  0.03496874 0.09304251       train
4  0.07560451 0.13979810       train
5  0.08666216 0.15033489        test
6  0.15690459 0.26778994        test

このシミュレーションの肝は、tau_true(真の効果)が risk_score(解約リスク)と単調な関係にないという点である。実際に確認してみよう。

ggplot(coupon_df, aes(x = risk_score, y = tau_true)) +
  geom_point(alpha = 0.15, color = "steelblue", size = 0.8) +
  geom_smooth(se = FALSE, color = "firebrick", linewidth = 1) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray40") +
  labs(x = "解約リスクスコア(risk_score)", y = "真のクーポン効果 tau(x)",
       title = "解約リスクが高いほど効果が大きい、わけではない")

解約リスクスコアと真のクーポン効果tau(x):中位リスクで山、高リスクで低下(山型・非単調)
cor(coupon_df$risk_score, coupon_df$tau_true)
[1] -0.4461945

図の通り、リスクスコアが低〜中位の範囲では効果は正でむしろリスクが上がるほど大きくなるが、ある水準を超えると急激に効果が小さくなり、最終的には(請求トラブル層を中心に)マイナスにまで落ち込む。相関係数も負の値になっており、「リスクが高いほど効果が大きい」という直感は、このDGPでは真逆に近い。これがAscarzaの発見を、シミュレーションの中で再現したものだと理解してほしい。

請求トラブルの有無で、真の効果tau_trueの分布がどう変わるかも見ておこう。

ggplot(coupon_df, aes(x = tau_true, fill = factor(billing_trouble))) +
  geom_histogram(alpha = 0.6, position = "identity", bins = 40) +
  geom_vline(xintercept = 0, linetype = "dashed", color = "gray30") +
  scale_fill_manual(values = c("steelblue", "firebrick"),
                     labels = c("請求トラブルなし", "請求トラブルあり")) +
  labs(x = "真のクーポン効果 tau(x)", y = "顧客数", fill = NULL,
       title = "請求トラブルの有無で、クーポン効果の分布がまったく異なる")

billing_troubleの有無別のtau(x)の分布:請求トラブルがある層はマイナス側に偏る

請求トラブル層ではCATEの分布がマイナス側に移る。ただし、この図が描いているのは条件付き平均効果であり、principal strataの個人ラベルではない。シミュレーションでは両方の潜在アウトカムを保存したので、負のCATE層の中身を直接数えられる。

strata_by_cate <- coupon_df %>%
  mutate(cate_group = if_else(tau_true < 0, "tau(x) < 0", "tau(x) >= 0")) %>%
  count(cate_group, principal_stratum) %>%
  group_by(cate_group) %>%
  mutate(share = n / sum(n)) %>%
  ungroup()

knitr::kable(strata_by_cate, digits = 3,
             col.names = c("CATE層", "principal stratum", "人数", "層内比率"))
CATE層 principal stratum 人数 層内比率
tau(x) < 0 lost cause 513 0.176
tau(x) < 0 persuadable 493 0.169
tau(x) < 0 sleeping dog 898 0.308
tau(x) < 0 sure thing 1016 0.348
tau(x) >= 0 lost cause 221 0.013
tau(x) >= 0 persuadable 2574 0.151
tau(x) >= 0 sleeping dog 994 0.058
tau(x) >= 0 sure thing 13291 0.778

表のとおり、\(\tau(x)<0\) の層にも4類型が混在する。このseedでは sleeping dogs は 30.8%、persuadables は 16.9% であり、負のCATEは「その人がsleeping dogだ」という分類ではない。同じ \(X\) をもつ集団内でsleeping dogsの確率がpersuadablesの確率より高いことを表す。

billing_trouble は解約確率を引き上げると同時に、効果indexを押し下げる。そのため「リスクが高いが、平均的にはクーポンが逆効果」という層が生じる。これを作るのはprincipal strataの構成比であり、billing_trouble == 1 が個人のsleeping-dogラベルそのものではない。

重要ここからの5ステップ
  1. 施策なしのリスクとCATEを区別する。
  2. train_df でCATEモデルを学習する。
  3. 価値・コスト・予算制約を入れて配分ルールを固定する。
  4. 学習に使っていない test_df でQini型カーブとIPW政策価値を評価する。
  5. 導入後もランダムholdoutを残し、効果の劣化や環境変化を監視する。

T-learner:処置群・対照群それぞれで予測モデルを作る

CATEを推定するもっとも素朴な方法がT-learnerである。考え方は単純明快。

  1. 処置群(\(D=1\))のデータだけを使って、\(Y\)\(X\) に回帰するモデル \(\hat\mu_1(x)\) を学習する。
  2. 対照群(\(D=0\))のデータだけを使って、同様に \(\hat\mu_0(x)\) を学習する。
  3. \(\hat\tau(x) = \hat\mu_1(x) - \hat\mu_0(x)\) を、その人のCATE推定値とする。

「T」はtwo-model(2つのモデル)のTである。RCTデータであれば、処置群・対照群それぞれの中で \(X\)\(Y\) の関係を推定するのは、通常の回帰と何も変わらない。ここでは feols()(線形確率モデルとして)を使って、2つの回帰を別々に推定する。

mu1_fit <- feols(stay ~ tenure_months + usage_hours + billing_trouble +
                    price_sensitivity + num_devices,
                  data = train_df %>% filter(D == 1))
mu0_fit <- feols(stay ~ tenure_months + usage_hours + billing_trouble +
                    price_sensitivity + num_devices,
                  data = train_df %>% filter(D == 0))

test_df$mu1_hat <- predict(mu1_fit, newdata = test_df)
test_df$mu0_hat <- predict(mu0_fit, newdata = test_df)
test_df$tau_hat_tlearner <- test_df$mu1_hat - test_df$mu0_hat

cor(test_df$tau_hat_tlearner, test_df$tau_true)
[1] 0.9074189

真値との相関は比較的高く出るはずである。ただし、これは「線形なDGPに線形モデルを当てた」からではない。このDGPは、ベースライン解約確率にロジスティック関数、効果にGaussian型の山、閾値指示関数、最後に確率のクリッピングを含む非線形なDGPである。それでも相関が高くなりやすいのは、標本が大きく、効果を作る観測変数がすべてモデルに入り、今回のパラメータでは支配的な低次のsignalを線形確率モデルが拾えるからである。したがってLPMは大まかな順位を捉えても、山の局所的な曲率、閾値での変化、クリッピング境界を正確に再現するわけではない。図で確認しよう。

ggplot(test_df, aes(x = tau_true, y = tau_hat_tlearner)) +
  geom_point(alpha = 0.1, color = "steelblue", size = 0.6) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "gray40") +
  labs(x = "真のtau(x)", y = "T-learnerによる推定値",
       title = "T-learner:真値との対応関係")

T-learnerによるtau(x)の推定値 vs 真値:概ね対角線上に乗るが、ばらつきもある

T-learnerの弱点:2本の推定誤差が蓄積する

T-learnerでは \(\hat\tau(x) = \hat\mu_1(x) - \hat\mu_0(x)\) という2つの推定量の差を作る。その分散は一般に

\[ \operatorname{Var}(\hat\tau(x)) =\operatorname{Var}(\hat\mu_1(x))+\operatorname{Var}(\hat\mu_0(x)) -2\operatorname{Cov}(\hat\mu_1(x),\hat\mu_0(x)) \]

である。処置群と対照群の分離した標本でモデルを学習すると、共分散をほぼ0と見なせる場面では2つの分散が加わる。ただし「分散が2倍」と言えるのは、両方の分散が等しいなどの追加条件があるときだけである。正確な教訓は、二本の局外モデルを別々に学習するため、各モデルのノイズやミススペシフィケーションがCATEに蓄積しうる、ということである。

\(\hat\mu_1(x)\) は処置群、\(\hat\mu_0(x)\) は対照群だけで学習するため、各学習標本は全体より小さい。柔軟なモデルではそれぞれの過学習ノイズが差に入る。これは「差だから必ず2倍」という機械的な法則ではないが、T-learnerで差が不安定になりうる重要な理由である。

この問題への対処として、機械学習の文脈で発達してきたのが sample splitting(標本分割)honesty(正直な木) という考え方である。

  • sample splitting:モデルの構造(例えば決定木の分岐点)を決めるために使うデータと、各葉の中で効果を推定するために使うデータを分ける。こうすることで、「効果が大きく見えたから」という理由で分岐を選び、その同じデータで効果を測る、という循環(過学習の温床)を避けられる。
  • honesty:各木の内部で、木の構造を決める標本と葉の効果を推定する標本を分ける。同じ観測が別の木で別の役割を担うことはある。honestyは適応的な分岐のバイアスを減らす重要な材料だが、それだけで有限標本の信頼区間を自動的に保証するわけではない。

causal forest:分割基準を「効果の異質性」にする

causal forest(Wager and Athey 2018)は、ランダムフォレストの発想をCATE推定に応用したものである。通常のランダムフォレスト(回帰木の集合)は「\(Y\) の予測誤差を最小化するように木を分割する」が、causal forestは「分割の前後で処置効果 \(\tau(x)\) の異質性が最大になるように木を分割する」という基準を使う。つまり、単に \(Y\) が予測しやすい方向に分割するのではなく、「この分割によって、左右の葉で効果の大きさがどれだけ違って見えるか」を基準にする。

さらにcausal forestは、上で説明したhonest treesの考え方を組み込む。各木の内部で分岐選択と葉の効果推定を分け、多数の木を平均する。理論上の一致性や漸近正規性には、honestyに加えてsubsampling、overlap、葉のサイズなどの正則条件も必要である。

これをgrfパッケージのcausal_forest()で実行する。RCTの既知の割当確率 \(e(x)=0.5\)W.hat として渡し、学習専用データでフォレストを作る。

X_train <- as.matrix(train_df %>%
  select(tenure_months, usage_hours, billing_trouble, price_sensitivity, num_devices))
X_test <- as.matrix(test_df %>%
  select(tenure_months, usage_hours, billing_trouble, price_sensitivity, num_devices))
Y_train <- train_df$stay
W_train <- train_df$D

cf <- causal_forest(X_train, Y_train, W_train,
                    W.hat = rep(0.5, nrow(train_df)))
test_df$tau_hat_cf <- predict(cf, X_test)$predictions

cor(test_df$tau_hat_cf, test_df$tau_true)
[1] 0.9231339
compare_df <- test_df %>%
  select(tau_true, tau_hat_tlearner, tau_hat_cf) %>%
  pivot_longer(cols = c(tau_hat_tlearner, tau_hat_cf),
               names_to = "method", values_to = "tau_hat") %>%
  mutate(method = recode(method,
                          tau_hat_tlearner = "T-learner",
                          tau_hat_cf = "causal forest"))

ggplot(compare_df, aes(x = tau_true, y = tau_hat)) +
  geom_point(alpha = 0.08, color = "steelblue", size = 0.5) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "gray40") +
  facet_wrap(~ method) +
  labs(x = "真のtau(x)", y = "推定値",
       title = "T-learnerとcausal forestの推定値 vs 真値")

T-learner vs causal forest:どちらもtau(x)の傾向を捉えるが、平滑さや極端な領域の扱いに違いがある

平均処置効果(ATE)も比較しておこう。average_treatment_effect(cf) はフォレストの学習標本を対象にするので、単純な群間差と真値も同じ train_df にそろえる。標本をそろえないと、推定手法の違いに標本構成の違いまで混ざってしまう。

ate_cf <- average_treatment_effect(cf)
ate_naive_train <- mean(train_df$stay[train_df$D == 1]) -
  mean(train_df$stay[train_df$D == 0])

ate_comparison <- data.frame(
  method = c("単純な群間差(train RCT)", "causal forestのATE(train)", "真のATE(train)"),
  estimate = unname(c(ate_naive_train, ate_cf["estimate"], mean(train_df$tau_true)))
)
knitr::kable(ate_comparison, digits = 4,
             col.names = c("手法", "推定値"))
手法 推定値
単純な群間差(train RCT) 0.0648
causal forestのATE(train) 0.0633
真のATE(train) 0.0605

RCTデータなので、train_df 内の単純な群間差でも、その学習母集団のATEを不偏に推定できる。causal forestの狙いは、個人の潜在アウトカム差を言い当てることではなく、共変量で条件付けた平均効果 \(\tau(x)\) を柔軟に推定することにある。

ヒント実証研究コーナー:Wager and Athey (2018)

研究の問い:ランダムフォレストを異質処置効果(CATE)の推定に使う場合、単に予測精度を追い求めるだけでなく、統計的推論(信頼区間の構築、漸近正規性)まで保証する方法はあるか。

データ・アプローチ:この論文は理論論文であり、特定のデータセットを分析するものではない。ランダムフォレストの木の生成過程に、honesty(標本分割)という制約を課した”causal forest”を提案し、その統計的性質を証明している。

識別戦略・モデル:通常のランダムフォレストは、木の構造を決めるためと予測値を計算するために、同じデータを使い回す。著者らは、木の構造を決める標本と、葉ごとの効果を推定する標本を分離する(honest tree)ことで、CATE推定量が漸近的に正規分布に従い、有効な信頼区間を構築できることを示した。

主要な発見:honestyを課したcausal forestは、通常のランダムフォレストに比べて個々の木の予測力はやや落ちるものの、CATE推定量として一致性と漸近正規性を持つことが証明された。これにより、機械学習の柔軟性と、計量経済学が重視する「推論の妥当性」を両立させる道が開かれた。

このトピックとの関係grf::causal_forest()はこの理論と密接に関係するgeneralized random forestの実装である。honestyは適応的バイアスを減らす材料だが、T-learnerの分散を自動的に「治す」ものではない。推論の理論保証は、honesty、subsampling、正則条件などの組合せに基づく。

4. ターゲティングの評価:Qini曲線とupliftカーブ

「churnスコア順」と「uplift順」を比べる

さて、いよいよ本題である。手に入れたCATE推定値 \(\hat\tau(x)\)(あるいは単純な解約リスクスコア)を使って、実際に誰に施策を配ると利益が最大になるかを評価する方法を考える。

評価の基本アイデアは、次の3つのターゲティング方針を比較することである。

  1. churnスコア順:解約リスクスコアが高い順にクーポンを配る。
  2. uplift順:推定されたCATE \(\hat\tau(x)\) が大きい順にクーポンを配る。
  3. ランダム:誰彼構わずランダムに配る(ベンチマーク)。

ここで作るのはQini型の累積uplift曲線である。文献やソフトウェアによって「Qini curve」と「uplift/gain curve」の正規化やベースラインの引き方は異なる。本講義では、横軸を配布割合、縦軸を累積増分継続者数とする定義に固定する。

なぜこの評価がRCTデータで因果的に正当なのかを確認しておく。ここで上位集団の並べ方は train_df で学習し、test_df の処置・アウトカムに触れる前に固定している。すると独立なテスト標本の上位k%の中でも処置はランダムなので、処置群と対照群の平均差がその集団の平均処置効果を推定する。同じテストアウトカムを見ながら並べ方やモデルを選べば、この論理は崩れる。

具体的な計算式は、配布上位 \(k\) 人について

\[ \text{累積増分}(k) = \left(\overline{Y}_{D=1, \text{top-}k} - \overline{Y}_{D=0, \text{top-}k}\right) \times k \]

とする。ランダムに配った場合、この量は \(k\) に比例して増えるはず(傾き一定の直線)であり、これがベンチマークになる。良いターゲティングルールは、この直線よりも急に増分利益を積み上げられるはずである。

qini_curve <- function(order_idx, D, Y, n, fracs = seq(0.02, 1, by = 0.02)) {
  rows <- lapply(fracs, function(f) {
    k <- floor(f * n)
    idx_k <- order_idx[1:k]
    Dk <- D[idx_k]
    Yk <- Y[idx_k]
    incremental <- (mean(Yk[Dk == 1]) - mean(Yk[Dk == 0])) * k
    data.frame(frac = f, k = k, incremental = incremental)
  })
  do.call(rbind, rows)
}

n_eval <- nrow(test_df)
order_by_score <- order(-test_df$risk_score)
order_by_uplift <- order(-test_df$tau_hat_cf)

qini_score <- qini_curve(order_by_score, test_df$D, test_df$stay, n_eval) %>%
  mutate(policy = "churnスコア順")
qini_uplift <- qini_curve(order_by_uplift, test_df$D, test_df$stay, n_eval) %>%
  mutate(policy = "uplift順(causal forest)")

ate_eval <- mean(test_df$stay[test_df$D == 1]) - mean(test_df$stay[test_df$D == 0])
qini_random <- data.frame(
  frac = seq(0.02, 1, by = 0.02),
  incremental = ate_eval * floor(seq(0.02, 1, by = 0.02) * n_eval),
  policy = "ランダム"
) %>% select(frac, incremental, policy)

qini_all <- bind_rows(
  qini_score %>% select(frac, incremental, policy),
  qini_uplift %>% select(frac, incremental, policy),
  qini_random
)
ggplot(qini_all, aes(x = frac, y = incremental, color = policy, linetype = policy)) +
  geom_line(linewidth = 1) +
  geom_hline(yintercept = 0, linetype = "dotted", color = "gray50") +
  labs(x = "配布割合(上位何%に配ったか)", y = "累積の増分継続者数(incremental stays)",
       color = NULL, linetype = NULL,
       title = "churnスコア順 vs uplift順 vs ランダム") +
  theme(legend.position = "bottom")

Qini曲線:churnスコア順のターゲティングは、ランダムにすら劣ることがある

このDGPでは、churnスコア順は多くの配布割合でランダムより悪くなる。これは高リスク層で負のCATEが多いというDGPの帰結であり、その集団の全員がsleeping dogsという意味ではない。一方、学習標本で作ったuplift順は、独立テスト標本でより効率的に増分継続者数を積み上げる。

25%の予算(配布割合0.25)の時点で、3つの方針の増分効果を数値で比較してみよう。

k25 <- floor(0.25 * n_eval)

incremental_at_k <- function(order_idx, D, Y, k) {
  idx_k <- order_idx[1:k]
  Dk <- D[idx_k]; Yk <- Y[idx_k]
  (mean(Yk[Dk == 1]) - mean(Yk[Dk == 0])) * k
}

inc_score_25 <- incremental_at_k(order_by_score, test_df$D, test_df$stay, k25)
inc_uplift_25 <- incremental_at_k(order_by_uplift, test_df$D, test_df$stay, k25)
inc_random_25 <- ate_eval * k25

budget_comparison <- data.frame(
  policy = c("churnスコア順", "uplift順(causal forest)", "ランダム"),
  incremental_stays_at_25pct = c(inc_score_25, inc_uplift_25, inc_random_25)
)
knitr::kable(budget_comparison, digits = 1,
             col.names = c("ターゲティング方針", "25%配布時点の累積増分継続者数"))
ターゲティング方針 25%配布時点の累積増分継続者数
churnスコア順 -46.6
uplift順(causal forest) 292.7
ランダム 115.8

この特定の人工DGPでは、churnスコア順の25%ターゲティングがほぼゼロまたはマイナスになりうる。これはAscarza (2018)の数値の複製ではなく、同論文の「高い解約リスクは高い処置効果を保証しない」という教訓を強調した例である。

ヒント実証研究コーナー:Ascarza (2018)

研究の問い:解約リスクが高い顧客をターゲットにしたリテンション施策は、本当に効果的か。

データ・アプローチ:通信会社1社とメディア企業1社で実施された、大規模なフィールド実験(RCT)のデータを用いている。両社とも、顧客の解約予測モデルを持っており、そのスコアに基づいて顧客をセグメント化した上で、リテンション施策(電話によるプロアクティブな働きかけなど)をランダムに割り当てている。

識別戦略・モデル:RCTによる無作為割り当てを利用し、解約リスクの水準ごとに施策の平均処置効果(CATE)を推定する。特に、解約予測モデルが出すスコアの水準と、実際の施策効果の大きさとの関係を、セグメント別に比較する。

主要な発見:リスクが高いほど施策効果が大きいとは限らず、高リスク層だけを狙う方針は必ずしも費用対効果が高くない。どのリスク層で効果が大きいかは、予測スコアから演繹せず、実験データで確かめる必要がある。

このトピックとの関係:本講義のシミュレーションは、この論文の発見を人工データで再現したものである。「解約リスクの高さ」と「施策への感応度」は別の量であるという教訓は、今回の講義全体を貫く中心的なメッセージであり、この論文がその出発点になっている。

5. 政策の直接評価:IPW(実務のキラー技術)

新しい配分ルールを、RCTログだけで評価する

ここでは、過去のRCTログからあらかじめ固定された配分ルール \(\pi(x)\) の価値を評価する。ルールは別の学習標本で作り、ここでは独立な test_df を一度だけ評価に使う。これがIPW(inverse probability weighting、逆確率重み付け)によるoff-policy evaluationである。

新しい配分ルール \(\pi: X \to \{0, 1\}\)政策価値(policy value)

\[ V(\pi) = E[Y(\pi(X))] \]

と定義する。つまり、「もし全員に \(\pi\) というルールで施策を割り当てたら、平均的なアウトカムはどうなるか」という反実仮想量である。

これを過去のRCTログ(実際の割り当て \(D_i\)、実現したアウトカム \(Y_i\)、割り当て確率 \(P(D_i \mid X_i)\) が既知)から推定する式が、IPW推定量である。

\[ \hat{V}(\pi) = \frac{1}{n} \sum_{i=1}^n \frac{\mathbb{1}\{D_i = \pi(X_i)\} \, Y_i}{P(D_i \mid X_i)} \]

不偏性の直感

この式が何をやっているのか、直感的に理解しておこう。ポイントは、「実際にログの中で \(\pi\) と同じ割り当てを(たまたま)受けた人たち」だけを取り出し、その人たちの割り当て確率の逆数で重み付けするという操作にある。

  • ある人 \(i\) が、新しいルール \(\pi\) ならクーポンをもらう(\(\pi(X_i)=1\))べき人だとする。RCTでは \(D_i=1\) になる確率が \(P(D_i=1\mid X_i)\) である。実際に \(D_i=1\)(クーポンをもらった)人だけを取り出すと、その人たちは「\(\pi\) が実際に実行された世界からのサンプル」とみなせる。
  • ただし、\(D_i=1\) になった人は、母集団全体のうち確率 \(P(D_i=1\mid X_i)\) でしか観測されない。観測されにくい人ほど、大きな重みをつけて「代表」させることで、実際にはRCTの一部でしか実現していない状況を、母集団全体を代表する形に復元できる。

RCTでは \(P(D_i=1\mid X_i) = P(D_i=0\mid X_i) = 0.5\)(既知の定数)なので、この重み付けは単純に「一致した人の \(Y_i\) を2倍する」という操作になる。

\(\hat{V}(\pi)\) の期待値を計算する。\(D_i \perp (Y_i(1), Y_i(0)) \mid X_i\)(RCTなので割り当ては潜在アウトカムと独立)であることを使うと、

\[ E\left[\frac{\mathbb{1}\{D_i = \pi(X_i)\} Y_i}{P(D_i \mid X_i)} \,\middle|\, X_i\right] = \sum_{d \in \{0,1\}} P(D_i=d \mid X_i) \cdot \frac{\mathbb{1}\{d = \pi(X_i)\} \, E[Y_i(d) \mid X_i]}{P(D_i=d \mid X_i)} \]

\(\mathbb{1}\{d=\pi(X_i)\}\) が1になるのは \(d=\pi(X_i)\) のときだけなので、和は1つの項だけが残り、\(P(D_i=d\mid X_i)\) が分母分子で打ち消し合って、

\[ = E[Y_i(\pi(X_i)) \mid X_i] \]

となる。両辺の期待値を \(X_i\) についてもとると \(E[\hat{V}(\pi)] = E[Y(\pi(X))] = V(\pi)\) となり、不偏性が示される。この打ち消し合いこそが「観測されにくい人を重み付けで代表させる」という直感の数式的な表現である。

Rシミュレーション:3つの候補ポリシーをIPWで評価する

「全員に配る」「churnスコア上位25%に配る」「uplift推定値が正の人に配る」という3つの候補ポリシーを、IPWで評価し、標準誤差・信頼区間もあわせて計算する。

ipw_policy_value <- function(policy_mask, D, Y, e = 0.5) {
  p_realized <- ifelse(D == 1, e, 1 - e)
  matched <- (D == 1) == policy_mask
  contrib <- ifelse(matched, Y / p_realized, 0)
  n <- length(Y)
  v_hat <- mean(contrib)
  se_hat <- sd(contrib) / sqrt(n)
  data.frame(v_hat = v_hat, se = se_hat,
             ci_low = v_hat - 1.96 * se_hat, ci_high = v_hat + 1.96 * se_hat)
}

policy_treat_all <- rep(TRUE, n_eval)
policy_top25_score <- seq_len(n_eval) %in% order_by_score[1:k25]
policy_uplift_positive <- test_df$tau_hat_cf > 0

policy_list <- list(
  "全員に配布" = policy_treat_all,
  "churnスコア上位25%" = policy_top25_score,
  "uplift推定値>0" = policy_uplift_positive
)

ipw_results <- do.call(rbind, lapply(names(policy_list), function(nm) {
  res <- ipw_policy_value(policy_list[[nm]], test_df$D, test_df$stay)
  res$policy <- nm
  res$frac_treated <- mean(policy_list[[nm]])
  res
}))

knitr::kable(ipw_results[, c("policy", "frac_treated", "v_hat", "se", "ci_low", "ci_high")],
             digits = 4,
             col.names = c("ポリシー", "配布割合", "IPW政策価値", "SE", "CI下限", "CI上限"))
ポリシー 配布割合 IPW政策価値 SE CI下限 CI上限
全員に配布 1.0000 0.8497 0.0111 0.8281 0.8714
churnスコア上位25% 0.2500 0.8018 0.0110 0.7803 0.8232
uplift推定値>0 0.8555 0.8775 0.0111 0.8558 0.8992
ggplot(ipw_results, aes(x = policy, y = v_hat)) +
  geom_point(size = 3, color = "steelblue") +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.15, color = "steelblue") +
  labs(x = NULL, y = "IPW政策価値(継続率)",
       title = "3つの候補ポリシーの政策価値比較(95%CI)")

候補ポリシーのIPW政策価値:95%信頼区間つきで比較

結果は、学習標本で固定した各ルールを、独立テスト標本に適用したときの政策価値である。このDGPでchurnスコア上位25%が低いのは、その層で平均処置効果が低いからである。個々の政策の利益比較には、さらにクーポンコストを差し引く必要がある。

sd(contrib) / sqrt(n) による通常の標準誤差は、テストデータから独立に固定された政策を条件とし、i.i.d.、overlap、有限分散などを仮定した近似である。評価データ上で閾値やモデルを選び、同じデータで最良値を報告すると選択バイアスが入るため、このCIはそのままでは妥当でない。ハイパーパラメータ調整まで行うなら、学習・validation・最終testの3分割、または外側foldで政策を丸ごと学習するnested cross-fittingが必要である。causal forestのOOB予測だけで、選択後の固定政策に対する通常のi.i.d. SEが保証されるわけではない。

重要ここが核心

RCTログに、候補ルールが選ぶ各行動の正の割当確率(overlap)があり、施策・母集団・環境が将来に運搬可能であれば、導入前のスクリーニングにIPWを使える。ただし、一つのログで大量のルールを探索し続けると評価標本へ過適合する。候補選択にはvalidationを、最終報告には手を触れていないtestを使い、導入後は新しいランダムholdoutで監視する。

6. 予算制約と配分:top-kルールとpolicy learning

予算制約下でのtop-k配分

実務では「全員にクーポンを配る」ことはコスト上できないことが多い。クーポン原資が全顧客の25%分しかない、という制約を考えよう。この場合、単純には推定された効果からコストを引いた「純便益」で並べ、上位k人に配るというルールが自然である。

顧客1人あたりのクーポンコストを \(c\)、継続1件あたりの価値(CLVの増分、第10回の内容と接続)を \(v\) とすると、顧客 \(i\) への純便益は

\[ \hat\tau(x_i) \times v - c \]

と書ける。この量が大きい順に並べ、予算が尽きるところで足切りする、というのがtop-kルールである。コスト \(c\) が全顧客で一定なら、この並び替えは単に \(\hat\tau(x_i)\) で並べ替えるのと同じ順序になる。ただし、これが条件付きで最適なのは、CATEが識別され安定的に運搬できる、価値とコストが既知、個人間のinterferenceがない、施策の効果が配布人数によって変わらない、といった条件の下での「静的な二値の個別配分ルール」の中でである。在庫・混雑・競合反応・長期効果があると、人ごとのCATEを並べるだけではグローバルな最適化にならない。

policy learning:empirical welfare maximization

もう一歩進んだ考え方が、Kitagawa and Tetenov (2018) が提案したpolicy learning(政策学習)である。ここまでは「効果を推定してから、その推定値に基づいてtop-kルールを作る」という2段階の発想だったが、policy learningは、あらかじめ定めた「解釈可能なルールのクラス」の中から、経験的な厚生(現在のデータで測った政策価値の推定値)を最大化するルールを直接探すというアプローチを取る。

例えば、「利用時間が\(h\)時間未満、かつ請求トラブルがない顧客にクーポンを配る」という単純な閾値ルールのクラスを考え、そのクラスの中で\(h\)を動かしながら経験的な政策価値(前節のIPW推定量に相当する量)を最大化する\(h\)を選ぶ、というイメージである。

このアプローチの利点は、(1) ルールが単純で解釈しやすい(現場の営業担当者に説明できる)こと、(2) Kitagawa and Tetenovは、overlap、有界な厚生、ルールクラスの複雑さなどの条件のもとで、学習ルールの後悔(regret:事前に定めたルールクラス内の最適ルールとの厚生の差)の有限標本上界を与えていることである。これは無制限な全政策集合のグローバル最適性を保証する主張ではない。この講義では実装には立ち入らないが、「効果推定→ルール作成」という2段階を経ずに、目的(厚生の最大化)から直接ルールを学習するという発想があることを覚えておいてほしい。

ヒント実証研究コーナー:Kitagawa and Tetenov (2018)

研究の問い:観測データやRCTのデータから、「誰を処置すべきか」という意思決定ルール(treatment assignment policy)を、統計的に最適な形で学習するには、どのような枠組みが良いか。

データ・アプローチ:理論論文であり、特定のデータセットの分析ではないが、労働訓練プログラムへの参加者選定など、政策的な処置割り当ての文脈を念頭に置いている。

識別戦略・モデル:あらかじめ定めた「ルールのクラス」(例えば、特定の共変量に関する単純な閾値ルールの集合)の中で、経験的厚生(empirical welfare:手元のデータで推定される政策価値)を最大化するルールを選ぶという、empirical welfare maximization(EWM)の枠組みを提案している。事前に定めたルールクラス内での母集団最適ルールとの厚生差(後悔、regret)を、統計的決定理論の枠組みで評価している。

主要な発見:上の条件のもとで、ルールクラスの複雑さ(VC次元に相当する指標)に応じた後悔の有限標本上界を示した。この上界は代表的な設定でサンプルサイズの平方根オーダーで縮小する。保証の比較対象はクラス内の最適ルールであり、クラス外により良い政策がないとは主張しない。

このトピックとの関係:本講義で扱ったIPWによる政策価値評価は、「候補となるルールをいくつか用意して比較する」という発想だったが、この論文はさらに一歩進んで「ルールのクラスの中から最良のものを直接探索する」という枠組みを与える。予算制約下での配分ルールを、根拠を持って設計したい場面で、この論文の枠組みが理論的な土台になる。

実データで最適ターゲティングを比べる

ここまでのシミュレーションだけでなく、Hitsch, Misra, and Zhang (2024) は実際のカタログ郵送キャンペーンでターゲティング手法を比較している。

結果は明確で、作成されたすべての最適ターゲティング政策が、全員に送る、または誰にも送らないという一律政策より高い利潤を生んだ。さらに、処置効果を直接推定する方法が、処置別アウトカムを別々に予測する間接的方法より良い利潤を生んだ。一方で、異なる時点の2つのキャンペーンを使った検証は、効果の異質性と政策の時間的な運搬可能性を別々に確認する重要性も示す。

ヒント実証研究コーナー:Hitsch, Misra, and Zhang (2024)

研究の問い:異質処置効果(CATE)を機械学習で推定し、それに基づいて個別化したターゲティングポリシーを作ることは、実際の企業の意思決定においてどれだけの経済的価値を生むか。

データ・アプローチ:高次元の顧客特性とランダムな郵送割当を含む、企業のカタログ郵送データを使う。約1年離れた2つのキャンペーンにより、ターゲティング利潤と時間的運搬可能性を検討する。

識別戦略・モデル:ランダム割当で異質処置効果を識別し、処置別アウトカムの予測から差を取る間接法と、処置効果を直接学習する方法を比較する。各推定値から利潤最大化ポリシーを構築する。

主要な発見:すべての推定最適ターゲティング政策は、全員処置・全員非処置の一律政策より大きい利潤を生んだ。特に、処置効果を直接学習する手法は、間接的なアウトカム予測法を大きく上回った。

このトピックとの関係:個別化の価値を「処置効果の予測精度」だけではなく、一律政策に対する利潤の差で評価する実証例である。ただし、論文で一律政策を上回ったことと、別の企業・時期でも必ず上回ることは別問題である。実装コストと独立test/holdoutでの価値差は引き続き確認する。

ノートビジネスの現場で:クーポン原資配分の年次計画

マーケティング部門が来年度のリテンションクーポン予算(例えば全顧客の20%分)をどう配分するか計画する場面を考えよう。実務的な意思決定プロセスは、次のようなステップを踏むことが望ましい。

  1. RCTログを学習・validation・最終testに分け、学習部分でCATEを推定する。
  2. 価値、コスト、予算制約を入れ、validationで候補ルールと閾値を選ぶ。
  3. 手を触れていないtestで、個別化政策と全員処置・全員非処置をIPWで比較する。価値差と実装コストを見て採用を決める。
  4. 導入後もランダムholdoutを残し、翌年度の評価と運搬可能性の監視に使う。

7. 観察データでCATEを推定するなら:DMLへの一言

ここまでの議論はすべてRCTデータを前提にしていた。観察データで標準的なCATEを識別するには、SUTVAに加えて、少なくとも観測済み \(X\) を条件とした非交絡

\[ (Y(0),Y(1))\perp D\mid X \]

とoverlap \(\varepsilon<e(X)<1-\varepsilon\) が必要である。つまり、収集した \(X\) で処置選択の違いを十分に説明でき、どの比較可能な層にも処置・非処置の両方がある、という仮定である。

この識別仮定の下で局外関数を柔軟に推定する枠組みが、Chernozhukov et al. (2018) のDML(double/debiased machine learning)である。DMLは未観測交絡を検出・除去する手法ではなく、識別を作るものでもない。あくまで識別仮定が満たされた後の推定・推論を改善する。

  • CATE推定において、傾向スコア \(e(x) = P(D=1\mid X=x)\) や、アウトカムの条件付き期待値 \(E[Y\mid X=x]\) といった「局外パラメータ(nuisance parameter)」を、機械学習(ランダムフォレスト、勾配ブースティングなど)で柔軟に推定する必要が出てくる。
  • 単純に機械学習の予測値をそのまま使うと、機械学習特有の推定誤差(正則化バイアスや過学習)が、知りたい因果パラメータの推定に悪影響を及ぼすことが知られている。
  • DMLは、直交化cross-fittingを組み合わせ、局外推定誤差の一次の影響を抑える。適切な収束率などの条件下で \(\sqrt n\) 漸近正規性が得られる代表的な対象は、ATEや低次元係数・投影などの低次元パラメータである。高次元の \(x\) ごとのCATE曲面全体に、同じ主張をそのまま適用してはいけない。

つまりDMLは、「観測した交絡因子での調整が識別に十分」という出発点の上で、MLによる局外関数推定と、低次元の因果パラメータの推論を橋渡しする枠組みである。未観測交絡が疑わしいなら、より良い設計、感度分析、IVなど別の識別戦略が必要になる。

ヒント実証研究コーナー:Chernozhukov et al. (2018)

研究の問い:機械学習の柔軟な予測モデルを、局外パラメータ(傾向スコアやアウトカムモデルなど)の推定に使いながら、興味のある因果パラメータについて、従来の計量経済学と同水準の統計的推論(漸近正規性、有効な信頼区間)を確保するには、どうすればよいか。

データ・アプローチ:理論論文であり、特定のデータセットの分析ではなく、一般的な推定枠組みを提案・証明している。

識別戦略・モデル:局外パラメータの推定に機械学習を用いる際に生じる「正則化バイアス」の問題を、(1) ネイマン直交性を満たすスコア関数を用いること、(2) データをK分割し、ある分割で学習した局外パラメータを別の分割でのパラメータ推定に使う(cross-fitting)こと、の2つの工夫で解決する。

主要な発見:直交化とcross-fittingを組み合わせ、局外関数が所定の収束率を満たすとき、ATEや低次元の構造パラメータについて \(\sqrt n\) 収束と漸近正規性を得られる。

このトピックとの関係:本講義で扱ったcausal forestも、木の構築において類似のsample splitting(honesty)の発想を採用しており、DMLの直交化・cross-fittingという考え方と地続きである。観察データでCATEを推定する必要が生じたときに、この論文が提示する枠組みが現代の出発点になる。

8. ビジネスの現場で(続き)

ノートビジネスの現場で:「AIターゲティング」の説明を求められたら

自社でCATEベースのターゲティングモデルを導入する際、経営会議で「このAIモデルは何をしているのか」と聞かれたときの説明の仕方を整理しておこう。

  • 「解約しそうな人を当てるモデル」ではなく「クーポンが効きそうな人を当てるモデル」であることを明確に区別して説明する。
  • モデルの評価は、「予測精度(AUCなど)」ではなく「モデル学習に使っていないランダム化testで、このルールに従って配布した場合の増分利益(Qini型曲線、IPW政策価値)」で示す。
  • モデルの推定に使ったデータが、ランダム化されたA/Bテストなのか、それとも観察データ(過去の営業判断でクーポンが配られたログ)なのかを明示する。後者であれば、非交絡・overlapをなぜ仮定できるかを説明し、DMLなどで観測済み交絡をどう調整したかも示す。DMLは未観測交絡を解消しない。

まとめ

重要まとめ
  • 解約リスクの高さと、施策への感応度(treatment effect)はまったく別の量である(Ascarza 2018)。churnスコア上位を狙うターゲティングは、ランダムに配るよりも悪い結果になることがある。
  • 4類型は個人のprincipal strata、CATEは条件付き平均である。負のCATEは「その人がsleeping dog」とは意味しない。
  • T-learnerの差の分散には2本の推定誤差と共分散が入り、必ず「2倍」ではない。causal forestのhonestyは適応的バイアスを減らすが、妥当な推論には他の正則条件も要る。
  • 政策は学習・validationで固定し、独立testでQini型曲線やIPW政策価値を評価する。OOB予測だけで、選択後の通常のSEが保証されるわけではない。
  • CATEと価値・コストに基づく配分は、安定性、SUTVA、既知の価値などの条件下での局所的な最適化である。Hitsch, Misra, and Zhang (2024) では、推定最適政策が全員処置・全員非処置のどちらよりも高い利潤を生んだ。
  • 観察データでは非交絡とoverlapが識別に必要である。DMLは未観測交絡を治さず、\(\sqrt n\) 推論の主な対象は低次元パラメータである。

宿題

  • ブラウザ実験室(playground11.html)で、今回のシミュレーションの数値を自分でいじって遊んでみよう。インストール不要、ブラウザだけでRが動く(初回ロードのみ30秒〜1分かかる)。
  • ChatGPT(または他の対話型AI)に「uplift modelingとpropensity modelingの違いを教えてください」と聞いてみて、今回の講義で学んだ「churnスコア vs uplift」という区別と、自分の理解がどう対応するか比べてみよう。
  • 自分の勤務先(または身近な企業)で「解約予測モデル」や「レコメンドスコア」が使われている場面を1つ思い出し、そのスコアが「予測」なのか「施策への感応度(uplift)」なのかを考えてみよう。もし予測スコアがそのまま施策の配分に使われているなら、それはpredict-then-actの罠に陥っている可能性がないか検討してみよう。
  • 今回のQini型曲線のシミュレーションで、mid_risk_bumpの中心位置やbilling_troubleの効果indexを変え、負のCATE層のprincipal-strata比率と曲線がどう変わるかを比べてみよう。
  • coding課題は assignment11.qmd を参照。動画サブスクリプションの解約引き留めクーポンRCTを題材に、T-learner・causal forest・Qini曲線・IPW政策評価を一通り自分の手で実装する。

次回予告

次回(第12回)はゲスト講義で、民間企業でのエコノミスト経験を持つゲスト講師から、大規模実験、需要予測、価格、広告、レコメンド、マーケットプレイス分析など、実務でこれらの技術がどう使われているかを直接聞く回になる。研究としての経済学と、事業意思決定としての経済学の違いを考える良い機会にしてほしい。

そして第13回からは、このコースの最後のパートである「動学」に入る。これまで扱ってきたのは基本的に「今この瞬間の意思決定」だったが、第13回では、消費者や企業が将来を見据えて(forward-looking に)行動する状況を、動的計画法とBellman方程式を使って定式化する。Rustのバスエンジン交換モデルという古典的な題材を通じて、動学的構造モデルが何を識別し、どのような反実仮想を可能にするかを学んでいく。

参考文献

  • Ascarza, Eva (2018) “Retention Futility: Targeting High-Risk Customers Might Be Ineffective,” Journal of Marketing Research, 55(1), 80-98.
  • Hitsch, Günter J., Sanjog Misra, and Walter W. Zhang (2024) “Heterogeneous Treatment Effects and Optimal Targeting Policy Evaluation,” Quantitative Marketing and Economics, 22(2), 115-168. https://doi.org/10.1007/s11129-023-09278-5
  • Athey, Susan and Guido W. Imbens (2019) “Machine Learning Methods That Economists Should Know About,” Annual Review of Economics, 11, 685-725.
  • Wager, Stefan and Susan Athey (2018) “Estimation and Inference of Heterogeneous Treatment Effects Using Random Forests,” Journal of the American Statistical Association, 113(523), 1228-1242.
  • Kitagawa, Toru and Aleksey Tetenov (2018) “Who Should Be Treated? Empirical Welfare Maximization Methods for Treatment Choice,” Econometrica, 86(2), 591-616.
  • Chernozhukov, Victor, Denis Chetverikov, Mert Demirer, Esther Duflo, Christian Hansen, Whitney Newey, and James Robins (2018) “Double/Debiased Machine Learning for Treatment and Structural Parameters,” The Econometrics Journal, 21(1), C1-C68.
  • 上武康亮・遠山祐太・若森直樹・渡辺安虎 (2025)『実証ビジネス・エコノミクス』日本評論社。