今年打 Kaggle 比赛用了 DeepSeek-Math-7B-RL 模型,学习时把 Claude 3.5 Sonnet 当作老師,這兩個模型強大的原因都離不開 RL。隱約感覺這個領域的技術很強很美於是準備接觸一下,奈何功底不扎實不好,看不懂 OpenAI Spinning Up,於是選擇了 HuggingFace DeepRL 這樣的入門課淺嘗。
導論#
強化學習的理念是,智能體(AI)通過與環境互動(通過試錯)並獲得獎勵(正面或負面)作為執行動作的反饋,從而從環境中學習。
- Agent 從環境中獲取第一幀,接收到 state ,基於 ,Agent 採取 action (比如向右走),環境進入新一幀(new state ),環境給予 agent 一定 reward (比如沒有死亡 +1 正獎勵)
Agent 的目標是 最大化 其累積獎勵,稱為預期回報。
獎勵假設:強化學習的核心思想#
強化學習基於獎勵假設,即所有目標都可以描述為預期回報的最大化(預期累積獎勵)。在強化學習中,為了獲得最佳行為,我們的目標是學會採取那些最大化預期累積獎勵的行動。
Tasks 分兩類#
-
Episodic: Has a starting point and an ending point. (遊戲關卡)
-
Continuous: Has a starting point but no ending point. (自駕)
State 和 Observation 之間有何區別?#
- State 是對世界狀態的完整描述(不存在隱藏信息),如象棋棋盤
- Observation 是對狀態的部分描述,如馬里奧遊戲鏡頭
Policy-based Methods#
在此方法中,策略是直接學習的。
將為每個狀態映射到該狀態下最佳對應的動作,或該狀態下可能動作集的概率分佈。
Value-based Methods#
不是訓練一個策略,而是訓練一個價值函數,該函數將每個狀態映射到處於該狀態的預期價值。
探索 / 利用 的權衡#
在強化學習中,我們需要平衡探索環境的程度與利用已知環境信息的程度。
何來深度?#
引入了深度神經網絡來解決強化學習問題
實踐舉例 - HUGGY#
建議直接用 colab 玩,在 WSL2 中跑的話需要做一步 virtual display 設置:
import os
os.environ['PYVIRTUALDISPLAY_DISPLAYFD'] = '0'
from pyvirtualdisplay import Display
display = Display(visible=0, size=(1400, 900))
display.start()
State Space#
要訓練 Huggy 去撿起我們扔出的棍子。這意味著它需要正確地朝棍子移動。
我們提供的環境信息:
- 目標(棍)位置
- 它與目標之間的相對位置
- 它雙腿的朝向。
根據所有這些信息,Huggy 可以利用它的Policy來決定下一步採取什麼行動以實現他的目標。
Action Space#
即 Huggy 可以執行的動作
關節電機驅動 Huggy 的腿部。
因此為了達到目標,Huggy 需要學會如何正確旋轉每條腿的關節電機來移動。
reward function#
因此我們的獎勵函數:
- 方向獎勵:獎勵接近目標。
- 達成目標獎勵:我們獎勵 Huggy 達成目標。
- 時間懲罰:每次行動時給予的固定時間懲罰,迫使儘快到達棍子。
- 旋轉懲罰:懲罰過多旋轉且過快的轉動。
Q-Learning 簡介#
value-based
- 從狀態 s 開始,我們按照策略 行動。
- 我們會累積未來的獎勵,但越遠的獎勵被折扣因子 減少其影響。
- 值函數計算的是從當前狀態開始,整個未來的 “回報” 期望值。
the goal of an RL agent is to have an optimal policy π*
Value 和 Policy 的關係#
找到最優的值函數就可以導出最優的策略
- :表示最優策略,在狀態 時應該採取的最佳行動。
- :表示state - action 值函數,它代表在狀態 下選擇動作 時,未來所有可能的折扣回報的期望值。
- :表示選擇能使 最大的動作 。即找到使得預期回報最大化的動作。
這個公式的核心觀點是,如果我們有了一个最優的 函數,那麼我們就可以通過選擇能夠最大化 的動作來確定最優策略 。也就是說,最優策略是在每個狀態 下,選擇使得未來期望回報最大的動作 。
兩種 Value Based Methods#
想像老鼠在迷宮裡尋找奶酪的場景:
- 狀態價值函數 相當於老鼠在某個位置(狀態)時的心情預期:“如果我從這裡開始按照我的策略行動,我有多大機會找到奶酪?”
- 動作價值函數 則更進一步:“如果我從這裡出發,並且選擇向右移動,我的成功概率是多少?” 動作價值函數幫助老鼠評估在每個狀態下執行不同動作的效果,從而更有針對性地選擇行動方向。
1. 狀態價值函數(State Value Function)#
狀態價值函數 評估在特定策略 下,從給定狀態 出發,期望獲得的累計獎勵。換句話說,它衡量的是 “如果我現在處於狀態 ,並遵循策略 ,那麼我的長期收益會是多少”。
- :狀態 的價值,即從 出發,遵循策略 所能期望得到的累積回報。
- :從時間步 開始的累積獎勵,通常表示為 ,其中 是折扣因子。
- :表示在策略 下的期望。
假設我們有一個迷宮,老鼠從某個位置(狀態 )開始,遵循某種策略來尋找奶酪。狀態價值函數可以告訴我們:如果老鼠從當前位置開始,它期望可以多快找到奶酪。
上圖的例子中,每個方格的數值表示某個狀態的價值。如狀態 的值是 ,意味著老鼠從這裡開始期望的累積獎勵較差(可能因為距離奶酪遠),而越接近奶酪的狀態(如值為 )則有較好的預期回報。
2. 動作價值函數(Action Value Function)#
動作價值函數 評估在特定策略 下,從給定狀態 採取動作 後,期望獲得的累計獎勵。它不僅僅考慮狀態的價值,還評估從該狀態出發,選擇某個特定動作 後的預期收益。
- :狀態 - 動作對 的價值,即從狀態 選擇動作 並遵循策略 所能期望得到的累積回報。
- 與狀態價值函數不同,動作價值函數評估從狀態 開始執行特定動作 的長期回報。
還是以上述的迷宮為例,假設老鼠現在在狀態 ,可以選擇四個方向(上、下、左、右)來尋找奶酪。動作價值函數 可以告訴我們老鼠選擇每個方向後會有什麼期望回報。例如,從某狀態 向右移動的價值可能比向左移動的價值高,因為向右移動會更快接近奶酪。
區別總結#
-
狀態價值函數 只考慮在某一狀態下,按照策略執行未來動作的累計期望回報。它不關注具體採取了哪個動作。
-
動作價值函數 則更加精細,它不僅考慮從某一狀態開始的回報,還關注在該狀態下執行特定動作後的期望回報。
貝爾曼方程#
還是前面的例子,老鼠從狀態 開始,每走一步得到的獎勵是 -1:
這表示老鼠從狀態 開始到達終點的累積回報是 -6。
像上面這樣直接計算每個狀態的累積回報顯然是一個高成本的過程,因為我們需要考慮每個可能的路徑及其獎勵,同時還有大量重複計算項。貝爾曼方程為我們提供了一種更簡潔的遞歸方法來解決這個問題。
貝爾曼方程的核心思想是:我們可以把一個狀態的價值表示為該狀態的即時獎勵加上未來狀態價值的折扣和。公式如下:
這樣就不需要從頭計算每個狀態的累積回報,而是遞歸地將即時獎勵和下一個狀態的價值相加。
讓我們回到之前的例子。如果我們已經知道狀態 的價值是 -5,那麼狀態 的價值可以通過貝爾曼方程遞歸計算得到:
如果不進行折扣(即 ),並且知道 ,即時獎勵 ,那麼:
這與我們之前直接計算的結果一致。
折扣因子 的作用#
貝爾曼方程中的折扣因子 決定了我們對未來獎勵的重視程度。
- 如果 很低(例如 0.1 或 0),那麼我們幾乎完全忽略未來獎勵,關注的是當前的即時回報。
- 如果 ,我們考慮未來所有的獎勵,未來的每一步與當前同樣重要。
- 如果 過大(比如百萬),它實際上失去了物理意義,因為未來的價值會被無限誇大,這在實際應用中是不合理的。
蒙特卡洛方法與時序差分學習#
Monte Carlo 和 Temporal Difference Learning 是兩種不同的訓練價值函數或策略函數的策略,它們都利用經驗來解決強化學習問題。
Monte Carlo: 在回合結束時學習#
蒙特卡洛方法(Monte Carlo Approach) 是一種通過反覆採樣來估計狀態或狀態 - 動作對價值的方法。在這個方法中,我們會在每一集結束後,根據該集的完整經驗來更新價值函數。下面將通過一個具體的例子來詳細說明蒙特卡洛方法的工作原理。
-
記錄狀態、動作、獎勵、以及下個狀態:
- 每當智能體進行一個動作,它會記錄當時的狀態、採取的動作、所獲得的獎勵,以及它轉移到的下一個狀態。
-
計算總的回報 :
- 智能體將累積該集中的所有獎勵,計算總的回報 ,用以評估當前策略的表現。
-
更新價值函數 :
- 更新某個狀態 的價值估計,公式如下:
- 更新某個狀態 的價值估計,公式如下:
其中:
- 是學習率,表示更新步長。
- 是實際獲得的總回報。
- 是當前對狀態 的價值估計。
更新後,智能體會以新的狀態價值函數開始下一集,並通過多次遊戲不斷學習、改進其策略。
還是以同樣的場景為例:
- 假設在遊戲一開始,老鼠對每個狀態的價值都是 0(即 )。
- 學習率 ,且我們不進行折扣(即 )。
- 老鼠在探索過程中獲得了一系列獎勵,假設累積回報 。
算法流程:
- 回報 。
- 更新狀態價值 :
帶入已知值:
通過這集的更新,老鼠對狀態 的價值估計變為 0.3。
時序差分學習:在每一步學習#
相比於蒙特卡洛方法,時序差分學習(Temporal Difference Learning,TD 學習) 不需要等到一整集結束後再更新,而是基於一步的經驗來逐步調整。更新的依據是從當前狀態 出發,觀察到的即時獎勵 和下一個狀態 的估計價值。
公式如下:
其中:
- 是學習率。
- 是即時獎勵。
- 是折扣因子,控制未來獎勵的權重。
這個更新方式叫做自舉法(Bootstrapping),因為它部分基於已有的價值估計(即 )來進行更新,而不是完整的樣本 。
繼續我們的老鼠找奶酪:
-
初始化價值函數:一開始,我們假設每個狀態的價值都為 0。
-
智能體進行探索:老鼠從某個狀態 開始,隨機選擇一個動作,假設老鼠向左移動並吃到了奶酪。此時它獲得了即時獎勵 。
-
計算 TD 目標:
TD 目標是即時獎勵加上下個狀態的折扣值:在這個例子中,假設下個狀態的價值 且 ,那麼:
-
更新價值函數:
使用 TD 目標更新狀態 的價值:代入值:
這樣,老鼠在每一步後都會更新當前狀態的價值。隨著老鼠不斷地與環境交互,價值函數會逐步反映各個狀態的真實價值。
對比總結:從偏差與方差的角度#
- 蒙特卡洛方法:等待整個回合結束後,通過計算累積回報來更新價值函數。它使用的是真實的回報值 。由於是基於一整集的經驗來更新價值函數,因此它能提供比較準確的價值估計,尤其適合在沒有明確的模型或轉移概率情況下使用。
- **無偏性**:蒙特卡洛方法直接從實際經驗中獲得完整回報,因此是無偏的,即估計的期望值和真實期望值相符。
- **高方差**:由於計算的是整個回合的回報,涉及多步的狀態轉移和獎勵,導致估計的方差較高。
- MC 方法不會受到偏差的影響。方差高但無偏,適合全局經驗的評估。
- 時序差分學習:在每一步之後及時更新價值函數,可以在沒有完整回報的情況下學習,使用的是估計的回報,即 來替代 。
- **有偏性**:TD 方法使用的是自舉,即初始值通常是任意的,並不一定準確,導致初期存在偏差,但隨著更多真實經驗的累積,偏差會逐漸減小。 偏差的存在可能導致嚴重問題,特別是在使用 off-policy + 函數逼近器(如神經網絡)時再加上 bias 時,往往會收斂失敗。Sutton 和 Barto 提出的 **“deadly triad”** 指的正是這種情況。
- **低方差**:因為只涉及單步的狀態轉移和獎勵,其方差相對較低。
- 方差低但有偏,初期估計不準,但適合實時更新。
Q-Learning 相關術語#
Q-Learning 是一種off-policy 的 value-based 方法,採用 TD 方法來訓練其動作價值函數:Q 函數。Q 這一字母代表 “Quality”,即在該狀態下該行動的價值。
給定一個狀態和動作,Q 函數輸出一個 state-action value(也稱為 Q value)
這裡再回顧一下 Value 和 Reward 之間的區別:
術語 | 解釋 |
---|---|
狀態的價值 | 預期累積獎勵,即智能體從某個狀態開始並依據策略行動時,期望獲得的總獎勵。 |
獎勵 | 在某個狀態下執行一個動作後,從環境中獲得的即時反饋。 |
在內部,我們的 Q 函數由一個 Q Table 編碼,該表中的每個單元格對應一個狀態 - 動作對值。可以將這個 Q Table 視為 Q 函數的記憶(Cheat Sheet)
可以看到初始狀態和向上移動的 state-action value 為 0
因此當訓練完成後會得到一個最優的 Q 函數,這意味著我們擁有了一個最優的 Q Table,相當於就擁有了一個最優策略,因為我們可以通過 Q Table 得知在每個狀態下採取的最佳行動。
算法流程#
好恐怖的 pseudocode:
Step 1: 初始化 Q 表#
為每個 state-action pair 初始化 Q 表。大多數情況下用 0 進行初始化。
def initialize_q_table(state_space, action_space):
Qtable = np.zeros((state_space, action_space))
return Qtable
Step 2:用 ε- 貪心策略選擇一個 action#
ε- 貪心策略是一種在 探索 / 利用 之間 trade-off 的策略。通過逐漸減少探索的概率,agent 在學習的初期傾向於探索新動作,而在後期則更加依賴已學得的知識來選擇最佳動作。
- 以概率 :選擇當前 Q-table 中 Q 值最高的動作(利用)。
- 以概率 :隨機選擇一個動作(探索)。
- 初始階段:當 時,agent 完全進行探索,即隨機選擇動作。
- 訓練過程:隨著訓練的進行, 逐漸減小(如指數衰減),agent 越來越多地選擇當前估計最優的動作,即進行利用。
def greedy_policy(Qtable, state):
# 利用:選擇具有最高狀態-動作值的動作
# 經典的 Q-learning 操作:查詢 Q 表,獲取當前狀態下獎勵最高的動作。
# 簡單、直接,就是純粹的“利用”。
action = np.argmax(Qtable[state][:]) # np.argmax 找到最大值的索引。
return action
def epsilon_greedy_policy(Qtable, state, epsilon):
random_num = random.uniform(0,1) # 隨機數
# 如果隨機數 > epsilon --> 利用
# 這是關鍵:在 1 - epsilon 的概率下,我們選擇利用,
# 貪婪地選擇 Q 表中最好的動作。
if random_num > epsilon:
# 在給定狀態下,選擇最高值的動作
action = greedy_policy(Qtable, state)
# 否則 --> 探索
# 如果隨機數小於 epsilon,智能體會進行探索,
# 隨機選擇一個動作,嘗試新的可能。
else:
action = env.action_space.sample() # 探索:選擇一個隨機動作。
return action
Step 3:執行動作 ,獲得獎勵 並進入下一狀態 #
在 Q-learning 中,這一步是算法的核心。當 agent 執行動作 時,它會從環境中得到反饋(獎勵 ),並且轉移到新的狀態 。此時,agent 不僅需要考慮當前獲得的即時獎勵,還需要評估未來的潛在回報。
這就引出了 Q-learning 的更新公式。
Step 4:更新 #
Q-learning 使用一個非常重要的更新規則,即在前面時序差分學習中見過的自舉:
- 是我們當前對狀態 - 動作對的估計值。
- 是 agent 在執行動作 後獲得的即時獎勵。
- 是在下一狀態 中,基於當前策略選擇最優動作的 Q 值。
- 是學習率,它決定了我們更新 Q 值的速度。
- 是折扣因子,用來平衡即時獎勵和未來獎勵的權重。
通過這種方式,Q-learning 的每一步更新都會根據下一狀態的最優 Q 值進行調整。
def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable):
# 外部訓練循環:執行 n_training_episodes 次的學習
for episode in tqdm(range(n_training_episodes)):
# epsilon 衰減:隨著訓練的進行,epsilon 會逐漸減少,減少探索的概率,增加利用
epsilon = min_epsilon + (max_epsilon - min_epsilon)*np.exp(-decay_rate*episode)
# 每輪開始時,重置環境,獲得初始狀態
state, info = env.reset()
step = 0
terminated = False
truncated = False
# 每一輪,智能體會和環境交互,最多執行 max_steps 步
for step in range(max_steps):
# 使用 epsilon greedy 策略選擇動作
action = epsilon_greedy_policy(Qtable, state, epsilon)
# 執行動作,並觀察新狀態、新獎勵
new_state, reward, terminated, truncated, info = env.step(action)
# 更新 Q 值:
# Q(s,a) = Q(s,a) + 學習率 * (獎勵 + 折扣因子 * max(Q(s',a')) - Q(s,a))
Qtable[state][action] = Qtable[state][action] + learning_rate * (reward + gamma * np.max(Qtable[new_state]) - Qtable[state][action])
# 如果智能體在當前狀態結束了(成功或失敗),停止此輪訓練
if terminated or truncated:
break
# 將智能體的下一狀態設置為新的狀態
state = new_state
return Qtable
Off-policy vs On-policy#
現在,讓我們討論策略的兩種類型 ——Off-policy 和 On-policy。
從策略學習的角度來看,二者的關鍵區別在於:
- On-policy:學習的是當前正在執行的策略,agent 實際的行為與學習的策略一致。
- Off-policy:學習的是不同於當前執行的策略,即使 agent 執行的是探索策略,更新時依然基於最優策略。
Off-policy (異策略)#
Off-policy 對於執行動作(acting policy)和更新 Q 值(updating policy)使用兩種不同的策略。在前面我們介紹的 Q-learning 中,agent 使用 epsilon-greedy 策略進行動作選擇(acting policy),但在更新 Q 值時,使用貪心策略(greedy policy)找到最優動作。
例如,在更新 Q 值時,agent 會選擇下個狀態中最優的動作 ,即:
這意味著,即使我們當前策略是 epsilon-greedy,也總是使用貪心策略更新 Q 值,從而加快收斂速度。這也是 Q-learning 被稱為 Off-policy 算法的原因。
On-policy (同策略)#
而 On-policy 則要求同一個策略同時用於執行動作和更新 Q 值。例如,在 SARSA 算法中,更新時選擇的動作也是基於當前策略,而不是貪心策略。這意味著 agent 始終使用 epsilon-greedy 策略選擇和更新動作,導致學習過程更貼近實際執行策略。
用一個例子來理解:
-
SARSA(On-policy):假設 agent 現在在一個狀態 ,它選擇了一个動作 ,接著進入了新狀態 ,並選擇了新的動作 。SARSA 用 (基於當前 epsilon-greedy 策略選擇的動作)來更新 Q 值。
-
Q-learning(Off-policy):在同樣的情況下,Q-learning 也是從 選擇動作 ,進入狀態 。但在更新 Q 值時,它並不管在 實際上選擇了哪個動作 ,而是用 中所有動作的最大 Q 值(即最優策略)來更新 Q 值。
Let's Dive Deeper!#
如果只是像 “Frozen Lake” 這樣的小游戏,建立一個 Q 表顯然可以在較短的時間內得到優秀的 Q 函數。
但當環境變為《Doom》這樣擁有龐大狀態空間(數百萬種不同狀態)的遊戲或現實模擬器,此時創建和更新 Q 表的效率將十分低下。
這就是為什麼現在要引入深度 Q 學習,使用一個神經網絡來近似給定狀態下每個動作的不同 Q 值。