通过使用简洁、高度预测性的特征,基于数值特征的算术组合自动生成,来创建更具可解释性的模型
https://medium.com/@wkennedy934?source=post_page---byline--e18aab45e96d--------------------------------https://towardsdatascience.com/?source=post_page---byline--e18aab45e96d-------------------------------- W Brett Kennedy
·发表于Towards Data Science ·32 分钟阅读·2024 年 10 月 6 日
–
在本文中,我们将探讨一个名为FormulaFeatures的工具。该工具主要用于可解释模型,如浅层决策树,在这些模型中,拥有少量简洁且高度预测性的特征可以大大提高模型的可解释性和准确性。
可解释模型在机器学习中的应用
本文是我关于可解释机器学习系列文章的一部分,之前的文章涉及了ikNN、加法决策树、遗传决策树以及PRISM 规则。
正如之前的文章所指出的(并且在其中做了更详细的阐述),使用可解释的预测模型往往具有强烈的驱动力:每个预测结果都能被很好地理解,并且我们可以确信该模型在未来未见数据上的表现是合理的。
提供可解释机器学习的模型种类有很多,尽管遗憾的是,这些模型的数量远少于我们期望的。除了上述文章中描述的模型,还有一些其他模型,例如决策树、决策表、规则集和规则列表(例如由imodels创建的)、最优稀疏决策树、广义加性模型(GAMs,例如可解释增强机),以及其他一些选项。
一般而言,创建既准确又可解释的预测机器学习模型是具有挑战性的。为了改善可解释机器学习的选项,四个主要的方法是:
-
开发更多的模型类型
-
提高现有模型类型的准确性或可解释性。这里指的是对现有模型类型或创建模型所使用的算法进行变体开发,而不是完全新颖的模型。例如,最优稀疏决策树和遗传决策树旨在创建更强的决策树,但最终仍然是决策树。
-
提供模型的数据、模型本身及其预测结果的可视化。这是例如ikNN采用的方法,该方法通过创建 2D kNN 模型的集成(即每个 kNN 模型仅使用一对特征)来工作。可以将这些 2D 空间进行可视化,从而高度透明地展示模型是如何工作的,以及为什么做出每个预测。
-
改善模型所使用特征的质量,以使模型更加准确或更加可解释。
FormulaFeatures 用于支持上述的最后一种方法。它是我自己开发的,旨在解决决策树中的一个常见问题:它们往往可以实现较高的准确度,但通常需要生长到较大的深度,这会使得模型缺乏可解释性。通过创建新的特征来捕捉连接原始特征与目标之间的函数的一部分,可以使决策树更加紧凑(因此也更具可解释性)。
基本思想是:对于任何带标签的数据集,都存在某个真实函数 f(x),将记录映射到目标列。这个函数可以有任意形式,可能简单也可能复杂,并且可以使用 x 中的任何特征集。但无论 f(x)的性质如何,通过创建模型,我们希望尽可能地基于现有数据逼近 f(x)。为了创建一个可解释的模型,我们还需要以清晰简洁的方式做到这一点。
如果特征本身能够捕捉到函数的显著部分,这将非常有帮助。例如,我们可能有一个预测客户流失的模型,并且我们为每个客户提供了包括以下内容的特征:他们在过去一年中的购买次数和购买的平均金额。然而,真实的 f(x)可能主要基于这些特征的乘积(即通过这两个特征的乘积计算出的过去一年购买的总金额)。
在实践中,我们通常永远无法知道真实的 f(x),但在这种情况下,我们假设客户是否在明年流失与他们前一年总购买额有很大的关系,而与他们的购买次数或平均购买金额的关系较小。
我们可能仅使用这两个原始特征就能建立一个准确的模型,但如果使用仅包含乘积特征的模型,模型会更加清晰且可解释。并且可能更加准确。
使用决策树的示例
如果我们只有两个特征,那么我们可以在二维图中查看它们。在这种情况下,我们可以仅查看 num_purc 和 avg_purc:每个客户过去一年内的购买次数及其平均购买金额。假设真实的 f(x)主要基于它们的产品,这个空间可能看起来像下图所示,浅蓝色区域表示将在明年流失的客户,深蓝色区域表示不会流失的客户。
如果使用决策树来建模,我们可以通过递归地划分数据空间来创建模型。图中的橙色线条显示了一组决策树可能使用的切分(对于第一组节点)来预测流失。如图所示,决策树可能首先在 num_purc 的 250 处进行切分,然后在 avg_purc 的 24 处进行切分,依此类推。接下来,它会继续进行切分,以拟合真实函数的曲线形状。切分次数越多,它能够拟合真实函数的程度就越接近。
这样做会创建一个决策树,类似于下图所示,其中圆圈代表内部节点,矩形代表叶子节点,椭圆形则代表子树,这些子树可能需要再生长几个层次才能达到较好的准确性。也就是说,这里仅展示了一个完整决策树的一部分,而这个完整的决策树需要通过这两个特征来建模。我们也可以在上面的图中看到:使用轴平行切分时,我们需要大量的切分才能较好地拟合两个类别之间的边界。
如果树已经生长得足够深,我们很可能能获得一个在准确性上非常强的树。但这个树将远非可解释。
可以像上面的图表那样查看决策空间(这确实使得模型的行为变得清晰),但只有在这里空间被限制为二维时才可行。通常情况下,这是不可能的,我们解读决策树的最佳方式是检查树本身。然而,当树包含几十个节点甚至更多时,就很难看到它试图捕捉的模式。
在这种情况下,如果我们为num_purc * avg_purc工程化一个特征,我们可能会得到一个非常简单的决策树,只有一个内部节点,分裂点为:num_purc * avg_purc > 25000。
在实践中,永远无法生成如此接近真实函数的特征,也永远无法创建节点非常少的完全准确的决策树。但通常可以通过特征工程创建比原始特征更接近真实f(x)的特征。
每当特征之间存在交互作用时,如果我们能够通过工程化特征捕捉到这些交互作用,这将有助于构建更紧凑的模型。
因此,使用 FormulaFeatures,我们试图创建像num_purchases * avg_value_of_purchases这样的特征,并且它们通常可以用于决策树等模型,以合理地捕获真实的函数。
同样,简单地知道num_purchases * avg_value_of_purchases是预测目标的关键(并且较高的值与较低的流失风险相关)本身就是一种有用的信息。但新的特征在寻求使可解释模型更准确、更具可解释性时最为有用。
正如我们将在下面描述的那样,FormulaFeatures 也以一种最小化创建其他特征的方式做到这一点,因此只返回一小组相关的特征。
可解释的机器学习与决策树
对于表格数据,预测问题的最佳模型通常是基于树的提升集成模型,特别是 LGBM、XGBoost 和 CatBoost。虽然这会因不同的预测问题而有所不同,但大多数情况下,这三种模型往往比其他模型表现更好(并且至少在 AutoML 方法之外,被认为是当前的技术前沿)。其他强大的模型类型,如 kNN、神经网络、贝叶斯加性回归树、SVM 等,也会偶尔表现最佳。然而,所有这些模型类型都非常难以解释,实际上是黑箱模型。
不幸的是,可解释的模型在准确性方面通常较弱。有时,准确度的下降非常小(例如,在第三位小数),在这种情况下,为了可解释性而牺牲一些准确性是值得的。然而,在其他情况下,可解释的模型可能比黑箱模型表现得更差。例如,对于一个单一的决策树来说,难以与多个决策树的集成模型竞争。
因此,创建一个强大的黑箱模型是很常见的,但同时要创建一个强大的可解释模型却可能具有挑战性(甚至不可能)。这就是 FormulaFeatures 旨在解决的问题。它试图捕捉黑箱模型能够表示的某些逻辑,但以一种简单且易于理解的方式。
可解释人工智能的许多研究集中在决策树上,并且与提高决策树的准确性和可解释性相关。这是很自然的,因为决策树是一种本质上容易理解的模型类型(当足够小的时候,决策树可以说与任何其他模型一样具有可解释性),并且通常相当准确(尽管这往往不是事实)。
其他可解释模型类型(如逻辑回归、规则、广义加性模型等)也有应用,但大部分研究集中在决策树上,因此本文大部分内容都涉及决策树。然而,FormulaFeatures 并非专门针对决策树,它同样可以用于其他可解释模型。事实上,一旦我们在下面解释了 FormulaFeatures,您会很容易理解它如何也可以应用于 ikNN、遗传决策树、加性决策树、规则列表、规则集合等。
更准确地说,在决策树的应用中,当我们使用决策树进行可解释性机器学习时,我们专门关注浅层决策树——这些树的深度相对较小,最深的节点可能限制在 3、4 或 5 层。这确保了两件事:首先,浅层决策树既可以提供所谓的局部解释,也可以提供所谓的全局解释。这两者是可解释性机器学习中的两个主要关注点。我将在这里解释这两者。
对于局部可解释性,我们希望确保模型做出的每个单独预测都是可以理解的。在这里,我们可以检查每个记录通过决策树所走的路径,以生成相应的决策。如果一条路径包含特征 num_purc * avg_purc,并且路径非常短,那么可以合理地理解它。另一方面,如果路径包含:num_purc > 250 且 avg_purc > 24 且 num_purc < 500 且 avg_purc_50,等等(就像上面生成的树,但没有 num_purc * avg_purc 特征的帮助)则可能变得非常难以解释。
对于全局可解释性,我们希望确保整个模型是可以理解的。这使我们能够看到在任何情况下都会做出的预测。同样,使用更紧凑的树结构,并且特征本身具有信息性,可以帮助实现这一点。在这种情况下,看到决策树如何输出预测的全貌要简单得多。
然而,我们应该对这一点做出限定,指出浅层决策树在回归问题中很难以准确的方式创建。每个叶节点只能预测一个单一值,因此一个具有 n 个叶节点的树最多只能输出 n 个不同的预测值。对于回归问题,这通常会导致较高的误差率:通常决策树需要创建大量叶节点,以涵盖所有可能预测的值范围,并且每个节点的精度要合理。
因此,浅层决策树通常仅适用于分类问题(如果可以预测的类别数量较少,完全有可能创建一个不包含太多叶节点的决策树,准确地预测这些类别)。FormulaFeatures 可以与其他可解释的回归模型一起使用,但通常不适用于决策树。
监督式和非监督式特征工程
现在我们已经了解了 FormulaFeatures 背后的部分动机,我们来看看它是如何工作的。
FormulaFeatures 是一种监督式特征工程方法,也就是说,它在生成特征时会考虑目标列,从而可以生成专门用于预测该目标的特征。FormulaFeatures 支持回归和分类目标(尽管如前所述,在使用决策树时,可能只有分类目标是可行的)。
利用目标列可以仅生成少量的工程特征,每个特征的复杂度可以根据需要进行调整。
另一方面,非监督方法并不考虑目标特征,而是使用某种生成特征的系统生成原始特征的所有可能组合。
一个例子是 scikit-learn 的 PolynomialFeatures,它将生成特征的所有多项式组合。如果原始特征是,例如:[a, b, c],那么 PolynomialFeatures 可以创建(根据指定的参数)一组工程特征,如:[ab, ac, bc, a², b², c²] ——也就是说,它将生成所有特征对的组合(使用乘法),以及所有原始特征的平方。
使用非监督方法时,通常会出现特征数量爆炸的情况。如果我们一开始有 20 个特征,仅返回通过每对特征相乘生成的特征就会产生 (20 * 19) / 2,或者说 190 个特征(即 20 选 2)。如果允许基于三特征集相乘生成特征,则有 20 选 3,或 1140 个特征。允许生成如 a²bc、a²bc² 等特征会导致更多的特征数量膨胀(尽管在这些特征中,可能会有一些是有用的)。
有监督的特征工程方法往往只返回这些特征的一个更小且更相关的子集。
然而,即使在有监督的特征工程的背景下(取决于使用的具体方法),特征的爆炸性增加仍然可能在一定程度上发生,从而导致特征工程过程耗时,并产生比任何下游任务(如预测、聚类或异常检测)能够合理使用的更多特征。FormulaFeatures 已优化以保持工程时间和返回特征的数量在可控范围内,其算法旨在限制生成特征的数量。
算法
该工具在数据集的数值特征上进行操作。在第一次迭代中,它检查每一对原始数值特征。对于每一对,它会基于四种基本算术运算(+、-、*和/)考虑四个潜在的新特征。出于性能和可解释性的考虑,我们将过程限制为这四种运算。
如果某些特征在预测目标的能力上表现优于两个父特征(稍后将描述),那么这些特征中最强的一个会被添加到特征集合中。例如,如果 A + B 和 A * B 都是强特征(都比 A 或 B 更强),则只会包括其中更强的一个。
随后的迭代将考虑将前一轮生成的所有特征与其他所有特征组合,再次选出最强的特征(如果有的话,超过它们的两个父特征)。通过这种方式,生成了一些实际可用的新特征,它们都比之前的特征强。
算法示例演示
假设我们从一个包含特征 A、B 和 C 的数据集开始,Y 是目标,且 Y 是数值型(这是一个回归问题)。
我们首先通过确定每个特征在其自身对目标的预测能力来开始。当前可用的版本对回归问题使用 R2,对分类问题使用 F1(宏观)。我们使用单一特征创建一个简单模型(分类或回归决策树),确定它预测目标列的效果,并通过 R2 或 F1 分数来衡量。
使用决策树使我们能够较好地捕捉特征与目标之间的关系——即使是相当复杂、非单调的关系——如果这些关系存在的话。
未来版本将支持更多的度量标准。然而,严格使用 R2 和 F1 并不是一个显著的限制。虽然其他度量可能对你的项目更相关,但在进行特征工程时使用这些度量标准能够很好地识别出与目标强相关的特征,即使这些特征的关联强度可能与使用其他度量时的结果不完全相同。
在这个示例中,我们首先计算每个原始特征的 R2 值,使用仅特征 A 训练决策树,然后使用仅特征 B,再使用仅特征 C。可能得到以下 R2 分数:
A 0.43
B 0.02
C -1.23
然后我们考虑这些特征对的组合,它们是:A & B、A & C 和 B & C。对于每一对,我们尝试四种算术操作:+、*、-和/。
如果 f(x)中存在特征交互,通常相关的原始特征可以通过新特征的组合来很好地表示这些交互,从而表现得比任何父特征都要好。
当检查 A & B 时,假设我们得到以下 R2 分数:
A + B 0.54
A * B 0.44
A - B 0.21
A / B -0.01
在这里,有两个操作的 R2 分数高于任何父特征(A 或 B),它们是+和*。我们选择其中较高的 A + B,并将其加入特征集。同样的操作适用于 A & B 和 B & C。在大多数情况下,不会添加任何特征,但通常会添加一个。
在第一次迭代后,我们可能会得到:
A 0.43
B 0.02
C -1.23
A + B 0.54
B / C 0.32
然后,在下一次迭代中,我们将把刚刚添加的两个特征与所有其他特征进行组合,包括彼此之间的组合。
在这之后,我们可能得到:
A 0.43
B 0.02
C -1.23
A + B 0.54
B / C 0.32
(A + B) - C 0.56
(A + B) * (B / C) 0.66
这一过程会持续,直到不再有改进,或者达到超参数max_iterations所指定的限制。
基于相关性进一步修剪
每次迭代结束后,会根据特征之间的相关性进一步修剪特征。检查当前迭代中创建的特征之间的相关性,对于两个或多个高度相关的特征,保留最强的一个,删除其他特征。这可以避免创建近乎冗余的特征,尤其是当特征变得更加复杂时,这种情况尤为明显。
例如:(A + B + C) / E 和 (A + B + D) / E 可能都很强,但非常相似,如果是这样,只有其中更强的一个会被保留。
但对于相关特征,还是有一定的容许度。一般来说,随着算法的进行,会创建更多复杂的特征,这些特征更准确地捕捉 x 中的特征与目标之间的真实关系。然而,创建的新特征可能与其基础的较简单特征相关联,FormulaFeatures 也会倾向于优先选择较简单的特征,而不是复杂的特征,其他条件相同的情况下。
例如,如果(A + B + C)与(A + B)相关联,那么即使(A + B + C)更强,也会保留这两个特征,以便后续迭代中可以将较简单的(A + B)与其他特征组合,可能会创建出更强的特征。
FormulaFeatures 如何限制所创建的特征
在上面的示例中,我们有特征 A、B 和 C,并看到真实的 f(x)的一部分可以用(A + B) - C 来近似。
我们最初只有原始特征。在第一次迭代后,我们可能生成(如上例所示)A + B 和 B / C,因此现在有五个特征。
在下一次迭代中,我们可能生成(A + B) — C。
这一过程通常是以下两者的结合:1) 将弱特征结合起来,使其更强大(并且更可能在下游任务中有用);以及 2) 将强特征结合起来,使其更强大,创建最有可能是最具预测性的特征。
但是,重要的是,这种组合仅在确认 A + B 本身是一个具有预测性的特征时才会进行,而不是单独的 A 或 B。这意味着,在确认 A + B 具有预测性之前,我们不会创建(A + B) — C。这确保了,对于任何创建的复杂特征,特征中的每个组件都是有用的。
以这种方式,每次迭代都会创建比之前更强大的特征集,并且以一种可靠且稳定的方式进行。它最大限度地减少了简单地尝试许多复杂特征组合的效果,这种做法很容易导致过拟合。
因此,FormulaFeatures 以一种有原则、深思熟虑的方式执行,每一步只创建少量工程化特征,并且每次迭代通常生成较少的特征。因此,总体而言,它倾向于创建低复杂度的特征。对于生成的复杂特征,可以证明这些特征是合理的。
对于大多数数据集来说,最终生成的工程化特征通常是仅由两个或三个原始特征组合而成的。也就是说,它通常会生成更像 A * B 这样的特征,而不是像(A * B) / (C * D)这样的组合。
实际上,要生成像(A * B) / (C * D)这样的特征,需要先证明 A * B 比 A 或 B 更具预测性,C * D 比 C 或 D 更具预测性,并且(A * B) / (C * D)比(A * B)或(C * D)更具预测性。由于这有许多条件,相对来说,像(A * B) / (C * D)这样复杂的特征生成的机会较少,更多的特征会像 A * B。
使用 1D 决策树在内部评估特征。
我们将在这里更详细地讨论如何使用决策树来评估每个特征,包括原始特征和工程化特征。
为了评估特征,还可以使用其他方法,如简单的相关性测试。但创建简单的非参数模型,特别是决策树,具有一些优势:
-
1D 模型训练和测试都非常快速,这使得评估过程能够非常迅速地执行。我们可以快速确定哪些工程化特征能够预测目标,以及它们的预测效果如何。
-
1D 模型比较简单,因此可以合理地在小样本数据上训练,从而进一步提高效率。
-
虽然 1D 决策树模型相对简单,但它们能够捕捉特征与目标之间的非单调关系,因此可以检测出特征的预测性,即使这些关系复杂到简单的相关性测试可能会遗漏的程度。
-
这确保了所有特征在自身上都有用,因此支持这些特征本身就是一种可解释性。
使用一维模型评估每个特征也存在一些局限性,特别是:使用单一特征会排除识别有效的特征组合。这可能导致错过一些有用的特征(那些单独不有用但与其他特征结合时有用的特征),但可以使得处理过程执行得非常快速。它还确保所有生成的特征本身都是可预测的,这有助于提高可解释性。
目标是:当特征仅在与其他特征结合时才有用时,创建一个新的特征来捕捉这一点。
这种特征工程方式的另一个局限性是,几乎所有经过工程处理的特征都会具有全局意义,这通常是期望的,但这也意味着该工具可能会遗漏生成那些仅在特定子空间中有用的特征。然而,考虑到这些特征将被可解释的模型使用,比如浅层决策树,只有在特定子空间中有效的特征的价值远低于使用更复杂模型(如大型决策树)时的情况。
决策树复杂性的影响
FormulaFeatures 确实创建了比原始特征更复杂的特征,这确实降低了树的可解释性(假设这些工程化特征被树使用一次或多次)。
同时,使用这些特征可以允许更小的决策树,从而使得模型总体上更加准确且易于解释。也就是说,尽管树中使用的特征可能很复杂,但树本身可能会显著更小(或者在保持大小在合理范围内时,准确性大幅提高),从而在可解释性上实现净收益。
当 FormulaFeatures 与浅层决策树结合使用时,生成的工程特征往往会被放在树的顶部(因为这些特征最强大,最能最大化信息增益)。没有任何单一特征能在任何一步完美地划分数据,这意味着几乎总是需要进一步的分裂。其他特征则被用在树的更低层次,这些特征往往是更简单的工程特征(仅基于两个,或者有时三个,原始特征),或者是原始特征。总体而言,这可以生成相当易于解释的决策树,并且往往将更复杂的工程特征的使用限制在一个有用的水平。
ArithmeticFeatures
为了更好地解释 FormulaFeatures 的一些背景,我将介绍另一个工具,也是我自己开发的,叫做 ArithmeticFeatures,它类似但略为简单。接着我们将探讨 ArithmeticFeatures 的一些局限性,而 FormulaFeatures 则是为了克服这些问题而设计的。
ArithmeticFeatures 是一个简单的工具,但我在多个项目中发现它非常有用。我最初创建它,是因为在我所从事的各种项目中,生成一组简单的算术组合来处理可用的数值特征是一个经常出现的需求。之后,我将其托管在 github 上。
它的目的和特点类似于 scikit-learn 的 PolynomialFeatures。它也是一个无监督的特征工程工具。
给定一个数据集中的数值特征集,它会生成一组新的特征。对于每一对数值特征,它会生成四个新特征:加法、减法、乘法和除法操作的结果。
这可以生成一组有用的特征,但也会生成大量的特征,且可能包含冗余特征,这意味着在使用后需要进行特征选择。
Formula Features 旨在解决如上所述的问题,这个问题通常出现在包括 ArithmeticFeatures 在内的无监督特征工程工具中:即特征数量的爆炸性增长。由于没有目标来引导过程,它们只是以可能的方式将数值特征进行组合。
快速列出差异:
-
FormulaFeatures 会生成更少的特征,但每一个生成的特征都会被确认是有用的。而 ArithmeticFeatures 则没有检查哪些特征是有用的。它会生成所有原始特征和算术操作组合的特征。
-
FormulaFeatures 只会生成比其父特征更具预测性的特征。
-
对于任何给定的特征对,FormulaFeatures 最多会包含一个组合,这个组合是最能预测目标的组合。
-
FormulaFeatures 会继续循环,直到达到指定的迭代次数,或者只要它能创建更强大的特征,因此能够生成比 ArithmeticFeatures 更强的特征,后者仅限于基于原始特征对的特征。
ArithmeticFeatures 由于只执行一次迭代(以管理生成的特征数量),通常在它能够创建的特征上有很大限制。
假设数据集描述的是房屋,目标特征是房价。这可能与诸如 num_bedrooms、num_bathrooms 和 num_common rooms 等特征相关。很可能与房屋的总房间数强相关,假设它是:num_bedrooms + num_bathrooms + num_common rooms。然而,ArithmeticFeatures 只能基于原始特征对生成工程特征,因此只能生成:
-
num_bedrooms + num_bathrooms
-
num_bedrooms + num_common rooms
-
num_bathrooms + num_common rooms
这些可能是有信息量的,但生成 num_bedrooms + num_bathrooms + num_common rooms(如 FormulaFeatures 所能做到的)作为特征,不仅更清晰,而且比仅使用原始特征对的特征生成更简洁的树(和其他可解释的模型)。
另一个基于算术运算的流行特征工程工具是 AutoFeat,它与 ArithmeticFeatures 类似,也以无监督的方式执行,因此会生成非常大量的特征。AutoFeat 可以执行多个迭代,在每次迭代中生成更复杂的特征,但数量也会增加。此外,AutoFeat 支持一元操作,例如平方、平方根、对数等,这使得它可以生成如 A²/log(B) 这样的特征。
所以,我已经讲过了使用 FormulaFeatures 而不是无监督特征工程的动机,但也应该提到:像 PolynomialFeatures、ArithmeticFeatures 和 AutoFeat 这样的无监督方法通常也是有用的,特别是在任何情况下都会进行特征选择时。
FormulaFeatures 更注重可解释性(在某种程度上也考虑了内存效率,但主要动机是可解释性),因此它有不同的目的。
使用特征工程进行特征选择
使用像 PolynomialFeatures、ArithmeticFeatures 和 AutoFeat 这样的无监督特征工程工具增加了对特征选择的需求,但特征选择通常在任何情况下都会执行。
也就是说,即使使用像 FormulaFeatures 这样的监督式特征工程方法,通常在特征工程过程之后进行一些特征选择仍然是有用的。事实上,即使特征工程过程没有产生新的特征,特征选择仍然可能有用,单纯是为了减少模型中使用的原始特征数量。
虽然 FormulaFeatures 尽力减少创建的特征数量,但它本身并不执行特征选择,因此可能会生成比任何给定任务所需更多的特征。我们假设在大多数情况下,工程化的特征将用于预测任务,但相关特征仍然取决于使用的特定模型、超参数、评估指标等,而这些是 FormulaFeatures 无法预测的。
相关的是,与许多其他特征工程过程相比,使用 FormulaFeatures 时,特征选择工作(如果执行的话)可以变得更简单,因为需要考虑的特征会少得多。处理许多特征时,特征选择可能会变得缓慢且困难。例如,使用包装方法选择特征时会变得不可行。
API 签名
该工具采用了 fit-transform 模式,与 scikit-learn 的 PolynomialFeatures 以及许多其他特征工程工具(包括 ArithmeticFeatures)使用的模式相同。因此,可以轻松地将此工具替换为其他工具,以确定哪一个最适合任何给定的项目。
简单代码示例
在本示例中,我们加载了 iris 数据集(这是一个由 scikit-learn 提供的玩具数据集),将数据拆分为训练集和测试集,使用 FormulaFeatures 来工程化一组附加特征,并使用这些特征拟合决策树模型。
这是一个相当典型的示例。使用 FormulaFeatures 只需要创建一个 FormulaFeatures 对象,进行拟合并转换可用数据。这将生成一个新的数据框架,可用于后续任务,在本例中用于训练分类模型。
import pandas as pd
from sklearn.datasets import load_iris
from formula_features import FormulaFeatures
# Load the data
iris = load_iris()
x, y = iris.data, iris.target
x = pd.DataFrame(x, columns=iris.feature_names)
# Split the data into train and test
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=42)
# Engineer new features
ff = FormulaFeatures()
ff.fit(x_train, y_train)
x_train_extended = ff.transform(x_train)
x_test_extended = ff.transform(x_test)
# Train a decision tree and make predictions
dt = DecisionTreeClassifier(max_depth=4, random_state=0)
dt.fit(x_train_extended, y_train)
y_pred = dt.predict(x_test_extended)
将工具设置为 verbose=1 或 verbose=2 允许更详细地查看过程。
github 页面还提供了一个名为 demo.py 的文件,里面包含了一些使用 FormulaFeatures 的示例,尽管其签名非常简单。
获取特征得分示例
获取特征得分(我们在此示例中展示的)可能有助于理解生成的特征以及进行特征选择。
在本示例中,我们使用来自 openml 的 gas-drift 数据集(www.openml.org/search?type=data&sort=runs&id=1476&status=active,基于 Creative Commons 许可证)。
它与之前的示例大致相同,但还调用了 display_features() API,该 API 提供了有关工程化特征的信息。
data = fetch_openml('gas-drift')
x = pd.DataFrame(data.data, columns=data.feature_names)
y = data.target
# Drop all non-numeric columns. This is not necessary, but is done here
# for simplicity.
x = x.select_dtypes(include=np.number)
# Divide the data into train and test splits. For a more reliable measure
# of accuracy, cross validation may also be used. This is done here for
# simplicity.
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=0.33, random_state=42)
ff = FormulaFeatures(
max_iterations=2,
max_original_features=10,
target_type='classification',
verbose=1)
ff.fit(x_train, y_train)
x_train_extended = ff.transform(x_train)
x_test_extended = ff.transform(x_test)
display_df = x_test_extended.copy()
display_df['Y'] = y_test.values
print(display_df.head())
# Test using the extended features
extended_score = test_f1(x_train_extended, x_test_extended, y_train, y_test)
print(f"F1 (macro) score on extended features: {extended_score}")
# Get a summary of the features engineered and their scores based
# on 1D models
ff.display_features()
这将生成以下报告,列出每个特征索引、F1 宏观得分和特征名称:
0: 0.438, V9
1: 0.417, V65
2: 0.412, V67
3: 0.412, V68
4: 0.412, V69
5: 0.404, V70
6: 0.409, V73
7: 0.409, V75
8: 0.409, V76
9: 0.414, V78
10: 0.447, ('V65', 'divide', 'V9')
11: 0.465, ('V67', 'divide', 'V9')
12: 0.422, ('V67', 'subtract', 'V65')
13: 0.424, ('V68', 'multiply', 'V65')
14: 0.489, ('V70', 'divide', 'V9')
15: 0.477, ('V73', 'subtract', 'V65')
16: 0.456, ('V75', 'divide', 'V9')
17: 0.45, ('V75', 'divide', 'V67')
18: 0.487, ('V78', 'divide', 'V9')
19: 0.422, ('V78', 'divide', 'V65')
20: 0.512, (('V67', 'divide', 'V9'), 'multiply', ('V65', 'divide', 'V9'))
21: 0.449, (('V67', 'subtract', 'V65'), 'divide', 'V9')
22: 0.45, (('V68', 'multiply', 'V65'), 'subtract', 'V9')
23: 0.435, (('V68', 'multiply', 'V65'), 'multiply', ('V67', 'subtract', 'V65'))
24: 0.535, (('V73', 'subtract', 'V65'), 'multiply', 'V9')
25: 0.545, (('V73', 'subtract', 'V65'), 'multiply', 'V78')
26: 0.466, (('V75', 'divide', 'V9'), 'subtract', ('V67', 'divide', 'V9'))
27: 0.525, (('V75', 'divide', 'V67'), 'divide', ('V73', 'subtract', 'V65'))
28: 0.519, (('V78', 'divide', 'V9'), 'multiply', ('V65', 'divide', 'V9'))
29: 0.518, (('V78', 'divide', 'V9'), 'divide', ('V75', 'divide', 'V67'))
30: 0.495, (('V78', 'divide', 'V65'), 'subtract', ('V70', 'divide', 'V9'))
31: 0.463, (('V78', 'divide', 'V65'), 'add', ('V75', 'divide', 'V9'))
这包括了原始特征(特征 0 到 9)以提供上下文。在这个示例中,经过工程化的特征在预测能力上稳步提升。
也提供了绘图功能。在回归目标的情况下,工具会呈现一个散点图,将每个特征映射到目标。在分类目标的情况下,工具会呈现一个箱线图,展示按类别标签划分的特征分布。通常情况下,原始特征在每个类别中的分布差异不大,而工程化特征则能显示出明显的差异。例如,生成的一个特征 (V99 / V47) - (V81 / V5) 显示出强烈的分离:
分离度虽然不完美,但比任何原始特征都更干净。
这正是特征工程的典型表现;虽然每个特征的分离并不完美,但每个特征都很强大,通常比原始特征强得多。
测试结果
测试在合成数据和真实数据上进行。工具在合成数据上表现非常好,尽管这更多的是调试和测试,而非有意义的评估。对于真实数据,选择了来自 OpenML 的 80 个随机分类数据集,但只有包含至少两个数值特征的数据集才能被纳入,最终留下了 69 个文件。测试包括对数据进行一次单一的训练-测试分割,然后在数值特征上训练并评估模型,分别在增加特征之前和之后进行。
使用宏观 F1 作为评估指标,评估带和不带工程化特征的 scikit-learn DecisionTreeClassifer,并设置 max_leaf_nodes = 10(对应 10 个生成的规则)以确保模型具有可解释性。
在许多情况下,工具对浅层决策树的准确性没有提供改进,或者仅提供了轻微的改进,这是可以预期的。没有任何特征工程技术能在所有情况下都有效。更重要的是,工具在许多情况下显著提高了准确性,这令人印象深刻。这是在没有调优或特征选择的情况下完成的,调优和特征选择可以进一步提高工具的有效性。
使用其他可解释模型会得到不同的结果,可能比浅层决策树表现得更强或更弱,而浅层决策树确实表现出了相当强的结果。
在这些测试中,我们发现将 max_iterations 限制为 2,比设置为 3 时得到更好的结果。这是一个超参数,必须针对不同的数据集进行调优。对于大多数数据集,使用 2 或 3 都能获得不错的结果,而对于其他数据集,设置更高,甚至更高(将其设置为 None 允许过程继续,只要能够生成更有效的特征)也可能表现良好。
在大多数情况下,工程化新特征的时间仅为几秒钟,在所有情况下都没有超过两分钟,即使许多测试文件有数百列和几千行。
结果如下:
Dataset Score Score
Original Extended Improvement
isolet 0.248 0.256 0.0074
bioresponse 0.750 0.752 0.0013
micro-mass 0.750 0.775 0.0250
mfeat-karhunen 0.665 0.765 0.0991
abalone 0.127 0.122 -0.0059
cnae-9 0.718 0.746 0.0276
semeion 0.517 0.554 0.0368
vehicle 0.674 0.726 0.0526
satimage 0.754 0.699 -0.0546
analcatdata_authorship 0.906 0.896 -0.0103
breast-w 0.946 0.939 -0.0063
SpeedDating 0.601 0.608 0.0070
eucalyptus 0.525 0.560 0.0349
vowel 0.431 0.461 0.0296
wall-robot-navigation 0.975 0.975 0.0000
credit-approval 0.748 0.710 -0.0377
artificial-characters 0.289 0.322 0.0328
har 0.870 0.870 -0.0000
cmc 0.492 0.402 -0.0897
segment 0.917 0.934 0.0174
JapaneseVowels 0.573 0.686 0.1128
jm1 0.534 0.544 0.0103
gas-drift 0.741 0.833 0.0918
irish 0.659 0.610 -0.0486
profb 0.558 0.544 -0.0140
adult 0.588 0.588 0.0000
anneal 0.609 0.619 0.0104
credit-g 0.528 0.488 -0.0396
blood-transfusion-service-center 0.639 0.621 -0.0177
qsar-biodeg 0.778 0.804 0.0259
wdbc 0.936 0.947 0.0116
phoneme 0.756 0.743 -0.0134
diabetes 0.716 0.661 -0.0552
ozone-level-8hr 0.575 0.591 0.0159
hill-valley 0.527 0.743 0.2160
kc2 0.683 0.683 0.0000
eeg-eye-state 0.664 0.713 0.0484
climate-model-simulation-crashes 0.470 0.643 0.1731
spambase 0.891 0.912 0.0217
ilpd 0.566 0.607 0.0414
one-hundred-plants-margin 0.058 0.055 -0.0026
banknote-authentication 0.952 0.995 0.0430
mozilla4 0.925 0.924 -0.0009
electricity 0.778 0.787 0.0087
madelon 0.712 0.760 0.0480
scene 0.669 0.710 0.0411
musk 0.810 0.842 0.0326
nomao 0.905 0.911 0.0062
bank-marketing 0.658 0.645 -0.0134
MagicTelescope 0.780 0.807 0.0261
Click_prediction_small 0.494 0.494 -0.0001
page-blocks 0.669 0.816 0.1469
hypothyroid 0.924 0.907 -0.0161
yeast 0.445 0.487 0.0419
CreditCardSubset 0.785 0.803 0.0184
shuttle 0.651 0.514 -0.1368
Satellite 0.886 0.902 0.0168
baseball 0.627 0.701 0.0738
mc1 0.705 0.665 -0.0404
pc1 0.473 0.550 0.0770
cardiotocography 1.000 0.991 -0.0084
kr-vs-k 0.097 0.116 0.0187
volcanoes-a1 0.366 0.327 -0.0385
wine-quality-white 0.252 0.251 -0.0011
allbp 0.555 0.553 -0.0028
allrep 0.279 0.288 0.0087
dis 0.696 0.563 -0.1330
steel-plates-fault 1.000 1.000 0.0000
在 69 个案例中,模型在有无公式特征工程的情况下表现更好,49 个案例有明显改进。一些值得注意的例子包括:
-
Japanese Vowels 从 .57 提升到 .68
-
gas-drift 从 .74 提升到 .83
-
hill-valley 从 .52 提升到 .74
-
climate-model-simulation-crashes 从 .47 提升到 .64
-
banknote-authentication 从 .95 提升到 .99
-
page-blocks 从 .66 提升到 .81
使用具有强预测能力的模型和工程化特征
迄今为止,我们主要关注浅层决策树,并指出公式特征也可以生成对其他可解释模型有用的特征。但这也留下了它们在更强预测模型中的实用性问题。总体而言,公式特征与这些工具结合使用并不总是有效。
在大多数情况下,强大的预测模型,如提升树模型(例如 CatBoost、LGBM、XGBoost),通常能够推断出 FormulaFeatures 捕捉的模式。虽然它们将这些模式以大量决策树的形式捕捉,并将它们组合成一个集成模型,而不是单一特征,但效果是相同的,而且通常可能更强,因为这些树并不局限于简单的、可解释的操作符(+、-、* 和 /)。
因此,即使经过精心设计的特征与真实的 f(x) 非常接近,使用强模型时,可能不会在准确性上获得显著的提升。在这种情况下,尝试使用 FormulaFeatures 可能是值得的,我在一些项目中发现它很有帮助,但大多数情况下,增益是最小的。
实际上,正是在较小的(可解释的)模型中,像 FormulaFeatures 这样的工具才变得最为有用。
处理具有大量原始特征的数据
基于算术运算的特征工程的一个限制是,当原始特征的数量非常庞大时,它可能会变得非常慢,在数据科学中,遇到包含数百个特征的表格是相对常见的。这个问题对无监督特征工程方法的影响更加严重,但监督方法也可能显著变慢。
在这些情况下,甚至创建成对的工程特征也可能导致过拟合,因为可以生成大量特征,其中一些仅仅因为偶然的原因表现非常好。
为了解决这个问题,FormulaFeatures 限制了在输入数据中包含大量列时考虑的原始列的数量。因此,当数据集包含大量列时,只有最具预测性的特征会在第一次迭代后被考虑。随后的迭代会正常进行;只是第一次迭代中使用的原始特征会有所修剪。
一元函数
默认情况下,FormulaFeatures 不会包含一元函数,如平方、平方根或对数(尽管如果指定相关参数,它是可以执行这些操作的)。如上所述,一些工具,如 AutoFeat,也可选择性地支持这些操作,并且它们在某些时候是有价值的。
在某些情况下,像 A² / B 这样的特征可能比不含平方操作符的等效形式 A / B 更能预测目标。然而,如果没有足够的准确性,包含一元操作符可能会导致误导性的特征,并且可能不会显著提高任何使用这些特征的模型的准确性。
在使用决策树时,只要特征在有无一元函数的情况下保持单调关系,模型的最终准确度就不会发生变化。而且,大多数一元函数保持值的排名顺序(如正弦和余弦是例外,通常在强烈怀疑存在周期性模式时可以合理使用)。例如,A 中的值将与 A² 中的值具有相同的排名值(假设 A 中的所有值都是正数),因此平方运算不会增加任何预测能力——决策树将等效处理这些特征。
同样,从解释能力的角度来看,简单的函数通常可以捕捉到几乎与复杂函数相同的模式:例如,像 A / B 这样的简单函数通常比 A² / B 这样的公式更易于理解,但仍然传达相同的意思,即两个特征的比值才是相关的。
限制默认使用的操作符集也可以使过程执行得更快,并且更加规范化。
系数
对于在工程特征中包含系数的问题,也可以提出类似的论点。像 5.3A + 1.4B 这样的特征可能比简单的 A + B 更好地捕捉 A 和 B 与 Y 的关系,但系数通常是多余的,容易计算错误,并且即使大致正确也难以理解。
在乘法和除法运算的情况下,系数通常是无关紧要的(至少在与决策树一起使用时如此)。例如,5.3A * 1.4B 在大多数情况下与 A * B 在功能上是等效的,因为它们之间的差异是一个常数,可以被约掉。同样地,无论是否使用系数,都会存在单调关系,因此,当与仅关心特征值排序而非具体值的模型(如决策树)一起使用时,特征是等效的。
缩放
如果与决策树(或类似的模型类型,如加法决策树、规则或决策表)一起使用,则无需对 FormulaFeatures 生成的特征进行缩放。但对于某些模型类型,如支持向量机(SVM)、k 最近邻(kNN)、改进 kNN(ikNN)、逻辑回归等(包括任何基于点之间距离计算的模型),由 FormulaFeatures 工程化的特征可能与原始特征在尺度上差异较大,因此需要进行缩放。这是直接可以做到的,只是需要记住这一点。
可解释的机器学习
在本文中,我们讨论了可解释模型,但至少应该快速指出,FormulaFeatures 对于所谓的可解释模型也非常有用,并且这可能实际上是更重要的应用。
为了解释可解释性的概念:当很难或不可能创建具有足够准确性的可解释模型时,我们通常会开发黑箱模型(例如提升模型或神经网络),然后为该模型创建事后解释。这样做被称为可解释的人工智能(或 XAI)。这些解释试图使黑箱模型更易理解。相关技术包括:特征重要性、ALE 图、代理模型和反事实。
这些可以是许多场景中的重要工具,但它们也有限制,因为它们只能提供对模型的近似理解。而且,它们可能并不适用于所有环境:在某些情况下(例如出于安全性或合规性要求),可能需要严格使用可解释模型:即使用那些没有任何关于模型行为疑问的模型。
而且,即使不是严格要求,通常还是更倾向于在可能的情况下使用可解释模型:对模型和模型做出的预测有较好的理解通常是非常有用的。
话虽如此,使用黑箱模型和事后解释通常是预测问题中最合适的选择。由于 FormulaFeatures 能够生成有价值的特征,它可以支持 XAI,从而使特征重要性、图表、代理模型或反事实更具可解释性。
例如,可能无法将浅层决策树作为实际模型来使用,但它可以作为代理模型:一种简单、可解释的模型,近似实际模型。在这些情况下,与可解释模型一样,拥有一组良好的构造特征可以使代理模型更具可解释性,并更能捕捉实际模型的行为。
安装
该工具使用一个 单一的 .py 文件,可以直接下载并使用。除了 numpy、pandas、matplotlib 和 seaborn(用于绘制生成的特征图表)之外,没有其他依赖项。
结论
FormulaFeatures 是一个基于数值特征之间的算术关系来构造特征的工具。这些特征本身可以提供信息,但在与可解释的机器学习模型一起使用时,特别有用。
尽管这种方法并不总是能提高所有模型的准确性,但它通常能提高可解释模型的准确性,例如浅层决策树。
因此,它可以成为一个有用的工具,使得在预测问题中使用可解释模型变得更为可行——它可能允许在本应仅限于黑箱模型的问题中使用可解释模型。而且,在使用可解释模型时,它可能使这些模型更准确或更具可解释性。例如,使用分类决策树时,我们可能能够用更少的节点实现相似的准确度,或者使用相同数量的节点实现更高的准确度。
FormulaFeatures 通常能够很好地支持可解释的机器学习,但也存在一些局限性。它不适用于类别特征或其他非数值特征。而且,即使是数值特征,某些交互作用也可能难以通过算术函数捕捉。对于特征对与目标列之间存在更复杂关系的情况,使用ikNN可能更为合适。该方法基于最近邻原理,因此能够捕捉特征与目标之间任意复杂度的关系。
本文重点讨论了标准决策树,但为了实现最有效的可解释机器学习,尝试其他可解释模型也是很有用的。例如,直接看到这些方法如何应用到遗传决策树上就很简单,这些决策树与标准决策树类似,只是通过自助法和遗传算法创建的。对大多数其他可解释模型也是如此。
所有图片均由作者提供

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



