带着爱和梦想去生活

Feature_selector实现高效的特征选择

· Read in about 3 min · (526 Words)

[参考整理]

具体可以参考我的Google云盘

https://drive.google.com/drive/folders/1cRk1d592TY3IywA6I4hWthks0QTDeTRh

下面简要介绍一下功能介绍

Introduction: Feature Selector Usage

在这个笔记本中,我们将使用FeatureSelector类来选择要从数据集中删除的要素。 此类有五种方法可用于查找要删除的功能:

  • 具有高missing-values百分比的特征
  • 具有高相关性的特征
  • 对模型预测结果无贡献的特征(即zero importance)
  • 对模型预测结果只有很小贡献的特征(即low importance)
  • 具有单个值的特征(即数据集中该特征取值的集合只有一个元素)

requirements

lightgbm==2.1.1
matplotlib==2.1.2
seaborn==0.8.1
numpy==1.14.5
pandas==0.23.1
scikit-learn==0.19.1
from feature_selector.selector import FeatureSelector
import pandas as pd

Example Dataset

该数据集被用作[Kaggle的[Home Credit Default Risk Competition]竞赛的一部分(https://www.kaggle.com/c/home-credit-default-risk/)。 它适用于受监督的机器学习分类任务,其目标是预测客户是否违约贷款。 整个数据集可以[这里]下载,我们将使用10,000行的小样本。

特征选择器设计用于机器学习任务,但可以应用于任何数据集。基于特征重要性的方法确实需要受监督的机器学习问题。

train = pd.read_csv('../data/credit_example.csv')
train_labels = train['TARGET']
train.head()

数据集中有几个分类列。 使用基于特征重要性的方法时,FeatureSelector使用独热编码处理这些。

train = train.drop(columns = ['TARGET'])
train.head()

Implementation

FeatureSelector有五个函数用于识别要删除的列:

  • identify_missing
  • identify_single_unique
  • identify_collinear
  • identify_zero_importance
  • identify_low_importance

这些方法根据指定的标准查找要删除的功能。 标识的特征存储在FeatureSelectorops属性(Python字典)中。 我们可以手动删除已识别的功能,或使用FeatureSelector中的remove功能来实际删除功能。

Create the Instance

“FeatureSelector”只需要一个数据集,其中包含行中的观察值和列中的特征(标准结构化数据)。 我们正在处理分类机器学习问题,因此我们也传递了训练标签。

# 创建 feature-selector 实例,并传入features 和labels
fs = FeatureSelector(data = train, labels = train_labels)
fs

1. Missing Values

该方法用于选择missing value 百分比大于指定值(通过missing_threshold指定百分比)的feature。该方法能应用于监督学习和非监督学习的特征选择。

# 选择出missing value 百分比大于60%的特征
fs.identify_missing(missing_threshold=0.6)
# 查看选择出的特征(可以通过ops字典访问missing)
missing_features = fs.ops['missing']
missing_features
# 绘制所有特征missing value百分比的直方图
# 该方法内部使用pandas 统计数据集中所有feature的missing value 的百分比,然后选择出百分比大于阈值的特征
fs.plot_missing()
# 有关缺失值的详细信息,我们可以访问`missing_stats`属性,这是所有要素缺失分数的数据框。
fs.missing_stats.head(17)

2. Single Unique Value

该方法用于选择只有单个取值的feature,单个值的feature的方差为0,对于模型的训练不会有任何作用(从信息熵的角度看,该feature的熵为0)。该方法可应用于监督学习和非监督学习。

# 选择出只有单个值的feature
fs.identify_single_unique()
# 查看选择出的feature
single_unique = fs.ops['single_unique']
single_unique
#绘制所有feature unique value的直方图
fs.plot_unique()
# 该方法内部的内部实现很简单,只是通过DataFrame.nunique方法统计了每个feature取值的个数,然后选择出nunique==1的feature。
fs.unique_stats.head()

3. Collinear (highly correlated) Features

该方法基于Pearson相关系数找到共线特征对。 对于高于指定阈值的每对(以绝对值表示),它标识要删除的变量之一。 我们需要传入一个correlation_threshold

该方法用于选择相关性大于指定值(通过correlation_threshold指定值)的feature。该方法同样适用于监督学习和非监督学习。

fs.identify_collinear(correlation_threshold=0.975,one_hot='True')
# 不对feature进行one-hot encoding(默认为False), 然后选择出相关性大于97.5%的feature, 
fs.identify_collinear(correlation_threshold=0.975)
# 查看选择的feature
correlated_features = fs.ops['collinear']
correlated_features[:5]
# 绘制选择的特征的相关性heatmap
fs.plot_collinear()
# 绘制所有特征的相关性heatmap
fs.plot_collinear(plot_all=True)
fs.identify_collinear(correlation_threshold=0.98)
fs.plot_collinear()

该方法内部主要执行步骤如下:

  • 根据参数’one_hot’对数据集特征进行one-hot encoding(调用pd.get_dummies方法)。如果’one_hot=True’则对特征将进行one-hot encoding,并将编码的特征与原数据集整合起来组成新的数据集,如果’one_hot=False’则什么不做,进入下一步;

  • 计算步骤1得出数据集的相关矩阵 C (通过DataFrame.corr(),注意 C 也为一个DateFrame),并取相关矩阵的上三角部分得到 C_{upper} ;

  • 遍历 C_{upper} 的每一列(即每一个特征),如果该列的任何一个相关值大于correlation_threshold,则取出该列,并放到一个列表中(该列表中的feature,即具有high 相关性的特征,之后会从数据集去除);

要查看阈值以上的核心化的详细信息,我们访问record_collinear属性,该属性是一个数据框。 将删除drop_feature,对于将要删除的每个要素,可能存在与corr_feature之间的几个相关性,它们位于correlation_threshold之上。

fs.record_collinear

4. Zero Importance Features

此方法依赖于机器学习模型来识别要删除的要素。因此,它需要有标签的监督学习问题。该方法通过使用[LightGBM库](http://lightgbm.readthedocs.io/en/latest/Quick-Start.html)中实现的梯度增强机来查找特征重要性。

要减少计算出的特征重要性的方差,模型将默认训练10次。默认情况下,该模型还使用验证集(15%的训练数据)进行早期停止训练,以确定要训练的最佳估计量。以下参数可以传递给identify_zero_importance方法:

首先对数据进行单热编码,以便在模型中使用。这意味着可以从一热编码创建一些零重要性特征。要查看单热编码列,我们可以访问FeatureSelectorone_hot_features

注意事项_:与其他方法相比,模型的特征不确定性是非确定性的(具有一点随机性)。运行此方法的结果可以在每次运行时更改。




该方法用于选择对模型预测结果毫无贡献的feature(即zero importance,从数据集中去除或者保留该feature对模型的结果不会有任何影响)。

该方法以及之后的identify_low_importance都只适用于监督学习(即需要label,这也是为什么实例化feature-selector时需要传入labels参数的原因)。feature-selector通过用数据集训练一个梯度提升机(Gradient Boosting machine, GBM),然后由GBM得到每一个feature的重要性分数,对所有特征的重要性分数进行归一化处理,选择出重要性分数等于零的feature

  • 为了使计算得到的feature重要性分数具有很小的方差,identify_zero_importance内部会对GBM训练多次,取多次训练的平均值,得到最终的feature重要性分数。

  • 为了防止过拟合,identify_zero_importance内部从数据集中抽取一部分作为验证集,在训练GBM的时候,计算GBM在验证集上的某一metric,当metric满足一定条件时,停止GBM的训练。

需要注意GBM训练过程是随机的,所以每次运行identify_zero_importance得到feature importance分数都会发生变化,但按照importance排序之后,至少前几个最重要的feature顺序不会变化。

该方法内部主要执行了以下步骤: * 对各个feature进行one-hot encoding,然后将one-hot encoding的feature和原数据集合并成新的数据集(使用pd.get_dummies完成); * 根据task的取值,实例化lightgbm.LGBMClassifier或者lightgbm.LGBMRegressor model; * 根据early_stopping的取值选择是否需要提前停止训练,并向model.fit传入相应的参数,然后开始训练model; * 根据model得到该次训练的feature importance; * 执行n_iterations次步骤1-4; * 取多次训练的feature importance的平均值,得到最终的feature importance; * 选择出feature importance等于0的feature;

# 选择zero importance的feature,
# 
# 参数说明:
#          task: 'classification' / 'regression', 如果数据的模型是分类模型选择'classificaiton',
#                否则选择'regression'
#          eval_metric: 判断提前停止的metric. for example, 'auc' for classification, and 'l2' for regression problem
#          n_iteration: 训练的次数
#          early_stopping: True/False, 是否需要提前停止

fs.identify_zero_importance(task = 'classification', 
                            eval_metric = 'auc', 
                            n_iterations = 10, 
                            early_stopping = True)

运行GBM需要对这些功能进行one-hot。 这些功能保存在FeatureSelectorone_hot_features属性中。 原始功能保存在base_features中。

one_hot_features = fs.one_hot_features
base_features = fs.base_features
print('There are %d original features' % len(base_features))
print('There are %d one-hot features' % len(one_hot_features))

FeatureSelectordata属性保存原始数据框。 在一次独热编码之后,data_all属性保存原始数据加上一独热编码特征。

fs.data_all.head(10)
zero_importance_features = fs.ops['zero_importance']
zero_importance_features[10:15]

Plot Feature Importances

使用plot_feature_importances'的特征重要性图将向我们显示plot_n`最重要的特征(在标准化的尺度上,特征总和为1)。 它还向我们展示了累积特征重要性与特征数量的关系。

当我们绘制要素重要性时,我们可以传入一个“阈值”,用于标识达到指定累积要素重要性所需的要素数。 例如,threshold = 0.99将告诉我们占总重要性99%所需的功能数量。

# 查看选择出的zero importance feature
# 前12个最重要的feature归一化后的importance分数的条形图
# feature 个数与feature importance累积和的关系图
fs.plot_feature_importances(threshold = 0.99, plot_n = 12)
# 可以在FeatureSelector的feature_importances属性中访问所有要素重要性
fs.feature_importances.head(10)
# 我们可以使用这些结果来仅选择'n'最重要的特征。 例如,如果我们想要前100名最重要,我们可以做以下事情
one_hundred_features = list(fs.feature_importances.loc[:99, 'feature'])
print(len(one_hundred_features))
one_hundred_features[:10]

5. Low Importance Features

该方法是使用identify_zero_importance计算的结果,选择出对importance累积和达到指定阈值没有贡献的feature(这样说有点拗口),即图5中蓝色虚线之后的feature。该方法只适用于监督学习。identify_low_importance有点类似于PCA中留下主要分量去除不重要的分量。

# 选择出对importance累积和达到99%没有贡献的feature
fs.identify_low_importance(cumulative_importance = 0.99)
# 查看选择出的feature(该方法选择出的feature其实包含了zero importance的feature)
len(fs.ops['low_importance'])
# 要删除的低重要性功能是那些对指定的累积重要性没有贡献的功能。 这些也可以在`ops`字典中找到。
low_importance_features = fs.ops['low_importance']
low_importance_features[:5]

6 Removing Features

一旦我们确定要删除的功能,我们就可以通过多种方式删除这些功能。 我们可以访问removal_ops字典中的任何功能列表并手动删除列。 我们也可以使用remove方法,传入识别我们想要删除的特征的方法。

此方法返回结果数据,然后我们可以将其用于机器学习。 仍然可以在功能选择器的“data”属性中访问原始数据。

小心用于删除功能的方法! 在使用remove函数之前检查将要删除的功能是个好主意。

feature-selector中提供了remove方法将选择的特征从数据集中去除,并返回去除特征之后的数据集。

train_no_missing = fs.remove(methods = ['missing'])
train_no_missing_zero = fs.remove(methods = ['missing', 'zero_importance'])
# 要从所有方法中删除要素,请传入method ='all'。 
# 在我们执行此操作之前,我们可以使用check_removal检查要删除的功能数量。 这将返回已识别要删除的所有功能的列表。
all_to_remove = fs.check_removal()
all_to_remove[10:25]
# 去除所有类型的特征
#    参数说明:
#       methods: 
#               desc:  需要去除哪些类型的特征
#               type:  string / list-like object
#             values:  'all' 或者是 ['missing', 'single_unique', 'collinear', 'zero_importance', 'low_importance']
#                      中多个方法名的组合
#      keep_one_hot: 
#              desc: 是否需要保留one-hot encoding的特征
#              type: boolean
#              values: True/False
#              default: True

train_removed = fs.remove(methods = 'all')
### Handling One-Hot Features

# 如果我们查看返回的数据框,我们可能会注意到原始数据中没有的几个新列。 
# 这些是在对机器学习进行独热编码时创建的。 要删除所有独热编码特征值,我们可以将`keep_one_hot = False`传递给`remove`方法。

train_removed_all = fs.remove(methods = 'all', keep_one_hot=False)
print('Original Number of Features', train.shape[1])
print('Final Number of Features: ', train_removed_all.shape[1])

7 Alternative Option for Using all Methods

如果我们不想一次运行一个识别方法,我们可以使用identify_all在一次调用中运行所有方法。

对于此函数,我们需要传入参数字典以用于每个单独的识别方法。

以下代码在一次调用中完成上述步骤。

fs = FeatureSelector(data = train, labels = train_labels)

# 少了下面任何一个参数都会报错,raise ValueError
fs.identify_all(selection_params = {'missing_threshold': 0.6, 
                                    'correlation_threshold': 0.98, 
                                    'task': 'classification', 
                                    'eval_metric': 'auc', 
                                    'cumulative_importance': 0.99})
train_removed_all_once = fs.remove(methods = 'all', keep_one_hot = True)
fs.feature_importances.head()

由于要素重要性已更改,删除的要素数量之间存在轻微差异。 由missingsingle_uniquecollinear确定要删除的特征数量将保持不变,因为它们是确定性的,但是zero_importancelow_importance的特征数量可能因训练模型而有所不同 多次。

8 Conclusions

这个笔记本展示了如何使用FeatureSelector类从数据集中删除功能。 这个实现有一些重要的注意事项:

  • 功能重要性将在机器学习模型的多次运行中发生变化
  • 决定是否保留通过独热编码创建的额外功能
  • 尝试各种参数的几个不同值,以确定哪些参数最适合机器学习任务
  • 对于相同的参数,缺失,单一唯一和共线的输出将保持相同
  • 特征选择是机器学习工作流程的关键步骤,可能需要多次迭代才能进行优化