• NSGA-NET中的micro内部细节搜索


    NSGA-NET中的micro内部细节搜索

    micro做了啥

    这篇文章的micro采用的是nasnet的搜索目标以及编码方式,只不过nasnet采用的是强化学习(如下图所示),作者这里将其更改为ea相关的算法来进行优化。搜索的目标主要是一个normal cell和一个reduction cell,构建网络的时候两个cell交替排列。用ea进行优化的时候,目标也是acc和flops。

    名称

    优化的问题描述继承了pymop这个库中的Problem这个类,优化的方法调用了pymoo的minimize函数,不如vega中的算法step比较明确

    micro的编码方式

    每个cell单元都包含5个block,每个block有两个节点,描述当前基本单元的op类型以及输入的index在哪

    比如:

    image

    每两个array构成一个基本的单元,NAGA-NET作者将他们的输出都进行相加。比如[array([7, 0]), array([6, 1])],表示这个block的两个operation分别为7和6,输入的index分别为0, 1。然后当前的输出的index作为2,方便后续的节点使用这个输出。一共有5个这样的block,所以对于一个normal cell或者reduction cell而言,编码长度为4*5=20,当然作者这里是将normal cell和reduction cell一起搜索的,所以群体中个体的基因编码的长度是40。

    根据这样的编码方式就可以得到网络的节点以及拓扑方式,如下是一些例子

    image

    random产生新个体

    作者在继承problem这个类的时候,定义了low boundry和up boundry这两个变量,如下图,这两个变量在random搜索的时候回用到,即random产生新个体的时候会在其中用到在上下界之间随机选取数值。

    image

    交叉和变异

    作者没有显示的写出来crossover和mutation的方式,但是调用了pymoo中的两个函数分别是PointCrossover和PolynomialMutation。

    def nsganet(
            pop_size=100,
            sampling=RandomSampling(var_type=np.int),
            selection=TournamentSelection(func_comp=binary_tournament),
            crossover=PointCrossover(n_points=2),  # 一种crossover的方式,双点crossover
            mutation=PolynomialMutation(eta=3, var_type=np.int),  # 一种mutation的方式
            eliminate_duplicates=True,
            n_offsprings=None,
            **kwargs):
    

    交叉

    pointcrossover函数如下

    class PointCrossover(Crossover):
    
        def __init__(self, n_points):  # 作者在这里设置的是两点交叉
            super().__init__(2, 2)
            self.n_points = n_points
    
        def _do(self, problem, pop, parents, **kwargs):
    
            # get the X of parents and count the matings
            X = pop.get("X")[parents.T]  # X为选取的两个parents
            _, n_matings, n_var = X.shape  # n mating 为1,获取基因的长度
    
            # start point of crossover
            r = np.row_stack([random.perm(n_var-1) + 1 for _ in range(n_matings)])[:, :self.n_points]  # 选取index
            r.sort(axis=1)
            r = np.column_stack([r, np.full(n_matings, n_var)])
    
            # the mask do to the crossover
            M = np.full((n_matings, n_var), False)
    
            # create for each individual the crossover range
            for i in range(n_matings):
    
                j = 0
                while j < r.shape[1] - 1:
                    a, b = r[i, j], r[i, j + 1]
                    M[i, a:b] = True  # 选取一个片段,进行交换
                    j += 2
    
            _X = crossover_mask(X, M)  # 根据mask,对X进行变异,_X为变异之后的基因片段
            return pop.new("X", _X)
    

    其中X为选取的parent,包含两个基因,通过random选取r,即为两个index,最后交换着两个基因片段内的基因。以下为程序debug过程中的输出

    image

    X为选取的两个上一代的样本,_X为产生的子代,M为mask,表示是否交换基因片段。

    变异

    变异作者调用的是PolynomialMutation函数,即多项式变异,多项式变异该函数的执行过程如下

    [v_k' = v_k +delta imes(u_k-l_k) ]

    [delta=left{ egin{aligned} &[2 imes u+(1-2 imes u)(1-delta_1)^{eta_m+1}]^{frac{1}{eta_m+1}}-1 && if uleq0.5\ &1-[2 imes(1-u)+2 imes(u-0.5)(1-delta_2)^{eta_m+1}]^{frac{1}{eta_m+1}} && if u>0.5 end{aligned} ight. ]

    其中(delta_1 = (v_k-l_k)/(u_k-l_k)), (delta_2 = (u_k-v_k)/(u_k-l_k))(u)是一个[0,1]区间内的随机数。(eta_m)是由用户选取的分布指数。(u_k)(l_k)分别是对应基因位的上下界,(v_k)是所选取的基因位, (u)为随机生成的数

    代码实现如下

        def _do(self, problem, pop, **kwargs):
            pdb.set_trace()
            X = pop.get("X").astype(np.double)  # 选取两个样本,而不是一个
            Y = np.full(X.shape, np.inf)
    
            if self.prob is None:
                self.prob = 1.0 / problem.n_var  # 1/40.
    
            do_mutation = random.random(X.shape) < self.prob  # 长度越大,做mutation的概率越小,平均是选取一位来做mutation
    
            Y[:, :] = X
            # 以下,根据选取的基因位,选择其上下boundary
            xl = np.repeat(problem.xl[None, :], X.shape[0], axis=0)[do_mutation]
            xu = np.repeat(problem.xu[None, :], X.shape[0], axis=0)[do_mutation]
    
            if self.var_type == np.int:  # true
                xl -= 0.5  # 下界由0减少0.5,
                xu += (0.5 - 1e-16)  # 上界+0.5
    
            X = X[do_mutation]  # 选取需要变异的基因位
    
            delta1 = (X - xl) / (xu - xl)  # 计算delta1
            delta2 = (xu - X) / (xu - xl)  # 计算delta2
    
            mut_pow = 1.0 / (self.eta + 1.0)
    
            rand = random.random(X.shape)
            mask = rand <= 0.5
            mask_not = np.logical_not(mask)  # 逻辑not
    
            deltaq = np.zeros(X.shape)
            # 计算如下的value,并且将结果赋给delta
            xy = 1.0 - delta1
            val = 2.0 * rand + (1.0 - 2.0 * rand) * (np.power(xy, (self.eta + 1.0)))
            d = np.power(val, mut_pow) - 1.0
            deltaq[mask] = d[mask]
    
            xy = 1.0 - delta2
            val = 2.0 * (1.0 - rand) + 2.0 * (rand - 0.5) * (np.power(xy, (self.eta + 1.0)))
            d = 1.0 - (np.power(val, mut_pow))
            deltaq[mask_not] = d[mask_not]
    
            # mutated values
            _Y = X + deltaq * (xu - xl)  # 得到的结果为浮点数
    
            # back in bounds if necessary (floating point issues)
            _Y[_Y < xl] = xl[_Y < xl]
            _Y[_Y > xu] = xu[_Y > xu]
    
            # set the values for output
            Y[do_mutation] = _Y  # 将变异得到的结果赋给下一代
    
            if self.var_type == np.int:
                Y = np.rint(Y).astype(np.int)
    
            off = OutOfBoundsRepair().do(problem, pop.new("X", Y))
            return off
    

    其中problem中存储的是刚刚说到的基因编码的上下界, 如下图

    image

    选取需要变异的基因位如下图,每个基因平均选一位进行变异,

    image

    原来的基因如下

    image

    变异之后的结果如下

    image

    可以看到进行上面操作之后,基因还是和原来几乎一样,变化的不是特别的大。

  • 相关阅读:
    mysql自动增长怎么恢复从1开始
    Python 中的多维字典
    如何将JS里变量的值赋给文本框
    使用nagios+python监控nginx进程数
    python getopt使用
    Nagios安装完后status map,trends等页面访问出错之解决
    nagios监控3306端口
    AdventureWorks Databases 2008 下载地址
    multiselect获取选中的多个下拉项的值(逗号分割的字符串)
    用CSS让网页背景图片居中的方法
  • 原文地址:https://www.cnblogs.com/yongjieShi/p/14812190.html
Copyright © 2020-2023  润新知