neat-python是对遗传算法的拓展,结合了神经网络和遗传算法的模型,本文旨在使用不在讲解。
本文利用neat计算x²来进行一个neat的初步使用,通过本文教程可以大致使用neat完成一些基础任务。
安装neat
pip install neat-python
一、配置config文件
neat利用config文件来进行参数配置,具体的配置文件模板可以在官方文档中找到:配置文件模板地址。
1.1 模板
[NEAT]fitness_criterion= maxfitness_threshold= 3.9pop_size = 50reset_on_extinction = False[DefaultGenome]# node activation optionsactivation_default= squareactivation_mutate_rate = 0.0activation_options= square# node aggregation optionsaggregation_default= sumaggregation_mutate_rate = 0.0aggregation_options= sum# node bias optionsbias_init_mean= 0.0bias_init_stdev = 1.0bias_max_value= 30.0bias_min_value= -30.0bias_mutate_power = 0.5bias_mutate_rate = 0.7bias_replace_rate = 0.1# genome compatibility optionscompatibility_disjoint_coefficient = 1.0compatibility_weight_coefficient = 0.5# connection add/remove ratesconn_add_prob = 0.5conn_delete_prob = 0.5# connection enable optionsenabled_default = Trueenabled_mutate_rate= 0.01feed_forward = Trueinitial_connection= full# node add/remove ratesnode_add_prob = 0.2node_delete_prob = 0.2# network parametersnum_hidden = 0num_inputs = 1num_outputs = 1# node response optionsresponse_init_mean= 1.0response_init_stdev= 0.0response_max_value= 30.0response_min_value= -30.0response_mutate_power = 0.0response_mutate_rate = 0.0response_replace_rate = 0.0# connection weight optionsweight_init_mean = 0.0weight_init_stdev = 1.0weight_max_value = 30weight_min_value = -30weight_mutate_power= 0.5weight_mutate_rate= 0.8weight_replace_rate= 0.1[DefaultSpeciesSet]compatibility_threshold = 3.0[DefaultStagnation]species_fitness_func = maxmax_stagnation = 20species_elitism= 2[DefaultReproduction]elitism = 2survival_threshold = 0.2
文件取名为config-feedforward.txt。
本文将不阐述每一个参数的内容,仅介绍将会使用的一些参数。
1.2.1 选择激活函数
在本文中,我们需要计算x²,所以首先在脑海中需要明白我们的图形产出是一个二次方程图形:
所以我们选择了:
activation_default= squareactivation_mutate_rate = 0.0activation_options= square
其他的激活函数可以查看官方文档:激活函数列表。
在使用neat时,需要明白我们输出的结果是什么,比如本文,我们知道结果是平方,所以用square;如果需要输出是或否,那可以选择sigmoid。
1.2.2 输入输出
在本文中,我们只有一个x,得到的结果只有一个y,例如2输出4,3输出9,所以在这部分我们设置为:
num_hidden = 0num_inputs = 1num_outputs = 1
我们没有隐藏层,如果需要隐藏层,可以设置num_hidden。
1.2.3 种群数量及适应值阈值
本文中,将每一个族群设置为50个,阈值设为3.9,表示每一次计算会有50个子代,当适应值达到3.9,将停止迭代:
fitness_threshold= 3.9pop_size = 50
1.2.4 其他参数
我们需要设置的参数绝大部分都是上面的内容,其他参数特殊情况特殊配置,一般使用情况下可以直接使用默认参数值。
二、编写neat
local_dir = os.path.dirname(__file__)config_path = os.path.join(local_dir, 'config-feedforward.txt') # 配置文件名称config = neat.config.Config(neat.DefaultGenome,neat.DefaultReproduction,neat.DefaultSpeciesSet,neat.DefaultStagnation,config_path)# 配置文件中每一组p = neat.Population(config)p.add_reporter(neat.StdOutReporter(True))stats = neat.StatisticsReporter()p.add_reporter(stats)winner = p.run(eval_genomes, 1000) # 定义一个eval_genomes方法进行迭代,总共迭代100次
2.1 eval_genomes函数
def eval_genomes(genomes, config):# x ** 2x_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 样本y_list = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] # 样本结果for genome_id, genome in genomes: # 循环50个子代genome.fitness = 4.0 # 初始化适应度net = neat.nn.FeedForwardNetwork.create(genome, config) # 创建前馈网络for i, x in enumerate(x_list):output = net.activate((x, )) # 计算genome.fitness -= (output[0] - y_list[i]) ** 2 # 使用误差来进行适应值的降低
当进行p.run函数时,将进行eval_genomes函数的调用,在eval_genomes函数中,适当地给予每一个子代(基因)适应度的调整,来选择合适的子代,当子代达到我们的适应值阈值时,将会停止迭代。
三、运行
Population's average fitness: -42724756.48750 stdev: 220655177.29976Best fitness: 3.98619 - size: (1, 1) - species 1 - id 3838Best individual in generation 87 meets fitness threshold - complexity: (1, 1)
本次运行结果到第87代已经产生最佳适应度为3.98612的子代,我们可以通过保存计算结果来进行使用。
四、保存计算结果
4.1 保存
pickle.dump(winner, open("best.pickle", "wb"))
4.2 调用
path = "xx/xx/xx/best.pickle"with open(path, "rb") as f:net = pickle.load(f)local_dir = os.path.dirname(__file__)config_path = os.path.join(local_dir, 'config-feedforward.txt')config = neat.config.Config(neat.DefaultGenome,neat.DefaultReproduction,neat.DefaultSpeciesSet,neat.DefaultStagnation,config_path)net = neat.nn.FeedForwardNetwork.create(net, config)output = net.activate((4,))print(output[0])
输出结果为:
15.842392247827298
可以看到,结果已经是我们想要的了。
五、完整代码
# -*- coding: utf-8 -*-import osimport neatimport pickledef eval_genomes(genomes, config):# x ** 2x_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]y_list = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]for genome_id, genome in genomes:genome.fitness = 4.0net = neat.nn.FeedForwardNetwork.create(genome, config)for i, x in enumerate(x_list):output = net.activate((x, ))genome.fitness -= (output[0] - y_list[i]) ** 2if __name__ == '__main__':path = "xx/xx/xx/best.pickle"with open(path, "rb") as f:net = pickle.load(f)local_dir = os.path.dirname(__file__)config_path = os.path.join(local_dir, 'config-feedforward.txt')config = neat.config.Config(neat.DefaultGenome,neat.DefaultReproduction,neat.DefaultSpeciesSet,neat.DefaultStagnation,config_path)net = neat.nn.FeedForwardNetwork.create(net, config)output = net.activate((4,))print(output[0])# local_dir = os.path.dirname(__file__)# config_path = os.path.join(local_dir, 'config-feedforward.txt')## config = neat.config.Config(#neat.DefaultGenome,#neat.DefaultReproduction,#neat.DefaultSpeciesSet,#neat.DefaultStagnation,#config_path# )## p = neat.Population(config)## p.add_reporter(neat.StdOutReporter(True))# stats = neat.StatisticsReporter()# p.add_reporter(stats)## winner = p.run(eval_genomes, 1000)# pickle.dump(winner, open("best.pickle", "wb"))