DARTS:DIFFERENTIABLE ARCHITECTURE SEARCH

  • 推荐阅读
  • cnn (for cifar10)
    • architect.py
    • genotypes.py
    • model_search.py
    • train_search.py
    • utils.py

推荐阅读

Source Code
解读参考
公式推导

cnn (for cifar10)

主要对影响理解的代码做以解释

architect.py

该函数是为了求alpha的导数而设置。

class Architect(object):#计算alpha的梯度def __init__(self, model, args):self.network_momentum = args.momentumself.network_weight_decay = args.weight_decayself.model = modelself.optimizer = torch.optim.Adam(self.model.arch_parameters(), #训练路径参数alphalr=args.arch_learning_rate, betas=(0.5, 0.999), weight_decay=args.arch_weight_decay)def _compute_unrolled_model(self, input, target, eta, network_optimizer):loss = self.model._loss(input, target) #首先计算一下前向损失theta = _concat(self.model.parameters()).data #将所有权重copy一下try:#替代了optimizer.step() 从而不改变网络权重moment = _concat(network_optimizer.state[v]['momentum_buffer'] for v in self.model.parameters()).mul_(self.network_momentum)except:moment = torch.zeros_like(theta)'''带权重衰减和动量的sgd optimizer.step()会使momentum变化 network_optimizer.state[v]['momentum_buffer'] 会变成计算之后的momentv_{t+1} = mu * v_{t} + g_{t+1} + weight_decay * w  其中weight_decay*w是正则项 w_{t+1} = w_{t} - lr * v_{t+1}所以:moment = mu * v_{t} dw = g_{t+1} + weight_decay * w v_{t+1} = moment + dww'= w - lr * v_{t+1}'''dtheta = _concat(torch.autograd.grad(loss, self.model.parameters())).data + self.network_weight_decay*thetaunrolled_model = self._construct_model_from_theta(theta.sub(eta, moment+dtheta))#求w‘return unrolled_modeldef step(self, input_train, target_train, input_valid, target_valid, eta, network_optimizer, unrolled):# eta是网络的学习率self.optimizer.zero_grad() #将该batch的alpha的梯度设置为0if unrolled:#展开要求w‘self._backward_step_unrolled(input_train, target_train, input_valid, target_valid, eta, network_optimizer)else: #不展开的话首先将valid计算一次前向传播,再计算损失self._backward_step(input_valid, target_valid)self.optimizer.step() #alpha梯度更新def _backward_step(self, input_valid, target_valid):loss = self.model._loss(input_valid, target_valid)loss.backward()def _backward_step_unrolled(self, input_train, target_train, input_valid, target_valid, eta, network_optimizer):unrolled_model = self._compute_unrolled_model(input_train, target_train, eta, network_optimizer) #w‘unrolled_loss = unrolled_model._loss(input_valid, target_valid) #L_val(w')unrolled_loss.backward() #得到了新的权重dalpha = [v.grad for v in unrolled_model.arch_parameters()] #dalpha{L_val(w', alpha)}vector = [v.grad.data for v in unrolled_model.parameters()] #dw'{L_val(w', alpha)}implicit_grads = self._hessian_vector_product(vector, input_train, target_train)#更新最后的梯度gradient = dalpha - lr*hessianfor g, ig in zip(dalpha, implicit_grads):g.data.sub_(eta, ig.data)for v, g in zip(self.model.arch_parameters(), dalpha):if v.grad is None:v.grad = Variable(g.data)else:v.grad.data.copy_(g.data)def _construct_model_from_theta(self, theta):#theta=w'model_new = self.model.new()model_dict = self.model.state_dict()params, offset = {}, 0for k, v in self.model.named_parameters():v_length = np.prod(v.size())params[k] = theta[offset: offset+v_length].view(v.size())offset += v_lengthassert offset == len(theta)model_dict.update(params)model_new.load_state_dict(model_dict)return model_new.cuda()def _hessian_vector_product(self, vector, input, target, r=1e-2):R = r / _concat(vector).norm()# w+ = w + eps*dw'for p, v in zip(self.model.parameters(), vector):p.data.add_(R, v)loss = self.model._loss(input, target)grads_p = torch.autograd.grad(loss, self.model.arch_parameters())# w- = w - eps*dw'for p, v in zip(self.model.parameters(), vector):p.data.sub_(2*R, v)loss = self.model._loss(input, target)grads_n = torch.autograd.grad(loss, self.model.arch_parameters())for p, v in zip(self.model.parameters(), vector):p.data.add_(R, v)return [(x-y).div_(2*R) for x, y in zip(grads_p, grads_n)]

genotypes.py

该函数中只有PRIMITIVES暂时有用,其他的设置可以先不用管。其他的变量是NASNetAmoebaNet以及DARTS搜索出来的cell结构。


Genotype = namedtuple('Genotype', 'normal normal_concat reduce reduce_concat')PRIMITIVES = ['none','max_pool_3x3','avg_pool_3x3','skip_connect','sep_conv_3x3','sep_conv_5x5','dil_conv_3x3','dil_conv_5x5'
]
DARTS_V1 = Genotype(normal=[('sep_conv_3x3', 1), ('sep_conv_3x3', 0), ('skip_connect', 0), ('sep_conv_3x3', 1), ('skip_connect', 0), ('sep_conv_3x3', 1), ('sep_conv_3x3', 0), ('skip_connect', 2)], normal_concat=[2, 3, 4, 5], reduce=[('max_pool_3x3', 0), ('max_pool_3x3', 1), ('skip_connect', 2), ('max_pool_3x3', 0), ('max_pool_3x3', 0), ('skip_connect', 2), ('skip_connect', 2), ('avg_pool_3x3', 0)], reduce_concat=[2, 3, 4, 5])
DARTS_V2 = Genotype(normal=[('sep_conv_3x3', 0), ('sep_conv_3x3', 1), ('sep_conv_3x3', 0), ('sep_conv_3x3', 1), ('sep_conv_3x3', 1), ('skip_connect', 0), ('skip_connect', 0), ('dil_conv_3x3', 2)], normal_concat=[2, 3, 4, 5], reduce=[('max_pool_3x3', 0), ('max_pool_3x3', 1), ('skip_connect', 2), ('max_pool_3x3', 1), ('max_pool_3x3', 0), ('skip_connect', 2), ('skip_connect', 2), ('max_pool_3x3', 1)], reduce_concat=[2, 3, 4, 5])DARTS = DARTS_V2


DARTS_V2举例来说,nomal代表的是normal cellreduction代表的是reduction cell(即feature map减小,通道加倍),(‘sep_conv_3x3’, 1)中1代表的上图标红的1,即说明该节点与1相连并且操作是3×33\times 33×3的深度可分离卷积。

model_search.py

MixedOp
将两节点各种操作实现并加入列表,其中若是pooling,则在后面接上BN
输出则是对各操作的加权相加,所以输入输出维度一样

class MixedOp(nn.Module):def __init__(self, C, stride):super(MixedOp, self).__init__()self._ops = nn.ModuleList()for primitive in PRIMITIVES:op = OPS[primitive](C, stride, False)if 'pool' in primitive:op = nn.Sequential(op, nn.BatchNorm2d(C, affine=False))self._ops.append(op)def forward(self, x, weights):#weights就是alpha 输出是对各操作的加权相加return sum(w * op(x) for w, op in zip(weights, self._ops))

Cell

class Cell(nn.Module):def __init__(self, steps, multiplier, C_prev_prev, C_prev, C, reduction, reduction_prev):super(Cell, self).__init__()self.reduction = reductionif reduction_prev:self.preprocess0 = FactorizedReduce(C_prev_prev, C, affine=False)else:self.preprocess0 = ReLUConvBN(C_prev_prev, C, 1, 1, 0, affine=False)self.preprocess1 = ReLUConvBN(C_prev, C, 1, 1, 0, affine=False)self._steps = stepsself._multiplier = multiplierself._ops = nn.ModuleList()self._bns = nn.ModuleList()for i in range(self._steps): # i = 0,1,2,3for j in range(2+i): # j = 2,3,4,5 因为每处理一个节点,该节点就变为下一个节点的前继stride = 2 if reduction and j < 2 else 1op = MixedOp(C, stride) #初始化得到一个节点到另一个节点的操作集合self._ops.append(op) # len = 14'''self._ops[0,1]代表的是内部节点0的前继操作self._ops[2,3,4]代表的是内部节点1的前继操作self._ops[5,6,7,8]代表的是内部节点2的前继操作self._ops[9,10,11,12,13]代表的是内部节点3的前继操作'''def forward(self, s0, s1, weights):s0 = self.preprocess0(s0)s1 = self.preprocess1(s1)states = [s0, s1]offset = 0for i in range(self._steps):#因为先将该节点到另一个节点各操作后的输出相加,再把该节点与所有前继节点的操作相加 所以输出维度不变s = sum(self._ops[offset+j](h, weights[offset+j]) for j, h in enumerate(states)) offset += len(states)states.append(s)'''i=1:  s2 = self._ops[0](s0,weights[0]) + self._ops[1](s1,weights[1])即内部节点0i=2:  s3 = self._ops[2](s0,weights[2]) + self._ops[3](s1,weights[3]) + self._ops[4](s2,weights[4])即内部节点1i=3、4依次计算得到s4,s5由此可知len(weights)也等于14,因为有8个操作,所以weight[i]有8个值'''return torch.cat(states[-self._multiplier:], dim=1) #将后4个节点的输出按channel拼接 channel变为4倍# eg:(10,3,2,2)+(10,3,2,2)-->(10,6,2,2)

Network(只解析了初始化部分以及genotype部分)

class Network(nn.Module):def __init__(self, C, num_classes, layers, criterion, steps=4, multiplier=4, stem_multiplier=3):# c =16 num_class = 10,layers = 8 ,criterion = nn.CrossEntropyLoss().cuda()super(Network, self).__init__()self._C = Cself._num_classes = num_classesself._layers = layersself._criterion = criterionself._steps = steps # 4self._multiplier = multiplier # 4 通道数乘数因子 因为有4个中间节点 代表通道数要扩大4倍C_curr = stem_multiplier*C # 48self.stem = nn.Sequential(nn.Conv2d(3, C_curr, 3, padding=1, bias=False),nn.BatchNorm2d(C_curr))# 对于第一个cell来说,stem即是s0,也是s1# C_prev_prev, C_prev是输出channel 48# C_curr 现在是输入channel 16C_prev_prev, C_prev, C_curr = C_curr, C_curr, Cself.cells = nn.ModuleList()reduction_prev = Falsefor i in range(layers):# 在 1/3 和 2/3 层减小特征size并且加倍通道.if i in [layers//3, 2*layers//3]:C_curr *= 2reduction = Trueelse:reduction = Falsecell = Cell(steps, multiplier, C_prev_prev, C_prev, C_curr, reduction, reduction_prev)reduction_prev = reductionself.cells += [cell]C_prev_prev, C_prev = C_prev, multiplier*C_curr'''layers = 8, 第2和5个cell是reduction_cellcells[0]: cell = Cell(4, 4, 48,  48,  16, false,  false) 输出[N,16*4,h,w]cells[1]: cell = Cell(4, 4, 48,  64,  16, false,  false) 输出[N,16*4,h,w]cells[2]: cell = Cell(4, 4, 64,  64,  32, True,   false) 输出[N,32*4,h/2,w/2]cells[3]: cell = Cell(4, 4, 64,  128, 32, false,  false) 输出[N,32*4,h/2,w/2]cells[4]: cell = Cell(4, 4, 128, 128, 32, false,  false) 输出[N,32*4,h/2,w/2]cells[5]: cell = Cell(4, 4, 128, 128, 64, True,   false) 输出[N,64*4,h/4,w/4]cells[6]: cell = Cell(4, 4, 128, 256, 64, false,  false) 输出[N,64*4,h/4,w/4]cells[7]: cell = Cell(4, 4, 256, 256, 64, false,  false) 输出[N,64*4,h/4,w/4]'''self.global_pooling = nn.AdaptiveAvgPool2d(1)self.classifier = nn.Linear(C_prev, num_classes)self._initialize_alphas()def genotype(self):def _parse(weights):# weights [14,8]gene = []n = 2start = 0for i in range(self._steps):#[0,1,2,3] 代表了中间节点end = start + nW = weights[start:end].copy() #获取当前中间节点至前继节点的权重#sorted返回按alpha降序排列的前继节点的索引edges = sorted(range(i + 2), key=lambda x: -max(W[x][k] for k in range(len(W[x])) if k != PRIMITIVES.index('none')))[:2]for j in edges:k_best = Nonefor k in range(len(W[j])):# [0~7]if k != PRIMITIVES.index('none'):if k_best is None or W[j][k] > W[j][k_best]:k_best = k #找出与前继节点不包含none的操作中alpha值最大的操作索引gene.append((PRIMITIVES[k_best], j))start = endn += 1return genegene_normal = _parse(F.softmax(self.alphas_normal, dim=-1).data.cpu().numpy()) #按行进行softmaxgene_reduce = _parse(F.softmax(self.alphas_reduce, dim=-1).data.cpu().numpy())concat = range(2+self._steps-self._multiplier, self._steps+2) # [2,3,4,5]genotype = Genotype(normal=gene_normal, normal_concat=concat,reduce=gene_reduce, reduce_concat=concat)return genotype

这里对于为什么要除去none操作,文中给了两点解释

对于第一点比较好理解,但是对于第二点我不是很理解,为什么BN的存在不会影响分类结果以及none操作如何影响节点的结果表示?

train_search.py

def train(train_queue, valid_queue, model, architect, criterion, optimizer, lr):objs = utils.AvgrageMeter()top1 = utils.AvgrageMeter()top5 = utils.AvgrageMeter()for step, (input, target) in enumerate(train_queue):model.train()n = input.size(0)input = Variable(input, requires_grad=False).cuda()target = Variable(target, requires_grad=False).cuda(async=True)# get a random minibatch from the search queue with replacementinput_search, target_search = next(iter(valid_queue))input_search = Variable(input_search, requires_grad=False).cuda()target_search = Variable(target_search, requires_grad=False).cuda(async=True)# 计算并更新alpha的梯度architect.step(input, target, input_search, target_search, lr, optimizer, unrolled=args.unrolled) optimizer.zero_grad()logits = model(input)loss = criterion(logits, target)loss.backward() nn.utils.clip_grad_norm(model.parameters(), args.grad_clip) #默认是先求L2正则 控制权重变化不那么大optimizer.step() #更新网络内部的权重prec1, prec5 = utils.accuracy(logits, target, topk=(1, 5))objs.update(loss.data[0], n)top1.update(prec1.data[0], n)top5.update(prec5.data[0], n)if step % args.report_freq == 0:logging.info('train %03d %e %f %f', step, objs.avg, top1.avg, top5.avg)return top1.avg, objs.avg

utils.py

drop_path
dropout的拓展,将运算单元drop的方法应用到路径上,会随机的丢弃一些路径来实现正则化,但是至少保证有一条路径是连接输入和输出的,方法源自于分形网络:

Larsson, Gustav, Michael Maire, and Gregory Shakhnarovich. “Fractalnet: Ultra-deep neural networks without residuals.” arXiv preprint arXiv:1605.07648 (2016).

def drop_path(x, drop_prob):
# 假设x已经是cuda向量if drop_prob > 0.:keep_prob = 1.-drop_prob# bernoulli以keep_prob的概率生成1# 例如当keep_prob=0.3时,生成的10个数中可能有3个为1mask = Variable(torch.cuda.FloatTensor(x.size(0), 1, 1, 1).bernoulli_(keep_prob))# 除以keep_prob是为了让输出预期相同x.div_(keep_prob)x.mul_(mask)return x

关于输出预期相同 cs231n中对dropout做了详细解说,这里简单说明一下,在训练阶段,我们drop了一些神经元,但是在测试阶段,所有神经元都处于活动状态。假设我们以ppp的概率去保留一些神经元,在训练阶段,一个神经元被丢失后,该神经元预期的输出变为p∗x+(1−p)∗0p*x+(1-p)*0p∗x+(1−p)∗0,因为该神经元的输出有1−p1-p1−p的概率变为0,所以该神经元的输出预期化简为p∗xp*xp∗x。而测试阶段,所有神经元都处于活动状态,所以该神经元的输出预期就是xxx,为了让测试阶段和训练阶段输出预期一致,所以在训练阶段,输出必须除以ppp。

【深度学习】【ICLR2019】DARTS代码解读相关推荐

  1. java编程石头剪刀布图片_石头、剪刀、布!10分钟带你打开深度学习大门,代码已开源...

    原标题:石头.剪刀.布!10分钟带你打开深度学习大门,代码已开源 沉沉 发自 宇宙中心 量子位 出品 | 公众号 QbitAI 深度学习技术的不断普及,越来越多的语言可以用来进行深度学习项目的开发,即 ...

  2. 循环神经网络matlab程序设计,神经网络及深度学习(包含matlab代码).pdf

    神经网络及深度学习(包含matlab代码) 神经网络及深度学习 (包含 MATLAB 仿真) 人工神经网络(Artificial Neural Network,即 ANN ), 作为对人脑最简单的一种 ...

  3. 【HSI】高光谱的数据集分类深度学习实战及代码理解

    [HSI]高光谱的数据集分类深度学习实战及代码理解 文章目录 [HSI]高光谱的数据集分类深度学习实战及代码理解 一.配置文件编写 二.高光谱图像的处理 2.1图像数据变换 2.2 数据整合 2.3 ...

  4. 深度学习怎么跑代码?

    深度学习怎么跑代码?从事深度学习的研究者都知道,深度学习代码需要设计海量的数据,需要很大很大很大的计算量,以至于CPU算不过来,需要通过GPU帮忙,今天怎么教大家免费使用GPU跑深度学习代码. 深度学 ...

  5. 深度学习在恶意代码检测方面的应用简单调研

    随着互联网的繁荣,现阶段的恶意代码也呈现出快速发展的趋势,主要表现为变种数量多.传播速度快.影响范围广.在这样的形势下,传统的恶意代码检测方法已经无法满足人们对恶意代码检测的要求.比如基于签名特征码的 ...

  6. 六种人体姿态估计的深度学习模型和代码总结

    六种人体姿态估计的深度学习模型和代码总结 姿态估计的目标是在RGB图像或视频中描绘出人体的形状,这是一种多方面任务,其中包含了目标检测.姿态估计.分割等等.有些需要在非水平表面进行定位的应用可能也会用 ...

  7. 机器学习深度学习算法及代码实现

    原文地址:https://blog.csdn.net/qq_31456593/article/details/69340697 最近在学机器学习,学习过程中收获颇多,在此留下学习记录,希望与同道中人相 ...

  8. 深度学习模型保存_解读计算机视觉的深度学习模型

    作者 | Dipanjan(DJ)Sarkar 来源 | Medium 编辑 | 代码医生团队 介绍 人工智能(AI)不再仅限于研究论文和学术界.业内不同领域的企业和组织正在构建由AI支持的大规模应用 ...

  9. 如何看懂一个深度学习的项目代码

    搞深度学习的人,两大必备日常除了读论文之外就是读代码. 深度学习项目代码,小到几百行的测试demo,大到成千万行的开源项目,读起来方法肯定各有不同. 如下图Mask R-CNN项目代码和PyTorch ...

  10. 【深度学习】深度学习手写代码汇总(建议收藏,面试用)

    这几天一些同学在面试的时候,遇到了一些手写代码的题,因为之前都没有准备到,所以基本上在写的时候都有点蒙. 今天我就把一些常见的考题给大家整理下,这些题也是我之前准备面试的时候整理的,很多的代码都是网上 ...

最新文章

  1. ESP8266串口处理
  2. [C#] 连接数据库并验证用户名和密码
  3. 2017蓝桥杯省赛---java---B---7(日期问题)
  4. Spring 异常处理三种方式
  5. DiskGenius的 “终止位置参数溢出”错误解决方法。
  6. mac 使用js打开计算器_Numi for Mac(mac计算器软件)
  7. How to show only next line after the matched one?
  8. Python实现信号滤波(基于scipy)
  9. 女士品茶 - 简单摘录
  10. 常见面试算法题汇总(Android面试)
  11. lisp scheme 果壳_学习LISP(一): Scheme编辑器 Edwin
  12. jboot jboot.properties 设置
  13. 美国的做法致芯片市值持续暴跌5000亿美元,美国芯片则祈求中国买更多芯片
  14. 修改host文件实现内网传输
  15. boss直聘账号异常登不上_python爬虫Scrapy:爬取boss数据
  16. 筹码集中度的判断方法
  17. 退一步海阔天空——不算结局的结局
  18. 2022年最新网络与数据安全法规政策、国标、报告合集(141份)
  19. 30分钟搞定极光sdk安卓客户端接入
  20. ubuntu14.04 adb devices无法识别手机

热门文章

  1. Unity游戏开发案例分享
  2. nssa和stub_Stub区域和NSSA区域比较
  3. 图片去底色功能怎么用?怎么把图片背景色变成透明的?
  4. https数据传输协议(安全套接字层超文本传输协议)
  5. 重磅来袭!Typora官方主题+自定义主题完美整合
  6. 51单片机最小系统及晶振电容的选择
  7. 三星手机败走中国,血战印度
  8. 2022春季苹果发布会将会发布什么?
  9. python中set什么意思_python中set详解
  10. 在计算机操作中粘贴的快捷键是什么,电脑复制粘贴的快捷键是什么