Artificial-intelligence-with-python-genetic-algorithms

提供:Dev Guides
移動先:案内検索

PythonとAI –遺伝的アルゴリズム

この章では、AIの遺伝的アルゴリズムについて詳しく説明します。

遺伝的アルゴリズムとは何ですか?

遺伝的アルゴリズム(GA)は、自然selectionと遺伝学の概念に基づいた検索ベースのアルゴリズムです。 GAは、進化的計算として知られるはるかに大きな計算のブランチのサブセットです。

GAはジョン・ホランドと彼の学生およびミシガン大学の同僚、特にDavid Eによって開発されました。 ゴールドバーグ。 その後、さまざまな最適化の問題で試行され、高い成功を収めています。

GAには、指定された問題に対する可能な解決策のプールがあります。 その後、これらのソリューションは、組換えと突然変異(自然遺伝学のように)を受け、新しい子供を生み出し、プロセスはさまざまな世代で繰り返されます。 各個人(または候補解)には(その目的関数値に基づいて)適合値が割り当てられ、適合者には、適合者および適合者*個体を生み出す高いチャンスが与えられます。 これは、*サバイバルオブザフィット*のダーウィン理論に沿ったものです。

したがって、停止基準に達するまで、世代を超えて優れた個人またはソリューションを「進化」させ続けます。

遺伝的アルゴリズムは本質的に十分にランダム化されていますが、履歴情報も活用するため、ランダムローカル検索(これまでのところランダムソリューションを試し、最高のものを追跡する)よりもはるかに優れたパフォーマンスを発揮します。

最適化の問題にGAを使用する方法は?

最適化とは、設計、状況、リソース、システムを可能な限り効果的にすることです。 次のブロック図は、最適化プロセスを示しています-

最適化の問題

最適化プロセスのGAメカニズムの段階

以下は、問題の最適化に使用されるGAメカニズムの一連のステップです。

  • ステップ1-初期母集団をランダムに生成します。
  • ステップ2-最適なフィットネス値を持つ初期ソリューションを選択します。
  • ステップ3-突然変異および交差演算子を使用して、選択したソリューションを再結合します。
  • ステップ4-子孫を母集団に挿入します。
  • ステップ5-今、停止条件が満たされている場合、最適なフィットネス値でソリューションを返します。 それ以外の場合は、手順2に進みます。

必要なパッケージのインストール

Pythonで遺伝的アルゴリズムを使用して問題を解決するには、 DEAP と呼ばれるGA用の強力なパッケージを使用します。 これは、アイデアのラピッドプロトタイピングとテストのための新しい進化的計算フレームワークのライブラリです。 コマンドプロンプトで次のコマンドを使用してこのパッケージをインストールできます-

pip install deap
*anaconda* 環境を使用している場合、次のコマンドを使用してdeapをインストールできます-
conda install -c conda-forge deap

遺伝的アルゴリズムを使用したソリューションの実装

このセクションでは、遺伝的アルゴリズムを使用したソリューションの実装について説明します。

ビットパターンの生成

次の例は、 One Max 問題に基づいて、15個のものを含むビット文字列を生成する方法を示しています。

示されているように必要なパッケージをインポートします-

import random
from deap import base, creator, tools

評価関数を定義します。 遺伝的アルゴリズムを作成する最初のステップです。

def eval_func(individual):
   target_sum = 15
   return len(individual) - abs(sum(individual) - target_sum),

今、正しいパラメータでツールボックスを作成します-

def create_toolbox(num_bits):
   creator.create("FitnessMax", base.Fitness, weights=(1.0,))
   creator.create("Individual", list, fitness=creator.FitnessMax)

ツールボックスを初期化する

   toolbox = base.Toolbox()
toolbox.register("attr_bool", random.randint, 0, 1)
toolbox.register("individual", tools.initRepeat, creator.Individual,
   toolbox.attr_bool, num_bits)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

評価演算子を登録します-

toolbox.register("evaluate", eval_func)

今、クロスオーバー演算子を登録します-

toolbox.register("mate", tools.cxTwoPoint)

突然変異演算子を登録する-

toolbox.register("mutate", tools.mutFlipBit, indpb = 0.05)

繁殖のための演算子を定義します-

toolbox.register("select", tools.selTournament, tournsize = 3)
return toolbox
if __name__ == "__main__":
   num_bits = 45
   toolbox = create_toolbox(num_bits)
   random.seed(7)
   population = toolbox.population(n = 500)
   probab_crossing, probab_mutating = 0.5, 0.2
   num_generations = 10
   print('\nEvolution process starts')

人口全体を評価する-

fitnesses = list(map(toolbox.evaluate, population))
for ind, fit in zip(population, fitnesses):
   ind.fitness.values = fit
print('\nEvaluated', len(population), 'individuals')

世代を作成して繰り返します-

for g in range(num_generations):
   print("\n- Generation", g)

次世代の個人を選択する-

offspring = toolbox.select(population, len(population))

今、選択した個人のクローンを作成します-

offspring = list(map(toolbox.clone, offspring))

子孫に交差と突然変異を適用します-

for child1, child2 in zip(offspring[::2], offspring[1::2]):
   if random.random() < probab_crossing:
   toolbox.mate(child1, child2)

子供のフィットネス値を削除する

del child1.fitness.values
del child2.fitness.values

今、突然変異を適用する-

for mutant in offspring:
   if random.random() < probab_mutating:
   toolbox.mutate(mutant)
   del mutant.fitness.values

無効なフィットネスを持つ個人を評価します-

invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
fitnesses = map(toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
   ind.fitness.values = fit
print('Evaluated', len(invalid_ind), 'individuals')

今、人口を次世代の個人に置き換えます-

population[:] = offspring

現在の世代の統計を出力します-

fits = [ind.fitness.values[0] for ind in population]
length = len(population)
mean = sum(fits)/length
sum2 = sum(x*x for x in fits)
std = abs(sum2/length - mean**2)**0.5
print('Min =', min(fits), ', Max =', max(fits))
print('Average =', round(mean, 2), ', Standard deviation =',
round(std, 2))
print("\n- Evolution ends")

最終出力を印刷します-

   best_ind = tools.selBest(population, 1)[0]
   print('\nBest individual:\n', best_ind)
   print('\nNumber of ones:', sum(best_ind))
Following would be the output:
Evolution process starts
Evaluated 500 individuals
- Generation 0
Evaluated 295 individuals
Min = 32.0 , Max = 45.0
Average = 40.29 , Standard deviation = 2.61
- Generation 1
Evaluated 292 individuals
Min = 34.0 , Max = 45.0
Average = 42.35 , Standard deviation = 1.91
- Generation 2
Evaluated 277 individuals
Min = 37.0 , Max = 45.0
Average = 43.39 , Standard deviation = 1.46
… … … …
- Generation 9
Evaluated 299 individuals
Min = 40.0 , Max = 45.0
Average = 44.12 , Standard deviation = 1.11
- Evolution ends
Best individual:
[0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1,
 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0,
 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1]
Number of ones: 15

記号回帰問題

これは、遺伝的プログラミングで最もよく知られている問題の1つです。 シンボリック回帰の問題はすべて、任意のデータ分布を使用し、最も正確なデータをシンボリック式で近似しようとします。 通常、RMSE(Root Mean Square Error)のような測定は、個人のフィットネスを測定するために使用されます。 これは古典的なリグレッサーの問題であり、ここでは式 5x ^ 3 ^ -6x ^ 2 ^ + 8x = 1 を使用しています。 上記の例に従っているすべての手順に従う必要がありますが、主な部分はプリミティブセットを作成することです。これらは評価を開始できるように、個人のビルディングブロックであるためです。 ここでは、プリミティブの古典的なセットを使用します。

次のPythonコードはこれを詳細に説明します-

import operator
import math
import random
import numpy as np
from deap import algorithms, base, creator, tools, gp
def division_operator(numerator, denominator):
   if denominator == 0:
      return 1
   return numerator/denominator
def eval_func(individual, points):
   func = toolbox.compile(expr=individual)
   return math.fsum(mse)/len(points),
def create_toolbox():
   pset = gp.PrimitiveSet("MAIN", 1)
   pset.addPrimitive(operator.add, 2)
   pset.addPrimitive(operator.sub, 2)
   pset.addPrimitive(operator.mul, 2)
   pset.addPrimitive(division_operator, 2)
   pset.addPrimitive(operator.neg, 1)
   pset.addPrimitive(math.cos, 1)
   pset.addPrimitive(math.sin, 1)
   pset.addEphemeralConstant("rand101", lambda: random.randint(-1,1))
   pset.renameArguments(ARG0 = 'x')
   creator.create("FitnessMin", base.Fitness, weights = (-1.0,))
   creator.create("Individual",gp.PrimitiveTree,fitness=creator.FitnessMin)
   toolbox = base.Toolbox()
   toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min_=1, max_=2)
   toolbox.expr)
   toolbox.register("population",tools.initRepeat,list, toolbox.individual)
   toolbox.register("compile", gp.compile, pset = pset)
   toolbox.register("evaluate", eval_func, points = [x/10. for x in range(-10,10)])
   toolbox.register("select", tools.selTournament, tournsize = 3)
   toolbox.register("mate", gp.cxOnePoint)
   toolbox.register("expr_mut", gp.genFull, min_=0, max_=2)
   toolbox.register("mutate", gp.mutUniform, expr = toolbox.expr_mut, pset = pset)
   toolbox.decorate("mate", gp.staticLimit(key = operator.attrgetter("height"), max_value = 17))
   toolbox.decorate("mutate", gp.staticLimit(key = operator.attrgetter("height"), max_value = 17))
   return toolbox
if __name__ == "__main__":
   random.seed(7)
   toolbox = create_toolbox()
   population = toolbox.population(n = 450)
   hall_of_fame = tools.HallOfFame(1)
   stats_fit = tools.Statistics(lambda x: x.fitness.values)
   stats_size = tools.Statistics(len)
   mstats = tools.MultiStatistics(fitness=stats_fit, size = stats_size)
   mstats.register("avg", np.mean)
   mstats.register("std", np.std)
   mstats.register("min", np.min)
   mstats.register("max", np.max)
   probab_crossover = 0.4
   probab_mutate = 0.2
   number_gen = 10
   population, log = algorithms.eaSimple(population, toolbox,
      probab_crossover, probab_mutate, number_gen,
      stats = mstats, halloffame = hall_of_fame, verbose = True)

基本的な手順はすべて、ビットパターンの生成中に使用した手順と同じであることに注意してください。 このプログラムは、10世代後の最小、最大、標準(標準偏差)として出力を提供します。