1. 程式人生 > >DQN(Deep Q-learning)入門教程(六)之DQN Play Flappy-bird ,MountainCar

DQN(Deep Q-learning)入門教程(六)之DQN Play Flappy-bird ,MountainCar

在[DQN(Deep Q-learning)入門教程(四)之Q-learning Play Flappy Bird](https://www.cnblogs.com/xiaohuiduan/p/12990510.html)中,我們使用q-learning演算法去對Flappy Bird進行強化學習,而在這篇部落格中我們將使用神經網路模型來代替Q-table,關於DQN的介紹,可以參考我前一篇部落格:[DQN(Deep Q-learning)入門教程(五)之DQN介紹](https://www.cnblogs.com/xiaohuiduan/p/12993691.html) 在這篇部落格中將使用DQN做如下操作: - Flappy Bird - MountainCar-v0 再回顧一下DQN的演算法流程: ![](https://img2020.cnblogs.com/blog/1439869/202005/1439869-20200531221528540-524428036.png) 專案地址:[Github](https://github.com/xiaohuiduan/flappy-bird-dqn) ## MountainCar-v0 MountainCar的訓練好的Gif示意圖如下所示,汽車起始位置位於山的底部,最終目標是駛向右邊山的插旗的地方,其中,汽車的引擎不能夠直接駛向終點,必須藉助左邊的山體的重力加速度才能夠駛向終點。 ![](https://img2020.cnblogs.com/blog/1439869/202005/1439869-20200531221529590-163690453.gif) `MountainCar-v0`由OpenAI提供,python包為gym,官網網站為[https://gym.openai.com/envs/MountainCar-v0/](https://gym.openai.com/envs/MountainCar-v0/)。在Gym包中,提供了很多可以用於強化學習的環境(env): ![](https://img2020.cnblogs.com/blog/1439869/202005/1439869-20200531221529960-2144456892.png) 在MountainCar-v0中,**狀態有2個變數**,car position(汽車的位置),car vel(汽車的速度),**action一共有3種**:` Accelerate to the Left`,` Don't accelerate`,`Accelerate to the Right`,然後當車達到旗幟的地方(position = 0.5)會得到$reward = 1$的獎勵,如果沒有達到則為$-1$。但是如果當你執行步驟超過200次的時候,遊戲就會結束。詳情可以參考[原始碼](https://github.com/openai/gym/blob/master/gym/envs/classic_control/mountain_car.py)(ps:官方文件中沒有這些說明)。 下面介紹一下gym中幾個常用的函式: - ```python env = gym.make("MountainCar-v0") ``` 這個就是建立一個`MountainCar-v0`的遊戲環境。 - ```python state = env.reset() ``` 重置環境,返回重置後的state - ```python env.render() ``` 將執行畫面展示在螢幕上面,當我們在訓練的時候可以不使用這個來提升速度。 - ```python next_state, reward, done, _ = env.step(action) ``` 執行action動作,返回下一個狀態,獎勵,是否完成,info。 ### 初始化Agent 初始化Agent直接使用程式碼說明吧,這個還是比較簡單的: ```python import keras import random from collections import deque import gym import numpy as np from keras.layers import Dense from keras.models import Sequential class Agent(): def __init__(self, action_set, observation_space): """ 初始化 :param action_set: 動作集合 :param observation_space: 環境屬性,我們需要使用它得到state的shape """ # 獎勵衰減 self.gamma = 1.0 # 從經驗池中取出資料的數量 self.batch_size = 50 # 經驗池 self.memory = deque(maxlen=2000000) # 探索率 self.greedy = 1.0 # 動作集合 self.action_set = action_set # 環境的屬性 self.observation_space = observation_space # 神經網路模型 self.model = self.init_netWork() def init_netWork(self): """ 構建模型 :return: 模型 """ model = Sequential() # self.observation_space.shape[0],state的變數的數量 model.add(Dense(64 * 4, activation="tanh", input_dim=self.observation_space.shape[0])) model.add(Dense(64 * 4, activation="tanh")) # self.action_set.n 動作的數量 model.add(Dense(self.action_set.n, activation="linear")) model.compile(loss=keras.losses.mean_squared_error, optimizer=keras.optimizers.RMSprop(lr=0.001)) return model ``` 我們使用佇列來儲存經驗,這樣的話新的資料就會覆蓋遠古的資料。此時我們定義一個函式,專門用來將資料儲存到經驗池中,然後定義一個函式用來更新$\epsilon$探索率。 ```python def add_memory(self, sample): self.memory.append(sample) def update_greedy(self): # 小於最小探索率的時候就不進行更新了。 if self.greedy > 0.01: self.greedy *= 0.995 ``` ### 訓練模型 首先先看程式碼: ```python def train_model(self): # 從經驗池中隨機選擇部分資料 train_sample = random.sample(self.memory, k=self.batch_size) train_states = [] next_states = [] for sample in train_sample: cur_state, action, r, next_state, done = sample next_states.append(next_state) train_states.append(cur_state) # 轉成np陣列 next_states = np.array(next_states) train_states = np.array(train_states) # 得到next_state的q值 next_states_q = self.model.predict(next_states) # 得到state的預測值 state_q = self.model.predict_on_batch(train_states) # 計算Q現實 for index, sample in enumerate(train_sample): cur_state, action, r, next_state, done = sample if not done: state_q[index][action] = r + self.gamma * np.max(next_states_q[index]) else: state_q[index][action] = r self.model.train_on_batch(train_states, state_q) ``` 大家肯定從上面的程式碼發現一些問題,使用了兩個for迴圈,why?首先先說一下兩個for迴圈分別的作用: - 第一個for迴圈:得到`train_states`和`next_states`,其中``next_states``是為了計算Q現實。 - 第二個for迴圈:計算Q現實 可能有人會有一個疑問,為什麼我不寫成一個for迴圈呢?實際上寫成一個for迴圈是完全沒有問題的,很