在上一篇文章 強化學習 1 —— 一文讀懂馬爾科夫決策過程 MDP 介紹了馬爾科夫過程,本篇接着來介紹如何使用動態規划方法來求解。
動態規划的關鍵點有兩個:
- 一是問題的最優解可以由若干小問題的最優解構成,即通過尋找子問題的最優解來得到問題的最優解。
- 二是可以找到子問題狀態之間的遞推關系,通過較小的子問題狀態遞推出較大的子問題的狀態。
在上一篇中我們提到的狀態價值的貝爾曼方程:
從這個式子我們可以看出,我們可以定義出子問題求解每個狀態的狀態價值函數,同時這個式子又是一個遞推的式子, 意味着利用它,我們可以使用上一個迭代周期內的狀態價值來計算更新當前迭代周期某狀態 s 的狀態價價值。可見,強化學習的問題是滿足這兩個條件的。
可以把 Decision Making 分為兩個過程
-
Prediction (策略評估)
- 輸入: MDP\(<S, A, P, R, \gamma>\) 和 策略 $\pi $
- 輸出:value function \(V_\pi\)
-
Control (策略控制,即尋找最優策略)
- 輸入:MDP$<S, A, P, R, \gamma> $
- 輸出:最優的價值函數 \(V^*\) 和最優的策略 \(\pi^*\)
一、策略評估
首先,我們來看如何使用動態規划來求解強化學習的預測問題,即求解給定策略的狀態價值函數的問題。這個問題的求解過程我們通常叫做策略評估(Policy evaluation)
策略評估的基本思路是從任意一個狀態價值函數開始,依據給定的策略,結合貝爾曼期望方程、狀態轉移概率和獎勵同步迭代更新狀態價值函數,直至其收斂,得到該策略下最終的狀態價值函數。
假設我們在第 t 輪迭代已經計算出了所有的狀態的狀態價值 \(v_t(s')\) ,那么在第 t+1 輪我們可以利用第 t 輪計算出的狀態價值計算出第 t+1 輪的狀態價值 \(v_{t+1}(s)\) 。這是通過貝爾曼方程來完成的,即 Iteration on Bellman exception backup
1、策略評估實例
這是一個經典的Grid World的例子。我們有一個4x4的16宮格。只有左上和右下的格子是終止格子。該位置的價值固定為0,個體如果到達了該2個格子,則停止移動,此后每輪獎勵都是0。個體在16宮格其他格的每次移動,得到的即時獎勵\(R\)都是-1。注意個體每次只能移動一個格子,且只能上下左右4種移動選擇,不能斜着走, 如果在邊界格往外走,則會直接移動回到之前的邊界格。衰減因子我們定義為 \(\gamma=1\)。這里給定的策略是隨機策略,即每個格子里有25% 的概率向周圍的4個格子移動。
首先我們初始化所有格子的狀態價值為0,如上圖\(k=0\)的時候。現在我們開始策略迭代了。由於終止格子的價值固定為 0,我們可以不將其加入迭代過程。在 \(k=1\) 的時候,我們利用上面的貝爾曼方程先計算第二行第一個格子的價值:
第二行第二個格子的價值:
其他的格子都是類似的,第一輪的狀態價值迭代的結果如上圖 \(k=1\) 的時候。現在我們第一輪迭代完了。開始動態規划迭代第二輪了。還是看第二行第一個格子的價值:
第二行第二個格子的價值是:
最終得到的結果是上圖 \(k=2\) 的時候。第三輪的迭代如下:
最終得到的結果是上圖 \(k=3\) 的時候。就這樣一直迭代下去,直到每個格子的策略價值改變很小為止。這時我們就得到了所有格子的基於隨機策略的狀態價值。
二、策略控制
1、Policy Iteration
對於策略控制問題,一種可行的方法就是根據我們之前基於任意一個給定策略評估得到的狀態價值來及時調整我們的動作策略,這個方法我們叫做策略迭代 (Policy Iteration)。
如何調整呢?最簡單的方法就是貪婪法。考慮一種如下的貪婪策略:個體在某個狀態下選擇的行為是其能夠到達后續所有可能的狀態中狀態價值最大的那個狀態。如上面的圖右邊。當我們計算出最終的狀態價值后,我們發現,第二行第一個格子周圍的價值分別是0,-18,-20,此時我們用貪婪法,則我們調整行動策略為向狀態價值為0的方向移動,而不是隨機移動。也就是圖中箭頭向上。而此時第二行第二個格子周圍的價值分別是-14,-14,-20, -20。那么我們整行動策略為向狀態價值為-14的方向移動,也就是圖中的向左向上。
- 故 Policy Iteration 共分為兩個部分
- Evaluate the policy $ \pi $,(通過給定的 $ \pi $ 計算 V)
- Improve the policy $ \pi $,(通過貪心策略)
如果我們有 一個 策略 \(\pi\),我們可以用策略 估計出它的狀態價值函數 \(v_\pi(s)\), 然后根據策略改進提取出更好的策略 \(\pi'\),接着再計算 \(v_{\pi'}(s)\), 然后再獲得更好的 策略 \(\pi''\),直到相關滿足相關終止條件。

計算公式如下:
1、評估價值 (Evaluate)
2、改進策略(Improve)
每次迭代都是基於確定的策略,故策略 \(\pi\) 不再寫出,對應的加上了下標
可以把 \(q^{\pi_i}(s,a)\) 想象成一個表格,其中橫軸代表不同的狀態,縱軸代表每種狀態下不同的動作產生的價值,然后在當前狀態下選擇一個價值最大的行為價值作為當前的狀態價值。
Policy Iteration 的具體算法為:

Bellman Optimality Equation
利用狀態價值函數和動作價值函數之間的關系,得到
當到達 最優的時候,一個狀態的價值就等於在當前 狀態下最大的那個動作價值
把上面兩個式子結合起來有Bellman Optimality Equation
2、Value Iteration
觀察第三節的圖發現,我們如果用貪婪法調整動作策略,那么當 \(k=3\) 的時候,我們就已經得到了最優的動作策略。而不用一直迭代到狀態價值收斂才去調整策略。那么此時我們的策略迭代優化為價值迭代。
比如當 \(k=2\) 時,第二行第一個格子周圍的價值分別是0,-2,-2,此時我們用貪婪法,則我們調整行動策略為向狀態價值為0的方向移動,而不是隨機移動。也就是圖中箭頭向上。而此時第二行第二個格子周圍的價值分別是-1.7,-1.7,-2, -2。那么我們整行動策略為向狀態價值為-1.7的方向移動,也就是圖中的向左向上。
我們沒有等到狀態價值收斂才調整策略,而是隨着狀態價值的迭代及時調整策略, 這樣可以大大減少迭代次數。此時我們的狀態價值的更新方法也和策略迭代不同。現在的貝爾曼方程迭代式子如下:
然后直接提取最優策略 $ \pi $
Value Iteration 的算法為:

三、實例展示
下面是斯坦福大學的一個網頁,可以幫助更好的直觀的理解策略迭代和價值迭代兩種不同的迭代方式。開始時如下圖左上所示,其中每一個格子代表一個狀態,每個狀態有都有上下左右四個動作,每個狀態上都有一個數字,代表當前的狀態價值,其中有些狀態下半部分還有一個數字,代表進入當前狀態所能獲得的獎勵。我們的策略是四個方向的概率都相等,即各有0.25的概率。要做的就是找出每個狀態下的最優策略。
3.1 策略迭代
如圖右上所示,點擊\(Policy \; Evaluation\) 執行一次策略評估,可以看到有些狀態已經發生了變化,相應的狀態價值 \(V(s)\) 也已經更新,此時再點擊\(Policy \; Updata\) 來更新策略,如圖做下所示,可與i看到,有些狀態的策略已經發生了變化,已經在當前的狀態價值下提高了策略。如此反復迭代,最后的結果如圖右下所示,此時每個狀態都有最好的狀態價值和策略。
3.2 價值迭代
價值迭代是不停的迭代狀態價值 \(V\), 然后提取出相應的動作價值\(q\),然后從相應的 q 中尋找一個最大的最為當前狀態價值。點擊 \(Toggle\; Value \; Iteration\)等幾秒鍾就可以看到迭代 結果:

四、代碼理解
gym是 OpenAI 開放的一個開發、比較各種強化學習算法的工具庫,提供了不少內置的環境,是學習強化學習不錯的一個平台。我們本次使用其中提供給的一個最簡單的環境 FrozenLake-v0。如下如所示,開始時agent在 s 位置,G代表金子,我們要控制 agent 找到金子的同時獲得更多的獎勵,與上面例子一樣,agent在每個狀態下有四種動作(上下左右),F代表障礙,H代表洞,要避免調入洞中,具體描述可以訪問:http://gym.openai.com/envs/FrozenLake-v0/ 查看。
4.1、Policy Iteration
import numpy as np
import gym
def extract_policy(v, gamma = 1.0):
""" 從價值函數中提取策略 """
policy = np.zeros(env.env.nS)
for s in range(env.env.nS):
q_sa = np.zeros(env.env.nA)
for a in range(env.env.nA):
q_sa[a] = sum([p * (r + gamma * v[s_]) for p, s_, r, _ in env.env.P[s][a]])
policy[s] = np.argmax(q_sa)
return policy
def compute_policy_v(env, policy, gamma=1.0):
""" 計算價值函數 """
v = np.zeros(env.env.nS)
eps = 1e-10
while True:
prev_v = np.copy(v)
for s in range(env.env.nS):
policy_a = policy[s]
v[s] = sum([p * (r + gamma * prev_v[s_]) for p, s_, r, _ in
env.env.P[s][policy_a]])
if (np.sum((np.fabs(prev_v - v))) <= eps):
break
return v
def policy_iteration(env, gamma = 1.0):
""" Policy-Iteration algorithm """
# env.env.nS: 16
# env.env.nA: 4
# env.env.ncol / env.env.nrow: 4
policy = np.random.choice(env.env.nA, size=(env.env.nS))
max_iterations = 200000
gamma = 1.0
for i in range(max_iterations):
old_policy_v = compute_policy_v(env, policy, gamma)
new_policy = extract_policy(old_policy_v, gamma)
if (np.all(policy == new_policy)):
print ('Policy-Iteration converged at step %d.' %(i+1))
break
policy = new_policy
return policy
if __name__ == '__main__':
env_name = 'FrozenLake-v0'
env = gym.make(env_name)
optimal_policy = policy_iteration(env, gamma = 1.0)
print(optimal_policy)
# Policy-Iteration converged at step 6.
# [0. 3. 3. 3. 0. 0. 0. 0. 3. 1. 0. 0. 0. 2. 1. 0.]
4.2、Value Iteration
def extract_policy(v, gamma = 1.0):
""" 從狀態函數中提取策略 """
policy = np.zeros(env.env.nS)
for s in range(env.env.nS):
# q_sa: 在狀態 s 下的 所有動作價值
q_sa = np.zeros(env.action_space.n)
for a in range(env.action_space.n):
for next_sr in env.env.P[s][a]:
# next_sr is a tuple of (probability, next state, reward, done)
p, s_, r, _ = next_sr
q_sa[a] += (p * (r + gamma * v[s_]))
# print("q_sa: ", q_sa)
policy[s] = np.argmax(q_sa)
# print('np.argmax(q_sa): ', policy[s])
return policy
def value_iteration(env, gamma = 1.0):
""" Value-iteration algorithm """
# env.env.nS: 16
# env.env.nA: 4
# env.env.ncol / env.env.nrow: 4
v = np.zeros(env.env.nS)
max_iterations = 100000
eps = 1e-20
for i in range(max_iterations):
prev_v = np.copy(v)
for s in range(env.env.nS):
# env.env.P[s][a]]: 狀態 s 下動作 a 的概率
q_sa = [sum([p*(r + prev_v[s_]) for p, s_, r, _ in
env.env.P[s][a]]) for a in range(env.env.nA)]
v[s] = max(q_sa)
if (np.sum(np.fabs(prev_v - v)) <= eps):
print ('Value-iteration converged at iteration# %d.' %(i+1))
break
return v
if __name__ == '__main__':
env_name = 'FrozenLake-v0'
# env 中是 gym 提供的本次問題的環境信息
env = gym.make(env_name)
gamma = 1.0
optimal_v = value_iteration(env, gamma)
policy = extract_policy(optimal_v, gamma)
print('policy:', policy)
# Value-iteration converged at iteration# 1373.
# policy: [0. 3. 3. 3. 0. 0. 0. 0. 3. 1. 0. 0. 0. 2. 1. 0.]
五、總結
本篇博客介紹了如何使用動態規划來求解MDP問題,還介紹了兩種迭代算法。可以發現,對於這兩個算法,有一個前提條件是獎勵 R 和狀態轉移矩陣 P 我們是知道的,因此我們可以使用策略迭代和價值迭代算法。對於這種情況我們叫做 Model base
。同理可知,如果我們不知道環境中的獎勵和狀態轉移矩陣,我們叫做 Model free
。那對於 Model_free
情況下應該如何求解 MDP 問題呢?這就是下一篇文章要講的 蒙特卡洛(MC)采樣法。
參考資料:
2、博客園 劉建平Pinard