本章詳細討論了人工智慧的遺傳算法。
What are Genetic Algorithms?
遺傳算法(GAs)是基於自然選擇和遺傳學概念的基於搜索的算法。GAs是一個更大的計算分支(稱爲進化計算)的子集。
GAs是由John Holland和他的學生以及密西根大學的同事開發的,最著名的是David E.Goldberg。此後,它在各種優化問題上進行了嘗試,並取得了很高的成功。
在GAs中,我們有一個給定問題的可能解決方案庫。這些解決方案然後進行重組和突變(就像在自然遺傳學中一樣),產生新的孩子,這個過程在不同的世代重複。每個個體(或候選解)都被賦予一個適應值(基於其目標函數值),而更適合的個體被賦予更高的交配機會,並產生更適合的個體個體。這符合達爾文的適者生存理論。
因此,它在幾代人中不斷進化出更好的個體或解決方案,直到達到一個停止標準。
遺傳算法在本質上是充分隨機的,但它們的性能比隨機局部搜索(我們只是嘗試隨機解決方案,跟蹤到目前爲止最好的)要好得多,因爲它們也利用了歷史信息。
How to Use GA for Optimization Problems?
優化是使設計、情況、資源和系統儘可能有效的行爲。下面的框圖顯示了優化過程&負;
Stages of GA mechanism for optimization process
下面是用於優化問題的GA機制的一系列步驟。
步驟1:隨機生成初始總體。
步驟2:選擇具有最佳適應值的初始解決方案。
步驟3:使用變異和交叉算子重新組合所選的解。
第4步:將後代插入種羣。
步驟5-現在,如果滿足停止條件,則返回具有最佳適應值的解決方案。否則轉到步驟2。
Installing Necessary Packages
爲了在Python中使用遺傳算法來解決這個問題,我們將使用一個強大的GA包DEAP。它是一個用於快速原型和思想測試的新型進化計算框架庫。我們可以在命令提示符下使用以下命令安裝此包−
pip install deap
如果您使用的是anaconda環境,則可以使用以下命令安裝deap−
conda install -c conda-forge deap
Implementing Solutions using Genetic Algorithms
本節將向您介紹使用遺傳算法實現解決方案的方法。
Generating bit patterns
下面的示例演示如何根據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)
刪除child的健身值
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
Symbol Regression Problem
這是遺傳規劃中最著名的問題之一。所有符號回歸問題都使用任意的數據分布,並試圖用符號公式擬合最精確的數據。通常,像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代之後給我們輸出最小,最大,標準差。