# 教程:使用 DoWhy+EconML进行因果推理
关注他
16 人赞同了该文章
本教程介绍如何使用DoWhy+EconML库进行因果推理。在这个过程中,这个教程将强调因果推理与机器学习的联系,也即:如何通过机器学习来建立因果效应的estimators,以及如何使用因果推理来建立更robust的机器学习模型。在数据科学中与因果推理相关的例子:
- A/B实验:如果改变算法,会导致更到的成功率吗?
- 政策决策:如果采用这种治疗/政策,是否会带来更健康的患者/更多的收入?
- 政策评估:现在的政策是有帮助还是有害?
- 归因:人们购买是因为推荐算法吗?还是其它的原因?
在本教程中,可以学到如下内容:
- 了解因果推理对决策的必要性,以及预测(prediction)和决策(decision-making)任务之间的区别;
- 通过因果推理的四个步骤来对因果效应进行评估:模型(model)、识别(identify)、估计(estimate)和反驳(refute);
- 通过DoWhy+EconML仅使用4行代码就可以估计因果效应,并且使用最新的统计和机器学习方法来估计因果效应,并评估其建模假设的鲁棒性;
- 通过Jupyter对真实案例进行研究,在不同的场景中应用因果推理,包括:估计客户忠诚度对未来交易的影响,预测哪些用户将受到干预(例如广告)的积极影响,对产品进行定价,以及估计哪些因子对outcome贡献最大;
- 了解因果推理与现代机器学习面临的挑战之间的关系;
Why causal inference?
许多关键的数据科学任务都与决策( decision-making)有关。数据科学家经常被要求支持各级决策者(decision-makers),期望充分利用数据来支持实现预期的结果。决策者包括:决定投资和资源配置的执行官、决定折扣政策的营销人员、确定要提供哪些功能的产品团队或决定对患者实施哪种治疗的医生。
每个决策者都在问一个假设(what-if)问题。要基于数据给出这些问题的答案,需要了解事件的causes以及如何采取行动来改善未来的结果。
Defining a causal effect
假设我们想知道采取行动A对结果Y的causal effect。要定义因果效果,需要考虑两个世界:世界1(真实世界):采取行动A并观察Y的地方。世界2(反事实世界):没有采取行动A(但其他一切都一样)。因果效应是在现实世界中获得的Y值与反现实世界中获得的Y值之间的差异。
真实世界与反事实世界
换言之,保持其他一切不变的情况下,因为A变化导致的Y的变化。在保持其他事物不变的同时改变A被称为干预,用一个特殊的符号表示: ( )。从形式上讲,因果效应是指Y因A的变化而变化的幅度:
为了进行估计,黄金标准是进行一个随机实验,其中一个随机集合被作用 =1,而另一个子集没有作用也即 =0。这些子集近似于不相交的真实世界和反现实世界,随机化确保了这两个子集之间没有系统性的差异(也即“保持其他一切不变”)。
然而,并不是在所有场景都可以进行随机实验,我们通常需要依靠已经观测或记录的数据来回答因果的问题。这些观察到的数据由于相关性和未观察到的混杂(confounding)而有偏差,因此在A=1和A=0的两个集合中可能存在系统性差异。例如,新的营销活动可能在假期期间展开,新功能可能只应用于高活动用户,或者老年患者可能更容易接受新药,等等。因果推理方法的目的是从数据中消除这种相关性和混杂,并估计一个*干预*的真实效果。
The difference between prediction and causal inference
Two fundamental challenges for causal inference
- 观察不到反事实的世界
- 不能直接计算因果关系
- 必须估计反事实
- 验证存在挑战
- 多个因果机制可以适用于单个数据分布
- 仅数据不足以进行因果推理
- 需要领域知识和假设
The four steps of causal inference
由于没有可用的具有ground-truth的数据与估计值进行比较,因果推理需要一系列步骤来保证一个好的估计值。我们通过一个示例数据集来说明这四个步骤。本教程需要下载两个库:DoWhy和EconML。两者都可以通过以下命令安装:
pip install dowhy econml
step0. 加载数据
# 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)
step1. Modeling
第一步是将我们的领域知识编码成一个因果模型,通常用graph来表示。因果推理分析的结果很大程度上依赖输入假设,因此这一步非常重要。为了估计因果效应,大部分常见的问题涉及指定两类变量:
- 混杂变量(Confounders):这些是干预和outcome的共同的cause。因此,任何观察到的干预和outcome之间的相关性可能只是由于混杂变量,而不是由于干预和outcome之间的因果关系;
- 工具变量(Instrumental Variables):这些是导致干预的特殊变量,但并不直接影响outcome。此外,它们不受任何影响outcome的变量的影响。如果使用正确的方式,工具变量可以帮助减少偏差。
# 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"],
intrumental_variables=data["instrument_names"])
# visualize the graph
model.view_model(layout="dot")
from IPython.display import Image, display
display(Image(filename="causal_model.png"))
通常,可以指定一个因果图,该图描述给定数据集的数据生成过程。图中的每个箭头都表示因果关系:“A->B”表示变量A导致变量B。
# 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")
step2. Identification
提供领域知识的两种方式(通过混淆因子和工具变量的命名变量集,或通过因果图)都对应于一个潜在的因果图。给定一个因果图和一个目标量(如A对B的影响),identification的过程是检查给定观测变量是否可以估计目标量。重要的是,identification只考虑观测数据中可用的变量;它不需要数据本身。与上述两类变量相关,因果推理的identification方法主要有两种:
- 后门标准(更一般地说,调整集):如果干预A和结果Y的所有共同原因都可以观测到,那么后门标准意味着可以通过对所有共同原因给定的情况下来识别因果效应。这是一个简化的定义(关于正式定义,请参阅CausalML一书的第3章), 其中 代表A和Y的confounders。
- Instrumental variable (IV) identification:如果有一个工具变量可用,那么即使有共同的原因是不可观察的,那么我们可以估计影响。 IV identification的原理是工具变量只直接影响干预变量,因此工具变量对outcome的影响可以分为两个连续的部分:工具变量对干预的影响和干预对outcome的影响。然后估计工具变量对干预变量的影响效应,以及干预变量对outcome的影响效应。对于只有两个取值的工具变量,估计的效应如下:
# II. Identify causal effect and return target estimands
identified_estimand = model.identify_effect(proceed_when_unidentifiable=True)
print(identified_estimand)
step3. Estimation
顾名思义,在估计步骤来构建一个estimator,该estimator可以计算在上一步中确定的estimand identified。许多估计estimators已经被提出用于因果推理。DoWhy实现了一些标准的estimators,而EconML实现了一组使用机器学习方法的估计器。
我们展示了一个使用DoWhy的倾向评分分层(Propensity Score Stratification)的例子,以及使用EconML实现的基于机器学习的方法Double-ML。
# 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)
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)
step4. Refutation
最后,检查估计的鲁棒性是因果分析中最重要的一步。我们使用步骤1-3获得了一个估计值,但每个步骤可能都做出了某些假设,但这些假设可能是错的。如果没有一个合适的验证集,这一步可以依赖反驳测试(refutation tests),该测试使用一个estimator的属性来反驳所获得的估计的正确性。例如,反驳测试(placebo_treatment_refuter)检查当干预变量被随机变量替换时,estimator返回估计值0是否与所有其他变量独立。
# 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)
## The DoWhy+EconML solution
我们将使用DoWhy+EconML库进行因果推理。DoWhy为这四个步骤提供了通用API,EconML为估算步骤提供了高级Estimator。
DoWhy可以做可视化、形式化和测试所做的假设,这样可以更好地理解分析结果并避免得出错误的结论。主要通过关注假设并尽可能引入对假设有效性的自动检查来实现。DoWhy的强大之处在于它提供了一个formal的因果框架来编码领域知识,并且它可以运行自动化检查任何估计器方法的因果估计的鲁棒性。
此外,随着数据的维度变高,我们需要能够处理已知混淆的专门方法。EconML实现了许多最先进的因果估计方法。这个包有一个用于所有技术的通用API,每种技术都作为一系列机器学习任务来实现,允许使用任何现有的机器学习软件来解决这些子任务,从而允许您使用熟悉的ML模型,而不是学习新的工具箱。EconML的强大之处在于,你可以实现因果推理的最新技术,就像运行线性回归或随机林一样简单。
DoWhy+EconML提供了一个最先进的端到端因果推理框架,包括最新的因果估计和自动鲁棒性评估,使回答假设问题变得更加容易。
A mystery dataset: Can you find out if if there is a causal effect?
为了完成这四个步骤,让我们考虑一下Mystery Dataset问题。假设你得到了一些关于治疗和结果的数据。你能确定是治疗导致了结果,还是相关性纯粹是由于另一个共同的原因?
下面我们创建一个数据集,其中真正的因果关系是由随机变量决定的,它的取值是0或1。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
import dowhy.datasets, dowhy.plotter
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"]])
step1. Model assumptions about the data-generating process using a causal graph
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")
step2. Identify the correct estimand for the target quantity based on the causal model
identified_estimand = model.identify_effect(proceed_when_unidentifiable=True)
print(identified_estimand)
由于这是观察到的数据,因此需要警惕此数据集中是否缺少未观察到的混淆因素。如果有,那么忽略这些变量将导致错误的估计。如果要禁用警告,可以在identify_effect函数中添加参数proceed_when_unidentifiable=True 。
step3. Estimate the target estimand
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"]])
如上所见,对于非线性数据生成过程,线性回归模型无法区分因果关系和观察到的相关性。但是,如果DGP是线性的,那么简单的线性回归就可以了。要了解这一点,请尝试在上面的单元格10中设置为is_linear=True。
为了对非线性数据(以及具有高维混杂因子的数据)建模,我们需要更先进的方法。下面是一个使用EconML的机器学习estimator的例子。该estimator使用基于机器学习的方法,如gradient boosting trees来学习结果和混杂因子之间的关系,以及干预和混杂因素之间的关系,最后比较结果和干预之间的残差。
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)
### step4. Check robustness of the estimate using refutation tests
res_random=model.refute_estimate(identified_estimand, dml_estimate, method_name="random_common_cause")
print(res_random)
res_placebo=model.refute_estimate(identified_estimand, dml_estimate,
method_name="placebo_treatment_refuter", placebo_type="permute",
num_simulations=20)
print(res_placebo)
## Connections to fundamental machine learning challenges
因果关系与机器学习模型的许多基本挑战有关,包括分布外泛化、公平性、可解释性和隐私性。使用因果关系来帮助解决上述的挑战是一个活跃的研究领域。
## Case-studies using DoWhy+EconML