part2_01Sklearn数据预处理

数据和特征决定了机器学习的上限,模型和算法只是去逼近这个上限。特征工程的本质是一项工程活动,目的是最大限度地从原始数据中提取特征以供算法和模型使用。数据预处理则是特征工程的一个主要部分。

简单来说,数据预处理就是将原始数据转化为便于机器学习模型学习的数据,但在实际的场景中,文本及图像等非数字数据应当如何处理?数据本身的结构问题(如缺失、取值范围过大、结构不平衡)又该怎么解决?应当掌握:

  • 数据预处理的种类
  • 数据预处理的常用方法及场景
  • 用Sklearn如何实现数据预处理

1 数据预处理的种类

数据预处理是指对收集到的数据进行审核、筛选、排序、变换及变形等。进行预处理的原因在于,现实场景中数据大体上是不完整、不一致的脏数据,无法直接进行数据挖掘,或者挖掘结果较差;还存在数据结构的缺陷问题,比如各个维度上的数据取值差异过大,需要中心化;对文本及图像数据无法直接处理。数据预处理的方法有:数据清理、数据集成、数据变换、数据规约等。这些处理技术在建立模型之前使用,可以提高数据挖掘的质量,降低数据挖掘所花费的时间。

  • 缺失值处理——在数据采集过程中容易产生数据的缺失,在样本量较小的情况下,这些数据无法简单的将缺失值舍弃,此时缺失值的处理非常有必要,主要涉及缺失值的插补工作,如特殊值插补、均值插补、矩阵补全等。
  • 数据标准化、规范化——数据标准化是将样本的属性缩放到某个指定的范围;数据规范化是将样本的某个范数(如L1范数)缩放到1,规范化的过程是针对单个样本的,将每个样本缩放到单位范数。
  • 稀疏化——将稠密的特征项进行稀疏化处理,通过只存储和处理非0元素,从而大幅降低存储空间需求及计算复杂度。
  • 特征编码——对于非数据化的特征,通常是类别特征,进行编码使其转化为一种数字化特征,使其能够被模型接受用于模型训练,常见的方法有特征二元化、独热编码
  • 特征提取——对于复杂的非数据化特征(如文本或者图像)进行提取时,从给定的集合特征中选出相关特征子集的过程称为特征选择。与特征编码不同的是,特征编码是对特征进行变化,从一种形式转化到另一种形式,其呈现方式发生了变化;特征提取则是通过判断一个“像素”集合是否属于某种特征,是对数据维度的特定集合组合的提取。

上述5种分类不是相互独立的,部分分类是有重合的,分类依据在于其主要的应用场景。

2 缺失值处理

如果训练样本足够大,是可以通过舍弃含有缺失值的样本,对剩下的样本进行训练,这也是缺失值处理的一种方法。但是对于小样本数据,无法通过简单的舍弃样本数据来对样本进行清洗,而是要进行缺失值的插补操作,即通过数据的已知部分去推断缺失部分,或者人为地设定缺失部分的值。

什么是缺失值?在某个维度上数据为空、NaN、Null,及任何没有实际意义的数据字符或者认定为表示空的字符(串)都可视为缺失值。

# numpy用于生成可用于计算的缺失值np.nan,因为python自带None值是不支持运算的,sklearn.impute库中的SimpleImputer方法用于处理缺失值
import numpy as np
from sklearn.impute import SimpleImputer
# 初始化缺失值处理器,指定缺失值参数missing_values,默认是np.nan;缺失值补全策略有均值、中位数、常数、众数(mean、median、constant、most_frequent),按需修改对象名和参数名即可
imp_mean=SimpleImputer(missing_values=np.nan,strategy='mean')
X=[[13,22],[5,3],[7,np.nan],[np.nan,5],[3,7]] # 使用列表,注意维度是2,否则报错
# 执行缺失值处理的步骤,在fit原始数据之后才能对其进行转换
imp_mean.fit(X)
print(u'均值处理,结果如下:')   # 加u避免字符编码的格式问题
print(imp_mean.transform(X))
'''
均值处理,结果如下:
[[13.   22.  ]
 [ 5.    3.  ]
 [ 7.    9.25]
 [ 7.    5.  ]
 [ 3.    7.  ]]  使用的是每一列元素的均值,求和在除以参与求和的元素数目
'''
# 使用ndarray进行实验
y=np.arange(5)
Y=y.astype(float) # 因为np.nan属于浮点数,ndarray中元素的数据类型要求一致
Y1=np.insert(Y,0,np.nan) # 使用insert()函数插入np.nan,并返回一个新的对象
Y1.resize(6,1) # 使用resize()函数直接修改原数组的形状,转化为2维的
imp_mean.fit(Y1)
print(u'均值处理,结果如下:')
print(imp_mean.transform(Y1))

3 数据的规范化

在最简单的情况下,评级的标准化意味着将在不同尺度上测量的值调整到概念上的共同尺度。在更复杂的情况下,规范化指更复杂的调整,其意图是使得调整值的整个概率分布对齐。

在统计学的另一种用法中,规范化是指创建统计的位移和缩放版本。其意图是在比较不同数据集的相应规范化值时,这些正则化值可以消除数据集差异。某些类型的规范化只涉及缩放,以获得相对于某个大小变量的值。于是测量的对象变为数据变化比率,而不是针对标量进行测量。

在机器学习中,需要学习的数据样本存在多个维度,而这些维度性质各异,通常具有不同的量纲和数量级。例如,样本的特征A取值范围为01,而特征B的取值范围是100200,当各个特征之间的水平相差很大时,如果直接使用原始指标进行分析,会增加数值较大的特征在综合分析中的影响力,同时,相对地削弱了数值较小的特征的影响程度。因此,为了保证结果的可靠性,需要对原始指标数据进行规范化处理。

3.1 缩放规范化

是将数据按比例缩放,使之落入一个较小的特定区间,如[0,1]。在某些评价指标中常会使用到缩放规范化,用于去除数据的单位限制,将其转化为无量纲的纯数值,便于不同单位或者量级的指标进行比较和加权。

如果特征之间的数值差异较大,可以先从单个特征的最大值和最小值入手,将它们作为控制缩放的因子。有两种方法——存在样本{xn(k)},其特征数为k,于是有最小值-最大值缩放(Min-Max Scaling)最大绝对值缩放(Max Absolute Scaling)

  1. 最小值-最大值缩放preprocessing.MinMaxScaler()

    将该样本每个特征下的最小值和最大值(首先怎么找到最小值和最大值)之差作为缩放倍数,分子则是每个特征下的值与最小值之差。其中,全为0的特征,其缩放后的值为0。如以下公式所示:

$$
x_{i(k)}=\frac{x_{i(k)}-min{x_{i(k)}}}{max{x_{i(k)}}-min{x_{i(k)}}}
$$

​ 经过最小值-最大值缩放后的特征,取值范围是[0,1],从而将其转化为无量纲的纯数值,在之后的模型训练中,可能会进一步提高模型性能。代码实现如下:

# numpy用于生成缺失值,引用sklearn.preprocessing库,其中有绝大部分的数据预处理方法
from sklearn import preprocessing
import numpy as np
# 原始数据X   注意要求数据必须是2维的
X=np.array([[3,-2.,2.],[2.,0.,0.],[-1,1.,3.]])
print('原始数据:\n')
print(X)
'''
[[ 3. -2.  2.]
 [ 2.  0.  0.]
 [-1.  1.  3.]]
'''
# 初始化数据预处理器,此处为最小值-最大值缩放
min_max_scaler=preprocessing.MinMaxScaler()
# 数据转换并打印
X_minmax=min_max_scaler.fit_transform(X)
print("缩放规范化结果如下:\n")
print(X_minmax)
'''
[[1.         0.         0.66666667]
 [0.75       0.66666667 0.        ]
 [0.         1.         1.        ]]
'''
# 输出其缩放倍数,按列去看
print('缩放倍数为:\n')
print(min_max_scaler.scale_) # [0.25       0.33333333 0.33333333]
# 输出其最小值
print('输出每一列的最小调整:\n') 
print(min_max_scaler.min_)  # [0.25       0.66666667 0.        ]
print('输出每一列的最小值:\n')
print(min_max_scaler.data_min_) # [-1. -2.  0.]
print('输出每一列的最大值:\n')
print(min_max_scaler.data_max_)  # [3. 1. 3.]
# 用自己的数据测试
import pandas as pd
data=pd.read_csv('n0001.csv')
data0=data.drop('PC Time Stamp',axis=1)
data0
# 后面的代码将原始数据的这一句修改即可,沿用上面的代码
X=data0  # 注意 和前面不同的是,此处的对象是dataframe,不是ndarray
# 画图看下效果
import matplotlib.pyplot as plt
plt.plot(X)
plt.show()
plt.plot(X_minmax)
plt.show()
# 文档的保存(数据格式是数据框的情况下)
data0.to_csv('data0.csv')
yyy=pd.DataFrame(X_minmax) # ndarray转化为DataFrame
yyy.to_csv('X_minmax.csv')
  1. 最大绝对值缩放preprocessing.MaxAbsScaler()

    最大绝对值缩放与最小值-最大值缩放类似,但是其执行效率更高。具体的公式如下:
    $$
    x_{i(k)}=\frac{x_{i(k)}}{\vert{max{x_{i(k)}}}\vert}
    $$
    因为分子上没有对最小值的操作,所以上式的取值范围是[-1,1]且数据分布是以0为中心的(有负值的情况下),整体上来说分布更加稀疏、合理。

    # numpy用于生成缺失值,引用sklearn.preprocessing库,其中有绝大部分的数据预处理方法
    from sklearn import preprocessing
    import numpy as np
    # 原始数据X
    X=np.array([[3,-2.,2.],[2.,0.,0.],[-1,1.,3.]])
    print('原始数据:\n')
    print(X)
    # 初始化数据预处理器,此处为最大绝对值缩放
    max_abs_scaler=preprocessing.MaxAbsScaler()
    # 数据转换并打印
    X_maxabs=max_abs_scaler.fit_transform(X)
    print("缩放规范化结果如下:\n")
    print(X_maxabs)
    # 输出其缩放倍数,按列去看
    print('缩放倍数为:\n')
    print(max_abs_scaler.scale_) # [3. 2. 3.]就是分母的数值
    
  2. 特定的取值区间

    使得数据分布在任意区间,而非[0,1]或者[-1,1]。最小值-最大值缩放可以预先设定范围,数据变化满足如下公式:
    $$
    x_{i(k)}=\frac{x_{i(k)}-min{x_{i(k)}}}{max{x_{i(k)}}-min{x_{i(k)}}}
    $$

    $$
    x_{scale-i(k)}=(max-min)*x_{i(k)}+min
    $$

    式中,x scale-i(k)表示预设最大值和最小值之后的缩放变换输出;下面的max、min分别表示预设的最值,上面的则是数据中的

# 初始化数据预处理器,此处为任意最小值-最大值缩放,需要配置feature_range来转化输出范围
min_max_scaler=preprocessing.MinMaxScaler(feature_range=(-1,1))
# 修改最小值-最大值缩放中这一句代码即可,代码中无负值时,上述三种缩放方法差别不大

对于实时的应用场景,这种缩放方法不完美,当有新样本加入时,如果样本中的某个特征的数值超出了最值的预设界限,则会引发最大值和最小值的更新,从而造成最值不稳定的情况,其转化后的特征值也会存在不稳定的状况(假设对样本进行实时的转化)。

在上述代码的基础上,引入一个超出边界的数据,并观察其输出结果。(有些问题,待商榷)

3.2 标准化

标准化(Standardization),又称为标准差规范化或者Z标准化(Z-score Normalization),对样本的每个特诊分别进行计算使其满足正态分布——均值为0,方差为1,公式如下:
$$
\overline{x}{i(k)}=\frac{x-\mu}{\sigma}
$$
μ代表所有样本的均值,σ代表所有样本的标准差。

数据集的标准化是使用Sklearn中的机器学习模型的大多数要求,如果单个特征没有呈现标准的正态分布,那么它们的表现可能很糟糕。例如,在算法学习中目标函数使用到的许多元素(如支持向量机的RBF核或者线性模型的L1和L2调整器)都假定所有特征中心在0附近,且方差相同。如果某个特征的方差比其他特征大一个数量级,它可能支配目标函数,使得估计器无法正确地学习其他特征。

# numpy用于生成缺失值,引用sklearn.preprocessing库,其中有绝大部分的数据预处理方法
from sklearn import preprocessing
import numpy as np
# 原始数据X
X=np.array([[3,-2.,2.],[2.,0.,0.],[1.,2.,3.]])
print('原始数据:\n')
print(X)
# 初始化数据预处理器
standard_scaler=preprocessing.StandardScaler()
# 数据转换并打印
X_standard=standard_scaler.fit_transform(X)
print("缩放规范化结果如下:\n")
print(X_standard)
# 输出标准化变化后的均值[ 0.00000000e+00  0.00000000e+00 -7.40148683e-17]
print(X_standard.mean(axis=0))
# 输出标准化变化后的标准差 [1. 1. 1.]
print(X_standard.std(axis=0))

同时,所有预处理方法都提供函数接口,前述均为类接口,函数调用的代码实现如下:

#将这一句
X_standard=standard_scaler.fit_transform(X)
#换成
X_standard=preprocessing.scale(X)

3.3 范数规范化

范数是具有“长度”这一距离概念的函数,其为向量空间内所有向量赋予非零的正长度或者大小。距离的定义是一个较为宽广的概念,只要满足非负、自反、三角不等式就可以称为距离。范数是一种强化了的距离概念,它在定义上比距离多了一条数乘的运算法则。

函数与几何图形之间存在对应的关系,特别是在三维以下的空间内,函数是几何图像的数学概括,而几何图像是函数的高度形象化;但当函数与几何超出三维空间时就很难直观地呈现出来,于是存在映射,映射表达的就是一个集合通过某种关系转化为另一个集合。

为了更好地在数学上表示这种映射关系(这里特指线性映射),引入了矩阵。矩阵是用于表征上述空间映射的线性关系。通过向量表示上述映射中提到的集合,常说的基就是这个集合的最一般关系。即,一个集合(向量),通过一种映射关系(矩阵),得到另外一个集合(向量)。

向量的范数就是用于表示这个原有集合的大小;矩阵的范数,是表示这个变化过程的大小的一个度量。

所有的范数,都可以表达为L-P范数的形式
$$
|x|_p=(\sum |x_i|)^\frac{1}{p}
$$

  1. L0范数——实际上用于度量向量中非0元素的个数

  2. L1范数——来源于曼哈顿的出租车司机在曼哈顿街道中由一点到另一点所需要走过的距离,用于表示向量中元素的绝对值之和,可以度量两个向量之间的差异,如绝对误差和(Sum of Absolute Difference,SAD)。

  3. L2范数——用于表示向量的距离,即欧氏距离(Euclidean Distance,ED),也可以度量两个向量之间的差距,如平方差和(Sum of Squared Difference,SSD)。

  4. L∞范数——在向量元素{xi}的集合中,必然存在一个最大绝对值|x|max,而这个最大绝对值在∞次方后,其他元素绝对值的∞次方相对于它而言趋近于0,所以再进行开方后,得到的就是 |x|max。即:
    $$
    |x|{∞}=|x|
    $$

同一组向量,随着P值的增加,范数值趋向于该向量元素中的最大值,图形更加饱满;相反,随着P值的减小,曲线贴近坐标轴,最极端的情况,L0表示向量元素的个数,就在依附在坐标轴上进行表示。

范数正则化的处理就是向量中每个元素除以向量的范数。若存在范数公式norm(x),则范数正则化变化公式为:
$$
\overline{x}{i(k)}=\frac{x{i(k)}}{norm(x)}
$$
对x拔进行相应的范数计算,得到其值为1,因为分母本身就是一个“常数”。

# numpy用于生成缺失值,引用sklearn.preprocessing库,其中有绝大部分的数据预处理方法
from sklearn import preprocessing
import numpy as np
# 原始数据X
X=np.array([[3,-2.,2.],[2.,0.,0.]])
print('原始数据:\n')
print(X)
'''
[[ 3. -2.  2.]
 [ 2.  0.  0.]]
'''
# 范数规范化(按行向量计算)的函数化实现,norm可选参数为l1,l2,max
X_norm=preprocessing.normalize(X,norm='l2')
print(X_norm)
'''
[[ 0.72760688 -0.48507125  0.48507125]
 [ 1.          0.          0.        ]]
'''
# 范数规范化的类实现,norm可选参数为l1,l2,max
normalizer=preprocessing.Normalizer(norm='l1').fit(X)
print(normalizer.transform(X))
'''
[[ 0.42857143 -0.28571429  0.28571429]
 [ 1.          0.          0.        ]]
'''

我们需要范数规则化的原因:监督机器学习用一句话总结就是:Minimize your error while regularizing your parameters。其中“minimize error”是目的,一般采用最小化损失函数的做法来达到。
“regularizing parameters”就是一种保障,它可以防止模型发生过拟合,让模型的参数规模尽量向着“简单”的方向进化。(根据奥卡姆剃刀原理 Occam’s Razor,简单的模型虽然不尽准确,却也有更好的泛化能力。)

posted @ 2022-12-18 23:08  努力生活的小林  阅读(134)  评论(0)    收藏  举报