文章目錄
- 1. 前言:當一個任務有多個目標
- 2. 目標導向的強化學習 (GoRL) 簡介
- 3. HER算法:化失敗為成功的智慧
- 4. 代碼實踐:用PyTorch實現HER+DDPG
-
- 4.1 自定義環境 (WorldEnv)
- 4.2 智能體與算法 (DDPG)
- 4.3 HER的核心:軌跡經驗回放
- 4.4 主流程與訓練
- 5. 訓練結果與分析
- 6. 總結
1. 前言:當一個任務有多個目標
經典的深度強化學習算法,如 PPO、SAC 等,在各自擅長的任務中都取得了非常好的效果。但它們通常都局限在解決單個任務上,換句話說,訓練好的算法,在運行時也只能完成一個特定的任務。
想象一個場景:我們想讓一個機械臂能把桌子上的任何一個物體重置到任意一個指定位置。對于傳統強化學習而言,如果目標物體的初始位置和目標位置每次都變化,那么這就是一個全新的任務。即便任務的“格式”——抓取并移動——是一樣的,但策略本身可能需要重新訓練。這顯然效率極低。
為了解決這類問題,目標導向的強化學習 (Goal-Oriented Reinforcement Learning, GoRL) 應運而生。它的核心思想是學習一個通用策略,這個策略能夠根據給定的目標 (goal) 來執行相應的動作,從而用一個模型解決一系列結構相同但目標不同的復雜任務。
然而,在諸如機械臂抓取等真實場景中,獎勵往往是稀疏的。只有當機械臂成功將物體放到指定位置時,才會獲得正獎勵,否則獎勵一直為0或-1。在訓練初期,智能體很難通過隨機探索完成任務并獲得獎勵,導致學習效率極低。
為了解決稀疏獎勵下的學習難題,OpenAI 在2017年提出了事后經驗回放 (Hindsight Experience Replay, HER) 算法。HER 的思想極為巧妙:即使我們沒有完成預設的目標,但我們總歸是完成了“某個”目標。 通過這種“事后諸葛亮”的方式,將失敗的經驗轉化為成功的學習樣本,從而極大地提升了在稀疏獎勵環境下的學習效率。
本文將從 HER 的基本概念出發,結合一個完整的 PyTorch 代碼實例,帶你深入理解 HER 是如何與 DDPG 等經典算法結合,并有效解決目標導向的強化學習問題的。
完整代碼:下載鏈接
2. 目標導向的強化學習 (GoRL) 簡介
在目標導向的強化學習中,傳統的馬爾可夫決策過程 (MDP) 被擴展了。除了狀態 S
、動作 A
、轉移概率 P
之外,還引入了目標空間 G
。策略 π
不僅依賴于當前狀態 s
,還依賴于目標 g
,即 π(a|s, g)
。
獎勵函數 r
也與目標相關,記為 r_g
。在本文的設定中,狀態 s
包含了智能體自身的信息(例如坐標),而目標 g
則是狀態空間中的一個特定子集(例如一個目標坐標)。我們使用一個映射函數 φ
將狀態 s
映射到其對應的目標 g
。
在 GoRL 中,一個常見的挑戰是稀疏獎勵。例如,只有當智能體達到的狀態 s'
對應的目標 φ(s')
與我們期望的目標 g
足夠接近時,才給予獎勵。這可以用以下公式表示:
其中,δ_g
是一個很小的閾值。這意味著,在絕大多數情況下,智能體得到的獎勵都是-1,學習信號非常微弱。
3. HER算法:化失敗為成功的智慧
HER 的核心思想在于重新利用失敗的軌跡。
假設智能體在一次任務(一個 episode)中,目標是 g
,但最終沒有達到,整個軌跡獲得的獎勵都是-1。這條“失敗”的軌跡對于學習如何達到目標 g
幾乎沒有幫助。
但 HER 會這樣想:雖然智能體沒有達到目標 g
,但它在軌跡的最后達到了某個狀態 s_T
。這個狀態 s_T
自身就可以被看作是一個目標,我們稱之為“事后目標” g' = φ(s_T)
。如果我們把這次任務的目標“篡改”為 g'
,那么這條軌跡就變成了一條成功的軌跡!因為智能體確實達到了 g'
。
通過這種方式,HER 能夠從任何軌跡中都提取出有價值的學習信號,將稀疏的獎勵變得稠密。
在具體實現時,HER 會從一條完整的軌跡中,隨機采樣一個時間步 (s_t, a_t, r_t, s_{t+1})
,然后根據一定策略選擇一個新的目標 g'
來替換原始目標 g
,并根據新目標重新計算獎勵 r'
。
HER 提出了幾種選擇新目標 g'
的策略,其中最常用也最直觀的是 future
策略:在當前時間步 t
之后,從該軌跡中隨機選擇一個未來狀態 s_k (k > t)
,將其對應的 φ(s_k)
作為新的目標 g'
。
這種方法保證了新目標是在當前狀態之后可以達到的,使得學習過程更加穩定和有效。
HER 作為一個通用的技巧,可以與任何 off-policy 的強化學習算法(如 DQN, DDPG, SAC)結合。在本文的實踐中,我們將它與 DDPG 算法相結合。
4. 代碼實踐:用PyTorch實現HER+DDPG
接下來,我們通過一個完整的 PyTorch 代碼項目來學習 HER 的實現。任務非常直觀:在一個二維平面上,智能體需要從原點 (0, 0)
移動到一個隨機生成的目標點。
4.1 自定義環境 (WorldEnv)
首先,我們定義一個簡單的二維世界環境。
- 狀態空間: 4維向量
[agent_x, agent_y, goal_x, goal_y]
。 - 動作空間: 2維向量
[move_x, move_y]
,每個分量的范圍是[-1, 1]
。 - 目標: 在每個 episode 開始時,在
[3.5, 4.5] x [3.5, 4.5]
區域內隨機生成一個目標點。 - 獎勵: 如果智能體與目標的距離小于閾值
0.15
,獎勵為0
;否則為-1
。 - 終止條件: 達到目標,或達到最大步數
50
。
# 自定義環境
import numpy as np
import random
from typing import Tupleclass WorldEnv:"""二維世界環境類,用于目標導向的強化學習任務智能體需要從起始位置移動到隨機生成的目標位置"""def __init__(self) -> None:"""初始化環境參數"""# 距離閾值,當智能體與目標的距離小于等于此值時認為任務完成 (標量)self.distance_threshold: float = 0.15# 動作邊界,限制每個動作分量的取值范圍為[-1, 1] (標量)self.action_bound: float = 1.0# 地圖邊界,智能體活動范圍為[0, 5] x [0, 5] (標量)self.map_bound: float = 5.0# 最大步數,防止無限循環 (標量)self.max_steps: int = 50# 當前狀態,智能體在二維平面上的坐標 (2維向量)self.state: np.ndarray = None# 目標位置,智能體需要到達的目標坐標 (2維向量)self.goal: np.ndarray = None# 當前步數計數器 (標量)self.count: int = 0def reset(self) -> np.ndarray:"""重置環境到初始狀態Returns:np.ndarray: 包含當前狀態和目標位置的觀測向量 (4維向量: [state_x, state_y, goal_x, goal_y])"""# 在目標區域[3.5, 4.5] x [3.5, 4.5]內隨機生成目標位置 (2維向量)goal_x = 4.0 + random.uniform(-0.5, 0.5)goal_y = 4.0 + random.uniform(-0.5, 0.5)self.goal = np.array([goal_x, goal_y])# 設置智能體初始位置為原點 (2維向量)self.state = np.array([0.0, 0.0])# 重置步數計數器 (標量)self.count = 0# 返回包含狀態和目標的觀測向量 (4維向量)return np.hstack((self.state, self.goal))def step(self, action: np.ndarray) -> Tuple[np.ndarray, float, bool]:"""執行一個動作并返回下一個狀態、獎勵和是否結束Args:action (np.ndarray): 智能體的動作,包含x和y方向的移動量 (2維向量)Returns:Tuple[np.ndarray, float, bool]: - 下一個觀測狀態 (4維向量: [state_x, state_y, goal_x, goal_y])- 獎勵值 (標量)- 是否結束標志 (布爾值)"""# 將動作限制在有效范圍內[-action_bound, action_bound] (2維向量)action = np.clip(action, -self.action_bound, self.action_bound)# 計算執行動作后的新位置,并確保在地圖邊界內[0, map_bound] (標量)new_x = max(0.0, min(self.map_bound, self.state[0] + action[0]))new_y = max(0.0, min(self.map_bound, self.state[1] + action[1]))# 更新智能體位置 (2維向量)self.state = np.array([new_x, new_y])# 增加步數計數 (標量)self.count += 1# 計算當前位置與目標位置之間的歐幾里得距離 (標量)distance = np.sqrt(np.sum(np.square(self.state - self.goal)))# 計算獎勵:如果距離大于閾值則給予負獎勵-1.0,否則給予0獎勵 (標量)reward = -1.0 if distance > self.distance_threshold else 0.0# 判斷是否結束:距離足夠近或達到最大步數 (布爾值)if distance <= self.distance_threshold or self.count >= self.max_steps:done = Trueelse:done = False# 返回新的觀測狀態、獎勵和結束標志# 觀測狀態包含當前位置和目標位置 (4維向量)return np.hstack((self.state, self.goal)), reward, done
4.2 智能體與算法 (DDPG)
我們選擇 DDPG (深度確定性策略梯度) 作為基礎的 off-policy 算法。DDPG 包含一個 Actor (策略網絡) 和一個 Critic (Q值網絡),非常適合處理連續動作空間問題。
PolicyNet
: Actor 網絡,輸入狀態s
(包含目標g
),輸出一個確定性的動作a
。QValueNet
: Critic 網絡,輸入狀態s
和動作a
,輸出該狀態-動作對的Q值。DDPG
: 算法主類,集成了 Actor 和 Critic,并包含目標網絡、優化器、軟更新和update
邏輯。這里的實現是標準的 DDPG。
# 要訓練的智能體和采用的算法
import torch
import torch.nn.functional as F
import numpy as np
from typing import Dict, Anyclass PolicyNet(torch.nn.Module):"""策略網絡(Actor網絡)用于輸出連續動作空間中的動作值"""def __init__(self, state_dim: int, hidden_dim: int, action_dim: int, action_bound: float) -> None:"""初始化策略網絡Args:state_dim (int): 狀態空間維度 (標量)hidden_dim (int): 隱藏層神經元數量 (標量)action_dim (int): 動作空間維度 (標量)action_bound (float): 動作邊界值,動作取值范圍為[-action_bound, action_bound] (標量)"""super(PolicyNet, self).__init__()# 第一個全連接層:狀態維度 -> 隱藏層維度self.fc1 = torch.nn.Linear(state_dim, hidden_dim)# 第二個全連接層:隱藏層維度 -> 隱藏層維度self.fc2 = torch.nn.Linear(hidden_dim, hidden_dim)# 輸出層:隱藏層維度 -> 動作維度(本環境中動作維度為2)self.fc3 = torch.nn.Linear(hidden_dim, action_dim)# 動作邊界,用于將輸出限制在有效范圍內 (標量)self.action_bound = action_bounddef forward(self, x: torch.Tensor) -> torch.Tensor:"""前向傳播計算動作輸出Args:x (torch.Tensor): 輸入狀態 (batch_size, state_dim)Returns:torch.Tensor: 輸出動作,范圍在[-action_bound, action_bound] (batch_size, action_dim)"""# 通過兩個隱藏層,使用ReLU激活函數 (batch_size, hidden_dim)x = F.relu(self.fc2(F.relu(self.fc1(x))))# 輸出層使用tanh激活函數,將輸出限制在[-1, 1],然后乘以action_bound# 得到范圍在[-action_bound, action_bound]的動作 (batch_size, action_dim)return torch.tanh(self.fc3(x)) * self.action_boundclass QValueNet(torch.nn.Module):"""Q值網絡(Critic網絡)用于評估給定狀態和動作的Q值"""def __init__(