当前位置: 首页 > 科技观察

机器学习11种特征选择策略总结!

时间:2023-03-17 14:40:55 科技观察

太多的特征会增加模型的复杂度和过拟合,而太少的特征会导致模型欠拟合。将模型优化到足够复杂以概括其性能,但又足够简单以供训练、维护和解释是特征选择的主要工作。“特征选择”是指一些特征可以保留,一些特征可以舍弃。本文的目的是概述一些特征选择策略:删除未使用的列删除具有缺失值的列不相关的特征低方差特征多重共线性特征系数p值方差膨胀因子(VIF)使用sci-kit基于特征重要性的特征选择learnforautomaticfeatureselection主成分分析(PCA)此演示的数据集是根据PyCaret的MIT许可发布的-PyCaret是一个开源低代码机器学习库。数据集相当干净,但我做了一些预处理。请注意,我使用此数据集是为了演示不同的特征选择策略是如何工作的,而不是为了构建最终模型,因此模型性能无关紧要。首先加载数据集:importpandasaspddata='https://raw.githubusercontent.com/pycaret/pycaret/master/datasets/automobile.csv'df=pd.read_csv(data)df.sample(5)数据集包含202行26列——每行代表一个汽车实例,每列代表它的特征和对应的价格。这些列是:df.columns>>Index(['symboling','normalized-losses','make','fuel-type','aspiration','num-of-doors','body-style','驱动轮','发动机位置','轴距','长度','宽度','高度','整备质量','发动机类型','气缸数',“发动机尺寸”、“燃油系统”、“缸径”、“冲程”、“压缩比”、“马力”、“峰值转速”、“城市mpg”、“高速公路mpg”、“价格”'],dtype='object')现在让我们深入了解特征选择的11种策略。1.删除未使用的列当然,最简单的策略是您的直觉。虽然直观,但有时在最终模型中不以任何形式使用某些列(例如列“ID”、“FirstName”、“LastName”等)是很有用的。如果您知道不会使用某个特定的列,请随时将其删除。在我们的数据中,所有列都没有这个问题,所以我不会在这一步中删除任何列。2.去除有缺失值的列缺失值在机器学习中是不可接受的,因此我们使用不同的策略来清理缺失数据(例如imputation)。但是如果列中有很多数据丢失,完全删除它是非常好的。#每列的总空值ndf.isnull().sum()>>符号化0归一化损失35make0fuel-type0aspiration0num-of-doors2body-style0drive-wheels0engine-location0wheel-base0length0width0height0curb-weight0engine-type0num-of-cylinders0engine-size0fuel-system0bore0stroke0compression-ratio0horsepower0peak-rpm0city-mpg0highway-mpg0price0dtype:int643.不相关的特征无论算法是回归(预测数字)还是分类(预测类别),特征都必须与目标相关。如果一个特征没有表现出相关性,它就是消除的主要目标。可以分别测试数值和分类特征的相关性。数值变量#目标和特征之间的相关性(df.corr().loc['price'].plot(kind='barh',figsize=(4,10)))在这个例子中,peak-rpm,compression-Features像ratio,stroke,bore,height,symboling跟价格关系不大,所以可以去掉。可以手动删除列,但我更喜欢使用相关阈值(在本例中为0.2)以编程方式执行此操作:#dropuncorrelatednumericfeatures(threshold<0.2)corr=abs(df.corr().loc['price'])corr=corr[corr<0.2]cols_to_drop=corr.index.to_list()df=df.drop(cols_to_drop,axis=1)分类变量您可以使用箱线图查找目标和分类特征之间的相关性:将seaborn导入为snssns。boxplot(y='price',x='fuel-type',data=df)柴油车的中位价格高于汽油车??。这意味着这个分类变量可以解释汽车价格,所以应该去掉。每个分类列都可以像这样单独检查。4.Lowvariancefeatures检查我们的特征差异:importnumpyasnp#varianceofnumericfeatures(df.select_dtypes(include=np.number).var().astype('str'))这里的“bore”非常低方差,尽管这是删除的候选者。在这个特定的例子中,我不愿意删除它,因为它的值在2.54到3.94之间,所以方差很低:df['bore'].describe()5.MulticollinearityoccurredwhenanytwofeaturesMulticollinearityoccurredwhenthere是它们之间的相关性。在机器学习中,期望每个特征应该独立于其他特征,即它们之间没有共线性。大马力车辆往往具有大发动机尺寸。因此,您可能希望消除其中一个,让另一个确定目标变量-价格。我们可以分别测试数值和分类特征的多重共线性:数值变量热图是检查和查找相关特征的最简单方法。将matplotlib.pyplot导入为pltsns.set(rc={'figure.figsize':(16,10)})sns.heatmap(df.corr(),annot=True,linewidths=.5,center=0,cbar=False,cmap="PiYG")plt.show()大多数特征都在某种程度上相关,但有些特征具有非常高的相关性,例如长度与轴距和发动机尺寸与马力。可以根据相关阈值手动或以编程方式删除这些功能。我将手动删除共线性阈值为0.80的特征。#dropcorrelatedfeaturesdf=df.drop(['length','width','curb-weight','engine-size','city-mpg'],axis=1)也可以使用称为方差膨胀的因子(VIF)来确定多重共线性并删除基于高VIF值的特征。我稍后会展示这个例子。分类变量与数值特征类似,也可以检查分类变量之间的共线性。卡方独立性检验等统计检验非常适合它。让我们检查数据集中的两个分类列——燃料类型和车身风格——是独立的还是相关的。df_cat=df[['fuel-type','body-style']]df_cat.sample(5)然后我们将在每一列中创建类别的交叉表/列联表。crosstab=pd.crosstab(df_cat['fuel-type'],df_cat['body-style'])crosstab最后,我们将对交叉表运行卡方检验,这将告诉我们这两个特征是否独立。fromscipy.statsimportchi2_contingencychi2_contingency(crosstab)按顺序输出卡方值、p值、自由度和预期频率数组。p-value<0.05,因此我们可以拒绝特征之间没有关联的零假设,即两个特征之间存在统计上显着的关系由于这两个特征之间存在关联,我们可以选择删除其中一个.到目前为止,我已经展示了在实施模型之前应用的特征选择策略。这些策略在第一轮特征选择中很有用,可以构建初始模型。但是一旦建立了模型,就可以获得更多关于每个特征在模型性能中的适应度的信息。基于这些新信息,可以进一步确定要保留哪些特征。下面我们使用最简单的线性模型演示其中一些方法。#删除有缺失值的列sdf=df.dropna()fromsklearn.model_selectionimporttrain_test_split#获取分类特征的假人sdf=pd.get_dummies(df,drop_first=True)#XfeaturesX=df.drop('price',axis=1)#ytargety=df['price']#splitdataintotrainingandtestingsetX_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3,random_state=42)fromsklearn.linear_modelimportLinearRegression#scalingfromsklearn预处理importStandardScalerscaler=StandardScaler()X_train=scaler.fit_transform(X_train)X_test=scaler.fit_transform(X_test)#转换回dataframeX_train=pd.DataFrame(X_train,columns=X.columns.to_list())X_test=pd.DataFrame(X_test,columns=X.columns.to_list())#instantiatemodelmodel=LinearRegression()#fitmodel.fit(X_train,y_train)现在我们已经拟合了模型,让我们进行另一轮特征选择。6.特征系数如果你正在运行回归任务,特征适应度的一个关键指标是回归系数(所谓的beta系数),它显示了特征在模型中的相对贡献。有了这些信息,可以删除贡献很少或没有贡献的特征。#特征系数coeffs=model.coef_#可视化系数index=X_train.columns.tolist()(pd.DataFrame(coeffs,index=index,columns=['coeff']).sort_values(by='coeff').plot(kind='barh',figsize=(4,10)))有些特征的beta系数很小,对车价的预测贡献不大。这些特征可以被过滤掉:#filtervariablesnearzerocoefficientvaluetemp=pd.DataFrame(coeffs,index=index,columns=['coeff']).sort_values(by='coeff')temp=temp[(temp['系数']>1)|(temp['coeff']<-1)]#dropthosefeaturescols_coeff=temp.index.to_list()X_train=X_train[cols_coeff]X_test=X_test[cols_coeff]7.回归中的p值,p值告诉我们预测变量和目标之间的关系是否具有统计显着性。statsmodels库提供具有特征系数和相关p值的回归输出函数。如果有些特征不显着,可以一个一个去掉,每次都重新运行模型,直到找到一组p值显着的特征,用更高的adjustedR2提高性能。importstatsmodels.apiassmols=sm.OLS(y,X).fit()print(ols.summary())8.方差膨胀因子(VIF)方差膨胀因子(VIF)是另一种衡量多重共线性的方法。它被测量为整体模型方差与每个单独特征的方差的比率。特征的高VIF表明它与一个或多个其他特征相关。根据经验:VIF=1表示无相关性VIF=1-5适度相关VIF>5高度相关VIF是消除多重共线性特征的有用技术。对于我们的演示,删除所有高于10的VIF。fromstatsmodels.stats.outliers_influenceimportvariance_inflation_factor#calculateVIFvif=pd.Series([variance_inflation_factor(X.values,i)foriinrange(X.shape[1])],index=X.columns)#在表中显示VIFindex=X_train.columns.tolist()vif_df=pd.DataFrame(vif,index=index,columns=['vif']).sort_values(by='vif',ascending=False)vif_df[vif_df['vif']<10]9.根据特征重要性选择决策树/随机森林,以使用最小化杂质(以基尼系数杂质或信息增益衡量)的特征来分割数据。找到最佳特征是算法在分类任务中工作方式的关键部分。我们可以通过feature_importances_属性访问最佳特征。让我们在我们的数据集上实现一个随机森林模型并过滤一些特征。fromsklearn.ensembleimportRandomForestClassifier#instantiatemodelmodel=RandomForestClassifier(n_estimators=200,random_state=0)#fitmodelmodel.fit(X,y)现在让我们看看特征重要性:#featureimportanceimportances=model.feature_importances_#visualizationcols=X.columns(pd.DataFrame(importances,cols,columns=['importance']).sort_values(by='importance',ascending=True).plot(kind='barh',figsize=(4,10)))输出上面显示了每个特征在减少每个节点/分裂中的重要性。由于随机森林分类器有很多估计量(例如上例中的200棵决策树),因此可以使用置信区间来计算相对重要性的估计值。#计算特征重要性的标准差std=np.std([i.feature_importances_foriinmodel.estimators_],axis=0)#visualizationfeat_with_importance=pd.Series(importances,X.columns)fig,ax=plt.subplots(figsize=(12,5))feat_with_importance.plot.bar(yerr=std,ax=ax)ax.set_title("Featureimportances")ax.set_ylabel("Meandecreaseinimpurity")现在我们知道每个特征的重要性了此外,您可以手动(或以编程方式)确定要保留和删除哪些功能。10.使用ScikitLearn进行自动特征选择sklearn库中有一个完整的模块,只需几行代码即可处理特征选择。sklearn中有许多自动化过程,但这里我只展示一些:scores选择特定数量的用户定义特征(k)。这些分数是通过计算X(自变量)和y(因变量)变量之间的卡方统计量来确定的。在sklearn中,您需要做的就是决定保留多少特征。如果你想保留10个特征,实现将如下所示:#选择K个最佳特征X_best=SelectKBest(chi2,k=10).fit_transform(X,y)#最佳特征数量X_best.shape[1]>>10if有大量的特征,你可以指定保留的特征百分比。假设我们想要保留75%的特征并丢弃剩余的25%:#keep75%topfeaturesX_top=SelectPercentile(chi2,percentile=75).fit_transform(X,y)#numberofbestfeaturesX_top.shape[1]>>36regularization正则化减少了过度拟合。如果特征太多,正则化会通过缩小特征系数(称为L2正则化)或将某些特征系数设置为零(称为L1正则化)来控制它们的效果。一些模型内置了L1/L2正则化作为超参数来惩罚特征。可以使用转换器SelectFromModel消除这些功能。让我们用penalty='l1'来实现一个LinearSVC算法。然后使用SelectFromModel删除一些功能。#implementalgorithmfromsklearn.svmimportLinearSVCmodel=LinearSVC(penalty='l1',C=0.002,dual=False)model.fit(X,y)#使用元转换器选择特征选择器=SelectFromModel(estimator=model,prefit=True)X_new=selector.transform(X)X_new.shape[1]>>2#所选特征的名称feature_names=np.array(X.columns)feature_names[selector.get_support()]>>array(['wheel-base','horsepower'],dtype=object)序贯法序贯法是一种经典的统计技术。在这种情况下,一次添加/删除一个功能并检查模型性能,直到它针对要求进行了优化。顺序方法有两种变体。前向选择技术从0个特征开始,然后添加使错误最小化的特征;然后添加另一个功能,依此类推。向后选择在相反的方向上起作用。该模型从包含的所有特征开始并计算误差;然后它消除了可以进一步减少错误的功能。重复该过程,直到保留所需数量的特征。#实例化模型模型=RandomForestClassifier(n_estimators=100,random_state=0)#选择featuresselector=SequentialFeatureSelector(estimator=model,n_features_to_select=10,direction='backward',cv=2)选择器。fit_transform(X,y)#检查名称特征selectedfeature_names=np.array(X.columns)feature_names[selector.get_support()]>>array(['bore','make_mitsubishi','make_nissan','make_saab','aspiration_turbo','num-of-doors_two','bodystyle_hatchback','engine-type_ohc','num-of-cylinders_twelve','fuel-system_spdi'],dtype=object)11.主成分分析(PCA)PCA的主要目的是减少维度的高维特征空间。原始特征被重新投影到新的维度(即主成分)。最终目标是找到最能解释数据方差的特征数量。#importPCAmodulefromsklearn.decompositionimportPCA#scalingdataX_scaled=scaler.fit_transform(X)#fitPCAtodatapca=PCA()pca.fit(X_scaled)evr=pca.explained_variance_ratio_#可视化每个主要图形componentsplt解释的方差(figsize=(12,5))plt.plot(range(0,len(evr)),evr.cumsum(),marker="o",linestyle="--")plt.xlabel("分量数")plt.ylabel("Cumulativeexplainedvariance")这20个主成分解释了80%以上的方差,所以模型可以对这20个成分(特征)进行拟合。可以预先确定方差阈值并选择所需数量的主成分。总结这是对可应用于特征选择的各种技术的有用指南。一些技术在拟合模型之前应用,例如去除缺失值的列、不相关的列、具有多重共线性的列以及使用PCA进行降维,而其他技术则在基本模型实现之后应用,例如特征系数、p值、VIF等等。虽然不是所有的策略都会在一个项目中得到充分的运用,但是这些策略是我们测试的方向。