用python實現遺傳算法

2021-02-21 數據小白的進階之路
Hello,大家好!最近事情比較多,一個月沒有寫公眾號了,但也積累了些不錯的內容可以分享,今天就給大家介紹的是遺傳算法,並用python加以實現。在遺傳算法的學習過程中,在CSDN上看到一篇已有人分享的python代碼,因此直接借鑑過來,並結合《數學建模與數學實驗》進行補充。(電子書和原博主連結見文末)遺傳算法介紹

遺傳算法(Genetic Algorithm),是由美國的J.Holland教授於1975年首先提出的,是受達爾文的進化論的啟發,借鑑生物進化過程而提出的一種啟發式搜索算法。

生存競爭,適者生存:對環境適應度高的個體參與繁殖的機會比較多,後代就會越來越多。適應度低的個體參與繁殖的機會比較少,後代就會越來越少。

簡單來說就是一個繁殖的過程,會發生基因交叉(Crossover),基因突變(Mutation),適應度(Fitness)低的個體會被逐步淘汰,而適應度高的個體會越來越多。

操作思路初始化

初始化進化代數計數器t←0,最大進化代數T(一般100~500),初始化變異概率α(一般0.0001~0.2)、交叉概率β(一般0.4~0.99),隨機生成M個(一般20~100)個體作為初始群體P(t)。

編碼

遺傳算法中,首要問題就是如何對解進行編碼(解的形式),編碼影響到交叉、變異等運算,很大程度上決定了遺傳算法的效率。

二進位編碼:每個基因值為符號0和1所組成的二進位數;

格雷編碼:與二進位編碼類似,連續兩個整數所對應編碼僅一碼之差;

實數編碼:每個基因值用某一範圍內的一個實數來表示;

符號編碼:染色體編碼串中的基因值取自一個無數值含義,而只有代碼含義的符號集。

個體評價

計算P(t)中各個個體的適應度值。適應度函數:也稱評價函數,是根據目標函數確定的用於區分群體中個體好壞的標準,適應度函數值的大小是對個體的優勝劣汰的依據。

選擇

選擇運算的作用是對個體進行優勝劣汰:從父代群體中選取一些適應度高個體遺傳到下一代群體中。

輪盤賭法:又稱比例選擇算子,個體i被選中的概率Pi與其適應度成正比:

兩兩競爭:從父代中隨機地選取兩個個體,比較適應度值,保存優秀個體,淘汰較差的個體

排序選擇:根據各個體的適應度大小進行排序,然後基於所排序號進行選擇。

交叉

是指對兩個相互配對的染色體,依據交叉概率按某種方式相互交換其部分基因,從而形成兩個新的個體。

變異

是對群體中的個體的某些基因座上的基因值作變動,模擬生物在繁殖過程,新產生的染色體中的基因會以一定的概率出錯。

Python實現初始化

DNA_SIZE = 24
POP_SIZE = 100
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.005
N_GENERATIONS = 100

編碼

在遺傳算法中,最常用的就是二進位編碼,我們可以利用np.random.randint()來實現:

pop = np.random.randint(2, size = (POP_SIZE, DNA_SIZE*2))

numpy.random.randint(low, high=None, size=None, dtype='l')1、low:生成的數值最低要大於等於low;當hign = None時,生成的數值要在[0, low)區間內;2、high:如果使用這個值,則生成的數值在[low, high)區間;3、size:輸出隨機數的尺寸。默認是None的,僅僅返回滿足要求的單一隨機數;4、dtype:想要輸出的格式。如int64、int等等。

一般來說DNA長度越長,所包含的信息越多,結果就越精確,當然還得具體問題具體分析。

雖然二進位編碼實現容易,便於操作,但是對於一些連續函數的優化問題,由於其隨機性使得其局部搜索能力較差,當解迫近於最優解後,由於其變異後表現型變化很大,不連續,所以會遠離最優解,達不到穩定。

格雷碼的好處就在於能夠提高遺傳算法的局部搜索能力,相比二進位精度更高。

因此,給大家介紹如何把二進位轉化為格雷碼(原理大家自行查閱):

def BToG(pop):
    pop_g = []
    for i in pop:
        new = [i[0]]
        for j in range(1,len(i)):
            new.append(new[j-1]^i[j])
        pop_g.append(new)
    return np.array(pop_g)

「^」是按位異或運算符:當兩對應的二進位相異時,結果為1,相同結果為0。

當然,對其進行編碼後還需要再將其解碼回二進位:

def jiema(pop_g):

    pop = []
    for i in pop_g:
       new = [i[0]]
       for j in range(1,len(i)):
         if i[j] == 0:
           new.append(new[j-1])
         else:
           if new[j-1] == 0:
             new.append(1)
           else:
             new.append(0)
      pop.append(new)
    return np.array(pop)

不同的編碼,後續所對應的變換和突變也會有所不同,因為格雷碼和二進位碼比較相似,因此在這就先介紹這兩種,其它編碼歡迎加好友一起探討學習。

個體評價

通常評價函數可以由目標函數直接或間接改造得到。比如,目標函數或目標函數的倒數/相反數經常被直接用作適應度函數。一般情況下,適應度是非負的,並且總是希望適應度越大越好。比較好的適應度函數應單值、連續、非負、最大化。

def get_fitness(pop): 

  x1, x2 = translateDNA(pop)

  pred = F(x1, x2)

  return (pred - np.min(pred))

註:

translateDNA()是自定義的函數,用於將二進位碼轉換為數值,在測試部分會提到;F()是目標函數。

該評價函數是為了評價最大值的適應度,減去最小的結果是為了避免出現負數的情況;同理,如果我們想要最小值的適應度,就可以改成np.max (pred)-pred即可。

選擇

兩兩競爭和排序選擇都容易產生局部最優解的情況,因此遺傳算法中更多使用的是輪盤賭法:

def select(pop, fitness):
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
                           p=(fitness)/(fitness.sum()))
    return pop[idx]

參數說明:

numpy.random.choice(a, size=None, replace=True, p=None)

1、從a(只要是ndarray都可以,但必須是一維的)中隨機抽取數字,並組成指定大小(size)的數組;

2、replace:True表示可以取相同數字,False表示不可以取相同數字;

3、數組p:與數組a相對應,表示取數組a中每個元素的概率,默認為選取每個元素的概率相同。

變異

def mutation(child, MUTATION_RATE=0.003):
    if np.random.rand() < MUTATION_RATE:
        mutate_point = np.random.randint(0, DNA_SIZE)
        child[mutate_point] = child[mutate_point]^1

交叉:單點交叉&兩點交叉

單點交叉:

def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):
    new_pop = []
    for father in pop: 
        child = father
        if np.random.rand() < CROSSOVER_RATE:
            mother = pop[np.random.randint(POP_SIZE)]
            cross_points = np.random.randint(low=0, high=DNA_SIZE*2)
            child[cross_points:] = mother[cross_points:] 
            mutation(child) 
            new_pop.append(child)
    return new_pop

兩點交叉:

def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):
    new_pop = []
    for father in pop: 
        child = father
        if np.random.rand() < CROSSOVER_RATE: 
            mother = pop[np.random.randint(POP_SIZE)]
            cross_points_1 = np.random.randint(low=0, high=DNA_SIZE*2)
            cross_points_2 = np.random.randint(low= cross_points_1, high=DNA_SIZE*2)
            child[cross_points_1: cross_points_2] = mother[cross_points_1:cross_points_2]
            mutation(child)
            new_pop.append(child)
    return new_pop

到此,遺傳算法中所有的步驟我們均已實現,後面我們就以原博主所用的例子將所有步驟串起來,來進行測試。(此次測試與原博主不同的是採用格雷編碼以及兩點交叉遺傳)

我們再拿原博主的代碼跑一次做個對比。

從結果中可以看出,雖然我們用了新的編碼和遺傳形式進行計算,但結果並沒有原博主的好,所以具體問題具體分析,用算法前得多加思考,多加嘗試。

參考資料

1、https://blog.csdn.net/u014484715/article/details/40046307

2、《數學建模與數學實驗》

相關焦點

  • 一文讀懂遺傳算法工作原理(附Python實現)
    好了,現在我假設你已基本理解了遺傳算法的要領,那麼現在讓我們用它在數據科學的場景中應用一番5、遺傳算法的應用5.1 特徵選取試想一下每當你參加一個數據科學比賽,你會用什麼方法來挑選那些對你目標變量的預測來說很重要的特徵呢?
  • 送你一份Python算法的打怪升級路線圖 | 用 Python 實現各種常用算法
    對程式設計師來說,算法的重要性不言而喻,算法基礎是否紮實,間接決定了你是月薪5000,還是月薪50000。實驗樓上線了一門免費的Python算法入門課——【用 Python 實現各種常用算法】,主要包括兩部分內容:一是各種算法的基本原理講解,二是各種算法的代碼實現,內容由淺入深,非常適合剛入門Python的同學學習。
  • 遺傳算法簡介、基本原理及算法實現
    三、遺傳算法實現遺傳算法的機理相對複雜,而使用時可能需要編程實現,有沒有什麼比較方便的現成的工具能夠快速使用遺傳算法呢?答案!沒有!的反義詞!哈哈!在Matlab中已經由封裝好的工具箱命令,通過調用就能夠十分方便的使用遺傳算法啦!
  • 從零開始:用Python實現KNN算法
    算法(簡稱KNN算法)的邏輯很簡單,它易於理解和實現,是你可以使用的有力工具。通過學習這個教程,你將能夠用Python從0開始實現一個KNN算法。這個實現主要用來處理分類問題,我們將使用鳶尾花分類問題來演示這個例子。這個教程的學習要求是你能夠使用Python編碼,並且對實現一個KNN算法感興趣。
  • 最全 Python 算法實現資源匯總!
    整理 | Rachel責編 | Jane出品 | Python大本營(ID:pythonnews)【導語】數據結構與算法是所有人都要學習的基礎課程為了幫助大家在這個假期能提高學習效率,進階 Python 技能,筆者為大家推薦了一份用 Python代碼實現算法的資源帖,涵蓋從入門到高級的各類算法。下文中,筆者首先對項目的整體內容進行了一個歸納,之後為大家選取了幾個內容比較豐富的部分,供大家更高效地使用這一資源。
  • 用 python 實現各種排序算法
    /usr/bin/python  import sys    def insert_sort(a):      ''''' 插入排序    有一個已經有序的數據序列,要求在這個已經排好的數據序列中插入一個數,    但要求插入後此數據序列仍然有序。
  • 為什麼有些算法崗位,需要用C++而不是python?
    說到效率性方面問題就會涉及到程式語言的執行效率,如果不是解決實際的問題,單純比較程式語言執行的效率沒有太大的意義,一件事如果用兩種程式語言都能搞定的情況下,誰用的時間最短而且消耗的精力最小就採用誰,說到python語言在人工智慧裡面算是明星程式語言了,有人稱之為膠水語言,能夠把各種程式語言組合在一起工作
  • 什麼是遺傳算法?怎樣繪製遺傳算法流程圖
    遺傳算法是一種通過模擬自然進化過程搜索最優解的操作方法,遺傳算法有三個基本算子選擇、交叉和變異。對於遺傳算法我們也可以使用流程圖對其整個過程進行總結歸納,那要怎樣繪製遺傳算法流程圖呢?下面是分享的簡單操作方法,希望可以幫助大家。
  • 遺傳算法的基本概念和實現(附 Java 實現案例)
    基因遺傳算法是一種靈感源於達爾文自然進化理論的啟發式搜索算法。該算法反映了自然選擇的過程,即最適者被選定繁殖,並產生下一代。本文簡要地介紹了遺傳算法的基本概念和實現,希望能為讀者展示啟發式搜索的魅力。   如上圖(左)所示,遺傳算法當個體由多條染色體組成,每條染色體由多個基因組成。上圖(右)展示了染色體分割和組合方式。
  • 詳解 | 如何用Python實現機器學習算法
    來自:AI科技大本營(ID:rgznai100)用Python實現出來的機器學習算法都是什麼樣子呢
  • 小白學數據:教你用Python實現簡單監督學習算法
    監督學習算法會從數據集中學習得出訓練樣本和其目標變量之間的關係,然後將學習到的關係對新樣本(未被標記的樣本)進行分類。為了闡明監督學習的工作原理,我們用根據學生學習時間預測其考試成績的例子來說明。為了獲得更高的精度,最好的方法是測試多個不同的算法,同時,對每個算法嘗試不同的參數。可以通過交互檢驗選擇最好的算法和參數。對於給定問題,在選取算法時,算法的精度、訓練時間、線性、參數數目以及特殊情況都要考慮在內。
  • [Python] geatpy 多目標遺傳算法求解最短路
    進化算法geatpy調包俠養成文章最後開了個坑,說有空的時候要補上遺傳算法在路徑求解問題中的應用,這次就給大家整一個。問題描述簡單來說,本次的例子是一個多目標無向圖求最短路徑的問題(該問題的原型來自於我同門的論文,危貨的路徑優化)。
  • 教程| 基於遺傳算法的拼圖遊戲解決方案
    選自GitHub機器之心編譯參與:林川、劉曉坤這是一個GitHub項目,介紹了一種基於遺傳算法的帶有板塊尺寸自動檢測功能的拼圖遊戲解決方案。連結:https://github.com/nemanja-m/gaps安裝把 repo 下載到本地$ git clone https://github.com/nemanja-m/gaps.git$cdgaps安裝要求$ pip install -r requirements.txt$ sudo apt-get install python-tk
  • 常見面試算法:k-近鄰算法原理與python案例實現
    k 近鄰算法的輸入為實例的特徵向量,對應於特徵空間的點;輸出為實例的類別,可以取多類。k 近鄰算法假設給定一個訓練數據集,其中的實例類別已定。分類時,對新的實例,根據其 k 個最近鄰的訓練實例的類別,通過多數表決等方式進行預測。因此,k近鄰算法不具有顯式的學習過程。k 近鄰算法實際上利用訓練數據集對特徵向量空間進行劃分,並作為其分類的「模型」。
  • 高斯混合模型(GMM):理念、數學、EM算法和python實現
    高斯混合模型是一種流行的無監督學習算法。GMM方法類似於K-Means聚類算法,但是由於其複雜性,它更健壯,更有用。K-means聚類使用歐式距離函數來發現數據中的聚類。只要數據相對於質心呈圓形分布,此方法就可以很好地工作。
  • 遺傳算法Python實戰 008.魔術方塊
    我們通過算法來實現一下看看那算法實現首先導入需要的包,本次要用到一個新的方法:import random,datetimefrom bisect import bisect_leftfrom mathdef _get_improvement(): # 隨機初始化初代種群,對於遺傳算法來說,初始情況完全不重要 parent = bestParent = fnCustomCreate() yield bestParent # 歷史健壯性集合,這個集合可以記錄歷史上一些最佳的健壯性 historicalFitnesses
  • 如何使用python完成數學建模常見算法
    在數學建模中主流的程式語言是MATLAB,但隨著python/R中數學軟體包的不斷完善,熟悉這兩種程式語言的同學也可以快速數學建模的編程環節。後面我們將介紹幾種常見數學建模算法的python實現,旨在展示python在本領域的強大威力。
  • 用Python實現隨機森林算法
    除了仍然根據從訓練數據樣本建立複合模型之外,隨機森林對用做構建樹(tree)的數據特徵做了一定限制,使得生成的決策樹之間沒有關聯,從而提升算法效果。本文章旨在探討如何用 Python 實現隨機森林算法。
  • 【算法】Python實現機器學習算法
    2 如何用Python實現決策樹系列算法?人生苦短,就用 Python。在 Kaggle 最新發布的全球數據科學/機器學習現狀報告中,來自 50 多個國家的 16000 多位從業者紛紛向新手們推薦 Python 語言,用以學習機器學習。
  • 經典 | Python實現八大排序算法,面試必備
    本文使用python實現了一些常用的排序方法。用python實現如下:def straight_insertion_sort(self, value_list): """ 直接插入排序 :param value_list: 無序列表 :return: """ return self.