Python での遺伝的アルゴリズムによるガベージ コレクション戦略の最適化

Python での遺伝的アルゴリズムによるガベージ コレクション戦略の最適化

遺伝的アルゴリズムは、進化のプロセスに性質が似ている最適化手法です。これは乱暴な例えかもしれませんが、よく見ると、ダーウィンの自然選択は、環境内で繁栄するのに完全に適合した生物を生み出すことを目標とする最適化タスクに大体似ています。

この記事では、わずか数時間でガベージ収集ロボットを「進化」させる遺伝的アルゴリズムを Python で実装する方法を紹介します。

[[348489]]

背景

私がこれまでに出会った遺伝的アルゴリズムの原理に関する最高のチュートリアルは、Melanie Mitchell 著の『Complexity: A Guided Tour』という複雑系に関する優れた本からのものです。

ある章で、ミッチェルは、ゴミを拾うことだけが人生の目的であるロビーというロボットを紹介し、GA を使用してロビーの制御ポリシーを最適化する方法について説明します。以下では、この問題に対する私のアプローチを説明し、Python でアルゴリズムを実装する方法を示します。この種のアルゴリズムを構築するための優れたパッケージがいくつかあります (DEAP など) が、このチュートリアルでは基本的な Python、Numpy、および TQDM (オプション) のみを使用します。

これは単なる例ですが、GA は多くの実際のアプリケーションで使用されています。データ サイエンティストとして、私はハイパーパラメータの最適化とモデルの選択にこれらを頻繁に使用します。計算コストは​​高くなりますが、GA を使用すると、検索空間の複数の領域を並行して探索できるため、勾配を計算する場合に適しています。

問題の説明

Robby という名前のロボットは、ゴミで満たされ、4 つの壁に囲まれた 2D グリッドの世界に住んでいます (下の図を参照)。このプロジェクトの目標は、ロボットが効率的にゴミを拾い、壁に衝突しないようにするための最適な制御戦略を開発することです。

ロビーは、自分の周囲の上、下、左、右の 4 つのブロックと、自分がいるブロックしか見ることができません。各ブロックには、空、ゴミあり、壁の 3 つのオプションがあります。したがって、ロビーには 3⁵ = 243 通りのケースが存在します。ロビーは、上、下、左、右(4 種類)に移動する、ランダムに移動する、ゴミを拾う、静止する、という 7 つの異なるアクションを実行できます。

したがって、ロビーの制御戦略は、0 から 6 までの 243 個の数字 (ロビーが 243 の考えられる状況で実行すべきアクションに対応) で構成される「DNA」文字列としてエンコードできます。

方法

あらゆる GA の最適化手順は次のとおりです。

  1. 問題に対する初期のランダムな解決策の「集団」を生成する
  2. 個人の「適応度」は、問題をどれだけうまく解決できるかに基づいて評価されます。
  3. 最も適切な解決策は、「生殖」し、「遺伝」物質を次の世代の子孫に伝えることである。
  4. 最適化されたソリューションのセットが得られるまで、手順2と3を繰り返します。

このタスクでは、ランダムな DNA 文字列 (ランダム制御ポリシーに対応) に初期化された Robby の第 1 世代を作成しました。次に、シミュレーションでは、これらのロボットをランダムに割り当てられたグリッドワールドで実行し、そのパフォーマンスを観察します。

適合性

ロボットの適応度は、n 回の移動でどれだけのゴミを拾うか、何回壁に衝突するかによって決まります。この例では、ロボットがゴミを拾うたびに 10 ポイントが与えられ、壁にぶつかるたびに 5 ポイントが減点されます。その後、ロボットは適応度に関連した確率で「交尾」し(つまり、ゴミをたくさん拾うロボットは繁殖する可能性が高くなります)、新しい世代のロボットが誕生します。

交尾

「交配」を実現するにはいくつかの方法があります。ミッチェルのバージョンでは、両親の2本のDNA鎖をランダムにつなぎ合わせて結合し、次世代の子供を作った。私の実装では、各親から各遺伝子をランダムに割り当てます (つまり、243 個の遺伝子のそれぞれについて、コインを投げてどの遺伝子が継承されるかを決定します)。

たとえば、私の方法を使用すると、最初の 10 個の遺伝子のうち、親と子の可能性のある遺伝子は次のようになります。

  1. 親 1: 1440623161
  2. 親2: 2430661132
  3. 子供: 2440621161

突然変異

このアルゴリズムで再現する自然選択からのもう一つの概念は「突然変異」です。子どもの遺伝子の大部分は両親から受け継がれますが、遺伝子変異(ランダム割り当て)の可能性もわずかながら考慮に入れました。この突然変異率により、新たな可能性を探求することができます。

Python実装

最初のステップは、必要なパッケージをインポートし、このタスクのパラメータを設定することです。これらのパラメータは出発点として選択しましたが、調整可能なので、ぜひ実験してみてください。

  1. 「」 「
  2. パッケージのインポート
  3. 「」 「
  4. numpyをnpとしてインポートする
  5. tqdm.notebookからtqdmをインポート
  6.  
  7. 「」 「
  8. パラメータの設定
  9. 「」 「
  10. # シミュレーション設定
  11. pop_size = 200 # 世代ごとのロボットの数
  12. num_breeders = 100 # 各世代で交配できるロボットの数
  13. num_gen = 400 # 世代の総数
  14. iter_per_sim = 100 # 各ロボットのガベージコレクションシミュレーションの数
  15. moves_per_iter = 200 # シミュレーションごとにロボットが実行できる移動回数
  16.  
  17. # グリッド設定
  18. rubbish_prob = 0.5 # 各グリッド内のゴミの確率
  19. grid_size = 10 # 0 グリッドサイズ(壁を除く)
  20.  
  21. # 進化設定
  22. wall_penalty = -5 # 壁にぶつかったときに減点されるフィットポイントの数
  23. no_rub_penalty = -1 # 空のマスにあるゴミを拾うとペナルティポイントが加算されます
  24. rubbish_score = 10 # ゴミを拾うとポイントがもらえます
  25. 突然変異率 = 0.01 # 突然変異の確率

次に、グリッド ワールド環境のクラスを定義します。各セルには、それぞれ空のセル、ゴミのあるセル、壁に対応するマーカー「o」、「x」、「w」が付けられます。

  1. クラス環境:
  2. 「」 「
  3. ゴミで満たされたグリッド環境を表すために使用されるクラス。各セルは次のように表すことができます。
  4. 'o' : 空
  5. 'x' : ゴミ
  6. 'w' : 壁
  7. 「」 「
  8. def __init__(self, p=rubbish_prob, g_size=grid_size):
  9. self.p = p # セルがゴミである確率
  10. self.g_size = g_size # 壁は含まれません
  11.  
  12. # グリッドを初期化し、ランダムにガベージを割り当てる
  13. self.grid = np.random.choice([ 'o' , 'x' ],サイズ=(self.g_size+2,self.g_size+2), p=(1 - self.p, self.p))
  14.          
  15. # 外側の四角を壁として設定する
  16. self.grid[:,[0,self.g_size+1]] = 'w'  
  17. self.grid[[0,self.g_size+1], :] = 'w'  
  18.  
  19. show_grid(self)を定義します。
  20. # 現在の状態のグリッドを印刷する
  21. 印刷(self.grid)
  22.  
  23. def remove_rubbish(self,i,j):
  24. # 指定されたセル (i,j) からゴミを消去します
  25. if self.grid[i,j] == 'o' : # セルはすでに空です
  26. 戻る 間違い 
  27. それ以外
  28. 自己.グリッド[i,j] = 'o'  
  29. 戻る 真実 
  30.  
  31. get_pos_string(self,i,j)を定義します。
  32. # ロボットに「見える」セル (i,j) 内のセルを示す文字列を返します
  33. self.grid[i-1,j] + self.grid[i,j+1] + self.grid[i+1,j] + self.grid[i,j-1] + self.grid[i,j] を返す

次に、ロボットを表すクラスを作成します。このクラスには、アクションを実行し、適応度を計算し、親ロボットのペアから新しい DNA を生成するメソッドが含まれています。

  1. クラスロボット:
  2. 「」 「
  3. ゴミ収集ロボットを表すために使用される
  4. 「」 「
  5. def __init__(self, p1_dna=None, p2_dna=None, m_rate=mutation_rate, w_pen=wall_penalty, nr_pen=no_rub_penalty, r_score=rubbish_score):
  6. self.m_rate = m_rate # 突然変異率
  7. self.wall_penalty = w_pen # 壁にぶつかったときのペナルティ
  8. self.no_rub_penalty = nr_pen # 空のマスにあるゴミを拾った場合のペナルティ
  9. self.rubbish_score = r_score # ゴミ拾いの報酬
  10. self.p1_dna = p1_dna # 親2のDNA
  11. self.p2_dna = p2_dna # 親2のDNA
  12.          
  13. # シーン文字列から遺伝子インデックスを検索するための辞書を生成する
  14. con = [ 'w' , 'o' , 'x' ] # 壁、空、ゴミ
  15. 自己.situ_dict = dict()
  16. カウント= 0
  17. 詐欺場合
  18. のために  欠点
  19. ダウンインコンの場合
  20. のために  欠点
  21. pos in conの場合:
  22. self.situ_dict[上++下++位置] =カウント 
  23. カウント+= 1
  24.          
  25. # DNAを初期化する
  26. 自己.get_dna()
  27.  
  28. get_dna(self)を定義します。
  29. # ロボットのDNA文字列を初期化する
  30. self.p1_dnaNone の場合:
  31. # 親がいないときにDNAをランダムに生成する
  32. self.dna = '' . join ([str(x) for x in np.random.randint(7, size = 243)])
  33. それ以外
  34. 自己.dna = 自己.mix_dna()
  35.  
  36. mix_dna(self)を定義します。
  37. # 親のDNAからロボットのDNAを生成する
  38. mix_dna = '' . join ([np.random.choice([self.p1_dna,self.p2_dna])[i]範囲(243)]iについて
  39.  
  40. #突然変異を追加
  41. iが範囲(243)の場合:
  42. np.random.rand() > 1 の場合 - self.m_rate:
  43. mix_dna = mix_dna[:i] + str(np.random.randint(7)) + mix_dna[i+1:]
  44.  
  45. mix_dnaを返す
  46.  
  47. def シミュレート (self、n_iterations、n_moves、debug = False ):
  48. # ガベージコレクションをシミュレートする
  49. 合計スコア = 0
  50. 範囲(n_iterations)の場合:
  51. self.score = 0 # 適合スコア
  52. self.envir = 環境()
  53. self.i, self.j = np.random.randint(1,self.envir.g_size+1, size =2) # 初期位置をランダムに割り当てる
  54. デバッグの場合:
  55. print( '前' )
  56. print( '開始位置:' , self.i, self.j)
  57. 自己.envir.show_grid()
  58. のために 動く 範囲(n_moves):
  59. 自己.行動()
  60. tot_score += 自己スコア
  61. デバッグの場合:
  62. print( 'after' )
  63. print( '終了位置:' ,self.i, self.j)
  64. 自己.envir.show_grid()
  65. print( 'スコア:' , self.score)
  66. tot_score / n_iterations # n 回の反復の平均スコアを返します
  67.  
  68. def act(self):
  69. # DNAとロボットの位置に基づいてアクションを実行する
  70. post_str = self.envir.get_pos_string(self.i, self.j) # ロボットの現在の位置
  71. gene_idx = self.situ_dict[post_str] # 現在の位置のDNAの関連インデックス
  72. act_key = self.dna[gene_idx] # DNAからアクションを読み取る
  73. act_key == '5'の場合:
  74. # ランダム移動
  75. act_key = np.random.choice([ '0' , '1' , '2' , '3' ])
  76.  
  77. act_key == '0'の場合:
  78. 自己.mv_up()
  79. act_key == '1'の場合:
  80. 自己.mv_right()
  81. act_key == '2'の場合:
  82. 自己.mv_down()
  83. act_key == '3'の場合:
  84. 自己.mv_left()
  85. act_key == '6'の場合:
  86. 自己ピックアップ()
  87.  
  88. mv_up(自分)を定義します。
  89. # 上に移動
  90. self.i == 1の場合:
  91. 自己スコア += 自己壁ペナルティ
  92. それ以外
  93. 自分.i -= 1
  94.  
  95. mv_right(自分)を定義します。
  96. # 右に移動
  97. self.j == self.envir.g_sizeの場合:
  98. 自己スコア += 自己壁ペナルティ
  99. それ以外
  100. 自己.j += 1
  101.  
  102. mv_down(self)を定義します。
  103. # 下に移動
  104. self.i == self.envir.g_size の場合:
  105. 自己スコア += 自己壁ペナルティ
  106. それ以外
  107. 自分.i += 1
  108.  
  109. mv_left(self)を定義します。
  110. # 左に移動
  111. self.j == 1の場合:
  112. 自己スコア += 自己壁ペナルティ
  113. それ以外
  114. 自己.j -= 1
  115.  
  116. def pickup(自分):
  117. # ゴミ拾い
  118. 成功 = self.envir.remove_rubbish(self.i, self.j)
  119. 成功した場合:
  120. # ゴミ拾いに成功しました
  121. 自己スコア += 自己ゴミスコア
  122. それ以外
  123. # 現在のブロックではゴミは拾われていません
  124. 自己スコア += 自己無こすりペナルティ

最後に、遺伝的アルゴリズムを実行します。以下のコードでは、ロボットの初期集団を生成し、自然淘汰を実行させます。このアルゴリズムを実装するには、確かにより高速な方法 (並列化を活用するなど) がありますが、このチュートリアルでは、わかりやすくするために速度を犠牲にしました。

  1. # 初期人口
  2. pop = [Robot()xが範囲(pop_size)]
  3. 結果 = []
  4.  
  5. # 進化を実行する
  6. itqdm(range(num_gen))場合:
  7. スコア = np.zeros(pop_size)
  8.      
  9. # すべてのロボットをループする
  10. enumerate(pop)idx、rob場合:
  11. # ガベージコレクションシミュレーションを実行し、適合度を計算する
  12. スコア = rob.simulate(iter_per_sim、 moves_per_iter)
  13. スコア[idx] = スコア
  14.  
  15. results.append([scores.mean(), scores.max ()]) # 各世代の平均値と最大値を保存する
  16.  
  17. best_robot = pop[scores.argmax()] # 最高のロボットを保存する
  18.  
  19. # 交尾できるロボットの数を制限する
  20. inds = np.argpartition(scores, -num_breeders)[-num_breeders:] # 適応度に基づいて上位ロボットのインデックスを取得します
  21. サブポップ = []
  22. indsidxの場合:
  23. サブポップ.append(pop[idx])
  24. スコア = スコア[inds]
  25.  
  26. # 二乗して正規化する
  27. norm_scores = (スコア -スコア.min ()) ** 2
  28. norm_scores = norm_scores / norm_scores.sum ()
  29.  
  30. # 次世代ロボットの創出
  31. 新しいポップ = []
  32. 範囲(pop_size)内のの場合:
  33. # 適合度の高い親を選択する
  34. p1、p2 = np.random.choice(サブポップ、p = norm_scores、サイズ= 2、置換= False )
  35. new_pop.append(ロボット(p1.dna、p2.dna))
  36.  
  37. ポップ = new_pop

当初、ほとんどのロボットはゴミを拾わず、常に壁にぶつかっていましたが、数世代後には、いくつかの単純な戦略(「ゴミがある場合は拾う」、「壁の隣にある場合は壁にぶつからない」など)が見られるようになりました。何百回もの反復を経て、驚くべきガベージ コレクションの天才たちの世代が誕生しました。

結果

下のグラフは、400 世代にわたってロボットの集団で成功するガベージ コレクション戦略を「進化」させることができたことを示しています。

進化した制御戦略の品質を評価するために、直感的に理解できるルールをいくつか含むベンチマーク戦略を手動で作成しました。

  • ゴミが現在のブロックにある場合は、それを拾います
  • 隣接するマスにゴミが見えている場合は、そのマスに移動する
  • 壁に近い場合は反対方向に移動する
  • それ以外の場合はランダムに移動する

平均すると、このベースライン ポリシーは 426.9 の適応度を達成しましたが、最終的な「進化した」ボットは平均 475.9 の適応度を達成しました。

戦略分析

この最適化アプローチの素晴らしい点は、直感に反する解決策を見つけることができることです。ロボットは人間が設計する可能性のある合理的なルールを学習できただけでなく、人間が決して考えないような戦略を自発的に考案することもできました。近視や記憶障害を克服するために「マーカー」を使用する高度な技術が登場した。

たとえば、ロボットが現在ゴミのあるマスにいて、東と西のマスにゴミがあるのが見えている場合、単純なアプローチとしては、現在のマスにあるゴミをすぐに拾い、ゴミのあるマスに移動することになります。この戦略の問題点は、ロボットがいったん(たとえば西に)移動すると、東にもう 1 つのジャンクがあることを思い出す方法がないことです。この問題を克服するために、私たちは進化するロボットが次の手順を実行するのを観察しました。

  • 西に移動する(現在のマスにゴミを目印として残す)
  • ゴミを拾って東へ進みます(ゴミを目印として見ることができます)
  • ゴミを拾って東に移動してください。
  • 最後のゴミを拾う

この最適化から生じる直感に反する戦略の別の例を以下に示します。 OpenAI は、より複雑な最適化手法である強化学習を使用して、エージェントにかくれんぼを教えました。これらのエージェントは「人間の」ポリシーを学習することから始めますが、最終的には新しいソリューションを学習することがわかります。

結論は

遺伝的アルゴリズムは生物学とコンピューター サイエンスを独自の方法で組み合わせたものであり、必ずしも最速のアルゴリズムではありませんが、私の意見では最も美しいアルゴリズムの 1 つです。

<<:  27回の機械学習インタビューの後、重要な概念を強調しましょう

>>:  掃除ロボットが話し始めた。人工知能アシスタントは、家庭の6つの主要分野で努力している。

ブログ    

推薦する

2017 年グローバル人工知能人材ホワイトペーパー: 世界トップクラスの AI 人材の秘密を解明!

人工知能における競争は優秀な人材に基づいて行われます。国の将来の発展方向として、AI技術は経済発展、...

...

...

...

AIによる自動思考の隠れたコストについて

[[273454]]多くの薬と同様に、精神を明晰に保つために特に使用される薬であるモダフィニルには、...

PaddlePaddleのクリック率に基づくディープラーニング手法の試み

序文チーム内でクリック率に関する記事をいくつか共有した際に、広告のクリック率の推定値を計算する一般的...

研究によると、GPT-4モデルはエラーを自己修正する能力があり、AIコードのさらなる商業化を促進することが期待されています。

7月5日、マサチューセッツ工科大学(MIT)とマイクロソフトの研究者らは、GPT-4モデルには優れ...

...

開発者に人気の機械学習 API 10 選

つい最近まで、人工知能には科学者が白衣を着て研究室で研究を行う必要があると考えられていました。この科...

科学者が警告:AIが生成したコンテンツでAIを訓練すると、数世代以内に「ゴミ」が生成され始める

6月20日のニュースによると、人工知能分野の専門家は、AIが生成したコンテンツがオンラインに投稿され...

人工知能の3つの主要分野とその産業応用

人工知能は、人間の知能の拡張と拡大をシミュレートするための理論、方法、技術、アプリケーション システ...

安全性は小さな問題ではありません。これがAI時代に彼らが選択することです

AI時代においては、セキュリティを早急に再定義する必要があります。人工知能やモノのインターネットなど...

大きなモデルが常に「事実」を間違えた場合はどうすればよいでしょうか? 300以上の論文のレビューはこちら

ビッグモデルは事実の知識を習得する上で優れた能力と可能性を示していますが、ドメイン知識の欠如、リアル...

...

AI時代なのに、まだビッグデータが分からない?

[[322803]]近年、IT 業界でどの技術方向が最もホットであるかと聞かれれば、それは ABC...