<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          用強(qiáng)化學(xué)習(xí)通關(guān)超級馬里奧!

          共 4342字,需瀏覽 9分鐘

           ·

          2022-04-26 14:15

          ?Datawhale干貨?
          作者:肖遙,華中農(nóng)業(yè)大學(xué),Datawhale優(yōu)秀學(xué)習(xí)者

          DQN算法實(shí)踐之速通超級馬里奧

          作為強(qiáng)化學(xué)習(xí)(Reinforce Learning,RL)的初學(xué)者,常常想將RL的理論應(yīng)用于實(shí)際環(huán)境,以超級馬里奧為例,當(dāng)看著自己訓(xùn)練的AI逐漸適應(yīng)環(huán)境,得分越來越高,到最后能完美躲避所有障礙,快速通關(guān)時(shí),你肯定能體會到算法的魅力,成就感十足!本文不拘泥于DQN(Deep Q Learning Network)算法的深層原理,主要從代碼實(shí)現(xiàn)的角度,為大家簡潔直白的介紹DQN以及其改進(jìn)方法,接著,基于Pytorch官方強(qiáng)化學(xué)習(xí)教程,應(yīng)用改進(jìn)后的DQN算法訓(xùn)練超級馬里奧,并得到更為優(yōu)秀的結(jié)果。

          本文主要內(nèi)容:

          主要參考與項(xiàng)目地址:
          算法理論參考:https://datawhalechina.github.io/easy-rl
          算法代碼參考:https://github.com/datawhalechina/easy-rl/tree/master/codes

          書籍:Datawhale強(qiáng)化學(xué)習(xí)教程

          Pytorch官方強(qiáng)化學(xué)習(xí)示例:
          https://pytorch.org/tutorials/intermediate/mario_rl_tutorial.html
          https://github.com/yfeng997/MadMario
          本文項(xiàng)目地址:https://github.com/likemango/DQN-mario-xiaoyao

          一、Basic DQN

          DQN用一個(gè)神經(jīng)網(wǎng)絡(luò)替換Q-Learning中的最優(yōu)動作價(jià)值函數(shù)Q*表格,彌補(bǔ)了Q-Learning只能表示有限個(gè)狀態(tài)的缺陷。訓(xùn)練DQN網(wǎng)絡(luò)模型常見的代碼流程如下:

          def?train(cfg,?env,?agent):
          ????'''?訓(xùn)練
          ????'
          ''
          ????print('開始訓(xùn)練!')
          ????print(f'環(huán)境:{cfg.env_name}, 算法:{cfg.algo_name}, 設(shè)備:{cfg.device}')
          ????rewards?=?[]??#?記錄所有回合的獎勵
          ????ma_rewards?=?[]??#?記錄所有回合的滑動平均獎勵
          ????for?i_ep?in?range(cfg.train_eps):
          ????????ep_reward?=?0??#?記錄一回合內(nèi)的獎勵
          ????????state?=?env.reset()??#?重置環(huán)境,返回初始狀態(tài)
          ????????while?True:
          ????????????action?=?agent.choose_action(state)??#?選擇動作
          ????????????next_state,?reward,?done,?_?=?env.step(action)??#?更新環(huán)境,返回transition
          ????????????agent.memory.push(state,?action,?reward,
          ??????????????????????????????next_state,?done)??#?保存transition
          ????????????state?=?next_state??#?更新下一個(gè)狀態(tài)
          ????????????agent.update()??#?更新智能體
          ????????????ep_reward?+=?reward??#?累加獎勵
          ????????????if?done:
          ????????????????break
          ????????rewards.append(ep_reward)
          ????????if?ma_rewards:
          ????????????ma_rewards.append(0.9?*?ma_rewards[-1]?+?0.1?*?ep_reward)
          ????????else:
          ????????????ma_rewards.append(ep_reward)
          ????????if?(i_ep?+?1)?%?10?==?0:
          ????????????print('回合:{}/{}, 獎勵:{}'.format(i_ep?+?1,?cfg.train_eps,?ep_reward))
          ????print('完成訓(xùn)練!')
          ????env.close()
          ????return?rewards,?ma_rewards

          其中cfg表示訓(xùn)練過程中的參數(shù),env表示訓(xùn)練的交互環(huán)境,agent表示一個(gè)DQN的類對象。DQN類中的核心內(nèi)容有:經(jīng)驗(yàn)緩存(memory)、動作選擇(choose_action)和模型參數(shù)更新(update)這三個(gè)部分:memory用于存儲訓(xùn)練過程中的經(jīng)驗(yàn)五元組(state,action,reward,next_state,done);choose_action方法實(shí)現(xiàn)了輸入狀態(tài)state,輸出相應(yīng)的動作結(jié)果,一般采用ε-greedy方法,探索概率為ε,網(wǎng)絡(luò)選擇動作概率為1-ε,這是DQN訓(xùn)練中重要的超參數(shù)之一;在update方法中,采樣memory中的五元組信息,使用TD(temporary difference)算法進(jìn)行計(jì)算出TD target和TD Error,再通過做反向梯度計(jì)算,最后做模型參數(shù)更新(https://datawhalechina.github.io/easy-rl/#/chapter3/chapter3?id=temporal-difference)。

          Basic DQN能夠解決一些簡單的離散動作問題,例如gym環(huán)境中的“CartPole”,然而對于稍微復(fù)雜的環(huán)境卻難以得到好的效果。DQN方法的缺點(diǎn)是存在非均勻的高估問題(OverEstimate),在多輪學(xué)習(xí)更新中,會造成最優(yōu)動作價(jià)值函數(shù)Q*偏離真實(shí)值,使得網(wǎng)絡(luò)無法輸出正確的結(jié)果(https://datawhalechina.github.io/easy-rl/#/chapter7/chapter7)。

          高估發(fā)生在兩個(gè)地方:
          1.Update中計(jì)算TD target時(shí)取最大化操作。
          2.Update中的自舉(bootstraping)操作。

          一些常見的改進(jìn)辦法是對Update方法以及網(wǎng)絡(luò)模型進(jìn)行優(yōu)化,盡可能的減小高估問題,下面介紹一些易于實(shí)現(xiàn)且高效的改進(jìn)方法。

          二、Nature DQN

          所謂自舉,即利用網(wǎng)絡(luò)模型自己去更新自己,既然自舉會造成高估問題,那么可以不用網(wǎng)絡(luò)本身去更新自己——一個(gè)直接的想法是使用另一個(gè)新的網(wǎng)絡(luò)去更新DQN網(wǎng)絡(luò)。新網(wǎng)絡(luò)的模型結(jié)構(gòu)與DQN本身一樣,在計(jì)算TD target時(shí)使用該網(wǎng)絡(luò)的計(jì)算結(jié)果,因而也稱該網(wǎng)絡(luò)為目標(biāo)網(wǎng)絡(luò)(target network)。結(jié)合上面介紹的Basic DQN,NatureDQN的實(shí)現(xiàn)如下(policy_net為DQN網(wǎng)絡(luò),target_net為目標(biāo)網(wǎng)絡(luò)):

          class?DQN:
          ????def?__init__(self,?state_dim,?action_dim,?cfg):
          ????????self.action_dim?=?action_dim??#?總的動作個(gè)數(shù)
          ????????self.device?=?cfg.device??#?設(shè)備,cpu或gpu等
          ????????self.gamma?=?cfg.gamma??#?獎勵的折扣因子
          ????????#?e-greedy策略相關(guān)參數(shù)
          ????????self.frame_idx?=?0??#?用于epsilon的衰減計(jì)數(shù)
          ????????self.epsilon?=?lambda?frame_idx:?cfg.epsilon_end?+?(cfg.epsilon_start?-?cfg.epsilon_end)?*?\
          ????????????math.exp(-1.?*?frame_idx?/?cfg.epsilon_decay)
          ????????self.batch_size?=?cfg.batch_size
          ????????self.policy_net?=?MLP(state_dim,?action_dim,hidden_dim=cfg.hidden_dim).to(self.device)
          ????????self.target_net?=?MLP(state_dim,?action_dim,hidden_dim=cfg.hidden_dim).to(self.device)
          ????????#?復(fù)制參數(shù)到目標(biāo)網(wǎng)路targe_net
          ????????for?target_param,?param?in?zip(self.target_net.parameters(),self.policy_net.parameters()):?
          ????????????target_param.data.copy_(param.data)
          ????????self.optimizer?=?optim.Adam(self.policy_net.parameters(),?lr=cfg.lr)?#?優(yōu)化器
          ????????self.memory?=?ReplayBuffer(cfg.memory_capacity)?#?經(jīng)驗(yàn)回放

          ????def?choose_action(self,?state):
          ????????'''?選擇動作
          ????????'
          ''
          ????????self.frame_idx?+=?1
          ????????if?random.random()?>?self.epsilon(self.frame_idx):
          ????????????with?torch.no_grad():
          ????????????????state?=?torch.tensor([state],?device=self.device,?dtype=torch.float32)
          ????????????????q_values?=?self.policy_net(state)
          ????????????????action?=?q_values.max(1)[1].item()?#?選擇Q值最大的動作
          ????????else:
          ????????????action?=?random.randrange(self.action_dim)
          ????????return?action
          ????def?update(self):
          ????????if?len(self.memory)?#?當(dāng)memory中不滿足一個(gè)批量時(shí),不更新策略
          ????????????return
          ????????#?從經(jīng)驗(yàn)回放中(replay?memory)中隨機(jī)采樣一個(gè)批量的轉(zhuǎn)移(transition)
          ????????state_batch,?action_batch,?reward_batch,?next_state_batch,?done_batch?=?self.memory.sample(
          ????????????self.batch_size)
          ????????#?轉(zhuǎn)為張量
          ????????state_batch?=?torch.tensor(state_batch,?device=self.device,?dtype=torch.float)
          ????????action_batch?=?torch.tensor(action_batch,?device=self.device).unsqueeze(1)??
          ????????reward_batch?=?torch.tensor(reward_batch,?device=self.device,?dtype=torch.float)??
          ????????next_state_batch?=?torch.tensor(next_state_batch,?device=self.device,?dtype=torch.float)
          ????????done_batch?=?torch.tensor(np.float32(done_batch),?device=self.device)
          ????????#?計(jì)算當(dāng)前狀態(tài)(s,a)對應(yīng)的Q(s,?a)
          ????????q_values?=?self.policy_net(state_batch).gather(dim=1,?index=action_batch)?
          ????????#?計(jì)算下一時(shí)刻的狀態(tài)(s_t,a)對應(yīng)的Q值
          ????????next_q_values?=?self.target_net(next_state_batch).max(1)[0].detach()?
          ????????#?計(jì)算期望的Q值,對于終止?fàn)顟B(tài),此時(shí)done_batch[0]=1,?對應(yīng)的expected_q_value等于reward
          ????????expected_q_values?=?reward_batch?+?self.gamma?*?next_q_values?*?(1-done_batch)
          ????????loss?=?nn.MSELoss()(q_values,?expected_q_values.unsqueeze(1))??#?計(jì)算均方根損失
          ????????#?優(yōu)化更新模型
          ????????self.optimizer.zero_grad()??
          ????????loss.backward()
          ????????for?param?in?self.policy_net.parameters():??#?clip防止梯度爆炸
          ????????????param.grad.data.clamp_(-1,?1)
          ????????self.optimizer.step()

          在一定更新回合后需要將DQN網(wǎng)絡(luò)參數(shù)復(fù)制給目標(biāo)網(wǎng)絡(luò),只需要在訓(xùn)練中增加如下代碼:

          ????????if?(i_ep?+?1)?%?cfg.target_update?==?0:??#?智能體目標(biāo)網(wǎng)絡(luò)更新
          ????????????agent.target_net.load_state_dict(agent.policy_net.state_dict())


          三、Double DQN

          在NatureDQN中,執(zhí)行網(wǎng)絡(luò)參數(shù)更新方法update時(shí),用target_net計(jì)算next_q_values用到了取最大值操作,其目的是獲得在狀態(tài)next_state時(shí),target_net取最大值的動作a*_target,并輸出該最大值Q*_max_target值,這一步同樣會造成高估問題。既然如此,不采用a*_target并減小Q*_max_target值,那么高估問題就能在一定程度得到緩解。

          Double DQN的做法是利用policy_net在next_state時(shí),policy_net取最大值的動作為a*_policy,然后再將該動作帶入target_net中進(jìn)行計(jì)算獲得新的Q*_max_target^值。由于最優(yōu)動作選自policy_net而不是target_net,所以容易得出target_net(next_state)[a*_target] >= target_net(next_state)[a*_policy],因此有Q*_max_target^ <=Q*_max_target,這樣一定程度減小Q估計(jì),緩解因自舉高估帶來的不穩(wěn)定問題。在update方法中對expected_q_values的計(jì)算做如下修改:

          ????????#?計(jì)算當(dāng)前狀態(tài)(s,a)對應(yīng)的Q(s,?a)
          ????????q_values?=?self.policy_net(state_batch).gather(dim=1,?index=action_batch)?
          ????????#?next_q_values?=?self.target_net(next_state_batch).max(1)[0].detach()?
          ????????#?用policy_net計(jì)算下一個(gè)狀態(tài)s_t的最優(yōu)動作a*
          ????????next_action_batch?=?torch.argmax(self.policy_net(state_batch),axis=1).unsueeze(1)
          ????????#?用target_net計(jì)算下一時(shí)刻的狀態(tài)(s_t_,a)對應(yīng)的Q值
          ????????next_q_values?=?self.target_net(next_state_batch).gather(dim=1,index=next_action_batch)
          ????????#?計(jì)算期望的Q值,對于終止?fàn)顟B(tài),此時(shí)done_batch[0]=1,?對應(yīng)的expected_q_value等于reward
          ????????expected_q_values?=?reward_batch?+?self.gamma?*?next_q_values?*?(1-done_batch)
          ????????loss?=?nn.MSELoss()(q_values,?expected_q_values.unsqueeze(1))??#?計(jì)算均方根損失
          ????????#?優(yōu)化更新模型
          ????????self.optimizer.zero_grad()??
          ????????loss.backward()
          ????????for?param?in?self.policy_net.parameters():??#?clip防止梯度爆炸
          ????????????param.grad.data.clamp_(-1,?1)
          ????????self.optimizer.step()

          四、Dueling DQN

          Dueling DQN與上述兩種優(yōu)化方式不同,它直接修改網(wǎng)絡(luò)模型,用一個(gè)A*網(wǎng)絡(luò)和V*網(wǎng)絡(luò)去表示Q*,其中A*表示為最優(yōu)優(yōu)勢函數(shù)(optimal advantage function),V*表示最優(yōu)狀態(tài)價(jià)值函數(shù)(optimal state value function ),它們?nèi)叩年P(guān)系為A* = Q* - V*(https://datawhalechina.github.io/easy-rl/#/chapter7/chapter7?id=dueling-dqn)。
          Dueling DQN與原始DQN網(wǎng)絡(luò)結(jié)構(gòu)的對比如下圖所示:

          同時(shí)為了降低采用不同動作時(shí)Q*值的方差,實(shí)際中常用Q* = V* + A* - mean(A*)來進(jìn)一步優(yōu)化網(wǎng)絡(luò),加速收斂。在代碼角度上,常見的 Dueling網(wǎng)絡(luò)模型實(shí)現(xiàn)如下:

          class?DuelingNet(nn.Module):
          ????def?__init__(self,?state_dim,?action_dim,hidden_size=128):
          ????????super(DuelingNet,?self).__init__()
          ????????
          ????????#?隱藏層
          ????????self.hidden?=?nn.Sequential(
          ????????????nn.Linear(state_dim,?hidden_size),
          ????????????nn.ReLU()
          ????????)
          ????????
          ????????#?優(yōu)勢函數(shù)
          ????????self.advantage?=?nn.Sequential(
          ????????????nn.Linear(hidden_size,?hidden_size),
          ????????????nn.ReLU(),
          ????????????nn.Linear(hidden_size,?action_dim)
          ????????)
          ????????
          ????????#?價(jià)值函數(shù)
          ????????self.value?=?nn.Sequential(
          ????????????nn.Linear(hidden_size,?hidden_size),
          ????????????nn.ReLU(),
          ????????????nn.Linear(hidden_size,?1)
          ????????)
          ????????
          ????def?forward(self,?x):
          ????????x?=?self.hidden(x)
          ????????advantage?=?self.advantage(x)
          ????????value?????=?self.value(x)
          ?#?Q*?=?A*?+?V*?-?mean(A*)?
          ????????return?value?+?advantage??-?advantage.mean()


          五、官方代碼及模型分析

          ??本文在DQN訓(xùn)練超級馬里奧的項(xiàng)目中,參考了pytorch官方的強(qiáng)化學(xué)習(xí)教程,該教程代碼耦合度低,邏輯結(jié)構(gòu)清晰,非常值得初學(xué)者學(xué)習(xí)。核心代碼及其功能如下圖所示:


          超級馬里奧的訓(xùn)練環(huán)境來自

          gym_super_mario_bros(https://github.com/flexpad/gym-super-mario-bros),代理(agent)與環(huán)境(env)交互返回的是游戲當(dāng)前的RGB圖像,因而在開始訓(xùn)練之前需要對圖像做一系列的預(yù)處理操作,該教程的這部分工作做的十分完善,非常值得學(xué)習(xí)和借鑒。在完成預(yù)處理數(shù)據(jù)后,我們就集中精力在DQN的算法實(shí)現(xiàn)上。官方代碼的具體分析可以在這篇文章中找到:https://zhuanlan.zhihu.com/p/402519441?utm_source=wechat_session&utm_medium=social&utm_oi=951210242982260736.

          筆者應(yīng)用該代碼在實(shí)際訓(xùn)練中還發(fā)現(xiàn)一些問題:

          1. 首先是直接訓(xùn)練該模型對硬件設(shè)備要求高,很可能會出現(xiàn)顯存不足的問題(報(bào)錯(cuò):”CUDA out of memory”);
          2. 其次是訓(xùn)練結(jié)果模型效果不佳,根據(jù)官方提供的已訓(xùn)練完成的模型去測試(官方給出訓(xùn)練時(shí)間為GPU約20小時(shí),CPU約80小時(shí),但未說明具體設(shè)備),發(fā)現(xiàn)通關(guān)率仍然較低(筆者測試每回合累積獎勵大約在1300~2000之間,小概率能夠通關(guān);筆者對模型修改后訓(xùn)練測試結(jié)果是穩(wěn)定通關(guān)且每回合累積獎勵3032);
          3. 最后是探索率設(shè)置不夠合理,DQN模型訓(xùn)練完成后,在實(shí)際使用時(shí)應(yīng)該將探索率ε設(shè)置為0,然而原作者依然給出了0.1的探索率,將該探索率設(shè)置為0后會出現(xiàn)每次agent都在同一地方失?。ɡ纭翱▔恰保┑痊F(xiàn)象,推測原作者之所以這樣做,目的是防止agent總是在同樣決策下走向相同的失敗結(jié)局而有意增加了隨機(jī)性,這有悖于DQN的算法原理。DQN算法的決策過程決定了它是一種確定性的策略,也就是說,對給定的輸入狀態(tài),每次的輸出結(jié)果都是相同的,如果在訓(xùn)練完成后,實(shí)際測試時(shí)還需要增加探索率去避免“卡墻角”或者“碰壁”等情況,那只能說明模型沒有訓(xùn)練好,有待于對模型做進(jìn)一步改進(jìn),最終訓(xùn)練好的結(jié)果一定是agent每次都會以相同的策略通關(guān)而不存在隨機(jī)性;如果想得到每回合游戲有不一樣的通關(guān)方式,那么就需要考慮采用其他算法,例如策略學(xué)習(xí)算法,每一次的動作做概率抽樣。

          六、模型改進(jìn)

          針對上述問題,筆者對官方模型做出如下修改:

          1. 修改經(jīng)驗(yàn)緩存memory的大小。原則上來說,經(jīng)驗(yàn)緩存越大,那么能夠存儲更加久遠(yuǎn)的對局信息,是有助于模型學(xué)習(xí)與改進(jìn)的,然而受限于硬件顯存空間的問題,可以考慮降低經(jīng)驗(yàn)緩存的大??;而且基于直覺判斷,超級馬里奧第一關(guān)的環(huán)境也并沒有太過復(fù)雜,因而適當(dāng)降低緩存大小,在較為低配的硬件設(shè)備上依然可以得到不錯(cuò)的訓(xùn)練效果。(筆者機(jī)器配置為i7-9750H和GTX1660Ti,顯存6G,筆者在本機(jī)和Colab上均做過嘗試,將memory從100000調(diào)整為18000可以完成訓(xùn)練。如果不更改配置至少需要20G顯存,讀者可更具機(jī)器硬件情況進(jìn)行調(diào)整)。
          2. 模型改進(jìn)。將官方給出D2QN(Nature DQN + Double DQN)改為D3QN(Nature DQN + Double DQN + Dueling DQN).DQN算法本身不可避免的存在高估等問題,使用這些DQN變體能夠大幅提高DQN算法的準(zhǔn)確性和穩(wěn)定性(https://zhuanlan.zhihu.com/p/98784255?utm_source=wechat_session&utm_medium=social&utm_oi=951210242982260736)。在官方代碼基礎(chǔ)上,使用Dueling network對原文件中”neural.py”的網(wǎng)絡(luò)模型進(jìn)行修改。
          3. 調(diào)整超參數(shù)。將探索率ε的最小值設(shè)置為0,設(shè)置更大的BatchSize并減小的學(xué)習(xí)率,DQN調(diào)參可以參考(https://zhuanlan.zhihu.com/p/345353294)

          七、訓(xùn)練結(jié)果

          筆者使用筆記本訓(xùn)練,總時(shí)間為24+13+10=47小時(shí),根據(jù)不同的硬件環(huán)境,訓(xùn)練時(shí)間可能會有較大變化。0~24小時(shí)、24~37小時(shí)、37~47小時(shí)三個(gè)階段每百回合平均獎勵的變化如圖所示:

          訓(xùn)練第一階段的每百回合平均獎勵

          訓(xùn)練第二階段的每百回合平均獎勵

          訓(xùn)練的第三階段的每百回合平均獎勵

          從圖中可以看出,在局部區(qū)域獎勵波動較大,一般屬于正常情況,總體上看,平均獎勵是隨著訓(xùn)練的進(jìn)行而上升的,說明模型的表現(xiàn)已經(jīng)越來越好。在訓(xùn)練之余,還可以通過replay.py對已經(jīng)訓(xùn)練出來的模型進(jìn)行測試,以驗(yàn)證模型學(xué)習(xí)的進(jìn)步(最后一個(gè)階段的獎勵雖然在升高,但是還沒有達(dá)到測試時(shí)的3032,原因是此時(shí)依然有較低的概率在做隨機(jī)探索。同時(shí),強(qiáng)化學(xué)習(xí)的模型并不是訓(xùn)練的越久越好,選擇訓(xùn)練階段中獎勵更高的模型往往會是一種更優(yōu)的選擇)。測試模型結(jié)果:

          八、總結(jié)

          DQN算法作為強(qiáng)化學(xué)習(xí)的入門算法之一,將強(qiáng)化學(xué)習(xí)的核心理論(馬爾科夫決策過程、貝爾曼方程等)清晰的融入到算法的實(shí)現(xiàn)中,基于DQN算法中的問題,又催生出各式各樣DQN算法變體,大幅提高了算法的有效性。在解決離散動作空間的問題上,D3QN(Dueling DDQN)通常都具有不錯(cuò)的表現(xiàn)。大家可以結(jié)合實(shí)際游戲環(huán)境或者參考項(xiàng)目源代碼,訓(xùn)練出屬于你自己的超級馬里奧!

          干貨學(xué)習(xí),點(diǎn)三連

          瀏覽 47
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日日伊人| 日韩有码一区 | 婷婷丁香综合网 | 亚洲色五月| 黄色视频网站免费观看 |