书接上文,进行数据清洗过程后,我们得到了没有空值、异常值、错误值的数据,但想要用海量的数据来建立我们所需要的算法模型,仅仅是数据清洗的过程是不够的,因为有的数据类型是数值,有的是字符,怎样将不同类型的数据联系起来?以及在保证最大化信息量的前提下,怎样得到便于分析的数据?这就是特征预处理要做的工作。
特征预处理是数据预处理过程的重要步骤,是对数据的一个的标准的处理,几乎所有的数据处理过程都会涉及该步骤。在进行特征预处理之前需要确定标注,即标签(label)。标注的选择主要与我们的目的相契合,比如要探究不同种族、不同地区的人们长寿是否与性别相关,那么性别就可以作为标注来表明我们的目的。
目标是寻找最优特征子集。特征选择能剔除与标注不相关(irrelevant)或冗余(redundant )的特征,从而达到减少特征个数,提高模型精确度,减少运行时间的目的。另一方面,选取出真正相关的特征简化模型,协助理解数据产生的过程。
之所以要考虑特征选择,是因为机器学习经常面临过拟合的问题。 过拟合的表现是模型参数太贴合训练集以及验证集数据,在训练集上效果很好而在测试集上表现不好。简言之模型的泛化能力差。过拟合的原因是模型对于训练集数据来说太复杂,要解决过拟合问题,一般考虑用特征选择的方法对数据进行降维。
特征选择的思想主要有三种:
特征选择与提取最全总结之过滤法 - 腾讯云开发者社区-腾讯云 (tencent.com)
相关系数是衡量特征与标注相关性最直接简单的方法,取值范围在[-1,1],当相关系数接近或等于0时,说明该特征与标注的相关性很小或不相关。皮尔逊相关系数可以用来探究任何数据类型之间的相关性,即使有离散非二值的数据,经过定序处理后也可用相关系数的方法。
以下代码为相关系数的输出,若要遍历特征全集根据相关系数筛选特征,还需要通过函数或方程等方法遍历全集。
import pandas as pd
s1=pd.Series([0.1,0.2,1.1,2.4,1.3,0.3,0.5])
s2=pd.Series([0.5,0.4,1.2,2.5,1.1,0.7,0.1])
s1.corr(s2,method='pearson ') #输出s1、s2之间的相关系数
Pearson相关系数的一个明显缺陷是,它只对线性关系敏感。如果关系是非线性的,即便两个变量具有一一对应的关系,Pearson相关性也可能会接近 0 。
卡方检验用在属性相关性检验上,一般所探查的数据类型为类别性数据。卡方检验的本质是推测两组数据之间的差异,其检验的原假设是”两组数据是相互独立的”。卡方检验返回卡方值和P值两个统计量,其中卡方值很难界定有效的范围,而p值,我们一般使用0.01或0.05作为显著性水平,即p值判断的边界。卡方检验的具体介绍见:
卡方检验_百度百科 (baidu.com)
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest #常用的特征选择的函数
from sklearn.feature_selection import chi2
iris = load_iris()
X, y = iris.data, iris.target #iris数据集,y取值有0,1,2,为标签化数据
#选择K=2个最好的特征,返回选择特征后的数据
X_new = SelectKBest(chi2, k=2).fit_transform(X, y) #chi2为指定检验为卡方检验
先看X的前5行数据
X[0:5,:]
y的前5行数据
y[:5]
最后提取出的新特征的前5行,可以看到结果为提取X的最后两列特征。
互信息法是用来捕捉每个特征与标注之间的任意关系(包括线性和非线性关系)的过滤方法,适用于特征和标注都是离散二值的研究。互信息从字面上理解为相互包含的信息,互信息法不返回p值或F值类似的统计量,它返回“每个特征与标注之间的互信息量的估计”,这个估计量在[0,1]之间取值,为0则表示两个变量独立,为1则表示两个变量完全相关。互信息的计算公式为:
其中:H()表示求熵,例如:
关于熵的定义在此不再赘述,不理解的话可去搜一下资料。
想把互信息直接用于特征选择其实不是太方便:
最大信息系数(MIC)克服了这两个问题。它首先寻找一种最优的离散化方式,然后把互信息取值转换成一种度量方式,取值区间在 [0,1] 。minepy提供了MIC功能。
简单来说,最大信息系数就是将两个变量在二维空间中进行离散化处理,然后将二维空间按照行列数规定分成一定数量的区间块,并计算这些区间块的互信息,这就是一种分割下的互信息值。接着计算按照其他分割方法下的区块的互信息值,最后在这些互信息中选出最大的一个互信息进行归一化处理后(除以log(最小互信息))得到的就是最大信息系数。python中有包可以直接计算MIC,这是另外一位博主的代码,仅供参考:
from minepy import MINE
m = MINE()
x = np.random.uniform(-1, 1, 10000) #创建10000个取值在-1到1之间的随机数
m.compute_score(x, x**2) #取x的平方
print(m.mic())
from sklearn.feature_selection import SelectKBest
#由于MINE的设计不是函数式的,定义mic方法将其为函数式的,返回一个二元组,二元组的第2项设置成固定的P值0.5
def mic(x, y):
m = MINE()
m.compute_score(x, y)
return (m.mic(), 0.5)
# 选择K个最好的特征,返回特征选择后的数据
SelectKBest(lambda X, Y: array(map(lambda x:mic(x, Y), X.T)).T,k=2).fit_transform(iris.data, iris.target)
过滤特征选择法还有一种方法不需要度量特征 x(i) 和标注Y的信息量。这种方法先要计算各个特征的方差,然后根据阈值,选择方差大于阈值的特征。这是因为方差越大,该特征所包含的信息量越多,对于标注有着更强的解释含义。
VarianceThreshold是特征选择的简单基线方法。可以删除方差超过某个阈值的所有特征。默认情况下,它会删除所有零差异特征,即所有样本中具有相同值的特征。代码如下:
from sklearn.feature_selection import VarianceThreshold
X = [[0, 0, 1], [0, 1, 0], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 1, 1]]
# 假设X是特征全集中的三个离散二值特征子集
sel = VarianceThreshold(threshold=(.8 * (1 - .8)))
# 方差选择法,返回值为特征选择后的数据
# 参数threshold为方差的阈值,因为二项分布的方差为p(1-p),则上面代码表示当方差超过(.8 * (1 - .8)时删除特征
print(sel.fit_transform(X))
返回结果为第二、第三列。
基本思想为:最初时创建一个空的集合F,在n个特征子集选出一个特征子集加入到F中,这时通过交叉验证来判断错误率,接着继续往F集合中加入特征子集计算错误率,循环上述过程直至获得n个错误率或达到我们设定的阈值,最后挑选出最小错误率的特征子集。
与向前搜索往F集合里增加特征相反的是,向后搜索是刚开始时F集合就包含了若干个特征子集,得出错误率后,每次删除一个特征子集,直到达到阈值或集合为空,然后得到最小错误率的特征集合。
递归特征消除(RFE)就是通过递归地考虑越来越小的特征集来选择特征。首先,对初始特征集训练估计器,通过coef_属性或feature_importances_属性获得每个特征的重要性。然后,从当前的特征集中删除最不重要的特征。也就是说通过不断地建立模型,每一次删除掉差的特征,直至遍历所有的特征。这种方法的效果很依赖于我们所选择的模型,以上面iris数据集为例:
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数
RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(iris.data, iris.target)
基本思想:建立简单的回归模型,常用的模型是基于树的预测模型(SelectFromModel),能够计算特征的重要程度。基于树的方法比较易于使用,因为他们对非线性关系的建模比较好,并且不需要太多的调试,但要注意过拟合问题。参考:打牛地的博客-CSDN博客_封装式特征选择
比如LR加入正则。通过L1正则项来选择特征:L1正则方法具有稀疏解的特性,因此天然具备特征选择的特性,但是要注意,L1没有选到的特征不代表不重要,原因是两个具有高相关性的特征可能只保留了一个,如果要确定哪个特征重要应再通过L2正则方法交叉检验。
from sklearn.svm import LinearSVC
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectFromModel
iris = load_iris()
X, y = iris.data, iris.target
X.shape #查看X的规格,显示为150行4列
(150, 4)
lsvc = LinearSVC(C=0.01, penalty="l1", dual=False).fit(X, y)
#penalty:{‘l1’, ‘l2’}, default=’l2’,正则化方法,默认为L1
#loss:{‘hinge’, ‘squared_hinge’}, default=’squared_hinge’,即hinge的平方
#dual:bool, default=True,Prefer dual=False when n_samples > n_features. 令样本数大于特征数
#C:float, default=1.0,正则化的强度与 C 成反比.
#max_iter:int, default=1000,要运行的最大迭代次数。
model = SelectFromModel(lsvc, prefit=True)
X_new = model.transform(X)
X_new.shape
(150,3) #返回三个特征
训练能够对特征打分的预选模型:RandomForest和Logistic Regression等都能对模型的特征打分,通过打分获得相关性后再训练最终模型。代码以博主的文章为例:
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectFromModel
iris = load_iris()
X, y = iris.data, iris.target
X.shape
(150, 4)
clf = ExtraTreesClassifier()
clf = clf.fit(X, y)
clf.feature_importances_ # 返回每个特征的重要性
array([ 0.04..., 0.05..., 0.4..., 0.4...])
model = SelectFromModel(clf, prefit=True)
X_new = model.transform(X)
X_new.shape
(150, 2)
接下来用三个思想来分析一个简单的数据集
import numpy as np
import pandas as pd
import scipy.stats as ss
df=pd.DataFrame({'A':ss.norm.rvs(size=10),'B':ss.norm.rvs(size=10),'C':ss.norm.rvs(size=10),'D':np.random.randint(low=0,high=2,size=10)})
#创建一个df数据集,其中A、B、C都是符合正态分布大小为10的随机数,D为取值为0到1、大小为10的整数。
from sklearn.svm import SVR #SVR为常用的回归模型包、回归器
from sklearn.tree import DecisionTreeRegressor #DecisionTreeRegressor为决策树回归器
X=df.loc[:,['A','B','C']] #将A、B、C设置为特征
Y=df.loc[:,['D']] #将D设置为标注
from sklearn.feature_selection import SelectKBest,RFE,SelectFromModel
#上面SelectKBest为过滤思想用到的特征选择包,RFE为包裹思想中的递归特征消除法,SelectFromModel为嵌入思想的模型选择包
#过滤思想
skb=SelectKBest(k=2) #从特征中选出两个特征
skb.fit(X,Y) #拟合模型,输出指定函数
skb.transform(X) #转化选择的两个特征
#包裹思想
rfe=RFE(estimator=SVR(kernel='linear'),n_features_to_select=2,step=1)
#estimator为指定回归器,linear为线性回归,n_features_to_select=2表示选择两个特征,step=1表示每次删除一个特征
rfe.fit_transform(X,Y) #转化出选择的两个特征和标注
#嵌入思想
sfm=SelectFromModel(estimator=DecisionTreeRegressor(),threshold=0.1)
#threshold=0.1 表示重要性低于0.1时就删除特征
sfm.fit_transform(X,Y)
以上就是特征选择的基本方法和简单的代码操作,想要学习复杂度较高的代码的小伙伴可以去Sklearn的官网看看。其他内容待下回分解......
部分资料和代码来源于以下参考文献:
特征预处理 - 知乎 (zhihu.com)
机器学习 特征选择(Feature Selection)方法汇总 - 知乎 (zhihu.com)
数据分析3——预处理理论(特征工程、数据清洗、特征预处理)_啧啧啧@的博客-CSDN博客
机器学习 特征选择(过滤法 封装法 嵌入法)_打牛地的博客-CSDN博客_封装式特征选择
华为大佬用159小时讲完的Python数据分析-数据挖掘教程,整整600集,零基础快速入门 手把手教学,学完即可就业_哔哩哔哩_bilibili