莫烦强化学习-Q Learning
參考鏈接:
https://mofanpy.com/tutorials/machine-learning/reinforcement-learning/intro-q-learning/
第2章 Q-learning
強化學習中有名的算法,Q-learning。由第一章可知,Q-learning的分類是model-free,基于價值,單步更新,離線學習。
2.1 什么是Q-Learning
2.1.1 行為準則
我們做事情都會有一個自己的行為準則, 比如小時候爸媽常說”不寫完作業(yè)就不準看電視”。所以我們在 寫作業(yè)的這種狀態(tài)下, 好的行為就是繼續(xù)寫作業(yè), 直到寫完它, 我們還可以得到獎勵, 不好的行為就是沒寫完就跑去看電視了, 被爸媽發(fā)現(xiàn), 后果很嚴重。小時候這種事情做多了, 也就變成我們不可磨滅的記憶。這和我們要提到的 Q learning 有什么關(guān)系呢? 原來 Q learning 也是一個決策過程, 和小時候的這種情況差不多。我們舉例說明.
假設(shè)現(xiàn)在我們處于寫作業(yè)的狀態(tài)而且我們以前并沒有嘗試過寫作業(yè)時看電視, 所以現(xiàn)在我們有兩種選擇 , 1, 繼續(xù)寫作業(yè), 2, 跑去看電視. 因為以前沒有被罰過, 所以我選看電視, 然后現(xiàn)在的狀態(tài)變成了看電視, 我又選了繼續(xù)看電視, 接著我還是看電視, 最后爸媽回家, 發(fā)現(xiàn)我沒寫完作業(yè)就去看電視了, 狠狠地懲罰了我一次, 我也深刻地記下了這一次經(jīng)歷, 并在我的腦海中將 “沒寫完作業(yè)就看電視” 這種行為更改為負面行為, 我們在看看 Q learning 根據(jù)很多這樣的經(jīng)歷是如何來決策的吧。
2.1.2 QLearning 決策
假設(shè)我們的行為準則已經(jīng)學習好了, 現(xiàn)在我們處于狀態(tài)s1, 我在寫作業(yè), 我有兩個行為 a1, a2, 分別是看電視和寫作業(yè), 根據(jù)我的經(jīng)驗, 在這種 s1 狀態(tài)下, a2 寫作業(yè)帶來的潛在獎勵要比 a1 看電視高(比較不同決策的價值), 這里的潛在獎勵我們可以用一個有關(guān)于 s 和 a 的 Q 表格代替, 在我的記憶Q表格中, Q(s1, a1)=-2 要小于 Q(s1, a2)=1, 所以我們判斷要選擇 a2 作為下一個行為。現(xiàn)在我們的狀態(tài)更新成 s2 , 我們還是有兩個同樣的選擇, 重復上面的過程, 在行為準則Q 表中尋找 Q(s2, a1)和Q(s2, a2) 的值, 并比較他們的大小, 選取較大的一個。接著根據(jù) a2 我們到達 s3 并在此重復上面的決策過程. Q learning 的方法也就是這樣決策的。看完決策, 我看在來研究一下這張行為準則 Q 表是通過什么樣的方式更改, 提升的。
2.1.3 QLearning 更新
所以我們回到之前的流程, 根據(jù) Q 表的估計, 因為在 s1 中, a2 的值比較大, 通過之前的決策方法, 我們在 s1 采取了 a2, 并到達 s2, 這時我們開始更新用于決策的 Q 表, 接著我們并沒有在實際中采取任何行為, 而是再想象自己在 s2 上采取了每種行為, 分別看看兩種行為哪一個的 Q 值大, 比如說 Q(s2, a2) 的值比 Q(s2, a1) 的大, 所以我們把大的 Q(s2, a2) 乘上一個衰減值 gamma (比如是0.9) 并加上到達s2時所獲取的獎勵 R (這里還沒有獲取到我們的棒棒糖, 所以獎勵為 0), 因為會獲取實實在在的獎勵 R , 我們將這個作為我現(xiàn)實中 Q(s1, a2) 的值, 但是我們之前是根據(jù) Q 表估計 Q(s1, a2) 的值。所以有了現(xiàn)實和估計值, 我們就能更新Q(s1, a2) , 根據(jù)估計與現(xiàn)實的差距, 將這個差距乘以一個學習效率 alpha 累加上老的 Q(s1, a2) 的值變成新的值。但時刻記住, 我們雖然用 maxQ(s2) 估算了一下 s2 狀態(tài), 但還沒有在 s2 做出任何的行為, s2 的行為決策要等到更新完了以后再重新另外做。這就是 off-policy 的 Q learning 是如何決策和學習優(yōu)化決策的過程。
2.1.4 QLearning 整體算法
這一張圖概括了我們之前所有的內(nèi)容。這也是 Q learning 的算法, 每次更新我們都用到了 Q 現(xiàn)實和 Q 估計, 而且 Q learning 的迷人之處就是 在 Q(s1, a2) 現(xiàn)實中, 也包含了一個 Q(s2) 的最大估計值, 將對下一步的衰減的最大估計和當前所得到的獎勵當成這一步的現(xiàn)實, 很奇妙吧。最后我們來說說這套算法中一些參數(shù)的意義。Epsilon greedy 是用在決策上的一種策略, 比如 epsilon = 0.9 時, 就說明有90% 的情況我會按照 Q 表的最優(yōu)值選擇行為, 10% 的時間使用隨機選行為。alpha是學習率, 來決定這次的誤差有多少是要被學習的, alpha是一個小于1 的數(shù)。gamma 是對未來 reward 的衰減值。
2.1.5 QLearning 中的 Gamma
我們重寫一下 Q(s1) 的公式, 將 Q(s2) 拆開, 因為Q(s2)可以像 Q(s1)一樣,是關(guān)于Q(s3) 的, 所以可以寫成這樣, 然后以此類推, 不停地這樣寫下去, 最后就能寫成這樣。**可以看出Q(s1) 是有關(guān)于之后所有的獎勵, 但這些獎勵正在衰減, 離 s1 越遠的狀態(tài)衰減越嚴重。**不好理解? 行, 我們想象 Qlearning 的機器人天生近視眼, gamma = 1 時, 機器人有了一副合適的眼鏡, 在 s1 看到的 Q 是未來沒有任何衰變的獎勵, 也就是機器人能清清楚楚地看到之后所有步的全部價值, 但是當 gamma =0, 近視機器人沒了眼鏡, 只能摸到眼前的 reward, 同樣也就只在乎最近的大獎勵, 如果 gamma 從 0 變到 1, 眼鏡的度數(shù)由淺變深, 對遠處的價值看得越清楚, 所以機器人漸漸變得有遠見, 不僅僅只看眼前的利益, 也為自己的未來著想。
2.1.6 補充
1.為什么不直接用現(xiàn)實值更新老的Q值呢?
Q值是未來發(fā)展情況的累計變量,不只有下一步的現(xiàn)實值
Q值的定義,從當前狀態(tài)開始,之后每一次狀態(tài)決策都采取最優(yōu)解,直到最后一個狀態(tài)(Game over)的動作質(zhì)量(quality)。
Q值可以一眼看穿未來,這就是Q-learning 的迷人之處。
獎勵表 R 是自然生成客觀存在的。
2.2 小例子
2.2.1 要點
這一次我們會用 tabular Q-learning 的方法實現(xiàn)一個小例子, 例子的環(huán)境是一個一維世界, 在世界的右邊有寶藏, 探索者只要得到寶藏嘗到了甜頭, 然后以后就記住了得到寶藏的方法, 這就是他用強化學習所學習到的行為。
-o---T # T 就是寶藏的位置, o 是探索者的位置Q-learning 是一種記錄行為值 (Q value) 的方法, 每種在一定狀態(tài)的行為都會有一個值 Q(s, a), 就是說行為 a 在 s 狀態(tài)的值是 Q(s, a)。 s 在上面的探索者游戲中, 就是 o 所在的地點了。 而每一個地點探索者都能做出兩個行為 left/right, 這就是探索者的所有可行的 a 啦。
如果在某個地點 s1, 探索者計算了他能有的兩個行為, a1/a2=left/right, 計算結(jié)果是 Q(s1, a1) > Q(s1, a2), 那么探索者就會選擇 left 這個行為。 這就是 Q learning 的行為選擇簡單規(guī)則。
當然我們還會細說更具體的規(guī)則。 在之后的教程中, 我們會更加詳細得講解 RL 中的各種方法, 下面的內(nèi)容, 大家大概看看就行, 有個大概的 RL 概念就行, 知道 RL 的一些關(guān)鍵步驟就行, 這節(jié)的算法不用仔細研究。
2.2.2 預設(shè)值
這一次需要的模塊和參數(shù)設(shè)置:
import numpy as np import pandas as pd import timeN_STATES = 6 # 1維世界的寬度 ACTIONS = ['left', 'right'] # 探索者的可用動作 EPSILON = 0.9 # 貪婪度 greedy ALPHA = 0.1 # 學習率 GAMMA = 0.9 # 獎勵遞減值 MAX_EPISODES = 13 # 最大回合數(shù) FRESH_TIME = 0.3 # 移動間隔時間2.2.3 Q 表
對于 tabular Q learning, 我們必須將所有的 Q values (行為值) 放在 q_table 中, 更新 q_table 也是在更新他的行為準則。 q_table 的 index 是所有對應(yīng)的 state (探索者位置), columns 是對應(yīng)的 action (探索者行為)。
def build_q_table(n_states, actions):table = pd.DataFrame(np.zeros((n_states, len(actions))), # q_table 全 0 初始columns=actions, # columns 對應(yīng)的是行為名稱)return table# q_table: """left right 0 0.0 0.0 1 0.0 0.0 2 0.0 0.0 3 0.0 0.0 4 0.0 0.0 5 0.0 0.0 """2.2.4 定義動作
接著定義探索者是如何挑選行為的。 這是我們引入 epsilon greedy 的概念。 因為在初始階段, 隨機的探索環(huán)境, 往往比固定的行為模式要好, 所以這也是累積經(jīng)驗的階段, 我們希望探索者不會那么貪婪(greedy)。 所以 EPSILON 就是用來控制貪婪程度的值。 EPSILON 可以隨著探索時間不斷提升(越來越貪婪), 不過在這個例子中, 我們就固定成 EPSILON = 0.9, 90% 的時間是選擇最優(yōu)策略, 10% 的時間來探索。
# 在某個 state 地點, 選擇行為 def choose_action(state, q_table):state_actions = q_table.iloc[state, :] # 選出這個 state 的所有 action 值if (np.random.uniform() > EPSILON) or (state_actions.all() == 0): # 非貪婪 or 或者這個 state 還沒有探索過action_name = np.random.choice(ACTIONS)else:action_name = state_actions.argmax() # 貪婪模式return action_name2.2.5 環(huán)境反饋 S_, R
做出行為后, 環(huán)境也要給我們的行為一個反饋, 反饋出下個 state (S_) 和 在上個 state (S) 做出 action (A) 所得到的 reward ?。 這里定義的規(guī)則就是, 只有當 o 移動到了 T, 探索者才會得到唯一的一個獎勵, 獎勵值 R=1, 其他情況都沒有獎勵。
def get_env_feedback(S, A):# This is how agent will interact with the environmentif A == 'right': # move rightif S == N_STATES - 2: # terminateS_ = 'terminal'R = 1else:S_ = S + 1R = 0else: # move leftR = 0if S == 0:S_ = S # reach the wallelse:S_ = S - 1return S_, R2.2.6 環(huán)境更新
接下來就是環(huán)境的更新了, 不用細看。
def update_env(S, episode, step_counter):# This is how environment be updatedenv_list = ['-']*(N_STATES-1) + ['T'] # '---------T' our environmentif S == 'terminal':interaction = 'Episode %s: total_steps = %s' % (episode+1, step_counter)print('\r{}'.format(interaction), end='')time.sleep(2)print('\r ', end='')else:env_list[S] = 'o'interaction = ''.join(env_list)print('\r{}'.format(interaction), end='')time.sleep(FRESH_TIME)2.2.7 強化學習主循環(huán)
最重要的地方就在這里。 你定義的 RL 方法都在這里體現(xiàn)。 在之后的教程中, 我們會更加詳細得講解 RL 中的各種方法, 下面的內(nèi)容, 大家大概看看就行, 這節(jié)內(nèi)容不用仔細研究。
def rl():q_table = build_q_table(N_STATES, ACTIONS) # 初始 q tablefor episode in range(MAX_EPISODES): # 回合step_counter = 0S = 0 # 回合初始位置is_terminated = False # 是否回合結(jié)束update_env(S, episode, step_counter) # 環(huán)境更新while not is_terminated:A = choose_action(S, q_table) # 選行為S_, R = get_env_feedback(S, A) # 實施行為并得到環(huán)境的反饋q_predict = q_table.loc[S, A] # 估算的(狀態(tài)-行為)值if S_ != 'terminal':q_target = R + GAMMA * q_table.iloc[S_, :].max() # 實際的(狀態(tài)-行為)值 (回合沒結(jié)束)else:q_target = R # 實際的(狀態(tài)-行為)值 (回合結(jié)束)is_terminated = True # terminate this episodeq_table.loc[S, A] += ALPHA * (q_target - q_predict) # q_table 更新S = S_ # 探索者移動到下一個 stateupdate_env(S, episode, step_counter+1) # 環(huán)境更新step_counter += 1return q_table寫好所有的評估和更新準則后, 我們就能開始訓練了, 把探索者丟到環(huán)境中, 讓它自己去玩吧。
if __name__ == "__main__":q_table = rl()print('\r\nQ-table:\n')print(q_table)2.2.8 補充
1.運行到q_predict = q_table.loc[S, A]出現(xiàn)了一個KeyError。
解決方式:把action_name = state_actions.argmax()改成action_name = ACTIONS[state_actions.argmax()]就可以了。
2.3 Q-learning 算法更新
2.3.1 要點
上次我們知道了 RL 之中的 Q-learning 方法是在做什么事, 今天我們就來說說一個更具體的例子。讓探索者學會走迷宮。黃色的是天堂 (reward 1), 黑色的地獄 (reward -1)。大多數(shù) RL 是由 reward 導向的, 所以定義 reward 是 RL 中比較重要的一點。
2.3.2 算法
**整個算法就是一直不斷更新 Q table 里的值, 然后再根據(jù)新的值來判斷要在某個 state 采取怎樣的 action。**Qlearning 是一個 off-policy 的算法, 因為里面的 max action 讓 Q table 的更新可以不基于正在經(jīng)歷的經(jīng)驗(可以是現(xiàn)在學習著很久以前的經(jīng)驗,甚至是學習他人的經(jīng)驗)。不過這一次的例子, 我們沒有運用到 off-policy, 而是把 Qlearning 用在了 on-policy 上, 也就是現(xiàn)學現(xiàn)賣, 將現(xiàn)在經(jīng)歷的直接當場學習并運用。
2.3.3 算法的代碼形式
首先我們先 import 兩個模塊, maze_env 是我們的環(huán)境模塊, 已經(jīng)編寫好了, 大家可以直接在這里下載, maze_env 模塊我們可以不深入研究, 如果你對編輯環(huán)境感興趣, 可以去看看如何使用 python 自帶的簡單 GUI 模塊 tkinter 來編寫虛擬環(huán)境. 我也有對應(yīng)的教程。maze_env 就是用 tkinter 編寫的. 而 RL_brain 這個模塊是 RL 的大腦部分, 我們下節(jié)會講。
from maze_env import Maze from RL_brain import QLearningTable下面的代碼, 我們可以根據(jù)上面的圖片中的算法對應(yīng)起來, 這就是整個 Qlearning 最重要的迭代更新部分啦。
def update():# 學習 100 回合for episode in range(100):# 初始化 state 的觀測值observation = env.reset()while True:# 更新可視化環(huán)境env.render()# RL 大腦根據(jù) state 的觀測值挑選 actionaction = RL.choose_action(str(observation))# 探索者在環(huán)境中實施這個 action, 并得到環(huán)境返回的下一個 state 觀測值, reward 和 done (是否是掉下地獄或者升上天堂)observation_, reward, done = env.step(action)# RL 從這個序列 (state, action, reward, state_) 中學習RL.learn(str(observation), action, reward, str(observation_))# 將下一個 state 的值傳到下一次循環(huán)observation = observation_# 如果掉下地獄或者升上天堂, 這回合就結(jié)束了if done:break# 結(jié)束游戲并關(guān)閉窗口print('game over')env.destroy()if __name__ == "__main__":# 定義環(huán)境 env 和 RL 方式env = Maze()RL = QLearningTable(actions=list(range(env.n_actions)))# 開始可視化環(huán)境 envenv.after(100, update)env.mainloop()2.3.4 補充
1.為什么訓練的時候,紅色方框會斜著走?
應(yīng)該是 tkinter 的跳幀問題,實際上還是走了 2 步
2.observation的初始值(5,5,35,35)對應(yīng)成我們可以理解的坐標(x,y),這應(yīng)該是怎么轉(zhuǎn)換的呢?
是正方形的x1,y1,x2,y2 坐標。
3.您好,我看maze_env的代碼時,沒有看到針對不同state對action的限制。
比如當已經(jīng)走到maze的邊界,這時不需要限制方塊的行為嗎?運行run this后,發(fā)現(xiàn)方塊在邊界的移動刷新速率并不總相同,是否因為上述action未受限?
在maze_env.py的第95行有限制。
if action == 0:if s[1] > UNIT:base_action[1] -= UNIT elif action == 1: if s[1] < (MAZE_H - 1) * UNIT:base_action[1] += UNIT elif action == 2: if s[0] < (MAZE_W - 1) * UNIT:base_action[0] += UNIT elif action == 3: if s[0] > UNIT:base_action[0] -= UNIT但是在rl的action輸出上,不會對上下左右動作做限制。
2.4 Q-learning 思維決策
2.4.1 代碼主結(jié)構(gòu)
與上回不一樣的地方是, 我們將要以一個 class 形式定義 Q learning, 并把這種 tabular q learning 方法叫做 QLearningTable。
class QLearningTable:# 初始化def __init__(self, actions, learning_rate=0.01, reward_decay=0.9, e_greedy=0.9):# 選行為def choose_action(self, observation):# 學習更新參數(shù)def learn(self, s, a, r, s_):# 檢測 state 是否存在def check_state_exist(self, state):2.4.2 預設(shè)值
import numpy as np import pandas as pdclass QLearningTable:def __init__(self, actions, learning_rate=0.01, reward_decay=0.9, e_greedy=0.9):self.actions = actions # a listself.lr = learning_rate # 學習率self.gamma = reward_decay # 獎勵衰減self.epsilon = e_greedy # 貪婪度self.q_table = pd.DataFrame(columns=self.actions, dtype=np.float64) # 初始 q_table2.4.3 決定行為
這里是定義如何根據(jù)所在的 state, 或者是在這個 state 上的 觀測值 (observation) 來決策。
def choose_action(self, observation):self.check_state_exist(observation) # 檢測本 state 是否在 q_table 中存在(見后面標題內(nèi)容)# 選擇 actionif np.random.uniform() < self.epsilon: # 選擇 Q value 最高的 actionstate_action = self.q_table.loc[observation, :]# 同一個 state, 可能會有多個相同的 Q action value, 所以我們亂序一下action = np.random.choice(state_action[state_action == np.max(state_action)].index)else: # 隨機選擇 actionaction = np.random.choice(self.actions)return action2.4.4 學習
同上一個簡單的 q learning 例子一樣, 我們根據(jù)是否是 terminal state (回合終止符) 來判斷應(yīng)該如何更行 q_table. 更新的方式是不是很熟悉呢:
update = self.lr * (q_target - q_predict)這可以理解成神經(jīng)網(wǎng)絡(luò)中的更新方式, 學習率 * (真實值 - 預測值). 將判斷誤差傳遞回去, 有著和神經(jīng)網(wǎng)絡(luò)更新的異曲同工之處。
def learn(self, s, a, r, s_):self.check_state_exist(s_) # 檢測 q_table 中是否存在 s_ (見后面標題內(nèi)容)q_predict = self.q_table.loc[s, a]if s_ != 'terminal':q_target = r + self.gamma * self.q_table.loc[s_, :].max() # 下個 state 不是 終止符else:q_target = r # 下個 state 是終止符self.q_table.loc[s, a] += self.lr * (q_target - q_predict) # 更新對應(yīng)的 state-action 值2.4.5 檢測 state 是否存在
這個功能就是檢測 q_table 中有沒有當前 state 的步驟了, 如果還沒有當前 state, 那我們就插入一組全 0 數(shù)據(jù), 當做這個 state 的所有 action 初始 values。
def check_state_exist(self, state):if state not in self.q_table.index:# append new state to q tableself.q_table = self.q_table.append(pd.Series([0]*len(self.actions),index=self.q_table.columns,name=state,))總結(jié)
以上是生活随笔為你收集整理的莫烦强化学习-Q Learning的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux NAT设定
- 下一篇: 最详细的U-BOOT源码分析及移植