强化学习(八) - 深度Q学习(Deep Q-learning, DQL,DQN)原理及相关实例
深度Q學習原理及相關實例
- 8. 深度Q學習
- 8.1 經驗回放
- 8.2 目標網絡
- 8.3 相關算法
- 8.4 訓練算法
- 8.5 深度Q學習實例
- 8.5.1 主程序
- 程序注釋
- 8.5.2 DQN模型構建程序
- 程序注釋
- 8.5.3 程序測試
- 8.6 雙重深度Q網絡
- 8.7 對偶深度Q網絡
8. 深度Q學習
深度Q學習將深度學習和強化學習相結合,是第一個深度強化學習算法。深度Q學習的核心就是用一個人工神經網絡q(s,a;θ),s∈S,a∈Aq(s,a;\theta),s∈\mathcal{S},a∈\mathcal{A}q(s,a;θ),s∈S,a∈A來代替動作價值函數。其中θ\thetaθ為神經網絡權重,在前面文章中,也使用過w\text{w}w。由于神經網絡具有強大的表達能力,能夠自動尋找特征,所以采用神經網絡有潛力比傳統人工特征強大得多。最近基于深度Q網絡的深度強化學習算法有了重大的進展,在目前學術界有非常大的影響力。當同時出現異策、自益和函數近似時,無法保證收斂性,會出現訓練不穩定或訓練困難等問題。針對出現的各種問題,研究人員主要從以下兩方面進行了改進。
- 經驗回放(experience replay):將經驗(即歷史的狀態、動作、獎勵等)存儲起來,再在存儲的經驗中按一定的規則采樣。
- 目標網絡(target network):修改網絡的更新方式,例如不把剛學習到的網絡權重馬上用于后續的自益過程。本節后續內容將從這兩條主線出發,介紹基于深度Q網絡的強化學習算法。
8.1 經驗回放
V. Mnih 等在 2013 年 發 表 文 章 《Playing Atari with deep reinforcement leaming》,提出了基于經驗回放的深度Q網絡,標志著深度Q網絡的誕生,也標志著深度強化學習的誕生1。
采用批處理的模式能夠提供穩定性。經驗回放就是一種讓經驗的概率分布變得穩定的技術,它能提高訓練的穩定性。
經驗回放主要有“存儲”和 “采樣回放”兩大關鍵步驟。其相關算法在之后會介紹, 現在主要來看其特征。
- 存儲:將軌跡以(St,At,Rt+1,St+1)(S_t,A_t, R_{t+1}, S_{t+1})(St?,At?,Rt+1?,St+1?) 等形式存儲起來;
- 采樣回放:使用某種規則從存儲的(St,At,Rt+1,St+1)(S_t,A_t, R_{t+1}, S_{t+1})(St?,At?,Rt+1?,St+1?) 中隨機取出一條或多條經驗。
經驗回放有以下好處。
- 在訓練Q網絡時,可以消除數據的關聯,使得數據更像是獨立同分布的(獨立同分布是很多有監督學習的證明條件)。這樣可以減小參數更新的方差,加快收斂。
- 能夠重復使用經驗,對于數據獲取困難的情況尤其有用。從存儲的角度,經驗回放可以分為集中式回放和分布式回放。
回放可以分為以下幾種,
- 集中式回放:智能體在一個環境中運行,把經驗統一存儲在經驗池中。
- 分布式回放:智能體的多份拷貝(worker)同時在多個環境中運行,并將經驗統一存 儲于經驗池中。由于多個智能體拷貝同時生成經驗,所以能夠在使用更多資源的同
時更快地收集經驗。從采樣的角度,經驗回放可以分為均勻回放和優先回放。 - 均勻回放:等概率從經驗集中取經驗,并且用取得的經驗來更新最優價值函數。
- 優先回放(PrioritizedExperienceReplay, PER): 為經驗池里的每個經驗指定一個優
先級,在選取經驗時更傾向于選擇優先級高的經驗。
T. Schaul等 于 2016年發表文章《Prioritized experience replay》,提出了優先回放。優先回放的基本思想是為經驗池里的經驗指定一個優先級,在選取經驗時更傾向于選擇優先級高的經驗。一般的做法是,如果某個經驗(例如經驗iii)的優先級為pip_ipi?,那么選取該經驗的概率為
pi=pi∑kpkp_i = \frac{p_i}{\sum_{k} p_k}pi?=∑k?pk?pi??
經驗值有許多不同的選取方法,最常見的選取方法有成比例優先和基于排序優先。
- 成比例優先(proportional priority):第iii個經驗的優先級為
pi=(δi+ε)αp_i = {(\delta_i + \varepsilon)^{\alpha}}pi?=(δi?+ε)α
其中δi\delta_iδi?是時序差分誤差,ε\varepsilonε是預先選擇的一個小正數,α\alphaα是正參數。 - 基于排序優先(rank-basedpriority):第iii個經驗的優先級為
pi=(1ranki)αp_i = (\frac{1}{\text{rank}_{i}})^{\alpha}pi?=(ranki?1?)α
其中ranki\text{rank}_{i}ranki?是第iii個經驗從大到小排序的排名, 排名從1開始。
經驗回放也不是完全沒有缺點。例如,它也會導致回合更新和多步學習算法無法使用。一般情況下,如果我們將經驗回放用于Q學習,就規避了這個缺點。
8.2 目標網絡
對于基于自益的Q學習,其回報的估計和動作價值的估計都和權重θ\thetaθ有關。當權重值變化時,回報的估計和動作價值的估計都會變化。在學習的過程中,動作價值試圖追逐一個變化的回報,也容易出現不穩定的情況。可以使用之前介紹的半梯度下降的算法來解決這個問題。在半梯度下降中,在更新價值參數θ\thetaθ時,不對基于自益得到的回報估計UtU_{t}Ut?求梯度。其中一種阻止對UtU_tUt?求梯度的方法就是將價值參數復制一份得到θtarget\theta_{\text{target}}θtarget?, 在計算UtU_tUt?時用θtarget\theta_{\text{target}}θtarget?目標計算。
基于這一方法,V. Mnih等 在 2015年發表了論文《Human-level control through deep reinforcement learning》提出了目標網絡(target network) 這一概念。 目標網絡是在原有的神經網絡之外再搭建一份結構完全相同的網絡。原先就有的神經網絡稱為評估網絡( evaluation network)。在學習的過程中,使用目標網絡來進行自益得到回報的評估值,作 為學習的目標。在權重更新的過程中,只更新評估網絡的權重,而不更新目標網絡的權重。這樣,更新權重時針對的目標不會在每次迭代都變化,是一個固定的目標。在完成一定次數的更新后,再將評估網絡的權重值賦給目標網絡,進而進行下一批更新。這樣,目標網絡也能得到更新。由于在目標網絡沒有變化的一段時間內回報的估計是相對固定的,目標網絡的引入增加了學習的穩定性。所以,目標網絡目前已經成為深度Q學習的主流做法。
8.3 相關算法
現在我們考慮使用深度Q學習算法來訓練智能體玩游戲2。
在每一個時間步驟中,智能體從游戲動作集A=1,...K\mathcal{A} = {1, ... K}A=1,...K中選擇一個動作。該動作被傳遞給模擬器并修改其內部狀態和游戲分數。在一般情況下,環境可能是隨機的。仿真器的內部狀態不被智能體觀察到,相反,智能體觀察到一個來自仿真器的圖像xt∈Rdx_t\in \mathbb{R}^dxt?∈Rd,這是一個代表當前屏幕的像素值的向量。此外,它還會收到代表游戲分數變化的獎勵 rtr_trt?。需要注意的是,一般情況下,游戲得分可能取決于之前的整個動作和觀察序列;關于一個動作的反饋可能只有在經過數千次的時間步長之后才會收到。
由于智能體只能觀察當前屏幕,任務是部分觀察,許多模擬器狀態在感知上是異構的(即不可能只從當前屏幕xtx_txt?中完全了解當前情況)。因此,動作和觀察的序列st=x1,a1,x2,...,at?1,xts_t = x_1,a_1,x_2,...,a_{t-1},x_tst?=x1?,a1?,x2?,...,at?1?,xt? 被輸入到算法中,然后算法根據這些序列學習游戲策略。仿真器中的所有序列都被假定為在有限的時間步長內終止。這個形式化的過程產生了一個大而有限的馬爾科夫決策過程(MDP),在這個過程中,每個序列都是一個獨立的狀態。因此,我們可以將標準的強化學習方法應用于MDP,只需將完整序列sts_tst?作為時間ttt的狀態表示即可。
智能體的任務是在模擬器中選擇最佳的動作最大化未來的損失.我們做一個標準的假設,對未來的每一步回報采用一個折扣因子γ\gammaγ(γ\gammaγ從始至終設置為0.99),然后定義了在時間ttt上經過折扣后的回報Rt=∑t′=tTγt′?trt′R_t = \sum_{t'=t}^{T}\gamma^{t'-t}r_{t'}Rt?=∑t′=tT?γt′?trt′?,其中TTT為最終停止的時間步。我們定義最佳動作價值函數Q?(s,a)Q^*(s, a)Q?(s,a)作為遵循任何策略所能獲得的最大預期收益。在經過一些狀態sss和采取一些動作aaa后,Q?(s,a)=max?πE[Rt∣st=s,at=a,π]Q^*(s, a) = \max_{\pi}\mathbb{E}[R_t|s_t = s, a_t =a, \pi]Q?(s,a)=maxπ?E[Rt?∣st?=s,at?=a,π],其中π\piπ作為在狀態sss采取的動作aaa的映射,即策略。
最優行為價值函數遵循一個重要的恒等式,這個恒等式被稱為貝爾曼方程(Bellman equation)。這基于以下直覺:如果狀態s′s's′在下一個時間步的最優值Q?(s′,a′)Q^*(s', a')Q?(s′,a′)對于所有可能的行動a′a'a′都已知,那么最優策略就是選擇使期望值r+γQ?(s′,a′)r + \gamma Q^*(s', a')r+γQ?(s′,a′)最大化的行動a′a'a′:
Q?(s,a)=Es′[r+γmax?a′Q?(s′,a′)∣s,a]Q^*(s, a) = \mathbb{E}_{s'}[r + \gamma \max_{a'}Q^*(s', a')|s, a]Q?(s,a)=Es′?[r+γa′max?Q?(s′,a′)∣s,a]
許多強化學習算法背后的基本思想是通過使用貝爾曼方程作為迭代更新來估計動作價值函數,Qi+1(s,a)=Es′[r+γmax?a′Qi(s′,a′)∣s,a]Q_{i+1}(s, a) = \mathbb{E}_{s'}[r + \gamma \max_{a'}Q_{i}(s', a')|s, a]Qi+1?(s,a)=Es′?[r+γmaxa′?Qi?(s′,a′)∣s,a]。這些價值迭代算法都收斂于最優動作價值函數,當i→∞i\to \infini→∞時Qi→Q?Q_i \to Q^*Qi?→Q?。在實踐中,這種基本的方法是不切實際的,因為動作-價值函數是對每個狀態分別估計的,沒有任何泛化。相反,通常使用函數逼近器來估計動作價值函數Q(s,a;θ)≈Q?(s,a)Q(s, a;\theta) \approx Q^*(s, a)Q(s,a;θ)≈Q?(s,a)。在強化學習中這是典型的線性函數逼近器,但是有時用非線性函數逼近器代替,如神經網絡。我們把帶有權值θ\thetaθ的神經網絡函數逼近器稱為Q網絡。Q網絡可以通過在迭代iii中調整參數θi\theta_iθi?來訓練減少貝爾曼方程中的均方誤差, 其中最佳目標值r+γmax?a′Q?(s′,a′)r+\gamma \max_{a'}Q^*(s', a')r+γmaxa′?Q?(s′,a′)被替代為近似目標值y=r+γmax?a′Q(s′,a′;θi?)y =r+\gamma \max_{a'}Q(s', a';\theta_i^{-})y=r+γmaxa′?Q(s′,a′;θi??),其使用先前的一些迭代中的參數θi?\theta_{i}^{-}θi??。這就產生了一個損失函數Li(θi)L_i(\theta_i)Li?(θi?)的序列,它在每次迭代iii時發生變化,
Li(θi)=Es,a,r[(Es′[y∣s,a]?Q(s,a;θi))2]=Es,a,r,s′[(y?Q(s,a;θi))2]+Es,a,r[Vs′[y]]\begin{aligned}L_i(\theta_i) & = \mathbb{E}_{s, a,r} [(\mathrm{E}_{s'}[y|s,a] - Q(s,a;\theta_i))^2] \\ &= \mathbb{E}_{s, a,r, s'}[(y - Q(s, a;\theta_i))^2]+ \mathrm{E}_{s, a, r}[\mathrm{V}_{s'}[y]]\end{aligned}Li?(θi?)?=Es,a,r?[(Es′?[y∣s,a]?Q(s,a;θi?))2]=Es,a,r,s′?[(y?Q(s,a;θi?))2]+Es,a,r?[Vs′?[y]]?
請注意,目標取決于網絡權重;這與用于監督學習的目標不同,后者在學習開始前是固定的。在優化的每一個階段,我們在優化第iii個損失函數Li(θi)L_i(\theta_i)Li?(θi?)時,保持上一次迭代的參數θi?\theta_{i}^-θi??固定,從而產生一系列定義明確的優化問題。最后一項是目標的方差,它不依賴于我們當前優化的參數θi\theta_iθi?,因此可以忽略。將損失函數相對于權重進行微分,我們得出以下梯度:
?θiL(θi)=Es,a,r,s′[(r+γmax?a′Q(s′,a′;θi?)?Q(s,a;θi))?θiQ(s,a;θi))]\nabla_{\theta_i}L(\theta_i) = \mathbb{E}_{s,a,r,s'}[(r+\gamma\max_{a'}Q(s',a';\theta_{i}^-)-Q(s, a;\theta_i))\nabla_{\theta_i}Q(s, a;\theta_i))]?θi??L(θi?)=Es,a,r,s′?[(r+γa′max?Q(s′,a′;θi??)?Q(s,a;θi?))?θi??Q(s,a;θi?))]
與其計算上述梯度中的全部期望值,不如通過隨機梯度下降來優化損失函數,這通常是計算上的便利。在這個框架中,通過在每一個時間步長后更新權重,使用單樣本替換期望值,并設置θi?=θi?1\theta_{i}^- = \theta_{i-1}θi??=θi?1?,可以恢復熟悉的Q-learning算法。
需要注意的是,這個算法是無模型的:它直接使用仿真器的樣本來解決強化學習任務,而不需要明確地估計獎賞和過渡動態P(r,s′∣s,a)P(r, s'|s, a)P(r,s′∣s,a).它也是off-policy:它學習貪婪的策略a=arg?max?a′Q(s,a′;θ)a = \argmax_{a'}Q(s,a';\theta)a=a′argmax?Q(s,a′;θ),以確保充分探索狀態空間。在實際工作中,行為分布往往由ε\varepsilonε-greedy策略選擇,遵循概率1?ε1-\varepsilon1?ε貪婪策略,選擇概率ε\varepsilonε的隨機行動。
8.4 訓練算法
訓練深度Q-網絡的完整算法在下圖所示的算法1中提出。智能體根據基于Q表的ε\varepsilonε-貪婪策略選擇和執行動作。 由于使用任意長度的歷史作為神經網絡的輸入可能是困難的,Q函數因此工作在由上述函數?\phi?產生的固定長度的歷史表征上。該算法以兩種方式修改了標準的在線Q-learning,使其適用于訓練大型神經網絡而不產生分歧。
首先,這里使用了經驗回放,我們將智能體在每個時間步的經驗et=(st,at,rt,st+1)e_t=(s_t,a_t,r_t,s_{t+1})et?=(st?,at?,rt?,st+1?)存儲在一個數據集Dt=e1,...,etD_t={e_1,...,e_t}Dt?=e1?,...,et?中,將許多情節(其中一個情節的結束發生在達到終端狀態時)匯集到重放存儲器中。在算法的內循環過程中,我們對從存儲樣本池中隨機抽取的經驗樣本(s,a,r,s′)~U(D)(s,a,r,s') \thicksim U(D)(s,a,r,s′)~U(D)進行Q-learning更新,或稱minibatch更新。這種方法比標準的在線Q-learning有幾個優勢。
- 第一,每一步的經驗都有可能被用于許多權重更新,這使得數據效率更高。
- 第二,直接從連續的樣本中學習是低效的,因為樣本之間有很強的相關性;隨機化樣本可以打破這些相關性,從而降低更新的方差。
- 第三,在對策略進行學習時,當前的參數決定了參數訓練的下一個數據樣本。例如,如果最大化動作是向左移動,那么訓練樣本將以左手邊的樣本為主;如果最大化動作隨后切換到右邊,那么訓練分布也將切換。
很容易看出,不需要的反饋循環可能會出現,參數可能會被卡在一個糟糕的局部最小值中,甚至是災難性的偏離。通過使用經驗重放,行為分布是對其以前的許多狀態進行平均,平滑學習,避免參數的振蕩或發散。需要注意的是,通過經驗重放學習時,需要進行off-policy學習(因為我們當前的參數與用于生成樣本的參數不同),這也是選擇Q-learning的動機。
在實踐中,算法只在重放存儲器中存儲最后的NNN個經驗元組,并在執行更新時從DDD中隨機均勻取樣。這種方法在某些方面是有局限性的,因為內存緩沖區并不能區分重要的轉折,而且由于內存大小NNN是有限的,所以總是用最近的轉折來覆蓋。同樣,均勻采樣對重放內存中的所有轉折給予同等的重要性。
對在線Q-learning的第二個修改旨在進一步提高方法與神經網絡的穩定性,就是在Q-learning更新中使用一個單獨的網絡來生成目標yjy_jyj?,即,建立目標網絡。更準確的說,每一次C更新,我們都會克隆網絡Q,得到一個目標網絡Q^\hat{Q}Q^?,并使用Q^\hat{Q}Q^?來生成Q-learning目標yjy_jyj?,用于后續C更新Q。與標準的在線Q-learning相比,這種修改使得算法更加穩定,在標準的在線Q-learning中,增加Q(st,at)Q(s_t,a_t)Q(st?,at?)的更新往往也會增加所有aaa的Q(st+1,a)Q(s_{t+1},a)Q(st+1?,a),因此也會增加目標yjy_jyj?,可能會導致策略的振蕩或分歧。使用較舊的參數集生成目標,在對Q進行更新和更新影響目標yjy_jyj?之間增加了一個延遲,使得分歧或振蕩的可能性大大降低。
將更新r+γmax?a′Q(s′,a′;θi?)?Q(s,a;,θi)r+\gamma \max_{a'}Q(s',a';\theta_{i}^{-})-Q(s, a;,\theta_i)r+γmaxa′?Q(s′,a′;θi??)?Q(s,a;,θi?)中的誤差項約束為-1和1之間是很有幫助的.因為絕對值損失函數∣x∣|x|∣x∣對x的所有負值都有-1的導數,對x的所有正值都有1的導數,所以將平方誤差剪裁為?1-1?1和111之間相當于對(?1,1)(-1,1)(?1,1)區間外的誤差使用絕對值損失函數.這種形式的誤差剪裁進一步提高了算法的穩定性.
8.5 深度Q學習實例
在這個實例里我們采用"LunarLander-v2"環境。
LunarLander-v2 著陸臺總是在坐標(0,0)處。坐標是狀態向量的前兩個數字。從屏幕頂部移動到著陸臺并以零速度降落的獎勵大約是100…140點。如果著陸器遠離著陸臺,就會失去獎勵。如果著陸器墜毀或靜止,則事件結束,獲得額外的-100或+100分。每條腿的地面接觸是+10。發射主引擎每格為-0.3分。解決了就是+200分。可以在起落架外降落。燃料是無限的,所以智能體可以學習飛行,然后在第一次嘗試降落。有四個離散動作可供選擇:什么都不做、發射左方位引擎、發射主引擎、發射右方位引擎。
8.5.1 主程序
這是我們的主程序, 在其中我們建立相關環境并調用了子函數來建立深度強化網絡模型進行訓練。神經網絡框架采用pytorch, 神經網絡部分簡單的采用3層全連接神經網絡。
import gym
import random
import torch
import numpy as np
from collections import deque
import matplotlib.pyplot as plt
from dqn_agent import Agent
import osdef dqn(n_episode=2000, max_t=1000, eps_start=1.0, eps_end=0.01, eps_decay=0.995, mode='train'):"""Deep Q-Learning:param n_episode:maximum number of training episodes:param max_t:maximum number of timesteps per episode:param eps_start:starting value of epsilon, for epsilon-greedy action selection:param eps_end:minimum value of epsilon:param eps_decay:multiplicative factor (per episode) for decreasing epsilon:return: final score"""scores = []scores_window = deque(maxlen=100)eps = eps_startif mode == 'train':for i_episode in range(1, n_episode+1):# 初始化狀態state = env.reset()score = 0for t in range(max_t):action = agent.act(state, eps)next_state, reward, done, _ = env.step(action)agent.step(state, action, reward, next_state, done)state = next_statescore += rewardif done:breakscores_window.append(score)scores.append(score)eps = max(eps_end, eps_decay*eps)print('\rEpisode {}\t Average Score:{:.2f}'.format(i_episode, np.mean(scores_window)), end="")if i_episode % 100 == 0:print('\rEpisode {}\rAverage Score :{:.2f}'.format(i_episode, np.mean(scores_window)))if np.mean(scores_window) >= 200.0:print('\nEnvironment solved in {:d} episode! \t Average Score: {:.2f}'.format(i_episode, np.mean(scores_window)))torch.save(agent.qnetwork_local.state_dict(), 'checkpoint.pth')breakelse:# 訓練一次state = env.reset()for j in range(200):action = agent.act(state, eps)print('state :{} action :{}'. format(state, action))env.render()next_state, reward, done, _ = env.step(action)print('next_state={}, reward={}, done={}'.format(next_state, reward, done))agent.step(state, action, reward, next_state, done)if done:breakreturn scoresif __name__ == '__main__':os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'env = gym.make('LunarLander-v2')env.seed(0)print('State shape: ', env.observation_space.shape)print('Number of actions: ', env.action_space.n)MODE = 'train'if MODE == 'debug':# 調試模式agent = Agent(state_size=8, action_size=4, seed=1,debug_mode=True)scores = dqn(mode='test')elif MODE == 'run':agent = Agent(state_size=8, action_size=4, seed=1)agent.qnetwork_local.load_state_dict(torch.load('checkpoint.pth'))# 以當前策略運行for i in range(3):state = env.reset()for j in range(200):action = agent.act(state)env.render()state, reward, done, _ = env.step(action)if done:breakenv.close()else:# 訓練模式agent = Agent(state_size=8, action_size=4, seed=1)scores = dqn()# plot the scoresfig = plt.figure()ax = fig.add_subplot(111)plt.plot(np.arange(len(scores)), scores)plt.ylabel('Score')plt.xlabel('Episode #')plt.show()
程序注釋
MODE = 'train'if MODE == 'debug':# 調試模式agent = Agent(state_size=8, action_size=4, seed=1,debug_mode=True)scores = dqn(mode='test')elif MODE == 'run':agent = Agent(state_size=8, action_size=4, seed=1)agent.qnetwork_local.load_state_dict(torch.load('checkpoint.pth'))...else:# 訓練模式agent = Agent(state_size=8, action_size=4, seed=1)scores = dqn()
在這里提供了程序運行的三種模式,“debug”, “run”, "train"模式。debug模式是為了方便查看在程序運行過程中的各種參數,方便程序調試和后期更改而設置的。run模式是在模型訓練完成后可以使用訓練完成的神經網絡來查看最終效果。 train模式即訓練模式,沒有太多相關數據輸出。
8.5.2 DQN模型構建程序
這部分為模型構建子程序,包含了DQN最重要的算法。程序包含了3個類, 分別是class QNetwork, class Agent和class ReplayBuffer。
import numpy as np
import random
from collections import namedtuple, dequeimport torch
import torch.nn.functional as F
import torch.nn as nn
import torch.optim as optimBUFFER_SIZE = int(1e4) # 經驗回放的緩沖區的大小
BATCH_SIZE = 64 # 最小訓練批數量
GAMMA = 0.99 # 折扣率
TAU = 1e-3 # 用于目標函數的柔性策略更新
LR = 5e-4 # 學習率
UPDATE_EVERY = 4 # 更新網絡的頻率device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")class QNetwork(nn.Module):"""Actor (Policy) Model."""def __init__(self, state_size, action_size, seed, fc1_units=64, fc2_units=64):"""Initialize parameters and build model.Params======state_size (int): Dimension of each stateaction_size (int): Dimension of each actionseed (int): Random seed"""super(QNetwork, self).__init__()self.seed = torch.manual_seed(seed)self.fc1 = nn.Linear(state_size, fc1_units)self.fc2 = nn.Linear(fc1_units, fc2_units)self.fc3 = nn.Linear(fc2_units, action_size)def forward(self, state):"""Build a network that maps state -> action values."""x = F.relu(self.fc1(state))x = F.relu(self.fc2(x))return self.fc3(x)class Agent():"""與環境相互作用,從環境中學習。"""def __init__(self, state_size, action_size, seed, debug_mode=False):"""初始化智能體對象。Params======state_size (int): dimension of each stateaction_size (int): dimension of each actionseed (int): random seed"""self.state_size = state_sizeself.action_size = action_sizeself.seed = random.seed(seed)self.debug_mode = debug_modeprint('Program running in {}'.format(device))# Q-Networkself.qnetwork_local = QNetwork(state_size, action_size, seed).to(device)self.qnetwork_target = QNetwork(state_size, action_size, seed).to(device)self.optimizer = optim.Adam(self.qnetwork_local.parameters(), lr=LR) # 自適應梯度算法# print('Q-Network_local:{}\nQ-Network_target:{}'.format(self.qnetwork_local, self.qnetwork_target))# 經驗回放if self.debug_mode is True:self.memory = ReplayBuffer(action_size, BUFFER_SIZE, 1, seed)else:self.memory = ReplayBuffer(action_size, BUFFER_SIZE, BATCH_SIZE, seed)# 初始化時間步 (for updating every UPDATE_EVERY steps)self.t_step = 0def step(self, state, action, reward, next_state, done):# 在經驗回放中保存經驗self.memory.add(state, action, reward, next_state, done)# 在每個時間步UPDATE_EVERY中學習self.t_step = (self.t_step + 1) % UPDATE_EVERYif self.t_step == 0:# 如果內存中有足夠的樣本,取隨機子集進行學習if len(self.memory) > BATCH_SIZE:experiences = self.memory.sample()self.learn(experiences, GAMMA)if self.debug_mode is True:experiences = self.memory.sample()self.learn(experiences, GAMMA)def act(self, state, eps=0.):"""根據當前策略返回給定狀態的操作.Params======state (array_like): 當前的狀態eps (float): epsilon, 用于 epsilon-greedy action selection"""state = torch.from_numpy(state).float().unsqueeze(0).to(device)# 將qn更改成評估形式self.qnetwork_local.eval()# 禁用梯度with torch.no_grad():# 獲得動作價值action_values = self.qnetwork_local(state)# 將qn更改成訓練模式self.qnetwork_local.train()# Epsilon-greedy action selectionif random.random() > eps:return np.argmax(action_values.cpu().data.numpy())else:return random.choice(np.arange(self.action_size))def learn(self, experiences, gamma):"""使用給定的一批經驗元組更新值參數。Params======experiences (Tuple[torch.Tensor]): tuple of (s, a, r, s', done) tuples gamma (float): discount factor"""states, actions, rewards, next_states, dones = experiencesif self.debug_mode is True:print('\nstates={}, actions={}, rewards={}, next_states={}, dones={}'.format(states, actions, rewards, next_states, dones))# compute and minimize the loss# 從目標網絡得到最大的預測Q值(下一個狀態)Q_targets_next = self.qnetwork_target(next_states).detach().max(1)[0].unsqueeze(1)# 計算當前狀態的Q目標Q_targets = rewards + (gamma * Q_targets_next * (1 - dones))# 從評估網絡中獲得期望的Q值Q_expected = self.qnetwork_local(states).gather(1, actions)if self.debug_mode is True:print('Q_target_next={}, \nQ_target ={}, \nQ_expected={},'.format(Q_targets_next, Q_targets, Q_expected))# Compute lossloss = F.mse_loss(Q_expected, Q_targets)# Minimize the lossself.optimizer.zero_grad()loss.backward()# 執行單個優化步驟self.optimizer.step()# ------------------- update target network ------------------- #self.soft_update(self.qnetwork_local, self.qnetwork_target, TAU)def soft_update(self, local_model, target_model, tau):""":柔性更新模型參數。θ_target = τ*θ_local + (1 - τ)*θ_targetParams======local_model (PyTorch model): weights will be copied fromtarget_model (PyTorch model): weights will be copied totau (float): 插值參數"""for target_param, local_param in zip(target_model.parameters(), local_model.parameters()):# 柔性更新, 將src中數據復制到self中target_param.data.copy_(tau * local_param.data + (1.0 - tau) * target_param.data)class ReplayBuffer:"""Fixed-size buffer to store experience tuples."""def __init__(self, action_size, buffer_size, batch_size, seed):"""Initialize a ReplayBuffer object.Params======action_size (int): dimension of each actionbuffer_size (int): maximum size of bufferbatch_size (int): size of each training batchseed (int): random seed"""self.action_size = action_sizeself.memory = deque(maxlen=buffer_size)self.batch_size = batch_sizeself.experience = namedtuple("Experience", field_names=["state", "action", "reward", "next_state", "done"])self.seed = random.seed(seed)def add(self, state, action, reward, next_state, done):"""在memory中添加一段新的經驗."""e = self.experience(state, action, reward, next_state, done)self.memory.append(e)def sample(self):"""從memory中隨機抽取一批經驗."""experiences = random.sample(self.memory, k=self.batch_size)states = torch.from_numpy(np.vstack([e.state for e in experiences if e is not None])).float().to(device)actions = torch.from_numpy(np.vstack([e.action for e in experiences if e is not None])).long().to(device)rewards = torch.from_numpy(np.vstack([e.reward for e in experiences if e is not None])).float().to(device)next_states = torch.from_numpy(np.vstack([e.next_state for e in experiences if e is not None])).float().to(device)dones = torch.from_numpy(np.vstack([e.done for e in experiences if e is not None]).astype(np.uint8)).float().to(device)return (states, actions, rewards, next_states, dones)def __len__(self):"""Return the current size of internal memory."""return len(self.memory)
程序注釋
class QNetwork類構建了三層的神經網絡模型,class ReplayBuffer類定義了關于經驗回訪的相關功能。class Agent是最重要的類,它調用了class QNetwork和class ReplayBuffer來創建DQN模型。所以我們主要看一下class Agent的相關函數和功能。
self.state_size = state_sizeself.action_size = action_sizeself.seed = random.seed(seed)self.debug_mode = debug_mode# Q-Networkself.qnetwork_local = QNetwork(state_size, action_size, seed).to(device)self.qnetwork_target = QNetwork(state_size, action_size, seed).to(device)self.optimizer = optim.Adam(self.qnetwork_local.parameters(), lr=LR) # 自適應梯度算法
初始化智能體對象,并構建神經網絡。在這里我們需要建立兩個神經網絡,其中“qnetwork_local”作為訓練使用的神經網絡,在此之外,我們還要建立qnetwork_target目標網絡,來優化我們的訓練過程。在這里使用了“自適應梯度算法”來作為神經網絡的優化器。
# 經驗回放if self.debug_mode is True:self.memory = ReplayBuffer(action_size, BUFFER_SIZE, 1, seed)else:self.memory = ReplayBuffer(action_size, BUFFER_SIZE, BATCH_SIZE, seed)
根據相關的模式來建立經驗回放功能的類。
def step(self, state, action, reward, next_state, done):...def act(self, state, eps=0.):...def learn(self, experiences, gamma):...def soft_update(self, local_model, target_model, tau):
這些是在訓練過程中使用到的函數,它們的功能如下所示。其作用是方便與理解,其關系并不是完全如圖所示的流線型關系。例如,soft_update函數是在learn函數中調用的的一個函數,其關系并不算是線性的。
8.5.3 程序測試
接下來將模式設置為Mode = train運行程序進行訓練,要實現平均分數大于200分的目標,我的電腦需要跑40分鐘左右。使用run模式運行模型如下,
8.6 雙重深度Q網絡
之前曾提到Q學習會帶來最大化偏差,而雙重Q學習卻可以消除最大化偏差。基于查找表的雙重Q學習引入了兩個動作價值的估計Q(0)Q(0)Q(0)和Q(1)Q(1)Q(1),每次更新動作價值時用其中的一個網絡確定動作,用確定的動作和另外一個網絡來估計回報。對于深度Q學習也有同樣的結論。Deepmind于2015年發表論文《Deepreinforcement learning with double Q-learning》,將雙重Q學習用于深度Q網絡,得到了雙重深度Q網絡(Double Deep Q Network,Double DQN)。考慮到深度Q網絡已經有了評估網絡和目標網絡兩個網絡,所以雙重深度Q學習在估計回報時只需要用評估網絡確定動作,用目標網絡確定回報的估計即可。所以,只需要將
y=r+γmax?aQ(s′,a;θtarget)y =r+\gamma \max_{a}Q(s', a;\theta_{target})y=r+γamax?Q(s′,a;θtarget?)
更改為
y=r+γQ(s′,arg?max?aQ(s′,a;θi);θ?)y =r+\gamma Q(s', \argmax_{a}Q(s', a;\theta_i);\theta^-)y=r+γQ(s′,aargmax?Q(s′,a;θi?);θ?)
就得到了帶經驗回放的雙重深度Q網絡算法。
8.7 對偶深度Q網絡
Z.Wang等在2015年發表論文《Dueling network architectures for deepreinforcement learning》,提出了一種神經網絡的結構——對偶網絡(duelnetwork)。對偶網絡理論利用動作價值函數和狀態價值函數之差定義了一個新的函數——優勢函數(advantage function):
A(s,a)=Q(s,a)?V(s,a)A(s,a) = Q(s, a) - V(s, a)A(s,a)=Q(s,a)?V(s,a)
對偶Q網絡仍然用Q(θ)Q(\theta)Q(θ)來估計動作價值,只不過這時候Q(θ)Q(\theta)Q(θ)是狀態價值估計V(s;θ)V(s;\theta)V(s;θ)和優勢函數估計A(s,a;θ)A(s,a;\theta)A(s,a;θ)的疊加,即
Q(s,a;θ)=V(s;θ)+A(s,a;θ)Q(s,a;\theta)=V(s;\theta)+A(s,a;\theta)Q(s,a;θ)=V(s;θ)+A(s,a;θ)
其中V(θ)V(\theta)V(θ)和A(θ)A(\theta)A(θ)可能都只用到了θ\thetaθ中的部分參數。在訓練的過程中,V(θ)V(\theta)V(θ)和A(θ)A(\theta)A(θ)是共同訓練的,訓練過程和單獨訓練普通深度QQQ網絡并無不同之處。
來自于《強化學習:原理與python實現》 ??
參考于《Human-level control through deep reinforcement learning》Volodymyr Mnih等 ??
總結
以上是生活随笔為你收集整理的强化学习(八) - 深度Q学习(Deep Q-learning, DQL,DQN)原理及相关实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 本地训练,立等可取,30秒音频素材复刻霉
- 下一篇: 关于微信小程序中如何实现数据可视化-ec