因果推断及其与机器学习的联系教程(使用DoWhy+EconML)#
本教程将逐步介绍如何使用DoWhy+EconML库进行因果推断。在此过程中,我们将强调与机器学习的联系——机器学习如何帮助构建因果效应估计器,以及因果推理如何帮助构建更稳健的机器学习模型。
数据科学问题的例子,这些本质上是因果推断问题:* A/B实验:如果我改变算法,会导致更高的成功率吗?* 政策决策:如果我们采用这种治疗/政策,会导致更健康的患者/更多的收入等吗?* 政策评估:根据我现在所知道的,我的政策是有帮助还是有害?* 信用归因:人们是因为推荐算法而购买的吗?他们无论如何都会购买吗?
在本教程中,您将:* 了解因果推理在决策中的必要性,以及预测任务和决策任务之间的区别。
通过因果推理的四个步骤进行实践:模型、识别、估计和反驳。
了解如何使用DoWhy+EconML通过4行代码帮助您估计因果效应,利用统计学和机器学习的最新方法来估计因果效应并评估其对建模假设的鲁棒性。
通过Jupyter笔记本完成真实世界的案例研究,学习如何在不同场景中应用因果推理,包括评估客户忠诚度计划对未来交易的影响、预测哪些用户会受到干预(如广告)的积极影响、产品定价以及归因哪些因素对结果贡献最大。
了解因果推理与现代机器学习模型挑战之间的联系。
目录
1 为什么需要因果推断?
1.1 定义因果效应
1.2 预测与因果推断的区别
1.3 因果推断的两个基本挑战
2 因果推理的四个步骤
2.1 DoWhy+EconML 解决方案
2.2 一个神秘的数据集:你能找出是否存在因果效应吗?
2.2.1 使用因果图对数据生成过程进行模型假设
2.2.2 根据因果模型识别目标量的正确估计量
2.2.3 估计目标估计量
2.2.4 使用反驳测试检查估计的稳健性
3 使用DoWhy+EconML的案例研究
3.1 估计客户忠诚度计划的影响
3.2 在线公司的推荐A/B测试
3.3 用户细分以进行目标干预
3.4 软件公司的多投资归因
4 与基础机器学习挑战的连接
5 更多资源
5.1 DoWhy+EconML 库
5.2 关于因果推断及其与机器学习联系的视频讲座
5.3 详细的KDD因果推断教程
5.4 关于因果性和机器学习的书籍章节
5.5 微软的因果性与机器学习小组
为什么需要因果推断?#
许多关键的数据科学任务都涉及决策。数据科学家经常被要求支持各个层次的决策者,帮助他们充分利用数据以实现期望的结果。例如,一位高管做出投资和资源配置决策,一位营销人员确定折扣政策,一个产品团队优先考虑发布哪些功能,或者一位医生决定为患者提供哪种治疗。
这些决策者中的每一个都在提出一个假设性问题。对这些问题的数据驱动答案需要理解事件的原因以及如何采取行动以改善未来的结果。
定义因果效应#
假设我们想要找到采取行动A对结果Y的因果效应。为了定义因果效应,考虑两个世界:1. 世界1(现实世界):采取了行动A并观察到了Y 2. 世界2(反事实世界):没有采取行动A(但其他一切都相同)
因果效应是现实世界中获得的Y值与反事实世界中获得的Y值之间的差异。

换句话说,A导致Y当且仅当改变A会导致Y的变化,保持其他一切不变。在保持其他一切不变的情况下改变A被称为干预,并用特殊符号\(do(A)\)表示。
正式地,因果效应是Y通过A的单位干预变化而改变的量:
为了估计效果,黄金标准是进行随机实验,其中随机选择的单位子集被处理(\(A=1\)),而另一个子集不被处理(\(A=0\))。这些子集近似于不相交的真实世界和反事实世界,随机化确保了两个子集之间没有系统性差异(“保持其他一切不变”)。
然而,进行随机实验并不总是可行的。为了回答因果问题,我们通常需要依赖观察数据或记录数据。这些观察数据受到相关性和未观察到的混杂因素的影响,因此在哪些单位上采取了行动和哪些单位没有采取行动之间存在系统性差异。例如,新的营销活动可能在假期期间部署,新功能可能仅应用于高活跃用户,或者老年患者可能更有可能接受新药,等等。因果推理方法的目标是从数据中去除这些相关性和混杂因素,并估计行动的真实效果,如上式所示。
预测与因果推断的区别#
|
|
因果推理的两个基本挑战#
我们从未观察到反事实的世界
无法直接计算因果效应
必须估计反事实
验证中的挑战
多种因果机制可以适应单一的数据分布 * 仅凭数据不足以进行因果推断 * 需要领域知识和假设
因果推理的四个步骤#
由于没有可用的真实测试数据集可以与估计值进行比较,因果推断需要一系列原则性步骤来实现良好的估计器。
让我们通过一个示例数据集来说明这四个步骤。本教程要求您下载两个库:DoWhy 和 EconML。两者都可以通过以下命令安装:pip install dowhy econml。
[1]:
# Required libraries
import dowhy
from dowhy import CausalModel
import dowhy.datasets
# Avoiding unnecessary log messges and warnings
import logging
logging.getLogger("dowhy").setLevel(logging.WARNING)
import warnings
from sklearn.exceptions import DataConversionWarning
warnings.filterwarnings(action='ignore', category=DataConversionWarning)
# Load some sample data
data = dowhy.datasets.linear_dataset(
beta=10,
num_common_causes=5,
num_instruments=2,
num_samples=10000,
treatment_is_binary=True,
stddev_treatment_noise=10)
I. 建模
第一步是将我们的领域知识编码成一个因果模型,通常表示为图。因果推理分析的最终结果很大程度上取决于输入的假设,因此这一步非常重要。为了估计因果效应,大多数常见问题涉及指定两种类型的变量:
混杂因素 (common_causes): 这些是同时影响行动和结果的变量。因此,行动和结果之间观察到的任何相关性可能仅仅是由于混杂因素变量,而不是由于行动对结果的任何因果关系。
工具变量(instruments):这些是特殊的变量,它们引起行动,但不直接影响结果。此外,它们不受任何影响结果的变量的影响。如果使用得当,工具变量可以帮助减少偏差。
[2]:
# I. Create a causal model from the data and domain knowledge.
model = CausalModel(
data=data["df"],
treatment=data["treatment_name"],
outcome=data["outcome_name"],
common_causes=data["common_causes_names"],
instruments=data["instrument_names"])
为了可视化图表,我们可以编写,
[3]:
model.view_model(layout="dot")
from IPython.display import Image, display
display(Image(filename="causal_model.png"))
通常,你可以指定一个因果图来描述给定数据集的数据生成过程的机制。图中的每个箭头表示一个因果机制:“A->B”意味着变量A导致变量B。
[4]:
# I. Create a causal model from the data and given graph.
model = CausalModel(
data=data["df"],
treatment=data["treatment_name"][0],
outcome=data["outcome_name"][0],
graph=data["gml_graph"])
model.view_model(layout="dot")
II. 识别
提供领域知识的两种方式(无论是通过命名的混杂变量集和工具变量集,还是通过因果图)都对应于一个潜在的因果图。给定一个因果图和一个目标量(例如,A对B的影响),识别的过程是检查在给定观察变量的情况下是否可以估计目标量。重要的是,识别只考虑观察数据中可用的变量名称;它不需要访问数据本身。与上述两种变量相关,因果推断有两种主要的识别方法。
后门准则(或更一般地,调整集):如果动作A和结果Y的所有共同原因都被观察到,那么后门准则意味着可以通过对所有共同原因进行条件化来识别因果效应。这是一个简化的定义(有关正式定义,请参阅CausalML书籍的第3章)。
\[E[Y│do(A=a)] = E_W E[Y|A=a, W=w]\]
其中 \(W\) 指的是 \(A\) 和 \(Y\) 的共同原因(混杂因素)的集合。
工具变量(IV)识别:如果有可用的工具变量,那么即使行动和结果的任何(或没有)共同原因未被观察到,我们也可以估计效果。工具变量识别利用了工具仅直接影响行动的事实,因此工具对结果的影响可以分为两个连续部分:工具对行动的影响和行动对处理的影响。然后,它依赖于估计工具对行动和结果的影响来估计行动对结果的影响。对于二元工具,效果估计由以下公式给出,
[5]:
# II. Identify causal effect and return target estimands
identified_estimand = model.identify_effect(proceed_when_unidentifiable=True)
print(identified_estimand)
Estimand type: EstimandType.NONPARAMETRIC_ATE
### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
─────(E[y|W3,W4,W1,W2,W0])
d[v₀]
Estimand assumption 1, Unconfoundedness: If U→{v0} and U→y then P(y|v0,W3,W4,W1,W2,W0,U) = P(y|v0,W3,W4,W1,W2,W0)
### 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!
III. 估计
顾名思义,估计步骤涉及构建一个统计估计器,该估计器可以计算在前一步骤中确定的目标估计量。已经提出了许多用于因果推断的估计器。DoWhy 实现了一些标准估计器,而 EconML 实现了一组强大的使用机器学习的估计器。
我们展示了一个使用DoWhy进行倾向得分分层的例子,以及一个使用EconML的基于机器学习的方法,称为Double-ML。
[6]:
# III. Estimate the target estimand using a statistical method.
propensity_strat_estimate = model.estimate_effect(identified_estimand,
method_name="backdoor.dowhy.propensity_score_stratification")
print(propensity_strat_estimate)
*** Causal Estimate ***
## Identified estimand
Estimand type: EstimandType.NONPARAMETRIC_ATE
### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
─────(E[y|W3,W4,W1,W2,W0])
d[v₀]
Estimand assumption 1, Unconfoundedness: If U→{v0} and U→y then P(y|v0,W3,W4,W1,W2,W0,U) = P(y|v0,W3,W4,W1,W2,W0)
## Realized estimand
b: y~v0+W3+W4+W1+W2+W0
Target units: ate
## Estimate
Mean value: 9.992994156114678
[7]:
import econml
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LassoCV
from sklearn.ensemble import GradientBoostingRegressor
dml_estimate = model.estimate_effect(identified_estimand,
method_name="backdoor.econml.dml.DML",
method_params={
'init_params': {'model_y':GradientBoostingRegressor(),
'model_t': GradientBoostingRegressor(),
'model_final':LassoCV(fit_intercept=False), },
'fit_params': {}
})
print(dml_estimate)
*** Causal Estimate ***
## Identified estimand
Estimand type: EstimandType.NONPARAMETRIC_ATE
### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
─────(E[y|W3,W4,W1,W2,W0])
d[v₀]
Estimand assumption 1, Unconfoundedness: If U→{v0} and U→y then P(y|v0,W3,W4,W1,W2,W0,U) = P(y|v0,W3,W4,W1,W2,W0)
## Realized estimand
b: y~v0+W3+W4+W1+W2+W0 |
Target units: ate
## Estimate
Mean value: 9.992748891600764
Effect estimates: [[9.99274889]]
IV. 反驳
最后,检查估计的鲁棒性可能是因果分析中最重要的步骤。我们使用步骤1-3获得了一个估计,但每一步都可能做出某些可能不成立的假设。在没有适当的验证“测试”集的情况下,这一步依赖于反驳测试,这些测试试图利用良好估计器的特性来反驳所获得估计的正确性。例如,一个反驳测试(placebo_treatment_refuter)检查当行动变量被替换为与所有其他变量独立的随机变量时,估计器是否返回估计值为0。
[8]:
# IV. Refute the obtained estimate using multiple robustness checks.
refute_results = model.refute_estimate(identified_estimand, propensity_strat_estimate,
method_name="placebo_treatment_refuter")
print(refute_results)
Refute: Use a Placebo Treatment
Estimated effect:9.992994156114678
New effect:-0.006921466905131964
p value:0.96
DoWhy+EconML 解决方案#
我们将使用DoWhy+EconML库进行因果推断。DoWhy为四个步骤提供了一个通用的API,而EconML为估计步骤提供了高级估计器。
DoWhy 允许您可视化、形式化并测试您所做的假设,以便您更好地理解分析并避免得出错误的结论。它通过明确关注假设并尽可能引入对假设有效性的自动检查来实现这一点。正如您将看到的,DoWhy 的强大之处在于它提供了一个正式的因果框架来编码领域知识,并且可以运行自动鲁棒性检查以验证来自任何估计方法的因果估计。
此外,随着数据变得高维,我们需要能够处理已知混杂因素的专门方法。这里我们使用EconML,它实现了许多最先进的因果估计方法。该包为所有技术提供了一个通用的API,每种技术都作为一系列机器学习任务实现,允许使用任何现有的机器学习软件来解决这些子任务,使您能够插入您已经熟悉的ML模型,而不是学习一个新的工具包。EconML的强大之处在于,您现在可以像运行线性回归或随机森林一样轻松地实现最先进的因果推断。
Together, DoWhy+EconML 通过提供一个最先进的、端到端的因果推理框架,包括最新的因果估计和自动化鲁棒性程序,使得回答“如果”问题变得容易得多。
一个神秘的数据集:你能找出是否存在因果关系吗?#
为了逐步了解这四个步骤,让我们考虑一下神秘数据集问题。假设你得到了一些包含治疗和结果的数据。你能确定治疗是否导致了结果,还是这种相关性纯粹是由于另一个共同的原因?
[9]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
import dowhy.datasets, dowhy.plotter
下面我们创建一个数据集,其中真实的因果效应由随机变量决定。它可以是0或1。
[10]:
rvar = 1 if np.random.uniform() > 0.2 else 0
is_linear = False # A non-linear dataset. Change to True to see results for a linear dataset.
data_dict = dowhy.datasets.xy_dataset(10000, effect=rvar,
num_common_causes=2,
is_linear=is_linear,
sd_error=0.2)
df = data_dict['df']
print(df.head())
dowhy.plotter.plot_treatment_outcome(df[data_dict["treatment_name"]], df[data_dict["outcome_name"]],
df[data_dict["time_val"]])
Treatment Outcome w0 s w1
0 17.098831 34.939507 -3.377379 0.283867 0.504289
1 18.320187 37.218290 3.418561 2.237479 1.102549
2 14.073424 28.034111 -2.808841 8.091403 0.161078
3 9.691199 20.592411 1.853534 4.465082 1.562526
4 8.888019 18.451855 1.693628 4.518675 0.698603
关于使用因果图的数据生成过程的模型假设#
[11]:
model= CausalModel(
data=df,
treatment=data_dict["treatment_name"],
outcome=data_dict["outcome_name"],
common_causes=data_dict["common_causes_names"],
instruments=data_dict["instrument_names"])
model.view_model(layout="dot")
根据因果模型确定目标量的正确估计量#
[12]:
identified_estimand = model.identify_effect(proceed_when_unidentifiable=True)
print(identified_estimand)
Estimand type: EstimandType.NONPARAMETRIC_ATE
### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
────────────(E[Outcome|w0,w1])
d[Treatment]
Estimand assumption 1, Unconfoundedness: If U→{Treatment} and U→Outcome then P(Outcome|Treatment,w0,w1,U) = P(Outcome|Treatment,w0,w1)
### Estimand : 2
Estimand name: iv
No such variable(s) found!
### Estimand : 3
Estimand name: frontdoor
No such variable(s) found!
由于这是观察数据,警告询问您在此数据集中是否缺少任何未观察到的混杂因素。如果有,那么忽略它们将导致错误的估计。如果您想禁用警告,可以使用proceed_when_unidentifiable=True作为identify_effect的附加参数。
估计目标估计量#
[13]:
estimate = model.estimate_effect(identified_estimand,
method_name="backdoor.linear_regression")
print(estimate)
print("Causal Estimate is " + str(estimate.value))
# Plot Slope of line between action and outcome = causal effect
dowhy.plotter.plot_causal_effect(estimate, df[data_dict["treatment_name"]], df[data_dict["outcome_name"]])
*** Causal Estimate ***
## Identified estimand
Estimand type: EstimandType.NONPARAMETRIC_ATE
### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
────────────(E[Outcome|w0,w1])
d[Treatment]
Estimand assumption 1, Unconfoundedness: If U→{Treatment} and U→Outcome then P(Outcome|Treatment,w0,w1,U) = P(Outcome|Treatment,w0,w1)
## Realized estimand
b: Outcome~Treatment+w0+w1
Target units: ate
## Estimate
Mean value: 1.9984660510163166
Causal Estimate is 1.9984660510163166
正如你所看到的,对于一个非线性的数据生成过程,线性回归模型无法从观察到的相关性中区分出因果效应。
如果数据生成过程(DGP)是线性的,那么简单的线性回归就会有效。为了验证这一点,请尝试在上面的10号单元格中将is_linear=True设置为真。
为了建模非线性数据(以及具有高维混杂因素的数据),我们需要更先进的方法。以下是使用EconML中的双重机器学习估计器的示例。该估计器使用基于机器学习的方法,如梯度提升树,来学习结果与混杂因素之间的关系,以及处理与混杂因素之间的关系,最后比较结果与处理之间的残差变化。
[14]:
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LassoCV
from sklearn.ensemble import GradientBoostingRegressor
dml_estimate = model.estimate_effect(identified_estimand, method_name="backdoor.econml.dml.DML",
control_value = 0,
treatment_value = 1,
confidence_intervals=False,
method_params={"init_params":{'model_y':GradientBoostingRegressor(),
'model_t': GradientBoostingRegressor(),
"model_final":LassoCV(fit_intercept=False),
'featurizer':PolynomialFeatures(degree=2, include_bias=True)},
"fit_params":{}})
print(dml_estimate)
*** Causal Estimate ***
## Identified estimand
Estimand type: EstimandType.NONPARAMETRIC_ATE
### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
────────────(E[Outcome|w0,w1])
d[Treatment]
Estimand assumption 1, Unconfoundedness: If U→{Treatment} and U→Outcome then P(Outcome|Treatment,w0,w1,U) = P(Outcome|Treatment,w0,w1)
## Realized estimand
b: Outcome~Treatment+w0+w1 |
Target units: ate
## Estimate
Mean value: 1.0899098944494576
Effect estimates: [[1.08990989]]
如您所见,DML方法获得了更好的估计,即更接近真实因果效应1。
使用反驳测试检查估计的稳健性#
[15]:
res_random=model.refute_estimate(identified_estimand, dml_estimate, method_name="random_common_cause")
print(res_random)
Refute: Add a random common cause
Estimated effect:1.0899098944494576
New effect:1.085246993578022
p value:0.96
[16]:
res_placebo=model.refute_estimate(identified_estimand, dml_estimate,
method_name="placebo_treatment_refuter", placebo_type="permute",
num_simulations=20)
print(res_placebo)
Refute: Use a Placebo Treatment
Estimated effect:1.0899098944494576
New effect:-0.00016849220899518638
p value:0.39172166125577124
使用DoWhy+EconML的案例研究#
在实践中,随着数据变得高维化,简单的估计器将无法估计出正确的因果效应。更高级的监督机器学习模型也不起作用,通常比简单的回归更差,因为它们包含了额外的正则化技术,这些技术有助于最小化预测误差,但可能会对估计因果效应产生不良影响。因此,我们需要专门用于估计因果效应的方法。同时,我们还需要合适的反驳方法,以检查估计的稳健性。
这里是一个使用DoWhy+EconML处理高维数据集的示例。
更多详情请参阅此notebook。
下面我们提供了展示DoWhy+EconML使用案例的链接。
评估客户忠诚度计划的影响#
在线公司的推荐A/B测试#
用户细分以进行针对性干预#
软件公司的多投资归因#
与基础机器学习挑战的连接#
因果关系与构建机器学习模型中的许多基本挑战相关,包括分布外泛化、公平性、可解释性和隐私。

因果关系如何帮助解决上述许多挑战是一个活跃的研究领域。
更多资源#
DoWhy+EconML 库#
DoWhy 代码: microsoft/dowhy
DoWhy 笔记本: microsoft/dowhy
EconML 代码: microsoft/econml
EconML 笔记本: microsoft/EconML
关于因果推断及其与机器学习联系的视频讲座#
微软研究院网络研讨会:https://note.microsoft.com/MSR-Webinar-DoWhy-Library-Registration-On-Demand.html
详细的KDD因果推断教程#
关于因果关系和机器学习的书籍章节#
微软的因果性与机器学习小组#
https://www.microsoft.com/en-us/research/group/causal-inference/

