Paddle-NEAT——飞桨进化神经网络组件
目录
Paddle-NEAT——飞桨进化神经网络组件写在前面:NEAT 简介基因组的表示基因组的变异节点变异连接变异基因组的交叉最后稍微介绍一下 NEAT 的两种改进形式:HyperNEATAdaptive HyperNEAT运行Paddle-NEAT安装继续来立我们的棍子吧运行一下当然走一下迷宫也是可以滴运行一下写在前面:
最近自己写了个把 neat-python 和 paddlepaddle 深度学习框架相结合的套件,取名叫 Paddle-NEAT。链接会在下面的安装方式处给出。
该套件涉及到了机器学习的另一个方向——遗传算法与进化策略。但本文的重点是介绍其中的一个分支——NEAT(NeuroEvolution of Augmenting Topologies)
遗传算法与进化策略可以跳脱出梯度的限制(因为不以梯度作为更新的依据),在高并行的计算条件下,说不定会有超越深度学习的效果。
噢对了,现在已经有将遗传算法与 NAS 相结合的文章了——Genetic CNN。
注意:本文的代码环境基于 Jupyter Notebook
NEAT 简介
遗传算法是个通用的框架,因此需要根据具体的问题来定义遗传算法
整体框架可分为三部分:交叉、变异与适应度
我们的 NEAT 将神经元及其连接定义成基因组
基因组的表示
从上图可以看出,节点变量分为节点变量与连接变量两种。其中节点变量包括了节点属性等变量,连接变量则包括了输入节点、输出节点、权重与连接是否可行等属性。
通过将节点与连接各列成一张链表,网络计算时查询即可得到输出。
基因组的变异
同样的,对应节点与连接两种基因组组成,变异类型也有结点变异和链接变异两种
节点变异
在连接列表中随机选择一个连接,然后中间加入一个节点
连接变异
随机选择两个不同的节点,增加一段连接
基因组的交叉
我们的基因组如何交叉呢?
首先,我们要把父母双方的基因先按顺序铺开对齐,如果是双方都有的连接id(称为 innovation),那就随机选择一个;
如果有不匹配的基因,那么继承具有更好 fitness 的一方;
如果双方的 fitness 相同,那就随便选了。
最后稍微介绍一下 NEAT 的两种改进形式:
HyperNEAT
HyperNEAT是对NEAT的扩展,它用一个单独的网络(称为CPPN,用于合成图案生成网络)间接地编码网络(称为衬底)的权重
Adaptive HyperNEAT
Adaptive HyperNEAT是HyperNEAT的一个扩展,它间接地对初始权值和权值的更新规则进行编码,以便在网络的“生命周期”内进行一些学习
运行Paddle-NEAT
安装
!pip install git+/AgentMaker/Paddle-NEAT.git!pip install neat-python
本套件底层仍然是 neat-python,但某些算子可以借由 paddle2.0 实现加速
网络配置与节点信息参照下面的 neat-python 中的介绍
neat-python 链接:
/CodeReclaimers/neat-python
继续来立我们的棍子吧
%%writefile cart_pole.pyimport osimport clickimport gymimport neatfrom paddle_neat.multi_env_eval import MultiEnvEvaluatorfrom paddle_neat.neat_reporter import LogReporterfrom paddle_neat.recurrent_net import RecurrentNetmax_env_steps = 200# 创建环境def make_env():return gym.make("CartPole-v0")# 创建网络def make_net(genome, config, bs):return RecurrentNet.create(genome, config, bs)# 定义输出层def activate_net(net, states):outputs = net.activate(states).numpy()return outputs[:, 0] > 0.5@mand()@click.option("--n_generations", type=int, default=100)def run(n_generations):config_path = os.path.join(os.path.dirname(__file__), "neat_cart_pole.cfg")config = neat.Config(neat.DefaultGenome,neat.DefaultReproduction,neat.DefaultSpeciesSet,neat.DefaultStagnation,config_path,)evaluator = MultiEnvEvaluator(make_net, activate_net, make_env=make_env, max_env_steps=max_env_steps)def eval_genomes(genomes, config):for _, genome in genomes:genome.fitness = evaluator.eval_genome(genome, config)pop = neat.Population(config)stats = neat.StatisticsReporter()pop.add_reporter(stats)reporter = neat.StdOutReporter(True)pop.add_reporter(reporter)logger = LogReporter("neat.log", evaluator.eval_genome)pop.add_reporter(logger)pop.run(eval_genomes, n_generations)if __name__ == "__main__":run()
运行一下
!python cart_pole.py
当然走一下迷宫也是可以滴
%%writefile maze.pyimport multiprocessingimport osimport clickimport neatimport paddleimport numpy as npimport sys sys.path.append("..") from paddle_neat import t_mazefrom paddle_neat.activations import tanh_activationfrom paddle_neat.adaptive_linear_net import AdaptiveLinearNetfrom paddle_neat.multi_env_eval import MultiEnvEvaluatorfrom paddle_neat.neat_reporter import LogReporterbatch_size = 4DEBUG = Truedef make_net(genome, config, _batch_size):input_coords = [[-1.0, 0.0], [0.0, 0.0], [1.0, 0.0], [0.0, -1.0]]output_coords = [[-1.0, 0.0], [0.0, 0.0], [1.0, 0.0]]return AdaptiveLinearNet.create(genome,config,input_coords=input_coords,output_coords=output_coords,weight_threshold=0.4,batch_size=batch_size,activation=tanh_activation,output_activation=tanh_activation,)def activate_net(net, states, debug=False, step_num=0):if debug and step_num == 1:print("\n" + "=" * 20 + " DEBUG " + "=" * 20)print(net.delta_w_node)print("W init: ", net.input_to_output.numpy()[0])outputs = net.activate(states).numpy()if debug and (step_num - 1) % 100 == 0:print("\nStep {}".format(step_num - 1))print("Outputs: ", outputs[0])print("Delta W: ", net.delta_w.numpy()[0])print("W: ", net.input_to_output.numpy()[0])return np.argmax(outputs, axis=1)@mand()@click.option("--n_generations", type=int, default=10000)@click.option("--n_processes", type=int, default=1)def run(n_generations, n_processes):config_path = os.path.join(os.path.dirname(__file__), "neat_maze.cfg")config = neat.Config(neat.DefaultGenome,neat.DefaultReproduction,neat.DefaultSpeciesSet,neat.DefaultStagnation,config_path,)envs = [t_maze.TMazeEnv(init_reward_side=i, n_trials=100) for i in [1, 0, 1, 0]]evaluator = MultiEnvEvaluator(make_net, activate_net, envs=envs, batch_size=batch_size, max_env_steps=1000)if n_processes > 1:pool = multiprocessing.Pool(processes=n_processes)def eval_genomes(genomes, config):fitnesses = pool.starmap(evaluator.eval_genome, ((genome, config) for _, genome in genomes))for (_, genome), fitness in zip(genomes, fitnesses):genome.fitness = fitnesselse:def eval_genomes(genomes, config):for i, (_, genome) in enumerate(genomes):try:genome.fitness = evaluator.eval_genome(genome, config, debug=DEBUG and i % 100 == 0)except Exception as e:print(genome)raise epop = neat.Population(config)stats = neat.StatisticsReporter()pop.add_reporter(stats)reporter = neat.StdOutReporter(True)pop.add_reporter(reporter)logger = LogReporter("log.json", evaluator.eval_genome)pop.add_reporter(logger)winner = pop.run(eval_genomes, n_generations)print(winner)final_performance = evaluator.eval_genome(winner, config)print("Final performance: {}".format(final_performance))generations = reporter.generation + 1return generationsif __name__ == "__main__":run()
运行一下
!python maze.py