遺伝的アルゴリズムで画像を学習したかった

きっかけ

rogerjohansson.blog
上の記事を読んでpythonで作ってみたくなった。記事内では世代の中の個体数は1体だけだったので10体に増やし交叉を取り入れてみたらどうなるだろうと思った。

方針

それぞれの個体はポリゴンの集合を持ちそれぞれのポリゴンは頂点座標、rgb値、透明度を持っている。ウィンドウ表示はtkinterが楽だったがtkinter.canvasでは透明度を変更できなかったのでpillowでnew imageにポリゴンを描画して最後にcanvasにその画像を貼る形にした。この方が処理も早くなる。


交叉では二点交叉を使いポリゴンの集合を入れ替える。コードはDEAPのcxTwoPoint()を参考にした。

def cxTwoPoint(ind1, ind2):
    size = min(len(ind1), len(ind2))
    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
   
    ind1[cxpoint1:cxpoint2], ind2[cxpoint1:cxpoint2] \
        = ind2[cxpoint1:cxpoint2], ind1[cxpoint1:cxpoint2]
        
    return ind1, ind2

しかしこのままではshallow copyになってしまうのでcopy.deepcopyでコピーした値を返す。

突然変異はそれぞれの個体に対して一定確率でポリゴンを追加したり削除したり並び順を変えたりポリゴンの頂点座標を変えたり画素を変えたりした。これはリンクをたどった先のソースコードpythonで真似しただけ。

最初は選択方法としてimagehashを使おうと思ったが、より細かくしたかったので画像をnp.arrayとしてピクセルごとの画素の差の2乗の和を比べて小さいものをエリート選択で決めていくことにした。

その他いろいろあるが割愛。

結果


f:id:busongames:20180907231138j:plainf:id:busongames:20180907230423p:plain
モナリザ
f:id:busongames:20180907231135p:plainf:id:busongames:20180907230429p:plain
appleロゴ
f:id:busongames:20180907231141j:plainf:id:busongames:20180907230436p:plain
emoji

すべて60万世代後の結果である。モナリザのオリジナル画像はどっかにいってしまったので近い構図の画像を貼った。

これ交叉いらなくね

一つの世代に10個体にしたが、最終的にどれも全く変わらないものが10個できてしまった。他のポリゴンとの重ねあいで色合いができていくので交叉したら適応度が下がることはよくよく考えてみれば当たり前だった。最初の方に同じような個体が選択されてそれ同士が交叉してもあまり変わらないので10個が同じ画像になったと考えられる。
最終的に個体数を1体に減らしそれを突然変異させた息子と比べるようにした。こっちの方が早く計算もできるしポリゴンの数も100をこえ複雑な形に対応できるようになった!

f:id:busongames:20180907232450j:plainf:id:busongames:20180907232219p:plain