DARTS算法改进:全局比较与SENet集成提升神经网络架构搜索性能
1. 项目概述与核心思路拆解在计算机视觉领域构建一个高性能的卷积神经网络CNN模型往往需要研究者具备深厚的领域知识和大量的试错经验。从ResNet的残差连接到Inception的多尺度特征融合每一次架构上的突破都伴随着巨大的人工设计成本。神经网络架构搜索Neural Architecture Search, NAS技术的出现旨在将我们从这种“手工炼丹”的困境中解放出来通过算法自动地在庞大的搜索空间中寻找最优的网络结构。在众多NAS方法中基于梯度的可微分架构搜索DARTS因其将离散的架构搜索问题松弛为连续优化问题从而能利用高效的梯度下降进行搜索显著降低了计算开销成为了该领域的一个里程碑。然而在实际使用和深入研究DARTS的过程中我发现它存在两个关键瓶颈限制了其潜力的完全发挥。首先是其候选操作选择机制的不公平性。DARTS在为一个中间节点选择输入边时采用的是“局部比较”策略它独立地比较连接该节点与每一个前驱节点之间的所有候选操作如3x3卷积、5x5深度可分离卷积、跳跃连接等然后为每一对节点只保留权重最高的那个操作。这听起来合理但仔细推敲就会发现一个问题一个节点最终只能保留两条输入边来自两个不同的前驱节点。如果来自节点A的最佳操作权重是0.35而来自节点B的次佳操作权重是0.34那么即使0.34大于来自节点C的最佳操作权重0.5节点B的这条边也会因为“局部竞争”失败而被丢弃最终可能选择了权重为0.35和0.5的两条边。这显然不是全局最优的选择更像是一种“小组赛”而非“总决赛”的选拔机制容易让一些整体表现更优的“选手”因为分组不利而提前出局。其次DARTS为了追求搜索效率在优化过程中引入了大量的近似策略。例如在双层优化中它通过一步梯度下降来近似内部优化网络权重ω的最优解。这些近似虽然加速了搜索但不可避免地引入了误差就像用快速估算代替精确计算长期累积会导致最终搜索出的架构在独立训练Evaluation时性能出现“崩塌”Performance Collapse表现远不及搜索阶段Search的预期。一个典型的表现就是搜索出的Cell中充斥着大量的“跳跃连接”Skip Connection操作。跳跃连接虽然能缓解梯度消失但过多使用会导致网络退化成一个浅层网络无法进行有效的特征变换和学习。针对这两个问题我设计并实现了一个改进方案。我的核心思路是双管齐下将“局部比较”升级为“全局比较”在为一个中间节点选择最终的两条输入边时不再分别在每一对节点内部竞争而是将所有可能连接到该节点的候选操作来自所有前驱节点放在同一个池子里进行排序直接选出权重最高的两个。这确保了选拔的绝对公平性理论上能搜索到更优、更多样化的网络架构例如允许一个节点的两条输入边都来自同一个前驱节点这是原版DARTS规则所禁止的。引入SENet注意力机制进行性能补偿在搜索得到的每个Cell无论是Normal Cell还是Reduction Cell的输出后固定插入一个Squeeze-and-ExcitationSENet模块。这个模块的作用是显式地建模通道间的相互依赖关系通过“挤压-激励”操作让网络自适应地校准每个特征通道的权重增强重要特征抑制次要特征。这相当于给搜索出的“骨架”网络增加了智能的“特征调节器”不仅能提升模型的表征能力其带来的性能增益也能有效对冲因DARTS近似优化而造成的精度损失增强了最终模型的鲁棒性。简而言之这个项目可以看作是对经典DARTS算法的一次“外科手术式”的精准改进。我们既修正了其选拔机制上的“程序不公”又为其“体质虚弱”近似策略导致的性能损失开出了一剂补药SENet。下面我将深入每个环节拆解实现细节、分享实操心得并附上完整的代码关键片段和避坑指南。2. 核心细节解析与实操要点2.1 全局比较算法从理论到代码实现原版DARTS的选拔逻辑在代码层面通常体现为在搜索结束后对每个中间节点j遍历其所有前驱节点i (ij)对每一对(i, j)选择alpha[i][j]架构参数中权重最大的操作。最终为节点j保留来自不同前驱节点的、权重最高的两条边。我们的全局比较算法则需要改变这个逻辑。核心在于我们需要为每个中间节点j维护一个全局的候选边列表。算法步骤拆解收集对于目标中间节点j遍历所有可能的前驱节点i (ij)。对于每一对(i, j)我们有7个候选操作例如none, max_pool_3x3, avg_pool_3x3, skip_connect, sep_conv_3x3, sep_conv_5x5, dil_conv_3x3, dil_conv_5x5。我们将每一条边表示为一个三元组(i, o, weight)其中o是操作索引weight是alpha[i][j][o]的值经过softmax归一化后的权重。排序将所有收集到的(i, o, weight)三元组放入一个列表按照weight进行降序排序。选择从排序后的列表中依次选取权重最高的两条边作为节点j的最终输入边。这里有一个关键点我们不再要求这两条边必须来自不同的前驱节点i。如果权重最高的两条边恰好都来自同一个前驱节点那么节点j就只接收来自该节点的两条不同操作的特征。这扩展了搜索空间是全局比较带来的直接优势。构建架构根据最终选出的边构建出离散的Cell架构。代码实现关键片段PyTorch风格def parse_global(alpha_normal, alpha_reduce): 根据全局比较算法从架构参数alpha中解析出最终的Cell结构。 alpha_normal: 对应于Normal Cell的架构参数形状为 [num_edges, num_ops] alpha_reduce: 对应于Reduction Cell的架构参数 def _parse(alpha): num_nodes 4 # 中间节点数假设为4对应节点2,3,4,5 gene [] start 0 for n in range(num_nodes): node_idx n 2 # 中间节点索引从2开始 edges [] # 收集所有可能连接到当前节点node_idx的边 for i in range(node_idx): # i 是所有前驱节点 # 获取连接节点i到node_idx的边的操作权重 edge_weights alpha[start i] # 形状: [num_ops] # 对这条边上的操作权重进行softmax得到归一化权重 ops_weights F.softmax(edge_weights, dim-1) # 找出这条边上权重最大的操作及其权重 max_weight, max_op torch.max(ops_weights, dim-1) edges.append((i, max_op.item(), max_weight.item())) # 按权重降序排序 edges.sort(keylambda x: -x[2]) # 选择权重最高的两条边 selected_edges edges[:2] # 将选中的边前驱节点索引 操作索引加入到基因中 gene.append(selected_edges) start node_idx return gene gene_normal _parse(alpha_normal) gene_reduce _parse(alpha_reduce) return gene_normal, gene_reduce注意上述代码是搜索结束后解析最终架构的简化示例。在实际的DARTS搜索循环中alpha是连续可微的我们使用mixed_op即公式(3)中的\bar{H}^{(i,j)}进行前向传播。全局比较的逻辑是在搜索结束后根据训练好的alpha来确定最终离散架构时使用的。实操心得与避坑点权重稳定性在搜索初期alpha参数是随机初始化的各操作权重相差不大。直接进行全局排序可能不稳定。因此一定要在搜索充分收敛后再进行架构解析。通常我会在搜索完成50个epoch后取最后几个epoch的alpha的平均值来进行解析以获得更稳定的架构。与P-DARTS等方法的兼容如果你同时采用了P-DARTS渐进式搜索的策略即随着搜索进行逐步增加网络深度和剪枝操作全局比较的逻辑需要在每个搜索阶段结束时应用。要确保在剪枝例如限制每个中间节点的候选边数量之前你的全局比较逻辑已经选出了最优的边。可视化的重要性解析出gene基因编码表示Cell的结构后务必将其可视化。检查生成的Cell图确认是否出现了“单节点输入”即一个中间节点的两条输入边来自同一个前驱节点的情况。这是验证全局比较算法是否正确生效的最直观方式。可以使用graphviz或networkx库进行绘制。2.2 SENet模块集成位置、实现与超参选择SENet模块的集成相对直接但其插入的位置和实现细节对最终效果有细微影响。集成位置我们的策略是在每一个Cell的输出之后下一个Cell的输入之前插入SENet模块。具体到DARTS的堆叠模式中网络由多个相同的“阶段Stage”组成每个阶段包含N个重复的Cell。更精确的插入点是在每个Cell的concat操作之后在作为最终输出之前。这样SENet处理的是该Cell融合了所有中间节点信息的综合特征。SENet前向传播过程详解 对于一个输入特征图U其形状为[Batch, C, H, W]Squeeze压缩通过全局平均池化Global Average Pooling, GAP将空间维度H x W压缩为1 x 1得到[Batch, C, 1, 1]的特征。这一步聚合了全局空间信息产生一个通道描述符。def forward(self, x): b, c, _, _ x.size() y self.avg_pool(x).view(b, c) # Squeeze: [b, c, 1, 1] - [b, c]Excitation激励通过一个简单的门控机制两个全连接层来学习各通道的重要性权重。第一个全连接层将通道数降维到C/rr是缩减比率通常设为16使用ReLU激活第二个全连接层恢复通道数到C使用Sigmoid激活得到每个通道的权重0到1之间。y self.fc1(y) # [b, c] - [b, c//r] y self.relu(y) y self.fc2(y) # [b, c//r] - [b, c] y self.sigmoid(y).view(b, c, 1, 1) # 重塑为 [b, c, 1, 1]Scale重标定将学习到的通道权重y与原始特征图U逐通道相乘完成特征重标定。return x * y.expand_as(x) # 广播相乘PyTorch实现代码import torch.nn as nn import torch.nn.functional as F class SELayer(nn.Module): def __init__(self, channel, reduction16): super(SELayer, self).__init__() self.avg_pool nn.AdaptiveAvgPool2d(1) self.fc nn.Sequential( nn.Linear(channel, channel // reduction, biasFalse), nn.ReLU(inplaceTrue), nn.Linear(channel // reduction, channel, biasFalse), nn.Sigmoid() ) def forward(self, x): b, c, _, _ x.size() y self.avg_pool(x).view(b, c) y self.fc(y).view(b, c, 1, 1) return x * y.expand_as(x)在Cell中的集成示例class Cell(nn.Module): def __init__(self, genotype, C_prev_prev, C_prev, C, reduction, reduction_prev): # ... 初始化操作 ... # 根据genotype构建混合操作MixedOp等 self.se SELayer(C * 4) # 注意concat后通道数变为 4 * C def forward(self, s0, s1): # ... 原有的DARTS Cell前向传播逻辑 ... # 假设最终得到 states[4]即4个中间节点的输出列表 # 按通道维度拼接 concat_feature torch.cat([states[i] for i in self._concat], dim1) # 通过SENet模块 output self.se(concat_feature) return output超参数选择与调优经验缩减比率r默认值16是一个广泛验证过的良好起点。对于通道数较少的网络如搜索初期C16可以尝试更小的r例如8以避免压缩过度损失信息。对于大型网络保持16即可。插入频率我们选择在每个Cell后都插入SENet。你也可以尝试更稀疏的插入方式例如只在每个“阶段Stage”的最后一个Cell后插入或在Reduction Cell后插入。但我的实验表明在每个Cell后插入能提供最稳定和显著的性能提升虽然会轻微增加参数量和计算量。计算开销SENet增加的参数量极少仅为2*C^2/r两个全连接层。对于C64的通道r16时增加的参数量约为2*64*64/16 512个相对于动辄数十万、数百万的卷积网络参数来说可忽略不计。其计算开销主要来自GAP和两个全连接层在GPU上运行效率很高。重要提示在搜索阶段我们同样需要在超网络SuperNet的每个Cell后加入SENet模块。因为架构参数alpha的优化是在包含SENet的完整计算图中进行的。这样搜索出的架构才是与SENet模块协同工作最优的架构。3. 完整实验流程与核心环节实现3.1 实验环境搭建与数据准备硬件与软件环境GPU至少需要一张显存大于8GB的GPU如NVIDIA RTX 2080 Ti, Tesla V100。DARTS搜索过程对显存要求较高因为需要将整个超网络包含所有候选操作放在显存中。使用V100 32GB能保证更流畅的搜索。深度学习框架PyTorch 1.7.0。确保CUDA版本与PyTorch版本匹配。关键Python库torch,torchvision,numpy,tensorboard用于可视化训练过程。数据集准备CIFAR-10CIFAR-10是一个包含10类、6万张32x32彩色图像的数据集其中5万张训练1万张测试。使用torchvision可以轻松加载和预处理。import torchvision.transforms as transforms import torchvision.datasets as datasets # 搜索阶段的数据预处理与原文一致 train_transform transforms.Compose([ transforms.RandomCrop(32, padding4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)), ]) valid_transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)), ]) # 将训练集划分为搜索用的训练集和验证集各25000张 train_data datasets.CIFAR10(root./data, trainTrue, downloadTrue, transformtrain_transform) train_split, valid_split torch.utils.data.random_split(train_data, [25000, 25000]) train_loader torch.utils.data.DataLoader(train_split, batch_size64, shuffleTrue, num_workers4) valid_loader torch.utils.data.DataLoader(valid_split, batch_size64, shuffleFalse, num_workers4)注意搜索阶段使用训练集的一部分作为验证集来优化架构参数alpha这是DARTS的标准做法。在架构搜索完成后我们需要用完整的5万张训练集从头开始训练搜索得到的最优网络并在1万张测试集上评估最终性能。3.2 搜索阶段实现双层优化与全局比较的融合搜索阶段的核心是交替优化网络权重ω和架构参数α。我们需要将全局比较的思想融入到DARTS的搜索循环中。关键在于在搜索时我们仍然使用连续的混合操作MixedOp全局比较仅在搜索结束后解析最终架构时使用。搜索循环伪代码精讲# 初始化定义超网络包含所有候选操作的MixedOp、SENet模块、优化器 model SuperNet(C16, num_classes10, layers8, se_reduction16).cuda() # 架构参数alpha每个可学习边对应一组长度为num_ops的参数 arch_parameters model.arch_parameters() # 定义两个优化器分别优化模型权重和架构参数 optimizer torch.optim.SGD(model.weight_parameters(), lr0.025, momentum0.9, weight_decay3e-4) arch_optimizer torch.optim.Adam(arch_parameters, lr3e-4, betas(0.5, 0.999), weight_decay1e-3) # 学习率调度器Cosine Annealing scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_maxfloat(50), eta_min0.001) for epoch in range(50): # 训练阶段优化网络权重 ω model.train() for step, (input, target) in enumerate(train_loader): input, target input.cuda(), target.cuda() # 1. 前向传播使用当前的α和ω计算训练损失 logits model(input) loss_train criterion(logits, target) # 2. 反向传播更新ω optimizer.zero_grad() loss_train.backward() optimizer.step() # 架构更新阶段优化架构参数 α model.eval() # 注意在更新α时通常不启用BatchNorm的running stat更新 for step, (input, target) in enumerate(valid_loader): input, target input.cuda(), target.cuda() # 1. 前向传播计算验证集损失 logits model(input) loss_val criterion(logits, target) # 2. 反向传播更新α (DARTS使用一阶近似这里省略二阶导计算以加速) arch_optimizer.zero_grad() loss_val.backward() arch_optimizer.step() scheduler.step() # 可选每隔一定epoch保存当前的alpha和模型状态或可视化当前搜索趋势 # 搜索结束使用全局比较算法解析最终架构 genotype_normal, genotype_reduce parse_global(model.arch_parameters_normal, model.arch_parameters_reduce) print(Normal Cell Genotype:, genotype_normal) print(Reduction Cell Genotype:, genotype_reduce)关键参数设置与论文保持一致初始通道数 C16。这是一个较小的基数搜索完成后评估时会按比例放大例如C36。网络层数搜索时8层。这是为了控制搜索时的计算和内存开销。Batch Size64。优化器ω优化器SGD学习率0.025使用余弦退火衰减动量0.9权重衰减3e-4。α优化器Adam学习率3e-4权重衰减1e-3。搜索轮数Epoch50。通常足够让alpha收敛。3.3 评估阶段从头训练与性能验证搜索得到最优的genotype即Normal Cell和Reduction Cell的结构描述后我们需要根据这个结构重新初始化一个全新的网络而不是沿用搜索时的超网络权重并使用完整的CIFAR-10训练集5万张进行充分训练最后在测试集上评估。构建最终网络from models.final_model import NetworkCIFAR # 根据解析出的genotype构建最终网络 C 36 # 评估阶段使用更大的通道数 model NetworkCIFAR(C, num_classes10, layers20, auxiliaryFalse, genotype_normal, genotype_reduce, seTrue, se_reduction16) model model.cuda()训练策略600个Epoch数据增强采用标准的CIFAR-10增强策略包括随机水平翻转、随机裁剪32x32padding4和归一化。优化器SGD with Nesterov Momentum。初始学习率0.025余弦退火调度至0。动量0.9权重衰减3e-4。标签平滑Label Smoothing这是一个非常有效的正则化技巧可以轻微提升模型泛化能力。在交叉熵损失中将真实标签从1.0平滑到0.9将非真实标签从0.0平滑到0.1/9。辅助分类器Auxiliary Head在网络的中间层例如2/3深度处添加一个辅助分类器并在训练时以较小的权重如0.4将其损失加到总损失中有助于缓解梯度消失促进深层网络训练。在测试时移除。Cutout随机遮挡输入图像的一部分区域如16x16像素是一种强大的正则化方法对CIFAR-10提升明显。DropPathStochastic Depth在训练时以一定概率随机“丢弃”即置为恒等映射Cell中的某条路径。这相当于一种层级的Dropout能有效防止过拟合提升模型鲁棒性。训练代码片段示例criterion nn.CrossEntropyLoss(label_smoothing0.1).cuda() # 标签平滑 optimizer torch.optim.SGD(model.parameters(), lr0.025, momentum0.9, weight_decay3e-4, nesterovTrue) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max600) for epoch in range(600): model.train() for input, target in train_loader_full: # 使用完整的5万张训练集 input, target input.cuda(), target.cuda() input cutout(input, length16) # Cutout数据增强 logits, logits_aux model(input) loss criterion(logits, target) if auxiliary: # 如果使用辅助分类器 loss_aux criterion(logits_aux, target) loss 0.4 * loss_aux optimizer.zero_grad() loss.backward() nn.utils.clip_grad_norm_(model.parameters(), 5.0) # 梯度裁剪 optimizer.step() scheduler.step() # 每隔一定epoch在测试集上验证 if epoch % 10 0: test_acc evaluate(model, test_loader) print(fEpoch {epoch}, Test Acc: {test_acc:.2f}%)4. 实验结果分析与常见问题排查4.1 实验结果深度解读在CIFAR-10数据集上我们进行了严格的对比实验。下表汇总了我们的方法GC-DARTS-SE与其他主流NAS方法及人工设计网络的性能对比方法类型测试错误率 (%)参数量 (M)搜索成本 (GPU Days)备注ResNet-110 [6]人工设计6.411.7-基线模型NASNet-A [3]基于RL2.653.32000计算开销巨大AmoebaNet-A [4]基于进化算法3.343.23150耗时极长DARTS (原版)[9]基于梯度4.103.41.5我们的基线SNAS [35]基于梯度2.852.81.5PC-DARTS [12]基于梯度2.573.60.1部分通道连接GC-DARTS (仅全局比较)基于梯度2.93~3.4~1.5本文改进一DARTS-SE (仅加SE)基于梯度2.71~3.6~1.5本文改进二GC-DARTS-SE (本文)基于梯度2.48~3.6~1.5综合改进结果分析全局比较的有效性仅使用全局比较GC-DARTS错误率从4.10%降至2.93%下降了1.17个百分点。这直接证明了局部比较策略的缺陷以及全局比较策略的有效性。搜索出的架构中跳跃连接Skip Connection的数量得到了有效控制如图8所示网络结构更加多样化。SENet的补偿作用仅在原版DARTS上添加SENet模块DARTS-SE错误率降至2.71%下降了1.39个百分点。这说明SENet确实能有效补偿DARTS因近似优化带来的性能损失增强了模型的表征能力。组合效果的优越性将两者结合GC-DARTS-SE取得了最佳效果错误率低至2.48%。这证明了两个改进点是互补的全局比较找到了更好的架构骨架而SENet则进一步强化了这个骨架的特征提取能力。搜索成本我们的方法搜索成本与原版DARTS相当约1.5 GPU天远低于基于RL和进化算法的方法在效率和性能上取得了很好的平衡。架构可视化图5展示了我们方法搜索到的最优Normal Cell和Reduction Cell。一个有趣的发现是最优Normal Cell中没有出现跳跃连接主要由深度可分离卷积sep_conv和空洞卷积dil_conv构成。这打破了传统DARTS中跳跃连接占主导的局面说明全局比较机制能更公平地评估卷积操作的价值搜索出更具表达力的结构。4.2 常见问题与排查技巧实录在复现和改进DARTS的过程中我踩过不少坑。这里将典型问题及解决方案整理成表供大家参考。问题现象可能原因排查步骤与解决方案搜索过程不稳定alpha权重震荡剧烈1. 学习率设置不当尤其是α的学习率过高。2. 优化器选择不当α对优化器很敏感。3. Batch Size太小导致梯度估计噪声大。1.降低α的学习率尝试从3e-4降至1e-4或5e-5。这是最常见也最有效的解决方法。2.更换α优化器原论文使用Adam但有人发现SGD with momentum在某些情况下更稳定。可以尝试torch.optim.SGD(arch_parameters, lr0.01, momentum0.9)。3.增大Batch Size在显存允许范围内尽可能使用更大的Batch Size如128。搜索出的Cell全是跳跃连接Skip Connect发生了“性能崩塌”Performance Collapse这是DARTS的经典问题。跳跃连接权重在优化过程中占据绝对优势挤占了其他操作。1.检查搜索轮数过早停止搜索可能加剧此问题。确保搜索足够轮数如50 epoch。2.使用早停策略Early Stop如DARTS提出的当Normal Cell中跳跃连接数量达到2时提前终止搜索。3.采用公平竞争策略如Fair-DARTS使用sigmoid替代softmax让操作权重独立。我们的全局比较算法本身就能缓解此问题因为它打破了“每对节点必选一个”的规则即使某对节点上跳跃连接权重最高也可能在全局比较中被其他节点上权重更高的卷积操作淘汰。评估阶段精度远低于搜索阶段验证精度1.深度不一致Depth Gap搜索时网络浅如8层评估时网络深如20层。2.过拟合搜索出的架构在搜索验证集上过拟合。3.训练策略不同搜索和评估阶段的数据增强、正则化强度不同。1.采用渐进式搜索P-DARTS在搜索过程中逐步增加网络深度让架构参数适应更深的网络。2.增加搜索时的正则化在搜索阶段也使用DropPath、Cutout等。3.确保评估阶段训练充分使用更强的数据增强Cutout, AutoAugment、更长的训练周期600 epoch、标签平滑、辅助分类器等。我们的SENet模块也有助于提升评估阶段的泛化能力。显存溢出Out of Memory超网络同时包含所有候选操作显存占用大。1.使用PC-DARTS的思路在MixedOp中每次只对一小部分通道如1/4进行所有操作的计算其余通道直接通过大幅节省显存。2.减少初始通道数C搜索时使用更小的C如8。3.减少网络层数搜索时使用更少的层如6层。4.梯度累积减小Batch Size但多次前向传播后再更新梯度模拟大Batch Size效果。SENet模块似乎没有带来提升1.插入位置不当可能插在了不合适的层。2.缩减比率r不合适对于小网络r16可能压缩过度。3.与BatchNorm层冲突SENet的缩放操作可能会影响BatchNorm的统计量。1.确保SENet插入在Cell的concat操作之后、输出之前。这是经过验证的最佳位置之一。2.调整r对于C16的小网络尝试r8或r4。3.调整BatchNorm的动量尝试使用较小的momentum如0.1或使用其他归一化层如GroupNorm。在SENet之后可以不加BatchNorm或将其放在SENet之前。全局比较算法解析出的架构不合理1.alpha未充分收敛在搜索早期就进行解析。2.排序逻辑错误代码中按alpha原始值排序未经过softmax。1.在搜索结束后解析使用最后几个epochalpha的平均值进行解析增加稳定性。2.确认排序依据必须对每条边上的alpha向量进行softmax得到归一化的操作权重ops_weights再取最大权重值进行全局排序。切记是F.softmax(edge_weights, dim-1)后的最大值。我个人在实际操作中的体会是DARTS及其变体的复现对细节非常敏感。一个微小的改动比如学习率调度器的类型、权重衰减的值、甚至是随机种子的设置都可能导致搜索结果有显著差异。因此严格控制变量、做好详细的实验记录包括完整的超参数和随机种子是至关重要的。当遇到问题时首先从最简单的配置开始例如先在不加SENet、不使用Cutout的情况下跑通原版DARTS然后逐步添加你的改进模块并观察每个模块带来的影响。可视化工具如TensorBoard对于监控alpha权重的变化趋势、训练/验证损失曲线非常有帮助能让你直观地判断搜索过程是否健康。最后任何NAS方法的最终价值都要在独立训练和评估中体现务必给予评估阶段最充分的训练和最强的正则化才能公平地比较不同方法的真实性能。