はじめに
deapの使い方メモです。
ここでは、deap\examples\onemax_numpy.py を例にメモを記載します。
※ setupは記載しません。
deap
https://deap.readthedocs.io/en/master/
deap github
https://github.com/DEAP/deap
OneMax?
[1, 1, 1, 1, 0, 0, 0, 1, 1, 0]のように0,1からなる数列の和を最大化する問題です。
⇒ 求めるべき解は[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]となります。
下記コードでは、長さ100の数列を問題としています。
コードと実行結果
メモ付きコードを貼ります。
メモ付きコード
import random
import numpy
from deap import algorithms
from deap import base
from deap import creator
from deap import tools
# parameter
n_gene = 100 # 1個体あたりの遺伝子数
n_individuals = 300 # 1世代あたりの個体数
n_generations = 1000 # 世代数
p_cxpb = 0.5 # 交叉率(交叉する個体数)
p_mutpb = 0.2 # 変異率(変異する個体数)
p_mutate = 0.05 # 変異確率(遺伝子の変異率)
n_tournsize = 3 # トーナメントサイズ
def evalOneMax(individual):
"""評価関数 onemax"""
return sum(individual),
def init_creator():
"""目的関数の方向性を設定"""
# 評価する目的関数は1つ、個体の適応度を最大化
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
# numpy.ndarrayクラスを継承して、
# fitness=creator.FitnessMaxというメンバ変数を追加したIndividualクラスを作成する
creator.create("Individual", numpy.ndarray, fitness=creator.FitnessMax)
return creator
def my_gene_generator(min, max):
"""遺伝子生成関数"""
return random.randint(min, max)
def init_generator(creator):
"""遺伝子、個体、世代の生成手法の設定"""
toolbox = base.Toolbox()
# 遺伝子を生成する関数の定義
toolbox.register("attr_bool", my_gene_generator, 0, 1)
# 個体を生成する関数の定義
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, n_gene)
# 世代を生成する関数の定義
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
return toolbox
def operator_registration(toolbox):
"""評価関数・戦略の設定"""
toolbox.register("evaluate", evalOneMax) # evaluate = 評価関数
toolbox.register("mate", tools.cxTwoPoint) # mate = 2点交叉
toolbox.register("mutate", tools.mutFlipBit, indpb=p_mutate) # mutate = bit反転
toolbox.register("select", tools.selTournament, tournsize=n_tournsize) # select = tournament(3)
def stats_register():
"""ステート定義設定"""
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", numpy.mean)
stats.register("std", numpy.std)
stats.register("min", numpy.min)
stats.register("max", numpy.max)
return stats
def get_cxpoint(size):
"""2点交叉用 2点の設定"""
cxpoint1 = random.randint(1, size)
cxpoint2 = random.randint(1, size - 1)
if cxpoint2 >= cxpoint1:
cxpoint2 += 1
else: # Swap the two cx points
cxpoint1, cxpoint2 = cxpoint2, cxpoint1
return cxpoint1, cxpoint2
def cxTwoPointCopy(ind1, ind2):
"""numpy用 2点交叉"""
size = min(len(ind1), len(ind2))
cxpoint1, cxpoint2 = get_cxpoint(size)
ind1[cxpoint1:cxpoint2], ind2[cxpoint1:cxpoint2] \
= ind2[cxpoint1:cxpoint2].copy(), ind1[cxpoint1:cxpoint2].copy()
return ind1, ind2
def set_seed(seed=42):
random.seed(seed)
def main(toolbox):
set_seed()
# 初期世代の生成
pop = toolbox.population(n=n_individuals)
# numpy用 elite=1 戦略
hof = tools.HallOfFame(1, similar=numpy.array_equal)
# stats定義
stats = stats_register()
# main loop
algorithms.eaSimple(pop, toolbox, cxpb=p_cxpb, mutpb=p_mutpb, ngen=n_generations, stats=stats,
halloffame=hof)
# best個体の表示
best_ind = tools.selBest(pop, 1)[0]
print("Best individual is \n Eval:\n %s, \n Gene:\n %s" % (best_ind.fitness.values, best_ind))
return pop, stats, hof
if __name__ == "__main__":
# 目的関数の方向性を設定
creator = init_creator()
# 遺伝子、個体、世代の生成手法の設定
toolbox = init_generator(creator)
# 進化手法の設定
operator_registration(toolbox)
# メインルーチン
main(toolbox)
実行結果
実行結果
gen nevals avg std min max
0 300 49.88 4.82344 36 64
1 172 54.27 3.60792 45 68
2 181 57.24 3.32 47 68
・・・
987 183 98.9767 2.3642 89 100
988 171 99.1467 2.02941 89 100
989 192 99.0567 2.26424 90 100
990 176 99.1167 2.12047 88 100
991 183 99.2733 1.94901 90 100
992 164 98.97 2.30704 90 100
993 178 99.03 2.0581 90 100
994 188 98.9767 2.24264 89 100
995 174 98.95 2.3211 86 100
996 177 98.83 2.33833 90 100
997 186 99.0367 2.15453 89 100
998 177 98.9833 2.16095 91 100
999 175 98.9933 2.25683 90 100
1000 181 98.8967 2.23443 89 100
Best individual is
Eval:
(100.0,),
Gene:
[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
はまりどころ
① 評価関数の方向性(最大化/最小化)を定める方法がよくわからない
・適応度を最小化する際は負の重みを、最大化する際には正の重みをつける(値は何でも良い)
・weightはタプルである必要がある
# 評価する目的関数は1つ、個体の適応度を最大化
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
# 評価する目的関数は2つ、1つ目は適応度最小化、2つ目は最大化
creator.create("FitnessMulti", base.Fitness, weights=(-1.0, 1.0))
② 遺伝子の生成・評価はどうするの?
・toolboxに生成関数を登録する
ここでは、my_gene_generatorという0 or 1 をランダムに生成する関数を
toolbox へ "attr_bool" という名前で登録する
def my_gene_generator(min, max):
"""遺伝子生成関数"""
return random.randint(min, max)
def init_generator(creator):
"""遺伝子、個体、世代の生成手法の設定"""
toolbox = base.Toolbox()
# 遺伝子を生成する関数の定義
toolbox.register("attr_bool", my_gene_generator, 0, 1)
・toolboxに評価関数を登録する
ここでは、evalOneMaxという関数を
toolbox へ "evaluate" という名前で登録する
def evalOneMax(individual):
"""評価関数 onemax"""
return sum(individual),
def operator_registration(toolbox):
"""評価関数・戦略の設定"""
toolbox.register("evaluate", evalOneMax) # evaluate = 評価関数
③ メインループどこ?
・ここです
algorithms.eaSimple(pop, toolbox, cxpb=p_cxpb, mutpb=p_mutpb, ngen=n_generations, stats=stats, halloffame=hof)