via:https://keon.io/rl/deep-q-learning-with-keras-and-gym/
綜述
這篇blog將會展示深度強化學習(深度Q學習)是如何使用Keras與Gym環境使機器學會玩CartPole游戲的。只有78行代碼哦
我將會解釋一切,不需要你對強化學習有任何的先決知識。
這篇文章中使用的代碼的倉庫在這里:GitHub
強化學習
強化學習是一種允許你創造能從環境中交互學習的AI agent 的機器學習算法。就跟我們學習騎自行車一樣,這種類型的AI通過試錯來學習。如上圖所示,大腦代表AI agent並在環境中活動。當每次行動過后,agent接收到環境反饋。反饋包括回報(reward)和環境的下個狀態(state)。回報由模型設計者定義。如果類比人類學習自行車,我們會將車從起始點到當前位置的距離定義為回報。
深度強化學習
2013年,在DeepMind 發表的著名論文Playing Atari with Deep Reinforcement Learning中,他們介紹了一種新算法,深度Q網絡(DQN)。文章展示了AI agent如何在沒有任何先驗信息的情況下通過觀察屏幕學習玩游戲。結果令人印象深刻。這篇文章開啟了被我們成為“深度強化學習”的新時代。這種學習算法是混合了深度學習與強化學習的新算法。
在Q學習算法中,有一種函數被稱為Q函數,它用來估計基於一個狀態的回報。同樣地,在DQN中,我們使用一個神經網絡估計基於狀態的回報函數。我們將在之后細致地討論這一部分工作。
Cartpole游戲
通常訓練一個agent玩Atari游戲通常會好一會兒(從幾個小時到一天)。所以我們將訓練agent玩一個簡單的游戲,CartPole,並使用在上面論文中的一些思想。
CartPole是OpenAI gym中最簡單的一個環境。正如你在文章一開始看到的那個gif一樣,CartPole的目的就是桿子平衡在移動的小車上。除了像素信息,還有四種信息可以用作狀態,像是,桿子的角度和車在滑軌的位置。agent可以通過施加左(0)或右(1)的動作,使小車移動。
Gym使游戲環境的交互非常方便:
next_state, reward, done, info = env.step(action)
如我們上面所說,action要么是0要么是1。當我們將這些數字串入環境中將會得出結果。“env”是游戲環境類。“done”為標記游戲結束與否的布爾量。當前狀態“state”,“action”,“next_state”與“reward”是我們用於訓練agent的數據。
使用Keras實現簡單神經網絡
這篇文章不是關於深度學習或神經網絡的。所以我們將神經網絡試做黑箱算法。這個算法的功能是從成對的輸入與輸出數據學習某種模式並且可以基於不可見的輸入數據預測輸出。但是我們應該理解在DQN算法中的那部分神經網絡算法。
注意到我們使用的神經網絡類似於上圖所示的網絡。我們使用一個包含四種輸入信息的輸入層和三個隱藏層。但是我們在輸出層有兩個節點因為在這個游戲中有兩個按鈕(0與1)
keras庫使基礎神經網絡的使用變得非常簡單。下面的代碼會生成一個空的神經網絡模型。“activation”,“loss”與“optimizer”是定義神經網絡特征的參數但我們不打算在這里討論它們。
# Neural Net for Deep Q Learning # Sequential() creates the foundation of the layers. model = Sequential() # Dense is the basic form of a neural network layer # Input Layer 4 and Hidden Layer with 128 nodes model.add(Dense(64, input_dim=4, activation='tanh')) # Hidden layer with 128 nodes model.add(Dense(128, activation='tanh')) # Hidden layer with 128 nodes model.add(Dense(128, activation='tanh')) # Output Layer with 2 nodes model.add(Dense(2, activation='linear')) # Create the model based on the information above model.compile(loss='mse', optimizer=RMSprop(lr=self.learning_rate))
為了讓模型可以基於環境數據理解與預測,我們不得不給它提供數據。下列代碼所示,“fit()”方法為模型提供“states”和“target_f”信息。你可以忽視其余參數。
這個訓練過程使模型從某個狀態“state”預測回報函數值“target_f”。
model.fit(state, target_f, nb_epoch=1, verbose=0)
當你在模型調用"predict()"函數時,模型根據之前訓練過的數據將預測現在狀態的回報函數
prediction = model.predict(state)
實現深度Q算法(DQN)
DQN算法最重要的特征是記憶(remember)與回顧(replay)方法。它們都有很簡明的概念。
記憶(remember)
對於DQN來說一個挑戰就是運用在算法中的神經網絡區域通過覆蓋掉先前學習的經驗來遺忘它們。所以我們需要記錄下先前的經驗與觀察值以便再用這些先前數據訓練模型。我們將調用代表經驗的數組數據“memory”和“remember()”函數來添加狀態,回報,和下次狀態到“memory”中。
在本例中,“memory”列表中有以下形式的數據:
memory = [(state, action, reward, next_State)...]
“remember()”只是簡單的存儲上述這些數據:
def remember(self, state, action, reward, next_state, done): self.memory.append((state, action, reward, next_state, done))
“done”只是一個標記是否為最后一個狀態的布爾量。
是不是很簡單??
回放(replay)
"replay()"從存儲在“memory”中的數據(經驗)中訓練神經網絡。首先,我們從“memory”中抽出部分數據並叫他們“bathces”
batches = min(batch_size, len(self.memory))
batches = np.random.choice(len(self.memory), batches)
上面的代碼將打亂memory中的bathces的索引數。舉個例子,如果batchce為[1,5,2,7],每個數據代表在memory中的索引數1,5,2,7。
為了使agent在長期運行中表現的更好,我們不僅僅需要考慮即時回報(immediate rewards),還要考慮未來回報(future rewards)。為了實現這一目標,我們定義“discount rate”(折扣因子) 即“gamma”。這樣,agent將學習已有的狀態然后想方設法最大化未來回報。
for i in batches: # Extract informations from i-th index of the memory state, action, reward, next_state = self.memory[i] # if done, make our target reward (-100 penality) target = reward if not done: # predict the future discounted reward target = reward + self.gamma * \ np.amax(self.model.predict(next_state)[0]) # make the agent to approximately map # the current state to future discounted reward # We'll call that target_f target_f = self.model.predict(state) target_f[0][action] = target # Train the Neural Net with the state and target_f self.model.fit(state, target_f, nb_epoch=1, verbose=0)
agent如何選擇行為?
我們的agent在最初的一部分時間會隨機選擇行為,這被“exploration rate”或“epsilon”參數表征。這是因為在最初對agent最好的策略就是在他們掌握模式前嘗試一切。當agent沒有隨機選擇行為,它會基於當前狀態預測回報值並且選擇能夠將回報最大化的行為。“np.argmax()”函數可以取出“act_values[0]”中的最大值。
def act(self, state): if np.random.rand() <= self.epsilon: # The agent acts randomly return env.action_space.sample() # Predict the reward value based on the given state act_values = self.model.predict(state) # Pick the action based on the predicted reward return np.argmax(act_values[0])
“act_values[0]”中的數據類似“[0.67, 0.2]”,每個數字分別代表0和1的回報,於是“argmax()”會取出更大數值所代表的的行為。比如在[0.67, 0.2]中,argmax()返回0因為0索引代表的數據的回報最大。
超參數
有一些超參數是強化學習agent所必需的,你會在下面一次又一次的看到這些參數。
·episodes
我們想讓agent玩游戲的次數
·gamma discount rate(折扣因子),以便計算未來的折扣回報。
·epsilon exploration rate,這個比率表征一個agent隨機選擇行為的程度
·epsilon_decay 上述參數的衰減率。我們希望隨着agent更擅長游戲的同時減少它探索的次數。
·epsilon_min 這個參數是我們希望agent采取的最少的探索次數。
·learning_rata 這個參數決定了神經網絡在每次迭代時的學習率(學習程度)。
編寫深度Q學習agent的代碼
我在上面分別描述了agent算法的每一部分。下面的代碼實現了上述討論的一切並編寫了一個整潔的類,叫做“DQNAgent”
# Deep-Q learning Agent class DQNAgent: def __init__(self, env): self.env = env self.memory = [] self.gamma = 0.9 # decay rate self.epsilon = 1 # exploration self.epsilon_decay = .995 self.epsilon_min = 0.1 self.learning_rate = 0.0001 self._build_model() def _build_model(self): model = Sequential() model.add(Dense(128, input_dim=4, activation='tanh')) model.add(Dense(128, activation='tanh')) model.add(Dense(128, activation='tanh')) model.add(Dense(2, activation='linear')) model.compile(loss='mse', optimizer=RMSprop(lr=self.learning_rate)) self.model = model def remember(self, state, action, reward, next_state, done): self.memory.append((state, action, reward, next_state, done)) def act(self, state): if np.random.rand() <= self.epsilon: return env.action_space.sample() act_values = self.model.predict(state) return np.argmax(act_values[0]) # returns action def replay(self, batch_size): batches = min(batch_size, len(self.memory)) batches = np.random.choice(len(self.memory), batches) for i in batches: state, action, reward, next_state, done = self.memory[i] target = reward if not done: target = reward + self.gamma * \ np.amax(self.model.predict(next_state)[0]) target_f = self.model.predict(state) target_f[0][action] = target self.model.fit(state, target_f, nb_epoch=1, verbose=0) if self.epsilon > self.epsilon_min: self.epsilon *= self.epsilon_decay
讓我們來訓練它吧!
這部分會比較短,我會在注釋中解釋。
if __name__ == "__main__": # 為agent初始化gym環境參數 env = gym.make('CartPole-v0') agent = DQNAgent(env) # 游戲的主循環 for e in range(episodes): # 在每次游戲開始時復位狀態參數 state = env.reset() state = np.reshape(state, [1, 4]) # time_t 代表游戲的每一幀 # 我們的目標是使得桿子盡可能長地保持豎直朝上 # time_t 越大,分數越高 for time_t in range(5000): # turn this on if you want to render # env.render() # 選擇行為 action = agent.act(state) # 在環境中施加行為推動游戲進行 next_state, reward, done, _ = env.step(action) next_state = np.reshape(next_state, [1, 4]) # reward缺省為1 # 在每一個agent完成了目標的幀agent都會得到回報 # 並且如果失敗得到-100 reward = -100 if done else reward # 記憶先前的狀態,行為,回報與下一個狀態 agent.remember(state, action, reward, next_state, done) # 使下一個狀態成為下一幀的新狀態 state = copy.deepcopy(next_state) # 如果游戲結束done被置為ture # 除非agent沒有完成目標 if done: # 打印分數並且跳出游戲循環 print("episode: {}/{}, score: {}" .format(e, episodes, time_t)) break # 通過之前的經驗訓練模型 agent.replay(32)
結果
在一開始,agent通過隨機行為探索游戲環境
算法會經過多個階段訓練agent
1.小車操作agent試圖平衡桿子
2.但是出界,游戲結束
3.當它距離邊界太近時它不得不移動小車,於是桿子掉了。
4.agent最后掌握了平衡並學會控制桿子。
經過幾百個episodes的訓練后,它開始學習如何最大化分數。
一個大師級CartPole玩家都誕生了。
文章中使用的代碼在GitHub中。我為想要跳過訓練的朋友提供了訓練好的權重。
參考
- Playing Atari with Deep Reinforcement Learning
- Human-level Control Through Deep Reinforcement Learning