神经架构搜索深度解析(Neural Architecture Search, NAS)

目录

  1. [NAS 基础理论](#NAS 基础理论)
  2. 搜索空间设计
  3. 基于强化学习的搜索
  4. 基于进化的搜索
  5. DARTS:可微分架构搜索
  6. [高效 NAS 方法](#高效 NAS 方法)
  7. [硬件感知 NAS](#硬件感知 NAS)
  8. [NAS 在不同领域的应用](#NAS 在不同领域的应用)
  9. 评估指标与基准
  10. 前沿与展望

1. NAS 基础理论

1.1 什么是神经架构搜索

复制代码
神经架构搜索 (Neural Architecture Search, NAS) 的定义:

  自动设计最优的神经网络架构

  ┌─────────────────────────────────────────────────────────────────┐
  │                                                                 │
  │   传统方法: 人工设计架构                                        │
  │   ┌───────────────────────────────────────────────────────┐    │
  │   │  专家经验 → 设计架构 → 训练 → 评估 → 调整 → ...       │    │
  │   └───────────────────────────────────────────────────────┘    │
  │                                                                 │
  │   NAS: 自动搜索架构                                             │
  │   ┌───────────────────────────────────────────────────────┐    │
  │   │  搜索空间 → 搜索策略 → 架构评估 → 最优架构             │    │
  │   └───────────────────────────────────────────────────────┘    │
  │                                                                 │
  └─────────────────────────────────────────────────────────────────┘

  NAS 的三个核心组件:
    1. 搜索空间 (Search Space): 定义可能的架构
    2. 搜索策略 (Search Strategy): 如何探索搜索空间
    3. 评估策略 (Evaluation Strategy): 如何评估架构性能

1.2 NAS 的理论框架

复制代码
NAS 的数学形式化:

  给定:
    搜索空间 A
    评估函数 f(a) (架构 a 的性能)
    计算预算 B
    
  目标:
    a* = argmax_{a∈A} f(a)
    s.t. 计算成本 ≤ B
    
  挑战:
    1. 搜索空间巨大: |A| 可能达到 10^18
    2. 评估昂贵: 每个架构需要训练
    3. 离散优化: 架构是离散变量

1.3 NAS 的发展历史

复制代码
NAS 发展时间线:

  2017  ──┬──  NASNet (强化学习)
          │    首次大规模 NAS
          │
  2018  ──┼──  ENAS (权重共享)
          │    大幅降低计算成本
          │
  2019  ──┼──  DARTS (可微分搜索)
          │    连续松弛,梯度优化
          │
  2020  ──┼──  EfficientNet (复合缩放)
          │    系统化的模型缩放
          │
  2021  ──┼──  AutoFormer (ViT NAS)
          │    Transformer 架构搜索
          │
  2022+ ──┴──  大模型 NAS, 一次性 NAS

2. 搜索空间设计

2.1 搜索空间概述

复制代码
搜索空间 (Search Space):

  定义: 所有可能的架构的集合

  设计原则:
    1. 表达能力: 能表示好的架构
    2. 大小适中: 不能太大 (无法搜索) 也不能太小 (错过好架构)
    3. 先验知识: 包含已知的有效设计

  搜索空间类型:
    1. 宏观搜索空间: 整个网络架构
    2. 微观搜索空间: 重复的单元 (Cell)

2.2 Cell-based 搜索空间

复制代码
Cell-based 搜索空间:

  核心思想:
    搜索一个基本单元 (Cell)
    然后堆叠多个 Cell 形成完整网络

  Cell 结构:
    - N 个节点 (Node)
    - 每个节点有多个输入
    - 每个输入有选择的操作

  ┌─────────────────────────────────────────────────────────────────┐
  │                                                                 │
  │   Cell 结构:                                                    │
  │                                                                 │
  │   输入 ─┬─► [操作1] ─┬─► [操作2] ─┬─► 输出                    │
  │         │            │            │                             │
  │         └─► [操作3] ─┘            │                             │
  │                                   │                             │
  │         ┌─► [操作4] ─────────────┘                             │
  │         │                                                       │
  │         └─► [操作5] ───────────────► 输出                       │
  │                                                                 │
  └─────────────────────────────────────────────────────────────────┘
python 复制代码
import torch
import torch.nn as nn

class Cell(nn.Module):
    """
    Cell-based 搜索空间中的基本单元
    
    理论:
      搜索一个基本 Cell 结构
      堆叠多个 Cell 形成完整网络
      
      优势:
        - 搜索空间小
        - 架构可复用
        - 便于迁移
    """
    def __init__(self, num_nodes=4, num_ops=8):
        super().__init__()
        
        self.num_nodes = num_nodes
        
        # 可选操作
        self.ops = nn.ModuleDict({
            'none': Zero(),
            'skip_connect': Identity(),
            'sep_conv_3x1': SepConv(3, 1),
            'sep_conv_5x1': SepConv(5, 1),
            'sep_conv_3x2': SepConv(3, 2),
            'sep_conv_5x2': SepConv(5, 2),
            'dil_conv_3x2': DilConv(3, 2),
            'avg_pool_3x1': Pool('avg', 3),
            'max_pool_3x1': Pool('max', 3)
        })
        
        # 节点输入选择
        self.node_inputs = nn.ModuleDict()
        for i in range(2, num_nodes + 2):
            self.node_inputs[f'node_{i}'] = nn.ModuleDict({
                f'input_{j}': nn.ModuleDict({
                    op_name: op for op_name, op in self.ops.items()
                })
                for j in range(i)
            })
    
    def forward(self, s0, s1, weights):
        """
        s0, s1: 前两个 Cell 的输出
        weights: 架构参数 (每个操作的权重)
        """
        states = [s0, s1]
        
        for i in range(2, self.num_nodes + 2):
            # 对每个输入加权求和
            node_input = 0
            for j in range(i):
                for op_name, op in self.ops.items():
                    weight = weights[f'node_{i}'][f'input_{j}'][op_name]
                    node_input = node_input + weight * op(states[j])
            
            states.append(node_input)
        
        # 拼接所有中间节点
        output = torch.cat(states[2:], dim=1)
        
        return output

class SepConv(nn.Module):
    """深度可分离卷积"""
    def __init__(self, kernel_size, stride):
        super().__init__()
        self.op = nn.Sequential(
            nn.ReLU(),
            nn.Conv2d(32, 32, kernel_size, stride=stride, padding=kernel_size//2, groups=32),
            nn.Conv2d(32, 32, 1),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.Conv2d(32, 32, kernel_size, stride=1, padding=kernel_size//2, groups=32),
            nn.Conv2d(32, 32, 1),
            nn.BatchNorm2d(32)
        )
    
    def forward(self, x):
        return self.op(x)

"""
Cell-based 搜索空间的优势:

  1. 搜索空间小:
     只搜索一个 Cell
     而非整个网络
     
  2. 可迁移:
     Cell 可以在不同数据集上复用
     可以堆叠不同数量的 Cell
     
  3. 先验知识:
     Cell 结构基于已有的设计经验

2.3 宏观搜索空间

复制代码
宏观搜索空间:

  搜索整个网络架构
  包括每一层的操作和连接

  优势:
    - 更大的搜索空间
    - 可能发现更好的架构
    
  劣势:
    - 搜索空间巨大
    - 计算成本高
    - 难以迁移
python 复制代码
class MacroSearchSpace(nn.Module):
    """
    宏观搜索空间
    
    搜索整个网络架构
    """
    def __init__(self, num_layers=20, num_ops=8):
        super().__init__()
        
        self.num_layers = num_layers
        
        # 每层可选的操作
        self.ops = nn.ModuleList([
            nn.ModuleDict({
                'conv_3x3': nn.Conv2d(32, 32, 3, padding=1),
                'conv_5x5': nn.Conv2d(32, 32, 5, padding=2),
                'sep_conv_3x3': SepConv(3, 1),
                'sep_conv_5x5': SepConv(5, 1),
                'dil_conv_3x3': DilConv(3, 1),
                'max_pool_3x3': nn.MaxPool2d(3, padding=1),
                'avg_pool_3x3': nn.AvgPool2d(3, padding=1),
                'skip': nn.Identity()
            })
            for _ in range(num_layers)
        ])
    
    def forward(self, x, arch_params):
        """
        x: [B, C, H, W]
        arch_params: 每层的操作选择
        """
        for i in range(self.num_layers):
            op_name = arch_params[i]
            x = self.ops[i][op_name](x)
        
        return x

"""
宏观 vs 微观搜索空间:

  宏观:
    搜索整个网络
    搜索空间大
    难以迁移
    
  微观 (Cell-based):
    搜索基本单元
    搜索空间小
    便于迁移
    
  实践中 Cell-based 更常用
"""

3. 基于强化学习的搜索

3.1 NASNet

复制代码
论文: "Learning Transferable Architectures for Scalable Image Recognition" 
      (Zoph et al., 2018)

核心思想:
  使用强化学习搜索最优的 Cell 结构

  方法:
    1. RNN 控制器生成架构描述
    2. 训练子模型评估性能
    3. 用性能作为奖励更新控制器
python 复制代码
class NASNetController(nn.Module):
    """
    NASNet 控制器
    
    使用 RNN 生成架构描述
    
    理论:
      RNN 逐步生成架构的每个选择
      使用 REINFORCE 算法训练
    """
    def __init__(self, num_ops=8, num_nodes=4):
        super().__init__()
        
        self.num_ops = num_ops
        self.num_nodes = num_nodes
        
        # RNN 控制器
        self.rnn = nn.LSTMCell(32, 32)
        
        # 预测头
        self.op_logits = nn.Linear(32, num_ops)
        self.input_logits = nn.Linear(32, num_nodes)
        
        # 嵌入
        self.op_embedding = nn.Embedding(num_ops, 32)
        self.input_embedding = nn.Embedding(num_nodes, 32)
    
    def forward(self, batch_size=1):
        """
        生成架构描述
        """
        # 初始化
        h = torch.zeros(batch_size, 32)
        c = torch.zeros(batch_size, 32)
        
        ops = []
        inputs = []
        
        for i in range(self.num_nodes):
            # 预测操作
            op_logit = self.op_logits(h)
            op = torch.multinomial(torch.softmax(op_logit, dim=-1), 1)
            ops.append(op)
            
            # 预测输入
            input_logit = self.input_logits(h)
            input_idx = torch.multinomial(torch.softmax(input_logit, dim=-1), 1)
            inputs.append(input_idx)
            
            # 更新 RNN
            op_emb = self.op_embedding(op)
            input_emb = self.input_embedding(input_idx)
            h, c = self.rnn(op_emb + input_emb, (h, c))
        
        return ops, inputs

"""
NASNet 的训练:

  REINFORCE 算法:
    1. 控制器采样架构
    2. 训练子模型
    3. 用验证准确率作为奖励
    4. 更新控制器
    
  奖励: R = 准确率
  
  梯度: ∇θ J(θ) = E[R · ∇θ log p(arch; θ)]
"""

3.2 REINFORCE 算法

python 复制代码
class REINFORCE:
    """
    REINFORCE 算法用于 NAS
    
    理论:
      策略梯度方法
      用性能作为奖励更新控制器
    """
    def __init__(self, controller, lr=0.001):
        self.controller = controller
        self.optimizer = torch.optim.Adam(controller.parameters(), lr=lr)
        
        # 基线 (减少方差)
        self.baseline = 0
    
    def train_step(self, architectures, rewards):
        """
        architectures: 采样的架构
        rewards: 对应的性能
        """
        # 计算策略梯度
        log_probs = []
        for arch in architectures:
            log_prob = self.controller.log_prob(arch)
            log_probs.append(log_prob)
        
        log_probs = torch.stack(log_probs)
        
        # 减去基线
        advantages = rewards - self.baseline
        
        # 策略梯度损失
        loss = -(log_probs * advantages).mean()
        
        # 更新
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()
        
        # 更新基线
        self.baseline = 0.9 * self.baseline + 0.1 * rewards.mean().item()
        
        return loss.item()

"""
REINFORCE 的理论:

  策略梯度定理:
    ∇θ J(θ) = E[∇θ log π(a|s) · R(a)]
    
  在 NAS 中:
    策略: π(arch; θ) = 控制器生成架构的概率
    奖励: R(arch) = 架构的验证准确率
    
  基线:
    b = E[R] (移动平均)
    减少方差: ∇θ J(θ) = E[∇θ log π(a|s) · (R(a) - b)]
"""

4. 基于进化的搜索

4.1 进化算法概述

复制代码
进化算法 (Evolutionary Algorithm) 用于 NAS:

  核心思想:
    模拟自然选择
    保留好的架构,淘汰差的架构
    
  流程:
    1. 初始化种群
    2. 评估适应度 (性能)
    3. 选择 (保留好的)
    4. 变异 (生成新架构)
    5. 重复步骤 2-4
python 复制代码
class EvolutionaryNAS:
    """
    基于进化的 NAS
    
    理论:
      模拟自然选择
      保留好的架构,淘汰差的架构
    """
    def __init__(self, population_size=50, mutation_rate=0.1):
        self.population_size = population_size
        self.mutation_rate = mutation_rate
        self.population = []
    
    def initialize(self, search_space):
        """初始化种群"""
        for _ in range(self.population_size):
            arch = search_space.random_architecture()
            self.population.append(arch)
    
    def evolve(self, search_space, num_generations=100):
        """进化搜索"""
        for gen in range(num_generations):
            # 评估适应度
            fitness = []
            for arch in self.population:
                acc = self.evaluate(arch)
                fitness.append(acc)
            
            # 选择
            selected = self.select(fitness)
            
            # 变异
            new_population = []
            for arch in selected:
                new_arch = self.mutate(arch, search_space)
                new_population.append(new_arch)
            
            self.population = new_population
        
        # 返回最优架构
        best_idx = np.argmax(fitness)
        return self.population[best_idx]
    
    def select(self, fitness):
        """
        选择策略
        
        理论:
          保留适应度高的个体
          轮盘赌选择或锦标赛选择
        """
        # 锦标赛选择
        selected = []
        for _ in range(self.population_size):
            # 随机选择 k 个个体
            k = 3
            candidates = np.random.choice(len(self.population), k, replace=False)
            
            # 选择适应度最高的
            best = candidates[np.argmax([fitness[i] for i in candidates])]
            selected.append(self.population[best])
        
        return selected
    
    def mutate(self, arch, search_space):
        """
        变异操作
        
        理论:
          随机修改架构的部分选择
          探索搜索空间
        """
        new_arch = arch.copy()
        
        # 随机选择要变异的位置
        num_mutations = int(len(arch) * self.mutation_rate)
        mutation_positions = np.random.choice(len(arch), num_mutations, replace=False)
        
        for pos in mutation_positions:
            # 随机选择新操作
            new_arch[pos] = search_space.random_operation()
        
        return new_arch

"""
进化算法的优势:

  1. 简单: 易于实现
  2. 灵活: 可以处理各种约束
  3. 并行: 可以并行评估多个架构
  
  劣势:
    1. 计算量大
    2. 可能陷入局部最优
"""

4.2 AmoebaNet

复制代码
论文: "Regularized Evolution for Image Classifier Architecture Search" 
      (Real et al., 2019)

核心创新:
  正则化进化: 移除最老的个体 (而非最差的)

  理论:
    避免过拟合搜索历史
    保持种群多样性

5. DARTS:可微分架构搜索

5.1 DARTS 核心思想

复制代码
论文: "DARTS: Differentiable Architecture Search" (Liu et al., 2019)

核心创新:
  将离散的架构选择松弛为连续优化
  使用梯度下降搜索架构

  理论:
    原始问题: 离散选择 (argmax)
    松弛后: 连续权重 (softmax)
    
    可以用梯度下降优化
python 复制代码
class DARTS(nn.Module):
    """
    DARTS (Differentiable Architecture Search)
    
    核心创新:
      将离散选择松弛为连续优化
      使用梯度下降搜索架构
      
    理论:
      每个操作有一个权重 α
      输出 = Σ α_i · o_i(x)
      
      训练结束后,选择权重最大的操作
    """
    def __init__(self, num_nodes=4, num_ops=8):
        super().__init__()
        
        self.num_nodes = num_nodes
        
        # 操作
        self.ops = nn.ModuleList([
            nn.ModuleList([
                nn.ModuleDict({
                    'sep_conv_3x3': SepConv(3, 1),
                    'sep_conv_5x5': SepConv(5, 1),
                    'dil_conv_3x3': DilConv(3, 1),
                    'avg_pool_3x3': nn.AvgPool2d(3, padding=1),
                    'max_pool_3x3': nn.MaxPool2d(3, padding=1),
                    'skip_connect': nn.Identity(),
                    'none': Zero()
                })
                for _ in range(i)  # 每个节点有 i 个输入
            ])
            for i in range(2, num_nodes + 2)
        ])
        
        # 架构参数 (可学习)
        self.arch_params = nn.ParameterList()
        for i in range(2, num_nodes + 2):
            # 每个节点的每个输入的操作权重
            param = nn.Parameter(torch.randn(i, num_ops) * 0.001)
            self.arch_params.append(param)
    
    def forward(self, s0, s1):
        """
        s0, s1: 前两个 Cell 的输出
        """
        states = [s0, s1]
        
        for i in range(self.num_nodes):
            # 获取当前节点的架构权重
            weights = torch.softmax(self.arch_params[i], dim=-1)
            
            # 对每个输入加权求和
            node_input = 0
            for j in range(i + 2):
                for k, (op_name, op) in enumerate(self.ops[i][j].items()):
                    if op_name != 'none':
                        node_input = node_input + weights[j, k] * op(states[j])
            
            states.append(node_input)
        
        # 拼接所有中间节点
        output = torch.cat(states[2:], dim=1)
        
        return output
    
    def get_architecture(self):
        """获取最终架构"""
        arch = []
        for i in range(self.num_nodes):
            weights = torch.softmax(self.arch_params[i], dim=-1)
            ops = weights.argmax(dim=-1)
            arch.append(ops)
        
        return arch

"""
DARTS 的训练:

  双层优化:
    外层: 优化架构参数 α
    内层: 优化网络权重 w
    
    min_α L_val(w*(α), α)
    s.t. w*(α) = argmin_w L_train(w, α)
    
  实际实现:
    交替优化:
      1. 固定 α,更新 w (训练集)
      2. 固定 w,更新 α (验证集)
"""

5.2 DARTS 的问题与改进

复制代码
DARTS 的问题:

  1. 搜索不稳定:
     架构参数可能震荡
     搜索结果不一致
     
  2. 性能差距:
     搜索时的性能 ≠ 评估时的性能
     权重共享导致评估不准
     
  3. 搜索空间偏好:
     倾向于选择 skip_connect
     因为 skip_connect 训练更快

改进方法:
  1. P-DARTS: 渐进式搜索
  2. PC-DARTS: 部分通道连接
  3. Fair DARTS: 公平竞争
  4. DARTS-: 修正稳定性
python 复制代码
class FairDARTS(nn.Module):
    """
    Fair DARTS
    
    改进:
      使用 Sigmoid 替代 Softmax
      每个操作独立竞争
      
    理论:
      Softmax 导致操作之间竞争
      Sigmoid 允许多个操作共存
    """
    def __init__(self, num_nodes=4, num_ops=8):
        super().__init__()
        
        # 架构参数
        self.arch_params = nn.ParameterList([
            nn.Parameter(torch.randn(i, num_ops))
            for i in range(2, num_nodes + 2)
        ])
    
    def forward(self, s0, s1):
        states = [s0, s1]
        
        for i in range(self.num_nodes):
            # 使用 Sigmoid (每个操作独立)
            weights = torch.sigmoid(self.arch_params[i])
            
            # 加权求和
            node_input = 0
            for j in range(i + 2):
                for k, op in enumerate(self.ops[i][j]):
                    node_input = node_input + weights[j, k] * op(states[j])
            
            states.append(node_input)
        
        return torch.cat(states[2:], dim=1)

"""
FairDARTS vs DARTS:

  DARTS (Softmax):
    操作之间竞争
    容易导致 collapse
    
  FairDARTS (Sigmoid):
    每个操作独立
    更公平的竞争
"""

6. 高效 NAS 方法

6.1 权重共享

复制代码
权重共享 (Weight Sharing):

  核心思想:
    所有架构共享同一组权重
    避免为每个架构单独训练
    
  理论:
    超网络 (Supernet): 包含所有可能的架构
    子网络: 从超网络中采样
    
    训练超网络
    评估子网络
python 复制代码
class Supernet(nn.Module):
    """
    超网络 (Supernet)
    
    理论:
      包含所有可能的架构
      训练时随机采样子网络
      评估时选择最优子网络
    """
    def __init__(self, num_ops=8):
        super().__init__()
        
        # 所有可能的操作
        self.all_ops = nn.ModuleDict({
            'sep_conv_3x3': SepConv(3, 1),
            'sep_conv_5x5': SepConv(5, 1),
            'dil_conv_3x3': DilConv(3, 1),
            'avg_pool_3x3': nn.AvgPool2d(3, padding=1),
            'max_pool_3x3': nn.MaxPool2d(3, padding=1),
            'skip_connect': nn.Identity()
        })
    
    def forward(self, x, arch):
        """
        arch: 架构描述 (每层选择的操作)
        """
        for op_name in arch:
            x = self.all_ops[op_name](x)
        return x
    
    def train_step(self, dataloader, num_samples=10):
        """训练步骤: 随机采样多个架构"""
        total_loss = 0
        
        for _ in range(num_samples):
            # 随机采样架构
            arch = self.random_architecture()
            
            # 训练
            for batch in dataloader:
                x, y = batch
                output = self.forward(x, arch)
                loss = F.cross_entropy(output, y)
                loss.backward()
                total_loss += loss.item()
        
        return total_loss / num_samples

"""
权重共享的优势:

  1. 高效:
     不需要为每个架构单独训练
     大幅降低计算成本
     
  2. 一次性:
     训练一次超网络
     可以评估所有子网络
     
  劣势:
    1. 评估不准:
       共享权重可能不适合某些架构
       
    2. 搜索空间受限:
       需要设计合适的超网络
"""

6.2 一次性 NAS (One-shot NAS)

复制代码
一次性 NAS:

  核心思想:
    训练一个超网络
    一次性评估所有子网络
    
  流程:
    1. 训练超网络 (包含所有架构)
    2. 评估所有子网络
    3. 选择最优子网络
python 复制代码
class OneShotNAS:
    """
    一次性 NAS
    
    理论:
      训练一个超网络
      一次性评估所有子网络
      选择最优
    """
    def __init__(self, supernet):
        self.supernet = supernet
    
    def search(self, val_loader):
        """搜索最优架构"""
        # 训练超网络
        self.train_supernet(val_loader)
        
        # 评估所有子网络
        best_arch = None
        best_acc = 0
        
        for arch in self.enumerate_architectures():
            acc = self.evaluate_architecture(arch, val_loader)
            if acc > best_acc:
                best_acc = acc
                best_arch = arch
        
        return best_arch

"""
一次性 NAS 的优势:

  1. 高效:
     只训练一次
     
  2. 简单:
     实现简单
     
  3. 灵活:
     可以评估任意架构
"""

7. 硬件感知 NAS

7.1 硬件感知概述

复制代码
硬件感知 NAS (Hardware-Aware NAS):

  目标:
    不仅优化准确率
    还优化硬件效率 (延迟、能耗、内存)

  多目标优化:
    max Accuracy(arch)
    s.t. Latency(arch) ≤ T
         Memory(arch) ≤ M
         Energy(arch) ≤ E
python 复制代码
class HardwareAwareNAS:
    """
    硬件感知 NAS
    
    理论:
      同时优化准确率和硬件效率
      使用多目标优化
    """
    def __init__(self, accuracy_predictor, latency_predictor):
        self.accuracy_predictor = accuracy_predictor
        self.latency_predictor = latency_predictor
    
    def search(self, target_latency):
        """
        搜索满足延迟约束的最优架构
        """
        best_arch = None
        best_acc = 0
        
        for arch in self.enumerate_architectures():
            # 预测准确率
            acc = self.accuracy_predictor(arch)
            
            # 预测延迟
            latency = self.latency_predictor(arch)
            
            # 检查约束
            if latency <= target_latency and acc > best_acc:
                best_acc = acc
                best_arch = arch
        
        return best_arch

"""
硬件感知的指标:

  1. 延迟 (Latency):
     推理时间
     影响实时性
     
  2. 内存 (Memory):
     模型大小和激活内存
     影响部署
     
  3. 能耗 (Energy):
     功耗
     影响移动设备
     
  4. FLOPs:
     浮点运算次数
     理论计算量
"""

7.2 ProxylessNAS

复制代码
论文: "ProxylessNAS: Direct Neural Architecture Search on Target Task and Hardware" 
      (Cai et al., 2019)

核心创新:
  直接在目标硬件上搜索
  不需要代理任务

  理论:
    传统 NAS: 在小数据集上搜索 → 迁移
    ProxylessNAS: 直接在目标数据集和硬件上搜索

8. NAS 在不同领域的应用

8.1 图像分类

复制代码
NAS 在图像分类中的应用:

  1. NASNet: 搜索最优的 Cell 结构
  2. EfficientNet: 复合缩放
  3. Once-for-All: 一次性搜索多个模型

8.2 目标检测

复制代码
NAS 在目标检测中的应用:

  1. NAS-FPN: 搜索最优的特征金字塔
  2. DetNAS: 搜索检测器的骨干网络
  3. SpineNet: 搜索多尺度特征融合

8.3 语义分割

复制代码
NAS 在语义分割中的应用:

  1. Auto-DeepLab: 搜索分割网络架构
  2. DPC: 搜索密集预测的 Cell

8.4 NLP

复制代码
NAS 在 NLP 中的应用:

  1. Evolved Transformer: 搜索 Transformer 架构
  2. AutoBERT: 搜索 BERT 架构
  3. NAS-BERT: 高效的 BERT 搜索

9. 评估指标与基准

9.1 评估指标

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    NAS 评估指标                                      │
├─────────────────┬───────────────────────────────────────────────────┤
│  指标            │  定义与说明                                       │
├─────────────────┼───────────────────────────────────────────────────┤
│  准确率          │  在测试集上的分类准确率                           │
├─────────────────┼───────────────────────────────────────────────────┤
│  搜索成本        │  搜索所需的 GPU 小时数                            │
├─────────────────┼───────────────────────────────────────────────────┤
│  FLOPs          │  模型的浮点运算次数                                │
├─────────────────┼───────────────────────────────────────────────────┤
│  参数量          │  模型的参数数量                                    │
├─────────────────┼───────────────────────────────────────────────────┤
│  延迟            │  推理时间                                          │
├─────────────────┼───────────────────────────────────────────────────┤
│  可迁移性        │  在不同数据集上的表现                              │
└─────────────────┴───────────────────────────────────────────────────┘

9.2 基准数据集

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    NAS 基准数据集                                    │
├─────────────────┬───────────────────────────────────────────────────┤
│  数据集          │  说明                                            │
├─────────────────┼───────────────────────────────────────────────────┤
│  NAS-Bench-101  │  搜索空间: 423K 架构                             │
│  NAS-Bench-201  │  搜索空间: 15K 架构                              │
│  CIFAR-10       │  常用的代理数据集                                 │
│  ImageNet       │  最终评估数据集                                   │
└─────────────────┴───────────────────────────────────────────────────┘

10. 前沿与展望

10.1 前沿方向

复制代码
NAS 的前沿:

  1. 大模型 NAS:
     搜索 LLM 架构
     高效的搜索方法
     
  2. 多目标 NAS:
     同时优化多个目标
     准确率、延迟、能耗
     
  3. 可解释 NAS:
     理解为什么某些架构好
     架构设计规律
     
  4. 终身 NAS:
     持续搜索新架构
     适应新任务和硬件
     
  5. NAS 理论:
     搜索空间的理论分析
     搜索算法的收敛性

10.2 NAS 的挑战

复制代码
NAS 的挑战:

  1. 计算成本:
     搜索成本仍然很高
     需要更高效的算法
     
  2. 泛化性:
     搜索到的架构可能过拟合
     需要更好的评估方法
     
  3. 搜索空间设计:
     搜索空间的设计需要先验知识
     如何设计更好的搜索空间
     
  4. 可重复性:
     NAS 实验难以重复
     需要标准化的基准

附录

A. 发展时间线

复制代码
2017  ──┬──  NASNet (强化学习)
        │
2018  ──┼──  ENAS (权重共享)
        │
2019  ──┼──  DARTS (可微分搜索)
        │
2020  ──┼──  EfficientNet (复合缩放)
        │
2021  ──┼──  Once-for-All (一次性 NAS)
        │
2022+ ──┴──  大模型 NAS

B. 核心公式速查

公式 含义
a* = argmax f(a) NAS 目标
∇θ J = ER · ∇log π REINFORCE 梯度
out = Σ αᵢ · oᵢ(x) DARTS 松弛
min_α L_val(w*(α), α) 双层优化

C. 推荐资源

  • Zoph, B., et al. (2018). NASNet
  • Liu, H., et al. (2019). DARTS
  • Tan, M., & Le, Q. (2019). EfficientNet
  • Cai, H., et al. (2019). ProxylessNAS

文档结束

神经架构搜索深度解析(Neural Architecture Search, NAS)

版本:v1.0 | 最后更新:2026-06-02


目录

  1. [NAS 基础理论](#NAS 基础理论)
  2. 搜索空间设计
  3. 基于强化学习的搜索
  4. 基于进化的搜索
  5. DARTS:可微分架构搜索
  6. [高效 NAS 方法](#高效 NAS 方法)
  7. [硬件感知 NAS](#硬件感知 NAS)
  8. [NAS 在不同领域的应用](#NAS 在不同领域的应用)
  9. 评估指标与基准
  10. 前沿与展望

1. NAS 基础理论

1.1 什么是神经架构搜索

复制代码
神经架构搜索 (Neural Architecture Search, NAS) 的定义:

  自动设计最优的神经网络架构

  ┌─────────────────────────────────────────────────────────────────┐
  │                                                                 │
  │   传统方法: 人工设计架构                                        │
  │   ┌───────────────────────────────────────────────────────┐    │
  │   │  专家经验 → 设计架构 → 训练 → 评估 → 调整 → ...       │    │
  │   └───────────────────────────────────────────────────────┘    │
  │                                                                 │
  │   NAS: 自动搜索架构                                             │
  │   ┌───────────────────────────────────────────────────────┐    │
  │   │  搜索空间 → 搜索策略 → 架构评估 → 最优架构             │    │
  │   └───────────────────────────────────────────────────────┘    │
  │                                                                 │
  └─────────────────────────────────────────────────────────────────┘

  NAS 的三个核心组件:
    1. 搜索空间 (Search Space): 定义可能的架构
    2. 搜索策略 (Search Strategy): 如何探索搜索空间
    3. 评估策略 (Evaluation Strategy): 如何评估架构性能

1.2 NAS 的理论框架

复制代码
NAS 的数学形式化:

  给定:
    搜索空间 A
    评估函数 f(a) (架构 a 的性能)
    计算预算 B
    
  目标:
    a* = argmax_{a∈A} f(a)
    s.t. 计算成本 ≤ B
    
  挑战:
    1. 搜索空间巨大: |A| 可能达到 10^18
    2. 评估昂贵: 每个架构需要训练
    3. 离散优化: 架构是离散变量

1.3 NAS 的发展历史

复制代码
NAS 发展时间线:

  2017  ──┬──  NASNet (强化学习)
          │    首次大规模 NAS
          │
  2018  ──┼──  ENAS (权重共享)
          │    大幅降低计算成本
          │
  2019  ──┼──  DARTS (可微分搜索)
          │    连续松弛,梯度优化
          │
  2020  ──┼──  EfficientNet (复合缩放)
          │    系统化的模型缩放
          │
  2021  ──┼──  AutoFormer (ViT NAS)
          │    Transformer 架构搜索
          │
  2022+ ──┴──  大模型 NAS, 一次性 NAS

2. 搜索空间设计

2.1 搜索空间概述

复制代码
搜索空间 (Search Space):

  定义: 所有可能的架构的集合

  设计原则:
    1. 表达能力: 能表示好的架构
    2. 大小适中: 不能太大 (无法搜索) 也不能太小 (错过好架构)
    3. 先验知识: 包含已知的有效设计

  搜索空间类型:
    1. 宏观搜索空间: 整个网络架构
    2. 微观搜索空间: 重复的单元 (Cell)

2.2 Cell-based 搜索空间

复制代码
Cell-based 搜索空间:

  核心思想:
    搜索一个基本单元 (Cell)
    然后堆叠多个 Cell 形成完整网络

  Cell 结构:
    - N 个节点 (Node)
    - 每个节点有多个输入
    - 每个输入有选择的操作

  ┌─────────────────────────────────────────────────────────────────┐
  │                                                                 │
  │   Cell 结构:                                                    │
  │                                                                 │
  │   输入 ─┬─► [操作1] ─┬─► [操作2] ─┬─► 输出                    │
  │         │            │            │                             │
  │         └─► [操作3] ─┘            │                             │
  │                                   │                             │
  │         ┌─► [操作4] ─────────────┘                             │
  │         │                                                       │
  │         └─► [操作5] ───────────────► 输出                       │
  │                                                                 │
  └─────────────────────────────────────────────────────────────────┘
python 复制代码
import torch
import torch.nn as nn

class Cell(nn.Module):
    """
    Cell-based 搜索空间中的基本单元
    
    理论:
      搜索一个基本 Cell 结构
      堆叠多个 Cell 形成完整网络
      
      优势:
        - 搜索空间小
        - 架构可复用
        - 便于迁移
    """
    def __init__(self, num_nodes=4, num_ops=8):
        super().__init__()
        
        self.num_nodes = num_nodes
        
        # 可选操作
        self.ops = nn.ModuleDict({
            'none': Zero(),
            'skip_connect': Identity(),
            'sep_conv_3x1': SepConv(3, 1),
            'sep_conv_5x1': SepConv(5, 1),
            'sep_conv_3x2': SepConv(3, 2),
            'sep_conv_5x2': SepConv(5, 2),
            'dil_conv_3x2': DilConv(3, 2),
            'avg_pool_3x1': Pool('avg', 3),
            'max_pool_3x1': Pool('max', 3)
        })
        
        # 节点输入选择
        self.node_inputs = nn.ModuleDict()
        for i in range(2, num_nodes + 2):
            self.node_inputs[f'node_{i}'] = nn.ModuleDict({
                f'input_{j}': nn.ModuleDict({
                    op_name: op for op_name, op in self.ops.items()
                })
                for j in range(i)
            })
    
    def forward(self, s0, s1, weights):
        """
        s0, s1: 前两个 Cell 的输出
        weights: 架构参数 (每个操作的权重)
        """
        states = [s0, s1]
        
        for i in range(2, self.num_nodes + 2):
            # 对每个输入加权求和
            node_input = 0
            for j in range(i):
                for op_name, op in self.ops.items():
                    weight = weights[f'node_{i}'][f'input_{j}'][op_name]
                    node_input = node_input + weight * op(states[j])
            
            states.append(node_input)
        
        # 拼接所有中间节点
        output = torch.cat(states[2:], dim=1)
        
        return output

class SepConv(nn.Module):
    """深度可分离卷积"""
    def __init__(self, kernel_size, stride):
        super().__init__()
        self.op = nn.Sequential(
            nn.ReLU(),
            nn.Conv2d(32, 32, kernel_size, stride=stride, padding=kernel_size//2, groups=32),
            nn.Conv2d(32, 32, 1),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.Conv2d(32, 32, kernel_size, stride=1, padding=kernel_size//2, groups=32),
            nn.Conv2d(32, 32, 1),
            nn.BatchNorm2d(32)
        )
    
    def forward(self, x):
        return self.op(x)

"""
Cell-based 搜索空间的优势:

  1. 搜索空间小:
     只搜索一个 Cell
     而非整个网络
     
  2. 可迁移:
     Cell 可以在不同数据集上复用
     可以堆叠不同数量的 Cell
     
  3. 先验知识:
     Cell 结构基于已有的设计经验

2.3 宏观搜索空间

复制代码
宏观搜索空间:

  搜索整个网络架构
  包括每一层的操作和连接

  优势:
    - 更大的搜索空间
    - 可能发现更好的架构
    
  劣势:
    - 搜索空间巨大
    - 计算成本高
    - 难以迁移
python 复制代码
class MacroSearchSpace(nn.Module):
    """
    宏观搜索空间
    
    搜索整个网络架构
    """
    def __init__(self, num_layers=20, num_ops=8):
        super().__init__()
        
        self.num_layers = num_layers
        
        # 每层可选的操作
        self.ops = nn.ModuleList([
            nn.ModuleDict({
                'conv_3x3': nn.Conv2d(32, 32, 3, padding=1),
                'conv_5x5': nn.Conv2d(32, 32, 5, padding=2),
                'sep_conv_3x3': SepConv(3, 1),
                'sep_conv_5x5': SepConv(5, 1),
                'dil_conv_3x3': DilConv(3, 1),
                'max_pool_3x3': nn.MaxPool2d(3, padding=1),
                'avg_pool_3x3': nn.AvgPool2d(3, padding=1),
                'skip': nn.Identity()
            })
            for _ in range(num_layers)
        ])
    
    def forward(self, x, arch_params):
        """
        x: [B, C, H, W]
        arch_params: 每层的操作选择
        """
        for i in range(self.num_layers):
            op_name = arch_params[i]
            x = self.ops[i][op_name](x)
        
        return x

"""
宏观 vs 微观搜索空间:

  宏观:
    搜索整个网络
    搜索空间大
    难以迁移
    
  微观 (Cell-based):
    搜索基本单元
    搜索空间小
    便于迁移
    
  实践中 Cell-based 更常用
"""

3. 基于强化学习的搜索

3.1 NASNet

复制代码
论文: "Learning Transferable Architectures for Scalable Image Recognition" 
      (Zoph et al., 2018)

核心思想:
  使用强化学习搜索最优的 Cell 结构

  方法:
    1. RNN 控制器生成架构描述
    2. 训练子模型评估性能
    3. 用性能作为奖励更新控制器
python 复制代码
class NASNetController(nn.Module):
    """
    NASNet 控制器
    
    使用 RNN 生成架构描述
    
    理论:
      RNN 逐步生成架构的每个选择
      使用 REINFORCE 算法训练
    """
    def __init__(self, num_ops=8, num_nodes=4):
        super().__init__()
        
        self.num_ops = num_ops
        self.num_nodes = num_nodes
        
        # RNN 控制器
        self.rnn = nn.LSTMCell(32, 32)
        
        # 预测头
        self.op_logits = nn.Linear(32, num_ops)
        self.input_logits = nn.Linear(32, num_nodes)
        
        # 嵌入
        self.op_embedding = nn.Embedding(num_ops, 32)
        self.input_embedding = nn.Embedding(num_nodes, 32)
    
    def forward(self, batch_size=1):
        """
        生成架构描述
        """
        # 初始化
        h = torch.zeros(batch_size, 32)
        c = torch.zeros(batch_size, 32)
        
        ops = []
        inputs = []
        
        for i in range(self.num_nodes):
            # 预测操作
            op_logit = self.op_logits(h)
            op = torch.multinomial(torch.softmax(op_logit, dim=-1), 1)
            ops.append(op)
            
            # 预测输入
            input_logit = self.input_logits(h)
            input_idx = torch.multinomial(torch.softmax(input_logit, dim=-1), 1)
            inputs.append(input_idx)
            
            # 更新 RNN
            op_emb = self.op_embedding(op)
            input_emb = self.input_embedding(input_idx)
            h, c = self.rnn(op_emb + input_emb, (h, c))
        
        return ops, inputs

"""
NASNet 的训练:

  REINFORCE 算法:
    1. 控制器采样架构
    2. 训练子模型
    3. 用验证准确率作为奖励
    4. 更新控制器
    
  奖励: R = 准确率
  
  梯度: ∇θ J(θ) = E[R · ∇θ log p(arch; θ)]
"""

3.2 REINFORCE 算法

python 复制代码
class REINFORCE:
    """
    REINFORCE 算法用于 NAS
    
    理论:
      策略梯度方法
      用性能作为奖励更新控制器
    """
    def __init__(self, controller, lr=0.001):
        self.controller = controller
        self.optimizer = torch.optim.Adam(controller.parameters(), lr=lr)
        
        # 基线 (减少方差)
        self.baseline = 0
    
    def train_step(self, architectures, rewards):
        """
        architectures: 采样的架构
        rewards: 对应的性能
        """
        # 计算策略梯度
        log_probs = []
        for arch in architectures:
            log_prob = self.controller.log_prob(arch)
            log_probs.append(log_prob)
        
        log_probs = torch.stack(log_probs)
        
        # 减去基线
        advantages = rewards - self.baseline
        
        # 策略梯度损失
        loss = -(log_probs * advantages).mean()
        
        # 更新
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()
        
        # 更新基线
        self.baseline = 0.9 * self.baseline + 0.1 * rewards.mean().item()
        
        return loss.item()

"""
REINFORCE 的理论:

  策略梯度定理:
    ∇θ J(θ) = E[∇θ log π(a|s) · R(a)]
    
  在 NAS 中:
    策略: π(arch; θ) = 控制器生成架构的概率
    奖励: R(arch) = 架构的验证准确率
    
  基线:
    b = E[R] (移动平均)
    减少方差: ∇θ J(θ) = E[∇θ log π(a|s) · (R(a) - b)]
"""

4. 基于进化的搜索

4.1 进化算法概述

复制代码
进化算法 (Evolutionary Algorithm) 用于 NAS:

  核心思想:
    模拟自然选择
    保留好的架构,淘汰差的架构
    
  流程:
    1. 初始化种群
    2. 评估适应度 (性能)
    3. 选择 (保留好的)
    4. 变异 (生成新架构)
    5. 重复步骤 2-4
python 复制代码
class EvolutionaryNAS:
    """
    基于进化的 NAS
    
    理论:
      模拟自然选择
      保留好的架构,淘汰差的架构
    """
    def __init__(self, population_size=50, mutation_rate=0.1):
        self.population_size = population_size
        self.mutation_rate = mutation_rate
        self.population = []
    
    def initialize(self, search_space):
        """初始化种群"""
        for _ in range(self.population_size):
            arch = search_space.random_architecture()
            self.population.append(arch)
    
    def evolve(self, search_space, num_generations=100):
        """进化搜索"""
        for gen in range(num_generations):
            # 评估适应度
            fitness = []
            for arch in self.population:
                acc = self.evaluate(arch)
                fitness.append(acc)
            
            # 选择
            selected = self.select(fitness)
            
            # 变异
            new_population = []
            for arch in selected:
                new_arch = self.mutate(arch, search_space)
                new_population.append(new_arch)
            
            self.population = new_population
        
        # 返回最优架构
        best_idx = np.argmax(fitness)
        return self.population[best_idx]
    
    def select(self, fitness):
        """
        选择策略
        
        理论:
          保留适应度高的个体
          轮盘赌选择或锦标赛选择
        """
        # 锦标赛选择
        selected = []
        for _ in range(self.population_size):
            # 随机选择 k 个个体
            k = 3
            candidates = np.random.choice(len(self.population), k, replace=False)
            
            # 选择适应度最高的
            best = candidates[np.argmax([fitness[i] for i in candidates])]
            selected.append(self.population[best])
        
        return selected
    
    def mutate(self, arch, search_space):
        """
        变异操作
        
        理论:
          随机修改架构的部分选择
          探索搜索空间
        """
        new_arch = arch.copy()
        
        # 随机选择要变异的位置
        num_mutations = int(len(arch) * self.mutation_rate)
        mutation_positions = np.random.choice(len(arch), num_mutations, replace=False)
        
        for pos in mutation_positions:
            # 随机选择新操作
            new_arch[pos] = search_space.random_operation()
        
        return new_arch

"""
进化算法的优势:

  1. 简单: 易于实现
  2. 灵活: 可以处理各种约束
  3. 并行: 可以并行评估多个架构
  
  劣势:
    1. 计算量大
    2. 可能陷入局部最优
"""

4.2 AmoebaNet

复制代码
论文: "Regularized Evolution for Image Classifier Architecture Search" 
      (Real et al., 2019)

核心创新:
  正则化进化: 移除最老的个体 (而非最差的)

  理论:
    避免过拟合搜索历史
    保持种群多样性

5. DARTS:可微分架构搜索

5.1 DARTS 核心思想

复制代码
论文: "DARTS: Differentiable Architecture Search" (Liu et al., 2019)

核心创新:
  将离散的架构选择松弛为连续优化
  使用梯度下降搜索架构

  理论:
    原始问题: 离散选择 (argmax)
    松弛后: 连续权重 (softmax)
    
    可以用梯度下降优化
python 复制代码
class DARTS(nn.Module):
    """
    DARTS (Differentiable Architecture Search)
    
    核心创新:
      将离散选择松弛为连续优化
      使用梯度下降搜索架构
      
    理论:
      每个操作有一个权重 α
      输出 = Σ α_i · o_i(x)
      
      训练结束后,选择权重最大的操作
    """
    def __init__(self, num_nodes=4, num_ops=8):
        super().__init__()
        
        self.num_nodes = num_nodes
        
        # 操作
        self.ops = nn.ModuleList([
            nn.ModuleList([
                nn.ModuleDict({
                    'sep_conv_3x3': SepConv(3, 1),
                    'sep_conv_5x5': SepConv(5, 1),
                    'dil_conv_3x3': DilConv(3, 1),
                    'avg_pool_3x3': nn.AvgPool2d(3, padding=1),
                    'max_pool_3x3': nn.MaxPool2d(3, padding=1),
                    'skip_connect': nn.Identity(),
                    'none': Zero()
                })
                for _ in range(i)  # 每个节点有 i 个输入
            ])
            for i in range(2, num_nodes + 2)
        ])
        
        # 架构参数 (可学习)
        self.arch_params = nn.ParameterList()
        for i in range(2, num_nodes + 2):
            # 每个节点的每个输入的操作权重
            param = nn.Parameter(torch.randn(i, num_ops) * 0.001)
            self.arch_params.append(param)
    
    def forward(self, s0, s1):
        """
        s0, s1: 前两个 Cell 的输出
        """
        states = [s0, s1]
        
        for i in range(self.num_nodes):
            # 获取当前节点的架构权重
            weights = torch.softmax(self.arch_params[i], dim=-1)
            
            # 对每个输入加权求和
            node_input = 0
            for j in range(i + 2):
                for k, (op_name, op) in enumerate(self.ops[i][j].items()):
                    if op_name != 'none':
                        node_input = node_input + weights[j, k] * op(states[j])
            
            states.append(node_input)
        
        # 拼接所有中间节点
        output = torch.cat(states[2:], dim=1)
        
        return output
    
    def get_architecture(self):
        """获取最终架构"""
        arch = []
        for i in range(self.num_nodes):
            weights = torch.softmax(self.arch_params[i], dim=-1)
            ops = weights.argmax(dim=-1)
            arch.append(ops)
        
        return arch

"""
DARTS 的训练:

  双层优化:
    外层: 优化架构参数 α
    内层: 优化网络权重 w
    
    min_α L_val(w*(α), α)
    s.t. w*(α) = argmin_w L_train(w, α)
    
  实际实现:
    交替优化:
      1. 固定 α,更新 w (训练集)
      2. 固定 w,更新 α (验证集)
"""

5.2 DARTS 的问题与改进

复制代码
DARTS 的问题:

  1. 搜索不稳定:
     架构参数可能震荡
     搜索结果不一致
     
  2. 性能差距:
     搜索时的性能 ≠ 评估时的性能
     权重共享导致评估不准
     
  3. 搜索空间偏好:
     倾向于选择 skip_connect
     因为 skip_connect 训练更快

改进方法:
  1. P-DARTS: 渐进式搜索
  2. PC-DARTS: 部分通道连接
  3. Fair DARTS: 公平竞争
  4. DARTS-: 修正稳定性
python 复制代码
class FairDARTS(nn.Module):
    """
    Fair DARTS
    
    改进:
      使用 Sigmoid 替代 Softmax
      每个操作独立竞争
      
    理论:
      Softmax 导致操作之间竞争
      Sigmoid 允许多个操作共存
    """
    def __init__(self, num_nodes=4, num_ops=8):
        super().__init__()
        
        # 架构参数
        self.arch_params = nn.ParameterList([
            nn.Parameter(torch.randn(i, num_ops))
            for i in range(2, num_nodes + 2)
        ])
    
    def forward(self, s0, s1):
        states = [s0, s1]
        
        for i in range(self.num_nodes):
            # 使用 Sigmoid (每个操作独立)
            weights = torch.sigmoid(self.arch_params[i])
            
            # 加权求和
            node_input = 0
            for j in range(i + 2):
                for k, op in enumerate(self.ops[i][j]):
                    node_input = node_input + weights[j, k] * op(states[j])
            
            states.append(node_input)
        
        return torch.cat(states[2:], dim=1)

"""
FairDARTS vs DARTS:

  DARTS (Softmax):
    操作之间竞争
    容易导致 collapse
    
  FairDARTS (Sigmoid):
    每个操作独立
    更公平的竞争
"""

6. 高效 NAS 方法

6.1 权重共享

复制代码
权重共享 (Weight Sharing):

  核心思想:
    所有架构共享同一组权重
    避免为每个架构单独训练
    
  理论:
    超网络 (Supernet): 包含所有可能的架构
    子网络: 从超网络中采样
    
    训练超网络
    评估子网络
python 复制代码
class Supernet(nn.Module):
    """
    超网络 (Supernet)
    
    理论:
      包含所有可能的架构
      训练时随机采样子网络
      评估时选择最优子网络
    """
    def __init__(self, num_ops=8):
        super().__init__()
        
        # 所有可能的操作
        self.all_ops = nn.ModuleDict({
            'sep_conv_3x3': SepConv(3, 1),
            'sep_conv_5x5': SepConv(5, 1),
            'dil_conv_3x3': DilConv(3, 1),
            'avg_pool_3x3': nn.AvgPool2d(3, padding=1),
            'max_pool_3x3': nn.MaxPool2d(3, padding=1),
            'skip_connect': nn.Identity()
        })
    
    def forward(self, x, arch):
        """
        arch: 架构描述 (每层选择的操作)
        """
        for op_name in arch:
            x = self.all_ops[op_name](x)
        return x
    
    def train_step(self, dataloader, num_samples=10):
        """训练步骤: 随机采样多个架构"""
        total_loss = 0
        
        for _ in range(num_samples):
            # 随机采样架构
            arch = self.random_architecture()
            
            # 训练
            for batch in dataloader:
                x, y = batch
                output = self.forward(x, arch)
                loss = F.cross_entropy(output, y)
                loss.backward()
                total_loss += loss.item()
        
        return total_loss / num_samples

"""
权重共享的优势:

  1. 高效:
     不需要为每个架构单独训练
     大幅降低计算成本
     
  2. 一次性:
     训练一次超网络
     可以评估所有子网络
     
  劣势:
    1. 评估不准:
       共享权重可能不适合某些架构
       
    2. 搜索空间受限:
       需要设计合适的超网络
"""

6.2 一次性 NAS (One-shot NAS)

复制代码
一次性 NAS:

  核心思想:
    训练一个超网络
    一次性评估所有子网络
    
  流程:
    1. 训练超网络 (包含所有架构)
    2. 评估所有子网络
    3. 选择最优子网络
python 复制代码
class OneShotNAS:
    """
    一次性 NAS
    
    理论:
      训练一个超网络
      一次性评估所有子网络
      选择最优
    """
    def __init__(self, supernet):
        self.supernet = supernet
    
    def search(self, val_loader):
        """搜索最优架构"""
        # 训练超网络
        self.train_supernet(val_loader)
        
        # 评估所有子网络
        best_arch = None
        best_acc = 0
        
        for arch in self.enumerate_architectures():
            acc = self.evaluate_architecture(arch, val_loader)
            if acc > best_acc:
                best_acc = acc
                best_arch = arch
        
        return best_arch

"""
一次性 NAS 的优势:

  1. 高效:
     只训练一次
     
  2. 简单:
     实现简单
     
  3. 灵活:
     可以评估任意架构
"""

7. 硬件感知 NAS

7.1 硬件感知概述

复制代码
硬件感知 NAS (Hardware-Aware NAS):

  目标:
    不仅优化准确率
    还优化硬件效率 (延迟、能耗、内存)

  多目标优化:
    max Accuracy(arch)
    s.t. Latency(arch) ≤ T
         Memory(arch) ≤ M
         Energy(arch) ≤ E
python 复制代码
class HardwareAwareNAS:
    """
    硬件感知 NAS
    
    理论:
      同时优化准确率和硬件效率
      使用多目标优化
    """
    def __init__(self, accuracy_predictor, latency_predictor):
        self.accuracy_predictor = accuracy_predictor
        self.latency_predictor = latency_predictor
    
    def search(self, target_latency):
        """
        搜索满足延迟约束的最优架构
        """
        best_arch = None
        best_acc = 0
        
        for arch in self.enumerate_architectures():
            # 预测准确率
            acc = self.accuracy_predictor(arch)
            
            # 预测延迟
            latency = self.latency_predictor(arch)
            
            # 检查约束
            if latency <= target_latency and acc > best_acc:
                best_acc = acc
                best_arch = arch
        
        return best_arch

"""
硬件感知的指标:

  1. 延迟 (Latency):
     推理时间
     影响实时性
     
  2. 内存 (Memory):
     模型大小和激活内存
     影响部署
     
  3. 能耗 (Energy):
     功耗
     影响移动设备
     
  4. FLOPs:
     浮点运算次数
     理论计算量
"""

7.2 ProxylessNAS

复制代码
论文: "ProxylessNAS: Direct Neural Architecture Search on Target Task and Hardware" 
      (Cai et al., 2019)

核心创新:
  直接在目标硬件上搜索
  不需要代理任务

  理论:
    传统 NAS: 在小数据集上搜索 → 迁移
    ProxylessNAS: 直接在目标数据集和硬件上搜索

8. NAS 在不同领域的应用

8.1 图像分类

复制代码
NAS 在图像分类中的应用:

  1. NASNet: 搜索最优的 Cell 结构
  2. EfficientNet: 复合缩放
  3. Once-for-All: 一次性搜索多个模型

8.2 目标检测

复制代码
NAS 在目标检测中的应用:

  1. NAS-FPN: 搜索最优的特征金字塔
  2. DetNAS: 搜索检测器的骨干网络
  3. SpineNet: 搜索多尺度特征融合

8.3 语义分割

复制代码
NAS 在语义分割中的应用:

  1. Auto-DeepLab: 搜索分割网络架构
  2. DPC: 搜索密集预测的 Cell

8.4 NLP

复制代码
NAS 在 NLP 中的应用:

  1. Evolved Transformer: 搜索 Transformer 架构
  2. AutoBERT: 搜索 BERT 架构
  3. NAS-BERT: 高效的 BERT 搜索

9. 评估指标与基准

9.1 评估指标

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    NAS 评估指标                                      │
├─────────────────┬───────────────────────────────────────────────────┤
│  指标            │  定义与说明                                       │
├─────────────────┼───────────────────────────────────────────────────┤
│  准确率          │  在测试集上的分类准确率                           │
├─────────────────┼───────────────────────────────────────────────────┤
│  搜索成本        │  搜索所需的 GPU 小时数                            │
├─────────────────┼───────────────────────────────────────────────────┤
│  FLOPs          │  模型的浮点运算次数                                │
├─────────────────┼───────────────────────────────────────────────────┤
│  参数量          │  模型的参数数量                                    │
├─────────────────┼───────────────────────────────────────────────────┤
│  延迟            │  推理时间                                          │
├─────────────────┼───────────────────────────────────────────────────┤
│  可迁移性        │  在不同数据集上的表现                              │
└─────────────────┴───────────────────────────────────────────────────┘

9.2 基准数据集

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    NAS 基准数据集                                    │
├─────────────────┬───────────────────────────────────────────────────┤
│  数据集          │  说明                                            │
├─────────────────┼───────────────────────────────────────────────────┤
│  NAS-Bench-101  │  搜索空间: 423K 架构                             │
│  NAS-Bench-201  │  搜索空间: 15K 架构                              │
│  CIFAR-10       │  常用的代理数据集                                 │
│  ImageNet       │  最终评估数据集                                   │
└─────────────────┴───────────────────────────────────────────────────┘

10. 前沿与展望

10.1 前沿方向

复制代码
NAS 的前沿:

  1. 大模型 NAS:
     搜索 LLM 架构
     高效的搜索方法
     
  2. 多目标 NAS:
     同时优化多个目标
     准确率、延迟、能耗
     
  3. 可解释 NAS:
     理解为什么某些架构好
     架构设计规律
     
  4. 终身 NAS:
     持续搜索新架构
     适应新任务和硬件
     
  5. NAS 理论:
     搜索空间的理论分析
     搜索算法的收敛性

10.2 NAS 的挑战

复制代码
NAS 的挑战:

  1. 计算成本:
     搜索成本仍然很高
     需要更高效的算法
     
  2. 泛化性:
     搜索到的架构可能过拟合
     需要更好的评估方法
     
  3. 搜索空间设计:
     搜索空间的设计需要先验知识
     如何设计更好的搜索空间
     
  4. 可重复性:
     NAS 实验难以重复
     需要标准化的基准

附录

A. 发展时间线

复制代码
2017  ──┬──  NASNet (强化学习)
        │
2018  ──┼──  ENAS (权重共享)
        │
2019  ──┼──  DARTS (可微分搜索)
        │
2020  ──┼──  EfficientNet (复合缩放)
        │
2021  ──┼──  Once-for-All (一次性 NAS)
        │
2022+ ──┴──  大模型 NAS

B. 核心公式速查

公式 含义
a* = argmax f(a) NAS 目标
∇θ J = ER · ∇log π REINFORCE 梯度
out = Σ αᵢ · oᵢ(x) DARTS 松弛
min_α L_val(w*(α), α) 双层优化
相关推荐
AI刀刀1 小时前
Kimi 保存 pdf 显示该页的尺寸超出范围令人困扰,AI 导出鸭一键修复参数,导出 PDF 更顺畅
人工智能·pdf·ai导出鸭
冬奇Lab2 小时前
Agent 系列(13):Agent 安全与防护——提示词注入、工具滥用、数据泄露怎么防
人工智能·llm·agent
冬奇Lab2 小时前
每日一个开源项目(第122篇):headroom - 给 AI Agent 装上上下文压缩层,Token 最高省 95%
人工智能·开源·资讯
科技与数码2 小时前
鸿蒙6.1小艺伴随式AI体验:让阅读效率翻倍
人工智能·华为·harmonyos
实在智能RPA2 小时前
药企GMP合规自动化破局:实在Agent的功能完整度评估与落地实践
运维·人工智能·ai·自动化
市象2 小时前
当 Google I/O 出现在抖音,前沿科技有了新现场
人工智能·科技
程序猿追3 小时前
棋盘上的博弈:我在 HarmonyOS 里塞了一个五子棋“大脑”
人工智能·华为·harmonyos
是烨笙啊3 小时前
在 Claude code 中如何利用模型缓存节省 token
人工智能·缓存·ai编程