神经架构搜索基础
神经架构搜索(Neural Architecture Search, NAS)的目标是在给定的搜索空间中,自动发现性能最优或接近最优的神经网络架构。NAS由三要素构成:搜索空间(Search Space)、搜索策略(Search Strategy)和评估策略(Evaluation Strategy)。1
1. 搜索空间定义
搜索空间定义了NAS可以探索的所有候选架构的集合。
1.1 基本结构类型
链式结构
最简单的架构形式,层按顺序堆叠:
输入 → Layer1 → Layer2 → ... → LayerN → 输出
数学表示为:
其中 为激活函数, 为第 层的权重矩阵。
多分支结构
引入跳跃连接(skip connection),允许特征融合:
↘ Layer2 → Add →
Input → Layer1 → Layer3 → ... → Output
↗ ↗
典型代表:ResNet的残差连接、DenseNet的密集连接。
细胞结构(Cell Structure)
NASNet2提出的革命性设计:将复杂网络分解为正常细胞(Normal Cell)和缩减细胞(Reduction Cell)。
Normal Cell: 保持空间分辨率
Reduction Cell: 空间分辨率减半
每个细胞内部包含 个节点的有向无环图(DAG),节点间通过操作连接。
1.2 搜索空间参数化
设搜索空间 由以下要素定义:
| 要素 | 选项 |
|---|---|
| 操作集 | conv_3x3, conv_5x5, max_pool, skip_connect, sep_conv_3x3, sep_conv_5x5 |
| 节点数 | 每个细胞的节点数 |
| 边连接 | 全连接、稀疏连接 |
| 通道数 | 固定、搜索 |
| 输入分辨率 | 固定、弹性 |
DAG表示:一个细胞可表示为 个节点的有向图,每个节点 的特征 为:
其中 是从节点 到节点 的操作。
1.3 搜索空间大小分析
假设每个节点有 种操作可选,共有 条可能的边,则候选架构数:
以NASNet的搜索空间为例:
- 节点数
- 边数
- 操作数
这解释了为何需要高效的搜索策略。
2. 搜索策略
2.1 强化学习方法
控制器网络
NASNet2使用RNN控制器预测网络架构:
class Controller(nn.Module):
"""RNN控制器预测架构编码"""
def __init__(self, num_ops, hidden_size=64):
super().__init__()
self.rnn = nn.LSTM(
input_size=hidden_size,
hidden_size=hidden_size,
num_layers=2
)
self.fc = nn.Linear(hidden_size, num_ops)
def forward(self, embedded_input, hidden=None):
output, hidden = self.rnn(embedded_input, hidden)
logits = self.fc(output)
return logits, hidden
def sample_architecture(self):
"""采样一个架构"""
arch_codes = []
hidden = None
for _ in range(num_layers):
logits, hidden = self.rnn(self.embed, hidden)
probs = F.softmax(logits, dim=-1)
op_idx = torch.multinomial(probs, 1).item()
arch_codes.append(op_idx)
return arch_codes奖励函数设计
其中 为架构 的验证准确率。
训练过程:
- 控制器采样架构
- 训练架构 得到验证准确率
- 使用策略梯度更新控制器:
2.2 进化算法
基本框架
初始化: 生成初始种群 P
迭代:
1. 选择: 从P中选择父代
2. 变异: 对父代进行操作变异
3. 评估: 评估子代适应度
4. 更新: 替换种群中的个体
终止: 达到终止条件
操作符定义
| 操作类型 | 描述 | 示例 |
|---|---|---|
| 变异 | 随机改变一条边 | conv_3x3 → conv_5x5 |
| 添加边 | 添加新连接 | 无连接 → skip_connect |
| 删除边 | 移除现有连接 | skip_connect → 无 |
| 添加节点 | 在现有节点间插入 | - |
| 改变通道数 | 调整通道宽度 | 64 → 128 |
NSGA-Net算法
NSGA-Net3使用NSGA-II框架同时优化多个目标:
class EvolutionSearch:
def __init__(self, pop_size=20, n_gens=50):
self.pop_size = pop_size
self.n_gens = n_gens
self.history = []
def mutate(self, parent):
"""架构变异操作"""
child = copy.deepcopy(parent)
op = random.choice(['mutate_op', 'add_edge', 'remove_edge'])
if op == 'mutate_op':
# 随机选择一个边,改变其操作
edge = random.choice(list(child.edges))
ops = [op for op in OPS if op != child.get_op(edge)]
child.set_op(edge, random.choice(ops))
elif op == 'add_edge':
# 添加新边
child.add_random_edge()
return child
def select_parents(self, population, fitness):
"""锦标赛选择"""
indices = random.sample(range(len(population)), k=2)
winner_idx = indices[0] if fitness[indices[0]] > fitness[indices[1]] else indices[1]
return population[winner_idx]2.3 贝叶斯优化
高斯过程代理
使用高斯过程(GP)建模架构-性能映射:
采集函数(Acquisition Function):
| 函数 | 公式 | 特点 |
|---|---|---|
| UCB | 平衡探索与利用 | |
| EI | 期望改进 | |
| PI | 概率改进 |
BOHB算法
BOHB(Bayesian Optimization with Hyperband)4结合贝叶斯优化与Hyperband早停:
class BOHB:
def __init__(self, config_space, eta=3):
self.config_space = config_space
self.eta = eta # 早停缩减因子
self.trial_generator = TrialGenerator(config_space)
def suggest(self):
"""基于GP模型建议下一个配置"""
# 使用贝叶斯优化选择配置
configs = self.trial_generator.generate_candidates(k=20)
scores = [self.gp.predict(cfg) for cfg in configs]
return configs[np.argmax(scores)]
def observe(self, config, reward):
"""更新GP模型"""
self.gp.update(config, reward)2.4 梯度方法
详见 DARTS可微架构搜索。
3. 评估策略
3.1 完整训练评估
传统方法:对每个候选架构完整训练至收敛。
问题:计算代价极高。以搜索空间大小 为例,即使每次训练仅需1小时,总训练时间也超过 年。
3.2 权重共享机制
ENAS(Efficient NAS)5提出的核心思想:所有候选架构共享一组权重。
超网络构建
class SuperNet(nn.Module):
"""超网络,包含所有可能的路径"""
def __init__(self, num_nodes, num_ops):
super().__init__()
self.num_nodes = num_nodes
# 每个边有num_ops个可选操作,每种操作有独立权重
self.edge_weights = nn.ParameterList([
nn.Parameter(torch.randn(num_ops, C_in, C_out) / sqrt(C_in))
for _ in range(num_edges)
])
def forward(self, inputs, architecture):
"""基于架构编码前向传播"""
nodes = [inputs]
for node_idx in range(1, self.num_nodes):
node_value = 0
for prev_idx in range(node_idx):
edge_idx = get_edge_idx(prev_idx, node_idx, architecture)
op_idx = architecture[edge_idx]
node_value += F.relu(
torch.einsum('bic,co->bio', nodes[prev_idx],
self.edge_weights[edge_idx][op_idx])
)
nodes.append(node_value)
return nodes[-1]权重更新
其中 为架构 对应的权重, 为采样子架构集。
3.3 代理指标评估
使用无需训练的指标快速评估架构:
| 指标类型 | 代表方法 | 计算复杂度 |
|---|---|---|
| 参数相关 | 参数量、FLOPs | |
| 梯度相关 | SNIP、GraSP | |
| 信息论 | 熵、互信息 | |
| 随机预言 | 随机权重精度 |
详见 免训练NAS。
4. 多目标优化
4.1 Pareto最优性
当存在多个优化目标时,引入Pareto最优概念:
定义:架构 是Pareto最优的,当且仅当不存在架构 使得:
其中至少一个是严格不等式。
Pareto前沿:所有Pareto最优解构成的集合。
4.2 优化方法
加权求和法
问题:无法发现非凸Pareto前沿。
NSGA-III
NSGA-III6使用参考点方法处理多目标:
class NSGA_III:
def __init__(self, n_objectives, n_ref_points):
self.n_objectives = n_objectives
self.reference_points = self.generate_reference_points(n_ref_points)
def niching_selection(self, combined_pop, n_select):
"""基于参考点的选择"""
# 计算每个个体到参考点的距离
distances = self.compute_perpendicular_distances(combined_pop)
# 选择最近的参考点对应的个体
selected = self.select_closest_to_reference_points(
combined_pop, distances, n_select
)
return selected5. NAS关键挑战
5.1 搜索-评估差距
搜索过程中评估的架构与最终部署架构性能不一致:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 权重初始化影响 | 不同架构权重初始化不同 | 预训练超网络 |
| 过拟合 | 搜索空间过拟合训练集 | 正则化、验证集 |
| 早停误差 | 早停评估不准确 | 延长训练、代理指标 |
5.2 可复现性问题
NAS结果的可复现性较低,主要原因:
- 随机性:初始化、采样、变异
- 硬件差异:浮点精度、并行化
- 超参数敏感:学习率、批量大小
- 环境依赖:库版本、CUDA版本
解决方案:NAS-Bench基准测试库(详见 NAS基准测试)。
5.3 扩展性问题
| 问题 | 描述 | 研究方向 |
|---|---|---|
| 搜索空间指数增长 | 边数/操作数增加导致空间指数爆炸 | 层次化搜索、模块化 |
| 评估成本 | 大规模网络训练成本高 | 早停、知识蒸馏 |
| 多任务迁移 | 搜索结果难以迁移到不同任务 | 任务无关表示 |
6. 实践指南
6.1 何时使用NAS
适合场景:
- 计算资源充足(>100 GPU days)
- 搜索空间定义清晰
- 目标硬件明确
- 需要超越人工设计的最优结果
不适合场景:
- 小规模数据集(可能过拟合搜索空间)
- 快速原型开发(NAS开销过大)
- 资源受限环境(建议使用 现成的高效架构)
6.2 推荐工作流
┌─────────────────────────────────────────────────────────┐
│ NAS工作流程 │
├─────────────────────────────────────────────────────────┤
│ 1. 明确需求 │
│ - 任务类型(分类/检测/分割) │
│ - 约束条件(延迟/参数量/FLOPs) │
│ - 部署环境(GPU/移动端/嵌入式) │
│ │
│ 2. 定义搜索空间 │
│ - 选择基础操作集 │
│ - 确定网络深度/宽度范围 │
│ - 设计细胞结构或层次化模式 │
│ │
│ 3. 选择搜索策略 │
│ - 资源充足 → DARTS/进化算法 │
│ - 资源受限 → 免训练NAS │
│ - 多目标 → NSGA-III │
│ │
│ 4. 评估与选择 │
│ - 多指标Pareto最优 │
│ - 完整训练验证 │
│ - 目标硬件实测 │
└─────────────────────────────────────────────────────────┘
参考文献
Footnotes
-
Elsken T, Metzen JH, Hutter F. Neural Architecture Search: A Survey. JMLR 2019. ↩
-
Zoph B, Vasudevan V, Shlens J, Le QV. Learning Transferable Architectures for Scalable Image Recognition. CVPR 2018. ↩ ↩2
-
Lu ZY, et al. NSGA-Net: Neural Architecture Search using Multi-objective Genetic Algorithm. GECCO 2020. ↩
-
Falkner S, Klein A, Hutter F. BOHB: Robust and Efficient Hyperparameter Optimization at Scale. ICML 2018. ↩
-
Pham H, et al. Efficient Neural Architecture Search via Parameters Sharing. ICML 2018. ↩
-
Deb K, Jain H. An Evolutionary Many-Objective Optimization Algorithm Using Reference-Point-Based Nondominated Sorting Approach. IEEE TEVC 2014. ↩