特征工程:对原始数据进行一系列工程处理,将其提炼为特征,作为输入供算法和模型使用。
特征工程的目的:去除原数据中的杂项和冗余,设计更高效的特征以刻画要求解的问题和预测模型之间的关系。
特征工程的重要性:
1.特征越好,灵活性越强。好的特征的灵活性在于它允许你选择不复杂的模型,同时运行速度也更快,也更容易和维护。
2.特征越好,构建的模型越简单。好的特征可以在参数不是最优的情况,依然得到很好的性能,减少调参的工作量和时间,也就可以大大降低模型复杂度。
3.特征越好,模型的性能越出色。特征工程的目的本来就是为了提升模型的性能。
一般常用的数据类型分为结构化数据和非结构化数据。结构化数据可以看做是关系型数据库的一张表,每列都有清晰的定义,每一行数据表示一个样本信息;非结构化数据主要是文本、图像、音频、视频数据,其包含的信息无法用一个简单的数值表示,也没有清晰的类别定义,并且每个数据的大小互不相同。
这里主要学习结构化数据的预处理方法。
由于各种原因,现实中许多数据包含缺失值,通常其编码为空白,NaN或其他占位符,缺失值会使建模过程混乱,导致不可靠的输出。
1.直接使用含有缺失值得特征:当仅有少量样本缺失该特征时可以尝试使用。
2.删除含有缺失值的特征:大多数样本都缺失改特征,且该特征效用很小。
3.插值补全缺失值。
分类变量:通常分类水平出现的次数多,出现概率就高,众数填充。
数值型变量:样本属性的距离是可度量的,用有效值的均值填充。
import numpy as np
from sklearn.impute import SimpleImputer#填充缺失值的包
imp=SimpleImputer(missing_values=np.nan,strategy='mean').fit(X)
X=[[np.nan,2],[6,np.nan],[7,6]]
print(imp.transform(X))
#填充稀疏矩阵
import scipy.sparse as sp
imp=SimpleImputer(missing_values=np.nan,strategy='mean').fit(X)
X=sp.csc_matrix([[-1,2],[6,-1],[7,6]])
print(imp.transform(X).toarray())
#填充pandas类别数据
import pandas as pd
df=pd.DataFrame([["a","x"],[np.nan,"y"],["a",np.nan],["b","y"]])
imp=SimpleImputer(strategy='most_frequent').fit(df)
print(imp.transform(df))
对样本进行分类后,根据同类其他数据该属性的均值补全,均值不可行,尝试用众数(most_frequent)或中位数(median)补全。
用固定值补全缺失的属性值
利用机器学习的方法,将缺失属性作为预测目标进行预测,将样本是否缺少该属性划分训练集和测试集,采用回归,决策树等机器学习算法训练模型,再利用训练得到的模型预测测试集中样本该属性的数值。
这个方法的缺陷是如果其他属性与缺失属性无关,则预测结果毫无意义,如果预测结果非常准确,则说明这个缺失属性没有必要纳入数据集中。
通常特征不是连续值,而是分类值,这样的特征可以有效的编码为整数。将属性映射到高维空间,采用**独热编码(one-hot)**技术。将包含K个离散取值范围的属性值扩展为K+1个属性值,若该属性值缺失,则扩展后的第K+1个属性值置为1。
X_train= [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]
from sklearn.preprocessing import OneHotEncoder
enc=preprocessing.OneHotEncoder()
enc.fit(X_train)
X_test=[['female', 'from US', 'uses Safari'],['male', 'from Europe', 'uses Safari']]
print(enc.transform(X_test).toarray())
print(enc.categories_)#查看编码规则的接口
#如果测试集中出现了训练集中没有的数值属性,需要单独手动编码
import pandas as pd
df=pd.DataFrame([['A'],['B']])
dic={'A':0,'B':1}
df[0]=df[0].map(dic)
print(df)
这种方法是最精确的做法,完整保留了原数据的全部信息,不用考虑缺失值,缺点是增加了数据的维度,计算量大大增加,且只有在样本量非常大时效果才好。
多重插补认为待插补的值是随机的,实际上通常是估计出待插补的值,再加上不同的噪声,形成多组可选插补值,根据某种选择依据,选出最合适的插补值。
压缩感知通过利用信号本身所具有的稀疏性,从部分观测样本中回复原信号。压缩感知分为感知测量和重构恢复两个阶段。
结合业务手动对缺失值插补,这种方法需要对问题领域有很高的认识和理解,如果确实数据较多,会比较费时费力。
寻找与该样本最接近的样本,使用其该属性数值来补全。
异常值分析是检验数据是否有录入错误以及含有不和常理的数据。忽视异常值是非常危险的,不加剔除把异常值包括近数据的计算分析过程中,对结果会产生不良影响。
异常值是指观测中的个别值,其数值明显偏离其余的观测值。异常值也称为离群点,异常值分析也称为离群点分析。
用pandas库的descirbe()方法观察数据的描述性统计。
data.describe([0.01,0.03,0.1,0.25,0.5,0.75,0.9,0.97,0.99]).T
3∂原则的前提假设:数据服从正态分布。在3∂原则下,数值如超过3倍标准差,则将其视为异常值。正负3∂的概率是99.7%,那么距离平均值3∂之外的值出现的概率为P(|x-u| > 3∂) <= 0.003,属于极个别的小概率时间。如果数据不服从正太分布,也可以用远离平均值多少倍标准差来描述。
#提取出测试集的所有异常值的索引
deltest = []
for i in col:
bool_ = np.abs((Xtest.loc[:,i] - Xtrain.loc[:,i].mean())/Xtrain.loc[:,i].std())>3
ind = Xtest[bool_].index
deltest.extend(list(ind))
#去重之后有异常值的行索引
deltest1 = list(set(deltest))
#删除训练集特征的异常值
Xtrain = Xtrain.drop(index=deltrain1)
这种方法是利用箱线图的四分位距(IQR)对异常值进行检测。箱线图的定义如下:
四分位距(IQR)就是上四分位与下四分位的差值。而我们通过IQR的1.5倍为标准,规定:超过上四分位+1.5倍IQR,或者下四分位-1.5倍IQR的点为异常值。python代码实现:
Percentile = np.percentile(df['length'],[0,25,50,75,100])
IQR = Percentile[3] - Percentile[1]
UpLimit = Percentile[3]+ageIQR*1.5
DownLimit = Percentile[1]-ageIQR*1.5
也可以用seaborn的可视化方法boxplot来实现:
f,ax=plt.subplots(figsize=(10,8))
sns.boxplot(y='length',data=df,ax=ax)
plt.show()
以上三种方法是比较简单的异常值检测方法,接下来是一些较复杂的异常值检测方法。
顾名思义,该方法会构建一个概率分布模型,并计算对象符合该模型的概率,将低概率对象视为异常点。
如果模型是簇的组合,则异常点是不在任何簇的对象;如果模型是回归,异常点是远离预测值的对象。
优缺点:
**一个对象的离群点得分由它的KNN距离给定。**k的取值会影响离群点的得分,如果k太小,则少量的临近离群点会导致较低的离群点得分;如果k太大,则点数小于k的簇中所有的对象可能都成了离群点。为了增强鲁棒性,可以采用k个最近邻的平均距离。
优缺点:
一种常用的定义密度的方法是:定义密度为到k个最近邻的平均距离的倒数。如果该距离小,则密度高,反之亦然。
另一种密度定义是使用DBSCAN聚类算法使用的密度定义,即一个对象的密度等于该对象指定距离d内对象的个数。
优缺点:
一个对象是基于聚类的离群点,如果该对对象不属于任何簇,那么该对象属于离群点。
离群点对初始聚类的影响:如果通过聚类检测离群点,由于离群点影响聚类,存在一个问题:聚类结果是否有效。这也是K-means算法的缺点,对离群值敏感。为了解决这个问题,可以使用对象聚类;删除离群点;再次对象聚类(这个不能保证是最优的结果)。
优缺点:
除了以上提及的方法,还有两个专门用于检测异常点的方法比较常用:One Class SVM和Isolation Forest。
首先应该考虑数据集的扩充,在刚刚图片数据集扩充一节介绍了多种数据扩充的办法,而且数据越多,给模型提供的信息也越大,更有利于训练出一个性能更好的模型。
如果在增加小类样本数量的同时,又增加了大类样本数据,可以考虑放弃部分大类数据(通过对其进行欠采样方法)。
一般分类任务最常使用的评价指标就是准确度了,但它在类别不平衡的分类任务中并不能反映实际情况,原因就是即便分类器将所有类别都分为大类,准确度也不会差,因为大类包含的数量远远多于小类的数量,所以这个评价指标会偏向于大类类别的数据。
其他可以推荐的评价指标有以下几种
其中 TP、FP、TN、FN 分别表示正确预测的正类、错误预测的正类、预测正确的负类以及错误预测的负类。图例如下:
可以使用一些策略该减轻数据的不平衡程度。该策略便是采样(sampling),主要有两种采样方法来降低数据的不平衡性。
一种简单的人工样本数据产生的方法便是,对该类下的所有样本每个属性特征的取值空间中随机选取一个组成新的样本,即属性值随机采样。
你可以使用基于经验对属性值进行随机采样而构造新的人工样本,或者使用类似朴素贝叶斯方法假设各属性之间互相独立进行采样,这样便可得到更多的数据,但是无法保证属性之前的线性关系(如果本身是存在的)。
有一个系统的构造人工数据样本的方法 SMOTE(Synthetic Minority Over-sampling Technique)。SMOTE 是一种过采样算法,它构造新的小类样本而不是产生小类中已有的样本的副本,即该算法构造的数据是新样本,原数据集中不存在的。
它基于距离度量选择小类别下两个或者更多的相似样本,然后选择其中一个样本,并随机选择一定数量的邻居样本,然后对选择的那个样本的一个属性增加噪声,每次处理一个属性。这样就构造了更多的新生数据。
强烈建议不要对待每一个分类都使用自己喜欢而熟悉的分类算法。应该使用不同的算法对其进行比较,因为不同的算法适用于不同的任务与数据。
决策树往往在类别不均衡数据上表现不错。它使用基于类变量的划分规则去创建分类树,因此可以强制地将不同类别的样本分开。目前流行的决策树算法有:C4.5、C5.0、CART和Random Forest等。
你可以使用相同的分类算法,但使用一个不同的角度,比如你的分类任务是识别那些小类,那么可以对分类器的小类样本数据增加权值,降低大类样本的权值(这种方法其实是产生了新的数据分布,即产生了新的数据集),从而使得分类器将重点集中在小类样本身上。
一个具体做法就是,在训练分类器时,若分类器将小类样本分错时额外增加分类器一个小类样本分错代价,这个额外的代价可以使得分类器更加“关心”小类样本。如 penalized-SVM 和 penalized-LDA 算法。
如果你锁定一个具体的算法时,并且无法通过使用重采样来解决不均衡性问题而得到较差的分类结果。这样你便可以使用惩罚模型来解决不平衡性问题。但是,设置惩罚矩阵是一个复杂的事,因此你需要根据你的任务尝试不同的惩罚矩阵,并选取一个较好的惩罚矩阵。
从一个新的角度来理解问题,比如我们可以将小类的样本作为异常点,那么问题就变成异常点检测与变化趋势检测问题。
仔细对问题进行分析和挖掘,是否可以将问题划分为多个更小的问题,可以尝试如下方法:
特征缩放主要分为两种方法,归一化和正则化。
1.归一化:也称为标准化,这里不仅仅是对特征,实际上对于原始数据也可以进行归一化处理,它是将特征(或者数据)都缩放到一个指定的,大致相同的数据区间内。
2.归一化的原因:
某些算法要求样本数据或特征的数值具有零均值和单位方差;
为了消除样本数据或者特征之间的量纲影响。如下图所示是包含两个属性的目标函数的等高线。
数量级的差异将导致量级较大的属性占主导地位,从左图可以看到量级较大的属性会让椭圆的等高线压缩为直线,使得目标函数仅依赖于该属性。
数量级的差异会导致迭代收敛速度减慢。原始的特征进行梯度下降时,每一步梯度的方向会偏离最小值(等高线中心点)的方向,迭代次数较多且学习率必须非常小,否则非常容易引起宽幅震荡。但经过标准化后,每一步梯度的方向都几乎指向最小值方向,迭代次数较少。
所有依赖于样本距离的算法对数据的数量级都非常敏感。
3.常用的两种归一化方法:
线性函数归一化(Min-Max Scaling)。它对原始数据进行线性变换,使得结果映射到[0,1]范围内,实现对原始数据的等比缩放,公式: x’ = (x - X_min) / (X_max - X_min)
零均值归一化(Z-Score Normalization)。将原始数据映射到均值为零,标准差为1的分布上,公式:x’ = (x - μ)/σ
data_std=(data-data.mean(axis = 0))/(data.std(axis = 0))
4.如果数据分成训练集、验证集、测试集,那三个数据集都要用相同的归一化参数,数值都是通过训练集计算得到的,即上述两种方法中的最大值、最小值、均值和方差都是训练集的,可以理解为验证集测试集都是未知数据。
5.归一化不是万能的,在实际应用中,通过梯度下降法求解的模型是需要归一化的,这包括线性回归、逻辑回归、支持向量机、神经网络等模型。但决策树模型不需要,以C4.5为例,决策树在分裂节点的时候主要依据数据集D关于特征X的信息增益比,而信息增益比和特征是否归一化无关,归一化不会改变样本在特征X上的信息增益。
1.正则化是将样本或者特征的某个范数(L1或L2)缩放到单位1。
假设数据集为:
对样本首先计算Lp范数,得到:
正则化后的结果是:每个属性值除以其Lp范数
from sklearn import preprocessing
X = [[ 1., -1., 2.],[ 2., 0., 0.],[ 0., 1., -1.]]
x_normalized_l1=preprocessing.normalize(X,norm='l1',axis=0)
x_normalized_l2=preprocessing.normalize(X,norm='l2',axis=0)
2.正则化的过程是针对单个样本的,对每个样本将它缩放到单位范数。
归一化是针对单个属性的,需要用到所有样本在该属性上的值。
3.通常如果使用二次型(如点积)或者其他核方法计算两个样本之间的相似性时,该方法很有用。
定义:序号编码一般用于处理类别间有大小关系的数据,转换后依然保留大小关系。
定义:独热编码通常用于处理类别间不具有大小关系的特征。
优点:
缺点:
1.高维特征带来的问题:
2.决策树模型不推荐对离散特征进行独热编码:
产生样本切分不平衡问题,此时切分增益会非常小。
比如对血型做独热编码操作,那么对每个特征是否 A 型、是否 B 型、是否 AB 型、是否 O 型,会有少量样本是 1 ,大量样本是 0。这种划分的增益非常小,因为拆分之后:
较小的那个拆分样本集,它占总样本的比例太小。无论增益多大,乘以该比例之后几乎可以忽略。
较大的那个拆分样本集,它几乎就是原始的样本集,增益几乎为零。
影响决策树的学习。
决策树依赖的是数据的统计信息。而独热码编码会把数据切分到零散的小空间上。在这些零散的小空间上,统计信息是不准确的,学习效果变差。
本质是因为独热编码之后的特征的表达能力较差。该特征的预测能力被人为的拆分成多份,每一份与其他特征竞争最优划分点都失败。最终该特征得到的重要性会比实际值低。
二进制编码主要分为两步:
1.先采用序号编码给每个类别赋予一个类别ID;
2.接着将类别ID对应的二进制编码作为结果。
二进制本质上是利用二进制对类别ID进行哈希映射,最终得到0/1特征向量,并且特征维度小于独热编码,更加节省存储空间。
将数值型的属性转化为布尔型的属性。通常用于假设属性取值分布是伯努利分布的情形。
指定一个阈值m,>=m,二元化后取1,否则取0。m是一个关键的超参数,它的取值需要根据模型和具体业务来确定。
离散化就是将连续的数值属性转化为连续的数值属性。
线性模型
海量离散特征+简单模型
优点:模型简单
缺点:特征工程困难,但一旦有成功经验就可以推广,可以并行研究
非线性模型
少量连续特征+复杂模型
优点:不需要复杂的特征工程
缺点:模型负责
分箱是离散化的常用方法。
1.将所有样本在连续的数值属性j的取值从小到大排列。
2.从小到大依次选择分箱边界。分箱的数量以及每个箱子的大小都是超参数,需要指定。
3.给定属性j的取值a,判断a在哪个箱子中,将其划分到对应编号的箱子中,属性值取编号。
根据业务经验指定。
根据模型指定:根据具体任务来训练分箱之后的数据集,通过超参数搜索来确定最优的分箱数量和分箱边界。
import numpy as np
from sklearn import preprocessing
X=np.array([[-1,5,15,28],[0,6,14,3.],[6.,3,11.8,6]])
est=preprocessing.KBinsDiscretizer(n_bins=[2,2,2,2],encode='ordinal',strategy='kmeans').fit(X)
est.transform(X)
print(est)
print(est.bin_edges_)#返回每个箱子的边缘
选择分箱大小的经验:
数据离散化的特性: