因果推断完全指南

从相关到因果:系统性掌握因果发现、因果效应估计和反事实推理的理论与实践

👤 尘渊因果研究团队 📅 2025-01-25 ⏱️ 预计阅读 60分钟 🔴 专家级 👁 3,892 阅读

🤔 为什么需要因果推断

传统机器学习擅长发现变量之间的统计相关性,能够准确预测"会发生什么"。然而,在很多实际场景中,我们需要回答更深层的问题:"如果...会怎样?"、"为什么会这样?"。这正是因果推断的核心价值所在。

💡 相关性 ≠ 因果性

经典的例子:冰淇淋销量与溺水事件高度相关,但这并不意味着冰淇淋导致溺水。真正的因果变量是炎热的天气。

🎭
混杂因素 Confounding
同时影响处理变量和结果变量的第三方变量,是因果推断中最常见的偏差来源。
🔗
中介变量 Mediation
处理变量通过中介变量间接影响结果,揭示因果机制的"黑箱"。
⚖️
调节变量 Moderation
改变处理变量与结果变量之间关系强度的变量,解释"何时"因果效应最强。

📚 因果推断基础

潜在结果框架 (Potential Outcomes)

Neyman-Rubin潜在结果框架是因果推断的数学基础。对于个体i和处理变量T,定义:

Yi(1) = 个体i接受处理时的潜在结果
Yi(0) = 个体i未接受处理时的潜在结果
个体因果效应 = Yi(1) - Yi(0)
⚠️ 根本问题

对于任意个体,我们只能观察到一种潜在结果(接受处理或未接受处理),永远无法直接观察到个体因果效应。这就是因果推断的"反事实"本质。

平均因果效应 (ATE)

ATE = E[Y(1) - Y(0)] = E[Y(1)] - E[Y(0)]

🏗️ 因果推断框架

1. Pearl的结构因果模型 (SCM)

Judea Pearl提出的因果图模型为因果推断提供了图形化的数学语言。

因果有向无环图 (DAG) 示例

Z 混杂因素 T 处理 M 中介 Y 结果

■ 直接效应 ■ 间接效应(通过中介) ■ 混杂路径

2. do-演算 (Do-Calculus)

Pearl的do-演算提供了一套规则,用于从观测数据中识别因果效应。

P(Y | do(T=t)) = Σz P(Y | T=t, Z=z) P(Z=z)

(后门准则调整公式)

🔍 因果发现算法

算法 假设条件 适用场景 时间复杂度
PC算法 faithful, 无潜变量 连续/离散变量,中小规模 O(n2)
GES faithful, 大样本 需要评分函数优化 O(nk)
NOTEARS 可微分, 无环约束 深度学习结合,大规模 O(n3)
FCI 允许潜变量 存在未观测混杂因素 指数级

PC算法实现

from causallearn.search.ConstraintBased.PC import pc from causallearn.utils.GraphUtils import GraphUtils # 运行PC算法 cg = pc(data, alpha=0.05, indep_test="fisherz") # 可视化结果 print(cg.G) GraphUtils.to_pydot(cg.G).write_png("causal_graph.png")

📊 因果效应估计

1. 重加权方法

逆概率加权 (IPW)

from sklearn.linear_model import LogisticRegression import numpy as np # 估计倾向得分 ps_model = LogisticRegression() ps_model.fit(X, T) propensity_scores = ps_model.predict_proba(X)[:, 1] # 计算IPW权重 weights = T / propensity_scores + (1 - T) / (1 - propensity_scores) # 加权估计ATE ate_ipw = np.mean(weights * T * Y) - np.mean(weights * (1 - T) * Y)

2. 分层/匹配方法

from sklearn.neighbors import NearestNeighbors # 倾向得分匹配 ps = propensity_scores.reshape(-1, 1) nbrs = NearestNeighbors(n_neighbors=1).fit(ps[T==0]) distances, indices = nbrs.kneighbors(ps[T==1]) # 匹配后计算ATE matched_control_outcomes = Y[T==0][indices.flatten()] ate_matching = np.mean(Y[T==1]) - np.mean(matched_control_outcomes)

3. 双重稳健估计 (Doubly Robust)

✨ 双重稳健性

只要倾向得分模型或结果模型之一正确,估计就是一致的。这是因果推断中最常用的稳健方法之一。

🔮 反事实推理

反事实推理回答"如果当初...会怎样"的问题,是因果推断的最高层次。

三步反事实算法

  1. 溯因 (Abduction): 根据证据E=e,更新外生变量的分布 P(U|E=e)
  2. 行动 (Action): 修改模型,将T设置为反事实值t'
  3. 预测 (Prediction): 在新模型中计算Y的期望值
# 使用DoWhy进行反事实分析 from dowhy import CausalModel model = CausalModel( data=data, treatment='T', outcome='Y', graph='digraph {Z->T;Z->Y;T->Y}' ) # 估计反事实结果 cf_outcomes = model.refute_estimate( identified_estimand, estimate, method_name="placebo_treatment_refuter" )

🛠️ 实践应用

工具推荐

  • DoWhy: 微软开源的因果推断库,提供完整的因果分析流程
  • CausalML: Uber的因果机器学习库,专注于 uplift modeling
  • EconML: 微软的经济机器学习库,支持复杂条件平均处理效应
  • dowhy.gcm: 基于结构因果模型的因果推断

完整分析流程

# 1. 建模 causal_graph = """ digraph { Age -> Income; Age -> Purchase; Income -> Purchase; Ad -> Purchase; } """ model = CausalModel( data=df, treatment='Ad', outcome='Purchase', graph=causal_graph ) # 2. 识别 identified_estimand = model.identify_effect() print(identified_estimand) # 3. 估计 estimate = model.estimate_effect( identified_estimand, method_name="backdoor.propensity_score_weighting" ) print(f"因果效应估计: {estimate.value}") # 4. 反驳检验 refutation = model.refute_estimate( identified_estimand, estimate, method_name="placebo_treatment_refuter" ) print(refutation)