實作自訂包裝器¶
在本教學中,我們將說明如何實作您自己的自訂包裝器。包裝器是為您的環境以模組化方式新增功能的絕佳方式。這將為您節省大量樣板程式碼。
我們將展示如何透過以下方式建立包裝器:
在學習本教學之前,請務必查看 gymnasium.wrappers
模組的文件。
繼承自 gymnasium.ObservationWrapper
¶
如果您想將一些函數應用於環境回傳的觀察值,則觀察包裝器會很有用。如果您實作觀察包裝器,您只需要透過實作 gymnasium.ObservationWrapper.observation()
方法來定義此轉換。此外,如果轉換改變了觀察值的形狀(例如,透過將字典轉換為 numpy 陣列,如下例所示),您應該記得更新觀察空間。
假設您有一個 2D 導航任務,其中環境回傳的觀察值為字典,其鍵為 "agent_position"
和 "target_position"
。常見的做法可能是捨棄一些自由度,只考慮目標相對於智能體的位置,即 observation["target_position"] - observation["agent_position"]
。為此,您可以實作如下的觀察包裝器
import numpy as np
from gym import ActionWrapper, ObservationWrapper, RewardWrapper, Wrapper
import gymnasium as gym
from gymnasium.spaces import Box, Discrete
class RelativePosition(ObservationWrapper):
def __init__(self, env):
super().__init__(env)
self.observation_space = Box(shape=(2,), low=-np.inf, high=np.inf)
def observation(self, obs):
return obs["target"] - obs["agent"]
繼承自 gymnasium.ActionWrapper
¶
動作包裝器可用於在將動作應用於環境之前,對其進行轉換。如果您實作動作包裝器,則需要透過實作 gymnasium.ActionWrapper.action()
來定義該轉換。此外,您應該透過更新包裝器的動作空間來指定該轉換的域。
假設您有一個環境,其動作空間的類型為 gymnasium.spaces.Box
,但您只想使用有限的動作子集。那麼,您可能想要實作以下包裝器
class DiscreteActions(ActionWrapper):
def __init__(self, env, disc_to_cont):
super().__init__(env)
self.disc_to_cont = disc_to_cont
self.action_space = Discrete(len(disc_to_cont))
def action(self, act):
return self.disc_to_cont[act]
if __name__ == "__main__":
env = gym.make("LunarLanderContinuous-v2")
wrapped_env = DiscreteActions(
env, [np.array([1, 0]), np.array([-1, 0]), np.array([0, 1]), np.array([0, -1])]
)
print(wrapped_env.action_space) # Discrete(4)
繼承自 gymnasium.RewardWrapper
¶
獎勵包裝器用於轉換環境回傳的獎勵。與先前的包裝器一樣,您需要透過實作 gymnasium.RewardWrapper.reward()
方法來指定該轉換。
讓我們看一個例子:有時(特別是當我們無法控制獎勵時,因為它是內在的),我們想要將獎勵限制在一個範圍內以獲得一些數值穩定性。為此,我們可以實作如下的包裝器
from typing import SupportsFloat
class ClipReward(RewardWrapper):
def __init__(self, env, min_reward, max_reward):
super().__init__(env)
self.min_reward = min_reward
self.max_reward = max_reward
def reward(self, r: SupportsFloat) -> SupportsFloat:
return np.clip(r, self.min_reward, self.max_reward)
繼承自 gymnasium.Wrapper
¶
有時,您可能需要實作一個執行更複雜修改的包裝器(例如,根據 info
中的資料修改獎勵或變更渲染行為)。此類包裝器可以透過繼承自 gymnasium.Wrapper
來實作。
您可以分別在
__init__
中定義self.action_space
或self.observation_space
來設定新的動作或觀察空間您可以在
__init__
中定義self.metadata
來設定新的中繼資料您可以覆寫
gymnasium.Wrapper.step()
、gymnasium.Wrapper.render()
、gymnasium.Wrapper.close()
等。
如果您這樣做,則可以透過存取屬性 env
來存取傳遞給您包裝器的環境(該環境仍然可能包裝在其他包裝器中)。
讓我們也來看一個此案例的範例。大多數 MuJoCo 環境回傳的獎勵由不同的項組成:例如,可能有一個獎勵智能體完成任務的項,以及一個懲罰大型動作(即能量使用)的項。通常,您可以在環境初始化期間傳遞這些項的權重參數。但是,Reacher 不允許您這樣做!儘管如此,獎勵的所有個別項都會在 info 中回傳,因此讓我們為 Reacher 建立一個允許我們對這些項進行加權的包裝器
class ReacherRewardWrapper(Wrapper):
def __init__(self, env, reward_dist_weight, reward_ctrl_weight):
super().__init__(env)
self.reward_dist_weight = reward_dist_weight
self.reward_ctrl_weight = reward_ctrl_weight
def step(self, action):
obs, _, terminated, truncated, info = self.env.step(action)
reward = (
self.reward_dist_weight * info["reward_dist"]
+ self.reward_ctrl_weight * info["reward_ctrl"]
)
return obs, reward, terminated, truncated, info