特征工程

1. 特征工程

对于一个机器学习问题,数据和特征决定了结果的上限,而模型、算法的选择及优化是在逐步逼近这个上限特征工程,顾名思义,是对原始特征进行一系列的工程处理,生成有价值的新特征,并将这些特征作为输入以供算法和模型使用。基本的操作为衍生(升维)、筛选(降维)等。通过归纳和总结,将特征工程分为以下方面:

sklearn中的IRIS(鸢尾花)数据集来对特征处理功能进行说明。IRIS数据集包含4个特征(Sepal.Length(花萼长度)、Sepal.Width(花萼宽度)、Petal.Length(花瓣长度)、Petal.Width(花瓣宽度)),特征值都为正浮点数,单位为厘米。目标值为鸢尾花的分类(Iris Setosa(山鸢尾)、Iris Versicolour(杂色鸢尾),Iris Virginica(维吉尼亚鸢尾))

from sklearn.datasets import load_iris   # 导入数据集

# 加载数据集
iris = load_iris()
print(iris.data)
print(iris.target)

2. 特征预处理

2.1 特征数据无量纲化

在进行特征选择之前,一般会先进行数据无量纲化处理。这样,表征不同属性的各特征之间才有可比性。例如,分析一个人的身高和体重对健康的影响,如果使用米和千克作为单位,那么身高特征会在1.6~ 1.8m的数据内,体重特征在50~ 100kg的范围内,分析的结果会倾向于数据差别较大的体重特征。因此,为了得到更准确的结果,通常采用标准化/归一化来进行特征无量纲化处理,消除量纲对结果造成的影响。

2.1.1 标准化

将某列数据特征转化为均值为0,标准差为1的标准正态分布数据,消除了特征数据的量纲影响。常用的方法是z-score标准化。假设原始数据的均值为 μ \mu μ,标准差为 σ \sigma σ,那么标准化公式为: z = x − μ σ z=\frac{x-\mu}{\sigma} z=σxμ

from sklearn.preprocessing import StandardScaler

# 标准化,返回为标准化后的数据
StandardScaler().fit_transform(iris.data)
2.1.2 归一化

通过对某列属性数据进行线性变换映射到[0,1]或者[-1,1]之间。常用的归一化方法是Min-Max方法。这种方法问题是如果测试集存在数据不在 [ m i n , m a x ] [min,max] [min,max]这个范围,会导致max和min发生变化。所以这个方法针对指定的取值范围的特征。假设 X X X为原始数据, X m i n , X m a x X_{min},X_{max} Xmin,Xmax分别为数据最小值和最大值,归一化公式为: X n o r m = X − X m i n X m a x − X m i n X_{norm}=\frac{X-X_{min}}{X_{max}-X_{min}} Xnorm=XmaxXminXXmin

from sklearn.preprocessing import MinMaxScaler

# 区间缩放,返回为归一化后的数据
MinMaxScaler().fit_transform(iris.data)

思考:为什么对数值型特征做归一化呢?假设两种数值型特征, x 1 x_1 x1的取值范围为[0,10], x 2 x_2 x2的取值范围为[0,3],于是构造一个目标函数如下图(a)的等值图。

在学习速率相同时, x 1 x_1 x1的更新速度大于 x 2 x_2 x2,需要较多的迭代才能找到最优解。如果 x 1 x_1 x1 x 2 x_2 x2归一化到相同的数值区间,优化目标的等值图变成图(b)的图形, x 1 x_1 x1 x 2 x_2 x2的更新速度变得一致,容易更快的通过梯度下降找到最优解。但是,数据归一化并不是万能的。在实际应用中,通过梯度下降法求解的模型通常是要归一化的,包括线性回归、支持向量机、神经网络等。但对于决策树模型不适用,决策树在进行节点分裂时主要以数据集D关于特征x的信息增益比,而信息增益比与是否归一化是无关的,因为归一化并不会改变样本在特征x的信息增益。

2.1.3 L1/L2范数标准化

如果为了统一量纲,可通过L1/L2范数标准化。 x ′ = x ⃗ ∣ ∣ x ⃗ ∣ ∣ x^{'}=\frac{\vec x}{||\vec x||} x=x x 假如每个样本特征向量为 x ⃗ \vec x x ,样本标准化之后变成 x ′ x^{'} x

from sklearn.preprocessing import Normalizer

#归一化,返回值为归一化后的数据
Normalizer().fit_transform(iris.data)
2.2 缺失值处理

实际数据集中,难免会遇到一些缺失值NULL。一般用填充的方法来对这些缺失值进行填充。如果是连续特征:一种是选择该特征的所有样本值,取平均值来填充缺失值;另一种是取该特征下的特征值中位数来填充缺失值。如果是离散特征,一般选择该特征下最频繁出现的类别值填充缺失值。

from numpy import vstack, array, nan
from sklearn.preprocessing import Imputer

#缺失值计算,返回值为计算缺失值后的数据
Imputer().fit_transform(vstack((array([nan, nan, nan, nan]), iris.data)))
2.3 定量特征二值化

定量特征二值化在于设定一个阈值,大于阈值的为1,小于阈值的为0,公式如下: x ′ = { 1 , x > t h r e s h o l d 0 , x ≤ t h r e s h o l d x^{'}=\begin{cases} 1,x > threshold \\0,x \leq threshold \end{cases} x={1,x>threshold0,xthreshold

from sklearn.preprocessing import Binarizer

#二值化,阈值设置为3,返回值为二值化后的数据
Binarizer(threshold=3).fit_transform(iris.data)
2.4 离散特征的连续化处理
2.4.1 独热编码one-hot

如果数据集有类别特征,即离散特征,为定性非数值性特征,通常采用独热编码one-hot进行数值型转化。独热编码通常用于处理离散特征数据。例如血型,有A型、B型、AB型、O型四种血型。独热编码把血型变成一个四维稀疏向量,每一维度对应一种血型,A型血表示为(1,0,0,0)。除了第一A型血维度表示为1,其他血型维度都表示为0,这就是所谓的one-hot编码。经常用于离散特征处理。

from sklearn.preprocessing import OneHotEncoder

# 独热编码ont-hot,iris输出值为三种类型,进行one-hot编码
OneHotEncoder().fit_transform(iris.target.reshape((-1,1))).toarray()
2.4.2 虚拟编码dummy coding

与独热编码相似,但是又有些不同之处。一个离散特征一共有N个取值,它需要N-1个特征来代替。比如一个特征取值为高、中、低。虚拟编码只需要其中两个特征值,比如分别为高、中两个编码。如果样本特征值为高,那么编码为[1,0];如果特征值为中,编码为[0,1];如果特征值为低,编码为[0,0]。虚拟编码没有独热编码使用范围广。

2.4.2 特征嵌入embedding

这种特征处理方法一般常用于深度学习中。比如对于用户的ID特征,如果使用独热编码,那么会产生维度爆炸现象。针对高维稀疏特征,通过特征嵌入embedding将其嵌入到一个低维特征矩阵,从而得到一个低维特征向量。比如100万个用户ID,使用的嵌入特征矩阵行数为100w,列数可设置较小,比如20个,那么每一个用户ID可转化为一个20维的特征向量,进而参与模型训练。

2.5 数据变换

常见的数据变换有基于多项式、基于指数函数、基于对数函数。假如数据有4个特征,度为2的特征变换。
使用preprocessing库中的PolynomialFeatures类对数据进行多项式变换:

from sklearn.preprocessing import PolynomialFeatures

#多项式转换
#参数degree为度,默认值为2
PolynomialFeatures().fit_transform(iris.data)

也可自定义一个变换函数,调用preproccessing库的FunctionTransformer类对数据进行函数变换:

from numpy import log1p
from sklearn.preprocessing import FunctionTransformer

#自定义转换函数为对数函数的数据变换
#第一个参数是单变元函数
FunctionTransformer(log1p).fit_transform(iris.data)
2.6 异常特征样本清洗

在实际项目中的数据往往有很多异常数据,这些噪声数据会影响到模型的准确度,造成很大的偏差。那么,如何筛选出这些异常特征样本呢?

  • 聚类:比如通过KMeans聚类将训练样本聚成若干个簇,如果某个簇中样本数很少,而且簇质心和其他的簇距离很远,那么这个簇中的样本很可能是异常数据,可将其从训练集中过滤掉。
  • 异常点检验方法:使用one class SVM机器学习算法进行异常点检测并将其过滤。
2.7 不平衡数据处理

分类算法训练时,如果训练集的各个类别样本比例严重失衡,那么拟合的模型泛化能力会很差。比如,一个二分类问题,如果训练集A类样本90%,B类样本10%,而测试集A类样本50%,B类样本50%。如果不考虑类别平衡问题,训练的模型对于类别B的预测准确率会很低。

一般有两种方法解决这种类别不平衡问题:权重法和采样法

  • 权重法是对训练集的每个类别加一个权重class weight。如果某类别的样本数量较多,那么相应的权重就会较小。
  • 采样法有两种思路,一种是对类别样本数多的样本做子采样,比如训练集里A类样本90%,B类样本10%。那么可通过对A类的样本子采样,直到子采样得到的A类样本数和B类样本数一致为止。第二种思路是对类别样本数少的样本做过采样,比如对B类样本做过采样,直到过采样的B类样本数加上原来的B类样本的总样本数与A类样本数一致,然后再训练模型。但是采样法存在一个问题:采样后改变了训练集的数据分布,可能导致模型泛化能力较差。因此,提出了SMOTE算法通过人工合成的方法生成少类别的样本。

3. 特征选择

特征来源:1. 业务整理好的各特征数据,筛选出业务需要的特征;2. 从提供的业务特征数据中寻找高级的数据特征;

原始数据有很多数据特征,那么如何找到对业务问题有价值的特征数据呢?

  1. 人工选择:向该领域的专家咨询,寻求一些建议。比如药品疗效的分类问题,可询问专家哪些因素会对药品的疗效产生影响,较小或较大影响。这些特征作为特征的第一候选集;
  2. 机器选择:用统计学方法/机器学习方法等等筛选出重要的特征。

特征选择的常用方法:

3.1 Filter(过滤法)

过滤法是按照特征的发散性或相关性指标对各个特征进行评分,设定评分阈值或者选择阈值的个数,选择合适的特征。

3.1.1 方差选择法

根据某一特征的数据分布判断是否有利于区分样本,比如方差指标。方差越大的特征,这类特征有利于区分样本;方差越小的特征,比如方差为0,那么这个特征无法区分输出样本,对样本的区分没有什么作用。

from sklearn.feature_selection import VarianceThreshold

#方差选择法,返回值为特征选择后的数据
#参数threshold为方差的阈值
VarianceThreshold(threshold=3).fit_transform(iris.data)
3.1.2 相关系数法

相关系数法主要用于输出连续值的监督学习算法。分别计算所有训练集中各个特征与输出值之间的相关系数,设定一个阈值,选择相关系数较大的特征。

from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr

#选择K个最好的特征,返回选择特征后的数据
#第一个参数为计算评估特征是否好的函数,该函数输入特征矩阵和目标向量,输出二元组(评分,P值)的数组,数组第i项为第i个特征的评分和P值。在此定义为计算相关系数
#参数k为选择的特征个数
SelectKBest(lambda X, Y: array(map(lambda x:pearsonr(x, Y), X.T)).T, k=2).fit_transform(iris.data, iris.target)
3.1.3 卡方检验法

卡方检验法是检验某个特征分布和输出值分布之间的相关性。sklearn使用chi2类做卡方检验得到所有特征的卡方值与显著性水平P临界值,我们可给定卡方值阈值,选择卡方值较大的特征。卡方检验原理卡方检验是假设检验的其中一个方法,还可以使用F检验和t检验,区别是数据的统计分布不再是卡方分布,而是F分布/t分布。 X 2 = ∑ ( A − E ) 2 E \mathcal X^2=\sum\frac{(A-E)^2}{E} X2=E(AE)2

from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

#选择K个最好的特征,返回选择特征后的数据
SelectKBest(chi2, k=2).fit_transform(iris.data, iris.target)
3.1.4 互信息法

从信息熵的角度分析各个特征和输出值之间的关系评分。计算公式如下: I ( X , Y ) = ∑ x ∈ X ∑ y ∈ Y p ( x , y ) l o g p ( x , y ) p ( x ) p ( y ) I(X,Y)=\sum_{x\in X}\sum_{y\in Y}p(x,y)log\frac{p(x,y)}{p(x)p(y)} I(X,Y)=xXyYp(x,y)logp(x)p(y)p(x,y)

from sklearn.feature_selection import SelectKBest
 from minepy import MINE
 
 #由于MINE的设计不是函数式的,定义mic方法将其为函数式的,返回一个二元组,二元组的第2项设置成固定的P值0.5
 def mic(x, y):
     m = MINE()
     m.compute_score(x, y)
     return (m.mic(), 0.5)

#选择K个最好的特征,返回特征选择后的数据
SelectKBest(lambda X, Y: array(map(lambda x:mic(x, Y), X.T)).T, k=2).fit_transform(iris.data, iris.target)

一般情况下,优先使用卡方检验和互信息做特征选择。

3.2 Wrapper(包装法)

选择一个目标函数来一步步的筛选特征。

递归特征消除法(RFE)

递归消除特征法使用一个机器学习模型进行多轮训练,每轮训练后,消除若干权值系数的对应特征,再基于新特征集进行下一轮的训练。

以经典的SVM-RFE算法来讨论这个特征选择的思路。这个算法以支持向量机来做RFE的机器学习模型选择特征。它在第一轮训练的时候,会选择所有的特征来训练,得到了分类的超平面 w x + b = 0 wx+b=0 wx+b=0后,如果有n个特征,那么RFE-SVM会选择出w中分量的平方值 w i 2 w^2_i wi2最小的那个序号i对应的特征,将其排除,在第二类的时候,特征数就剩下n-1个了,我们继续用这n-1个特征和输出值来训练SVM,同样的,去掉 w i 2 w^2_i wi2最小的那个序号i对应的特征。以此类推,直到剩下的特征数满足我们的需求为止。

from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression

#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数
RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(iris.data, iris.target)
3.3 Embedded(嵌入法)

用机器学习的方法来选择特征,但是它和RFE的区别是它不是通过不停的筛掉特征来进行训练,而是使用的都是特征全集。

3.3.1 基于L1/L2正则项的特征选择法

正则化惩罚项越大,那么模型的系数就会越小。当正则化惩罚项大到一定的程度的时候,部分特征系数会变成0,当正则化惩罚项继续增大到一定程度时,所有的特征系数都会趋于0. 但是我们会发现一部分特征系数会更容易先变成0,这部分系数就是可以筛掉的。也就是说,我们选择特征系数较大的特征。常用的L1正则化和L2正则化来选择特征的基学习器是逻辑回归。

from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression

#带L1惩罚项的逻辑回归作为基模型的特征选择
SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(iris.data, iris.target)
3.3.2基于树模型的特征选择法

树模型中GBDT也可用来作为基模型进行特征选择,因为训练后的基模型含有特征系数coef_或者特征重要度feature importances这些重要参数,模型可根据这些参数来选择重要特种。sklearn调用feature_selection库的SelectFromModel类结合GBDT模型,来选择特征的代码如下:

from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier

#GBDT作为基模型的特征选择
SelectFromModel(GradientBoostingClassifier()).fit_transform(iris.data, iris.target)
3.4 高级特征

以上特征选择方法都是针对单个特征对业务问题的影响来进行选择的。事实上,多个特征组合具有更强的信息表达能力,能为模型带来更有价值的数据。高级特征是对单个特征进行特征组合形成的。比如车的路程特征与时间特征,那么可通过这两个特征得到车的平均速度这一高级特征。高级特征还可以和其他特征进行组合得到更高级的特征……。针对广告CTR预估,其中的模型是采用GBDT+LR方式来进行建模,先使用GBDT集成算法从原始数据中挖掘出高级特征,并将这些高级特征作为LR的输入,从而对广告点击量进行预测。需要注意的一点是,高级特征并不是所有的特征组合都有意义的,这需要结合业务和模型进行选择。而且各个不同的特征进行组合,很容易导致特征维度爆炸。

高级特征对于模型优化是十分重要的步骤。通常,建模时先不考虑复杂的高级特征,在得到一个基准模型之后,再寻找高级特征对基模型进行优化。

4. 降维

当特征选择完成后,但是由于特征矩阵维度过大,导致计算速度很慢,训练时间长等问题。因此,可考虑降低特征矩阵维度。常见的降维方法除了基于L1/L2正则的模型以外,还有主成分分析法PCA和线性判别分析LDA。线性判别分析本身也是一个分类模型。PCA和LDA有很多的相似点,其本质是要将原始的样本映射到维度更低的样本空间中,但是PCA和LDA的映射目标不一样:PCA是为了让映射后的样本具有最大的发散性;而LDA是为了让映射后的样本有最好的分类性能。所以说PCA是一种无监督的降维方法,而LDA是一种有监督的降维方法。

4.1 主成分分析PCA

调用sklearn的decomposition库中的PCA类进行选择特征。

from sklearn.decomposition import PCA

# 主成分分析法,返回值为降维后的数据,n_component表示主成分数量
PCA(n_components=2).fit_transform(iris.data)
4.2 线性判别分析法LDA

调用sklearn的lda库中的LDA类进行选择特征

from sklearn.lda import LDA

#线性判别分析法,返回降维后的数据
#参数n_components为降维后的维数
LDA(n_components=2).fit_transform(iris.data, iris.target)

5. 参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值