特征工程一直都是模型的核心,坊间一直流传一种说法,特征工程决定了模型的上限。而特征选择在特征工程中有着重要地位,一是由于愈发庞大的数据集导致模型计算困难,二是构造特征容易筛选特征却困难,所幸调包解决一切,sklearn中一般有以下几种方式进行特征选择:
- 通过特征的统计分量选择
- 通过模型选择的嵌入式选择
- 通过递归包装选择
1 通过统计分量的特征选择
数据类型 | 可用方法 |
---|---|
连续-连续 | 相关系数、假设检验 |
连续-离散 | 相关系数、连续二值化(最小Gini切分、最大熵增益切分) |
连续-离散 | 相关系数(定序) |
连续-离散 | 相关系数,熵相关、F分值 |
离散-离散(非二值) | 熵相关、Gini、相关系数(定序) |
sklearn中常用的类是variance_threshold, SelectKBest和SelectPercentile,第一个为过滤,后面两个为选择,本质都是对特征和目标target的统计量进行排序,既然是统计分量,就需要用到统计函数,sklearn中提供的统计函数主要有以下几种:
- chi2:卡方检验,通过用于2分类任务
- f_classif:分类任务中的f检验
- f_regression:回归任务中的f检验
- mutual_info_classif:分类任务中的互信息
- mutual_info_regression:回归任务中的互信息
下面的代码给出了常用的方法,特征个数的选择一般采用cv进行调整。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score
from sklearn.svm import SVC
from sklearn.feature_selection import variance_threshold, SelectKBest, SelectPercentile,chi2,f_classif,mutual_info_classif
#用sklearn中自带的肺癌数据集进行测试
data = load_breast_cancer()
X, y = data.get('data'), data.get('target')
#利用互信息进行特征选择,同时利用cv对k进行调参
estimator = SVC()
ks = [i for i in range(10,20)]
scores = []
for k in ks:
X_select = SelectKBest(mutual_info_classif, k = k).fit_transform(X, y)
once = cross_val_score(estimator, X_select, y).mean()
scores.append(once)
plt.plot(ks,scores)
plt.show()
关于卡方检验,F检验和互信息的内容可以参考文章,一般来说,互信息要比F检验和卡方检验来要的有效,因为后者一般只反映了线性关系。
2 通过模型的嵌入式特征选择
直接通过模型选择的方式往往比利用特征量过滤筛选来得更有效,这也是一种嵌入式的思想,这里也需要调参,特征的重要度threshold,我们可以用cv对所有特征的重要度范围进行筛选,选择合适的阈值。
from sklearn.feature_selection import SelectFromModel
estimator = SVC()
thresholds = np.linspace(0, (estimator.fit(X_train, y_train).feature_importances_).max(),20)
score = []
for i in thresholds:
X_embeded = SelectFromModel(estimator, threshold=i).fit_transform(X_train,y_train)
once = cross_val_score(estimator, X_embeded, y_train, cv=10).mean()
score.append(once)
plt.figure(figsize=(15,7))
plt.plot(thresholds,score)
plt.show()
best_threshold = thresholds[np.argmax(score)]
3 通过递归包装选择
最后一种方式,是将前两种方式相结合的方式,本质是嵌入式的方式,但是也包装了过滤筛选的方法,函数设定一个step值,每轮对特征进行全排列采样(每次筛选n-step个特征,每次不重复),并利用模型进行计算,计算结果再进行排序,将评价指标较低step的直接抛弃。每轮完成后将递归地调用下一轮计算,直到模型计算的评价指标不再上升,或者满足了最小样本数量要求为止,sklearn提供了两个函数RFE和RFECV,后者在其中添加了cv验证,就不需要再单独使用cv了。
from sklearn.feature_selection import RFECV, RFE
estimator = SVC()
rfe = RFECV(estimator, min_features_to_select=10, step=1, cv=10)
rfe.fit(X, y)
print([i for i,v in enumerate(rfe.get_support()) if v])
X_wrapper = rfe.transform(X)
其中,min_features_to_select为留下的最少的特征数,step为每一步过滤掉的特征数。
递归包装选择虽然高效,但是由于需要递归调用,所以需要比较长的计算时间。
总结
每种方法都不难理解,每种方法都有其优缺点:
(1)统计量过滤筛选:计算快速,但比较粗糙
(2)模型嵌入选择:根据某一算法进行选择,针对性强,用该算法选择的特征来训练模型,可以有效提高训练效果,缺点是需要优化阈值进行筛选,但结果在测试中更容易过拟合一些(高方差)。
(3)包装选择:同样继承了嵌入法的特点,同时不容易丢弃重要的特征组合情况,但是由于需要递归筛选,计算量很大,在一些大的数据集或复杂的算法中需要时间很长。
一般在数据量(维度)大时候,优先采用方差检验和互信息过滤,逻辑回归优先采用嵌入法,SVM优先采用包装法。具体情况再做具体调整。