在前一篇文章中我介绍了一些数据预处理的方法,原始数据在经过预处理之后可以被算法处理了,但是实际中可能有一些特征是没有必要的,比如在中国采集的一些数据,那么国籍就都是中国,其实也就没有意义了,反映在统计量上就是方差过小,也就是样本在这个特征上变化很小。还有一种情况是特征和最后的结果相关性很小,也就是这个特征不起作用,衡量这种相关性我们可以用卡方检验,F-检验以及互信息等。其实很多sklearn的算法本身带有coef_和feature_importance_属性,而这个属性就可以被利用来筛选特征。前面说过的方法其实都是在已有特征的基础上排除特征的方法,但是在实际中我们很多时候需要自己构造特征,构造出好的特征可以大大提升模型的性能,对于这方面我就不是很了解了,毕竟我也是个新手,没有太多经验。
sklearn中VarianceThreshold可以起到这个作用
from sklearn.feature_selection import VarianceThreshold
sel = VarianceThreshold(0.2)
sel.fit_transform(data)
上述代码起到移除方差小于0.2的特征的作用(只起到示意作用)。
单一变量选择就是通过某种得分来度量相关性,进而选择特征,sklearn中有两个比较常用
. SelectKBest :选择前k个最好的特征,也就是得分最高的K个
. SelectPercentile :选择前百分之几的特征,这个百分比由用户指定
所以从上面就可以看出必须要有一个方法来衡量这种相关性,来传给上面的两个方法,才能够做出选择。衡量方法也就是上面提到的,卡方检验,F-检验以及互信息。这几种方法在sklearn中是有专门的实现的,再进行单一变量特征选择的时候将他们作为参数传递进去。下面以卡方检验为例
>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectKBest
>>> from sklearn.feature_selection import chi2
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X.shape
(150, 4)
>>> X_new = SelectKBest(chi2, k=2).fit_transform(X, y)
>>> X_new.shape
(150, 2)
作为参数的打分的函数对于回归和分类是不同的:
-回归:f_regression, mutual_info_regression
-分类:chi2, f_classif, mutual_info_classif
值得注意的是互信息(mutual_info_regression, mutual_info_classif)可以得到特征和最后的结果之间的非线性的相关性,而卡方检验和F-检验应该只能够判断线性性。互信息的公式是
递归特征消除(RFE)很好理解,给定一个模型,要求这个模型要能够给出coef_或者feature_importance_,然后我们就能够根据训练的这些相关性特征删除得分最差的特征,然后再一次训练,重复这个过程,直到最后满足我们预设的特征个数。还可以用RFECV通过交叉验证找出最佳的特征个数。下面是一个示例。
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数
RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(iris.data, iris.target)
SlectFormModel也就是从模型中选择,感觉上和上面的递归特征消除有点相似,它是一个“元转换器”,可以和任何带有coef_或者feature_importance_的模型一起使用。另外我们会指定一个参数“阈值”,小于这个阈值的特征被认为是不重要的,并且会被移除,不像是RFE会反复地拟合模型每次移除一个。大致用法如下
clf = LassoCV()
# Set a minimum threshold of 0.25
sfm = SelectFromModel(clf, threshold=0.25)
sfm.fit(X, y)
n_features = sfm.transform(X).shape[1]
上面用的是LassoCV结合SelectFromModel进行特征选择。
带有L1惩罚项的线性模型容易产生稀疏解,那么我们可以让SelectFromModel和这种模型相结合,最后选择相关系数不为0的特征就行。一般的线性模型有Lasso,LogisiticRegression以及LinearSVC
>>> from sklearn.svm import LinearSVC
>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectFromModel
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X.shape
(150, 4)
>>> lsvc = LinearSVC(C=0.01, penalty="l1", dual=False).fit(X, y)
>>> model = SelectFromModel(lsvc, prefit=True)
>>> X_new = model.transform(X)
>>> X_new.shape
(150, 3)
对于SVC和LogisticRegression越小的C,越少的特征被选择。对于Lasso,越大的alpha越少的特征被选择。具体是什么原因,我也不知道。
这个方法的原理也是类似的,因为树在生成过程中一定也会选择特征。回忆一下决策树,不论是ID3,C4.5还是CART开始的时候都是先找一个最能够区分数据集的特征,CART是二叉树,第一步要找出最优特征和最优切分点,之后不断循环。这个过程不就是筛选出最优特征的过程吗?不过在应用中,代码还是和上面类似,和SelectFromModel已启用。
>>> from sklearn.ensemble import ExtraTreesClassifier
>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectFromModel
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X.shape
>(150, 4)
>>> clf = ExtraTreesClassifier()
>>> clf = clf.fit(X, y)
>>> clf.feature_importances_
array([ 0.04..., 0.05..., 0.4..., 0.4...])
>>> model = SelectFromModel(clf, prefit=True)
>>> X_new = model.transform(X)
>>> X_new.shape
(150, 2)
以上是一些特征筛选的方法,另外还有特征的构造,但本人经验不足,在此就略去这方面的论述了。