1.介紹
遺傳算法(Genetic Algorithm)遵循『適者生存』、『優勝劣汰』的原則,是一類借鑑生物界自然選擇和自然遺傳機制的隨機化搜索算法。
遺傳算法模擬一個人工種羣的進化過程,通過選擇(Selection)、交叉(Crossover)以及變異(Mutation)等機制,在每次迭代中都保留一組候選個體,重複此過程,種羣經過若干代進化後,理想情況下其適應度達到***近似最優***的狀態。
自從遺傳算法被提出以來,其得到了廣泛的應用,特別是在函數優化、生產調度、模式識別、神經網絡、自適應控制等領域,遺傳算法發揮了很大的作用,提高了一些問題求解的效率。
2.遺傳算法組成
- 編碼 -> 創造染色體
- 個體 -> 種羣
- 適應度函數
- 遺傳算子
- 選擇
- 交叉
- 變異
- 運行參數
- 是否選擇精英操作
- 種羣大小
- 染色體長度
- 最大迭代次數
- 交叉概率
- 變異概率
2.1 編碼與解碼
實現遺傳算法的第一步就是明確對求解問題的編碼和解碼方式。
對於函數優化問題,一般有兩種編碼方式,各具優缺點
- 實數編碼:直接用實數表示基因,容易理解且不需要解碼過程,但容易過早收斂,從而陷入局部最優
- 二進制編碼:穩定性高,種羣多樣性大,但需要的存儲空間大,需要解碼且難以理解
對於求解函數最大值問題,我選擇的是二進制編碼。
以我們的目標函數 f(x) = x + 10sin(5x) + 7cos(4x), x∈[0,9] 爲例。
假如設定求解的精度爲小數點後4位,可以將x的解空間劃分爲 (9-0)×(1e+4)=90000個等分。
2^16<90000<2^17,需要17位二進制數來表示這些解。換句話說,一個解的編碼就是一個17位的二進制串。
一開始,這些二進制串是隨機生成的。
一個這樣的二進制串代表一條染色體串,這裏染色體串的長度爲17。
對於任何一條這樣的染色體chromosome,如何將它復原(解碼)到[0,9]這個區間中的數值呢?
對於本問題,我們可以採用以下公式來解碼:
x = 0 + decimal(chromosome)×(9-0)/(2^17-1)
decimal( ): 將二進制數轉化爲十進制數
一般化解碼公式:
f(x), x∈[lower_bound, upper_bound]
x = lower_bound + decimal(chromosome)×(upper_bound-lower_bound)/(2^chromosome_size-1)
lower_bound: 函數定義域的下限
upper_bound: 函數定義域的上限
chromosome_size: 染色體的長度
通過上述公式,我們就可以成功地將二進制染色體串解碼成[0,9]區間中的十進制實數解。
2.2 個體與種羣
『染色體』表達了某種特徵,這種特徵的載體,稱爲『個體』。
對於本次實驗所要解決的一元函數最大值求解問題,個體可以用上一節構造的染色體表示,一個個體裏有一條染色體。
許多這樣的個體組成了一個種羣,其含義是一個一維點集(x軸上[0,9]的線段)。
2.3 適應度函數
遺傳算法中,一個個體(解)的好壞用適應度函數值來評價,在本問題中,f(x)就是適應度函數。
適應度函數值越大,解的質量越高。
適應度函數是遺傳算法進化的驅動力,也是進行自然選擇的唯一標準,它的設計應結合求解問題本身的要求而定。
2.4 遺傳算子
我們希望有這樣一個種羣,它所包含的個體所對應的函數值都很接近於f(x)在[0,9]上的最大值,但是這個種羣一開始可能不那麼優秀,因爲個體的染色體串是隨機生成的。
如何讓種羣變得優秀呢?
不斷的進化。
每一次進化都儘可能保留種羣中的優秀個體,淘汰掉不理想的個體,並且在優秀個體之間進行染色體交叉,有些個體還可能出現變異。
種羣的每一次進化,都會產生一個最優個體。種羣所有世代的最優個體,可能就是函數f(x)最大值對應的定義域中的點。
如果種羣無休止地進化,那總能找到最好的解。但實際上,我們的時間有限,通常在得到一個看上去不錯的解時,便終止了進化。
對於給定的種羣,如何賦予它進化的能力呢?
- 首先是選擇(selection)
- 選擇操作是從前代種羣中選擇***多對***較優個體,一對較優個體稱之爲一對父母,讓父母們將它們的基因傳遞到下一代,直到下一代個體數量達到種羣數量上限
- 在選擇操作前,將種羣中個體按照適應度從小到大進行排列
- 採用輪盤賭選擇方法(當然還有很多別的選擇方法),各個個體被選中的概率與其適應度函數值大小成正比
- 輪盤賭選擇方法具有隨機性,在選擇的過程中可能會丟掉較好的個體,所以可以使用精英機制,將前代最優個體直接選擇
- 其次是交叉(crossover)
- 兩個待交叉的不同的染色體(父母)根據交叉概率(cross_rate)按某種方式交換其部分基因
- 採用單點交叉法,也可以使用其他交叉方法
- 最後是變異(mutation)
- 染色體按照變異概率(mutate_rate)進行染色體的變異
- 採用單點變異法,也可以使用其他變異方法
一般來說,交叉概率(cross_rate)比較大,變異概率(mutate_rate)極低。像求解函數最大值這類問題,我設置的交叉概率(cross_rate)是0.6,變異概率(mutate_rate)是0.01。
因爲遺傳算法相信2條優秀的父母染色體交叉更有可能產生優秀的後代,而變異的話產生優秀後代的可能性極低,不過也有存在可能一下就變異出非常優秀的後代。這也是符合自然界生物進化的特徵的。
3.遺傳算法流程
在matlab下寫了個測試程序。
附上代碼https://github.com/yanshengjia/artificial-intelligence/tree/master/genetic-algorithm-for-functional-maximum-problem
測試結果
- 最優個體:00011111011111011
- 最優適應度:24.8554
- 最優個體對應自變量值:7.8569
- 達到最優結果需要的迭代次數:多次實驗後發現,達到收斂的迭代次數從20次到一百多次不等
迭代次數與平均適應度關係曲線(橫軸:迭代次數,縱軸:平均適應度)
有現成的工具可以直接使用遺傳算法,比如Matlab。
最後就再介紹一下如何在Matlab中使用遺傳算法。
在MATLAB中使用GA
1. 打開 Optimization 工具,在 Solver 中選擇 ga - genetic algorithm,在 Fitness function 中填入 @target
2. 在你的工程文件夾中新建 target.m,注意MATLAB的當前路徑是你的工程文件夾所在路徑
3. 在 target.m 中寫下適應度函數,比如
function [ y ] = target(x)
y = -x-10*sin(5*x)-7*cos(4*x);
end
*MATLAB中的GA只求解函數的(近似)最小值,所以先要將目標函數取反。
4. 打開 Optimization 工具,輸入 變量個數(Number of variables) 和 自變量定義域(Bounds) 的值,點擊 Start,遺傳算法就跑起來了。最終在輸出框中可以看到函數的(近似)最小值,和達到這一程度的迭代次數(Current iteration)和最終自變量的值(Final point)
5. 在 Optimization - ga 工具中,有許多選項。通過這些選項,可以設置下列屬性
- 種羣(Population)
- 選擇(Selection)
- 交叉(Crossover)
- 變異(Mutation)
- 停止條件(Stopping criteria)
- 畫圖函數(Plot functions)