南山坡上,老陈的果园年年丰收,可三十年前这里还是一片酸涩的野苹果林。让果园脱胎换骨的,不是买来什么神奇品种,而是一套“代代优选”的土办法:每年秋天,他挨棵尝果,把最甜的几棵树留下当母树;春天,他用这些树的花粉互相授粉,还把某些枝条嫁接起来;偶尔一场霜冻后冒出怪异的新芽,他也不急着剪掉,先养着看看。如此往复,果园一代比一代甜。AI 里的进化算法做神经架构搜索(NAS),玩的是同一套把戏:把网络设计看成苹果品种,让一群候选结构像果树一样“繁殖、变异、竞争”,几代下来,性能最优的结构自然胜出。本文就用这片果园,带你从头看懂进化算法 NAS 的每个环节。
掌握进化搜索的思路与工程细节,你就能把“手工调结构”升级为“自动选架构”,在模型压缩与自动化设计场景中省下大量人力。
可视化演示
通过种群进化选择、变异交叉操作、适应度收敛曲线三个模式,展示进化算法如何通过物竞天择机制搜索最优神经网络架构。
本节配有交互动效(进入视口后自动加载,约 3KB)。
种群进化与选择机制 将网络架构编码为基因序列通过变异交叉选择迭代优化种群逐代提升适应度 锦标赛选择从种群随机抽取k个选最优者k越大选择压力越强收敛越快但多样性降低 Population → Selection → Crossover/Mutation → New Population 进化算法借鉴生物进化机制在庞大搜索空间中高效探索
进化算法原理
三十年前老陈的果园里长着上百棵野苹果树,果子又小又涩。他没有一棵棵去嫁接改良,而是定下一条祖训:每年把最甜的几棵留下做母树,其余砍掉当柴火,来年只种这些母树的后代。几年过去,果园整体甜度明显提升——甜味就是选育的信号,满园果树就是候选的种群。
进化算法做神经架构搜索(NAS),把老陈的苹果园搬进了计算机:一个「种群」里放着若干候选网络结构,每个结构就是一棵果树;「适应度函数」负责给结构打分,相当于老陈尝一口苹果判断甜度;每一轮「进化」都按分数选父母、生后代、加变异,生成新的种群,如此代代相传。
相比随机搜索或网格搜索,进化算法的核心优势是有选择压力:好结构被选作父母的概率更高,后代在好结构附近探索,搜索逐步聚焦到高分区。适应度函数通常同时看精度和代价,比如:
1. 候选结构 \(a_i\):种群里的第 \(i\) 个网络结构,类比第 \(i\) 棵果树的全部性状。
2. 验证准确率 \(\text{Acc}(a_i)\):该结构在验证集上训练后的正确率,类比果实甜度评分。
3. 代价项 \(C(a_i)\):参数量或推理耗时,类比果树占用的土地和肥料。
4. 权衡系数 \(\lambda\):控制「精度」和「代价」的权重,\(\lambda\) 越大越偏爱轻量结构。
老陈的果园证明了「简单规则 + 迭代」的威力。进化 NAS 同样不预设最优答案,只设好打分标准和变异翻新机制,剩下的交给一代代「物竞天择」。理解了种群与适应度,你就拿到了通往后续所有环节的钥匙。
搜索空间设计
老陈每年播种前要做一个决定:果园里可以出现哪些品种?他圈定一份「候选菜单」——红富士、嘎啦、青苹果,砧木用矮化或乔化,树形用开心形或纺锤形。菜单之外的新品种,老陈再喜欢也种不出来,因为种子库里没有。这个「菜单」决定了他能在多大范围内改良果园。
NAS 里的「菜单」叫搜索空间。它定义了进化算法能搜索哪些网络结构:网络分成若干层或单元(cell),每个位置从候选算子集合 \(\mathcal{O}\) 里选一个算子(如 3×3 卷积、5×5 卷积、跳跃连接、池化),再决定层间连接方式。你圈定的算子越多、层数越多,搜索空间指数膨胀,但同时也给进化留出更多可能性。
搜索空间太大,进化会找不到北;太小,则可能装不下高性能结构。工程上常用单元式搜索:只搜索一个基础单元的结构,再堆叠若干次组成完整网络,把指数级空间压缩到可管理的规模。编码方式随之产生:
1. 长度 \(L\) 的编码 \(A\):一个候选网络被表示为 \(L\) 个位置的序列,类比一株果树的「性状清单」。
2. 候选算子集合 \(\mathcal{O}\):每个位置可选的算子类型,类比候选品种菜单。
3. 第 \(i\) 层算子 \(o^{(i)}\):编码第 \(i\) 个位置的选择,决定该层做什么运算。
4. 搜索空间规模 \(|\mathcal{S}|\):\(L\) 层每层有 \(|\mathcal{O}|\) 种选择,总量为 \(|\mathcal{O}|^L\)。
老陈的菜单越丰富,果园的「天花板」越高,但管理成本也越高。NAS 同理:搜索空间设计是进化搜索的边界,编码的好坏直接决定变异和交叉能不能产生有效的后代。先问自己「允许出现什么样的网络」,再问「如何找到最好的那个」。
变异与交叉
老陈的果园能不断变甜,靠两个动作:一是把「耐寒砧木」和「高糖接穗」嫁接,让一棵树同时拥有两家优点——这是「交叉」;二是有时一场倒春寒后,某个枝条的芽突然变异,结出带玫瑰香气的果子——这是「变异」。老陈从不觉得怪芽是麻烦,反而把它当成免费的品种试验。
NAS 里的交叉,是把两个父本结构的编码按某个位置切开再拼接:前半段取自父本 A,后半段取自母本 B,生成一个同时继承两段特征的新结构。变异则是随机改动编码的某一位——把某层的卷积换成池化,或把 3×3 换成 5×5——从而在局部引入全新变量。交叉负责「组合已有优点」,变异负责「探索未知区域」。
单点交叉是最简单的形式,设交叉点为 \(k\),子代 \(C\) 的前 \(k\) 位来自父本 A、后 \(L-k\) 位来自母本 B:
1. 父本 \(A\) 与母本 \(B\):从父代种群中选出的两个优秀个体。
2. 交叉点 \(k\):随机选一个位置,把编码切成两段。
3. 子代编码 \(C\):前 \(k\) 位来自 \(A\),后 \(L-k\) 位来自 \(B\)。
4. 变异运算:对 \(C\) 的每一位,以概率 \(p_m\) 从 \(\mathcal{O}\) 中重新选择算子,得到最终子代 \(C'\)。
嫁接和变异,一个守成、一个拓新,正是进化搜索中「探索与利用」的平衡。老陈从不只嫁接(否则果园会千篇一律),也不只等变异(否则找不到稳定方向)。设计进化算法时,同样要拿捏交叉与变异的配比——它们是驱动种群前进的两个轮子。
选择策略
果园最关键的环节,是决定「让谁生孩子」。老陈年轻时用轮盘赌:把每个品种的甜度画成转盘上的扇形面积,甜度越高扇区越大,指针停下指向谁就选谁——甜树被选中的次数自然多,但酸树偶尔也能入选,保住多样性。后来他嫌转盘太随机,改用锦标赛:随手拉出三棵树,当场比甜,最甜的那棵胜出。
选择策略决定了进化方向与多样性的平衡。NAS 中常见两类:轮盘赌选择(适应度比例选择)按适应度占比给出被选概率;锦标赛选择随机抽 \(k\) 个个体、留下其中适应度最高者,实现简单且不受适应度尺度影响。如果只留最好的,种群会迅速单一化,容易困在局部最优;如果完全不筛,进化就退化成随机搜索。
轮盘赌选择概率:
1. 个体 \(a_i\) 的适应度 \(F(a_i)\):打分环节给出的分数,类比甜度值。
2. 分母总适应度:把当代所有 \(N\) 个个体的分数相加,作为归一化基准。
3. 选中概率 \(P(a_i)\):分数占比越高,被选为父母的概率越大,所有个体概率之和为 1。
4. 精英保留:将分数最高的 \(E\) 个个体直接带入下一代,保证不退化。
老陈后来把两招组合用:先用锦标赛快速选出好父母,再额外留几棵「镇园母树」——这就是精英保留。选择不是简单「杀掉差的」,而是一套分配生育权的策略,它决定种群往哪个方向演化、又保住多少多样性。
适应度评估
老陈最头疼的不是选种,而是试吃。一棵树要长四五年才结果,等尝完这一批,种苗商的新品都换代了。于是他发明了「小苗试尝」:把候选苗种在小花盆里,施速效肥,45 天就让它们结出小球果,榨汁试个大概方向;对于那些去年试过同款砧木的,他干脆查旧账本推算。评估慢了,果园就追不上市场。
NAS 里每评估一个候选结构,都要从头训练一个神经网络,动辄数小时乃至数天,成为进化搜索最大的算力瓶颈。对应「小苗试尝」的思路有三招:一是减少训练轮次(早停),用少量轮次的学习趋势外推最终精度;二是用代理模型拟合已评测样本,预测未知结构的得分;三是权值共享(One-Shot/超网),所有候选共享同一套超网权重,评估时只微调几轮。
权值共享下的代理评估可以写成:
1. 共享权重 \(\theta_{\text{shared}}\):一个包含所有算子的超网,训练后各候选结构复用同一套参数。
2. 验证损失 \(\mathcal{L}_{\text{val}}\):候选结构在验证集上算出的损失,越小说明结构越好。
3. 取负号:损失负值越大代表损失越小,对应的适应度越高。
4. 代理评估 \(\hat{F}\):用廉价近似替代完整训练,显著提升每代可评估的个体数量。
「小苗试尝」让老陈每年能试几百个新组合,果园迭代速度快了一倍。评估成本直接决定进化算法「每代能跑多少个体、能跑多少代」——把每一口尝果的钱省下来,才能让物竞天择在可控预算内真正跑起来。
流程控制参数
老陈不会让果园永远进化下去。他每年在账本上记下当年最甜果实的评分,若连续三年几乎没有提升,就把全园主力品种定下来,专心卖果。他还发现种群规模不能太小——只有十几棵树时近亲授粉,果子越种越小;也不能太大——上千棵树的试吃成本他根本付不起。
进化 NAS 的流程控制,就是回答三个问题:种群多大?进化多少代?何时收手?种群规模 \(N\) 决定每代的探索广度与评估成本;进化代数 \(G_{\max}\) 是总预算上限;更聪明的是动态收敛判定——当最优适应度多代几乎不变时提前终止,把省下的算力留给下一轮搜索或正式训练。
常见的收敛判据是相对提升阈值:
1. 最优适应度 \(f_{\text{best}}^{(t)}\):第 \(t\) 代适应度最高的个体得分,类比当年果园最甜果实的评分。
2. 观察窗口 \(\tau\):向前回看多少代,通常取 5~10 代。
3. 相对提升量:窗口首尾最优分数的差再除以窗口起点分数,消除量纲影响。
4. 阈值判定:相对提升小于 \(\epsilon\)(如 0.01)即触发提前终止。
老陈的账本让他知道「该收手时就收手」。进化 NAS 同理:种群规模、代数上限、收敛阈值三者构成搜索的「预算表」。定得太松,烧光 GPU 预算还收不到好结构;定得太紧,还没进化几代就被迫停止。预算意识,是走向工程落地的必修课。
工程应用落地
果园成熟后,老陈开始对接不同客户:果汁厂要高糖低酸的品种,家庭庭院要矮化省地且结得快的品种,出口商要耐储存的硬果。同一套选育方法,换一套筛选标准,就能定向培育出不同市场的「最优品种」。老陈感慨:不是苹果变厉害了,而是我学会了按目标调评分标准。
进化 NAS 在工程中同样「按目标调标准」。在移动端,开发者把推理时延和模型体积写进适应度函数,进化出的网络既准又轻,可直接部署;在模型压缩场景,用进化搜索为大模型寻找紧凑结构,替换冗余层;在自动化平台,进化 NAS 被封装成「架构自动设计」服务,非专家用户只需提交数据集和约束,系统就自动交付候选模型。多目标适应度是落地的关键:
1. 精度项 \(\text{Acc}(a)\):模型在验证集上的准确率,任何场景都不可放弃的底线指标。
2. 算力项 \(\text{FLOPs}(a)\):浮点运算量,衡量模型计算的绝对规模。
3. 时延项 \(\text{Latency}(a)\):端侧推理一次所需的毫秒数,直接决定用户体感。
4. 权重 \(\lambda_1, \lambda_2\):按部署场景调节,轻量优先时调大,精度优先时调小。
从山脚野林到按需供果的果园,老陈验证的不仅是一种选育法,更是一套「先定目标、再让种群自己找出路」的工程哲学。进化 NAS 的落地,本质就是为不同场景定制合适的适应度函数和搜索预算,然后放心地把「找结构」这件事交给进化——这正是自动化设计时代的典型工作方式。
AI 应用场景
移动端 OCR 模型自动搜索
阶段:模型架构搜索 / 自动设计
解决的问题:手工设计轻量 OCR 网络需要反复尝试卷积核、通道数、模块组合,一版试训就要好几天;进化 NAS 把「精度 + 时延」写进适应度,自动生成能在手机端实时运行的网络,把“调结构”从体力活变成搜索任务。
具体案例:某 App 团队要为身份证识别定制模型,要求体积小于 5MB、单帧推理小于 50ms。他们用进化 NAS 跑 300 代,配合精英保留和权值共享,最终得到的结构比手工设计准确率高 1.2%,体积还小了 30%。老陈的果园经验在这里变成了算法:
大模型压缩与端侧部署
阶段:模型压缩 / 结构蒸馏
解决的问题:大模型部署到嵌入式设备时显存和能耗都不够,手工减层又容易掉点严重;进化 NAS 在约束下搜索紧凑子结构,替换原始模块,让模型在保持精度的同时大幅瘦身。
具体案例:把 7B 参数的对话模型压缩到能在手机本地运行的版本,团队用进化搜索逐一选取注意力头数量、FFN 宽度、层数等维度组合,最终找到一个稀疏且高保真的子结构,推理时延从秒级降到百毫秒级。这就像老陈用「矮化砧木 + 高糖接穗」定向培育庭院苹果树。
Python 演示
import randomimport math def simulated_fitness(arch, noise=0.01): """演示用代理评分:模拟训练后得到的验证准确率。""" depth, width, kernel = arch score = 0.92 * math.exp(-((depth - 3) ** 2 + (width - 4) ** 2) / 12.0) score += 0.03 * math.sin(kernel) return max(0.0, min(1.0, score + random.uniform(-noise, noise))) def crossover(a, b): """单点交叉:前段取父本 a,后段取母本 b。""" return (a[0], b[1], a[2]) def mutate(arch, p=0.3, max_depth=5, max_width=6): """以概率 p 随机改动一个维度,模拟变异。""" depth, width, kernel = list(arch) if random.random() < p: depth = max(1, min(max_depth, depth + random.choice([-1, 1]))) if random.random() < p: width = max(1, min(max_width, width + random.choice([-1, 1]))) if random.random() < p: kernel = random.choice([1, 3, 5]) return (depth, width, kernel) def evolution_search(pop_size=16, generations=50, top_k=4): """进化循环:评估 -> 选择精英 -> 交叉 -> 变异。""" pop = [(random.randint(1, 5), random.randint(1, 6), random.choice([1, 3, 5])) for _ in range(pop_size)] best = None for gen in range(generations): scored = sorted(((simulated_fitness(p), p) for p in pop), key=lambda x: x[0], reverse=True) best = scored[0] elites = [p for _, p in scored[:top_k]] pool = [p for _, p in scored[:pop_size // 2]] pop = list(elites) while len(pop) < pop_size: a = random.choice(pool) b = random.choice(pool) child = mutate(crossover(a, b)) pop.append(child) if (gen + 1) % 10 == 0: print("Generation %d best: %s fitness=%.4f" % (gen + 1, best[1], best[0])) return best if __name__ == "__main__": best_arch = evolution_search() print("Final best architecture:", best_arch[1], " fitness:", round(best_arch[0], 4))
simulated_fitness 换成实际训练评估即可。scored = sorted(((simulated_fitness(p), p) for p in pop), key=lambda x: x[0], reverse=True)给每个个体打分并按分数降序排列,适应度高的排前面。best = scored[0]记录当代最优个体,供最后输出和对比。elites = [p for _, p in scored[:top_k]]精英保留:排名前 top_k 的个体直接进入下一代,防止最优解丢失。pool = [p for _, p in scored[:pop_size // 2]]从排名前一半的个体里随机抽父母,模拟锦标赛的轻量版。child = mutate(crossover(a, b))先用交叉组合两个父母的编码片段,再用变异加入随机扰动,生成子代。while len(pop) < pop_size:用子代逐个填充种群,直到恢复设定的种群规模,完成一代更新。
常见误区
误区进化 NAS 不就是不断随机生成网络然后碰运气吗?多跑几代,总能撞上一个跑得准的结构吧。
正解进化算法有明确的选择压力:适应度决定谁有后代,好结构周围会被更密集地探索,后代在继承优秀片段的基础上做小步变异,搜索方向始终被引导,绝不是均匀瞎试。这就像老陈只让最甜的树留种,而不是每年把种子满山乱撒。
为何常见:变异看起来完全是随机的,初学者很容易把进化算法等同于随机搜索,忽略了「选择」这个定向引擎。
误区评估函数反正可以再优化,把种群翻十倍,找到优解的概率是不是也翻十倍?
正解每代评估成本 = 种群规模 × 单个体评估成本。种群过大,每代都要烧掉大量算力;更划算的组合是中等种群 + 精英保留 + 合理进化代数。好比老陈试吃 1000 颗果子会舌头麻木,还不如精挑 50 棵重点培养。
为何常见:大数定律的直觉让很多人以为样本越多越好,忽略了 NAS 里每个样本都要真金白银地训练一遍。
误区不训到收敛怎么知道谁好谁坏?代理评估听起来就不靠谱。
正解完整训练的评估成本过高,进化 NAS 普遍用早停 + 学习曲线外推、代理模型、权值共享等方式快速估分,排序可靠性足够引导搜索。代价只是一点噪声,换来的是每代能评估几百上千个候选。小苗试尝虽然不如大树结果准,但胜在快且够用。
为何常见:离线训练的习惯让人们追求绝对精确,不适应「近似评估 + 迭代修正」的搜索范式。
误区反正预算定了,跑满 500 代一定比跑 300 代找得更准。
正解预算是上限而不是目标。多数情况下最优适应度到中后期进入平台期,继续跑只是烧电;用相对提升阈值提前终止,把算力留给其他搜索轮次或正式训练。老陈连续三年尝不出提升就收手,而不是盲目再等十年。
为何常见:设置固定代数是工程里最简单的写法,人们懒得为「何时停止」设计一套判断机制。
面试题
Q1. 在进化算法 NAS 中,「种群」指的是什么?
- A.训练数据的批量分组
- B.一组候选的网络结构← 正确答案
- C.神经网络的隐藏层
- D.模型权重的集合
答案:B。种群是候选解的集合,类比果园里同期生长的全部果树。训练数据的批量叫 mini-batch,隐藏层是网络内部组件,权重是训练产物,三者都不是种群。
Q2. 适应度函数在进化算法中的最主要作用是什么?
- A.决定网络层数多少
- B.量化架构优劣分数← 正确答案
- C.加速梯度下降收敛
- D.初始化模型权重值
答案:B。适应度函数给每个候选结构打分,像老陈尝苹果判断甜度一样,分数用于排序和选择。决定层数是搜索空间设计的事,梯度下降和权重初始化属于训练阶段,与打分无关。
Q3. 锦标赛选择的典型操作是下列哪一项?
- A.按适应度占比随机抽
- B.随机抽k个留下最优← 正确答案
- C.每代保留最好的个体
- D.让所有个体循环比赛
答案:B。锦标赛是随机抽出 k 个个体、当场比适应度、留下最强者作为父母。A 描述的是轮盘赌,C 是精英保留,D 不是标准锦标赛流程。
Q4. 权值共享(超网)在进化 NAS 中主要解决什么问题?
- A.搜索空间过大的问题
- B.候选评估成本过高的问题← 正确答案
- C.变异概率设置的问题
- D.种群提前收敛的问题
答案:B。所有候选结构共享超网同一套权重,评估时只做几轮微调,不用从头训练,大幅降低单个个体的评分成本。搜索空间大靠编码设计约束,变异和收敛分别由算子与终止条件控制。
Q5. 交叉算子类比果园中的哪个操作?
- A.给果树施肥松土
- B.把两棵树杂交授粉← 正确答案
- C.剪掉多余的枝条
- D.喷洒防虫的农药
答案:B。交叉把两个父本结构的编码片段组合成子代,就像杂交授粉把两个品种的优点遗传给下一代。施肥、修剪、喷药都是种养环节,不产生新后代组合。
Q6. 收敛判定中「最优适应度变化幅度小于阈值 ε」通常会导致?
- A.继续固定代数进化
- B.提前终止进化过程← 正确答案
- C.扩大种群规模数量
- D.提高变异率的数值
答案:B。连续若干代最优分数几乎不再提升,说明搜索已进入平台期,继续跑只会浪费算力,应当提前终止。固定代数、扩大种群、调变异率都不是收敛判定带来的直接结果。
Q7. 多目标适应度 \(F(a)=\text{Acc}(a)-\lambda_1\cdot\text{FLOPs}(a)-\lambda_2\cdot\text{Latency}(a)\) 中,\(\lambda_1\) 增大意味着什么?
- A.更看重模型精度
- B.更看重算力开销← 正确答案
- C.更看重训练数据量
- D.更看重收敛速度
答案:B。\(\lambda_1\) 乘在 FLOPs 前面,它越大,算力项扣分越狠,进化越倾向选出轻量结构。精度权重体现在 Acc 项本身;数据量与收敛速度不在该公式里。
Q8. 进化算法的主要环节包括下列哪些?
- A.选择优秀个体← 正确答案
- B.交叉产生后代← 正确答案
- C.变异引入变化← 正确答案
- D.梯度回传更新权重
答案:ABC。进化三大环节是选择、交叉、变异,对应果园的选母树、杂交授粉和芽变。梯度回传是训练神经网络用的优化手段,属于「进化出来之后怎么训练」,不是进化环节本身。
Q9. 网络结构的编码中可能包含哪些信息?
- A.每层算子的类型← 正确答案
- B.层间连接的拓扑← 正确答案
- C.训练集的图片内容
- D.卷积核的大小尺寸← 正确答案
答案:ABD。编码描述的是结构本身:每层选什么算子、层与层怎么连、卷积核多大。训练集的内容属于数据层面,与结构编码无关,不会出现在个体基因里。
Q10. 工程落地时,多目标适应度可以纳入下列哪些指标?
- A.验证集准确率← 正确答案
- B.端侧推理时延← 正确答案
- C.模型参数量← 正确答案
- D.训练数据条数
答案:ABC。准确率、时延、参数量都是架构的属性,能写进适应度用于权衡。训练数据条数是数据集属性,不由网络结构决定,不能作为架构搜索的优化目标。