開發工具
Python版本:3.6.4python
相關模塊:算法
numpy模塊;網絡
argparse模塊;app
pygame模塊;dom
以及一些python自帶的模塊。函數
關注公衆號:Python學習指南,回覆:"小恐龍遊戲3" 獲取源碼及相關文件工具
環境搭建
安裝Python並添加到環境變量,pip安裝須要的相關模塊便可。學習
原理簡介
遺傳算法,即:開發工具
Genetic Algorithm, GA 是一種元啓發式算法,其核心思想與達爾文的進化理論很類似。簡單而言就是物種在進化過程當中,好的基因將獲得保留,很差的基因將被淘汰。通過不少代的演變以後,物種當前保留下來的基因就能夠看做是對當前環境適應度最好的基因了。設計
具體應用到咱們的小恐龍小遊戲上,咱們設計算法的思路以下。首先,隨機生成若干個小恐龍(好比100個):
self.dinos = [Dinosaur(cfg.IMAGE_PATHS['dino']) for _ in range(self.population_size)]
每一個恐龍的行動由一個小的神經網絡來控制:
self.populations = [Network() for _ in range(self.population_size)]
其中,每一個神經網絡都是由兩個全鏈接層組成,且他們的權重矩陣都是隨機生成的:
'''define the network''' class Network(): def __init__(self, fc1=None, fc2=None, **kwargs): self.fc1 = np.random.randn(5, 16) if fc1 is None else fc1 self.fc2 = np.random.randn(16, 2) if fc2 is None else fc2 self.fitness = 0 '''predict the action''' def predict(self, x): x = x.dot(self.fc1) x = self.activation(x) x = x.dot(self.fc2) x = self.activation(x) return x '''activation function''' def activation(self, x): return 0.5 * (1 + np.tanh(0.5 * x))
每一個全鏈接層的輸出結果由如下函數激活以保證輸出值的範圍都在0到1之間:
網絡的輸入值有5個,分別爲:
- 當前離小恐龍最近的障礙物與小恐龍的水平距離;
- 當前離小恐龍最近的障礙物離地面的高度;
- 當前離小恐龍最近的障礙物的寬度;
- 當前離小恐龍最近的障礙物的高度;
- 小恐龍水平方向上的奔跑速度.
畫個示意圖就是:
接着,咱們讓這些神經網絡來控制對應的小恐龍進行遊戲:
'''make decision for all dinos''' def makedecision(self, x): threshold = 0.55 actions = self.ai.predict(x) for i in range(len(actions)): action = actions[i] if self.dinos[i].is_dead: continue if action[0] >= threshold: self.dinos[i].jump(self.sounds) elif action[1] >= threshold: self.dinos[i].duck() else: self.dinos[i].unduck() self.ai.populations[i].fitness = self.dinos[i].score
直到全部的神經網絡都讓本身控制的小恐龍由於撞到路上的障礙物而死掉。接下來,咱們從這些神經網絡中選出幾個讓小恐龍存活的時間最久的(好比選兩個,也就是對應控制的小恐龍得分最高的兩個):
def keepbest(self): self.populations.sort(key=lambda x: x.fitness, reverse=True) self.keeped_nets = self.populations[:self.num_keeped_nets]
讓選出的神經網絡的權重矩陣進行交叉和變異,從而生成新的一批神經網絡:
'''crossover''' def crossover(self): def crossoverweight(fc1, fc2): assert len(fc1) == len(fc2) crossover_len = int(len(fc1) * random.uniform(0, 1)) for i in range(crossover_len): fc1[i], fc2[i] = fc2[i], fc1[i] return fc1, fc2 nets_new = [] size = min(self.num_keeped_nets * self.num_keeped_nets, self.population_size) for _ in range(size): net_1 = copy.deepcopy(random.choice(self.keeped_nets)) net_2 = copy.deepcopy(random.choice(self.keeped_nets)) for _ in range(self.num_crossover_times): net_1.fc1, net_2.fc1 = crossoverweight(net_1.fc1, net_2.fc1) net_1.fc2, net_2.fc2 = crossoverweight(net_1.fc2, net_2.fc2) nets_new.append(net_1) return nets_new '''mutate''' def mutate(self, net): def mutateweight(fc, prob): if random.uniform(0, 1) < prob: return fc * random.uniform(0.5, 1.5) else: return fc net = copy.deepcopy(net) net.fc1 = mutateweight(net.fc1, self.mutation_prob) net.fc2 = mutateweight(net.fc2, self.mutation_prob) return net
一樣地,這批神經網絡每一個都會分別控制一隻新的小恐龍來進行遊戲,直到這批新的神經網絡控制下的小恐龍再次所有死掉。此時,重複以前的動做,即選擇其中表現最好的幾個神經網絡並進行交叉變異,而後再次開始新的遊戲,如此反覆循環,直到獲得滿意的效果。
嗯,原理大概就是這樣,完整源代碼詳見相關文件唄~