<开场白>(*´゚∀゚`)ノ 不存在的
1.1.6 实践操作特征选择
上一篇可能抽象的东西比较多,我们现在从代码的角度分析一下整个特征选择的部分。
Python的Sklearn库中有一个包feature_selection主要就是做特征提取的,我们从这个包具体地看一下这个问题。
Sklearn中主要提供五种特征选取的方法,分别代表了不同的特征选择方式:
a.移除低方差的特征(Removing features with low variance)(filter方式):
这里VarianceThreshold类直接继承base包下的BaseEstimator,间接继承本包下的SelectorMixin
BaseEstimator主要是一些参数方法(get,set)以及打印版本等功能块:
例如上图这个静态私有的_get_param_names方法为公有函数提供参数检查。
- init先通过反射加载cls实例的deprecated信息,也就是检查函数是否版本过期并在不存在时为init赋值构造函数;
- signature为init构造体提供函数信息及函数绑定检查;
- parameters遍历init实例Init_signature的parameters字段值并取出除self(也就是实例自身,类函数第一参数)及变量key字段(因为signature返回tuple);
- 最后返回param的排序名称;
而该方法内的signature结构如下:
这部分和python inspect里的signature不太一样,先检查obj是否有callable方法,也就是是否有函数名直接调用的call方法,而后检查传入的obj是否是绑定好的实例方法,sig=signature(obj.func)则为obj内部每个方法提供递归的检查。后续的步骤应该是为实例添加参数绑定,将未绑定的参数的第一项作为self绑定实例,若存在则将都需的转化为tuple也就是*param的类型。总的来说这部分提供了参数的标准化。
后面的部分也都类似,依次检查obj是否属于FunctionType函数,functools.partial偏函数,type类或元类等等。
而SelectorMixin则有比较详细的说明文档doc:
用来返回一个提取特征的mask。内部的基本原理是一个调用自身抽象函数_get_support_mask的get_support函数作为正变换transform和反变换inverse_transform的基础,而这个抽象函数会在继承类中依据不同的类方法重写。正变换transform和反变换inverse_transform的功能主要有检查sparse的性质并做参数检查,而后返回safe_mask。
而后的VarianceThreshold类则是一个标准的fit流程:
分别是:
- 构建初始化的threshold;
- 检查数据类型并依据类型求出方差variance,这里主要是因为稀疏矩阵有自己的variance库,而dense则可以直接求(这里的check_array在init里导入validation下的check_array,可能是出于这个函数用的比较多吧);
- 错误检查;
- 重写抽象方法_get_support_mask;
其中check_is_fitted主要就是检查一下实例和属性的正常:
最后用self.variance>self.threshold做返回。整体涉及算法的部分很少,主要还是在框架下作了很多的数据转换以及检验的部分。其实就是在体现以用户为中心的思路,保证对不同数据源的Robust性质。
b.单变量特征选择(Univariate feature selection)(filter方法)
Univariate feature selection的类和函数都写在了init下面,不过原始的定义还是在univariate_selection里面,我们可以简单看一下比较常用的卡方检验,documentation的实例如下:
chi2就是卡方检验函数
里面包括数据格式化,二值变换以及卡方值计算,对照卡方公式看就好。
SelectKbest用传入的score函数初始化,并重写了继承_BaseFilter类下的_check_params和以及_BaseFilter继承SelectorMixin的_get_support_mask接口,而后添加工厂模式的topk特征选择,当然这里做了个小优化:
也就是说当k取0或all的时候不进行处理,算个小trick吧。
继承自_BaseFilter类下的fit函数将score规范化成输出,而继承自SelectorMixin的transform则进行正变换成标准mask输出。最后的结果就如同示例所示。
最后注意selectKbest实可以自定义score函数的,只要将输入的score函数替换为想要的函数表达式或者函数就可以。
例如下面用lambda表达式计算pearson相关系数作为score的函数
SelectKBest(lambda X, Y: array(map(lambda x:pearsonr(x, Y), X.T)).T, k=2).fit_transform(iris.data, iris.target)
再比如下面利用互信息计算score分数
SelectKBest(lambda X, Y: array(map(lambda x:mic(x, Y), X.T)).T, k=2).fit_transform(iris.data, iris.target)
因为SelectKBest的写法中score函数与SelectKBest框架是分离的,所以可以作为自由发挥的拓展。
c.递归特征消除(Recursive feature elimination)(wrapper)
递归消除特征法的原理其实就是使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练。类似于boosting的思路,例如用线性回归作为基分类器,对特征进行回归并抹除小系数特征做类似降维的行为,再用新的特征集重复回归。这也就是为什么官方说“递归特征消除 ([RFE](https://link.zhihu.com/?target=http%3A//sklearn.lzjqsdd.com/modules/generated/sklearn.feature_selection.RFE.html%23sklearn.feature_selection.RFE)
)通过递归减少考察的特征集规模来选择特征”。不过可能计算复杂度比较大,基分类器选取也有问题,没有见到很多用的。等以后用到的时候再补充了。
d.使用SelectFromModel选择特征(Feature selection using SelectFromModel)(Embedding)
这个就是带regularize的特征选择了,不同于其他的是这里的threshold相当于全局threshold而不是单个特征的。具体代码等下一部分说完regularize再讨论好了,这一章内容到知乎上限了…………(╯‵□′)╯︵┻━┻
两种基础模型
d1.基于L1的特征选择(L1-based feature selection)
d2.基于树的特征选择(Tree-based feature selection)
以及其拓展结合L2惩罚项来优化可以参考这位老哥的知乎:
e.特征选择融入pipeline(Feature selection as part of a pipeline)
pipeline,没什么好说的,Linux和Python最常用的东西。
今天就不讲具体的结构了,内容好像有点多知乎上传不了,等下次吧。