catboost特征重要性_特征选择总结之嵌⼊式特征选择(附代
码)part1
嵌⼊式特征选择⽅法,号称结合了过滤式和包裹式的优点,将特征选择嵌⼊到模型构建的过程中:
民族团结故事这是特征选择的⼀整个流程的总结,所谓嵌⼊式特征选择,就是通过⼀些特殊的模型拟合数据然后根据模型⾃⾝的某些对于特征的评价的属性来作为评价指标,最后再使⽤包裹式的特征选择⽅法来选择,当然,很多时候我们还是仅停留在计算出评价指标的阶段,因为包裹式特征选择的最⼤问题就是计算量和时间是三者之中最⼤的。
最常⽤的进⾏嵌⼊式特征选择的模型:树模型和带正则项的模型(线性回归、逻辑回归、svm、svr、神经⽹络等)。
鉴于最近在写回归类模型的⾯经,就先从这类模型的特征选择⽅法开始说起好了。⾸先最常⽤的就是⼴义线性回归⾥的L1正则化。
基于L1正则项的嵌⼊式特征选择
下⾯以lasso为例
import pandas as pd
from sklearn.datats import load_boston
from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
X=pd.DataFrame(load_boston().data)
y=load_boston().target
sd=StandardScaler()
X=sd.fit_transform(X)
coefs=[]
for alpha in [0.01,0.05,0.1,0.5,1]:
lr=Lasso(alpha=alpha)
lr.fit(X,y)
coefs.f_)
coefs=pd.DataFrame(coefs)
这⾥就存在⼀个很严重的问题了,我们取了5组不同的的正则化系数的情况下,得到的特征重要性(也就是线性模型的权重值)的变化情况很严重,⽐如从上往下数第4、5个特征的变动幅度太⼤了,虽然越⼤的正则化系数越容易得到⼩的权重系数,但是问题是这⾥的第4、5个特征的相对其它特征的权重系数的取值的排序也发⽣了⾮常⼤的变化,尤其是第5个特征,在正则化系数取值为0.01的时候其权重系数为-14.3945,可以说负贡献很⼤,但是当正则化系数为其他值是,权重系数⼤幅下降甚⾄变成了0。
这⾥的发⽣这种现象的原因有很多,⽐如存在多重共线性问题会导致权重的⽅差变化增⼤,通过查看原始数据的相关系数表可以看出,这列特征和其它特征存在较⼤的相关关系:
图中列名为4的列为权重变化很⼤的特征,可以看出它和其它特征之间存在着较强的相关性
当然,不仅仅是共线性的问题,不同的数据集本⾝就会产⽣不同的特征重要性判断,这个在后⾯的树模型的feature importance中也会有这样的问题,⽽且数据量越⼩越容易出⼳蛾⼦,关于更加详细系统的lasso的这种不稳定的原因,后续会写到回归类⾯经的专栏⾥,以
关于更加详细系统的lasso的这种不稳定的原因,后续会写到回归类⾯经的专栏⾥,以及多重共线性的相关的问题,这⾥不赘述了,因为要写起来太特么多了。
那么这⾥我们应该怎么解决这个问题?我们到底应该依赖与哪⼀个正则化系数给出的判定的结果。
1、思路⼀
lassocv的思路给出了⼀个⽐较暴⼒的解决⽅案,尝试⼤量的正则化系数,然后选择损失函数最⼩的模型对应的正则化系数。简单说,也就是对于上述的不稳定性问题采取⽆视的态度。就是取泛化性能最好的模型的特征重要性判断结果。
lr=LassoCV(alphas=np.linspace(0,1,100),cv=3)
lr.fit(X,y)
国际风筝节print(lr.alpha_) #这⾥的alpha为最佳的alpha值
print(lr.m_path_) #输出每⼀个alpha对应的交叉验证的m的值
类似的逻辑回归也有类似的LogisticRegressionCV的⽅法,原理基本是类似的,这⾥就不赘述了,⾃⼰去看官⽹的api就⾏。
不仅仅是线性回归和逻辑回归,任何⼴义线性模型如FM/FFM,神经⽹络,都可以使⽤L1正则项对损失函数施加惩罚项。
思路⼆
类似于集成的思想,使⽤stable lection,稳定性特征选择。
stability_lection.randomized_lasso - stability-lection 0.1.0 documentation t huijskens.github.io
稳定性特征选择,以lasso为例,实际上就是每次采样部分数据,并且在给定范围内随机选择⼀个正则化系数值来拟合输⼊数据与标签然后得到⼀串的权重系数。最后将不同样本⼦集,不同正则化系数得到的模型的所有权重系数的结果进⾏简单平均。懒的写代码了,源代码很简单,看看上⾯的官⽹就ok了。使⽤的库是scikit -contrib下的stable-lection,不过思路很简单,⾃⼰⼿写也⾏。
另外还有⼀个randomizedlasso的思路,使⽤不同的正则化系数⽣成不同的模型得到不同的特征重要
性评价然后取平均但是实际情况中发现这种⽅法的效果有限计算量还挺⼤,不如思路⼀来的简单粗暴所以不赘述,感兴趣的⾃⼰去看stable_lection的源代码就可以了,很easy的。
⼴义线性回归这块的差不多就写这么多,神经⽹络、svm之类的,思路基本类似,思路⼀相对来说更常见吧,反正也是调调包的问题,再展开写也没什么意义,⽹上教程⼀⼤堆,懒得写了,《那就这样吧》。
基于树模型的嵌⼊式特征选择及其变种
之所以说存在变种是因为还有boruta、null importance、eli5、shap、xgbfi等各种解决⽅案,从⼀般到特征的介绍吧。
最简单最原始的,xgboost、lightgbm、catboost,randomforest。。。。等,取其中⼀种集成树的python库定义⼀个模型,然后去拟合输⼊输出得到训练完毕的模型,输出特征的重要性即可。这张⽅法的好处在于,我们不需要额外的去做特征选择,因为模型训练的过程中⾃⾝已经完成了特征选择,得到了不同特征的评价得分(⽐较常⽤的是信息增益和分类使⽤次数,xgb貌似还有不少别的指标),然后根据这些得分的⼤⼩就可得到不同特征针对对应的模型的特征的贡献度了。
import lightgbm as lgb
from sklearn.datats import load_iris
X=load_iris().data
放鸡岛y=load_iris().target
clf=lgb.LGBMClassifier()
clf.fit(X,y)
print(clf.feature_importances_)
X的四个特征的分裂使⽤总次数,可以看出特征3的重要性最强。
需要注意的地⽅:⽤于评估特征重要性时候⾏列采样最好都设置为1
这个⽅法的问题在于,不稳定,稳定性的计算结果和训练集数据息息相关,这也就意味着特征重要性也会存在类似过拟合的现象,在A数据集中计算出⼀个结果,在B数据集中计算出另⼀个结果。
import numpy as np
import lightgbm as lgb
from sklearn.datats import load_iris
del_lection import StratifiedKFold
sfk=StratifiedKFold(5)
X=load_iris().data
y=load_iris().target
clfs=[]
for train_index,test_index in sfk.split(X,y):
X_train,X_test=X[train_index],X[test_index]
Y_train,Y_test=y[train_index],y[test_index]
clf=lgb.LGBMClassifier()
clf.fit(X_train,Y_train)
clfs.append(clf)
for clf in clfs:
print(clf.feature_importances_)
feature_s(X.shape[0])
for clf in clfs:
feature_importances+=clf.feature_importances_/5.0
可以看到,四个特征的特征重要性得分的先后顺序在不同的⼦模型中都存在⼀些差异,这⾥作为⼀种弥补的⽅法,我们可以对这5个⼦模型的特征重要性进⾏求和平均,当然如果⽤10折也可以,结果会更加稳定⼀些。
然⽽,这个时候⼜有另外⼀个问题了,我们怎么设定阈值来作为衡量特征是否有效⽆效的问题,⽐如说我们根据前⾯的⽅法得到了⼀组特征的重要性为【100,50,25,0,15,139,75】,那么这种简
单的“clf.faeture_importances”的⽅法并不会告诉我们怎么选择阈值,所以,后来⼜诞⽣了boruta、null importance、eli5等⽅式来解决这个问题。
null importance没有严格的理论证明,它是由⼀个kaggle上的⼤佬选⼿发明的⽅法,旨在通过引⼊原始特征的shuffle来作为判断特征重要性的标准。
需要强调的是,我们在进⾏feature importance特征重要性判定的时候,
再此之前还有另外⼀种思路,向原始数据中添加噪声项,然后以噪声项的特征重要性为衡量的标准:
del_lection import StratifiedKFold
sfk=StratifiedKFold(5)
X=load_iris().data心情歌词
y=load_iris().target
clfs=[]
X=pd.DataFrame(X,columns=load_iris().feature_names)
X['noi_1']=al(size=len(X))
for train_index,test_index in sfk.split(X,y):
X_train,X_test=X.iloc[train_index],X.iloc[test_index]
Y_train,Y_test=y[train_index],y[test_index]
clf=lgb.LGBMClassifier()
clf.fit(X_train,Y_train)鸡腿怎么烧
clfs.append(clf)
关于春联的小知识feature_s(X.shape[1])
for clf in clfs:
feature_importances+=clf.feature_importances_
feature_importances=pd.DataFrame(feature_importances)
feature_importances.lumns
哈哈哈哈哈哈,加⼊的噪声项居然⽐原始数据中的length和两个width特征还要好哈哈哈
为了避免某次正好⽣成了很好的噪声特征影响结果,我⼜进⾏了10次测试并且⽣成了50个模型计算均值来观察结果。win10系统更新
del_lection import StratifiedKFold
sfk=StratifiedKFold(5)
X=load_iris().data
班风学风
y=load_iris().target
clfs=[]
for _ in range(10):
X=pd.DataFrame(X,columns=load_iris().feature_names)
X['noi_1']=al(size=len(X))
for train_index,test_index in sfk.split(X,y):
X_train,X_test=X.iloc[train_index],X.iloc[test_index]
Y_train,Y_test=y[train_index],y[test_index]
clf=lgb.LGBMClassifier()
clf.fit(X_train,Y_train)
clfs.append(clf)
feature_s(X.shape[1])
for clf in clfs:
feature_importances+=clf.feature_importances_
feature_importances=pd.DataFrame(feature_importances)
feature_importances.lumns
打扰了打扰了
(补充:⼀般情况下,特征选择势必要删除部分特征,导致的结果就是偏差必然是不减少的,也就是模型在特征选择完毕之后的训练集上的评价指标不会⼤于特征选择前的训练集上的评价指标,特征选择的作⽤是能够降低特征的维度,降低⽅差,增⼤偏差,减少过拟合的风险,但是不代表⼀定能够降低测试集的表现—即泛化误差,⽐如上⾯的例⼦就很典型了,你把通过这种特征选择的⽅法删⼀删试
试就知道了精度下降死你)
接下来我们⽤null importance来试试。null importance的思路不是添加新的噪声项,直接把原始标签shuffle⼀下。。。就可以了,这样就达到了把所有特征相对于表现都shuffle的效果了,真聪明呢。然后直观的思路就是⽐较shuffle之后和shuffle之前的feature importance的⼤⼩,shuffle之后的特征统⼀都叫做原始特征的null feature。和前⾯的噪声⽅法⼀样,我们这⾥选择shuffle 50轮!(侧⾯也暴露了这种⽅法的问题就是为了尽量消除随机性的影响要进⾏多次测试,但是对于数据量很⼤的问题来说,要耗费不少时间)
这⾥我们就能得到shuffle之后的null feauture的特征重要性值了,如果shuffle 50次,则每个feature都会有50个shuffle之后的feature importance,我们可以将原始特征的特征重要性和这50个null importance的均值⽐较,或者分位数⽐较,不过创始⼈oliver⽤的是这个⽅法:
⽤的是四分之三位数,代码如下