實作自訂包裝器¶
在本教學中,我們將描述如何實作您自己的自訂包裝器。包裝器是以模組化的方式為您的環境添加功能的絕佳方法。這將為您節省大量樣板程式碼。
我們將展示如何透過以下方式建立包裝器
在繼續本教學之前,請務必查看 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