因果森林与政策学习 Causal Forest & Policy Learning
从平均处理效应(ATE)到条件平均处理效应 $CATE(x)$:谁受政策影响最大?因果森林(Athey-Tibshirani-Wager, 2019 PNAS)把随机森林改造成一个"非参数 CATE 估计器",并用诚实树(honest tree)给出渐近正态的置信区间。政策树(Athey-Wager, 2021 Operations Research)进一步从"估计异质性效应"跨到"把资源分配给谁最优"。本节讲 grf(R)与 econml(Python)的完整用法。
01 从 ATE 到 HTE / CATE
ATE 回答"政策平均影响多大",但政策制定者更关心:谁受影响最大?应该把资源优先给谁?这就需要刻画效应的异质性处理效应(Heterogeneous Treatment Effect, HTE),更具体地,条件平均处理效应:
$\tau(x)$ 是一个函数:给定协变量 $X=x$ 的子人群,处理效应的平均值。传统做法是研究者事先指定几个 $X$ 维度,做交互项或分样本回归(见 10 异质性分析)。这种做法的局限是:(1) 只能探索你想到的维度;(2) 高维协变量下手工交互项组合爆炸;(3) 多重检验下容易 p-hacking。因果森林的目标就是让数据自己告诉你 $\tau(x)$ 的形状。
(1) ATE:$\tau$ 是常数;(2) 线性异质性:$\tau(x)=\theta_0+\theta_1'x$(DML 的 IRM 模型);(3) 任意形状异质性:$\tau(x)$ 是任意函数(因果森林 / GFR)。越往后越灵活,但估计方差越大。
02 因果森林原理与诚实树
因果森林(Wager & Athey 2018; Athey, Tibshirani & Wager 2019)是随机森林的改造版。普通决策树在叶子节点预测 $Y$;因果树在叶子节点估计处理效应 $\hat\tau_\ell$(叶子 $\ell$ 内处理组与对照组的均值差)。递归分裂的目标不是最小化 $Y$ 的预测误差,而是最大化叶子节点之间的效应差异。
但这种做法有一个统计陷阱:如果用同一批数据既"选择分裂点"又"在叶子里估计 $\tau_\ell$",那么分裂会偏向于"噪声看起来差异很大"的位置,导致估计的 $\tau(x)$ 有偏、置信区间不覆盖。诚实树(honest tree)解决这个问题:
诚实性把"模型选择"和"效应估计"用不同样本分开,从而消除了过拟合。grf 包的默认参数 honesty=TRUE, honesty.fraction=0.5 就是这个机制。
分裂准则也和普通随机森林不同。普通树按"叶子内 $Y$ 的方差下降"分裂;因果树按"叶子之间的处理效应差异"分裂——具体地,对每个候选分裂点,计算左右两个子叶子的 $\hat\tau_\ell$ 之差,取最大化这个差异(同时要求每个叶子内处理组和对照组都有足够样本)的分裂点。这等价于一个"因果 R 平方"目标:我们要找的不是"最能预测 $Y$ 的分割",而是"最能区分效应的分割"。这是因果森林与预测随机森林最本质的算法差异。
另一个工程细节:ci.group.size。为了估计方差,grf 把树再分组,组内的树用不同的样本子集拟合,组间差异用来度量估计的不确定性。ci.group.size=2 是默认,正式研究建议设为 3 或 4 以获得更稳的方差估计。
03 渐近正态性与置信区间
Wager & Athey (2018) 的核心理论贡献是:在诚实树 + 适当正则化(叶子大小、分裂规则)下,$\sqrt{n}\big(\hat\tau(x)-\tau(x)\big)$ 渐近正态,且方差可估。grf 包通过 estimate.variance=TRUE 给出每一个预测点 $\hat\tau(x)$ 的置信区间。这在机器学习方法里非常罕见——多数 ML 只能给点估计,因果森林能给带置信区间的 CATE。
不要只看 $\hat\tau(x)$ 的点估计;要看它的置信区间宽度。如果 CI 跨过零,说明在这个 $x$ 处效应不显著异于零。报告时画"CATE 直方图"和"CATE 沿某 $X$ 变量的散点 + CI"。
04 R 完整代码:grf 包
# ============================================================
# 04.1 R: 用 grf 估计 CATE、变量重要性、置信区间
# install.packages("grf")
# ============================================================
library(grf)
set.seed(123)
# ---- 1. 模拟数据:n=2000, p=6;CATE 与 X1, X3 有关 ----
n <- 2000
p <- 6
X <- matrix(rnorm(n * p), n, p)
W <- rbinom(n, 1, 0.5) # 随机分配处理
# 真实 CATE(x) = X[,1] + X[,3](其他变量不影响效应)
tau_true <- X[, 1] + X[, 3]
Y <- tau_true * W + X[, 2] + pmin(X[, 4], 0) + rnorm(n)
# ---- 2. 拟合因果森林(默认 honesty=TRUE)----
cf <- causal_forest(
X = X,
Y = Y,
W = W,
num.trees = 2000, # 树数
min.node.size = 5, # 叶子最小样本
honesty = TRUE, # 诚实树
honesty.fraction = 0.5, # 一半选分裂、一半估效应
ci.group.size = 2 # 用于方差估计
)
# ---- 3. 用 out-of-bag 预测训练样本的 CATE ----
tau.hat.oob <- predict(cf)
hist(tau.hat.oob$predictions,
main = "OOB CATE 估计分布", xlab = "tau_hat(x)")
# ---- 4. 置信区间(在测试样本上)----
X.test <- matrix(rnorm(100 * p), 100, p)
tau.test <- predict(cf, X.test, estimate.variance = TRUE)
# tau.test$predictions : CATE 点估计
# tau.test$variance.estimates: 方差估计
# 95% CI = predictions ± 1.96 * sqrt(variance.estimates)
ci_lower <- tau.test$predictions - 1.96 * sqrt(tau.test$variance.estimates)
ci_upper <- tau.test$predictions + 1.96 * sqrt(tau.test$variance.estimates)
# ---- 5. 模型校准诊断(calibration test)----
test_calibration(cf)
# 输出:估计 ATE、最优线性投影 R^2、是否校准良好
# ---- 6. 变量重要性(哪些 X 决定了 CATE)----
varimp <- variable_importance(cf)
print(data.frame(variable = paste0("X", 1:p),
importance = round(varimp, 3)))
# X1, X3 的 importance 应显著高于 X2, X4, X5, X6
# ---- 7. 平均处理效应 ATE ----
ate_cf <- average_treatment_effect(cf)
print(ate_cf) # 估计 ATE 与标准误
# ============================================================
# 04.2 R: 用 policy_tree 从 CATE 到最优政策
# ============================================================
library(grf)
# 用上一步的 cf
# 政策树:直接在观察数据上学习"哪些 x 应被处理"
pt <- policy_tree(X, Y, W,
depth = 2, # 树深
policy = "overall") # 最大化整体福利
# 在测试样本上预测政策:1=建议处理, 0=建议不处理
policy.predict <- predict(pt, X.test)
table(policy.predict$predictions)
# 画政策树
plot(pt)
# 树的每个叶子给出"按哪个变量切分 -> 该叶子里是否建议处理"
# 这就是可解释的"政策规则"
05 Python 完整代码:econml CausalForestDML
# ============================================================
# 05.1 Python: econml CausalForestDML
# pip install econml scikit-learn
# ============================================================
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import GradientBoostingRegressor, GradientBoostingClassifier
from econml.dml import CausalForestDML
np.random.seed(42)
# ---- 1. 模拟数据(与 R 版相同)----
n, p = 2000, 6
X = np.random.normal(size=(n, p))
W = np.random.binomial(1, 0.5, size=n)
tau_true = X[:, 0] + X[:, 2] # 真实 CATE(x)
Y = tau_true * W + X[:, 1] + np.minimum(X[:, 3], 0) + np.random.normal(size=n)
# ---- 2. 拟合 CausalForestDML ----
est = CausalForestDML(
model_y=GradientBoostingRegressor(n_estimators=200, max_depth=3,
random_state=42),
model_t=GradientBoostingClassifier(n_estimators=200, max_depth=3,
random_state=42),
discrete_treatment=True, # W 是二值
n_estimators=1000, # 树数
min_samples_leaf=20,
max_samples=0.5,
honest=True, # 诚实分裂
inference=True, # 启用置信区间
random_state=42,
)
est.fit(Y, W, X=X)
# ---- 3. CATE 点估计与置信区间 ----
cate_hat = est.effect(X) # 每个个体的 CATE
lb, ub = est.effect_interval(X, alpha=0.05)
print("CATE 均值 =", cate_hat.mean())
print("CATE 标准差 =", cate_hat.std())
print("95% CI 半宽 均值 =", ((ub - lb) / 2).mean())
# ---- 4. 与真实 CATE 对比 ----
plt.figure(figsize=(6, 6))
plt.scatter(tau_true, cate_hat, alpha=0.5, s=10)
plt.plot([-4, 4], [-4, 4], "r--")
plt.xlabel("true CATE"); plt.ylabel("estimated CATE")
plt.title("Causal Forest: true vs estimated CATE")
plt.tight_layout()
plt.savefig("cate_true_vs_hat.png", dpi=120)
# ---- 5. 变量重要性(基于分裂频率)----
imp = est.feature_importances_
for i in range(p):
print(f"X{i+1}: importance = {imp[i]:.3f}")
# X1, X3 应最高
# ---- 6. 政策解释:用单棵决策树逼近 CATE ----
from econml.cate_interpreter import SingleTreeCateInterpreter
interp = SingleTreeCateInterpreter(include_model_uncertainty=True,
max_depth=2)
interp.interpret(est, X)
# interp.plot() # 在 Jupyter 中画可解释的 CATE 树
06 政策树 Policy Tree 与靶向
从"估计 $\tau(x)$"到"做出政策决策",Athey & Wager (2021, Operations Research) 提出政策树(Policy Tree):直接优化"给定预算下,把政策分配给谁,期望福利最大"。与 CATE 估计不同,政策树的目标不是让 $\hat\tau(x)$ 点估计准,而是让分配规则 $D^*(x)\in\{0,1\}$ 最大化 $\mathbb{E}[\tau(x)\cdot D^*(x)]$。
实务流程:
- 用因果森林估计 $\hat\tau(x)$ 与置信区间。
- 按 $\hat\tau(x)$ 排序,把政策资源优先分给 $\hat\tau(x)$ 最高的 $q\%$ 人群——这就是靶向(targeting)。
- 用政策树把"哪些人群该被处理"压缩成一条可解释的规则("X1 > 0.5 且 X3 > 0 时处理")。
- 在留出样本或 RCT 数据上验证政策树的期望收益是否显著优于"全民处理"或"随机处理"。
政策靶向可能把资源从"最贫困"转移到"对政策最敏感"的人群。Haushofer et al. (2025, AER) 标题就叫"Targeting Impact versus Deprivation",明确讨论按效应靶向 vs 按贫困靶向的权衡。统计上,靶向规则的估计本身需要诚实样本,否则会过拟合到训练数据的噪声。
07 与传统交互项 / 分样本回归对比
| 方法 | 异质性来源 | 可识别的形状 | 置信区间 | 主要风险 |
|---|---|---|---|---|
| 分样本回归 | 研究者预先切分 | 离散的、低维 | 有(但忽略了切分本身) | p-hacking、未校正多重检验 |
| 交互项 $D\times X$ | 研究者指定调节变量 | 线性异质性 | 有 | 只能探索想到的维度 |
| 分位数回归 | 沿 $Y$ 分布的异质性 | 分布形状 | 有 | 不是 $X$ 维度的异质性 |
| DML-IRM | 线性异质性 $\theta(X)$ | 线性 | 有(半参有效) | 形状受限 |
| 因果森林 | 数据驱动 | 任意非参数 | 有(渐近正态) | 需要大样本、外推风险 |
实务建议:把因果森林当作"发现工具",把传统交互项当作"确认工具"。先用因果森林发现"效应沿哪些 $X$ 维度分化",再用交互项在同一数据集(或新数据)上正式检验,避免"用同一数据发现又确认"的过拟合。
从写作角度,一份合格的因果森林章节通常包含四张图/表:(1) CATE 直方图,展示 $\hat\tau(x)$ 的分布形状——是集中在零附近(几乎没有异质性),还是两端有长尾(异质性很大);(2) CATE 沿关键协变量的散点图,横轴是某个连续 $X$,纵轴是 $\hat\tau(x)$,叠加核密度或低阶多项式拟合;(3) 变量重要性表,列出 Top 5-10 个驱动 CATE 的变量,并解释经济学含义;(4) 政策树图或靶向人群特征表,说明"建议处理"与"不建议处理"两组在 $X$ 上的差异。
还要特别说明 CATE 与 ATE 的关系:CATE 的加权平均就是 ATE。如果因果森林给出的 ATE 与 DML / OLS 估的 ATE 差很多,说明森林本身拟合有问题(过拟合、honesty 未开、样本量不足),必须先排查再继续解释异质性。
因果森林与 BART / 其他异质性方法
除了因果森林,估计 $\tau(x)$ 还有两条常见路线:(1) BART(Bayesian Additive Regression Trees),用贝叶斯树的后验分布给出 CATE 的后验区间,天然带不确定性;(2) S-/T-/X-/R-learner 等元学习器(meta-learner),把任意预测模型包装成 CATE 估计器。因果森林的优势是渐近正态性严格、grf/econml 包成熟;BART 的优势是后验分布自然、小样本稳;X-learner 在处理组与对照组样本严重不平衡时表现更好。实务中,Athey-Wager 团队的 grf 仍然是经济学顶刊的主流选择,因为它和 DML 同一学派、理论保证最清晰。
从"估计异质性"到"写论文",还有一个容易被忽略的环节:异质性发现的多重检验问题。当你把 20 个协变量都扔进因果森林,森林总会"找到"一些异质性——哪怕真实 DGP 里根本没有。这是因为 ML 的优化目标就是最小化训练误差,它会把噪声也当成信号。缓解办法:(1) 报告 CATE 的整体 $R^2$(grf 的 test_calibration 给出),若接近零则说明几乎没有真异质性;(2) 用独立的留出样本验证 $\hat\tau$ 的排序是否稳定;(3) 在论文里诚实说明"这是数据驱动的探索性分析,需要外部样本复制"。
应用论文精读:Delfino (2024) 与 Haushofer et al. (2025)
Delfino (2024, AER) 研究"鼓励男性进入传统粉色职业(如护士、幼儿教师)"的现场实验。她把招聘广告随机分配给男性求职者,有的广告强调"欢迎男性申请",有的没有。传统的 ATE 分析给出平均处理效应;进一步用因果森林探索异质性,发现效应集中在"对性别刻板印象敏感度低、家庭中有女性从事类似职业"的子人群。这种"RCT + 因果森林 + 子群体机制讨论"的写法,已经成为 AER 上实验论文的标准结构。
Haushofer et al. (2025, AER) 研究肯尼亚现金转移支付的靶向问题。传统做法是按"贫困程度"选受益人;论文提出另一种思路:按"预期效应大小"选受益人。他们用因果森林估计每个家庭的 CATE,比较两种靶向规则下的总福利。核心发现是:按效应靶向 vs 按贫困靶向各有优劣——按贫困靶向帮到最需要的人,但按效应靶向总福利更大。这篇论文把"政策学习"从统计学概念真正带进了发展经济学的政策讨论。
从因果森林到政策建议的写作模板
在论文中报告因果森林结果时,建议按以下顺序:(1) 先报告 ATE 与传统异质性(交互项 / 分样本);(2) 报告因果森林的校准诊断($R^2$、CI 覆盖率);(3) 画 CATE 直方图与变量重要性;(4) 用政策树给出"可执行"的靶向规则;(5) 在外部样本或 bootstrap 上验证政策规则的稳定性;(6) 讨论靶向的伦理与公平含义(如 Haushofer et al. 做的)。这个结构既能让计量审稿人满意,也能让政策读者看懂"你到底建议怎么做"。
最后提醒:因果森林不是"自动发现因果"的黑箱。它仍然需要你先回答上一节 DAG 框架里的问题——处理 $D$ 与结果 $Y$ 之间的后门路径是否被 $X$ 阻断?如果答案是否定的,因果森林给出的 $\hat\tau(x)$ 仍然是有偏的。把因果森林放在"可忽略性成立前提下,非参数地刻画异质性"的位置,而不是"绕过识别假设"的捷径。
08 逐步流程
test_calibration() 或 econml 内置诊断,确认 $\hat\tau$ 校准良好;若 R² 接近 0,说明数据里几乎没有异质性。09 论文案例
causal_forest() 的理论出处。grf::policy_tree() 即实现此文算法。10 常见错误
不开 honesty 等于用同一批数据既选分裂又估效应,置信区间严重偏窄、覆盖率失真。grf 与 econml 默认 honesty=TRUE,不要关掉。
因果森林每个叶子需要足够的处理组和对照组样本。$n=200$、$p=50$ 时,CATE 估计噪声极大。经验法则:$n$ 至少上千、$p$ 不超过 $n/10$;样本小就用线性 DML。
因果森林在数据稀疏区域(X 取值极端)的预测是外推,不可信。报告 CATE 时叠加 $X$ 的核密度,只在"有足够样本支撑"的区域下结论。
训练样本上 $\hat\tau$ 高的人,在新数据上不一定真是高效应组。政策靶向必须在留出样本或新实验上验证期望福利。
因果森林是补充工具,不是替代。论文应同时报告 OLS ATE、交互项异质性、因果森林 CATE,三者一致才有说服力。
因果森林在可忽略性假设下识别 CATE。它不能解决未观测混淆——如果原始 DGP 有 IV 或 RDD 依赖,应该用 IV-LASSO / 工具变量森林,而不是普通因果森林。
进阶资料
- grf 官方文档:
grf-labs.github.io/grf,含大量案例。 - econml 文档:
econml.readthedocs.io,含CausalForestDML与SingleTreeCateInterpreter。 - Athey & Imbens (2019), Annual Review of Economics "Machine Learning Methods that Economists Should Know About" —— 综述。
- Chernozhukov et al. (2018 JEL / Annu. Rev. Econ.) 综述,与 DML 配套阅读。