目次
1. 遺伝的アルゴリズムについて
遺伝的アルゴリズムは、生物進化の理論に基づいて最適解を計算するアルゴリズムで、自然淘汰と適者生存を核としています。
遺伝的アルゴリズムについてはインターネット上にたくさんの解説がありますが、この章ではpython を使って関数の最大値を計算する遺伝的アルゴリズムを実装し、その詳細を議論します。
この章で使用される関数は次のとおりです: y = x^2、定義域は 0-10
遺伝的アルゴリズムは遺伝子の遺伝子組換えを使用するため、ここでの各遺伝子は 10101 などのバイナリ シーケンスです。
ドメインに戻るバイナリ マッピングはデコード プロセスであり、start + (end - start) * tmp / (pow(2,length)-1) 、 start はドメインの左の値、 end はドメインの右辺の値、 tmp はバイナリ シーケンス、length はバイナリ シーケンスの長さです。このような計算を通じて、バイナリを特定の定義ドメインにマッピングすることができます。
では、バイナリ シーケンスの長さはどのように定義されるのでしょうか。
これは計算精度に基づいています。たとえば、ここで計算される精度は 0.01 です。次に、合計 10*10^2 = 0.01->0.02->...->10.00 の間の 1000 個の数値が必要です。2^10 = 1024 は 1000 個の数値を保存できるため、必要なバイナリは 10 です。その場合、バイナリ シーケンスの長さは 10 です。
2. 遺伝的アルゴリズムの手順
次に、遺伝的アルゴリズムのステップを見てください。
ここにあるのは、自分で実装したコードに基づく段階的な分析です. オンライン実装とのいくつかの詳細または違いは最後に追加されます.
1. 母集団の初期化: 生成されたバイナリ シーケンスは通常 1 つではないため、これは以下の交叉操作を改善するためです。さらに、複数のバイナリシーケンスも最適解を見つけるのに役立ちます
2. デコード: デコードとは、生成されたバイナリ シーケンスを対応するドメインにマッピングすることを意味します. バイナリによって生成される値は非常に大きいため. たとえば、上記の例では、ドメインは 0-10 で精度は 0.01 であるため、バイナリの長さはは 10 です。マッピングがない場合、どのバイナリも定義 10 のドメインを超えます。
復号化が完了すると、初期化母集団はすべて、定義のドメイン内のランダムな n (生成されたバイナリ シーケンスの数) のポイントになります。
3. 適応度の計算: 定義領域でランダムにデコードされた点を計算します. 例ではy = x^2 を適応度関数と呼びます.
適応度の計算とは、実際にはこれらの値に対応する y (x) を計算することです。たとえば、最大値が必要な場合は、ランダムに生成された x を見て、y が大きい方を操作します。この操作を繰り返せば最大値がわかる
4.ルーレットは父系の生存確率を選択します:n点がランダムに生成されると、適応度関数が計算された後、対応するn個の関数値が生成されます。関数値が大きいほど、彼が優れていると言えます (最大値を計算したい場合)。その場合、彼が生き残る確率は高くなります。対応する方法がルーレットで、例えば生成されるyの値が2と9の場合、2/(2+9)と9/(2+9)が2と9が生き残る確率で、これがルーレットです。
注: これは単なる確率であり、絶対的な 9 > 2 ではありません。9 回生存する必要があります
5. 交叉: 交叉とは、父方の系統のバイナリに従ってランダムなビットを交換する操作であり、継承のアイデアが表示されます。
6. 突然変異: 突然変異とは、極値から飛び出し、子孫のバイナリ コードをランダムに反転させることです。0->1,1->0
具体的な実装方法はコードで説明
3. コードの実装
図に示すように、この章の遺伝的アルゴリズムは、y = x^2 の最大値を計算することです。
遺伝的アルゴリズムの定義は次のとおりです。
その中で、mutation_rate はバイナリ シーケンスの突然変異の確率であり、大きすぎてはいけません。そうしないと、子孫が親と完全に異なる場合、遺伝的アルゴリズムはその意味を失います。
parent_rate は親世代に保存される確率です.例えば全部で 10 個の母集団があり, 0.3 は 3 つの親を保存します.ここでの保存方法はルーレットによって実現されます.
達成される効果は次のとおりです。
3.1 ユーティリティ機能
コードのモジュール化のために、ここの utils に格納されている 4 つの関数があります。
3.1.1 目的関数
はフィットネス関数です
# 目标函数
def function(x):
# y = np.sin(x) * np.exp(-x)
y = x**2
return y
3.1.2 デコード
decode は、渡されたバイナリ シーケンス マトリックス bit_matrix (n*m、n は母集団の数、m はバイナリの長さ) に従ってエンコードし、開始と終了のドメインで n 個の 10 進引数を生成します。
# 将二进制编码为十进制,并映射到定义域中
def decode(bit_matrix,num_group,start,end,length):
ret = np.zeros(num_group)
temp = [] # 保存转换的十进制数
for i in range(num_group):
tmp = int(''.join(map(lambda x:str(x),bit_matrix[i])),2) # 获得每一条染色体的十进制
ret[i] = start + (end - start) * tmp / (pow(2,length)-1) # 映射回原始的定义域
temp.append(tmp)
return temp,ret
3.1.3 クロスオーバー
ここで実装されるメソッドは異なります
この章で実装された遺伝的アルゴリズムでは、母集団の数は固定されています 。つまり、初期化は 10 で、親が 3 を保持した後、交叉によって生成された子は 7 だけです。これは、変数のカウント変数です。コード。
parent_groups は、保持後の 3 つではなく、親のすべての母集団です。
クロスオーバーを実現する方法は、ランダムなバイナリ シーケンスの 2 つのセットをクロスすることです。ラストリターン
# 交叉繁殖
def cross(count,parents_groups,length,cross_num=2):
childen = [] # 子代
while len(childen) != count: # 保证子代的数量和父代一样
index = np.random.choice(np.arange(length),cross_num,replace=False) # 随机交换cross_num个基因
male = parents_groups[np.random.randint(0,len(parents_groups+1))] # 从父代中随机挑选两个交叉繁殖
female = parents_groups[np.random.randint(0,len(parents_groups+1))]
childen_one = male.copy()
childen_two = female.copy()
childen_one[index] = female[index] # 交换父母双方的基因产生两个子代
childen.append(childen_one)
if len(childen) == count:
break
childen_two[index] = male[index]
childen.append(childen_two)
return np.array(childen)
3.1.4 バリエーション
突然変異は集団が突然変異を生み出すためのもので、ランダムに生成された新しい子孫が極端な値から飛び出すことができるかもしれません
実装も非常にシンプルで、num_mutation はバイナリ ミューテーションの数を制御できます。
# 变异
def mutation(children,mutation_rate,length,num_mutation=1):
children_mutation = []
for i in range(len(children)):
tmp = children[i]
if np.random.random() < mutation_rate:
index = np.random.choice(np.arange(length),num_mutation,replace=False)
for j in range(num_mutation): # 变异
if tmp[index[j]] == 1:
tmp[index[j]] = 0
else:
tmp[index[j]]= 1
children_mutation.append(tmp)
return np.array(children_mutation)
3.2 主な機能部分
注意点がいくつかあります. 適応度を計算するときは以下の操作を行ってください.そうしないとルーレット選択時にエラーが発生します. 確率はマイナスにならないから
3.3 コード
主な機能部分:
import numpy as np
import matplotlib.pyplot as plt
from utils import decode,function,cross,mutation
# 设定超参数
start,end = 0,10
length = 10 # 染色体长度 bit,精度
num_group = 10 # 种群数量
iteration_time = 2000 # 迭代次数
mutation_rate = 0.1 # 变异率
parents_rate = 0.3 # 父代中的保存个数(概率)
# 初始化二进制种群
init_group = np.random.randint(0,2,size=(num_group,length))
parents_group = init_group # 父代
# 迭代
decode_parents_group = 0
for i in range(iteration_time):
# 将二进制种群转为十进制,并映射到定义域中
_, decode_parents_group = decode(bit_matrix=parents_group, num_group=num_group, start=start, end=end, length=length)
# 计算种群适应度
f = function(decode_parents_group)
f = (f - np.min(f))+1e-8 # 防止 f 为负值或 0
select = np.random.choice(np.arange(num_group),int(num_group*parents_rate),replace=True,p=f/sum(f))
best_parents_group = parents_group[select] # 父代中的保留
count = len(parents_group) - len(best_parents_group) # 计算差值
# 交叉繁殖
children = cross(count=count, parents_groups=parents_group, length=length)
children = np.concatenate((best_parents_group, children))
# 变异
children = mutation(children=children,mutation_rate=mutation_rate,length=length)
parents_group = children
fun = function(decode_parents_group)
x = np.linspace(start,end,100)
plt.plot(x,function(x),color='r')
plt.scatter(decode_parents_group,function(decode_parents_group))
plt.title('max is :%.4f' % np.max(fun))
plt.show()
ユーティリティ セクション:
import numpy as np
# 目标函数
def function(x):
# y = np.sin(x) * np.exp(-x)
y = x**2
return y
# 将二进制编码为十进制,并映射到定义域中
def decode(bit_matrix,num_group,start,end,length):
ret = np.zeros(num_group)
temp = [] # 保存转换的十进制数
for i in range(num_group):
tmp = int(''.join(map(lambda x:str(x),bit_matrix[i])),2) # 获得每一条染色体的十进制
ret[i] = start + (end - start) * tmp / (pow(2,length)-1) # 映射回原始的定义域
temp.append(tmp)
return temp,ret
# 交叉繁殖
def cross(count,parents_groups,length,cross_num=2):
childen = [] # 子代
while len(childen) != count: # 保证子代的数量和父代一样
index = np.random.choice(np.arange(length),cross_num,replace=False) # 随机交换cross_num个基因
male = parents_groups[np.random.randint(0,len(parents_groups+1))] # 从父代中随机挑选两个交叉繁殖
female = parents_groups[np.random.randint(0,len(parents_groups+1))]
childen_one = male.copy()
childen_two = female.copy()
childen_one[index] = female[index] # 交换父母双方的基因产生两个子代
childen.append(childen_one)
if len(childen) == count:
break
childen_two[index] = male[index]
childen.append(childen_two)
return np.array(childen)
# 变异
def mutation(children,mutation_rate,length,num_mutation=1):
children_mutation = []
for i in range(len(children)):
tmp = children[i]
if np.random.random() < mutation_rate:
index = np.random.choice(np.arange(length),num_mutation,replace=False)
for j in range(num_mutation): # 变异
if tmp[index[j]] == 1:
tmp[index[j]] = 0
else:
tmp[index[j]]= 1
children_mutation.append(tmp)
return np.array(children_mutation)
4. その他
ネットでの実装と矛盾している箇所が多く、よく分からない箇所があります
たとえば、親を保持する場合、繰り返し予約できますか?
この章の方法は問題ありません (繰り返さないために False に変更できます)。ここで個人的には、親を繰り返さずに保持することを選択した場合、予約された親は大から小への確率値に従って予約されると個人的に思います。 、だから初期化はあまり良くない 良いときは、極値の穴に陥りやすい
select = np.random.choice(np.arange(num_group),int(num_group*parents_rate),replace=True,p=f/sum(f))
例えば、突然変異率や突然変異の数が多すぎると、親が子孫に残した情報が完全に破壊され、継承の意味が失われます。
ここで y = np.sin(x) * np.exp(-x) を計算した結果は次のとおりです。