2017 年,我亲眼见证了一次范式转变。Google 的研究人员刚刚发布了 NASNet,这是一个完全由机器学习算法发现的神经网络架构。这个发布之所以不同寻常,并不只是因为自动化系统发现了一个新架构,而是因为这个架构在 ImageNet 上超过了所有人类设计的网络。ImageNet 这个基准测试在将近十年里一直推动着计算机视觉的进步。
它的含义立刻击中了我。多年来,我花了无数小时设计神经网络架构,调整层深度、修改 filter sizes、尝试 skip-connections。这件事部分是艺术,部分是科学,而且非常耗时。算法能够自动化这个过程,并且做得比人类专家更好——这个想法感觉像是科幻变成了现实。
但计算成本也高得惊人。Google 团队使用 500 块 GPU,运行 4 天,才发现 NASNet(Zoph 等,2018,《Learning Transferable Architectures for Scalable Image Recognition》)。这大约是 22,400 GPU-days,也就是约 537,600 GPU-hours。按照 2024 年云端 V100 等高端实例每 GPU-hour 1–3 美元的价格计算,这意味着 50 万到 160 万美元的计算成本。这个数字清楚说明,为什么这种方法对大多数实践者或组织来说并不可及。
NOTE
读者应该预期这些数字会随着云价格演化而变化,并且成本会因供应商和实例类型不同而显著变化。
快进到 2024 年,格局已经发生了巨大变化。过去需要海量计算资源才能完成的事情,现在只需要单块 GPU、几个小时就能做到。神经架构搜索已经从只有科技巨头才能接触的学术奇观,演化为任何 ML 实践者都可以利用的实用工具。我个人也使用过 NAS 技术,在生产系统中发现了一些架构,使模型性能提升 15%–20%,同时将推理时间降低 40%。
这种从计算上不可承受到实践中可用的演化,是自动化机器学习中最重要的进展之一。本章中,我们将探索 NAS 如何工作、为什么它变得如此高效,以及你如何将这些技术应用到自己的项目中。我们将深入 NAS 的三个核心组件:search spaces、search strategies 和 performance estimation。最重要的是,我们会聚焦现代技术,也就是那些不需要超级计算机也能交付结果的方法。
NOTE
本章中的所有代码示例都已经过测试和验证。完整实现、详细输出、实验变体和更多高级技术,请参考配套 Jupyter notebook:Chapter6.ipynb。
理解神经架构搜索
在深入 NAS 如何工作之前,有必要先理解为什么架构设计本身如此困难。当我在 2010 年代早期开始接触深度学习时,架构设计感觉更像炼金术,而不是工程。你会尝试不同层、激活函数和连接方式的组合,更多依赖直觉,而不是系统性原则。
即使设计一个简单的卷积神经网络,也涉及很多决策:
多少层? 层太少,模型表示能力不足;层太多,又可能出现梯度消失和过拟合。
什么 filter sizes? 3×3 filters 捕捉局部特征,而 5×5 或 7×7 filters 可以看到更大模式。但更大的 filters 意味着更多参数。
每层多少 filters? 这会直接影响模型容量和计算成本。
Pooling layers 放在哪里? Pooling 会降低空间维度,但也会丢失信息。
什么时候使用 skip-connections? ResNet 证明它们对非常深的网络至关重要,但到底应该放在哪里?
什么 activation functions? ReLU、Leaky ReLU、ELU、Swish,每种都有不同属性。
把这些决策乘以几十层之后,组合爆炸就会变得难以承受。一个中等规模网络可能有数百万种可能架构配置,而评估每一种配置都需要训练完整模型,这个过程可能需要数小时甚至数天。
NAS 的三大支柱
神经架构搜索通过三个关键组件系统性解决这一挑战:
- Search space:哪些架构可以被发现?
- Search strategy:我们如何探索可能性空间?
- Performance estimation:我们如何高效评估候选架构?
可以把它们看作支持自动化架构发现的基本约束和机制。Search space 定义能找到什么的边界;search strategy 决定我们如何智能探索;performance estimation 则让整个过程在计算上可行。
我分享一个近期工作中 NAS 实际应用的具体例子。我正在做一个项目,开发一个用于分析胸部 X 光片、检测肺炎的模型。挑战在于找到一个架构,既要准确到足以用于临床,又要快到可以在资源受限的边缘设备上运行。
一开始,我们使用标准 ResNet-50 架构。它在验证集上达到 89.2% 准确率,但在目标硬件上每张图像需要 850 毫秒,实际部署太慢。我们本可以花几周手动尝试不同架构,调整层深度和宽度,但最后我们决定应用 NAS。
使用一个现代 NAS 框架 AutoKeras,我们定义了一个 search space,包括:
- 可变网络深度,10–50 层
- 不同 block types:ResNet、DenseNet、MobileNet blocks
- 不同 filter sizes 和数量
- 不同 attention mechanisms
- Skip-connection patterns
搜索探索的一些具体变体包括:12 到 48 层的网络;使用 depthwise separable convolutions,也就是 MobileNet 风格,或标准 3×3 convolutions 的 blocks;attention mechanisms 放在每四个 block 之后,或者只放在网络 bottleneck;skip-connection patterns 从 dense,也就是 DenseNet 风格,到 sparse,也就是 ResNet 风格。最终获胜架构使用 28 层、depthwise separable convolutions,只在两个战略位置使用 attention,并采用一种不寻常的 asymmetric skip pattern。
搜索在 4 块 GPU 上运行 12 小时,探索了 200 多个不同架构。换算一下:48 GPU-hours,按每小时约 2 美元计算,云成本不到 100 美元。它替代的手工实验,本来会花我们团队大约 2 到 3 周工程时间,并且可能只能凭直觉尝试 10–15 个架构。ROI 非常可观——不仅是成本上的,更重要的是它找到了一个我们手工不太可能发现的架构。
最终发现的架构很有意思:它为了效率使用了一种不寻常的 MobileNet blocks 组合,为了准确率策略性放置 attention layers,并采用了我不会想到手动尝试的 skip-connections 模式。
结果超出了预期:
| Metric | ResNet-50 Baseline | NAS-Discovered |
|---|---|---|
| Accuracy | 89.2% | 91.7% |
| Inference time | 850ms | 340ms |
| Model size | 100%(baseline) | 60% |
这个架构让我们成功部署了模型。关键洞察是,NAS 不只是找到了一个更好的架构,它找到了一个针对我们特定约束和需求优化的架构。
Search Space Design:定义边界
Search space 是任何 NAS 系统中最关键的组件,但在讨论中常常被忽视,因为大家往往更关注搜索算法。Search space 从根本上决定了哪些架构可以被发现——它是 NAS 算法作画的画布。
约束的艺术
刚开始尝试 NAS 时,我犯过一个常见错误:我定义的 search spaces 太宽泛了。我以为给算法最大自由度,就会带来最有新意的发现。结果,我得到的是搜索过程极其漫长,而且常常收敛到奇怪、表现很差的架构。
我学到的关键洞察是:好的 search spaces 会编码人类知识,同时仍然允许真正发现。它们足够受约束,可以引导搜索进入有前景区域;但也足够灵活,可以找到令人惊讶的解决方案。
Search Spaces 的类型
Macro search spaces 从零定义完整网络架构。你需要指定可能的 layer types,例如 convolutional、pooling、fully connected,它们的可能参数,例如 filter sizes、strides、activation functions,以及它们之间如何连接。这提供最大灵活性,但会导致巨大搜索空间。
Cell-based search spaces 更受约束,也往往更实用。你不是设计完整网络,而是搜索一个小型计算单元,也就是 “cell”,然后在整个网络中重复使用。Google 的 NASNet 推广了这种方法,并且它已经成为主流范式。关键洞察是:好的局部计算模式通常能够组合成好的全局架构——一个在某一尺度上有效处理特征的优秀 cell,堆叠后通常也能很好处理多个尺度上的特征。这种可组合性不是先验显然成立的,但经验上非常有效,尤其是在视觉任务中。
Cell-based search 的优雅之处在于,你可以发现一个小而高效的计算模式,然后把它扩展。我发现这对计算机视觉任务尤其有效,因为你希望架构能够通过简单增减 cells 数量来处理不同输入分辨率。
示例 6-1 展示了我通常如何定义一个 cell-based search space。
示例 6-1:AutoKeras search space 定义
import autokeras as ak
def create_nas_model():
"""
Define a search space for image classification using AutoKeras.
AutoKeras handles the search space internally with its high-level API.
"""
model = ak.ImageClassifier(
num_classes=10, # Adjust based on your dataset
multi_label=False,
max_trials=20,
directory='nas_results',
project_name='image_classification_nas'
)
return model
# Example usage:
model = create_nas_model()
print("AutoKeras model created successfully!")
虽然 search spaces 的概念框架很直接,但实际实现时,需要仔细考虑如何用程序表达架构选择。配套 notebook 展示了这一过程:从简单配置类,到能够处理复杂架构模式的复杂 search space 定义。
我们的实现方法采用一种层级策略:先定义基础 building blocks,例如 layer types 和 parameter ranges,再把它们组合成完整架构规格。这种模块化方法使我们可以轻松实验不同 search space 设计,并根据具体问题领域适配。
Hierarchical search spaces 会在不同尺度上将 operations 组织为 modules。你可能有 low-level operations,例如 convolutions、activations;mid-level modules,例如 residual blocks、attention mechanisms;以及 high-level architectural patterns,例如 pyramid structures、encoder-decoders。
任务特定考虑
我学到的最重要经验之一,是 search spaces 应针对具体领域和任务定制。适用于图像分类的 operations 和 patterns,可能完全不适合自然语言处理或时间序列分析:
Computer vision:Search spaces 通常包括各种 convolutional operations、pooling strategies、normalization techniques,以及 skip-connections 等 architecture patterns。近期工作也纳入了 attention mechanisms 和新的 activation functions。
Natural language processing:Transformers 的兴起,使 NLP search space 转向 attention mechanisms、各种 positional encodings、layer normalization variants 和 feed-forward network configurations。
Time series analysis:这类 search spaces 通常聚焦时间模式,包括不同类型 recurrent units、dilated convolutions,以及面向 sequential data 的 attention mechanisms。
Medical imaging:在医疗领域工作时,我发现 search spaces 如果包含领域特定 operations 会更有收益,例如用于处理多尺度图像的 dilated convolutions,以及适合医疗图像特征的专用 normalization techniques。
新兴的专用 Search Spaces
近期进展催生了针对特定数据类型和应用的高度专用 search spaces。Graph neural architecture search(GraphNAS)是一个很有意思的发展方向,它聚焦自动化设计 Graph Neural Networks(GNNs),用于处理非欧式图数据。
GraphNAS 涉及多个层级上的复杂 search spaces:
- Micro-level:节点消息交换机制
- Macro-level:层连接模式
- Pooling-level:图级表示学习
GASSO 等近期进展会联合搜索 GNN architectures 和 graph structures,而 GAUSS 则针对大规模图优化效率。
多目标 Search Spaces
NAS 的一个重要突破,是发展出明确优化多个目标的 search spaces,而不只是优化准确率。MoENAS(Mixture-of-Experts-based Neural Architecture Search)代表了一种新方法,它在 mixture-of-experts architectures 空间中导航,发现同时准确、公平、稳健且泛化能力强的 deep neural networks,尤其适用于资源受限边缘设备。
这种方法展示了令人印象深刻的结果:相比 state-of-the-art edge DNNs,准确率提升 4.02%,同时将肤色相关准确率差异从 14.09% 大幅降低到 5.60%。同时,它也增强了鲁棒性,并降低了过拟合。
这种对多目标优化的明确关注,包括 fairness 和 robustness,代表该领域正在成熟,也承认了真实世界 AI 部署中伦理考虑的重要性。
NOTE
考虑到 EU AI Act 以及美国各州正在出现的 AI 监管趋势,这种能力正变得越来越重要。这些监管可能要求在高风险 AI 应用中展示可证明的公平性。NAS 提供了一种系统化方式来优化这些要求,而不是把公平性当作事后补充。
平衡效率与发现
创建高效 search spaces,也就是小而约束明确,和实现真正发现,也就是大而灵活,之间总存在张力。我总结了一些经验法则,用于取得平衡:
Start constrained:先基于你所在领域已知优秀架构,定义一个相对较小的 search space。这能确保搜索过程可控,并且更可能找到合理解。
Expand gradually:如果初始搜索发现的架构位于空间边界,就扩展这些维度。如果所有发现的架构都使用了你允许的最大深度,就尝试增加深度范围。
Incorporate domain knowledge:纳入在类似问题上表现良好的 operations 和 patterns,但也不要害怕加入一些 wild cards,它们可能带来意外发现。
Monitor search progress:现代 NAS 工具提供优秀的搜索过程可视化。使用这些洞察来不断优化 search space。
NAS-Bench 革命
NAS 研究中最重要的发展之一,是创建了带预计算结果的 benchmark search spaces。NAS-Bench-101 和 NAS-Bench-201 包含数千个架构及其已计算的训练结果,使研究人员无需昂贵训练,就可以开发和测试 NAS 算法。
虽然这些 benchmarks 主要是研究工具,但它们教给我们关于 search space 设计的宝贵经验。对这些 benchmarks 的分析揭示了:
Most random architectures perform poorly:绝大多数随机架构表现很差。只有一小部分可能架构能取得好表现。
Local structure matters:结构相似的架构往往表现也相似。
Some operations are more critical than others:某些具体 operations,例如 skip-connections,对性能有不成比例的影响。
这些洞察已经影响了实践应用中更有效 search spaces 的设计。
Search Strategies:在干草堆里找针
定义好 search space 后,我们需要策略来高效探索它。这正是 NAS 中算法创新的闪光点。挑战在于,在拥有数百万甚至数十亿可能架构的空间中,找到少数表现极其优秀的架构。
Search Strategies 的演化
2018 年我刚开始做 NAS 时,该领域主要由两种方法主导:reinforcement learning 和 evolutionary algorithms。两者都很强大,但计算成本高。那一年稍晚出现的 differentiable methods 改变了整个领域,使 NAS 对计算资源有限的实践者也变得可用。
选择搜索策略:实践视角
搜索策略的选择会显著影响发现架构的质量以及所需计算资源。在我们的实现中,我们发现:
Evolutionary approaches 在你有直观方式对架构进行 mutation,并且可以在多个 GPU 上并行评估时表现出色。
Differentiable methods 在 search space 可以表达为 continuous relaxations,且有足够内存容纳 supernets 时最有效。
Hybrid approaches 通常在多样化问题类型上提供最稳健表现。
Notebook 中包含一个综合对比系统,使你可以在同一 synthetic dataset 上实验不同策略,便于理解它们各自优势和取舍。
Reinforcement Learning:最早的方法
Reinforcement learning(RL)将架构搜索视为 sequential decision-making problem。一个 RL agent,通常实现为 RNN controller,通过基于性能获得 reward 来学习构建架构。
基于 RL 的 NAS 有一些很有吸引力的优势:
Flexibility:RL controllers 可以处理任意 search spaces 和 constraints。想同时优化准确率和延迟?很容易,只需要设计 composite reward function。
Theoretical foundation:该方法建立在成熟 RL 理论之上,在特定条件下有收敛保证。
Parallelization:多个架构可以同时评估,controller 可以从全部结果中学习。
TIP
关于 reward function 设计的实践提醒:为 RL-based NAS 构造有效 reward functions,更像艺术而不是科学。朴素做法是直接使用 validation accuracy,但这往往会导致架构过拟合搜索过程。我发现,将准确率与模型大小和推理延迟的 regularization terms 结合效果更好,但相对权重需要实验。一个不错的起点是:reward = accuracy – 0.1 * log(params) – 0.1 * log(latency)。效率权重太大,会得到过小且表现不足的模型;权重太小,则会得到臃肿、难以部署的架构。
ENAS(Efficient Neural Architecture Search)等现代 RL-based NAS 系统,通过 weight sharing 大幅降低计算成本,使其变得更实用。
Evolutionary Algorithms:受自然启发的搜索
Evolutionary algorithms(EA)会维护一个候选架构 population,并通过 mutation 和 selection 演化。我发现这种方法尤其直观,因为它模拟了生物系统通过逐步改进演化出复杂结构的过程。
典型 evolutionary NAS 流程如下:
- Initialize:创建随机 architecture population。
- Evaluate:训练并测试每个架构。
- Select:保留表现最好的架构。
- Mutate:通过修改存活者创建新架构。
- Repeat:持续执行直到收敛或预算耗尽。
示例 6-2 展示了这一概念的实现。
示例 6-2:Evolutionary NAS 实现
# Simplified evolutionary NAS (see notebook for complete implementation)
def evolutionary_nas(population_size=50, generations=100, search_config=None):
"""
Simplified evolutionary NAS implementation.
"""
if search_config is None:
search_config = SearchSpaceConfig()
# Initialize random population
population = []
for _ in range(population_size):
arch = search_config.sample_architecture()
population.append(arch)
fitness_history = []
for generation in range(generations):
# Evaluate all architectures
# NOTE: evaluate_architecture_placeholder represents the
# computationally expensive step where each candidate
# architecture would be:
# 1. Instantiated as a real neural network
# 2. Trained on your dataset (typically for multiple epochs)
# 3. Evaluated on a validation set
# This is where 95%+ of NAS computation time is spent.
fitness_scores = [evaluate_architecture_placeholder(arch)
for arch in population]
fitness_history.append(max(fitness_scores))
# Select top performers and create next generation
# (see notebook for complete implementation)
if generation % 10 == 0:
print(f"Generation {generation}: Best fitness = "
f"{max(fitness_scores):.4f}")
return population[0], fitness_history # Return best architecture
NOTE
本章中的 Python 片段有两个用途。完整、可运行示例,例如 AutoKeras 代码,可以直接执行。带 placeholder functions 的骨架示例用于说明算法结构,并在配套 notebook 中提供完整实现。如果你更偏好语言无关的伪代码,可以关注正文中的算法说明;如果你想运行代码,请参考 notebook。
Evolutionary approaches 的主要优势是:
Multi-objective optimization:天然支持优化多个冲突目标。
Robustness:相比基于梯度的方法,更不容易陷入局部最优。
Interpretability:容易理解和调试。
缺点是计算成本高——和 RL 方法一样,传统 EA-based NAS 需要训练大量完整模型。
Differentiable NAS:改变游戏规则
2018 年 DARTS(Differentiable Architecture Search)的出现,彻底改变了架构搜索领域。DARTS 不再把架构搜索视为离散优化问题,而是将其重构为可以用梯度下降求解的连续优化问题。
核心洞察非常精彩,而且很简单:不要为架构图中每条边选择一个 operation,而是保留所有可能 operations 的加权组合。随后,这些权重可以用标准 backpropagation 优化,如示例 6-3 所示。
示例 6-3:用于 Differentiable NAS 的 Mixed Operation
class MixedOperation(nn.Module):
"""
A mixed operation that combines multiple candidate operations.
Core component of differentiable NAS.
"""
def __init__(self, operations, channels):
super().__init__()
self.operations = nn.ModuleList()
# Create all candidate operations
for op_name in operations:
if op_name == 'conv3':
op = nn.Conv2d(channels, channels, 3, padding=1)
elif op_name == 'conv5':
op = nn.Conv2d(channels, channels, 5, padding=2)
elif op_name == 'maxpool':
op = nn.MaxPool2d(3, stride=1, padding=1)
elif op_name == 'skip':
op = nn.Identity()
else:
raise ValueError(f"Unknown operation: {op_name}")
self.operations.append(op)
# Learnable weights for combining operations
self.weights = nn.Parameter(torch.randn(len(operations)))
def forward(self, x):
# Weighted combination of all operations
weights_softmax = F.softmax(self.weights, dim=0)
output = sum(w * op(x) for w, op in
zip(weights_softmax, self.operations))
return output
# Complete implementation available in accompanying notebook
Differentiable NAS 的优势非常有吸引力:
Speed:比离散方法快几个数量级。
Memory efficiency:只需要在内存中存储一个 supernet。
Integration:可以无缝适配已有深度学习框架。
不过,DARTS 也有自己的挑战:
Memory consumption:supernet 可能非常大。
Optimization instability:连续松弛形式可能很难优化。
Performance gaps:连续近似可能无法完美反映离散架构的表现。
CRITICAL WARNING:DARTS 不稳定性和性能崩塌
Discretization gap 是 DARTS 发现的架构低于预期的最常见原因之一。在连续松弛期间,算法可能偏好一个被选择权重为 51% 的 operation,而另一个 operation 是 49%;但离散化之后,只剩下 51% 的 operation,这可能丢弃有价值的信息。我见过一些案例,DARTS 搜索期间架构显示 92% 准确率,但从零训练时只有 88% 准确率。
更关键的是,原始 DARTS 存在一种被充分记录的 failure mode,称为 “performance collapse”,也就是搜索退化到主要选择 skip-connections。发生这种情况的原因是,skip-connections 是最容易优化的 operations,因为它们没有可学习参数,所以会积累更高 architecture weights,即使它们并不带来更好的最终表现。你可能完成一次 DARTS 搜索,最后发现架构几乎全是 identity connections——训练很快,但准确率很差。
对于生产使用,我建议避免 vanilla DARTS,而使用稳定变体:
- PC-DARTS(Partially-Connected DARTS) :通过 partial channel connections 降低内存并防止 collapse。
- FairDARTS:使用 sigmoid-based discretization,解决 skip-connection bias。
- SDARTS(Stabilized DARTS) :增加 regularization,以防止 degenerate solutions。
- DARTS+ :通过提前停止 architecture parameters 来防止 collapse。
在得出结论或部署到生产之前,一定要把发现的架构在完整数据集上从零训练验证。如果发现的架构被 skip-connections 主导,应将其视为 warning sign,并考虑使用上述稳定变体之一。
近年来许多工作,例如 PC-DARTS、FairDARTS、DrNAS,已经解决了这些限制,使 differentiable NAS 更加实用。
Gradient-Based Methods 和高级技术
近期 gradient-based NAS 的进展引入了复杂技术,用于处理连续松弛带来的挑战。这些方法使用梯度下降优化 architecture parameters,同时保持最终架构的离散性质。
该领域也发展出了 zero-shot 和 predictor-based methods,可以在不完整训练的情况下估计架构表现。这些方法会在 NAS benchmarks 上训练 performance predictors,或在 labeled data 稀缺时使用 self-supervised pretext tasks,从而大幅降低架构搜索的计算需求。
Hybrid Approaches:两者优势兼得
现代 NAS 系统常常结合多种搜索策略,以利用它们的互补优势。我发现这些方法尤其有吸引力:
RL + weight sharing:使用 reinforcement learning 进行智能探索,同时在架构之间共享权重以降低计算成本。
EA + supernets:在 weight-sharing supernet 内演化架构,结合进化的多目标能力与共享训练的效率。
Differentiable + multifidelity:用 differentiable search 做初始探索,然后用更高 fidelity 的评估精修有前景候选项。
选择合适策略
搜索策略的选择取决于你的具体需求:
使用 RL 的情况:
- 你需要 search space 设计上的最大灵活性。
- 你正在优化复杂、不可微目标。
- 你拥有充足计算资源。
使用 evolutionary methods 的情况:
- 你需要优化多个冲突目标。
- 你希望搜索动态可解释。
- 你的 search space 包含离散、分类选择。
使用 differentiable methods 的情况:
- 计算效率至关重要。
- 你的 search space 可以表达为 continuous relaxation。
- 你正在使用标准深度学习 operations。
实践中,我通常会先使用 differentiable methods,因为它们效率高;必要时,再利用获得的洞察,引导其他方法做更有针对性的搜索。
Performance Estimation:效率优先
早期 NAS 系统的阿喀琉斯之踵,是需要完整训练每个候选架构。Search spaces 包含数百万种可能,而每次评估需要数小时甚至数天,计算需求很快变得不可承受。Performance estimation strategies 通过开发无需完整训练即可预测架构质量的方法来解决这个问题。
弥合评估差距
实现 NAS 最大挑战之一,是弥合研究展示和实际应用之间的差距。学术论文常常假设有无限计算资源,但真实世界实现必须在严格预算约束下工作。
配套 notebook 中的方法通过实现一条完整 evaluation pipeline 来处理这一问题:
- 使用展现真实学习模式的 synthetic data
- 展示 evaluation fidelity 如何影响架构排名
- 展示如何验证 low-fidelity evaluations 与 high-fidelity performance 的相关性
- 包含 timing 和 resource usage tracking,帮助你预算计算成本
这种实践导向确保你学到的技术可以立即应用到具有现实约束的真实项目中。
训练瓶颈
要理解为什么 performance estimation 如此关键,考虑一下数学问题。一个中等 search space 可能包含 10^6 个可能架构。如果每个架构完整训练需要 2 小时,穷举搜索将需要 20 万年计算时间。即使有 1000 块 GPU 并行运行,也需要 200 年。显然,我们需要更创新的方法。
Multifidelity Evaluation:少训练,多学习
Multifidelity evaluation 利用了一个洞察:你不需要完整训练,就能获得有关架构潜力的有用信息。正如我们在第 3 章讨论超参数优化时看到的,我们可以在不同 “fidelities” 上评估架构,也就是不同计算投入级别。
最常见 fidelity 维度包括:
- Training epochs:训练更少 epochs,例如 10 而不是 100。
- Dataset size:使用训练数据子集。
- Model resolution:使用更低分辨率输入训练。
- Reduced precision:训练时使用 mixed precision 或 quantization。
我发现 Successive Halving 对 NAS 尤其有效。先让许多架构训练少量 epochs,淘汰表现最差者,然后让存活者训练更久。重复这个过程。这种锦标赛式淘汰会将计算资源集中在最有前景的候选项上。示例 6-4 展示了 Successive Halving。
示例 6-4:用于 NAS 的 Successive Halving
def successive_halving_nas(architectures, max_epochs=100,
reduction_factor=2):
"""
Implement Successive Halving for efficient architecture evaluation.
"""
candidates = architectures.copy()
epochs = max_epochs // (reduction_factor ** 3) # Start with fewer epochs
print(f"Starting Successive Halving with {len(candidates)} candidates")
round_num = 1
while len(candidates) > 1 and epochs <= max_epochs:
print(f"\nRound {round_num}: Evaluating {len(candidates)} "
f"candidates for {epochs} epochs")
# Train all candidates for current epoch budget
results = []
for i, arch in enumerate(candidates):
score = train_and_evaluate_placeholder(arch, epochs=epochs)
results.append((arch, score))
if i % 10 == 0:
print(f" Evaluated {i+1}/{len(candidates)} candidates")
# Keep top performers
results.sort(key=lambda x: x[1], reverse=True)
keep_count = max(1, len(results) // reduction_factor)
candidates = [arch for arch, _ in results[:keep_count]]
print(f" Best score this round: {results[0][1]:.4f}")
print(f" Keeping top {keep_count} candidates")
# Double the training budget for next round
epochs *= reduction_factor
round_num += 1
return candidates[0] if candidates else None
Hyperband 会通过运行多个使用不同初始资源分配的 Successive Halving brackets 来扩展这一思想。Bayesian Optimization and HyperBand(BOHB)则结合 Hyperband 和 Bayesian optimization,智能选择要评估的架构。这些方法可以将 NAS 搜索时间降低 10 倍到 100 倍,同时找到同样优秀的架构。
One-Shot Architecture Search:训练一次,评估很多
最具变革性的效率技术,是 one-shot architecture search,也称为 weight sharing。核心思想很优雅:不是独立训练每个架构,而是训练一个包含所有可能 operations 的 “supernet”,然后通过继承相应权重来评估候选架构。
当我第一次在 ENAS 论文中看到这个概念时,它看起来几乎好得不像真的。训练一次,评估数千个架构?我内心的怀疑者质疑这种近似的准确性。但实证结果很有说服力:通过 weight sharing 找到的架构,通常可以匹配甚至超过独立训练发现的架构,而且计算成本只是后者的一小部分。示例 6-5 给出了一个实现 one-shot NAS supernet 的脚本。
示例 6-5:用于 One-Shot NAS 的 Supernet
class Supernet(nn.Module):
def __init__(self, search_space):
super().__init__()
# Create layers for all possible operations
self.operations = nn.ModuleDict()
for op_name, op_config in search_space.items():
self.operations[op_name] = create_operation(op_config)
def forward(self, x, architecture):
# Forward pass using only operations specified in architecture
for layer_spec in architecture:
op_name = layer_spec['operation']
x = self.operations[op_name](x)
return x
def evaluate_architecture_fast(supernet, architecture, test_loader):
"""Evaluate architecture using pre-trained supernet weights"""
supernet.eval()
correct = 0
total = 0
with torch.no_grad():
for data, targets in test_loader:
outputs = supernet(data, architecture)
_, predicted = torch.max(outputs.data, 1)
total += targets.size(0)
correct += (predicted == targets).sum().item()
return correct / total
效率提升非常显著。在最近一个项目中,我们训练 supernet 50 个 epochs,大约在 4 块 GPU 上运行 8 小时,随后只花 20 分钟就评估了 10000 多个不同架构。这种方法让探索远大于过去的 search spaces 成为可能。
Learning Curve Extrapolation
另一种聪明方法,是从早期训练中外推 learning curves。每个架构只训练几个 epochs,拟合 validation accuracy 的增长曲线,然后预测最终表现。
WARNING
Learning curve extrapolation 最适用于训练动态平滑且单调的情况。对于使用 learning rate warmup、cyclic learning rates、cosine annealing 或其他非单调 schedules 的模型,早期表现可能无法可靠预测最终表现。一个模型在 warm-up 阶段可能看起来很差,但之后表现出色。在依赖 extrapolation 之前,应验证“单调提升”假设是否适用于你的具体训练设置——先画出几条完整 learning curves 检查。
我在计算预算极其有限时成功用过这个技术。对于一个医疗健康项目,我们需要评估数百个架构,但只能承受极少训练时间。通过对 5-epoch 训练运行拟合指数曲线,我们能够以合理准确率预测 50-epoch 表现,如示例 6-6 所示。
示例 6-6:Learning Curve Extrapolation
import numpy as np
from scipy.optimize import curve_fit
def exponential_curve(x, a, b, c):
return a * (1 - np.exp(-b * x)) + c
def predict_final_accuracy(early_accuracies, target_epochs):
epochs = np.arange(1, len(early_accuracies) + 1)
# Fit exponential curve to early training
try:
params, _ = curve_fit(exponential_curve, epochs, early_accuracies)
predicted = exponential_curve(target_epochs, *params)
return max(0, min(1, predicted)) # Clamp to valid range
except:
return early_accuracies[-1] # Fallback to last observed value
Zero-Cost Proxies:即时架构评估
Performance estimation 中最新突破是 zero-cost proxies,也就是完全不训练就可以为架构打分的方法。这些技术分析未训练网络的属性,例如 gradient flow、parameter correlations、network connectivity,以预测训练后的表现。
当我第一次读到 zero-cost proxies 时,我很怀疑。不训练怎么可能预测网络表现?但实证结果很有说服力,底层直觉也讲得通:神经网络的某些结构属性,例如梯度流特征,能够预测其学习能力。
NOTE
没有任何单一 zero-cost proxy 可以在所有场景中都表现良好。SNIP 对架构排序可能不同于 Synflow 或 GradNorm 等其他 proxies。近期工作,例如 ZenNAS、NASWOT 表明,组合多个 proxies 可以获得更稳健的排序。本章稍后的 ZeroCostFilter 类会展示这种 ensemble 方法。
最成功的 zero-cost proxies 之一是 SNIP(Single-shot Network Pruning),它度量单次 forward-backward pass 后的梯度大小。示例 6-7 给出了一个实现。
示例 6-7:SNIP Score 计算
def snip_score(model, data_loader):
"""Compute SNIP score for architecture ranking"""
model.train()
# Single forward-backward pass
data, targets = next(iter(data_loader))
outputs = model(data)
loss = F.cross_entropy(outputs, targets)
# Compute gradients
gradients = torch.autograd.grad(
loss, model.parameters(), create_graph=False
)
# SNIP score is sum of gradient magnitudes
score = sum(torch.sum(torch.abs(grad)) for grad in gradients)
return score.item()
Zero-cost proxies 使架构排序变得极快。在最近一个项目中,我们用它们把 50000 个随机架构预过滤到前 500 个,只用了 30 分钟,然后再对过滤后的集合应用更昂贵的评估方法。
Surrogate Models:学习预测性能
Surrogate models 采用另一种方法:训练一个单独机器学习模型,根据架构特征预测架构表现。一旦训练完成,surrogate 可以即时评估新架构。
这种方法对迭代优化架构尤其有价值。在用常规方法评估数百个架构后,可以用这些数据训练 surrogate model,然后用它引导后续搜索,如示例 6-8 所示。
示例 6-8:Architecture Surrogate Model
from sklearn.ensemble import RandomForestRegressor
import numpy as np
class ArchitectureSurrogate:
def __init__(self):
self.model = RandomForestRegressor(n_estimators=100)
self.encoder = ArchitectureEncoder()
def train(self, architectures, accuracies):
"""Train surrogate on architecture-accuracy pairs"""
features = [self.encoder.encode(arch) for arch in architectures]
self.model.fit(features, accuracies)
def predict(self, architecture):
"""Predict accuracy for new architecture"""
features = self.encoder.encode(architecture)
return self.model.predict([features])[0]
class ArchitectureEncoder:
def encode(self, architecture):
"""Convert architecture to feature vector"""
features = []
features.append(len(architecture)) # Network depth
features.append(sum(op['filters'] for op in architecture))
# Add more architectural features...
return np.array(features)
组合方法以获得最高效率
最有效的现代 NAS 系统会组合多种 performance estimation strategies:
- Zero-cost filtering:用 proxies 淘汰明显糟糕的架构。
- Multifidelity screening:对剩余候选项应用 Successive Halving。
- One-shot evaluation:使用 supernet weights 高效排序。
- Full training:只完整训练最前面的少数候选项。
这种多阶段漏斗式方法可以评估数百万个架构,同时只完整训练少数几个,实现效率和准确率之间的最佳平衡。
Efficient NAS:让它变得实用
NAS 从计算成本过高的研究奇观演化为实用工具,主要由效率创新推动。现代 NAS 系统可以使用个人实践者和小团队都能负担的计算预算,发现 state-of-the-art 架构。
效率革命
Google 在 2017 年发布 NASNet 时,22,400 GPU-day 的计算成本成为头条,不仅因为技术成就,也因为资源投入极其夸张。它像是在宣告,只有计算预算几乎无限的组织才能参与自动化架构发现。
随后发生的转变非常惊人。到 2024 年,我经常运行 NAS 实验,在数小时而非数月内找到有竞争力的架构,使用的只是容易获得的硬件。这场效率革命由几个关键创新驱动,它们协同工作,将计算需求降低了几个数量级。
构建生产就绪的 NAS 系统
目前为止覆盖的技术,代表了 NAS 的构建块,但要把它们组合成一个连贯、生产就绪的系统,还需要额外考虑。Notebook 中包含一个完整的 AdvancedNeuralArchitectureSearch 类,展示了:
Integrated workflow management:协调 search space definition、strategy selection 和 evaluation pipelines。
Result tracking and analysis:全面记录和可视化搜索进度。
Robustness and error handling:优雅处理训练失败和资源约束。
Reproducibility:确保搜索结果可以复现并跨运行比较。
这个端到端实现展示了如何把学术 NAS 技术转化为可以集成进现有 ML workflows 的实用工具。
Weight Sharing:高效 NAS 的基础
Weight sharing 仍然是 NAS 中最重要的效率创新。核心洞察是:不要独立训练数千个架构,而是训练一个覆盖 search space 中所有可能架构的 supernet。
关键在于确保 supernet training process 不偏向某些 operation 类型。在早期实现中,我注意到更简单的 operations,例如 identity connections,会占主导,因为它们更容易优化,从而导致架构虽然高效但不一定准确。
现代 weight-sharing 实现通过谨慎训练策略解决这一问题,如示例 6-9 所示。
示例 6-9:使用 Balanced Sampling 的 Supernet Training
class SupernetTraining:
"""
Educational implementation demonstrating supernet training concepts.
NOTE: For production use, prefer established frameworks:
- NNI's OneShotTrainer: Full-featured supernet training with
multiple search strategies
- AutoGluon's NAS module: Production-ready with automatic
search space configuration
- timm's training utilities: Excellent for vision model training
and evaluation
This custom implementation is provided for educational purposes
to illustrate the mechanics of balanced operation sampling during
supernet training.
"""
def __init__(self, supernet, search_space):
self.supernet = supernet
self.search_space = search_space
self.operation_counters = {} # Track operation usage
def sample_architecture(self):
"""Sample architecture ensuring balanced operation usage"""
architecture = []
for layer_idx in range(self.search_space.num_layers):
# Get available operations for this layer
ops = self.search_space.get_layer_operations(layer_idx)
# Balance operation sampling to prevent bias
op_weights = self.compute_balanced_weights(ops)
chosen_op = np.random.choice(ops, p=op_weights)
architecture.append(chosen_op)
self.operation_counters[chosen_op] = \
self.operation_counters.get(chosen_op, 0) + 1
return architecture
def train_step(self, batch):
"""Single training step with architecture sampling"""
# Sample different architecture for each training step
architecture = self.sample_architecture()
# Forward pass with sampled architecture
outputs = self.supernet(batch['inputs'], architecture)
loss = F.cross_entropy(outputs, batch['targets'])
# Backward pass updates only active operations
loss.backward()
return loss.item()
Once-For-All Networks:解耦训练与部署
Once-for-all(OFA)networks 是 weight sharing 的逻辑演化,其中权重在所有层之间共享。OFA 不是搜索单个最佳架构,而是训练一个 supernet,可以为不同部署约束即时提供专用架构。示例 6-10 给出了一个例子。
这个过程如下:
- Progressive training:训练一个支持多种架构配置的 supernet,例如不同 depth、width、kernel sizes。
- Constraint-aware sampling:训练期间,基于部署约束采样 subnetworks。
- Instant deployment:对于任何新部署场景,无需额外训练即可提取最优 subnetwork。
示例 6-10:Once-for-All Subnet 提取
class KernelSizeOnlyOFA(nn.Module):
"""OFA that only varies kernel sizes - simplified for demonstration"""
def __init__(self, num_classes=10):
super().__init__()
# Implementation details in notebook...
def extract_subnet(self, constraints):
"""Extract subnet configuration based on constraints"""
efficiency_level = constraints.get('efficiency', 'medium')
if efficiency_level == 'mobile':
return {
'stage1_block1_kernel': 3,
'stage1_block2_kernel': 3,
'stage2_block1_kernel': 3,
'stage2_block2_kernel': 3,
'use_skip': False
}
# ... other configurations
在一个 10 类图像分类任务部署场景中,类似 CIFAR-10 复杂度,这种方法可以得到如下结果:
| Deployment Target | Accuracy | Latency |
|---|---|---|
| Server deployment | ~94% | ~15ms |
| Mobile deployment | ~92% | ~45ms |
| Edge device | ~89% | ~120ms |
NOTE
这些数字是示例性的;实际性能取决于你的具体数据集、模型架构和目标硬件。关键洞察是,三个模型都来自同一个 supernet,因此无需为每个部署目标分别训练。
值得注意的是,OFA 和类似 weight-sharing 技术,在图像分类任务上展示了最强结果,因为卷积架构具有可预测计算模式,能够很好地跨硬件目标迁移。对于自然语言处理、时间序列预测或图问题等其他领域,效率收益可能不同。“训练一次、部署多个 subnets” 的底层原则依然有价值,但在生产中采用前,你应在具体任务上验证准确率—延迟取舍。
Progressive Search Strategies
另一个关键效率创新是 progressive search,它从简单架构开始,并逐渐增加复杂度。这种方法类似人类设计直觉:先从一个能工作的基础结构开始,再增加复杂性。
在不熟悉领域工作时,我发现 progressive search 特别有价值。示例 6-11 展示了一个例子。
示例 6-11:Progressive NAS 实现
class ProgressiveNAS:
def __init__(self):
self.search_stages = [
{'max_depth': 5,
'operations': ['conv3', 'pool']},
{'max_depth': 10,
'operations': ['conv3', 'conv5', 'pool', 'skip']},
{'max_depth': 20,
'operations': ['conv3', 'conv5', 'dconv', 'attention', 'skip']},
]
def run_search(self):
best_arch = None
for stage in self.search_stages:
print(f"Stage: depth={stage['max_depth']}, "
f"ops={len(stage['operations'])}")
# Initialize search space for this stage
search_space = self.create_search_space(stage)
# Warm-start with previous best architecture if available
if best_arch:
search_space.add_seed_architecture(best_arch)
# Run search for this stage
stage_best = self.search_stage(search_space)
if stage_best:
best_arch = stage_best
return best_arch
Hardware-Aware Optimization
现代 NAS 系统越来越多地纳入 hardware awareness,在优化准确率的同时,明确优化部署约束,例如 latency、memory usage 和 energy consumption。
可以假设一家自动驾驶汽车公司作为例子。准确率要求非常严格,因为它是 safety-critical application;但延迟约束同样关键,因为系统需要实时决策。你需要在特定车载硬件上同时达到高准确率和低延迟的架构。
NOTE
对于自动驾驶汽车、自动无人机或医疗设备等安全关键应用,延迟要求常常是硬约束,而不是软目标。自动驾驶汽车感知系统可能有严格 50 毫秒 deadline——任何超过这个阈值的系统都不可接受,无论准确率多高。NAS 可以把这些编码为 hard constraints,淘汰任何超过阈值的架构,而不是把它们当作可与准确率权衡的 soft penalties。这会显著改变搜索动态:搜索不再探索完整 Pareto frontier,而是只聚焦所有约束都满足的可行区域。
Hardware-aware search 会纳入几种策略:
Direct latency measurement:不是使用 proxy metrics,而是直接将候选架构部署到目标硬件上并测量真实 latency。
Multi-objective optimization:使用 evolutionary algorithms 维护 accuracy-latency trade-offs 的 Pareto frontier。
Hardware-specific operations:在 search space 中包含针对目标 inference engine,例如 TensorRT,优化过的 operations。
示例 6-12 给出了实现该概念的类。
示例 6-12:Hardware-Aware NAS
class HardwareAwareNAS:
def __init__(self, target_device):
self.target_device = target_device
self.latency_predictor = LatencyPredictor(target_device)
def evaluate_architecture(self, architecture):
"""Multi-objective evaluation: accuracy and latency"""
# Standard accuracy evaluation
accuracy = self.measure_accuracy(architecture)
# Hardware-specific latency measurement
latency = self.latency_predictor.predict(architecture)
# Combine into single fitness score
# Weight based on application requirements
fitness = (0.7 * accuracy +
0.3 * (1.0 - latency / self.max_acceptable_latency))
return {
'fitness': fitness,
'accuracy': accuracy,
'latency': latency
}
def pareto_selection(self, population):
"""Select architectures on Pareto frontier"""
pareto_front = []
for candidate in population:
is_dominated = False
for other in population:
if (other['accuracy'] >= candidate['accuracy'] and
other['latency'] <= candidate['latency'] and
(other['accuracy'] > candidate['accuracy'] or
other['latency'] < candidate['latency'])):
is_dominated = True
break
if not is_dominated:
pareto_front.append(candidate)
return pareto_front
NAS 发现架构的生产部署工具
一旦通过 NAS 发现最优架构,高效部署它还需要专门工具。前面的概念讨论概述了原则,但实践者需要具体工具来完成生产部署。
AWS Deployment Stack
Amazon SageMaker Neo:为边缘部署编译训练好的模型,并自动针对目标硬件优化,例如 ARM、x86、GPU 等。Neo 可以通过生成硬件特定代码,将 inference latency 降低 2 倍甚至更多,如示例 6-13 所示。
示例 6-13:SageMaker Neo 编译
import sagemaker
from sagemaker.pytorch import PyTorchModel
pytorch_model = PyTorchModel(
model_data='s3://bucket/nas-discovered-model.tar.gz',
role=sagemaker.get_execution_role(),
framework_version='2.0',
py_version='py310'
)
# Compile for edge deployment
compiled_model = pytorch_model.compile(
target_instance_family='ml_c5', # or 'deeplens', 'jetson_nano', etc.
input_shape={'input': [1, 3, 224, 224]},
output_path='s3://bucket/compiled-model/',
framework='pytorch'
)
Amazon SageMaker Training Compiler:优化 deep learning models 的训练,最多可以减少 50% 的训练时间。对于在完整数据集上重新训练 NAS-discovered architectures,这尤其有用。
AWS Inferentia / AWS Trainium:为 inference(Inferentia)和 training(Trainium)优化的自定义 ML 芯片。NAS 发现的架构可以使用 AWS Neuron SDK 专门编译到这些芯片上,如示例 6-14 所示。
示例 6-14:AWS Neuron 编译
import torch_neuronx
# Compile NAS-discovered model for Inferentia
model_neuron = torch_neuronx.trace(
model,
example_inputs,
compiler_args=['--target', 'inf2']
)
model_neuron.save('nas_model_inf2.pt')
Cross-Platform Optimization Tools
ONNX Runtime:Microsoft 的推理引擎,提供硬件无关优化。将 NAS-discovered architecture 导出为 ONNX 格式,可实现可移植部署,如示例 6-15 所示。
示例 6-15:ONNX 导出和优化
import torch.onnx
# Export NAS-discovered model to ONNX
torch.onnx.export(
nas_model,
dummy_input,
"nas_model.onnx",
input_names=['input'],
output_names=['output'],
dynamic_axes={'input': {0: 'batch_size'}}
)
# Optimize with ONNX Runtime
import onnxruntime as ort
session = ort.InferenceSession(
"nas_model.onnx",
providers=['CUDAExecutionProvider', 'CPUExecutionProvider']
)
Intel OpenVINO:为 Intel 硬件优化模型,包括 CPUs、integrated GPUs、VPUs。当 NAS-discovered architectures 部署到 Intel 处理器边缘设备上时,这尤其有用。
NVIDIA TensorRT:用于 NVIDIA GPUs 的高性能推理优化器。它可以通过 layer fusion、precision calibration 和 kernel optimization 实现 2 到 10 倍加速,如示例 6-16 所示。
示例 6-16:TensorRT 转换
import tensorrt as trt
import torch2trt
# Convert NAS-discovered PyTorch model to TensorRT
model_trt = torch2trt.torch2trt(
nas_model,
[dummy_input],
fp16_mode=True, # Enable FP16 for faster inference
max_batch_size=32
)
Integrated Deployment Workflow
对于生产 NAS pipelines,我推荐以下工作流:
- Search phase:使用 NAS 发现最优架构。
- Validation phase:从零训练发现的架构,并验证性能。
- Optimization phase:应用部署特定优化,例如 Neo、TensorRT、OpenVINO。
- Benchmarking phase:在目标硬件上测量真实 latency / throughput。
- Deployment phase:部署优化后的模型并接入监控。
这种端到端方法确保 NAS 带来的效率收益可以转化为真实生产收益。
Zero-Cost Proxies 用于快速过滤
Zero-cost proxies 已成为高效 NAS 的必备工具,使我们能在投入训练计算资源之前,快速淘汰明显很差的架构。
我开发过一个多阶段过滤 pipeline,使用多个互补 proxies,如示例 6-17 所示。
示例 6-17:使用多个 Proxies 的 Zero-Cost Filter
class ZeroCostFilter:
def __init__(self):
self.proxies = [
GradientNormProxy(),
SNIPProxy(),
SynFlowProxy(),
ConnectivityProxy()
]
def filter_architectures(self, architectures, keep_fraction=0.1):
"""Filter architectures using multiple zero-cost proxies"""
scores = {}
# Compute all proxy scores
for proxy in self.proxies:
proxy_scores = proxy.score_batch(architectures)
for arch, score in zip(architectures, proxy_scores):
if arch not in scores:
scores[arch] = []
scores[arch].append(score)
# Combine proxy scores (ensemble)
final_scores = {}
for arch, proxy_scores in scores.items():
# Simple average of normalized scores
normalized = [
(s - min(proxy_scores)) / (max(proxy_scores) - min(proxy_scores))
for s in proxy_scores
]
final_scores[arch] = sum(normalized) / len(normalized)
# Keep top fraction
sorted_archs = sorted(
architectures,
key=lambda x: final_scores[x],
reverse=True
)
keep_count = int(len(architectures) * keep_fraction)
return sorted_archs[:keep_count]
这种过滤方法被证明非常有效。在一次近期实验中,我们从 100000 个随机架构开始,使用 zero-cost proxies 过滤出前 1000 个,大约耗时 2 小时,然后对过滤后的集合应用更昂贵的评估方法。这个流程最终找到的最佳架构与实际 global optimum 只差 0.3% 准确率,该结果基于 CIFAR-10 测量;在 ImageNet 等更复杂数据集上,差距预计会更大,因为 search space correlations 可能较弱。整个搜索一天完成,而穷举评估预计需要 6 个月。
实践实现指南
根据我实现高效 NAS 系统的经验,给实践者的关键指南如下:
Start simple:先使用成熟、高效方法,例如 DARTS 和 progressive search,再尝试自定义方法。
Leverage existing tools:AutoKeras、NNI、Optuna 等现代框架用很少代码就能提供复杂 NAS 能力。
Design for your constraints:将你的部署约束,例如 latency、memory、energy,明确纳入搜索过程。
Use multiple evaluation stages:采用漏斗方法,逐步使用越来越昂贵的评估方法。
Monitor resource usage:在整个搜索过程中跟踪计算成本,以优化 efficiency-accuracy trade-off。
Validate results:始终验证通过高效方法发现的架构,在从零训练时是否真正表现良好。
NAS 的效率革命让自动化架构发现可以被从个人研究者到大型组织的广泛实践者使用。关键是理解哪些效率技术适合你的具体约束和需求。
实践应用与工具
NAS 的成熟伴随着优秀工具的发展,这些工具让实践者更容易使用这些技术。你现在不需要从零实现复杂搜索算法,而是可以通过高层 API 使用复杂 NAS 能力。
真实世界中的 NAS:集成与部署
发现新架构固然令人兴奋,但最终目标是把它们部署到真实应用中。Notebook 不仅展示如何找到好架构,也展示如何:
- 将发现的架构导出为标准 PyTorch 或 TensorFlow 模型
- 在 held-out datasets 上验证架构表现,确保泛化
- 对比不同发现架构的计算效率
- 与已有训练 pipelines 和部署 workflows 集成
这些实践考虑在 NAS 的学术讨论中常被忽视,但对在行业环境中成功应用这些技术至关重要。
AutoKeras:简单优先
AutoKeras 是最易用的 NAS 入口,其 API 抽象掉了大部分复杂性。我经常推荐给那些想尝试 NAS、但不想深入技术细节的实践者。示例 6-18 提供了一个例子。
示例 6-18:AutoKeras 图像分类
import autokeras as ak
import tensorflow as tf
def autokeras_image_classification_example():
"""
Example using AutoKeras for neural architecture search on CIFAR-10.
"""
try:
# Load CIFAR-10 dataset
(x_train, y_train), (x_test, y_test) = \
tf.keras.datasets.cifar10.load_data()
# Use subset for faster demonstration
x_train, y_train = x_train[:10000], y_train[:10000]
x_test, y_test = x_test[:5000], y_test[:5000]
# Normalize images
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0
# Create AutoKeras classifier
clf = ak.ImageClassifier(
max_trials=10,
overwrite=True,
objective="val_accuracy",
project_name="autokeras_cifar10_search"
)
# Search for best architecture
clf.fit(x_train, y_train, validation_split=0.2, epochs=3)
# Evaluate
test_loss, test_acc = clf.evaluate(x_test, y_test, verbose=0)
print(f"Test Accuracy: {test_acc:.4f}")
return clf, clf.export_model()
except ImportError:
print("AutoKeras not installed. Install with: pip install autokeras")
return None, None
AutoKeras 会自动处理 search space 设计、search strategy 和 performance estimation。虽然这限制了 customization,但它让只想要结果的实践者也能使用 NAS。
NNI:企业级 NAS
Microsoft 的 NNI(Neural Network Intelligence)提供更复杂的 NAS 能力,适用于生产环境。它支持多种搜索策略、分布式执行和广泛自定义。示例 6-19 展示了一个实现。
示例 6-19:NNI DARTS 实现
import nni
from nni.algorithms.nas.pytorch import DARTS
# Define search space
class SearchSpace(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = LayerChoice([
nn.Conv2d(3, 32, 3, padding=1),
nn.Conv2d(3, 32, 5, padding=2),
nn.Conv2d(3, 32, 7, padding=3)
])
self.pool = nn.MaxPool2d(2)
self.conv2 = LayerChoice([
nn.Conv2d(32, 64, 3, padding=1),
nn.Conv2d(32, 64, 5, padding=2)
])
self.fc = nn.Linear(64 * 8 * 8, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool(x)
x = F.relu(self.conv2(x))
x = self.pool(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
# Configure DARTS search strategy
model = SearchSpace()
strategy = DARTS()
# Run NAS experiment
strategy.run(model, train_loader, val_loader, epochs=50)
NNI 的优势在于灵活性和可扩展性。它可以将搜索分布到多台机器,并支持自定义 search strategies 和 spaces。
Ray Tune + Optuna:灵活且强大
Ray Tune 和 Optuna 的组合,为自定义 NAS 实现提供了很好的灵活性,同时保持易用性,如示例 6-20 所示。
示例 6-20:使用 ASHA Scheduler 的 Ray Tune NAS
def ray_tune_nas_example():
"""
Example using Ray Tune for distributed architecture search.
"""
try:
import ray
from ray import tune, train
from ray.tune.search.optuna import OptunaSearch
from ray.tune.schedulers import ASHAScheduler
from ray.tune import Tuner, TuneConfig
# Ensure Ray is initialized
if not ray.is_initialized():
ray.init(ignore_reinit_error=True)
def train_architecture(config):
"""Training function for Ray Tune."""
# Build architecture from config
architecture = []
for i in range(config['num_layers']):
layer_config = {
'type': config['layer_type'],
'filters': config['base_filters'] * (2 ** (i // 3)),
'kernel_size': config['kernel_size'],
'activation': config['activation']
}
architecture.append(layer_config)
# Simulate training epochs with intermediate reporting
for epoch in range(config['max_epochs']):
intermediate_score = \
evaluate_architecture_placeholder(architecture)
epoch_bonus = 0.1 * (1 - np.exp(-epoch / 20))
current_score = intermediate_score + epoch_bonus
# Report intermediate result
train.report({"accuracy": current_score, "epoch": epoch})
# Define search space
search_space = {
'num_layers': tune.randint(3, 15),
'base_filters': tune.choice([32, 64, 128]),
'kernel_size': tune.choice([3, 5, 7]),
'layer_type': tune.choice(['conv', 'depthwise_conv']),
'activation': tune.choice(['relu', 'swish', 'gelu']),
'max_epochs': 50,
}
# Configure search algorithm and scheduler
search_alg = OptunaSearch(metric="accuracy", mode="max")
scheduler = ASHAScheduler(
metric="accuracy",
mode="max",
max_t=50,
grace_period=5,
reduction_factor=2
)
# Run tuning
tuner = Tuner(
train_architecture,
param_space=search_space,
tune_config=TuneConfig(
num_samples=100,
search_alg=search_alg,
scheduler=scheduler
)
)
results = tuner.fit()
best_result = results.get_best_result(metric="accuracy", mode="max")
print(f"Best config: {best_result.config}")
print(f"Best accuracy: {best_result.metrics['accuracy']:.4f}")
return best_result.config, results
except ImportError:
print("Ray Tune not installed. Install with: pip install 'ray[tune]'")
return None, None
这种方法既能让你定义自定义 search spaces 和 training procedures,又能利用复杂优化算法。
行业成功案例
下面分享一些组织成功应用 NAS 的具体例子:
Mobile computer vision:一家社交媒体公司使用 NAS 优化其实时图像滤镜和人脸检测 pipeline,用于移动端部署。发现的架构比 MobileNetV2 准确率高 25%,同时在常见智能手机上运行速度快 40%。
Medical imaging:一家放射影像 AI 初创公司将 NAS 应用于胸部 X 光肺炎检测,将检测准确率从 89% 提升到 93%,同时推理时间降低 60%。
Industrial IoT:一家制造企业使用 hardware-aware NAS 优化用于 edge deployment 的振动传感器分析模型,以预测设备故障,最终故障检测提升 15%,功耗降低 3 倍。
Financial services:一家金融科技公司将 NAS 应用于实时交易欺诈检测,发现的 ensemble architectures 将误报降低 30%,同时保持检测率。
PRACTICAL IMPLEMENTATION GUIDE
在项目中实现 NAS 时,可以遵循以下 progression:
Establish a baseline first:运行 NAS 之前,先在自己的数据上训练一个已知强架构,例如 ResNet-50、EfficientNet-B0 或类似模型,并进行适当超参数调优。这会给你一个具体目标,让你判断 NAS 是否真的找到了更好的架构,还是只是在重新发现已知方案。如果 NAS 不能超过调优良好的 ResNet,问题很可能在你的 search space 或 evaluation strategy,而不是数据潜力。
Start with the notebook examples:先运行 synthetic data examples,理解概念。
Adapt the search space:根据你的领域修改 SearchSpaceConfig 类。
Choose your strategy:为了简单起见,先从 evolutionary search 开始,然后探索更高级方法。
Validate correlations:确保你的评估方法与最终表现相关。
Scale gradually:先用小 search spaces 和预算开始,随着信心增长再扩展。
Notebook 包含详细注释和文档,会引导你完成该过程的每一步。
根据我在生产环境部署 NAS 的经验,关键建议如下:
Start with established tools:使用 AutoKeras 或 NNI 等成熟框架,而不是从零实现。
Define clear objectives:明确你在优化什么——只是准确率,还是带约束的准确率?
Validate thoroughly:始终验证 NAS-discovered architectures 在你的具体部署环境中表现良好。
Plan for iteration:NAS 很少是一次完成的过程;随着需求精炼,应计划多轮搜索迭代。
Consider transfer learning:从在类似问题上表现良好的架构开始,然后搜索改进。
WHEN NAS ROI IS LOW:决策框架
NAS 并不总是正确选择。在投入架构搜索之前,可以考虑以下 NAS 成本收益比可能不划算的场景。
适合跳过 NAS 的情况:
Well-solved problems:对于 ImageNet 分类或常见 NLP benchmarks 等标准任务,预设计架构,例如 EfficientNet、BERT 变体,已经被高度优化。NAS 不太可能找到明显更好的解决方案。
Small datasets:数据有限时,架构选择不如 regularization 和 data augmentation 重要。搜索成本通常无法被边际收益证明合理。
Tight timelines:如果你需要几天内而不是几周内部署模型,使用经过验证的架构配合 transfer learning。
Limited compute budget:如果无法承担 48 到 100+ GPU-hours 的搜索成本,ROI 很可能为负。
当 NAS ROI 较低时的替代方案:
Architecture scaling:从 EfficientNet-B0 开始,并根据 accuracy / latency 需求扩展到 B1、B2 等。
Compound scaling:使用成熟 scaling rules,例如 depth、width、resolution,而不是搜索。
Transfer learning + fine-tuning:使用预训练模型,并只优化最后几层。
Hyperparameter optimization:通常 80% 的收益来自正确 HPO,而不是架构变化。
NAS 值得使用的情况:
- 没有成熟架构的新领域
- 严格硬件约束,例如特定边缘设备、自定义加速器
- 多目标优化需求,例如 accuracy + fairness + latency
- 2%–5% 准确率提升能转化为显著业务价值的问题
一个好的经验法则是:NAS 应当相对你的最佳 baseline 至少带来 3%–5% 提升,才值得搜索成本。如果 baseline 已经很强,而提升很有限,ROI 很可能为负。
从 Notebook 到生产:下一步
本章和配套 notebook 中的实现,为理解和实验 NAS 提供了坚实基础。要从实验走向生产部署,可以考虑以下额外步骤:
Data integration:用真实数据替代 synthetic datasets,并确保架构搜索和最终训练中使用相同预处理与 augmentation strategies。
Scaling considerations:Notebook 示例设计为在单机运行。对于大规模搜索,可考虑使用 Ray 或 Kubernetes 等框架的分布式实现。
Architecture validation:始终通过在完整数据集上从零训练来验证发现的架构。搜索时表现与最终表现之间的相关性,应在你的具体领域中得到验证。
Integration with MLOps:考虑发现的架构如何与你现有模型训练、验证和部署 pipelines 集成。
小结
在结束本章时,值得回顾 NAS 已经走了多远,以及它将走向哪里。这个领域已经从需要海量计算资源的有趣学术实验,演化为任何 ML 实践者都可以使用的实用工具。
几个趋势正在塑造 NAS 的未来:
Integration with foundation models:NAS 越来越多被用于适配和专门化大型预训练模型,以满足特定任务和部署约束。
Multimodal architecture search:将 NAS 扩展到在统一架构中同时处理多个数据模态,例如 vision、language、audio。
Sustainable AI:人们越来越关注能效架构,并减少搜索和部署的环境影响。近期工作已经开始明确将 carbon footprint 和 energy consumption 纳入 multi-objective NAS 的目标,不只是优化推理效率,也优化搜索过程本身的环境成本。一些框架现在会与准确率指标一起报告估算 CO2 emissions,使实践者可以作出知情取舍。随着 ML 的环境影响越来越受到监管机构和公众审视,预计 carbon-aware NAS 将成为标准实践。
Automated MLOps:将 NAS 与更广泛的 MLOps pipelines 集成,在生产系统中实现持续架构优化。
一个很有意思的发展,是 architecture search 中 agentic workflows 的出现。近期研究表明,foundation models 与 evolutionary search 结合,可以自主导航整个优化空间,包括问题定义、算法选择和超参数调优。这代表一种转变:从传统 search spaces,转向更智能、适应性更强,能够推理架构设计决策的系统。
类似地,AIDE(AI-Driven Exploration)展示了由 LLM 驱动的 agents 如何将 ML engineering 概念化为 code optimization problem,使用 tree search methods 通过生成、调试和改进 ML 代码,系统性探索和优化解决方案。这种方法通过利用大语言模型内嵌的领域知识,达到了 state-of-the-art 结果。
NAS 只是本书探索的更广泛 AutoML 生态中的一个组成部分。通过 NAS 发现的架构,仍然需要合适的 feature engineering,第 4 章;hyperparameter optimization,第 5 章;并且需要集成进生产系统,第 11–13 章。
下一章中,我们将从核心技术转向动手实践,使用 AutoGluon 处理 tabular data,从环境设置、TabularPredictor 基础和生产考虑开始。
从手工架构设计到自动化发现的旅程非常惊人。过去需要深厚专业知识和数月实验才能完成的事情,现在拥有基础 ML 知识的实践者,可以在数小时或数天内完成。架构设计的这种民主化,是让机器学习更易访问、更实用的最重要发展之一。
当你开始把这些技术应用到自己的项目中时,请记住:NAS 是工具,不是魔法解决方案。最好的结果来自有思考的应用:理解你的约束,选择合适 search spaces,并彻底验证结果。在这些基础到位后,NAS 可以帮助你发现相比手工设计具有更好性能、更快推理和更高资源利用效率的架构。