【人工智能算法】算法基础之优化训练

本文重点:

  • 爬山算法
  • 模拟退火算法
  • Nelder-Mead算法

爬山算法

爬山算法实现起来比贪心随机算法要复杂一些,但贪心随机算法的劣势之一在于无法进行细调。使用贪心随机算法时,以一个随机生成的向量值作为长期记忆,一旦找到表现更好的随机向量,立刻替换原来的长期记忆,而无法通过细调来得到可能就在附近的最优解,只能听天由命。
爬山算法则是在当前向量值的基础上进行细调,是名副其实“爬山”的过程。假设你被随机扔在崇山峻岭的某个半山腰,而你的目的是爬到附近区域的最高峰,你肯定会看看自己附近一步以内的位置,看看哪个方向更高,再决定往哪个方向走;重复这一过程,直到附近再也找不到更高的位置,你就到达了局部的最高峰,这个寻找最高峰的过程也就结束了。在爬山算法中,当到达离初始点最近的最高点时,算法终止;该最高点也称“局部最大值”。
除非是一个极为简单的模型,否则你也许永远也别想找到全局最大值或全局最小值;相反应该试图在邻近区域找到更佳的局部最大值或局部最小值。如果发现训练过程“陷在”某个区域出不来,可能就需要随机赋值然后重新开始训练;当然这种情况也有一定的概率是在局部最小值或局部最大值附近结束训练。
爬山算法由两个独立的函数来实现,第一个函数执行算法的初始化工作,第二个函数给出迭代过程。

爬山算法初始化:

"""
初始化的长期机器
加速度
各维度上的初始步骤
"""
function  initHillClimb(ltm,acceleration,initialVelocity){
    
    for i in from 0 to ltm.length{
        stepSize[i] = initialVelocity        
    }
    candidate[0]=-acceleration
    candidate[1]=-1/acceleration
    candidate[2]=0
    candidate[3]=1/acceleration
    candidate[4]=acceleration
    }

爬山算法的迭代

function iterateHillClimb(ltm){
    len = ltm.length
    for i from 0 to len{
        best=-1
        //要求最小值,故把最佳得分初始化为正无穷,任意小都小于正无穷,因此把第一个得分初始化为正无穷
        bestScore = +Infinity
        //在当前维度上尝试所有可能的行为
        for j from 0 to candidate.length{
            //尝试各个行为并评估结果,但评估完后立即回退
            temp = score.calculateScore(ltm)
            ltm[i]=ltm[i]-(stepSize[i]*candidate[j])
            //任一维度上若有最佳的性能提升则报错得分和行为序号
            if(temp<bestScore){
            bestScore = temp
            best = j
            }
        }
        //完成再当前维度上的尝试,看是否有某个行为产生了更好的结果,弱势,则在该方向上移动
        if(best!=-1){
                ltm[i]=ltm[i]+(stepSize[i]*candidate[best])
                stepSize[i]=stepSize[i]*candicate[best]
        }
    }
}

模拟退火算法

Scott Kirkpatrick和其他几位研究者在20世纪80年代初期提出了模拟退火算法。最初提出这一算法是为了通过模拟金属退火的过程,更好地优化集成电路(Integrated Circuit, IC)芯片设计。
算法从某个“高温点”开始,此时长期记忆在一个很大的区间内随机取值。随着训练过程的进行,“温度”开始下降,因此长期记忆的变化范围也逐渐受到限制。模拟退火算法多能产生比较优秀的解,就如同退火过程能够使金属得到更佳的晶体结构

模拟退火算法的应用

对任意公式,给定一定数目的输入,模拟退火算法都能求出什么样的输入可以得到对应公式的最小值。在旅行商问题中,要求计算旅行商必须走过的总距离,就相当于机器学习算法中的误差计算或者是评估函数。
模拟退火算法在问世之初,主要在集成电路设计方面广为应用。为了完成芯片的设计任务,大多数集成电路芯片内部都由大量逻辑门组成。就像代数表达式大多能够化简一样,集成电路芯片的布局也一样可以化简。

模拟退火算

模拟退火算法与爬山算法有个相似之处在于,它们都会基于当前位置,来考虑下一步采取什么样的行动。这些“下一步行动”都是随机选择的,如果随机到的某个行为得到了比当前位置更好的结果,那么算法就一定会移动到这个新的位置;而如果该行为得到的结果比当前位置要更差,则算法会随机选择是否移动到这个新位置,“温度”越高,则“是”的随机概率就越大
模拟退火算法流程图
采取“更坏策略”的概率是一个很重要的特性。不像贪心随机算法那样,时时刻刻都求最好,模拟退火算法有时候会选择以退为进

模拟退火算法的伪代码

function iteration(ltm,cycles){
    len = ltm.length
    k++
    currentTemperature = coolingSchedule(k)
    for cycle from 0 to cycles{
       //备份当前状态
        oldState = itm.clone()
        //随机选取行为
        performRandonize(ltm)
        //结果是否改善 如果结果有所改善,则保存新的位置(贪心)
        trialError=calculateScore(ltm)
        //当前迭代结果是否改善,若改善,一定保存新位置
        keep = False
        if trialError<currentError{
            keep=true    
        }else{
            p = calcProbablity(currentError,trialError,currentTemperature)
            if(p>rand()){
                    keep = true
            }
        }    
        //是否保存新位置
        if(keep){
            currentError = trialError
            //如果结果优于之前的全局最佳误差
            if(trialError<globalBestError){
            globalBestError = trialError
            oldState = ltm.clone()
            globalBest = ltm.clone()
            }
        }else{
            ltm = oldState.clone()    
        }
   }    
}

退火概率

拟退火算法总是随机移动,有时得到的结果甚至还不如之前,如果是一个完全的“贪心”算法,就压根儿不会考虑结果更差的新位置;但这种做法有时却适得其反。
拟退火算法接受更坏结果的概率是由一个3个输入的函数计算出来的,3个输入分别是:

  • 当前误差;
  • 前序误差;
  • 当前“温度”。
    在这里插入图片描述
    这个概率函数输入为前序误差、当前误差和当前“温度”,返回值为一个(0, 1)区间内的数值。返回值为1则表示选择更坏结果的可能性是100%,为0则代表不可能选择更坏的结果。将得到的数值与随机数比较,若随机数大于所得概率数值,则接受更坏的结果作为解。

Nelder-Mead算法

Nelder-Mead算法由JohnNelder和RogerMead提出[插图],是一种可以用于优化评估函数目标向量的优化算法,有时也称“下山单纯形法”或者“变形虫法”。Nelder-Mead算法比较容易可视化,也因此比较容易理解。在解空间搜索方面,该算法普遍具有较好的效果,所以可以作为学习高级训练算法较好的切入点。Nelder-Mead算法首先要构造一个“单纯形”。假设求解的问题是N维的,则该单纯形具有N+1个顶点,每个顶点都是解空间中的一个点,这些顶点构成的几何体称作“单纯形”,将每个顶点用直线连接起来,每个顶点都对应单纯形的一个角。假如要优化的向量是二维的,那么对应的单纯形就是一个三角形。单纯形本质上是一组可能解的集合,由于算法总是保存着N+1个可能的解,因此随着训练的进行,单纯形的形状也会不断改变。当训练到后期时,这个单纯形就会变得很小,此时,只要选取表现最好的顶点即可得到问题的解。
能够比贪心随机算法和爬山算法更快地收敛到误差较低的解,大多数时候比模拟退火算法都快
Nelder-Mead算法最初会假定一个解向量,如果无法对解向量的可能值做出有效的推断,不妨随机选取一个向量;而如果此前用其他算法训练过,就可以把之前算法的结果作为初始的猜测值,Nelder-Mead算法能够更进一步地调整解的具体值。毫无疑问,所谓“前一个算法”,当然也可以同样是Nelder-Mead算法。
就以二维解空间的问题为例,可得下列条件:

  • 维数=N = 2;
  • 顶点数=N+1 = 3(即一个三角形);
  • 单纯形:3个顶点的几何体,每个顶点都具有两个维度(因此形状是三角形)。
    Nelder-Mead算法的第一步是生成初始单纯形,这个单纯形会随着迭代的持续进行而改变形状。最开始的单纯形会取初始解向量作为一个初始顶点,将初始顶点的N个维度各自改变一定的量,即可分别得到其他N个顶点。通常情况下,初始单纯形的各边都设定为等长的。
    Nelder-Mead算法的一次迭代包含以下步骤。
    (1)分别找出对应的最差、次差和最佳顶点。
    (2)把最差的顶点向更好的一边“反射”。
    (3)反射成功,则扩张。
    (4)反射失败,则收缩

反射

反射
当前单纯形由实线构成,其中表现最差的顶点是Xh。要对Xh做反射变换,先取Xs和Xl的中点,记为c(如图所示)。还是以搜索队为例,表现最差的队员Xh将其他两人中点c相对自己所在的方向作为最佳前进方向,究其缘由是因为另外两者所在的位置表现比Xh好,因此有理由假定Xh身后都是更差的位置。由于要往另外两者方向前进,为了尽可能成为表现最好的搜索队员,Xh直接设定了一个远超另二者的目标点Xr。选取了点Xr之后,就要对其进行评估计算:该点确实表现更好吗?若是,则进行扩张操作;若不是,则进行收缩操作。

扩张操作

搜索队员Xh发现移动到点Xr有可能让他的表现更好,他就有点贪心了,想要跑得更远一点儿。如果我们正在深入峡谷,选取更远的点或许能够让我们更快地到达最佳点。在这里插入图片描述
搜索队员Xh计算出Xr是更好的位置,于是内心就有点儿膨胀了,不假思索地跳到了更远些的Xe点[插图]。当然,就算是“不假思索”也没啥关系,即使跳过头了,他也不过是回到了“最初的起点”,无非还是最差的那个罢了。本次迭代到这儿就结束了,下一次迭代将重新计算评估每个顶点的表现,并选出表现最差的那个。

收缩操作

当反射得到的点表现还不如当前最差点的时候,就要进行收缩操作
在这里插入图片描述
队员Xh想要移动到点Xr处,来提升自己的表现,但要是点Xr的表现比当前点还更差,最好的选择就成了向另二者靠拢。移动方向依然是另二者的中点所在方向,但由于原始目标破灭,这次移动就没有超过另二者的连线,而是到了单纯形内往另二者中点Xc方向上的某个点——但愿能够对表现有所提升吧。不管怎么说,到这儿,本次迭代也就结束了,下一次迭代会重新评估所有顶点并继续前面的操作。

Nelder-Mead算法的终止条件

对于像Nelder-Mead算法这类的迭代算法,把握住它们的终止时机很重要。一般来说有以下3个独立的判断标准:

  • 迭代次数超过了最大值;
  • 评估结果足够好了;
  • 各顶点相互之间离得足够近了。
    算法一旦终止,即可认为当前的最佳点就是问题的解。倘若顶点都挤作一团,还可以选择将最佳顶点作为顶点之一构造一个新的单纯形,重新运行一遍算法,前述的搜索过程又会再来一次。
    Nelder-Mead算法每次迭代只需要重新评估一个顶点,在这一点上它是比较高效的。在机器学习任务中,计算机大多数的时间都用在对训练数据进行评估上了;而在Nelder-Mead算法中,由于每次迭代只有最差点需要移动,因此也就只需要重新计算一个点就好了。除了反射、扩张和收缩操作之外,Nelder-Mead算法的很多旧式实现还存在一个被称为“萎缩”的步骤,但随后的研究发现这个步骤多此一举了,因此Nelder-Mead算法的绝大多数现代实现都不再包含此步骤。不像贪心随机算法、模拟退火算法和爬山算法那么“暴力”,Nelder-Mead算法一次可以基于多个位置进行协同搜索。这种协同搜索的策略在其他高级算法中也很常见,比如粒子群优化算法(Particle Swarm Optimization, PSO)和遗传算法(Genetic Algorithm, GA)都使用了大量的多点协同求解方
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值