PyGad GA调用适应度函数次数超预期问题求助
问题:PyGad中fitness函数额外调用次数的原因与优化方法
我正在用PyGad的GA函数优化神经网络,测试调试阶段在fitness函数里加了保存每个解及其准确率的代码。设置了initial_population=10,sol_per_pop=10。
原本以为fitness函数的调用次数是10减去keep_elitism=2和keep_parents=2保留的解的数量,但实际第一代调用了26次,后续每代16次。第一代多的10次应该是PyGad初始化导致的。
因为fitness函数耗时极长,想限制每代的调用次数,求解释额外调用的原因,以及减少次数的方法。我的GA实例代码如下:
ga_instance = pg.GA(initial_population=initial_population, num_generations=100, num_parents_mating=2, fitness_func=fitness, sol_per_pop=10, num_genes=18, gene_type=GENE_TYPE, gene_space=GENE_SPACE, parent_selection_type='tournament', K_tournament=5, keep_parents=2, keep_elitism=2, save_solutions=True, save_best_solutions=True, crossover_type='single_point', mutation_type='adaptive', allow_duplicate_genes=True, mutation_num_genes=[4,2], stop_criteria='saturate_5', on_generation=callback_gen, suppress_warnings=False)
原因分析与解决方法
一、额外调用次数的核心原因
1. 第一代26次调用的拆解
第一代的调用次数由两部分组成:
- 初始种群计算:你传入了
initial_population=10,PyGad会先对这10个初始解执行fitness计算,这是10次调用。 - 第一代进化流程调用:和后续每代的进化逻辑一致,这部分占16次,累计后就是26次。
2. 后续每代16次调用的根源
这16次主要来自两个环节:
- 锦标赛选择的重复计算:你使用了
tournament选择策略,且K_tournament=5、num_parents_mating=2。每次选父代需要进行2轮锦标赛,每轮随机抽取5个个体。如果PyGad内部的适应度缓存未生效(或因设置导致失效),会重复计算这些已存在个体的fitness,这部分就占了10次调用。 - 新个体的计算:每代需要生成
sol_per_pop - keep_parents - keep_elitism = 10-2-2=6个新个体,每个都要执行一次fitness计算,这是6次调用。
两者相加10+6=16,刚好对应后续每代的调用次数。
另外,你使用的adaptive变异类型,可能会因需要根据适应度调整变异概率,触发少量额外调用,但不是核心原因。
二、减少fitness调用次数的实用方法
1. 手动实现适应度缓存(最有效)
在fitness函数内部加一个缓存字典,把已经计算过的解的结果存起来,避免重复计算。因为解是数组,要转成可哈希的元组当键:
fitness_cache = {} def fitness(solution, solution_idx): solution_key = tuple(solution) if solution_key in fitness_cache: return fitness_cache[solution_key] # 你的神经网络评估逻辑 accuracy = ... fitness_val = accuracy # 假设适应度直接用准确率 # 存入缓存 fitness_cache[solution_key] = fitness_val # 保存解和准确率的代码 ... return fitness_val
不管PyGad因什么原因重复调用同一个解,都只会计算一次,能大幅降低调用次数。
2. 调整选择策略或参数
- 若坚持用锦标赛选择:减小
K_tournament的值(比如设为2或3),减少每轮锦标赛涉及的个体数,从而降低重复计算次数。 - 更换选择策略:改用
sss(稳态选择)或rws(轮盘赌选择),这些策略直接用已计算好的适应度值,不会重复触发fitness调用。
3. 简化变异策略
如果自适应变异不是必须的,换成普通的random变异类型,避免因调整变异概率带来的额外fitness调用。
4. 验证PyGad内部缓存
PyGad本身应该会缓存适应度,但可能因allow_duplicate_genes=True等设置导致缓存失效。手动缓存是最可靠的兜底方案,不用依赖内部机制。
内容的提问来源于stack exchange,提问作者whichedge
相关产品推荐
相关产品推荐

