§01學習重點
- 說出本章的訓練訊號與前面所有章的根本差異:不是現成的,得用行動去換,而且換回來的又少又晚
- 分清「獎勵」與「回報」是兩本不同的帳,並把一個序列決策任務拆成馬可夫決策過程的組件
- 解釋價值為什麼不是狀態的固有屬性,而是「狀態+走法」合起來才定得出的量
- 用貝爾曼自洽關係說明「動一格、牽全局」,並親手把一張價值表逐輪修到自洽
- 寫出 Q 學習的單步更新式,說出每一項在做什麼、\(\alpha\) 與 \(\gamma\) 各管什麼
- 說出 ε-貪婪在解哪個問題,並用實驗指出全貪婪會卡死在哪裡
- 解釋從查表換成函數逼近之後,表格版的收斂保證是怎麼弄丟的
- 各用一句話說出策略梯度與演員—評論家在做什麼,以及離線強化學習面對的是什麼處境
§02課程內容
一、訊號得用行動去換
前面十八章的訓練訊號有一個共同點:它是現成的。監督式學習每筆資料自帶答案,非監督式學習整批資料攤在你面前,你想掃幾遍就掃幾遍,訊號不會因此變少。第 1 章第五小節已經指出,強化學習(reinforcement learning)動搖的正是這個前提,也把該備的詞備齊了——代理、環境、狀態、動作、獎勵,加上折扣因子 \(\gamma\),以及兩個難題的名字:信用分配(credit assignment)與探索利用的取捨。那一節還用一個兩萬趟的模擬讓你親眼看到,延遲又稀疏的回饋要累積極多趟才能從雜訊裡浮出來。
這些不重講。第 1 章畫的是處境,本章給的是機器:先把「一連串決策」寫成一個能動手算的數學物件,再造出一本能把遲來的總分拆回每一步的帳,然後看這本帳在環境不亮底牌時要怎麼修,最後把它從表格搬進深度網路——以及搬進去之後付了什麼代價。
全章的例子都住在同一個世界裡。想像一位新手快遞員,第一天接下一個完全陌生的城區:手上一疊要送的件,沒有前輩帶路,地圖只標了街道、不標哪裡塞車。每一趟跑完,他知道的只有這趟花了多少時間、有沒有準時;沒有人會站在路口告訴他「剛才那個右轉是對的」。他要學的東西——哪個路口該轉、哪條巷子別碰——全部得自己一趟一趟換回來。
二、把一趟送貨寫成數學:馬可夫決策過程
先把回饋迴圈完整走一圈。在每個時刻 \(t\),快遞員看到自己的狀態 \(s_t\)——他在哪個路口、車上還剩哪些件;他挑一個動作 \(a_t\)——駛進哪一條巷道;城市隨後回應:他出現在新的狀態 \(s_{t+1}\),同時一筆獎勵 \(r_{t+1}\) 記到帳上——可能是負的(這一步花掉的分鐘數),偶爾是正的(一件包裹送達簽收)。然後下一圈開始。
下標的記法先講清楚,全章照此使用:動作先落地,獎勵晚一步記帳。時刻 \(t\) 做的事,後果記在 \(t+1\)——這不是數學上的必要,是一個提醒裝置,提醒你獎勵永遠是「上一步的結果」,不是「這一步的說明書」。(第 1 章把第 \(t\) 步拿到的獎勵寫成 \(r_t\),是另一種同樣常見的編號法;兩者只差下標挪了一格。)
環境怎麼回應,第 1 章只說了一句「同一個動作未必把你帶到同一個地方」。現在把這句話升級成一個明確寫得出來的物件:轉移機率(transition probability)\(Pr(s_{t+1} \mid s_t, a_t)\)。條件機率的記號這一章會大量出現,值得停一句:直槓右邊放的是「已知的事」,左邊放的是「要問的事」,整串讀作「在目前狀態是 \(s_t\)、選了動作 \(a_t\) 的條件下,下一個狀態落在各處的機率」。同一個路口右轉,八成順利進巷、兩成被臨時管制堵在原地——這就是一個轉移機率。挑動作因此有了準確的意思:挑不同的動作,等於挑不同的「下一步分布」。你不能指定未來,但你能挑要從哪個分布裡抽。
這個寫法藏著一個重大的假設:下一步的分布只由「現在的狀態+現在的動作」決定,跟你是怎麼走到這裡的完全無關。這條假設叫馬可夫性質(Markov property),具備這條性質的決策問題叫馬可夫決策過程(Markov decision process)。它為什麼要緊?因為它允許你按狀態記帳,而不是按歷史記帳。走了二十步的歷史有指數多種,逐一記錄誰也存不起;狀態卻只有那麼多個。往後整章的每一本帳,都是這條假設撐起來的。
比喻: 快遞員中午交接班。他只要告訴接班的人兩件事——「我人在市場口,車上還剩這七件」——對方就能接手跑完下半天,完全不必聽他從早上第一趟講起。過去的路怎麼繞、在哪裡吃過虧,全部濃縮在「現在的位置與件況」裡了,這就是馬可夫性質。這個比喻的失準之處:真實的交接總有帳面外的資訊——哪條巷子今天在辦活動、上午的雨讓哪段路變滑——接班的人拿不到這些,接手後的判斷就會走樣。這其實正是「你看到的狀態不等於完整狀態」的處境,術語叫部分可觀測(partially observable)。本章主線假設看得到完整狀態;部分可觀測的後果,第六節會回收一次。
接著把「這一趟總共值多少」定出來。從任一時刻 \(t\) 起算、往後所有獎勵的折扣總和,叫回報(return):
逐項拆解:\(G_t\) 是從時刻 \(t\) 往後看的回報,多數文獻也用這個字母;\(r_{t+k+1}\) 是之後第 \(k+1\) 筆入帳的獎勵;\(\gamma\) 是第 1 章那個介於 0 與 1 之間的折扣因子,越晚入帳的獎勵權重以指數衰減——衰減的直覺與兩個理由第 1 章講過了,這裡只補一句它保證總和有限的原因:若單步獎勵的絕對值不超過某個上限 \(r_{\max}\),則 \(|G_t| \le r_{\max}(1+\gamma+\gamma^2+\cdots) = r_{\max}/(1-\gamma)\),這是 \(\gamma<1\) 的幾何級數,趟再長也加不到無窮大。第 1 章寫的整趟回報 \(R\) 就是這裡的 \(G_0\):同一本帳,差別只在那裡從整趟的頭起算、獎勵編號也早一格。
這裡要正面拆掉一個最常見的混淆:「獎勵」與「回報」是兩個詞、兩本帳。獎勵 \(r\) 是單步入帳的那一筆;回報 \(G\) 是從某一刻起往後全部獎勵的折扣總和。快遞員在意的從來不是單筆——為了躲一段慢路多繞三分鐘(單筆更虧),換來後面一路順暢(總帳更賺),這筆生意划不划算,只有回報答得了。代理要放到最大的是回報的期望,不是任何一筆獎勵。
最後一個組件是決策規則本身。策略(policy)\(\pi[a \mid s]\) 替每個狀態配一個動作分布:讀作「在狀態 \(s\) 下選各個動作的機率」(\(\pi\) 讀 pi;方括號沿用本站的函數記法)。分布可以退化成單點——每個狀態永遠選同一個動作,叫確定性策略(deterministic policy);也可以把機率質量留在好幾個動作上,叫隨機策略(stochastic policy)。策略還分一套用到底的與隨時間換的(定常與非定常),本課只處理前者。第 1 章說過的策略網路,就是用一個深度網路來擔任 \(\pi\) 這個角色——但表格能講清楚的事先用表格講,網路第五節才需要登場。
三、替每個路口記一本帳:價值函數與貝爾曼自洽
第 1 章把信用分配問的是:一趟下來只換到一個總分,功勞該記在哪一步?本章的正面回答是:造一本帳,讓「每一步值多少」成為一個可以查的數字。這本帳叫價值函數(value function),有兩種記法。
狀態價值(state value)\(v[s]\):從狀態 \(s\) 出發、之後照策略 \(\pi\) 走到底,平均能拿到多少回報。動作價值(action value)\(q[s,a]\):在狀態 \(s\) 硬選動作 \(a\)——不管 \(\pi\) 想選什麼——之後再照 \(\pi\) 走到底,平均能拿多少。寫成式子:
逐項拆解:\(\mathbb{E}[\cdot]\) 是期望值(第 7 章的推導用過)——同一個起點重跑極多趟,每趟的 \(G_t\) 都不一樣(環境有隨機性、策略可能也有),把它們平均起來會停在哪個數,就是期望值;下標 \(\pi\) 標明「之後照 \(\pi\) 走」;直槓右邊照舊是給定的條件。記法提醒兩句:多數強化學習文獻把這兩個函數寫成大寫的 \(V(s)\) 與 \(Q(s,a)\),你讀外部材料時對得上即可;另外小寫 \(v\) 也在第 3 章當過輸出層權重 \(v_d\)——那是帶下標的純量,這裡是帶方括號的函數,兩者無關。
定義裡的下標 \(\pi\) 不是裝飾,它打掉另一個常見誤解:價值不是狀態的固有屬性。同一個路口,對一個會繞開塞車的走法而言四通八達,對一個逢路必堵的走法而言是陷阱前廳——「這個狀態值多少」必須連著「你打算怎麼走」一起問才有答案。走法一換,整本帳都要重記。
兩本帳裡,\(q\) 才是信用分配的答案載體。獎勵晚到、稀疏,都沒關係——只要 \(q[s,a]\) 估得準,「在這個路口選這個轉向」的長期後果就被壓縮成一格數字,晚到的功過等於已經沿著時間往回滲透到每一步頭上。而且這本帳可以直接讀出更好的走法:假如你手上有「最好的走法」那本 \(q\) 帳,在每個狀態挑 \(q\) 最大的動作(稱為貪婪讀出),得到的就是最好的策略——帳修好了,路自己浮出來。
問題只剩:帳從哪來?突破口是價值之間互相鎖定的一條關係。從 \(s\) 出發的回報,拆開看就是「第一步入帳的獎勵」加上「打了折的、從下一站起算的回報」——於是這一格的帳必須等於下一格帳的加權平均:
逐項拆解:\(s'\) 跑遍所有可能的下一狀態;\(r[s,a,s']\) 是「從 \(s\) 做 \(a\) 落在 \(s'\)」這一步入帳的獎勵;內層求和以轉移機率為權重,把每個可能落點的「獎勵+打折後繼價值」平均起來;外層求和再按策略選各動作的機率平均一次。這條式子叫貝爾曼方程(Bellman equation)。它說的是自洽:每一格的帳都被它的鄰居鎖定。你若改了某個路口的估計,所有通得到它的路口的帳都跟著不對,得連鎖修正。這種「用一個估計值去更新另一個估計值」的做法有個名字,叫自舉(bootstrapping)——它是本章接下來兩種演算法共同的骨架。
自洽關係最直接的用法:假如環境全貌已知——整張轉移機率表攤在桌上——就把貝爾曼方程當更新規則,逐格反覆代入,帳會自己收斂到自洽。這個「已知全貌」是很奢侈的特權,教學上卻是最好的起點。底下這段程式造一張假設的路線圖:集散倉出發,目前這套走法一律先走市場口;市場口有五成五的機率直達大樓門口、兩成五被引到園道、兩成原地再排一輪:
import numpy as np
# 假設的一張送貨路線圖(環境全貌已知,教學用):
# 0 集散倉、1 市場口、2 園道、3 大樓門口、4 完成(終端,價值恆為 0)
# 每一步的獎勵=負的耗費分鐘數;簽收那一步 +9
gamma = 0.92
# 目前這套走法(策略 A):在集散倉一律「走市場口」
# trans[s] = [(機率, 下一站, 獎勵), ...]
trans = {
0: [(1.00, 1, -4.0)],
1: [(0.55, 3, -6.0), (0.25, 2, -3.0), (0.20, 1, -2.0)],
2: [(1.00, 3, -2.0)],
3: [(1.00, 4, +9.0)],
}
v = np.zeros(5)
for sweep in range(1, 100):
new = v.copy()
for s, outs in trans.items():
new[s] = sum(p * (r + gamma * v[s2]) for p, s2, r in outs)
delta = float(np.max(np.abs(new - v)))
v = new
if sweep in (1, 2, 3, 10) or delta < 1e-9:
print(f"第 {sweep:2d} 輪 v = "
+ " ".join(f"{x:8.4f}" for x in v[:4])
+ f" 最大修正 {delta:.9f}")
if delta < 1e-9:
break
# 帳本自洽後,評估集散倉的另一個動作「改走園道」:必達園道,耗 5 分鐘
q_market = -4.0 + gamma * v[1]
q_park = -5.0 + gamma * v[2]
print(f"q[集散倉, 走市場口] = {q_market:+.4f}")
print(f"q[集散倉, 改走園道] = {q_park:+.4f}")實跑輸出:第 1 輪四站的估計是 -4.0000 -4.4500 -2.0000 9.0000,第 2 輪變成 -8.0940 -1.1748 6.2800 9.0000,第 3 輪 -5.0808 1.3322 6.2800 9.0000——注意集散倉那格先衝低再彈回,前幾輪的帳看起來越修越亂,那是正常的:每一格都在等它的鄰居穩下來。到第 10 輪已收斂到 -2.2543 1.8975 6.2800 9.0000,第 17 輪最大修正掉到 \(10^{-9}\) 以下,帳完全自洽。
自洽的帳立刻能拿來換路。集散倉其實還有另一個沒被目前走法選過的動作「改走園道」:多花一分鐘(−5 對 −4),但必達、不賭。用同一條「獎勵+打折後繼價值」的算法評估兩個動作:走市場口 \(q = -4 + 0.92 \times 1.8975 = -2.2543\),改走園道 \(q = -5 + 0.92 \times 6.2800 = +0.7776\)。帳一攤開,答案自己說話:該換路。
把這兩個動作接起來輪替,就是動態規劃(dynamic programming):固定走法、把價值表修到自洽(評估),再照帳把每格換成目前最賺的動作(改善),然後對新走法重新評估——如此往復,直到走法不再變動。可以證明每輪改善都不會更差,而狀態與動作有限時這個輪替必然停在最好的走法上。評估與改善的先後與細粒度還有各種排程變形,本課不展開,想深入的讀者可循原書對照節的頁碼去看。
比喻: 老手快遞員厲害在哪?他心裡有一本帳:站在城區任何一個路口,他都能報出「從這裡到收工,平均還要幾分鐘」。這本帳不是一條一條路線背下來的——路線有無限多條,帳只有路口那麼多格;而且格與格互相咬合,市場口的估計改了三分鐘,所有通往市場口的路口估計都得跟著動。新手熬成老手的過程,就是把這本帳逐格修到自洽。失準之處有二:紙上的帳寫下就不動,價值的帳每一趟都在修;更要緊的是,帳上的數字是期望值,不是保證值——帳說平均十二分鐘的那段路,今天實際可能花你三十分鐘,帳沒有錯,它本來就只承諾平均。
四、環境不亮底牌時:實跑估帳、邊走邊修、Q 學習
動態規劃的前提在真實世界幾乎不成立:沒有人會把轉移機率表交給快遞員。這裡順手消一個歧義:接下來的方法叫無模型(model-free)方法,這個「模型」指的是環境模型——那張轉移機率表——不是前面各章「模型=函數族」的那個模型。無模型方法照樣可以有一大堆可學的參數;它「無」的是對環境運作規則的掌握。多數強化學習演算法正是為「不知道規則」的處境設計的;知道全貌,是上一節動態規劃獨享的特權。(另有一路反其道而行:先從經驗把環境模型學出來、再拿它做規劃,叫有模型(model-based)方法,本課點名即止。)
不知道機率表,最樸素的辦法是用腿補:從某個狀態出發真的跑完一整趟,記下實際回報;重複很多趟,用平均當估計。這叫蒙地卡羅方法(Monte Carlo method)。它不需要知道任何機率,代價是必須等一整趟結束才能記一筆帳——趟長的時候,帳修得極慢。
時序差分(temporal difference)方法把「等到底」換成「邊走邊修」。走一步,手上就多了兩樣東西:這一步實測入帳的 \(r_{t+1}\),和下一站帳上現有的估計 \(v[s_{t+1}]\)。把兩者拼成一個臨時答案 \(r_{t+1} + \gamma\, v[s_{t+1}]\),拿它跟出發前的估計 \(v[s_t]\) 相減,差額 \(\delta_t = r_{t+1} + \gamma\, v[s_{t+1}] - v[s_t]\) 就叫時序差分誤差(temporal difference error)。(\(\delta\) 這個字母第 11 章借給過批次正規化的平移參數,兩者無關;本章的 \(\delta_t\) 帶時間下標。)它的直覺值得記住:帳若已自洽,\(\delta_t\) 平均為零;\(\delta_t\) 不為零,它本身就是該修的量。往估計加上 \(\alpha\,\delta_t\)——\(\alpha\) 正是第 6 章管步長的學習率——帳就朝自洽挪了一小步。這裡自舉再次上場:臨時答案裡的 \(v[s_{t+1}]\) 自己也是個估計。有人因此把時序差分看成監督式學習換個名字——「臨時答案」不就是答案嗎?不是:監督式學習的答案釘在資料裡不動,這裡的答案有一半是自己的估計,你一更新帳本,它也跟著動。這件事在表格上還壓得住,代價要到第五節才全額浮現。
把同一招用在動作價值上,並且讓臨時答案裡的下一站直接按「帳上最好的動作」估,就得到 Q 學習(Q-learning):
逐項拆解:括號裡是動作版的時序差分誤差——\(r_{t+1}\) 是這一步實測的獎勵;\(\max_{a'} q[s_{t+1}, a']\) 是下一站帳上各轉向的最大值,代表「之後走到最好」的估計;減掉出發前的舊帳 \(q[s_t, a_t]\) 得到誤差;\(\alpha\) 決定一次修多少,\(\gamma\) 決定未來打幾折。整條式子只動被走過的那一格,其餘的帳原封不動。有一個近親叫 SARSA,跟 Q 學習只差一處:臨時答案裡的下一步動作不取帳上最大,而是取代理實際選出來的那一個。這個差別有名字:學習目標按自己實際走法估的叫同策略(on-policy)方法,探索用一套、學習目標另按最好走法估的叫異策略(off-policy)方法——Q 學習屬於後者,本課點名即止。
更新規則只說了「走到哪、修到哪」,沒說該往哪走。這正是第 1 章結尾留下的承諾——那裡說探索與利用的分配辦法第 19 章會給幾種,現在還這筆債。第一種,ε-貪婪(epsilon-greedy)策略:每次選動作時,以機率 \(1-\epsilon\) 拿帳上目前最好的(利用),以機率 \(\epsilon\) 從全部動作裡均勻亂抽一個(探索);\(\epsilon\) 通常取一個小數字,讓大部分時間在賺、留一小塊配額去量沒量過的。第二種,樂觀初始值:把沒試過的動作一律先記成「好得不像話」,貪婪選擇自己就會把每個動作都試一輪——不確定性被偽裝成了吸引力。第三種下一節之後才登場:讓策略本身是隨機的,探索就內建在抽樣裡,不必外加。
三種辦法裡前兩種可以直接量給你看。底下把送貨城區做成 3×4 的街區圖:集散倉在西北角、收件大樓在東南角、市場口一進去就大排長龍,每一步都有 15% 的機率被臨時管制堵在原地。同一套 Q 學習,只換「起手帳怎麼記、要不要留探索配額」三種組合:
import numpy as np
rng = np.random.default_rng(0)
# 假設的送貨城區:3×4 個路口。集散倉在西北角,收件大樓在東南角。
ROWS, COLS = 3, 4
START, GOAL = (0, 0), (2, 3)
JAM = (1, 1) # 市場口:一進去就大排長龍
MOVES = {0: (-1, 0), 1: (1, 0), 2: (0, -1), 3: (0, 1)} # 北南西東
gamma, alpha = 0.92, 0.3
def step(pos, a):
if rng.random() < 0.15: # 臨時交通管制:這一步被堵在原地
nxt = pos
else:
r_, c_ = pos[0] + MOVES[a][0], pos[1] + MOVES[a][1]
nxt = (r_, c_) if 0 <= r_ < ROWS and 0 <= c_ < COLS else pos
reward = -1.0 # 每一步都花時間
if nxt == JAM:
reward -= 4.0 # 塞在市場口的額外代價
if nxt == GOAL:
return nxt, reward + 10.0, True # 送達簽收
return nxt, reward, False
def train(eps, q_init):
q = np.full((ROWS, COLS, 4), q_init)
for _ in range(3000):
pos, done, steps = START, False, 0
while not done and steps < 60:
a = int(rng.integers(4)) if rng.random() < eps \
else int(np.argmax(q[pos]))
nxt, r, done = step(pos, a)
target = r + (0.0 if done else gamma * float(np.max(q[nxt])))
q[pos][a] += alpha * (target - q[pos][a])
pos, steps = nxt, steps + 1
return q
def evaluate(q): # 關掉探索,實測最終走法的平均回報
total = 0.0
for _ in range(1000):
pos, done, steps = START, False, 0
while not done and steps < 60:
pos, r, done = step(pos, int(np.argmax(q[pos])))
total += r * gamma ** steps
steps += 1
return total / 1000
for tag, eps, q0 in [("全貪婪+悲觀起手", 0.00, -30.0),
("ε=0.15+悲觀起手", 0.15, -30.0),
("全貪婪+樂觀起手", 0.00, +20.0)]:
q = train(eps, q0)
print(f"{tag}:最終走法平均回報 {evaluate(q):+.2f}")
if eps == 0.15:
for r_ in range(ROWS): # 這組學到的每格最佳轉向
print(" " + " ".join(
"◎" if (r_, c_) == GOAL else "↑↓←→"[int(np.argmax(q[r_, c_]))]
for c_ in range(COLS)))實跑輸出:全貪婪+悲觀起手的最終走法平均回報是 -12.42;ε=0.15+悲觀起手是 +1.84;全貪婪+樂觀起手是 +1.90。中間那組學到的轉向圖,從集散倉一路「東、東、南、南、東」抵達收件大樓,五步路,剛好從北側繞開市場口。
最值得看的是第一組怎麼死的。悲觀起手把所有沒試過的巷道都記成 −30——聽起來只是謹慎,但配上全貪婪就成了自我封印:起手帳全部同分,貪婪選擇挑了第一個轉向(北),而集散倉北邊是圍牆,走不動、扣一分。看這一格的帳怎麼動:第一次更新從 −30 修成 −29.58——明明在虧,帳面卻上升了,因為臨時答案 \(-1 + 0.92 \times (-30) = -28.6\) 比 −30 好看;此後它一路收斂到 −12.5,全程壓著其他三個從沒試過的 −30,於是永遠選北。三千趟跑完,這位快遞員沒有送達過任何一件包裹,全部時間花在對著北邊的圍牆確認它還是一堵牆。這正是第 1 章那個不對稱論證的實驗版:試錯的代價立刻入帳,不試的代價永遠隱形——差別在這裡你親眼看到了帳面數字怎麼一步步把它鎖死。而解法便宜得驚人:留 15% 的配額亂走(第二組),或把沒試過的先記成好料(第三組),都足以打破封印。
補兩句收尾。其一,表格版的 Q 學習在夠寬的條件下(每格被更新無窮多次、學習率適當遞減)可以證明收斂到最好的帳;證明用的數學工具超出本課範圍,結論可以放心引用。其二,本章到此給了兩套東西——價值函數回答「功勞記給誰」,ε-貪婪這類辦法回答「配額怎麼分」——信用分配與探索利用是兩個獨立的難題,一個關於歸功,一個關於嘗試,常被混為一談;它們在演算法裡由不同的零件負責,混在一起想,兩個都想不清。
五、表格撐不住之後:函數逼近與深度 Q 網路
上一節的帳是一張 12 個路口 × 4 個轉向的表格,48 格,怎麼填都填得起。真實問題不長這樣。假設狀態要如實一點:位置之外還有時段、天候、車上剩件的組合、每段路的即時路況——隨便相乘就是天文數字。表格法在這裡遇到的不是「慢」,是雙重的「不可能」:存不下(格子比記憶體多),也填不滿(絕大多數狀態一輩子遇不到第二次,那一格永遠沒有第二筆資料可修)。
出路是第 1 章那行老式子。把查表換成一個帶參數的函數:吃進狀態的向量表示,吐出每個動作的價值估計,寫成 \(q[\mathbf{s}, a, \boldsymbol\phi]\)。相似的狀態共用參數,見過的狀態自動把經驗攤給沒見過的鄰居——填不滿的問題被泛化接手了。用深度網路擔任這個函數、按 Q 學習的目標訓練,就是擬合 Q 學習(fitted Q-learning)。
代價藏在一個不起眼的地方。查表時代,更新只動被走過的那一格,目標裡的 \(\max_{a'} q[s_{t+1}, a']\) 查的是別的格子,它不動。換成共用參數之後,同一次更新挪動的是 \(\boldsymbol\phi\)——而預測值和目標值都是 \(\boldsymbol\phi\) 的函數。你把預測往目標推了一步,目標卻因為同一步挪動也跑掉了。這在領域裡有個通行的說法:追著一個會動的目標。第四節說過時序差分不是監督式學習換個名字,根源就在這裡——監督式學習的答案釘在資料裡不動,自舉的答案是自己的估計,會被自己的更新拖著走。
會不會真的出事?造一個最小的實驗看看。假設深夜時段兩個相鄰路口甲、乙毫無延誤,所有獎勵都是 0,真實價值也就是 0——任何非零估計都是純誤差,我們只看誤差是消掉還是長大。表格版給甲、乙各記一格;函數逼近版圖省事,用一個參數 \(w\) 管兩個路口,甲的帳面掛 \(w\)、乙離收工遠一倍所以掛 \(2w\)。再假設訓練資料不均:每十筆更新有九筆落在甲(快遞員常路過甲、少在乙收尾):
import numpy as np
gamma, alpha = 0.92, 0.1
# 假設深夜時段兩個相鄰路口甲、乙毫無延誤:所有獎勵都是 0,真實價值也是 0。
# 任何非零的估計值都是純誤差——我們只看誤差是消掉,還是長大。
v = np.array([1.0, 2.0]) # 表格版:甲、乙各記一格,起手各帶一點誤差
w = 1.0 # 共用參數版:甲的帳面掛 w、乙的帳面掛 2w
print("輪次 表格甲 表格乙 共用參數 w")
for k in range(1, 13):
# 每一輪:九次「甲→乙、獎勵 0」的更新,一次「乙→收工、獎勵 0」的更新
for _ in range(9):
v[0] += alpha * (0 + gamma * v[1] - v[0])
w += alpha * (0 + gamma * (2 * w) - w) * 1 # 甲的特徵值是 1
v[1] += alpha * (0 + 0 - v[1])
w += alpha * (0 + 0 - 2 * w) * 2 # 乙的特徵值是 2
if k in (1, 2, 4, 8, 12):
print(f"{k:3d} {v[0]:8.4f} {v[1]:8.4f} {w:10.4f}")實跑輸出(列出第 1、2、4、8、12 輪):表格版的甲從 1.5146 一路降到 0.6210、乙從 1.8000 降到 0.5649——甲前兩輪還被乙的高估帶著飄高了一小段,但乙每輪都在向現實靠攏,甲跟著回頭,兩格的誤差都在死去。共用參數 \(w\) 則是 1.2400、1.5376、2.3641、5.5888、13.2123——每輪乘上約 1.24,指數成長。同一批資料、同一條更新規則,只差「分開記帳」還是「共用參數」。要說清楚的是:九比一的失衡與係數 2 是刻意挑的組合,挑它是為了把「收斂保證沒了」變成看得見的數字;不是每個函數逼近都會爆,但從這裡開始,沒有定理再替你擔保,穩不穩定要靠工程手段自己掙。
比喻: 新手快遞員不確定自己的估計,去找隔壁路段的同事對答案;他不知道的是,同事帳上的數字有一大半正是抄他上週的帳來的。於是他每照「同事的說法」修一次自己的帳,下週拿到的參考答案也跟著動——兩個人可以一起越飄越遠,因為過程中沒有任何一刻,有人真的去量現實。失準之處:演算法裡的目標並不是純抄來的——每一筆目標都摻了一項真實入帳的獎勵 \(r_{t+1}\),現實一直有一隻腳踩在裡面;表格版正因如此可證明收斂。互相追逐失控,要再加上參數共用與更新失衡這類條件才會發生——但深度網路恰恰兩樣都佔。
把擬合 Q 學習真正做穩、做出名的是深度 Q 網路(deep Q-network):讓卷積網路直接吃遊戲畫面,在雅達利電玩基準上用同一套架構學會多款遊戲,是深度強化學習的成名作。本課只點兩個它賴以站穩的工程手段,機制各一句。經驗回放(experience replay):把走過的每一步存進一個大池子,訓練時從池裡隨機重抽舊經驗——相鄰兩步高度相關,打散了才像一批獨立樣本。目標網路凍結(target network):算目標時用一份釘住不動的舊參數,隔一段時間才同步一次——等於強行把「會動的目標」釘住一陣子,正面回應上面比喻裡「沒有標準答案」的困境。另外,更新式裡那個 \(\max\) 運算會系統性高估(在一堆帶雜訊的估計裡挑最大,挑到的多半是被雜訊抬高的那個),解法叫雙 Q 學習(double Q-learning)——挑動作與估價值交給兩套分開的參數,點名即止。
六、不記帳的另一條路:策略梯度與演員—評論家
本章到這裡的路線都是「先修帳、再讀帳」:價值估準了,策略是從帳裡讀出來的副產品。還有一條路反過來:跳過帳本,直接調策略。把策略寫成一個帶參數的網路 \(\pi[a \mid s, \boldsymbol\phi_\pi]\)(參數比照本站多網路慣例加助憶下標),目標很直白——讓實際換回高回報的那些軌跡,在策略的分布裡變得更常出現;低回報的變少。沿著這個目標對 \(\boldsymbol\phi_\pi\) 做梯度上升,這一族方法叫策略梯度(policy gradient),最早的代表演算法叫 REINFORCE,推導的完整鏈本課略過。
有個表面上的悖論值得停一下:獎勵是離散事件,送達就是送達、遲到就是遲到,哪來的梯度?答案在隨機策略身上。策略輸出的是機率,參數動一點,各動作的機率就平滑地挪一點,抽樣出來的軌跡分布跟著平滑地變——回報的期望值因此是參數的平滑函數,梯度就有了。這也是第四節預告的第三種探索辦法:策略本身帶隨機,嘗試內建在抽樣裡。第二節埋的伏筆在這裡回收:部分可觀測時,兩個實際不同的地點在你眼中可能一模一樣,押死單一動作的走法會在其中一處永遠撞牆,保留機率質量的隨機策略反而是必要的,不只是權宜。
策略梯度的軟肋是訊號抖:同一套策略,這批軌跡運氣好、下批運氣差,估出來的梯度方向晃得厲害。壓抖的頭號手段是減去一條基線(baseline)——把每條軌跡的回報先扣掉「在這個狀態下不管怎麼做平均都拿得到的部分」,只讓超出平均的部分說話;這個「比平均好多少」的量叫優勢(advantage)。而「這個狀態平均拿多少」正是 \(v[s]\)——帳本從門口被請出去,又從窗戶回來了。讓一個價值網路 \(v[\mathbf{s}, \boldsymbol\phi_v]\) 用時序差分邊走邊修、專職提供基線,策略網路照優勢調參數,兩個網路一個決定怎麼做、一個評估做得值不值——這個分工架構叫演員—評論家(actor-critic),是本章兩條路線的會合點,也是許多現代系統的骨架。
最後點名兩個處境,各一句。有些場域根本不容新手試錯——假設要學的是危險品配送的路線決策,你不可能放一個隨機初始化的策略上路瞎跑。只能從既有的歷史紀錄裡學、完全不與環境互動的設定,叫離線強化學習(offline reinforcement learning)。另一個晚近的想法是決策 Transformer(Decision Transformer):把歷史紀錄裡的狀態、動作、回報排成一條序列,用第 12 章那種「猜下一個」的訓練方式直接預測下一個動作該是什麼——序列預測的骨架原封不動,換了餵的東西。順帶一條分界線:模仿學習(imitation learning)也從紀錄學,但它只學「專家做了什麼」,拿不到也不用獎勵訊號;離線強化學習的紀錄裡有獎勵,所以它有機會勝過紀錄的來源,而不只是複製。
收個尾。這一章從頭到尾只做了一件事:訓練訊號不再現成之後,把「學」重新組裝起來——馬可夫決策過程把摸索寫成數學,價值函數把遲來的總分拆回每一步,時序差分讓帳邊走邊修,ε-貪婪把嘗試變成刻意分配的配額,函數逼近讓帳裝得下真實世界、代價是交出收斂保證。所以別把「深度」當成讓強化學習變簡單的魔法——恰恰相反,深度網路一進場,表格時代的定理就退場了;換回來的是泛化,是把第 1 章那句「訊號又少又晚」的處境,真正扛進了高維世界。
§03原書對照
原書第 19 章是全書唯一以整章篇幅處理序列決策的一章,二十八頁從形式化定義一路鋪到深度網路與離線學習,公式密度在全書後段名列前茅。以下按主題整理原書鋪了什麼、在哪幾頁,供想深入的讀者按圖索驥。
開場與形式化的階梯。原書 p.374 的開場用下棋一口氣攤開序列決策的難處:回饋稀疏、獎勵與成因在時間上錯開、環境帶隨機性、探索與利用得做取捨,四個難點在同一頁並排。接著 pp.375–378 用一隻在滑冰面上覓食的企鵝當貫穿全節的例子,把馬可夫過程、馬可夫獎勵過程、馬可夫決策過程到部分可觀測的版本一級一級疊上去,每一級只新添一種元素,並配六張圖把狀態轉移、獎勵、動作、策略與回饋迴圈逐一畫出。想看「一個決策問題的定義是怎麼一塊塊組起來的」,這幾頁是全書坡度最緩的一段。
價值與貝爾曼方程。原書 pp.378–382 先給狀態價值與動作價值的期望值定義,再寫出兩者互相表示的關係,串成貝爾曼方程;p.379 的圖 19.7 把每個格子的狀態價值與四個動作各自的價值直接標成數字,並示範怎麼從動作價值讀出更好的策略,是「價值」這個抽象詞最具體的一張圖;p.381 的兩張圖再把「加權求和」畫成看得見的分支展開。想把「價值必須自洽」這句話落到符號層面的人,這一節值得逐式讀。
表格法三家。pp.383–386 依序處理動態規劃(含策略迭代與價值迭代等排程變形,p.384)、蒙地卡羅方法(pp.384–385,含探索起點與 ε-貪婪的取捨)與時序差分方法;SARSA 與 Q 學習的更新式在 p.385 並排出現,p.386 的圖 19.12 給了 Q 學習單步更新的完整數值演算,適合拿來手算核對。
深度網路上場。pp.386–389 講表格存不下時怎麼辦:擬合 Q 學習為何失去收斂保證(p.386)、深度 Q 網路在雅達利遊戲上的網路設定與訓練技巧——獎勵裁剪、經驗回放、目標網路凍結(pp.387–388)——以及最大化運算造成的高估偏差與雙 Q 學習的修正(pp.388–389)。原書在這幾頁給足了工程細節,想重現該系統的人可直接對表。
策略梯度與演員—評論家。pp.390–392 從軌跡機率出發,把梯度更新式一步步化簡到只剩策略項,每個代換都寫在紙面上;pp.392–394 接著講 REINFORCE 演算法、用基線與控制變量壓變異數、再以狀態相依的基線引出優勢估計;pp.394–395 把時序差分嫁接進策略梯度,得到演員—評論家架構。想看「為什麼能對離散的獎勵做梯度法」的完整論證,這六頁是主戰場。
離線強化學習與章末資源。pp.395–396 處理不能與環境互動的情境,並介紹決策 Transformer 怎麼把它改寫成序列預測問題(圖 19.17,p.395)。章末註記 pp.397–399 是一張濃縮的領域地圖,從圍棋系統的里程碑到用人類回饋微調聊天機器人的來龍去脈都有文獻線索;pp.400–401 的八道習題中,第 19.1 題適合入門自測,第 19.2 與 19.5 題分別要求證明策略改進定理與收縮映射性質,適合想走理論路線的讀者。原書第 19 章對應印刷頁 pp.374–401。
§04作業和解答
作業一:獎勵是單筆,回報是總帳
假設一趟六步的配送,每步入帳的獎勵依序是 \(-2, -1, -5, -1, -1, +12\)(前五筆是趕路與等待的時間成本,最後一筆是送達簽收)。(a)分別在 \(\gamma = 0.92\) 與 \(\gamma = 0.5\) 之下,算出從起點看的回報 \(G_0\)(第一筆獎勵權重為 1,之後逐筆乘 \(\gamma\))。(b)兩個 \(\gamma\) 各把最後那筆 \(+12\) 折成多少?(c)用「權重衰減到 1% 以下需要幾步」估一估:兩個 \(\gamma\) 各讓代理實質上「看得到」多遠的未來?(提示:解 \(\gamma^k < 0.01\)。)
解答 SOLUTION
(a)\(\gamma = 0.92\) 時,六筆權重依序是 \(1, 0.92, 0.8464, 0.7787, 0.7164, 0.6591\),加權求和得 \(G_0 = -0.7381\);\(\gamma = 0.5\) 時權重是 \(1, 0.5, 0.25, 0.125, 0.0625, 0.03125\),得 \(G_0 = -3.5625\)。同一趟路,有耐心的帳說「小虧」,短視的帳說「大虧」——差別全在最後那筆大獎勵被折掉多少。
(b)\(\gamma = 0.92\) 把 \(+12\) 折成 \(12 \times 0.6591 = 7.9090\);\(\gamma = 0.5\) 折成 \(12 \times 0.03125 = 0.3750\)。後者幾乎等於看不見這筆獎勵。
(c)解 \(\gamma^k < 0.01\):\(\gamma = 0.92\) 需要 \(k \ge 56\)(\(0.92^{56} \approx 0.0094\)),\(\gamma = 0.5\) 只需要 \(k \ge 7\)(\(0.5^7 \approx 0.0078\))。也就是說 \(\gamma = 0.92\) 的代理實質上看得到約五十多步內的未來,\(\gamma = 0.5\) 的代理超過七步就視同不存在。\(\gamma\) 名義上是折扣,實際上是視野旋鈕——這也解釋了為什麼獎勵特別延遲的任務通常要把 \(\gamma\) 設得很接近 1。(本題數值以 numpy 重算核對過。)
作業二:手解一次貝爾曼自洽
假設兩個路口甲、乙,\(\gamma = 0.92\)。目前的走法:在甲一律走河堤便道到乙,這一步獎勵 \(-3\);在乙,五成機率直接送達收工(獎勵 \(+8\),一趟結束),五成機率被引導繞回甲(獎勵 \(-2\))。(a)列出 \(v[甲]\)、\(v[乙]\) 的貝爾曼方程並解出數值。(b)甲其實還有另一個動作「走大馬路直送」:直接送達收工,獎勵 \(+1\)。目前的走法該不該換?(c)換了之後,乙的帳會變成多少?這說明了什麼?
解答 SOLUTION
(a)照「這一格=獎勵+打折後繼價值的平均」寫:\(v[甲] = -3 + 0.92\, v[乙]\);\(v[乙] = 0.5 \times 8 + 0.5 \times (-2 + 0.92\, v[甲])\)。代入整理:\(v[甲] = -3 + 0.92(3 + 0.46\, v[甲])\),移項得 \(0.5768\, v[甲] = -0.24\),所以 \(v[甲] = -0.4161\)、\(v[乙] = 3 + 0.46 \times (-0.4161) = 2.8086\)。注意乙有一半機率把你丟回甲,兩條方程互相引用——這正是「自洽」的意思,兩格的帳必須同時成立。
(b)動作「走大馬路直送」一步收工,動作價值就是 \(q[甲, 直送] = +1\)。目前走法下 \(q[甲, 便道] = v[甲] = -0.4161\)。\(+1 > -0.4161\),該換。
(c)換了之後甲的價值變成 \(v[甲] = +1\),乙的方程重算:\(v[乙] = 0.5 \times 8 + 0.5 \times (-2 + 0.92 \times 1) = +3.46\)——乙自己的走法一步都沒改,帳卻從 \(2.8086\) 漲到 \(3.46\),因為它有一半機率落回的那個甲變好了。這說明兩件事:價值依賴整套策略,改一處、動全局;所以「評估」與「改善」必須輪替著做——每換一次路,全部的帳都要重新修到自洽,這正是第三節動態規劃輪替結構的由來。(本題數值以 numpy 重算核對過。)
作業三:手算一步 Q 學習與 ε-貪婪的配額
某路口的四個轉向在帳上的動作價值是 \(q = [-3.0, -1.2, -6.5, -2.0]\)(依序為直行、右轉、迴轉、左轉),\(\gamma = 0.92\)、\(\alpha = 0.3\)。快遞員選了右轉,實測獎勵 \(-1\),抵達的下一路口帳上最好的動作價值是 \(-0.8\)。(a)算出時序差分誤差與更新後的 \(q[s, 右轉]\)。(b)若用 \(\epsilon = 0.2\) 的 ε-貪婪選動作:抽中「帳上最好的動作」的總機率是多少?某個指定的非最好動作呢?經過這個路口 200 次,期望試探迴轉幾次?(c)如果某一步的時序差分誤差恰為零,代表什麼?
解答 SOLUTION
(a)臨時答案是 \(r + \gamma \max_{a'} q = -1 + 0.92 \times (-0.8) = -1.736\);誤差 \(\delta = -1.736 - (-1.2) = -0.536\);更新後 \(q[s, 右轉] = -1.2 + 0.3 \times (-0.536) = -1.3608\)。帳上原本說右轉值 \(-1.2\),走了一步的現實說它值 \(-1.736\),學習率讓新帳往現實靠三成。
(b)貪婪那份機率 \(1-\epsilon = 0.8\) 全給最好的動作,亂抽那份 \(\epsilon = 0.2\) 均分給四個動作、每個 \(0.05\)——最好的動作也在亂抽的名單裡,所以它被抽中的總機率是 \(0.8 + 0.05 = 0.85\);指定的非最好動作(例如迴轉)是 \(0.05\)。經過 200 次,期望試探迴轉 \(200 \times 0.05 = 10\) 次。帳上最差的迴轉(\(-6.5\))也保有固定配額——它可能真的爛,也可能只是上次運氣差;不留這個配額,第二種可能永遠翻不了案。
(c)誤差為零表示「出發前的估計」與「單步實測+後繼估計」拼出來的臨時答案剛好相等——帳在這一步上自洽,這一步不需要修。反過來說,時序差分方法的全部動力就來自不自洽的地方:誤差在哪裡出現,修正就發生在哪裡。(本題數值以 numpy 重算核對過。)
§05參考資料
- Sutton & Barto《Reinforcement Learning: An Introduction》全文 — 強化學習的標準教科書,作者免費釋出全文;本章每個主題都能在裡面找到完整版
- OpenAI Spinning Up in Deep RL — 從本章程度銜接到深度強化學習實作的工程教材,術語表與演算法家譜特別好用
- David Silver 的強化學習公開課 — 十講影片把馬可夫決策過程到函數逼近的理論骨架講得極清楚,適合搭配本章逐節對照
- Gymnasium:強化學習練習環境庫 — 想把本章的表格 Q 學習改到現成環境上跑,這是社群維護的標準介面
- Lilian Weng:Policy Gradient Algorithms 總覽 — 第六節只給了概覽,這篇把策略梯度家族從 REINFORCE 到演員—評論家逐一寫開
- Andrej Karpathy:Deep Reinforcement Learning: Pong from Pixels — 用約百行 numpy 從零實作策略梯度的著名教學文,讀完第六節後看它最有感
- Understanding Deep Learning(MIT Press) — 本課課綱主題所本的原書出版頁(ISBN 9780262048644,2023-12 出版)
- udlbook 官方網站(作者釋出的 PDF、投影片與習題) — 原書作者維護的免費資源站(udlbook.com 會轉址到此)