本文將為大家展示如何通過(guò) Numpy 庫(kù)和 50行 Python 代碼,使用標(biāo)準(zhǔn)的 OpenAI Gym平臺(tái)創(chuàng)建智能體 (agent),就教會(huì)機(jī)器處理推車桿問(wèn)題 (cart pole problem),保持平衡。
推車桿問(wèn)題 (cart pole problem),大家可以類比好像在手指尖上垂直平衡鉛筆一樣,需要通過(guò)左右推動(dòng)來(lái)平衡車頂部的桿,這是個(gè)非常具有挑戰(zhàn)性的問(wèn)題!
今天,我們不過(guò)多的討論強(qiáng)化學(xué)習(xí)的基礎(chǔ)理論,希望大家在下面的編譯器里,不斷嘗試,體會(huì)這個(gè)項(xiàng)目。一開始,大家只需要點(diǎn)擊“Start”,開始配置需要的環(huán)境即可。
快速入門強(qiáng)化學(xué)習(xí) (RL)
如果你是機(jī)器學(xué)習(xí)或強(qiáng)化學(xué)習(xí)領(lǐng)域的新人,先了解一下下面的一些基礎(chǔ)知識(shí)和術(shù)語(yǔ),為后面做鋪墊。如果你已經(jīng)掌握了基礎(chǔ)知識(shí),那可以跳過(guò)這部分內(nèi)容。
強(qiáng)化學(xué)習(xí)
強(qiáng)化學(xué)習(xí)旨在教會(huì)我們的智能體 (算法或機(jī)器) 執(zhí)行特定的任務(wù)或動(dòng)作,而無(wú)需顯式地告訴它該如何做。想象一個(gè)嬰兒在隨機(jī)抬動(dòng)自己的腿,當(dāng)站立起來(lái)時(shí)就給予他一個(gè)獎(jiǎng)勵(lì)。同樣地,智能體的目標(biāo)是在其生命周內(nèi)最大化獎(jiǎng)勵(lì)值,而獎(jiǎng)勵(lì)取決于特定的任務(wù)。比如寶寶站立這個(gè)例子,站立時(shí)給予獎(jiǎng)勵(lì)記為1,否則記為0。
AlphaGo 就是一個(gè)典型的強(qiáng)化學(xué)習(xí)智能體例子,教會(huì)智能體如何玩游戲并最大化其獎(jiǎng)勵(lì) (即贏得游戲)。而在本文中就將創(chuàng)建一個(gè)智能體,教它如何通過(guò)左右推動(dòng)推車來(lái)解決推車上的桿平衡問(wèn)題。
狀態(tài)
狀態(tài)即當(dāng)前游戲的樣子,通常用數(shù)字來(lái)表示。在乒乓球比賽中,它可能是每個(gè)球拍與x、y坐標(biāo)軸的垂直位置或者是乒乓球的速度。在推車桿的情況下,這里的狀態(tài)由4個(gè)數(shù)字組成:即推車的位置,推車的速度,桿的位置 (作為角度) 和桿的角速度。這4個(gè)數(shù)字作為向量 (或數(shù)組) 提供給智能體,這非常重要:將狀態(tài)作為一組數(shù)字意味著智能體能夠?qū)λM(jìn)行一些數(shù)學(xué)運(yùn)算,以便決定如何根據(jù)狀態(tài)來(lái)采取什么行動(dòng)。
策略
策略是一種可以處理游戲狀態(tài)的函數(shù) (例如棋盤的位置或者推車和桿的位置), 并輸出智能體在該位置應(yīng)該采取的動(dòng)作 (例如移動(dòng)或?qū)⑼栖囃频阶筮?。在智能體采取相應(yīng)的操作后,游戲?qū)⒁韵乱粋€(gè)狀態(tài)更新,此時(shí)將再次根據(jù)其輸入策略做出決策,這個(gè)過(guò)程一直持續(xù)到游戲達(dá)到某個(gè)終止條件時(shí)結(jié)束。策略同樣是個(gè)非常關(guān)鍵的因素,因?yàn)樗从沉耸侵悄荏w背后的決策能力,這也是我們所需要認(rèn)真考慮的。
點(diǎn)積 (dot product)
兩個(gè)數(shù)組 (向量) 之間的點(diǎn)積可以簡(jiǎn)單理解為,將第一個(gè)數(shù)組的每個(gè)元素乘以第二個(gè)數(shù)組的對(duì)應(yīng)元素,并將它們?nèi)考釉谝黄稹<僭O(shè)想要計(jì)算數(shù)組 A 和 B 的點(diǎn)積,形如 A[0]*B[0]+A[1]*B[1] ......隨后將使用此運(yùn)算結(jié)果再乘以一個(gè)狀態(tài) (同樣是一個(gè)向量) 和一個(gè)策略值 (同樣也是一個(gè)向量)。這部分內(nèi)容將在下一節(jié)詳細(xì)介紹。
制定策略
為了解決推車游戲,我們希望所設(shè)計(jì)的機(jī)器學(xué)習(xí)策略能夠贏得游戲或最大化游戲獎(jiǎng)勵(lì)。對(duì)于智能體而言,這里將接收4維數(shù)組所表示策略,每一維代表每個(gè)組成的重要性 (推車的位置,桿位等四個(gè)組成)。隨后,再將點(diǎn)積的結(jié)果與策略、狀態(tài)向量進(jìn)行處理并輸出最終的結(jié)果。根據(jù)結(jié)果的正負(fù)值決定是向左還是向右推動(dòng)推車。這聽起來(lái)可能有點(diǎn)抽象,下面就通過(guò)一個(gè)具體的例子,來(lái)看看整個(gè)過(guò)程將發(fā)生什么。
假設(shè)推車在游戲中靜止地處在中間位置,當(dāng)桿向右傾斜時(shí)車也將向右傾斜,如下圖這樣:
所對(duì)應(yīng)的的狀態(tài)如下圖所示:
此時(shí)的狀態(tài)向量為 [0, 0, 0.2, 0.05]。直觀地說(shuō),現(xiàn)在我們想要將推車推向右側(cè),并將桿拉直。這里通過(guò)訓(xùn)練中得到了一個(gè)很好的策略,即 [-0.116, 0.332, 0.207, 0.352]。將上面的狀態(tài)向量與策略向量進(jìn)行點(diǎn)積處理,如果得到的結(jié)果為正,則將推車向右推動(dòng);反之則向左推動(dòng)。
顯然,這里的輸出是個(gè)正數(shù),這意味著在這種策略下智能體將推車向右推動(dòng),這也正是我們想要的結(jié)果。那么,該如何得到這個(gè)策略向量呢,以便智能體能夠朝著我們希望的方向推動(dòng)?或者說(shuō)如果隨機(jī)選擇一個(gè)策略,那么智能體又該如何行動(dòng)呢?
開始編輯
在該項(xiàng)目主頁(yè) repl.it 上彈出一個(gè) Python 實(shí)例。repl.it允許用戶快速啟動(dòng)大量不同編程環(huán)境的云實(shí)例環(huán)境并在強(qiáng)大云編譯器 (IDE) 中編輯代碼,這個(gè)強(qiáng)大的 IDE 能在任何地方訪問(wèn),如下圖所示。
安裝所需的包
安裝這個(gè)項(xiàng)目所需的兩個(gè)軟件包:numpy 用于幫助數(shù)值計(jì)算,而 OpenAI Gym 則作為智能體的模擬器。如下圖所示,只需在編輯器左側(cè)的包搜索工具中輸入 gym 和 numpy,然后單擊加號(hào)按鈕即可安裝這兩個(gè)包。
創(chuàng)建基礎(chǔ)環(huán)境
這里首先將剛安裝的兩個(gè)依賴包導(dǎo)入到 main.py 腳本中并設(shè)置一個(gè)新的 gym環(huán)境。隨后定義一個(gè)名為 play 的函數(shù),該函數(shù)將被賦予一個(gè)環(huán)境和一個(gè)策略向量,在環(huán)境中執(zhí)行策略向量并返回分?jǐn)?shù)以及每個(gè)時(shí)間步的游戲觀測(cè)值。最后,將通過(guò)分?jǐn)?shù)高低來(lái)反映策略的效果好壞,以及在單次游戲中策略的表現(xiàn)。如此,就可以測(cè)試不同的策略,查看他們?cè)谟螒蛑械谋憩F(xiàn)!
import gymimport numpy as npenv = gym.make('CartPole-v1')
下面從函數(shù)定義開始,將游戲重置為開始狀態(tài),如下所示。
defplay(env,policy): observation = env.reset()
接著初始化一些變量,用來(lái)跟蹤游戲是否達(dá)到終止條件,策略得分以及游戲中每個(gè)步驟的觀測(cè)值,如下所示。
done = False score = 0observations=[]
現(xiàn)在,只需要一些時(shí)間步來(lái)開始游戲,直到 gym 提示游戲結(jié)束為止。
for _ in range(5000): observations += [observation.tolist()] # Record the observations for normalization and replay if done: # If the simulation was over last iteration, exit loop break # Pick an action according to the policy matrix outcome = np.dot(policy, observation) action = 1 if outcome > 0 else 0 # Make the action, record reward observation, reward, done, info = env.step(action) score += rewardreturnscore,observations
如下,這部分的代碼主要是用于開始游戲并記錄結(jié)果,而與策略相關(guān)的代碼就是這兩行:
outcome=np.dot(policy,observation) action = 1 if outcome > 0 else 0
在這里所做的只是對(duì)策略向量和狀態(tài) (觀測(cè)) 數(shù)組之間進(jìn)行點(diǎn)積運(yùn)算,就像在之前具體例子中所展現(xiàn)的那樣。隨后根據(jù)結(jié)果的正負(fù),選擇1或0 (向左或右) 的動(dòng)作。到這里為止,main.py 腳本如下所示:
import gymimport numpy as npenv = gym.make('CartPole-v1')def play(env, policy): observation = env.reset() done = False score = 0 observations = [] for _ in range(5000): observations += [observation.tolist()] # Record the observations for normalization and replay if done: # If the simulation was over last iteration, exit loop break # Pick an action according to the policy matrix outcome = np.dot(policy, observation) action = 1 if outcome > 0 else 0 # Make the action, record reward observation, reward, done, info = env.step(action) score += reward return score, observations
下面開始尋找該游戲的最優(yōu)策略!
第一次游戲
現(xiàn)在已經(jīng)有了一個(gè)函數(shù),用來(lái)反映策略的好壞。因此,接下來(lái)要做的事開始制定一些策略,并查看他們的表現(xiàn)如何。如果一開始你想嘗試一些隨機(jī)的策略,那么這些策略的結(jié)果將會(huì)怎樣呢?這里使用 numpy 來(lái)隨機(jī)生成一些的策略,這些策略都是4維數(shù)組或1x4矩陣,即選擇4個(gè)0到1之間的數(shù)字作為游戲的策略,如下所示。
policy = np.random.rand(1,4)
有了這些策略以及上面所創(chuàng)建的環(huán)境,下面就可以開始游戲并獲得策略分?jǐn)?shù):
score, observations = play(env, policy)print('Policy Score', score)
只需點(diǎn)擊運(yùn)行即可開始游戲,它將輸出每個(gè)策略所對(duì)應(yīng)的得分,如下所示。
最后,所有的策略獲得的最高得分為500,在這里隨機(jī)生成的策略可能并不能得到太好的結(jié)果,而且通過(guò)隨機(jī)生成的方式,很難解釋智能體是如何進(jìn)行游戲的。下一步將介紹如何選擇并設(shè)置游戲的策略,來(lái)查看智能體的游戲表現(xiàn)。
觀察我們的智能體
這里使用 Flask 來(lái)設(shè)置輕量級(jí)服務(wù)器,以便可以在瀏覽器中查看智能體的表現(xiàn)。 Flask 是一個(gè)輕量級(jí)的 Python HTTP 服務(wù)器框架,可以為 HTML UI 和數(shù)據(jù)提供服務(wù)。由于渲染和 HTTP 服務(wù)器背后的細(xì)節(jié)對(duì)智能體的訓(xùn)練并不重要,在這里只是簡(jiǎn)單介紹下。首先需要將 Flask 安裝為 Python 包,就像上面安裝 gym 和 numpy 包一樣,如下所示。
接下來(lái),在腳本的底部創(chuàng)建一個(gè) flask 服務(wù)器,它將在 /data 端點(diǎn)上公開游戲的每個(gè)幀的記錄,并在 / 上托管 UI,如下所示。
from flask import Flaskimport jsonapp = Flask(__name__, static_folder='.')@app.route("/data")def data(): return json.dumps(observations)@app.route('/')def root(): return app.send_static_file('./index.html')app.run(host='0.0.0.0', port='3000')
此外,還需要添加兩個(gè)文件:一個(gè)是項(xiàng)目的空白 Python 文件,這是 repl.it 用于檢測(cè) repl 是處于評(píng)估模式還是項(xiàng)目模式的關(guān)鍵。這里只需使用新文件按鈕添加空白的 Python 腳本即可。隨后,還需要?jiǎng)?chuàng)建一個(gè)將承載渲染 UI 的 index.html 文件。在此不需要深入了解這部分的內(nèi)容,只需將此 index.html 上傳到 repl.it 項(xiàng)目即可。
好了,現(xiàn)在的項(xiàng)目目錄應(yīng)該像這樣,如下所示:
有了這兩個(gè)新文件,當(dāng)運(yùn)行 repl 時(shí)它將回放所選擇的游戲策略,便于我們尋找一個(gè)最優(yōu)的策略。
策略搜索
在第一次游戲中只是通過(guò) numpy 為智能體隨機(jī)生成一些策略并開始游戲。那么,如何選擇一些游戲策略,并在游戲結(jié)束時(shí)只保留那個(gè)結(jié)果最好的策略呢?在這里,制定游戲時(shí)并不只是生成一個(gè)策略,而是通過(guò)編寫一個(gè)循環(huán)來(lái)生成一些策略,跟蹤每個(gè)策略的執(zhí)行情況并在最后保存最佳的策略。
首先創(chuàng)建一個(gè)名為 max 的元組,它將存儲(chǔ)游戲過(guò)程所出現(xiàn)的最佳策略得分、觀測(cè)和策略數(shù)組,如下所示。
max = (0, [], [])
接下來(lái)將生成并評(píng)估10個(gè)策略,并將得分最大值的策略保存。此外,這里還需要在 /data 端點(diǎn)返回最佳策略的重放,如下所示。
for _ in range(10): policy = np.random.rand(1,4) score, observations = play(env, policy) if score > max[0]: max = (score, observations, policy)print('Max Score', max[0])
此外,這個(gè)端點(diǎn):
@app.route("/data")def data(): return json.dumps(observations)
應(yīng)改為:
@app.route("/data")def data(): return json.dumps(max[1])
最后 main.py 腳本應(yīng)像這樣,如下圖所示:
import gymimport numpy as npenv = gym.make('CartPole-v1')def play(env, policy): observation = env.reset() done = False score = 0 observations = [] for _ in range(5000): observations += [observation.tolist()] # Record the observations for normalization and replay if done: # If the simulation was over last iteration, exit loop break # Pick an action according to the policy matrix outcome = np.dot(policy, observation) action = 1 if outcome > 0 else 0 # Make the action, record reward observation, reward, done, info = env.step(action) score += reward return score, observationsmax = (0, [], [])for _ in range(10): policy = np.random.rand(1,4) score, observations = play(env, policy) if score > max[0]: max = (score, observations, policy)print('Max Score', max[0])from flask import Flaskimport jsonapp = Flask(__name__, static_folder='.')@app.route("/data")def data(): return json.dumps(max[1])@app.route('/')def root(): return app.send_static_file('./index.html')app.run(host='0.0.0.0', port='3000')
如果現(xiàn)在運(yùn)行 repl,正常情況所得到的最大分?jǐn)?shù)應(yīng)為500。如果沒有的話,請(qǐng)?jiān)俅螄L試運(yùn)行 repl! 通過(guò)這種方式,能夠完美地觀察游戲策略是如何讓桿達(dá)到平衡的!
如何加速?
(1)這里智能體達(dá)到平衡的速度并不夠塊?;叵肭懊嬷贫ú呗詴r(shí),首先只是在0到1范圍內(nèi)隨機(jī)創(chuàng)建了策略數(shù)組,這恰好是有效的。但如果這里智能體翻轉(zhuǎn)大于運(yùn)算符所設(shè)定的那樣,那么可能將看到災(zāi)難性的失敗結(jié)果。可以嘗試將 action> 0 if outcome>0 else 0 改為 action=1 if outcome<0 else 0。
效果似乎并沒有很明顯,這可能是因?yàn)槿绻『眠x擇少于而不是大于,那么可能永遠(yuǎn)也找不到解決游戲的策略。為了緩解這種情況,在實(shí)際操作時(shí)也應(yīng)該生成一些帶負(fù)數(shù)的策略。雖然這將使得搜索一個(gè)好策略的過(guò)程變得更加困難 (因?yàn)榘S多負(fù)的策略并不好),但所帶來(lái)的好處是不再需要通過(guò)特定算法來(lái)匹配特定游戲。如果嘗試在 OpenAI gym 的其他環(huán)境中這樣做,那么算法肯定會(huì)失敗。
要做到這一點(diǎn),不能使用 policy = np.random.rand(1,4),需要將改為 policy = np.random.rand(1,4) -0.5。如此,所生成的策略中每個(gè)數(shù)字都在-0.5到0.5之間,而不是0到1。但是由于這樣做會(huì)使得最優(yōu)策略的搜索過(guò)程變得困難,因此在上面的 for 循環(huán)中,不要迭代10個(gè)策略,更改這部分的代碼嘗試搜索100個(gè)策略 (for _ in range (100):)。當(dāng)然,你也可以先嘗試迭代10次,看看用負(fù)的策略獲得最優(yōu)策略的困難性。
好了,現(xiàn)在的 main.py 腳本可參考
https://gist.github.com/MikeShi42/e1c5551bbf2cb2064da962ad8b198c1b
如果現(xiàn)在運(yùn)行 repl,無(wú)論使用的是否大于或小于,仍然可以找到一個(gè)好的游戲策略。
(2)不僅如此,即使所生成的策略可能能夠在一次游戲中得到最高分500的結(jié)果,那它能夠在每次游戲中都有這樣的表現(xiàn)呢?當(dāng)生成100個(gè)策略并選擇在單次運(yùn)行中表現(xiàn)最佳的策略時(shí),該策略可能只是單次最佳策略,或者它可能是一個(gè)非常糟糕的策略,只是恰好在一次游戲中有非常好的性能。因?yàn)橛螒虮旧砭哂须S機(jī)性 (如起始位置每次都不同),因此策略可以只是在一個(gè)起始位置表現(xiàn)良好,而不是在其他位置。
因此,為了解決這個(gè)問(wèn)題,需要評(píng)估一個(gè)策略在多次實(shí)驗(yàn)中的表現(xiàn)。現(xiàn)在就采取之前實(shí)驗(yàn)得到的最佳政策,查看它在100次游戲?qū)嶒?yàn)中的表現(xiàn)。在這里對(duì)最優(yōu)策略進(jìn)行100次游戲 (最大索引值為2) 并記錄每次的游戲得分。隨后使用 numpy 計(jì)算該策略的平均分?jǐn)?shù)并將其打印到終端。你可能會(huì)注意到,最佳的游戲策略實(shí)際上并不一定是最優(yōu)秀的。
總結(jié)
好了,以上已經(jīng)成功創(chuàng)建了一個(gè)能夠非常有效地解決推車桿問(wèn)題的 AI 智能體。當(dāng)然它還有很大的改進(jìn)空間,這將是后續(xù)系列文章的一部分。此外,后續(xù)的工作還可以對(duì)一些問(wèn)題展開研究:
尋找“真正的”最優(yōu)策略 (即在100次獨(dú)立游戲中表現(xiàn)良好)
優(yōu)化最佳策略搜索所需的次數(shù) (即樣本效率問(wèn)題)
選擇正確搜索策略,而不是嘗試隨機(jī)地選擇。
在其他的環(huán)境中創(chuàng)建。
-
AI
+關(guān)注
關(guān)注
87文章
30898瀏覽量
269135 -
編譯器
+關(guān)注
關(guān)注
1文章
1634瀏覽量
49134 -
python
+關(guān)注
關(guān)注
56文章
4797瀏覽量
84695
原文標(biāo)題:50行代碼教AI實(shí)現(xiàn)動(dòng)作平衡 | 附完整代碼
文章出處:【微信號(hào):rgznai100,微信公眾號(hào):rgznai100】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論