特征工程作为模型建立之前数据预处理工作,可以说对于模型训练结果起着至关重要的作用。 就像前辈们说的那样:** 数据和特征决定了机器学习的上线, 而模型和算法只是逼近这个上线而已**。 个人觉得这一部分工作充满了技巧,并且非常有技术含量。
1. 什么是特征工程
特征工程就是对于原始数据进行数据预处理, 尽可能把可以把后续模型建立需要的特征提取出来。并把原始数据规整化,为后续建模做准备。
特征工程主要分为三大部分:
数据预处理:对应sklearn: sklearn-Processing data
特征选择:对应 sklearn: sklearn-Feature selection
降维:对应sklearn: sklearn-Dimensionality reduction
文中使用iris数据集进行演示。先导入测试数据。这里的features包含四列特征数据, target则是目标数据,是对数据的分类标签。
from sklearn.datasets import load_iris
iris = load_iris()
features = iris.data()
target = iris.target()
2. 数据预处理
对于拿到的原始数据, 会有这样那样的问题, 需要进行原始处理。 一般来说需要处理的问题如下:
- 量纲不统一: 特征数据的计量单位和数据数值分布范围不统一, 这样的数据在训练中会使得数值较大的特征对于训练权重较大, 数值较小的特征就会被忽略不计, 即便有可能在业务上非常重要。
- 信息冗余: 对于某些特征, 我们只需关心定性的信息, 比如胖瘦, 比如高矮, 并不需要具体体重和高度的数值, 可以通过认为规定阈值对于这类数据进行二值化。
- 类别特征值需要进行哑编码。 类别特征, 比如颜色特征如: 红,黄, 蓝, 绿。 这类特征数值化,可以拓展为四个特征, 分辨用0,1 编码。 红色则为, 1,0,0,0 黄色则为0 1 0 0。
- 缺失值, 缺失值可以通过某些方法进行填充。
- 对于现有特征进行数据变化达成非线性效果。
2.1 无量纲化
把不同数值规格的数据转换为同一个规格, 比如0附近。
2.1.1 标准化( z-score standardization ) ( 用于列向量)
对于服从正态分布或假定服从正态分布的特征转换为标准正态分布, 公式如下:
sklean只用preprocess库的StandardScaler类可以轻松实现:
from sklearn.preprocessing import StandardScaler
standard_feature = StandardScaler().fit_transform(features)
2.1.2 区间缩放
使用最大值和最小值进行区间缩放, 可以把数据缩放到[0,1]区间:
from sklearn.preprocessing import MinMaxScaler
MaxMin_feature = MinMaxScaler().fit_transform(features)
2.1.3 归一化 (对行向量处理)
归一化可以使不同维度的数据在点乘和核函数计算相似性时拥有统一的标准, 数学上就是把每整个向量转化为“单位向量”。公式如下:
公式可以简单理解为, 向量的每一个维度除以向量的模。 模长的计算方式sklearn支持两种,分别为L1, L2, 两个公式分别如下:
from sklearn.preprocessing import Normalizer
normal_feature = Normalizer().fit_transform(features)
2.1.3 不同无量纲化方法的使用场景
- z-score standardization:
使用用于用距离来度量相似性的算法, 如各种聚类算法。 或者PCA,LDA需要用到协方差进行降维,同时数据近似服从正态分布时, - 其他归一方法:
不涉及距量度, 协方差计算, 数据不符合正态分布, 可以使用区间缩放或者其他归一方法。 比如图像处理的RGB图转换为灰度图时, 数值分布在[0, 255]范围,不符合正态分布。
2.2 对于定量特征二值化(用于列向量)
定性: 高和不高
定量: 180cm, 160cm
如果定量数据本质上只关心定性信息时, 可以通过设置阈值对定量信息二值化。 在这个过程中会损失掉我们不关心的定量信息只留下我们关心的定性信息。公式如下:
使用sklearn种preprocess包的Binarizer库可以轻松上实现:
## 阈值姑且设置为3
from sklearn.preprocessing import Binarizer
bin_feature = Binarizer(threshold=3).fit_transform(features)
2.3 对于定性特征进行哑编码。
对于定性文字标记的特征, 并且特征之间没有大小关系,在建模之前需要对其进行哑编码。 由于每一个定性标签之间并没有大小关系,所以不能直接转化为数字。 需要把每一个标签拆分成单独的特征,进行0,1 编码。
如下例子:
一条特征,四个样本, 分别为['A', 'B', 'C', 'A'], 通过哑编码后可以展开为4x3的矩阵。 每一个类别展开为单独的特征,代码如下。 可通过sklearn里面preprocess库,OneHotEncoder配合LabelEncoder实现。
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import LabelEncoder
import numpy as np
features = np.array(list('ABCA'))
le = LabelEncoder()
features_label = le.fit_transform(features)
enc = OneHotEncoder()
one_hot = enc.fit_transform(features_label.reshape(4, 1)).toarray()
print(one_hot)
## 输出
[[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]
[ 1. 0. 0.]]
2.4 缺失值处理(用于列向量)
数据预处理时候对于缺失值是比较常见的。 最简单粗暴的方法莫过于直接把带有缺失值的样本去掉。 但是某些情况下, 几乎每条数据都带有缺失值, 这样的话就需要对缺失值进行填充。
sklearn.preprocess.Inputer类中提供了简单的缺失值填充方法。
from numpy import vstack, array, nan
from sklearn.preprocessing import Imputer
features = Imputer(strategy='mean').fit_transform(vstack((array([nan, nan, nan, nan]), iris.data)))
Inputer() 里面有一个重要参数为strategy, 有三个可选参数, mean, median, most_frequent. 分别为均值, 中位数, 频率最高的值。
除此之外, 还可以把缺失值当成是标签, 通过建立模型来预测缺失值。
2.5 数据变换
2.5.1 多项式变换(对行向量处理)
当特征比较少的时候, 可以对很少的特征进行多项式变化,产生更多的特征。 多项式变换,简单来说就是把现有的特征排列组合相乘。 如果是degree为2的变换则是, 把现有的特征中, 抽取两个相乘,并且获得所有组合的结果。 公式如下。
如果是四个特征, 多项式变幻时会补充一个特征1, 然后计算出5个特征的两两不重复的组合, 包括自己和自己的组合。
sklearn.preprocessing库的PolynomialFeatures的类可以轻松实现:
poly_feature = PolynomialFeatures(degree=2).fit_transform(features)
2.5.2 自定义变换
使用自定义函数对于原始数据进行变换。 preprocessing种的FunctionTransformer可以实现:
from numpy import log1p
from sklearn.preprocessing import FunctionTransformer
log1p_feature = FunctionTransformer(log1p).fit_transform(features)
FunctionTransformer 里面的需要填入一个函数,用来做特征变换。
2.5.3 数据预处理sklearn.preprocessing 的方法总结。
类 | 功能 | 说明 |
---|---|---|
StandardScaler | 无量纲化 | 标准化,基于特征矩阵的列,将特征值转换至服从标准正态分布 |
MinMaxScaler | 无量纲化 | 区间缩放,基于最大最小值,将特征值转换到[0, 1]区间上 |
Normalizer | 归一化 | 基于特征矩阵的行,将样本向量转换为“单位向量” |
Binarizer | 二值化 | 基于给定阈值,将定量特征按阈值划分 |
OneHotEncoder | 哑编码 | 将定性数据编码为定量数据 |
Imputer | 缺失值计算 | 计算缺失值,缺失值可填充为均值等 |
PolynomialFeatures | 多项式数据转换 | 多项式数据转换 |
FunctionTransformer | 自定义单元数据转换 | 使用单变元的函数来转换数据 |
3. 特征选择
数据预处理完成以后,特征比较多时,就需要对特征进行选择。 使有意义的特征输入模型进行训练。 特征选择通常来说从两个方面入手:
- 特征是否发散:特征如果不发散, 方差很小接近于0, 就意味着这个特征在所有样本中的分布是没有差异的, 说明这个特征对于样品区分没有贡献。
- 特征与目标相关项: 对于分类问题, 特征与分类目标相关性越大说明特征对于分类问题作用也就越大。
特征选择的方法:
- Filter: 过滤法, 通过给定的方差阈值对特征进行分类, 只保留方差过阈值的特征。
- Wrapper: 包装发, 根基后续模型的学习效果, 目标函数, 每次选取若干或者排除若干特征。 来寻找最优特征。
- Embedded: 嵌入法, Filter与Wrapper 的结合。 先使用某些机器学习算法和模型训练,得到各个特征的权值洗漱,根据系数大小选择特征。例如使用RandomForest.
在sklearn中可以使用feature_selection库来进行特征选择。
3.1 Filter法
3.1.1 方差选择法
方差选择法需要计算各特征的方差,然后根据给定的方差阈值选择特征。
from sklearn.feature_selection import VarianceThreshold
selected_feature = VarianceThreshold(threshold=3).fit_transform(features)
这里的threshold根据不同的feature应该有所不同, 所以需要对于样本有一定的了解。
3.1.2 卡方检验法
对于每一个特征与目标分类标签进行卡方检验,根据得出的chi2统计和p-value对特征进行选择。
from sklearn.feature_selection import chi2
from sklearn.feature_selection import SelectKBest
selected_feature = SelectKBest(chi2, k=2).fit_transform(features, target)
3.2 Wrapper
3.2.1 递归特征消除法
递归特征消元法需要给定一个预测模型(基模型),然后使用最初的特征进行模型训练得到每一个特征的权值, 然后丢弃权值最小的的特征,重新训练模型。 如此往复直到得到所需要的数量时停止。
sklearn.feature_selection中的RFE类实现。
代码例子中,选取逻辑回归模型作为基模型。
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
selected_feature = RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(features, target)
3.3 Embedded
3.3.1 基于惩罚项的特征选择法
使用带惩罚项的基模型, 除了筛选特征,同时进行降维。 使用feature_selection库的SelectFromModel类结合L1惩罚项的逻辑回归模型实现:
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
selected_feature = SelectFromModel(estimator=LogisticRegression(penalty="l1", C=0.1)).fit_transform(features, target)
3.3.2 基于树模型的特征选择
通过树模型的GBDT作为基模型进行特征选择, 使用SelectFromModel结合GBDT模型实现:
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
selected_feature = SelectFromModel(estimator=GradientBoostingClassifier()).fit_transform(features, target)
3.4 总结
类 | 所属方式 | 说明 |
---|---|---|
VarianceThreshold | Filter | 方差选择法 |
SelectKBest | Filter | 可选关联系数、卡方校验、最大信息系数作为得分计算的方法 |
RFE | Wrapper | 递归地训练基模型,将权值系数较小的特征从特征集合中消除 |
SelectFromModel | Embedded | 训练基模型,选择权值系数较高的特征 |
4 降维
当特征选择完成后,可以直接训练模型了,但是可能由于特征矩阵过大,导致计算量大,训练时间长的问题,因此降低特征矩阵维度也是必不可少的。常见的降维方法除了以上提到的基于L1惩罚项的模型以外。
另外还有主成分分析法(PCA)和线性判别分析(LDA),线性判别分析本身也是一个分类模型。PCA和LDA有很多的相似点,其本质是要将原始的样本映射到维度更低的样本空间中,但是PCA和LDA的映射目标不一样:
- PCA是为了让映射后的样本具有最大的发散性
- 而LDA是为了让映射后的样本有最好的分类性能。
所以说PCA是一种无监督的降维方法,而LDA是一种有监督的降维方法。
4.1 主成分分析法(PCA)
使用decomposition库的PCA类选择特征的代码如下:
from sklearn.decomposition import PCA
selected_feeature = PCA(n_components=2).fit_transform(features)
print(selected_feeature)
4.2 线性判别分析法(LDA)
使用LDA进行降维的代码如下:
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
selected_feeature = LDA(n_components=n).fit_transform(features, target)
print(selected_feeature)
总结
特征工程包含下面几个部分:
- 数据预处理:标准化, 区间缩放, 二值化, 哑编码, 缺失值处理, 多项式变换, 函数变换
- 特征选择: 方差选择, 卡方检验, 递归消除, 基于模型消除
- 数据降维: PCA, LDA