查看原文
其他

机器学习之超详细的特征选择教程(过滤法、包装法、嵌入法、合成法)

模型视角 模型视角 2023-09-04

在进行数据处理或机器学习时,哪些特征该涵盖进来,哪些则要剔除掉是一个重要的问题。如果将无关的特征纳入进来,就造成了特征冗余,还有可能影响预测准确率。在现有特征中,如何有效地进行特征选择呢?

特征选择的一般流程

特征选择的一般过程:

  • 生成子集:搜索特征子集,为评价函数提供特征子集
  • 设置评价函数:评价特征子集的好坏
  • 确定停止准则:与评价函数相关,一般是阈值,评价函数达到一定标准后就可停止搜索
  • 验证:在验证数据集上验证选出来的特征子集的有效性

理论上我们可以枚举出所有的特征子集,但是, 当特征数量很大的时候, 这个搜索空间会很大,如何找最优特征还是需要一些经验结论。

特征选择的常用方法

根据特征选择的形式,可分为三大类方法:

  • 过滤法(Filter):按照发散性或相关性对各个特征进行评分,设定阈值或者待选择特征的个数进行筛选
  • 包装法(Wrapper):根据评价函数,每次选择若干特征,或者排除若干特征
  • 嵌入法Embedded():先使用某些机器学习的模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征(类似于Filter,只不过系数是通过训练得来的)
  • 特征合成。比较经典的降维方法有主成分分析法(PCA),它可以将多个原始维度数据合成新维度数据。

过滤法

过滤法的基本想法是:分别对每个特征 ,计算 相对于预测结果 的信息量 ,得到 个结果。然后 按照从大到小排序,输出前 个特征。

但这里有两个重要细节需要考虑:1.使用什么方法评价信息量 2.如何选。

评价方法

  • 相关系数方法(如Pearson,Kendal系数等),看特征与结果的相关性;
  • 卡方检验,假设检验的重要方法,看特征与结果之间是否独立;
  • 互信息和最大信息系数,信息论的方法;
  • 距离相关系数
  • 方差选择法,通过特征本身的变异率进行筛选,变异率小的特征不考虑

相关系数

皮尔森相关系数是一种简单的,能帮助理解特征和响应变量之间关系的方法,衡量的是变量之间的线性相关性,结果的取值区间为[-1,1] , -1 表示完全的负相关, +1 表示完全的正相关, 0 表示没有线性相关性。Scipypearsonr方法能够同时计算相关系数和p-value

from scipy.stats import pearsonr
x1 = [5180951973846530,  13513613665574015735862# 特征1数据
x2 = [7.027.523.032.015.544.010.529.536.047.527.028.526.541.512.50.519.048.50.524.0#特征2数据
y = [146454723692246272955564608433,  54099,  248# 结果数据
print(pearsonr(x1,y))
print(pearsonr(x2,y))

输出结果为 PearsonRResult(statistic=0.020864449411346502, pvalue=0.930425438739739)
PearsonRResult(statistic=0.9939219884168465, pvalue=1.0516806437334743e-18)

通过结果可以看到x1与y之间几乎没有线性相关性,x2与y之间的相关性则非常强。我们将图画出来:

import numpy as np
import matplotlib.pyplot as plt
plt.xkcd()
plt.rcParams['font.family'] = ['SimHei']
plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.scatter(x1,y,label='Pearson相关性为0.020')
plt.xlabel('x1')
plt.ylabel('y')
plt.legend()
plt.subplot(1,2,2)
plt.scatter(x2,y,label='Pearson相关性为0.994')
plt.xlabel('x2')
plt.ylabel('y')
plt.legend()
plt.tight_layout()
plt.savefig('images/feature0101.png')
plt.show()

线性相关性强的两个变量之间数据点在一条直线周围。不过Pearson相关系数的一个明显缺陷是,它只是测量的变量之间的线性相关性。如果关系是非线性的,Pearson相关性也可能会接近 0 。比如下面这幅图,二者之间有二次关系,但线性相关系数为0.

x3 = range(-10,11)
y2 = [x**2 for x in x3]
plt.scatter(x3,y2,label='Pearson相关性为0.0')
plt.tight_layout()
plt.savefig('images/feature0102.png')
plt.show()

卡方检验

经典的卡方检验是依据总体分布状况,计算出分类变量中各类别的期望频数,与分布的观察频数进行对比,判断期望频数与观察频数是否有显著差异. 两个变量之间是否独立的检验步骤如下(:

  • (1)提出假设
  • (2)根据概率的乘法法则计算理论数:理论数的计算方法;
  • (3)计算检验统计量(其中表示观察值,表示期望值);
  • (4)确定自由度:2×2列联表的自由度是列联表的行数,是列联表的列数,若自由度=1,则应做连续性校正;
  • (5)建立拒绝域;
  • (6)根据p值或临界值得出是否要拒绝原假设的结论

例子

巴西医生马廷思收集犯有各种贪污、受贿罪的官员与廉洁官员之寿命的调查资料:500名贪官中有348人寿命小于平均寿命、152人的寿命大于或等于平均寿命;590名廉洁官中有93人的寿命小于平均寿命、497人的寿命大于或等于平均寿命.这里的“平均寿命”是指“当地人均寿命”.试分析官员在经济上是否清白与他们寿命的长短之间是否独立?

列联表


短寿长寿合计
贪官348152500
廉洁官93497590
合计4416491090

由计算公式得 , 所以我们有 的把握可以认为在经济上不清白的人易过早死亡.

我们可以借助scipy中的统计函数stats.chi2_contigency进行计算

import pandas as pd
df = pd.DataFrame([[348,152],[93,497]],index=['贪官','廉洁官'],columns=['短寿','长寿']) # 构建数据
from scipy.stats import chi2_contingency
chi2_contingency(df)

结果如下:
Chi2ContingencyResult(statistic=323.40433728617546, pvalue=2.626485030719825e-72, dof=1, expected_freq=array([[202.29357798, 297.70642202], [238.70642202, 351.29357798]]))

通过结果第二项P值(极小)推断出拒绝原假设,认为是否贪污与寿命长短之间是有关联的。

通过sklearn库中的特征选择函数和卡方函数可以方便的对数据进行筛选,我们以鸢尾花数据集为例:

from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest # 选取最优的2个特征
from sklearn.feature_selection import chi2 # 引入卡方特征筛选方法
iris = load_iris() # 加载数据集
X, y = iris.data, iris.target #iris数据集

前五条数据如下:


sepal length (cm)sepal width (cm)petal length (cm)petal width (cm)target
05.13.51.40.20
14.931.40.20
24.73.21.30.20
34.63.11.50.20
453.61.40.20

接下来进行特征筛选:

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

结果如下:array([[1.4, 0.2], [1.4, 0.2], [1.3, 0.2], [1.5, 0.2], [1.4, 0.2], [1.7, 0.4], ...]] 很明显,最后的结果选择的是 petal length (cm) 和 petal width (cm)作为相关性最强的特征。

互信息与最大信息系数

经典的互信息(Mutual information)也是评价类别型变量对类别型变量的相关性的,互信息公式如下:

是0/1离散值的时候,这个公式如上。很容易推广到 是多个离散值的情况。这里的 都是从训练集上得到的。 公式根据 KL 距离(Kullback-Leibler)获得:

也就是说, MI 衡量的是 x_i 和 y 的独立性。如果它俩独立 P(x_i,y)=p(x_i)p(y) ,那么 KL 距离值为0,也就是 不相关了,可以去除 。相反,如果两者密切相关,那么 MI 值会很大。


在对 MI 进行排名后,最后剩余的问题就是如何选择 个值(前 k 个 )。

方差选择法

方差方法先要计算各个特征的方差,然后根据阈值,选择方差大于阈值的特征。为了消除特征量纲不统一的问题,可以先进行特征归一化处理。

包装法

基本思想:对于每一个待选的特征子集,都在训练集上训练一遍模型,然后在测试集上根据误差大小选择出特征子集。需要先选定特定算法,通常选用普遍效果较好的算法, 例如随机森林Random Forest,支持向量机SVM, k近邻kNN等等。具体生成子集的方法可以采用前向搜索法(增量式添加新特征)、后向搜索法(在最全子集中逐个递减)、递归特征消除法(使用一个基模型来进行多轮训练,每轮训练后通过学习器返回的特征重要性,消除若干权重较低的特征,再基于新的特征集进行下一轮训练)

这里介绍递归特征消除法(Recursive feature elimination)的Python实现。我们对乳腺癌数据进行特征消除,先导入数据:

from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest # 选取最优的2个特征
from sklearn.feature_selection import chi2 # 引入卡方特征筛选方法
iris = load_iris() # 加载数据集
X, y = iris.data, iris.target  #iris数据集

前五条数据如下:


01234
mean radius17.9920.5719.6911.4220.29
mean texture10.3817.7721.2520.3814.34
mean perimeter122.8132.913077.58135.1
mean area100113261203386.11297
mean smoothness0.11840.084740.10960.14250.1003
mean compactness0.27760.078640.15990.28390.1328
mean concavity0.30010.08690.19740.24140.198
mean concave points0.14710.070170.12790.10520.1043
mean symmetry0.24190.18120.20690.25970.1809
mean fractal dimension0.078710.056670.059990.097440.05883
radius error1.0950.54350.74560.49560.7572
texture error0.90530.73390.78691.1560.7813
perimeter error8.5893.3984.5853.4455.438
area error153.474.0894.0327.2394.44
smoothness error0.0063990.0052250.006150.009110.01149
compactness error0.049040.013080.040060.074580.02461
concavity error0.053730.01860.038320.056610.05688
concave points error0.015870.01340.020580.018670.01885
symmetry error0.030030.013890.02250.059630.01756
fractal dimension error0.0061930.0035320.0045710.0092080.005115
worst radius25.3824.9923.5714.9122.54
worst texture17.3323.4125.5326.516.67
worst perimeter184.6158.8152.598.87152.2
worst area201919561709567.71575
worst smoothness0.16220.12380.14440.20980.1374
worst compactness0.66560.18660.42450.86630.205
worst concavity0.71190.24160.45040.68690.4
worst concave points0.26540.1860.2430.25750.1625
worst symmetry0.46010.2750.36130.66380.2364
worst fractal dimension0.11890.089020.087580.1730.07678
target00000

一共有30个特征,我们现在要选出和肿瘤良性/恶性相关的最重要的5个特征:

from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier # 随机森林

#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数
RFE(estimator=RandomForestClassifier(), n_features_to_select=5).fit_transform(cancer.data, cancer.target)

模型流程:

最后筛选得到的5个特征为:

array([[1.471e-01, 2.538e+01, 1.846e+02, 2.019e+03, 2.654e-01],
       [7.017e-02, 2.499e+01, 1.588e+02, 1.956e+03, 1.860e-01],
       [1.279e-01, 2.357e+01, 1.525e+02, 1.709e+03, 2.430e-01],
       ...,
       [5.302e-02, 1.898e+01, 1.267e+02, 1.124e+03, 1.418e-01],
       [1.520e-01, 2.574e+01, 1.846e+02, 1.821e+03, 2.650e-01],
       [0.000e+00, 9.456e+00, 5.916e+01, 2.686e+02, 0.000e+00]])

嵌入法

基于惩罚项的特征选择法

通过L1正则项来选择特征:L1正则方法具有稀疏解的特性,因此天然具备特征选择的特性。我们所说的正则化,就是在原来的损失的基础上,加上了一些正则化项或者称为模型复杂度惩罚项。以线性回归为例子。最初的损失函数(最小二乘法):

加上L1正则项 (lasso回归) :

加上L2正则项 (岭回归):

这里用逻辑回归模型+L1政策项对乳腺癌数据进行特征选择

from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression

#带L1惩罚项的逻辑回归作为基模型的特征选择   
feature_new = SelectFromModel(LogisticRegression(penalty="l1", solver='liblinear',C=0.25)).fit_transform(X,y)

输出消除特征之后的新数据:

from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression

#带L1惩罚项的逻辑回归作为基模型的特征选择   
feature_new = SelectFromModel(LogisticRegression(penalty="l1", solver='liblinear',C=0.25)).fit_transform(X,y)

输出消除特征之后新特征的形状:

feature_new.shape

(569,7) 说明经过特征消除之后剩下7个特征。我们可以通过调节模型中的系数C来调整最终确定的特征数量。

特征合成

PCA方法

PCA把原先的个特征用数目更少的个特征取代,新的个特征一要保证最大化样本方差,二保证相互独立的。新特征是旧特征的线性组合,提供一个新的框架来解释结果。

PCA 算法步骤

设有 个样本, 每个样本有 个指标 (变量): , 得到原始数据矩阵:

  1. 对原始指标数据矩阵 做标准化 (或中心化) 处理, 并计算标准化样本数据矩阵的协方差矩阵, 其中
  1. 求出 的特征值 和相应的特征向量, 以及相应的正交化单位特征向量
  1. 选择主成分。在已确定的全部个主成分中合理选择个,一般用方差贡献率

解释主成分所反映信息量的大小,的确定是以累计贡献率

达到足够大(一般在 以上)为原则。另外, 也可以考虑特征值大于 1 、陡坡图、平行分析等原则。前 个主成分表示为

其中, 称为因子载荷。

  1. 计算指标在不同主成分线性组合中的系数:

我们使用sklearn的函数进行PCA算法

from sklearn.datasets import load_breast_cancer
from sklearn.decomposition import PCA
cancer = load_breast_cancer() # 加载数据集
X = cancer.data
n_components = 5
pca = PCA(n_components=n_components)
pca.fit(X)
# 显示结果
n_feature = X.shape[1]
col_names = ['X'+str(i) for i in range(1,n_feature+1)]
index_names = ['F'+str(i) for i in range(1,n_components+1)]
comps = pd.DataFrame(pca.components_,index=index_names,columns=col_names)
print('解释方差:',pca.explained_variance_)
print('解释方差占比:',pca.explained_variance_ratio_)

解释方差:[4.43782605e+05 7.31010006e+03 7.03833742e+02 5.46487379e+01 3.98900178e+01]
解释方差占比:[9.82044672e-01 1.61764899e-02 1.55751075e-03 1.20931964e-04 8.82724536e-05]

其实从解释方差占比来看,第一个主成分就已经解释98%的方差变动了。我们不妨看一下解释方差的陡坡图:

plt.plot(range(1,6),pca.explained_variance_ratio_,label='解释性方差占比')
plt.plot(range(1,6),pca.explained_variance_ratio_.cumsum(),label='累计方差占比')
plt.legend()
plt.tight_layout()
plt.savefig('images/feature0104.png')
plt.xlabel('主成分个数')
plt.show()


以上就是特征选择方法的介绍了~

数学建模付费交流群已经建立了!(具体规则见:数学建模交流群来了~欢迎加入!)欢迎加入!入群须进行自我介绍,结识更多志同道合的朋友!

参考资料:

  • 孙佳伟【机器学习】特征选择(Feature Selection)方法汇总 https://zhuanlan.zhihu.com/p/74198735
  • 模型视角 用主成分分析法PCA进行综合评价(附Python代码)https://mp.weixin.qq.com/s/9KMvpCHY2m46Y2YRGlPl_w

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存