社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  机器学习算法

机器学习中的模型评估与选择

一个普普通通简简单单平平凡凡的神 • 8 年前 • 553 次点击  

1 引言

人类在对客观世界的问题进行判断时,往往都会根据过去的经验来指导自己。例如,为什么看到微湿的路面、感到和风、看到晚霞,就认为明天是好天了呢?这是因为在我们的生活经验中已经见过了很多类似的情况,头一天观察到上述的特征后,第二天天气通常会很好,为什么色泽青绿、根蒂蜷缩、敲声浊响,就能判断出是正熟的好瓜?因为我们吃过、看到过很多西瓜,所以基于色泽、根蒂、敲声这几个特征我们就可以做出相当好的判断。可以看出,我们能做出有效的判断,是因为我们已经累计了许多经验,而通过对经验的利用,就能对新的情况作出有效的决策。

那么上面对经验的利用是靠我们人类自身完成的,计算机能帮忙吗?

机器学习正是这门学科,它致力于研究如何通过计算的手段,利用经验来改善系统自身的性能。在计算机系统中,”经验“通常以”数据“的形式存在,因此,机器学习所研究的主要内容,是关于在计算机上从数据中产生”模型“(model)的算法,即“学习算法”(learning algorithm)。有了学习算法,我们把经验数据提供给它,它就能基于这些数据产生模型;在面对新的情况时(例如看到一个没有剖开的西瓜),模型会给我们提供相应的判断(例如好瓜)。如果说计算机科学是研究关于“算法”的学问,那么类似的,可以说机器学习是研究关于“学习算法”的学问。

2 模型评估与选择

2.1 经验误差与过拟合

通常我们把分类错误的样本占样本总数的比例成为“错误率” (error rate),即如果在 ​ 个样本中有 ​ 个样本分类错误,则错误率 ​;相应的,​ 称为“精度” (accuracy),即 “精度=1-错误率”。更一般的,我们把学习期的实际预测输出与样本的真实输出之间的差异成为“误差” (error),学习器在训练集上的误差称为“训练误差” (training error) 或“经验误差” (empirical error),在新样本上的误差称为“泛化误差” (generalization error)。显然,我们希望得到泛化误差最小的学习器。然而,我们事先并不知道新样本是什么样,实际能做的是努力使经验误差最小化。在很多情况下,我们可以学得一个经验误差很小、在训练集上表现很好的学习器,例如甚至对所有训练样本都分类正确,即分类错误率为零,分类精度为100%,但这是不是我们想要的学习器呢?遗憾的是,这样的学习器在多数情况下都不好。

我们实际希望的,是在新样本上能表现得很好的学习器。为了达到这个目的,应该从训练样本中尽可能学出适用于所有潜在样本的“普遍规律”,这样才能在遇到新样本时做出正确的判断。然而,当学习器把训练样本学得“太好”的时候,很可能已经把训练样本自身的一些特点当做了所有潜在样本都会具有的一般性质,这样就会导致泛化能力下降。这种现象在机器学习中称为“过拟合” (overfitting)。与“过拟合”相对的是“欠拟合” (underfitting),这是指对训练样本的一般性质尚未学好。

有很多因素可能导致过拟合,其中最常见的情况是由于学习能力过于强大,以至于把训练样本所包含的不太一般的特性也都学习到了,而欠拟合则通常是由于学习能力低下而造成的。欠拟合比较容易克服,例如在决策树中扩展分支、在神经网络学习中增加训练轮数等,而过拟合则很麻烦。过拟合是机器学习面临的关键障碍,各类学习算法都必然带有一些针对过拟合的措施;然而必须认识到,过拟合是无法彻底避免的,我们所能做的只是“缓解”,或者说减小其风险。关于这一点,可大致这样理解:机器学习面临的问题通常是 NP 难甚至更难,而有效的学习算法必然是在多项式时间内运行完成,若克彻底避免过拟合,则通过经验误差最小化就能获得最优解,这就意味着我们构造性地证明了 “P = NP”;因此,只要相信 “P \not = NP”,过拟合就不可避免。

在现实任务中,我们往往有很多学习算法可供选择,甚至对同一个学习算法,当使用不同的参数匹配时,也会产生不同的模型。那么,我们该选用哪一个学习算法,使用哪一种配置参数呢?这就是机器学习中的 “模型选择” (model selection) 问题。理想的解决方案当然是对候选模型的泛化误差进行评估,然后选择泛化误差最小的那个模型。然而如上面所讨论的,我们无法直接获得泛化误差,而训练误差又由于过拟合现象的存在而不适合作为标准,那么,在现实中如何进行模型评估与选择呢?

2.2 评估方法

通常,我们可以通过实验测试来对学习器的泛化误差进行评估并进而做出选择。为此,需要使用一个“测试集” (testing set) 来测试学习器对新样本的判别能力,然后以测试集上的 “测试误差” (testing error) 作为泛化误差的近似。通常我们假设测试样本也是从样本真实分布中独立同分布采样而得。但需要注意的是,测试集应该尽可能与训练集互斥,即测试样本尽量不在训练集中出现、未在训练过程中使用过。

测试样本为什么要尽可能不出现在训练集中呢?为理解这一点,不妨考虑这样一个场景:老师出了 10 道题供同学们练习,考试时老师又用同样的这 10 道题作为试题,这个考试成绩能否有效反应出同学们学得好不好呢?答案是否定的,可能有的同学只会做这 10 道题却能得高分。回到我们的问题上来,我们希望得到泛化性能很强的模型,好比是希望同学们对课程学得很好、获得了对所学知识 “举一反三” 的能力;训练样本相当于给同学们练习的习题,测试过程则是相当于考试。显然,若测试样本被用作训练了,则得到的将是过于 “乐观” 的估计结果。

可是,我们只有一个包含 m 个样例的数据集 D=\{(x_1, y_1), (x_2, y_2), ... , (x_m, y_m)\} ​, 既要训练,又要测试,怎样才能做到呢?答案是:通过对 D 进行适当的处理,从中产生出训练集 S 和测试集​ T 。下面介绍几种常见的做法。

2.2.1 留出法

“留出法” (hold-out) 直接将数据集 D ​ 划分成两个互斥的集合,其中一个集合作为训练集 S ​,另一个作为测试集 T ​,即 ​ D=S\cup T,S\cap T=\varnothing ​。在​ S 上训练出模型后,用​ T 来评估其测试误差,作为对泛化误差的估计。

以二分类任务为例,假定 ​D 包含 1000 个样本,将其划分为 S ​ 包含 700 个样本,​ T 包含 300 个样本,用 S 进行训练后,如果模型在 T ​上有 90 个样本分类错误,那么其错误率为 ​ 90/300\times100\% ,相应的,精度为 1-30\%=70\% ​。

需注意的是,训练​ / 测试集的划分要尽可能保持数据分布的一致性,避免因数据划分过程引入额外的偏差而对最终结果产生影响,例如在分类任务中至少要保持样本的类别比例相似。如果从采样 (sampling) 的角度来看待数据集的划分过程,则保留类别比例的采样方式通常称为 “分层采样” (stratified sampling)。例如通过对 D ​ 进行分层采样而获得含 70\% ​样本的训练集 S ​ 和含 30\% ​ 样本的测试集 S ​,若 D ​ 包含 500 个正例、500 个反例,则分层采样得到的 S ​ 应包含 350 个正例、350 个反例,而 T ​ 则包含 150 个正例和 150 个反例;若 ​ S 、​ T 中样本类别比例差别很大,则误差估计将由于训练​ / 测试数据分布的差异而产生偏差。

另一个需要注意的问题是,即便在给定的训练 / ​测试集的样本比例后,仍存在多种划分方式对初始数据集 D ​ 进行分割。例如在上面的例子中,可以把 ​ D 中的样本排序,然后把前 350 个正例放到训练集中,也可以把最后 350 个正例放到训练集中,…… 这些不同的划分将导致不同的训练​测试集,相应的,模型评估的结果也会有差别。因此,单次使用留出法得到的估计结果往往不够稳定可靠,在使用留出法时,一般要采用若干次随机划分、重复进行实验评估后取平均值作为留出法的评估结果。例如进行 100 次随机划分,每次产生一个训练​ / 测试集用于实验评估,100 次后就得到 100 个结果,而留出法返回的则是这 100 个结果的平均。

​此外,我们希望评估的是用 D ​ 训练出的模型的性能,但留出法需划分训练 / ​测试集,这就会导致一个窘境 (awkward situation):若令训练集 S 包含绝大多数样本,则训练出的模型可能更接近于用 D ​ 训练出的模型,但由于 T 比较小,评估结果可能不够稳定准确;若令测试集 T 多包含一些样本,则训练集 ​ S 与 ​ D 差别更大了,被评估的模型与用 D 训练出的模型相比可能有较大的差别,从而降低了评估结果的保真性 (fidelity)。这个问题没有完美的解决方案,常见做法是将大约 2/3\sim 4/5 ​ 的样本用于训练,剩余样本用于测试。

2.2.2 交叉验证法

“交叉验证法” (cross validation) 先将数据集 D ​划分为 k ​个大小相似的互斥子集,即 D=D_1\cup D_2\cup \cdots \cup D_k,D_i \cap D_j = \varnothing \, (i \not= j) ​。每个子集 D_i ​都尽可能保持数据分布的一致性,即从 ​D 中通过分层采样得到。然后,每次用 ​ k-1 个子集的并集作为训练集,余下的那一个子集作为测试集;这样就可获得​ k 组训练​测试集,从而可进行 k 次训练和测试,最终返回的是这 k 个测试结果的均值。显然,交叉验证法评估结果的稳定性和保真性在很大程度上取决于 k 的取值,为强调这一点,通常把交叉验证法称为 “​ k 折交叉验证” (​ k - fold cross validation)。​ 最常用的取值是 10,此时称为 10 折交叉验证;其他常用的 k 值有 5、20 等。

与留出法相似,将数据集 ​ D 划分为 ​ k 个子集同样存在多种划分方式。为减小因样本划分不同而引入的差别, k 折交叉验证通常要随机使用不同的划分重复 ​ p 次,最终的评估结果是这 p ​次​ k 折交叉验证结果的均值,例如常见的有“10 次 10 折交叉验证”。

假定数据集 D 中包含 ​ m 个样本,若令 k=m ​,则得到了交叉验证法的一个特例:留一法 (Leave-One-Out, 简称 LOO)。显然,留一法不受随机样本划分方式的影响,因为 ​ 个样本只有唯一的方式划分为 ​ 个子集​每个子集包含一个样本;留一法使用的训练集与初始数据集相比只少了一个样本,这就使得在绝大多数情况下,留一法中被实际评估的模型与期望评估的用 D 训练出的模型很相似。因此,留一法的评估结果往往被认为比较准确。然而,留一法也有其缺陷:在数据集比较大时,训练 m 个模型的计算开销可能是难以忍受的 (例如数据集包含 1 百万个样本,则需要训练 1 百万个模型),而这还是在未考虑算法调参的情况下。另外,留一法的估计结果也未必永远比其他评估方法准确;“没有免费的午餐” 定理对实验评估方法同样适用。

2.2.3 自助法

我们希望评估的是用 D 训练出的模型。但在留出法和交叉验证法中,由于保留了一部分样本用于测试,因此实际评估的模型所使用的训练集比​ D 小,这必然会引入一些因训练样本规模不同而导致的估计偏差。留一法受训练样本规模变化的影响较小,但计算复杂度又太高了。有没有什么办法可以减少训练样本规模不同造成的影响,同时还能比较高效地进行实验估计呢?

“自助法” (bootstrapping) 是一个比较好的解决方案,它直接以自助采样法 (bootstrap sampling) 为基础。给定包含 m 个样本的数据集 D ​,我们对它进行采样产生数据集 D' ​:每次随机从 ​ D 中挑选一个样本,将其拷贝放入 D' ​,然后再将该样本放回初始数据集 ​ 中,是的该样本在下次采样时仍有可能被采到;这个过程重复执行 m 次后,我们就得到了包含​ m 个样本的数据集 ​,这就是自助采样的结果。显然, D 中有一部分样本会在 D' 中多次出现,而另一部分样本不出现。可以做一个简单的估计,样本在 m 次采样中始终不被采到的概率是  (1-\frac{1}{m})^{m} ​,取极限得到:

\displaystyle { \lim_{x \to \infty}(1-\frac{1}{m})^m \mapsto \frac{1}{e} \approx 0.368 }

即通过自助采样,初始数据集 ​ D 中约有 ​ 36.8\% 的样本未出现在采样数据集 D' 中。于是我们可将 D' 用作训练集, D\setminus D' ​ 用作测试集;这样实际评估的模型与期望评估的模型都使用 ​ m 个训练样本,而我们仍有数据总量约 1/3 ​ 的、没在训练集中出现的样本用于测试。这样的测试结果,亦称 “外包估计” (out-of-bag estimate)。

自助法在数据集较小、难以有效划分训练 / ​测试集时很有用;此外,自助法能从初始数据集中产生多个不同的训练集,这对集成学习等方法有很大的好处。然而,自助法产生的数据集改变了初始数据集的分布,这会引入估计偏差。因此,在初始数据集足够时,留出法和交叉验证法更为常用一些。

2.2.4 调参与最终模型

大多数学习算法都有些参数 (parameter) 需要设定,参数配置不同,学得模型的性能往往有显著差别。因此,在进行模型评估与选择时,除了要对适用学习算法进行选择,还需要对算法参数进行设定,这就是通常所说的 “参数调节” 或简称 “调参” (parameter tuning)。

这里需要注意一点:学习算法的很多参数是在实数范围内取值,因此,对每种参数配置都训练出模型来是不可行的。现实中常用的做法,是对每个参数选定一个范围和变化步长,例如在  [0, 0.2] ​范围内以 0.05 为步长,则实际要评估的候选参数值有 5 个,最终是从这 5 个候选值中产生选定值。显然,这样选定的参数值往往不是 “最佳” 值,但这是在计算开销和性能估计之间进行折中的结果,通过这个折中,学习过程才变得可行。事实上,即便在进行这样的折中后,调参往往仍很困难。可以简单估算一下:假定算法有 3 个参数,每个参数仅考虑 5 个候选值,这样对每一组训练​ / 测试集就有​  5^2=125 个模型需要考察;很多强大的学习算法有大量参数需要设定,这将导致极大的调参工程,以至于在不少应用任务中,参数调得好不好往往对最终模型性能有关键性影响(机器学习常涉及两类参数:一类是算法的参数,亦称 “超参数”,数目常在 10 以内;另一类是模型的参数,数目可能很多,例如大型 “深度学习” 模型甚至有上百亿个参数;两者调参的不同之处在于前者通常是由人工设定多个参数候选值后产生模型,后者则是通过学习来产生多个候选模型,例如神经网络在不同轮数停止训练)。

给定包含 m 个样本的数据集 D ​,在模型评估与选择过程中由于需要留出一部分数据进行评估测试,事实上我们只使用了一部分数据训练模型。因此,在模型选择完成后,学习算法和参数配置已选定,此时应该用数据集 D 重新训练模型。这个模型在训练过程中使用了所有​ m 个样本,这才是我们最终提交给用户的模型。

另外,需要注意的是,我们通常把学得模型在实际使用中遇到的数据称为测试数据,为了加以区分,模型评估与选择中用于评估测试的数据集常称为 “验证集” (validation set)。例如,在研究对比不同算法的泛化性能时,我们用测试集上的判断效果来估计模型在实际使用时的泛化能力,而把训练数据另外划分为训练集和验证集,基于验证集上的性能来进行模型选择和调参。


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/QNvDLI4aAr
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/12091