第6章 学习模型评估和超参数调优的最佳实践

Python Alan 1个月前 (04-25) 138次浏览 0个评论 扫描二维码

其它章节内容请见机器学习之PyTorch和Scikit-Learn

在前面的章节中,我们学习了用于分类的基本机器学习算法以及如何在喂给这些算法前处理好数据。下面该学习通过调优算法和评估模型表现来构建良好机器学习模型的最佳实践了。本章中,我们将学习如下内容:

  • 评估机器学习模型表现
  • 诊断机器学习算法常见问题
  • 调优机器学习模型
  • 使用不同的性能指标评估预测模型

通过管道流程化工作流

在前面章节中应用不同预处理技术时,比如第4章 构建优秀的训练数据集 – 数据预处理中用于特征缩放的标准化,或第5章 通过降维压缩数据中用于数据压缩的主成分分析,我们学习到需要复用在拟合训练数据缩放及压缩新数据时的参数,比如对分离测试集中的样本。本节中,我们会学习极其趁手的工具,scikit-learn中的Pipeline类。它让我们可以拟合包含任意数量变换步骤的模型并将其用于预测新数据。

加载威斯康星州乳腺癌数据集

本章中,我们会使用威斯康星州乳腺癌数据集,包含有569个恶性和良性肿瘤细胞的样本。数据集中的前两列分别存储样本的唯一ID和对应的诊断结果(M = 恶性, B = 良性)。3-32列包含30个通过细胞核数字化图像计算的真实值特征,可用于构建模型预测肿瘤是良性的还是恶性的。威斯康星州乳腺癌数据集存储在UCI机器学习库中,有关数据集更详细的信息请见https://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+(Diagnostic)

获取威斯康星州乳腺癌数据集

可以在本书代码库中找到一份本数据集(以及本书中使用的其它数据集)的拷贝,以防读者离线操作或是UCI服务器上https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data临时掉线。比如我们从本地目录加载数据集,只需将如下行:

替换为:

本节中,我们会读取数据集并分3步将其分成训练集和测试集:

  1. 我们先使用pandas直接从UCI网站上读取数据数据集:
  2. 接着将30个特征赋给NumPy数组X。使用对象LabelEncoder,我们将原字符串形式的类标签('M' 和 'B')转换成整数:
  3. 在将类标签(诊断结果)编码到数组y中后,恶性肿瘤以类1进行表示,良性肿瘤以类0进行表示。可以对手动添加的类标签调用拟合的LabelEncoder中的transform方法进行映射的验证:
  4. 在进入下一小节构建第一个模型管道前,我们将数据集分成训练集(占数据的80%)和单独的测试集(占数据的20%):

在管道中组合转换器和评估器

在前一章中,我们学习很多学习算法要求输入特征处于同一量级来获取最优性能。因威斯康星州乳腺癌数据集用不同的量级度量,我们要在将其喂给逻辑回归等线性分类器前标准化其中的列。此外,我们假定通过主成分分析(PCA)将数据由初始的30维压缩到二维子空间,这一用于维度下降的特征提取技术在第5章中进行过介绍。

这里就不再单独对训练集和测试集做模型拟合与数据变换了,而是在管道中串起StandardScalerPCALogisticRegression对象:

make_pipeline函数可接收任意数量的scikit-learn转换器(支持fittransform方法作为输入的对象),后接实现了fitpredict方法的scikit-learn评估器。在前面的示例代码中,我们提供了两个scikit-learn转换器StandardScalerPCA,另有一个LogisticRegression评估器作为make_pipeline函数的输入,通过这些对象构建了一个scikit-learn Pipeline对象。

可以把scikit-learn的Pipeline看成是元估计器或对独立转换器和评估器的封装。如果调用Pipelinefit方法,数据会通过对中间步骤调用fittransform传递给一系列转换器,直至到达估计器对象(管道中的最后一个元素)。然后评估器对变换后的训练数据进行拟合。

上例中对pipe_lr管道执行fit方法时,首先StandardScaler对训练数据调用fittransform。然后将变换后的数据传入管道中的下一个对象PCA。类似前一步,PCA也对缩放后的输入数据执行fittransform,然后传递给管道中的最一个元素,估计器。

最后,训练数据在经过StandardScalerPCA的变换后使用LogisticRegression估计器进行拟合。同样,我们应注意对管道的中间步数是没有限制的,如果希望使用管道做预测任务,最后一个管道元素就要是估计器。

与对管道调用fit相似,如果其最后一步是估计器的话管道还实现了一个predict方法。如果将数据集喂给Pipeline对象实例的predict调用,数据会通过transform调用在中间步骤间传递。最后一步,估计器会返回对变换数据的预测。

scikit-learn库的管道是很强大的封装工具,在本书后续会频繁使用到。为确保诸君很好地掌握了Pipeline对象原理,请仔细看图6.1,图中总结 了前面的讨论:

第6章 学习模型评估和超参数调优的最佳实践

图6.1:管道对象的内部工作原理

使用k-折交叉难评估模型表现

本节中,我们会学习常见的交叉验证技术holdout交叉验证和k-折交叉验证,可帮助我们获取模型泛化表现可靠的预估,也就是对未见数据的模型表现。

holdout方法

评估机器学习模型泛化性能的经典且流行的方式是holdout方法。使用holdout方法,我们将初始数据集分成训练集和测试集,前者用于模型训练,后者用于评估泛化性能。但,在典型的机器学习应用中,我们还会调优和比较不同的参数配置以进一步提升未见数据预测的表现。这一过程称为模型选择,这一名称是指我们对给定分类问题我们希望选择调优参数(超参数)最优值。但如果在模型选择期间反复地复用同一测试集,那它就会变成训练集的一部分,进而模型更有可能变得过拟合。虽然有这问题,很多人仍会使用测试集来做模型实践,这并不是一种好的机器学习实践。

使用holdout方法做模型选择更好的方式是将数据分成三个部分:训练集、验证集和测试集。训练集用于拟合不同模型,然后使用验证集的表现做模型选择。测试集的优势是模型在训练和模型选择阶段未见过这部分数据,这样所获取的对新数据的泛化能力的评估偏置更小。图6.2描绘了holdout交叉难的概念,其中我们在通过不同超参数值训练后使用验证集反复评估模型的表现。一旦对超参数值达到满意程度,就开始对测试集评估模型泛化表现:

第6章 学习模型评估和超参数调优的最佳实践

图6.2:如何使用训练集、验证集和测试集

holdout方法的一个缺点是其性能评估对如何将训练数据集分成训练和验证子集极其敏感,评估随数据样本的不同而不同。在下一小节中,我们会学习更健壮的性能评估技术,k-折交叉验证,其中我们对训练数据的k个子集重复k次holdout方法。

K-折交叉验证

在k-折交叉验证中,我们随机将训练数据集无放回的分成k部分。此处的 k – 1 个折叠,也称为训练折叠,用于做模型训练,而1折叠又称为测试折叠,用于做性能评估。重复这一流程k次,这们便获取了k个模型及性能评估。

有放回和无放回采样

我们在第3章中学习过一个演示有放回和无放回采样的示例。如果读者尚未读取该章或是要复习一下,请参阅通过随机森林组合多个决策树一节中的有放回和无放回抽样补充信息。

然后我们根据不同的独立折叠获取相对holdout方法对测试数据子区分区不那么敏感的性能评估,计算模型的平均性能。通常我们使用k-折交叉验证做模型调优,也即找到产生满意泛化性能的最优超参数值,通过对测试折叠评估模型表现来进行评估。

一旦找到满意的超参数值,就可以对完整训练数据集重新训练模型并使用独立测试数据集获取最终的性能评估。在k-折交叉验证后对整个训练数据集拟合模型的根本原因是,首先我们一般感兴趣的是单个最终模型(对比k个单独模型),其次提供更多的训练样本来学习算法一般能产生更精确、健壮的模型。

因k-折交叉验证是一种无放回的重采样技术,其优势是在每次迭代中,每个样本都只会用到一次,训练和测试部分是无关的。此外,所有测试折叠之间也不相关,也即测试折叠没有重叠部分。图6.3总结了k = 10的k-折交叉验证概念。训练数据集划分为10个部分,在10次迭代中,9个部分用于训练,一个部分用作模型评估的训练数据集。

并且每个部分的预估性能Ei (比如,分类准确度或错误率)之后用于计算评估模型的平均性能E

第6章 学习模型评估和超参数调优的最佳实践

图6.3:k-折交叉验证的原理

总之,k-折交叉验证通过校验集比holdout方法更好地使用了数据集,因为k-折交叉验证中所有的数据点都用于了评估。

按经验k-折交叉验证中k的一个很好的标准值是10。比如Ron Kohavi 对真实数据集和各种实验表明10-折交叉验证在偏置和方差间做了最好的权衡(A Study of Cross-Validation and Bootstrap for Accuracy Estimation and Model Selection by Kohavi, RonInternational Joint Conference on Artificial Intelligence (IJCAI), 14 (12): 1137-43, 1995,https://www.ijcai.org/Proceedings/95-2/Papers/016.pdf)。

但如果处理的是相对较小的数据集,增加折叠次数会很有用。如果我们增加k的值,每次迭代中会使用更多的训练数据,这会通过平分独立模型评估的泛化性能来逐步降低悲观偏差。但更大的k值也会增加交叉验证算法的运行时并产生更高方差的评估,因为训练折叠彼此更相近。另一方面,如果我们处理的是大数据集,可以选择较小的k值,比如k = 5,这样减少了重复拟合和评估不同折叠模型评估的计算成本,但仍能获得模型平均性能的精准评估。

留一法交叉验证

k-折交叉验证的一种特殊情况是留一法交叉验证 (LOOCV。在留一法中,我们会将折叠数量设置为与训练样本数相同(k = n) ,这样每次迭代只有一个训练样本用于测试,在处理非常小的数据集时推荐此法。

对标准k-折交叉验证做部分改进的方法是分层k-折交叉验证,它会产生更好的偏置和方差预估,尤其是针对非均分类,在前面引用的Ron Kohavi论文中也进行了研究。在分层交叉验证中,每个折叠中保存类标签占比以确保各折叠能够代码训练数据集中的类占比,我们使用scikit-learn中的StratifiedKFold迭代器进行讲解:

首先我们使用训练集中的y_train类标签初始化了sklearn.model_selection模块中的StratifiedKFold迭代器,并通过n_splits参数指定了折叠的数量。在使用kfold迭代器遍历k折叠时,我们使用返回的train中的索引拟合本章一开始配置的逻辑回归管道。使用pipe_lr管道,我们保证了每次迭代中样本进行了适当的缩放(比如做标准化)。然后使用test索引来计算模型的精确度打分,用采集的scores列表计算平均准确度和预估的标准差。

虽然以上的示例代码对于讲解k-折交叉验证原理很有用,实际上scikit-learn还实现了一个k-折交叉验证打分器,可更简洁地使用分层k-折交叉验证评估模型:

cross_val_score方法极其有用的特性是可以将不同折叠的评估分发到主机的多个中央处理单元(CPU)上。如果将n_jobs参数设置为1,仅有一个CPU用于评估表现,和前面的StratifiedKFold示例一样。但设置n_jobs=2时,可以将10轮交叉验证分发到两个CPU上(前提是主机上有两个两个CPU),设置n_jobs=-1,会使用主机上的所有可用CPU来进行并行计算。

评估泛化表现

有关泛化表现的方差如何评估交叉验证的详细讨论不在本书范畴内,但读者可参考有关模型评估和交叉验证更全面的文章(Model Evaluation, Model Selection, and Algorithm Selection in Machine Learning by S. Raschka),在https://arxiv.org/abs/1811.12808上有分享。这篇文章讨论了多种交叉验证技术,比如.632和.632+自举重采样交叉验证(bootstrap cross-validation)法。

此外,可在https://www.jmlr.org/papers/v6/markatou05a.html阅读M. Markatou与其他人合著的优秀文章(Analysis of Variance of Cross-validation Estimators of the Generalization Error by M. MarkatouH. TianS. Biswas, and G. M. HripcsakJournal of Machine Learning Research, 6: 1127-1168, 2005)。

使用学习曲线和验证曲线调试算法

本节中,我们来看两个非常简单但强大的诊断工具,可帮助我们提升学习算法的性能:学习曲线和验证曲线,在接下的小节中,我们会讨论如何使用学习曲线诊断学习算法是否有过拟合(高方差)或欠拟合(高偏置)的问题。另外,我们还会学习验证曲线,它辅助我们处理学习算法中的常见问题。

通过学习曲线诊断偏置和方差问题

如果模型对给定训练数据集过于复杂,比如非常深的决策树,模型会倾向于过度拟合训练数据,但对未见数据的泛化不佳。通常,收集更多的训练样本可降低过拟合的程度。

但在实践中,采集更多数据通常很昂贵或根本就不可行。通过将模型训练和验证精度按训练数据集大小的函数进行绘制,我们可以轻易地监测出模型是否存在高方差或高偏置,以及收集更多的数据是否有助于解决这一问题。

但在讨论如何在scikit-learn中绘制学习曲线之前,我们通过下图先讨论这两种常见的模型问题:

第6章 学习模型评估和超参数调优的最佳实践

图6.4:常见模型问题

左上角的图显示模型有高偏置。这个模型的训练精度和交叉验证精度都低,说明对训练数据是欠拟合的。处理这一问题的常见方式是增加模型参数,比如通过采集或构建更多的特征或在支持向量机(SVM) 或逻辑回归分类器中降低正则化程度。

右上角的图显示模型存在高方差问题,可通过训练精度和交叉验证精度之间的大间隔看出。要解决过拟合问题,我们可以收集更多的训练数据,减少模型的复杂度或增加正则化参数。

对于非正则化模型,可以通过特征选择(第4章)或特征提取(第5章)来降低过拟合程序来减少特征数。虽然采集更多的训练数据通常能降低过拟合的机率,但并不总是有效,比如,如果训练数据噪音极高或是模型已经非常接近最优。

在下面的小节中,我们会学习如何使用验证曲线来处理这些模型,但我们先来学习如何使用scikit-learn中的学习曲线函数来评估模型:

注意我们在实例化LogisticRegression对象(默认迭代1000次)时传递了一个额外参数max_iter=10000,来避免小数据集或极端正则化参数值的收敛问题(在下一节中会讲解)。成功执行以上代码后,会获得如下的学习曲线图:

第6章 学习模型评估和超参数调优的最佳实践

图6.5:展示训练和验证数据集精度对训练样本数的学习曲线

通过learning_curve函数中的train_sizes参数,我们可以控制用于生成学习曲线的训练样本绝对或相对数。这里,我们设置train_sizes=np.linspace(0.1, 1.0, 10)来使用对训练数据集大小做10个空间上均分的相对间隔。默认,learning_curve函数使用分层k-折交叉验证计算分类器的交叉验证精度,我们通过cv参数设置k = 10用于10-折分层交叉验证。

然后,我们通过返回的训练数据集不同大小的交叉验证训练和测试分数来计算平均精度,使用Matplotlib的plot函数进行了绘制。此外,我们添加了平均精度的标准差绘图,使用fill_between函数来表明评估的方差。

在上面的学习曲线图中可以看出,在训练时如果250个以上的样本时模型对训练和验证数据集的表现都很好。还可以看到样本小于250个时训练数据集的训练精度会上升,验证精度和训练精度的间隔会拉大,这表示过拟合的程度在上升。

处理验证曲线的过拟合和欠拟合

验证曲线是通过解决过拟合或欠拟合问题提升模型表现很有用的工具。验证曲线与学习曲线相关,但它绘制的是样本大小对训练和测试精度的函数,我们变化模型参数的值,比如逻辑回归中的逆向正则化参数C

我们下面学习如何通过scikit-learn创建验证曲线:

使用以上的代码,我们获取参数C的验证曲线图:

第6章 学习模型评估和超参数调优的最佳实践

图6.6: SVM超参数C的验证曲线图

类似于learning_curve函数,validation_curve函数默认使用分层k-折交叉验证来评估分类器的表现。在validation_curve函数内,我们指定待评估的参数。本例中为CLogisticRegression分类器的逆向正则化参数,写作logisticregression__C,用于访问s通过param_range参数设置的指定范围值 scikit-learn管道中的LogisticRegression对象。类似前一节中的学习曲线示例,我们绘制了训练和交叉验证精度与相关的标准差。

虽然不同C值的精度判别分别小,我们还是可以看出在增加正则化强度(小C值)时模型会有轻微的欠拟合。但在C为大值时,就表示降低正则化强度,因此模型会趋向对数据轻微的过拟合。本例中,最佳点位于C值在0.1和1.0之间。

通过网格搜索调优机器学习模型

 

第6章 学习模型评估和超参数调优的最佳实践

翻译整理中…

喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址