新闻中心

+

模型优化

发布时间 : 2024-04-29 03:41:03

优化是应用数学的一个分支,也是机器学习的核心组成部分。实际上,机器学习算法 = 模型表征 + 模型评估 + 模型优化。其中,模型优化所做的事情就是在模型表征空间(假设空间)中找到模型评估指标最好的模型。需要注意的是不同的优化算法对应的模型表征和评估指标不尽相同。

先前,我很纠结是把损失函数放在模型评估中,还是放在模型优化这一篇博客中。准确地说,损失函数是用来作为模型评估的标准,不同的模型有不同的损失函数,例如逻辑回归使用交叉熵损失函数,线性回归使用均方误差损失函数。我们统计模型的损失,从而评估模型的优劣。因此,损失函数放到模型评估中是顺理成章的事情。但是,损失函数在模型优化中同样起到非常重要的作用。

很少有模型从一开始就是完美的,我们需要不断地优化模型,让模型逐渐达到理论最优值,而我们优化的目标就是损失函数——让损失函数达到最小值。从这角度来讲,将损失函数放到模型优化中似乎也非常有道理,因此我最终还是将损失函数放到模型优化这一篇博客中。

在本篇博客中除了讲述损失函数外,还包括机器学习中经典的优化算法、模型调参等相关知识,内容主要来源于博主阅读的书籍以及博主的个人领悟,若有偏驳之处,还望指出。

不严谨地讲,凸优化是指在标准优化问题的范畴内,要求目标函数和约束函数是凸函数的一类优化问题。凸优化是一个很广且复杂的领域,在此仅仅只作简单的介绍。

在介绍凸优化前需要先弄明白什么是凸函数。国内外教材关于凸函数和凹函数的定义不同,在查阅资料时需要细心区别。凸函数的严格定义为:函数 L 是凸函数,当且仅当对定义域中的任意两点 x, y 和任意实数 λ ∈ [ 0 , 1 ] \lambda \in [0, 1] λ[0,1] 总有
L ( λ x + ( 1 ? λ ) y ) ≤ λ L ( x ) + ( 1 ? λ ) L ( y ) L(\lambda x + (1 - \lambda)y) \leq \lambda L(x) + (1 - \lambda)L(y) L(λx+(1?λ)y)λL(x)+(1?λ)L(y)
该不等式的一个直观解释是,凸函数曲面上任意两点连接而成的线段,上面任意一点都不会处于该函数曲面的下方,见下图。

凸函数.png

那么如何判断一个函数是凸函数呢?最简单的方法是判断当前函数的二阶导数 f’’ 是否大于等于 0,若 f ′ ′ ≥ 0 f'' \geq 0 f0 则 f 是凸函数,当且仅当 f ′ ′ > 0 f'' > 0 f>0,f 是严格凸函数。

后面要讲的平方误差损失函数 L ( f , y ) = ( f ? y ) 2 L(f, y) = (f - y)^2 L(f,y)=(f?y)2 就是一个严格凸函数,我们将其展开 f 2 ? 2 f y + y 2 f^2 - 2fy + y^2 f2?2fy+y2,对 f 求二次导数,最终可得 2,2 > 0,因此平方误差损失函数是一个严格凸函数。

除了借助原函数的二阶导数外,另外一种方法是判断目标函数的二阶 Hessian 矩阵来验证凸性。那么问题来了,怎么求目标函数的二阶 Hessian 矩阵?如何通过二阶 Hessian 矩阵来判断凸性?

  • 目标函数的二阶 Hessian 矩阵可通过对目标函数的二阶求导获得,令二阶 Hessian 矩阵为 H。
  • 如果 Hessian 矩阵满足半正定的性质 ∑ i = 1 n H i ≥ 0 \sum_{i=1}^nH_i \geq 0 i=1n?Hi?0(半正定矩阵的行列式非负),函数为凸函数。

对比这两种方法,可以发现第一种方法针对实数 X,而第二种方法针对向量 X。关于半正定矩阵的内容可以参考这篇文章 浅谈「正定矩阵」和「半正定矩阵」

根据凸优化的不严谨定义,我们知道凸优化处理的目标函数和约束函数是凸函数,而凸函数的局部极小值都是全局最小值,因此我们在对凸函数求导时,求得的极值都是全局最小值。那么什么是局部极小,什么是全局最小?

在模型优化过程中我们时常会谈到两种“最优”:局部极小(local minimum)和全部最小(global minimum),令 E 表示模型在训练集上的误差,模型的训练过程可看作一个参数寻优过程,即在参数空间中寻找一组最优参数使得 E 最小。

  • 局部极小点:参数空间内梯度为零的点,只要其误差函数值小于邻点的误差函数值,就是局部极小点。局部极小点可以有多个。
  • 全局最小点:局部极小点中误差函数值最小的点,即为全局最小点。全局最小点只有一个。

全局最小与局部极小

借用下《机器学习》的图,若构成侵权则立即删除。

全局最小点一定是局部极小点,但局部极小点不一定是全局最小点。

那么我们如何找到局部极小点,以及找到全局最小点呢?

【梯度下降算法】:传送门

【随机梯度下降算法】:施工中

损失函数(loss function)用来估量模型预测值与真实值之间的匹配程度,它是一个非负实值函数。损失函数越小,模型的性能以及鲁棒性就越好。

【表示方式】:通常,我们用大写的 L 来表示样本的损失函数。
L ( y , f ( x ) ) L(y, f(x)) L(y,f(x))

损失函数是经验风险函数的核心部分,也是结构风险函数重要组成部分。那么什么是经验风险函数?结构风险函数又是什么呢?要阐明经验风险函数以及结构风险函数前,得先介绍风险函数(期望损失)。

我们知道模型的输入、输出 (X, Y) 是随机变量,遵循联合分布 P(X, Y)。例如 X 为年龄, Y 为身高,一般来说身高随着年龄的变化呈现正态分布。令 R e x p ( f ) R_{exp}(f) Rexp?(f) 表示损失函数的期望,则期望的表达式如下:
R e x p ( f ) = E p [ L ( Y , f ( X ) ) ] = ∫ x × y L ( y , f ( x ) ) P ( x , y ) d x d y R_{exp}(f) = E_p[L(Y, f(X))] = \int_{x imes y} L(y, f(x))P(x, y)dxdy Rexp?(f)=Ep?[L(Y,f(X))]=x×y?L(y,f(x))P(x,y)dxdy
这是理论上模型 f(X) 关于联合分布 P(X, Y) 平均意义下的损失,称为风险函数(risk function)或期望损失(expected loss)。

但是通常情况下,实际与理论存在差异:很少有数据集完美地符合理论上的分布,或多或少都会存在噪声、异常值,使得数据集与理论的分布存在一定的偏移。而这种非理论的,我们一般称之为经验,所以基于实际数据集的损失函数被称为经验风险函数,用于区分风险函数。下面,我们再用正规的数学语言来定义经验风险函数。

一般地,给定一个训练数据集
T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) } T = \{(x_1, y_1), (x_2, y_2), \ldots, (x_N, y_N)\} T={(x1?,y1?),(x2?,y2?),,(xN?,yN?)}
模型 f(X) 关于训练数据集的平均损失称为经验风险(empirical risk)或经验损失(empirical loss),记作 R e x p R_{exp} Rexp?,而 f(X) 本身则被称为经验风险函数。
R e m p ( f ) = 1 N ∑ i = 1 N L ( y i , f ( x i ) ) R_{emp}(f) = \frac{1}{N}\sum_{i=1}^NL(y_i, f(x_i)) Remp?(f)=N1?i=1N?L(yi?,f(xi?))

期望风险 R e x p ( f ) R_{exp}(f) Rexp?(f) 是模型关于联合分布的期望损失,经验风险 R e m p ( f ) R_{emp}(f) Remp?(f) 是模型关于训练样本集的平均损失。根据大数定律,当样本容量 N 趋于无穷时,经验风险 R e m p ( f ) R_{emp}(f) Remp?(f) 趋于期望风险 R e x p ( f ) R_{exp}(f) Rexp?(f)。这也是机器学习所需数据量大的原因之一,数据量越大,则噪声对于整体的影响就会小很多,从而使得训练数据集尽可能贴合真实的数据分布情况。

但需要注意的是,经验风险能取得效果的前提是数据量足够大,如果此时的数据量不够大,那么会发生什么?试想这样一种情形,假设某个数据集符合正态分布,你从该数据集中获取到了一部分数据,而这部分数据恰好都处于正态分布的一侧,然后你用这部分数据去训练模型,接着用剩余的数据集来做测试,最终发现错误率非常高。

原因也非常简单,因为数据量过小,且这部分数据不具有代表性,不能代表整个数据集。此外,数据量过小还会导致模型过度拟合,产生过拟合现象,这怎么理解呢?比如 A 告诉你喝酒是不对的,因为只有 A 告诉过你,所以你很相信 A 说的话,一直认为喝酒是不对的(模型拟合得太好了)。之后你遇到了 B,B 告诉你喝酒可以消愁,是正确的事情,那么你就会开始犹豫,到底喝酒是正确还是不正确?最后你得出了一个结论,视情况而定,在不同的情境下得到的答案也不同。模型训练的过程也是如此,如果数据量过小,模型掌握的知识不够多就有很大概率做出错误的决策。当数据量增加时,模型掌握的知识也越丰富,那么做出正确决策的概率也会随之增加。

正是基于上述原因,为了防止过拟合,结构风险(structural risk)应运而生,在经验风险上加上表示模型复杂度的正则化项(regularizer)或罚项(penalty term)。
R s r m = 1 N ∑ i = 1 N L ( y i , f ( x i ; θ ) + λ J ( f ) ) R_{srm} = \frac{1}{N}\sum_{i=1}^N L(y_i, f(x_i; heta) + \lambda J(f)) Rsrm?=N1?i=1N?L(yi?,f(xi?;θ)+λJ(f))
其中,J(f) 表示模型的复杂度。模型 f 越复杂,复杂度 J(f) 就越大;反之,模型 f 越简单,复杂度 J(f) 就越小。也就是说,复杂度表示了对模型的惩罚。 λ ≥ 0 \lambda \geq 0 λ0 是系数,用以权衡经验风险和模型复杂度。

此时我们的任务就转变为:在保证经验风险足够小的同时也确保结构风险足够小。假如有两个模型 A 和 B,它们的经验风险相差不大 A 略小于 B,但 A 的结构风险远大于 B,此时我们应选择 B 而非 A。奥卡姆剃刀原理也可以说明这一点。

有监督学习的损失函数

在有监督学习中,损失函数刻画了模型和训练样本的匹配程度。假设训练样本的形式为 ( x i , y i ) (x_i, y_i) (xi?,yi?),其中 x i ∈ X x_i \in X xi?X 表示第 i 个样本点的特征, y i ∈ Y y_i \in Y yi?Y 表示该样本点的标签,f 表示 x i x_i xi? 经过模型后的输出。 L ( f , y i ) L(f, y_i) L(f,yi?) 表示当前样本的损失值,L 越小,表明模型在该样本点匹配得越好。

分类问题

以二分类问题为例,Y = {1, -1},因此 fy > 0 表示分类正确,fy < 0 表示分类错误。

【0-1 损失函数】: s i g n ? f ( x i , θ ) = y i sign \ f(x_i, heta) = y_i sign?f(xi?,θ)=yi?
L 0 ? 1 ( f , y ) = 1 f y ≤ 0 = { 1 , y ≠ f ( x ) 0 , y = f ( x ) L_{0-1}(f, y) = 1_{fy\leq 0} = \begin{cases} 1, \quad y eq f(x) \\ 0, \quad y = f(x) \end{cases} L0?1?(f,y)=1fy0?={1,y??=f(x)0,y=f(x)?
其中 1 p 1_p 1p? 是指示函数(Indicator Function),当且仅当 P 为真时取值为 1,否则取值为 0。在上述式子中,fy < 0 为真,表示分类错误取值为 1。假设总共有 100 个样本,其中 5 个样本分类错误,那么损失函数的最终取值为 5。

0-1损失函数

该损失函数能够直观地刻画分类的错误率,但是由于其非凸、非光滑的特点,使得该算法很难直接对该函数进行优化。

【合页(Hinge)损失函数】:
L h i n g e ( f , y ) = m a x { 0 , 1 ? f y } . L_{hinge}(f, y) = max\{0, 1-fy\}. Lhinge?(f,y)=max{0,1?fy}.
Hinge 损失函数是 0-1 损失函数相对紧的凸上界,且当 fy ≥ 1 时,该函数不对其做任何惩罚。Hinge 损失在 fy=1 处不可导,因此不能用梯度下降法进行优化,而是用次梯度下降法(Subgradient Descent Method)。

【Logistic 损失函数】:
L l o g i s t i c ( f , y ) = l o g 2 ( 1 + e x p ( ? f y ) ) . L_{logistic}(f, y) = log_2(1 + exp(-fy)). Llogistic?(f,y)=log2?(1+exp(?fy)).
Logistic 损失函数也是 0-1 损失函数的凸上界,且该函数处处光滑,因此可以用梯度下降法进行优化。但是,该损失函数对所有的样本点都有所惩罚,因此对异常值相对更敏感一些。

【交叉熵(Cross Entropy)损失函数】:当预测值 f ∈ [ ? 1 , 1 ] f \in [-1,1] f[?1,1] 时。
L c r o s s ? e n t r o p y ( f , y ) = ? l o g 2 ( 1 + f y 2 ) . L_{cross \ entropy}(f, y) = -log_2(\frac{1 + fy}{2}). Lcross?entropy?(f,y)=?log2?(21+fy?).
交叉熵损失函数也是 0-1 损失函数的光滑凸上界。

二分类问题的损失函数.jpg

回归问题

【平方损失函数】:光滑函数,能够用梯度下降发进行优化。但当预测值距离真实值越远时,平方损失函数的惩罚力度越大,因此对异常点比较敏感。
L s q u a r e ( f , y ) = ( f ? y ) 2 L_{square}(f, y) = (f - y)^2 Lsquare?(f,y)=(f?y)2

【绝对损失函数】:对异常点更鲁棒一些,但是,在 f = y 处无法求导数。
L a b s o l u t e ( f , y ) = ∣ f ? y ∣ L_{absolute}(f, y) = |f - y| Labsolute?(f,y)=f?y

【Huber 损失函数】:综合考虑可导性和对异常点的鲁棒性。在 |f - y| 较小时为平方损失,在 |f - y| 较大时为线性损失,处处可导且对异常点鲁棒。
L H u b e r ( f , y ) = { ( f ? y ) 2 , ∣ f ? y ∣ ≤ δ 2 δ ∣ f ? y ∣ ? δ 2 , ∣ f ? y ∣ > δ L_{Huber}(f, y) = \begin{cases} (f - y)^2, \quad |f - y| \leq \delta \\ 2\delta |f - y| - \delta^2, \quad |f - y| > \delta \end{cases} LHuber?(f,y)={(f?y)2,f?yδ2δf?y?δ2,f?y>δ?
回归问题的损失函数.jpg

大多数学习算法都有参数需要设定,参数配置不同,学得模型的性能往往有显著差别,这就是通常所说的“参数调节”或简称“调参”(parameter tuning)。

模型调参和模型选择没什么本质区别:对每种参数配置都训练出模型,然后把对应最好模型的参数作为结果。这里的参数与前面所讲的参数有所不同,无法在训练过程中调整,需要用户手动去设置,这一类参数我们称之为“超参数”。

模型调参主要针对超参数进行调整,最终达到的效果是——模型在训练集上的准确性以及防止过拟合能力的大和谐,简单地说就是协调模型的偏差和方差

网格搜索

通过查找搜索范围内的所有的点(超参数组合,超参数的笛卡尔积)来确定最优值。假设现在有三个参数年龄、性别、是否有工作:

  • 年龄:低、中、高;
  • 性别:男、女;
  • 是否有工作:是、否;

最终所有的参数组合如下表所示:

序号年龄性别是否有工作序号年龄性别是否有工作
17
28
39
410
511
612

可以看到参数组合的个数 = 年龄的取值个数 x 性别的取值个数 x 是否有工作的取值个数 = 3 x 2 x 2 = 12。如果参数的个数有 10 个,且每个参数的取值个数都为 10,那么最终参数组合个数为 10 的 10 次方。虽然网格搜索法可以找到最优的参数组合,但十分消耗计算资源和时间,特别是需要调优的超参数比较多或参数的取值个数较多。一般来说,当超参数少于四个且取值不多时,可以考虑使用网格搜索法。

【改进方案】:在实际应用中,一般会先使用较广的搜索范围和较大的步长,来寻找全局最优值可能的位置;然后逐渐缩小搜索范围和步长,来寻找更精确的最优值。

  • 优点:这种方法可以降低所需的时间和计算量。
  • 缺点:由于目标函数一般是非凸的,所以很可能会错过全局最优值。

我们可以使用 scikit-learn 中的 GridSearchCV 来实现对参数的评估,从而找到最优参数。

  • 首先,由用户列出一个较小的超参数值域,这些超参数值域的笛卡尔集(排列组合)为一组组超参数。
  • 网格搜索算法使用每组超参数训练模型,并挑选验证集误差最小的超参数组合。

关于 GridSearchCV 的更多内容请参考官方文档 传送门

【代码实现】:使用 GridSearchCV 来寻找 AdaBoost 算法的超参数。

 

上述案例获得的最佳参数组合恰好位于参数组合列表的边界,因此很有可能不是全局最优值,需要再做一次搜索。

 

最终得出最佳参数组合:{‘learning_rate’: 1, ‘n_estimators’: 50}

随机搜索

不再查搜索范围内的所有参数组合,而是在搜索范围中随机选取参数组合。具体地,随机搜索法通过固定次数的迭代(用户设定),采用随机采样分布的方式搜索合适的参数。相比网格搜索,随机搜索计算开销更小,执行速度更快,但因为是随机采样,不能保证恰好就采样到全局最优值,因此结果无法得到保证。

为什么随机搜索法的结果无法得到保证,却仍然具有应用价值呢?它的理论依据是什么?

【理论依据】:样本点集足够大,通过随机采样也能大概率地找到全局最优值,或其近似值。

我们可以使用 scikit-learn 中的 RandomizedSearchCV 类实现。关于 RandomizedSearchCV 的更多内容请参考官方文档 传送门

【代码实现】:使用 RandomizedSearchCV 来寻找 AdaBoost 算法的超参数。

 

通过上述代码可以看到,随机搜索法每次获得的结果都不同,且很有可能距离全局最优值很远,因此我们需要多尝试几次,最终确定全局最优值的位置。

无论是网格搜索法还是随机搜索法,都需要用户先写出参数列表,然而学习算法的很多参数都是在实数范围内取值,那么如何确定参数列表本身也成为了一个难题。

【做法 1】:对每个参数选定一个范围和变化步长,例如在先前代码示例中的 learning_rate 参数,它的取值范围在 [0, 1] 范围内。我们可以把 0.1 为步长,待评估的候选参数值设置为 5 个,则最终是从这 5 个候选值中产生选定值,即 [1, 0.9, 0.8, 0.7, 0.6]。

【做法 2】:SciPy 提供 uniform 函数,可以用于均匀随机采样,默认生成 0 与 1 之间的随机采样数值。对于 learning_rate 这一类的参数就非常使用。

 

贝叶斯优化

网格搜索和随机搜索在测试一个新点时,会忽略前一个点的信息;而贝叶斯优化算法则充分利用了之前的信息。贝叶斯优化算法通过对目标函数形状进行学习,找到使目标函数向全局最优值提升的参数。

具体来说,它学习目标函数形状的方法是:

  1. 首先根据先验分布,假设一个搜集函数;
  2. 然后,每一次使用新的采样点来测试目标函数时,利用这个信息来更新目标函数的先验分布;
  3. 最后,算法测试由后验分布给出的全局最值最可能出现的位置的点。

对于贝叶斯优化算法,有一个需要注意的地方,一旦找到了一个局部最优值,它会在该区域不断采样,所以很容易陷入局部最优值。为了弥补这个缺陷,贝叶斯优化算法会在探索和利用之间找到一个平衡点,“探索”就是在还未取样的区域获取采样点;而“利用”则是根据后验分布在最可能出现全局最值的区域进行采样。

关于贝叶斯优化更多的内容可以阅读这两篇文章:

最后,在谈谈网格搜索法、随机搜索法和贝叶斯优化三者的区别。假设现在有三个炼金术士 A、B、C,他们在探索古迹的时候找到了一篇记录如何炼制长生不老丹的书籍,里面记录了炼制的药材,但却遗漏了每份药材添加的量。这三位炼金术士耗费数年时间收集了大量的药材,于今日准备开始炼制长生不老丹。

  • A 列出所有药材的所有排列组合,准备一一尝试。
  • B 随机选择药材的量,希望能够撞大运炼制出不老丹。
  • C 先随机选择药材的量,然后根据每次炼制后的结果进行反思。在放入5 克枸杞和当归后,炼丹炉没有发生爆炸,C 眼睛一亮,似乎 5克枸杞和当归是正确的配比,枸杞和当归的量即可固定下来,在后续的试验中可以考虑其他药材的配比。

A、B、C 的做法正好对应了网格搜索、随机搜索以及贝叶斯优化三种超参数优化方案。

×
全国服务热线 : 0898-08980898

平台注册入口