遺傳算法簡介


  遺傳算法是模擬生物在自然環境中的遺傳和進化過程而形成的一種自適應全局優化概率搜索算法。最優化問題的目標函數和約束條件種類繁多,有的是線性的,有的是非線性的;有的是連續的,有的是離散的;有的是單峰值的,有的是多峰值的。隨着研究的深入,人們逐漸認識到在很多復雜情況下要想完全精確地求出其最優解既不可能,也不現實,因而求出其近似最優解或滿意解是人們的主要着眼點之一。遺傳算法為解決這類問題提供了一個有效的途徑和通用框架,開創了一種新的全局優化搜索算法。

  傳統最優算法都是建立在確定性基礎上的搜索,在搜索過程中遇到一個決策點時,對於選a還是選b,其結果是確定的。比如貪婪法,就是按照貪婪策略選擇,同樣的條件下,每個決策選1000次結果都是一樣的。隨機算法就不會有這么確定的結果,它是一種帶啟發式的隨機搜索,非常適合一些傳統方法難以解決的復雜問題或非線性問題,在人工智能、自適應控制、機器學習等領域得到了廣泛的應用。

遺傳算法原理

  遺傳算法中將n維決策向量X=[x1  x... xn]用n個記號Xi(i=1,2,...,n)所組成的符號串X來表示:X = XX... Xn.  把每一個X看做一個遺傳基因,它的所有可能取值稱為等位基因。這樣X就可看做是由n個遺傳基因所組成的一個染色體。一般情況下染色體的長度n是固定的,但對一些問題n也可以是變化的。根據不同的情況這里的等位基因可以是一組整數,也可以是某一范圍內的實數值,或者是純粹的一個記號。最簡單的等位基因是由0和1這兩個整數組成的,相應的染色體就可表示為一個二進制符號串。這種編碼所形成的排列形式X是個體的基因型,與它對應的X值是個體的表現型。通常個體的表現型和其基因型是一一對應的,但有時也允許基因型和表現型是多對一的關系。染色體X也稱為個體X,對於每一個個體X,要按照一定的規則確定出其適應度。個體的適應度與其對應的個體表現型X的目標函數值相關聯,X越接近於目標函數的最優點,其適應度越大;反之其適應度越小。遺傳算法中決策變量X組成了問題的解空間。對問題最優解的搜索是通過對染色體X的搜索過程來進行的,從而由所有的染色體X就組成了問題的搜索空間。

  生物的進化是以群體為主體的。與此相對應遺傳算法的運算對象是由M個個體所組成的集合,稱為種群。與生物一代一代的自然進化過程相類似,遺傳算法的運算過程也是一個反復迭代過程。第t代群體記做P(t),經過一代遺傳和進化后得到第t+1代群體,它們也是由多個個體組成的集合,記做P(t+1)。這個群體不斷地經過遺傳和進化操作,並且每次都按照優勝劣汰的規則將適應度較高的個體更多地遺傳到下一代,這樣最終在群體中將會得到一個優良的個體X,它所對應的表現型X將達到或接近於問題的最優解X*

  生物的進化過程主要是通過染色體之間的交叉和染色體的變異來完成的。與此相對應,遺傳算法中最優解的搜索過程也模仿生物的這個進化過程,使用所謂的遺傳算子(genetic operator)作用於群體P(t)中,進行下述遺傳操作從而得到新一代群體P(t+1)

1. 選擇(selection)

  根據各個個體的適應度,按照一定的規則或方法,從第t代群體P(t)中選擇出一些優良的個體遺傳到下一代群體P(t+1)中。

2. 交叉(crossover)

  將群體P(t)內的各個個體隨機搭配成對,對每一對個體以某個概率(稱為交叉概率,crossover rate)交換它們之間的部分染色體。

3. 變異(mutation)

  對群體P(t)中的每一個個體,以某一概率(稱為變異概率,mutation rate)改變某一個或某一些基因座上的基因值為其他的等位基因。

遺傳算法的運算過程 

  遺傳算法的運算過程如下圖所示:

步驟1:初始化。設置進化代數計數器t=0,設置最大進化代數T;隨機生成M個個體作為初始群體P(0);

步驟2:個體評價。計算群體P(t)中各個個體的適應度;

步驟3:選擇運算。將選擇算子作用於群體;

步驟4:交叉運算。將交叉算子作用於群體;

步驟5:變異運算。將變異算子作用於群體。群體P(t)經過選擇、交叉、變異運算之后得到下一代群體P(t+1);

步驟6:終止條件判斷。若t≤T,則t=t+1,轉到步驟2;若t>T,則以進化過程中所得到的具有最大適應度的個體作為最優解輸出,終止計算。

計算示例

  以Ras函數(Rastrigin's Function)為目標函數,求其在x1,x2∈[-5,5]上的最小值。這個函數對模擬退火、進化計算等算法具有很強的欺騙性,因為它有非常多的局部最小值點和局部最大值點,很容易使算法陷入局部最優,而不能得到全局最優解。如下圖所示,該函數只在(0,0)處存在全局最小值0。

(1)變量編碼

  遺傳算法的運算對象是表示個體的符號串,所以必須把變量x1,x2編碼為一種符號串。這里將[-5,5]的區間內的數用10位2進制數表示,將它們連接在一起所組成的20位無符號二進制整數就形成了個體的基因型,表示一個可行解。例如基因型X=00000000001111111111所對應的表現型是X =[-5,5]T。個體的表現型和基因型X之間可通過編碼和解碼程序相互轉換。將10位2進制數碼轉b換為[-5,5]區間內的實數公式為:f(b) = 10*b/1023 - 5

(2)初始群體的產生

  遺傳算法是對群體進行的進化操作,需要給其准備一些表示起始搜索點的初始群體數據。種群的個體數決定了遺傳算法的多樣性。數目越多,種群的多樣性越好,但是會增加計算量,降低運行效率。但如果數目過少,會因為遺傳多樣性降低而導致比較容易出現早熟現象。所謂早熟問題,就是在遺傳運算初期,少數個體適應度非常高(可能是局部最優解),這樣在遺傳過程中,這些個體在下一代所占的比例很高,使得交叉和變異對種群多樣性的作用被嚴重降低,種群多樣性無法保證,最終因為局部最優解的存在而錯過全局最優解。一般建議種群數目取值最小為20。本例中群體規模的大小取為50,即群體由50個個體組成,每個個體可通過隨機方法產生。

(3)適應度計算

  遺傳算法中以個體適應度的大小來評定各個個體的優劣程度,從而確定其遺傳機會的大小。本例中,目標函數總取非負值,但是以求函數最小值為優化目標,考慮將1/(Ras(X)+0.01)作為個體的適應度,目標函數Ras(X)的值越大,個體適應度越低。為計算函數的目標值須先對個體基因型X進行解碼。由於目標函數可能有正有負,有時求最大值,有時求最小值,因此需要在目標函數與適應度函數之間進行變換。

  適應度函數的設計主要應滿足以下幾個條件:

  1.單值、連續、非負。這個條件很容易理解和實現。

  2.合理、一致性。要求適應度反映對應解的優劣程度,這個條件的達成往往比較難以衡量

  3.計算量小。適應度函數設計應盡可能簡單,這樣可以減少計算時間,降低計算成本。

  4.通用性強。適應度對某類具體問題,應盡可能通用,最好無需使用者改變適應度函數中的參數。

(4)選擇運算

  選擇運算把當前群體中適應度較高的個體按某種規則或模型遺傳到下一代群體中。一般要求適應度較高的個體將有更多的機會遺傳到下一代群體中。本例中采用與適應度成正比的概率(又稱為輪盤賭選擇)來確定各個個體復制到下一代群體中的數量。其具體操作過程是首先計算出群體中所有個體的適應度的總和Σfi,其次計算出每個個體的相對適應度的大小fi/Σfi. 每個概率值組成一個區域,全部概率值之和為1。最后再產生一個0到1之間的隨機數依據該隨機數出現在上述哪一個概率區域內來確定各個個體被選中的次數。為了進行說明,假設某種群中有5個個體,每個個體的選擇概率(fi/Σfi)分別是:0.1、0.2、0.1、0.4、0.2

  假如在區間[0,1]上隨機生成一個點0.53,根據累積概率0.4<0.53<0.8,於是第4個個體被選中。由上圖可以看出,個體的選擇概率(或相對適應度fi/Σfi)越大,被選中的概率就越大。

(5)交叉運算  

  交叉運算是遺傳算法中產生新個體的主要操作過程,它以某一概率相互交換某兩個個體之間的部分染色體。本例采用單點交叉的方法,其具體操作過程是:首先對群體進行隨機配對,其次隨機設置交叉點位置,最后再相互交換配對染色體之間的部分基因。交叉概率決定了新產生個體的頻度,這是保證種群多樣性的關鍵參數之一。交叉概率太小,會導致新個體產生速度慢,影響種群多樣性,抑制早熟現象的能力就會較差;但是交叉概率不能太大,過高的交叉概率會使基因的遺傳變得不穩定,優良的基因比較容易被破壞,使得遺傳算法的性能近似於隨機搜索算法的性能。一般交叉概率取0.4~0.9之間的值,0.8是比較常用的值。在本例中進行交叉時先生成一個0-1區間內的隨機概率p,如果兩個待交叉的染色體不同並且 p 小於設定好的交叉概率pc,那么就分別在2進制編碼的0:9和10:19位中間隨機選取一個位置,交換對應的基因片段,從而形成兩個新的個體。假如兩條染色體分別如下(染色體中的0:9位代表x2,10:19位代表x1):

  1111 0011 1100 0001 1000 
  0100 0011 0100 1001 1001 

在第7位和第16位處交叉(兩點交叉),結果為:

  1110 0011 1100 1001 1000 
  0101 0011 0100 0001 1001 

(6)變異運算  

  變異運算是對個體的某一個或某一些基因座上的基因值按某一較小的概率進行改變,它也是產生新個體的一種操作方法。變異概率太小不利於產生新個體,對種群的多樣性有影響。但是變異概率太大也會使基因的遺傳變的不穩定,優良的基因比較容易被破壞。根據遺傳學原理,基因突變是一個小概率事件,在遺傳算法中,變異算子對種群的影響也應遠遠小於交叉算子。一般建議變異概率取值小於0.2。變異算子主要解決兩個問題,一是如何確定變異位置,另一個是如何進行基因變異。常用的變異算子有:單點變異--對基因編碼隨機選擇一個點,以隨機概率進行變異運算;固定位置變異--對基因上一個或幾個固定位置上的基因片段,以隨機的概率進行變異;均勻變異--對基因上的每個片段,都使用均勻分布的隨機數,以較小的隨機概率進行變異運算。具體的變異算法與基因編碼方式有關,比如二進制編碼和浮點數編碼,直接將某一位從1變0,或者從0變1就實現了變異。對符號編碼的基因,直接將某個位置上的符號替換成符合該位置要求的其他符號即可實現變異。對於屬性序列編碼方式,改變某個屬性的值也算是實現了變異。總之,變異只是一個抽象的要求,具體的算法實現則千姿百態。

# -*- coding:cp936 -*-
import numpy as np
import matplotlib.pyplot as plt

# 初始化種群
def init():
    return np.random.randint(0, 0xFFFFF + 1, size = M)
    
# 編碼轉換為決策變量
def B2X(popB,popX):
    for i in range(M):
        x1 = 10.0 * ((popB[i] & 0xFFC00) >> 10) / 1023.0 - 5.0
        x2 = 10.0 * (popB[i] & 0x003FF) / 1023.0  - 5.0
        popX[:,i] = np.array([x1, x2])
        
# 目標函數定義
def ras(x):
    y = 20 + x[0]**2 + x[1]**2 - 10*(np.cos(2*np.pi*x[0])+np.cos(2*np.pi*x[1]))
    return y

# 適應度計算
def getfitness(popB,popX):    
    B2X(popB,popX)
    fitness = 1.0 / (ras(popX) + 0.01)
    return fitness
  
# 依據適應度選擇要進行繁殖的個體    
def selection(fitness,popB):
    select_probability = fitness/sum(fitness)
    cumulative_sum = np.cumsum(select_probability)
    indexes = np.searchsorted(cumulative_sum, np.random.random(M))
    # resample according to indexes
    popB[:] = popB[indexes]

# 進行基因交叉,實現基因交換
def crossover(popB): 
    np.random.shuffle(popB)     # 隨機打亂種群中個體的順序(種群內前一個與后一個配對)
    for i in range(M/2):  
        p = np.random.random()  # 隨機生成一個0~1內的數
        if p < pc:              # 如果這個數落在交叉概率區間內,則交換部分基因
            index1 = np.random.randint(0,10)    # 隨機選擇交叉點
            if (popB[2*i] & (1<<index1)) != (popB[2*i+1] & (1<<index1)):
                if (popB[2*i] & (1<<index1)):   # 個體1的該位為1(個體2對應位為0)
                    popB[2*i] &= ~(1<<index1)   # 個體1該位變為0
                    popB[2*i+1]|= (1<<index1)   # 個體2該位變為1
                else:                           # 個體1的該位為0(個體2對應位為1)
                    popB[2*i] |=  (1<<index1)   # 個體1該位變為1
                    popB[2*i+1]&= ~(1<<index1)  # 個體2該位變為0             
            
            index2 = np.random.randint(10,20)   # 隨機選擇交叉點
            if (popB[2*i] & (1<<index2)) != (popB[2*i+1] & (1<<index2)):
                if (popB[2*i] & (1<<index2)):   # 個體1的該位為1(個體2對應位為0)
                    popB[2*i] &= ~(1<<index2)   # 個體1該位變為0
                    popB[2*i+1] |= (1<<index2)  # 個體2該位變為1
                else:                           # 個體1的該位為0(個體2對應位為1)
                    popB[2*i] |=  (1<<index2)   # 個體1該位變為1
                    popB[2*i+1]&= ~(1<<index2)  # 個體2該位變為0

# 進行基因變異
def mutation(popB):   
    for i in range(M): 
        p = np.random.random()  # 隨機生成一個0~1內的數
        if p < pm:              # 如果這個數落在變異概率區間內,則進行變異處理
            index = np.random.randint(0,20) # 采用單點變異
            if (popB[i] & (1<<index)):      # 個體i的該位為1
                popB[i] &= ~(1<<index)      # 個體i的該位變為0
            else:
                popB[i] |=  (1<<index)     # 個體i的該位變為1
                
    
if __name__ == '__main__':
    M = 50          # 種群大小
    T = 100     # 進化代數
    pc = 0.8     # 交叉概率
    pm = 0.05     # 變異概率
   
    popB = np.zeros(M, dtype='uint32')   # 由編碼表示的種群    
    popX = np.zeros((2,M))               # 由決策變量表示的種群
    fitness = np.zeros(M)                # 個體適應度
    fitness_record = np.zeros((2,T))     # 記錄適應度隨進化代數的變化
    
    popB = init() # 隨機初始化種群
    t = 0
    while t < T:
        fitness = getfitness(popB,popX)  # 計算適應度  
        i = np.argmax(fitness)
        fitness_record[0,t] = sum(fitness)/M
        fitness_record[1,t] = fitness[i]
        selection(fitness,popB)          # 選擇
        crossover(popB)                  # 交叉
        mutation(popB)                   # 變異
        t = t + 1
        
    max_index = np.argmax(fitness)
    print "X: " , popX[:,max_index]
    print "Y: ", ras(popX[:,max_index])  # 計算最小值
    
    plt.plot(np.arange(T),fitness_record[0,:],color='b',label='mean')
    plt.plot(np.arange(T),fitness_record[1,:],color='r',label='best')
    plt.legend(loc='best')
    plt.show()

  運行幾次結果如下圖所示,可以發現搜索會經常陷入局部極小值中(早熟),黃色背景的那一次運行找到了全局最小值。對比MATLAB遺傳算法工具箱中的結果,發現它也會經常陷入局部極小值。。。至於怎么解決這個問題,這又是另一個問題了。

  適應度隨着進化代數變化曲線如下圖所示。其中紅色曲線為每一次進化時種群內最大適應度,藍色曲線為種群的平均適應度。可以發現,隨着進化的推進,它們都趨於增大。

遺傳算法的特點

  為解決各種優化計算問題,人們提出了各種各樣的優化算法,如單純形法、梯度法、動態規划法、分支定界法等。這些優化算法各有各的長處,各有各的適用范圍,也各有各的限制。遺傳算法是一類可用於復雜系統優化計算的魯棒搜索算法,與其他一些優化算法相比它主要有下述幾個特點:

  (1)遺傳算法以決策變量的編碼作為運算對象。傳統的優化算法往往直接利用決策變量的實際值本身來進行優化計算,但遺傳算法不是直接以決策變量的值,而是以決策變量的某種形式的編碼為運算對象。這種對決策變量的編碼處理方式,使得我們在優化計算過程中可以借鑒生物學中染色體和基因等概念,可以模仿自然界中生物的遺傳和進化等機理,也使得我們可以方便地應用遺傳操作算子。特別是對一些無數值概念,或很難有數值概念而只有代碼概念的優化問題,編碼處理方式更顯示出了其獨特的優越性。

  (2)遺傳算法直接以目標函數值作為搜索信息。傳統的優化算法不僅需要利用目標函數值,而且往往需要目標函數的導數值等其他一些輔助信息才能確定搜索方向。而遺傳算法僅使用由目標函數值變換來的適應度函數值,就可確定進一步的搜索方向和搜索范圍,無須目標函數的導數值等其他一些輔助信息。這個特性對很多無法或很難求導數的目標函數,或導數不存在的函數的優化問題,以及組合優化問題等,應用遺傳算法就顯得比較方便,因為它避開了函數求導這個障礙。再者,直接利用目標函數值或個體適應度,也使得我們可以把搜索范圍集中到適應度較高的部分搜索空間中,從而提高了搜索效率。

  (3)遺傳算法同時使用多個搜索點的搜索信息。傳統的優化算法往往是從解空間中的一個初始點開始最優解的迭代搜索過程。單個搜索點所提供的搜索信息畢竟不多,所以搜索效率不高,有時甚至使搜索過程陷於局部最優解而停滯不前。遺傳算法從由很多個體所組成的一個初始群體開始最優解的搜索過程,而不是從一個單一的個體開始搜索。對這個群體所進行的選擇、交叉、變異等運算,產生出的乃是新一代的群體,在這之中包括了很多群體信息。這些信息可以避免搜索一些不必搜索的點,所以實際上相當於搜索了更多的點,這是遺傳算法所特有的一種隱含並行性

  (4)遺傳算法使用概率搜索技術。很多傳統的優化算法往往使用的是確定性的搜索方法,一個搜索點到另一個搜索點的轉移有確定的轉移方法和轉移關系,這種確定性往往也有可能使得搜索永遠達不到最優點,因而也限制了算法的應用范圍。而遺傳算法屬於一種自適應概率搜索技術,其選擇、交叉、變異等運算都是以一種概率的方式來進行的,從而增加了其搜索過程的靈活性。雖然這種概率特性也會使群體中產生一些適應度不高的個體,但隨着進化過程的進行,新的群體中總會更多地產生出許多優良的個體。實踐和理論都已證明,在一定條件下遺傳算法總是以概率1收斂於問題的最優解。當然,交叉概率和變異概率等參數也會影響算法的搜索效果和搜索效率,所以如何選擇遺傳算法的參數在其應用中是一個比較重要的問題。而另一方面,與其他一些算法相比,遺傳算法的魯棒性又會使得參數對其搜索效果的影響盡可能低。

 

參考:

http://blog.csdn.net/u010902721/article/details/23531359

http://blog.csdn.net/u012176591/article/details/45039673

http://garfileo.is-programmer.com/2011/2/19/hello-ga.24563.html

http://www.obitko.com/tutorials/genetic-algorithms/ga-basic-description.php

https://gist.github.com/bellbind/741853


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM