從像素玩打磚塊的深度強化學習#
注意
由於底層 gym
和 atari-py
依賴項的授權/安裝問題,本文目前未經測試。 透過開發一個減少依賴佔用空間的範例,協助改進本文!
本教學示範如何從頭開始實作深度強化學習 (RL) 代理程式,方法是使用策略梯度方法,該方法學習使用螢幕像素作為 NumPy 的輸入來玩打磚塊電玩遊戲。 您的打磚塊代理程式將使用人工神經網路作為其策略,隨時隨地獲取經驗。
打磚塊是一款 1972 年推出的 2D 遊戲,兩名玩家使用「球拍」來玩桌球的一種形式。 每位玩家在螢幕上上下移動球拍,並嘗試透過觸碰球將球擊向對手的方向。 目標是擊球,使其越過對手的球拍(他們錯失擊球機會)。 根據規則,如果玩家達到 21 分,他們就獲勝。 在打磚塊中,學習與對手對戰的 RL 代理程式顯示在右側。
此範例基於 code 由 Andrej Karpathy 為 2017 年在加州大學柏克萊分校舉辦的 Deep RL Bootcamp 開發。 他 2016 年的 blog post 也提供了有關打磚塊 RL 中使用的機制和理論的更多背景資訊。
先決條件#
OpenAI Gym:為了協助遊戲環境,您將使用 Gym — 一個開放原始碼 Python 介面 由 OpenAI 開發,可協助執行 RL 任務,同時支援許多模擬環境。
Python 和 NumPy:讀者應具備一些 Python、NumPy 陣列操作和線性代數的知識。
深度學習和深度 RL:您應該熟悉深度學習的主要概念,這些概念在 Deep learning 論文中進行了解釋,該論文由 Yann LeCun、Yoshua Bengio 和 Geoffrey Hinton 於 2015 年發表,他們被認為是該領域的先驅。 本教學課程將嘗試引導您了解深度 RL 的主要概念,您將找到各種文獻,其中包含原始來源的連結,以方便您使用。
Jupyter 筆記本環境:由於 RL 實驗可能需要高運算能力,因此您可以使用 Binder 或 Google Colaboratory(提供免費的有限 GPU 和 TPU 加速)在雲端免費執行本教學課程。
Matplotlib:用於繪製影像。 查看安裝指南,以在您的環境中進行設定。
本教學課程也可以在隔離的環境中在本機執行,例如 Virtualenv 和 conda。
目錄#
關於 RL 和深度 RL 的注意事項
深度 RL 詞彙表
設定打磚塊
預處理幀 (觀察)
建立策略 (神經網路) 和前向傳遞
設定更新步驟 (反向傳播)
定義折扣獎勵 (預期報酬) 函數
將代理程式訓練 3 個回合
下一步
附錄
關於 RL 和深度 RL 的注意事項
如何在您的 Jupyter 筆記本中設定影片播放
關於 RL 和深度 RL 的注意事項#
在 RL 中,您的代理程式透過與環境互動,使用所謂的策略來獲取經驗,從而從試錯中學習。 採取一個動作後,代理程式會收到有關其獎勵(可能獲得也可能未獲得)以及環境的下一個觀察的資訊。 然後,它可以繼續採取另一個動作。 這會在多個回合中發生,和/或直到任務被視為完成。
代理程式的策略透過將代理程式的觀察「對應」到其動作來運作 — 也就是說,將代理程式觀察到的呈現方式與所需的動作進行指派。 整體目標通常是最佳化代理程式的策略,使其最大化每次觀察的預期獎勵。
如需有關 RL 的詳細資訊,Richard Sutton 和 Andrew Barton 撰寫了一本入門書籍。
查看本教學課程結尾的附錄以取得更多資訊。
深度 RL 詞彙表#
以下是深度 RL 術語的簡明詞彙表,您可能會覺得它對本教學課程的其餘部分很有用
在有限視野的世界中,例如打磚塊遊戲,學習代理程式可以在一個回合中探索(和利用)環境。 代理程式通常需要許多回合才能學習。
代理程式使用動作與環境互動。
採取動作後,代理程式會透過獎勵(如果有的話)接收一些回饋,具體取決於它採取的動作及其所處的狀態。 狀態包含有關環境的資訊。
代理程式的觀察是狀態的部分觀察 — 這是本教學課程偏好的術語(而不是狀態)。
代理程式可以根據累積獎勵(也稱為價值函數)和策略來選擇動作。 累積獎勵函數使用代理程式的策略來估計代理程式訪問的觀察的品質。
策略(由神經網路定義)輸出動作選擇(作為(對數)機率),這些選擇應最大化代理程式所處狀態的累積獎勵。
從觀察中獲得的預期報酬,以動作為條件,稱為動作價值函數。 為了讓短期獎勵比長期獎勵更重要,您通常會使用折扣因子(通常是介於 0.9 和 0.99 之間的浮點數)。
代理程式每次策略「執行」期間的動作和狀態(觀察)序列有時稱為軌跡 — 這樣的序列會產生獎勵。
您將透過使用策略梯度的「在策略」方法來訓練您的打磚塊代理程式 — 這是一種演算法,屬於基於策略方法系列。 策略梯度方法通常使用 梯度下降 來更新策略的參數,以符合長期累積獎勵,而梯度下降廣泛用於機器學習。 而且,由於目標是最大化函數(獎勵),而不是最小化它,因此該過程也稱為梯度上升。 換句話說,您使用策略讓代理程式採取動作,目標是最大化獎勵,這是透過計算梯度並使用它們來更新策略(神經)網路中的參數來完成的。
設定打磚塊#
1. 首先,您應該安裝 OpenAI Gym(使用 pip install gym[atari]
- 此套件目前在 conda 上不可用),並匯入 NumPy、Gym 和必要的模組
import numpy as np
import gym
Gym 可以使用 Monitor
包裝函式監控和儲存輸出
from gym import wrappers
from gym.wrappers import Monitor
2. 針對打磚塊遊戲實例化 Gym 環境
env = gym.make("Pong-v0")
3. 讓我們檢閱一下 Pong-v0
環境中可用的動作
print(env.action_space)
print(env.get_action_meanings())
共有 6 個動作。 但是,LEFTFIRE
實際上是 LEFT
,RIGHTFIRE
— RIGHT
,而 NOOP
— FIRE
。
為了簡單起見,您的策略網路將有一個輸出 — 「向上移動」(索引為 2
或 RIGHT
)的(對數)機率。 另一個可用的動作將索引為 3(「向下移動」或 LEFT
)。
4. Gym 可以 MP4 格式儲存代理程式學習的影片 — 透過執行以下程式碼,將 Monitor()
包裝在環境周圍
env = Monitor(env, "./video", force=True)
雖然您可以在 Jupyter 筆記本中執行各種 RL 實驗,但在訓練後呈現 Gym 環境的影像或影片以視覺化您的代理程式如何玩打磚塊遊戲可能相當具有挑戰性。 如果您想在筆記本中設定影片播放,您可以在本教學課程結尾的附錄中找到詳細資訊。
預處理幀 (觀察)#
在本節中,您將設定一個函數來預處理輸入資料(遊戲觀察),使其可供神經網路消化,神經網路只能處理張量(浮點型別的多維陣列)形式的輸入。
您的代理程式將使用打磚塊遊戲的幀 — 螢幕幀的像素 — 作為策略網路的輸入觀察。 遊戲觀察會告知代理程式球在哪裡,然後再將其(透過前向傳遞)饋送到神經網路(策略)中。 這類似於 DeepMind 的 DQN 方法(附錄中將進一步討論)。
打磚塊螢幕幀為 210x160 像素,具有 3 個色彩維度(紅色、綠色和藍色)。 陣列使用 uint8
(或 8 位元整數)編碼,這些觀察儲存在 Gym Box 實例上。
1. 檢查打磚塊的觀察
print(env.observation_space)
在 Gym 中,代理程式的動作和觀察可以是 Box
(n 維)或 Discrete
(固定範圍整數)類別的一部分。
2. 您可以檢視隨機觀察 — 一幀 — 透過
1) Setting the random `seed` before initialization (optional).
2) Calling Gym's `reset()` to reset the environment, which returns an initial observation.
3) Using Matplotlib to display the `render`ed observation.
(您可以參考 OpenAI Gym 核心 API 以取得有關 Gym 核心類別和方法的更多資訊。)
import matplotlib.pyplot as plt
env.seed(42)
env.reset()
random_frame = env.render(mode="rgb_array")
print(random_frame.shape)
plt.imshow(random_frame)
若要將觀察饋送到策略(神經)網路中,您需要將其轉換為具有 6,400 個(80x80x1)浮點陣列的 1D 灰階向量。 (在訓練期間,您將使用 NumPy 的 np.ravel()
函數來展平這些陣列。)
3. 設定幀(觀察)預處理的協助程式函數
def frame_preprocessing(observation_frame):
# Crop the frame.
observation_frame = observation_frame[35:195]
# Downsample the frame by a factor of 2.
observation_frame = observation_frame[::2, ::2, 0]
# Remove the background and apply other enhancements.
observation_frame[observation_frame == 144] = 0 # Erase the background (type 1).
observation_frame[observation_frame == 109] = 0 # Erase the background (type 2).
observation_frame[observation_frame != 0] = 1 # Set the items (rackets, ball) to 1.
# Return the preprocessed frame as a 1D floating-point array.
return observation_frame.astype(float)
4. 預處理稍早的隨機幀以測試函數 — 策略網路的輸入是 80x80 1D 影像
preprocessed_random_frame = frame_preprocessing(random_frame)
plt.imshow(preprocessed_random_frame, cmap="gray")
print(preprocessed_random_frame.shape)
建立策略 (神經網路) 和前向傳遞#
接下來,您將策略定義為一個簡單的前饋網路,該網路使用遊戲觀察作為輸入並輸出動作對數機率
對於輸入,它將使用打磚塊電玩遊戲幀 — 具有 6,400 個(80x80)浮點陣列的預處理 1D 向量。
隱藏層將使用 NumPy 的點積函數
np.dot()
計算輸入的加權總和以用於陣列,然後套用非線性啟動函數,例如 ReLU。然後,輸出層將再次執行權重參數的矩陣乘法和隱藏層的輸出(使用
np.dot()
),並透過 softmax啟動函數傳送該資訊。最後,策略網路將為代理程式輸出一個動作對數機率(給定該觀察)— 環境中索引為 2 的打磚塊動作(「向上移動球拍」)的機率。
1. 讓我們實例化輸入、隱藏和輸出層的某些參數,並開始設定網路模型。
首先為實驗建立隨機數字產生器實例(為可重複性而植入種子)
rng = np.random.default_rng(seed=12288743)
然後
設定輸入(觀察)維度 — 您的預處理螢幕幀
D = 80 * 80
設定隱藏層神經元的數量。
H = 200
將您的策略(神經)網路模型實例化為空字典。
model = {}
在神經網路中,權重是重要的可調整參數,網路透過向前和向後傳播資料來微調這些參數。
2. 使用稱為 Xavier 初始化 的技術,使用 NumPy 的 Generator.standard_normal()
設定網路模型的初始權重,該函數傳回標準常態分佈上的隨機數字,以及 np.sqrt()
model["W1"] = rng.standard_normal(size=(H, D)) / np.sqrt(D)
model["W2"] = rng.standard_normal(size=H) / np.sqrt(H)
3. 您的策略網路首先隨機初始化權重,並將輸入資料(幀)從輸入層向前饋送到輸出層,途經隱藏層。 此過程稱為前向傳遞或前向傳播,並在函數 policy_forward()
中概述
def policy_forward(x, model):
# Matrix-multiply the weights by the input in the one and only hidden layer.
h = np.dot(model["W1"], x)
# Apply non-linearity with ReLU.
h[h < 0] = 0
# Calculate the "dot" product in the outer layer.
# The input for the sigmoid function is called logit.
logit = np.dot(model["W2"], h)
# Apply the sigmoid function (non-linear activation).
p = sigmoid(logit)
# Return a log probability for the action 2 ("move up")
# and the hidden "state" that you need for backpropagation.
return p, h
請注意,有兩個啟動函數用於判斷輸入和輸出之間的非線性關係。 這些非線性函數會套用至圖層的輸出
整流線性單元 (ReLU):如上定義為
h[h<0] = 0
。 它針對負輸入傳回 0,如果為正數則傳回相同的值。Sigmoid:在下方定義為
sigmoid()
。 它「包裝」最後一層的輸出,並傳回 (0, 1) 範圍內的動作對數機率。
4. 使用 NumPy 的 np.exp()
單獨定義 sigmoid 函數,以計算指數
def sigmoid(x):
return 1.0 / (1.0 + np.exp(-x))
設定更新步驟 (反向傳播)#
在深度 RL 演算法的學習期間,您會使用動作對數機率(給定觀察)和折扣報酬(例如,打磚塊遊戲中為 +1 或 -1),並執行反向傳遞或反向傳播來更新參數 — 策略網路的權重。
1. 讓我們在 NumPy 模組的陣列乘法協助下定義反向傳遞函數 (policy_backward()
) — np.dot()
(矩陣乘法)、np.outer()
(外積計算)和 np.ravel()
(將陣列展平為 1D 陣列)
def policy_backward(eph, epdlogp, model):
dW2 = np.dot(eph.T, epdlogp).ravel()
dh = np.outer(epdlogp, model["W2"])
dh[eph <= 0] = 0
dW1 = np.dot(dh.T, epx)
# Return new "optimized" weights for the policy network.
return {"W1": dW1, "W2": dW2}
使用網路的中間隱藏「狀態」(eph
) 和回合的動作對數機率梯度 (epdlogp
),policy_backward
函數會將梯度向後傳播通過策略網路並更新權重。
2. 在代理程式訓練期間套用反向傳播時,您需要為每個回合儲存多個變數。 讓我們實例化空列表來儲存它們
# All preprocessed observations for the episode.
xs = []
# All hidden "states" (from the network) for the episode.
hs = []
# All gradients of probability actions
# (with respect to observations) for the episode.
dlogps = []
# All rewards for the episode.
drs = []
您將在訓練期間的每個回合結束時手動重設這些變數,在它們「滿」之後,並使用 NumPy 的 np.vstack()
重新整形。 這在本教學課程結尾的訓練階段中示範。
3. 接下來,為了在最佳化代理程式策略時執行梯度上升,通常會使用深度學習最佳化器(您正在使用梯度執行最佳化)。 在此範例中,您將使用 RMSProp — 一種自適應最佳化方法。 讓我們為最佳化器設定一個折扣因子 — 衰減率 —
decay_rate = 0.99
4. 您還需要儲存梯度(在 NumPy 的 np.zeros_like()
協助下),以便在訓練期間進行最佳化步驟
首先,儲存更新緩衝區,該緩衝區會將批次中的梯度相加
grad_buffer = {k: np.zeros_like(v) for k, v in model.items()}
其次,儲存 RMSProp 記憶體以供最佳化器進行梯度上升
rmsprop_cache = {k: np.zeros_like(v) for k, v in model.items()}
定義折扣獎勵 (預期報酬) 函數#
在本節中,您將設定一個函數來計算折扣獎勵 (discount_rewards()
) — 從觀察中獲得的預期報酬 — 該函數使用 1D 獎勵陣列作為輸入(在 NumPy 的 np.zeros_like()
) 函數的協助下)。
為了讓短期獎勵比長期獎勵更重要,您將使用折扣因子 (gamma),它通常是介於 0.9 和 0.99 之間的浮點數。
gamma = 0.99
def discount_rewards(r, gamma):
discounted_r = np.zeros_like(r)
running_add = 0
# From the last reward to the first...
for t in reversed(range(0, r.size)):
# ...reset the reward sum
if r[t] != 0:
running_add = 0
# ...compute the discounted reward
running_add = running_add * gamma + r[t]
discounted_r[t] = running_add
return discounted_r
訓練代理程式多個回合#
本節涵蓋如何設定訓練過程,在此過程中,您的代理程式將學習使用其策略玩打磚塊遊戲。
打磚塊遊戲策略梯度方法的虛擬碼
實例化策略 — 您的神經網路 — 並隨機初始化策略網路中的權重。
初始化隨機觀察。
隨機初始化策略網路中的權重。
在多個回合中重複
將觀察輸入策略網路,並為代理程式輸出動作機率(前向傳播)。
代理程式針對每個觀察採取動作,觀察收到的獎勵,並收集狀態動作經驗的軌跡(在預定義的回合數或批次大小之上)。
計算交叉熵(帶有正號,因為您需要最大化獎勵而不是最小化損失)。
對於每批回合
使用交叉熵計算您的動作對數機率的梯度。
計算累積報酬,為了讓短期獎勵比長期獎勵更重要,請使用折扣因子折扣。
將動作對數機率的梯度乘以折扣獎勵(「優勢」)。
執行梯度上升(反向傳播)以最佳化策略網路的參數(其權重)。
最大化導致高獎勵的動作的機率。
您可以隨時停止訓練,或/並檢查磁碟 /video
目錄中已儲存的播放 MP4 影片。 您可以設定適合您設定的最大回合數。
1. 為了示範目的,讓我們將訓練回合數限制為 3。 如果您使用硬體加速(CPU 和 GPU),您可以將數字增加到 1,000 或更多。 為了比較,Andrej Karpathy 的原始實驗大約進行了 8,000 個回合。
max_episodes = 3
2. 設定批次大小和學習率值
批次大小決定了模型執行參數更新的頻率(以回合為單位)。 它是您的代理程式可以收集狀態動作軌跡的次數。 在收集結束時,您可以執行動作機率倍數的最大化。
學習率有助於限制權重更新的幅度,以防止它們過度校正。
batch_size = 3
learning_rate = 1e-4
3. 為 Gym 的 render
方法設定遊戲呈現預設變數(它用於顯示觀察,是可選的,但在偵錯期間可能很有用)
render = False
4. 透過呼叫 reset()
設定代理程式的初始(隨機)觀察
observation = env.reset()
5. 初始化先前的觀察
prev_x = None
6. 初始化獎勵變數和回合計數
running_reward = None
reward_sum = 0
episode_number = 0
7. 為了模擬幀之間的運動,將策略網路的單個輸入幀 (x
) 設定為目前和先前預處理幀之間的差異
def update_input(prev_x, cur_x, D):
if prev_x is not None:
x = cur_x - prev_x
else:
x = np.zeros(D)
return x
8. 最後,使用您預先定義的函數啟動訓練迴圈
:tags: [output_scroll]
while episode_number < max_episodes:
# (For rendering.)
if render:
env.render()
# 1. Preprocess the observation (a game frame) and flatten with NumPy's `ravel()`.
cur_x = frame_preprocessing(observation).ravel()
# 2. Instantiate the observation for the policy network
x = update_input(prev_x, cur_x, D)
prev_x = cur_x
# 3. Perform the forward pass through the policy network using the observations
# (preprocessed frames as inputs) and store the action log probabilities
# and hidden "states" (for backpropagation) during the course of each episode.
aprob, h = policy_forward(x, model)
# 4. Let the action indexed at `2` ("move up") be that probability
# if it's higher than a randomly sampled value
# or use action `3` ("move down") otherwise.
action = 2 if rng.uniform() < aprob else 3
# 5. Cache the observations and hidden "states" (from the network)
# in separate variables for backpropagation.
xs.append(x)
hs.append(h)
# 6. Compute the gradients of action log probabilities:
# - If the action was to "move up" (index `2`):
y = 1 if action == 2 else 0
# - The cross-entropy:
# `y*log(aprob) + (1 - y)*log(1-aprob)`
# or `log(aprob)` if y = 1, else: `log(1 - aprob)`.
# (Recall: you used the sigmoid function (`1/(1+np.exp(-x)`) to output
# `aprob` action probabilities.)
# - Then the gradient: `y - aprob`.
# 7. Append the gradients of your action log probabilities.
dlogps.append(y - aprob)
# 8. Take an action and update the parameters with Gym's `step()`
# function; obtain a new observation.
observation, reward, done, info = env.step(action)
# 9. Update the total sum of rewards.
reward_sum += reward
# 10. Append the reward for the previous action.
drs.append(reward)
# After an episode is finished:
if done:
episode_number += 1
# 11. Collect and reshape stored values with `np.vstack()` of:
# - Observation frames (inputs),
epx = np.vstack(xs)
# - hidden "states" (from the network),
eph = np.vstack(hs)
# - gradients of action log probabilities,
epdlogp = np.vstack(dlogps)
# - and received rewards for the past episode.
epr = np.vstack(drs)
# 12. Reset the stored variables for the new episode:
xs = []
hs = []
dlogps = []
drs = []
# 13. Discount the rewards for the past episode using the helper
# function you defined earlier...
discounted_epr = discount_rewards(epr, gamma)
# ...and normalize them because they have high variance
# (this is explained below.)
discounted_epr -= np.mean(discounted_epr)
discounted_epr /= np.std(discounted_epr)
# 14. Multiply the discounted rewards by the gradients of the action
# log probabilities (the "advantage").
epdlogp *= discounted_epr
# 15. Use the gradients to perform backpropagation and gradient ascent.
grad = policy_backward(eph, epdlogp, model)
# 16. Save the policy gradients in a buffer.
for k in model:
grad_buffer[k] += grad[k]
# 17. Use the RMSProp optimizer to perform the policy network
# parameter (weight) update at every batch size
# (by default: every 10 episodes).
if episode_number % batch_size == 0:
for k, v in model.items():
# The gradient.
g = grad_buffer[k]
# Use the RMSProp discounting factor.
rmsprop_cache[k] = (
decay_rate * rmsprop_cache[k] + (1 - decay_rate) * g ** 2
)
# Update the policy network with a learning rate
# and the RMSProp optimizer using gradient ascent
# (hence, there's no negative sign)
model[k] += learning_rate * g / (np.sqrt(rmsprop_cache[k]) + 1e-5)
# Reset the gradient buffer at the end.
grad_buffer[k] = np.zeros_like(v)
# 18. Measure the total discounted reward.
running_reward = (
reward_sum
if running_reward is None
else running_reward * 0.99 + reward_sum * 0.01
)
print(
"Resetting the Pong environment. Episode total reward: {} Running mean: {}".format(
reward_sum, running_reward
)
)
# 19. Set the agent's initial observation by calling Gym's `reset()` function
# for the next episode and setting the reward sum back to 0.
reward_sum = 0
observation = env.reset()
prev_x = None
# 20. Display the output during training.
if reward != 0:
print(
"Episode {}: Game finished. Reward: {}...".format(episode_number, reward)
+ ("" if reward == -1 else " POSITIVE REWARD!")
)
一些注意事項
如果您先前已執行過實驗並想要重複執行,您的
Monitor
實例可能仍在執行,這可能會在您下次嘗試訓練代理程式時擲回錯誤。 因此,您應該先透過呼叫env.close()
來關閉Monitor
,方法是取消註解並執行以下儲存格
# env.close()
在打磚塊遊戲中,如果玩家沒有將球擊回,他們會收到負獎勵 (-1),而另一位玩家會獲得 +1 獎勵。 代理程式透過玩打磚塊遊戲獲得的獎勵具有顯著的變異數。 因此,最佳實務是以相同的平均值(使用
np.mean()
)和標準差(使用 NumPy 的np.std()
)對其進行正規化。當僅使用 NumPy 時,深度 RL 訓練過程(包括反向傳播)會跨越多行程式碼,這些程式碼可能看起來相當長。 其中一個主要原因是您未使用具有自動微分庫的深度學習框架,而自動微分庫通常會簡化此類實驗。 本教學課程示範如何從頭開始執行所有操作,但您也可以使用許多基於 Python 的框架(具有「autodiff」和「autograd」),您將在本教學課程結束時了解這些框架。
下一步#
您可能會注意到,如果您將回合數從 100 個增加到 500 個或 1,000 個以上,則訓練 RL 代理程式需要很長時間,具體取決於您用於此任務的硬體 — CPU 和 GPU。
如果您給予策略梯度方法足夠的時間,它們可以學習任務,而 RL 中的最佳化是一個具有挑戰性的問題。 訓練代理程式學習玩打磚塊遊戲或任何其他任務可能效率不高,並且需要大量回合。 您也可能會在訓練輸出中注意到,即使在數百個回合之後,獎勵也可能具有高變異數。
此外,與許多基於深度學習的演算法一樣,您應該考慮策略必須學習的大量參數。 在打磚塊遊戲中,如果網路的隱藏層中有 200 個節點,且輸入維度大小為 6,400 (80x80),則此數字加起來為 100 萬或更多。 因此,新增更多 CPU 和 GPU 來協助訓練始終是一個選項。
您可以使用更先進的基於策略梯度的演算法,這有助於加速訓練、提高對參數的敏感度並解決其他問題。 例如,存在「自我對弈」方法,例如 近端策略最佳化 (PPO),由 John Schulman 等人在 2017 年開發,這些方法被 used 用於訓練 OpenAI Five 代理程式超過 10 個月,以在競技級別玩 Dota 2。 當然,如果您將這些方法應用於較小的 Gym 環境,則應該需要數小時而不是數月才能訓練。
一般來說,RL 挑戰和可能的解決方案有很多,您可以在 Reinforcement learning, fast and slow 中探索其中一些,作者為 Matthew Botvinick、Sam Ritter、Jane X. Wang、Zeb Kurth-Nelson、Charles Blundell 和 Demis Hassabis (2019)。
如果您想了解有關深度 RL 的更多資訊,您應該查看以下免費教育資料
Spinning Up in Deep RL:由 OpenAI 開發。
RL lectures 由 David Silver (DeepMind, UCL) 講授。
從頭開始使用 NumPy 建立神經網路是了解更多關於 NumPy 和深度學習的好方法。 但是,對於實際應用,您應該使用專門的框架 — 例如 PyTorch、JAX、TensorFlow 或 MXNet — 這些框架提供類似 NumPy 的 API,具有內建的自動微分和 GPU 支援,並且專為高效能數值運算和機器學習而設計。
附錄#
關於 RL 和深度 RL 的注意事項#
在 監督式深度學習中,對於影像辨識、語言翻譯或文字分類等任務,您更有可能使用大量標記資料。 然而,在 RL 中,代理程式通常不會收到直接的明確回饋,指出正確或錯誤的動作 — 它們依賴其他訊號,例如獎勵。
深度 RL 將 RL 與 深度學習 相結合。 該領域在更複雜的環境(例如電玩遊戲)中取得了第一個重大成功,時間是 2013 年 — 在電腦視覺領域的 AlexNet 突破一年後。 Volodymyr Mnih 和 DeepMind 的同事發表了一篇名為 Playing Atari with deep reinforcement learning 的研究論文(以及 2015 年更新),指出他們能夠訓練一個代理程式,該代理程式可以以人類水平玩街機學習環境中的幾款經典遊戲。 他們的 RL 演算法 — 稱為深度 Q 網路 (DQN) — 在神經網路中使用了 卷積層,該網路近似於 Q 學習 並使用了 經驗重播。
與您在本範例中使用的簡單策略梯度方法不同,DQN 使用一種「離策略」基於價值的方法(近似於 Q 學習),而原始的 AlphaGo 使用策略梯度和 蒙地卡羅樹搜尋。
策略梯度搭配函數近似,例如神經網路,最早在 2000 年由 Richard Sutton 等人撰寫相關文獻。它們受到許多早期研究的影響,包括統計梯度追蹤演算法,例如 REINFORCE (Ronald Williams, 1992) 以及協助深度學習演算法學習的反向傳播 (Geoffrey Hinton, 1986)。 搭配神經網路函數近似的強化學習 (RL) 在 1990 年代由 Gerald Tesauro (時間差分學習與 TD-Gammon, 1995) 的研究中被引入,他與 IBM 合作開發了一個學習西洋雙陸棋的 agent (1992),以及 Long-Ji Lin (使用神經網路的機器人強化學習, 1993)。
自 2013 年以來,研究人員提出了許多著名的深度強化學習 (deep RL) 方法來學習解決複雜任務,例如圍棋的 AlphaGo (David Silver 等人, 2016)、透過自我對弈掌握圍棋、西洋棋和將棋的 AlphaZero (David Silver 等人, 2017-2018)、Dota 2 的 OpenAI Five (使用自我對弈) (OpenAI, 2019) 以及星海爭霸II 的 AlphaStar (使用搭配經驗回放、自我模仿學習 和 策略蒸餾 的 actor-critic 演算法) (Oriol Vinyals 等人, 2019)。此外,還有其他實驗,例如 Electronic Arts/DICE 的工程師將深度強化學習應用於戰地風雲 1。
深度強化學習研究中,電子遊戲之所以受歡迎的原因之一是,與真實世界的實驗 (例如遙控直升機的強化學習...) 不同,虛擬模擬可以提供更安全的測試環境,例如遙控直升機 (Pieter Abbeel 等人, 2006)。
如果您有興趣了解深度強化學習對其他領域 (例如神經科學) 的影響,您可以參考 Matthew Botvinick 等人 (2020) 的論文,作者包含 Matthew Botvinick 等人。
如何在 Jupyter Notebook 中設定影片播放#
如果您使用 Binder (一個免費的、基於 Jupyter Notebook 的工具),您可以設定 Docker 映像檔,並將
freeglut3-dev
、xvfb
和x11-utils
新增至apt.txt
設定檔以安裝初始依賴項。然後,在binder/environment.yml
中的channels
下,新增gym
、pyvirtualdisplay
以及您可能需要的任何其他項目,例如python=3.7
、pip
和jupyterlab
。查看以下文章以獲取更多資訊。如果您使用 Google Colaboratory (另一個免費的、基於 Jupyter Notebook 的工具),您可以安裝並設定 X 虛擬幀緩衝區/Xvfb、X11、FFmpeg、PyVirtualDisplay、PyOpenGL 和其他依賴項,以啟用遊戲環境的影片播放,如下文進一步描述。
如果您使用 Google Colaboratory,請在 notebook 儲存格中執行以下命令,以協助影片播放
# Install Xvfb and X11 dependencies. !apt-get install -y xvfb x11-utils > /dev/null 2>&1 # To work with videos, install FFmpeg. !apt-get install -y ffmpeg > /dev/null 2>&1 # Install PyVirtualDisplay for visual feedback and other libraries/dependencies. !pip install pyvirtualdisplay PyOpenGL PyOpenGL-accelerate > /dev/null 2>&1
然後,新增以下 Python 程式碼
# Import the virtual display module. from pyvirtualdisplay import Display # Import ipythondisplay and HTML from IPython for image and video rendering. from IPython import display as ipythondisplay from IPython.display import HTML # Initialize the virtual buffer at 400x300 (adjustable size). # With Xvfb, you should set `visible=False`. display = Display(visible=False, size=(400, 300)) display.start() # Check that no display is present. # If no displays are present, the expected output is `:0`. !echo $DISPLAY # Define a helper function to display videos in Jupyter notebooks:. # (Source: https://star-ai.github.io/Rendering-OpenAi-Gym-in-Colaboratory/) import sys import math import glob import io import base64 def show_any_video(mp4video=0): mp4list = glob.glob('video/*.mp4') if len(mp4list) > 0: mp4 = mp4list[mp4video] video = io.open(mp4, 'r+b').read() encoded = base64.b64encode(video) ipythondisplay.display(HTML(data='''<video alt="test" autoplay loop controls style="height: 400px;"> <source src="data:video/mp4;base64,{0}" type="video/mp4" /> </video>'''.format(encoded.decode('ascii')))) else: print('Could not find the video!')
如果您想在 Jupyter notebook 中查看最後 (非常快速) 的遊戲過程,並且先前已實作
show_any_video()
函數,請在儲存格中執行以下程式碼show_any_video(-1)
如果您在 Linux 或 macOS 的本機環境中按照本教學的指示操作,您可以將大部分程式碼新增到一個 Python (
.py
) 檔案中。然後,您可以在終端機中透過python your-code.py
執行您的 Gym 實驗。若要啟用 rendering,您可以按照官方 OpenAI Gym 文件 (請確保您已安裝 Gym 和 Xvfb,如指南中所述) 使用命令列介面。