使用主成分分析的特征提取——一个简化的可视化演示
理解特征和主成分之间的转换

介绍
没有扎实的线性代数基础,理解主成分分析(PCA)背后的数学是一个挑战。当我在旧金山的 General Assembly 教授数据科学时,我发现帮助学生可视化特征和主成分之间的转换极大地增强了他们的理解。 PCA 是一种降维技术,它有四个主要部分:特征协方差、特征分解、主分量变换和根据解释的方差选择分量。这个博客的目的是分享一个视觉演示,这个演示是帮助学生理解最后两步。
降维的快速回顾
维度的诅咒
为了理解 PCA,我们必须知道它的目的。为了理解这个目的,我们必须知道 维数灾难 ,这个方法解决的根本问题。虽然我们总是可以在维基百科上找到详尽的定义,但以下提供了更简单直观的解释:
随着要素或维度数量的增长,我们需要精确归纳的数据量也呈指数级增长
— Charles Isbell,佐治亚理工学院交互计算学院教授
维度的诅咒—Prasad Pore 撰写的 KDNuggets 帖子。
有两种方法可以降低维数:
- 特征消除:我们直接去掉一些特征。
- 特征提取:我们保留所有特征的重要部分。我们应用主成分分析来实现这一点。注意,PCA 不是进行特征提取的唯一方法。
主成分分析
PCA 是一种降维方法,即识别我们数据中的重要关系,根据这些关系转换现有数据,然后量化这些关系的重要性,这样我们就可以保留最重要的关系,丢弃其他关系。为了记住这个定义,我们可以把它分成四个步骤:
- 我们通过一个 协方差矩阵 来识别特征之间的关系。
- 通过协方差矩阵的线性变换或 特征分解 ,得到 特征向量和特征值 。
- 然后,我们使用特征向量将数据转换成主分量。
- 最后,我们使用特征值量化这些关系的重要性,并保留重要的主成分**。**

Figure 1
同样,我们可以在维基百科上找到所有术语的完整解释。我发现下面的帖子更直观,也更有帮助。
- PCA——由“阿米巴说恢复莫妮卡”(我觉得很好笑)发布的 Stackoverflow 回复。
一个伟大的可视化由 3blue1brown 为步骤 2 发布(事实上,我强烈推荐他们的整个线性代数动画系列)。以下章节包括步骤 3 和步骤 4 的可视化。
简化的视觉演示
下面的演示使用虹膜数据库中单个数据点的特征向量来呈现特征和主成分之间的线性变换。我描述计算时没有使用任何线性代数术语。但是,如果您了解两个向量之间的点积(因为我们演示了单个数据点的转换)和矩阵乘法(当我们转换所有数据点时),将会很有帮助。

Figure 2
- 特征:顶部蓝色横线表示。注意,x1、x2、x3 和 x4 代表单个虹膜的四个特征(即,萼片长度、萼片宽度、花瓣长度和花瓣宽度),而不是四个不同的虹膜。
- 特征向量:用绿色矩阵表示
- 主成分:左侧橙色竖线表示

Figure 3
图 3 包括每个部分的值。学习和理解数学方法的最好方法之一是验证数字相加。从这里,我们将做一些简单的计算。
步骤 3 的视觉演示
- 将特征转换成主成分。

Figure 4
为了将特征转换成主分量,我们将特征与特征向量逐个元素相乘,如上面的图 4 所示,并在水平方向上相加。
2。将主成分转换回特征。

当然,通过执行上面图 5 所示的计算,您可以将主成分转换回原始特征。
步骤 4 的视觉演示
在可视化了变换背后的数学之后,让我们演示一下特征消除和特征提取之间的区别。

Figure 6
如上面的图 6 所示,如果我们选择通过特征消除来降低维数,我们将完全删除一些特征,在本例中是 x3 和 x4。当我们将维度从 4 减少到 2 时,由花瓣长度和花瓣宽度提供的所有信息都丢失了。

Figure 7
相比之下,当我们通过 PCA 等特征提取方法降低维数时,我们通过选择解释特征之间大多数关系的主成分来保留最重要的信息。这就是特征值发挥作用的地方,它帮助我们了解每个主成分包含多少信息。
在我们的例子中,基于与每个特征向量相关的归一化特征值,第一和第二主成分(即 pc1 和 pc2)解释了特征变化的 95%以上,如下图 8 所示。因此,我们只保留 pc1 和 pc2。

Lambda is the eigenvalue

Figure 8
注意,每个主成分包含来自所有四个特征的信息。通过只保留 pc1 和 pc2,我们从所有四个特征中提取最重要的信息,并将维数从 4 降低到 2。
评论
我想用几句话来结束我的博客,包括 PCA 的使用,PCA 的利弊,以及何时不使用 PCA。
数据清理很重要
- PCA 对异常值和缺失值很敏感。
标准化数据
- PCA 使用欧几里德距离作为其特征向量相似性度量,因此确保我们在应用 PCA 之前缩放特征。
- 如果我们在模型中使用 GridSearchCV,确保我们将 PCA 放在管道中。在运行 GridSearch 之前拟合和转换训练数据会导致 GridSearch 内部交叉验证期间的数据泄漏。详见我上一篇博文。
优点
- PCA 在不丢失任何特征信息的情况下降低了维数。
- 减少存储数据所需的存储空间
- 加速学习算法(用更低的维度)。
- 解决多重共线性问题(所有主成分相互正交)。
- 帮助可视化高维数据(在将维度减少到 2 或 3 之后)。
缺点
- 使用主成分分析可以防止对原始特征的解释,以及它们的影响,因为特征向量是没有意义的。
五氯苯甲醚的潜在使用案例(非详尽清单)
- 我们有许多具有高度多重共线性的要素。
- 我们有太多的功能导致算法运行非常缓慢。
滥用五氯苯甲醚(并非详尽清单)
- 我们不应该使用 PCA 来降低维数,以防止过度拟合。我们应该使用正则化(如 L1 和 L2)来代替。
- 在用原始数据运行机器学习模型之前,我们不应该盲目地应用 PCA。如果使用原始数据效果不好,我们应该考虑 PCA 作为替代方法。
资源和信用
- 为了进一步研究 PCA,我推荐了 Coursera 上的斯坦福大学吴恩达教授的机器学习课程(第 8 周)。【https://www.coursera.org/learn/machine-learning? 号
- 我还引用了大会全球讲师 Matt Brems 为他的学生准备的材料。他还写了一篇关于 PCA 细节的博文。https://towards data science . com/a-一站式主成分分析-5582fb7e0a9c
特性工厂第 2 部分:MLFlow 简介

如果你读了我的第一篇文章,你有希望很好地理解什么是特性工厂,为什么它很重要,以及如何最好地培养一个特性工厂的一般想法。如果你没有,建议你先去看看。在这篇后续文章中,我想开始深入 MLFlow 并通过一些代码示例介绍主要概念。
ML 生命周期

首先,让我们对经典的机器学习生命周期有一个共同的理解:
一个商业问题被识别,其中机器学习的应用可能是有价值的
一个团队收集了大量数据
数据工程师开始清理和标准化数据,为分析做准备
主题专家挖掘数据,寻找有用的新信号(特征)
数据科学家研究手头问题的最佳算法,记录他们所有的个人运行
一旦决定了一个模型,开发-运营团队就把这个模型部署到一个服务平台,比如 Sagemaker、Azure ML,或者一个定制的实现,比如一个包装在 web 服务中的容器。
在这个过程中有一些常见的棘手问题,主要是生命周期/部署管理和分析跟踪。
部署一个训练有素的机器学习模型并不像人们预期的那样简单,如果你曾经试图弄清楚如何去做,你可能会发现围绕它的文档并不像 ML 生命周期中的其他阶段那样全面。此外,在部署之后,您仍然必须处理监控、再培训和重新部署。如果没有标准化的流程,这可能会占用您的开发-运营团队大量时间,如果您的公司或团队要转换云服务(例如从 AWS 到 Azure),您必须从头开始学习流程。

Outdated data science organization
如果你曾经看过这样的图表,你就会知道跟踪你的数据科学项目的漫长而艰难的过程。为你正在处理的每个问题创建一个新的电子表格,保存在你公司每个人共享的云存储上(希望如此),确保你的团队可以看到你所有的试验,这样他们就不会浪费时间尝试相同的设置,你明白了吧。
我总是使用 Excel 来跟踪我的项目,你可能有另一个你喜欢的工具,但它很可能是一个个人日志,很难与他人分享和扩展。很难跟踪谁尝试了什么,什么有效,什么无效,以及正在使用的数据集。正如你从上面的图片中所看到的,该表缺少了很多东西,添加东西可能会迫使你完全重新设计它。如果有人想尝试梯度推进或想添加当今使用的许多其他神经网络超参数中的一个,会怎么样?正如你所看到的,这种跟踪概念是不可持续的。
介绍 MLFlow

MLFlow 是面向数据科学家和开发运营工程师的开源解决方案。在我的上一篇文章中,我从较高的层面解释了为什么 MLFlow 如此伟大,所以在这里我想深入一下,从治理开始。我们将介绍如何使用 MLFlow 来交付治理和组织实验。我们还将谈到我们可以跟踪哪些细节来帮助模型构建过程,以及如何选择和部署模型。
治理
治理是对数据科学过程有一个完整的鸟瞰图的想法,从原始数据到摄取到实验到建模,甚至是部署。对任何可能影响有价值的东西的过程进行治理是非常重要的,无论是货币、监管,尤其是面向用户的(即 ML 模型做出实时决策)。因此,必须对数据科学过程进行跟踪和管理。在我们授权将模型部署到生产中之前,我们需要知道模型来自哪里,对它们进行了何种类型和深度的测试,以及进入它们的确切数据
MLFlow 允许您将数据科学工件组织成两个主要类别:
实验 —你试图解决的首要问题,例如:销售点欺诈。通常围绕一个公共数据集创建
运行 —特征工程和模型训练中的每个单独“尝试”:Excel 表格的每一行(具有更大的灵活性)
这种组织设计的伟大之处在于,它允许您设计希望在运行之间跟踪的元数据,并且不会强迫您使用一些公共参数。您可以在给定的运行中存储您想要的任何内容,并且它不需要与上次运行完全匹配。例如,对于一个给定的运行,您可以跟踪您想要的任何参数(模型类型、超参数、训练/测试分割)、您想要的度量标准( fpr 、 r 、 f1 ),即使它是针对不同的模型流派(二元/多类分类、回归)。
说够了,让我们开始演示:
出于本演示的目的,我将使用 Splice Machine 的 MLManager ,因为这是我通常日常使用的(因为它在 Splice DB 上本地运行),但在大多数情况下,它看起来与普通 MLFlow 相同,我将指出该示例在 Splice Machine 之外工作所需的任何更改。
基础
在这个演示中,我们将使用修改过的 Kaggle 数据集来预测供应链环境中的订单延迟。作为数据科学团队中的典型,我们将有不止一个数据科学家在这个项目上合作,每个人都执行许多转换和建模技术。
首先,我们导入必要的库并创建一个用于跟踪的 MLManager 对象(注意,对于普通的 mlflow,没有对象,只需用 ml flow 替换 Manager)。我们现在从 create _ experiment 函数开始实验,并传入一个名称。如果该实验已经存在,它将自动成为活动实验。

创建管理器后,我们可以将数据接收到数据帧中,并开始分析和转换。请务必注意将信息记录(记录)到 MLFlow 的方式:
log_param(或 lp) :允许你记录任何你想要的模型/管道参数。想想训练/测试分割、超参数、转换管道的阶段、特定源数据集的描述。
log_params : MLManager 的特定函数,该函数采用参数列表来记录并自动处理所有参数
log_metric(或 lm) :允许您记录任何模型指标。考虑训练时间、准确度、精确度、AUC 等。
log_metrics : MLManager 的特定函数,它获取一个度量列表并自动处理它们
log_artifact :允许您将对象记录到特定的运行中。想想图表、系列模型、图片等等。
log_artifacts : MLManager 的特定函数,获取工件列表并处理所有工件
log _ feature _ transformation:ml manager 特定函数,它采用未经训练的 Spark 管道并记录数据集中每个特性的所有转换步骤
log _ Pipeline _ stages:ml manager 特定函数,采用 Spark 管道(合适或不合适)并以可读格式记录所有阶段
log _ model _ params:ml manager 特定函数,采用拟合的火花模型或管道模型,并记录所有参数和超参数
log _ Evaluator _ metrics:ml manager 特定函数,采用拼接评估器并记录其所有度量
log _ spark _ model:ml manager 的特定函数,它获取一个经过训练的 Spark 模型并将其记录下来以供部署。这是因为,在拼接机的实施中,模型直接存储在拼接机数据库中,而不是 S3 或外部存储器中。
MLFlow 的伟大之处在于它足够灵活,允许您以任何您想要的方式设计您的工作流,根据您的团队需要和期望的治理流程的要求获得粒度或高级别。
如果这有点神秘,下面的演示应该会让它更容易理解。
开始跑步
我们喜欢保持非常有条理,所以我们将立即开始我们的第一次运行,并在运行过程中记录所有的特性转换和发现。当我们创建各种新功能和功能转换时,我们可以使用 log_param 函数单独记录这些功能,或者我们可以使用(特定拼接)log_feature_transformations 函数并传入一个 Spark Pipeline 对象。请注意,这个批量日志记录功能仅适用于 Spark ML 管道。

‘dataSize’ is simply an arbitrary key:value MLFlow tag


Preprocessing our dataset by converting our string columns into numeric and creating a feature vector
在上面的单元格中,我们调用 manager . log _ feature _ transformations 并传入一个 Spark 管道。如上所述,这是拼接机器的特定功能,仅适用于 MLManager。为了创建这个函数的输出,您将循环通过您的管道的阶段以及您的数据帧的列,并“跟踪”每一列的路径,查看它受哪个变压器和估计器影响,分别记录这些发现。

Feature transformations for each column
上表描述了每一列的预处理步骤。例如,首先使用一个 StringIndexer 将列 CITY_destination 从一个字符串值索引为一个整数值,然后使用一个热编码,最后在最后一个名为 featuresVec 的列中组合成一个特征向量
一旦我们有了最终形式的数据集,我们就可以使用 Splice Machine 的本地 Spark 数据源(在另一篇文章中会有更多的介绍)来创建并插入我们转换后的数据集到一个 SQL 表中,这样我们就可以跟踪这个特定运行所使用的数据。然后,我们可以记录一个参数或一个标记,指定数据库中的表名,以供以后分析时参考。这很有用,因为如果我们的模型在未来表现奇怪,我们可以看到它被训练的确切数据集,并可能找到灾难的原因。

Create a table in Splice database from the Spark Dataframe, and insert that dataframe into the DB table
因为将数据帧作为表格直接存储在拼接数据库中是拼接特定的功能,所以您可以使用普通 MLFlow 做的是使用 mlflow.log_artifact 将您的数据帧作为工件记录。这将把您的数据集链接到有问题的运行,以便将来进行分析。另一个选择是调用 df.write.sav e 并传入一个 S3 桶来将数据帧保存到 S3,在到 S3 位置的运行中记录一个参数。
培训模型
一旦我们的数据准备好了,我们就可以开始训练一些模型了。我们希望尝试许多不同的模型,看看哪一个具有最佳基准性能,然后缩小所选模型的超参数范围。我们可以通过多个模型迭代运行一个循环,跟踪每个模型的度量。对于我们运行的每个模型,我们可以开始一个新的运行,并记录数据集所来自的表(上面创建的表)。在这些运行过程中,我们还可以记录诸如接收机工作特性(ROC)曲线之类的图表。

Trying multiple models and logging all parameters and results
在我们运行一些超参数调优并且对我们选择的模型感到满意之后,我们可以将模型保存到 MLFlow(它被连续存储在 Splice DB 中),并在 MLFlow UI 中的工件中看到它。
manager.log_evaluator_metrics()的输出

manager.stop_and_log_timer()的输出

manager.log_model_params()的输出

正如您在上面所看到的,一些拼接机内置的日志记录功能为您提供了一些很好的管理,但是您也可以在 MLFlow 中实现这些功能:
要记录您的模型参数,您可以使用 model.extractParamMap()提取参数映射,并遍历您的参数,将每个参数记录到 MLFlow。
要记录根据您的数据进行训练和根据您的测试数据进行预测所花费的时间,您可以导入本地时间库,用 t1 = time.time()获得您的训练的开始和结束时间,并记录两个时间的差值。
为了记录您所有的指标,您可以使用 Spark 评估器来获取评估指标,并遍历可用的指标,使用 mlflow.log_metric 记录每个指标。
当其他数据科学家构建运行来测试他们的想法时,他们可以在同一个实验中填充,允许模型的交叉比较。任何东西都可以在这些实验和运行中保存,允许团队开发他们自己的工作标准。下面我们展示了两个比较:一个图表将特定的模型超参数与单个指标进行比较(在这种情况下,r 与树的数量),另一个图表显示了四个模型构建的整体视图,显示了每个模型的优势和劣势。

Comparing Number of Trees against r²

Comparing all metrics of each model against each other
我非常欣赏第二个图表,因为您可以看到每个模型的更全面的视图。例如,在这里您可以看到 magenta 模型明显优于所有其他模型,除了 r。因此,除非 r 是一个关键的评估指标,否则这将是一个明确的选择。
模型部署

Model tracking screen before deployment
既然一切都被跟踪和比较了,那么是时候部署您的模型了。没有部署,你的模型只不过是一个漂亮的 Jupyter 笔记本,但 MLFlow 允许轻松地基于代码部署到 AzureML 或 AWS Sagemaker。在拼接平台中,我们有一个方便的用户界面用于部署,因此无论您的云服务如何,部署都保持一致和简单。如果你使用普通的 MLFlow,你可以使用内置的 AzureML 或者sage makerAPI 来部署你的模型。

Model deployment UI
当部署到 Azure ML 或 AWS Sagemaker 时,只需选择您的地区、名称和一些其他细节,您的模型就上路了。部署完成后,您可以在 UI 中跟踪所有作业以及是谁部署了它们。

Model tracking screen after deployment
摘要
据此,我们使用 MLFlow 改造,训练,测试并部署了第一个机器学习模型。在任何时候,团队中的任何人都可以导航到 MLFlow UI,并查看从原始数据到部署模型所采取的所有步骤。随着数据的变化和模型需要被监控或调整,每个人都确切地知道去哪里看。
我真的很喜欢使用 MLFlow 及其所有令人难以置信的功能。如果你自己是 MLFlow 的粉丝,请随意评论我可能错过的任何其他伟大功能,欢迎所有建议!
要了解更多关于 MLFlow 的信息,请点击此处,要了解更多关于 Splice Machine 的 MLManager 实现,请点击此处,或请求演示此处。
神经网络的特征重要性
让机器学习变得容易理解,提供变量关系解释

Photo by Markus Spiske on Unsplash
机器学习中最大的挑战之一是让模型自己说话。不仅开发具有强大预测能力的强大解决方案很重要,而且在许多商业应用中,了解模型如何提供这些结果也很有趣:哪些变量参与最多,相关性的存在,可能的因果关系等等。
这些需求使得基于树的模型成为这个领域的一个好武器。它们是可扩展的,并且允许非常容易地计算变量解释。每个软件都提供这个选项,我们每个人都至少尝试过一次用随机森林或类似的方法计算变量重要性报告。对于神经网络,这种益处被认为是禁忌。神经网络通常被视为一个黑箱,很难从其中提取有用的信息用于其他目的,如特征解释。
在这篇文章中,我试图提供一个优雅而聪明的解决方案,用几行代码,允许你挤压你的机器学习模型并提取尽可能多的信息,以便提供特征重要性,个性化重要的相关性并试图解释因果关系。
数据集
给定一个真实的数据集,我们试图研究哪些因素影响最终的预测性能。为了实现这个目标,我们从 UCI 机器学习知识库中获取数据。特权数据集是联合循环发电厂数据集,当发电厂设置为满负荷工作时,在那里收集了 6 年的数据。特征包括每小时平均变量:环境温度(AT)、环境压力(AP)、相对湿度(RH)和排气真空(V ),以预测电厂的每小时净电能输出(PE)。
所涉及的变量通过 Pearson 相关链接进行关联,如下表所示。

Correlation Matrix
梯度增强特征重要性
我们开始构建一个简单的基于树的模型,以便提供能量输出 (PE) 预测,并计算标准的特征重要性估计。这最后一步使我们能够比标准的相关指数更多地说明变量之间的关系。这些数字总结了在内部空间划分期间(在训练阶段)指出特定特征时,所有树的杂质指数的减少。Sklearn 应用归一化,以便提供可累加为 1 的输出。这也是一个免费的结果,可以在训练后间接获得。
gb = GradientBoostingRegressor(n_estimators=100)
gb.fit(X_train, y_train.values.ravel())plt.bar(range(X_train.shape[1]), gb.feature_importances_)
plt.xticks(range(X_train.shape[1]), ['AT','V','AP','RH'])

GradientBoosting Features Importance
这个结果很容易解释,并且似乎复制了计算与我们的目标变量的相关性的初始假设(相关矩阵的最后一行):值越高,这个特定特征预测我们的目标的影响就越大。
尽管我们通过梯度推进取得了很好的结果,但我们不想完全依赖这种方法……我们想推广计算特征重要性的过程,让我们自由开发另一种具有相同灵活性和解释能力的机器学习模型;更进一步:提供变量之间存在显著伤亡关系的证据。
排列重要性
为我们的实验确定的模型无疑是神经网络,因为它们享有黑盒算法的声誉。为了揭开这个刻板印象的神秘面纱,我们将关注排列的重要性。其易于实现,结合其有形的理解和适应性,使其成为回答问题的一致候选:哪些特性对预测的影响最大?
排列重要性是在模型拟合后计算的。所以我们只能压榨它,得到我们想要的。这种方法的工作原理很简单:如果我随机打乱数据中的单个特征,让目标和所有其他特征保持不变,这会如何影响最终的预测性能?
从变量的随机重新排序中,我期望得到:
- 不太准确的预测,因为产生的数据不再符合现实世界中观察到的任何东西;
- 最差的表现,来自最重要变量的混乱。这是因为我们正在破坏数据的自然结构。如果我们的洗牌破坏了一个牢固的关系,我们将损害我们的模型在训练中所学到的东西,导致更高的错误(高错误=高重要性)。

Permutation Importance at work
实际上,这是我们真实场景中发生的事情…
我们选择了合适的神经网络结构来模拟小时电能输出 ( EP )。记住也要在一个较低的范围内调整目标变量:我经典地减去平均值,除以标准差,这有助于训练。
inp = Input(shape=(scaled_train.shape[1],))
x = Dense(128, activation='relu')(inp)
x = Dense(32, activation='relu')(x)
out = Dense(1)(x)model = Model(inp, out)
model.compile(optimizer='adam', loss='mse')model.fit(scaled_train, (y_train - y_train.mean())/y_train.std() , epochs=100, batch_size=128 ,verbose=2)
在预测阶段,梯度提升和神经网络在平均绝对误差方面达到相同的性能,分别为 2.92 和 2.90(记住要反向预测)。
至此,我们结束了培训,让我们开始随机抽样。
我们计算验证数据上每个特征的混洗(总共 4 次= 4 个显式变量),并提供每一步的误差估计;记住每一步都要把数据恢复到原来的顺序。然后,我将我们在每个洗牌阶段获得的 MAE 绘制成相对于原始 MAE 的百分比变化(大约 2.90)
plt.bar(range(X_train.shape[1]), (final_score - MAE)/MAE*100)
plt.xticks(range(X_train.shape[1]), ['AT','V','AP','RH'])

Permutation Importance as percentage variation of MAE
上图复制了射频特征重要性报告,并证实了我们最初的假设:环境温度(AT)是预测电能输出(PE) 的最重要和最相关的特征。尽管处的排气真空(V) 和与 PE (分别为 0.87 和 0.95)表现出相似且高度相关的关系,但它们在预测阶段具有不同的影响。这一现象是一个软例子,说明高相关性(皮尔逊术语)并不总是高解释力的同义词。
因果关系
为了避免虚假关系,证明相关性总是一种阴险的操作。同时,很难出示伤亡行为的证据。在文献中,有很多证明因果关系的方法。其中最重要的是格兰杰因果关系检验。这种技术广泛应用于时间序列领域,以确定一个时间序列是否有助于预测另一个时间序列:即证明(根据滞后值的 f 检验)它增加了回归的解释力。
间接的,这就是我们已经做的,计算排列重要性。改变每一个变量并寻找性能变化,我们正在证明这个特性在预测预期目标方面有多大的解释力。
为了证明因果关系,我们现在要做的是证明数据混洗为性能变化提供了有意义的证据。我们对无洗牌和有洗牌的最终预测进行操作,并验证两个预测总体的平均值是否有差异。这意味着随机的平均预测也可以被任何随机的预测子群观察到。因此,这正是我们将对每个特征所做的:我们将合并有置换和无置换的预测,我们将随机抽样一组预测,并计算它们的平均值与无置换预测的平均值之间的差异。
np.random.seed(33)
id_ = 0 #feature index
merge_pred = np.hstack([shuff_pred[id_], real_pred])
observed_diff = abs(shuff_pred[id_].mean() - merge_pred.mean())
extreme_values = []
sample_d = []for _ in range(10000):
sample_mean = np.random.choice(merge_pred,
size=shuff_pred[id_].shape[0]).mean()
sample_diff = abs(sample_mean - merge_pred.mean())
sample_d.append(sample_diff)
extreme_values.append(sample_diff >= observed_diff)
np.sum(extreme_values)/10000 #p-value
为了控制一切,可视化我们的模拟结果是一个很好的选择。我们绘制了模拟平均差异的分布(蓝色条)并标记了实际观察到的差异(红线)。我们可以看到,对于在处,有证据表明在没有滑移的情况下做出的预测在均值上存在差异(低 p 值:低于 0.1)。其他变量不会带来均值的显著提高。

Simulation distributions and relative p-values
相关性并不总是意味着因果关系!考虑到这一点,我们根据一个选定特征增加解释力的能力来证明因果关系。我们用我们的统计学家和程序员的知识,重新创造了一种方法来证明这个概念,利用了我们以前在排列重要性方面的发现,增加了关于变量关系的信息。
摘要
在这篇文章中,我介绍了排列重要性,这是一种计算特性重要性的简单而聪明的技术。它对各种模型(我使用神经网络只是作为个人选择)和每个问题都很有用(模拟程序适用于分类任务中的**:在计算排列重要性时,记得选择适当的损失度量**,如交叉熵,避免模糊的准确性)。我们还使用了排列来展示一种方法,证明了攻击 p 值的变量之间的因果关系!
保持联系: Linkedin
基于 Python 的遗传算法特征约简
介绍
在某些情况下,使用原始数据来训练机器学习算法可能不是合适的选择。当由原始数据训练时,该算法必须自己进行特征挖掘,以检测彼此不同的组。但是这需要大量的数据来自动进行特征挖掘。对于小数据集,数据科学家最好自己执行特征挖掘步骤,并告诉机器学习算法使用哪个特征集。
所使用的特征集必须代表数据样本,因此我们必须注意选择最佳特征。数据科学家建议使用某些类型的特征,这些特征似乎有助于根据以前的经验表示数据样本。一些特征可能证明它们在代表样本方面的稳健性,而另一些则不然。
可能有一些类型的特征可能通过降低分类问题的准确度或增加回归问题的误差来影响训练模型的结果。例如,在特征向量中可能有一些噪声元素,因此它们应该被去除。特征向量也可以包括 2 个或更多相关元素。仅仅使用一种元素将替代另一种元素。为了去除这类元素,有两个有用的步骤,即特征选择和减少。本教程重点介绍特征减少。
假设有 3 个特征 F1、F2 和 F3,每个特征有 3 个特征元素。因此,特征向量长度是 3×3 = 9。特征选择仅选择特定类型的特征,而排除其他特征。比如只选择 F1 和 F2,去掉 F3。特征向量长度现在是 6 而不是 9。在特征缩减中,每个特征的特定元素可能被排除。例如,该步骤可能会从 F3 中移除第一个元素和第三个元素,同时保留第二个元素。因此,特征向量长度从 9 减少到仅仅 7。
在开始本教程之前,值得一提的是,它是我的 LinkedIn 个人资料中先前发布的 2 个教程的扩展。
第一个教程的标题是“使用 NumPy 和 Fruits360 图像数据集的分类的人工神经网络实现”。它首先从 Fruits360 数据集的 4 个类中提取长度为 360 的特征向量。然后,它使用 NumPy 从头开始构建人工神经网络(ANN ),以便对数据集进行分类。这里有https://www . LinkedIn . com/pulse/artificial-neural-network-implementation-using-numpy-fruits 360-gad。这里有它的 GitHub 项目:【https://github.com/ahmedfgad/NumPyANN】T4。
第二个教程的标题是“使用遗传算法优化人工神经网络”。建立并使用遗传算法优化神经网络参数,以提高分类精度。这里有https://www . LinkedIn . com/pulse/artificial-neural-networks-optimization-using-genetic-Ahmed-gad。它的 GitHub 项目也在这里:https://github.com/ahmedfgad/NeuralGenetic。
本教程讨论了如何使用遗传算法(GA)来减少从长度为 360 的 Fruits360 数据集提取的特征向量。本教程首先讨论要遵循的步骤。之后主要用 NumPy 和 Sklearn 用 Python 实现步骤。
本教程的实现可以在我的 GitHub 页面这里找到:https://github.com/ahmedfgad/FeatureReductionGenetic
遗传算法从一个初始群体开始,该群体由多个染色体(即解)组成,其中每个染色体具有一个基因序列。使用适应度函数,遗传算法选择最佳解作为父代来创建新的种群。这种新群体中的新解是通过对双亲应用两种操作来创建的,这两种操作是交叉和变异。当应用遗传算法解决一个给定的问题时,我们必须确定基因的表示,合适的适应度函数,以及如何应用交叉和变异。让我们看看事情是如何运作的。

关于 GA 的更多信息
您可以从我准备的以下资源中了解有关 GA 的更多信息:
- 遗传算法优化导论
https://www . LinkedIn . com/pulse/introduction-优化-遗传-算法-ahmed-gad/
https://www . kdnugges . com/2018/03/introduction-optimization-with-genetic-algorithm . html
- 遗传算法(GA)优化—分步示例
https://www . slide share . net/AhmedGadFCIT/genetic-algorithm-ga-optimization-step by step-example
- 遗传算法在 Python 中的实现
https://www . LinkedIn . com/pulse/genetic-algorithm-implementation-python-Ahmed-gad/
https://www . kdnugges . com/2018/07/genetic-algorithm-implementation-python . html
https://towardsdatascience . com/genetic-algorithm-implementation-in-python-5ab 67 bb 124 a 6
https://github.com/ahmedfgad/GeneticAlgorithmPython
我还在 2018 年写了一本书,其中一章涉及 GA。这本书被称为“Ahmed faw zy Gad‘使用深度学习和 CNN 的实用计算机视觉应用’。2018 年 12 月,Apress,978–1–4842–4167–7”,可在施普林格https://www.springer.com/us/book/9781484241660获得。
染色体表示
GA 中的基因是染色体的组成部分。首先,我们需要确定染色体中有哪些基因。要做到这一点,要考虑到每一个可能影响结果的属性都应该被视为一个基因。因为我们问题的目标是选择最佳的特征元素集合,因此每个特征元素的选择与否都会影响结果。因此,每个特征元素被视为一个基因。染色体将由所有基因(即所有特征元素)组成。因为有 360 个特征元素,那么就会有 360 个基因。一个很好的信息是,染色体的长度是 360。
在确定了被选择的基因是什么之后,接下来就是确定基因的代表性。有不同的表示法,如十进制、二进制、浮点、字符串等。我们的目标是知道基因(即特征元素)是否在缩减的特征集中被选择。因此,分配给基因的值应该反映它是否被选择。基于这一描述,很明显每个基因有两个可能的值。一个值表示基因被选中,另一个值表示基因未被选中。因此,二进制表示是最佳选择。当基因值为 1 时,则它将在缩减的特征集中被选择。当值为 0 时,它将被忽略。
总的来说,染色体将由 360 个二进制表示的基因组成。根据下图,在特征向量和染色体之间存在一对一的映射。即染色体中的第一个基因与特征向量中的第一个元素相联系。当该基因的值为 1 时,这意味着选择了特征向量中的第一个元素。

适应度函数
通过得到如何产生染色体,初始种群可以很容易地被随机初始化为 NumPy。在初始化之后,选择双亲。GA 是基于达尔文的理论适者生存。即选择当前种群中的最佳解进行交配,以产生更好的解。通过保留好的解决方案并删除坏的解决方案,我们可以达到最优或次优的解决方案。
用于选择双亲的标准是与每个解(即染色体)相关联的适应值。适应值越高,解决方案越好。使用适应度函数来计算适应度值。那么,在我们的问题中,什么是最好的函数呢?我们的问题的目标是创建一个减少的特征向量,增加分类精度。因此,判断解决方案好坏的标准是分类精度。因此,适应度函数将返回一个数字,该数字指定每个解决方案的分类精度。精度越高,解决方案越好。
为了返回分类精度,必须有一个机器学习模型,以通过每个解决方案返回的特征元素进行训练。对于这种情况,我们将使用支持向量分类器(SVC)。
数据集分为训练样本和测试样本。基于训练数据,SVC 将使用由群体中的每个解决方案选择的特征元素来训练。经过训练后,会根据测试数据进行测试。
基于每个解的适应值,我们可以选择其中最好的作为父代。这些父母被一起放在交配池中,以产生后代,这些后代将成为下一代新群体的成员。这样的后代是通过对选择的亲本应用交叉和变异操作来创建的。让我们按照下面的讨论来配置这样的操作。
交叉和变异
在适应度函数的基础上,我们可以在当前群体中筛选出最好的解,称为父代。GA 假设将两个好的解决方案配对会产生第三个更好的解决方案。交配意味着交换父母双方的一些基因。使用交叉操作来交换基因。有不同的方式可以应用这样的操作。本教程使用单点交叉,其中一个点分割染色体。点之前的基因取自一个解,点之后的基因取自另一个解。
通过应用交叉,所有的基因都取自先前的双亲。新的后代中没有引入新的基因。如果所有父母中都有一个不好的基因,那么这个基因就会传递给后代。为此,应用突变操作以便在后代中引入新的基因。在基因的二进制表示中,通过翻转一些随机选择的基因的值来应用突变。如果基因值是 1,那么它将是 0,反之亦然。
产生后代后,我们可以创造下一代的新种群。这个群体除了后代外,还包括以前的父母。
至此,所有步骤都讨论完毕。接下来是用 Python 实现它们。请注意,我之前写了一篇名为“用 Python 实现遗传算法”的教程,用于用 Python 实现遗传算法,我将修改它的代码来解决我们的问题。还不如读一读。
Python 实现
该项目分为两个文件。一个名为“GA.py”的文件将 GA 步骤的实现保存为函数。另一个文件,也就是主文件,只是导入这个文件并在一个循环中调用它的函数,这个循环遍历所有代。
主文件首先根据下面的代码读取从 Fruits360 数据集中提取的特征。这些特征返回到数据输入变量中。有关提取这些要素的详细信息,请参见本教程开头提到的两个教程。该文件还读取变量 data_outputs 中与样本相关的分类标签。
选择一些样本进行训练,其指数存储在train _ indexes变量中。类似地,测试样本索引存储在test _ indexes变量中。
**import** numpy
**import** GA
**import** pickle
**import** matplotlib.pyplot
f = open(**"dataset_features.pkl"**, **"rb"**)
data_inputs = pickle.load(f)
f.close()
f = open(**"outputs.pkl"**, **"rb"**)
data_outputs = pickle.load(f)
f.close()
num_samples = data_inputs.shape[0]
num_feature_elements = data_inputs.shape[1]
train_indices = numpy.arange(1, num_samples, 4)
test_indices = numpy.arange(0, num_samples, 4)
print(**"Number of training samples: "**, train_indices.shape[0])
print(**"Number of test samples: "**, test_indices.shape[0])
**"""
Genetic algorithm parameters:
Population size
Mating pool size
Number of mutations
"""** sol_per_pop = 8 *# Population size.* num_parents_mating = 4 *# Number of parents inside the mating pool.* num_mutations = 3 *# Number of elements to mutate.
# Defining the population shape.* pop_shape = (sol_per_pop, num_feature_elements)
*# Creating the initial population.* new_population = numpy.random.randint(low=0, high=2, size=pop_shape)
print(new_population.shape)
best_outputs = []
num_generations = 100
它初始化遗传算法的所有参数。这包括根据 sol_per_pop 变量设置为 8 的每个种群的解数,在num _ parents _ matting变量中设置为 4 的后代数,以及在 num_mutations 变量中设置为 3 的突变数。之后,它在一个名为 new_population 的变量中随机创建初始种群。
有一个名为 best_outputs 的空列表,它保存每代之后的最佳结果。这有助于在完成所有代之后可视化 GA 的进程。在 num_generations 变量中,代数被设置为 100。请注意,您可以更改所有这些参数,这可能会产生更好的结果。
在准备好特性、类标签和 GA 参数之后,我们可以根据下一个代码进行 GA 的迭代。首先,通过调用 GA 文件中定义的名为 cal_pop_fitness() 的适应度函数来计算所有解的适应度值。该函数接受当前总体、提取的特征、类标签、训练索引和测试索引。该函数在名为 fitness 的变量中返回所有解决方案的适应值。记住,适应值代表分类精度。最佳(即最高)分类精度保存在最佳输出列表中。
基于计算的适应值,使用 GA.py 文件中定义的select _ matting _ pool()函数,选择具有最高分类精度的最佳解作为交配池中的亲代。它接受当前人口、适应值和要返回的父代数量。它将选择的双亲返回到双亲变量中。
**for** generation **in** range(num_generations):
print(**"Generation : "**, generation)
*# Measuring the fitness of each chromosome in the population.* fitness = GA.cal_pop_fitness(new_population, data_inputs, data_outputs, train_indices, test_indices)
best_outputs.append(numpy.max(fitness))
*# The best result in the current iteration.* print(**"Best result : "**, best_outputs[-1])
*# Selecting the best parents in the population for mating.* parents = GA.select_mating_pool(new_population, fitness, num_parents_mating)
*# Generating next generation using crossover.* offspring_crossover = GA.crossover(parents, offspring_size=(pop_shape[0]-parents.shape[0], num_feature_elements))
*# Adding some variations to the offspring using mutation.* offspring_mutation = GA.mutation(offspring_crossover, num_mutations=num_mutations)
*# Creating the new population based on the parents and offspring.* new_population[0:parents.shape[0], :] = parents
new_population[parents.shape[0]:, :] = offspring_mutation
下一步是对选择的父代应用交叉操作来创建后代。这是在 GA.py 文件中定义的 crossover() 函数内完成的。它接受父数组和后代数组的形状,稍后返回到后代交叉变量中。然后使用 GA.py 文件中的**突变()**函数对数组进行突变操作。除了交叉结果,该函数还接受突变的数量。
因为新群体除了后代之外还包括所选的父母,所以父母和后代 _ 突变数组都被保存到新 _ 群体变量中。之后,新的一代应用于新的人口。
在所有代完成之后,执行下一个代码,以便返回最佳选择的特征元素集合和所选元素的数量。在 100 代完成后,该算法使用了 174 个特征元素,以达到 99.59%的准确度。
fitness = GA.cal_pop_fitness(new_population, data_inputs, data_outputs, train_indices, test_indices)
*# Then return the index of that solution corresponding to the best fitness.* best_match_idx = numpy.where(fitness == numpy.max(fitness))[0]
best_match_idx = best_match_idx[0]
best_solution = new_population[best_match_idx, :]
best_solution_indices = numpy.where(best_solution == 1)[0]
best_solution_num_elements = best_solution_indices.shape[0]
best_solution_fitness = fitness[best_match_idx]
print(**"best_match_idx : "**, best_match_idx)
print(**"best_solution : "**, best_solution)
print(**"Selected indices : "**, best_solution_indices)
print(**"Number of selected elements : "**, best_solution_num_elements)
print(**"Best solution fitness : "**, best_solution_fitness)
matplotlib.pyplot.plot(best_outputs)
matplotlib.pyplot.xlabel(**"Iteration"**)
matplotlib.pyplot.ylabel(**"Fitness"**)
matplotlib.pyplot.show()
上面的代码还显示了一个图表,显示了 GA 在所有代中的进度,如下所示。

这里是主文件的完整代码。
import numpy
import GA
import pickle
import matplotlib.pyplot
f = open("dataset_features.pkl", "rb")
data_inputs = pickle.load(f)
f.close()
f = open("outputs.pkl", "rb")
data_outputs = pickle.load(f)
f.close()
num_samples = data_inputs.shape[0]
num_feature_elements = data_inputs.shape[1]
train_indices = numpy.arange(1, num_samples, 4)
test_indices = numpy.arange(0, num_samples, 4)
print("Number of training samples: ", train_indices.shape[0])
print("Number of test samples: ", test_indices.shape[0])
"""
Genetic algorithm parameters:
Population size
Mating pool size
Number of mutations
"""
sol_per_pop = 8 # Population size.
num_parents_mating = 4 # Number of parents inside the mating pool.
num_mutations = 3 # Number of elements to mutate.
# Defining the population shape.
pop_shape = (sol_per_pop, num_feature_elements)
# Creating the initial population.
new_population = numpy.random.randint(low=0, high=2, size=pop_shape)
print(new_population.shape)
best_outputs = []
num_generations = 100
for generation in range(num_generations):
print("Generation : ", generation)
# Measuring the fitness of each chromosome in the population.
fitness = GA.cal_pop_fitness(new_population, data_inputs, data_outputs, train_indices, test_indices)
best_outputs.append(numpy.max(fitness))
# The best result in the current iteration.
print("Best result : ", best_outputs[-1])
# Selecting the best parents in the population for mating.
parents = GA.select_mating_pool(new_population, fitness, num_parents_mating)
# Generating next generation using crossover.
offspring_crossover = GA.crossover(parents, offspring_size=(pop_shape[0]-parents.shape[0], num_feature_elements))
# Adding some variations to the offspring using mutation.
offspring_mutation = GA.mutation(offspring_crossover, num_mutations=num_mutations)
# Creating the new population based on the parents and offspring.
new_population[0:parents.shape[0], :] = parents
new_population[parents.shape[0]:, :] = offspring_mutation
# Getting the best solution after iterating finishing all generations.
# At first, the fitness is calculated for each solution in the final generation.
fitness = GA.cal_pop_fitness(new_population, data_inputs, data_outputs, train_indices, test_indices)
# Then return the index of that solution corresponding to the best fitness.
best_match_idx = numpy.where(fitness == numpy.max(fitness))[0]
best_match_idx = best_match_idx[0]
best_solution = new_population[best_match_idx, :]
best_solution_indices = numpy.where(best_solution == 1)[0]
best_solution_num_elements = best_solution_indices.shape[0]
best_solution_fitness = fitness[best_match_idx]
print("best_match_idx : ", best_match_idx)
print("best_solution : ", best_solution)
print("Selected indices : ", best_solution_indices)
print("Number of selected elements : ", best_solution_num_elements)
print("Best solution fitness : ", best_solution_fitness)
matplotlib.pyplot.plot(best_outputs)
matplotlib.pyplot.xlabel("Iteration")
matplotlib.pyplot.ylabel("Fitness")
matplotlib.pyplot.show()
遗传算法实现
下面列出了 GA.py 文件的实现。在 cal_pop_fitness() 函数中,SVC 根据每个解决方案选择的特征元素进行训练。在被训练之前,根据其基因被赋予值 1 的所选元素来过滤特征。这是在 reduce_features() 函数中完成的。除了所有样本的完整功能之外,它还接受当前的解决方案。
经过训练后,使用**classification _ accuracy()**函数计算其分类精度。该函数返回存储在 cal_pop_fitness() 函数中名为 accuracy 的数组中的精度。
crossover() 和 mutation() 函数的实现与我之前的教程“Python 中的遗传算法实现”中讨论的非常相似。一个主要的区别是突变()函数通过翻转它们的值来改变随机选择的基因,因为我们使用二进制表示。
import numpy
import sklearn.svm
def reduce_features(solution, features):
selected_elements_indices = numpy.where(solution == 1)[0]
reduced_features = features[:, selected_elements_indices]
return reduced_features
def classification_accuracy(labels, predictions):
correct = numpy.where(labels == predictions)[0]
accuracy = correct.shape[0]/labels.shape[0]
return accuracy
def cal_pop_fitness(pop, features, labels, train_indices, test_indices):
accuracies = numpy.zeros(pop.shape[0])
idx = 0
for curr_solution in pop:
reduced_features = reduce_features(curr_solution, features)
train_data = reduced_features[train_indices, :]
test_data = reduced_features[test_indices, :]
train_labels = labels[train_indices]
test_labels = labels[test_indices]
SV_classifier = sklearn.svm.SVC(gamma='scale')
SV_classifier.fit(X=train_data, y=train_labels)
predictions = SV_classifier.predict(test_data)
accuracies[idx] = classification_accuracy(test_labels, predictions)
idx = idx + 1
return accuracies
def select_mating_pool(pop, fitness, num_parents):
# Selecting the best individuals in the current generation as parents for producing the offspring of the next generation.
parents = numpy.empty((num_parents, pop.shape[1]))
for parent_num in range(num_parents):
max_fitness_idx = numpy.where(fitness == numpy.max(fitness))
max_fitness_idx = max_fitness_idx[0][0]
parents[parent_num, :] = pop[max_fitness_idx, :]
fitness[max_fitness_idx] = -99999999999
return parents
def crossover(parents, offspring_size):
offspring = numpy.empty(offspring_size)
# The point at which crossover takes place between two parents. Usually, it is at the center.
crossover_point = numpy.uint8(offspring_size[1]/2)
for k in range(offspring_size[0]):
# Index of the first parent to mate.
parent1_idx = k%parents.shape[0]
# Index of the second parent to mate.
parent2_idx = (k+1)%parents.shape[0]
# The new offspring will have its first half of its genes taken from the first parent.
offspring[k, 0:crossover_point] = parents[parent1_idx, 0:crossover_point]
# The new offspring will have its second half of its genes taken from the second parent.
offspring[k, crossover_point:] = parents[parent2_idx, crossover_point:]
return offspring
def mutation(offspring_crossover, num_mutations=2):
mutation_idx = numpy.random.randint(low=0, high=offspring_crossover.shape[1], size=num_mutations)
# Mutation changes a single gene in each offspring randomly.
for idx in range(offspring_crossover.shape[0]):
# The random value to be added to the gene.
offspring_crossover[idx, mutation_idx] = 1 - offspring_crossover[idx, mutation_idx]
return offspring_crossover
联系作者
- 电子邮件:ahmed.f.gad@gmail.com
- 领英:https://linkedin.com/in/ahmedfgad/
- https://kdnuggets.com/author/ahmed-gad
- YouTube:【https://youtube.com/AhmedGadFCIT
- https://towardsdatascience.com/@ahmedfgad
- GitHub:https://github.com/ahmedfgad
特征选择和降维
探索 Kaggle“不要过度适应 II”竞赛中的特征选择和降维技术

根据维基百科,“特征选择是选择相关特征子集用于模型构建的过程”,或者换句话说,选择最重要的特征。
在正常情况下,领域知识起着重要的作用,我们可以选择我们认为最重要的特性。例如,在预测房价时,卧室的数量和面积通常被认为是重要的。
不幸的是,在“不要过度适应”II 竞赛中,使用领域知识是不可能的,因为我们有一个二元目标和 300 个“神秘起源”的连续变量,迫使我们尝试自动特征选择技术。
完整的笔记本可以在这里找到。
特征选择与降维
通常,特征选择和降维被组合在一起(如本文中所示)。虽然这两种方法都用于减少数据集中的要素数量,但有一个重要的区别。
特征选择是简单地选择和排除给定的特征而不改变它们。
维度缩减将特征转换到一个更低的维度。
在本文中,我们将探讨以下特征选择和维度缩减技术:
特征选择
- 移除缺少值的要素
- 移除方差较小的要素
- 移除高度相关的要素
- 单变量特征选择
- 递归特征消除
- 使用 SelectFromModel 进行特征选择
降维
- 主成分分析
基线模型
我们将使用逻辑回归作为基线模型。我们首先分成测试集和训练集,并缩放数据:
# prepare for modeling
X_train_df = train.drop(['id', 'target'], axis=1)
y_train = train['target']# scaling data
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train_df)lr = LogisticRegression(solver='liblinear')
lr_scores = cross_val_score(lr,
X_train,
y_train,
cv=5,
scoring='roc_auc')print('LR Scores: ', lr_scores)LR Scores: [0.80729167 0.71875 0.734375 0.80034722 0.66319444]
我们可以从交叉验证分数的变化中看到模型过度拟合。我们可以尝试通过特征选择来提高这些分数。
移除缺少值的要素
检查缺失值是任何机器学习问题的良好开端。然后,我们可以删除超出我们定义的阈值的列。
# check missing values
train.isnull().any().any()False
不幸的是,对于我们的降维工作来说,这个数据集没有缺失值。
移除方差较小的要素
在 sklearn 的特征选择模块中我们找到了VarianceThreshold。它会移除方差未达到某个阈值的所有要素。默认情况下,它会移除方差为零的要素或所有样本值相同的要素。
from sklearn import feature_selection
sel = feature_selection.VarianceThreshold()
train_variance = sel.fit_transform(train)
train_variance.shape(250, 302)
比赛描述说我们的特征都是连续的。从上面我们可以看到,所有列中没有具有相同值的特性,因此我们在这里没有要删除的特性。
我们可以随时重新审视这一技术,并考虑移除方差较低的特征。
移除高度相关的要素
高度相关或共线的特征会导致过度拟合。
当一对变量高度相关时,我们可以删除其中一个以降低维度,而不会丢失太多信息。我们应该保留哪一个?与目标相关性更高的那个。
让我们探索一下我们的特征之间的相互关系:
# find correlations to target
corr_matrix = train.corr().abs()print(corr_matrix['target'].sort_values(ascending=False).head(10))target 1.000000
33 0.373608
65 0.293846
217 0.207215
117 0.197496
91 0.192536
24 0.173096
295 0.170501
73 0.167557
183 0.164146
这里我们看到了与我们的目标变量高度相关的特征。特征 33 与目标的相关性最高,但是相关性值只有 0.37,所以相关性很弱。
我们还可以检查功能与其他功能的相关性。下面我们可以看到一个相关矩阵。看起来我们的特征没有一个高度相关。

Correlation Matrix
让我们尝试删除相关值大于 0.5 的要素:
# Find index of feature columns with high correlation
to_drop = [column for column in matrix.columns if any(matrix[column] > 0.50)]
print('Columns to drop: ' , (len(to_drop)))Columns to drop: 0
使用高度相关的特性,我们没有要删除的列。让我们继续探索其他策略。
单变量特征选择
单变量特征选择通过基于单变量统计测试选择最佳特征来工作。
我们可以使用 sklearn 的 SelectKBest 来选择一些要保留的特性。该方法使用统计测试来选择与目标具有最高相关性的特征。这里我们将保留前 100 个功能。
from sklearn.feature_selection import SelectKBest, f_classif*# feature extraction*
k_best = SelectKBest(score_func=f_classif, k=100)*# fit on train set*
fit = k_best.fit(X_train, y_train)*# transform train set*
univariate_features = fit.transform(X_train)
递归特征消除
递归特征选择通过消除最不重要的特征来工作。它以递归方式继续,直到达到指定的特征数。递归消除可用于任何通过coef_或feature_importances_为特征分配权重的模型
这里,我们将使用随机森林来选择 100 个最佳功能:
from sklearn.feature_selection import RFE# feature extraction
rfe = RFE(rfc, n_features_to_select=100)# fit on train set
fit = rfe.fit(X_train, y_train)# transform train set
recursive_features = fit.transform(X_train)
使用SelectFromModel功能选择
像递归特征选择一样,sklearn 的SelectFromModel与任何具有coef_或feature_importances_属性的估计器一起使用。它会移除值低于设定阈值的要素。
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier# define model
rfc = RandomForestClassifier(n_estimators=100)# feature extraction
select_model = feature_selection.SelectFromModel(rfc)# fit on train set
fit = select_model.fit(X_train, y_train)# transform train set
model_features = fit.transform(X_train)
主成分分析
PCA(主成分分析)是一种降维技术,它将数据投影到一个更低维的空间中。
虽然有许多有效的降维技术,主成分分析是我们在这里探讨的唯一例子。
PCA 在许多情况下都很有用,但特别是在多重共线性过大或预测因子的解释不重要的情况下。
这里我们将应用 PCA 并保留 90%的方差:
from sklearn.decomposition import PCA
# pca - keep 90% of variance
pca = PCA(0.90)principal_components = pca.fit_transform(X_train)
principal_df = pd.DataFrame(data = principal_components)print(principal_df.shape)(250, 139)
我们可以看到剩下的 139 个特征解释了我们数据中 90%的差异。
结论
特征选择是任何机器学习过程的重要部分。在这里,我们探讨了几种有助于提高模型性能的特征选择和降维方法。
Airbnb 柏林数据集上的特征选择和回归
数据科学的多学科领域可以从结构化和非结构化数据中提取深刻的见解和知识。它统一了统计学、数据分析和机器学习等相关概念,用数据来理解和分析实际现象。机器学习(ML)使计算机能够在没有明确编程的情况下进行学习和解释。ML 方法广泛应用于金融、医疗保健、材料科学、运输、石油和天然气等领域。
在这项研究中,我们对柏林地区的 Airbnb 数据集[1]进行了分析,并使用回归分析预测了 Airbnb 的价格。我们使用了基于 python 的开源数据分析平台的“sci kit-learn”[3]库中可用的随机森林回归[2]模型,这些图是使用 Matplotlib 创建的。
Airbnb 数据集的形状是(22552,96)。这 96 个要素中有许多是非数字要素,并且许多要素有缺失值。使用标签编码器将一些非数字特征转换为数字特征。使用正向填充方法填充缺失值,并从“$”、“、”等中清除某些要素。使用条带法。此外,我们使用 Airbnb 的经度和纬度创建了 Airbnb 到柏林中心的新特征“距离”。这个新要素随后被追加到原始数据集中。数据处理完成后,我们使用 scikit-learn 中的“功能选择”来选择重要的功能。我们计算每个特征和目标之间的 χ2 ,并选择具有最佳 χ2 分数的期望数量的特征。最佳的 25 个特征和 χ2 分数在下面的表 1 中列出。
**Table 1\. Best 25 features of the dataset and their corresponding *χ2* scores.
Features χ2 scores**
maximum_nights 2.152729e+10
minimum_nights 8.856912e+05
security_deposit 7.716334e+05
number_of_reviews 8.341015e+04
square_feet 2.982368e+04
property_type 2.222414e+04
calculated_host_listings_count 1.072021e+04
neighbourhood_cleansed 1.071895e+04
accommodates 7.771539e+03
room_type 4.234147e+03
neighbourhood 3.254091e+03
guests_included 3.061216e+03
bedrooms 2.380380e+03
cancellation_policy 1.444670e+03
distance 9.580979e+02
host_is_superhost 6.349556e+02
neighbourhood_group_cleansed 5.957898e+02
instant_bookable 3.502228e+02
bathrooms 3.290317e+02
review_scores_rating 2.151681e+02
is_location_exact 9.053878e+01
review_scores_cleanliness 5.194306e+01
review_scores_accuracy 2.410470e+01
bed_type 7.055763e+00
longitude 1.016177e-01
接下来,只选择了前五个特性。这个子集被分成训练和测试数据。“价格”被设定为目标特征。训练数据子集用于拟合随机森林,而测试子集用于预测。通过使用包括前十、前十五、前二十和前二十五个特征的不同子集来重复这一过程。对于每种情况,我们计算的值定义为
**adjusted R^2 = 1-R^2(N-1)/(N-M-1)**
其中 N 是数据集中的点数,M 是独立要素的数量,而是拟合优度的测量值。随着模型特征数量的增加而增加。因为总是增加而从不减少,所以随着更多的术语被添加到模型中,它可能看起来更适合。在回归分析中,向数据中添加更多的变量是很诱人的,这可能会产生误导。其中一些变量可能是重要的,但人们不能确定这种重要性只是偶然的。通过对那些额外变量进行惩罚来对此进行补偿。相应的曲线如图 1 所示。

图一。图中的特征数和相应的调整后的 R
结论:
通过图表进行探索性数据分析(EDA)无疑是有用和重要的。然而,对于大型数据集,采用 EDA 变得极其困难,因为有大量的图要分析。因此,通过 scikit learn 的 SelectKBest 方法进行的特性选择提供了一种定义良好的特性选择方式,如这里所示。
通过过滤选择特征:什么会出错?(剧透:很多)
如何通过单变量过滤正确(和不正确)执行特征选择的说明
从计算机科学到心理学,处理具有比观察数量多得多的特征的数据集现在是许多领域的共同之处。这通常被称为“p > n”问题(其中 p =特征数,n =观察数),“维数灾难”,或者我个人最喜欢的“短胖数据”。
传统方法,如线性回归,在这种情况下会失效。我们可以在一个玩具示例中观察到这一点,在这个示例中,我们删减了“mtcars”数据集,以生成#行< # of columns. When trying to calculate beta coefficients, we run into singularity problems.
Feature Selection
A common strategy to deal with this issue is to perform feature selection where the aim is to find a meaningful subset of features to use in model construction. There are many different ways to carry out feature selection (for a summary check out this 概览。这里,我们将关注最简单的变体,即单变量过滤器的特征选择。与其他特征选择策略一样,单变量过滤器的目的是找到与结果相关的特征子集。这可以通过将特征与结果相关联并仅选择那些满足特定阈值的特征(例如, r >的绝对值)以非常直接的方式来实现。
为什么专注于单变量过滤器进行特征选择?
正如在统计学习的元素 (ESL)和 2002 年的论文、中指出的,许多发表的作品都错误地执行了特征选择。毫无疑问,这将继续是一个问题,因为随着研究人员努力处理更大的数据集,p > n 数据在生物、社会科学和健康领域继续变得更加常见。这并不奇怪为什么过滤通常是不正确的,乍看起来,简单地删除数据集中与结果无关的特征似乎并没有什么错。
为了说明通过过滤进行的特征选择是如何错误地和正确地完成的,我们将首先生成一个简短、丰富的数据集,其中我们的预测器在很大程度上与我们的因变量“y”无关。关于模型拟合,我们将基本上按照第 245–247 页的 ESL 进行,并通过一个例子进行编码,看看这些数字是如何产生的。首先,让我们生成一些基本正交的数据。
单变量过滤错误的方式
现在我们有了数据,我将通过筛选交叉验证的之外的数据来说明如何错误地过滤数据。也就是说,通过获取整个数据集,保持与结果相关的特征等于或高于 r = .1,并且随后建立模型。对于 100 次迭代,我们将把这个数据集分成训练集和测试集,并检查观察值和预测值之间的相关性分布。

Distribution of outcomes for the incorrectly filtered model. The thick red line indicates the mean.
总的来说,这些模型看起来很棒,观察值和预测值之间的平均相关性大约为 0.5。唯一的问题是,模拟数据在很大程度上是独立的,平均相关性应该在 0 左右!我们通过选择基于所有数据的变量引入了偏差。在变量选择后实施验证方案并不能准确描述模型在真正独立的测试数据集上的应用。现在让我们观察如何通过正确过滤来执行选择。
正确的单变量过滤方法
当正确执行单变量过滤时,特征选择发生在外部交叉验证循环中,而模型调整发生在内部循环中。如果我前面提到的样本有偏差是真的,我们应该会在内部验证循环中看到类似的高相关性,但在外部循环和后续遗漏的样本中完全缺乏泛化能力。
我们将在 SBF 的 caret 中使用一个函数来实现这一点。在“sbfControl”中对控制滤波的外部验证回路进行说明,而在“trainControl”中对参数调整进行正常说明。对于连续的结果和特征,caret 利用广义加法模型将结果与每个特征联系起来——这是一个很好的接触,可以发现非线性关系。这些模型中的每一个的 p 值被用作过滤标准。默认值设置为 p = .05,这意味着只有当特征与该级别的结果有显著关系时,才会保留这些特征。

A closer at whats happening during the nested validation for filtering. Thick lines indicate the distribution mean.
正如预期的那样,我们在内部训练循环中看到了很强的关系,但在外部训练循环中以及当我们最终将我们的模型拟合到遗漏的数据时,我们观察到了本质上正交的预测(平均值 r 正好在 0 附近)。如果你查看第 246 页的 ESL ,你会注意到类似的结果模式。
与 Glmnet 的比较
最后,作为质量保证检查,我们将把我们的结果与正则化模型(一种添加了参数以防止过度拟合的模型)进行比较,正则化模型应该给出与正确的单变量过滤方法几乎相同的结果。

Distribution of outcomes for the glmnet models. The thick green line indicates the mean.
不出所料,平均 r right 在 0 左右。
在结束之前,我们将最后快速浏览一次,并排查看正确的过滤、不正确的过滤和 glmnet 模型,注意不正确的过滤会对预测产生多大的偏差。

Resulting distributions of the three approaches
包装完毕
总之,如果您决定使用单变量过滤进行特征筛选,请确保在交叉验证方案中执行特征选择**,而不是在整个数据集上执行**。这样做将导致特征已经“见证”了遗漏的数据,因此不能有效地模拟将模型应用到真实的测试集。需要注意的一点是,如果没有考虑结果,这并不一定适用于筛选特征。例如,删除以下要素:彼此高度相关、不包含方差或者是其他要素集的线性组合。最后,根据您的需要,还值得记住的是,有许多防止过度拟合和/或本质上执行特征选择的正则化方法可能是比任何用于特征选择的包装器或过滤器方法更好的选择(并且更快)。**
Python 中随机搜索的特征选择
如何在 Python 中使用随机搜索进行要素选择

Photo by Ian Gonzalez on Unsplash
特征选择一直是机器学习的一大任务。根据我的经验,我可以肯定地说,特征选择比型号选择本身更重要。
特征选择和共线性
我已经写了一篇关于功能选择的文章。这是一种在二元分类模型中测量特征重要性的无监督方式,使用皮尔逊卡方检验和相关系数。
一般来说,对于简单的特征选择,无监督的方法通常是足够的。然而,每个模型都有自己的方式来“思考”这些特征,并处理它们与目标变量的相关性。而且,还有不太在意共线性(即特征之间的相关性)的模型,以及其他出现非常大问题的模型(例如线性模型)。
尽管可以通过模型引入的某种相关性度量标准(例如,对线性回归系数执行的 t-test 的 p 值)对特性进行排序,但仅采用最相关的变量是不够的。想想一个等于另一个的特征,只是乘以 2。这些特征之间的线性相关性如果 1 和这个简单的乘法不影响与目标变量的相关性,所以如果我们只取最相关的变量,我们就取原始特征和相乘的那个。这导致了共线性**,这对我们的模型来说是相当危险的。**
这就是为什么我们必须引入一些方法来更好地选择我们的功能。
随机搜索
随机搜索是数据科学家工具箱中非常有用的工具。这是一个非常简单的技术,经常使用,例如,在交叉验证和超参数优化中。
很简单。如果你有一个多维网格,并想在这个网格上寻找使某个目标函数最大化(或最小化)的点,随机搜索工作如下:
- 在网格上随机取一点,测量目标函数值
- 如果该值比迄今为止达到的最佳值更好,则将该点保存在内存中。
- 重复预定的次数
就是这样。只是产生随机点,并寻找最好的一个。
这是寻找全局最小值(或最大值)的好方法吗?当然不是。我们寻找的点在一个非常大的空间中只有一个(如果我们幸运的话),并且我们只有有限的迭代次数。在一个 N- 点网格中得到那个单点的概率是 1/N 。
那么,为什么随机搜索会被这么多使用呢?因为我们从来没有真正想要最大化我们的绩效评估;我们想要一个好的,合理的高值,它不是可能的最高值,以避免过度拟合。
这就是为什么随机搜索是可行的,并且可以用于特征选择。
如何使用随机搜索进行特征选择
随机搜索可用于特征选择,结果相当好。类似于随机搜索的程序的一个例子是随机森林模型,它为每棵树执行特征的随机选择。
这个想法非常简单:随机选择特性**,通过 k 倍交叉验证测量模型性能,并重复多次。提供最佳性能的功能组合正是我们所寻求的。**
更准确地说,以下是要遵循的步骤:
- 生成一个介于 1 和特征数之间的随机整数 N 。
- 生成一个 0 到 N-1 之间的 N 整数随机序列,不重复。这个序列代表了我们的特征阵列。记住 Python 数组是从 0 开始的。
- 在这些特征上训练模型,并用 k-fold 交叉验证对其进行交叉验证,保存一些性能测量的平均值。****
- 从第 1 点开始重复,重复次数不限。
- 最后,根据所选择的性能度量,获得给出最佳性能的特征阵列。
Python 中的一个实际例子
对于这个例子,我将使用包含在 sklearn 模块中的乳腺癌数据集。我们的模型将是一个逻辑回归,我们将使用准确性作为性能测量来执行 5 重交叉验证。
首先要导入必要的模块。
**import sklearn.datasets
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
import numpy as np**
然后我们可以导入乳腺癌数据,并将其分解为输入和目标。
**dataset= sklearn.datasets.load_breast_cancer()
data = dataset.data
target = dataset.target**
我们现在可以创建一个逻辑回归对象。
**lr = LogisticRegression()**
然后,我们可以测量所有特征在 k 倍 CV 中的平均准确度。
**# Model accuracy using all the features
np.mean(cross_val_score(lr,data,target,cv=5,scoring="accuracy"))
# 0.9509041939207385**
是 95%。让我们记住这一点。
现在,我们可以实现一个随机搜索,例如,300 次迭代。
**result = []# Number of iterations
N_search = 300# Random seed initialization
np.random.seed(1)for i in range(N_search):
# Generate a random number of features
N_columns = list(np.random.choice(range(data.shape[1]),1)+1)
# Given the number of features, generate features without replacement
columns = list(np.random.choice(range(data.shape[1]), N_columns, replace=False))
# Perform k-fold cross validation
scores = cross_val_score(lr,data[:,columns], target, cv=5, scoring="accuracy")
# Store the result
result.append({'columns':columns,'performance':np.mean(scores)})# Sort the result array in descending order for performance measure
result.sort(key=lambda x : -x['performance'])**
在循环和排序函数结束时,结果列表的第一个元素就是我们要寻找的对象。
我们可以使用这个值来计算这个特性子集的新性能度量。
**np.mean(cross_val_score(lr, data[:,result[0][‘columns’]], target, cv=5, scoring=”accuracy”))
# 0.9526741054251634**
如您所见,精确度提高了。
结论
随机搜索可以是执行特征选择的强大工具。它并不意味着给出为什么一些特性比其他特性更有用的原因(相对于其他特性选择过程,如递归特性消除),但它可以是一个有用的工具,可以在更短的时间内达到好的结果。
自动化人工智能的特征选择评估
如何为特征选择算法设置自动选择

特征选择算法寻求在数据中有效且高效地找到相关特征的最优子集。随着要素数量和数据集大小的增加,这一预处理步骤变得至关重要。在本帖中,我们将展示我们在 TapReason 开发的独特自动流程,用于评估特定数据集的不同特征选择算法的有效性。在第一和第二部分,我们解释了执行特征选择的动机,并回顾了常见的特征选择方法(FSM)。第三部分描述了我们在研究中使用的方法及其结果。在第四部分,我们提出了 TapReason 算法来评估和选择特征选择算法。
动机
在机器学习和模式识别中,特征是正在观察的现象的单个可测量的属性或特征。收集和处理数据可能是一个昂贵且耗时的过程。因此,选择信息丰富的、有区别的和独立的特征是有效算法的关键步骤。
在我们生活的信息时代,数据集的规模在实例数量和可用特性数量上都变得越来越大。在许多不同的领域中,拥有数万个或更多要素的数据集已经变得非常常见。此外,在许多情况下,算法开发人员通过使用称为特征生成的过程来增加可用特征的数量。要素生成是从一个或多个现有要素创建新要素的过程,以便在统计分析中使用。通常,特征生成过程将现有特征的表示转换成新的更复杂的表示,这将有助于算法更有效地执行。该过程显著增加了特征的数量,尤其是如果它是自动完成的话。以如此多的特征结束的问题是,一些算法在多个维度上表现很差,并且在一些维度上,它可能导致过度拟合。
对于高维数据,通常许多特征对于给定的学习任务是不相关的或冗余的,在性能或计算成本方面具有有害的后果(“维数灾难”)。特征选择是降维、去除无关数据、提高学习精度、提高结果可理解性的有效方法【1】。
由于一些预测模型(PM)与特定的特征选择方法一起工作得更好,我们需要仔细选择 FSM 以最好地适合 PM。在 TapReason 中,人工智能算法会自动选择预测模型。因此,我们需要找到一个能够有效支持我们使用的所有 PM 的特征选择算法。此外,我们还提出了如何衡量特征选择效率的问题。我们如何知道我们在选择功能方面越来越好?以及如何利用积累的知识来改进 FS 算法?
常见特征选择方法
许多特征选择方法已经被提出并研究用于机器学习应用。它们可以分为四大类:嵌入式、包装器、过滤器和混合方法【1】。
过滤方法
滤波方法是预处理方法,独立于学习算法,具有很好的通用性。他们试图从数据中评估特征的优点,忽略了所选特征子集对学习算法性能的影响【1】。它们的计算复杂度很低,但是学习算法的精度不能保证得到提高。例如,一些方法是根据方差、相关性、单变量特征选择(基于单变量统计测试的选择,例如 chi2 测试、F 值)、通过压缩技术(例如 PCA)或通过计算与输出的相关性(例如 Gram-Schmidt、互信息)来对特征进行排序。
包装方法
包装方法是根据变量子集对给定预测器的有用性来评估变量子集的方法。这些方法使用预定学习算法的预测准确性来确定所选子集的良好性。这些方法的两个主要缺点是:
- 当观察次数不足时,过拟合风险增加。
- 当变量数量很大时,计算时间很长。
例如,在线性回归分析中提出了逐步方法,如人工神经网络的 AIC、BIC 和 MSE。
嵌入式方法
嵌入式方法将特征选择作为训练过程的一部分,并且通常特定于给定的学习算法,因此可能比前两类更有效。像决策树或人工神经网络这样的传统机器学习算法就是嵌入式方法的例子。
混合方法
提出了混合方法来结合过滤器和包装器的最佳特性。首先,使用过滤方法来减少特征维度空间,这将被后续的包装器考虑。然后,使用包装器来寻找最佳候选子集。混合方法通常实现包装器特有的高精度和过滤器特有的高效率【2】。在 TapReason 中,我们使用混合方法过滤器并嵌入到各种组合中,以自动找到最适合手头问题的特征选择方法。
评估有限状态机有效性的方法
对于研究预测方法对特征选择方法的反应的过程,我们选择了九种 FSM 和九种预测方法(PM ),并计算预测的均方对数误差。在表 1 中,您可以看到不同 PM 的均方对数误差与 FSM 的相关性。每一列的颜色分别从绿色(最好)到红色(最差)。根据表 1,对于大多数 PM,使用低方差的 FSM 是最差的 FSM。根据表 1,朴素贝叶斯是最差的 PM。全非零 FSM 是由至少一个 FSM 选择的所有特征的组合。所有 5 个非零 FSM 是由至少 5 个 FSM 选择的所有特征的组合。

Table 1: feature selection method Vs prediction methods. Image by Author.
有几种方法可以分析表 1 所示的结果,以评估不同的特性选择方法。例如:
- 绝对最小值、最佳均方对数误差 FSM/PM。在表 1 中,最佳结果是用于随机森林回归的梯度推进(FSM)。
- 对列中得分最高的 FSM 进行排名,chi 测试,LassoCV,所有 5 个非零特征,都获得了两次最高分。
- 对 FSM 得分的平均值进行排名。在表 1 中,它是所有 5 个非零功能的 FSM。
上述每种方法都指向不同的最优 FSM。因此我们引入了第四种方法:
4.最小𝛥-在要素选择算法中描述。这种方法显示出最有希望的结果。
在表 2 中,我们显示了 PM 的均方对数误差和最小均方对数误差之间的𝛥。使用 FSM 行 X 1000 的平均值分析结果表,在 LSV: L1 产生最小值。可以看到,每一列都有一个 FSM 为零,这是 PM 的最小均方对数误差。FSM、LassoCV 和所有 5 个非零具有 PM ( FSM)的最小均方对数误差中的三个。Min )但是他们都没有最小的平均𝛥.FSM LSV: L1 具有最小的平均𝛥.

Table 2: The FSM/PM table according 𝛥 from the Minimum. Image by Author.
根据这一分析,选择 Lasso 回归 FSM,因为它在所有 PMs 中对 LSV: L1 具有最好的均方对数误差。0.0384782765 的结果比较接近绝对最小值(绝对值)。Min) 最适为 0.03735447816。但是进一步的研究发现,结果相对较差的 PM 倾向于使选择过程偏向对它们表现最好的算法。因此,我们决定通过根据它们与绝对最小值(绝对值)的距离调整 PMs 的权重来抵消这种影响。最小。根据以下等式计算距离:

*密克罗尼西亚联邦。min-*FSM 的最小均方对数误差。
绝对。Min- 最小均方对数误差在 FSM/PM 表中。
下午。Min- 预测方法的最小均方对数误差。
PM- 最小均方对数误差。

Table 3: The FSM/PM table according to equation 1. Image by Author.
在表 3 中,我们可以看到这个例子的最终结果没有变化。但是现在 FSM 用更高的 FSM。Min 对平均值的影响(权重)较小,因此对所选的 FSM 的影响较小。这样,我们给了所有 FSM 一个机会,但不会因为大𝛥而使结果偏离最小值。
该算法
在 TapReason,我们使用几十种不同的有限状态机来搜索每个问题的最佳算法。这些 FSM 包括各种各样的滤波器、嵌入式方法以及基于过去性能构建的集合 FSM。针对每个问题,针对每个客户端定期离线更新功能选择方法。实现的算法如下:

因此,很少选择以前的方法。
未来的研究
我们知道,某些预测方法与某些特征选择方法一起使用效果更好。由于在所呈现的过程中,首先选择 FSM,所以在该过程中不表达 PM 偏好。因此,我们可以为每个 PM 收集最佳 FSM 的信息,并在选择 PM 后使用它。
[1]http://www.ijarcst.com/conference/first/conf8.pdf
[2]https://bib.irb.hr/datoteka/763354.mi pro _ 2015 _ jovicbrkicbogunovic . pdf
机器学习的特征选择(1/2)

https://images.app.goo.gl/JMcX4RLACqLSuhMw8
特征选择,也称为变量选择,是一个强大的想法,对您的机器学习工作流有重大影响。
你为什么需要它?
嗯,你喜欢把你的功能数量减少 10 倍吗?或者如果做 NLP,甚至 1000x。除了更小的特征空间,导致更快的训练和推理,还能在准确性上有可观察到的改进,或者你为你的模型使用的任何度量?如果这还不能引起你的注意,我不知道还有什么可以。
不相信我?几天前我工作时就遇到过这种事。
所以,这是一篇由两部分组成的博文,我将解释并展示如何在 Python 中进行自动特征选择,这样你就可以升级你的 ML 游戏。
将只介绍过滤器方法,因为它们比包装器方法更通用,计算量更小,而嵌入特征选择方法嵌入在模型中,不如过滤器方法灵活。
留在原处等候😉
第二部分可以访问这里
首先,最基本的
所以,你需要为你的模型找到最强大的,也就是重要的特性。我们将断言在一个重要的特征和目标变量之间有一个有意义的关系(不一定是线性的,但稍后会有更多的介绍),这类似于target ~ f(feature)。最简单的关系是线性关系,识别这种关系的强大工具是相关性。相关性意味着两个变量之间的关联或依赖,这正是我们所需要的。有很多方法来计算它,但是考虑到这篇博文的目的是密集地提供实用的建议,这里有“最简单的”2: Spearman 和 Pearson 方法,在熊猫身上
>>> # shamelessly taken from here: [https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.corr.html](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.corr.html)
>>> df = pd.DataFrame([(.2, .3), (.0, .6), (.6, .0), (.2, .1)],
... columns=['dogs', 'cats'])
>>> # df.corr(method="spearman")
>>> df.corr(method="pearson")
dogs cats
dogs 1.0 0.3
cats 0.3 1.0
因此,一旦计算出来,我们就可以得到指数,并使用它们来选择高度相关的特征,这些特征将进一步用于模型训练。在实践中,如果变量之间确实有显著的线性相关性,它应该在 0.7 以上。这些相关性测试的好处在于,在实践中,这些测试非常稳健,有时甚至可以识别非线性相关性,如果可能的话,可以用一条线进行局部近似,例如,二阶多项式相关性,或对数和平方根相关性,甚至指数相关性。相关系数会更小,可能在 0.5 到 0.7 之间。当得到这样的值时,打开 EDA 模式并绘制这些值,也许你可以发现一些依赖关系。我有时会。
pearson = pd.concat([features_df, target_df], axis=1).corr(method="pearson")
indices = pearson[abs(pearson["prediction"]) > 0.55].index
另一种方法是使用 chi2 测试。大概是这样的:
chi_sq = feature_selection.chi2(X, y)
corr_table = pd.DataFrame(zip(*chi_sq), columns = ("Correlation", "P-value"))
top_features = corr_table.sort_values("Correlation", ascending=False).head()["Correlation"]
最后一点,您需要记住:通常,数据集中的要素不仅与目标变量相关,而且它们之间也相关。**你不要这个!**在选择特征时,您(或算法)应尽可能选择最少数量的最重要的特征,这些特征之间尽可能正交/不相关。在第二篇博文中,将会介绍一些实现这一点的方法,敬请关注。
更大的枪:特征重要性和基于模型的选择
好的,很多这些方法都是基于统计学的,这很好,但是有时候,你只需要少一点形式主义,多一点科学知识。
scikit-learn 中的一些模型具有coef_或feature_importances_属性。一旦这些模型被训练,属性就被填充了对于特征选择非常有价值的信息。这里有两个例子,使用决策树和 L1 正则化。
基于决策树的方法
feature_importance_tree = tree.DecisionTreeClassifier()
feature_importance_tree.fit(X, y)
feature_importance_list = feature_importance_tree.feature_importances_.tolist()
indices = zip(*sorted(enumerate(feature_importance_list), key=lambda x: x[1], reverse=True)[:5])[0]
X_tree = X[:, indices]
scores = [model.fit(X_tree[train], y[train]).score(X_tree[test], y[test]) for train, test in kfcv]
既然我引起了你的注意,让我解释一下。决策树是非常好的工具,具有高度的可解释性,事实证明,它不仅仅在分类/回归问题上有用。在这个例子中,一个DecisionTreeClassifier被快速拟合到一个数据集上,然后feature_importances_被用来挑选最相关的特征,并训练一个更大、更复杂、更慢的模型。在实践中,如果您有大量数据,您可能会选择 next 方法的变体,但是对于较小的数据,这种方法非常好,能够捕获具有非线性依赖关系的特征。此外,ExtraTreesClassifier也可以很好地处理更大的数据,如果正则化(更浅的树,每片叶子更多的样本)甚至更好。永远要实验。
基于 L1 方法
对于那些还不知道的人来说,L1 正则化,由于它的性质,在模型中引入了稀疏…这正是我们所需要的,真的!
clf = linear_model.LassoCV()
sfm = feature_selection.SelectFromModel(clf, threshold=0.002)
sfm.fit(X, y)
X_l1 = sfm.transform(X)
scores = [model.fit(X_l1[train], y[train]).score(X_l1[test], y[test]) for train, test in kfcv]
与上面的树示例一样,训练了一个小模型,但与示例不同的是,coef_是驱动 sklearn 实现的特征选择的因素。因为使用了 L1,模型中的大多数系数将是 0 或接近于 0,所以任何更大的系数都可以算作一个重要特征。这适用于线性依赖关系。对于非线性模型,可以尝试 SVM,或者在线性模型之前使用 sklearn 的RBFSampler。
关于性能和大数据集的重要说明。
说你有 NLP 问题,用 TF-IDF。在一个合理的数据集上,你会有一个巨大的输出矩阵,类似于几千行(文档)和几百万列(n 元语法)。在这样的矩阵上运行任何模型都是耗时耗内存的,所以你最好使用一些速度快的东西。在这种情况下,我肯定会推荐 L1 的方法,但是用SGDClassifier(penalty="l1")代替LassoCV。这两种方法几乎是等效的,但是在大型数据集上,后者的运行速度几乎快一个数量级。所以要牢记在心。
此外,请记住,在收敛之前,您不需要训练您的要素选择模型,您不会将它们用于预测,并且最相关的要素将在模型中首先被选择。
收场白
这里大部分代码来自我在 GitHub 上的一个老项目,这里。这应该行得通,但如果行不通,不要害羞——HMU。
请注意,这篇博文和下一篇博文中的所有特征选择都适用于特征向量。这意味着,这里没有方法可以应用于视觉问题,例如,除非你想减少 CNN 最后一层的“功能”,这可能是一个好主意,idk。
此外,记住这一点,没有免费的午餐——想一想你准备做出哪些取舍,选择几个方法,进行实验,选择最适合你的问题的方法。
如果你正在读这篇文章,我想感谢你,并希望上面写的对你有很大的帮助,就像对我一样。请在评论区让我知道你的想法。你的反馈对我很有价值。
特征选择:识别最佳输入特征
在本文中,我们将了解什么是特征选择,特征选择和降维的区别。特征重要性如何帮助?了解不同的技术,如过滤方法、包装器和嵌入方法,以便用 Python 代码识别最佳特性。

什么是特征选择?
特征选择也被称为属性选择或变量选择,是特征工程的一部分。它是在数据集中选择最相关的属性或特征子集进行预测建模的过程。
在我们讲述一些机器学习金融应用之前,我们先来了解一下什么是机器学习。机器…
www.datadriveninvestor.com](https://www.datadriveninvestor.com/2019/02/08/machine-learning-in-finance/)
选定的功能有助于预测模型识别隐藏的业务洞察力。
如果我们需要预测 IT 人员的工资,那么根据我们的共同理解,我们需要工作经验、技能、工作地点和当前职位。这些是有助于薪资预测的几个关键特征。如果数据集包含人的身高,我们知道该特征与工资预测无关,因此不应作为特征选择的一部分。
特征选择是决定包含哪些相关原始特征和排除哪些不相关特征进行预测建模的过程。
特征选择和降维的区别
特征选择和降维的目标是减少数据集中属性或特征的数量。
特征选择和降维的关键区别在于,在特征选择中,我们不改变原始特征,然而,在降维中,我们从原始特征中创建新特征。这种使用降维的特征转换通常是不可逆的。
特性选择基于某些统计方法,比如我们将在本文中讨论的过滤器、包装器和嵌入式方法。
对于降维,我们使用像主成分分析(PCA)这样的技术
需要特征选择
- **帮助更快地训练模型:**我们已经减少了相关特征的数量,因此训练要快得多。
- 提高模型解释能力并简化模型 —它通过仅包含最相关的特征来降低模型的复杂性,因此易于解释。这对于解释预测模型非常有帮助
- **提高模型的准确性:**我们只包括与我们的预测相关的特征,这些特征提高了模型的准确性。不相关的特征会引入噪声并降低模型的准确性
- **减少过度拟合:**过度拟合是指预测模型不能很好地概括测试数据或基于训练的未知数据。为了减少过度拟合,我们需要去除数据集中的噪声,并包括对预测影响最大的特征。噪声来自数据集中不相关的特征。当预测模型作为训练的一部分已经学习了噪声时,那么它将不会很好地对看不见的数据进行概括。
不同的特征选择方法
- 过滤器
- 包装材料
- 嵌入式方法
特征选择的过滤方法
过滤方法基于某个单变量度量对每个特征进行排名,然后选择排名最高的特征。一些单变量指标是
- **方差:**去除常数和准常数特征
- **卡方:**用于分类。这是一种独立性的统计测试,用于确定两个变量的相关性。
- **相关系数:**删除重复特征
- 信息增益或互信息:评估自变量在预测目标变量时的相关性。换句话说,它决定了独立特征预测目标变量的能力。
过滤方法的优点
- 过滤方法是模型不可知的
- 完全依赖数据集中的特征
- 计算速度非常快
- 基于不同的统计方法
过滤方法的缺点
- 过滤方法着眼于单个特征,以确定其相对重要性。一个特性本身可能没什么用,但是当它与其他特性结合起来时,可能是一个重要的影响因素。过滤方法可能会遗漏这些特征。
选择最佳特征的过滤标准
使用选择独立特征
- 与目标变量高度相关
- 与其他独立变量相关性低
- 自变量的较高信息增益或互信息
特征选择的包装方法
包装器方法搜索输入要素的最佳子集来预测目标变量。它选择提供模型最佳精度的特征。包装器方法使用基于先前模型的推理来决定是否需要添加或删除新特性。
包装方法有
- **穷举搜索:**评估输入特征的所有可能组合,以找到为所选模型提供最佳准确度的输入特征子集。当输入特征的数量变大时,计算开销非常大;
- **正向选择:**从一个空特征集开始,并保持一次添加一个输入特征,并评估模型的准确性。这个过程一直持续到我们用预定数量的特征达到一定的精度;
- **逆向选择:**从所有特征开始,然后保持一次移除一个特征,以评估模型的准确性。保留产生最佳准确度的特征集。
总是在测试数据集上评估模型的准确性。
优势
- 对每个输入要素之间的要素依赖性进行建模
- 取决于所选的型号
- 基于特征子集选择精度最高的模型
缺点:
- 计算非常昂贵,因为训练发生在每个输入特征集组合上
- 不依赖模型
特征选择的嵌入式方法
嵌入式方法使用过滤器和包装器特征选择方法的质量。特征选择嵌入在机器学习算法中。
过滤方法不包括学习,只涉及特征选择。包装器方法使用机器学习算法来评估特征子集,而不包含关于分类或回归函数的特定结构的知识,因此可以与任何学习机结合
嵌入式特征选择算法包括
- 决策树
- 正则化——L1(拉索)和 L2(山脊)正则化
通过拟合模型,使用这些机器学习技术。这些方法为我们提供了更好的准确性的特征重要性。
点击阅读更多关于 L1 和 L2 合法化的信息
在下一篇文章中,我们将使用过滤方法 在 python 中实现一些特征选择方法
参考资料:
http://ijcsit . com/docs/Volume % 202/vol 2 issue 3/ijcsit 2011020322 . pdf
https://arxiv.org/pdf/1907.07384.pdf
http://people.cs.pitt.edu/~iyad/DR.pdf
https://link . springer . com/chapter/10.1007% 2f 978-3-540-35488-8 _ 6
Python 中的要素选择—递归要素消除
寻找用于机器学习模型训练的最佳特征有时可能是一项难以完成的任务。我并不是说这个过程本身很难,只是有太多的方法可供选择。你可能已经读过很多像主成分分析这样的方法,它绝不是一个坏方法,但它不会告诉你哪些特征是最重要的——它会返回主成分,这些主成分实际上是特征的组合(用最简单的话来解释)。

为了解决这个问题,出现了递归特征消除技术。在本文中,我将讨论带交叉验证的递归特性消除( RFECV ),因为它比不带交叉验证的选项更常用。首先,让我们讨论一些术语:
递归——为了产生特定的结果或效果,多次做或说同样的事情【1】(只要谷歌一下‘递归’这个词,你马上就能找到它的要点)
特征 —被观察现象的个体可测量属性或特征[2] —数据集中的属性
交叉验证 —一种评估 ML 模型的技术,通过在可用输入数据的子集上训练几个 ML 模型,并在数据的互补子集上评估它们。使用交叉验证来检测过度拟合,即未能归纳出一个模式[3]
好了,现在解释了一些基本术语,是时候简要解释一下 RFE 背后的想法了。我会说这个街区完美地解释了它:
如前所述,递归特征消除(RFE,Guyon 等人( 2002 ))基本上是预测器的向后选择。该技术首先在整个预测因子集上建立一个模型,并计算每个预测因子的重要性分数。然后移除最不重要的预测值,重新构建模型,并再次计算重要性分数。在实践中,分析师指定要评估的预测值子集的数量以及每个子集的大小。因此,子集大小是 RFE 的调整参数。优化性能标准的子集大小用于基于重要性排名选择预测器。然后,最佳子集用于训练最终模型。[4]
说到它背后的理论,基本上就是这样了。当然,仍有一些方面需要讨论,但我不想用一堆理论来抨击你——请自行进一步探索。
好了,现在开始有趣的事情——编码!
1.数据集介绍和准备
首先,让我们讨论一下所使用的数据集。我将使用著名的泰坦尼克号数据集。我之所以选择使用这个数据集,是因为它非常有名,而且很容易清理和准备。和往常一样,你的笔记本要从导入最常见的疑点开始——【Numpy】熊猫Matplotlib。我还从 Scikit-Learn 中导入了random forestsStratifiedKFold和 RFECV 。该模型将通过随机森林进行训练,由于类别不平衡(存活/未存活),将需要分层以确保每个类别的良好代表性:
https://gist.github.com/dradecic/761479ba15e6d371b2303008c614444a#file-rfecv_1_imports-py
现在,您可以读入数据集并检查它的头部:

Head of titanic.csv
以下是关于数据清理需要做的事情:
- PassengerId 和 车票 应该被丢弃——第一个只是一个任意的整数值,第二个对每个乘客都是截然不同的。
- 性别 列的值应重新映射为 0 和 1,而不是“男性”和“女性”
- 从 姓名 中提取人物头衔,如先生、夫人、小姐……等,如果头衔常见(先生、小姐),则进一步转换为 0 和 1-0。),如果不是,则为 1(博士、牧师、上尉)。最后,名名名应该去掉
- 船舱 应替换为 船舱 _ 已知 —如果值为 NaN ,否则为 1
- 虚拟列应该从 开始创建 并且应该删除第一个虚拟列以避免共线性问题
- 年龄 中缺失的值应填入平均值的整数值
下面的代码片段将处理提到的所有问题:
https://gist.github.com/dradecic/2b6c1d81e6089cf6022b36f82b460f4b
完成后,数据集应该是这样的:

Head of cleaned titanic.csv
您的数据集现在已经清理和准备好了,您可以继续下一部分。
2.移除相关特征
RFE 的主要问题是运行起来可能很昂贵——所以你应该事先尽你所能减少功能的数量。移除相关要素是一种很好的方法,因为您可能知道,您不希望数据集中的要素高度相关,因为它们提供相同的信息-一个要素就足够了。
为了解决这个问题,你可以绘制一个相关矩阵,然后记下哪些特征是相关的,并手动删除它们,但总有一个更聪明的方法。我决定与您分享一个简单的片段,它将检查哪些特征的相关系数高于 0.8。由于显而易见的原因,这样做时,您应该总是删除目标变量。不管怎样,这是片段:
https://gist.github.com/dradecic/f8d32045aa886756f59adc1ca50eabd1
如果您现在检查 关联 _ 特性 集合中的内容,您会看到:

Correlated features
这很好,因为该数据集不包含任何相关的要素。在您的工作中,通常您的数据集不会有这么少的属性,其中一些属性可能是相关的,因此这是查找它们的最快方法。
从数据集中删除它们的过程就像调用 一样简单。drop() 和 passingcorrelated _ features作为参数。
3.运行 RFECV
现在有趣的部分终于开始了。您将需要声明两个变量— X 和 target ,其中第一个表示所有特性,第二个表示目标变量。然后你将创建一个机器学习算法的实例(我使用的是 RandomForests )。在其中,您可以有选择地传递一个随机状态种子以获得可再现性。现在你可以创建一个 RFECV 的实例,这里需要参数:
- 估算器 —您的模型实例
- 步骤 —每次迭代要移除的特征数量
- cv —您的交叉验证,使用 StratifiedKFold 并将 K 设置为 10
- 评分 —评分标准,您想要优化的内容。我使用了“准确性”,但您可以选择任何其他选项
它应该是这样的:
https://gist.github.com/dradecic/ce30af3efc6072f18e67f0d54a13f8e7
执行这个单元可能需要一些时间——我的意思是,如果你的电脑是新的,需要测试的功能很少,这可能不会发生,但仍然需要几秒钟。
一旦执行完成,您可以使用这一行代码来查看有多少特性是产生最佳准确度的最佳特性(或者您选择的任何度量):
https://gist.github.com/dradecic/4b27705203dd018168f2eb4ddfeeca79
不仅如此,您还可以绘制出使用每种特性所获得的精度:
https://gist.github.com/dradecic/94305fc88c19976aa64ffec3716d4bba

Accuracy obtained vs. Num Features used
可以看到,使用 7 特性时,精确度约为 82.5% ,这对于我们所做的大量准备工作来说当然不算糟糕。
您还可以打印出哪些特性被认为是最不重要的,并用下面的代码片段删除它们:
https://gist.github.com/dradecic/d2bb599f662c8f586b4180d5baf17038
RFECV 的实例也有一个漂亮的feature _ importances属性,值得一试:

Feature importances
好吧,好吧,先别冲我吼。我知道这不能告诉你太多。谢天谢地,这很容易想象。一种方法是创建一个 DataFrame 对象,属性作为一列,重要性作为另一列,然后简单地按照重要性降序排列数据帧。然后,您可以使用绘图库(如***【Matplotlib】***)来绘制条形图(这种情况下最好是水平的)以获得漂亮的视觉表示。下面是这样做的代码:
https://gist.github.com/dradecic/4bc8f929a86795c0d9c5e663293cd71f
运行此代码单元后,您将获得特性重要性的可视化表示:

RFECV — Feature Importance
这基本上就是递归特征消除!这张图表会告诉你一切。现在,您可以尝试使用这 7 个特征来训练模型,稍后,您可以尝试子集化并仅使用三个最重要的特征( 票价 、 年龄 和 性别 )。
4.结论
最后,我只想说谢谢你把这篇文章读完。RFE 当然不是唯一使用的特征选择方法,但它是其中之一,我当然觉得它没有得到应有的关注。我希望这篇文章背后的代码和逻辑能对你的日常工作和/或兼职项目有所帮助。
直到下一次…
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
参考
[1]https://dictionary . Cambridge . org/dictionary/English/recursive
[2]https://en . Wikipedia . org/wiki/Feature _(machine _ learning)
[3]https://docs . AWS . Amazon . com/machine-learning/latest/DG/cross-validation . html
[4]https://book down . org/max/FES/recursive-feature-elimination . html
使用过滤器方法在 Python 中选择要素
在本文中,我们将使用 Pythons 中的过滤方法实现特征选择。这里我们将探讨包括相关性、互信息和的过滤方法
先决条件:功能选择理解

特征选择的过滤方法
过滤方法基于一些单变量度量对每个特征进行排名,然后选择排名最高的特征。一些单变量指标是
- **方差:**去除常数和准常数特征
- **卡方:**用于分类。这是一种独立性的统计测试,用于确定两个变量的相关性。
- **相关系数:**删除重复特征
- 信息增益或互信息:评估自变量在预测目标变量时的相关性。换句话说,它决定了独立特征预测目标变量的能力
过滤方法的优势
- 过滤方法是模型不可知的
- 完全依赖数据集中的特征
- 计算速度非常快
- 基于不同的统计方法
过滤方法的缺点
- 过滤方法着眼于单个特征,以确定其相对重要性。一个特性本身可能没什么用,但是当它与其他特性结合起来时,可能是一个重要的影响因素。过滤方法可能会遗漏这些特征。
选择最佳特征的过滤标准
使用选择独立特征
- 与目标变量高度相关
- 与另一个独立变量相关性低
- 自变量的较高信息增益或互信息
此处有可用的数据集
导入所需的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
from sklearn.model_selection import train_test_split
**from sklearn.feature_selection import mutual_info_regression, mutual_info_classif**
读取数据集中的数据
dataset= pd.read_csv(‘c:\\data\\auto-mpg.csv’)
dataset.head(2)

dataset.info()

我们看到马力不是浮动的,但是上面的数据显示马力是数字的。
检查分类特征的存在
dataset.describe(include=’O’)

将马力特性更新为 int 并用 0 填充所有空值
dataset[‘horsepower’] = pd.to_numeric(dataset[‘horsepower’], errors=’coerce’).fillna(0).astype(int)
我们现在再次检查分类变量的存在
dataset.describe(include=’O’)

我们看到马力不再是一个分类变量,汽车名称是唯一的分类变量。
为汽车名称创建一个 labelEncoder,用 0 和 n_classes-1 之间的值对汽车名称进行编码。在我们的例子中,汽车名称的 n _ classes 是 305
**from sklearn.preprocessing import LabelEncoder**
labelencoder = LabelEncoder()
**X_en= dataset.iloc[:, 8].values
X_en = labelencoder.fit_transform(X_en)**
创建输入要素 X 和目标变量 y
X= dataset.iloc[:,1:7]
X[‘Car’] = X_en
y= dataset.iloc[:,0].values
在将所有输入要素转换为数字(包括目标变量)后,创建一个包含所有输入要素的数据集
full_data= X.copy()
full_data[‘mpg’]= y
full_data.head(2)

应用过滤方法的步骤 1
识别与目标变量高度相关的输入特征。
这里我们打印每个输入特征与目标变量的相关性
**importances = full_data.drop(“mpg”, axis=1).apply(lambda x: x.corr(full_data.mpg))**
indices = np.argsort(importances)
print(importances[indices])

绘制这些数据以便可视化
**names=['cylinders','displacement','horsepower','weight','acceleration','model year', 'car']** plt.title('Miles Per Gallon')
**plt.barh(range(len(indices)), importances[indices], color='g', align='center')**
plt.yticks(range(len(indices)), [names[i] for i in indices])
plt.xlabel('Relative Importance')
plt.show()

我们希望保留仅与目标变量高度相关的特征。这意味着输入特征对预测目标变量有很大的影响。
我们将阈值设置为绝对值 0.4。只有当输入特征与目标变量的相关性大于 0.4 时,我们才保留输入特征
for i in range(0, len(indices)):
** if np.abs(importances[i])>0.4:**
print(names[i])

Features with correlation >0.4 with target variable
X= dataset[ [‘cylinders’, ‘displacement’, ‘horsepower’, ‘weight’, ‘acceleration’, ‘model year’]]
我们现在已经将输入特征从 7 个减少到 6 个。汽车名称被删除,因为它与 mpg(每加仑英里数)的相关性不高
应用过滤方法的步骤 2
识别与其他独立变量相关性较低的输入特征。
根据步骤 1 遍历所有过滤后的输入要素,并检查每个输入要素与所有其他输入要素的相关性。
我们将保留与其他输入要素不高度相关的输入要素
for i in range(0,len(X.columns)):
for j in range(0,len(X.columns)):
if i!=j:
corr_1=np.abs(X[X.columns[i]].corr(X[X.columns[j]]))
**if corr_1 <0.3:
print( X.columns[i] , " is not correlated with ", X.columns[j])**
**elif corr_1>0.75:
print( X.columns[i] , " is highly correlated with ", X.columns[j])**

排量,马力,气缸,重量高度相关。我们将只保留其中的一个。
基于上述结果,我们保留气缸、加速度和车型年,并删除马力、排量和重量
X= dataset[ [‘cylinders’, ‘acceleration’, ‘model year’]]
应用过滤方法的步骤 3
求自变量相对于目标变量的信息增益或互信息
mi = mutual_info_regression(X, y)
绘制交互信息
mi = pd.Series(mi)
mi.index = X.columns
mi.sort_values(ascending=False)
mi.sort_values(ascending=False).plot.bar(figsize=(10, 4))

我们现在有了预测每加仑英里数的特征重要性。每加仑行驶的英里数可以根据汽车的汽缸数量、汽车制造年份和加速度来预测。
用于特征选择的过滤方法因此是模型不可知的,简单且易于解释
代码可用此处
特征选择技术
关于如何减少数据集中要素数量的端到端指南,包含 Python 中的实际示例。

Photo by Clem Onojeghuo on Unsplash
介绍
据《福布斯》报道,每天大约产生 2.5 万亿字节的数据[1]。然后可以使用数据科学和机器学习技术来分析这些数据,以便提供见解和做出预测。尽管在大多数情况下,最初收集的数据需要在开始任何统计分析之前进行预处理。有许多不同的原因可能需要进行预处理分析,例如:
- 收集的数据格式不正确(如 SQL 数据库、JSON、CSV 等)。
- 缺失值和异常值。
- 缩放和标准化。
- 减少数据集中存在的固有噪声(部分存储的数据可能已损坏)。
- 数据集中的某些要素可能不会收集任何信息用于我们的分析。
在本文中,我将带您了解如何使用 Kaggle 蘑菇分类数据集在 Python 中减少数据集中的要素数量。这篇文章中使用的所有代码(以及更多!)可在 Kaggle 和我的 GitHub 账户上获得。
减少统计分析过程中使用的要素数量可能会带来多种好处,例如:
- 精度提高。
- 过度拟合风险降低。
- 在训练中加速。
- 改进的数据可视化。
- 增加我们模型的可解释性。
事实上,统计证明,当执行机器学习任务时,存在每个特定任务都应该使用的最佳数量的特征(图 1)。如果添加的特性多于绝对必要的特性,那么我们的模型性能将会下降(因为添加了噪声)。真正的挑战是找出要使用的功能的最佳数量(实际上,这取决于我们可用的数据量以及我们要完成的任务的复杂性)。这就是特征选择技术帮助我们的地方!

Figure 1: Relationship between Classifier Performance and Dimensionality [2]
特征选择
有许多不同的方法可用于特征选择。其中一些最重要的是:
- 过滤方法 =过滤我们的数据集,只取其中包含所有相关特征的子集(例如,使用皮尔逊相关的相关矩阵)。
- 包装方法 =遵循过滤方法的相同目标,但使用机器学习模型作为其评估标准(例如,向前/向后/双向/递归特征消除)。我们向我们的机器学习模型提供一些特征,评估它们的性能,然后决定是否添加或删除这些特征以提高准确性。因此,这种方法可能比过滤更精确,但是计算量更大。
- 嵌入式方法 =与过滤方法一样,嵌入式方法也利用了机器学习模型。这两种方法之间的区别在于,嵌入式方法检查我们的 ML 模型的不同训练迭代,然后根据每个特征对 ML 模型训练的贡献大小来排列每个特征的重要性(例如,LASSO 正则化)。

Figure 2: Filter, Wrapper and Embedded Methods Representation [3]
实际实施
在本文中,我将利用蘑菇分类数据集,通过观察给定的特征来尝试预测蘑菇是否有毒。在这样做的同时,我们将尝试不同的特征消除技术,看看这会如何影响训练时间和整体模型精度。
首先,我们需要导入所有必需的库。
下图显示了我们将在本例中使用的数据集。

Figure 3: Mushroom Classification dataset
在将这些数据输入我们的机器学习模型之前,我决定对所有的分类变量进行一次热编码,将我们的数据分成特征( X )和标签( Y ),最后在训练集和测试集中使用。
特征重要性
基于集合(例如额外的树和随机森林)的决策树模型可以用于对不同特征的重要性进行排序。了解我们的模型赋予哪些特征最重要,对于理解我们的模型如何做出预测(从而使其更具解释力)至关重要。同时,我们可以去掉那些不会给我们的模型带来任何好处的特征(或者迷惑它做出错误的决定!).
如下所示,使用所有特征训练随机森林分类器,在大约 2.2 秒的训练时间内达到 100%的准确度。在下面的每个例子中,每个模型的训练时间将在每个片段的第一行打印出来,供您参考。
2.2676709799999992
[[1274 0]
[ 0 1164]]
precision recall f1-score support
0 1.00 1.00 1.00 1274
1 1.00 1.00 1.00 1164
accuracy 1.00 2438
macro avg 1.00 1.00 1.00 2438
weighted avg 1.00 1.00 1.00 2438
一旦我们的随机森林分类器被训练,我们就可以创建一个特征重要性图,以查看哪些特征被认为对我们的模型进行预测是最重要的(图 4)。在本例中,下面只显示了前 7 项功能。

Figure 4: Feature Importance Plot
既然我们知道了随机森林认为哪些特性是最重要的,我们可以尝试使用前 3 个来训练我们的模型。
正如我们在下面看到的,仅使用 3 个特征导致准确度仅下降 0.03%,并且将训练时间减半。
1.1874146949999993
[[1248 26]
[ 53 1111]]
precision recall f1-score support
0 0.96 0.98 0.97 1274
1 0.98 0.95 0.97 1164
accuracy 0.97 2438
macro avg 0.97 0.97 0.97 2438
weighted avg 0.97 0.97 0.97 2438
我们还可以通过可视化一个经过训练的决策树结构来理解如何执行特征选择。
0.02882629099999967
[[1274 0]
[ 0 1164]]
precision recall f1-score support
0 1.00 1.00 1.00 1274
1 1.00 1.00 1.00 1164
accuracy 1.00 2438
macro avg 1.00 1.00 1.00 2438
weighted avg 1.00 1.00 1.00 2438
位于树结构顶部的特征是我们的模型为了执行分类而保留的最重要的特征。因此,通过只选择顶部的前几个特征并丢弃其他特征,可能会导致创建一个具有可观准确度分数的模型。

Figure 5: Decision Tree Visualization
递归特征消除(RFE)
递归特征消除(RFE)将机器学习模型的实例和要使用的最终期望数量的特征作为输入。然后,它通过使用机器学习模型准确性作为度量标准来对特征进行排序,从而递归地减少要使用的特征的数量。
创建一个 for 循环,其中输入要素的数量是我们的变量,然后可以通过跟踪每个循环迭代中记录的精度来找出我们的模型需要的最佳要素数量。使用 RFE 支持方法,我们可以找出被评估为最重要的特征的名称*(*rfe . support返回一个布尔列表,其中 TRUE 表示一个特征被认为是重要的,FALSE 表示一个特征不被认为是重要的)。
210.85839133899998
Overall Accuracy using RFE: 0.9675963904840033
SelecFromModel
SelectFromModel 是另一种 Scikit-learn 方法,可用于特征选择。该方法可用于具有 coef_ 或***feature _ importances _***属性的所有不同类型的 Scikit-learn 模型(拟合后)。与 RFE 相比,SelectFromModel 是一个不太健壮的解决方案。事实上,SelectFromModel 只是根据计算出的阈值来删除不太重要的特性(不涉及优化迭代过程)。
为了测试 SelectFromModel 的功效,我决定在这个例子中使用一个 ExtraTreesClassifier。
ExtraTreesClassifier(极度随机化的树)是基于树的集成分类器,与随机森林方法相比,它可以产生更小的方差(因此降低了过度拟合的风险)。随机森林和极度随机化的树之间的主要区别在于,在极度随机化的树中,节点被采样而没有替换。
1.6003950479999958
[[1274 0]
[ 0 1164]]
precision recall f1-score support
0 1.00 1.00 1.00 1274
1 1.00 1.00 1.00 1164
accuracy 1.00 2438
macro avg 1.00 1.00 1.00 2438
weighted avg 1.00 1.00 1.00 2438
相关矩阵分析
另一种可用于减少数据集中要素数量的方法是检查要素与标注的相关性。
使用皮尔逊相关,我们返回的系数值将在-1 和 1 之间变化:
- 如果两个特征之间的相关性为 0,这意味着改变这两个特征中的任何一个都不会影响另一个。
- 如果两个特征之间的相关性大于 0,这意味着增加一个特征中的值也会增加另一个特征中的值(相关系数越接近 1,两个不同特征之间的结合越强)。
- 如果两个特征之间的相关性小于 0,这意味着增加一个特征中的值将使另一个特征中的值减小(相关系数越接近-1,两个不同特征之间的这种关系就越强)。
在这种情况下,我们将只考虑与输出变量相关度至少为 0.5 的特征。
bruises_f 0.501530
bruises_t 0.501530
gill-color_b 0.538808
gill-size_b 0.540024
gill-size_n 0.540024
ring-type_p 0.540469
stalk-surface-below-ring_k 0.573524
stalk-surface-above-ring_k 0.587658
odor_f 0.623842
odor_n 0.785557
Y 1.000000
Name: Y, dtype: float64
现在,我们可以通过创建一个相关矩阵来进一步了解不同相关特征之间的关系。

Figure 6: Correlation Matrix of highest correlated features
此分析中另一个可能的控制方面是检查所选变量是否高度相关。如果是,那么我们只需要保留其中一个相关的,而丢弃其他的。
最后,我们现在可以只选择与 Y 最相关的特征,并训练/测试 SVM 模型来评估这种方法的结果。
0.06655320300001222
[[1248 26]
[ 46 1118]]
precision recall f1-score support
0 0.96 0.98 0.97 1274
1 0.98 0.96 0.97 1164
accuracy 0.97 2438
macro avg 0.97 0.97 0.97 2438
weighted avg 0.97 0.97 0.97 2438
单变量选择
单变量特征选择是一种统计方法,用于选择与我们的对应标签具有最强关系的特征。使用 SelectKBest 方法,我们可以决定使用哪些指标来评估我们的特性以及我们想要保留的 K 个最佳特性的数量。根据我们的需求,可以使用不同类型的评分功能:
- 分类 = chi2,f_classif,mutual_info_classif
- 回归= f _ 回归,mutual _ info _ 回归
在本例中,我们将使用 chi2(图 7)。

Figure 7: Chi-squared Formula [4]
卡方(Chi2) 可以将非负值作为输入,因此,首先,我们将输入数据的范围定在 0 到 1 之间。
1.1043402509999964
[[1015 259]
[ 41 1123]]
precision recall f1-score support
0 0.96 0.80 0.87 1274
1 0.81 0.96 0.88 1164
accuracy 0.88 2438
macro avg 0.89 0.88 0.88 2438
weighted avg 0.89 0.88 0.88 2438
套索回归
当将正则化应用于机器学习模型时,我们向模型参数添加惩罚,以避免我们的模型试图过于接近我们的输入数据。通过这种方式,我们可以使我们的模型不那么复杂,并且我们可以避免过度拟合(使学习到我们的模型,不仅是关键数据特征,还有它的内在噪声)。
一种可能的正则化方法是拉索(L1)回归。当使用 Lasso 回归时,如果输入特征的系数对我们的机器学习模型训练没有积极的贡献,它们就会缩小。这样,一些特征可能会被自动丢弃,为它们分配等于零的系数。
LassoCV Best Alpha Scored: 0.00039648980844788386
LassoCV Model Accuracy: 0.9971840741918596
Variables Eliminated: 73
Variables Kept: 44
一旦训练了我们的模型,我们可以再次创建一个特性重要性图,以了解哪些特性被我们的模型认为是最重要的(图 8)。这真的很有用,尤其是在试图理解我们的模型是如何决定做出预测的时候,因此使我们的模型更容易解释。

Figure 8: Lasso Feature Importance
感谢阅读!
联系人
如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:
文献学
[1]什么是大数据?—大数据世界入门指南。阿努什里·苏布拉马年,爱德华卡!。访问地点:https://www.edureka.co/blog/what-is-big-data/
[2]分类中的维数灾难,假人的计算机视觉。访问:https://www . vision dummy . com/2014/04/curse-dimensionality-affect-class ification/
[3]整合化学计量学和统计学,推动成功的蛋白质组学生物标志物发现,ResearchGate。访问:https://www . research gate . net/publication/324800823 _ Integrated _ chemo metrics _ and _ Statistics _ to _ Drive _ Successful _ Proteomics _ Biomarker _ Discovery
[4]卡方检验,淡水中的生命。请访问:https://www . lifeinfreshwater . org . uk/Stats % 20 for % 20 twits/Chi-squared . html
分类的要素选择技术及其应用的 Python 技巧
关于如何使用最常见的特征选择技术解决分类问题的教程

选择使用哪些功能是任何机器学习项目中的关键一步,也是数据科学家日常工作中的一项经常性任务。在本文中,我回顾了分类问题中最常见的特征选择技术,将它们分为 6 大类。我提供了如何在机器学习项目中使用它们的技巧,并尽可能用 Python 代码给出例子。你准备好了吗?
TL;灾难恢复-汇总表
下表总结了主要方法,并在以下部分进行了讨论。

什么是特征选择,为什么有用?
机器学习中最大的两个问题是过拟合(拟合在数据集之外不可概括的数据方面)和维数灾难(高维数据的非直观和稀疏特性)。
通过减少模型中的特征数量,尝试优化模型性能,特征选择有助于避免这两个问题。这样做,特性选择还提供了一个额外的好处:模型解释。随着特征的减少,输出模型变得更简单和更容易解释,并且人类更有可能相信模型做出的未来预测。
无监督方法
减少特征数量的一个简单方法是对数据应用降维技术。这通常以无人监督的方式完成,即不使用标签本身。
降维实际上并不选择特征的子集,而是在低维空间中产生一组新的特征。这个新的集合可以用于分类过程本身。
以下示例使用降维后的特征进行分类。更准确地说,它使用主成分分析**(PCA)的前 2 个成分作为新的特征集。**
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
from sklearn.svm import SVC
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormapimport numpy as np
h = .01
x_min, x_max = -4,4
y_min, y_max = -1.5,1.5# loading dataset
data = load_iris()
X, y = data.data, data.target# selecting first 2 components of PCA
X_pca = PCA().fit_transform(X)
X_selected = X_pca[:,:2]# training classifier and evaluating on the whole plane
clf = SVC(kernel='linear')
clf.fit(X_selected,y)
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)# Plotting
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
plt.figure(figsize=(10,5))
plt.pcolormesh(xx, yy, Z, alpha=.6,cmap=cmap_light)
plt.title('PCA - Iris dataset')
plt.xlabel('Dimension 1')
plt.ylabel('Dimension 2')
plt.scatter(X_pca[:,0],X_pca[:,1],c=data.target,cmap=cmap_bold)
plt.show()

在评估特征的上下文中,降维的另一个用途是用于可视化:在较低维度的空间中,更容易从视觉上验证数据是否是潜在可分的,这有助于设置对分类准确性的期望。在实践中,我们对特征的子集执行维度缩减(例如 PCA ),并检查标签如何分布在缩减的空间中。如果它们看起来是分开的,这是一个明显的迹象,表明使用这组特征时,预期会有高的分类性能。
在下面的例子中,在一个 2 维的缩减空间中,不同的标签被显示为是相当可分的。这表明,在训练和测试分类器时,人们可以期待高性能。
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
import matplotlib.pyplot as pltfrom mlxtend.plotting import plot_pca_correlation_graphdata = load_iris()
X, y = data.data, data.targetplt.figure(figsize=(10,5))
X_pca = PCA().fit_transform(X)
plt.title('PCA - Iris dataset')
plt.xlabel('Dimension 1')
plt.ylabel('Dimension 2')
plt.scatter(X_pca[:,0],X_pca[:,1],c=data.target)
_ = plot_pca_correlation_graph(X,data.feature_names)
****
除此之外,我还绘制了相关圆**,它显示了每个原始维度和新 PCA 维度之间的相关性。直观地说,该图显示了每个原始特征对新创建的 PCA 成分的贡献。在上面的例子中,花瓣长度和宽度与第一个主成分分析维度高度相关,萼片宽度对第二个维度贡献很大。**
单变量滤波方法

过滤方法旨在对特征的重要性进行排序,而不使用任何类型的分类算法。
单变量过滤方法单独评估每个特征,并且不考虑特征的相互作用。这些方法包括为每个特征提供一个分数,通常基于统计测试。
分数通常或者测量因变量和特征之间的相关性(例如 Chi2 和用于回归的 Pearls 相关系数),或者测量给定类别标签的特征分布之间的差异(f 检验和 T 检验)。
分数通常对基础数据的统计属性做出假设。理解这些假设对于决定使用哪种测试是很重要的,即使其中一些假设对于违反假设是稳健的。
基于统计测试的分数提供了一个 p 值,可以用来排除一些特征。如果 p 值高于某个阈值(通常为 0.01 或 0.05),则会出现这种情况。
常见测试包括:

包sklearn实现了一些过滤方法。然而,由于大多数都是基于统计测试的,所以也可以使用统计包(比如statsmodels)。
下面是一个例子:
from sklearn.feature_selection import f_classif, chi2, mutual_info_classif
from statsmodels.stats.multicomp import pairwise_tukeyhsdfrom sklearn.datasets import load_irisdata = load_iris()
X,y = data.data, data.targetchi2_score, chi_2_p_value = chi2(X,y)
f_score, f_p_value = f_classif(X,y)
mut_info_score = mutual_info_classif(X,y)pairwise_tukeyhsd = [list(pairwise_tukeyhsd(X[:,i],y).reject) for i in range(4)]print('chi2 score ', chi2_score)
print('chi2 p-value ', chi_2_p_value)
print('F - score score ', f_score)
print('F - score p-value ', f_p_value)
print('mutual info ', mut_info_score)
print('pairwise_tukeyhsd',pairwise_tukeyhsd)Out:chi2 score [ 10.82 3.71 116.31 67.05]
chi2 p-value [0\. 0.16 0\. 0\. ]
F - score score [ 119.26 49.16 1180.16 960.01]
F - score p-value [0\. 0\. 0\. 0.]
mutual info [0.51 0.27 0.98 0.98]
pairwise_tukeyhsd [[True, True, True], [True, True, True], [True, True, True], [True, True, True]]
对特征进行分级的可视化方法
箱线图和小提琴图
箱线图/小提琴图可能有助于可视化给定类别的特征分布。对于 Iris 数据集,下面显示了一个示例。
这是有用的,因为统计测试通常只评估这种分布的平均值之间的差异。因此,这些图提供了关于特征质量的更多信息
import pandas as pd
import seaborn as sns
sns.set()
df = pd.DataFrame(data.data,columns=data.feature_names)
df['target'] = data.targetdf_temp = pd.melt(df,id_vars='target',value_vars=list(df.columns)[:-1],
var_name="Feature", value_name="Value")
g = sns.FacetGrid(data = df_temp, col="Feature", col_wrap=4, size=4.5,sharey = False)
g.map(sns.boxplot,"target", "Value");
g = sns.FacetGrid(data = df_temp, col="Feature", col_wrap=4, size=4.5,sharey = False)
g.map(sns.violinplot,"target", "Value");
****
用 ROC 曲线进行特征排序
ROC 曲线可用于按重要性顺序排列特征,这给出了排列特征性能的直观方式。
这种技术最适合二进制分类任务。为了应用于多类问题,可以使用微观或宏观平均值或基于多重比较的标准(类似于成对 Tukey 的范围测试)。
以下示例绘制了各种特征的 ROC 曲线。
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.metrics import auc
import numpy as np# loading dataset
data = load_iris()
X, y = data.data, data.targety_ = y == 2plt.figure(figsize=(13,7))
for col in range(X.shape[1]):
tpr,fpr = [],[]
for threshold in np.linspace(min(X[:,col]),max(X[:,col]),100):
detP = X[:,col] < threshold
tpr.append(sum(detP & y_)/sum(y_))# TP/P, aka recall
fpr.append(sum(detP & (~y_))/sum((~y_)))# FP/N
if auc(fpr,tpr) < .5:
aux = tpr
tpr = fpr
fpr = aux
plt.plot(fpr,tpr,label=data.feature_names[col] + ', auc = '\
+ str(np.round(auc(fpr,tpr),decimals=3)))plt.title('ROC curve - Iris features')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend()
plt.show()

多元滤波方法
这些方法考虑了变量之间的相关性,而没有考虑任何类型的分类算法。
mRMR
****mRMR(最小冗余最大相关性)是一种启发式算法,通过考虑特征的重要性和它们之间的相关性来寻找接近最优的特征子集。
其思想是,即使两个特征高度相关,如果它们高度相关,将它们都添加到特征集中可能不是一个好主意。在这种情况下,添加两个特征会增加模型的复杂性(增加过度拟合的可能性),但由于特征之间的相关性,不会添加重要的信息。
在一组 N 特征的 S 中,特征的相关性( D )计算如下:

其中 I 为互信息算子。
特征的冗余表示如下:

集合 S 的 mRMR 分数定义为( D - R) 。目标是找到具有最大值( D-R) 的特征子集。然而,在实践中,我们执行增量搜索(也称为前向选择),在每一步,我们添加产生最大 mRMR 的特征。
该算法由算法作者自己用 C 实现。你可以在这里找到这个包的源代码,以及原始论文。
在名称pymrmr上创建了一个(未维护的)python 包装器。如果pymrmr有问题,我建议直接调用 C 级函数。
下面的代码举例说明了pymrmr的用法。注意,pandas数据帧的列应按照 C 级包中的描述进行格式化(此处为)。
import pandas as pd
import pymrmrdf = pd.read_csv('some_df.csv')
# Pass a dataframe with a predetermined configuration.
# Check http://home.penglab.com/proj/mRMR/ for the dataset requirements
pymrmr.mRMR(df, 'MIQ', 10)
输出:
*** This program and the respective minimum Redundancy Maximum Relevance (mRMR)
algorithm were developed by Hanchuan Peng <hanchuan.peng@gmail.com>for
the paper
"Feature selection based on mutual information: criteria of
max-dependency, max-relevance, and min-redundancy,"
Hanchuan Peng, Fuhui Long, and Chris Ding,
IEEE Transactions on Pattern Analysis and Machine Intelligence,
Vol. 27, No. 8, pp.1226-1238, 2005.*** MaxRel features ***
Order Fea Name Score
1 765 v765 0.375
2 1423 v1423 0.337
3 513 v513 0.321
4 249 v249 0.309
5 267 v267 0.304
6 245 v245 0.304
7 1582 v1582 0.280
8 897 v897 0.269
9 1771 v1771 0.269
10 1772 v1772 0.269*** mRMR features ***
Order Fea Name Score
1 765 v765 0.375
2 1123 v1123 24.913
3 1772 v1772 3.984
4 286 v286 2.280
5 467 v467 1.979
6 377 v377 1.768
7 513 v513 1.803
8 1325 v1325 1.634
9 1972 v1972 1.741
10 1412 v1412 1.689
Out[1]:
['v765',
'v1123',
'v1772',
'v286',
'v467',
'v377',
'v513',
'v1325',
'v1972',
'v1412']
包装方法

包装器方法背后的主要思想是搜索哪组特性最适合特定的分类器。这些方法可以总结如下,并且在所使用的搜索算法方面有所不同。
- 选择一个性能指标(可能性、AIC、BIC、F1 分数、准确度、MSE、MAE…),记为 M.
- 选择一个分类器/回归器/ …,在这里记为 C 。
- ****用给定的搜索方法搜索**不同的特征子集。对于每个子集 **S,执行以下操作:
- 使用 S 作为分类器的特征,以交叉验证的方式训练和测试C**;**
- 从交叉验证程序中获得平均分数(对于指标 M ),并将该分数分配给子集S**;**
- 选择一个新的子集并重做步骤一个。
详述步骤 3
第三步未指定将使用哪种类型的搜索方法。几乎在任何情况下,测试所有可能的特征子集都是禁止的(强力选择),因为这将需要执行步骤 3 指数次(特征数量的 2 次方)。除了时间复杂性之外,由于有如此大量的可能性,很可能某个特征组合仅仅是随机地表现得最好,这使得强力解决方案更容易过度拟合。
搜索算法在实践中往往能很好地解决这个问题。它们倾向于实现接近蛮力解决方案的性能,具有更少的时间复杂度和更少的过拟合机会。
正向选择和反向选择(又名修剪)在实践中被大量使用,以及它们的搜索过程的一些小变化。
反向选择包括从具有全部特征的模型开始,并且在每一步中,移除没有特征的模型具有最高分数。正向选择以相反的方式进行:它从一组空的特征开始,并添加最能提高当前分数的特征。
向前/向后选择仍然倾向于过度拟合,因为通常,分数倾向于通过添加更多特征来提高。避免这种情况的一种方法是使用惩罚模型复杂性的分数,如 AIC 或 BIC。
包装方法结构的图示如下。值得注意的是,特征集是(1)通过搜索方法找到的,以及(2)在打算使用的同一分类器上交叉验证的。****

第三步还开放了交叉验证参数。通常,使用 k-fold 程序。然而,使用大 k 会给整个包装器方法带来额外的复杂性。
包装方法的 Python 包
(http://rasbt.github.io/mlxtend/)是一个用于各种数据科学相关任务的有用包。这个包的包装方法可以在 SequentialFeatureSelector 上找到。它提供向前和向后的功能选择,有些变化。
该包还提供了一种通过函数 plot _ sequential _ feature _ selection 将分数可视化为要素数量的函数的方法。
下面的例子摘自包的主页。
from mlxtend.feature_selection import SequentialFeatureSelector as SFS
from mlxtend.plotting import plot_sequential_feature_selection as plot_sfsfrom sklearn.linear_model import LinearRegression
from sklearn.datasets import load_bostonboston = load_boston()
X, y = boston.data, boston.targetlr = LinearRegression()sfs = SFS(lr,
k_features=13,
forward=True,
floating=False,
scoring='neg_mean_squared_error',
cv=10)sfs = sfs.fit(X, y)
fig = plot_sfs(sfs.get_metric_dict(), kind='std_err')plt.title('Sequential Forward Selection (w. StdErr)')
plt.grid()
plt.show()

嵌入式方法

训练一个分类器归结为一个优化问题,其中我们试图最小化其参数的函数(这里记为𝜃).这个函数被称为损失函数**(记为𝐿(𝜃)).**
在一个更一般的框架中,我们通常希望最小化一个目标** 函数,它考虑了损失函数和对模型复杂性的惩罚(或正则化)(ω(𝜃)😗*
obj(𝜃)=𝐿(𝜃)+ω(𝜃)
线性分类器的嵌入式方法
对于线性分类器(例如线性 SVM、逻辑回归),损失函数表示为:

其中每个 xʲ 对应一个数据样本,而 Wᵀxʲ 表示系数向量 (w₁,w₂,…w_n) 与每个样本中的特征的内积。
对于线性 SVM 和逻辑回归,铰链和逻辑损失分别为:

线性分类器的两个最常见的惩罚是 L-1 和 L-2 惩罚:

λ 的值越高,惩罚越强,最优目标函数将趋向于以收缩越来越多的系数 w_i 而结束。
众所周知,“L1”惩罚会创建稀疏模型,这简单地意味着,在优化过程中,通过使一些系数等于零,它倾向于从模型中选择一些特征。****
另一个常见的处罚是 L-2。虽然 L-2 缩小了系数,因此有助于避免过拟合,但它不会创建稀疏模型,因此它不适合作为特征选择技术。
对于一些线性分类器(线性 SVM,逻辑回归),可以有效地使用 L-1 罚分,这意味着有有效的数值方法来优化最终的目标函数。对于其他几个分类器(各种核 SVM 方法、决策树等等),情况就不一样了。因此,不同的分类器应该使用不同的正则化方法。
带有正则化的逻辑回归示例如下所示,我们可以看到,随着 C 的减少,算法排除了一些特征(想想如果 C 为 1/λ )。
import numpy as np
import matplotlib.pyplot as pltfrom sklearn.svm import LinearSVC
from sklearn.model_selection import ShuffleSplit
from sklearn.model_selection import GridSearchCV
from sklearn.utils import check_random_state
from sklearn import datasets
from sklearn.linear_model import LogisticRegressionrnd = check_random_state(1)# set up dataset
n_samples = 3000
n_features = 15# l1 data (only 5 informative features)
X, y = datasets.make_classification(n_samples=n_samples,
n_features=n_features, n_informative=5,
random_state=1)cs = np.logspace(-2.3, 0, 50)coefs = []
for c in cs:
clf = LogisticRegression(solver='liblinear',C=c,penalty='l1')
# clf = LinearSVC(C=c,penalty='l1', loss='squared_hinge', dual=False, tol=1e-3)
clf.fit(X,y)
coefs.append(list(clf.coef_[0]))
coefs = np.array(coefs)
plt.figure(figsize=(10,5))
for i,col in enumerate(range(n_features)):
plt.plot(cs,coefs[:,col])
plt.xscale('log')
plt.title('L1 penalty - Logistic regression')
plt.xlabel('C')
plt.ylabel('Coefficient value')
plt.show()

基于树的模型的特征重要性
另一种常见的特征选择技术包括从基于树的模型中提取特征重要性等级。
特征重要性本质上是由每个变量产生的分裂标准中的单个树的改进的平均值。换句话说,它是在使用特定变量分割树时分数(决策树符号上所谓的“杂质”)提高了多少。
它们可用于对要素进行分级,然后选择要素的子集。然而,应小心使用特性重要性,因为它们会受到偏差和的影响,并呈现出与高度相关特性相关的意外行为,不管它们有多强。
如本文中的所示,随机森林特征重要性偏向于具有更多类别的特征。此外,如果两个特征高度相关,无论特征的质量如何,它们的分数都会大大降低。
以下是如何从随机森林中提取要素重要性的示例。虽然是回归变量,但对于分类器来说,过程是相同的。
from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressorimport numpy as npboston = load_boston()
X = boston.data
Y = boston.target
feat_names = boston.feature_names
rf = RandomForestRegressor()
rf.fit(X, Y)
print("Features sorted by their score:")
print(sorted(zip(map(lambda x: round(x, 4), rf.feature_importances_), feat_names),
reverse=True))
Out:
Features sorted by their score:
[(0.4334, 'LSTAT'), (0.3709, 'RM'), (0.0805, 'DIS'), (0.0314, 'CRIM'), (0.0225, 'NOX'), (0.0154, 'TAX'), (0.0133, 'PTRATIO'), (0.0115, 'AGE'), (0.011, 'B'), (0.0043, 'INDUS'), (0.0032, 'RAD'), (0.0016, 'CHAS'), (0.0009, 'ZN')]
额外:树模型的主要杂质分数
如上所述,“杂质”是决策树算法在决定分割节点时使用的分数。有许多决策树算法(IDR3、C4.5、CART 等),但一般规则是,我们用来分割树中节点的变量是对杂质产生最高改善的变量。
最常见的杂质是基尼杂质和熵。基尼系数杂质的改进被称为“基尼系数重要性,而熵的改进是信息增益。****

SHAP:来自树模型的可靠特征重要性
(感谢恩里克·加斯帕里尼·菲乌萨·多纳西门托的建议!)
SHAP 实际上远不止于此。它是一种算法,提供任何预测模型之外的模型解释。然而,对于基于树的模型,它特别有用:作者为这种模型开发了高速和精确(不仅仅是局部)的解释,与 X GBoost 、 LightGBM 、 CatBoost 和 scikit-learn 树模型兼容。
我鼓励检查一下 SHAP 提供的解释能力(比如特征依赖、交互效果、模型监控……)。下面,我(仅)绘制了 SHAP 输出的特征重要性,当对它们进行排序以进行特征选择时,这些特征重要性比原始树模型输出的特征重要性更可靠。这个例子摘自他们的 github 页面。
import xgboost
import shap# load JS visualization code to notebook
shap.initjs()# train XGBoost model
X,y = shap.datasets.boston()
model = xgboost.train({"learning_rate": 0.01}, xgboost.DMatrix(X, label=y), 100)# explain the model's predictions using SHAP values
# (same syntax works for LightGBM, CatBoost, and scikit-learn models)
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X)shap.summary_plot(shap_values, X, plot_type="bar")

结论——何时使用每种方法?
嵌入式方法对于避免过度拟合和选择有用的变量通常非常有效。它们也是时间有效的,因为它们嵌入在目标函数中。它们的主要缺点是它们可能无法用于所需的分类器。
包装方法在实践中往往工作得很好。然而,它们在计算上是昂贵的,特别是当处理数百个特征时。但是如果你有计算资源,这是一个很好的方法。
如果特征集非常大(大约数百或数千),因为过滤方法很快,它们可以很好地作为选择的第一阶段,以排除一些变量。随后,可以将另一种方法应用于已经缩减的特征集。例如,如果您想要创建要素的组合,将它们相乘或相除,这将非常有用。
906

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



