在前面我们分别讨论了特征工程中的特征选择与特征表达, 本文我们来讨论特征预处理的相关问题. 主要包括特征的归一化和标准化, 异常特征样本清洗与样本数据不平衡问题的处理.
1. 特征的标准化和归一化
由于标准化和归一化这两个词经常混用, 所以本文不再区别标准化和归一化, 而通过具体的标准化和归一化方法来区别具体的预处理操作.
z-score 标准化: 这是最常见的特征预处理方式, 基本所有的线性模型在拟合的时候都会做 z-score 标准化. 具体的方法是求出样本特征 x 的均值 mean 和标准差 std, 然后用(x-mean)/std 来代替原特征. 这样特征就变成了均值为 0, 方差为 1 了. 在 sklearn 中, 我们可以用 StandardScaler 来做 z-score 标准化. 当然, 如果我们是用 pandas 做数据预处理, 可以自己在数据框里面减去均值, 再除以方差, 自己做 z-score 标准化.
max-min 标准化: 也称为离差标准化, 预处理后使特征值映射到 [0,1] 之间. 具体的方法是求出样本特征 x 的最大值 max 和最小值 min, 然后用 (x-min)/(max-min) 来代替原特征. 如果我们希望将数据映射到任意一个区间[a,b], 而不是[0,1], 那么也很简单. 用(x-min)(b-a)/(max-min)+a 来代替原特征即可. 在 sklearn 中, 我们可以用 MinMaxScaler 来做 max-min 标准化. 这种方法的问题就是如果测试集或者预测数据里的特征有小于 min, 或者大于 max 的数据, 会导致 max 和 min 发生变化, 需要重新计算. 所以实际算法中, 除非你对特征的取值区间有需求, 否则 max-min 标准化没有 z-score 标准化好用.
L1/L2 范数标准化: 如果我们只是为了统一量纲, 那么通过 L2 范数整体标准化也是可以的, 具体方法是求出每个样本特征向量 $\vec{x}$ 的 L2 范数 $||\vec{x}||_2$, 然后用 $\vec{x}/||\vec{x}||_2$ 代替原样本特征即可. 当然 L1 范数标准化也是可以的, 即用 $\vec{x}/||\vec{x}||_1$ 代替原样本特征. 通常情况下, 范数标准化首选 L2 范数标准化. 在 sklearn 中, 我们可以用 Normalizer 来做 L1/L2 范数标准化.
此外, 经常我们还会用到中心化, 主要是在 PCA 降维的时候, 此时我们求出特征 x 的平均值 mean 后, 用 x-mean 代替原特征, 也就是特征的均值变成了 0, 但是方差并不改变. 这个很好理解, 因为 PCA 就是依赖方差来降维的, 如果我们做了 z-score 标准化, 所以特征的方差为 1, 那么就没法来降维了.
虽然大部分机器学习模型都需要做标准化和归一化, 也有不少模型可以不做做标准化和归一化, 主要是基于概率分布的模型, 比如决策树大家族的 CART, 随机森林等. 当然此时使用标准化也是可以的, 大多数情况下对模型的泛化能力也有改进.
2. 异常特征样本清洗
我们在实际项目中拿到的数据往往有不少异常数据, 有时候不筛选出这些异常数据很可能让我们后面的数据分析模型有很大的偏差. 那么如果我们没有专业知识, 如何筛选出这些异常特征样本呢? 常用的方法有两种.
第一种是聚类, 比如我们可以用 KMeans 聚类将训练样本分成若干个簇, 如果某一个簇里的样本数很少, 而且簇质心和其他所有的簇都很远, 那么这个簇里面的样本极有可能是异常特征样本了. 我们可以将其从训练集过滤掉.
第二种是异常点检测方法, 主要是使用 iForest 或者 one class SVM, 使用异常点检测的机器学习算法来过滤所有的异常点.
当然, 某些筛选出来的异常样本是否真的是不需要的异常特征样本, 最好找懂业务的再确认一下, 防止我们将正常的样本过滤掉了.
3. 处理不平衡数据
这个问题其实不算特征预处理的部分, 不过其实它的实质还是训练集中各个类别的样本的特征分布不一致的问题, 所以这里我们一起讲.
我们做分类算法训练时, 如果训练集里的各个类别的样本数量不是大约相同的比例, 就需要处理样本不平衡问题. 也许你会说, 不处理会怎么样呢? 如果不处理, 那么拟合出来的模型对于训练集中少样本的类别泛化能力会很差. 举个例子, 我们是一个二分类问题, 如果训练集里 A 类别样本占 90%,B 类别样本占 10%. 而测试集里 A 类别样本占 50%, B 类别样本占 50%, 如果不考虑类别不平衡问题, 训练出来的模型对于类别 B 的预测准确率会很低, 甚至低于 50%.
如何解决这个问题呢? 一般是两种方法: 权重法或者采样法.
权重法是比较简单的方法, 我们可以对训练集里的每个类别加一个权重 class weight. 如果该类别的样本数多, 那么它的权重就低, 反之则权重就高. 如果更细致点, 我们还可以对每个样本加权重 sample weight, 思路和类别权重也是一样, 即样本数多的类别样本权重低, 反之样本权重高. sklearn 中, 绝大多数分类算法都有 class weight 和 sample weight 可以使用.
如果权重法做了以后发现预测效果还不好, 可以考虑采样法.
采样法常用的也有两种思路, 一种是对类别样本数多的样本做子采样, 比如训练集里 A 类别样本占 90%,B 类别样本占 10%. 那么我们可以对 A 类的样本子采样, 直到子采样得到的 A 类样本数和 B 类别现有样本一致为止, 这样我们就只用子采样得到的 A 类样本数和 B 类现有样本一起做训练集拟合模型. 第二种思路是对类别样本数少的样本做过采样, 还是上面的例子, 我们对 B 类别的样本做过采样, 直到过采样得到的 B 类别样本数加上 B 类别原来样本一起和 A 类样本数一致, 最后再去拟合模型.
上述两种常用的采样法很简单, 但是都有个问题, 就是采样后改变了训练集的分布, 可能导致泛化能力差. 所以有的算法就通过其他方法来避免这个问题, 比如 SMOTE 算法通过人工合成的方法来生成少类别的样本. 方法也很简单, 对于某一个缺少样本的类别, 它会随机找出几个该类别的样本, 再找出最靠近这些样本的若干个该类别样本, 组成一个候选合成集合, 然后在这个集合中不停的选择距离较近的两个样本, 在这两个样本之间, 比如中点, 构造一个新的该类别样本. 举个例子, 比如该类别的候选合成集合有两个样本 $(x_1,y),(x_2,y)$, 那么 SMOTE 采样后, 可以得到一个新的训练样本 $(\frac{x_1+x_2}{2},y)$, 通过这种方法, 我们可以得到不改变训练集分布的新样本, 让训练集中各个类别的样本数趋于平衡. 我们可以用 imbalance-learn 这个 Python 库中的 SMOTEENN 类来做 SMOTE 采样.
4. 结语
特征工程系列终于写完了, 这个系列的知识比较零散, 更偏向工程方法, 所以不像算法那么紧凑, 写的也不是很好, 希望大家批评指正. 如果有其他好的特征工程方法需要补充的, 欢迎留言评论.
来源: https://www.cnblogs.com/pinard/p/9093890.html