0 - 引入
在数据预处理之后,我们通常需要选择有意义的特征进行后续的训练,一般选取特征的依据有如下两个方面:
- 特征是否发散(我觉得更好的描述应该是,特征分布是否正常):如果一个特征不发散,则方差接近0,说明各个样本的该特征相差无几,所以该特征对于样本的区分用处不大;
- 特征与目标的相关性:对于目标相关性高的特征应该优先选择。
因此,基于上述两个方面,特征选择的方法可以划分为下面三种:
- 过滤法:按照发散性或者相关性以及设定阈值对各个特征进行过滤,从而选择特征;
- 包装法:根据目标函数(通常是预测效果评分),选择或者排除若干特征;
- 嵌入法:使用某些机器学习的算法和模型进行训练,得到各个特征的权重,根据权重大小选择特征。
下面通过sklearn中的feature_selection模块并基于iris数据集来介绍不同的特征选择方式。
导入iris数据集。
from sklearn.datasets import load_iris iris = load_iris()
1 - sklearn方法查询表
下面给出feature_selection中对应不同特征选择过程的方法,以便于查询,具体的使用后面部分将详细展开。
类 | 所属方式 | 说明 |
VarianceThreshold | 过滤法 | 方差选择法 |
SelectKBest | 过滤法 | 可选关系系数、卡方校验、最大信息系数作为计算方法 |
RFE | 包装法 | 递归地训练基模型,将权重较小的特征从特征集合中删除 |
SelectFromModel | 嵌入法 | 训练基模型,选择权重高的特征 |
2 - 过滤法
2.1 - 方差选择法
计算各个特征的方差,而后根据阈值选择大于方差的特征,代码如下:
from sklearn.feature_selection import VarianceThreshold VarianceThreshold(threshold=3).fit_transform(iris.data)
2.2 - 相关系数法
计算各个特征与特征值的相关系数以及相关系数的P值,而后根据结果进行选择,代码如下:
from sklearn.feature_selection import SelectKBest from scipy.stats import pearsonr SelectKBest(lambda X,Y: array(list(map(lambda x:pearsonr(x, Y), X.T))).T[0], k=2).fit_transform(iris.data, iris.target)
2.3 - 卡方检验法
考虑N个自变量对于M个因变量的样本频数的观察值和期望的差距,根据公式$\chi^2=\sum \frac{(A-E)^2}{E}$构建统计量,这个统计量就是自变量对因变量的相关性,代码如下:
from sklearn.feature_selection import SelectKBest from sklearn.feature_selection import chi2 SelectKBest(chi2, k=2).fit_transform(iris.data, iris.target)
2.4 - 互信息法
根据公式$I(X;Y)=\sum_{x\in X}\sum_{y\in Y}p(x,y)log\frac{p(x,y)}{p(x)p(y)}$计算特征对于目标的信息系数(相关性),而后进行选择,代码如下:
from sklearn.feature_selection import SelectKBest from minepy import MINE def mic(x, y): m = MINE() m.compute_score(x, y) return (m.mic(), 0.5) #选择K个最好的特征,返回特征选择后的数据 SelectKBest(lambda X, Y: array(list(map(lambda x:mic(x, Y), X.T))).T[0], k=2).fit_transform(iris.data, iris.target)
3 - 包装法
3.1 - 特征递归消除法
使用一个基模型进行多轮训练,每轮训练后,消除若干权重小的特征,再基于新的特征进行下一轮训练,代码如下:
from sklearn.feature_selection import RFE from sklearn.linear_model import LogisticRegression RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(iris.data, iris.target)
4 - 嵌入法
4.1 - 基于惩罚项的特征选择法
带惩罚项的基模型,除了刷选出特征外,同时也进行了降维,带L1惩罚项的逻辑回归模型的特征选择代码如下:
from sklearn.feature_selection import SelectFromModel from sklearn.linear_model import LogisticRegression SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(iris.data, iris.target)
L1惩罚项降维的原理在于保留多个对目标值具有同等相关性的特征中的一个,所以舍弃掉的特征不代表不重要。因此可以通过结合L2惩罚项来优化。
若一个特征在L1中的权值为1,选在在L2中权值差别不大并且在L1中权值为0的特征构成同类集合,将这一集合的特征平分L1中的权值,因此需要一个新的逻辑回归模型,代码如下:
from sklearn.linear_model import LogisticRegression class LR(LogisticRegression): def __init__(self, threshold=0.01, dual=False, tol=1e-4, C=1.0, fit_intercept=True, intercept_scaling=1, class_weight=None, random_state=None, solver='liblinear', max_iter=100, multi_class='ovr', verbose=0, warm_start=False, n_jobs=1): #权值相近的阈值 self.threshold = threshold LogisticRegression.__init__(self, penalty='l1', dual=dual, tol=tol, C=C, fit_intercept=fit_intercept, intercept_scaling=intercept_scaling, class_weight=class_weight, random_state=random_state, solver=solver, max_iter=max_iter, multi_class=multi_class, verbose=verbose, warm_start=warm_start, n_jobs=n_jobs) #使用同样的参数创建L2逻辑回归 self.l2 = LogisticRegression(penalty='l2', dual=dual, tol=tol, C=C, fit_intercept=fit_intercept, intercept_scaling=intercept_scaling, class_weight = class_weight, random_state=random_state, solver=solver, max_iter=max_iter, multi_class=multi_class, verbose=verbose, warm_start=warm_start, n_jobs=n_jobs) def fit(self, X, y, sample_weight=None): #训练L1逻辑回归 super(LR, self).fit(X, y, sample_weight=sample_weight) self.coef_old_ = self.coef_.copy() #训练L2逻辑回归 self.l2.fit(X, y, sample_weight=sample_weight) cntOfRow, cntOfCol = self.coef_.shape #权值系数矩阵的行数对应目标值的种类数目 for i in range(cntOfRow): for j in range(cntOfCol): coef = self.coef_[i][j] #L1逻辑回归的权值系数不为0 if coef != 0: idx = [j] #对应在L2逻辑回归中的权值系数 coef1 = self.l2.coef_[i][j] for k in range(cntOfCol): coef2 = self.l2.coef_[i][k] #在L2逻辑回归中,权值系数之差小于设定的阈值,且在L1中对应的权值为0 if abs(coef1-coef2) < self.threshold and j != k and self.coef_[i][k] == 0: idx.append(k) #计算这一类特征的权值系数均值 mean = coef / len(idx) self.coef_[i][idx] = mean return self
from sklearn.feature_selection import SelectFromModel SelectFromModel(LR(threshold=0.5, C=0.1)).fit_transform(iris.data, iris.target)
4.2 - 基于树模型的特征选择法
可以用GBDT来作为基模型来进行特征选择,代码如下:
from sklearn.feature_selection import SelectFromModel from sklearn.ensemble import GradientBoostingClassifier SelectFromModel(GradientBoostingClassifier()).fit_transform(iris.data, iris.target)