一、特征选择的重要性
机器学习过程中,特征选择是非常重要的一步。可以说,选到了合适的特征,那么模型的训练就成功了一半。
一方面,遗漏掉重要特征肯定是我们无法接受的,这会导致我们的模型拟合能力不足;另一方面冗余特征也可能会造成严重的后果,比如:
- 带来额外的计算量,导致训练时间过长;
- 模型过拟合,对新样本泛化能力不足;
- 模型可读性(解释性)差。
![92b15485f732f0c990506f3e87d3a159.png](https://i-blog.csdnimg.cn/blog_migrate/f918f88389d17fa6a38f009f2b359145.jpeg)
所谓大浪淘沙始见金,在训练模型的过程中,我们不光要考虑模型、参数等的选择与调优,还要花足够的时间来选取合适的特征。
二、特征选择的方法
在现在的业界共识中,特征选择方法基本上可以归类为三种:过滤法、包裹法和嵌入法。
1. 过滤法
过滤法最好理解,就是通过一定的统计测量方法对每个特征进行评分和排序,然后按照一定的标准过滤出最优的特征子集。过滤法不用考虑后续的学习器,因此计算性能比较好。
2. 包裹法
包裹法的开销会大一些,效果一般也更好一些,因为这种方法是针对特定的模型量身定制的。使用包裹法时,要先确定准备采纳的学习器(模型),然后使用该学习器的性能指标作为特征子集的评价准则。也就是说,包裹法的目标就是为了给特定学习器选择最有利于其性能的特征子集。
3. 嵌入法
嵌入法则更进一步,将特征选择和学习器(模型)训练的过程合二为一,在训练的过程中自动完成了特征选择。
三、scikit-learn中的特征选择
下边两张图罗列了scikit-learn中提供的一些特征选择的方法,我先来为大家简单介绍一下。
- 方差阈值法隶属于过滤法,这种方法最为简单,但其效果并不是十分稳定、可靠。这种方法认为低方差的特征所蕴含的信息量较少,应该丢弃;而高方差的特征蕴含信息较多,应予以保留。但是在实践过程中,我们会经常遇到删除了低方差的特征,但是模型性能不升反降的情况。因此这种方法仅适用于作为一个探索性的选择;
- sklearn中提供了一系列用于单变量选择的方法,这些方法的原理是根据某些特定的统计指标来分别评测每一个特征,并根据得分、排序或者p-value来完成筛选。根据IV、AUC、相关系数等来进行特征选择的方法需要我们自己来实现,sklearn中并没有现成的工具可用(不用担心,我会教大家的);
- sklearn中也提供了包裹式的特征选择方法,比如SelectFromModel、RFE(递归消除法)、RFECV(RFE的交叉验证版),它们都需要我们指定一个评估器(estimator,比如LogisticRegression),然后针对这个评估器,它们会自动选择最佳的特征子集。
- sklearn中也有嵌入式的特征选择方法,如RandomizedLasso、RandomizedLogisticRegression等,它们在模型训练的过程中会自动完成特征选择的过程。
后续我们会详细探讨它们各自的原理、实践等更多细节,今天我们就拿最简单的方差阈值法来为大家演示一下特征选择的过程。
![60e62dc5b00f59f34534fcbb835fa3ff.png](https://i-blog.csdnimg.cn/blog_migrate/6506b9a13f8f76be51bc25279b240e98.jpeg)
![2efe50236ad959693fe77f5ee935fe5d.png](https://i-blog.csdnimg.cn/blog_migrate/227e58c94359b73fcc1d43a9f53519af.jpeg)
四、一个方差阈值法的简单例子
我们以sklearn中的iris数据集为例,该数据集一共有4个特征。我们使用numpy.var()方法来计算每个特征的方差并打印出来,并且通过数据的.shape属性来获取特征数。
from sklearn.datasets import load_iris from sklearn.feature_selection import VarianceThreshold import numpy as np # 加载数据集 iris = load_iris() X = iris.data y = iris.target # 打印数据集中的特征数和每个特征的方差 print('原数据集中的特征数:', X.shape[1], '') print('原数据集中不同特征的方差:', np.var(X, axis=0), '') # 使用VarianceThreshold来过滤掉方差在0.6以下的特征 selector = VarianceThreshold(threshold=0.6) X_new = selector.fit_transform(X) # 打印新数据集的特征数 print('方差阈值法选择的特征数:', X_new.shape[1])
输出为:
原数据集中的特征数: 4 原数据集中不同特征的方差: [0.68112222 0.18871289 3.09550267 0.57713289] 方差阈值法选择的特征数: 2
![abf7c18a35116a8c828519abffa33482.png](https://i-blog.csdnimg.cn/blog_migrate/86bcdcd49328a0b382eaa2cdc44deb15.jpeg)
那么接下来,我们以新老特征集分别来训练一个模型来看看效果。
首先看看原数据集:
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=101) model = LogisticRegression() model.fit(X_train, y_train) y_pred = model.predict(X_test) acc = accuracy_score(y_test, y_pred) print('准确率:', acc)
输出为:
准确率: 0.9736842105263158
然后我们看看新数据集:
X_train, X_test, y_train, y_test = train_test_split(X_new, y, random_state=101) model = LogisticRegression() model.fit(X_train, y_train) y_pred = model.predict(X_test) acc = accuracy_score(y_test, y_pred) print('准确率:', acc)
输出为:
准确率: 0.9473684210526315
可以看到,我们在仅保留了两个特征的情况下,仅仅牺牲了一点准确率。
![cc5b9d0e7fd6d2912085bb2dfdb1bdcc.png](https://i-blog.csdnimg.cn/blog_migrate/b1e1c30a010e32fa37771ae880af75bd.jpeg)
![655d517c69442fe646cbefd0cbdba000.png](https://i-blog.csdnimg.cn/blog_migrate/6e9daa84425e335ceb9b32c054b2a69b.jpeg)
事实上,在这个例子中,这种通过牺牲模型性能来换取计算性能的操作意义不大,因为这个数据集仅有150个样本、4个特征,但是当我们面临成千上万的特征、数以亿计的样本时,我们就有必要进行权衡了。当然,特征选择的目的远不止于此,在之后的探讨中,我们会发现它的更多优点。