zoukankan      html  css  js  c++  java
  • 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

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

  • 相关阅读:
    UVA 11925 Generating Permutations 生成排列 (序列)
    UVA 1611 Crane 起重机 (子问题)
    UVA 11572 Unique snowflakes (滑窗)
    UVA 177 PaperFolding 折纸痕 (分形,递归)
    UVA 11491 Erasing and Winning 奖品的价值 (贪心)
    UVA1610 PartyGame 聚会游戏(细节题)
    UVA 1149 Bin Packing 装箱(贪心)
    topcpder SRM 664 div2 A,B,C BearCheats , BearPlays equalPiles , BearSorts (映射)
    UVA 1442 Cave 洞穴 (贪心+扫描)
    UVA 1609 Foul Play 不公平竞赛 (构(luan)造(gao)+递归)
  • 原文地址:https://www.cnblogs.com/yongjieShi/p/14812190.html
Copyright © 2011-2022 走看看