变量选择是模型构建的一个重要方面,每个建模人员都必须学习。通过变量选择,可以排除相关变量以及数据噪音等,有助于提高模型的泛化性。
许多新手认为,保留所有(或更多)的变量就能产生最佳的模型,因为没有丢失任何信息。可悲的是,他们错了!从模型中删除一个变量,变量个数减少了却增加了模型的精度,这种事情你遇到过多少次?至少,我已经碰到过很多次。这样的变量往往被发现是相关的,而且会妨碍实现更高的模型精度。
Boruta原本是一个R包,但是github上面有对应的python版本,点击链接1 ,链接2
Boruta算法
Boruta是一种特征选择算法。精确地说,它是随机森林周围的一种包装算法。这个包的名字来源是斯拉夫神话中一个居住在松林的恶魔。我们知道,特征选择是预测模型中很关键的一步。当构建一个数据集包含多个变量的模型时,这个步骤尤为重要。
当你有兴趣了解变量相关性的价值,而不是只局限于建立一个具有良好的预测精度黑盒的预测模型时候,用Boruta算法来处理这些数据集无疑是最佳选择。
Boruta函数通过循环的方式评价各变量的重要性,在每一轮迭代中,对原始变量和影子变量进行重要性比较。如果原始变量的重要性显著高于影子变量的重要性,则认为该原始变量是重要的;如果原始变量的重要性明显低于影子变量的重要性,则认为该原始变量是不重要的。
原始变量:就是我们输入的要进行特征选择的变量。
影子变量:就是根据原始变量生成的变量,生成规则是:
2.1 先向原始变量中加入随机干扰项,这样得到的是扩展后的变量
2.2 从扩展后的变量中进行抽样,得到影子变量
使用python来实现影子特征,类似于:
1 2 3 4 5 6 z = train_df[f].values np.random.shuffle(z) train_df[f + "shadow" ] = z
下面是Boruta算法运行的步骤:
首先,它通过创建混合数据的所有特征(即影子特征)为给定的数据集增加了随机性。
然后,它训练一个随机森林分类的扩展数据集,并采用一个特征重要性措施(默认设定为平均减少精度),以评估的每个特征的重要性,越高则意味着越重要。
在每次迭代中,它检查一个真实特征是否比最好的影子特征具有更高的重要性(即该特征是否比最大的影子特征得分更高)并且不断删除它视为非常不重要的特征。
最后,当所有特征得到确认或拒绝,或算法达到随机森林运行的一个规定的限制时,算法停止。
有何不同?
Boruta遵循所有相关的特征选择方法,它可以捕获结果变量有关的所有的特征。相比之下,大多数传统的特征选择算法都遵循一个最小的优化方法,它们依赖于特征的一个小的子集,会在选择分类上产生最小错误。
在对数据集进行随机森林模型的拟合时,你可以递归地处理每个迭代过程中表现不佳的特征。该方法最大限度地减少了随机森林模型的误差,这将最终形成一个最小化最优特征子集。这通过选择一个输入数据集的过度精简版本发生,反过来,会丢失一些相关的特征。
另一方面,Boruta找到所有的特征,无论其与决策变量的相关性强弱与否。这使得它非常适合被应用于生物医学领域,一部分人会感兴趣了解哪些人类的基因(特征)与某种程度上的特定的医疗条件(目标变量)相关。
案例实现
以kaggle的Porto Seguro’s Safe Driver Prediction 比赛数据为例进行操作。
如果你没有安装Boruta模块的话,直接运行下面命令进行安装模块包
接下来主要加载所需要的模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from __future__ import print_functionimport pandas as pdimport numpy as npfrom datetime import datetimefrom sklearn.ensemble import RandomForestClassifier from boruta import BorutaPy pd.set_option('display.max_rows' , 1000 ) pd.set_option('display.max_columns' , 1000 ) def timer (start_time=None ): if not start_time: start_time = datetime.now() return start_time elif start_time: thour, temp_sec = divmod ((datetime.now() - start_time).total_seconds(), 3600 ) tmin, tsec = divmod (temp_sec, 60 ) print ('\n Time taken: %i hours %i minutes and %s seconds.' % (thour, tmin, round (tsec, 2 )))
加载数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 train = pd.read_csv('train.csv' , dtype={'target' : np.int8, 'id' : np.int32}) X = train.drop(['id' ,'target' ], axis=1 ).values y = train['target' ].values tr_ids = train['id' ].values n_train = len (X) test = pd.read_csv('test.csv' , dtype={'id' : np.int32}) X_test = test.drop(['id' ], axis=1 ).values te_ids = test['id' ].values rfc = RandomForestClassifier(n_estimators=200 , n_jobs=4 , class_weight='balanced' , max_depth=6 ) boruta_selector = BorutaPy(rfc, n_estimators='auto' , verbose=2 ) start_time = timer(None ) boruta_selector.fit(X, y) timer(start_time)
接下来对结果进行分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 print ('\n Initial features: ' , train.drop(['id' ,'target' ], axis=1 ).columns.tolist() )print ('\n Number of selected features:' )print (boruta_selector.n_features_)feature_df = pd.DataFrame(train.drop(['id' ,'target' ], axis=1 ).columns.tolist(), columns=['features' ]) feature_df['rank' ]=boruta_selector.ranking_ feature_df = feature_df.sort_values('rank' , ascending=True ).reset_index(drop=True ) print ('\n Top %d features:' % boruta_selector.n_features_)print (feature_df.head(boruta_selector.n_features_))feature_df.to_csv('boruta-feature-ranking.csv' , index=False ) print ('\n Feature ranking:' )print (boruta_selector.ranking_)selected = train.drop(['id' ,'target' ], axis=1 ).columns[boruta_selector.support_] train = train[selected] train['id' ] = tr_ids train['target' ] = y train = train.set_index('id' ) train.to_csv('train_boruta_filtered.csv' , index_label='id' ) test = test[selected] test['id' ] = te_ids test = test.set_index('id' ) test.to_csv('test_boruta_filtered.csv' , index_label='id' )
另外Boruta支持其他模型,比如xgboost或lightgbm,下面以lightGBM为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 trn_df = pd.read_csv("train.csv" , index_col=0 ) target = trn_df.target del trn_df["target" ]clf = LGBMClassifier(boosting_type="rf" , num_leaves=1024 , max_depth=6 , n_estimators=500 , subsample=.623 , colsample_bytree=.5 ) feat_selector = BorutaPy(clf, n_estimators=500 , verbose=2 , random_state=None ) feat_selector.fit(trn_df.values, target.values) print (feat_selector.support_)print (trn_df.columns[feat_selector.support_])