精华内容
下载资源
问答
  • 特征选择方法

    万次阅读 2016-12-07 14:16:08
    通常情况下,我们经常不管三七二十一,选择一种自己最熟悉或者最方便的特征选择方法(往往目的是降维,而忽略了对特征和数据理解的目的)。 在许多机器学习相关的书里,很难找到关于特征选择的内容,因为特征选择...

    特征选择主要有两个功能:

    1. 减少特征数量、降维,使模型泛化能力更强,减少过拟合
    2. 增强对特征和特征值之间的理解

    拿到数据集,一个特征选择方法,往往很难同时完成这两个目的。通常情况下,我们经常不管三七二十一,选择一种自己最熟悉或者最方便的特征选择方法(往往目的是降维,而忽略了对特征和数据理解的目的)。

    在许多机器学习相关的书里,很难找到关于特征选择的内容,因为特征选择要解决的问题往往被视为机器学习的一种副作用,一般不会单独拿出来讨论。

    本文将结合 Scikit-learn提供的例子 介绍几种常用的特征选择方法,它们各自的优缺点和问题。

    1 去掉取值变化小的特征 Removing features with low variance

    这应该是最简单的特征选择方法了:假设某特征的特征值只有0和1,并且在所有输入样本中,95%的实例的该特征取值都是1,那就可以认为这个特征作用不大。如果100%都是1,那这个特征就没意义了。当特征值都是离散型变量的时候这种方法才能用,如果是连续型变量,就需要将连续变量离散化之后才能用,而且实际当中,一般不太会有95%以上都取某个值的特征存在,所以这种方法虽然简单但是不太好用。可以把它作为特征选择的预处理,先去掉那些取值变化小的特征,然后再从接下来提到的的特征选择方法中选择合适的进行进一步的特征选择。

    2 单变量特征选择 Univariate feature selection

    单变量特征选择能够对每一个特征进行测试,衡量该特征和响应变量之间的关系,根据得分扔掉不好的特征。对于回归和分类问题可以采用卡方检验等方式对特征进行测试。

    这种方法比较简单,易于运行,易于理解,通常对于理解数据有较好的效果(但对特征优化、提高泛化能力来说不一定有效);这种方法有许多改进的版本、变种。

    2.1 Pearson相关系数 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))

    Lower noise (0.71824836862138386, 7.3240173129992273e-49)Higher noise (0.057964292079338148, 0.31700993885324746)

    这个例子中,我们比较了变量在加入噪音之前和之后的差异。当噪音比较小的时候,相关性很强,p-value很低。

    Scikit-learn提供的 f_regrssion 方法能够批量计算特征的p-value,非常方便,参考sklearn的 pipeline

    Pearson相关系数的一个明显缺陷是,作为特征排序机制,他只对线性关系敏感。如果关系是非线性的,即便两个变量具有一一对应的关系,Pearson相关性也可能会接近0。

    x = np.random.uniform(-1, 1, 100000)
    print pearsonr(x, x**2)[0]

    -0.00230804707612

    更多类似的例子参考 sample plots 。另外,如果仅仅根据相关系数这个值来判断的话,有时候会具有很强的误导性,如 Anscombe’s quartet ,最好把数据可视化出来,以免得出错误的结论。

    2.2 互信息和最大信息系数 Mutual information and maximal information coefficient (MIC)

    以上就是经典的互信息公式了。想把互信息直接用于特征选择其实不是太方便:1、它不属于度量方式,也没有办法归一化,在不同数据及上的结果无法做比较;2、对于连续变量的计算不是很方便(X和Y都是集合,x,y都是离散的取值),通常变量需要先离散化,而互信息的结果对离散化的方式很敏感。

    最大信息系数克服了这两个问题。它首先寻找一种最优的离散化方式,然后把互信息取值转换成一种度量方式,取值区间在[0,1]。 minepy 提供了MIC功能。

    反过头来看y=x^2这个例子,MIC算出来的互信息值为1(最大的取值)。

    from minepy import MINE
    m = MINE()
    x = np.random.uniform(-1, 1, 10000)
    m.compute_score(x, x**2)
    print m.mic()

    1.0

    MIC的统计能力遭到了 一些质疑 ,当零假设不成立时,MIC的统计就会受到影响。在有的数据集上不存在这个问题,但有的数据集上就存在这个问题。

    2.3 距离相关系数 (Distance correlation)

    距离相关系数是为了克服Pearson相关系数的弱点而生的。在x和x^2这个例子中,即便Pearson相关系数是0,我们也不能断定这两个变量是独立的(有可能是非线性相关);但如果距离相关系数是0,那么我们就可以说这两个变量是独立的。

    R的 energy 包里提供了距离相关系数的实现,另外这是 Python gist 的实现。

    #R-code
    > x = runif (1000, -1, 1)
    > dcor(x, x**2)
    [1] 0.4943864

    尽管有MIC和距离相关系数在了,但当变量之间的关系接近线性相关的时候,Pearson相关系数仍然是不可替代的。第一、Pearson相关系数计算速度快,这在处理大规模数据的时候很重要。第二、Pearson相关系数的取值区间是[-1,1],而MIC和距离相关系数都是[0,1]。这个特点使得Pearson相关系数能够表征更丰富的关系,符号表示关系的正负,绝对值能够表示强度。当然,Pearson相关性有效的前提是两个变量的变化关系是单调的。

    2.4 基于学习模型的特征排序 (Model based ranking)

    这种方法的思路是直接使用你要用的机器学习算法,针对每个单独的特征和响应变量建立预测模型。其实Pearson相关系数等价于线性回归里的标准化回归系数。假如某个特征和响应变量之间的关系是非线性的,可以用基于树的方法(决策树、随机森林)、或者扩展的线性模型等。基于树的方法比较易于使用,因为他们对非线性关系的建模比较好,并且不需要太多的调试。但要注意过拟合问题,因此树的深度最好不要太大,再就是运用交叉验证。

    在 波士顿房价数据集 上使用sklearn的 随机森林回归 给出一个单变量选择的例子:

    from sklearn.cross_validation import cross_val_score, ShuffleSplit
    from sklearn.datasets import load_boston
    from sklearn.ensemble import RandomForestRegressor
    
    #Load boston housing dataset as an example
    boston = load_boston()
    X = boston["data"]
    Y = boston["target"]
    names = boston["feature_names"]
    
    rf = RandomForestRegressor(n_estimators=20, max_depth=4)
    scores = []
    for i in range(X.shape[1]):
         score = cross_val_score(rf, X[:, i:i+1], Y, scoring="r2",
                                  cv=ShuffleSplit(len(X), 3, .3))
         scores.append((round(np.mean(score), 3), names[i]))
    print sorted(scores, reverse=True)

    [(0.636, ‘LSTAT’), (0.59, ‘RM’), (0.472, ‘NOX’), (0.369, ‘INDUS’), (0.311, ‘PTRATIO’), (0.24, ‘TAX’), (0.24, ‘CRIM’), (0.185, ‘RAD’), (0.16, ‘ZN’), (0.087, ‘B’), (0.062, ‘DIS’), (0.036, ‘CHAS’), (0.027, ‘AGE’)]

    3 线性模型和正则化

    单变量特征选择方法独立的衡量每个特征与响应变量之间的关系,另一种主流的特征选择方法是基于机器学习模型的方法。有些机器学习方法本身就具有对特征进行打分的机制,或者很容易将其运用到特征选择任务中,例如回归模型,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_)
    

    Linear model: 0.984 * X0 + 1.995 * X1 + -0.041 * X2

    在这个例子当中,尽管数据中存在一些噪音,但这种特征选择模型仍然能够很好的体现出数据的底层结构。当然这也是因为例子中的这个问题非常适合用线性模型来解:特征和响应变量之间全都是线性关系,并且特征之间均是独立的。

    在很多实际的数据当中,往往存在多个互相关联的特征,这时候模型就会变得不稳定,数据中细微的变化就可能导致模型的巨大变化(模型的变化本质上是系数,或者叫参数,可以理解成W),这会让模型的预测变得困难,这种现象也称为多重共线性。例如,假设我们有个数据集,它的真实模型应该是Y=X1+X2,当我们观察的时候,发现Y’=X1+X2+e,e是噪音。如果X1和X2之间存在线性关系,例如X1约等于X2,这个时候由于噪音e的存在,我们学到的模型可能就不是Y=X1+X2了,有可能是Y=2X1,或者Y=-X1+3X2。

    下边这个例子当中,在同一个数据上加入了一些噪音,用随机森林算法进行特征选择。

    from sklearn.linear_model import LinearRegression
    
    size = 100
    np.random.seed(seed=5)
    
    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_)

    Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2

    系数之和接近3,基本上和上上个例子的结果一致,应该说学到的模型对于预测来说还是不错的。但是,如果从系数的字面意思上去解释特征的重要性的话,X3对于输出变量来说具有很强的正面影响,而X1具有负面影响,而实际上所有特征与输出变量之间的影响是均等的。

    同样的方法和套路可以用到类似的线性模型上,比如逻辑回归。

    3.1 正则化模型

    正则化就是把额外的约束或者惩罚项加到已有模型(损失函数)上,以防止过拟合并提高泛化能力。损失函数由原来的E(X,Y)变为E(X,Y)+alpha||w||,w是模型系数组成的向量(有些地方也叫参数parameter,coefficients),||·||一般是L1或者L2范数,alpha是一个可调的参数,控制着正则化的强度。当用在线性模型上时,L1正则化和L2正则化也称为Lasso和Ridge。

    3.2 L1正则化/Lasso

    L1正则化将系数w的l1范数作为惩罚项加到损失函数上,由于正则项非零,这就迫使那些弱的特征所对应的系数变成0。因此L1正则化往往会使学到的模型很稀疏(系数w经常为0),这个特性使得L1正则化成为一种很好的特征选择方法。

    Scikit-learn为线性回归提供了Lasso,为分类提供了L1逻辑回归。

    下面的例子在波士顿房价数据上运行了Lasso,其中参数alpha是通过grid search进行优化的。

    from sklearn.linear_model import Lasso
    from sklearn.preprocessing import StandardScaler
    from sklearn.datasets import load_boston
    
    boston = load_boston()
    scaler = StandardScaler()
    X = scaler.fit_transform(boston["data"])
    Y = boston["target"]
    names = boston["feature_names"]
    
    lasso = Lasso(alpha=.3)
    lasso.fit(X, Y)
    
    print "Lasso model: ", pretty_print_linear(lasso.coef_, names, sort = True)

    Lasso model: -3.707 * LSTAT + 2.992 * RM + -1.757 * PTRATIO + -1.081 * DIS + -0.7 * NOX + 0.631 * B + 0.54 * CHAS + -0.236 * CRIM + 0.081 * ZN + -0.0 * INDUS + -0.0 * AGE + 0.0 * RAD + -0.0 * TAX

    可以看到,很多特征的系数都是0。如果继续增加alpha的值,得到的模型就会越来越稀疏,即越来越多的特征系数会变成0。

    然而,L1正则化像非正则化线性模型一样也是不稳定的,如果特征集合中具有相关联的特征,当数据发生细微变化时也有可能导致很大的模型差异。

    3.3 L2正则化/Ridge regression

    L2正则化将系数向量的L2范数添加到了损失函数中。由于L2惩罚项中系数是二次方的,这使得L2和L1有着诸多差异,最明显的一点就是,L2正则化会让系数的取值变得平均。对于关联特征,这意味着他们能够获得更相近的对应系数。还是以Y=X1+X2为例,假设X1和X2具有很强的关联,如果用L1正则化,不论学到的模型是Y=X1+X2还是Y=2X1,惩罚都是一样的,都是2 alpha。但是对于L2来说,第一个模型的惩罚项是2alpha,但第二个模型的是4*alpha。可以看出,系数之和为常数时,各系数相等时惩罚是最小的,所以才有了L2会让各个系数趋于相同的特点。

    可以看出,L2正则化对于特征选择来说一种稳定的模型,不像L1正则化那样,系数会因为细微的数据变化而波动。所以L2正则化和L1正则化提供的价值是不同的,L2正则化对于特征理解来说更加有用:表示能力强的特征对应的系数是非零。

    回过头来看看3个互相关联的特征的例子,分别以10个不同的种子随机初始化运行10次,来观察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_)
      ridge = Ridge(alpha=10)
      ridge.fit(X,Y)
      print "Ridge model:", pretty_print_linear(ridge.coef_)
      print
    

    Random seed 0 Linear model: 0.728 * X0 + 2.309 * X1 + -0.082 * X2 Ridge model: 0.938 * X0 + 1.059 * X1 + 0.877 * X2

    Random seed 1 Linear model: 1.152 * X0 + 2.366 * X1 + -0.599 * X2 Ridge model: 0.984 * X0 + 1.068 * X1 + 0.759 * X2

    Random seed 2 Linear model: 0.697 * X0 + 0.322 * X1 + 2.086 * X2 Ridge model: 0.972 * X0 + 0.943 * X1 + 1.085 * X2

    Random seed 3 Linear model: 0.287 * X0 + 1.254 * X1 + 1.491 * X2 Ridge model: 0.919 * X0 + 1.005 * X1 + 1.033 * X2

    Random seed 4 Linear model: 0.187 * X0 + 0.772 * X1 + 2.189 * X2 Ridge model: 0.964 * X0 + 0.982 * X1 + 1.098 * X2

    Random seed 5 Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2 Ridge model: 0.758 * X0 + 1.011 * X1 + 1.139 * X2

    Random seed 6 Linear model: 1.199 * X0 + -0.031 * X1 + 1.915 * X2 Ridge model: 1.016 * X0 + 0.89 * X1 + 1.091 * X2

    Random seed 7 Linear model: 1.474 * X0 + 1.762 * X1 + -0.151 * X2 Ridge model: 1.018 * X0 + 1.039 * X1 + 0.901 * X2

    Random seed 8 Linear model: 0.084 * X0 + 1.88 * X1 + 1.107 * X2 Ridge model: 0.907 * X0 + 1.071 * X1 + 1.008 * X2

    Random seed 9 Linear model: 0.714 * X0 + 0.776 * X1 + 1.364 * X2 Ridge model: 0.896 * X0 + 0.903 * X1 + 0.98 * X2

    可以看出,不同的数据上线性回归得到的模型(系数)相差甚远,但对于L2正则化模型来说,结果中的系数非常的稳定,差别较小,都比较接近于1,能够反映出数据的内在结构。

    4 随机森林

    随机森林具有准确率高、鲁棒性好、易于使用等优点,这使得它成为了目前最流行的机器学习算法之一。随机森林提供了两种特征选择的方法:mean decrease impurity和mean decrease accuracy。

    4.1 平均不纯度减少 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.5298, ‘LSTAT’), (0.4116, ‘RM’), (0.0252, ‘DIS’), (0.0172, ‘CRIM’), (0.0065, ‘NOX’), (0.0035, ‘PTRATIO’), (0.0021, ‘TAX’), (0.0017, ‘AGE’), (0.0012, ‘B’), (0.0008, ‘INDUS’), (0.0004, ‘RAD’), (0.0001, ‘CHAS’), (0.0, ‘ZN’)]

    这里特征得分实际上采用的是 Gini Importance 。使用基于不纯度的方法的时候,要记住:1、这种方法存在 偏向 ,对具有更多类别的变量会更有利;2、对于存在关联的多个特征,其中任意一个都可以作为指示器(优秀的特征),并且一旦某个特征被选择之后,其他特征的重要度就会急剧下降,因为不纯度已经被选中的那个特征降下来了,其他的特征就很难再降低那么多不纯度了,这样一来,只有先被选中的那个特征重要度很高,其他的关联特征重要度往往较低。在理解数据时,这就会造成误解,导致错误的认为先被选中的特征是很重要的,而其余的特征是不重要的,但实际上这些特征对响应变量的作用确实非常接近的(这跟Lasso是很像的)。

    特征随机选择 方法稍微缓解了这个问题,但总的来说并没有完全解决。下面的例子中,X0、X1、X2是三个互相关联的变量,在没有噪音的情况下,输出变量是三者之和。

    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.278, 0.66, 0.062]

    当计算特征重要性时,可以看到X1的重要度比X2的重要度要高出10倍,但实际上他们真正的重要度是一样的。尽管数据量已经很大且没有噪音,且用了20棵树来做随机选择,但这个问题还是会存在。

    需要注意的一点是,关联特征的打分存在不稳定的现象,这不仅仅是随机森林特有的,大多数基于模型的特征选择方法都存在这个问题。

    4.2 平均精确率减少 Mean decrease accuracy

    另一种常用的特征选择方法就是直接度量每个特征对模型精确率的影响。主要思路是打乱每个特征的特征值顺序,并且度量顺序变动对模型的精确率的影响。很明显,对于不重要的变量来说,打乱顺序对模型的精确率影响不会太大,但是对于重要的变量来说,打乱顺序就会降低模型的精确率。

    这个方法sklearn中没有直接提供,但是很容易实现,下面继续在波士顿房价数据集上进行实现。

    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)
    

    Features sorted by their score: [(0.7276, ‘LSTAT’), (0.5675, ‘RM’), (0.0867, ‘DIS’), (0.0407, ‘NOX’), (0.0351, ‘CRIM’), (0.0233, ‘PTRATIO’), (0.0168, ‘TAX’), (0.0122, ‘AGE’), (0.005, ‘B’), (0.0048, ‘INDUS’), (0.0043, ‘RAD’), (0.0004, ‘ZN’), (0.0001, ‘CHAS’)]

    在这个例子当中,LSTAT和RM这两个特征对模型的性能有着很大的影响,打乱这两个特征的特征值使得模型的性能下降了73%和57%。注意,尽管这些我们是在所有特征上进行了训练得到了模型,然后才得到了每个特征的重要性测试,这并不意味着我们扔掉某个或者某些重要特征后模型的性能就一定会下降很多,因为即便某个特征删掉之后,其关联特征一样可以发挥作用,让模型性能基本上不变。

    5 两种顶层特征选择算法

    之所以叫做顶层,是因为他们都是建立在基于模型的特征选择方法基础之上的,例如回归和SVM,在不同的子集上建立模型,然后汇总最终确定特征得分。

    5.1 稳定性选择 Stability selection

    稳定性选择是一种基于二次抽样和选择算法相结合较新的方法,选择算法可以是回归、SVM或其他类似的方法。它的主要思想是在不同的数据子集和特征子集上运行特征选择算法,不断的重复,最终汇总特征选择结果,比如可以统计某个特征被认为是重要特征的频率(被选为重要特征的次数除以它所在的子集被测试的次数)。理想情况下,重要特征的得分会接近100%。稍微弱一点的特征得分会是非0的数,而最无用的特征得分将会接近于0。

    sklearn在 随机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.62, ‘CHAS’), (0.595, ‘B’), (0.39, ‘TAX’), (0.385, ‘CRIM’), (0.25, ‘DIS’), (0.22, ‘NOX’), (0.125, ‘INDUS’), (0.045, ‘ZN’), (0.02, ‘RAD’), (0.015, ‘AGE’)]

    在上边这个例子当中,最高的3个特征得分是1.0,这表示他们总会被选作有用的特征(当然,得分会收到正则化参数alpha的影响,但是sklearn的随机lasso能够自动选择最优的alpha)。接下来的几个特征得分就开始下降,但是下降的不是特别急剧,这跟纯lasso的方法和随机森林的结果不一样。能够看出稳定性选择对于克服过拟合和对数据理解来说都是有帮助的:总的来说,好的特征不会因为有相似的特征、关联特征而得分为0,这跟Lasso是不同的。对于特征选择任务,在许多数据集和环境下,稳定性选择往往是性能最好的方法之一。

    5.2 递归特征消除 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
    
    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’)]

    6 一个完整的例子

    下面将本文所有提到的方法进行实验对比,数据集采用Friedman #1 回归数据( 这篇论文 中的数据)。数据是用这个公式产生的:

    X1到X5是由 单变量分布 生成的,e是 标准正态变量 N(0,1)。另外,原始的数据集中含有5个噪音变量 X5,…,X10,跟响应变量是独立的。我们增加了4个额外的变量X11,…X14,分别是X1,…,X4的关联变量,通过f(x)=x+N(0,0.01)生成,这将产生大于0.999的关联系数。这样生成的数据能够体现出不同的特征排序方法应对关联特征时的表现。

    接下来将会在上述数据上运行所有的特征选择方法,并且将每种方法给出的得分进行归一化,让取值都落在0-1之间。对于RFE来说,由于它给出的是顺序而不是得分,我们将最好的5个的得分定为1,其他的特征的得分均匀的分布在0-1之间。

    from sklearn.datasets import load_boston
    from sklearn.linear_model import (LinearRegression, Ridge, 
                      Lasso, RandomizedLasso)
    from sklearn.feature_selection import RFE, f_regression
    from sklearn.preprocessing import MinMaxScaler
    from sklearn.ensemble import RandomForestRegressor
    import numpy as np
    from minepy import MINE
    np.random.seed(0)
    size = 750
    X = np.random.uniform(0, 1, (size, 14))
    #"Friedamn #1” regression problem
    Y = (10 * np.sin(np.pi*X[:,0]*X[:,1]) + 20*(X[:,2] - .5)**2 +
       10*X[:,3] + 5*X[:,4] + np.random.normal(0,1))
    #Add 3 additional correlated variables (correlated with X1-X3)
    X[:,10:] = X[:,:4] + np.random.normal(0, .025, (size,4))
    names = ["x%s" % i for i in range(1,15)]
    ranks = {}
    def rank_to_dict(ranks, names, order=1):
      minmax = MinMaxScaler()
      ranks = minmax.fit_transform(order*np.array([ranks]).T).T[0]
      ranks = map(lambda x: round(x, 2), ranks)
      return dict(zip(names, ranks ))
    lr = LinearRegression(normalize=True)
    lr.fit(X, Y)
    ranks["Linear reg"] = rank_to_dict(np.abs(lr.coef_), names)
    ridge = Ridge(alpha=7)
    ridge.fit(X, Y)
    ranks["Ridge"] = rank_to_dict(np.abs(ridge.coef_), names)
    lasso = Lasso(alpha=.05)
    lasso.fit(X, Y)
    ranks["Lasso"] = rank_to_dict(np.abs(lasso.coef_), names)
    rlasso = RandomizedLasso(alpha=0.04)
    rlasso.fit(X, Y)
    ranks["Stability"] = rank_to_dict(np.abs(rlasso.scores_), names)
    #stop the search when 5 features are left (they will get equal scores)
    rfe = RFE(lr, n_features_to_select=5)
    rfe.fit(X,Y)
    ranks["RFE"] = rank_to_dict(map(float, rfe.ranking_), names, order=-1)
    rf = RandomForestRegressor()
    rf.fit(X,Y)
    ranks["RF"] = rank_to_dict(rf.feature_importances_, names)
    f, pval  = f_regression(X, Y, center=True)
    ranks["Corr."] = rank_to_dict(f, names)
    mine = MINE()
    mic_scores = []
    for i in range(X.shape[1]):
      mine.compute_score(X[:,i], Y)
      m = mine.mic()
      mic_scores.append(m)
    ranks["MIC"] = rank_to_dict(mic_scores, names)
    r = {}
    for name in names:
      r[name] = round(np.mean([ranks[method][name] 
                   for method in ranks.keys()]), 2)
    methods = sorted(ranks.keys())
    ranks["Mean"] = r
    methods.append("Mean")
    print "\t%s" % "\t".join(methods)
    for name in names:
      print "%s\t%s" % (name, "\t".join(map(str, 
                 [ranks[method][name] for method in methods])))
    

    从以上结果中可以找到一些有趣的发现:

    特征之间存在 线性关联 关系,每个特征都是独立评价的,因此X1,…X4的得分和X11,…X14的得分非常接近,而噪音特征X5,…,X10正如预期的那样和响应变量之间几乎没有关系。由于变量X3是二次的,因此X3和响应变量之间看不出有关系(除了MIC之外,其他方法都找不到关系)。这种方法能够衡量出特征和响应变量之间的线性关系,但若想选出优质特征来提升模型的泛化能力,这种方法就不是特别给力了,因为所有的优质特征都不可避免的会被挑出来两次。

    Lasso能够挑出一些优质特征,同时让其他特征的系数趋于0。当如需要减少特征数的时候它很有用,但是对于数据理解来说不是很好用。(例如在结果表中,X11,X12,X13的得分都是0,好像他们跟输出变量之间没有很强的联系,但实际上不是这样的)

    MIC对特征一视同仁,这一点上和关联系数有点像,另外,它能够找出X3和响应变量之间的非线性关系。

    随机森林基于不纯度的排序结果非常鲜明,在得分最高的几个特征之后的特征,得分急剧的下降。从表中可以看到,得分第三的特征比第一的小4倍。而其他的特征选择算法就没有下降的这么剧烈。

    Ridge将回归系数均匀的分摊到各个关联变量上,从表中可以看出,X11,…,X14和X1,…,X4的得分非常接近。

    稳定性选择常常是一种既能够有助于理解数据又能够挑出优质特征的这种选择,在结果表中就能很好的看出。像Lasso一样,它能找到那些性能比较好的特征(X1,X2,X4,X5),同时,与这些特征关联度很强的变量也得到了较高的得分。

    总结

    1. 对于理解数据、数据的结构、特点来说,单变量特征选择是个非常好的选择。尽管可以用它对特征进行排序来优化模型,但由于它不能发现冗余(例如假如一个特征子集,其中的特征之间具有很强的关联,那么从中选择最优的特征时就很难考虑到冗余的问题)。
    2. 正则化的线性模型对于特征理解和特征选择来说是非常强大的工具。L1正则化能够生成稀疏的模型,对于选择特征子集来说非常有用;相比起L1正则化,L2正则化的表现更加稳定,由于有用的特征往往对应系数非零,因此L2正则化对于数据的理解来说很合适。由于响应变量和特征之间往往是非线性关系,可以采用basis expansion的方式将特征转换到一个更加合适的空间当中,在此基础上再考虑运用简单的线性模型。
    3. 随机森林是一种非常流行的特征选择方法,它易于使用,一般不需要feature engineering、调参等繁琐的步骤,并且很多工具包都提供了平均不纯度下降方法。它的两个主要问题,1是重要的特征有可能得分很低(关联特征问题),2是这种方法对特征变量类别多的特征越有利(偏向问题)。尽管如此,这种方法仍然非常值得在你的应用中试一试。
    4. 特征选择在很多机器学习和数据挖掘场景中都是非常有用的。在使用的时候要弄清楚自己的目标是什么,然后找到哪种方法适用于自己的任务。当选择最优特征以提升模型性能的时候,可以采用交叉验证的方法来验证某种方法是否比其他方法要好。当用特征选择的方法来理解数据的时候要留心,特征选择模型的稳定性非常重要,稳定性差的模型很容易就会导致错误的结论。对数据进行二次采样然后在子集上运行特征选择算法能够有所帮助,如果在各个子集上的结果是一致的,那就可以说在这个数据集上得出来的结论是可信的,可以用这种特征选择模型的结果来理解数据。
    展开全文
  • 特征选择方法总结

    千次阅读 2018-09-18 18:59:25
    问题 在数据处理中经常会遇到特征太多造成的计算负担和一些其他的影响,如过拟合等,不但使得预测结果不准确,还消耗计算时间。所以特征选择就显得非常重要了。...在文章中大佬对于特征选择方法分...

    问题

    在数据处理中经常会遇到特征太多造成的计算负担和一些其他的影响,如过拟合等,不但使得预测结果不准确,还消耗计算时间。所以特征选择就显得非常重要了。

    特征选择:从给定的特征集合中选取出相关特征子集的过程成为“特征选择”。

    通过这一操作,不仅能够减少特征的维度,也能得到更能体现目标值的几个特征。在周志华的《机器学习》中第十一章对于特征选择也是有所提到。在文章中大佬对于特征选择的方法分为三类:过滤式(filter),包裹式(wrapper),和嵌入式(embedding).


    过滤式(filter)

    过滤式:主要是在数据处理过程就对特征进行筛选了,也就是说和机器学习算法无关。该方法本质就是利用数据的数理统计中的某些指标数值来进行筛选。例如卡方,还有信息增益。对于每一个不同的统计量,我们都需要一个阈值来确定那些特征是我们想要的,而这个统计量我们称为“相关统计量”。

    方差筛选:

    计算每一个特征的方差值,默认情况是删除所有方差为0的特征,很显然,特征为零的数据意味着数据变化为零,这对结果预测的帮助非常小。同样可以通过设定一个阈值,来筛选特征。
    例子代码:

    from sklearn.feature_selection import VarianceThreshold  # 导入python的相关模块
    
    X = [[7, 4, 1], [5, 9, 6], [1, 2, 8], [4, 1, 5]]  #样本情况
    select_Feature = VarianceThreshold(threshold=(6))  # 筛选特征的方差大于阈值的特征
    select_Feature.fit_transform(X)  # 返回的结果为选择的特征
    
    print(select_Feature.fit_transform(X))
    
    结果:
    [[4 1]
     [9 6]
     [2 8]
     [1 5]]
    

    除了这个简单易懂的方差筛选法外,还有卡方,信息增益等


    卡方检验 筛选
    其实卡方的本质和误差计算类似,也是实际值与期望值的差经过一些运算得到的一个统计量,

    对于这个统计量来说

    这里写图片描述

    目的是验两个变量独立性的方法,其中
    这里写图片描述为实际值,E是期望值,所以当两者越接近的话,意味着差异越小,从实际意义上来讲就是特征这里写图片描述 与类别这里写图片描述之间计算的卡方值越大,说明与假设的偏差越大,而通常原假设:这里写图片描述这里写图片描述之间相互独立,那么我们就可以否定原假设,认为他们是相关的。
    举例说明:

    是否化妆总计
    化妆15(55)95(55)110
    不化妆85(45)5(45)90
    100100200

    原假设:化妆与性别没有关系。
    将表中数据带入到公式去,计算可以得出:
    在这里插入图片描述
    我们得到的结果是129.3,通过查表可以得出推理论犯错的概率p>0.999,也就是存在99.9的可能性是错的。

    而该方法在Sklearn中都可以直接调用,如下:

    from sklearn.feature_selection import chi2
    from sklearn.feature_selection import SelectKBest
    import pandas as pd
    import numpy as np
    
    #利用卡方计量来筛选7个满足条件的特征
    x_data = SelectKBest(chi2, k=7).fit_transform(x_data, y_data)
    

    信息增益(information gain)
    信息增益主要是通过计算信息熵(entropy)与条件熵的差值的大小排序,这个要对熵进行解释一下,这个概念主要是在物理学中对系统状态混乱程度的量化,如果一个系统不确定的程度越大,那么其熵就越大。所得出的一种特征选择的方法,计算方法如下:

    熵的表达式
    这个计算得出的信息熵E(X),这个比较容易理解。
    其次我们需要计算条件熵,

    在这里插入图片描述
    。该公式以例子来给大家解说一下

    时间效率智商成绩好坏

    我们可以看出Y=好/坏的分别概率为3/5,2/5,而我们条件概率,就是在时间多/少确认的情况下(概率为3/5,2/5),效率是否高的概率,例如在已知时间多的情况下,成绩好的占2/3,坏的1/3,H(Y|X = 多)=-(2/3)log(2/3)-(1/3)log(1/3),p(x=多)的概率为3/5,所以将全部的情况计算相加就可以得到条件熵了。

    随后我们将两者相减,按照差值排序就可以了。

    代码:

    import numpy as np
    import pandas as pd
    import math
    
    class IG():
        def __init__(self,X,y):
    
            X = np.array(X)
            n_feature = np.shape(X)[1]#取出x的特征数
            n_y = len(y)#类标的长度
    
            orig_H = 0 #信息熵
            for i in set(y):#
                orig_H += -(y.count(i)/n_y)*math.log(y.count(i)/n_y)
    
            #条件信息熵
            condi_H_list = []
            for i in range(n_feature):
                feature = X[:,i]
                sourted_feature = sorted(feature)
                threshold = [(sourted_feature[inde-1]+sourted_feature[inde])/2 for inde in range(len(feature)) if inde != 0 ]
    
                thre_set = set(threshold)
                if float(max(feature)) in thre_set:
                    thre_set.remove(float(max(feature)))
                if min(feature) in thre_set:
                    thre_set.remove(min(feature))
                pre_H = 0
                for thre in thre_set:
                    lower = [y[s] for s in range(len(feature)) if feature[s] < thre]
                    highter = [y[s] for s in range(len(feature)) if feature[s] > thre]
                    H_l = 0
                    for l in set(lower):
                        H_l += -(lower.count(l) / len(lower))*math.log(lower.count(l) / len(lower))
                    H_h = 0
                    for h in set(highter):
                        H_h += -(highter.count(h) / len(highter))*math.log(highter.count(h) / len(highter))
                    temp_condi_H = len(lower)/n_y *H_l+ len(highter)/n_y * H_h
                    condi_H = orig_H - temp_condi_H
                    pre_H = max(pre_H,condi_H)
                condi_H_list.append(pre_H)
    
            #返回所需要的信息增益值
            self.IG = condi_H_list
    
    
        def getIG(self):
            return self.IG
    
    
    
    
    if __name__ == "__main__":
         X = [[1, 0, 0, 1],
              [0, 1, 1, 1],
              [0, 0, 1, 0]]
         y = [0, 0, 1]
    
        
        print(IG(X,y).getIG())
    
    结果:
    [0.17441604792151594, 0.17441604792151594, 0.17441604792151594, 0.6365141682948128]
    
    
    

    信息增益比
    信息增益比,可以说是信息增益的进一步的指标,因为信息增益存在的缺点是它会倾向于分类较多的特征。
    其主要的思想是,对于样本集合D来说,随即变量X是样本的类别,即假设有k个类别,每一个类别的概率为在这里插入图片描述,而在这里插入图片描述表示类别k的样本个数,|D|表示样本总数。

    这里写图片描述

    基尼系数
    基尼指数(基尼不纯度):表示在样本集合中一个随机选中的样本被分错的概率。Gini指数越小表示集合中被选中的样本被分错的概率越小,也就是说集合的纯度越高,反之,集合越不纯。

    这里写图片描述

    基尼系数=样本选中的概率*样本选错的概率,

    以上三种计量值都是决策树对于节点的选择依据,而基尼系数就是CART的标准,而信息增益是ID3算法的标准。他们的目的都是将分类的集合更加的纯净,这样在最优化的时候可以更快的达到最优解。


    包裹式(wrapper)

    与过滤式刚好相反,包裹式选择的特征是在机器学习的过程中,并不像过滤式那样在机器学习前就已经选择好了。也就是说通过机器学习得出的结果来判定那些特征更加适合,**就像特地给脚做一双鞋一样,选择效果最好的,最合适脚的鞋。**可能这就包裹式的由来吧(瞎猜的,小声bb)。
    因为其使用机器学习性能来评价特征的好坏,也就是说必须通过多次计算从而对比得出最好的结果,这样的方法缺点也是很明显的,首先是计算量非常大,不像过滤式计算复杂度是常数级的,而它可能是次方级的。其次是其求出来的结果可能是一个局部最优解,类似贪心算法。

    未完待续。。。

    展开全文
  • 特征选择方法-统计方法

    千次阅读 2018-04-15 19:19:53
    信息熵:在信息论与概率统计中,熵是表示随机变量不确定性的度量。对于离散型随机变量集合X,其概率分布为则随机变量X的熵为熵越大,表示随机变量...而在机器学习中,熵的值越大表示所含的信息量越多(特征选择算法...

    信息熵:

    在信息论与概率统计中,熵是表示随机变量不确定性的度量。对于离散型随机变量集合X,其概率分布为


    则随机变量X的熵为


    熵越大,表示随机变量的不确定性就越大。

    例如,当随机变量X的集合取值只有0和1时,其概率分布为


    则,熵为


    当H(p)的值为0时,说明变量完全没有不确定性,当p=0.5,也就是H(P)=1时(最大值),说明随机变量的不确定性最大。而在机器学习中,熵的值越大表示所含的信息量越多(特征选择算法也是利用该思想)。

    而在特征选择计算方法中,熵的计算公式为


    其中,D为数据集,|D|为样本集中样本的个数,|C_k|为类别C_k中的样本个数,K为数据集中类别的个数。上式表示的意思是对于数据集D熵等于该类别在数据集中所有取值的概率乘以log以2为底的概率之和。在特征选择方法中,K的取值则为2类,一种是包含特征词的类别,一种是不包含特征词的类别,则将类别分为了1和0。

    条件熵

    条件熵H(Y|X)表示在已知随机变量X的条件下随机变量Y的不确定性。随机变量X给定的条件下随机变量Y的条件熵H(Y|X),定义为X给定条件下Y的条件概率分布的熵对X的数学期望


    当熵和条件熵中的概率由数据估计(特别是极大似然估计,表明该方法中的参数是估计出来的不是数据集中的具体数据)得到时,所对应的熵与条件熵分布称为经验熵和经验条件熵。

    针对特征选择方法中,特征A对数据集D的条件熵H(D|A)为


    在上式中,对于在特征词A下数据集D的条件熵是特征词A的所有不同取值的比例分别乘以该特征词子集合的信息熵之和。

    信息增益

    信息增益表示得知特征X的信息而使得类Y的信息的不确定性减少的程度。信息增益是度量某个特征词属于某个特定类别的信息量。特征词的信息增益值等于特征词的经验熵与特征在给定条件下的经验熵之差。给定特征词W与类别C,用IG表示W对于类别C的信息增益。公式如下



    其中, IG(W,C)表示特征词W在类别C下的信息增益值。 H(C)表示类别C的信息熵。 H(C|W)表示特征词W在类别C中的条件熵。

    互信息

         在信息论中为了更好的描述事物之间的联系,引入了互信息的概念。对于两个随机变量X和Y,他们之间在某种程度上也是相互联系的,即他们之间存在着一定的依赖关系,互信息反应了两个随机变量之间相互依存关系的强弱。

    定义为:


    在特征选择方法中,I(C;F)可以从全局上衡量特征词F和类别C之间的关系,具有较高区分能力的词都具有较高的互信息值。

    卡方统计

    卡方统计是特征选择方法中效果最好的算法之一。卡方统计是标准化的值,用来检验两个事件的独立性。在特征选择算法中,卡方统计度量词与类别的相关程度,一个类别中的词,如果卡方统计值为零,表明该词与该类别是独立不相关的,该词不包含该类别的信息。如果卡方统计值很大,说明该词包含很多该类别的信息。卡方统计的计算公式如式所示。

    特征词与类别的关联表

    频率

    属于特征词w

    不属于特征词w

    属于类别c

    A

    C

    不属于类别c

    B

    D

    表示特征词t和类别c之间的卡方统计值。N为文本的数量,N=A+B+C+D。我们在做卡方统计计算时,都采用简化的卡方统计计算公式,公式如下所示,其中的变量如上表所示。


    参考文献

    https://wenku.baidu.com/view/12d8e161915f804d2b16c161.html

    https://blog.csdn.net/xwd18280820053/article/details/70739368

    https://www.jianshu.com/p/9bbe71750547

    统计学习方法

    一种改进的基于条件互信息的特征选择算法

    展开全文
  • 几种常用的特征选择方法

    万次阅读 多人点赞 2016-12-14 16:33:38
    几种常用的特征选择方法

    结合Scikit-learn介绍几种常用的特征选择方法

    特征选择(排序)对于数据科学家、机器学习从业者来说非常重要。好的特征选择能够提升模型的性能,更能帮助我们理解数据的特点、底层结构,这对进一步改善模型、算法都有着重要作用。

    特征选择主要有两个功能:

    1. 减少特征数量、降维,使模型泛化能力更强,减少过拟合
    2. 增强对特征和特征值之间的理解

    拿到数据集,一个特征选择方法,往往很难同时完成这两个目的。通常情况下,我们经常不管三七二十一,选择一种自己最熟悉或者最方便的特征选择方法(往往目的是降维,而忽略了对特征和数据理解的目的)。

    在许多机器学习相关的书里,很难找到关于特征选择的内容,因为特征选择要解决的问题往往被视为机器学习的一种副作用,一般不会单独拿出来讨论。

    本文将结合 Scikit-learn提供的例子 介绍几种常用的特征选择方法,它们各自的优缺点和问题。

    1 去掉取值变化小的特征 Removing features with low variance

    这应该是最简单的特征选择方法了:假设某特征的特征值只有0和1,并且在所有输入样本中,95%的实例的该特征取值都是1,那就可以认为这个特征作用不大。如果100%都是1,那这个特征就没意义了。当特征值都是离散型变量的时候这种方法才能用,如果是连续型变量,就需要将连续变量离散化之后才能用,而且实际当中,一般不太会有95%以上都取某个值的特征存在,所以这种方法虽然简单但是不太好用。可以把它作为特征选择的预处理,先去掉那些取值变化小的特征,然后再从接下来提到的的特征选择方法中选择合适的进行进一步的特征选择。

    2 单变量特征选择 Univariate feature selection

    单变量特征选择能够对每一个特征进行测试,衡量该特征和响应变量之间的关系,根据得分扔掉不好的特征。对于回归和分类问题可以采用卡方检验等方式对特征进行测试。

    这种方法比较简单,易于运行,易于理解,通常对于理解数据有较好的效果(但对特征优化、提高泛化能力来说不一定有效);这种方法有许多改进的版本、变种。

    2.1 Pearson相关系数 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))

    Lower noise (0.71824836862138386, 7.3240173129992273e-49)Higher noise (0.057964292079338148, 0.31700993885324746)

    这个例子中,我们比较了变量在加入噪音之前和之后的差异。当噪音比较小的时候,相关性很强,p-value很低。

    Scikit-learn提供的 f_regrssion 方法能够批量计算特征的p-value,非常方便,参考sklearn的 pipeline

    Pearson相关系数的一个明显缺陷是,作为特征排序机制,他只对线性关系敏感。如果关系是非线性的,即便两个变量具有一一对应的关系,Pearson相关性也可能会接近0。

    x = np.random.uniform(-1, 1, 100000)
    print pearsonr(x, x**2)[0]

    -0.00230804707612

    更多类似的例子参考 sample plots 。另外,如果仅仅根据相关系数这个值来判断的话,有时候会具有很强的误导性,如 Anscombe’s quartet ,最好把数据可视化出来,以免得出错误的结论。

    2.2 互信息和最大信息系数 Mutual information and maximal information coefficient (MIC)

    以上就是经典的互信息公式了。想把互信息直接用于特征选择其实不是太方便:1、它不属于度量方式,也没有办法归一化,在不同数据及上的结果无法做比较;2、对于连续变量的计算不是很方便(X和Y都是集合,x,y都是离散的取值),通常变量需要先离散化,而互信息的结果对离散化的方式很敏感。

    最大信息系数克服了这两个问题。它首先寻找一种最优的离散化方式,然后把互信息取值转换成一种度量方式,取值区间在[0,1]。 minepy 提供了MIC功能。

    反过头来看y=x^2这个例子,MIC算出来的互信息值为1(最大的取值)。

    from minepy import MINE
    m = MINE()
    x = np.random.uniform(-1, 1, 10000)
    m.compute_score(x, x**2)
    print m.mic()

    1.0

    MIC的统计能力遭到了 一些质疑 ,当零假设不成立时,MIC的统计就会受到影响。在有的数据集上不存在这个问题,但有的数据集上就存在这个问题。

    2.3 距离相关系数 (Distance correlation)

    距离相关系数是为了克服Pearson相关系数的弱点而生的。在x和x^2这个例子中,即便Pearson相关系数是0,我们也不能断定这两个变量是独立的(有可能是非线性相关);但如果距离相关系数是0,那么我们就可以说这两个变量是独立的。

    R的 energy 包里提供了距离相关系数的实现,另外这是 Python gist 的实现。

    #R-code
    > x = runif (1000, -1, 1)
    > dcor(x, x**2)
    [1] 0.4943864

    尽管有MIC和距离相关系数在了,但当变量之间的关系接近线性相关的时候,Pearson相关系数仍然是不可替代的。第一、Pearson相关系数计算速度快,这在处理大规模数据的时候很重要。第二、Pearson相关系数的取值区间是[-1,1],而MIC和距离相关系数都是[0,1]。这个特点使得Pearson相关系数能够表征更丰富的关系,符号表示关系的正负,绝对值能够表示强度。当然,Pearson相关性有效的前提是两个变量的变化关系是单调的。

    2.4 基于学习模型的特征排序 (Model based ranking)

    这种方法的思路是直接使用你要用的机器学习算法,针对每个单独的特征和响应变量建立预测模型。其实Pearson相关系数等价于线性回归里的标准化回归系数。假如某个特征和响应变量之间的关系是非线性的,可以用基于树的方法(决策树、随机森林)、或者扩展的线性模型等。基于树的方法比较易于使用,因为他们对非线性关系的建模比较好,并且不需要太多的调试。但要注意过拟合问题,因此树的深度最好不要太大,再就是运用交叉验证。

    在 波士顿房价数据集 上使用sklearn的 随机森林回归 给出一个单变量选择的例子:

    from sklearn.cross_validation import cross_val_score, ShuffleSplit
    from sklearn.datasets import load_boston
    from sklearn.ensemble import RandomForestRegressor
    
    #Load boston housing dataset as an example
    boston = load_boston()
    X = boston["data"]
    Y = boston["target"]
    names = boston["feature_names"]
    
    rf = RandomForestRegressor(n_estimators=20, max_depth=4)
    scores = []
    for i in range(X.shape[1]):
         score = cross_val_score(rf, X[:, i:i+1], Y, scoring="r2",
                                  cv=ShuffleSplit(len(X), 3, .3))
         scores.append((round(np.mean(score), 3), names[i]))
    print sorted(scores, reverse=True)

    [(0.636, ‘LSTAT’), (0.59, ‘RM’), (0.472, ‘NOX’), (0.369, ‘INDUS’), (0.311, ‘PTRATIO’), (0.24, ‘TAX’), (0.24, ‘CRIM’), (0.185, ‘RAD’), (0.16, ‘ZN’), (0.087, ‘B’), (0.062, ‘DIS’), (0.036, ‘CHAS’), (0.027, ‘AGE’)]

    3 线性模型和正则化

    单变量特征选择方法独立的衡量每个特征与响应变量之间的关系,另一种主流的特征选择方法是基于机器学习模型的方法。有些机器学习方法本身就具有对特征进行打分的机制,或者很容易将其运用到特征选择任务中,例如回归模型,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_)
    

    Linear model: 0.984 * X0 + 1.995 * X1 + -0.041 * X2

    在这个例子当中,尽管数据中存在一些噪音,但这种特征选择模型仍然能够很好的体现出数据的底层结构。当然这也是因为例子中的这个问题非常适合用线性模型来解:特征和响应变量之间全都是线性关系,并且特征之间均是独立的。

    在很多实际的数据当中,往往存在多个互相关联的特征,这时候模型就会变得不稳定,数据中细微的变化就可能导致模型的巨大变化(模型的变化本质上是系数,或者叫参数,可以理解成W),这会让模型的预测变得困难,这种现象也称为多重共线性。例如,假设我们有个数据集,它的真实模型应该是Y=X1+X2,当我们观察的时候,发现Y’=X1+X2+e,e是噪音。如果X1和X2之间存在线性关系,例如X1约等于X2,这个时候由于噪音e的存在,我们学到的模型可能就不是Y=X1+X2了,有可能是Y=2X1,或者Y=-X1+3X2。

    下边这个例子当中,在同一个数据上加入了一些噪音,用随机森林算法进行特征选择。

    from sklearn.linear_model import LinearRegression
    
    size = 100
    np.random.seed(seed=5)
    
    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_)

    Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2

    系数之和接近3,基本上和上上个例子的结果一致,应该说学到的模型对于预测来说还是不错的。但是,如果从系数的字面意思上去解释特征的重要性的话,X3对于输出变量来说具有很强的正面影响,而X1具有负面影响,而实际上所有特征与输出变量之间的影响是均等的。

    同样的方法和套路可以用到类似的线性模型上,比如逻辑回归。

    3.1 正则化模型

    正则化就是把额外的约束或者惩罚项加到已有模型(损失函数)上,以防止过拟合并提高泛化能力。损失函数由原来的E(X,Y)变为E(X,Y)+alpha||w||,w是模型系数组成的向量(有些地方也叫参数parameter,coefficients),||·||一般是L1或者L2范数,alpha是一个可调的参数,控制着正则化的强度。当用在线性模型上时,L1正则化和L2正则化也称为Lasso和Ridge。

    3.2 L1正则化/Lasso

    L1正则化将系数w的l1范数作为惩罚项加到损失函数上,由于正则项非零,这就迫使那些弱的特征所对应的系数变成0。因此L1正则化往往会使学到的模型很稀疏(系数w经常为0),这个特性使得L1正则化成为一种很好的特征选择方法。

    Scikit-learn为线性回归提供了Lasso,为分类提供了L1逻辑回归。

    下面的例子在波士顿房价数据上运行了Lasso,其中参数alpha是通过grid search进行优化的。

    from sklearn.linear_model import Lasso
    from sklearn.preprocessing import StandardScaler
    from sklearn.datasets import load_boston
    
    boston = load_boston()
    scaler = StandardScaler()
    X = scaler.fit_transform(boston["data"])
    Y = boston["target"]
    names = boston["feature_names"]
    
    lasso = Lasso(alpha=.3)
    lasso.fit(X, Y)
    
    print "Lasso model: ", pretty_print_linear(lasso.coef_, names, sort = True)

    Lasso model: -3.707 * LSTAT + 2.992 * RM + -1.757 * PTRATIO + -1.081 * DIS + -0.7 * NOX + 0.631 * B + 0.54 * CHAS + -0.236 * CRIM + 0.081 * ZN + -0.0 * INDUS + -0.0 * AGE + 0.0 * RAD + -0.0 * TAX

    可以看到,很多特征的系数都是0。如果继续增加alpha的值,得到的模型就会越来越稀疏,即越来越多的特征系数会变成0。

    然而,L1正则化像非正则化线性模型一样也是不稳定的,如果特征集合中具有相关联的特征,当数据发生细微变化时也有可能导致很大的模型差异。

    3.3 L2正则化/Ridge regression

    L2正则化将系数向量的L2范数添加到了损失函数中。由于L2惩罚项中系数是二次方的,这使得L2和L1有着诸多差异,最明显的一点就是,L2正则化会让系数的取值变得平均。对于关联特征,这意味着他们能够获得更相近的对应系数。还是以Y=X1+X2为例,假设X1和X2具有很强的关联,如果用L1正则化,不论学到的模型是Y=X1+X2还是Y=2X1,惩罚都是一样的,都是2 alpha。但是对于L2来说,第一个模型的惩罚项是2 alpha,但第二个模型的是4*alpha。可以看出,系数之和为常数时,各系数相等时惩罚是最小的,所以才有了L2会让各个系数趋于相同的特点。

    可以看出,L2正则化对于特征选择来说一种稳定的模型,不像L1正则化那样,系数会因为细微的数据变化而波动。所以L2正则化和L1正则化提供的价值是不同的,L2正则化对于特征理解来说更加有用:表示能力强的特征对应的系数是非零。

    回过头来看看3个互相关联的特征的例子,分别以10个不同的种子随机初始化运行10次,来观察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_)
    	ridge = Ridge(alpha=10)
    	ridge.fit(X,Y)
    	print "Ridge model:", pretty_print_linear(ridge.coef_)
    	print
    

    Random seed 0 Linear model: 0.728 * X0 + 2.309 * X1 + -0.082 * X2 Ridge model: 0.938 * X0 + 1.059 * X1 + 0.877 * X2

    Random seed 1 Linear model: 1.152 * X0 + 2.366 * X1 + -0.599 * X2 Ridge model: 0.984 * X0 + 1.068 * X1 + 0.759 * X2

    Random seed 2 Linear model: 0.697 * X0 + 0.322 * X1 + 2.086 * X2 Ridge model: 0.972 * X0 + 0.943 * X1 + 1.085 * X2

    Random seed 3 Linear model: 0.287 * X0 + 1.254 * X1 + 1.491 * X2 Ridge model: 0.919 * X0 + 1.005 * X1 + 1.033 * X2

    Random seed 4 Linear model: 0.187 * X0 + 0.772 * X1 + 2.189 * X2 Ridge model: 0.964 * X0 + 0.982 * X1 + 1.098 * X2

    Random seed 5 Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2 Ridge model: 0.758 * X0 + 1.011 * X1 + 1.139 * X2

    Random seed 6 Linear model: 1.199 * X0 + -0.031 * X1 + 1.915 * X2 Ridge model: 1.016 * X0 + 0.89 * X1 + 1.091 * X2

    Random seed 7 Linear model: 1.474 * X0 + 1.762 * X1 + -0.151 * X2 Ridge model: 1.018 * X0 + 1.039 * X1 + 0.901 * X2

    Random seed 8 Linear model: 0.084 * X0 + 1.88 * X1 + 1.107 * X2 Ridge model: 0.907 * X0 + 1.071 * X1 + 1.008 * X2

    Random seed 9 Linear model: 0.714 * X0 + 0.776 * X1 + 1.364 * X2 Ridge model: 0.896 * X0 + 0.903 * X1 + 0.98 * X2

    可以看出,不同的数据上线性回归得到的模型(系数)相差甚远,但对于L2正则化模型来说,结果中的系数非常的稳定,差别较小,都比较接近于1,能够反映出数据的内在结构。

    4 随机森林

    随机森林具有准确率高、鲁棒性好、易于使用等优点,这使得它成为了目前最流行的机器学习算法之一。随机森林提供了两种特征选择的方法:mean decrease impurity和mean decrease accuracy。

    4.1 平均不纯度减少 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.5298, ‘LSTAT’), (0.4116, ‘RM’), (0.0252, ‘DIS’), (0.0172, ‘CRIM’), (0.0065, ‘NOX’), (0.0035, ‘PTRATIO’), (0.0021, ‘TAX’), (0.0017, ‘AGE’), (0.0012, ‘B’), (0.0008, ‘INDUS’), (0.0004, ‘RAD’), (0.0001, ‘CHAS’), (0.0, ‘ZN’)]

    这里特征得分实际上采用的是 Gini Importance 。使用基于不纯度的方法的时候,要记住:1、这种方法存在 偏向 ,对具有更多类别的变量会更有利;2、对于存在关联的多个特征,其中任意一个都可以作为指示器(优秀的特征),并且一旦某个特征被选择之后,其他特征的重要度就会急剧下降,因为不纯度已经被选中的那个特征降下来了,其他的特征就很难再降低那么多不纯度了,这样一来,只有先被选中的那个特征重要度很高,其他的关联特征重要度往往较低。在理解数据时,这就会造成误解,导致错误的认为先被选中的特征是很重要的,而其余的特征是不重要的,但实际上这些特征对响应变量的作用确实非常接近的(这跟Lasso是很像的)。

    特征随机选择 方法稍微缓解了这个问题,但总的来说并没有完全解决。下面的例子中,X0、X1、X2是三个互相关联的变量,在没有噪音的情况下,输出变量是三者之和。

    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.278, 0.66, 0.062]

    当计算特征重要性时,可以看到X1的重要度比X2的重要度要高出10倍,但实际上他们真正的重要度是一样的。尽管数据量已经很大且没有噪音,且用了20棵树来做随机选择,但这个问题还是会存在。

    需要注意的一点是,关联特征的打分存在不稳定的现象,这不仅仅是随机森林特有的,大多数基于模型的特征选择方法都存在这个问题。

    4.2 平均精确率减少 Mean decrease accuracy

    另一种常用的特征选择方法就是直接度量每个特征对模型精确率的影响。主要思路是打乱每个特征的特征值顺序,并且度量顺序变动对模型的精确率的影响。很明显,对于不重要的变量来说,打乱顺序对模型的精确率影响不会太大,但是对于重要的变量来说,打乱顺序就会降低模型的精确率。

    这个方法sklearn中没有直接提供,但是很容易实现,下面继续在波士顿房价数据集上进行实现。

    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)
    

    Features sorted by their score: [(0.7276, ‘LSTAT’), (0.5675, ‘RM’), (0.0867, ‘DIS’), (0.0407, ‘NOX’), (0.0351, ‘CRIM’), (0.0233, ‘PTRATIO’), (0.0168, ‘TAX’), (0.0122, ‘AGE’), (0.005, ‘B’), (0.0048, ‘INDUS’), (0.0043, ‘RAD’), (0.0004, ‘ZN’), (0.0001, ‘CHAS’)]

    在这个例子当中,LSTAT和RM这两个特征对模型的性能有着很大的影响,打乱这两个特征的特征值使得模型的性能下降了73%和57%。注意,尽管这些我们是在所有特征上进行了训练得到了模型,然后才得到了每个特征的重要性测试,这并不意味着我们扔掉某个或者某些重要特征后模型的性能就一定会下降很多,因为即便某个特征删掉之后,其关联特征一样可以发挥作用,让模型性能基本上不变。

    5 两种顶层特征选择算法

    之所以叫做顶层,是因为他们都是建立在基于模型的特征选择方法基础之上的,例如回归和SVM,在不同的子集上建立模型,然后汇总最终确定特征得分。

    5.1 稳定性选择 Stability selection

    稳定性选择是一种基于二次抽样和选择算法相结合较新的方法,选择算法可以是回归、SVM或其他类似的方法。它的主要思想是在不同的数据子集和特征子集上运行特征选择算法,不断的重复,最终汇总特征选择结果,比如可以统计某个特征被认为是重要特征的频率(被选为重要特征的次数除以它所在的子集被测试的次数)。理想情况下,重要特征的得分会接近100%。稍微弱一点的特征得分会是非0的数,而最无用的特征得分将会接近于0。

    sklearn在 随机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.62, ‘CHAS’), (0.595, ‘B’), (0.39, ‘TAX’), (0.385, ‘CRIM’), (0.25, ‘DIS’), (0.22, ‘NOX’), (0.125, ‘INDUS’), (0.045, ‘ZN’), (0.02, ‘RAD’), (0.015, ‘AGE’)]

    在上边这个例子当中,最高的3个特征得分是1.0,这表示他们总会被选作有用的特征(当然,得分会收到正则化参数alpha的影响,但是sklearn的随机lasso能够自动选择最优的alpha)。接下来的几个特征得分就开始下降,但是下降的不是特别急剧,这跟纯lasso的方法和随机森林的结果不一样。能够看出稳定性选择对于克服过拟合和对数据理解来说都是有帮助的:总的来说,好的特征不会因为有相似的特征、关联特征而得分为0,这跟Lasso是不同的。对于特征选择任务,在许多数据集和环境下,稳定性选择往往是性能最好的方法之一。

    5.2 递归特征消除 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
    
    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’)]

    6 一个完整的例子

    下面将本文所有提到的方法进行实验对比,数据集采用Friedman #1 回归数据( 这篇论文 中的数据)。数据是用这个公式产生的:

    X1到X5是由 单变量分布 生成的,e是 标准正态变量 N(0,1)。另外,原始的数据集中含有5个噪音变量 X5,…,X10,跟响应变量是独立的。我们增加了4个额外的变量X11,…X14,分别是X1,…,X4的关联变量,通过f(x)=x+N(0,0.01)生成,这将产生大于0.999的关联系数。这样生成的数据能够体现出不同的特征排序方法应对关联特征时的表现。

    接下来将会在上述数据上运行所有的特征选择方法,并且将每种方法给出的得分进行归一化,让取值都落在0-1之间。对于RFE来说,由于它给出的是顺序而不是得分,我们将最好的5个的得分定为1,其他的特征的得分均匀的分布在0-1之间。

    from sklearn.datasets import load_boston
    from sklearn.linear_model import (LinearRegression, Ridge, 
    								  Lasso, RandomizedLasso)
    from sklearn.feature_selection import RFE, f_regression
    from sklearn.preprocessing import MinMaxScaler
    from sklearn.ensemble import RandomForestRegressor
    import numpy as np
    from minepy import MINE
    np.random.seed(0)
    size = 750
    X = np.random.uniform(0, 1, (size, 14))
    #"Friedamn #1” regression problem
    Y = (10 * np.sin(np.pi*X[:,0]*X[:,1]) + 20*(X[:,2] - .5)**2 +
    	 10*X[:,3] + 5*X[:,4] + np.random.normal(0,1))
    #Add 3 additional correlated variables (correlated with X1-X3)
    X[:,10:] = X[:,:4] + np.random.normal(0, .025, (size,4))
    names = ["x%s" % i for i in range(1,15)]
    ranks = {}
    def rank_to_dict(ranks, names, order=1):
    	minmax = MinMaxScaler()
    	ranks = minmax.fit_transform(order*np.array([ranks]).T).T[0]
    	ranks = map(lambda x: round(x, 2), ranks)
    	return dict(zip(names, ranks ))
    lr = LinearRegression(normalize=True)
    lr.fit(X, Y)
    ranks["Linear reg"] = rank_to_dict(np.abs(lr.coef_), names)
    ridge = Ridge(alpha=7)
    ridge.fit(X, Y)
    ranks["Ridge"] = rank_to_dict(np.abs(ridge.coef_), names)
    lasso = Lasso(alpha=.05)
    lasso.fit(X, Y)
    ranks["Lasso"] = rank_to_dict(np.abs(lasso.coef_), names)
    rlasso = RandomizedLasso(alpha=0.04)
    rlasso.fit(X, Y)
    ranks["Stability"] = rank_to_dict(np.abs(rlasso.scores_), names)
    #stop the search when 5 features are left (they will get equal scores)
    rfe = RFE(lr, n_features_to_select=5)
    rfe.fit(X,Y)
    ranks["RFE"] = rank_to_dict(map(float, rfe.ranking_), names, order=-1)
    rf = RandomForestRegressor()
    rf.fit(X,Y)
    ranks["RF"] = rank_to_dict(rf.feature_importances_, names)
    f, pval  = f_regression(X, Y, center=True)
    ranks["Corr."] = rank_to_dict(f, names)
    mine = MINE()
    mic_scores = []
    for i in range(X.shape[1]):
    	mine.compute_score(X[:,i], Y)
    	m = mine.mic()
    	mic_scores.append(m)
    ranks["MIC"] = rank_to_dict(mic_scores, names)
    r = {}
    for name in names:
    	r[name] = round(np.mean([ranks[method][name] 
    							 for method in ranks.keys()]), 2)
    methods = sorted(ranks.keys())
    ranks["Mean"] = r
    methods.append("Mean")
    print "\t%s" % "\t".join(methods)
    for name in names:
    	print "%s\t%s" % (name, "\t".join(map(str, 
    						 [ranks[method][name] for method in methods])))
    

    从以上结果中可以找到一些有趣的发现:

    特征之间存在 线性关联 关系,每个特征都是独立评价的,因此X1,…X4的得分和X11,…X14的得分非常接近,而噪音特征X5,…,X10正如预期的那样和响应变量之间几乎没有关系。由于变量X3是二次的,因此X3和响应变量之间看不出有关系(除了MIC之外,其他方法都找不到关系)。这种方法能够衡量出特征和响应变量之间的线性关系,但若想选出优质特征来提升模型的泛化能力,这种方法就不是特别给力了,因为所有的优质特征都不可避免的会被挑出来两次。

    Lasso能够挑出一些优质特征,同时让其他特征的系数趋于0。当如需要减少特征数的时候它很有用,但是对于数据理解来说不是很好用。(例如在结果表中,X11,X12,X13的得分都是0,好像他们跟输出变量之间没有很强的联系,但实际上不是这样的)

    MIC对特征一视同仁,这一点上和关联系数有点像,另外,它能够找出X3和响应变量之间的非线性关系。

    随机森林基于不纯度的排序结果非常鲜明,在得分最高的几个特征之后的特征,得分急剧的下降。从表中可以看到,得分第三的特征比第一的小4倍。而其他的特征选择算法就没有下降的这么剧烈。

    Ridge将回归系数均匀的分摊到各个关联变量上,从表中可以看出,X11,…,X14和X1,…,X4的得分非常接近。

    稳定性选择常常是一种既能够有助于理解数据又能够挑出优质特征的这种选择,在结果表中就能很好的看出。像Lasso一样,它能找到那些性能比较好的特征(X1,X2,X4,X5),同时,与这些特征关联度很强的变量也得到了较高的得分。

    总结

    1. 对于理解数据、数据的结构、特点来说,单变量特征选择是个非常好的选择。尽管可以用它对特征进行排序来优化模型,但由于它不能发现冗余(例如假如一个特征子集,其中的特征之间具有很强的关联,那么从中选择最优的特征时就很难考虑到冗余的问题)。
    2. 正则化的线性模型对于特征理解和特征选择来说是非常强大的工具。L1正则化能够生成稀疏的模型,对于选择特征子集来说非常有用;相比起L1正则化,L2正则化的表现更加稳定,由于有用的特征往往对应系数非零,因此L2正则化对于数据的理解来说很合适。由于响应变量和特征之间往往是非线性关系,可以采用basis expansion的方式将特征转换到一个更加合适的空间当中,在此基础上再考虑运用简单的线性模型。
    3. 随机森林是一种非常流行的特征选择方法,它易于使用,一般不需要feature engineering、调参等繁琐的步骤,并且很多工具包都提供了平均不纯度下降方法。它的两个主要问题,1是重要的特征有可能得分很低(关联特征问题),2是这种方法对特征变量类别多的特征越有利(偏向问题)。尽管如此,这种方法仍然非常值得在你的应用中试一试。
    4. 特征选择在很多机器学习和数据挖掘场景中都是非常有用的。在使用的时候要弄清楚自己的目标是什么,然后找到哪种方法适用于自己的任务。当选择最优特征以提升模型性能的时候,可以采用交叉验证的方法来验证某种方法是否比其他方法要好。当用特征选择的方法来理解数据的时候要留心,特征选择模型的稳定性非常重要,稳定性差的模型很容易就会导致错误的结论。对数据进行二次采样然后在子集上运行特征选择算法能够有所帮助,如果在各个子集上的结果是一致的,那就可以说在这个数据集上得出来的结论是可信的,可以用这种特征选择模型的结果来理解数据。

    Tips

    什么是 卡方检验 ?用方差来衡量某个观测频率和理论频率之间差异性的方法

    什么是 皮尔森卡方检验 ?这是一种最常用的卡方检验方法,它有两个用途:1是计算某个变量对某种分布的拟合程度,2是根据两个观测变量的 Contingency table 来计算这两个变量是否是独立的。主要有三个步骤:第一步用方差和的方式来计算观测频率和理论频率之间卡方值;第二步算出卡方检验的自由度(行数-1乘以列数-1);第三步比较卡方值和对应自由度的卡方分布,判断显著性。

    什么是 p-value ?简单地说,p-value就是为了验证假设和实际之间一致性的统计学意义的值,即假设检验。有些地方叫右尾概率,根据卡方值和自由度可以算出一个固定的p-value,

    什么是 响应变量(response value) ?简单地说,模型的输入叫做explanatroy variables,模型的输出叫做response variables,其实就是要验证该特征对结果造成了什么样的影响

    什么是 统计能力(statistical power) ?

    什么是 度量(metric) ?

    什么是 零假设(null hypothesis) ?在相关性检验中,一般会取“两者之间无关联”作为零假设,而在独立性检验中,一般会取“两者之间是独立”作为零假设。与零假设相对的是备择假设(对立假设),即希望证明是正确的另一种可能。

    什么是 多重共线性 ?

    什么是 grid search ?

    That’s it

    References

    1. http://blog.datadive.net/selecting-good-features-part-i-univariate-selection/
    2. http://blog.datadive.net/selecting-good-features-part-ii-linear-models-and-regularization/
    3. http://scikit-learn.org/stable/modules/feature_selection.html#univariate-feature-selection
    4. http://www.quora.com/What-are-some-feature-selection-methods
    5. http://www.quora.com/What-are-some-feature-selection-algorithms
    6. http://www.quora.com/What-are-some-feature-selection-methods-for-SVMs
    7. http://www.quora.com/What-is-the-difference-between-principal-component-analysis-PCA-and-feature-selection-in-machine-learning-Is-PCA-a-means-of-feature-selection

    文章出处: http://chaoslog.com/te-zheng-xuan-ze.html

    展开全文
  • 常用的特征选择方法

    万次阅读 多人点赞 2018-09-20 17:54:04
    常用的特征选择方法 链接:https://www.zhihu.com/question/28641663/answer/110165221 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者:城东 链接:特征工程到底是什么? - 城东...
  • 特征选择方法

    千次阅读 2017-10-09 14:41:56
    本文介绍了特征选择的三类方法,过滤式,包裹式,嵌入式。
  • 特征选择 (feature_selection) Filter 1. 移除低方差的特征 (Removing features with low variance) 2. 单变量特征选择 (Univariate feature selection) Wrapper 3. 递归特征消除 (Recursive...
  • 机器学习特征选择方法

    千次阅读 2018-06-26 16:38:47
    特征选择当数据预处理完成后,我们需要选择有意义的特征输入机器学习的算法和模型进行训练。通常来说,从两个方面考虑来选择特征:特征是否发散:如果一个特征不发散,例如方差接近于0,也就是说样本在这个特征上...
  • RF、GBDT、XGboost特征选择方法

    万次阅读 多人点赞 2018-04-19 22:35:02
    RF、GBDT、XGboost都可以做特征选择属于特征选择中的嵌入式方法。比如在sklearn中,可以用属性feature_importances_去查看特征的重要度, 比如:from sklearn import ensemble #grd = ensemble....
  • 特征选择的几种方法

    千次阅读 2020-02-08 23:53:56
    目录 1 过滤法(Filter) 1.1 方差选择法 1.2 相关系数法 1.3 卡方检验 1.4 互信息法 1.5 relief算法 2 包裹法(Wrapper) 2.1 递归特征消除法 2.2 特征干扰法 3 嵌入法(Embedded) ...4 特征选择方法的优...
  • 特征选择/筛选方法总结

    万次阅读 多人点赞 2019-03-30 14:51:16
    1.特征选择介绍 1)特征分类 相关特征:对于学习任务(例如分类问题)有帮助,可以提升学习算法的效果; 无关特征:对于我们的算法没有任何帮助,不会给算法的效果带来任何提升; 冗余特征:不会对我们的算法...
  • 特征选择方法CHI和IG

    千次阅读 2016-09-14 15:07:34
    1)TF-IDF在特征选择时的误区。 TF-IDF用于向量空间模型,进行文档相似度计算是相当有效的。但在文本分类中单纯使用TF-IDF来判断一个特征是否有区分度是不够的。 它仅仅综合考虑了该词在文档中的重要程度和文档...
  • 常用特征选择方法及python代码

    千次阅读 2019-03-04 14:53:38
    特征选择 数据集中存在大量冗余的变量时不仅有损模型性能,而且还会带来建模成本的提升,因此,进行特征选择还是很有必要的。 进行特征选择最起码会带来一下三方面的好处: 减少过拟合几率:冗余数据少了,基于噪音...
  • 特征选择的基本方法概述

    千次阅读 2017-09-30 08:16:41
    特征选择主要有三种方法:1、Filter方法 其主要思想是:对每一维的特征“打分”,即给每一维的特征赋予权重,这样的权重就代表着该维特征的重要性,然后依据权重排序。 主要的方法有: Chi-squared test(卡方检验...
  • 结合Scikit-learn介绍几种常用的特征选择方法

    万次阅读 多人点赞 2016-06-07 22:51:32
    特征选择主要有两个功能:减少特征数量、降维,使模型泛化能力更强,减少过拟合增强对特征和特征值之间的理解拿到数据集,一个特征选择方法,往往很难同时完成这两个目的。通常情况下,我们经常不管三七二十一,选择...
  • 下面列举一些最常用的方法 1、根据阈值过滤掉方差小的变量。 2、通过计算变量与标签的相关系数,留下相关性高的特征。 3、根据决策树或者随机森林,选择重要...特征选择方法,大部分在sklearn库中有对应的实现。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 488,601
精华内容 195,440
关键字:

属于特征选择的方法