1. 程式人生 > >莫煩老師遺傳演算法程式碼筆記(+布林型索引)

莫煩老師遺傳演算法程式碼筆記(+布林型索引)

在學習莫煩老師的遺傳演算法---找曲線最高點的例子的時候,讀程式碼的時候發現有個布林型索引。

參見:https://morvanzhou.github.io/tutorials/machine-learning/evolutionary-algorithm/2-01-genetic-algorithm/

全部程式碼註釋筆記如下:

"""
Visualize Genetic Algorithm to find a maximum point in a function.
視覺化遺傳演算法去找到一個函式的最高點
"""
import numpy as np
import matplotlib.pyplot as plt

DNA_SIZE = 10            # DNA length
POP_SIZE = 100           # population size,種群中個體數目
CROSS_RATE = 0.8         # mating probability (DNA crossover),0.8的概率進行交叉配對
MUTATION_RATE = 0.003    # mutation probability,變異強度
N_GENERATIONS = 200      #迭代次數
X_BOUND = [0, 5]         # x upper and lower bounds,指定x的取值範圍


def F(x):
    return np.sin(10*x)*x + np.cos(2*x)*x     # to find the maximum of this function


# find non-zero fitness for selection
#我們都需要一個評估好壞的方程, 這個方程通常被稱為 fitness適應度.
#為了找到下面這個曲線當中的最高點. 那麼這個 fitness 方程可以定義為高度, 越高的點, fitness 越高.
def get_fitness(pred):
    return pred + 1e-3 - np.min(pred)#因為如果直接返回pred可能是負值,而我們在計算概率的時候不能為負值。
    #要進行處理,np.min表示取最小,為最大的負數,可以使全部只變成正的;1e-3為了讓float進行相除防止小數點後的數被省略


# convert binary DNA to decimal and normalize it to a range(0, 5)
#對基因的翻譯,如這裡函式,x軸是實數,這裡解釋瞭如何將遺傳0、1序列翻譯成實數。用十進位制二進位制轉換
#pop (population)是一個儲存二進位制 DNA 的矩陣, 他的 shape 是這樣 (pop_size, DNA_size)
#這裡DNA_SIZE,X_BOUND是超引數
def translateDNA(pop):
    return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2**DNA_SIZE-1) * X_BOUND[1]
    #dot()函式是矩陣乘,而*則表示逐個元素相乘
	#np.arange()函式返回一個有終點和起點的固定步長的排列
	#pop.dot(2 ** np.arange(DNA_SIZE)[::-1])已經轉換成十進位制
	#但是需要歸一化到0~5,如有1111這麼長的DNA,要產生的十進位制數範圍是[0, 15], 而所需範圍是 [-1, 1],就將[0,15]縮放到[-1,1]這個範圍
	#a[::-1]相當於 a[-1:-len(a)-1:-1],也就是從最後一個元素到第一個元素複製一遍。所以你看到一個倒序
	#np.arange(DNA_SIZE)[::-1]得到10,9,8,...,0

#這裡進行優勝劣汰的選擇步驟
#適者生存的 select() 很簡單, 我們只要按照適應程度 fitness 來選 pop 中的 parent 就好. fitness 越大, 越有可能被選到.
def select(pop, fitness):    # nature selection wrt pop's fitness
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,p=fitness/fitness.sum())
	#這裡概率不能為負,所以pred要進行非負處理
	#replace表示抽樣後是否放回,這裡為True表示有放回,則可能會出現相同的索引值
    # p 就是選它的比例,按比例來選擇適應度高的,也會保留一些適應度低的,因為也可能後面產生更好的變異
    #np.random.choice表示從序列中取值  np.arange()函式返回一個有終點和起點的固定步長的排列
    return pop[idx]

#繁衍,交叉父母的基因
def crossover(parent, pop):     # mating process (genes crossover)
    if np.random.rand() < CROSS_RATE: #這裡是0.8的概率父親會選擇一個母親進行交叉配對
        i_ = np.random.randint(0, POP_SIZE, size=1)                           #select another individual from pop選擇母親索引一個
        cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool) #得到一行[01001100]也是0、1為了選擇哪些點進行交叉;然後進行布林化
        parent[cross_points] = pop[i_, cross_points]
		#布林型陣列可以用於陣列索引,布林型陣列長度必須跟被索引的軸長度一致
		#生成布林陣列可以組合應用多個布林條件,使用&(和),|(或)之類的布林算數運算子,python的關鍵字and和or在布林型陣列中無效
		#parent[cross_points]即parent列表中取出cross_points為True地方的值&&&&&!!!!
		#【母親是pop的i_索引行DNA,選出母親對應在cross_points為TRUE的地方的值】賦給【父親DNA對應在cross_points選出為TRUE的地方的值】。
    return parent

#繁衍,有變異的基因會出現
#將某些 DNA 中的 0 變成 1, 1 變成 0
def mutate(child):
    for point in range(DNA_SIZE):
        if np.random.rand() < MUTATION_RATE:
            child[point] = 1 if child[point] == 0 else 0
    return child

#產生離散均勻分佈的整數,若high不為None時,取[low,high)之間隨機整數,否則取值[0,low)之間隨機整數。
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE))   # initialize the pop DNA
#pop = np.=random.randint(0,2,(1,DNA_SIZE).repeat(POP_SIZE,axis=0))這裡是生成了一樣的DNA,後面也可以隨著變異變成不一樣的

#[[01001100],
# [10111100],
# ...]
plt.ion()       # something about plotting開啟影象互動模式

x = np.linspace(*X_BOUND, 200)
#linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
#X_BOUND = [0, 5],要產生200個樣本點
#返回固定間隔的資料。他將返回num個等間距的樣本,在區間[start,stop]中。其中,區間的結束端點可以被排除在外(用endpoint標識)
plt.plot(x, F(x))

for _ in range(N_GENERATIONS):
    F_values = F(translateDNA(pop))    # compute function value by extracting DNA傳入到F函式

    # something about plotting
    if 'sca' in globals(): sca.remove()
    sca = plt.scatter(translateDNA(pop), F_values, s=200, lw=0, c='red', alpha=0.5); plt.pause(0.05)#plt.pause表示顯示秒數

    # GA part (evolution)
    fitness = get_fitness(F_values) #計算適應度
    print("Most fitted DNA: ", pop[np.argmax(fitness), :])
    pop = select(pop, fitness)#這裡選出了另外一種population
    pop_copy = pop.copy()# 備個份
    for parent in pop: #這裡parent為遍歷pop,一次為其中一行,而這裡的pop是從原pop中按適應度概率有放回的選出了POP_SIZE行
        child = crossover(parent, pop_copy)#繁衍
        child = mutate(child) #進行變異
        parent[:] = child       # parent is replaced by its child# 寶寶變大人

plt.ioff(); plt.show()

#在使用matplotlib的過程中,不能像matlab一樣同時開幾個視窗進行比較,可以採用互動模式,但是放在腳本里執行一閃而過,影象並不停留
#python視覺化庫matplotlib有兩種顯示模式:阻塞(block)模式&互動(interactive)模式
#在互動模式下:plt.plot(x)或plt.imshow(x)是直接出影象,不需要plt.show()
#如果在指令碼中使用ion()命令開啟了互動模式,沒有使用ioff()關閉的話,則影象不會常留。防止這種情況,需要在plt.show()之前加上ioff()命令。
#在阻塞模式下:開啟一個視窗以後必須關掉才能開啟下一個新的視窗。這種情況下,預設是不能像Matlab一樣同時開很多視窗進行對比的
#plt.plot(x)或plt.imshow(x)是直接出影象,需要plt.show()後才能顯示影象

這裡布林型索引在crossover函式中出現:

def crossover(parent, pop):     # mating process (genes crossover)
    if np.random.rand() < CROSS_RATE: #這裡是0.8的概率父親會選擇一個母親進行交叉配對
        i_ = np.random.randint(0, POP_SIZE, size=1)                           #select another individual from pop選擇母親索引一個
        cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool) #得到一行[01001100]也是0、1為了選擇哪些點進行交叉;然後進行布林化
        parent[cross_points] = pop[i_, cross_points]
		#布林型陣列可以用於陣列索引,布林型陣列長度必須跟被索引的軸長度一致
		#生成布林陣列可以組合應用多個布林條件,使用&(和),|(或)之類的布林算數運算子,python的關鍵字and和or在布林型陣列中無效
		#parent[cross_points]即parent列表中取出cross_points為True地方的值&&&&&!!!!
		#【母親是pop的i_索引行DNA,選出母親對應在cross_points為TRUE的地方的值】賦給【父親DNA對應在cross_points選出為TRUE的地方的值】。
    return parent

      這裡函式輸入parent是主迴圈中遍歷pop的每一行得到的array,是一個個體的基因,即一行;輸入的pop是全部個體的DNA,ndarry型別(從原pop中按適應度概率有放回的選出了POP_SIZE行)。

       當隨機值小於超引數cross_rate時就要進行交叉配對,i_是從全部個體中選出的索引值,如4,cross_points是隨機生成一個DNA長度的二進位制序列,再轉化為布林值。

parent[cross_points]:parent(可看成父親的DNA)列表中取出cross_points為True索引處的值

pop[i_, cross_points]:pop第i_行(可看為母親的DNA)列表中取出cross_points為True索引處的值,

【母親是pop的i_索引行DNA,選出母親對應在cross_points為TRUE的地方的值】賦給【父親DNA對應在cross_points選出為TRUE的地方的值】

這裡我理解應該是布林型索引記住了值和索引

然後這裡我舉了一個有5個樣本,8長度的DNA例子

參看布林型索引另一例子:https://blog.csdn.net/xsl15181685808/article/details/79734872