原文:
towardsdatascience.com/easy-methods-for-causal-inference-bb5d3da4f8ca
图片由 Mika Baumeister 在 Unsplash 提供
想象一下,你已经构建了一个出色的机器学习模型,可以非常准确地预测你的目标值。在某些情况下,你的工作可能就在这一点结束了。然而,通常业务不仅想知道会发生什么,还想知道如何影响结果。正如这句格言所说:
知道未来是银,能够改变未来是金。
这个简单的事实不言而喻,你知道它来自你的个人生活。知道下周的彩票号码是好事,但只有当你能相应地调整你的号码时。
作为商业案例,考虑客户流失问题,即停止与你进行业务往来的客户。知道客户想要离开你是好事,但真正的问题是:如何防止这位客户流失?
业务希望有一种干预方式,例如通过发放优惠券或授予这位客户某种会员升级,以降低客户流失的概率。
如果 x = “给客户发放优惠券” 和 y = 流失概率,我们希望能够做出因果陈述:如果我做 x,y 会发生什么?人们也喜欢称之为假设情景。x 被称为处理变量。
图像由作者提供。
这比做出相关性陈述要困难得多。观察到冰淇淋销量与鲨鱼攻击相关很容易。但一个导致另一个吗?可能不是。而是好天气驱使人们购买冰淇淋并在海边游泳,使他们暴露在鲨鱼面前。因此,关闭所有冰淇淋店作为减少鲨鱼攻击的方法很可能会不起作用。
在这篇文章中,我将向你展示如何得出正确的结论。我将向你展示的方法在算法层面上极其简单:只需训练模型并对它们的预测进行轻微的后处理。然而,在因果推断中,你必须小心在训练过程中包含哪些特征。不是越多越好。这就是为什么我会首先向你展示一个简单的工具来检查你应该包含哪些特征,然后才是实际的方法。
小心这些特征
让我们从我的另一篇文章中举一个例子。你可以在这里阅读,但我也会简要回顾一下:
假设我们有一个关于公司员工的数据库。我们知道他们的责任感、他们工作的加班、他们拥有的收入以及他们的幸福感。
作者图片。
你想回答以下问题:
加班如何影响收入?
在这里,加班是我们的处理方法,而收入是结果。如果你之前从未听说过因果推断,解决方案似乎非常明显:
-
训练一个包含所有特征责任感、加班和幸福感的模型来预测收入,然后
-
为加班输入许多数字,看看收入如何变化。
如果模型有很好的预测能力,这应该会奏效,对吧?
很遗憾,不是。
这并不普遍适用,正如我在上面的另一篇文章中展示的那样。原因是典型的机器学习模型只学习相关性,而不是因果关系。如果你不小心,模型会学会降低冰淇淋销量以减少鲨鱼袭击的数量。
然而,通过仔细选择你的特征,上述方法仍然有效!你的特征集必须是所谓的足够调整集。让我们看看这意味着什么。
足够调整集
足够调整集的概念相当理论化,我不会在这篇文章中详细介绍,因为我可以写一个关于它的整个系列文章。我只是想指向www.dagitty.net/dags.html,你可以检查自己的特征是否形成一个足够调整集。
首先,你必须指定一个因果图,这是到目前为止进行因果推断最难的部分。这是一个告诉你哪些特征可能影响其他特征的图。对于上述数据,让我们假设以下因果关系:
作者图片。来源:www.dagitty.net/
此图编码了以下假设:
-
责任感影响某人工作多少加班以及收入。(如果我责任感强,我可能会工作更长,也可能更努力或更勤奋,这可能会导致更高的收入。)
-
加班影响幸福感和收入。(过多的加班让人不快乐,但会带来更多的钱。)
-
收入也影响幸福感。(更多的钱,更多的幸福!)
还有更多隐藏的假设,你可以通过箭头的缺失来识别。例如,图编码了收入不会影响加班,或者幸福感不会影响责任感。我们还假设没有其他因素影响收入或其他变量,这是一个相当强的假设。
你可以争论这些假设是否有意义。它们通常不可验证,这使得因果推理如此困难。但为了我们的目的,让我们采用这个图,因为它足够合理。
好的,所以我们已经决定了数据集的因果结构。再次强调,这是难点,你必须在这里小心。现在找到足够的调整集——即使得我们的简单方法训练模型并插入不同的处理变量值——是一个纯粹的图论任务。Dagitty 现在可以帮我们完成这个任务。
Dagitty
你可以轻松地从上面的图片中拼凑出图。然后,在左上角,你可以标记
-
超时作为暴露(处理),即你想要玩弄以查看它如何改变结果的东西,
-
收入作为结果。
网站会告诉你,责任感是一个位于右上角的足够的调整集。
作者图片。
你现在可以点击责任感并将其标记为调整过,同样的框会告诉你你已经正确调整。如果你将幸福感也设置为调整过,它会告诉你调整不正确。
这对你意味着什么
你应该只使用特征来训练模型
-
超时和
-
责任感。
你应该不包括幸福感,你也不应该省略责任感,****如果你想就超时如何影响收入做出因果陈述。
再次强调,在因果关系中,更多的特征并不总是更好的。
在本文的剩余部分,让我们做出以下简单假设:我们数据集中的所有特征形成一个足够的调整集。
元学习器
现在,最后一部分比预期的要长,对此表示歉意。然而,这是必要的,因为否则你可能会使用看似简单的方法得出错误的结论。但有了我们的足够调整集假设——在采取任何因果行动之前你应该始终考虑的——一切都会顺利。现在让我们来谈谈如何找到因果效应。
对于数据科学家来说,元学习是最容易计算因果效应的方法,请记住这一点。基本上,你可以拿任何你熟悉和喜爱的机器学习模型,并将其插入另一个算法——元学习器——该算法使用此模型来输出因果效应。
我即将向你展示的元算法很容易实现,但你也可以使用像EconML或CausalML这样的库,用更少的代码实现相同的功能。但没有什么比了解底层发生的事情更好的了,对吧?
二元处理
到目前为止,我没有具体说明处理(超时、优惠券等)是什么类型的变量。在以下内容中,我将假设处理是二元的,因为大多数元学习器只处理这些。
注意,这通常没有问题,因为你总是可以离散化连续特征,比如加班,将其表示为“每周加班少于/多于 3 小时”。像“客户获得优惠券”这样的处理已经是二进制的。
在这种情况下,我们可以看到我们的干预/处理的提升是什么:如果我们采取某种行动会发生什么,如果不采取行动会发生什么。
S-学习者
记得我提出的朴素方法,即只训练一个模型,并为你处理变量插入不同的值?这就是所谓的S-学习者,其中 S 代表单个。在二进制处理T的情况下,我们为T插入 1 和 0,然后减去结果。就是这样。
首先,训练:
作者提供的图片
T只是我们的特征之一,但是我们想要切换以观察它如何影响结果的特殊特征。训练后,我们像这样使用模型:
作者提供的图片
然后,对于每一行,我们得到一个提升的估计,即从 0 到 1 设置处理的结果。
T-学习者
这也很简单。当使用 T-学习者——T 代表两个——你训练两个模型。一个在T = 1 的数据集部分,另一个在T = 0 的数据集上。
作者提供的图片
然后,以自然的方式使用它:
作者提供的图片
如果你有了这些处理效应,你可以计算你想要的任何其他统计量,例如:
-
所有观察的平均处理效应,
-
观察子组的条件处理效应。
实现
让我们实现这两个学习者,这样你就能看到有多简单。首先,让我们加载数据。
!pip install polars
import polars as pl
data = pl.read_csv("https://raw.githubusercontent.com/Garve/datasets/main/causal_data.csv")
数据看起来是这样的:
作者提供的图片
S-学习者
现在,训练一个包含处理变量的单个模型。
from sklearn.ensemble import HistGradientBoostingRegressor
model = HistGradientBoostingRegressor()
X = data.select(pl.all().exclude("y"))
y = data.select("y").to_numpy().ravel()
model.fit(X.to_numpy(), y)
现在,插入训练数据——或者任何其他具有相同格式的数据集——一次替换t为所有 1,所有 0。
X_all_treatment = X.with_columns(t=1).to_numpy() # set treatment to one
X_all_control = X.with_columns(t=0).to_numpy() # set treatment to zero
treatment_effects = model.predict(X_all_treatment) - model.predict(X_all_control)
print(treatment_effects)
# Output:
# [ 0.02497236 3.95121282 4.15999904 ... 3.89655012 0.04411704
# -0.06875453]
你可以看到,对于某些行,处理增加了输出 4,但对于某些行,则没有。通过简单的
print(treatment_effects.mean())
你会发现平均处理效应大约是 2。所以,如果你要处理你的数据集中的每一个个体,与没有人接受任何处理相比,你的结果平均会增加大约 2。
T-学习者
这里,我们将训练两个模型。
model_0 = HistGradientBoostingRegressor()
model_1 = HistGradientBoostingRegressor()
data_0 = data.filter(pl.col("t") == 0)
data_1 = data.filter(pl.col("t") == 1)
X_0 = data_0.select(["x1", "x2", "x3"]).to_numpy()
y_0 = data_0.select("y").to_numpy().ravel()
X_1 = data_1.select(["x1", "x2", "x3"]).to_numpy()
y_1 = data_1.select("y").to_numpy().ravel()
model_0.fit(X_0, y_0)
model_1.fit(X_1, y_1)
data_without_treatment = data.select(["x1", "x2", "x3"]).to_numpy()
treatment_effects = model_1.predict(data_without_treatment) - model_0.predict(data_without_treatment)
结果与 S-学习者的输出相似。
结论
在这篇文章中,我们了解到估计因果效应并不像人们想象的那样简单。S-学习者的方法论非常自然且易于实现,但它只有在训练特征形成一个足够的调整集时,才能产生有效的因果洞察。对于 T-学习者来说,这也是得出因果结论的另一种选择。
这两种方法都有其优势和劣势。在进行 S 学习时,模型可能会选择忽略处理特征,例如。在这种情况下,预测的治疗效果都将为零,即使实际情况并非如此。T 学习器的问题之一是两个数据集中可能有一个真的非常小。如果只有 10 个观察值,且处理值为 1,你可能不太能信任这个模型。
还有其他方法,例如 Künzet 等人提出的X 学习器来解决这些问题,我们可能在未来的文章中讨论它们。
我希望你在今天学到了一些新的、有趣的、有价值的东西。感谢阅读!
如果您有任何问题,请通过LinkedIn联系我!
如果你想更深入地了解算法的世界,可以尝试我的新书《All About Algorithms》!我仍在寻找作者!
98

被折叠的 条评论
为什么被折叠?



