2011/06/09

遺伝的アルゴリズムとか

ちょっと興味があったので、小高 知宏 著 『はじめての機械学習』 を読んでみた。

で。 5 章 2 節 「遺伝的アルゴリズムによる知識獲得」には、簡単な GA (SGA: Simple GA)の実装例が掲載されている。

今回は、その挙動を Processing で可視化してみた。

完成品はこんな感じ。
各世代における適応度の推移グラフをProcessingで可視化したもの。
赤はエリート個体の適応度、青は当該世代における適応度の平均値。



【例題】

下の表に示した 20 個の整数から 1 つ以上の整数を選び出し、それらの合計値が 499 にもっとも近くなる組合せを求めよ。

   番 号       数 値   
1 31
2 41
3 59
4 26
5 53
6 58
7 97
8 93
9 23
10 84
    
   番 号       数 値   
11 -62
12 -64
13 -33
14 -83
15 -27
16 -95
17 -2
18 -88
19 -41
20 -97


たとえば、1 番目の数値である 31 と 6 番目の数値の 58 を選ぶと、合計値は 31 + 58 = 89 となる。

これが、目標値である 499 に近いほど優良な解となるため、適応度のパラメータには、499 との差の絶対値 |499 - s| を用いる。ただし、このままでは値の小さい方が適応度が高くなるため、以下のように充分に大きな数 fmax から差の絶対値を引くことにする。

適応度 =  fmax - |499 - s|

遺伝子座の設計に関する詳細は、当該書籍を参照するか、ソースコードを解読してほしい(あまり詳しく書くと著者に迷惑がかかるため)。



【実装】

Processing で動くように適当に書き換えたところ、こんな感じになった(ただしコンソール)。


ここまではコンソールアプリケーションなので、実行してもあまり見栄えはよくない。

グラフ描画に必要なのは、各世代におけるエリート個体の適応度、および(各世代における)全個体の適応度の平均値なので、それらを得るべく printp メソッドを改造してこんなものを作った。

  1. /* ======================================== */  
  2. /*    getFitnessValueOfElite() メソッド     */  
  3. /*        エリート個体の適応度を得る        */  
  4. /* ======================================== */  
  5. int getFitnessValueOfElite(int[][] pool) {  
  6.   int    fitness;           // 適応度  
  7.   int    elite        = 0;  // エリート遺伝子の処理用変数  
  8.   int    bestfit      = 0;  
  9.     
  10.   for(int i = 0; i < POOLSIZE; ++i) {  
  11.     fitness = evalfit(pool[i]);  
  12.     if(fitness > bestfit) { /* エリート解 */  
  13.       bestfit = fitness;  
  14.       elite = i;  
  15.     }  
  16.   }  
  17.   return bestfit;  
  18. }  

  1. /* ======================================== */  
  2. /*    getAvarageFitnessValue() メソッド     */  
  3. /*            適応度の平均を得る            */  
  4. /* ======================================== */  
  5. float getAvarageFitnessValue(int[][] pool) {  
  6.   double totalfitness = 0;  // 適応度の合計値  
  7.     
  8.   for(int i = 0; i < POOLSIZE; ++i) {  
  9.     totalfitness += evalfit(pool[i]);  
  10.   }  
  11.     
  12.   /* 平均の適応度を出力 */  
  13.   return (float)totalfitness / POOLSIZE;  
  14. }  



各世代における適応度を配列に保存しておき、draw メソッドで描画する。

  1. void draw() {  
  2.   background(0);  
  3.   smooth();  
  4.     
  5.   // 目盛り表示  
  6.   stroke(50);  
  7.   for(int i = 0; i < width; i += 50) {  
  8.     line(i, 0, i, height);  
  9.   }  
  10.   for(int i = 0; i < height; i += 50) {  
  11.     line(0, i, width, i);  
  12.   }  
  13.     
  14.   for(int i = 0; i < LASTG-1; ++i) {  
  15.     stroke(25500);  
  16.     line(i    , MAXFIT - eliteFitness[i],  
  17.          i + 1, MAXFIT - eliteFitness[i+1]);  
  18.       
  19.     stroke(00255);  
  20.     line(i    , MAXFIT - avgFitness[i],  
  21.          i + 1, MAXFIT - avgFitness[i+1]);  
  22.   }  
  23.     
  24.   // ガイドライン表示  
  25.   if(0 <= mouseX && mouseX < LASTG) {  
  26.     stroke(255);  
  27.     line(mouseX, mouseY,   
  28.          mouseX, MAXFIT - eliteFitness[mouseX]);  
  29.   
  30.   // 結果文字列表示  
  31.   if(mouseX < width / 2) textAlign(LEFT);  
  32.   else textAlign(RIGHT);  
  33.   text("第" + mouseX + "世代(適応度:" + eliteFitness[mouseX]+")",  
  34.        mouseX,   
  35.        MAXFIT - eliteFitness[mouseX] + 12);  
  36.   }  
  37. }  

これでよし。

ちなみにフルバージョンのソースコードはこちら。

0 件のコメント:

コメントを投稿

ひとことどうぞφ(・ω・,,)