简介这份资源是斯坦福大学Stefan Wager于2024年9月更新的《Causal Inference: A Statistical Learning Approach》教科书草稿面向数据科学、统计学、经济学与公共政策领域的学习者和研究者帮助其系统掌握基于统计学习方法的因果推断理论与应用框架。压缩包内仅含1个PDF文件大小约1.66MB轻量便携适合在电脑或平板上随时查阅。全书共十二章从随机对照试验中的差异均值估计与回归调整讲起依次覆盖无混淆性与倾向得分、双重稳健方法与双重机器学习、异质处理效应估计、政策学习与经验福利最大化、自适应实验、平衡估计器、回归断点设计并延伸至内生处理、局部平均处理效应、溢出与干扰等进阶主题每章附有文献注记便于延伸阅读。目前已有1996人学习下载适合希望从统计学习视角深入理解因果关系的中高级读者作为教材或参考书使用。1. 从一份 2024 年 9 月的草稿说起这本因果推断教材到底能解决什么如果你做过 AB 实验大概率遇到过这种场景实验组转化率涨了 3%老板问“这 3% 里有多少是真实因果效应多少是用户本来就打算买”你盯着 p 值答不上来。差异均值能告诉你两组有区别但回答不了“为什么”和“对谁有效”。这份 2024 年 9 月 6 日的草稿版教材作者是 Stefan Wager斯坦福大学统计学习方向的教授全书 16 章加习题从随机对照试验一路铺到回归断点、干扰效应和动态策略评估。它不是那种只讲潜在结果框架的入门读物而是把因果推断当成统计学习问题来写——双重机器学习、半参数效率界、策略学习、自适应实验这些在工业界真正落地时绕不开的工具书里都有独立章节。适合谁做实验平台的数据科学家、需要从观测数据里挖因果效应的分析师、以及想把因果推断和机器学习接起来的研究生。如果你只想知道“AB 实验怎么做 t 检验”这本书前两章够用如果你想搞清楚“为什么双重稳健估计比 IPW 更稳”“异质处理效应怎么建模”那它值得你花时间啃。2. 随机对照试验的估计量从差异均值到回归调整2.1 差异均值估计的无偏性到底依赖什么书里第 1 章开篇就给了 Neyman-Rubin 潜在结果框架的定义每个个体 i 有两个潜在结果 Y_i(1) 和 Y_i(0)个体因果效应 Δ_i Y_i(1) - Y_i(0)。根本问题是只能观测到一个所以 Δ_i 永远不可知。但随机化让我们能估计样本平均处理效应 SATE 和总体平均处理效应 ATE。差异均值估计量的形式很简单import numpy as np def diff_in_means(Y, W): Y: 观测结果数组, shape (n,) W: 处理分配数组, 0 或 1, shape (n,) 返回: 差异均值估计量 n1 np.sum(W 1) n0 np.sum(W 0) if n1 0 or n0 0: raise ValueError(处理组或对照组为空无法估计) tau_dm np.mean(Y[W 1]) - np.mean(Y[W 0]) return tau_dm逻辑说明这个估计量就是处理组均值减对照组均值。参数上唯一需要注意的是 n1 和 n0 都不能为 0否则分母炸掉。书里定理 1.1 证明了在 SUTVA 和随机分配条件下它是 SATE 的有限样本无偏估计。SUTVA 的意思是“个体 i 的潜在结果只取决于自己接受的处理不受其他人处理分配影响”——医学试验里通常成立但社交网络、市场平台这类有溢出效应的场景就不成立书里第 11、12 章专门处理这个问题。我一般会提醒团队差异均值无偏但不代表它是最优的。书里明确说了它没有利用协变量信息方差可能很大。如果你有基线协变量回归调整能显著降方差。2.2 回归调整为什么加了协变量反而更稳书里 1.2 节讲回归调整。核心思路是在差异均值的基础上用协变量 X 做调整估计量变成import numpy as np from sklearn.linear_model import LinearRegression def regression_adjustment(Y, W, X): Y: 结果, shape (n,) W: 处理, shape (n,) X: 协变量矩阵, shape (n, p) 返回: 回归调整估计量 # 对处理组和对照组分别拟合或者用交互项模型 # 这里用最简单的Y ~ W XW 的系数即估计量 X_aug np.column_stack([W, X]) model LinearRegression(fit_interceptTrue) model.fit(X_aug, Y) tau_adj model.coef_[0] # W 的系数 return tau_adj逻辑说明把处理指示变量 W 和协变量 X 一起放进线性回归W 的系数就是调整后的处理效应估计。参数上要注意如果 X 和处理效应有交互即处理效应随 X 变化这个模型设定就不够需要用 W×X 交互项。书里指出回归调整在随机试验中是无偏的而且当协变量与结果相关时方差比差异均值小。但有个坑如果模型设定错误比如真实关系是非线性的你用了线性估计量可能有偏。常见做法是至少把 X 中心化或者用更灵活的模型。提示回归调整的降方差效果取决于协变量与结果的相关强度。如果 X 和 Y 几乎不相关加了反而增加自由度消耗方差可能不降反升。2.3 有限样本 vs 总体推断你到底在推断什么书里区分了有限样本分析和总体渐近分析。定理 1.1 是无偏性不依赖任何分布假设条件在潜在结果上。但要做置信区间书里引入了总体抽样假设把 n 个研究对象的潜在结果看作从某个总体 P 中独立抽取。在这个假设下定理 1.2 给出了中心极限定理√n(τ̂_DM - τ) ⇒ N(0, V_DM)其中 V_DM Var[Y_i(0)]/(1-π) Var[Y_i(1)]/π。对应的方差估计量是def variance_dm(Y, W): 差异均值估计量的方差估计 n len(Y) n1 np.sum(W 1) n0 np.sum(W 0) var1 np.var(Y[W 1], ddof1) var0 np.var(Y[W 0], ddof1) # 书里公式 (1.10) 的 plug-in 版本 V_hat n / n0 * var0 n / n1 * var1 return V_hat参数说明ddof1 用样本方差的无偏估计。n/n0 和 n/n1 是权重反映两组样本量的倒数。这个方差估计量是一致的可以用来构造渐近置信区间τ̂ ± Φ^{-1}(1-α/2) * sqrt(V̂/n)。这里有个容易翻车的地方如果你做的是完全随机化试验固定 n1 个处理而不是 Bernoulli 试验每个个体独立以概率 π 接受处理方差公式会略有不同。书里在脚注里专门提了这一点。我见过有人混用公式置信区间覆盖率不对还以为是数据问题。3. 无混淆性与倾向得分观测研究的第一道防线3.1 分层估计把混杂变量切成块书里第 2 章转向观测研究。核心假设是无混淆性给定协变量 X处理分配 W 与潜在结果独立。用数学写就是 {Y_i(0), Y_i(1)} ⊥ W_i | X_i。这个假设没法用数据检验只能靠领域知识论证。分层估计的思路很直白把 X 的取值空间切成若干层每层内近似随机然后在层内做差异均值最后按层大小加权平均。import numpy as np import pandas as pd def stratified_estimation(Y, W, X, n_strata5): Y: 结果, W: 处理, X: 协变量单变量用于分层 返回: 分层估计量 # 按 X 的分位数分层 strata pd.qcut(X, qn_strata, labelsFalse, duplicatesdrop) tau_strat 0 total len(Y) for s in np.unique(strata): mask strata s n_s np.sum(mask) if n_s 0: continue Y_s Y[mask] W_s W[mask] if np.sum(W_s 1) 0 or np.sum(W_s 0) 0: continue # 该层只有一种处理跳过 tau_s np.mean(Y_s[W_s 1]) - np.mean(Y_s[W_s 0]) tau_strat (n_s / total) * tau_s return tau_strat逻辑说明qcut 按分位数切保证每层样本量大致均衡。参数 n_strata 控制层数太少则层内混杂没消干净太多则每层样本不足。书里建议每层至少要有几个处理组和对照组样本否则该层估计不稳定。常见做法是层数在 5 到 10 之间或者用交叉拟合的方式自动选层。坑在哪如果 X 是多维的分层会遭遇维数灾难——每个格子里的样本稀疏到无法估计。这时候倾向得分就派上用场了。3.2 逆倾向加权用概率倒数重新加权样本倾向得分 e(X) P(W1|X) 是把多维 X 压成一维的降维工具。逆倾向加权IPW估计量from sklearn.linear_model import LogisticRegression import numpy as np def ipw_estimation(Y, W, X): 逆倾向加权估计 # 第一步估计倾向得分 model LogisticRegression(max_iter1000) model.fit(X, W) e_hat model.predict_proba(X)[:, 1] # 截断极端倾向得分防止方差爆炸 e_hat np.clip(e_hat, 0.05, 0.95) # 第二步加权 n len(Y) tau_ipw np.mean(W * Y / e_hat) - np.mean((1 - W) * Y / (1 - e_hat)) return tau_ipw逻辑说明处理组样本用 1/e(X) 加权对照组用 1/(1-e(X)) 加权模拟出一个“伪总体”其中处理分配与 X 独立。参数上最关键的是截断阈值——如果某个样本的倾向得分接近 0 或 1它的权重会爆炸导致估计量方差极大。常见做法是截断在 0.05 到 0.95 之间或者用稳定化权重。书里 2.2 节还讨论了 IPW 的渐近性质在倾向得分模型正确设定时IPW 是无偏且渐近正态的。但模型错了就有偏。而且 IPW 对极端倾向得分非常敏感这是它最大的软肋。注意倾向得分模型的选择很关键。如果真实倾向得分是非线性的你用逻辑回归可能拟合不好。常见做法是加入样条项或使用梯度提升树但后者需要交叉拟合来避免过拟合偏差。3.3 倾向得分的实操边界书里在文献注记里提到倾向得分方法在经济学和流行病学中广泛使用但它的有效性高度依赖无混淆性假设。这个假设在观测研究中往往不可验证。我一般会建议团队做敏感性分析如果存在一个未观测混杂变量它需要多强才能推翻结论书里没有展开 Rosenbaum 边界但这是实操中必须补的一课。另一个边界是重叠性如果某些 X 取值下只有处理组或只有对照组倾向得分接近 0 或 1IPW 和分层都失效。这时候只能做局部估计或者重新定义目标人群。4. 双重稳健与双重机器学习当模型可能错时怎么办4.1 双重稳健估计的基本形式书里第 3 章讲双重稳健方法。核心思想同时建模结果回归 μ(X, W) E[Y|X, W] 和倾向得分 e(X)构造一个估计量只要其中一个模型正确估计就是一致的。AIPW增广逆倾向加权估计量import numpy as np from sklearn.linear_model import LinearRegression, LogisticRegression def aipw_estimation(Y, W, X): 增广逆倾向加权估计 # 结果模型 model_y LinearRegression() model_y.fit(np.column_stack([W, X]), Y) mu1 model_y.predict(np.column_stack([np.ones(len(Y)), X])) mu0 model_y.predict(np.column_stack([np.zeros(len(Y)), X])) # 倾向模型 model_w LogisticRegression(max_iter1000) model_w.fit(X, W) e_hat np.clip(model_w.predict_proba(X)[:, 1], 0.05, 0.95) # AIPW n len(Y) tau_aipw np.mean( mu1 - mu0 W * (Y - mu1) / e_hat - (1 - W) * (Y - mu0) / (1 - e_hat) ) return tau_aipw逻辑说明mu1 - mu0 是结果模型的直接估计后面两项是加权残差修正。如果结果模型对残差项期望为零估计量退化为结果模型估计如果倾向模型对修正项把结果模型的偏差纠回来。参数上两个模型都需要交叉拟合来避免过拟合——书里 3.1 节专门讲了这一点。4.2 双重机器学习交叉拟合与 Neyman 正交性书里 3.1 节的双重机器学习DML是 AIPW 的升级版。关键创新是交叉拟合把数据分成 K 折每折用其他折训练模型再对当前折做预测。这样避免了过拟合导致的偏差。from sklearn.model_selection import KFold import numpy as np def dml_estimation(Y, W, X, n_folds5): 双重机器学习估计部分线性模型 n len(Y) mu_hat np.zeros(n) e_hat np.zeros(n) kf KFold(n_splitsn_folds, shuffleTrue, random_state42) for train_idx, test_idx in kf.split(X): # 结果模型 model_y LinearRegression() model_y.fit(np.column_stack([W[train_idx], X[train_idx]]), Y[train_idx]) mu_hat[test_idx] model_y.predict( np.column_stack([W[test_idx], X[test_idx]]) ) # 倾向模型 model_w LogisticRegression(max_iter1000) model_w.fit(X[train_idx], W[train_idx]) e_hat[test_idx] model_w.predict_proba(X[test_idx])[:, 1] e_hat np.clip(e_hat, 0.05, 0.95) # 残差 Y_res Y - mu_hat W_res W - e_hat # 部分线性模型的 DML 估计 tau_dml np.mean(W_res * Y_res) / np.mean(W_res * W) return tau_dml逻辑说明交叉拟合保证 mu_hat 和 e_hat 是“样本外”预测避免过拟合偏差。Neyman 正交性意味着估计量对两个模型的局部扰动不敏感即使模型有轻微错误估计量的偏差也是二阶小量。参数上 n_folds 通常取 5 或 10折数越多计算越贵但偏差越小。书里 3.2 节还讨论了无混淆下的有效估计——在给定假设下能达到半参数效率界的估计量。这部分理论性较强但实操意义在于如果你用了 DML你的估计量在渐近意义上是最优的方差最小。4.3 双重稳健的实操坑第一个坑两个模型都错。双重稳健只在“至少一个正确”时保证一致性。如果两个都错估计量可能有偏。我一般会做交叉验证检查两个模型的预测性能如果都不好结论要谨慎。第二个坑倾向得分截断。DML 里同样需要截断否则权重爆炸。书里建议截断阈值随样本量调整但实操中 0.05 到 0.95 是常见起点。第三个坑交叉拟合的随机性。不同的折划分会给出略微不同的估计。常见做法是重复多次交叉拟合取平均同时报告标准差。5. 异质处理效应与策略学习从“平均有效”到“对谁有效”5.1 半参数建模与 CATE 估计书里第 4 章讲异质处理效应。条件平均处理效应 CATEτ(x) E[Y(1) - Y(0)|Xx]。估计 CATE 比 ATE 难得多因为每个 x 处的样本更少。书里介绍的方法包括半参数建模和针对处理异质性的损失函数。一个常见做法是用因果森林或 BART但书里更强调半参数效率理论。实操中我一般会用以下策略from sklearn.ensemble import GradientBoostingRegressor import numpy as np def cate_estimation(Y, W, X, n_folds5): 用 T-learner 估计 CATE from sklearn.model_selection import KFold n len(Y) tau_hat np.zeros(n) kf KFold(n_splitsn_folds, shuffleTrue, random_state42) for train_idx, test_idx in kf.split(X): # 分别拟合处理组和对照组 model1 GradientBoostingRegressor(n_estimators100, max_depth3) model0 GradientBoostingRegressor(n_estimators100, max_depth3) model1.fit(X[train_idx][W[train_idx] 1], Y[train_idx][W[train_idx] 1]) model0.fit(X[train_idx][W[train_idx] 0], Y[train_idx][W[train_idx] 0]) tau_hat[test_idx] model1.predict(X[test_idx]) - model0.predict(X[test_idx]) return tau_hat逻辑说明T-learner 分别对处理组和对照组拟合模型预测值之差就是 CATE 估计。参数上 max_depth 控制模型复杂度太深会过拟合太浅会欠拟合。书里指出T-learner 在样本量不均衡时表现差——如果处理组很少model1 拟合不好CATE 估计就有偏。5.2 策略学习从 CATE 到最优决策规则书里第 5 章讲策略学习。给定 CATE 估计最优策略是如果 τ(x) 0则处理否则不处理。但实操中要考虑成本——如果处理有成本 c则最优策略是 τ(x) c 时处理。书里 5.2 节的经验福利最大化框架把这个决策问题形式化了。策略价值 V(π) E[Y(π(X))]目标是找到最大化 V 的策略。书里给出了策略评估和策略学习的方法包括 AIPW 版本的策略价值估计。def policy_value(Y, W, X, policy, e_hat, mu_hat): 用 AIPW 估计策略价值 policy: 函数输入 X 返回 0/1 决策 pi policy(X) n len(Y) value np.mean( mu_hat[range(n), pi] (W pi) * (Y - mu_hat[range(n), W]) / np.where(pi 1, e_hat, 1 - e_hat) ) return value逻辑说明这个估计量是策略价值的双重稳健估计。参数上 mu_hat 是结果模型的预测矩阵两列分别对应处理和不处理。e_hat 是倾向得分。策略 π 可以是任何从 X 到 {0,1} 的映射。坑在哪策略学习容易过拟合——在训练集上找到的最优策略在测试集上可能表现差。书里建议用交叉验证或样本分割来评估策略价值。6. 避坑与排查因果推断落地时最容易翻车的五件事6.1 现象置信区间覆盖率远低于名义水平原因倾向得分极端值未截断或者方差估计公式用错完全随机化 vs Bernoulli 试验混用。解决检查倾向得分分布截断在 0.05 到 0.95确认试验设计类型用对应的方差公式。书里定理 1.2 的方差公式适用于 Bernoulli 试验完全随机化试验需要调整。6.2 现象DML 估计和 AIPW 估计差异很大原因两个模型的拟合质量差异大或者交叉拟合的折数太少导致预测不稳定。解决检查两个模型的交叉验证误差增加折数到 10重复多次交叉拟合取平均。如果差异持续存在说明至少一个模型设定有问题。6.3 现象CATE 估计在某些子群体上方差极大原因该子群体样本量太少或者处理组/对照组不均衡。解决检查子群体的样本量和处理分配比例如果样本太少考虑合并相似子群体或者用正则化方法如因果森林的 honesty 分裂来稳定估计。6.4 现象策略在训练集上表现好测试集上崩了原因策略过拟合或者策略价值估计用了同样的数据来选择和评估。解决用样本分割——一半数据学策略另一半评估或者用交叉验证的策略价值估计。书里 5.1 节的策略评估方法要求评估数据独立于策略学习数据。6.5 现象无混淆性假设不成立但不知道偏多少原因存在未观测混杂变量。解决做敏感性分析。常见做法是计算 Rosenbaum 边界或 E-value量化需要多强的未观测混杂才能推翻结论。书里没有展开这部分但这是观测研究发表的标配。7. 回归断点与干扰效应两个容易被低估的章节7.1 回归断点设计的局部线性回归书里第 8 章讲回归断点。核心场景处理分配由一个连续变量 X 是否超过阈值 c 决定。在阈值附近处理组和对照组近似随机。局部线性回归的实操import numpy as np from sklearn.linear_model import LinearRegression def rd_local_linear(Y, X, c, bandwidth): Y: 结果, X: 驱动变量, c: 阈值, bandwidth: 带宽 mask np.abs(X - c) bandwidth X_centered X[mask] - c W (X[mask] c).astype(int) # 分别拟合两侧 model1 LinearRegression() model0 LinearRegression() model1.fit(X_centered[W 1].reshape(-1, 1), Y[mask][W 1]) model0.fit(X_centered[W 0].reshape(-1, 1), Y[mask][W 0]) # 阈值处的截距差 tau_rd model1.intercept_ - model0.intercept_ return tau_rd逻辑说明带宽 bandwidth 是关键参数——太小则样本不足太大则线性近似不成立。书里 8.2 节讲了最优带宽选择和偏差感知推断。常见做法是用 IK 或 CCT 方法自动选带宽。7.2 干扰效应当 SUTVA 不成立时书里第 11、12 章讲干扰效应。社交网络、市场平台、传染病干预——这些场景下一个人的处理会影响另一个人的结果。书里介绍了暴露映射和置换检验以及有限总体方法。这部分实操门槛较高但书里给了清晰的框架。如果你做的是平台实验用户之间存在互动SUTVA 不成立差异均值估计有偏。书里建议用暴露映射重新定义处理或者用聚类随机化。7.3 一个具体技巧用模拟数据验证你的估计流程在真实数据上跑之前我习惯先用模拟数据验证整个流程。构造已知真实效应的数据跑一遍估计量看置信区间是否覆盖真值。import numpy as np def simulate_and_verify(n2000, true_tau1.0, seed42): 模拟数据验证因果估计流程 rng np.random.default_rng(seed) X rng.normal(0, 1, (n, 3)) e 1 / (1 np.exp(-X[:, 0])) W rng.binomial(1, e) Y0 X[:, 0] X[:, 1] rng.normal(0, 1, n) Y1 Y0 true_tau 0.5 * X[:, 2] Y W * Y1 (1 - W) * Y0 # 跑你的估计量 tau_hat aipw_estimation(Y, W, X) print(f真实 ATE: {true_tau 0.5 * np.mean(X[:, 2]):.3f}) print(f估计 ATE: {tau_hat:.3f}) return tau_hat逻辑说明这个模拟里处理效应是异质的随 X[:,2] 变化倾向得分是非线性的。如果你的估计量能接近真实值说明流程基本正确。参数上 n 越大越稳定true_tau 是基准效应。从那以后我每次上线新的因果推断流程前都强制走一遍模拟验证——先确认代码在已知真值的数据上能跑对再碰真实数据。这个习惯帮我省了至少三次“估计量符号反了”的翻车。希望帮到你。本文还有配套的精品资源点击获取