机器学习之特征工程

本文涉及的是特征选择。其实特征选择只是特征工程中的第一步。更深入的是使用特征创造或特征提取来寻找高级特征。

除了对业务的理解,有四种方法可以用来选择特征:
过滤法,嵌入法,包装法,和降维算法。

下面是通过代码对除降维之外的算法进行实操。

import pandas as pd
data = pd.read_csv("digit recognizor.csv")
data.head()

 

 可以看到有785-1个特征,对它们做特征选择。

x = data.iloc[:,1:]
y = data.iloc[:,0]

1.过滤法之方差过滤

from sklearn.feature_selection import VarianceThreshold
selector = VarianceThreshold() # 实例化,不填参数默认方差0
x_var0 = selector.fit_transform(x) # 获取删除不合格特征后的新特征矩阵
x_var0.shape

 

 默认方差阈值参数的情况下,过滤后还剩708个特征。

# 如果知道需要多少特征,找到特征方差的分位数作为阈值输入即可
import numpy as np
x_fsvar = VarianceThreshold(np.median(x.var().values)).fit_transform(x)
x_fsvar.shape

 

 这是按方差取值的中位数为阈值,保留一半特征的情况。

# 基于方差过滤的结果来进一步做卡方过滤
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
x_fschi = SelectKBest(chi2,k=300).fit_transform(x_fsvar,y)
x_fschi.shape

 

 2.过滤法之卡方过滤,

卡方检验的本质是推测两组数据之间的差异,其检验的原假设是”两组数据是相互独立的”。卡方检验返回卡方值和P值两个统计量,其中卡方值很难界定有效的范围,而p值,我们一般使用0.01或0.05作为显著性水平,<=0.05或0.01表示两组数据相关,拒绝原假设。

使用sklearn中的chi2和SelectKBest选取指定个数的特征(k=300)。对比一下方差过滤和进一步卡方过滤的效果:

# 验证一下效果
print(cross_val_score(RFC(n_estimators=10,random_state=0),x_fsvar,y,cv=5).mean())
print(cross_val_score(RFC(n_estimators=10,random_state=0),x_fschi,y,cv=5).mean())
# 相关过滤后模型效果降低了,要么调大K值,要么放弃相关性过滤

对于卡方过滤中的超参数K值,用学习曲线查看K对模型的影响

复制代码
# 选取超参数k,学习曲线
%matplotlib inline
import matplotlib.pyplot as plt
score = []
for i in range(390,220,-10):
    x_fschi = SelectKBest(chi2,k=i).fit_transform(x_fsvar,y)
    s = cross_val_score(RFC(n_estimators=10,random_state=0),x_fschi,y,cv=5).mean()
    score.append(s)
plt.figure()
plt.plot(range(390,220,-10), score)
plt.show()
# 随着保留特征数越多,模型效果越好。
复制代码

 

 实际运用中,学习曲线的方法耗时较长,也可以根据chi2获得各个特征对应的卡方值和p值,p值小于0.05时就能够拒绝特征与标签不相关的原假设,有95%的把握能够确定对应特征与标签相关。

# 获得各个特征对应的卡方值和p值
chivalue, pvalue_chi = chi2(x_fsvar,y)
print(chivalue, pvalue_chi)
# 可以看到p值都是0
# 现有数据集的特征都与标签相关,舍弃任何一个都可能舍弃对模型有用的信息。

 

 

#k取多少?我们想要消除所有p值大于设定值,比如0.05或0.01的特征:
k = chivalue.shape[0] - (pvalue_chi > 0.05).sum()
k

 

 k=392说明方差过滤后的现有特征都与标签相关,保留。

3. 过滤法之F检验,用法类似于卡方过滤。方差齐性检验,是用来捕捉每个特征与标签之间的线性关系的过滤方法。它即可以做回归也可以做分类;F检验在数据服从正态分布时效果会非常稳定。F检验的本质是寻找两组数据之间的线性关系,其原假设是”数据不存在显著的线性关系“。它返回F值和p值两个统计量。

# F检验
from sklearn.feature_selection import f_classif
F, pvalue_f = f_classif(x_fsvar,y)
print(F)
print(pvalue_f)

 

 

k = F.shape[0]-(pvalue_f>0.05).sum()
k
# 没有任何特征的p值大于0.01,所有特征都和标签相关,不需要F检验过滤。

4.过滤法之互信息法,用来捕捉每个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法,既可以做回归也可以做分类,不过互信息法比F检验更加强大,F检验只能够找出线性关系,而互信息法可以找出任意关系。返回“每个特征与目标之间的互信息量的估计”,这个估计量在[0,1]之间取值,为0则表示两个变量独立,为1则表示两个变量完全相关。

# 互信息法
from sklearn.feature_selection import mutual_info_classif as  MIC
result = MIC(x_fsvar,y)
# result.shape
k = result.shape[0]-sum(result<=0)
k
# 所有特征的互信息量估计都大于0,因此所有特征都与标签相关。

result>0表示特征与标签相关。

5.嵌入法,一种让算法自己决定使用哪些特征的方法,即特征选择和算法训练同时进行。依赖于算法自身的选择,比如coef_属性或feature_importances_属性来完成特征选择。

# 随机森林的嵌入法
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
RFC_ = RFC(n_estimators=10,random_state=0)
x_embedded = SelectFromModel(RFC_,threshold=0.005).fit_transform(x,y)
x_embedded.shape
# 指定阈值最终只剩下47个特征

 

 学习曲线来确定属性选择中feature_importances_的阈值

复制代码
# 画学习曲线
import numpy as np
import matplotlib.pyplot as plt
threshold = np.linspace(0,(RFC_.fit(x,y).feature_importances_).max(),20)
score = []
for i in threshold:
    x_embedded = SelectFromModel(RFC_,threshold=i).fit_transform(x,y)
    s = cross_val_score(RFC_,x_embedded,y,cv=5).mean()
    score.append(s)
plt.plot(threshold,score)
plt.show()
复制代码

 

 根据图像可以再对阈值进行细分找到更优化的值。此处略过,只看结果。

x_embedded = SelectFromModel(RFC_,threshold=0.00055).fit_transform(x,y)
x_embedded.shape
print(cross_val_score(RFC_,x_embedded,y,cv=5).mean())

 

 可以看到嵌入法直接把准确率提升到94%以上,相比方差过滤的

0.9388098166696807模型效果更好了。
6.包装法Wrapper

 

也是一个特征选择和算法训练同时进行的方法,与嵌入法十分相似,它也是依赖于算法自身的选择,比如coef_属性或feature_importances_属性来完成特征选择。包装法在初始特征集上训练评估器,并且通过coef_属性或通过feature_importances_属性获得每个特征的重要性。然后,从当前的一组特征中修剪最不重要的特征。在修剪的集合上递归地重复该过程,直到最终到达所需数量的要选择的特征。包装法要使用特征子集进行多次训练,因此它所需要的计算成本是最高的。但相比之下,包装法是最能保证模型效果的特征选择方法。

from sklearn.feature_selection import RFE
RFC_ = RFC(n_estimators=10,random_state=0)
selector = RFE(RFC_,n_features_to_select=340,step=50).fit(x,y)
selector.support_.sum()
# 直接指定340个特征

support_:返回所有的特征的是否最后被选 中的布尔矩阵,以及.ranking_返回特征的按数次迭代中综合重要性的排名。

selector.ranking_

 

 对包装法的选择特征个数画学习曲线

复制代码
# 对包装法画学习曲线
score = []
for i in range(1,751,50):
    x_wrapper = RFE(RFC_,n_features_to_select=i, step=50).fit_transform(x,y)
    once = cross_val_score(RFC_,x_wrapper,y,cv=5).mean()
    score.append(once)
plt.figure(figsize=[20,5])
plt.plot(range(1,751,50),score)
plt.xticks(range(1,751,50))
plt.show()
# 应用50个特征时,模型的表现就已经达到了90%以上,比嵌入法和过滤法都高效很
# 多。我们可以放大图像,寻找模型变得非常稳定的点来画进一步的学习曲线
复制代码

 

posted @   Yalking  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示