(第一部分 機器學習基礎)
第01章 機器學習概覽
第02章 一個完整的機器學習項目(上)
第02章 一個完整的機器學習項目(下)
第03章 分類
第04章 訓練模型
第05章 支持向量機
第06章 決策樹
第07章 集成學習和隨機森林
第08章 降維
(第二部分 神經網絡和深度學習)
第9章 啟動和運行TensorFlow
第10章 人工神經網絡
第11章 訓練深度神經網絡(上)
第11章 訓練深度神經網絡(下)
第12章 設備和服務器上的分布式 TensorFlow
第13章 卷積神經網絡
第14章 循環神經網絡
第15章 自編碼器
第16章 強化學習(上)
第16章 強化學習(下)
強化學習(RL)如今是機器學習的一大令人激動的領域,當然之前也是。自從 1950 年被發明出來后,它在這些年產生了一些有趣的應用,尤其是在游戲(例如 TD-Gammon,一個西洋雙陸棋程序)和機器控制領域,但是從未弄出什么大新聞。直到 2013 年一個革命性的發展:來自英國的研究者發起了Deepmind 項目,這個項目可以學習去玩任何從頭開始的 Atari 游戲,在多數游戲中,比人類玩的還好,它僅使用像素作為輸入而沒有使用游戲規則的任何先驗知識。這是一系列令人驚嘆的壯舉中的第一個,并在 2016 年 3 月以他們的系統阿爾法狗戰勝了世界圍棋冠軍李世石而告終。從未有程序能勉強打敗這個游戲的大師,更不用說世界冠軍了。今天,RL 的整個領域正在沸騰著新的想法,其都具有廣泛的應用范圍。DeepMind 在 2014 被谷歌以超過 5 億美元收購。
那么他們是怎么做到的呢?事后看來,原理似乎相當簡單:他們將深度學習運用到強化學習領域,結果卻超越了他們最瘋狂的設想。在本章中,我們將首先解釋強化學習是什么,以及它擅長于什么,然后我們將介紹兩個在深度強化學習領域最重要的技術:策略梯度和深度 Q 網絡(DQN),包括討論馬爾可夫決策過程(MDP)。我們將使用這些技術來訓練一個模型來平衡移動車上的桿子,另一個玩 Atari 游戲。同樣的技術可以用于各種各樣的任務,從步行機器人到自動駕駛汽車。
學習優化獎勵
在強化學習中,智能體在環境(environment)中觀察(observation)并且做出決策(action),隨后它會得到獎勵(reward)。它的目標是去學習如何行動能最大化期望獎勵。如果你不在意去擬人化的話,你可以認為正獎勵是愉快,負獎勵是痛苦(這樣的話獎勵一詞就有點誤導了)。簡單來說,智能體在環境中行動,并且在實驗和錯誤中去學習最大化它的愉快,最小化它的痛苦。
這是一個相當廣泛的設置,可以適用于各種各樣的任務。以下是幾個例子(詳見圖 16-1):
- 智能體可以是控制一個機械狗的程序。在此例中,環境就是真實的世界,智能體通過許多的傳感器例如攝像機或者傳感器來觀察,它可以通過給電機發送信號來行動。它可以被編程設置為如果到達了目的地就得到正獎勵,如果浪費時間,或者走錯方向,或摔倒了就得到負獎勵。
- 智能體可以是控制 MS.Pac-Man 的程序。在此例中,環境是 Atari 游戲的仿真,行為是 9 個操縱桿位(上下左右中間等等),觀察是屏幕,回報就是游戲點數。
- 相似地,智能體也可以是棋盤游戲的程序例如:圍棋。
- 智能體也可以不用去控制一個實體(或虛擬的)去移動。例如它可以是一個智能程序,當它調整到目標溫度以節能時會得到正獎勵,當人們需要自己去調節溫度時它會得到負獎勵,所以智能體必須學會預見人們的需要。
- 智能體也可以去觀測股票市場價格以實時決定買賣。獎勵的依據顯然為掙錢或者賠錢。
其實沒有正獎勵也是可以的,例如智能體在迷宮內移動,它每分每秒都得到一個負獎勵,所以它要盡可能快的找到出口!還有很多適合強化學習的領域,例如自動駕駛汽車,在網頁上放廣告,或者控制一個圖像分類系統讓它明白它應該關注于什么。
策略搜索
被智能體使用去改變它行為的算法叫做策略。例如,策略可以是一個把觀測當輸入,行為當做輸出的神經網絡(見圖16-2)。
這個策略可以是你能想到的任何算法,它甚至可以不被確定。舉個例子,例如,考慮一個真空吸塵器,它的獎勵是在 30 分鐘內撿起的灰塵數量。它的策略可以是每秒以概率P
向前移動,或者以概率1-P
隨機地向左或向右旋轉。旋轉角度將是-R
和+R
之間的隨機角度,因為該策略涉及一些隨機性,所以稱為隨機策略。機器人將有一個不確定的軌跡,它保證它最終會到達任何可以到達的地方,并撿起所有的灰塵。問題是:30分鐘后它會撿起多少灰塵?
你怎么訓練這樣的機器人?你可以調整兩個策略參數:概率P
和角度范圍R
。一個想法是這些參數嘗試許多不同的值,并選擇執行最佳的組合(見圖 16-3)。這是一個策略搜索的例子,在這種情況下使用野蠻的方法。然而,當策略空間太大(通常情況下),以這樣的方式找到一組好的參數就像是大海撈針。
另一種搜尋策略空間的方法是遺傳算法。例如你可以隨機創造一個包含 100 個策略的第一代基因,隨后殺死 80 個糟糕的策略,隨后讓 20 個幸存策略繁衍 4 代。一個后代只是它父輩基因的復制品加上一些隨機變異。幸存的策略加上他們的后代共同構成了第二代。你可以繼續以這種方式迭代代,直到找到一個好的策略。
另一種方法是使用優化技術,通過評估獎勵關于策略參數的梯度,然后通過跟隨梯度向更高的獎勵(梯度上升)調整這些參數。這種方法被稱為策略梯度(policy gradient, PG),我們將在本章后面詳細討論。例如,回到真空吸塵器機器人,你可以稍微增加概率P并評估這是否增加了機器人在 30 分鐘內拾起的灰塵的量;如果確實增加了,就相對應增加P
,否則減少P
。我們將使用 Tensorflow 來實現 PG 算法,但是在這之前我們需要為智能體創造一個生存的環境,所以現在是介紹 OpenAI 的時候了。
OpenAI 介紹
強化學習的一個挑戰是,為了訓練對象,首先需要有一個工作環境。如果你想設計一個可以學習 Atari 游戲的程序,你需要一個 Atari 游戲模擬器。如果你想設計一個步行機器人,那么環境就是真實的世界,你可以直接在這個環境中訓練你的機器人,但是這有其局限性:如果機器人從懸崖上掉下來,你不能僅僅點擊“撤消”。你也不能加快時間;增加更多的計算能力不會讓機器人移動得更快。一般來說,同時訓練 1000 個機器人是非常昂貴的。簡而言之,訓練在現實世界中是困難和緩慢的,所以你通常需要一個模擬環境,至少需要引導訓練。
OpenAI gym 是一個工具包,它提供各種各樣的模擬環境(Atari 游戲,棋盤游戲,2D 和 3D 物理模擬等等),所以你可以訓練,比較,或開發新的 RL 算法。
讓我們安裝 OpenAI gym。可通過pip
安裝:
$ pip install --upgrade gym
接下來打開 Python shell 或 Jupyter 筆記本創建您的第一個環境:
>>> import gym
>>> env = gym.make("CartPole-v0")
[2016-10-14 16:03:23,199] Making new env: MsPacman-v0
>>> obs = env.reset()
>>> obs
array([-0.03799846,-0.03288115,0.02337094,0.00720711])
>>> env.render()
使用make()
函數創建一個環境,在此例中是 CartPole 環境。這是一個 2D 模擬,其中推車可以被左右加速,以平衡放置在它上面的平衡桿(見圖 16-4)。在創建環境之后,我們需要使用reset()
初始化。這會返回第一個觀察結果。觀察取決于環境的類型。對于 CartPole 環境,每個觀測是包含四個浮點的 1D Numpy 向量:這些浮點數代表推車的水平位置(0 為中心)、其速度、桿的角度(0 維垂直)及其角速度。最后,render()
方法顯示如圖 16-4 所示的環境。
如果你想讓render()
讓圖像以一個 Numpy 數組格式返回,可以將mode
參數設置為rgb_array
(注意其他環境可能支持不同的模式):
>>> img = env.render(mode="rgb_array")
>>> img.shape # height, width, channels (3=RGB)
(400, 600, 3)
不幸的是,即使將mode
參數設置為rgb_array
,CartPole(和其他一些環境)還是會將將圖像呈現到屏幕上。避免這種情況的唯一方式是使用一個 fake X 服務器,如 XVFB 或 XDimMy。例如,可以使用以下命令安裝 XVFB 和啟動 Python:xvfb-run -s "screen 0 1400x900x24" python
。或者使用xvfbwrapper
包。
讓我們來詢問環境什么動作是可能的:
>>> env.action_space
Discrete(2)
Discrete(2)
表示可能的動作是整數 0 和 1,表示向左(0)或右(1)的加速。其他環境可能有更多的動作,或者其他類型的動作(例如,連續的)。因為桿子向右傾斜,讓我們向右加速推車:
>>> action = 1 # accelerate right
>>> obs, reward, done, info = env.step(action)
>>> obs
array([-0.03865608, 0.16189797, 0.02351508, -0.27801135])
>>> reward
1.0
>>> done
False
>>> info
{}
step()
表示執行給定的動作并返回四個值:
obs
:
這是新的觀測,小車現在正在向右走(obs[1]>0
,注:當前速度為正,向右為正)。平衡桿仍然向右傾斜(obs[2]>0
),但是他的角速度現在為負(obs[3]<0
),所以它在下一步后可能會向左傾斜。
reward
:
在這個環境中,無論你做什么,每一步都會得到 1.0 獎勵,所以游戲的目標就是盡可能長的運行。
done
:
當游戲結束時這個值會為True
。當平衡桿傾斜太多時會發生這種情況。之后,必須重新設置環境才能重新使用。
info
:
該字典可以在其他環境中提供額外的調試信息。這些數據不應該用于訓練(這是作弊)。
讓我們硬編碼一個簡單的策略,當桿向左傾斜時加速左邊,當桿向右傾斜時加速。我們使用這個策略來獲得超過 500 步的平均回報:
def basic_policy(obs):
angle = obs[2]
return 0 if angle < 0 else 1
totals = []
for episode in range(500):
episode_rewards = 0
obs = env.reset()
for step in range(1000): # 最多1000 步,我們不想讓它永遠運行下去
action = basic_policy(obs)
obs, reward, done, info = env.step(action)
episode_rewards += reward
if done:
break
totals.append(episode_rewards)
這個代碼希望能自我解釋。讓我們看看結果:
>>> import numpy as np
>>> np.mean(totals), np.std(totals), np.min(totals), np.max(totals)
(42.125999999999998, 9.1237121830974033, 24.0, 68.0)
即使有 500 次嘗試,這一策略從未使平衡桿在超過 68 個連續的步驟里保持直立。這不太好。如果你看一下 Juyter Notebook 中的模擬,你會發現,推車越來越強烈地左右擺動,直到平衡桿傾斜太多。讓我們看看神經網絡是否能提出更好的策略。
神經網絡策略
讓我們創建一個神經網絡策略。就像之前我們編碼的策略一樣,這個神經網絡將把觀察作為輸入,輸出要執行的動作。更確切地說,它將估計每個動作的概率,然后我們將根據估計的概率隨機地選擇一個動作(見圖 16-5)。在 CartPole 環境中,只有兩種可能的動作(左或右),所以我們只需要一個輸出神經元。它將輸出動作 0(左)的概率p
,動作 1(右)的概率顯然將是1 - p
。
例如,如果它輸出 0.7,那么我們將以 70% 的概率選擇動作 0,以 30% 的概率選擇動作 1。
你可能奇怪為什么我們根據神經網絡給出的概率來選擇隨機的動作,而不是選擇最高分數的動作。這種方法使智能體在探索新的行為和利用那些已知可行的行動之間找到正確的平衡。舉個例子:假設你第一次去餐館,所有的菜看起來同樣吸引人,所以你隨機挑選一個。如果菜好吃,你可以增加下一次點它的概率,但是你不應該把這個概率提高到 100%,否則你將永遠不會嘗試其他菜肴,其中一些甚至比你嘗試的更好。
還要注意,在這個特定的環境中,過去的動作和觀察可以被安全地忽略,因為每個觀察都包含環境的完整狀態。如果有一些隱藏狀態,那么你也需要考慮過去的行為和觀察。例如,如果環境僅僅揭示了推車的位置,而不是它的速度,那么你不僅要考慮當前的觀測,還要考慮先前的觀測,以便估計當前的速度。另一個例子是當觀測是有噪聲的的,在這種情況下,通常你想用過去的觀察來估計最可能的當前狀態。因此,CartPole 問題是簡單的;觀測是無噪聲的,而且它們包含環境的全狀態。
import tensorflow as tf
from tensorflow.contrib.layers import fully_connected
# 1. 聲明神經網絡結構
n_inputs = 4 # == env.observation_space.shape[0]
n_hidden = 4 # 這只是個簡單的測試,不需要過多的隱藏層
n_outputs = 1 # 只輸出向左加速的概率
initializer = tf.contrib.layers.variance_scaling_initializer()
# 2. 建立神經網絡
X = tf.placeholder(tf.float32, shape=[None, n_inputs]) hidden = fully_connected(X, n_hidden, activation_fn=tf.nn.elu,weights_initializer=initializer) # 隱層激活函數使用指數線性函數
logits = fully_connected(hidden, n_outputs, activation_fn=None,weights_initializer=initializer)
outputs = tf.nn.sigmoid(logits)
# 3. 在概率基礎上隨機選擇動作
p_left_and_right = tf.concat(axis=1, values=[outputs, 1 - outputs])
action = tf.multinomial(tf.log(p_left_and_right), num_samples=1)
init = tf.global_variables_initializer()
讓我們通讀代碼:
在導入之后,我們定義了神經網絡體系結構。輸入的數量是觀測空間的大小(在 CartPole 的情況下是 4 個),我們只有 4 個隱藏單元,并且不需要更多,并且我們只有 1 個輸出概率(向左的概率)。
接下來我們構建了神經網絡。在這個例子中,它是一個 vanilla 多層感知器,只有一個輸出。注意,輸出層使用 Logistic(Sigmoid)激活函數,以便輸出從 0 到 1 的概率。如果有兩個以上的可能動作,每個動作都會有一個輸出神經元,相應的你將使用 Softmax 激活函數。
最后,我們調用
multinomial()
函數來選擇一個隨機動作。該函數獨立地采樣一個(或多個)整數,給定每個整數的對數概率。例如,如果通過設置num_samples=5
,令數組為[np.log(0.5), np.log(0.2), np.log(0.3)]
來調用它,那么它將輸出五個整數,每個整數都有 50% 的概率是 0,20% 為 1,30% 為 2。在我們的情況下,我們只需要一個整數來表示要采取的行動。由于輸出張量(output)僅包含向左的概率,所以我們必須首先將 1 - output 連接它,以得到包含左和右動作的概率的張量。請注意,如果有兩個以上的可能動作,神經網絡將不得不輸出每個動作的概率,這時你就不需要連接步驟了。
好了,現在我們有一個可以觀察和輸出動作的神經網絡了,那我們怎么訓練它呢?
評價行為:信用分配問題
如果我們知道每一步的最佳動作,我們可以像通常一樣訓練神經網絡,通過最小化估計概率和目標概率之間的交叉熵。這只是通常的監督學習。然而,在強化學習中,智能體獲得的指導的唯一途徑是通過獎勵,獎勵通常是稀疏的和延遲的。例如,如果智能體在 100 個步驟內設法平衡桿,它怎么知道它采取的 100 個行動中的哪一個是好的,哪些是壞的?它所知道的是,在最后一次行動之后,桿子墜落了,但最后一次行動肯定不是完全負責的。這被稱為信用分配問題:當智能體得到獎勵時,很難知道哪些行為應該被信任(或責備)。想想一只狗在行為良好后幾小時就會得到獎勵,它會明白它得到了什么回報嗎?
為了解決這個問題,一個通常的策略是基于這個動作后得分的總和來評估這個個動作,通常在每個步驟中應用衰減率r
。例如(見圖 16-6),如果一個智能體決定連續三次向右,在第一步之后得到 +10 獎勵,第二步后得到 0,最后在第三步之后得到 -50,然后假設我們使用衰減率r=0.8
,那么第一個動作將得到10 +r×0 + r2×(-50)=-22
的分述。如果衰減率接近 0,那么與即時獎勵相比,未來的獎勵不會有多大意義。相反,如果衰減率接近 1,那么對未來的獎勵幾乎等于即時回報。典型的衰減率通常為是 0.95 或 0.99。如果衰減率為 0.95,那么未來 13 步的獎勵大約是即時獎勵的一半(0.9513×0.5
),而當衰減率為 0.99,未來 69 步的獎勵是即時獎勵的一半。在 CartPole 環境下,行為具有相當短期的影響,因此選擇 0.95 的折扣率是合理的。
當然,一個好的動作可能會伴隨著一些壞的動作,這些動作會導致平衡桿迅速下降,從而導致一個好的動作得到一個低分數(類似的,一個好行動者有時會在一部爛片中扮演主角)。然而,如果我們花足夠多的時間來訓練游戲,平均下來好的行為會得到比壞的更好的分數。因此,為了獲得相當可靠的動作分數,我們必須運行很多次并將所有動作分數歸一化(通過減去平均值并除以標準偏差)。之后,我們可以合理地假設消極得分的行為是壞的,而積極得分的行為是好的。現在我們有一個方法來評估每一個動作,我們已經準備好使用策略梯度來訓練我們的第一個智能體。讓我們看看如何。
策略梯度
正如前面所討論的,PG 算法通過遵循更高回報的梯度來優化策略參數。一種流行的 PG 算法,稱為增強算法,在 1929 由 Ronald Williams 提出。這是一個常見的變體:
首先,讓神經網絡策略玩幾次游戲,并在每一步計算梯度,這使得智能體更可能選擇行為,但不應用這些梯度。
運行幾次后,計算每個動作的得分(使用前面段落中描述的方法)。
如果一個動作的分數是正的,這意味著動作是好的,可應用較早計算的梯度,以便將來有更大的的概率選擇這個動作。但是,如果分數是負的,這意味著動作是壞的,要應用負梯度來使得這個動作在將來采取的可能性更低。我們的方法就是簡單地將每個梯度向量乘以相應的動作得分。
最后,計算所有得到的梯度向量的平均值,并使用它來執行梯度下降步驟。
讓我們使用 TensorFlow 實現這個算法。我們將訓練我們早先建立的神經網絡策略,讓它學會平衡車上的平衡桿。讓我們從完成之前編碼的構造階段開始,添加目標概率、代價函數和訓練操作。因為我們的意愿是選擇的動作是最好的動作,如果選擇的動作是動作 0(左),則目標概率必須為 1,如果選擇動作 1(右)則目標概率為 0:
y = 1. - tf.to_float(action)
現在我們有一個目標概率,我們可以定義損失函數(交叉熵)并計算梯度:
learning_rate = 0.01
cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits( labels=y, logits=logits)
optimizer = tf.train.AdamOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(cross_entropy)
注意,我們正在調用優化器的compute_gradients()
方法,而不是minimize()
方法。這是因為我們想要在使用它們之前調整梯度。compute_gradients()
方法返回梯度向量/變量對的列表(每個可訓練變量一對)。讓我們把所有的梯度放在一個列表中,以便方便地獲得它們的值:
gradients = [grad for grad, variable in grads_and_vars]
好,現在是棘手的部分。在執行階段,算法將運行策略,并在每個步驟中評估這些梯度張量并存儲它們的值。在多次運行之后,它如先前所解釋的調整這些梯度(即,通過動作分數乘以它們并使它們歸一化),并計算調整后的梯度的平均值。接下來,需要將結果梯度反饋到優化器,以便它可以執行優化步驟。這意味著對于每一個梯度向量我們需要一個占位符。此外,我們必須創建操作去應用更新的梯度。為此,我們將調用優化器的apply_gradients()
函數,該函數接受梯度向量/變量對的列表。我們不給它原始的梯度向量,而是給它一個包含更新梯度的列表(即,通過占位符遞送的梯度):
gradient_placeholders = []
grads_and_vars_feed = []
for grad, variable in grads_and_vars:
gradient_placeholder = tf.placeholder(tf.float32, shape=grad.get_shape())
gradient_placeholders.append(gradient_placeholder)
grads_and_vars_feed.append((gradient_placeholder, variable))
training_op = optimizer.apply_gradients(grads_and_vars_feed)
讓我們后退一步,看看整個運行過程:
n_inputs = 4
n_hidden = 4
n_outputs = 1
initializer = tf.contrib.layers.variance_scaling_initializer()
learning_rate = 0.01
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
hidden = fully_connected(X, n_hidden, activation_fn=tf.nn.elu,weights_initializer=initializer)
logits = fully_connected(hidden, n_outputs, activation_fn=None, weights_initializer=initializer)
outputs = tf.nn.sigmoid(logits)
p_left_and_right = tf.concat(axis=1, values=[outputs, 1 - outputs])
action = tf.multinomial(tf.log(p_left_and_right), num_samples=1)
y = 1. - tf.to_float(action)
cross_entropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=logits)
optimizer = tf.train.AdamOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(cross_entropy)
gradients = [grad for grad, variable in grads_and_vars]
gradient_placeholders = []
grads_and_vars_feed = []
for grad, variable in grads_and_vars:
gradient_placeholder = tf.placeholder(tf.float32, shape=grad.get_shape()) gradient_placeholders.append(gradient_placeholder)
grads_and_vars_feed.append((gradient_placeholder, variable))
training_op = optimizer.apply_gradients(grads_and_vars_feed)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
到執行階段了!我們將需要兩個函數來計算總折扣獎勵,給予原始獎勵,以及歸一化多次循環的結果:
def discount_rewards(rewards, discount_rate):
discounted_rewards = np.empty(len(rewards))
cumulative_rewards = 0
for step in reversed(range(len(rewards))):
cumulative_rewards = rewards[step] + cumulative_rewards * discount_rate discounted_rewards[step] = cumulative_rewards
return discounted_rewards
def discount_and_normalize_rewards(all_rewards, discount_rate):
all_discounted_rewards = [discount_rewards(rewards) for rewards in all_rewards]
flat_rewards = np.concatenate(all_discounted_rewards)
reward_mean = flat_rewards.mean()
reward_std = flat_rewards.std()
return [(discounted_rewards - reward_mean)/reward_std for discounted_rewards in all_discounted_rewards]
讓我們檢查一下運行的如何:
>>> discount_rewards([10, 0, -50], discount_rate=0.8)
array([-22., -40., -50.])
>>> discount_and_normalize_rewards([[10, 0, -50], [10, 20]], discount_rate=0.8)
[array([-0.28435071, -0.86597718, -1.18910299]), array([ 1.26665318, 1.0727777 ])]
對discount_rewards()
的調用正好返回我們所期望的(見圖 16-6)。你也可以驗證函數iscount_and_normalize_rewards()
確實返回了兩個步驟中每個動作的標準化分數。注意第一步比第二步差很多,所以它的歸一化分數都是負的;從第一步開始的所有動作都會被認為是壞的,反之,第二步的所有動作都會被認為是好的。
我們現在有了訓練策略所需的一切:
n_iterations = 250 # 訓練迭代次數
n_max_steps = 1000 # 每一次的最大步長
n_games_per_update = 10 # 每迭代十次訓練一次策略網絡
save_iterations = 10 # 每十次迭代保存模型
discount_rate = 0.95
with tf.Session() as sess:
init.run()
for iteration in range(n_iterations):
all_rewards = [] #每一次的所有獎勵
all_gradients = [] #每一次的所有梯度
for game in range(n_games_per_update):
current_rewards = [] #當前步的所有獎勵
current_gradients = [] #當前步的所有梯度
obs = env.reset()
for step in range(n_max_steps):
action_val, gradients_val = sess.run([action, gradients],
feed_dict={X: obs.reshape(1, n_inputs)}) # 一個obs
obs, reward, done, info = env.step(action_val[0][0]) current_rewards.append(reward)
current_gradients.append(gradients_val)
if done:
break
all_rewards.append(current_rewards)
all_gradients.append(current_gradients)
# 此時我們每10次運行一次策略,我們已經準備好使用之前描述的算法去更新策略,注:即使用迭代10次的結果來優化當前的策略。
all_rewards = discount_and_normalize_rewards(all_rewards)
feed_dict = {}
for var_index, grad_placeholder in enumerate(gradient_placeholders):
# 將梯度與行為分數相乘,并計算平均值
mean_gradients = np.mean([reward * all_gradients[game_index][step][var_index] for game_index, rewards in enumerate(all_rewards) for step, reward in enumerate(rewards)],axis=0)
feed_dict[grad_placeholder] = mean_gradients
sess.run(training_op, feed_dict=feed_dict)
if iteration % save_iterations == 0:
saver.save(sess, "./my_policy_net_pg.ckpt")
每一次訓練迭代都是通過運行10次的策略開始的(每次最多 1000 步,以避免永遠運行)。在每一步,我們也計算梯度,假設選擇的行動是最好的。在運行了這 10 次之后,我們使用discount_and_normalize_rewards()
函數計算動作得分;我們遍歷每個可訓練變量,在所有次數和所有步驟中,通過其相應的動作分數來乘以每個梯度向量;并且我們計算結果的平均值。最后,我們運行訓練操作,給它提供平均梯度(對每個可訓練變量提供一個)。我們繼續每 10 個訓練次數保存一次模型。
我們做完了!這段代碼將訓練神經網絡策略,它將成功地學會平衡車上的平衡桿(你可以在 Juyter notebook 上試用)。注意,實際上有兩種方法可以讓玩家游戲結束:要么平衡可以傾斜太大,要么車完全脫離屏幕。在 250 次訓練迭代中,策略學會平衡極點,但在避免脫離屏幕方面還不夠好。額外數百次的訓練迭代可以解決這一問題。
研究人員試圖找到一種即使當智能體最初對環境一無所知時也能很好地工作的算法。然而,除非你正在寫論文,否則你應該盡可能多地將先前的知識注入到智能體中,因為它會極大地加速訓練。例如,你可以添加與屏幕中心距離和極點角度成正比的負獎勵。此外,如果你已經有一個相當好的策略,你可以訓練神經網絡模仿它,然后使用策略梯度來改進它。
盡管它相對簡單,但是該算法是非常強大的。你可以用它來解決更難的問題,而不僅僅是平衡一輛手推車上的平衡桿。事實上,AlgPaGo 是基于類似的 PG 算法(加上蒙特卡羅樹搜索,這超出了本書的范圍)。
現在我們來看看另一個流行的算法。與 PG 算法直接嘗試優化策略以增加獎勵相反,我們現在看的算法是間接的:智能體學習去估計每個狀態的未來衰減獎勵的期望總和,或者在每個狀態中的每個行為未來衰減獎勵的期望和。然后,使用這些知識來決定如何行動。為了理解這些算法,我們必須首先介紹馬爾可夫決策過程(MDP)。
(第一部分 機器學習基礎)
第01章 機器學習概覽
第02章 一個完整的機器學習項目(上)
第02章 一個完整的機器學習項目(下)
第03章 分類
第04章 訓練模型
第05章 支持向量機
第06章 決策樹
第07章 集成學習和隨機森林
第08章 降維
(第二部分 神經網絡和深度學習)
第9章 啟動和運行TensorFlow
第10章 人工神經網絡
第11章 訓練深度神經網絡(上)
第11章 訓練深度神經網絡(下)
第12章 設備和服務器上的分布式 TensorFlow
第13章 卷積神經網絡
第14章 循環神經網絡
第15章 自編碼器
第16章 強化學習(上)
第16章 強化學習(下)