def do_work(self, max_gen): Log.info('*'*25) # the step 1 if StatusUpdateTool.is_evolution_running(): Log.info('Initialize from existing population data') gen_no = Utils.get_newest_file_based_on_prefix('begin') if gen_no is not None: Log.info('Initialize from %d-th generation'%(gen_no)) pops = Utils.load_population('begin', gen_no) self.pops = pops else: raise ValueError('The running flag is set to be running, but there is no generated population stored') else: gen_no = 0 Log.info('Initialize...') self.initialize_population() Log.info('EVOLVE[%d-gen]-Begin to evaluate the fitness'%(gen_no)) self.fitness_evaluate() Log.info('EVOLVE[%d-gen]-Finish the evaluation'%(gen_no)) gen_no += 1 for curr_gen in range(gen_no, max_gen): self.params['gen_no'] = curr_gen #step 3 Log.info('EVOLVE[%d-gen]-Begin to crossover and mutation'%(curr_gen)) self.crossover_and_mutation() Log.info('EVOLVE[%d-gen]-Finish crossover and mutation'%(curr_gen)) Log.info('EVOLVE[%d-gen]-Begin to evaluate the fitness'%(curr_gen)) self.fitness_evaluate() Log.info('EVOLVE[%d-gen]-Finish the evaluation'%(curr_gen)) self.environment_selection() Log.info('EVOLVE[%d-gen]-Finish the environment selection'%(curr_gen)) StatusUpdateTool.end_evolution()
def do_alter_resnet_mutation(self, position, indi): """ ----out_channel of resnet ----amount in one resnet """ mutation_p_type = '' mutation_p_count = 0 u_ = random.random() if u_ < 0.5: mutation_p_type = 'RESNET_OUT_CHANNEL' channel_list = StatusUpdateTool().get_output_channel() index_ = int(np.floor(np.random.random() * len(channel_list))) if indi.units[position].out_channel != channel_list[index_]: self.log.info( 'Unit at %d changes its output channel from %d to %d' % (position, indi.units[position].out_channel, channel_list[index_])) indi.units[position].out_channel = channel_list[index_] keep_out_channel = channel_list[index_] for i in range(position + 1, len(indi.units)): if indi.units[i].type == 1 or indi.units[i].type == 3: self.log.info( 'Due to above, the unit at %d should change its input channel from %d to %d' % (i, indi.units[i].in_channel, keep_out_channel)) indi.units[i].in_channel = keep_out_channel if indi.units[i].type == 1: break elif indi.units[i].type == 3: estimated_out_channel = indi.units[ i].k * indi.units[i].amount + indi.units[ i].in_channel if estimated_out_channel > indi.units[ i].out_channel: break else: self.log.info( 'Due to the above mutation, unit at %d changes its output channel from %d to %d' % (i, indi.units[i].out_channel, estimated_out_channel)) indi.units[ i].out_channel = estimated_out_channel keep_out_channel = estimated_out_channel mutation_p_count = 1 indi.reset_acc() else: mutation_p_type = 'RESNET_AMOUNT' min_resnet_unit, max_resnet_unit = StatusUpdateTool.get_resnet_unit_length_limit( ) amount = np.random.randint(min_resnet_unit, max_resnet_unit) if amount != indi.units[position].amount: self.log.info('Unit at %d changes its amount from %d to %d' % (position, indi.units[position].amount, amount)) indi.units[position].amount = amount mutation_p_count = 1 indi.reset_acc() return mutation_p_type, mutation_p_count
def do_alter_densenet_mutation(self, position, indi): mutation_p_type = 'DENSENET_AMOUNT' mutation_p_count = 0 k = indi.units[position].k if k == 12: _, amount_lower_limit, amount_upper_limit = StatusUpdateTool.get_densenet_k12( ) elif k == 20: _, amount_lower_limit, amount_upper_limit = StatusUpdateTool.get_densenet_k20( ) elif k == 40: _, amount_lower_limit, amount_upper_limit = StatusUpdateTool.get_densenet_k40( ) amount = np.random.randint(amount_lower_limit, amount_upper_limit + 1) if amount != indi.units[position].amount: self.log.info('Unit at %d changes its amount from %d to %d' % (position, indi.units[position].amount, amount)) if indi.units[position].amount < amount: new_out_channel = (amount - indi.units[position].amount ) * k + indi.units[position].out_channel else: new_out_channel = indi.units[position].out_channel - ( indi.units[position].amount - amount) * k indi.units[position].amount = amount self.log.info( 'Due to the above mutation, unit at %d changes its output channel from %d to %d' % (position, indi.units[position].out_channel, new_out_channel)) indi.units[position].out_channel = new_out_channel keep_out_channel = new_out_channel for i in range(position + 1, len(indi.units)): if indi.units[i].type == 1 or indi.units[i].type == 3: self.log.info( 'Due to the above mutation, unit at %d changes its input channel from %d to %d' % (i, indi.units[i].in_channel, keep_out_channel)) indi.units[i].in_channel = keep_out_channel if indi.units[i].type == 1: break elif indi.units[i].type == 3: estimated_out_channel = indi.units[i].k * indi.units[ i].amount + indi.units[i].in_channel if estimated_out_channel > indi.units[i].out_channel: break else: self.log.info( 'Due to the above mutation, unit at %d changes its output channel from %d to %d' % (i, indi.units[i].out_channel, estimated_out_channel)) indi.units[i].out_channel = estimated_out_channel keep_out_channel = estimated_out_channel mutation_p_count = 1 indi.reset_acc() return mutation_p_type, mutation_p_count
def initialize_population(self): ''' 种群初始化 ''' # utils 中.ini [IS_RUNNING] 参数置1 StatusUpdateTool.begin_evolution() pops = Population(params, 0) pops.initialize() self.pops = pops Utils.save_population_at_begin(str(pops), 0)
def initialize_population(self): ''' 种群初始化 ''' # ini文件IS_RUNNING参数置1 StatusUpdateTool.begin_evolution() # 初始化种群,传入ini文件的参数 pops = Population(params, 0) pops.initialize() self.pops = pops # 建立并写入种群begin文件 Utils.save_population_at_begin(str(pops), 0)
def __init__(self, learning_rate): ''' 需传入: 学习率、data ''' # trainloader, validate_loader = data_loader.get_train_valid_loader('/home/yanan/train_data', batch_size=128, augment=True, valid_size=0.1, shuffle=True, random_seed=2312390, show_sample=False, num_workers=1, pin_memory=True) #testloader = data_loader.get_test_loader('/home/yanan/train_data', batch_size=128, shuffle=False, num_workers=1, pin_memory=True) net = EvoCNNModel() cudnn.benchmark = True # net = net.cuda() criterion = nn.CrossEntropyLoss() best_acc = 0.0 self.data = MadeData() self.net = net self.criterion = criterion self.best_acc = best_acc # self.trainloader = trainloader # self.validate_loader = validate_loader self.file_id = os.path.basename(__file__).split('.')[0] #self.testloader = testloader #self.log_record(net, first_time=True) #self.log_record('+'*50, first_time=False) self.num_class = StatusUpdateTool.get_num_class() self.learning_rate = learning_rate
def process(self): total_epoch = StatusUpdateTool.get_epoch_size() train_loader, testloader = self.data.CIFR10() for p in range(total_epoch): self.train(p, train_loader) self.test(total_epoch, testloader) return self.best_acc
def do_mutation(self): _stat_param = {'offspring_new':0, 'offspring_from_parent':0, 'ADD':0, 'REMOVE':0, 'CHANNEL':0, 'POOLING_TYPE':0} mutation_list = StatusUpdateTool.get_mutation_probs_for_each() for indi in self.individuals: p_ = random.random() if p_ < self.prob: _stat_param['offspring_new'] += 1 mutation_type = self.select_mutation_type(mutation_list) if mutation_type == 0: _stat_param['ADD'] += 1 self.do_add_unit_mutation(indi) elif mutation_type == 1: _stat_param['REMOVE'] += 1 self.do_remove_unit_mutation(indi) elif mutation_type == 2: _stat_param['CHANNEL'] += 1 self.do_modify_conv_mutation(indi) elif mutation_type == 3: _stat_param['POOLING_TYPE'] += 1 self.do_modify_pooling_type_mutation(indi) else: raise TypeError('Error mutation type :%d, validate range:0-4'%(mutation_type)) else: _stat_param['offspring_from_parent'] += 1 self.log.info('MUTATION-mutated individuals:%d[ADD:%2d,REMOVE:%2d,CHANNEL:%2d,POOL:%2d, no_changes:%d'%(_stat_param['offspring_new'], \ _stat_param['ADD'], _stat_param['REMOVE'], _stat_param['CHANNEL'], _stat_param['POOLING_TYPE'], _stat_param['offspring_from_parent']))
def do_mutation(self): _stat_param = {'offspring_new':0, 'offspring_from_parent':0, 'ADD':0, 'REMOVE':0, 'ALTER':0, 'RESNET_OUT_CHANNEL':0, 'RESNET_AMOUNT':0, 'DENSENET_AMOUNT':0, 'POOLING_TYPE':0} mutation_list = StatusUpdateTool.get_mutation_probs_for_each() for indi in self.individuals: p_ = random.random() if p_ < self.prob: _stat_param['offspring_new'] += 1 mutation_type = self.select_mutation_type(mutation_list) if mutation_type == 0: _stat_param['ADD'] += 1 self.do_add_unit_mutation(indi) elif mutation_type == 1: _stat_param['REMOVE'] += 1 self.do_remove_unit_mutation(indi) elif mutation_type == 2: mutation_p_type, mutation_p_count = self.do_alter_mutation(indi) _stat_param[mutation_p_type] = mutation_p_count + _stat_param[mutation_p_type] _stat_param['ALTER'] += mutation_p_count if mutation_p_count == 0: _stat_param['offspring_new'] -= 1 _stat_param['offspring_from_parent'] += 1 else: raise TypeError('Error mutation type :%d, validate range:0-4'%(mutation_type)) else: _stat_param['offspring_from_parent'] += 1 self.log.info('MUTATION-mutated individuals:%d[ADD:%2d,REMOVE:%2d,ALTER:%2d,RESNET_OUT_CHANNEL:%2d, RESNET_AMOUNT:%2d, DENSENET_AMOUNT:%2d, POOLING_TYPE:%2d, no_changes:%d'%(_stat_param['offspring_new'], \ _stat_param['ADD'], _stat_param['REMOVE'], _stat_param['ALTER'], _stat_param['RESNET_OUT_CHANNEL'], _stat_param['RESNET_AMOUNT'], _stat_param['DENSENET_AMOUNT'], _stat_param['POOLING_TYPE'], _stat_param['offspring_from_parent']))
def test_individual(params=None, indi_no=2): if params is None: from utils import StatusUpdateTool params = StatusUpdateTool.get_init_params() ind = Individual(params, indi_no) ind.initialize() print(ind) print(ind.uuid())
def test_population(params=None, gen_no=0): if params is None: from utils import StatusUpdateTool params = StatusUpdateTool.get_init_params() gen_no = 20 pop = Population(params, gen_no) pop.initialize() print(pop)
def do_add_unit_mutation(self, indi): self.log.info('Do the ADD mutation for indi:%s'%(indi.id)) """ choose one position to add one unit, adding one conv or pooling unit is determined by a probability of 0.5. However, if the maximal number of pooling units have been added into the current individual, only conv unit will be add here """ # determine the position where a unit would be added mutation_position = int(np.floor(np.random.random()*len(indi.units))) self.log.info('Mutation position occurs at %d'%(mutation_position)) # determine the unit type for adding u_ = random.random() type_ = 1 if u_ < 0.5 else 2 self.log.info('A %s unit would be added due to the probability of %.2f'%('CONV' if type_ ==1 else 'POOLING', u_)) if type_ == 2: num_exist_pool_units = 0 for unit in indi.units: if unit.type == 2: num_exist_pool_units +=1 if num_exist_pool_units > StatusUpdateTool.get_pool_limit()[1]-1: type_ = 1 self.log.info('The added unit is changed to CONV because the existing number of POOLING exceeds %d, limit size:%d'%(num_exist_pool_units, StatusUpdateTool.get_pool_limit()[1])) #do the details if type_ == 2: add_unit = indi.init_a_pool(mutation_position+1, _max_or_avg=None) else: for i in range(mutation_position, -1, -1): if indi.units[i].type == 1: _in_channel = indi.units[i].out_channel break add_unit = indi.init_a_conv(mutation_position+1, _in_channel=_in_channel, _out_channel=None) for i in range(mutation_position+1, len(indi.units)): if indi.units[i].type == 1: indi.units[i].in_channel = add_unit.out_channel break new_unit_list = [] # add to the new list and update the number for i in range(mutation_position+1): new_unit_list.append(indi.units[i]) new_unit_list.append(add_unit) for i in range(mutation_position+1, len(indi.units)): unit = indi.units[i] unit.number += 1 new_unit_list.append(unit) indi.number_id += 1 indi.units = new_unit_list indi.reset_acc()
def do_modify_conv_mutation(self, indi): self.log.info('Do the CHANNEL mutation for indi:%s'%(indi.id)) conv_index_list = [] for i, unit in enumerate(indi.units): if unit.type == 1: conv_index_list.append(i) if len(conv_index_list) == 0: self.log.warn('No CONV unit exist in current individual, no mutation occurs') else: selected_index = int(np.floor(np.random.rand()*len(conv_index_list))) self.log.info('Mutation position %d'%(conv_index_list[selected_index])) channel_list = StatusUpdateTool().get_output_channel() index_ = int(np.floor(np.random.random()*len(channel_list))) if indi.units[conv_index_list[selected_index]].in_channel != channel_list[index_]: indi.reset_acc() if selected_index > 0: self.log.info('Unit at %d changes its input channel from %d to %d'%(conv_index_list[selected_index], indi.units[conv_index_list[selected_index]].in_channel, channel_list[index_])) indi.units[conv_index_list[selected_index]].in_channel = channel_list[index_] self.log.info('Due to above, the unit at %d should change its output channel from %d to %d'%(conv_index_list[selected_index-1], indi.units[conv_index_list[selected_index-1]].out_channel, channel_list[index_])) indi.units[conv_index_list[selected_index-1]].out_channel = channel_list[index_] else: self.log.warn('Mutation position is 0, the input channel should not be changed') else: self.log.info('Unit at %d changes its input channel from %d to %d'%(conv_index_list[selected_index], indi.units[conv_index_list[selected_index]].in_channel, channel_list[index_])) index_ = int(np.floor(np.random.random()*len(channel_list))) if indi.units[conv_index_list[selected_index]].out_channel != channel_list[index_]: indi.reset_acc() self.log.info('Unit at %d changes its out channel from %d to %d'%(conv_index_list[selected_index], indi.units[conv_index_list[selected_index]].out_channel, channel_list[index_])) indi.units[conv_index_list[selected_index]].out_channel = channel_list[index_] if selected_index < len(conv_index_list)-1: self.log.info('Due to above, the unit at %d should change its input channel from %d to %d'%(conv_index_list[selected_index+1], indi.units[conv_index_list[selected_index+1]].in_channel, channel_list[index_])) indi.units[conv_index_list[selected_index+1]].in_channel = channel_list[index_] else: self.log.info('Unit at %d is the last unit in the individual, therefore no need to change the input channel of the next unit') else: self.log.info('Unit at %d changes its out channel from %d to %d'%(conv_index_list[selected_index], indi.units[conv_index_list[selected_index]].out_channel, channel_list[index_]))
def process(self): total_epoch = StatusUpdateTool.get_epoch_size() for p in range(total_epoch): self.train(p) self.test(total_epoch) return self.best_acc
def __init__(self, individuals, prob_, _log): self.individuals = individuals self.prob = prob_ self.log = _log self.pool_limit = StatusUpdateTool.get_pool_limit()[1]
def do_add_unit_mutation(self, indi): self.log.info('Do the ADD mutation for indi:%s'%(indi.id)) """ choose one position to add one unit, adding one resnet/densenet or pooling unit is determined by a probability of 1/3. However, if the maximal number of pooling units have been added into the current individual, only resnet/densenet unit will be add here """ # determine the position where a unit would be added mutation_position = int(np.floor(np.random.random()*len(indi.units))) self.log.info('Mutation position occurs at %d'%(mutation_position)) # determine the unit type for adding u_ = random.random() if u_ < 0.333: type_ = 1 elif u_ < 0.666: type_ = 2 else: type_ = 3 type_string_list = ['RESNET', 'POOLING', 'DENSENET'] self.log.info('A %s unit would be added due to the probability of %.2f'%(type_string_list[type_-1], u_)) if type_ == 2: num_exist_pool_units = 0 for unit in indi.units: if unit.type == 2: num_exist_pool_units +=1 if num_exist_pool_units > StatusUpdateTool.get_pool_limit()[1]-1: u_ = random.random() type_ = 1 if u_ < 0.5 else 3 self.log.info('The added unit is changed to %s because the existing number of POOLING exceeds %d, limit size:%d'%('RESNET' if type_ == 1 else 'DENSENET', num_exist_pool_units, StatusUpdateTool.get_pool_limit()[1])) #do the details if type_ == 2: add_unit = indi.init_a_pool(mutation_position+1, _max_or_avg=None) else: for i in range(mutation_position, -1, -1): if indi.units[i].type == 1 or indi.units[i].type == 3: _in_channel = indi.units[i].out_channel break if type_ == 1: add_unit = indi.init_a_resnet(mutation_position+1, _amount=None, _in_channel=_in_channel, _out_channel=None) if type_ == 3: add_unit = indi.init_a_densenet(mutation_position+1, _amount=None, _k=None, _max_input_channel=None, _in_channel=_in_channel) keep_out_channel = add_unit.out_channel for i in range(mutation_position+1, len(indi.units)): if indi.units[i].type == 1 or indi.units[i].type == 3 : self.log.info('Due to the above mutation, unit at %d changes its input channel from %d to %d'%(i, indi.units[i].in_channel, keep_out_channel)) indi.units[i].in_channel = keep_out_channel if indi.units[i].type == 1: break elif indi.units[i].type == 3: estimated_out_channel = indi.units[i].k*indi.units[i].amount + indi.units[i].in_channel if estimated_out_channel > indi.units[i].out_channel: break else: self.log.info('Due to the above mutation, unit at %d changes its output channel from %d to %d'%(i, indi.units[i].out_channel, estimated_out_channel)) indi.units[i].out_channel = estimated_out_channel keep_out_channel = estimated_out_channel new_unit_list = [] # add to the new list and update the number for i in range(mutation_position+1): new_unit_list.append(indi.units[i]) new_unit_list.append(add_unit) for i in range(mutation_position+1, len(indi.units)): unit = indi.units[i] unit.number += 1 new_unit_list.append(unit) indi.number_id += 1 indi.units = new_unit_list indi.reset_acc()
def do_crossover(self): _stat_param = {'offspring_new':0, 'offspring_from_parent':0} new_offspring_list = [] for _ in range(len(self.individuals)//2): ind1, ind2 = self._choose_two_diff_parents() parent1, parent2 = copy.deepcopy(self.individuals[ind1]), copy.deepcopy(self.individuals[ind2]) p_ = random.random() if p_ < self.prob: _stat_param['offspring_new'] += 2 """ exchange their units from these parent individuals, the exchanged units must satisfy --- the number of pooling layer should not be more than the predefined setting --- if their is no changing after this crossover, keep the original acc -- a mutation should be given [to do---] """ first_begin_is_pool, second_begin_is_pool = True, True while first_begin_is_pool is True or second_begin_is_pool is True: pos1, pos2, pool_len1, pool_len2 = self._calculate_pool_numbers(parent1, parent2) try_count = 1 while pool_len1 > self.pool_limit or pool_len2 > self.pool_limit: pos1, pos2, pool_len1, pool_len2 = self._calculate_pool_numbers(parent1, parent2) try_count += 1 self.log.warn('The %d-th try to find the position for do crossover'%(try_count)) self.log.info('Position %d for %s, positions %d for %s'%(pos1, parent1.id, pos2, parent2.id)) unit_list1, unit_list2 = [], [] for i in range(0, pos1): unit_list1.append(parent1.units[i]) for i in range(pos2, len(parent2.units)): unit_list1.append(parent2.units[i]) for i in range(0, pos2): unit_list2.append(parent2.units[i]) for i in range(pos1, len(parent1.units)): unit_list2.append(parent1.units[i]) first_begin_is_pool = True if unit_list1[0].type == 2 else False second_begin_is_pool = True if unit_list2[0].type == 2 else False if first_begin_is_pool is True: self.log.warn('Crossovered individual#1 starts with a pooling layer, redo...') if second_begin_is_pool is True: self.log.warn('Crossovered individual#2 starts with a pooling layer, redo...') # reorder the number of each unit based on its order in the list for i, unit in enumerate(unit_list1): unit.number = i for i, unit in enumerate(unit_list2): unit.number = i # re-adjust the in_channel of the next layer last_output_from_list1 = 0 if pos1 == 0: last_output_from_list1 = StatusUpdateTool.get_input_channel() j = 0 i = -1 else: for i in range(pos1-1, -1, -1): if unit_list1[i].type == 1 or unit_list1[i].type == 3: last_output_from_list1 = unit_list1[i].out_channel break keep_out_channel = last_output_from_list1 for j in range(pos1, len(unit_list1)): if unit_list1[j].type == 1 or unit_list1[j].type == 3: self.log.info('Change the input channel of unit at %d to %d that is the output channel of unit at %d in %s'%(j, keep_out_channel, i, parent1.id)) unit_list1[j].in_channel = keep_out_channel if unit_list1[j].type == 1: break elif unit_list1[j].type == 3: estimated_out_channel = unit_list1[j].k*unit_list1[j].amount + unit_list1[j].in_channel if estimated_out_channel > unit_list1[j].out_channel: break; else: self.log.info('Due to the above change, unit at %d changes its output channel from %d to %d'%(j, unit_list1[j].out_channel, estimated_out_channel)) unit_list1[j].out_channel = estimated_out_channel keep_out_channel = estimated_out_channel last_output_from_list2 = 0 if pos2 == 0: last_output_from_list2 = StatusUpdateTool.get_input_channel() j = 0 i = -1 else: for i in range(pos2-1, -1, -1): if unit_list2[i].type == 1 or unit_list2[i].type == 3: last_output_from_list2 = unit_list2[i].out_channel break keep_out_channel = last_output_from_list2 for j in range(pos2, len(unit_list2)): if unit_list2[j].type == 1 or unit_list2[j].type == 3: self.log.info('Change the input channel of unit at %d to %d that is the output channel of unit at %d in %s'%(j, keep_out_channel, i, parent2.id)) unit_list2[j].in_channel = keep_out_channel if unit_list2[j].type == 1: break elif unit_list2[j].type == 3: estimated_out_channel = unit_list2[j].k*unit_list2[j].amount + unit_list2[j].in_channel if estimated_out_channel > unit_list2[j].out_channel: break; else: self.log.info('Due to the above change, unit at %d changes its output channel from %d to %d'%(j, unit_list2[j].out_channel, estimated_out_channel)) unit_list2[j].out_channel = estimated_out_channel keep_out_channel = estimated_out_channel parent1.units = unit_list1 parent2.units = unit_list2 offspring1, offspring2 = parent1, parent2 offspring1.reset_acc() offspring2.reset_acc() new_offspring_list.append(offspring1) new_offspring_list.append(offspring2) else: _stat_param['offspring_from_parent'] += 2 new_offspring_list.append(parent1) new_offspring_list.append(parent2) self.log.info('CROSSOVER-%d offspring are generated, new:%d, others:%d'%(len(new_offspring_list), _stat_param['offspring_new'],_stat_param['offspring_from_parent'])) return new_offspring_list
def initialize_population(self): StatusUpdateTool.begin_evolution() pops = Population(params, 0) pops.initialize() self.pops = pops Utils.save_population_at_begin(str(pops), 0)
self.fitness_evaluate() Log.info('EVOLVE[%d-gen]-Finish the evaluation' % (gen_no)) gen_no += 1 for curr_gen in range(gen_no, max_gen): self.params['gen_no'] = curr_gen # step 3 # .1 交叉变异 .2 估计适应度 .3 筛选 Log.info('EVOLVE[%d-gen]-Begin to crossover and mutation' % (curr_gen)) self.crossover_and_mutation() Log.info('EVOLVE[%d-gen]-Finish crossover and mutation' % (curr_gen)) Log.info('EVOLVE[%d-gen]-Begin to evaluate the fitness' % (curr_gen)) self.fitness_evaluate() Log.info('EVOLVE[%d-gen]-Finish the evaluation' % (curr_gen)) self.environment_selection() Log.info('EVOLVE[%d-gen]-Finish the environment selection' % (curr_gen)) StatusUpdateTool.end_evolution() if __name__ == '__main__': params = StatusUpdateTool.get_init_params() evoCNN = EvolveCNN(params) evoCNN.do_work(max_gen=5)