強化學習入門知識與經典專案分析1.3

靜候佳茵發表於2022-02-28

上一篇文章推導了貝爾曼方程,這一篇文章來繼續分享對應的馬爾可夫決策的案例,然後引入策略評估並證明其收斂性。
主要的學習資源是四個:

1. 貝爾曼方程的矩陣表示

對於上篇文章推導的貝爾曼方程

\[V_π(s) = \sum_aπ(a|s)[R_{s}^a + γ\sum_{s'}V_π(s')P_{ss'}^a] \]

為了便於運用矩陣,寫成如下:

\[V_π(s) =\sum_{a}π(a|s)·\sum_{s'}P_{ss'}^a·[R_{t+1}+γV_π(s')] \]

那麼判斷好維數就可以得出如下形式:

強化學習入門知識與經典專案分析1.3

可以抽象出公式:

\[V=Π P (R+γV) \]

其中V用於表示狀態值函式的向量,Π表示策略矩陣,P表示狀態轉移矩陣,R表示回報向量。通過求解可以得到:

\[V = (1-γΠP)^{-1}ΠPR \]

但矩陣表示起來比較困難,尤其狀態轉移矩陣P是三維的,我還沒有釐清這邊的關係,而且大部分資料都不選擇去詳細解釋這個部分,因為這種運算方法計算起來也比較困難,反而大家都採用迭代的方法。關於迭代的方法,我們下面的案例就會涉及到。該案例用python實現,由於其特殊的字典結構,形式上就可以不用陣列來構建矩陣,也沒有用上面推導的矩陣公式來求解狀態。

2. 學生馬爾科夫決策獎勵過程例項

繼續上一篇文章的例子,圖中的狀態數變成了 5 個,為了方便理解,我們把這五個狀態分別命名為:瀏覽手機中、第一節課、第二節課、第三節課、休息中;行為總數也是 5 個,但具體到某一狀態則只有 2 個可能的行為,這 5 個行為分別命名為:瀏覽手機、學習、離開瀏覽、泡吧、退出學習。與馬爾科夫獎勵過程不同,馬爾科夫決策過程的狀態轉移概率與獎勵函式均與行為相關。本例中多數狀態下某行為將以 100% 的概率到達一個後續狀態,但在狀態 第三節課 選擇 泡吧 行為除外。在對該馬爾科夫決策過程進行建模時,我們將使用字典這個資料結構來存放這些概率和獎勵。

強化學習入門知識與經典專案分析1.3

2.1 構建輔助函式

葉強老師先寫了utils.py作為輔助程式,如果只關注函式的用法,可以直接略過這部分的程式碼。

# 輔助函式
def str_key(*args):
    '''將引數用"_"連線起來作為字典的鍵,需注意引數本身可能會是tuple或者list型,
    比如類似((a,b,c),d)的形式。
    '''
    new_arg = []
    for arg in args:
        if type(arg) in [tuple, list]:
            new_arg += [str(i) for i in arg]
        else:
            new_arg.append(str(arg))
    return "_".join(new_arg)

def set_dict(target_dict, value, *args):
    target_dict[str_key(*args)] = value
    
def set_prob(P, s, a, s1, p = 1.0): # 設定概率字典
    set_dict(P, p, s, a, s1)

def get_prob(P, s, a, s1): # 獲取概率值
    return P.get(str_key(s,a,s1), 0)
    
def set_reward(R, s, a, r): # 設定獎勵字典
    set_dict(R, r, s, a)

def get_reward(R, s, a): # 獲取獎勵值
    return R.get(str_key(s,a), 0)

def display_dict(target_dict): # 顯示字典內容
    for key in target_dict.keys():
        print("{}: {:.2f}".format(key, target_dict[key]))
    print("")  
# 輔助方法
def set_value(V, s, v): # 設定價值字典
    set_dict(V, v, s)
    
def get_value(V, s): # 獲取價值值
    return V.get(str_key(s), 0)

def set_pi(Pi, s, a, p = 0.5): # 設定策略字典
    set_dict(Pi, p, s, a)
    
def get_pi(Pi, s, a): # 獲取策略(概率)值
    return Pi.get(str_key(s,a), 0)

2.2 構建MDP模型

MDP就是五元組(S,A,R,P,γ),整體用元組作為資料結構使用。狀態集S和動作集A就用列表定義,狀態轉移概率矩陣和獎勵矩陣就用字典儲存。具體引數就參考上面的圖中的數值。

from utils import str_key, display_dict
from utils import set_prob, set_reward, get_prob, get_reward
from utils import set_value, set_pi, get_value, get_pi

# 構建學生馬爾科夫決策過程
S = ['瀏覽手機中','第一節課','第二節課','第三節課','休息中']
A = ['瀏覽手機','學習','離開瀏覽','泡吧','退出學習']
R = {} # 獎勵Rsa
P = {} # 狀態轉移概率Pss'a
gamma = 1.0 # 衰減因子

# 根據學生馬爾科夫決策過程示例的資料設定狀態轉移概率和獎勵,預設概率為1
set_prob(P, S[0], A[0], S[0]) # 瀏覽手機中 - 瀏覽手機 -> 瀏覽手機中
set_prob(P, S[0], A[2], S[1]) # 瀏覽手機中 - 離開瀏覽 -> 第一節課
set_prob(P, S[1], A[0], S[0]) # 第一節課 - 瀏覽手機 -> 瀏覽手機中
set_prob(P, S[1], A[1], S[2]) # 第一節課 - 學習 -> 第二節課
set_prob(P, S[2], A[1], S[3]) # 第二節課 - 學習 -> 第三節課
set_prob(P, S[2], A[4], S[4]) # 第二節課 - 退出學習 -> 休息中
set_prob(P, S[3], A[1], S[4]) # 第三節課 - 學習 -> 休息中
set_prob(P, S[3], A[3], S[1], p = 0.2) # 第三節課 - 泡吧 -> 第一節課
set_prob(P, S[3], A[3], S[2], p = 0.4) # 第三節課 - 泡吧 -> 第二節課
set_prob(P, S[3], A[3], S[3], p = 0.4) # 第三節課 - 泡吧 -> 第三節課

set_reward(R, S[0], A[0], -1) # 瀏覽手機中 - 瀏覽手機 -> -1
set_reward(R, S[0], A[2],  0) # 瀏覽手機中 - 離開瀏覽 -> 0
set_reward(R, S[1], A[0], -1) # 第一節課 - 瀏覽手機 -> -1
set_reward(R, S[1], A[1], -2) # 第一節課 - 學習 -> -2
set_reward(R, S[2], A[1], -2) # 第二節課 - 學習 -> -2
set_reward(R, S[2], A[4],  0) # 第二節課 - 退出學習 -> 0
set_reward(R, S[3], A[1], 10) # 第三節課 - 學習 -> 10
set_reward(R, S[3], A[3], +1) # 第三節課 - 泡吧 -> -1

MDP = (S, A, R, P, gamma)

至此,描述學生馬爾科夫決策過程的模型就建立好了。當該 MDP 構建好之後,我們可以呼叫顯示字典的方法來檢視我們的設定是否正確:

print("----狀態轉移概率字典(矩陣)資訊:----")
display_dict(P)
print("----獎勵字典(函式)資訊:----")
display_dict(R)
# ----狀態轉移概率字典(矩陣)資訊:----
# 瀏覽手機中_瀏覽手機_瀏覽手機中: 1.00
# 瀏覽手機中_離開瀏覽_第一節課: 1.00
# 第一節課_瀏覽手機_瀏覽手機中: 1.00
# 第一節課_學習_第二節課: 1.00
# 第二節課_學習_第三節課: 1.00
# 第二節課_退出學習_休息中: 1.00
# 第三節課_學習_休息中: 1.00
# 第三節課_泡吧_第一節課: 0.20
# 第三節課_泡吧_第二節課: 0.40
# 第三節課_泡吧_第三節課: 0.40

# ----獎勵字典(函式)資訊:----
# 瀏覽手機中_瀏覽手機: -1.00
# 瀏覽手機中_離開瀏覽: 0.00
# 第一節課_瀏覽手機: -1.00
# 第一節課_學習: -2.00
# 第二節課_學習: -2.00
# 第二節課_退出學習: 0.00
# 第三節課_學習: 10.00
# 第三節課_泡吧: 1.00

2.3 初始化策略與價值

雖然已經描述好了MDP模型,但要體現強化學習的思想,還要初始化每個狀態的價值(開始為0),並且引入指定一個策略π,暫時先採用隨機策略。
初始條件下所有狀態的價值均設為 0。對於每一個狀態只有兩種可能性為的該學生馬爾科夫決策過程來說,每個可選行為的概率均為 0.5。

# S = ['瀏覽手機中','第一節課','第二節課','第三節課','休息中']
# A = ['瀏覽手機','學習','離開瀏覽','泡吧','退出學習']
# 設定行為策略:pi(a|.) = 0.5
Pi = {}
set_pi(Pi, S[0], A[0], 0.5) # 瀏覽手機中 - 瀏覽手機
set_pi(Pi, S[0], A[2], 0.5) # 瀏覽手機中 - 離開瀏覽
set_pi(Pi, S[1], A[0], 0.5) # 第一節課 - 瀏覽手機
set_pi(Pi, S[1], A[1], 0.5) # 第一節課 - 學習
set_pi(Pi, S[2], A[1], 0.5) # 第二節課 - 學習
set_pi(Pi, S[2], A[4], 0.5) # 第二節課 - 退出學習
set_pi(Pi, S[3], A[1], 0.5) # 第三節課 - 學習
set_pi(Pi, S[3], A[3], 0.5) # 第三節課 - 泡吧

print("----策略字典(矩陣)資訊:----")
display_dict(Pi)
# 初始時價值為空,訪問時會返回0
print("----狀態價值字典(矩陣)資訊:----")
V = {}
display_dict(V)
# ----策略字典(矩陣)資訊:----
# 瀏覽手機中_瀏覽手機: 0.50
# 瀏覽手機中_離開瀏覽: 0.50
# 第一節課_瀏覽手機: 0.50
# 第一節課_學習: 0.50
# 第二節課_學習: 0.50
# 第二節課_退出學習: 0.50
# 第三節課_學習: 0.50
# 第三節課_泡吧: 0.50

# ----狀態價值字典(矩陣)資訊:----

2.4 貝爾曼公式的函式實現

上一篇文章得到的貝爾曼公式是由下面兩個子公式實現的。

\[q_π(s,a)=R_s^a+γ\sum_{s'\in S}V_{π}(s')P_{ss'}^a \]

\[V_π(s) = \sum_{a\in A}π(a|s)q_π(s,a) \]

def compute_q(MDP, V, s, a):
    '''根據給定的MDP,價值函式V,計算狀態行為對s,a的價值qsa
    '''
    S, A, R, P, gamma = MDP
    q_sa = 0
    for s_prime in S:
        q_sa += get_prob(P, s,a,s_prime) * get_value(V, s_prime)
    q_sa = get_reward(R, s,a) + gamma * q_sa
    return q_sa


def compute_v(MDP, V, Pi, s):
    '''給定MDP下依據某一策略Pi和當前狀態價值函式V計算某狀態s的價值
    '''
    S, A, R, P, gamma = MDP
    v_s = 0
    for a in A:
        v_s += get_pi(Pi, s,a) * compute_q(MDP, V, s, a)
    return v_s
        

3. 迭代法(策略評估)

認真看的同學也發現了,前面的過程中每個狀態的價值還是初始化的狀態,狀態價值字典(矩陣) 為空。我們在第一節就提到了迭代法可以代替矩陣公式求解最終穩定的狀態的價值。接下來我們就正式介紹迭代法,或者稱為策略評估。

策略評估指計算給定策略下求解狀態價值函式的過程。對策略評估,我們可以使用如下方法:從任意一個狀態價值函式開始,依據給定的策略,結合貝爾曼方程、狀態轉移概率和獎勵同步迭代更新狀態價值函式,直至其收斂,得到該策略下最終的狀態價值函式。

對上述過程做幾點解釋:

  • 採用的迭代公式如下:

\[V_{k+1}(s) = \sum_{a\in A}π(a|s)[R_{s}^a + γ\sum_{s'\in S}V_{k}(s')P_{ss'}^a] \]

其中k表示迭代週期。

  • 同步迭代:學過數位電路的都知道,同步和非同步的區別在於時刻性。在本輪更新未結束前,採用計算的所有資料都是上一輪的。
  • 給定策略:在迭代過程中,策略始終不變,無論策略好壞。
  • 收斂性:關於這一部分知乎上有一篇專欄講的比較詳細 https://zhuanlan.zhihu.com/p/39279611
    這邊我自己也總結一下收斂性的證明思路。

3.1 策略迭代收斂性證明

3.1.1度量空間

常見的度量有:

  • 範數:\(d(x, y)=||x-y||_n,n>1\),其中\(||c||_n\)表示向量的n範數,\(||c||_n = (\sum_i|c_i|^n)^{\frac{1}{n}}\)
    • \(n=2\)時即為歐式距離
    • \(n=1\)時為絕對值距離,又叫曼哈頓距離,即\(d(x, y)=\sum_i|x_i-y_i|\)
    • \(n=\infty\)時為最大值距離,又叫切比雪夫距離,即\(d(x,y)=max_i|x_i-y_i|\)
  • 離散:

\[d(x,y)=\left\{ \begin{aligned} 0,x =y \\ 1, x \not = y\\ \end{aligned} \right. \]

度量空間的定義:一個度量空間由一個二元組 \(<M,d>\) 組成,其中 \(M\) 表示集合, \(d\) 表示 \(M\) 的一個度量,即對映 \(d:M×M\)→R 。其中 \(d\) 滿足如下的距離的三條公理:
對於任意 \(x,y,z\in M\) , 有

  1. (非負性) \(d(x,y)>0\) ,且 \(d(x,y)=0\)\(x=y\)
  2. (對稱性) \(d(x,y)=d(y,x)\)
  3. (三角不等式) \(d(x,z)<d(x,y)+d(y,z)\)

3.1.2完備的度量空間

強化學習入門知識與經典專案分析1.3

一個柯西序列是指一個這樣一個序列,它的元素隨著序數的增加而愈發靠近。更確切地說,在去掉有限個元素後,可以使得餘下的元素中任何兩點間的距離的最大值不超過任意給定的正的常數。

完備的度量空間的定義:一個度量空間\(<M,d>\)是完備的(或者說是柯西的),當且僅當所有在 \(M\) 中的柯西序列,都會收斂到 \(M\) 中。即在完備的度量空間中,對於任意在 \(M\) 中的點序列 \(a_1,a_2,a_3,...\in M\),如果序列是柯西的,那麼該序列收斂於 \(M\) , 即 \(\lim\limits_{n\rightarrow\infty}a_n\in M\)

3.1.3壓縮對映定理

壓縮對映的定義 : 對於一個度量空間\(<M,d>\),和一個函式對映\(f:M\)\(M\), 如果存在實數 \(k\in [0,1)\) , 使得對於 \(M\) 中的任意兩個點 \(x,y\) ,滿足\(d(f(x),f(y))\leqslant kd(x,y)\),那麼就稱\(f\)是該度量空間中的一個壓縮對映,其中滿足條件的最小的\(k\)值稱為利普希茨常數。

壓縮對映定理:對於完備的度量空間\(<M,d>\),如果\(f:M\)\(M\)是它的一個壓縮對映,那麼在該度量空間,存在唯一的點\(x_*\),滿足\(f(x_*)=x_*\),並且對於任意的\(x\in M\),定義序列\(f^2(x)=f(f(x)),f^3(x)=f(f^2(x)),f^4(x)=f(f^3(x)),···,f^n(x)=f(f^{n-1}(x))\),該序列會收斂於\(x_*\),即\(\lim\limits_{n\rightarrow\infty}f^n(x)=x_*\)
該定理的證明可以參考上面的知乎專欄。

3.1.4策略迭代的收斂性

在一般的馬爾可夫強化學習中,有限狀態集\(S=\{s_0,s_1,...,s_n\}\),對應的狀態值函式的向量\(V\)為:

\[V= \left( \begin{matrix} V(s_0)\\ V(s_1)\\ \vdots \\ V(s_n) \end{matrix} \right) \]

該向量屬於值函式空間 \(M\),考慮 \(M\) 是一個n維向量全空間。又因為我們迭代希望的是接近無窮,所以可以定義該空間的度量是無窮範數(切比雪夫距離),即

\[d(V,U)=\underset {s\in S}{max}|V(s)-U(s)| \]

其中 \(V\)\(U\) 都是 \(M\) 中的向量。
由於 \(M\) 是向量全空間,因此\(<M,d>\)是一個完備的度量空間。

類似於本文的開頭已經抽象出的貝爾曼矩陣表示形式:\(V=Π P (R+γV)\),策略評估也抽象出如下的貝爾曼迭代公式:

\[V_{new}=F(V)=Π P (R+γV) \]

若對任意策略,都以一定的概率在有限步終止,則可以證明\(F\)是一個壓縮對映:

\[\begin{aligned} d(F(U),F(V))&=||(Π P (R+γV))-(Π P (R+γU))||_\infty\\ &=||γΠ P(V-U)||_\infty\\ &\le ||γΠ P|V-U|_\infty||_\infty\\ &\le γ||U-V||_\infty=γd(U,V) \end{aligned} \]

第一個不等號是根據切比雪夫距離的定義,第二個不等號是因為 \(Π\)\(P\) 都是概率,所有的元素都不大於1。
根據壓縮對映定理,我們可以直接得到:在策略評估中,貝爾曼期望方程收斂於唯一的\(V'\),且滿足\(V'=Π P (R+γV')\)

3.2 策略評估例項

我們繼續本文的例子,其進行迭代,不斷更新狀態價值字典。

# 根據當前策略使用回溯法來更新狀態價值。
def update_V(MDP, V, Pi):
    '''給定一個MDP和一個策略,更新該策略下的價值函式V
    '''
    S, _, _, _, _ = MDP
    V_prime = V.copy()
    for s in S:
        #set_value(V_prime, s, V_S(MDP, V_prime, Pi, s))
        V_prime[str_key(s)] = compute_v(MDP, V_prime, Pi, s)
    return V_prime


# 策略評估,得到該策略下最終的狀態價值。
def policy_evaluate(MDP, V, Pi, n):
    '''使用n次迭代計算來評估一個MDP在給定策略Pi下的狀態價值,初始時價值為V
    '''
    for i in range(n):
        V = update_V(MDP, V, Pi)
        #display_dict(V)
    return V

測試驗證,我們期望的狀態價值結果應該是和所給例項中的狀態引數一致。

V = policy_evaluate(MDP, V, Pi, 100)
display_dict(V)
# 驗證狀態在某策略下的價值
v = compute_v(MDP, V, Pi, "第三節課")
print("第三節課在當前策略下的價值為:{:.2f}".format(v))
# 瀏覽手機中: -2.31
# 第一節課: -1.31
# 第二節課: 2.69
# 第三節課: 7.38
# 休息中: 0.00

# 第三節課在當前策略下的價值為:7.38

本系列第一個專案學生馬爾可夫已經分享完成,下一篇文章會介紹策略提升和價值迭代,引入格子世界專案

相關文章