2.特征构造、选择
特征构造
统计量特征:计数、求和、比例、标准差等
时间特征:相对时间、绝对时间,节假日,双休日等
地理信息:分桶
非线性变换:取log、平方、根号
数据分桶:等频、等距分桶、Dest-KS分桶、卡方分桶
特征组合、交叉:人、商品
数据分桶
等频分桶:区间的边界值要经过选择,使得每个区间包含大致相等的实例数量。
等距分桶:从最小值到最大值之间,均分为 N 等份;
Best-KS分桶:类似利用基尼指数进行二分类;
卡方分桶:自底向上的(即基于合并的)数据离散化方法。它依赖于卡方检验:具有最小卡方值的相邻区间合并在一起,直到满足确定的停止准则。
为什么要做数据分桶呢?
离散后稀疏向量内积乘法运算速度更快,计算结果也方便存储,容易扩展;
离散后的特征对异常值更具鲁棒性,如 age>30 为 1 否则为 0,对于年龄为 200 的也不会对模型造成很大的干扰;
LR 属于广义线性模型,表达能力有限,经过离散化后,每个变量有单独的权重,这相当于引入了非线性,能够提升模型的表达能力,加大拟合;
离散后特征可以进行特征交叉,提升表达能力,由 M+N 个变量编程 M*N 个变量,进一步引入非线形,提升了表达能力;
特征离散后模型更稳定,如用户年龄区间,不会因为用户年龄长了一岁就变化
当然还有很多原因,LightGBM 在改进 XGBoost 时就增加了数据分桶,增强了模型的泛化性。
特征选择
特征选择主要有两个功能:
减少特征数量、降维,使模型泛化能力更强,减少过拟合
增强对特征和特征值之间的理解
从两个方面考虑来选择特征:
特征是否发散:如果一个特征不发散,例如方差接近于0,也就是说样本在这个特征上基本上没有差异,这个特征对于样本的区分并没有什么用。
特征与目标的相关性:这点比较显见,与目标相关性高的特征,应当优选选择。
过滤式(filter):
概述:按照发散性或者相关性对各个特征进行评分,设定阈值或者待选择阈值的个数,选择特征。然后再训练学习器。特征选择过程与后续学习器无关
方法: Relief、方差选择、相关系数、卡方检验、互信息
- 数值型特征,方差很小的特征可不要
from sklearn.feature_selection import VarianceThreshold # 默认方差为0的特征会自动删除
X = [[0, 2, 0, 3], [0, 1, 4, 3], [0, 1, 1, 3]]
selector = VarianceThreshold() # 默认threshold=0.0
print(selector.fit_transform(X))
# variances_查看样本各个特征的方差
# get_params(deep=True):获取估计器参数,以字典形式返回
# get_support(indices=False):参数为False,返回满足的特征的索引为True,则特征列受否满足为True或False;
# inverse_transform(X):反转转换操作,返回X,剔除的特征列值用0替换的数组
scores按升序排序,选择排前k名所对应的特征
sklearn.feature_selection.SelectKBest(score_func=<function f_classif>, k=10)
scores按升序排序,选择排前百分percentile所对应的特征
sklearn.feature_selection.SelectPercentile(score_func=<function f_classif>, percentile=10)
-
分类特征,取值个数高度偏斜的那种可以先去掉
-
相关系数排序
选择相关系数大于阈值的部分特征;(当然有时候根据字段含义也可以选)
连续数据,正态分布,线性关系,用pearson相关系数是最恰当,当然用spearman相关系数也可以,效率没有pearon相关系数高。上述任一条件不满足,就用spearman相关系数,不能用pearson相关系数。
两个定序测量数据(顺序变量)之间也用spearman相关系数,不能用pearson相关系数。Pearson相关系数的一个明显缺陷是,作为特征排序机制,他只对线性关系敏感。如果关系是非线性的,即便两个变量具有一一对应的关系,Pearson相关性也可能会接近0。 -
利用假设检验得到特征与输出值之间的相关性
分类问题:chi2, f_classif, mutual_info_classif
回归问题:f_regression, mutual_info_regression
方法有比如卡方检验、t检验、F检验等。
卡方检验一般是检查离散变量与离散变量的相关性,当然离散变量的相关性信息增益和信息增益比也是不错的选择(可以通过决策树模型来评估来看) -
互信息
利用互信息从信息熵的角度分析相关性
包裹式(wrapper):
确定模型和评价准则之后,对特征空间的不同子集做交叉验证,进而搜索最佳特征子集
方法:LVM(Las Vegas Wrapper)、递归特征消除算法、基于机器学习模型的特征排序
主要思想:
包裹式从初始特征集合中不断的选择特征子集,训练学习器,根据学习器的性能来对子集进行评价,直到选择出最佳的子集。包裹式特征选择直接针对给定学习器进行优化。
优缺点:
从最终学习器的性能来看,包裹式比过滤式更好;
计算开销通常比过滤式特征选择要大得多。
特征子集的搜索问题,最容易想到的办法是穷举法,还可以在拉斯维加斯方法框架下使用随机策略进行子集搜索(Las Vegas Wrapper,LVW)。但是由于LVW算法中特征子集搜索采用了随机策略,每次特征子集评价都需要训练学习器,计算开销很大,如果初始特征数很多,算法可能运行很长时间都达不到停止条件,若有运行时间限制,可能给不出解。
因此,我们通常使用的是贪心算法:如前向搜索(在最优的子集上逐步增加特征,直到增加特征并不能使模型性能提升为止)、后向搜索、双向搜索(将前向搜索和后向搜索相结合)。
递归特征消除(Recursive feature elimination)
对特征含有权重的预测模型(如线性模型的权重系数),递归特征消除(RFE)通过递归考虑越来越少的特征集来选择特征。
具体流程如下:首先,对估计器进行初始特征集训练,并通过coef_或feature_importances_属性获取特征的重要性;然后,从当前的特征中删除最不重要的特征;最后,对以上过程进行递归重复,直至达到所需的特征数量。
对手写数字图片每个像素点权重进行排名
# 例1:递归特征消除示例
from sklearn.svm import SVC
from sklearn.datasets import load_digits
from sklearn.feature_selection import RFE
# 导入手写数字数据集
digits = load_digits()
print(digits.images.shape) # (1797, 8, 8)
X = digits.images.reshape((len(digits.images), -1))# (1797, 64)
y = digits.target
# 创建RFE并
svc = SVC(kernel="linear", C=1)
rfe = RFE(estimator=svc, n_features_to_select=1, step=1)
rfe.fit(X, y)
ranking = rfe.ranking_.reshape(digits.images[0].shape)
print(ranking.shape)
# 将排名可视化输出
plt.matshow(ranking, cmap=plt.cm.Blues)
plt.colorbar()
plt.title("Ranking of pixels with RFE")
plt.show()
# estimator,一种具有 fit 方法的监督学习估计器,它通过一个coef_属性或feature_importances_ 提供关于特征重要性的信息。
# n_features_to_select,要选择的特征的数量。如果没有,则选择一半的特征。
# step,如果大于或等于1,则对应于每次迭代中要删除的(整数)特性数。如果在(0.0,1.0)范围内,则对应于在每次迭代中要删除的特性的百分比(向下舍入)。
# n_features_,选择特征的数量。
# support_,所选特征的掩码。
# ranking_,特征的的排名
# estimator_
嵌入式(embedding):
概述:结合过滤式和包裹式,学习器训练过程中自动进行了特征选择。先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。
方法:LR+L1、决策树、lasso回归
主要思想:
在过滤式和包裹式特征选择方法中,特征选择过程与学习器训练过程有明显的分别。而嵌入式特征选择在学习器训练过程中自动地进行特征选择。
嵌入式选择最常用的是L1 正则化和L2正则化
正则化项越大,模型越简单,系数越小,
当正则化项增大到一定程度时,所有的特征系数都会趋于0,在这个过程中,会有一部分特征的系数先变成0。也就实现了特征选择过程。逻辑回归、线性回归、决策树都可以当作正则化选择特征的基学习器,只有可以得到特征系数或者可以得到特征重要度的算法才可以作为嵌入式选择的基学习器。
将LinearSVC 和SelectFromModel结合来评估特征的重要性进行特征选择,之后用RandomForestClassifier模型使用转换后的输出(即被选出的相关特征)进行训练。
from sklearn.pipeline import Pipeline
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectFromModel
from sklearn.svm import LinearSVC
from sklearn.ensemble import RandomForestClassifier
iris = load_iris()
X, y = iris.data, iris.target
clf = Pipeline([
('feature_selection', SelectFromModel(LinearSVC(penalty="l1", dual=False, max_iter=3000))),
('classfication', RandomForestClassifier(n_estimators=100))])
clf.fit(X, y)
# sklearn.feature_selection.SelectFromModel(estimator, threshold=None, prefit=False, norm_order=1, max_features=None)
# estimator,**SelectFromModel与任何训练后有coef_或feature_importances_属性的预测模型一起使用**
# threshold.用于特征选择的阈值。重要性大于或等于的特征被保留,也可以设置成一些抽象的值,比如mean,median,1.25*mean等等。
# prefit 是否对传入的基本分类器事先进行训练。
# norm_order
# max_features 所选特征数目
# threshold_
# estimator_