老李是云岭山区的老寻宝人,他的寻宝方法朴素得近乎天真:沿着山坡闷头往上走,哪里高就往哪里拐。这套“爬山法”在平缓丘陵上屡试不爽,可一旦进入群山深处,他就栽了跟头——每回都信心满满地攀上一座小山包,站稳后抬眼一望,才发现对面山谷里耸立着一座更巍峨的金山。故事反复上演,老李终于醒悟:光靠一腔孤勇向上爬不够,他还得学会“逃逸”——从这座小山顶上跳出去的本事。后来,老李陆续掌握了四门手艺:雇直升机换座山重新爬(随机重启);允许自己偶尔走一段下坡路以绕开峭壁(模拟退火);随身带冒险笔记,近期走过的路绝不轻易回头(禁忌搜索);召集一支探险队,让队员互相拼接藏宝图(遗传算法)。他渐渐悟出,这些手艺的共同点只有一个——在前进的道路上,巧妙地撒下一把恰到好处的“扰动”。

岗位关联

学习局部最优逃逸策略,能让AI从业者在应对真实因果结构搜索时,突破贪心算法的天花板,大幅提升找到全局高评分DAG的可靠性与稳定性——这是从“能用”进阶到“可信”的关键一跃。

目录
  1. 可视化演示
  2. 爬山搜索局限
  3. 随机重启机制
  4. 模拟退火算法
  5. 禁忌搜索策略
  6. 遗传算法探索
  7. 扰动平衡之道
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示DAG评分地形的局部最优陷阱、模拟退火概率性接受劣解的逃逸过程与多种逃逸策略的收敛对比,交互调节温度与扰动强度。

本节配有交互动效(进入视口后自动加载,约 3KB)。

评分地形:局部最优陷阱 DAG评分地形布满局部最优峰形成吸引盆地 爬山搜索沿梯度上升易困于最近局部峰顶 score=f(DAG) 需逃逸机制穿越势垒探索更优结构

爬山搜索局限

老李最初的寻宝方法就一条原则:哪里高往哪走。他管这叫“闷头爬”,从不看后路,也不观察远景。这种诚实而固执的策略,让他在连绵的丘陵地带收获颇丰——毕竟单峰的山丘上,向上走总能到顶。可当他真正进入群山深处,地形变得复杂起来:山坡起伏不定,小峰连大峰,老李时常费了半天力气攀上一座低矮的山包,才发现自己站的位置根本望不见真正的金山。

在基于分数的因果发现中,这种“闷头爬”对应的是爬山搜索算法(Hill Climbing)。算法的操作强度极小:每一步只对当前的DAG结构施加一次单边操作——增加一条有向边、删掉一条有向边,或者反转一条边的方向。执行完操作后,用评分函数(如BIC、BDeu)对新结构打分,只有分数提升才接受这次移动,否则就试下一个邻居结构。评分函数在这里扮演着“海拔计”的角色,它告诉算法哪个因果结构更符合观测数据。

问题恰恰出在“只接受提升”这个贪心规则上。DAG结构空间的“地形”远比真实群山崎岖:结构之间的评分落差是离散的、跳跃的,无数局部峰顶散布其中。爬山算法只看得到自己邻域内一步之遥的结构,看不见远处更高的评分区域,于是常常在某个不起眼的小山包上停下脚步——这就是著名的“局部最优困境”。

老李摔了几次跟头后终于明白:光会闷头爬是不够的,他迫切需要一套“逃逸战术”,让自己从那些看似安稳的小山包上跳出去,继续寻找真正的金山。

∄G′∈N(G∗) such that Score(G′)>Score(G∗)\displaystyle \nexists G' \in \mathcal{N}(G^*) \ \text{such that} \ \text{Score}(G') > \text{Score}(G^*)
这个式子的意思是:在邻域里找不到任何一个评分更高的结构时,就宣布当前位置为“局部最优”。
  1. 结构与邻域$G^*$ 是当前搜索所在的DAG结构,$\mathcal{N}(G^*)$ 是它的邻域——所有通过一次单边操作(增边、删边、反边)能得到的新DAG的集合。
    N(G∗)={G′∣single-edge operation}\mathcal{N}(G^*) = \{ G' \mid \text{single-edge} \ \text{operation} \}
  2. 两两评分比较$\text{Score}(\cdot)$ 是结构评分函数,将当前结构 $G^*$ 与任意邻居 $G'$ 分别代入,比较分数高低。
    Score(G′)≤Score(G∗),∀G′∈N(G∗)\text{Score}(G') \le \text{Score}(G^*), \quad \forall G' \in \mathcal{N}(G^*)
  3. 局部最优判定$\nexists$ 表示“不存在”。当没有任何邻居的分数严格高于当前结构,就称 $G^*$ 为局部最优——注意,这只是“周围最高”,并非“全局最高”。

随机重启机制

老李的第一次战略升级来自于一次偶然的教训。那天他攀上一座小山顶,发现前方是断崖,回头又是一路下坡,正绝望时,山下的向导用望远镜给他打信号:往东三公里的那座山,高得多!于是老李雇了一架直升机,将自己空投到东边的山脚下,重新开始爬。从此他悟出一个笨而有效的道理:一座山爬到头还是不行,那就多换几座山试试。

随机重启策略正是这种“换座山”思想的算法化。它彻底放弃单一路线的执念:从多个随机初始DAG结构出发,各自独立执行爬山搜索,每一次搜索都会收敛到一个(可能是局部的)最优结构。当所有搜索结束后,将这些局部最优解放在一起,挑出评分最高的DAG作为最终答案。

这种策略本质上是“用独立重复实验换概率”:单次爬山陷入局部最优的概率很高,但多次独立随机起点的爬山全部落在差的山包上的概率则指数级下降。不过它并非没有代价——每增加一次重启,计算量就线性地增加一轮完整的爬山过程。因此,重启次数 $R$ 需要在计算预算与成功率之间仔细权衡。在工程实践中,人们常常设定一个时间上限,或者当连续若干次重启的评分不再提升时,提前终止搜索。

老李的直升机策略很快就见效了:虽然每次都得重新买机票,但找到金山的概率翻了好几倍。随机重启教会他的道理是——如果一条路走不通,与其死磕,不如大大方方换一个起点,让运气站在概率这一边。

Gbest=arg⁡max⁡i=1R Score(Ghill(i))\displaystyle G_{\text{best}} = \arg\max_{i=1}^{R} \ \text{Score}\big(G_{\text{hill}}^{(i)}\big)
这个式子的意思是:一共重启 $R$ 次,每次爬山得到一个局部最优结构,最后从中选出评分最高的那个。
  1. 独立爬升$G_{\text{hill}}^{(i)}$ 表示第 $i$ 次从随机初始结构出发、经爬山算法收敛后得到的局部最优DAG。
    Ghill(i)=HillClimb(Ginit(i))G_{\text{hill}}^{(i)} = \text{HillClimb}\big(G_{\text{init}}^{(i)}\big)
  2. 逐次评分$\text{Score}\big(G_{\text{hill}}^{(i)}\big)$ 将每个爬行结果的评分函数值依次求出来,作为横向对比的依据。
  3. 取最大值下标$\arg\max_{i=1}^{R}$ 的含义是“找出使评分达到最大的那个下标 $i$”,$G_{\text{best}}$ 最终取这 $R$ 个结果中分数最高者。

模拟退火算法

直升机虽好,但烧钱太狠。老李开始琢磨一种更省钱的替代方案:能不能不换山峰,而是允许自己先往山下走几步,绕个远路再爬上去?困难在于,他拿不准什么时候该下坡、什么时候该咬紧牙关硬着头皮往上顶。这时,一位冶炼匠给了他启发:金属在高温下原子剧烈振动,冷却过程中逐渐安顿到低能排布——如果冷却太快,原子会被锁在高能缺陷中,这就是“退火”的智慧。

模拟退火算法(Simulated Annealing)正是受金属退火过程启发而设计的。它与爬山算法的核心区别在于:当候选结构的评分比当前结构更低时,爬山算法会直接拒绝,而模拟退火则以一个受“温度”控制的概率接受它。温度高时,算法愿意接受较差的解,从而大步探索远处的地形;温度逐渐降低后,接受差解的概率变小,算法越来越专注于精细爬坡。这个过程被称为“冷却调度”。

这个想法可以提炼为一个简洁的概率公式。设当前结构为 $G_{\text{current}}$,某个邻居结构为 $G_{\text{candidate}}$,两者评分之差为 $\Delta E$;如果 $\Delta E > 0$,说明候选解更差,此时以概率 $e^{-\Delta E / T}$ 接受它。当温度 $T$ 很大时,指数趋近1,几乎什么都敢试;当 $T$ 趋近0时,概率趋近0,算法退化为贪心爬山。正是这个“偶尔向下走”的机制,让算法得以翻越评分地形的鞍部,抵达全局最高峰。

老李把温度想象成自己的“冒险值”:刚进山时精力充沛、好奇心爆棚,什么岔路都敢试;随着天色渐晚(温度降低),他变得沉稳谨慎,只走最有把握的路。最终,他总能在傍晚前抵达当天视野里最高的那座山峰——这正是模拟退火的精妙之处。

P(accept)=exp⁡ ⁣(−ΔET)\displaystyle P(\text{accept}) = \exp\!\left(-\frac{\Delta E}{T}\right)
这个式子的意思是:候选解越差($\Delta E$ 越大)、温度越低($T$ 越小),接受这个较差解的概率就越小。
  1. 能量差$\Delta E = \text{Score}(G_{\text{current}}) - \text{Score}(G_{\text{candidate}})$。当候选结构评分更低时 $\Delta E > 0$;评分更高时 $\Delta E < 0$,此时无条件接受。
    ΔE=Score(Gcurrent)−Score(Gcandidate)\Delta E = \text{Score}(G_{\text{current}}) - \text{Score}(G_{\text{candidate}})
  2. 温度参数$T > 0$ 是当前温度,由冷却调度决定,随搜索进程逐步减小,控制着算法的冒险程度。
  3. 指数计算$\exp(\cdot)$ 是自然指数函数,输出范围是 $(0,1)$。$\Delta E$ 越大接受概率越小,$T$ 越大接受概率越大,两者共同决定“下坡”的胆量。

禁忌搜索策略

有了随机重启和模拟退火,老李的寻宝事业蒸蒸日上,但他又发现一个新毛病:有时明明已经走过了某条死路,却总是不自觉地绕回来,在山脚下反复打转。为了治这个“原地转圈”的毛病,老李买了一本探险笔记,每走过一段坡路就记上一笔,并给自己立规矩:最近三天走的路,坚决不回头走。这笔记让他的探索路线多了几分“记性”。

禁忌搜索算法(Tabu Search)的核心思想正是这样一本“探险笔记”。它把最近执行过的操作记录在一张禁忌表中——比如刚才在节点A到节点B之间添加了一条边,那么在接下来的若干次迭代中,“删除A到B的边”这一操作会被暂时禁用,如图中所示,从而防止搜索立即退回刚才的状态。这种有记忆的搜索路径,迫使算法不断开拓新的邻域,而不是在一个小区域里来回摆动。

禁忌表的核心是“禁忌期限”(tenure):一个操作被禁多久。期限太短,记忆形同虚设;期限太长,又可能错失良机。因此,禁忌搜索还配备了一条“特效规则”(aspiration criterion):如果某个被禁的操作事实上能带来比历史最佳评分更高的结构,那么破例允许多次,就像老李发现笔记上那条“死路”其实藏着一道暗门通向金矿时,果断打破自己的规矩。真正的智慧不是机械守规矩,而是知道何时该守、何时该破。

老李的探险笔记越记越厚,但他发现真正管用的不是笔记本身,而是他给自己立下的规矩和偶尔破例的判断力。禁忌搜索让他明白:好的探索者不但要有勇气走新路,还要有记性不去重复踩坑。

Tabu(e,t)=1[t−tlast(e)≤tenure(e)]\displaystyle \text{Tabu}(e, t) = \mathbb{1}\big[t - t_{\text{last}}(e) \le \text{tenure}(e)\big]
这个式子的意思是:如果某个操作距离最近一次被执行的代数还处于禁忌期限内,就标记为1(禁止),否则为0(允许)。
  1. 操作与时刻$e$ 指代某个具体的边操作,比如“添加边 $A \to B$”;$t$ 是当前迭代代数,记录搜索进行到第几步。
  2. 最近执行时刻$t_{\text{last}}(e)$ 是操作 $e$ 最近一次被执行的迭代代数;$\text{tenure}(e)$ 是它的禁忌期限,即需要被禁多少代。
    tlast(e)=last step when e was appliedt_{\text{last}}(e) = \text{last step when } e \text{ was applied}
  3. 指示函数$\mathbb{1}[\cdot]$ 是0/1指示函数:条件成立输出1,该操作被禁忌;否则输出0,可以执行。特效规则在条件成立时仍可破禁放行。

遗传算法探索

单打独斗的日子终于结束了。老李发现,自己的经验再丰富,也敌不过十个队员各自带着不同的观察。于是他召集了一支探险队,让大家分头进山,各自画出自己眼中的“藏宝图”。回到营地后,队员们把地图摊在桌上互相拼接:你看到了东坡的溪流,我认出了西岭的峭壁,两张图一交叉,一条全新的路线浮现出来。

遗传算法(Genetic Algorithm)就是把老李的探险队搬进了计算机。候选的DAG结构不再是一个个体,而是一整个“种群”。每个个体由邻接矩阵结构编码,评分函数则化身“适应度函数”——适应度高的个体有更大的概率被选中,作为“父本”“母本”参与进化。三种遗传算子驱动着种群演化:选择(按适应度高低配比父本)、交叉(交换两个DAG的部分边集,生成子代结构)、变异(随机增删几条边引入新鲜性)。

这里有一个关键细节:基于分数的因果发现中,适应度函数往往不能只看评分。一个边数过多的DAG即使拟合得再好,也可能过拟合数据。因此,人们在适应度中引入稀疏性惩罚项——评分越高越好,边数越多越糟,$\lambda$ 就是平衡两者的旋钮。这样一来,遗传算法不仅是在搜索评分高的结构,更是在搜索“评分又高、结构又简约”的DAG。经过一代又一代的进化,整个种群像老李的探险队一样,把散布在群山各处的线索一点点拼成完整的金山地图。

探险队的经验在代际之间不断积累和重组。老李最终发现,最优的路线不是某个队员独自找到的,而是整个群体在前人的肩膀上协同进化出来的——遗传算法的魅力正在于此。

Fitness(G)=Score(G)−λ⋅∣E(G)∣\displaystyle \text{Fitness}(G) = \text{Score}(G) - \lambda \cdot |\mathcal{E}(G)|
这个式子的意思是:一个DAG个体的适应度等于它的结构评分减去边的数量惩罚,$\lambda$ 越大,算法越偏好边数少的简约结构。
  1. 适应度主干$\text{Fitness}(G)$ 是个体 $G$ 的适应度,它是选择算子挑选父本的直接依据。
  2. 稀疏惩罚项$|\mathcal{E}(G)|$ 是图 $G$ 中有向边的总条数;$\lambda > 0$ 是惩罚系数,每多一条边就扣掉 $\lambda$ 分。
  3. 进化选择依据适应度越高,个体被选中、参与交叉与变异的概率越大;连续迭代后,种群整体向高适应度区域收敛。
    P(select G)=Fitness(G)∑G′Fitness(G′)P(\text{select } G) = \frac{\text{Fitness}(G)}{\sum_{G'}\text{Fitness}(G')}

扰动平衡之道

四个战术学完,老李坐在山腰上复盘,突然悟出一个通了天的大道理:随机重启是“大扰动”——直接换座山;模拟退火是“温度扰动”——概率性下坡;禁忌搜索是“记忆扰动”——制造有方向的绕路;遗传算法是“种群扰动”——靠多样性持续撞出新的可能性。它们形式各异,本质却完全一致:在搜索过程中,恰到好处地注入噪声,打破已经稳定的局面,让探索的触角伸向未知区域。

在机器学习中,这个道理被称作“探索与利用的权衡”。探索(Exploration)是敢闯未知区域,利用(Exploitation)是深挖当前最优区域。如果只探索不利用,算法就成了乱撞的没头苍蝇;如果只利用不探索,就回到爬山算法的老路,困于局部最优。所有逃逸策略本质上都是在动态调整这两者的比例,而它们共同遵守一条经验规律:搜索前期多探索,尽可能覆盖广阔的结构空间;搜索后期多利用,把已有高分结构打磨到极致。

这条规律可以形式化为“探索强度随搜索进程衰减”的模型。初期扰动强度大,算法像是手持强光手电在黑暗中扫视整片山谷;随着迭代进行,探索强度指数下降,手电光束逐渐收窄,最终汇聚到当前最优解的精细邻域上。成功率与稳定性往往是一对矛盾:扰动太强,结果稳定但可能永远飘在随机搜索附近;扰动太弱,稳定性高却很容易再次陷入局部最优。优秀的算法设计,就是在刀刃上寻找那个让成功率和稳定性同时令人满意的平衡点。

老李的金矿最终找到了。他总结说:四门手艺没有哪一招是万能的,关键是把它们融会贯通,在对的时间用对强度的扰动。这座山爬不上去了?换座山重新来;前面的路不确定?留一丝冒险的余温;同样的坑踩过了?记下来别再踩;一个人搜不动了?带上队伍一起探索。所谓局部最优逃逸,说到底,就是一门“何时该打破安稳、何时该守住成果”的平衡艺术。

Exploration(t)=α⋅e−βt\displaystyle \text{Exploration}(t) = \alpha \cdot e^{-\beta t}
这个式子的意思是:探索强度从初始值 $\alpha$ 开始,按指数规律随时间 $t$ 衰减,$\beta$ 控制衰减的快慢。
  1. 初始强度$\alpha$ 是搜索起始时刻的探索强度,控制搜索早期“撒沙子”的力度,$\alpha$ 越大,初期越敢于大范围扰动。
  2. 时间与衰减率$t$ 表示搜索进程(如已迭代的次数或已消耗的时间比例),$\beta > 0$ 是衰减系数,$\beta$ 越大,探索强度消退越快。
  3. 指数衰减$e^{-\beta t}$ 随着 $t$ 增大从1单调降到0,保证探索强度先高后低、平滑过渡,不会突然中断。
    lim⁡t→∞Exploration(t)=0\lim_{t \to \infty} \text{Exploration}(t) = 0

AI 应用场景

基因调控网络推断

阶段:结构搜索与模型选择阶段

解决的问题:单细胞RNA测序数据维度高、噪声大,基于评分的因果发现容易陷入局部最优,得到错误的基因调控关系。传统爬山算法往往找到的“最佳”网络只是局部最优,无法反映真实调控通路。采用随机重启与模拟退火结合的逃逸策略后,算法能在广阔的DAG空间中反复探索,显著提高找到全局高评分网络的概率。

具体案例:某生物信息团队用模拟退火搜索果蝇胚胎发育相关基因的调控网络。在2000个基因的候选空间里,单次爬山收敛出的网络仅覆盖了已知调控路径的30%,而引入带冷却调度的模拟退火后,算法允许在高温阶段接受大量较差结构,最终找到的调控网络与文献验证路径的重合度提升到72%。

微服务故障根因定位

阶段:因果图结构学习阶段

解决的问题:大型微服务系统中,一个服务异常可能由多个上游服务引发,故障传播图(因果DAG)的结构搜索同样面临局部最优问题:贪心搜索容易把根因指向错误的邻近服务。禁忌搜索的记忆机制恰好适合这类场景——它不允许算法刚“怀疑”过某个服务又立即翻案,避免了在几个可疑节点间来回振荡。

具体案例:运维团队积累了3000+故障事件日志,用禁忌搜索在服务依赖图上学习故障传播结构。相比爬山算法58%的根因定位准确率,禁忌搜索凭借禁忌表与特效规则,将准确率提升至81%,并且定位时间从原先手工排查的40分钟缩短到8分钟。

Python 演示

python117 行
import numpy as np def random_dag(n):    perm = np.random.permutation(n)    adj = np.zeros((n, n))    for i in range(n):        for j in range(i + 1, n):            if np.random.rand() < 0.3:                adj[perm[i], perm[j]] = 1    return adj def is_dag(adj):    n = adj.shape[0]    indeg = adj.sum(axis=0)    q = [i for i in range(n) if indeg[i] == 0]    cnt = 0    while q:        i = q.pop()        cnt += 1        for j in range(n):            if adj[i, j] > 0:                indeg[j] -= 1                if indeg[j] == 0:                    q.append(j)    return cnt == n def score(adj, data):    n = adj.shape[0]    rss = 0.0    for j in range(n):        parents = np.where(adj[:, j] > 0)[0]        if len(parents) == 0:            rss += (data[:, j] ** 2).sum()        else:            x = np.column_stack([np.ones(len(data)), data[:, parents]])            beta, _, _, _ = np.linalg.lstsq(x, data[:, j], rcond=None)            resid = data[:, j] - x @ beta            rss += (resid ** 2).sum()    return -(rss + 0.5 * adj.sum()) def neighbors(adj):    n = adj.shape[0]    res = []    for i in range(n):        for j in range(n):            if i == j:                continue            if adj[i, j] == 0:                cand = adj.copy()                cand[i, j] = 1                if is_dag(cand):                    res.append(cand)            else:                cand = adj.copy()                cand[i, j] = 0                res.append(cand)    return res def hill_climb(adj, data, max_steps=60):    best_adj = adj.copy()    best_s = score(adj, data)    for _ in range(max_steps):        improved = False        for cand in neighbors(best_adj):            s = score(cand, data)            if s > best_s:                best_s = s                best_adj = cand                improved = True        if not improved:            break    return best_adj, best_s def random_restart(n, data, restarts=8):    best_adj, best_s = None, -np.inf    for _ in range(restarts):        adj = random_dag(n)        adj, s = hill_climb(adj, data)        if s > best_s:            best_s = s            best_adj = adj    return best_adj, best_s def simulated_annealing(adj, data, steps=300, T0=1.0):    cur_adj = adj.copy()    cur_s = score(adj, data)    best_adj, best_s = cur_adj.copy(), cur_s    for t in range(steps):        T = T0 * (1.0 - t / steps)        nbrs = neighbors(cur_adj)        cand = nbrs[np.random.randint(len(nbrs))]        cand_s = score(cand, data)        if cand_s > cur_s or np.random.rand() < np.exp((cand_s - cur_s) / max(T, 1e-6)):            cur_adj, cur_s = cand, cand_s        if cur_s > best_s:            best_s = cur_s            best_adj = cur_adj.copy()    return best_adj, best_s if __name__ == "__main__":    np.random.seed(42)    n = 4    data = np.zeros((200, n))    data[:, 0] = np.random.randn(200)    for j in range(1, n):        data[:, j] = 0.8 * data[:, j - 1] + 0.2 * np.random.randn(200)     init = random_dag(n)    hc_adj, hc_s = hill_climb(init.copy(), data)    rr_adj, rr_s = random_restart(n, data, restarts=8)    sa_adj, sa_s = simulated_annealing(init.copy(), data)     print("爬山算法评分: %.2f" % hc_s)    print("随机重启评分: %.2f" % rr_s)    print("模拟退火评分: %.2f" % sa_s)    print("爬山边数=%d, 重启边数=%d, 退火边数=%d"          % (hc_adj.sum(), rr_adj.sum(), sa_adj.sum()))
目的:在4节点合成数据上对比爬山、随机重启与模拟退火三种结构搜索策略,展示局部最优逃逸技巧带来的评分提升。
  1. def is_dag(adj)通过拓扑排序判断邻接矩阵是否构成有向无环图,保证每一步搜索都停留在合法的因果结构空间。
  2. def score(adj, data)实现简化版BIC评分:对每个变量用其父节点做线性回归,累加残差平方和,再减去边数惩罚。
  3. def neighbors(adj)生成所有“单边操作”邻居:增边或删边,同时用 is_dag 过滤掉成环的非法结构。
  4. if s > best_s:爬山算法的贪心核心:只有评分更高才接受邻居,这一步正是导致局部最优的根源。
  5. def random_restart(n, data, restarts=8)用8个随机初始DAG分别爬山,再选出评分最高的结果,实现“换座山重新爬”。
  6. T = T0 * (1.0 - t / steps)模拟退火的线性冷却调度:温度从T0递减到接近0,控制整个搜索过程的冒险程度。
  7. np.exp((cand_s - cur_s) / max(T, 1e-6))当候选解更差时,以此概率接受它;这正是模拟退火能从局部最优“逃逸”的数学开关。

常见误区

误区以为局部最优和全局最优在结构上很接近,就像爬坡时偏离山顶几步,稍微调整就能到达。

正解在DAG结构空间中,“偏离几步”可能意味着完全不同的拓扑结构——不同的边集合、不同的父子关系。局部最优是评分地形中一座独立的山峰,与全局最优之间可能隔着深谷;从局部最优到全局最优,往往不是微调,而是推倒重来式的重组。

为何常见:受连续优化直觉影响,人们习惯把解空间想象成平滑曲面;但离散的DAG结构空间是跳跃的,一步之间边增删就会让结构发生质的变化。

误区既然随机重启有效,那就把重启次数设得大大的,比如一万次,总能碰到全局最优。

正解每次重启都伴有一次完整的爬山过程,计算成本线性增长。一万次重启意味着比一百次重启多花100倍时间,收益却逐步递减——当重启次数超过某个阈值后,再多的重启也只是在重复探索已被覆盖的区域。实践中应结合时间预算和“连续若干次重启评分无提升则提前停止”的策略。

为何常见:人们只看到重启增加探索覆盖面,忘了每次探索都要付账;在工程中,算力从来不是免费的。

误区模拟退火能逃出局部最优全靠温度,那把初始温度设成无穷大,是不是就能完美探索整个空间?

正解温度过高时接受概率接近1,算法会疯狂接受各种随意甚至很差的结构,搜索退化为随机游走,浪费大量迭代步数。温度的核心作用不是“高”,而是“从高到低平滑冷却”;初始温度只需高到让算法有足够概率跨出当前盆地即可,过高的温度反而让收敛变得极其缓慢。

为何常见:初学者把“允许冒险”误当成“越大越好”,忽略了模拟退火成功的关键在于温度随时间下降的节奏,而不是单点的温度值。

误区禁忌表越长,记忆越牢,算法越不会走回头路,那么不如把最近的所有操作全部禁掉。

正解禁忌期限过长会让大量合法操作被禁,邻域搜索可选择的范围急剧收窄,可能导致算法长时间在原地徘徊甚至错过最优解。禁忌期限需要与问题规模匹配——通常取与图中节点数或边数同量级的值,并通过实验调参。

为何常见:人们容易把“记性好”等同于“效果好”,却忽略了禁忌的本质是帮助搜索逃离局部,而过度的禁忌会反过来束缚探索的自由。

误区变异是引入新结构的来源,那变异概率设大一点,比如每次随机改掉一半的边,种群多样性一定很高。

正解过大的变异概率会随机破坏优良个体积累的“优秀基因”(好的边结构),使得种群在接近全局最优时反复被拉回随机状态,搜索退化为随机猜测。变异概率通常设置得很小(如0.01~0.1),它扮演的是“防止早熟”的配角,而主角是选择与交叉的协同进化。

为何常见:混淆了“多样性”与“破坏性”的界限;适度的噪声维护多样性,过度的噪声则摧毁了进化积累的知识。

面试题

Q1. 爬山搜索算法在DAG结构空间中陷入“局部最优”的直接原因是?

单选题 · easy

  • A.评分函数计算速度太慢
  • B.只接受评分提升的邻居← 正确答案
  • C.初始DAG结构必然错误
  • D.数据样本量不足导致欠拟合

答案:B。爬山算法每一步只接受评分更高的邻居结构,这种贪心策略使它看不见远处评分更高的区域,一遇到周围没有更高分的小峰就会停下。A是性能问题并非停滞原因;C与初始结构无关,随机起点也可正常工作;D虽会影响评分的准确性,但不是“陷入局部最优”的直接原因。

Q2. 模拟退火算法中,温度T在搜索过程中的变化规律是?

单选题 · mid

  • A.恒定为常数便于计算
  • B.不断升高以鼓励大胆探索
  • C.从高温逐渐冷却至低温← 正确答案
  • D.与评分函数值严格成正比

答案:C。模拟退火模拟金属退火过程,温度从初始高温逐步冷却到接近0。高温期接受较差解的概率大、探索范围广,低温期算法趋于保守、精细爬坡。A恒定温度会失去“先探索后利用”的平衡;B是加热而非退火;D温度与评分无直接数量关系。

Q3. 以下哪些是文中提到的局部最优逃逸策略?

多选题 · mid

  • A.随机重启策略← 正确答案
  • B.模拟退火算法← 正确答案
  • C.禁忌搜索方法← 正确答案
  • D.主成分分析技术

答案:ABC。随机重启、模拟退火、禁忌搜索都是本文讲述的局部最优逃逸手段;它们分别从多起点、概率性下坡与记忆回避三个角度打破贪心困境。主成分分析是降维算法,不属于结构搜索的逃逸策略。

Q4. 随机重启策略从多个初始DAG出发分别爬山,最终答案是?

单选题 · easy

  • A.第一个收敛的结果
  • B.评分最高的那个结果← 正确答案
  • C.所有结果的简单平均
  • D.最后一个收敛的结果

答案:B。随机重启将每次独立爬山的局部最优结构放在一起,用评分函数横向比较,最终选择分数最高的那一个。它不取时间顺序上的首尾,也不做结构上的平均——DAG结构的平均没有意义,评分才是硬指标。

Q5. 禁忌搜索中“特效规则”允许什么?

单选题 · mid

  • A.无条件接受任何邻居
  • B.禁忌操作创纪录则允许破禁← 正确答案
  • C.无限扩大禁忌表的长度
  • D.完全跳过邻域搜索

答案:B。特效规则(Aspiration Criterion)的典型设定是:某个操作虽然仍在禁忌期内,但如果执行它能够产生历史最佳评分以上的结构,就打破禁忌放行。A会让禁忌表形同虚设;C、D与特效规则无关。

Q6. 遗传算法中,适应度函数中的稀疏惩罚系数λ的主要作用是?

单选题 · mid

  • A.控制变异率的数值
  • B.惩罚边数鼓励简约结构← 正确答案
  • C.调节交叉概率大小
  • D.放大评分数值的差异

答案:B。适应度 $\text{Fitness}(G) = \text{Score}(G) - \lambda|\mathcal{E}(G)|$ 中,$\lambda$ 惩罚的是边数,防止算法一味追求拟合而生成过度复杂的因果图。它不直接控制变异率或交叉概率,也不改变评分的相对差异,而是引导种群走向简洁而高分的结构。

Q7. 关于模拟退火的接受概率,下列说法正确的是?

多选题 · mid

  • A.温度越高越可能接受差解← 正确答案
  • B.差值越大接受概率越低← 正确答案
  • C.温度趋零时退化为爬山← 正确答案
  • D.概率数值永远小于一

答案:ABC。温度高时指数项接近1,接受差解概率大;两结构评分差越大,指数越小,接受概率越低;温度趋近0时接受概率趋近0,算法只接受更好的解,退化为爬山。但概率并非永远小于一——当候选解更好时接受概率按1处理。

Q8. 在基于分数的因果发现中,“DAG结构空间”是指?

单选题 · easy

  • A.所有DAG构成的集合← 正确答案
  • B.数据集的存储空间
  • C.因果变量的取值范围
  • D.评分函数的参数空间

答案:A。基于分数方法的搜索发生在由所有可能的有向无环图组成的结构空间中,每一步单边操作都是在该空间内从一个DAG移动到另一个DAG。结构空间与数据存储、变量取值范围、评分函数参数是三个完全不同的概念。

Q9. 关于探索与利用的权衡,文中提到的规律包括?

多选题 · hard

  • A.扰动强度随时间递减是常见策略← 正确答案
  • B.搜索前期应多探索后期多利用← 正确答案
  • C.随机重启属于高扰动策略← 正确答案
  • D.最优策略是不做任何扰动

答案:ABC。探索强度 $\alpha e^{-\beta t}$ 随时间指数衰减,体现“前期广探索、后期精利用”的通用规律;随机重启属于大扰动高探索策略。不做任何扰动就是爬山算法本身的困境,显然不是最优策略。

Q10. 禁忌搜索中“禁忌表”存储的核心内容是?

单选题 · mid

  • A.全部被访问过的结构
  • B.近期边操作及禁忌期限← 正确答案
  • C.评分最高的历史结构
  • D.数据集的统计特征

答案:B。禁忌表记录的是近期执行过的边操作(增边、删边、反边等)以及它们剩余的禁忌代数,用来阻止算法在下一步立即逆行回退。它不存储全部历史结构,也不只保存最高分结构,更与数据集的统计特征无关。