#はじめに
pythonの遺伝アルゴリズム、ニューラルネットの基本処理を書いてみました。
遺伝アルゴリズムは専門家ではないのでなんちゃって遺伝アルゴリズムです。
#ニューラルネットとは
生物の神経細胞網を模した計算手法です。
Wikipediaへのリンク
#XORとは
2つの入力が同じだと0異なると1を出力する論理式です。
入力1 | 入力2 | 出力 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
#遺伝的アルゴリズムとは
私が遺伝的アルゴリズムで知っているのは、
・良い特徴の個体を選別していく
・良い特徴を持つ個体から特徴を引き継いだ新たな個体を生成する。
・世代交代を繰り返してよりよい特徴の個体を見つけ出し、答えをみつける。
ということです。
様々な手法があるようですが、私は素人なので今回は
・個体群を成績順にソートし
・下位10%は新規生成個体に置き換え
・+50%を上位50%とその他の個体を交差(後述)させ生成
・これを繰り返し最も適切な回答を導き出す個体を見つけ出す。
という手法をとりました。
#交差とは
2つの個体から特徴を取り出し、新たな個体を作ることだと理解しています。
実際の生物の世界でいうところの子が親の遺伝子を受け継ぐことを模したものです。
実際の遺伝はどちらかの親の遺伝子を1ビット毎にデジタル的に受け継ぐイメージですが、
今回はニューラルネットのシナプスの重み(float型)を配列化し、
2つの個体のシナプス重みを示す2つの配列の各数値の内分をとることで
2つの個体の特徴を遺伝させることを模しました。
例
| |w1 |w2 |w3 | w4|
|---|---|---|---|---|---|
|個体1のシナプス重み |1 |1 |3|4|
|個体2のシナプス重み |0 |0 |1|1|
|個体1との類似度 |1|0|0.5|0.1
|個体2との類似度 |0|1|0.5|0.9
|新個体のシナプス重み|1 |0 |2|1.3
ただ、局所解に収束する可能性がありますので2つ重みの外分の値も取りうることとしました。
#サンプルプログラム
from numpy.random import *
import numpy as np
import math
import sys
class Body: #各個体
#入力層の数
nn_input=2
#中間層の数
nn_hidden=3
nn_output=1
#シナプス重み初期値の範囲
w_range=10
#シナプス数
w_length=(nn_input+1)*(nn_hidden+1)+(nn_hidden+1)*(nn_output)
#シグモイド関数に入力する最小値
sigmoidinputmin=math.log(sys.float_info.min)
#シグモイド関数に入力する最大値
sigmoidinputmax=math.log(sys.float_info.max)
#交差時の遺伝をどの程度内分、外分にするか
#0・・・2つの重みの平均
#1・・・2つの重みの内分のいずれか
#1以上・・・外分(2つの重みの間の値とならないこと)もありうる
crossRatio=2
def __init__(self):
self.w=None #シナプス重み
self.score=-1 #評価結果
self.result=None #計算結果
#新規作成
def createNewBodyAtInit():
body=Body()
body.w=np.random.uniform(-Body.w_range,Body.w_range,Body.w_length)
body.calc()
return body
#結果を表示
def showResult(self):
aaa= str(self.score) + ":" + str(self.result)
print(aaa)
#交差
#引数 交差する個体
#戻り値 生成した個体
def cross(self,otherBody):
w1=self.w
w2=otherBody.w
newW=[]
for i in range(len(w1)):
a=np.random.random()*Body.crossRatio
newW.append(w1[i]*a+w2[i]*(1-a))
newBody=Body()
newBody.w=newW
newBody.calc()
return newBody
#評価計算
def calc(self):
result=[]
for _in in in_sample:
result.append(self.nn(_in))
_ = out_sample-result
error = _*_
self.result=result
self.score=sum(error)
#シグモイド関数
def sigmoid(self,x):
#入力が小さすぎるとオーバーフローするため、1を返す
if x<Body.sigmoidinputmin :
return 1
#大きすぎる場合、オーバーフローはないけど、
#上と同様の処理(なくても可)
if Body.sigmoidinputmax<x :
return 0
a=math.log(sys.float_info.max)
return 1/(1+math.e**-x)
#ニューラルネットの計算
def nn(self,inp):
w=self.w
w_num=0
hidden_o=[]
for i in range (Body.nn_hidden):
o=0
for j in range(Body.nn_input):
o+=w[w_num]*inp[j]
w_num+=1
o+=w[w_num]
w_num+=1
hidden_o.append(self.sigmoid(o))
o=0
for i in range (Body.nn_hidden):
o+=w[w_num]*hidden_o[i]
w_num+=1
o+=w[w_num]
w_num+=1
return self.sigmoid(o)
def main():
#入力値
global in_sample
in_sample=np.array([[0,0],
[0,1],
[1,0],
[1,1]])
#出力の目標値
global out_sample
out_sample=np.array([0,1,1,0]);
#個体数
body_counts=100
#世代数
loop=1000
#個体群
bodies=[]
#個体群の初期生成
for i in range(body_counts):
body=Body.createNewBodyAtInit()
bodies.append(body)
#世代交代しつつ評価
for i in range(loop):
print("***")
bodies=generate(bodies)
bodies[0].showResult()
#個体群再生成
#引数 世代交代前個体群
#戻り値 世代交代後個体群
def generate(bodies):
bodies=sort(bodies)
a1=0
length=len(bodies)
newgennum=length-1
#突然変異個体割合
mutationRatio=0.1
stop=newgennum-int(length*mutationRatio)
#突然変異個体の生成
while stop<newgennum :
body = Body.createNewBodyAtInit()
bodies[newgennum]=body
newgennum += -1
#交差個体の生成割合
crossRatio=0.5
#交差
stop=newgennum-int(length*crossRatio)
while stop<newgennum :
a2=int(length*np.random.random())
body = bodies[a1].cross(bodies[a2])
bodies[newgennum]=body
a1 += 1
a2 += -1
newgennum += -1
return bodies
#個体群を成績順にソートする。
#引数 ソート前個体群
#戻り値 ソート後個体群
def sort(bodies):
di = {}
for body in bodies:
di[body.score]=body
keys = sorted(di.keys())
li=[]
for key in sorted(di.keys()):
li.append(di[key])
return li
if __name__ =="__main__":
main()