🤔 为什么需要因果推断
传统机器学习擅长发现变量之间的统计相关性,能够准确预测"会发生什么"。然而,在很多实际场景中,我们需要回答更深层的问题:"如果...会怎样?"、"为什么会这样?"。这正是因果推断的核心价值所在。
💡 相关性 ≠ 因果性
经典的例子:冰淇淋销量与溺水事件高度相关,但这并不意味着冰淇淋导致溺水。真正的因果变量是炎热的天气。
混杂因素 Confounding
同时影响处理变量和结果变量的第三方变量,是因果推断中最常见的偏差来源。
中介变量 Mediation
处理变量通过中介变量间接影响结果,揭示因果机制的"黑箱"。
调节变量 Moderation
改变处理变量与结果变量之间关系强度的变量,解释"何时"因果效应最强。
📚 因果推断基础
潜在结果框架 (Potential Outcomes)
Neyman-Rubin潜在结果框架是因果推断的数学基础。对于个体i和处理变量T,定义:
Yi(1) = 个体i接受处理时的潜在结果
Yi(0) = 个体i未接受处理时的潜在结果
个体因果效应 = Yi(1) - Yi(0)
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) 示例
■ 直接效应 ■ 间接效应(通过中介) ■ 混杂路径
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)
✨ 双重稳健性
只要倾向得分模型或结果模型之一正确,估计就是一致的。这是因果推断中最常用的稳健方法之一。
🔮 反事实推理
反事实推理回答"如果当初...会怎样"的问题,是因果推断的最高层次。
三步反事实算法
- 溯因 (Abduction): 根据证据E=e,更新外生变量的分布 P(U|E=e)
- 行动 (Action): 修改模型,将T设置为反事实值t'
- 预测 (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)