本节主要介绍机器学习中的概念及数据的处理技术,具体包括缺失值处理、数据标准化与正则化、交叉验证、过拟合与欠拟合等。学习本节读者可以掌握不少数据处理的方法与小技巧。
在特征工程中时常会看到缺失值,而缺失值是必须要处理的,否则会影响模型的性能。缺失值的处理大致分为两大类:第一类是直接删除特征中含有缺失值的数据,这种方式比较“粗暴”,适用于训练数据量较大并且缺失数据占总数据比例不超过2%的情况,因为删除数据或多或少会带来信息的损失,在数据量较小的情况下不宜采用这种方式;第二类是通过不同的方式修复缺失值,这也是最常见的方式,可以尽量保留宝贵且有用的数据信息,特别是训练数据特别少的情况。
下面借助“泰坦尼克号乘员生存”数据集,分别讲解上面两类缺失值处理方式。由于泰坦尼克号沉没于1912年4月15日,历史比较久远,后来在进行乘员数据搜集的时候,有很多数据都是缺失的。整个数据集共有1309条记录,每条记录都弥足珍贵,因此对于缺失值需要谨慎细心地进行处理。
先看train.csv训练数据集中的数据,通过pandas加载数据,按照Survived分组并统计信息,基本信息如图2.6所示。
图2.6 基本信息
从上面的统计结果可以看出,训练数据集共有891条数据,其中“Age”列、“Cabin”列和“Embarked”列的特征都有缺失的情况。“Age”列的缺失率为19.9%,而“Cabin”列的缺失率更是高达77.1%,因此直接删除是不可行的。需要使用合理的方式对缺失值进行修复,最大限度地保留有用的信息。缺失值修复方式中,最常用的是使用均值或中位数进行填充。这里的“Age”列可以使用中位数进行填充,填充后的结果如图2.7所示。
图2.7 “Age”列的缺失值使用中位数填充
当然,也可以采用均值填充,只需要将上面代码中的median改成mean即可。除了简单地采用均值和中位数填充,还有一种比较常见的方式是用其他非空特征的数据训练一个模型,用模型预测缺失值的特征。这里的“Age”列是一个连续值,用“Age”列非空的数据训练一个回归模型,并用这个模型预测“Age”列为空的值,用预测值代替空值。这里的回归模型可以采用第1章的“薪酬预测”神经网络模型去做,也可以采用Sklearn等提供的方法进行数据的拟合。下面采用线性回归(Linear Regression)的方式拟合NaN的数据,用模型的预测值填充NaN数据。
“Cabin”列中的数据属于类别型数据,这样的数据虽然不能采用均值和中位数进行填充,但是可以借助该思想先统计非缺失值中“Cabin”列的统计信息,将出现最多的类别填充到缺失的“Cabin”列中,也可以采用非缺失值的数据训练一个分类模型,用分类模型的输出值填充缺失值。
数据的标准化与正则化是两个非常相似的概念,其目的都是对数据做相应的转换,便于提升模型的性能。接下来分别介绍数据标准化与数据正则化。
数据的标准化也叫归一化。常见的数据标准化方法有Z-Score和Min-Max。Z-Score标准化方法需要计算数据集的均值和标准差,利用均值和标准差将数据集转换为均值为0、方差为1的分布,并且转换后的数据序列无量纲,无量纲这一点很重要,因为在很多数据集中不同量纲的数据要加入一起计算,如线性回归任务中的体重预测,输入特征是身高(m)、三围(cm)、体脂率(%)、血氧浓度(mmHg)等。每个特征都有自己的量纲,而不同量纲的物理量参与到同一个 y = f ( Wx )的计算中存在解释上的困难,而且大的数据很容易左右 f ( Wx )的规则。特征数据很小的变动通过 f 规则的计算也可能产生较大的波动,影响模型的泛化能力,并且使训练变得困难,不容易收敛。
Z-Score的数学描述是,对序列 x 1 , x 2 ,…, x n 进行如下变换:
式中, , 。计算产生的新序列 y 1 , y 2 ,…, y n 服从均值为0、方差为1的分布,并且没有量纲。需要注意的是,这里除以 n -1而不是 n ,是因为使用样本对方差进行估计是有偏估计,除以 n -1是对这个偏差的修正,这个结论从数学上可以得到严格的证明。
下面使用PyTorch产生一个序列,然后用Z-Score进行归一化处理。
原始数据的可视化结果如图2.8所示。
图2.8 原始数据的可视化结果
Z-Score方法处理后的数据分布如图2.9所示。
图2.9 Z-Score方法处理后的数据分布
另一种数据标准化的常用方法是Min-Max,对序列 x 1 , x 2 ,…, x n 进行如下变换:
计算产生的新序列为 y 1 , y 2 ,…, y n ∈[0,1]且无量纲。下面仍然运用上面的二维数据进行Min-Max的标准化,然后可视化展现。
Min-Max数据标准化的结果如图2.10所示。
图2.10 Min-Max数据标准化的结果
特别容易和数据标准化混淆的概念是数据正则化,数据正则化与数据标准化最大的区别在于:数据标准化针对的是列数据,而数据正则化针对的是行数据。
为了更容易区分,这里将数据正则化叫作特征正则化,使用p-norm范数对每行数据进行相应的转换。通常 p 的取值为2。p-norm范数的计算公式为
式中, x 1 , x 2 ,…, x n 为数据的 n 个特征序列,p-norm范数实际上求的是每个特征的 p 次方之和的 p 次方根。求出每行数据的范数,用每个特征除以该范数便可以进行特征的正则化处理,计算公式为
这个公式很抽象,下面用一个简单的例子帮助读者理解特征的正则化处理。先构造一个Tensor变量,具体数据如下。
运用上面的|| x || p 公式,这里设置 p =2。
从正则化的结果来看,特征之间的差别性没有正则化之前那么大,而且正则化之后的数据无量纲。通常,进行特征的正则化处理会为模型带来性能上的提升。
模型有很多技巧,核心思想是用一部分数据训练模型,然后在另外一部分数据上进行验证,这种思想被称为交叉验证。
下面介绍3种常见的交叉验证方法。
(1)简单交叉验证(Hold-out Cross Validation),如图2.11所示。
图2.11 简单交叉验证
简单交叉验证从全部数据 S 中随机选择 s 个样本作为训练集train,剩下的数据作为测试集test。在训练集train上训练模型,并在测试集test上进行测试,预测每条测试数据的标签,最后通过预测标签和真实标签计算分类正确率。
虽然PyTorch没有提供划分测试集和训练集的方法,但是提供了Dataset接口和DataLoader接口,通过这两个接口可以实现任意数据的加载,PyTorch数据加载在后面章节会详细介绍。当然,也可以通过Sklearn的model_selection模块定义的train_test_split方法方便快捷地划分测试集和训练集。这里以“泰坦尼克号乘员生存”数据集为例,简单介绍train_test_split方法的使用。
输出结果为train_X.count:712,test_X.count:179,train_Y.count:712,test_Y.count:179。Train_test_split方法接收4个参数:特征数据X、标签数据Y、测试数据占总数据的百分比、random_state(是一个随机种子),相同的随机种子会得到相同的划分结果。
(2)K折交叉验证(K-fold Cross Validation),如图2.12所示。
图2.12 K ( K =10)折交叉验证
K折交叉验证将全部数据集划分成 K 个不相交的子集,每次从 K 个子集中选出一个作为测试集,剩下的作为验证集。K折交叉验证的好处是避免了简单交叉验证随机误差,将 K 次训练的结果取均值。从K折交叉验证执行的角度来看,共有 种划分情况。 K 种情况的平均可最大限度地避免简单交叉验证一次划分所带来的随机误差,因为简单交叉验证有可能正好将异常离群数据划分到训练集中,从而给模型性能带来影响。K折交叉验证可以最小化经验风险。
PyTorch训练过程中可以通过Sklearn提供的KFold方法进行K折交叉验证。下面仍然以“泰坦尼克号乘员生存”数据集为例简单介绍KFold方法的使用。
输出结果如下。
可以看到,使用KFold方法设置n_splits=5会产生一个迭代5次的迭代器,参数shuffle指定是否每次切分打乱数据的顺序,random_state则指定随机种子。KFold方法的split方法将在df_X数据集上运作并返回切分好的数据的索引。由索引便可到原始数据中查找真实数据,然后放入模型中进行训练。
(3)留一法(Leave-one-out Cross Validation)。留一法就是每次只留一个样本作为测试集,其他样本作为训练集,如果有 k 个样本,则需要训练 k 次,并测试 k 次。留一法的计算最烦琐,但样本利用率最高,适用于小样本的情况。
在数据科学中,有的模型效果好但是容易过拟合,有的模型效果差,学习不到数据之间的规律。本节主要介绍过拟合与欠拟合。
过拟合与欠拟合如图2.13所示。
图2.13 过拟合与欠拟合
图2.13是3个不同模型的拟合曲线,读者觉得哪个模型更好?为什么?细心的读者也许已经从图中的注解信息了解到,图2.13(a)是欠拟合的,图2.13(c)是过拟合的,只有图2.13(b)是拟合得很好的。图2.13(c)中也拟合到了每个数据点,为什么是过拟合?要解释这个问题需要引入偏差和方差的概念。前面提及,“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已”。
可以基于“特征决定机器学习的上限”这一思路来理解欠拟合。如果一个模型对数据拟合得不好,则代表模型不够强大,无法学习到数据内在的规律,在新的数据上测试会遇到“每个都认识,但是连起来就懵”的状况,不能很好地在测试集上工作,泛化能力低下。就如同情侣闹分手经常说的“你不懂我”。
由欠拟合引出一个概念,叫高偏差,它表示模型不懂数据,无法学到数据之间内在的规律,忽略数据特征。如果要理性地理解偏差的概念,可以在图2.13(a)上过每个点向拟合直线做垂线。点到拟合直线距离和的大小作为偏差的大小,仔细对比可知,图2.13(a)中点到拟合直线的距离的和最大,因此偏差最大,“最不懂数据”;图2.13(c)“最懂数据”,但是“迷失了自我”,在新数据上容易“失去自我”,导致泛化能力低下,这就是过拟合。
过拟合过度依赖训练数据,容易“近朱者赤,近墨者黑”,把训练数据中坏的特征或规律当成“真理”,把这些“真理”用到测试数据集上往往就会变成“歪理”。因此,过拟合和欠拟合都会使模型泛化能力降低。与过拟合相关联的一个概念是高方差,方差代表的是数据的离散程度,如果要理性理解方差,可以从图2.13中选出拟合曲线上的点,然后计算这些点的方差,方差越大表示越离散,拟合直线穿过的点越多,方差往往越大,越容易过拟合。
由此可知,偏差是指我们忽略了多少数据,而方差是指模型对数据的依赖程度。在建模过程中,总是在偏差和方差之间进行权衡,建立模型时,我们会尝试达到最佳平衡。偏差与方差适用于任何模型,从最简单到最复杂,是数据科学家应该理解的关键概念。