特征降维指的是采用某种映射方法,将高维向量空间的数据点映射到低维的空间中。在原始的高维空间中,数据可能包含冗余信息及噪声信息,其在实际应用中会对模型识别造成误差,降低模型准确率;而通过特征降维可以减少冗余信息造成的误差,从而提高模型准确率。
  特征降维的方法主要分为两类:特征选择和特征提取。

1 特征选择

  特征选择方法比较简单粗暴,直接将不重要的特征删除。特征选择方法主要包括三大类:过滤法(Filter)、包装法(Wrapper)和嵌入法(Embedded)。

  • 过滤法:根据发散性或者相关性对各个特征进行评分,通过设定阈值或者待选择阈值的个数来选择特征。
  • 包装法:根据目标函数(通常是预测效果评分)每次选择若干特征,或者排除若干特征。
  • 嵌入法:使用机器学习的某些算法和模型进行训练,得到各个权重的权值系数,并根据系数从大到小选择特征。
1.1 过滤法

  过滤法是依据特征向量和目标变量之间的关系来进行特征选择的。该类方法的主要特点有以下几方面:(1)不借助学习算法; (2)依赖于真实世界的数据集特征; (3)一般方式是为每一个特征进行“打分”评估(即给每一维权重赋予权重,权重即代表了该特征的重要性,然后按重要性进行排序)。其常用方法主要有:卡方检验、相关系数和互信息等。
  使用卡方检验进行特征选择的内容在前序博文中已经介绍过,具体可以参考:https://blog.csdn.net/yeshang_lady/article/details/112796313,这里不再赘述。

1.1.1 相关系数法

  使用相关系数法,先要计算各个特征对目标值的相关系数及p值,然后根据阈值筛选特征。

import numpy as np
from sklearn.datasets import load_iris
from array import array
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr

X,y=load_iris(return_X_y=True)
X_new=SelectKBest(lambda X,y:np.array(list(map(lambda x:pearsonr(x,y),X.T))).T[0],
                  k=2).fit_transform(X,y)

  常用的相关系数类型及其适用范围如下表:

类型适用范围
pearson线性数据
spearman线性数据或简单单调的非线性数据
kendall线性数据或简单单调的非线性数据
1.1.2 互信息

  互信息(与信息增益概念相同)可以衡量随机变量之间的依赖程度,该值越大,变量之间的依赖程度越高。Sklearn包中提供了依据互信息进行特征选择的两个方法:mutual_info_classif(针对分类任务)和mutual_info_regression(针对回归任务)。这两个函数的相同,以multi_info_classif为例,其中几个参数的说明如下表:
在这里插入图片描述

参数说明
X特征数据
y目标变量
discrete_features用来说明那些变量是离散的。当取值为True时,则将全部的变量当作离散型变量。当取值为array型变量时,可以在array中指定离散变量的索引。当取值为auto时,如果X的取值稀疏时当作离散变量,否则当作离散变量。
n_neighbors连续型变量计算互信息时的邻居树。该值越大,则互信息估计的方差越小。
from sklearn.feature_selection import mutual_info_classif
from sklearn.feature_selection import mutual_info_regression
from sklearn.datasets import load_iris
from sklearn.datasets import load_boston
from sklearn.feature_selection import SelectKBest

#mutual_info_classif和mutual_info_regression的结果不能直接放到SelectKBest中,
#需要做一些变化
##mutual_info_classif:分类任务
X,y=load_iris().data,load_iris().target
mi_c=mutual_info_classif(X,y,discrete_features=False)
X_new=SelectKBest(lambda X,y:mutual_info_classif(X,y,discrete_features=False),
                  k=2).fit_transform(X,y)

##mutual_info_regression:回归任务
X,y=load_boston().data,load_boston().target
mi_r=mutual_info_regression(X,y,discrete_features=False)
X_new=SelectKBest(lambda X,y:mutual_info_regression(X,y,discrete_features=False),
                  k=8).fit_transform(X,y)
1.1.3 方差选择法

  使用方差选择法,先要计算各个特征的方差,然后根据阈值选择方差大于阈值的特征(方差选择法认为特征取值越集中的特征,其包含的信息越少)。

from sklearn.feature_selection import VarianceThreshold
from sklearn.datasets import load_boston

X,y=load_boston().data,load_boston().target
X_new=VarianceThreshold(threshold=3).fit_transform(X,y)
1.1.4 F检验

  对单变量进行方差分析(https://blog.csdn.net/yeshang_lady/article/details/118054025)中的F检验,然后根据F检验统计值的大小进行特征筛选。Sklearn包中提供了专门的函数:f_classif(分类问题)和f_regression(回归问题)。

from sklearn.datasets import load_iris,load_boston
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_classif,f_regression
import pandas as pd
import numpy as np
#1.分类问题
X,y=load_iris(return_X_y=True)
f_val=f_classif(X,y)

X_new=SelectKBest(f_classif,k=2).fit_transform(X,y)
"""
#自写代码实现f_classif中F值的计算
$把类别当作因素A的不同水平
f_val_test=[]
for i in range(X.shape[1]):
    tmp=pd.DataFrame(np.c_[X[:,i],y],columns=['X','y'])
    tmp_mean=tmp['X'].mean()
    SSA_tmp=tmp.groupby('y')['X'].agg(['mean','count'])
    SSA=sum(((SSA_tmp['mean']-tmp_mean)**2)*SSA_tmp['count'])
    SSA_degree=tmp['y'].nunique()-1
    tmp['x_mean']=tmp.groupby('y').transform('mean')
    SSE=((tmp['X']-tmp['x_mean'])**2).sum()
    SSE_degree=tmp.shape[0]-tmp['y'].nunique()
    f_stat=(SSA/SSA_degree)/(SSE/SSE_degree)
    f_val_test.append(f_stat)
"""    

#2.回归问题
# 通过计算特征与目标变量之间的相关性,将相关性转为F值
X,y=load_boston(return_X_y=True)
f_val=f_regression(X,y)

X_new=SelectKBest(f_regression,k=5).fit_transform(X,y)
1.1.5 最大信息系数

  最大信息系数(Maximal Information Coefficient, MIC)用于衡量两个变量X和Y之间的关联程度(线性的或非线形的)。

from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from minepy import MINE

def mic(x,y):
    m=MINE()
    m.compute_score(x,y)
    return m.mic()

X,y=load_iris(return_X_y=True)
x_new=SelectKBest(lambda X,y:np.array(list(map(lambda x:mic(x,y),X.T))),
                  k=2).fit_transform(X,y)

阿里的书上最大信息系数部分讲的不清不楚,如果想要对最大信息系数有更多了解可以看参考资料4

1.1.6 其他

   在前面的案例中都使用了SelectKBest类来返回指定个数的特征。除了这个函数之外,sklearn中还提供了其他作用class来实现类似功能。具体如下表:

作用
SelectKBest选择前 K K K个分数最高的特征
SelectFpr对单变量特征进行假阳性率检验,选择pvalue高于指定阈值的特征
SelectFdr对单变量特征进行错误发现率检验,选择pvalue高于指定阈值的特征
SelectFwe对单变量特征进行多重比较错误检验,选择pvalue高于指定阈值的特征
SelectPercentile保留分数最高的前百分位的特征
GenericUnivariateSelect通过参数mode来选择使用以上5个函数中的任意函数进行单变量特征选择

这个类的用法也都相似。这些函数在使用时参数score_fun需要注意以下两点:

  • score_fun只能接收可调用的函数变量
  • score_fun指定的函数变量返回值为一个单独的array(代表score)变量或者为一对array(分别代表score和pvalue)。SelectFpr、SelectFdr和SelectFwe中的score_fun返回的类型必须为一对array。
1.2 包装法

  包装法的思路是将最终要用的学习器的性能作为特征子集的评价准则。此类特征选择方法中常用的为递归消除特征法。

1.2.1递归消除特征法(RFE)

  递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数(feature_importances_)低的特征,再基于新的特征集进行下一轮训练。Sklearn中除了提供了RFE之外,还提供了RFECV(比RFE多了交叉验证)。
在这里插入图片描述
类REF中的几个参数及其作用如下:

  • estimator: 学习器。要求学习器必须带有fit()方法,并且必须返回属性重要性等信息。
  • n_features_to_select: 最终保留的参数个数。
  • step:每一次迭代移除的特征数目或比例。
  • importance_getter:当取值为‘auto’时从学习器中获获取特征重要性信息,否则从自定义函数结果中获取参数重要性信息。
from sklearn.feature_selection import RFE
from sklearn.datasets import load_boston
from sklearn.linear_model import LinearRegression

X,y=load_boston().data,load_boston().target
X_new=RFE(estimator=LinearRegression(),
          n_features_to_select=8,step=3).fit_transform(X, y) #x_new有8个特征
1.2.2 SequentialFeatureSelector方法

  SequentialFeatureSelector是Sklearn.feature_selection中提供的一种特征选择方法。该方法使用贪心的策略不断地向特征子集中添加特征(前向策略)或从特征子集中移除特征(后向策略),而移入或移出的特征是根据学习器的交叉验证得分筛选出来的。在这里插入图片描述
在该类中,除了可以使用scoring参数自定义评价函数,还可以直接是使用sklearn中已经提供好的评价标准,具体可以参考网页:https://scikit-learn.org/stable/modules/model_evaluation.html#scoring-parameter

from sklearn.datasets import load_boston
from sklearn.feature_selection import SequentialFeatureSelector
from sklearn.linear_model import LinearRegression

X,y=load_boston(return_X_y=True)

sfs=SequentialFeatureSelector(LinearRegression(),n_features_to_select=6,
                                direction='backward',scoring='r2')
X_new=sfs.fit_transform(X,y)
1.3 嵌入法

  嵌入式特征选择法使用机器学习模型进行特征选择。特征选择过程与学习器相关,特征选择过程与学习器训练过程融合,在学习器训练过程中自动完成特征选择。常见的有基于惩罚项的特征选择和基于树模型的特征选择。Sklearn中提供了专门的类SelectFromModel:
在这里插入图片描述
参数的作用如下:

  • estimator: 基学习器。
  • threshold: 阈值。只有当特征的重要性大于或等于该阈值时,特征才会保留下来。
from sklearn.feature_selection import SelectFromModel
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier

X,y=load_iris(return_X_y=True)

#基于惩罚项的特征选择方法
X_new=SelectFromModel(LogisticRegression(penalty='l2',C=0.1,solver='lbfgs',
                      multi_class='auto'),max_features=2).fit_transform(X,y)

#基于树模型的特征选择方法
X_new_2=SelectFromModel(GradientBoostingClassifier(),
                      max_features=2).fit_transform(X,y)
2 特征提取

  特征提取主要是通过映射变换方法,将高维特征向量空间映射到低维特征向量空间中去,通过这种方法产生的的特征都不在原始数据中。常用的特征提取方法有主成分分析法和线性判别分析法。

2.1 主成分分析法

  主成分分析法(Principal Component Analysis,PCA)是最常用的线性降维方法,主要原理是通过某种线性投影,将高维的数据映射到低维的空间中表示,并期望在所投影的维度上的数据方差最大,以此达到使用较少的数据维度来保留较多的原始数据点特性的效果。通过PCA还可以将一组可能存在相关性的变量转换为一组线性不相关的变量(转换后的这组变量叫主成分)。

from sklearn.datasets import load_iris
from sklearn.decomposition import PCA

X,y=load_iris(return_X_y=True)
X_new=PCA(n_components=2).fit_transform(X)
2.2 线性判别分析法

  线性判别分析(Linear Discriminant Analysis, LDA)的思想非常简单:给定训练样例集,设法将样例投影到一条直线上,使得同类样例的投影点尽可能接近,异样样例的投影点尽可能远离;在对新样本进行分类时,将其投影到同样的直线上,再根据投影点的位置来确定新样本的类别。所以线性判别分析本质上是一种有监督的线性降维方法。LDA的主要优缺点在于:

  • 优点:在降维过程中可以使用类别的先验知识经验,而像PCA这样的无监督学习则无法使用类别先验知识。
  • 优点:LDA在样本分类信息依赖均值而不是方差的时候,比PCA之类的算法较优。
  • 缺点:LDA不适合对非高斯分布样本进行降维,PCA也有这个问题。
  • 缺点:LDA降维最多降到类别数k-1的维数,如果我们降维的维度大于k-1,则不能使用LDA。
  • 缺点:LDA可能过度拟合数据。
  • 缺点:LDA在样本分类信息依赖方差而不是均值的时候,降维效果不好。
from sklearn.datasets import load_iris
from sklearn.datasets import load_boston
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from sklearn.utils.multiclass import unique_labels

#1.分类任务
X,y=load_iris(return_X_y=True)
X_new=LDA(n_components=2).fit_transform(X, y)

#2.回归任务
#理论上LDA无法处理回归任务,但是发现如果将目标变量的浮点数改成整数可以绕过这个限制。
X,y=load_boston(return_X_y=True)
y=list(map(lambda x:int(x),y))
X_new=LDA(n_components=2).fit_transform(X, y)
参考资料
  1. 《阿里云天池大赛赛题解析》
  2. https://www.pianshen.com/article/68661439504/
  3. https://www.cnblogs.com/wanglei5205/p/8977986.html
  4. https://blog.csdn.net/qq_27586341/article/details/90603140
  5. https://www.cnblogs.com/pinard/p/6244265.html
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐