強化學習入門(四)策略梯度方法 Policy Gradient 求解強化學習問題
本文內容源自百度強化學習 7 日入門課程學習整理
感謝百度 PARL 團隊李科澆老師的課程講解
1.1 基本概念
1.2 區別
二、Policy Gradient 演算法
2.1 隨機策略中的 softmax 函數
2.2 一局遊戲 episode
2.3 軌跡的期望回報
2.4 優化策略函數
三、取樣方式:REINFORCE
3.1 蒙特卡洛 MC 與 時序差分 TD
3.2 REINFORCE 演算法
四、數據處理技巧
4.1 簡單場景的圖片預處理
4.2 使用衰減 reward 並 normalize reward
五、Policy Gradient 程式碼詳解
一、回顧 Value-based 和 Policy-based
1.1 基本概念
-
基於價值
- 優化策略價值函數,即 Q 的函數
- 優化好了以後,直接選取最優路徑即可
- 如:Sarsa,Q-learning,DQN
-
基於策略
- 不依賴於價值函數
- 一個策略走到底,看最後的總收益來決定該動作的好壞
- 如:Policy Gradient
1.2 區別
區別點一:
- Value-based:神經網路輸入狀態 S,輸出 Q,優化的也是 Q 函數
- 動作的輸出要看最大的 Q 值,所以是間接輸出動作
- Policy-based:輸入狀態 S,輸出動作 A
區別點二:
-
Value-based:先優化 Q 函數
- 優化到最優的時候,Q 表固定
- 根據 Q 表得到最優動作
- 所以動作選擇是固定的,即確定性策略
-
Policy-based:輸出動作的概率
- 這裡我們用 π_θ(a_t|s_t) 表示 policy 策略,θ 是神經網路的參數,在 s_t 狀態下,做出 a_t 動作的概率
- 所以如果我們在一個狀態下只有 3 個動作,則可以表示為:
π(a_1|s,θ) = p(a_1,s)=0.3 \\ π(a_2|s,θ) = p(a_2,s)=0.5 \\ π(a_3|s,θ) = p(a_3,s)=0.2- 這裡 3 個動作的概率相加為 1
- 輸出動作的時候,是根據概率進行隨機取樣,即概率越高採用這個動作的可能性越高(並不是一定採用概率最高的動作)
- 舉例:這種隨機策略適合於 「剪刀石頭布」 這樣隨機性很大的遊戲(DQN就不行,因為是確定性策略),最後優化可能 3 種動作都是 33.33% 的概率
- 為了輸出概率,那自然神經網路的輸出層用的是 「softmax」 激活函數
二、Policy Gradient 演算法
2.1 隨機策略中的 softmax 函數
softmax 的作用,就是把輸出映射到 0~1 的區間內,並使得所有的輸出相加等於 1,於是就可以等同於不同選擇的概率了
- 常常用於分類任務
計算方法:
- 每個輸出值求一個自然對數
- 然後除以所有輸出的自然對數的和
例如:在打乒乓球遊戲中
- 輸入的是遊戲影像(像素矩陣)
- 輸出的是動作選擇概率(向上 88%,向下 12%,停留不動 0%),向量形式:[0.88, 0.12, 0]
- 然後根據概率隨機挑選動作
2.2 一局遊戲 episode
當我們選擇一個動作以後,其實並不知道動作的優劣,而只有最終遊戲結束得到結果的時候,我們才能反推之前的動作優劣
- 每一個 episode 中,agent 不斷和環境交互,輸出動作,直到該 episode 結束
- 然後開啟另一個 episode
優化策略的目的:讓 「每一個」 episode 的 「總的」 reward 儘可能大
- 單個 episode 有很多 step 組成,每個 step 會獲得 reward
- 所有 episode 總的 reward 希望最大
- 所以怎麼去量化我的優化目標就是個難點!
2.3 軌跡的期望回報
軌跡 Trajectory :
-
從初始狀態出發,有不同的概率選擇動作
-
然後狀態發生變化(環境的隨機性,會導致環境的變化也是個概率分布,即狀態轉移概率 p(s'|s,a))
-
在新的狀態下,再通過不同概率選擇動作
-
狀態繼續發生變化
- 這裡我們能夠優化的是智慧體的選擇,而環境的隨機變化(狀態轉移概率)是客觀存在的,無法優化(控制)
-
不斷地交互,直到完成一個 episode(一局遊戲結束)
-
把這個 episode 中所有的連續的 s 變化 和 a 選擇串起來,就是一個 episode 的軌跡 Trajectory
-
軌跡:τ\ =\ \{s_1,a_1,s_2,a_2,...s_T,a_T\}
軌跡的概率:
- 軌跡的每一步概率連乘即可:
- p_θ(τ)\ = \ p(s_1)π_θ(a_1|s_1)p(s_2|s_1,a_1)π_θ(a_1|s_1)p(s_2|s_1,a_1)...
軌跡的總 reward:
- 即每一步獲得的 reward 之和
- R(τ)\ = \ \sum_{t=1}^Tr_t
我們和環境交互的軌跡可以有千千萬萬條,所以當我們跑了很多 episode ,獲得許多軌跡後,我們可以獲得 「期望回報」:
-
π_θ(a|s) 的期望回報:所有 episode 的平均回報
-
\overline{R_θ}\ =\ \sum_{τ}R(τ)p_θ(τ)
-
策略 π_θ(a|s) 下的期望回報就可以用來評價我們的策略優劣
難點:
- 無法窮舉所有的軌跡
- 無法獲得 「環境轉移概率」
解決方法:取近似值
- \overline{R_θ}\ =\ \sum_{τ}R(τ)p_θ(τ)\ \approx\ {1\over N}\sum_{n=1}^NR(τ)
- 這裡我們取得 N 條軌跡後(N足夠大),假設每條軌跡都是概率相等(隨機)
- 這個過程稱作:取樣(取樣 N 個 episode 來計算期望回報)
- 這樣就不需要知道 「環境轉移概率」 了
2.4 優化策略函數
我們可以用期望回報 \overline{R_θ} 來優化策略函數 π_θ(s,a)
-
在 DQN 中,我們的 loss 函數是來源於 目標 Q 和 預測 Q 之間的差別,我們希望優化過程是 預測 Q 不斷逼近 目標 Q,降低 loss(越小越好)
-
所以在 DQN 中,目標 Q 擔任的是一個正確的 label 指導
-
但是在 Policy 網路中,沒有這樣一個 正確的 label 指導
-
所以需要用到 \overline{R_θ}
優化目標:
- J(θ)\ =\ \overline{R_θ} 越大越好
- 這裡是一個梯度上升過程
策略梯度:
- 我們需要求解 \overline{R_θ} 對與 θ 的梯度,用這個梯度去更新網路
- 首先需要產生 N 條 episode 軌跡
- 每一條軌跡獲有一個總回報 R(τ)
- 獲得期望回報 \overline{R_θ}
- 然後求 \overline{R_θ} 對與 θ 的導數
- \triangledown \overline{R_θ}\ \approx\ {1\over N}\sum_{n=1}^N\sum_{t=1}^{T_n}R(τ^n)\triangledown \logπ_θ(a_t^n|s_t^n)
- 求導的過程中,可以約去不可知的 「環境轉移概率」
- 更新網路後,讓分數高的軌跡概率可以更大
- 所以 loss 的公式前面要加上負號,這樣就可以讓梯度下降變成梯度上升
- Loss = -R(τ)\log π_θ(a_t|s_t)
策略梯度公式推導過程:
三、取樣方式:REINFORCE
3.1 蒙特卡洛 MC 與 時序差分 TD
梯度策略分為 蒙特卡洛 MC 與 時序差分 TD
- 蒙特卡洛:演算法完成一個 episode,進行學習一次 learn()
- 完整運行一個 episode,我們可以知道每一步 step 的未來總收益 G_t
- 比如 :REINFORCE 演算法(最簡單,經典)
- 時序差分:
- 每一個 step 都更新一次,更新頻率更高
- 使用 Q 函數來近似表示未來總收益
- 比如:Actor-Critic 演算法
3.2 REINFORCE 演算法
流程:
- 首先拿到每一步的收益 [r_1, r_2,r_3...r_T]
- 那自然可以計算出每一步的未來總收益 [G_1,G_2,G_3...G_T]
- 未來總收益,代表的是每一個動作的真正價值
- 這樣就可以把每一個 G_t 代入公式 \triangledown \overline{R_θ}\ \approx\ {1\over N}\sum_{n=1}^N\sum_{t=1}^{T_n}G_t^n\triangledown \logπ_θ(a_t^n|s_t^n) 去優化每一個 action 的輸出
def calc_reward_to_go(reward_list, gamma=1.0):
for i in range(len(reward_list) - 2, -1, -1):
# G_t = r_t + γ·r_t+1 + ... = r_t + γ·G_t+1
reward_list[i] += gamma * reward_list[i + 1] # Gt
return np.array(reward_list)
這裡的程式碼就是把每一步的收益,轉成每一步的未來總收益
連續的 step 之間未來總收益有相關性:G_t\ = \ \sum_{t=k+1}^Tγ^{k-t-1}r_t\ =\ r_t\ +\ γG_{t+1}
所以在程式碼實現上,是從後往前計算,先計算 G_T,再計算 G_{T-1},依次進行
假設實際執行的動作 a_t 是 [0,1,0],計算得到的概率 π(a|s,θ) 是 [0.2,0.5,0.3],對每一個 action:Loss = -G_t\ ·\ [0,1,0]\ ·\ \log[0.2,0.5,0.3]
類比監督學習:
- 輸出的概率分布要儘可能貼近真實的情況
- 比如手寫數字識別中,如果一個數字是 8,那網路預測這個數字概率越高越好,比如 0.999 ,真實值 8 對應的是 1
- 通過迭代更新,希望識別 8 的時候,這個概率可以遠高於其他數字的概率
- 這裡我們使用交叉熵 Cross Entropy表示兩個概率分布之間的差別
- 目標是縮小差距,即把 Loss 傳入優化器自動優化
Policy Gradient 中:
- 輸出預測行動的概率,和真實採用的概率做比較
- 真實採用的動作是隨機選擇的 action,並不代表正確的 action
- 所以前面要乘以一個累計回報 G_t 作為對真實所採用的 action 的評價
- G_t 越大,說明當前輸出的 action 是優質的,我們就越是希望預測概率向實際動作逼近
- G_t 越小,說明當前輸出的 action 不好,所以 loss 的權重也更小,即不強求預測概率向該動作逼近
- Loss = -R(P'_{left}\ * \ \log (P_{left})\ +\ P'_{still}\ *\ log(P_{still})\ +\ P'_{right}\ *\ \log(P_{right}))
- 其中 R=G_t,P'代表實際執行的動作 a_t(是 one-hot 向量,比如 [0,1,0] ),P 代表神經網路預測的動作概率(比如 [0.5, 0.3, 0.2] )
- 由於我們可以拿到 episode 的整個軌跡,所以可以對這個 episode 中每一個 action 都計算一個 Loss
- 累加所有的 Loss,然後讓優化器去優化
def learn(self, obs, action, reward): # 輸入的就是獲得的軌跡,reward代表未來總收益
""" 用policy gradient 演算法更新policy model
"""
act_prob = self.model(obs) # 獲取輸出動作概率(由神經網路預測得到)
# log_prob = layers.cross_entropy(act_prob, action) # 交叉熵
log_prob = layers.reduce_sum(
-1.0 * layers.log(act_prob) * layers.one_hot(
action, act_prob.shape[1]),
dim=1)
cost = log_prob * reward # 乘的是當前 action 的未來總收益
cost = layers.reduce_mean(cost) # 所有 step 要取均值
optimizer = fluid.optimizer.Adam(self.lr)
optimizer.minimize(cost)
return cost
REINFORCE 流程圖:
- 首先要有一個 Policy Model 來輸出動作概率
- 用 sample() 函數得到具體的動作
- 動作和環境交互,得到 reward
- 一個 episode 運行完,得到軌跡數據(包含 3 個 list:S,a,G)
- 執行 learn() 函數,用軌跡數據構造 loss 函數(該部分可以進行抽取封裝)
- loss 函數放入優化器進行優化
PARL 中的 Policy Gradient 演算法:
分成了 model,algorithm,agent 三個類
- model 定義網路結構
- algorithm 預測動作輸出,構造 loss 函數,實現 learn() 函數
- agent 實現執行動作,和環境交互,獲取環境數據
四、數據處理技巧
4.1 簡單場景的圖片預處理
- 把圖片轉為灰度
- resize 到 80 * 80 * 1 的形狀
- 最後把圖片轉為 1 維向量
比如在 Pong 的乒乓球遊戲環境中:
# Pong 圖片預處理
def preprocess(image):
""" 預處理 210x160x3 uint8 frame into 6400 (80x80) 1維 float vector """
image = image[35:195] # 裁剪
image = image[::2,::2,0] # 下取樣,縮放2倍
image[image == 144] = 0 # 擦除背景 (background type 1)
image[image == 109] = 0 # 擦除背景 (background type 2)
image[image != 0] = 1 # 轉為灰度圖,除了黑色外其他都是白色
return image.astype(np.float).ravel()
當然我們也可以用 CNN 網路,但是對於簡單影像的環境,其實也可以這樣處理,就不用 CNN 網路了
4.2 使用衰減 reward 並 normalize reward
因為有些遊戲一個 episode 的時間很長,比如乒乓球遊戲 Pong,一方拿到 21 分遊戲才結束,所以整個過程有非常多的 step
所以要設計一個衰減因子,不需要考慮太長時間以後的收益,一般會設置為 0.99
另外需要對一個 episode 拿到的收益做 normalize,讓我們獲取的收益有正有負,基本在原點兩側均衡分布
通常這種歸一化的做法是為了加速訓練,對於 action 的快速收斂更有效果
# 根據一個episode的每個step的reward列表,計算每一個Step的Gt
def calc_reward_to_go(reward_list, gamma=0.99):
"""calculate discounted reward"""
reward_arr = np.array(reward_list)
for i in range(len(reward_arr) - 2, -1, -1):
# G_t = r_t + γ·r_t+1 + ... = r_t + γ·G_t+1
reward_arr[i] += gamma * reward_arr[i + 1]
# normalize episode rewards
reward_arr -= np.mean(reward_arr)
reward_arr /= np.std(reward_arr)
return reward_arr
五、Policy Gradient 程式碼詳解
強化學習演算法 Policy Gradient 解決 CartPole 問題,程式碼逐條詳解
