本文参考微软dowhy官网文档,并参考相关博客进行整理而来,官方地址:​​https://github.com/py-why/dowhy​

0x01. 概述

因果推理是基于观察数据进行反事实估计,分析干预与结果之间的因果关系。

DoWhy是微软发布的端到端因果推断Python库,主要特点是:

  • 基于一定经验假设的基础上,将问题转化为因果图,验证假设。
  • 提供因果推断的接口,整合了两种因果框架。
  • DoWhy支持对后门、前门和工具的平均因果效应的估计,自动验证结果的准确性、鲁棒性较高。

DoWhy 的整个因果推断过程可以划分为四大步骤:

  1. 「建模」(model): 利用假设(先验知识)对因果推断问题建模。
  2. 「识别」(identify): 在假设(模型)下识别因果效应的表达式(因果估计量)。
  3. 「估计」(estimate): 使用统计方法对表达式进行估计。
  4. 「反驳」(refute): 使用各种鲁棒性检查来验证估计的正确性。

先看一张流程图:

因果推断之微软开源的dowhy使用学习_python


对四个流程进行解释说明:

0x01_1. 建模

为每个问题创建一个因果图模型,以保证因果假设的明确性。该因果图不需要是完整的,你可以只提供部分图,来表示某些变量的先验知识(即指定其类型),DoWhy 支持自动将剩余的变量视为潜在的混杂因子

目前,DoWhy支持以下形式的因果假设:

  • 「图」(Graph): 提供 gml 或 dot 形式的因果图,具体可以是文件或字符串格式
  • 命名变量集合」(Named variable sets): 直接提供变量的类型,包括 混杂因子(common causes / cofounders)工具变量(instrumental variables)结果修改变量(effect modifiers)、**前门变量(front-door variables)**等。

0x01_2. 识别

基于构建的因果图,DoWhy 会基于所有可能的方式来识别因果效应。识别方式包含:

  • 后门准则(Back-door criterion)
  • 前门准则(Front-door criterion)
  • 工具变量(Instrumental Variables)
  • 中介-直接或间接结果识别(Mediation-Direct and indirect effect identification)

0x01_3. 估计

  • 基于估计干预分配的方法(Methods based on estimating the treatment assignment)
  • 基于倾向的分层(Propensity-based Stratification)
  • 倾向得分匹配(Propensity Score Matching)
  • 逆向倾向加权(Inverse Propensity Weighting)
  • 基于估计结果模型的方法(Methods based on estimating the outcome model)
  • 线性回归(Linear Regression)
  • 广义线性模型(Generalized Linear Models)
  • 基于工具变量等式的方法(Methods based on the instrumental variable equation)
  • 二元工具/Wald 估计器(Binary Instrument/Wald Estimator)
  • 两阶段最小二乘法(Two-stage least squares)
  • 非连续回归(Regression discontinuity)
  • 基于前门准则和一般中介的方法(Methods for front-door criterion and general mediation)
  • 两层线性回归(Two-stage linear regression)

0x01_4. 反驳

DoWhy 支持多种反驳方法来验证估计的正确性,具体列表如下:

  • 添加随机混杂因子:添加一个随机变量作为混杂因子后估计因果效应是否会改变(期望结果:不会)
  • 安慰剂干预:将真实干预变量替换为独立随机变量后因果效应是否会改变(期望结果:因果效应归零)
  • 虚拟结果:将真实结果变量替换为独立随机变量后因果效应是否会改变(期望结果:因果效应归零)
  • 模拟结果:将数据集替换为基于接近给定数据集数据生成过程的方式模拟生成的数据集后因果效应是否会改变(期望结果:与数据生成过程的效应参数相匹配)
  • 添加未观测混杂因子:添加一个额外的与干预和结果相关的混杂因子后因果效应的敏感性(期望结果:不过度敏感)
  • 数据子集验证:将给定数据集替换为一个随机子集后因果效应是否会改变(期望结果:不会)
  • 自助验证:将给定数据集替换为同一数据集的自助样本后因果效应是否会改变(期望结果:不会)

0x02. 入门案例学习

依旧以微软官方文档为主进行学习,其中包含基础案例和酒店住宿分析问题。

0x02_1. 安装环境

windows环境下,请在python3.8以上的环境下进行安装。

pip install dowhy

conda install -c conda-forge dowhy

其次是需要安装pygraphviz,windows具体安装教程参考:​​传送门​​

0x02_1. 导包加载数据

from dowhy import CausalModel
import dowhy.datasets

# Load some sample data
data = dowhy.datasets.linear_dataset(
beta=10, # 因果效应值
num_common_causes=5, # 混杂因子,用w表示,作用于干预变量和结果变量
num_instruments=2, # 工具变量,用 Z 表示,作用于干预变量(间接影响结果)
num_samples=10000, # 样本数量
treatment_is_binary=True) # 干预为二元变量,用 v 表示
df = data["df"] # DoWhy 使用 pandas 的 dataframe 来载入数据
print(df.head(5))

结果输出如下:

Z0     Z1     W0      W1      W2      W3      W4     v0     y
0 0.0 0.478130 0.313025 0.274645 1.794765 -1.551661 1.250724 True 17.033979
1 0.0 0.546833 -0.197845 -2.404349 1.120160 -1.011344 0.812087 True 3.074740
2 0.0 0.874135 1.047078 -0.056460 0.440413 -0.067349 0.970203 True 15.530070
3 0.0 0.913483 0.934282 -1.180582 0.498135 -1.079074 -1.659924 True 5.046809
4 0.0 0.366969 -0.364818 0.630643 -0.330043 0.572139 -0.433893 True 11.004590

输出 gml_graph,内容一致只是表达形式不同,查看一下构建的,代码如下:

print(data["dot_graph"])

结果如下:

digraph {v0->y;W0-> v0; W1-> v0; W2-> v0; W3-> v0; W4-> v0;Z0-> v0; Z1-> v0;W0-> y; W1-> y; W2-> y; W3-> y; W4-> y;}

0x02_2. 执行因果推断

0x02_2_1. 建模

model=CausalModel(
data = df,
treatment=data["treatment_name"],
outcome=data["outcome_name"],
graph=data["gml_graph"]
)
model.view_model() # 对构建的因果图可视化
from IPython.display import Image, display
display(Image(filename="causal_model.png"))

执行代码后,结果如下:

因果推断之微软开源的dowhy使用学习_学习_02


上图包含了数据中给定的先验知识,我们可以利用这张图来识别因果效应(从因果估计量到概率表达式)并进行估计。

0x02_2_2. 识别

可以脱离于数据,仅根据图进行识别,其给出的结果是一个用于计算的「表达式」。具体的代码如下:

identified_estimand = model.identify_effect()
print(identified_estimand)

输出结果如下:

Estimand type: EstimandType.NONPARAMETRIC_ATE

### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
─────(E[y|W1,W0,W4,W2,W3])
d[v₀]
Estimand assumption 1, Unconfoundedness: If U→{v0} and U→y then P(y|v0,W1,W0,W4,W2,W3,U) = P(y|v0,W1,W0,W4,W2,W3)

### Estimand : 2
Estimand name: iv
Estimand expression:
⎡ -1⎤
⎢ d ⎛ d ⎞ ⎥
E⎢─────────(y)⋅⎜─────────([v₀])⎟ ⎥
⎣d[Z₁ Z₀] ⎝d[Z₁ Z₀] ⎠ ⎦
Estimand assumption 1, As-if-random: If U→→y then ¬(U →→{Z1,Z0})
Estimand assumption 2, Exclusion: If we remove {Z1,Z0}→{v0}, then ¬({Z1,Z0}→y)

### Estimand : 3
Estimand name: frontdoor
No such variable(s) found!

0x02_2_3. 估计

estimate = model.estimate_effect(identified_estimand,
method_name="backdoor.propensity_score_stratification")
print(estimate)

输出结果如下:

*** Causal Estimate ***

## Identified estimand
Estimand type: EstimandType.NONPARAMETRIC_ATE

### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
─────(E[y|W1,W0,W4,W2,W3])
d[v₀]
Estimand assumption 1, Unconfoundedness: If U→{v0} and U→y then P(y|v0,W1,W0,W4,W2,W3,U) = P(y|v0,W1,W0,W4,W2,W3)

## Realized estimand
b: y~v0+W1+W0+W4+W2+W3
Target units: ate

## Estimate
Mean value: 9.942351552191596

可以通过 target_units 参数来选择因果效应分析的群体,如 ate(群体层面)、att(干预组)、ate(对照组)。也可以指定结果修改变量来分析不同变量对结果的影响。

0x02_2_4. 反驳

反驳阶段参考其他博客内容进行实验,采用多种方式验证。

添加一个随机的混杂因子变量

res_random=model.refute_estimate(identified_estimand, estimate, method_name="random_common_cause")
print(res_random)

输出结果如下:

Refute: Add a random common cause
Estimated effect:9.942351552191596
New effect:9.942351552191594
p value:2.0

添加一个未观测的混杂因子变量

res_unobserved=model.refute_estimate(identified_estimand, estimate, method_name="add_unobserved_common_cause", confounders_effect_on_treatment="binary_flip", confounders_effect_on_outcome="linear", effect_strength_on_treatment=0.01, effect_strength_on_outcome=0.02)
print(res_unobserved)

输出结果如下:

Refute: Add an Unobserved Common Cause
Estimated effect:9.942351552191596
New effect:9.451291104262298

用随机变量代替干预

res_placebo=model.refute_estimate(identified_estimand, estimate, method_name="placebo_treatment_refuter", placebo_type="permute")
print(res_placebo)

输出结果如下:

Refute: Use a Placebo Treatment
Estimated effect:9.942351552191596
New effect:-0.014386044006960131
p value:0.8999999999999999

移除数据的一个随机子集

res_subset=model.refute_estimate(identified_estimand, estimate, method_name="data_subset_refuter", subset_fraction=0.9)
print(res_subset)

输出结果如下:

Refute: Use a subset of data
Estimated effect:9.942351552191596
New effect:9.934071804782558
p value:0.8600000000000001

我们可以通过 random_seed 参数来保证结果的可重现性。

0x03. 总结

跟着官网的实验跑一趟,对该分析工具认识加深,特别是工具安装和使用,分析的结果可呈现。对该工具的认识也加深不少。

参考链接:

  1. ​微软DoWhy官方教程​
  2. ​因果推断框架 DoWhy 入门​