sklearnFeatureSelection - juedaiyuer/researchNote GitHub Wiki
sklearn的特征选择
关于数据集
IRIS数据集由Fisher在1936年整理,包含4个特征(Sepal.Length(花萼长度)、Sepal.Width(花萼宽度)、Petal.Length(花瓣长度)、Petal.Width(花瓣宽度)),特征值都为正浮点数,单位为厘米。目标值为鸢尾花的分类(Iris Setosa(山鸢尾)、Iris Versicolour(杂色鸢尾),Iris Virginica(维吉尼亚鸢尾))
特征选择主要有两个功能:
- 减少特征数量、降维,使模型泛化能力更强,减少过拟合
- 增强对特征和特征值之间的理解
拿到数据集,一个特征选择方法,往往很难同时完成这两个目的。通常情况下,我们经常不管三七二十一,选择一种自己最熟悉或者最方便的特征选择方法(往往目的是降维,而忽略了对特征和数据理解的目的)
结合scikit learn Feature selection 官方文档所做的日志
去掉取值变化小的特征
Removing features with low variance
这应该是最简单的特征选择方法了:假设某特征的特征值只有0和1,并且在所有输入样本中,95%的实例的该特征取值都是1,那就可以认为这个特征作用不大。如果100%都是1,那这个特征就没意义了。当特征值都是离散型变量的时候这种方法才能用,如果是连续型变量,就需要将连续变量离散化之后才能用,而且实际当中,一般不太会有95%以上都取某个值的特征存在,所以这种方法虽然简单但是不太好用。可以把它作为特征选择的预处理,先去掉那些取值变化小的特征,然后再从接下来提到的的特征选择方法中选择合适的进行进一步的特征选择。
单变量特征选择
Univariate feature selection
文档中的那些事
Page:863-16.6
单变量特征选择能够对每一个特征进行测试,衡量该特征和响应变量之间的关系,根据得分扔掉不好的特征。对于回归和分类问题可以采用卡方检验等方式对特征进行测试。
这种方法比较简单,易于运行,易于理解,通常对于理解数据有较好的效果(但对特征优化、提高泛化能力来说不一定有效);这种方法有许多改进的版本、变种。
常用的手段是计算皮尔森系数和互信息系数,皮尔森系数只能衡量线性相关性而互信息系数能够很好地度量各种相关性,但是计算相对复杂些
scikit-learn中提到的方法如下
SelectKBest (removes all but the k highest scoring features) 保留排名前k的特征
SelectPercentile (removes all but a user-specified highest scoring percentage of features) 保留前百分之多少的特征
using common univariate statistical tests for each feature: false positive rate SelectFpr, false discovery rate SelectFdr, or family wise error SelectFwe.
GenericUnivariateSelect allows to perform univariate feature selection with a configurable strategy. This allows to select the best univariate selection strategy with hyper-parameter search estimator.
提供了SelectKBest和SelectPercentile将各个特征进行评分,根据什么进行评分?
对于回归:f_regression, mutual_info_regression
对于分类:chi2, f_classif, mutual_info_classif
SelectFpr
class sklearn.feature_selection.SelectFpr(score_func=<function f_classif>, alpha=0.05)
Filter: Select the pvalues below alpha based on a FPR test.
FPR test stands for False Positive Rate test. It controls the total amount of false detections.
文档中的例子
单变量特征选择采用F-test方法为每一个特征评分
selector = SelectPercentile(f_classif, percentile=10)
查询每一个特征的评分的初始数据
>>> selector.pvalues_
array([ 1.66966919e-31, 1.32791652e-16, 3.05197580e-91,
4.37695696e-85, 9.59923284e-01, 6.89032244e-02,
8.81648912e-01, 7.33827233e-01, 6.23166180e-02,
9.50786144e-01, 5.35978280e-01, 2.14614838e-01,
1.76290884e-02, 6.77969031e-01, 9.73266367e-01,
1.04193245e-01, 5.06356821e-01, 4.27006672e-01,
8.09127248e-01, 6.49633825e-01, 4.63253850e-01,
3.78660855e-01, 7.43712803e-01, 8.58168975e-01])
评分的最终数据
>>> scores
array([ 3.40023500e-01, 1.75404693e-01, 1.00000000e+00,
9.31982956e-01, 1.96248043e-04, 1.28349453e-02,
6.04364703e-04, 1.48489812e-03, 1.33170253e-02,
2.42137266e-04, 2.99233890e-03, 7.38371720e-03,
1.93753746e-02, 1.86476675e-03, 1.30014138e-04,
1.08507528e-02, 3.26511592e-03, 4.08289928e-03,
1.01621552e-03, 2.06960729e-03, 3.69197830e-03,
4.65942261e-03, 1.42069433e-03, 7.33877072e-04])
描绘柱状图
plt.bar(X_indices - .45, scores, width=.2,
label=r'Univariate score ($-Log(p_{value})$)', color='darkorange')
matplotlib.pyplot.bar(left, height, width=0.8, bottom=None, hold=None, data=None, **kwargs)
left 参数
the x coordinates of the left sides of the bars
X_indices - .45
每一个柱状图偏移了一下,便于观察
svm特征选取
clf = svm.SVC(kernel='linear')
clf.fit(X, y)
sum函数,axis=1表示按行相加 , axis=0表示按列相加;svm的权重数据获取
svm_weights = (clf.coef_ ** 2).sum(axis=0)
svm_weights /= svm_weights.max()
F-test选取的特征,让SVM选取
相关概念
###皮尔森相关系数###
Pearson Correlation
皮尔森相关系数是一种最简单的,能帮助理解特征和响应变量之间关系的方法,该方法衡量的是变量之间的线性相关性,结果的取值区间为[-1,1],-1表示完全的负相关(这个变量下降,那个就会上升),+1表示完全的正相关,0表示没有线性相关
Pearson Correlation速度快、易于计算,经常在拿到数据(经过清洗和特征提取之后的)之后第一时间就执行。Scipy的pearsonr方法能够同时计算相关系数和p-value
import numpy as np
from scipy.stats import pearsonr
np.random.seed(0)
size = 300
x = np.random.normal(0, 1, size)
print "Lower noise", pearsonr(x, x + np.random.normal(0, 1, size))
print "Higher noise", pearsonr(x, x + np.random.normal(0, 10, size))
结果如下,当噪音比较小的时候,相关性很强,p-value很低
Lower noise (0.71824836862138408, 7.3240173129983507e-49)
Higher noise (0.057964292079338155, 0.31700993885324752)
Scikit-learn提供的f_regrssion方法能够批量计算特征的p-value,非常方便,参考sklearn的pipeline
缺陷
作为特征排序机制,他只对线性关系敏感。如果关系是非线性的,即便两个变量具有一一对应的关系,Pearson相关性也可能会接近0
互信息和最大信息系数
Mutual information and maximal information coefficient(MIC)
互信息直接用于特征选择其实不是太方便
- 它不属于度量方式,也没有办法归一化,在不同数据及上的结果无法做比较
- 对于连续变量的计算不是很方便(X和Y都是集合,x,y都是离散的取值),通常变量需要先离散化,而互信息的结果对离散化的方式很敏感
最大信息系数克服了这两个问题。它首先寻找一种最优的离散化方式,然后把互信息取值转换成一种度量方式,取值区间在[0,1]。minepy提供了MIC功能。
距离相关系数
Distance correlation
距离相关系数是为了克服Pearson相关系数的弱点而生的。在x和x^2这个例子中,即便Pearson相关系数是0,我们也不能断定这两个变量是独立的(有可能是非线性相关);但如果距离相关系数是0,那么我们就可以说这两个变量是独立的
R的energy包里提供了距离相关系数的实现,另外这是Python gist的实现
尽管有MIC和距离相关系数在了,但当变量之间的关系接近线性相关的时候,Pearson相关系数仍然是不可替代的
- Pearson相关系数计算速度快,这在处理大规模数据的时候很重要
- Pearson相关系数的取值区间是[-1,1],而MIC和距离相关系数都是[0,1]。这个特点使得Pearson相关系数能够表征更丰富的关系,符号表示关系的正负,绝对值能够表示强度。当然,Pearson相关性有效的前提是两个变量的变化关系是单调的。
基于学习模型的特征排序
Model based ranking
这种方法的思路是直接使用你要用的机器学习算法,针对每个单独的特征和响应变量建立预测模型。其实Pearson相关系数等价于线性回归里的标准化回归系数。假如某个特征和响应变量之间的关系是非线性的,可以用基于树的方法(决策树、随机森林)、或者扩展的线性模型等。基于树的方法比较易于使用,因为他们对非线性关系的建模比较好,并且不需要太多的调试。但要注意过拟合问题,因此树的深度最好不要太大,再就是运用交叉验证
线性模型和正则化
单变量特征选择方法独立的衡量每个特征与响应变量之间的关系
另一种主流的特征选择方法是基于机器学习模型的方法
有些机器学习方法本身就具有对特征进行打分的机制,或者很容易将其运用到特征选择任务中,例如回归模型,SVM,决策树,随机森林等等。说句题外话,这种方法好像在一些地方叫做wrapper类型,大概意思是说,特征排序模型和机器学习模型是耦和在一起的,对应的非wrapper类型的特征选择方法叫做filter类型。
回归模型
越是重要的特征在模型中对应的系数就会越大,而跟输出变量越是无关的特征对应的系数就会越接近于0。在噪音不多的数据上,或者是数据量远远大于特征数的数据上,如果特征之间相对来说是比较独立的,那么即便是运用最简单的线性回归模型也一样能取得非常好的效果
from sklearn.linear_model import LinearRegression
import numpy as np
np.random.seed(0)
size = 5000
#A dataset with 3 features
X = np.random.normal(0, 1, (size, 3))
#Y = X0 + 2*X1 + noise
Y = X[:,0] + 2*X[:,1] + np.random.normal(0, 2, size)
lr = LinearRegression()
lr.fit(X, Y)
#A helper method for pretty-printing linear models
def pretty_print_linear(coefs, names = None, sort = False):
if names == None:
names = ["X%s" % x for x in range(len(coefs))]
lst = zip(coefs, names)
if sort:
lst = sorted(lst, key = lambda x:-np.abs(x[0]))
return " + ".join("%s * %s" % (round(coef, 3), name)
for coef, name in lst)
print "Linear model:", pretty_print_linear(lr.coef_)
在很多实际的数据当中,往往存在多个互相关联的特征,这时候模型就会变得不稳定,数据中细微的变化就可能导致模型的巨大变化(模型的变化本质上是系数,或者叫参数,可以理解成W),这会让模型的预测变得困难,这种现象也称为多重共线性。例如,假设我们有个数据集,它的真实模型应该是Y=X1+X2,当我们观察的时候,发现Y’=X1+X2+e,e是噪音。如果X1和X2之间存在线性关系,例如X1约等于X2,这个时候由于噪音e的存在,我们学到的模型可能就不是Y=X1+X2了,有可能是Y=2X1,或者Y=-X1+3X2。
正则化模型
- sklearn正则化-个人wiki版本
随机森林的特征选择
随机森林具有准确率高,鲁棒性好,易于使用等优点,这使得它称为了目前最流行的机器学习算法之一。
特征打分方法
平均不纯度减少 mean decrease impurity
随机森林由多个决策树构成。决策树中的每一个节点都是关于某个特征的条件,为的是将数据集按照不同的响应变量一分为二。利用不纯度可以确定节点(最优条件),对于分类问题,通常采用 基尼不纯度 或者 信息增益 ,对于回归问题,通常采用的是 方差 或者最小二乘拟合。当训练决策树的时候,可以计算出每个特征减少了多少树的不纯度。对于一个决策树森林来说,可以算出每个特征平均减少了多少不纯度,并把它平均减少的不纯度作为特征选择的值。
sklearn中基于随机森林的特征重要度度量方法
from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor
import numpy as np
#Load boston housing dataset as an example
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]
rf = RandomForestRegressor()
rf.fit(X, Y)
print "Features sorted by their score:"
print sorted(zip(map(lambda x: round(x, 4), rf.feature_importances_), names),
reverse=True)
运行结果
Features sorted by their score:
[(0.4566, 'RM'), (0.3482, 'LSTAT'), (0.0696, 'DIS'), (0.033, 'CRIM'), (0.0249, 'NOX'), (0.0183, 'PTRATIO'), (0.0139, 'B'), (0.0138, 'AGE'), (0.0113, 'TAX'), (0.0062, 'INDUS'), (0.0027, 'RAD'), (0.0009, 'CHAS'), (0.0006, 'ZN')]
特征得分实际上采用了Gini Importance
使用基于不纯度的方法时,注意
- 这种方法存在偏向,对具有更多类别的变量会更有利
- 对于存在关联的多个特征,选择某个特征后,其它特征的重要度会急剧下降,导致错误的认为被选中的特征很重要,其余特征不重要,但实际上这些特征对响应变量的作用确实非常接近(很像Lasso)
互相关联的多个特征
一旦某个特征被选择之后,其他特征的重要度就会急剧下降,因为不纯度已经被选中的那个特征降下来了,其他的特征就很难再降低那么多不纯度了,这样一来,只有先被选中的那个特征重要度很高,其他的关联特征重要度往往较低。
解决方法
特征随机选择 方法稍微缓解了这个问题,但总的来说并没有完全解决
from sklearn.ensemble import RandomForestRegressor
import numpy as np
size = 10000
np.random.seed(seed=10)
X_seed = np.random.normal(0, 1, size)
X0 = X_seed + np.random.normal(0, .1, size)
X1 = X_seed + np.random.normal(0, .1, size)
X2 = X_seed + np.random.normal(0, .1, size)
X = np.array([X0, X1, X2]).T
Y = X0 + X1 + X2
rf = RandomForestRegressor(n_estimators=20, max_features=2)
rf.fit(X, Y);
print "Scores for X0, X1, X2:", map(lambda x:round (x,3),
rf.feature_importances_)
运行结果
Scores for X0, X1, X2: [0.272, 0.548, 0.179]
X1的重要度比X2要高,但实际上他们真正的重要度是一样的
数据量很大且没有噪音,并用了20颗树来做随机选择,但这个问题还是会存在
关联特征的打分存在不稳定的现象,不仅仅是随机森林特有的,大多数基于模型的特征选择方法都存在这个问题
平均准确度减少 Mean decrease accuracy
另一种常用的特征选择方法就是直接度量每个特征对模型精确率的影响
主要思路是打乱每个特征的特征值顺序,并且度量顺序变动对模型的精确率的影响。很明显,对于不重要的变量来说,打乱顺序对模型的精确率影响不会太大,但是对于重要的变量来说,打乱顺序就会降低模型的精确率
这个方法在sklearn中没有直接提供,但是很容易实现,下面继续在波士顿房价数据集上进行实现
两种顶层特征选择算法
之所以叫做顶层,是因为他们都是建立在基于模型的特征选择方法基础之上的,例如回归和SVM,在不同的子集上建立模型,然后汇总最终确定特征得分
稳定性选择 Stability selection
稳定性选择是一种基于二次抽样和选择算法相结合较新的方法,选择算法可以是回归、SVM或其他类似的方法。它的主要思想是在不同的数据子集和特征子集上运行特征选择算法,不断的重复,最终汇总特征选择结果,比如可以统计某个特征被认为是重要特征的频率(被选为重要特征的次数除以它所在的子集被测试的次数)。理想情况下,重要特征的得分会接近100%。稍微弱一点的特征得分会是非0的数,而最无用的特征得分将会接近于0。
sklearn在 随机lasso 和 随机逻辑回归 中有对稳定性选择的实现
随机lasso的波士顿房价的例子
from sklearn.linear_model import RandomizedLasso
from sklearn.datasets import load_boston
boston = load_boston()
#using the Boston housing data.
#Data gets scaled automatically by sklearn's implementation
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]
rlasso = RandomizedLasso(alpha=0.025)
rlasso.fit(X, Y)
print "Features sorted by their score:"
print sorted(zip(map(lambda x: round(x, 4), rlasso.scores_),
names), reverse=True)
运行结果
Features sorted by their score:
[(1.0, 'RM'), (1.0, 'PTRATIO'), (1.0, 'LSTAT'), (0.645, 'CHAS'), (0.555, 'B'), (0.385, 'TAX'), (0.385, 'CRIM'), (0.215, 'DIS'), (0.21, 'NOX'), (0.16, 'INDUS'), (0.07, 'ZN'), (0.035, 'RAD'), (0.02, 'AGE')]
在上边这个例子当中,最高的3个特征得分是1.0,这表示他们总会被选作有用的特征(当然,得分会收到正则化参数alpha的影响,但是sklearn的随机lasso能够自动选择最优的alpha)。接下来的几个特征得分就开始下降,但是下降的不是特别急剧,这跟纯lasso的方法和随机森林的结果不一样。能够看出稳定性选择对于克服过拟合和对数据理解来说都是有帮助的:总的来说,好的特征不会因为有相似的特征、关联特征而得分为0,这跟Lasso是不同的。对于特征选择任务,在许多数据集和环境下,稳定性选择往往是性能最好的方法之一
递归特征消除 Recursive feature elimination (RFE)
递归特征消除的主要思想是反复的构建模型(如SVM或者回归模型)然后选出最好的(或者最差的)的特征(可以根据系数来选),把选出来的特征放到一遍,然后在剩余的特征上重复这个过程,直到所有特征都遍历了。这个过程中特征被消除的次序就是特征的排序。因此,这是一种寻找最优特征子集的贪心算法。
RFE的稳定性很大程度上取决于在迭代的时候底层用哪种模型。例如,假如RFE采用的普通的回归,没有经过正则化的回归是不稳定的,那么RFE就是不稳定的;假如采用的是Ridge,而用Ridge正则化的回归是稳定的,那么RFE就是稳定的。
Sklearn提供了RFE包,可以用于特征消除,还提供了RFECV,可以通过交叉验证来对的特征进行排序。
from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression
from sklearn.datasets import load_boston
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]
#use linear regression as the model
lr = LinearRegression()
#rank all features, i.e continue the elimination until the last one
rfe = RFE(lr, n_features_to_select=1)
rfe.fit(X,Y)
print "Features sorted by their rank:"
print sorted(zip(map(lambda x: round(x, 4), rfe.ranking_), names))
运行结果
Features sorted by their rank:
[(1.0, 'NOX'), (2.0, 'RM'), (3.0, 'CHAS'), (4.0, 'PTRATIO'), (5.0, 'DIS'), (6.0, 'LSTAT'), (7.0, 'RAD'), (8.0, 'CRIM'), (9.0, 'INDUS'), (10.0, 'ZN'), (11.0, 'TAX'), (12.0, 'B'), (13.0, 'AGE')]
一个例子
数据产生的公式
Note
什么是 p-value?简单地说,p-value就是为了验证假设和实际之间一致性的统计学意义的值,即假设检验。有些地方叫右尾概率,根据卡方值和自由度可以算出一个固定的p-value,
知乎摘抄
作者:严林 链接:https://www.zhihu.com/question/28641663/answer/41653367 来源:知乎 著作权归作者所有,转载请联系作者获得授权。
特征选择是特征工程中的重要问题(另一个重要的问题是特征提取),坊间常说:数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。由此可见,特征工程尤其是特征选择在机器学习中占有相当重要的地位。
通常而言,特征选择是指选择获得相应模型和算法最好性能的特征集,工程上常用的方法有以下:
- 计算每一个特征与响应变量的相关性:工程上常用的手段有计算皮尔逊系数和互信息系数,皮尔逊系数只能衡量线性相关性而互信息系数能够很好地度量各种相关性,但是计算相对复杂一些,好在很多toolkit里边都包含了这个工具(如sklearn的MINE),得到相关性之后就可以排序选择特征了;
- 构建单个特征的模型,通过模型的准确性为特征排序,借此来选择特征,另外,记得JMLR'03上有一篇论文介绍了一种基于决策树的特征选择方法,本质上是等价的。当选择到了目标特征之后,再用来训练最终的模型;
- 通过L1正则项来选择特征:L1正则方法具有稀疏解的特性,因此天然具备特征选择的特性,但是要注意,L1没有选到的特征不代表不重要,原因是两个具有高相关性的特征可能只保留了一个,如果要确定哪个特征重要应再通过L2正则方法交叉检验;
- 训练能够对特征打分的预选模型:RandomForest和Logistic Regression等都能对模型的特征打分,通过打分获得相关性后再训练最终模型;
- 通过特征组合后再来选择特征:如对用户id和用户特征最组合来获得较大的特征集再来选择特征,这种做法在推荐系统和广告系统中比较常见,这也是所谓亿级甚至十亿级特征的主要来源,原因是用户数据比较稀疏,组合特征能够同时兼顾全局模型和个性化模型,这个问题有机会可以展开讲。
- 通过深度学习来进行特征选择:目前这种手段正在随着深度学习的流行而成为一种手段,尤其是在计算机视觉领域,原因是深度学习具有自动学习特征的能力,这也是深度学习又叫unsupervised feature learning的原因。从深度学习模型中选择某一神经层的特征后就可以用来进行最终目标模型的训练了。
整体上来说,特征选择是一个既有学术价值又有工程价值的问题,目前在研究领域也比较热,值得所有做机器学习的朋友重视。