特征预处理是特征工程的一部分,数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。特征工程在机器学习中占有相当重要的地位,在实际应用中,特征工程是机器学习成功的关键。特征工程是利用数据领域的相关只是来创建能够使机器学习算法达到最佳性能的过程。特征工程包含了数据预处理(Data PreProcessing),特征提取(Feature Extraction),特征选择(Feature Selection)和特征构造(Feature Construction)等子问题,而数据预处理又包括了数据清洗和特征预处理等子问题。特征工程的整体框架可参见下图。

python生成等间距向量_聚类

    本周主要学习的特征处理仅针对单个特征变量,包括无量纲化,特征分桶,统计变化及缺失值处理等。

一. 特征无量纲化

数据一般都是有单位的,因存在量纲不同,各变量在一起不能反映样本中每一个特征的重要程度,这就需要数据做标准化及归一化处理。之所以进行数据标准化的原因如下:

  • 某些算法要求样本具有零均值和单位标准差;
  • 需要消除样本不同属性具有不同两级时的影响。
  • 归一化有可能提高精度;数量级的差异将导致量级较大的属性占据主导地位,从而与实际情况相悖;
  • 数量级的差异将导致迭代收敛速度减慢。当使用梯度下降算法寻求最优解时,很有可能走“之字型”路线(垂直等高线走),从而导致需要迭代很多次才能收敛。
  • 依赖于样本距离的算法对于数据的数量级非常敏感。

1.标准化

    标准化的前提时特征值服从正态分布,经过标准化后转换为标准正态分布。基于原始数据的均值和标准差进行数据标准化。此种方法适用于属性的最大值和最小值未知的情况的情况,或有超出取值范围的离群数据的情况。均值和标准差都是在样本集上定义的,而不是在单个样本上定义的。标准化时针对某个属性的,需要用到所有样本在该属性上的值。

    在实际的建模过程中,都是将数据集划分为训练数据集和测试数据集,当训练的数据集进行数据的归一化处理时,需要计算训练数据集的均值,方差或者其他辅助统计量,那么在利用测试集进行模型的验证时,是否需要计算测试集对应的统计量呢,实际处理方式是仍然利用训练集的统计量进行归一化处理,这是因为测试数据模拟的是真实环境,在真实环境中可能无法得到进行归一化处理所需的统计量,因此需要用训练数据集的统计量对于测试集的数据进行归一化处理。

Sklearn中的专门的归一化方法standardScaler,以下是代码实现代码:

from sklearn.preprocessing import StandardScaler
 #标准化,返回值为标准后的数据
 standardScaler=StandardScaler().fit(X_train)
 standardScaler.transform(X_train)

2.归一化

    MinMax归一化,区间缩放发利用了边界值信息,将属性缩放到[0,1],减去最小值除以最大值与最小值的插值,实现代码如下:

From sklearn.preprocessing import MinMaxScaler
#区间缩放,返回值为缩放到[0,1]区间的数据
minMaxScaler=MinMaxScaler().fit(X_train)
minMaxScaler.transform(X_train)

    这种归一化的方式存在的缺陷就是当有新数据加入时,可能导致max和min的变化,需要重新定义,MinMaxScaler对异常值的存在非常敏感

MaxAbs归一法,单独的缩放和转换每个特征,使得训练集中的每个特征的最大绝对值将为1,将属性缩放到[-1,1],因其不会移动或剧中数据,所以不会破坏任何稀疏性。样本数值除以做大值的绝对值,实现代码如下:

From sklearn.preprocessing import MaxAbsScaler
maxAbsScaler=MaxAbsScaler().fit(X_train)
maxAbsScaler.transform(X_train)

3.正态分布

    正则化的过程是将每个样本缩放到单位范数(每个样本的范数为1)如果要使用如二次型或者其他核方法计算两个样本之间的相似性这种方法会很有用。该方法时文本分类和聚类分析中经常使用的向量空间模型。标准化的主要思想是对每个样本计算其P-范数,然后对该样本中每个元素除以该范数,这样处理的结果是使得每个处理后的样本的P-范数(l1-norm,l2-norm)等于1。

实现代码如下:

From sklearn.preprocessing import Normalizse
#归一化,返回值为归一化后的数据
Normalizer=Normalizer(norm=’l2’).fit(X_train)
Normalizer.transform(X_train)

4.标准化及归一化对照

    标准化和归一化相同点在于都取消了由于量纲不同引起的误差,否是一种线性变化,都是对向量X按照比例压缩再进行平移。但二者又存在以下几点不同。

  • 目的不同,归一化是为了消除量纲压缩到[0,1]区间;标准化只是调整特征整体分布;
  • 归一化与最大最小值有关,标准化与均值标准差有关
  • 归一化输出再[0,1]之间,标准化无限制。

    综上的差异点,所以若输出范围有要求,用归一化;若数据较为稳定,不存在极端的最大最小值,用归一化;若数据存在异常值和较多噪音,用标准化可以间接通过中心化避免异常值和极端值的影响。

    在分类聚类算法中,需要使用距离来度量相似性的时候,或者使用PCA技术进行降维的时候标准化表现更好。再不涉距离度量,协方差计算,数据不符合正态分布的时候,可以使用归一方法。基于树的模型不需要进行特征的归一化。例如随机森林,bagging与boosting等方法。如果是基于参数的模型或者基于距离的模型,因为需要对参数或者距离进行计算,都需要进行归一化处理。

    一般来说,建议先使用标准化。对于输出有要求时再尝试别的方法,如归一化或者更加复杂的方法。很多方法都可以将输出范围调整到[0,1],如果对于数据的分布有假设的话,更加有效的方法是使用相应的概率密度函数来转换。

二.特征分箱(数据离散化)

    离散化是数值型特征非常重要的一个处理,其实就是要将数值型数据转化成类别型数据。连续值的取值空间可能是无穷的,为了便于表示和在模型中处理,需要对连续值特征进行离散化处理。

分箱的重要性及其优势:

  • 离散特征的增加和减少都很容易,易于模型的快速迭代;
  • 稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展;
  • 离散化后的特征对异常数据有很强的鲁棒性;比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰;
  • 对于线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于模型引入了非线性,能够提升模型表达能力,加大拟合;
  • 离散化后可以进行特征交叉,由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力;
  • 特征离散化后,模型会更稳定;比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问;
  • 特征离散化以后,起到了简化了逻辑回归模型的作用,降低了模型过拟合的风险;
  • 可以将缺失作为独立的一类带入模型;
  • 将所有变量变换到相似的尺度上。

    分箱的方法分为无监督分箱及有监督分箱,其中无监督分箱法又有自定义分箱,等距分箱,等频分箱,聚类分箱及二值化。有监督的分箱方法有卡方分箱,最小熵法分箱。

    自定义分箱,是指根据业务经验或者常识等自行设定划分的区间,然后将原始数据归类到各个区间中。

    等距分箱是按照相同宽度将数据分成几等份,从最小值到最大值之间,均分为 N 等份,如果 A,B 为最小最大值, 则每个区间的长度为 W=(B−A)/N , 则区间边界值为A+W,A+2W,….A+(N−1)W 。这里只考虑边界,每个等份里面的实例数量可能不等。很明显其受异常值的影响比较大。Python中通过pandas中cut实现。

    等频分箱将数据分成几等份,每等份数据里面的个数是一样的。区间的边界值要经过选择,使得每个区间包含大致相等的实例数量。比如说 N=10 ,每个区间应该包含大约10%的实例。Python中通过pandas中qcut实现。

    聚类分箱基于k均值聚类的分箱,k均值聚类法将观测值聚为k类,但在聚类过程中需要保证分箱的有序性。第一个分箱中所有观测值都要小于第二个分箱中的观测值,第二个分箱中所有观测值都要小于第三个分箱中的观测值,等等。对预处理后的数据进行归一化处理;将归一化处理过的数据,应用k-means聚类算法,划分为多个区间(采用等距法设定k-means聚类算法的初始中心,得到聚类中心)。在得到聚类中心后将相邻的聚类中心的中点作为分类的划分点,将各个对象加入到距离最近的类中,从而将数据划分为多个区间;重新计算每个聚类中心,然后重新划分数据,直到每个聚类中心不再变化,得到最终的聚类结果。以下为实现代码:

from sklearn.cluster import KMeans
kmodel=KMeans(n_clusters=k)  #k为聚成几类
kmodel.fit(data.reshape(len(data),1))) #训练模型
c=pd.DataFrame(kmodel.cluster_centers_) #求聚类中心
c=c.sort_values(by=’列索引') #排序  
w=pd.rolling_mean(c,2).iloc[1:] #用滑动窗口求均值的方法求相邻两项求中点,作为边界点
w=[0] +list(w[0] + [ data.max() ]  #把首末边界点加上
d3= pd.cut(data,w,labels=range(k)) #cut函数

    二值化可以将数值型(numerical)的feature进行阀值化得到boolean型数据。这对于下游的概率估计来说可能很有用(比如:数据分布为Bernoulli分布时)。实现代码如下:

from sklearn.preprocessing import Binarizer
# Binarizer函数也可以设定一个阈值,结果数据值大于阈值的为1,小于阈值的为0
binarizer = Binarizer(threshold=0.0).fit(X_train)
binarizer.transform(X_train)

三.缺失值的处理

    对于每列数据,查看缺失数据量的多少,检查缺失率,对于缺失率高于阈值的变量可考虑进行删除,阈值有人为决定。对于缺失值的处理,知乎上有篇文章说的不错:https://www.zhihu.com/question/26639110,可以借鉴。常用的方法总结如下:

  • 一般缺失率达到90%的数据可考虑直接删除。
  • 当面对缺失数据较多的时候,如果对删除该变量存疑的话,可以考虑新建一个变量,该变量缺失为NO,不缺失为YES。
  • 最常用的方法就是均值,中位数进行填充,不过这种方式并不一定合适,因实际情况可能与填充差异较大,相当于人为添加了噪声。
  • 利用上下数据,插值法填充,这种方法更适合时间序列数据。
  • 利用算法进行拟合然后填充:这个方法的难度在于解释变量的寻找,在高位数据中,到底哪些变量才是适合用来做缺失数据的解释变量。

四.分类变量转化成数值变量

    一般对二元分类变量的处理,就是将其变成0-1变量,至于谁取1并么有太大影响,或者自定义。用LabelEncoder类进行处理,实现代码如下:

#类别型标签转换成数值型
lae = preprocessing.LabelEncoder()
lae.fit(iris.target_names)
print lae.transform(iris.target_names)

    对于多分类变量,我们可以用one-hot编码,虽然数据的维度变高了,但是通过这样,原始的信息没有丢失,同时其中的缺失变量在新变量中全部为0。

#每个特征是类别特征
X=[[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]]
from sklearn import preprocessing
#one hot编码
enc = preprocessing.OneHotEncoder()
enc.fit(X)
print enc.transform(X).toarray()

    以上是第三周机器学习的内容,但特征工程涉及的内容还很多,在之后会继续学习丰富。