特征选取是机器学习领域非常重要的一个方向。
主要有两个功能:
(1)减少特征数量、降维,使模型泛化能力更强,减少过拟合
(2)增强度特征和特征值之间的理解
几种常用的特征选取方法
一、去掉取值变化小的特征
考察某个特征下,样本的方差值,可以人为给定一个阈值,抛开那些小于这个阈值的特征。
二、单变量特征选择
单变量特征选择的出发点是计算某一特征和分类变量之间的关系,以此计算每一特征的得分,抛开那些排名靠后的特征。比较经典的方法是卡方检验。
(1)peason相关系数,缺点:只对线性相关系数敏感
(2)距离相关系数
比较: 第一、Pearson相关系数计算速度快,这在处理大规模数据的时候很重要。第二、Pearson相关系数的取值区间是[-1,1],距离相关系数都是[0,1]。这个特点使得Pearson相关系数能够表征更丰富的关系,符号表示关系的正负,绝对值能够表示强度。当然,Pearson相关性有效的前提是两个变量的变化关系是单调的。
(3)基于学习模型的特征排序
这种方法的思路是直接使用你要用的机器学习算法,针对每个单独的特征和响应变量建立预测模型
Pearson相关系数等价于线性回归里的标准化回归系数
三、线性模型和正则化
单变量特征选择方法独立的衡量每个特征与响应变量之间的关系,另一种主流的特征选择方法是基于机器学习模型的方法。可以对特征打分叫做wrapper类型,不能打分的称为filter类型。
多重共线性:多个互相关联的特征,这时候模型就会变得不稳定,数据中细微的变化就可能导致模型的巨大变化(模型的变化本质上是系数,或者叫参数,可以理解成W),这会让模型的预测变得困难。???
(1)正则化模型
正则化就是把额外的约束或者惩罚项加到已有模型(损失函数)上,以防止过拟合并提高泛化能力
L1正则化:
L2正则化:
L2正则化将系数向量的L2范数添加到了损失函数中。由于L2惩罚项中系数是二次方的,这使得L2和L1有着诸多差异,最明显的一点就是,L2正则化会让系数的取值变得平均。对于关联特征,这意味着他们能够获得更相近的对应系数。还是以Y=X1+X2为例,假设X1和X2具有很强的关联,如果用L1正则化,不论学到的模型是Y=X1+X2还是Y=2X1,惩罚都是一样的,都是2alpha。但是对于L2来说,第一个模型的惩罚项是2alpha,但第二个模型的是4*alpha。可以看出,系数之和为常数时,各系数相等时惩罚是最小的,所以才有了L2会让各个系数趋于相同的特点。
可以看出,L2正则化对于特征选择来说一种稳定的模型,不像L1正则化那样,系数会因为细微的数据变化而波动。所以L2正则化和L1正则化提供的价值是不同的,L2正则化对于特征理解来说更加有用:表示能力强的特征对应的系数是非零。
from sklearn.linear_model import Ridge from sklearn.metrics import r2_score size = 100 #We run the method 10 times with different random seeds for i in range(10): print "Random seed %s" % i np.random.seed(seed=i) 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) X3 = X_seed + np.random.normal(0, .1, size) Y = X1 + X2 + X3 + np.random.normal(0, 1, size) X = np.array([X1, X2, X3]).T lr = LinearRegression() lr.fit(X,Y) print "Linear model:", pretty_print_linear(lr.coef_)
四、随机森林
随机森林具有准确率高、鲁棒性好、易于使用等优点,这使得它成为了目前最流行的机器学习算法之一。随机森林提供了两种特征选择的方法:mean decrease impurity和mean decrease accuracy。
(1)平均不纯度减少
这里特征得分实际上采用的是Gini Importance。使用基于不纯度的方法的时候,要记住:1、这种方法存在偏向,对具有更多类别的变量会更有利;2、对于存在关联的多个特征,其中任意一个都可以作为指示器(优秀的特征),并且一旦某个特征被选择之后,其他特征的重要度就会急剧下降,因为不纯度已经被选中的那个特征降下来了,其他的特征就很难再降低那么多不纯度了,这样一来,只有先被选中的那个特征重要度很高,其他的关联特征重要度往往较低。在理解数据时,这就会造成误解,导致错误的认为先被选中的特征是很重要的,而其余的特征是不重要的,但实际上这些特征对响应变量的作用确实非常接近的(这跟Lasso是很像的)。
需要注意的一点是,关联特征的打分存在不稳定的现象,这不仅仅是随机森林特有的,大多数基于模型的特征选择方法都存在这个问题。
(2)平均精确率减少
另一种常用的特征选择方法就是直接度量每个特征对模型精确率的影响。主要思路是打乱每个特征的特征值顺序,并且度量顺序变动对模型的精确率的影响。很明显,对于不重要的变量来说,打乱顺序对模型的精确率影响不会太大,但是对于重要的变量来说,打乱顺序就会降低模型的精确率。
from sklearn.cross_validation import ShuffleSplit from sklearn.metrics import r2_score from collections import defaultdict X = boston["data"] Y = boston["target"] rf = RandomForestRegressor() scores = defaultdict(list) #crossvalidate the scores on a number of different random splits of the data for train_idx, test_idx in ShuffleSplit(len(X), 100, .3): X_train, X_test = X[train_idx], X[test_idx] Y_train, Y_test = Y[train_idx], Y[test_idx] r = rf.fit(X_train, Y_train) acc = r2_score(Y_test, rf.predict(X_test)) for i in range(X.shape[1]): X_t = X_test.copy() np.random.shuffle(X_t[:, i]) shuff_acc = r2_score(Y_test, rf.predict(X_t)) scores[names[i]].append((acc-shuff_acc)/acc) print "Features sorted by their score:" print sorted([(round(np.mean(score), 4), feat) for feat, score in scores.items()], reverse=True)
五、两种顶层特征算法特征选择
(1)稳定性选择
稳定性选择是一种基于二次抽样和选择算法相结合较新的方法,选择算法可以是回归、SVM或其他类似的方法。它的主要思想是在不同的数据子集和特征子集上运行特征选择算法,不断的重复,最终汇总特征选择结果,比如可以统计某个特征被认为是重要特征的频率(被选为重要特征的次数除以它所在的子集被测试的次数)
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)
(2)递归特征消除
递归特征消除的主要思想是反复的构建模型(如SVM或者回归模型)然后选出最好的(或者最差的)的特征(可以根据系数来选),把选出来的特征放到一遍,然后在剩余的特征上重复这个过程,直到所有特征都遍历了。这个过程中特征被消除的次序就是特征的排序。因此,这是一种寻找最优特征子集的贪心算法。
from sklearn.feature_selection import RFE from sklearn.linear_model import LinearRegression 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))
总结:
(1)对于理解数据、数据的结构、特点来说,单变量特征选择是个非常好的选择。尽管可以用它对特征进行排序来优化模型,但由于它不能发现冗余(例如假如一个特征子集,其中的特征之间具有很强的关联,那么从中选择最优的特征时就很难考虑到冗余的问题)。
(2)正则化的线性模型对于特征理解和特征选择来说是非常强大的工具。L1正则化能够生成稀疏的模型,对于选择特征子集来说非常有用;相比起L1正则化,L2正则化的表现更加稳定,由于有用的特征往往对应系数非零,因此L2正则化对于数据的理解来说很合适。由于响应变量和特征之间往往是非线性关系,可以采用basis expansion的方式将特征转换到一个更加合适的空间当中,在此基础上再考虑运用简单的线性模型。
(3)随机森林是一种非常流行的特征选择方法,它易于使用,一般不需要feature engineering、调参等繁琐的步骤,并且很多工具包都提供了平均不纯度下降方法。它的两个主要问题,1是重要的特征有可能得分很低(关联特征问题),2是这种方法对特征变量类别多的特征越有利(偏向问题)。尽管如此,这种方法仍然非常值得在你的应用中试一试。
(4)特征选择在很多机器学习和数据挖掘场景中都是非常有用的。在使用的时候要弄清楚自己的目标是什么,然后找到哪种方法适用于自己的任务。当选择最优特征以提升模型性能的时候,可以采用交叉验证的方法来验证某种方法是否比其他方法要好。当用特征选择的方法来理解数据的时候要留心,特征选择模型的稳定性非常重要,稳定性差的模型很容易就会导致错误的结论。对数据进行二次采样然后在子集上运行特征选择算法能够有所帮助,如果在各个子集上的结果是一致的,那就可以说在这个数据集上得出来的结论是可信的,可以用这种特征选择模型的结果来理解数据。