有时候特征太多了也是一个问题,容易过拟合。为了降低模型的复杂程度,降低过拟合的可能性,我们会减少需要的学习的特征。
特征选择法主要有三种,分别是过滤法,包裹法和嵌入法。
1.过滤法
这种方法主要是基于统计检验,根据特征之间的关系去掉具有线性相关的特征,还有根据特征和标签的关系,筛选关系大的特征。
这种方法的好处是具有通用性,不管什么算法都可以这么用。但是问题是这种方法没有考虑算法的特殊性,可能某些算法就是喜欢那些不起眼的特征,有这些特征,它的表现就更好。
简单介绍几种过滤法。
(1).方差阈值法
这种方法就是计算各个特征的方差,然后把方差过低的特征删除,为什么用这个办法?因为如果一个特征全都是一个数值,说明它对分类毫无意义,而这样的特征方差为0,当然这是一种极端情况,但是特征毫无波动,那它确实没什么用。sklearn提供了VarianceThreshold这个方法;
(2).假设检验法
这种一般是使用假设检验进行特征选择,消灭掉P值大于0.05的特征,这种教条主义的P值实际上经常帮倒忙,但是偶尔也会有不错的结果。
sklearn提供了卡方检验chi2,f检验r_regression等等,这些可以结合SelectFpr,SelectFdr,SelectFwe等方法使用
(3).特征相关性
删掉线性相关的特征,我觉得这个方法包含很多种,比如特征之间的互信息,皮尔逊相关系数等等
(4).Fish得分
这种方法有点类似LDA的思想,某一类的特征可能更集中,而不属于这个类的特征就要离得远一些。
还有一些方法,不仅上面提到的。可以再训练分类器之前就进行特征选择的方法,都属于过滤法。
2.包裹法
前向选择,后向选择,分支界定法,递归特征选择法都属于嵌入法,这种方法和特定的算法有一定的交互性。
sklearn提供了递归特征选择法RFE,RFECV,还有前向或者后向选择SequentialFeatureSelector,RFECV是RFE的交叉检验版本,SequentialFeatureSelector可以选择是使用前向还是后向选择,缺点是它居然没有记录每一次的评分。RFE必须带有特征重要性评分的分类器使用,对于基于高斯内核的支持向量机救没办法用了,而具有评分的分类器有树类型的分类器,线性回归等等。
所谓评分就是各个特征的重要评分,比如线性回归的参数就代表了各个特征的重要性,决策树再分裂节点的时候使用的特征具有的信息增益大小等等,具有这些的分类器才能使用RFE,但是SequentialFeatureSelector不用。
from mlxtend.feature_selection import SequentialFeatureSelector
from mlxtend.plotting import plot_sequential_feature_selection
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
#自定义一个评价函数,sklearn是按照分数越高越好来选特征的,所以我们取个负数
#FPR+FNR最大值为2
def score_fpr_fnr(model, X, y):
y_pred = model.predict(X)
mat = confusion_matrix(y, y_pred)
fpr = mat[0,1]/(mat[0,0]+mat[0,1])#假正类率,错误识别的负类除以整个负类
fnr = mat[1,0]/(mat[1,1] + mat[1,0])#假负类率
return 2 - fpr - fnr
#加载数据集二分类
data = load_breast_cancer()
dataset = data['data']
label = data['target']
#划分数据集
X_train, X_test, y_train, y_test = train_test_split(dataset, label, test_size=0.2, shuffle=42)
model = RandomForestClassifier()
val_score_list = []#记录每一次的交叉检验均值分数
test_score_list = []#记录每一次测试的分数
#每次要求的后向选择特征数量
for feature_number in range(1, X_train.shape[1]+1):
#不进行交叉验证,因为要算太久了,可以根据情况设置,设置自定义的评价方法
sfs = SequentialFeatureSelector(model, k_features=feature_number, forward=True,scoring=score_fpr_fnr, cv=None)
sfs.fit(X_train, y_train)
#训练分类器然后测试
X_train_transform = sfs.transform(X_train)
X_test_transform = sfs.transform(X_test)
model.fit(X_train_transform , y_train)
my_score= score_fpr_fnr(model,X_test_transform,y_test)#每一次选择特征的分数
#因为制作了一次交叉检验,所以是1,如果多次,就要循环取得
f_score = sfs.subsets_[1]['avg_score']
val_score_list .append(f_score)
test_score_list.append(my_score)
3.嵌入法
这种算法不具有迁移性,因为它与算法本身相关,比如线性回归算法,它的参数w代表了每一个特征的重要性,但是这种w无法用到决策树上面,由于这种方法是算法本身的一种性质,所以就是嵌入在算法内部的意思,因而叫嵌入法。
4.混合
以上三种方法可以进行混合使用,比如使用嵌入法对特征重要性排序,然后使用包裹法进行特征选择。
以后有空回来补充其他的代码