机器学习之超详细的特征选择教程(过滤法、包装法、嵌入法、合成法)
在进行数据处理或机器学习时,哪些特征该涵盖进来,哪些则要剔除掉是一个重要的问题。如果将无关的特征纳入进来,就造成了特征冗余,还有可能影响预测准确率。在现有特征中,如何有效地进行特征选择呢?
特征选择的一般流程
特征选择的一般过程:
生成子集:搜索特征子集,为评价函数提供特征子集 设置评价函数:评价特征子集的好坏 确定停止准则:与评价函数相关,一般是阈值,评价函数达到一定标准后就可停止搜索 验证:在验证数据集上验证选出来的特征子集的有效性
理论上我们可以枚举出所有的特征子集,但是, 当特征数量很大的时候, 这个搜索空间会很大,如何找最优特征还是需要一些经验结论。
特征选择的常用方法
根据特征选择的形式,可分为三大类方法:
过滤法(Filter):按照发散性或相关性对各个特征进行评分,设定阈值或者待选择特征的个数进行筛选 包装法(Wrapper):根据评价函数,每次选择若干特征,或者排除若干特征 嵌入法Embedded():先使用某些机器学习的模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征(类似于Filter,只不过系数是通过训练得来的) 特征合成。比较经典的降维方法有主成分分析法(PCA),它可以将多个原始维度数据合成新维度数据。
过滤法
过滤法的基本想法是:分别对每个特征 ,计算 相对于预测结果 的信息量 ,得到 个结果。然后 按照从大到小排序,输出前 个特征。
但这里有两个重要细节需要考虑:1.使用什么方法评价信息量 2.如何选。
评价方法
相关系数方法(如Pearson,Kendal系数等),看特征与结果的相关性; 卡方检验,假设检验的重要方法,看特征与结果之间是否独立; 互信息和最大信息系数,信息论的方法; 距离相关系数 方差选择法,通过特征本身的变异率进行筛选,变异率小的特征不考虑
相关系数
皮尔森相关系数是一种简单的,能帮助理解特征和响应变量之间关系的方法,衡量的是变量之间的线性相关性,结果的取值区间为[-1,1] , -1 表示完全的负相关, +1 表示完全的正相关, 0 表示没有线性相关性。Scipy
的pearsonr
方法能够同时计算相关系数和p-value
from scipy.stats import pearsonr
x1 = [51, 80, 95, 19, 73, 84, 65, 30, 1, 35, 13, 61, 36, 65, 57, 40, 15, 73, 58, 62] # 特征1数据
x2 = [7.0, 27.5, 23.0, 32.0, 15.5, 44.0, 10.5, 29.5, 36.0, 47.5, 27.0, 28.5, 26.5, 41.5, 12.5, 0.5, 19.0, 48.5, 0.5, 24.0] #特征2数据
y = [14, 64, 54, 72, 36, 92, 24, 62, 72, 95, 55, 64, 60, 84, 33, 5, 40, 99, 2, 48] # 结果数据
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人的寿命大于或等于平均寿命.这里的“平均寿命”是指“当地人均寿命”.试分析官员在经济上是否清白与他们寿命的长短之间是否独立?
列 列联表
短寿 | 长寿 | 合计 | |
---|---|---|---|
贪官 | 348 | 152 | 500 |
廉洁官 | 93 | 497 | 590 |
合计 | 441 | 649 | 1090 |
由计算公式得 , 所以我们有 的把握可以认为在经济上不清白的人易过早死亡.
我们可以借助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 | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | 0 |
1 | 4.9 | 3 | 1.4 | 0.2 | 0 |
2 | 4.7 | 3.2 | 1.3 | 0.2 | 0 |
3 | 4.6 | 3.1 | 1.5 | 0.2 | 0 |
4 | 5 | 3.6 | 1.4 | 0.2 | 0 |
接下来进行特征筛选:
#选择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数据集
前五条数据如下:
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
mean radius | 17.99 | 20.57 | 19.69 | 11.42 | 20.29 |
mean texture | 10.38 | 17.77 | 21.25 | 20.38 | 14.34 |
mean perimeter | 122.8 | 132.9 | 130 | 77.58 | 135.1 |
mean area | 1001 | 1326 | 1203 | 386.1 | 1297 |
mean smoothness | 0.1184 | 0.08474 | 0.1096 | 0.1425 | 0.1003 |
mean compactness | 0.2776 | 0.07864 | 0.1599 | 0.2839 | 0.1328 |
mean concavity | 0.3001 | 0.0869 | 0.1974 | 0.2414 | 0.198 |
mean concave points | 0.1471 | 0.07017 | 0.1279 | 0.1052 | 0.1043 |
mean symmetry | 0.2419 | 0.1812 | 0.2069 | 0.2597 | 0.1809 |
mean fractal dimension | 0.07871 | 0.05667 | 0.05999 | 0.09744 | 0.05883 |
radius error | 1.095 | 0.5435 | 0.7456 | 0.4956 | 0.7572 |
texture error | 0.9053 | 0.7339 | 0.7869 | 1.156 | 0.7813 |
perimeter error | 8.589 | 3.398 | 4.585 | 3.445 | 5.438 |
area error | 153.4 | 74.08 | 94.03 | 27.23 | 94.44 |
smoothness error | 0.006399 | 0.005225 | 0.00615 | 0.00911 | 0.01149 |
compactness error | 0.04904 | 0.01308 | 0.04006 | 0.07458 | 0.02461 |
concavity error | 0.05373 | 0.0186 | 0.03832 | 0.05661 | 0.05688 |
concave points error | 0.01587 | 0.0134 | 0.02058 | 0.01867 | 0.01885 |
symmetry error | 0.03003 | 0.01389 | 0.0225 | 0.05963 | 0.01756 |
fractal dimension error | 0.006193 | 0.003532 | 0.004571 | 0.009208 | 0.005115 |
worst radius | 25.38 | 24.99 | 23.57 | 14.91 | 22.54 |
worst texture | 17.33 | 23.41 | 25.53 | 26.5 | 16.67 |
worst perimeter | 184.6 | 158.8 | 152.5 | 98.87 | 152.2 |
worst area | 2019 | 1956 | 1709 | 567.7 | 1575 |
worst smoothness | 0.1622 | 0.1238 | 0.1444 | 0.2098 | 0.1374 |
worst compactness | 0.6656 | 0.1866 | 0.4245 | 0.8663 | 0.205 |
worst concavity | 0.7119 | 0.2416 | 0.4504 | 0.6869 | 0.4 |
worst concave points | 0.2654 | 0.186 | 0.243 | 0.2575 | 0.1625 |
worst symmetry | 0.4601 | 0.275 | 0.3613 | 0.6638 | 0.2364 |
worst fractal dimension | 0.1189 | 0.08902 | 0.08758 | 0.173 | 0.07678 |
target | 0 | 0 | 0 | 0 | 0 |
一共有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 、陡坡图、平行分析等原则。前 个主成分表示为
其中, 称为因子载荷。
计算指标在不同主成分线性组合中的系数:
我们使用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