遺傳演算法


本章詳細討論AI的遺傳演算法。

什麼是遺傳演算法?

遺傳演算法(GA)是基於自然選擇和遺傳學概念的基於搜尋的演算法。遺傳演算法是稱為進化計算的更大分支的一個子集。

GAs由John Holland及其在密歇根大學的學生和同事開發,最著名的是David E.Goldberg。自那以來,它一直在嘗試各種優化問題並取得了很高的成功。

在GAs中,我們為給定問題提供了一系列可能的解決方案。這些解決方案然後經歷重組和突變(如在自然遺傳學中),產生新的兒童,並且該過程在各代重複。每個個體(或候選解決方案)都被分配一個適應值(基於其目標函式值),並且適合者個體被賦予更高的配偶並產生更適合個體的機會。這符合達爾文適者生存理論。

因此,它不斷發展更好的個人或解決方案,直到達到停止標準。

遺傳演算法在本質上具有充分的隨機性,但它們比隨機區域性搜尋(我們只是嘗試隨機解決方案,追蹤迄今為止最好的)的效能好得多,因為它們也在利用歷史資訊。

如何使用遺傳演算法優化問題?

優化是使設計,狀況,資源和系統儘可能有效。 以下框圖顯示了優化過程 -

GA機制優化過程的階段

以下是用於優化問題的GA機制的一系列步驟。

  • 第1步 - 隨機生成初始群體。
  • 第2步 - 選擇具有最佳適應值的初始解決方案。
  • 第3步 - 使用變異和交叉運算元重組選定的解決方案。
  • 第4步 - 將後代插入群體。
  • 第5步 - 現在,如果停止條件得到滿足,則返回具有最佳適應值的解。 否則,請轉到第2步。

安裝必要的軟體包

要在Python中使用遺傳演算法來解決這個問題,我們將使用一個稱為DEAP的功能強大的GA包。 它是用於快速建立原型和測試思想的新型演化計算框架庫。在命令提示字元下使用以下命令來安裝此軟體包 -

pip install deap

如果您使用的是anaconda環境,則可以使用以下命令安裝deap -

conda install -c conda-forge deap

使用遺傳演算法實現解決方案

本節向您介紹使用遺傳演算法實現解決方案。

生成位元型樣

以下範例顯示了如何根據One Max問題生成一個包含15個字串的位串。

如下所示匯入必要的軟體包 -

import random
from deap import base, creator, tools

定義評估函式。 這是建立遺傳演算法的第一步。

def eval_func(individual):
   target_sum = 15
   return len(individual) - abs(sum(individual) - target_sum)

現在,使用正確的引數建立工具箱 -

def create_toolbox(num_bits):
   creator.create("FitnessMax", base.Fitness, weights=(1.0,))
   creator.create("Individual", list, fitness=creator.FitnessMax)

初始化工具箱

toolbox = base.Toolbox()
toolbox.register("attr_bool", random.randint, 0, 1)
toolbox.register("individual", tools.initRepeat, creator.Individual,
   toolbox.attr_bool, num_bits)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

註冊計算操作符 -

toolbox.register("evaluate", eval_func)

現在,註冊交叉運算子 -

toolbox.register("mate", tools.cxTwoPoint)

註冊一個可變運算子 -

toolbox.register("mutate", tools.mutFlipBit, indpb = 0.05)

定義育種操作符 -

toolbox.register("select", tools.selTournament, tournsize = 3)
return toolbox
if __name__ == "__main__":
   num_bits = 45
   toolbox = create_toolbox(num_bits)
   random.seed(7)
   population = toolbox.population(n = 500)
   probab_crossing, probab_mutating = 0.5, 0.2
   num_generations = 10
   print('\nEvolution process starts')

評估整個人口 -

fitnesses = list(map(toolbox.evaluate, population))
for ind, fit in zip(population, fitnesses):
   ind.fitness.values = fit
print('\nEvaluated', len(population), 'individuals')

經過幾代人的建立和疊代 -

for g in range(num_generations):
   print("\n- Generation", g)

選擇下一代個人 -

offspring = toolbox.select(population, len(population))

現在,克隆選定的個人 -

offspring = list(map(toolbox.clone, offspring))

對後代應用交叉和變異 -

for child1, child2 in zip(offspring[::2], offspring[1::2]):
   if random.random() < probab_crossing:
   toolbox.mate(child1, child2)

刪除孩子的適應值

del child1.fitness.values
del child2.fitness.values

現在,應用突變 -

for mutant in offspring:
   if random.random() < probab_mutating:
   toolbox.mutate(mutant)
   del mutant.fitness.values

評估與無效的健身個體 -

invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
fitnesses = map(toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
   ind.fitness.values = fit
print('Evaluated', len(invalid_ind), 'individuals')

現在,用下一代個體替代人口 -

population[:] = offspring

列印當代人的統計資料 -

fits = [ind.fitness.values[0] for ind in population]
length = len(population)
mean = sum(fits) / length
sum2 = sum(x*x for x in fits)
std = abs(sum2 / length - mean**2)**0.5
print('Min =', min(fits), ', Max =', max(fits))
print('Average =', round(mean, 2), ', Standard deviation =',
round(std, 2))
print("\n- Evolution ends")

列印最終輸出 -

   best_ind = tools.selBest(population, 1)[0]
   print('\nBest individual:\n', best_ind)
   print('\nNumber of ones:', sum(best_ind))
Following would be the output:
Evolution process starts
Evaluated 500 individuals
- Generation 0
Evaluated 295 individuals
Min = 32.0 , Max = 45.0
Average = 40.29 , Standard deviation = 2.61
- Generation 1
Evaluated 292 individuals
Min = 34.0 , Max = 45.0
Average = 42.35 , Standard deviation = 1.91
- Generation 2
Evaluated 277 individuals
Min = 37.0 , Max = 45.0
Average = 43.39 , Standard deviation = 1.46
… … … …
- Generation 9
Evaluated 299 individuals
Min = 40.0 , Max = 45.0
Average = 44.12 , Standard deviation = 1.11
- Evolution ends
Best individual:
[0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 
 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0,
 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1]
Number of ones: 15

符號回歸問題

這是遺傳程式設計中最著名的問題之一。 所有符號回歸問題都使用任意資料分布,並嘗試用符號公式來擬合最準確的資料。 通常,像RMSE(均方根誤差)這樣的度量用於度量個體的適應度。 這是一個經典的回歸問題,這裡我們使用方程:5x3-6x2 + 8x = 1。 我們需要按照上述範例中的所有步驟進行操作,但主要部分是建立基元集,因為它們是個人的構建基塊,因此可以開始評估。 這裡將使用經典的基元集。

以下Python程式碼詳細解釋了這一點 -

import operator
import math
import random
import numpy as np
from deap import algorithms, base, creator, tools, gp
def division_operator(numerator, denominator):
   if denominator == 0:
      return 1
   return numerator / denominator
def eval_func(individual, points):
   func = toolbox.compile(expr=individual)
   return math.fsum(mse) / len(points),
def create_toolbox():
   pset = gp.PrimitiveSet("MAIN", 1)
   pset.addPrimitive(operator.add, 2)
   pset.addPrimitive(operator.sub, 2)
   pset.addPrimitive(operator.mul, 2)
   pset.addPrimitive(division_operator, 2)
   pset.addPrimitive(operator.neg, 1)
   pset.addPrimitive(math.cos, 1)
   pset.addPrimitive(math.sin, 1)
   pset.addEphemeralConstant("rand101", lambda: random.randint(-1,1))
   pset.renameArguments(ARG0 = 'x')
   creator.create("FitnessMin", base.Fitness, weights = (-1.0,))
   creator.create("Individual",gp.PrimitiveTree,fitness=creator.FitnessMin)
   toolbox = base.Toolbox()
   toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min_=1, max_=2)
   toolbox.expr)
   toolbox.register("population",tools.initRepeat,list, toolbox.individual)
   toolbox.register("compile", gp.compile, pset = pset)
   toolbox.register("evaluate", eval_func, points = [x/10. for x in range(-10,10)])
   toolbox.register("select", tools.selTournament, tournsize = 3)
   toolbox.register("mate", gp.cxOnePoint)
   toolbox.register("expr_mut", gp.genFull, min_=0, max_=2)
   toolbox.register("mutate", gp.mutUniform, expr = toolbox.expr_mut, pset = pset)
   toolbox.decorate("mate", gp.staticLimit(key = operator.attrgetter("height"), max_value = 17))
   toolbox.decorate("mutate", gp.staticLimit(key = operator.attrgetter("height"), max_value = 17))
   return toolbox
if __name__ == "__main__":
   random.seed(7)
   toolbox = create_toolbox()
   population = toolbox.population(n = 450)
   hall_of_fame = tools.HallOfFame(1)
   stats_fit = tools.Statistics(lambda x: x.fitness.values)
   stats_size = tools.Statistics(len)
   mstats = tools.MultiStatistics(fitness=stats_fit, size = stats_size)
   mstats.register("avg", np.mean)
   mstats.register("std", np.std)
   mstats.register("min", np.min)
   mstats.register("max", np.max)
   probab_crossover = 0.4
   probab_mutate = 0.2
   number_gen = 10
   population, log = algorithms.eaSimple(population, toolbox,
      probab_crossover, probab_mutate, number_gen,
      stats = mstats, halloffame = hall_of_fame, verbose = True)

請注意,所有基本步驟與生成位元型樣時使用的步驟相同。 這個程式會給出10代後的輸出為min,max,std(標準偏差)。