莫煩老師遺傳演算法程式碼筆記(+布林型索引)
阿新 • • 發佈:2018-11-27
在學習莫煩老師的遺傳演算法---找曲線最高點的例子的時候,讀程式碼的時候發現有個布林型索引。
全部程式碼註釋筆記如下:
""" 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