DQN(Deep Q-learning)入門教程(六)之DQN Play Flappy-bird ,MountainCar
阿新 • • 發佈:2020-06-01
在[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迴圈是完全沒有問題的,很