面试(6):特征选择与特征提取(降维)

1、相同点与不同点

  • 相同点:特征选择和特征提取二者达成的效果相似,都是试图减少数据集中属性(特征)的数量,找出最有效(同类样本的不变性、不同样本的鉴别性、对噪声的鲁棒性)的特征。
  • 不同点:特征提取(降维)主要通过属性间的关系,通过组合原有属性产生新的属性,最终会改变特征空间;特征选择是在原始数据集中提取特征的子集,是一种包含关系,不改变特征空间。

2、特征选择

2.1 过滤型(Filter)

  评估单个特征和结果值之间的相关性,排序留下TOP相关的特征部分。
  缺点:没有考虑到特征之间的关联作用,可能把关联特征去除掉。
(1)方差选择法:根据阈值,移除低方差特征。
   SelectKBest移除那些除了评分最高的 K 个特征之外的所有特征。
   SelectPercentile移除除了用户指定的最高得分百分比之外的所有特征。

from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest, SelectPercentile
from sklearn.feature_selection import f_classif
iris = load_iris()
X, y = iris.data, iris.target
sp = SelectPercentile(f_classif, percentile= 90)
#得到返回至少含有90%特征信息的特征
# sp = SelectKBest(chi2, k=2)
X_result = sp.fit_transform(X, y)
#可以看到哪些特征被保留
sp.get_support()
#输出结果
array([ True, False,  True,  True], dtype=bool)

(2)相关系数法:根据每个特征与目标值的相关系数与P值,去掉相关系数小的特征。

import pandas as pd
from sklearn import  datasets
import numpy as np
import seaborn as sns
from sklearn.feature_selection import mutual_info_classif
iris = datasets.load_iris()
X = iris.data
y = iris.target
new_y = [y[i:i+1] for i in range(0, len(y), 1)]
data = np.hstack((X, new_y))
data_df = pd.DataFrame(data)
#0到3表示特征,4表示目标变量,画图查看相关性,如下图所示
sns.heatmap(data_df.corr(), annot= True, fmt=.2f)
————————————————
#feature_selection库的SelectKBest类结合相关系数来选择特征的代码如下:
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr

#选择K个最好的特征,返回选择特征后的数据
#第一个参数为计算评估特征是否好的函数,该函数输入特征矩阵和目标向量,
#输出二元组(评分,P值)的数组,数组第i项为第i个特征的评分和P值。在此定义为计算相关系数
#参数k为选择的特征个数
SelectKBest(lambda X, Y: array(map(lambda x:pearsonr(x, Y), X.T)).T, k=2).fit_transform(iris.data, iris.target)

(3)卡方检验:对于定性变量分析,经典的卡方检验是检验定性自变量对定性因变量的相关性。假设自变量有N种取值,因变量有M种取值,考虑自变量等于i且因变量等于j的样本频数的观察值与期望的差距,构建统计量:
χ 2 = ∑ ( A − E ) 2 E \chi^{2}=\sum \frac{(A-E)^{2}}{E} χ2=E(AE)2
  不难发现,这个统计量的含义简而言之就是自变量对因变量的相关性。用feature_selection库的SelectKBest类结合卡方检验来选择特征的代码如下:

from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

#选择K个最好的特征,返回选择特征后的数据
SelectKBest(chi2, k=2).fit_transform(iris.data, iris.target)

(4)互信息法:经典的互信息也是评价定性自变量对定性因变量的相关性的,互信息计算公式如下:
I ( X ; Y ) = ∑ x ∈ X ∑ y ∈ Y p ( x , y ) log ⁡ p ( x , y ) p ( x ) p ( y ) I(X ; Y)=\sum_{x \in X} \sum_{y \in Y} p(x, y) \log \frac{p(x, y)}{p(x) p(y)} I(X;Y)=xXyYp(x,y)logp(x)p(y)p(x,y)
  考虑两个随机变量X和Y,它们的联合概率密度函数为p(x,y),其边缘概率密度函数分别为p(x)和p(y),互信息I(X;Y)为联合分布p(x,y)和乘积分布p(x)p(y)之间的相对熵。

  ps:F检验:用来评估两个变量的线性相关性;
  T检验和F检验:
  在统计学中,一元线性回归里t检验和f检验等价,但在多元线性回归里,t检验可以检验各个回归系数显著性,f检验用来检验总体回归关系的显著性。
t检验常能用作检验回归方程中各个参数的显著性,而f检验则能用作检验整个回归关系的显著性。各解释变量联合起来对被解释变量有显著的线性关系,并不意味着每一个解释变量分别对被解释变量有显著的线性关系。
  在一般情形下,t检验与F检验的结果没有必然联系;但当解释变量之间两两不相关时,若所有解释变量的系数均通过t检验,那么回归方程也能通过F检验。

2.2 包裹型(Wrapper)

  把特征选择看成是对特征子集的搜索问题,筛选各种特征子集,用模型进行评估结果。典型的包裹型算法为特征递归删除算法。
  比如对逻辑回归算法:
  (1)用全量变量跑回归模型;(2)根据回归系数去掉系数比较小的5~10%的特征;(3)不断回归,删掉回归系数小的特征,直到准确率/auc大幅下降。

  给定一个外部的估计器,可以对特征赋予一定的权重(比如,线性模型的相关系数),recursive feature elimination ( RFE ) 通过考虑越来越小的特征集合来递归的选择特征。 首先,评估器在初始的特征集合上面训练并且每一个特征的重要程度是通过一个 coef_ 属性 或者 feature_importances_ 属性来获得。 然后,从当前的特征集合中移除最不重要的特征。在特征集合上不断的重复递归这个步骤,直到最终达到所需要的特征数量为止。

from sklearn.feature_selection import RFE
from sklearn.ensemble import GradientBoostingClassifier
from sklearn import datasets
iris = datasets.load_iris()
gbdt_RFE = RFE(estimator=GradientBoostingClassifier(random_state= 123),n_features_to_select=2)
gbdt_RFE.fit(iris.data, iris.target)
gbdt_RFE.support_
#特征选择输出结果
gbdt_RFE.support_
#输出结果为:
array([False, False,  True,  True], dtype=bool)
2.3 嵌入型(Embedded)

(1)基于惩罚项的特征选择法
  使用带惩罚项的基模型,除了筛选出特征外,同时也进行了降维。使用feature_selection库的SelectFromModel类结合带L1惩罚项的逻辑回归模型,来选择特征的代码如下:

from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
 
#带L1惩罚项的逻辑回归作为基模型的特征选择
SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(iris.data, iris.target)

  实际上,**L1惩罚项降维的原理在于保留多个对目标值具有同等相关性的特征中的一个,所以没选到的特征不代表不重要。**故,可结合L2惩罚项来优化。具体操作为:若一个特征在L1中的权值为1,选择在L2中权值差别不大且在L1中权值为0的特征构成同类集合,将这一集合中的特征平分L1中的权值,故需要构建一个新的逻辑回归模型:

from sklearn.linear_model import LogisticRegression
 
class LR(LogisticRegression):
    def __init__(self, threshold=0.01, dual=False, tol=1e-4, C=1.0,
                 fit_intercept=True, intercept_scaling=1, class_weight=None,
                 random_state=None, solver='liblinear', max_iter=100,
                 multi_class='ovr', verbose=0, warm_start=False, n_jobs=1):
 
        #权值相近的阈值
        self.threshold = threshold
        LogisticRegression.__init__(self, penalty='l1', dual=dual, tol=tol, C=C,
                 fit_intercept=fit_intercept, intercept_scaling=intercept_scaling, class_weight=class_weight,
                 random_state=random_state, solver=solver, max_iter=max_iter,
                 multi_class=multi_class, verbose=verbose, warm_start=warm_start, n_jobs=n_jobs)
        #使用同样的参数创建L2逻辑回归
        self.l2 = LogisticRegression(penalty='l2', dual=dual, tol=tol, C=C, fit_intercept=fit_intercept, intercept_scaling=intercept_scaling, class_weight = class_weight, random_state=random_state, solver=solver, max_iter=max_iter, multi_class=multi_class, verbose=verbose, warm_start=warm_start, n_jobs=n_jobs)
 
    def fit(self, X, y, sample_weight=None):
        #训练L1逻辑回归
        super(LR, self).fit(X, y, sample_weight=sample_weight)
        self.coef_old_ = self.coef_.copy()
        #训练L2逻辑回归
        self.l2.fit(X, y, sample_weight=sample_weight)
 
        cntOfRow, cntOfCol = self.coef_.shape
        #权值系数矩阵的行数对应目标值的种类数目
        for i in range(cntOfRow):
            for j in range(cntOfCol):
                coef = self.coef_[i][j]
                #L1逻辑回归的权值系数不为0
                if coef != 0:
                    idx = [j]
                    #对应在L2逻辑回归中的权值系数
                    coef1 = self.l2.coef_[i][j]
                    for k in range(cntOfCol):
                        coef2 = self.l2.coef_[i][k]
                        #在L2逻辑回归中,权值系数之差小于设定的阈值,且在L1中对应的权值为0
                        if abs(coef1-coef2) < self.threshold and j != k and self.coef_[i][k] == 0:
                            idx.append(k)
                    #计算这一类特征的权值系数均值
                    mean = coef / len(idx)
                    self.coef_[i][idx] = mean
        return self

  使用feature_selection库的SelectFromModel类结合带L1以及L2惩罚项的逻辑回归模型,来选择特征的代码如下:

from sklearn.feature_selection import SelectFromModel
 
#带L1和L2惩罚项的逻辑回归作为基模型的特征选择
#参数threshold为权值系数之差的阈值
SelectFromModel(LR(threshold=0.5, C=0.1)).fit_transform(iris.data, iris.target)

(2)基于树模型的特征选择法
  树模型中GBDT也可用来作为基模型进行特征选择,使用feature_selection库的SelectFromModel类结合GBDT模型,来选择特征的代码如下:

from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
 
#GBDT作为基模型的特征选择
SelectFromModel(GradientBoostingClassifier()).fit_transform(iris.data, iris.target)

3、特征提取(降维)

  当特征选择完成后,可以直接训练模型了,但是可能由于特征矩阵过大,导致计算量大,训练时间长的问题,因此降低特征矩阵维度也是必不可少的。常见的降维方法除了以上提到的基于L1惩罚项的模型以外,另外还有主成分分析法(PCA)和线性判别分析(LDA),线性判别分析本身也是一个分类模型。PCA和LDA有很多的相似点,其本质是要将原始的样本映射到维度更低的样本空间中,但是PCA和LDA的映射目标不一样:

  • PCA是为了让映射后的样本具有最大的发散性;
  • LDA是为了让映射后的样本有最好的分类性能。

  所以说PCA是一种无监督的降维方法,而LDA是一种有监督的降维方法。

3.1 主成分分析法(PCA)

  使用decomposition库的PCA类选择特征的代码如下:

from sklearn.decomposition import PCA
 
#主成分分析法,返回降维后的数据
#参数n_components为主成分数目
PCA(n_components=2).fit_transform(iris.data)
3.2 线性判别分析法(LDA)

  使用lda库的LDA类选择特征的代码如下:

from sklearn.lda import LDA
 
#线性判别分析法,返回降维后的数据
#参数n_components为降维后的维数
LDA(n_components=2).fit_transform(iris.data, iris.target)

  特征选择是一个重要的数据预处理过程,主要有两个原因:一是减少特征数量、降维,使模型泛化能力更强,减少过拟合;二是增强对特征和特征值之间的理解
  常见的特征选择方式:

  • 去除方差较小的特征
  • 正则化。1正则化能够生成稀疏的模型。L2正则化的表现更加稳定,由于有用的特征往往对应系数非零。
  • 随机森林,对于分类问题,通常采用基尼不纯度或者信息增益,对于回归问题,通常采用的是方差或者最小二乘拟合。一般不需要feature engineering、调参等繁琐的步骤。它的两个主要问题,1是重要的特征有可能得分很低(关联特征问题),2是这种方法对特征变量类别多的特征越有利(偏向问题)。
  • 稳定性选择。是一种基于二次抽样和选择算法相结合较新的方法,选择算法可以是回归、SVM或其他类似的方法。它的主要思想是在不同的数据子集和特征子集上运行特征选择算法,不断的重复,最终汇总特征选择结果,比如可以统计某个特征被认为是重要特征的频率(被选为重要特征的次数除以它所在的子集被测试的次数)。理想情况下,重要特征的得分会接近100%。稍微弱一点的特征得分会是非0的数,而最无用的特征得分将会接近于0。
  • 7
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值