1 特征选择的目标
机器学习中特征选择是一个重要步骤,以筛选出显著特色、摒弃非显著特色。这样做的作用是:
- 缩小特色(防止维度劫难),进步训练速度,升高运算开销;
- 缩小烦扰噪声,升高过拟合危险,晋升模型成果;
- 更少的特色,模型可解释性更好;
2 特征选择办法
特征选择办法个别分为三类:
2.1 过滤法--特征选择
通过计算特色的缺失率、发散性、相关性、信息量、稳定性等指标对各个特色进行评估抉择,罕用如缺失状况、单值率、方差验证、pearson相关系数、chi2卡方测验、IV值、信息增益及PSI等办法。
2.1.1 缺失率
通过剖析各特色缺失率,并设定阈值对特色进行筛选。阈值能够凭经验值(如缺失率<0.9)或可察看样本各特色整体散布,确定特色散布的异样值作为阈值。
# 特色缺失率miss_rate_df = df.isnull().sum().sort_values(ascending=False) / df.shape[0]
2.1.2 发散性
特色无发散性意味着该特征值根本一样,无辨别能力。通过剖析特色单个值得最大占比及方差以评估特色发散性状况,并设定阈值对特色进行筛选。阈值能够凭经验值(如单值率<0.9, 方差>0.001)或可察看样本各特色整体散布,以特色散布的异样值作为阈值。
# 剖析方差 var_features = df.var().sort_values() # 特色单值率sigle_rate = {}for var in df.columns: sigle_rate[var]=(df[var].value_counts().max()/df.shape[0])
2.1.2 相关性
特色间相关性高会节约计算资源,影响模型的解释性。特地对线性模型来说,会导致拟合模型参数的不稳固。罕用的剖析特色相关性办法如:
- 方差收缩因子VIF:
方差收缩因子也称为方差膨胀系数(Variance Inflation),用于计算数值特色间的共线性,个别当VIF大于10示意有较高共线性。
from statsmodels.stats.outliers_influence import variance_inflation_factor# 截距项df['c'] = 1name = df.columnsx = np.matrix(df)VIF_list = [variance_inflation_factor(x,i) for i in range(x.shape[1])]VIF = pd.DataFrame({'feature':name,"VIF":VIF_list})
- person相关系数:
用于计算数值特色两两间的相关性,数值范畴[-1,1]。
import seaborn as snscorr_df=df.corr()# 热力求sns.heatmap(corr_df)# 剔除相关性系数高于threshold的corr_dropthreshold = 0.9upper = corr_df.where(np.triu(np.ones(corr_df.shape), k=1).astype(np.bool))corr_drop = [column for column in upper.columns if any(upper[column].abs() > threshold)]
- Chi2测验
经典的卡方测验是测验类别型变量对类别型变量的相关性。
Sklearn的实现是通过矩阵相乘疾速得出所有特色的观测值和期望值,在计算出各特色的 2 值后排序进行抉择。在扩充了 chi2 的在连续型变量适用范围的同时,也不便了特征选择。
from sklearn.datasets import load_irisfrom sklearn.feature_selection import SelectKBestfrom sklearn.feature_selection import chi2x, y = load_iris(return_X_y=True)x_new = SelectKBest(chi2, k=2).fit_transform(x, y)
2.1.3 信息量
分类工作中,能够通过计算某个特色对于分类这样的事件到底有多大信息量奉献,而后特征选择信息量奉献大的特色。 罕用的办法有计算IV值、信息增益。
- 信息增益
如指标变量D的信息熵为 H(D),而D在特色A条件下的条件熵为 H(D|A),那么信息增益 G(D , A) 为:
信息增益(互信息)的大小即代表特色A的信息奉献水平。
from sklearn.feature_selection import mutual_info_classiffrom sklearn.datasets import load_irisx, y = load_iris(return_X_y=True)mutual_info_classif(x,y)
- IV
IV值(Information Value),在风控畛域是一个重要的信息量指标,掂量了某个特色(连续型变量须要先离散化)对指标变量的影响水平。其根本思维是依据该特色所命中黑白样本的比率与总黑白样本的比率,来比照和计算其关联水平。【Github代码链接】
2.1.4 稳定性
对大部分数据挖掘场景,特地是风控畛域,很关注特色散布的稳定性,其间接影响到模型应用周期的稳定性。罕用的是PSI(Population Stability Index,群体稳定性指标)。
- PSI
PSI示意的是理论与预期散布的差别,SUM( (理论占比 - 预期占比)* ln(理论占比 / 预期占比) )。
在建模时通常以训练样本(In the Sample, INS)作为预期散布,而验证样本作为理论散布。验证样本个别包含样本外(Out of Sample,OOS)和跨工夫样本(Out of Time,OOT)【Github代码链接】
2.2 嵌入法--特征选择
嵌入法是间接应用模型训练的到特色重要性,在模型训练同时进行特征选择。通过模型失去各个特色的权值系数,依据权值系数从大到小来抉择特色。罕用如基于L1正则项的逻辑回归、Lighgbm特色重要性抉择特色。
- 基于L1正则项的逻辑回归
L1正则办法具备稠密解的个性,直观从二维解空间来看L1-ball 为正方形,在顶点处时(如W2=C, W1=0的稠密解),更容易达到最优解。可见基于L1正则办法的会趋向于产生大量的特色,而其余的特色都为0。
from sklearn.feature_selection import SelectFromModelfrom sklearn.linear_model import LogisticRegressionx_new = SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(x, y)
- 基于树模型的特色排序
基于决策树的树模型(随机森林,Lightgbm,Xgboost等),树成长过程中也是启发式搜寻特色子集的过程,能够间接用训练后模型来输入特色重要性。
import matplotlib.pyplot as pltfrom lightgbm import plot_importancefrom lightgbm import LGBMClassifiermodel = LGBMClassifier()model.fit(x, y)plot_importance(model, max_num_features=20, figsize=(10,5),importance_type='split')plt.show()feature_importance = pd.DataFrame({ 'feature': model.booster_.feature_name(), 'gain': model.booster_.feature_importance('gain'), 'split': model.booster_.feature_importance('split') }).sort_values('gain',ascending=False)
当特色数量多时,对于输入的特色重要性,通常能够依照重要性的拐点划定下阈值抉择特色。
2.3 包装法--特征选择
包装法是通过每次抉择局部特色迭代训练模型,依据模型预测成果评分抉择特色的去留。个别包含产生过程,评估函数,进行准则,验证过程,这4个局部。
(1) 产生过程( Generation Procedure )是搜寻特色子集的过程,首先从特色全集中产生出一个特色子集。搜寻形式有齐全搜寻(如广度优先搜寻、定向搜寻)、启发式搜寻(如双向搜寻、后向抉择)、随机搜寻(如随机子集抉择、模拟退火、遗传算法)。
(2) 评估函数( Evaluation Function ) 是评估一个特色子集好坏水平的一个准则。
(3) 进行准则( Stopping Criterion )进行准则是与评估函数相干的,个别是一个阈值,当评估函数值达到这个阈值后就可进行搜寻。
(4) 验证过程( Validation Procedure )是在验证数据集上验证选出来的特色子集的实际效果。
首先从特色全集中产生出一个特色子集,而后用评估函数对该特色子集进行评估,评估的后果与进行准则进行比拟,若评估后果比进行准则好就进行,否则就持续产生下一组特色子集,持续进行特征选择。最初选出来的特色子集个别还要验证其实际效果。
- RFE
RFE递归特色打消是常见的特征选择办法。原理是递归地在残余的特色上构建模型,应用模型判断各特色的奉献并排序后做特征选择。
from sklearn.feature_selection import RFErfe = RFE(estimator,n_features_to_select,step)rfe = rfe.fit(x, y)print(rfe.support_)print(rfe.ranking_)
- 双向搜寻特征选择
鉴于RFE仅是后向迭代的办法,容易陷入部分最优,而且不反对Lightgbm等模型主动解决缺失值/类别型特色,便基于启发式双向搜寻及模拟退火算法思维,简略码了一个特征选择的办法【Github代码链接】,如下代码:
"""Author: 公众号-算法进阶基于启发式双向搜寻及模拟退火的特征选择办法。"""import pandas as pd import random from sklearn.metrics import precision_score, recall_score, f1_score, accuracy_score, roc_curve, aucdef model_metrics(model, x, y, pos_label=1): """ 评估函数 """ yhat = model.predict(x) yprob = model.predict_proba(x)[:,1] fpr, tpr, _ = roc_curve(y, yprob, pos_label=pos_label) result = {'accuracy_score':accuracy_score(y, yhat), 'f1_score_macro': f1_score(y, yhat, average = "macro"), 'precision':precision_score(y, yhat,average="macro"), 'recall':recall_score(y, yhat,average="macro"), 'auc':auc(fpr,tpr), 'ks': max(abs(tpr-fpr)) } return resultdef bidirectional_selection(model, x_train, y_train, x_test, y_test, annealing=True, anneal_rate=0.1, iters=10,best_metrics=0, metrics='auc',threshold_in=0.0001, threshold_out=0.0001,early_stop=True, verbose=True): """ model 抉择的模型 annealing 模拟退火算法 threshold_in 特色入模的>阈值 threshold_out 特色剔除的<阈值 """ included = [] best_metrics = best_metrics for i in range(iters): # forward step print("iters", i) changed = False excluded = list(set(x_train.columns) - set(included)) random.shuffle(excluded) for new_column in excluded: model.fit(x_train[included+[new_column]], y_train) latest_metrics = model_metrics(model, x_test[included+[new_column]], y_test)[metrics] if latest_metrics - best_metrics > threshold_in: included.append(new_column) change = True if verbose: print ('Add {} with metrics gain {:.6}'.format(new_column,latest_metrics-best_metrics)) best_metrics = latest_metrics elif annealing: if random.randint(0, iters) <= iters * anneal_rate: included.append(new_column) if verbose: print ('Annealing Add {} with metrics gain {:.6}'.format(new_column,latest_metrics-best_metrics)) # backward step random.shuffle(included) for new_column in included: included.remove(new_column) model.fit(x_train[included], y_train) latest_metrics = model_metrics(model, x_test[included], y_test)[metrics] if latest_metrics - best_metrics < threshold_out: included.append(new_column) else: changed = True best_metrics= latest_metrics if verbose: print('Drop{} with metrics gain {:.6}'.format(new_column,latest_metrics-best_metrics)) if not changed and early_stop: break return included #示例from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split(x, y)model = LGBMClassifier()included = bidirectional_selection(model, x_train, y_train, x_test, y_test, annealing=True, iters=50,best_metrics=0.5, metrics='auc',threshold_in=0.0001, threshold_out=0, early_stop=False,verbose=True)
注:公众号点击浏览原文可拜访github源码