Esempio n. 1
0
    def build_model(self):
        self.args.share_param = False
        self.with_retrain = True
        self.args.shared_initial_step = 0
        if self.args.search_mode == "macro":
            # generate model description in macro way (generate entire network description)
            from graphnas.search_space import MacroSearchSpace
            search_space_cls = MacroSearchSpace()
            self.search_space = search_space_cls.get_search_space()
            # layers_of_child_model is 2
            self.action_list = search_space_cls.generate_action_list(
                self.args.layers_of_child_model)
            # build RNN controller
            from graphnas.graphnas_controller import SimpleNASController
            self.controller = SimpleNASController(
                self.args,
                action_list=self.action_list,
                search_space=self.search_space,
                cuda=self.args.cuda)

            if self.args.dataset in ["cora", "citeseer", "pubmed"]:
                # implements based on dgl
                self.submodel_manager = CitationGNNManager(self.args)
            if self.args.dataset in ["Cora", "Citeseer", "Pubmed"]:
                # implements based on pyg
                self.submodel_manager = GeoCitationManager(self.args)

        if self.args.search_mode == "micro":
            self.args.format = "micro"
            self.args.predict_hyper = True
            if not hasattr(self.args, "num_of_cell"):
                self.args.num_of_cell = 2
            from graphnas_variants.micro_graphnas.micro_search_space import IncrementSearchSpace
            search_space_cls = IncrementSearchSpace()
            search_space = search_space_cls.get_search_space()
            from graphnas.graphnas_controller import SimpleNASController
            from graphnas_variants.micro_graphnas.micro_model_manager import MicroCitationManager
            self.submodel_manager = MicroCitationManager(self.args)
            self.search_space = search_space
            action_list = search_space_cls.generate_action_list(
                cell=self.args.num_of_cell)
            if hasattr(self.args, "predict_hyper") and self.args.predict_hyper:
                self.action_list = action_list + [
                    "learning_rate", "dropout", "weight_decay", "hidden_unit"
                ]
            else:
                self.action_list = action_list
            self.controller = SimpleNASController(
                self.args,
                action_list=self.action_list,
                search_space=self.search_space,
                cuda=self.args.cuda)
            if self.cuda:
                self.controller.cuda()

        if self.cuda:
            self.controller.cuda()
Esempio n. 2
0
 def build_model(self):
     if self.args.search_mode == "macro":
         search_space_cls = MacroSearchSpace()
         self.search_space = search_space_cls.get_search_space()
         self.action_list = search_space_cls.generate_action_list(
             self.args.layers_of_child_model)
         # build RNN controller
         if self.args.dataset in ["cora", "citeseer", "pubmed"]:
             # implements based on dgl
             self.submodel_manager = CitationGNNManager(self.args)
         if self.args.dataset in ["Cora", "Citeseer", "Pubmed",
                                  "CS", "Physics", "Computers", "Photo"]:
             # implements based on pyg
             self.submodel_manager = GeoCitationManager(self.args)
     print("Search space:")
     print(self.search_space)
     print("Generated Action List: ")
     print(self.action_list)
Esempio n. 3
0
 def build_model(self):
     self.args.share_param = False
     self.with_retrain = True
     self.args.shared_initial_step = 0
     if self.args.search_mode == "macro":
         # generate model description in macro way (generate entire network description)
         from graphnas.search_space import MacroSearchSpace
         search_space_cls = MacroSearchSpace()
         self.search_space = search_space_cls.get_search_space()
         self.action_list = search_space_cls.generate_action_list(self.args.layers_of_child_model)
         # build RNN controller
         from graphnas.graphnas_controller import SimpleNASController
         self.controller = SimpleNASController(self.args, action_list=self.action_list,
                                               search_space=self.search_space,
                                               cuda=self.args.cuda)
         if self.args.dataset in ["Cora", "Citeseer", "Pubmed"]:
             # implements based on pyg
             self.submodel_manager = GeoCitationManager(self.args)
     if self.cuda:
         self.controller.cuda()
Esempio n. 4
0
def eval(chromosome):
    args = build_args()

    dataset_list = ["Citeseer", "Pubmed", "cora"]
    base_list = [
        "pyg",
        "pyg",
        "dgl",
    ]
    for dataset, actions, base in zip(dataset_list, chromosome, base_list):
        # if dataset == "cora":
        #     continue
        args.dataset = dataset
        if base == "dgl":
            manager = CitationGNNManager(args)
        else:
            manager = GeoCitationManager(args)
        val_acc, test_acc = manager.evaluate(actions)
        print(test_acc)
        print("_" * 80)
        return test_acc
Esempio n. 5
0
 def build_model(self):
     if self.args.search_mode == "macro":
         # generate model description in macro way
         # (generate entire network description)
         search_space_cls = MacroSearchSpace()
         self.search_space = search_space_cls.get_search_space()
         self.action_list = search_space_cls.generate_action_list(
             self.args.layers_of_child_model)
         # build RNN controller
         if self.args.dataset in ["cora", "citeseer", "pubmed"]:
             # implements based on dgl
             self.submodel_manager = CitationGNNManager(self.args)
         if self.args.dataset in [
                 "Cora", "Citeseer", "Pubmed", "CS", "Physics", "Computers",
                 "Photo"
         ]:
             # implements based on pyg
             self.submodel_manager = GeoCitationManager(self.args)
     if self.args.search_mode == "micro":
         self.args.format = "micro"
         self.args.predict_hyper = True
         if not hasattr(self.args, "num_of_cell"):
             self.args.num_of_cell = 2
         search_space_cls = IncrementSearchSpace()
         search_space = search_space_cls.get_search_space()
         self.submodel_manager = MicroCitationManager(self.args)
         self.search_space = search_space
         action_list = search_space_cls.generate_action_list(
             cell=self.args.num_of_cell)
         if hasattr(self.args, "predict_hyper") and self.args.predict_hyper:
             self.action_list = action_list + [
                 "learning_rate", "dropout", "weight_decay", "hidden_unit"
             ]
         else:
             self.action_list = action_list
     print("Search space:")
     print(self.search_space)
     print("Generated Action List: ")
     print(self.action_list)
Esempio n. 6
0
if __name__ == "__main__":
    args = build_args()

    gnn_list = [
        ['gat', 'sum', 'linear', 4, 128, 'linear', 'sum', 'elu', 8, 6],
        ['gcn', 'sum', 'tanh', 6, 64, 'cos', 'sum', 'tanh', 6, 3],
        ['const', 'sum', 'relu6', 2, 128, 'gat', 'sum', 'linear', 2, 7],
    ]
    dataset_list = ["Citeseer", "Pubmed", "cora"]
    base_list = [
        "pyg",
        "pyg",
        "dgl",
    ]
    for dataset, actions, base in zip(dataset_list, gnn_list, base_list):
        # if dataset == "cora":
        #     continue
        args.dataset = dataset
        if base == "dgl":
            manager = CitationGNNManager(args)
        else:
            manager = GeoCitationManager(args)
        test_scores_list = []
        for i in range(100):
            val_acc, test_acc = manager.evaluate(actions)
            test_scores_list.append(test_acc)
        print("_" * 80)
        test_scores_list.sort()
        print(dataset, np.mean(test_scores_list[5:-5]),
              np.std(test_scores_list[5:-5]))
Esempio n. 7
0
class Trainer(object):
    """Manage the training process"""

    def __init__(self, args):
        """
        Constructor for training algorithm.
        Build sub-model manager and controller.
        Build optimizer and cross entropy loss for controller.

        Args:
            args: From command line, picked up by `argparse`.
        """
        self.args = args
        self.build_model()  # build controller and sub-model

    def _construct_action(self, actions):
        structure_list = []
        for single_action in actions:
            structure = []
            print('single_chromosome:', single_action)
            for action, action_name in zip(single_action, self.action_list):
                predicted_actions = self.search_space[action_name][action]
                structure.append(predicted_actions)
            structure_list.append(structure)
        return structure_list

    def build_model(self):
        if self.args.search_mode == "macro":
            search_space_cls = MacroSearchSpace()
            self.search_space = search_space_cls.get_search_space()
            self.action_list = search_space_cls.generate_action_list(
                self.args.layers_of_child_model)
            # build RNN controller
            if self.args.dataset in ["cora", "citeseer", "pubmed"]:
                # implements based on dgl
                self.submodel_manager = CitationGNNManager(self.args)
            if self.args.dataset in ["Cora", "Citeseer", "Pubmed",
                                     "CS", "Physics", "Computers", "Photo"]:
                # implements based on pyg
                self.submodel_manager = GeoCitationManager(self.args)
        print("Search space:")
        print(self.search_space)
        print("Generated Action List: ")
        print(self.action_list)

    # 基于搜索空间,对结构空间进行数字编码,并使用随机初始化获得个体组建种群
    def _generate_random_individual(self):
        ind = []
        # 每个action operator 使用数字编码,对每个action 随机采样
        for action in self.action_list:
            ind.append(np.random.randint(0,
                                         len(self.search_space[action])))
        return ind

    # 基于个体编码进行解码还原成GNN结构列表,为GNN建模做准备
    def _construct_action(self, actions):
        structure_list = []
        for single_action in actions:
            structure = []
            print('single_chromosome:', single_action)
            for action, action_name in zip(single_action, self.action_list):
                predicted_actions = self.search_space[action_name][action]
                structure.append(predicted_actions)
            structure_list.append(structure)
        return structure_list

    def form_gnn_info(self, gnn):
        if self.args.search_mode == "micro":
            actual_action = {}
            if self.args.predict_hyper:
                actual_action["action"] = gnn[:-4]
                actual_action["hyper_param"] = gnn[-4:]
            else:
                actual_action["action"] = gnn
                actual_action["hyper_param"] = [0.005, 0.8, 5e-5, 128]
            return actual_action
        return gnn

    def derive_from_history(self):
        with open(self.args.dataset + self.args.submanager_log_file) as f:
            lines = f.readlines()
        results = []
        for line in lines:
            actions = line[:line.index(";")]
            val_score = line.split(";")[-1]
            results.append((actions, val_score))
        results.sort(key=lambda x: x[-1], reverse=True)
        best_structure = ""
        best_score = 0
        for actions in results[:5]:
            actions = eval(actions[0])
            np.random.seed(123)
            torch.manual_seed(123)
            torch.cuda.manual_seed_all(123)
            val_scores_list = []
            for i in range(20):
                val_acc, test_acc = self.submodel_manager.evaluate(actions)
                val_scores_list.append(val_acc)

            tmp_score = np.mean(val_scores_list)
            if tmp_score > best_score:
                best_score = tmp_score
                best_structure = actions

        print("best structure:" + str(best_structure))
        # train from scratch to get the final score
        np.random.seed(123)
        torch.manual_seed(123)
        torch.cuda.manual_seed_all(123)
        test_scores_list = []
        for i in range(100):
            # manager.shuffle_data()
            val_acc, test_acc = self.submodel_manager.evaluate(best_structure)
            test_scores_list.append(test_acc)
        print(f"best results: {best_structure}: {np.mean(test_scores_list):.8f} +/- {np.std(test_scores_list)}")
        return best_structure
Esempio n. 8
0
# sys.path.extend(['/GraphNAS'])
from graphnas_variants.macro_graphnas.pyg.pyg_gnn_model_manager import GeoCitationManager
from eval_scripts.semi.eval_designed_gnn import build_args
torch.manual_seed(123)
torch.cuda.manual_seed_all(123)

if __name__ == "__main__":
    args = build_args()

    gnn_list = [[
        'generalized_linear', 'sum', 'leaky_relu', 6, 16, 'gat', 'sum',
        'leaky_relu', 1, 7
    ], ['gat_sym', 'sum', 'linear', 4, 256, 'cos', 'sum', 'softplus', 2, 6],
                [
                    'const', 'sum', 'relu', 4, 32, 'generalized_linear', 'sum',
                    'leaky_relu', 8, 3
                ]]
    dataset_list = ["Cora", "Citeseer", "Pubmed"]
    for dataset, actions in zip(dataset_list, gnn_list):
        args.dataset = dataset
        manager = GeoCitationManager(args)
        test_scores_list = []
        for i in range(100):
            val_acc, test_acc = manager.evaluate(actions)
            test_scores_list.append(test_acc)
        print("_" * 80)
        test_scores_list.sort()
        print(dataset, np.mean(test_scores_list[5:-5]),
              np.std(test_scores_list[5:-5]))
Esempio n. 9
0
class RL_Trainer(object):

    def __init__(self, args):
        self.args = args
        self.controller_step = 0  # counter for controller
        self.cuda = args.cuda
        self.epoch = 0
        self.start_epoch = 0
        self.max_length = self.args.shared_rnn_max_length

        self.with_retrain = False
        self.submodel_manager = None
        self.controller = None
        self.build_model()  # build controller and sub-model
        self.RL_train_time = []
        self.RL_search_time = []
        self.RL_train_acc = []
        self.RL_search_acc = []


        controller_optimizer = _get_optimizer(self.args.controller_optim)
        self.controller_optim = controller_optimizer(self.controller.parameters(), lr=self.args.controller_lr)

        if self.args.mode == "derive":
            self.load_model()

    def build_model(self):
        self.args.share_param = False
        self.with_retrain = True
        self.args.shared_initial_step = 0
        if self.args.search_mode == "macro":
            # generate model description in macro way (generate entire network description)
            from graphnas.search_space import MacroSearchSpace
            search_space_cls = MacroSearchSpace()
            self.search_space = search_space_cls.get_search_space()
            self.action_list = search_space_cls.generate_action_list(self.args.layers_of_child_model)
            # build RNN controller
            from graphnas.graphnas_controller import SimpleNASController
            self.controller = SimpleNASController(self.args, action_list=self.action_list,
                                                  search_space=self.search_space,
                                                  cuda=self.args.cuda)
            if self.args.dataset in ["Cora", "Citeseer", "Pubmed"]:
                # implements based on pyg
                self.submodel_manager = GeoCitationManager(self.args)
        if self.cuda:
            self.controller.cuda()

    def form_gnn_info(self, gnn):
        if self.args.search_mode == "micro":
            actual_action = {}
            if self.args.predict_hyper:
                actual_action["action"] = gnn[:-4]
                actual_action["hyper_param"] = gnn[-4:]
            else:
                actual_action["action"] = gnn
                actual_action["hyper_param"] = [0.005, 0.8, 5e-5, 128]
            return actual_action
        return gnn

    def train(self, action_list):
        model_path = "/home/jerry/experiment/RL_nas/graphnas/Citeseer"
        # Training the controller
        if not os.listdir(model_path):# 判断保存controler模型的文件夹是否为空,为空返回False,反之为Ture
            self.train_controller()
            print("*" * 35, "using controller search the initialize population", "*" * 35)
            populations, accuracies = self.derive(self.args.population_size, action_list)
            print("*" * 35, "the search DONE", "*" * 35)
            self.save_model()
        else:
            self.load_model() # 每次加载step序号最大controler模型search
            print("*" * 35, "using controller search the initialize population", "*" * 35)
            populations, accuracies = self.derive(self.args.population_size, action_list)
            print("*" * 35, "the search DONE", "*" * 35)
        return populations, accuracies

    def derive(self, sample_num, action_list):
        if sample_num is None and self.args.derive_from_history:
            return self.derive_from_history()
        else:
            if sample_num is None:
                sample_num = self.args.derive_num_sample
            gnn_list, _, entropies = self.controller.sample(sample_num, with_details=True)
            accuracies = []

            epoch = 0
            for action in gnn_list:
                once_RL_search_start_time = time.time()

                gnn = self.form_gnn_info(action)
                reward = self.submodel_manager.test_with_param(gnn, format=self.args.format,
                                                               with_retrain=self.with_retrain)
                acc_score = reward[1]
                accuracies.append(acc_score)

                once_RL_search_end_time = time.time()

                print("the", epoch, "epcoh controller train time: ",
                      once_RL_search_end_time - once_RL_search_start_time, 's')

                if epoch == 0:
                    self.RL_search_time.append(once_RL_search_start_time)
                    self.RL_search_time.append(once_RL_search_end_time)
                    self.RL_search_acc.append(acc_score)
                else:
                    self.RL_search_time.append(once_RL_search_end_time)
                    self.RL_search_acc.append(acc_score)

                epoch += 1
            father_path = path_get()[0]
            experiment_data_save("controler_search.txt", self.RL_search_time, self.RL_search_acc)
            print("all RL search time list: ", self.RL_search_time)
            print("all RL search acc list: ", self.RL_search_acc)

            for individual, ind_acc in zip(gnn_list, accuracies):
                print("individual:", individual, " val_score:", ind_acc)
            # gnn_structure 基因编码
            population = []
            for gnn_structure in gnn_list:
                i = 0
                single = []
                for operator, action_name in zip(gnn_structure, action_list):
                    if i == 9:
                        operator = 8
                    i += 1
                    single.append(self.search_space[action_name].index(operator))
                population.append(single)

            return population, accuracies

    def save_model(self):

        torch.save(self.controller.state_dict(), self.controller_path)
        torch.save(self.controller_optim.state_dict(), self.controller_optimizer_path)

        logger.info(f'[*] SAVED: {self.controller_path}')

        epochs, shared_steps, controller_steps = self.get_saved_models_info()

        for epoch in epochs[:-self.args.max_save_num]:
            paths = glob.glob(
                os.path.join(self.args.dataset, f'*_epoch{epoch}_*.pth'))

            for path in paths:
                utils.remove_file(path)

    def load_model(self):
        epochs, shared_steps, controller_steps = self.get_saved_models_info()

        if len(epochs) == 0:
            logger.info(f'[!] No checkpoint found in {self.args.dataset}...')
            return

        self.epoch = self.start_epoch = max(epochs)
        self.controller_step = max(controller_steps)

        self.controller.load_state_dict(
            torch.load(self.controller_path))
        self.controller_optim.load_state_dict(
            torch.load(self.controller_optimizer_path))
        logger.info(f'[*] LOADED: {self.controller_path}')

    def get_reward(self, gnn_list, entropies, hidden):
        """
        Computes the reward of a single sampled model on validation data.
        """
        if not isinstance(entropies, np.ndarray):
            entropies = entropies.data.cpu().numpy()
        if isinstance(gnn_list, dict):
            gnn_list = [gnn_list]
        if isinstance(gnn_list[0], list) or isinstance(gnn_list[0], dict):
            pass
        else:
            gnn_list = [gnn_list]  # when structure_list is one structure

        reward_list = []
        for gnn in gnn_list:
            gnn = self.form_gnn_info(gnn)
            reward = self.submodel_manager.test_with_param(gnn,
                                                      format=self.args.format,
                                                      with_retrain=self.with_retrain)

            if reward is None:  # cuda error hanppened
                reward = 0
            else:
                rewards = reward[0]#奖励计算正确

            reward_list.append(rewards)
            acc_validation = reward[1]

        if self.args.entropy_mode == 'reward':
            rewards = reward_list + self.args.entropy_coeff * entropies
        elif self.args.entropy_mode == 'regularizer':
            rewards = reward_list * np.ones_like(entropies)
        else:
            raise NotImplementedError(f'Unkown entropy mode: {self.args.entropy_mode}')

        return rewards, hidden, acc_validation

    def train_controller(self):
        """
            Train controller to find better structure.
        """
        print("*" * 35, "training controller", "*" * 35)
        model = self.controller
        model.train()

        baseline = None
        adv_history = []
        entropy_history = []
        reward_history = []

        hidden = self.controller.init_hidden(self.args.batch_size)
        total_loss = 0
        for step in range(self.args.controller_max_step):
            #contraller训练一次的时间
            once_controller_train_start_time = time.time()

            # sample graphnas
            structure_list, log_probs, entropies = self.controller.sample(with_details=True)

            # calculate reward
            np_entropies = entropies.data.cpu().numpy()
            results = self.get_reward(structure_list, np_entropies, hidden)
            torch.cuda.empty_cache()

            if results:  # has reward
                rewards, hidden, acc = results
            else:
                continue

            # discount
            if 1 > self.args.discount > 0:
                rewards = discount(rewards, self.args.discount)

            reward_history.extend(rewards)
            entropy_history.extend(np_entropies)

            # moving average baseline
            if baseline is None:
                baseline = rewards
            else:
                decay = self.args.ema_baseline_decay
                baseline = decay * baseline + (1 - decay) * rewards

            adv = rewards - baseline
            history.append(adv)
            adv = scale(adv, scale_value=0.5)
            adv_history.extend(adv)

            adv = utils.get_variable(adv, self.cuda, requires_grad=False)
            # policy loss
            loss = -log_probs * adv
            if self.args.entropy_mode == 'regularizer':
                loss -= self.args.entropy_coeff * entropies

            loss = loss.sum()  # or loss.mean()

            # update
            self.controller_optim.zero_grad()
            loss.backward()

            if self.args.controller_grad_clip > 0:
                torch.nn.utils.clip_grad_norm(model.parameters(),
                                              self.args.controller_grad_clip)
            self.controller_optim.step()

            total_loss += utils.to_item(loss.data)

            self.controller_step += 1
            torch.cuda.empty_cache()
            once_controller_train_end_time = time.time()
            print("the", step, "epcoh controller train time: ",
                  once_controller_train_end_time-once_controller_train_start_time, "s")

            if step == 0:
                self.RL_train_time.append(once_controller_train_start_time)
                self.RL_train_time.append(once_controller_train_end_time)
                self.RL_train_acc.append(acc)
            else:
                self.RL_train_time.append(once_controller_train_end_time)
                self.RL_train_acc.append(acc)
        print("all RL train time list: ", self.RL_train_time)
        print("all RL train acc list: ", self.RL_train_acc)
        print("*" * 35, "training controller over", "*" * 35)
        experiment_data_save("controler_train.txt", self.RL_train_time, self.RL_train_acc)

    def evaluate(self, gnn):
        """
        Evaluate a structure on the validation set.
        """
        self.controller.eval()
        gnn = self.form_gnn_info(gnn)
        results = self.submodel_manager.retrain(gnn, format=self.args.format)
        if results:
            reward, scores = results
        else:
            return
        logger.info(f'eval | {gnn} | reward: {reward:8.2f} | scores: {scores:8.2f}')

    @property
    def model_info_filename(self):
        return f"{self.args.dataset}_{self.args.search_mode}_{self.args.format}_results.txt"

    @property
    def controller_path(self):
        return f'{self.args.dataset}/controller_epoch{self.epoch}_step{self.controller_step}.pth'

    @property
    def controller_optimizer_path(self):
        return f'{self.args.dataset}/controller_epoch{self.epoch}_step{self.controller_step}_optimizer.pth'

    def get_saved_models_info(self):
        paths = glob.glob(os.path.join(self.args.dataset, '*.pth'))
        paths.sort()

        def get_numbers(items, delimiter, idx, replace_word, must_contain=''):
            return list(set([int(
                name.split(delimiter)[idx].replace(replace_word, ''))
                for name in items if must_contain in name]))

        basenames = [os.path.basename(path.rsplit('.', 1)[0]) for path in paths]
        epochs = get_numbers(basenames, '_', 1, 'epoch')
        shared_steps = get_numbers(basenames, '_', 2, 'step', 'shared')
        controller_steps = get_numbers(basenames, '_', 2, 'step', 'controller')

        epochs.sort()
        shared_steps.sort()
        controller_steps.sort()

        return epochs, shared_steps, controller_steps
Esempio n. 10
0
class Evolution_Trainer(object):
    """
    This class implements the Asyncronous Aging Evolution,
    proposed by Real et. al. on:

    Regularized Evolution for Image Classifier Architecture Search

    available on: https://arxiv.org/abs/1802.01548
    """
    def __init__(self, args):
        self.cycle = 0
        self.args = args
        self.random_seed = args.random_seed
        self.population = []
        self.accuracies = []
        self.population_size = args.population_size
        self.population_history = []
        self.accuracies_history = []
        self.sample_size = args.sample_size
        self.cycles = args.cycles
        self.init_time = 0
        self.ev_train_time = []
        self.ev_acc = []
        self.ev_random_initial_time = []
        self.ev_random_acc = []

        self.build_model()

        # self.__initialize_population_Random()
        # 种子初始化初始化由初始化函数实现
        # random初始化种群
        # if self.args.initialize_mode == "random":
        #     # 如果random_population已存在则不再初始化
        #     if not os.path.exists("random_population.txt"):
        #         self.__initialize_population_Random()
        #     else:
        #         print("*" * 35, "random_population DONE", "*" * 35)
    def build_model(self):
        if self.args.search_mode == "macro":
            search_space_cls = MacroSearchSpace()
            self.search_space = search_space_cls.get_search_space()
            self.action_list = search_space_cls.generate_action_list(
                self.args.layers_of_child_model)
            # build RNN controller
            if self.args.dataset in ["cora", "citeseer", "pubmed"]:
                # implements based on dgl
                self.submodel_manager = CitationGNNManager(self.args)
            if self.args.dataset in [
                    "Cora", "Citeseer", "Pubmed", "CS", "Physics", "Computers",
                    "Photo"
            ]:
                # implements based on pyg
                self.submodel_manager = GeoCitationManager(self.args)
        print("Search space:")
        print(self.search_space)
        print("Generated Action List: ")
        print(self.action_list)

    def _generate_random_individual(self):
        ind = []
        # 每个action operator 使用数字编码,对每个action 随机采样
        for action in self.action_list:
            ind.append(np.random.randint(0, len(self.search_space[action])))
        return ind

    def _construct_action(self, actions):
        structure_list = []
        for single_action in actions:
            structure = []
            print('single_chromosome:', single_action)
            for action, action_name in zip(single_action, self.action_list):
                predicted_actions = self.search_space[action_name][action]
                structure.append(predicted_actions)
            structure_list.append(structure)
        return structure_list

    def form_gnn_info(self, gnn):
        if self.args.search_mode == "micro":
            actual_action = {}
            if self.args.predict_hyper:
                actual_action["action"] = gnn[:-4]
                actual_action["hyper_param"] = gnn[-4:]
            else:
                actual_action["action"] = gnn
                actual_action["hyper_param"] = [0.005, 0.8, 5e-5, 128]
            return actual_action
        return gnn

    @ray.method(num_returns=2)
    def initialize_population_Random(self):
        print("\n\n===== Random initialize the populations =====")  # 随机初始化种群
        start_initial_population_time = time.time()

        epoch = 0
        while len(self.population) < self.population_size:

            once_random_initialize_start_time = time.time()
            individual = self._generate_random_individual()
            ind_actions = self._construct_action([individual
                                                  ])  # 将基因编码解码为GNN结构空间list
            gnn = self.form_gnn_info(ind_actions[0])
            _, ind_acc = self.submodel_manager.train(gnn,
                                                     format=self.args.format)
            print("individual:", individual, " val_score:", ind_acc)
            self.accuracies.append(
                ind_acc)  # 将种群中每个个体的acc_scores存入accuraies list
            self.population.append(
                individual)  # 将种群中每个个体的基因存入 populations list

            once_random_initialize_end_time = time.time()

            print(
                "the", epoch, "epcoh random initialize time: ",
                once_random_initialize_end_time -
                once_random_initialize_start_time, 's')
            if epoch == 0:
                self.ev_random_initial_time.append(
                    once_random_initialize_start_time)
                self.ev_random_initial_time.append(
                    once_random_initialize_end_time)
                self.ev_random_acc.append(ind_acc)
            else:
                self.ev_random_initial_time.append(
                    once_random_initialize_end_time)
                self.ev_random_acc.append(ind_acc)
            epoch += 1

        end_initial_pop_time = time.time()
        self.init_time = end_initial_pop_time - start_initial_population_time  # 计算初始化过程所需时间
        print("Time elapsed initializing population: " + str(self.init_time),
              's')
        print("===== Random initialize populations DONE ====")
        print("all random initialize time list: ", self.ev_random_initial_time)
        print("all random initialize population acc list: ",
              self.ev_random_acc)

        self.experiment_data_save(
            self.args.initialize_mode + "_" + self.args.dataset + "_" +
            self.args.evolution_sesd_name + ".txt",
            self.ev_random_initial_time, self.ev_random_acc)
        self.population_save(
            self.args.dataset + "_" + self.args.evolution_sesd_name +
            "_population.txt", self.population, self.accuracies)
        return self.population, self.accuracies

    def derive_from_population(self):

        population = self._construct_action(self.population)
        best_score_index, _ = self._get_best_individual_accuracy(
            self.accuracies)
        best_structure = self.form_gnn_info(population[best_score_index])

        print("[DERIVE] Best Structure:", str(best_structure))

        # train from scratch to get the final score
        np.random.seed(self.random_seed)
        torch.manual_seed(self.random_seed)
        torch.cuda.manual_seed_all(self.random_seed)
        test_scores_list = []

        for i in range(10):  # run 10 times to get Mean and Stddev
            val_acc, test_acc = self.submodel_manager.evaluate(best_structure)
            test_scores_list.append(test_acc)
        print("[DERIVE] Best Results: ", best_structure, ": ",
              np.mean(test_scores_list), "+/-", np.std(test_scores_list))

    def _get_best_individual_accuracy(self, accs):
        max_acc_index = 0
        max_acc = -1
        for index, acc in enumerate(accs):
            if acc > max_acc:
                max_acc = acc
                max_acc_index = index
        return max_acc_index, max_acc

    def experiment_data_save(self, name, time_list, acc_list):
        path = self.path_get()[1]
        with open(path + "/" + name, "w") as f:
            f.write(str(time_list))
            f.write("\n" + str(acc_list))
        print("the ", name, " have written")

    def population_save(self, name, population, accuracies):
        path = self.path_get()[1]
        with open(path + "/" + name, "w") as f:
            f.write(str(population))
            f.write("\n" + str(accuracies))
        print("the ", name, " have written")

    def population_read(self, name):
        path = self.path_get()[1]
        with open(path + "/" + name, "r") as f:
            all_data = f.readlines()
            population = all_data[0][:-1]
            accuracies = all_data[1]
            population = json.loads(population)
            accuracies = json.loads(accuracies)
            return population, accuracies

    def path_get(self):
        # 当前文件目录
        current_path = os.path.abspath('')
        # 当前文件夹父目录
        father_path = os.path.abspath(os.path.dirname(current_path))
        # corpus_path = os.path.join(father_path, corpus)
        return father_path, current_path

    @ray.method(num_returns=4)
    def train(self, history_population, history_validation_accuracy):

        # 传入history_population, history_validation_accuracy参数,为child fitness 做准备

        print("\n\n===== Evolution ====")
        # 从相应的目录中读取遗传种子自己的population与accuracies,进行本次进化操作
        population, accuracies = self.population_read(
            self.args.dataset + "_" + self.args.evolution_sesd_name +
            "_population.txt")

        child_acc_list = []

        print("the original populations:\n", population)
        print("the original accuracies:\n", accuracies)
        print("[POPULATION STATS] Mean/Median/Best: ", np.mean(accuracies),
              np.median(accuracies), np.max(accuracies))
        # 选择 获取parents
        parents_list = self._selection(population, accuracies)
        # 交叉 获取step2 child
        child_list = self._crossover(parents_list)
        # 变异 获取step2 child
        child_list = self._mutation(child_list)

        # 变异结束, 计算child_list中history_population中没有的gnn的acc
        # 已经存在history_population的gnn,直接从history_acc中获取
        # 计算 child_gnn中的gnn的fitness
        for child_gnn in child_list:
            if child_gnn in history_population:
                child_acc = history_validation_accuracy[
                    history_population.index(child_gnn)]
            else:
                child_acc = self._fitness_computation([child_gnn])
            child_acc_list.append(child_acc)

        return child_list, child_acc_list, population, accuracies

    def _fitness_computation(self, child_list):
        """
        在acc计算前,判断history_population中是否包含了child gnn结构,如果包含,不再计算acc
        直接从history_val_acc中获取
        """
        for child in child_list:
            child_actions = self._construct_action([child])
            gnn = self.form_gnn_info(child_actions[0])
            _, child_acc = self.submodel_manager.train(gnn,
                                                       format=self.args.format)
            return child_acc

    def _selection(self, population, accuracies):

        if self.args.selection_mode == "random":
            parent_list = []
            index_list = [index for index in range(len(population))]
            parent_index = np.random.choice(index_list,
                                            self.sample_size,
                                            replace=False)
            for index in parent_index:
                parent_list.append(population[index].copy())

        elif self.args.selection_mode == "wheel":
            print("wheel select")
            # 基于fitness计算采样概率:
            fitness = np.array(accuracies)
            fitness_probility = fitness / sum(fitness)
            fitness_probility = fitness_probility.tolist()
            index_list = [index for index in range(len(fitness))]
            parent_list = []
            # 如果self.sample_size不是偶数,需要处理
            if self.sample_size % 2 != 0:
                self.sample_size += 1
            # 基于fitness概率采样
            parent_index = np.random.choice(index_list,
                                            self.sample_size,
                                            replace=False,
                                            p=fitness_probility)
            for index in parent_index:
                parent_list.append(population[index].copy())

        elif self.args.selection_mode == "wheel_reverse":
            print("wheel select")
            # 基于fitness计算反向采样概率:
            fitness_reverse = 1 - np.array(accuracies)
            fitness_probility_reverse = fitness_reverse / sum(fitness_reverse)
            fitness_probility_reverse = fitness_probility_reverse.tolist()
            index_list = [index for index in range(len(fitness_reverse))]
            parent_list = []
            # 如果self.sample_size不是偶数,需要处理
            if self.sample_size % 2 != 0:
                self.sample_size += 1
            # 基于fitness概率采样
            parent_index = np.random.choice(index_list,
                                            self.sample_size,
                                            replace=False,
                                            p=fitness_probility_reverse)
            for index in parent_index:
                parent_list.append(population[index].copy())

        print("the parent_list:\n", parent_list)

        return parent_list

    def _crossover(self, parents):
        if self.args.crossover_mode == "single_point_crossover":
            print("crossover operator")
            child_list = []
            #单点交叉
            while parents:
                # step1:从parents list中无放回的取出一对父母
                parent_1 = parents.pop()
                parent_2 = parents.pop()
                # step2:测量parent长度,随机确定交叉点:
                crossover_point = np.random.randint(1, len(parent_1))
                # step3:对父母染色体进行交叉得到子代:
                # 交叉操作判断
                corssover_op = np.random.choice(
                    [True, False],
                    1,
                    p=[self.args.crossover_p, 1 - self.args.crossover_p])[0]

                if corssover_op:
                    child_1 = parent_1[:crossover_point] + parent_2[
                        crossover_point:]
                    child_2 = parent_2[:crossover_point] + parent_1[
                        crossover_point:]
                else:
                    child_1 = parent_1
                    child_2 = parent_2

                child_list.append(child_1)
                child_list.append(child_2)
        print("the child_list:\n", child_list)
        return child_list

    def _mutation(self, child_list):
        if self.args.mutation_mode == "single_point_mutation":
            print("mutation operator")
            for index in range(len(child_list)):
                # 对于index的child是否发生变异判断
                mutation_op = np.random.choice(
                    [True, False],
                    1,
                    p=[self.args.mutation_p, 1 - self.args.mutation_p])[0]
                if mutation_op:
                    # 对索引号为Index的child 随机选择可能的变异点
                    position_to_mutate = np.random.randint(
                        len(child_list[index]))
                    sp_list = self.search_space[
                        self.action_list[position_to_mutate]]
                    child_list[index][position_to_mutate] = np.random.randint(
                        0, len(sp_list))
        print("the child_list:\n", child_list)

        return child_list