人工智慧 (13) 遺傳演算法

Jason990420發表於2019-12-23

檔案建立日期: 2019/12/23

最後修訂日期: None

相關軟體資訊:

Win 10 Python 3.7.2 deap 1.3.0 Numpy 1.17.3 scoop 0.7.1.1

參考檔案: AI with Python Tutorial

說明: 所有內容歡迎引用, 只需註明來源及作者, 本文內容如有錯誤或用詞不當, 敬請指正.

標題: 人工智慧 (13) 遺傳演算法

遺傳演算法 ( GA, Genetic Algorithms )

基於搜尋的演算法,基於自然選擇和遺傳學的概念。為給定問題提供了可能的解決方案池。然後進行重組和突變,產生了新的方案,它會不斷髮展更好的個人或解決方案,直到達到停止標準為止。

  1. 隨機生成初始種群。
  2. 選擇具有最佳適應性值的初始解決方案。
  3. 使用變異和交叉運算元重新組合選定的解決方案。
  4. 將後代插入種群。
  5. 如果不滿足停止條件,再回到2。

例 1. 隨機生成500個人, 45個基因(0或1), 經十代繁殖及突變出只有15個基因為1的後代

# -----------------------------------------------------------------------------
# Using Genetic Algorithms
# ------------------------------------------------------------------------------

import random
from deap import base, creator, tools

def eval_func(individual):
    # N個基因(01), 最好的基因為15個是1
    # 當符合條件, 表示N個基因是符合要求的
    # 函式返回符合的基因數, 最好是N個
    target_sum = 15
    return len(individual) - abs(sum(individual) - target_sum)

def create_toolbox(num_bits):
    # 定義FitnessMax為Fitness(一個計算方案質量的度量), 引數為1.0
    creator.create("FitnessMax", base.Fitness, weights=(1.0,))
    # 定義Individual為list(FitnessMax)
    creator.create("Individual", list, fitness=creator.FitnessMax)
    # 建立一個工具箱
    toolbox = base.Toolbox()
    # 定義attr_bool為random.randint(0,1)
    toolbox.register("attr_bool", random.randint, 0, 1)
    # 定義individual為重複執行45次的list(random.randint(0,1))
    toolbox.register("individual", tools.initRepeat, creator.Individual,
            toolbox.attr_bool, num_bits)
    # 定義population為list(individual)
    toolbox.register("population", tools.initRepeat, list, toolbox.individual)
    # 定義evaluate為eval_func函式
    toolbox.register("evaluate", eval_func)
    # 定義mate為輸入在某兩點位內容交換, 會修改引數內容
    toolbox.register("mate", tools.cxTwoPoint)
    # 定義mutate為布林值翻轉函式, 翻轉機率為0.05
    toolbox.register("mutate", tools.mutFlipBit, indpb=0.05)
    # 定義select為隨機選擇3箇中的最佳元素 (執行k次)
    toolbox.register("select", tools.selTournament, tournsize=3)
    return toolbox

num_bits = 45                               # 4501的隨機數
toolbox = create_toolbox(num_bits)          # 建立工具箱
random.seed(7)
population = toolbox.population(n=500)      # 人口為[[0/1隨機數*45]*500]
probab_crossing, probab_mutating = 0.5, 0.2 # 基因交換/突變機率
num_generations = 10                        # 十代
print('\nEvolution process starts')

fitnesses = list(map(toolbox.evaluate, population)) # 對所有'人口'作eval_func()
for ind, fit in zip(population, fitnesses):
    ind.fitness.values = (fit,)                     # 質量=eval_func計算值
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))

例 2. 符號迴歸問題

# -----------------------------------------------------------------------------
# Symbol Regression Problem - 5x^3 - 6x^2 + 8x = 1
# Note: This script cannot run under Python again, must quit from Python,
#       then execute it again.
# ------------------------------------------------------------------------------

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:                        # 另外定義的目的在避免除0出錯
        return 1
    return numerator / denominator

def eval_func(individual, points):
    # sum[(輸入表示式-內建表示式的差)^2]/點數, 內建表示式: 5x^3-6x^2+8x-1
    func = toolbox.compile(expr=individual)
    mse = ((func(x) - (5 * x**3 - 6 * x**2 + 8 * x - 1))**2 for x in points)
    return (math.fsum(mse) / len(points),)

def create_toolbox():
    pset = gp.PrimitiveSet("MAIN", 1)       # 定義"MAIN"只有一個輸入變數
    pset.addPrimitive(operator.add, 2)      # +,-(),*,/ 有兩個輸入
    pset.addPrimitive(operator.sub, 2)      # -(), cos, sin 有一個輸入
    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)          # rand101是一個運算值, 定義方式不同
    pset.addEphemeralConstant("rand101", lambda: random.randint(-1,1))
    pset.renameArguments(ARG0='x')          # 第0個輸入變數為x
    # 定義FitnessMax為Fitness(一個計算方案質量的度量), 引數為-1.0
    creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
    # 定義Individual為gp.PrimitiveTree(FitnessMax)
    # gp.PrimitiveTree是最佳化遺傳程式設計操作而專門格式化的樹狀結構
    creator.create("Individual",gp.PrimitiveTree,fitness=creator.FitnessMin)
    # 建立一個工具箱
    toolbox = base.Toolbox()
    # 定義expr為生成表示式, pset原生資料集, 高度1~2
    toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min_=1, max_=2)
    # 定義indivial為迭代呼叫Individual來自expr的輸出
    toolbox.register("individual", tools.initIterate,
            creator.Individual, toolbox.expr)
    # 定義population為list(individual)
    toolbox.register("population",tools.initRepeat,list, toolbox.individual)
    # 定義compile為python執行表示式, 輸入型式為pset原生資料集
    toolbox.register("compile", gp.compile, pset=pset)
    # 定義evaluate為eval_func函式,20個點 [-1 ~ 0.9]
    toolbox.register("evaluate", eval_func,
            points=[x/10. for x in range(-10,10)])
    # 定義select為隨機選擇3箇中的最佳元素 (執行k次)
    toolbox.register("select", tools.selTournament, tournsize=3)
    # 定義mate為輸入在某點內容交換, 會修改引數內容
    toolbox.register("mate", gp.cxOnePoint)
    # 定義expr_mut為產生一個表示式樹, 在具有同樣高度下, 高度在0~2之間
    toolbox.register("expr_mut", gp.genFull, min_=0, max_=2)
    # 定義mutate為在樹中隨機找一個點, 以expr_mut取代
    toolbox.register("mutate", gp.mutUniform, expr=toolbox.expr_mut, pset=pset)
    # 定義mate為對GP樹上的某些度量實施靜態限制, 避免生成無效後代, 最大高度17
    toolbox.decorate("mate", gp.staticLimit(key=operator.attrgetter("height"),
            max_value=17))
    # 定義mumate為對GP樹上的某些度量實施靜態限制, 避免生成無效後代, 最大高度17
    toolbox.decorate("mutate", gp.staticLimit(key=operator.attrgetter("height"),
            max_value=17))
    return toolbox

random.seed(7)
toolbox = create_toolbox()                              # 建立工具箱
population = toolbox.population(n=450)                  # 建立表示式n=450
hall_of_fame = tools.HallOfFame(1)                      # 耳出一個最佳個體
stats_fit = tools.Statistics(lambda x: x.fitness.values)# 統計函式stats_fit
stats_size = tools.Statistics(len)                      # 統計函式stats_size
mstats = tools.MultiStatistics(fitness=stats_fit, size=stats_size) # 多重統計
mstats.register("avg", np.mean) # 定義avg為np.mean
mstats.register("std", np.std)  # 定義std為np.std
mstats.register("min", np.min)  # 定義min為np.min
mstats.register("max", np.max)  # 定義max為np.max
probab_crossover = 0.4          # 基因交換機率
probab_mutate = 0.2             # 基因突變機率
number_gen = 10                 # 十代
# eaSimple演算法(輸入個體,工具箱,交換機率,突變機率,代數目, 統計, 最佳個體,記錄)
# 最簡單的進化演算法 Back, Fogel and Michalewicz, “Evolutionary Computation 1 :
# Basic Algorithms and Operators”, 2000.
population, log = algorithms.eaSimple(population, toolbox,
probab_crossover, probab_mutate, number_gen,
stats=mstats, halloffame=hall_of_fame, verbose=True)
                              fitness                              size 
           ------------------------------------------ ----------------------------------
gen nevals  avg    gen    max      min  nevals  std     avg   gen max min nevals   std 
 0    450  65.3059   0  106.2100 41.3678 450 10.33940 3.73556  0    7  2   450   1.62449
 1    238  62.0487   1 1216.7200 41.8757 238 55.06550 3.80444  1   10  1   238   1.77813
 2    232  55.6696   2   79.6906 41.5774 232  7.45130 3.96889  2   13  1   232   1.87946
 3    248  52.9092   3   88.0317 41.5774 248  7.83529 4.17333  3   13  1   248   1.83574
 4    232  50.9084   4  104.8650 32.7140 232  9.00519 4.87556  4   13  1   232   2.12552
 5    208  47.6437   5   97.6900 32.7140 208  7.60911 5.52444  5   15  1   208   2.4323 
 6    218  45.7589   6  149.3100 32.7140 218  9.39208 6.68667  6   15  1   218   2.82088
 7    231  43.4631   7  104.8650 26.5704 231  9.41258 7.89333  7   19  1   231   3.3196 
 8    224  41.3779   8  161.5110 18.1649 224 11.25450 9.32222  8   28  1   224   3.96156
 9    223  39.3688   9  187.9040 15.1825 223 14.03400 10.7533  9   28  1   223   3.98152
10    235  37.5933  10  258.5880 14.3541 235 17.65240 12.1467 10   30  1   235   4.4866
本作品採用《CC 協議》,轉載必須註明作者和本文連結
Jason Yang

相關文章