
后台经常有同学问西瓜书第二章的题怎么做尤其是“周志华版机器学习第二章习题”这组内容很多人刷到一半就卡住了。模型评估与选择这一章整本书里最容易被低估它表面上讲的都是“切数据、算指标、做检验”这些零碎功夫实际上决定了你以后写实验对比、调模型、打比赛时的基本判断力。这篇文章不打算把习题答案抄一遍给你那没有意义而是把这章每类题目背后真正考的东西掰开讲明白再给你一些我自己刷题、带新人时常用的判断方法。我也不知道你们是从哪一届开始传的反正每年都有人拿着一份手写答案到处对对完之后发现换个数字还是不会做。问题不在计算在理解。评估方法、性能度量、统计检验、偏差方差分解这四个模块看着独立其实是一条完整的逻辑链先把数据怎么分搞清楚再用一套指标告诉你好不好最后用统计方法确认差异是真有还是偶然最后从理论上解释为什么会有这些现象。这条链子想通了第二章的题基本就通了。1. 先弄懂这章的主线评估方法、性能度量、比较检验1.1 为什么第二章是全书的分水岭机器学习的所有任务本质上都是“在有限数据上找一个尽可能逼近真实规律的模型”。但是你怎么知道一个模型逼近了没有这就需要一个统一的裁判体系。第二章干的就是这件事它不教你任何一个具体算法却定了所有算法用来被比较的规矩。很多同学学到后面神经网络、支持向量机、集成学习都能跑起来回头才发现自己对“训练集和测试集怎么分”“AUC到底在算什么”这些问题还是一笔糊涂账这就是第二章没吃透的表现。我见过不少真实案例某同学做实验对比两个模型直接拿全部数据训练然后说准确率98%一上测试集就崩。这不是模型的问题是评估协议的问题。第二章所有的习题本质上都在训练你一种“实验素养”你的结论必须建立在可信的评估流程之上而不是一个漂亮的数字上。所以不管你后边学什么算法这一章的思维方式都像地基一样挡在前面。1.2 三条主线之间的逻辑关系教材里第二章内容看着杂其实就三个模块。第一评估方法留出法、交叉验证法、自助法回答的是“数据怎么划分才是可信的”。第二性能度量错误率、精度、查准率、查全率、F1、ROC与AUC回答的是“模型好不好按什么标准算好”。第三比较检验假设检验、t检验、McNemar检验、Friedman检验回答的是“两个模型之间的差异到底是真差异还是随机波动”。这三条线有严格的先后关系。你先把数据切好然后在切好的数据上计算指标指标值有高有低后再用统计检验判断高低差异是否显著。习题也是按这个顺序出题的。很多人做题只盯着单个公式算不把这三步串起来结果做评估方法的题时不知道为什么要用性能度量做统计检验的题时不知道前面的指标是怎么参与计算的。建议你心里先挂好这张地图再往下看具体题目思路会清晰很多。2. 评估方法类习题数据划分的学问2.1 留出法分层采样不是一句空话留出法最简单直接把数据集分成训练集和测试集。但这里有个最重要的考点划分时要保持类别比例。比如数据集中正例占60%负例占40%那么训练集和测试集都应该大致维持这个比例这叫分层采样。很多同学会忽略为什么要分层。直观理解就是如果你为了让训练集大一点而不分层测试集里可能就只剩某一类样本了模型评估结果就会严重失真。举个例子100个样本里有90个正例、10个负例你随机切一刀如果测试集恰好全是负例那模型在测试集上算出来的准确率再高也说明不了问题因为它根本没见到足够多的正例。分层采样就是为了避免这种极端情况。习题里经常让你评估不同划分比例下的错误率其实考点就是训练集越大模型学得越好但测试集太小评估结果不可靠训练集太小模型学不充分评估值又会偏差。留出法的一个明显局限是它只切一次结果依赖于那一次特定的划分方差比较大。我个人的习惯是做留出法实验时固定随机种子然后至少重复划分十次取平均值。这样能缓解单次划分带来的随机性。但更严格的实验教材里给的答案其实是走交叉验证那套这也是为什么习题里经常把两种方法放在一起比较的原因。2.2 交叉验证法与自助法什么时候用哪个交叉验证法的核心思想是“多次划分、轮流当测试集”。最常用的是10折交叉验证把数据分成10份每次拿9份训练、1份测试轮10次最后把10次的结果平均。这样每个样本都恰好被当过一次测试样本评估结果比单次留出法稳定得多。有一种特殊情况叫留一法也就是k等于样本数每轮只留一个样本测试。留一法的特点是评估结果几乎无偏但计算开销极大而且当数据量大的时候单一样本的测试结果波动很大实际中不一定划算。这一点在习题里经常被拿出来比较你要理解“无偏”和“方差大”这两个词的含义回答时才不会只写表面话。自助法又是另一套路数它通过有放回采样生成训练集。意思是假设原始数据集有m个样本每次从里面随机抽一个抽完放回去再抽下一个重复m次得到一个m个样本的训练集。这个训练集会包含一些重复样本那些没被抽中的样本就组成测试集。有个经典结论当m足够大时某个样本始终没被抽中的概率大约是0.368也就是大约36.8%的样本会留在测试集里这叫袋外样本估计。自助法适合数据量小的时候。因为交叉验证和留出法在数据太少时训练集更小模型没法好好学。自助法通过有放回采样相当于在有限数据上制造了多个不同的训练集让模型能多学几轮。但代价也明显改变了原始数据分布引入了估计偏差。所以习题里如果问“数据量充足时该选什么方法”答案基本都是留出法或交叉验证法自助法留给小数据场景。2.3 评估方法题目里容易忽略的三个细节第一个细节是“训练集和测试集必须互斥”。这是原则问题如果测试集里的样本在训练集里出现过评估结果就是虚高学术上叫数据泄漏。习题里给的场景可能比较隐蔽比如让你判断某个数据划分方式能不能直接用考的就是这个互斥性原则。第二个细节是“随机种子对结果的影响”。同样一份数据用不同的随机种子划分两次实验的准确率可能差好几个点。很多人在做习题或者实验报告时没有固定随机种子结果换台电脑跑就不一样了。我一般在实验代码里把随机种子设成一个固定值比如42保证实验可复现。这个问题在后续做算法对比实验时尤其重要审稿人和面试官特别看重可复现性。第三个细节是“评估过程的重复次数”。教材里讲留出法时特别强调要多次随机划分取平均很多同学做得不彻底只跑一两次就当结论。严格一点的做法是重复100次甚至更多次计算平均性能和标准差。这样得到的结果才具有统计意义也才能作为后续统计检验的输入数据。3. 性能度量类习题指标背后是业务语言3.1 错误率与精度最常用的指标也有坑错误率和精度非常好理解一个是被分错的样本占比一个是分对的样本占比两者加起来等于1。习题里常见的计算题给定一个测试集和模型的预测结果直接统计就行。但这俩指标最大的问题是在类别不平衡的场景下会骗人。比如99%的样本都是负例模型全部预测成负例精度是99%看起来特别漂亮实际上这个模型什么也没学会。这种“虚假的高精度”在教材后面讲代价敏感学习、查准率查全率时会被反复提到。所以习题里一旦涉及“在类别不平衡数据上能不能用精度来评估”你要立刻反应过来应该用查准率、查全率或者AUC这些更鲁棒的指标。3.2 查准率、查全率与F1怎么在两组指标之间权衡查准率和查全率的定义很多同学背得滚瓜烂熟查准率是预测为正例的样本中有多少是真正的正例查全率是所有真实正例中有多少被找出来了。但一碰到具体计算尤其是用混淆矩阵来算的时候就经常搞混分母。我讲的时候喜欢用一个例子把正例想象成“你真正想找的东西”比如垃圾邮件检测里正例就是垃圾邮件。查准率回答的问题是“我拦下来的邮件里有多少真的是垃圾邮件”如果误杀太多正常的邮件查准率就低。查全率回答的问题是“所有垃圾邮件里我拦下来多少”如果漏掉很多垃圾邮件查全率就低。你要拦住所有垃圾邮件最简单的方法是把所有邮件都拦下来查全率是100%但查准率会非常低因为大量正常邮件被误判成了垃圾邮件。这个权衡就是题目背后真正想考的东西。F1是查准率和查全率的调和平均公式是2×查准率×查全率/(查准率查全率)。为什么要用调和平均而不是算术平均因为调和平均对低值更敏感它要求两个指标同时都高F1才高避免了一个极高一个极低时平均分虚高的情况。很多习题会让你根据两个不同模型的P和R计算F1然后判断哪个模型表现更好核心就是理解这个权衡逻辑。还有一种加权F1通过给查准率或查全率加权重来体现业务偏好比如在医疗诊断场景漏诊的代价比误诊高那就给查全率更高的权重。3.3 ROC曲线与AUC一个不用太在意阈值的选择ROC曲线和AUC是第二章里比较抽象的一部分。很多同学第一次看完只知道ROC曲线要画一条曲线曲线下面积叫AUC但不知道它到底解决什么问题。ROC曲线的横轴是假正例率纵轴是真正例率。它刻画的是模型在不同阈值下的表现。以二分类为例模型输出的通常是一个概率或分数大于阈值判为正例小于阈值判为负例。你把阈值从最高分往最低分挪每挪一步都能算出一对假正例率和真正例率把这些点连起来就得到了ROC曲线。想明白这一点你就会理解ROC曲线实际上把“所有可能阈值”的性能都画出来了因此它不依赖于某一个特定的阈值。AUC就是这条曲线下的面积。由于曲线在最理想的情况下是贴着左上角走的所以AUC越接近1说明模型把正例排在负例前面的能力越强。这个说法很多教材都有但我补一句更直观的AUC本质上衡量的是“随机抽一个正例、一个负例模型给正例打分的概率高于给负例打分的概率”。这一点在很多习题里是解题关键比如让你比较两个模型的AUC实际上就是在比较它们对样本排序质量的好坏。计算AUC时习题里经常给一些简化场景让你手动算面积。常规思路是先把样本按预测分数排序然后从高到低改变阈值记录真正例个数和假正例个数的累积变化最后画出折线算面积。还有一个简化的排序公式AUC等于所有正例排位之和减去正例数乘以(正例数加1)除以2的差值再除以正例数乘负例数。这个公式推导起来有点绕但做题时能省不少时间建议自己动手推一遍印象会深得多。3.4 代价敏感错误率比错误率更贴近真实业务前面讲的错误率默认每个错误分类的代价都一样。但实际中不是这样。教材里用了一个很经典的场景医疗诊断中把癌症患者误判为健康人和把健康人误判为癌症患者这两类错误的代价完全不同。误判健康人做进一步检查还能承受漏诊癌症患者可能就会出大事。代价敏感错误率的公式把两类错误分别乘上对应的代价权重再做归一化。习题里通常会给出一个代价矩阵让你计算给定预测结果下的代价敏感错误率。很多人做这类题算出来的数字总是怪怪的原因是混淆矩阵里的四个格子和代价矩阵的四个代价没对齐。这里教大家一个笨但可靠的办法先把混淆矩阵每个格子的含义用文字写清楚比如“真实为正、预测为负”再在代价矩阵里找到对应的代价相乘累加。别跳步就不会错。理解了代价敏感你回头看前面查准率和查全率就会有更深的感觉不同业务对错分类的容忍度不同指标选择本质上是一种业务决策不是一个纯粹的技术选择。习题里如果问“什么时候应该用代价敏感错误率而不是普通错误率”答案就一句话当不同错误分类的代价明显不一致时。4. 统计检验类习题差异是真有还是偶然4.1 假设检验的基本逻辑先假设没有差异到了统计检验这部分很多数学基础薄弱的同学就开始头疼。其实思想非常简单就是用概率反推假设两个模型性能一样那它们在同一测试集上产生当前这种性能差异的概率有多大如果这个概率非常小小到低于显著性水平比如0.05那我们就认为“两个模型性能一样”这个假设不靠谱从而接受“它们确实有差异”。教材里用二项分布来检验错误率给了一个例子一个模型在测试集上有若干个样本分类错误问它的泛化错误率是否能控制在某个阈值以内。这里用到的逻辑就是二项分布和正态分布近似。习题里出现的计算本质上是把错误率问题转化为“在假设泛化错误率为某个值的条件下观察到当前测试错误率的概率”然后查表判断是否落进拒绝域。我建议这部分不要死记临界值重点是理解两个关键词“置信度”和“显著度”。显著性水平是你能容忍的“误判概率”通常取0.05或0.01。置信度就是1减去显著性水平。很多习题里直接给临界值查表你只要知道查出来之后怎么比较就行。4.2 交叉验证t检验与McNemar检验两种常见的比法交叉验证t检验针对的是用k折交叉验证得到的一组成对性能值。每一折里两个模型各有一个测试结果差值可以算出来然后对这个差值序列做t检验看均值和0是否有显著差异。这里最关键的坑是“成对”的概念两个模型必须在同一份划分数据上测试才能对差值做t检验。如果各自用了不同的划分差值序列就没有意义这一点习题里经常埋雷。McNemar检验针对的是单次留出法实验的结果用来判断两个模型在同一个测试集上的错误率是否有显著差异。它关注两个特别的分歧数量模型A分错但模型B分对的样本数以及模型B分错但模型A分对的样本数。如果这两个数字差异很大就说明模型之间真的有差别。公式和卡方分布挂钩做完计算后查表判断。习题里常考的是怎么从混淆矩阵提取这两个数以及卡方值的计算公式代入。4.3 多数据集比较Friedman检验与Nemenyi后续检验当对比的模型不止两个而是三个以上并且数据集也有多个时就不能两两检查了需要做整体比较。Friedman检验的做法是在每个数据集上对多个算法排序比如性能最好的排第一名次好的排第二名然后比较所有算法在所有数据集上的平均序值看这些平均序值是否显著不同。如果Friedman检验拒绝了“所有算法性能相同”的假设再通过Nemenyi后续检验找出哪些算法之间有显著差异。这类题目在习题里属于综合应用很多人做的时候容易在“序值”上算错。我建议列一张表行是数据集列是算法先把每一行单独排序再把每一列的平均序值算出来最后套公式。画临界值图那部分如果习题不做要求理解意思即可不需要死记临界值表。这部分我讲得比较快绝对不是因为它不重要而是因为它和交叉验证t检验的逻辑一脉相承你理解了“用排序替代原始性能值”这个思想剩下的就是公式代入问题了。5. 偏差与方差分解从理论上理解过拟合5.1 分解公式误差的三个来源偏差-方差分解是第二章收尾的理论重头戏。它解决的问题是一个模型在新样本上期望的泛化误差到底从哪里来教材推导的最后给出了一个经典形式泛化误差可以分解成噪声、偏差、方差三项之和。数学上推导要用一些期望运算核心是引入一个“期望预测”也就是同一个模型在无数个不同训练集上训练后对同一个测试样本预测值的平均。这个期望预测和真实标记之间的差异是偏差期望预测和每个具体预测值之间的差异波动程度是方差真实标记本身自带的随机噪声是噪声。很多同学推导到这一步会在符号上迷路我的建议是不要把每个符号都当成抽象记号而是带入具体场景。比如你多次随机划分数据训练同一个模型每个模型对同一个样本x的预测值都略有不同这些预测值的平均就是期望预测这些预测值之间的波动就是方差。波动大说明模型对训练数据的选择非常敏感这就是过拟合的典型表现。偏差大则说明模型本身的表达能力不够怎么训练都达不到真实规律这是欠拟合。5.2 分解结果对实际调参的启示偏差和方差通常是一对矛盾。模型太简单比如线性模型去拟合非线性数据偏差大方差小模型太复杂比如很深的决策树偏差能降得很小但方差变得很大换一批训练数据就完全变了个样子。这就是教材里那句“偏差-方差窘境”的来源。习题里经常会问“为什么模型复杂度增加时测试误差会先下降后上升”标准答案就是前半段偏差在下降后半段方差在上升两者相加的曲线呈现一个先降后升的碗形。这里我建议你把这张偏差方差图和交叉验证方法联系起来想为什么需要交叉验证来选择模型复杂度因为交叉验证能比较稳定地估计不同复杂度模型的泛化误差帮你找到偏差和方差平衡的那个点。后面学习正则化、集成学习时你还会反复看到这个框架比如随机森林本质上就是通过多棵树的平均来降低方差而boosting则是在逐步降低偏差。第二章里的这个理论分解其实是在给后边所有高级方法做铺垫。6. 刷题过程中最常踩的坑与实操经验6.1 三个我必须提醒的低级失误第一个失误是混淆矩阵方向搞反。查准率的分母是“预测为正例的样本数”查全率的分母是“真实为正例的样本数”每年都有人把这两个分母搞混。做题的时候别嫌麻烦先把混淆矩阵四个格子标好再代入公式。第二个失误是AUC计算时排序方式出错。按预测分数从高到低还是从低到高排直接决定累积正例和负例的计数方向。我见过好几份代码画出来的ROC曲线形状奇怪最后发现是排序排反了。建议所有涉及ROC的题目统一按预测分数从高到低处理然后一步步更新真正例和假正例的个数。第三个失误是t检验忘记检查成对条件。只要两个模型不是在完全相同的训练测试划分上得到的性能值就不能直接拿来做配对t检验。很多同学从数据集的不同随机划分各自跑了一遍实验然后把两个模型的结果拿去检验这是完全没有意义的。遇到比较检验题第一步永远是问数据划分一致吗6.2 这些知识点在真实项目里怎么用说句实话真正做项目时很少有人会完整地把假设检验流程走一遍。但我个人在带项目时最常做的事情是固定评估协议先确定数据划分方式和随机种子再确定一个主指标和一个辅助指标然后所有模型都在这套协议下跑最后用多次重复实验的标准差来判断差异是否可信。我见过太多同学调模型的时候只盯着单次测试的准确率高了就开心低了就调参完全不考虑单次结果随机性带来的波动。如果你能养成一个习惯模型每个版本跑五遍取平均你会发现自己对“某次结果比上次高0.3个百分点”这种噪声极度不敏感这会帮你省掉大量无效调参的时间。第二张这章的知识放到实际里就是这一件事用严谨的评估协议过滤掉随机噪声带来的错觉。6.3 一个额外的学习顺序建议如果你基础一般我的建议是先跳过习题里偏证明的部分比如偏差方差公式的完整推导先把留出法、交叉验证、查准率查全率、ROC、AUC、t检验这些计算类题目刷明白再回头啃推导。计算题练的是工具使用能力推导题练的是数学功底后者可以慢慢补前者是你做实验立刻就要用的。另外提醒一句做题时不要一上来就看答案。第二章的答案看起来都很短很容易造成“我看懂了”的错觉。但实际上每道题的题眼都在题干里你自己算一遍把每一步公式和数字对应上再对答案。这样即便算错你也知道自己错在哪一步。我见过太多同学对着答案点头合上之后一道题也复现不出来这纯属浪费时间。最后再分享一个我自己的操作习惯。每次拿到一份数据我不会立刻去调模型而是先花半小时把评估方案定下来划分方式选什么指标选什么要不要做统计检验。这个半小时通常能换来后面少走好几天的弯路。第二章可能不会让你写出任何炫酷的模型但这一章的思维方式会一直跟着你走到非常后面的算法设计与实验分析里。你做题做得越扎实后边越能体会到这一点。