特征选择–主要分为三个模块:
创造新特征:在数据整理的时候,
import pandas as pd
data=pd.read_csv("digit recognizor.csv")# 手写体数字特征提取
X=data.iloc[:,1:] # 前面的标签不要
y=data.iloc[:,0]
y.unique() # 想要知道每个数据上有多少数据
# 体现0-9共十个数字每个数字有多少个样本
a=data.groupby("label").count()
方差过滤:消除方差为0的数据。
为什么使用方差过滤?
在机器学习的数据预处理的过程中常常会是使用到过滤法,而方差过滤是过滤法之一。所谓的方差过滤就是过滤掉那些特征方差较小的特征。
比如一个特征本身的方差很小,就表示样本在这个特征上基本没有差异,可能特征中的大多数值都一样,甚至整个特征的取值都相同,那这个特征对于样本区分没有什么作用。
所以可以设置一个过滤的阈值,过滤掉那些方差小的特征,从而达到特征筛选的目的。
在 sklearn中可以调用from sklearn.feature_selection import VarianceThreshold
类实现方差过滤。
以手写识别为例子:
from sklearn.feature_selection import VarianceThreshold
selector=VarianceThreshold() # 实例化,默认方差为0
X_var0=selector.fit_transform(X) # 获取具有特定价值的新特征矩阵
X_var0.shape # 使用方差进行过滤时,过滤掉了80+的数据
import numpy as np
np.median(X.var().values)
# 以特征方差中位数为阈值,考察特征选择结果
X_fsvar=VarianceThreshold(np.median(X.var().values)).fit_transform(X)
# 若特征是伯努利的随机变量,假设p=0.8 :表示如果某一种分类站到80%以上时,要删除特征
X_bvar=VarianceThreshold(.8*(1-0.8)).fit_transform(X)
X_bvar.shape # (42000, 685) 大概消除了100个特征
from sklearn.feature_selection import VarianceThreshold
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.neighbors import KNeighborsClassifier as KNN
from sklearn.model_selection import cross_val_score # 交叉验证
利用KNN和随机森林对全部特征,和以方差中位数为阈值的特征进行比较:
过滤:特征选择。
比较KNN在方差过滤前后的准确度:
# KNN在方差过滤之前
cross_val_score(KNN(),X,y,cv=5).mean() # 0.9658%%timeit # 计算运行时间
cross_val_score(KNN(),X,y,cv=5).mean()
# KNN在方差过滤之后
cross_val_score(KNN(),X_fsvar,y,cv=5).mean() # 0.966%%timeit # 计算运行时间
cross_val_score(KNN(),X_fsvar,y,cv=5).mean()
# 随机森林考察特征选择效果
%%timeit
cross_val_score(RFC(n_estimators=10,random_state=0),X,y,cv=5).mean()%%timeit
cross_val_score(RFC(n_estimators=10,random_state=0),X_fsvar,y,cv=5).mean()
使用方差过滤后的特征对KNN和RFC的影响:
tempdf=pd.DataFrame(X,y)
tempdf.reset_index(inplace=True)
tempdf
自定义函数:使用自定义函数提炼数据中两个特征之间的相关系数
#参考 自定义函数
import mathdef PearsonFirst(X,Y):'''公式一'''XY = X*YEX = X.mean()EY = Y.mean()EX2 = (X**2).mean()EY2 = (Y**2).mean()EXY = XY.mean()numerator = EXY - EX*EY # 分子denominator = math.sqrt(EX2-EX**2)*math.sqrt(EY2-EY**2) # 分母if denominator == 0:return 'NaN'rhoXY = numerator/denominatorreturn rhoXYdef PearsonSecond(X,Y):'''公式二'''XY = X*YX2 = X**2Y2 = Y**2n = len(XY)numerator = n*XY.sum() - X.sum()*Y.sum() # 分子denominator = math.sqrt(n*X2.sum() - X.sum()**2)*math.sqrt(n*Y2.sum() - Y.sum()**2) # 分母if denominator == 0:return 'NaN'rhoXY = numerator/denominatorreturn rhoXY
r1=PearsonFirst(tempdf['label'],tempdfempdf['pixel400']) # 使用公式一计算X与Y的相关系数
r2=PearsonFirst(tempdf['label'],tempdfempdf['pixel400']) # 使用公式二计算X与Y的相关系数
tempdf.corr(method="pearson") # 耗时太长,了解即可
选出与标签相关且有意义的特征。
sklearn.feature_selection.chi2
计算每个非负特征(标准化,归一化过程)和标签之间的卡方统计量。根据统计量进行排名,并结合sklearn.feature_selection.chi2
输入评选标准来选择K个特征。from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.model_selection import cross_val_score #交叉验证
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2 #卡方
# 假设需要300个特征
X_fschi = SelectKBest(chi2, k=300).fit_transform(X_fsvar, y)
# 从使用中位数方差过滤后的特征中选择
X_fschi.shape
# 用随机森林进行比较特征选择
cross_val_score(RFC(n_estimators=20,random_state=0),X_fschi,y,cv=10).mean()
计算时间:
k到底选择多大最合适?利用算法+学习曲线进行判断
# 选取超参数
%matplotlib inline
import matplotlib.pylab as plt
score=[]
for i in range(390,200,-10):X_fschi=SelectKBest(chi2,k=i).fit_transform(X_fsvar,y)once=cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()score.append(once)
plt.plot(range(390,200,-10),score)
plt.show()
如下图,最高大约在94%,因此K值大约选在380位置处。
对应score如下:
从特征工程的角度,选取卡方值较大,P值小于0.05的特征 ,即与标签有较大相关性。返回k值进行检验
chivalue,pyvalues_chi=chi2(X_fsvar,y) # 输入特征矩阵与标签
k=chivalue.shape[0]-(pyvalues_chi>0.05).sum() # 去除与标签无关的相关值
X_fschi=SelectKBest(chi2,k=k).fit_transform(X_fsvar,y)
cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean
# n_estimators=10 选取10颗树,使用RFC来进行交叉验证
k=chivalue.shape[0]-(pvalues_chi>0.05).sum() # 去除与标签无关的相关值
# 大于0.05表示与标签无关
求出K值,就可以用在下面的代码中:
X_fschi=SelectKBest(chi2,k=k).fit_transform(X_fsvar,y)
cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
如果使用全特征呢?
# 如果使用全特征呢?
chivalue,pvalues_chi=chi2(X,y) # 输入特征矩阵和标签
k=chivalue.shape[0]-(pvalues_chi>0.05).sum() # 去除与标签无关的相关值
k
可以看到全特征计算出的K值更多:↑
但是准确度小于中位数特征值处理后的准确度:↑
总结卡方检验的作用:
推测两组数据之间的差异,原假设是:两组数据之间是相互独立的。返回卡方值和P值这两个统计量。
P值 | <0.05或者0.01 | >0.05或者0.01 |
---|---|---|
数据差异 | 差异不是自然形成的 | 差异是自然形成的 |
相关性 | 两组数据是相关的 | 两组数据是相互独立的 |
原假设 | 拒绝原假设 | 接受原假设 |
从特征工程的角度&#xff0c;选取卡方值较大&#xff0c;P值小于0.05的特征&#xff0c;即与标签有较大相关性。返回K值进行检验。
调用SelectKBest之前&#xff0c;直接从chi2实例化后的模型中获得各个特征所对应的卡方值和P值。
feature_selection.f_classif
&#xff09;和&#xff08;连续型变量feature_selection.f_regression
&#xff09;两类。SelectKBest
联合使用&#xff0c;即也需要有超参数from sklearn.feature_selection import f_classif
F,pvalues_f&#61;f_classif(X_fsvar,y)
F
P值当中存在大量的0&#xff1a;↓
接着求出K值后&#xff0c;代入随机森林当中做计算&#xff1a;
X_fsF&#61;SelectKBest(f_classif,k&#61;k).fit_transform(X_fsvar,y)
cross_val_score(RFC(n_estimators&#61;10,random_state&#61;0),X_fschi,y,cv&#61;5).mean()
feature_selection.mutual_info_classif
&#xff08;互信息分类&#xff09;和feature_selection.mutual_info_regression
&#xff08;互信息回归&#xff09;#X_fsvar 利用中位数取的特征&#xff0c;使用的是互信息法的方式
from sklearn.feature_selection import mutual_info_classif as MIC
result &#61; MIC(X_fsvar,y)
result
k &#61; result.shape[0] - sum(result <&#61; 0)
k
根据算出的K值&#xff0c;计算交叉验证得到的准确度&#xff1a;
X_fsmic &#61; SelectKBest(MIC, k&#61;k).fit_transform(X_fsvar, y)
cross_val_score(RFC(n_estimators&#61;10,random_state&#61;0),X_fsmic,y,cv&#61;5).mean()
feature_selection.SelectFromModel
元变换器。可以与任何在拟合之后有coef_&#xff08;回归系数&#xff09;&#xff0c;feature_importances_属性或参数中可选惩罚项的评估器一起使用&#xff0c;如随机森林和树模型from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
RFC_ &#61; RFC(n_estimators &#61;10,random_state&#61;0)
X_embedded &#61; SelectFromModel(RFC_,threshold&#61;0.005).fit_transform(X,y)
X_embedded.shape
# 模型的维度有没有降低&#xff1f;
#学习曲线来找最佳阈值import numpy as np
import matplotlib.pyplot as pltRFC_.fit(X,y).feature_importances_
threshold &#61; np.linspace(0,(RFC_.fit(X,y).feature_importances_).max(),20)
score &#61; []
for i in threshold:X_embedded &#61; SelectFromModel(RFC_,threshold&#61;i).fit_transform(X,y)once &#61; cross_val_score(RFC_,X_embedded,y,cv&#61;5).mean()score.append(once)
plt.plot(threshold,score)
plt.show()
将最高部分的学习曲线进行放大&#xff1a;
score2 &#61; []
for i in np.linspace(0,0.004,20):X_embedded &#61; SelectFromModel(RFC_,threshold&#61;i).fit_transform(X,y)once &#61; cross_val_score(RFC_,X_embedded,y,cv&#61;5).mean()score2.append(once)
plt.figure(figsize&#61;[20,5])
plt.plot(np.linspace(0,0.004,20),score2)
plt.xticks(np.linspace(0,0.004,20))
plt.show()
coef_
或者feature_importances_
属性获取每个特征的重要性# 使用包装法
from sklearn.feature_selection import RFE
RFC_ &#61; RFC(n_estimators &#61;10,random_state&#61;0)
selector &#61; RFE(RFC_,n_features_to_select&#61;340,step&#61;50).fit(X,y)
selector.support_.sum()
selector.ranking_
X_wrapper&#61;selector.transform(X)
cross_val_score(RFC_,X_wrapper,y,cv&#61;5).mean()
# 使用sklearn的cross_val_score进行交叉验证
#调参 100
from sklearn.feature_selection import RFE
RFC_ &#61; RFC(n_estimators &#61;100,random_state&#61;0)
selector &#61; RFE(RFC_, n_features_to_select&#61;340, step&#61;50).fit(X, y) # 特征选择340个&#xff0c;每个步骤做50个参数的调整
selector.support_.sum()
selector.ranking_
X_wrapper &#61; selector.transform(X) # 通过包装法得到最优结果
cross_val_score(RFC_,X_wrapper,y,cv&#61;5).mean() # 使用交叉验证计算准确性