注意
前往結尾以下載完整的範例程式碼。
開始使用環境、TED 和轉換¶
注意
若要在筆記本中執行本教學,請在開頭新增一個包含以下內容的安裝儲存格
!pip install tensordict !pip install torchrl
歡迎來到入門教學!
以下是我們將涵蓋的主題清單。
如果您時間緊迫,您可以直接跳到最後一個教學,您自己的第一個訓練迴圈,如果您不清楚或想了解有關特定主題的更多資訊,您可以從那裡回溯到其他每個「入門」教學!
RL 中的環境¶
標準的 RL(強化學習)訓練迴圈涉及一個模型,也稱為策略,該模型經過訓練以完成特定環境中的任務。通常,此環境是一個模擬器,它接受動作作為輸入,並產生一個觀察以及一些元數據作為輸出。
在本文件中,我們將探索 TorchRL 的環境 API:我們將學習如何建立環境、與其互動,並了解它使用的資料格式。
建立環境¶
本質上,TorchRL 並不直接提供環境,而是為封裝模擬器的其他函式庫提供包裝器。envs
模組可以被視為通用環境 API 的提供者,以及模擬後端的中心樞紐,例如 gym(GymEnv
)、Brax(BraxEnv
)或 DeepMind Control Suite(DMControlEnv
)。
建立您的環境通常與底層後端 API 允許的一樣簡單。以下是使用 gym 的範例
執行環境¶
在 TorchRL 中,環境有兩個關鍵的方法:reset()
,用於初始化一個 episode;以及 step()
,用於執行 actor 選擇的動作。在 TorchRL 中,環境方法讀取和寫入 TensorDict
實例。本質上,TensorDict
是一個通用的、基於鍵的 tensor 資料載體。使用 TensorDict 而不是普通 tensor 的好處是,它使我們能夠互換地處理簡單和複雜的資料結構。由於我們的函式簽名非常通用,因此消除了適應不同資料格式的挑戰。簡單來說,在經過這個簡短的教學後,您將能夠操作簡單和高度複雜的環境,因為它們面向使用者的 API 是相同且簡單的!
讓我們將環境投入使用,看看 tensordict 實例長什麼樣子
reset = env.reset()
print(reset)
TensorDict(
fields={
done: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
observation: Tensor(shape=torch.Size([3]), device=cpu, dtype=torch.float32, is_shared=False),
terminated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([]),
device=None,
is_shared=False)
現在讓我們在動作空間中採取一個隨機動作。首先,對動作進行取樣
reset_with_action = env.rand_action(reset)
print(reset_with_action)
TensorDict(
fields={
action: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.float32, is_shared=False),
done: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
observation: Tensor(shape=torch.Size([3]), device=cpu, dtype=torch.float32, is_shared=False),
terminated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([]),
device=None,
is_shared=False)
這個 tensordict 具有與從 EnvBase()
獲得的 tensordict 相同的結構,但多了一個 "action"
條目。您可以像使用普通字典一樣輕鬆存取動作
print(reset_with_action["action"])
tensor([1.5947])
現在我們需要將這個動作傳遞給環境。我們會將整個 tensordict 傳遞給 step
方法,因為在更進階的情況下,例如多代理 RL 或無狀態環境,可能需要讀取多個 tensor
stepped_data = env.step(reset_with_action)
print(stepped_data)
TensorDict(
fields={
action: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.float32, is_shared=False),
done: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
next: TensorDict(
fields={
done: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
observation: Tensor(shape=torch.Size([3]), device=cpu, dtype=torch.float32, is_shared=False),
reward: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.float32, is_shared=False),
terminated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([]),
device=None,
is_shared=False),
observation: Tensor(shape=torch.Size([3]), device=cpu, dtype=torch.float32, is_shared=False),
terminated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([]),
device=None,
is_shared=False)
同樣,這個新的 tensordict 與前一個相同,只是它有一個 "next"
條目(它本身是一個 tensordict!),其中包含由我們的動作產生的觀察、獎勵和完成狀態。
我們將此格式稱為 TED,代表 TorchRL Episode Data format (TorchRL Episode 資料格式)。它是該函式庫中表示資料的普遍方式,無論是像這裡一樣動態地,還是靜態地使用離線資料集。
您需要在環境中執行 rollout 的最後一點資訊是如何將 "next"
條目移到根目錄以執行下一步。 TorchRL 提供了一個專用的 step_mdp()
函數,它正是做這件事:它過濾掉您不需要的資訊,並提供一個資料結構,該結構對應於您在 Markov Decision Process(或 MDP)中執行一個步驟後的觀察。
from torchrl.envs import step_mdp
data = step_mdp(stepped_data)
print(data)
TensorDict(
fields={
done: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
observation: Tensor(shape=torch.Size([3]), device=cpu, dtype=torch.float32, is_shared=False),
terminated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([]),
device=None,
is_shared=False)
環境 rollout¶
寫下這三個步驟(計算動作、執行步驟、在 MDP 中移動)可能有點乏味和重複。幸運的是,TorchRL 提供了一個不錯的 rollout()
函數,允許您隨意在閉環中執行它們
rollout = env.rollout(max_steps=10)
print(rollout)
TensorDict(
fields={
action: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.float32, is_shared=False),
done: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False),
next: TensorDict(
fields={
done: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False),
observation: Tensor(shape=torch.Size([10, 3]), device=cpu, dtype=torch.float32, is_shared=False),
reward: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.float32, is_shared=False),
terminated: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([10]),
device=None,
is_shared=False),
observation: Tensor(shape=torch.Size([10, 3]), device=cpu, dtype=torch.float32, is_shared=False),
terminated: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([10]),
device=None,
is_shared=False)
這個資料看起來很像上面的 stepped_data
,只是它的 batch size 現在等於我們透過 max_steps
參數提供的步驟數。 tensordict 的神奇之處還不止於此:如果您對這個環境的單個轉換感興趣,您可以像索引 tensor 一樣索引 tensordict
transition = rollout[3]
print(transition)
TensorDict(
fields={
action: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.float32, is_shared=False),
done: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
next: TensorDict(
fields={
done: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
observation: Tensor(shape=torch.Size([3]), device=cpu, dtype=torch.float32, is_shared=False),
reward: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.float32, is_shared=False),
terminated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([]),
device=None,
is_shared=False),
observation: Tensor(shape=torch.Size([3]), device=cpu, dtype=torch.float32, is_shared=False),
terminated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([]),
device=None,
is_shared=False)
TensorDict
將自動檢查您提供的索引是否為鍵(在這種情況下,我們沿鍵維度索引)或像這裡一樣的空間索引。
像這樣執行(沒有策略),rollout
方法可能看起來相當無用:它只是運行隨機動作。如果有一個策略可用,它可以被傳遞給該方法並用於收集資料。
然而,首先運行一個 naive 的、沒有策略的 rollout 來檢查從環境中可以預期什麼,這可能會很有用。
要體會 TorchRL API 的多功能性,請考慮這樣一個事實:rollout 方法是普遍適用的。無論您是使用像這樣的單個環境、跨多個進程的多個副本、多代理環境,甚至是它的無狀態版本,它都可以在所有用例中工作!
轉換環境¶
大多數情況下,您會想要修改環境的輸出,以更好地滿足您的需求。例如,您可能想要監控自上次重置以來執行的步驟數、調整圖像大小或將連續的觀察結果堆疊在一起。
在本節中,我們將研究一個簡單的轉換,StepCounter
轉換。轉換的完整列表可以在這裡找到。
轉換通過 TransformedEnv
與環境整合
from torchrl.envs import StepCounter, TransformedEnv
transformed_env = TransformedEnv(env, StepCounter(max_steps=10))
rollout = transformed_env.rollout(max_steps=100)
print(rollout)
TensorDict(
fields={
action: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.float32, is_shared=False),
done: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False),
next: TensorDict(
fields={
done: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False),
observation: Tensor(shape=torch.Size([10, 3]), device=cpu, dtype=torch.float32, is_shared=False),
reward: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.float32, is_shared=False),
step_count: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.int64, is_shared=False),
terminated: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([10]),
device=None,
is_shared=False),
observation: Tensor(shape=torch.Size([10, 3]), device=cpu, dtype=torch.float32, is_shared=False),
step_count: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.int64, is_shared=False),
terminated: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False),
truncated: Tensor(shape=torch.Size([10, 1]), device=cpu, dtype=torch.bool, is_shared=False)},
batch_size=torch.Size([10]),
device=None,
is_shared=False)
正如您所看到的,我們的環境現在多了一個條目 "step_count"
,它追蹤自上次重置以來的步驟數。鑑於我們將可選參數 max_steps=10
傳遞給轉換建構子,我們也在 10 步後截斷了軌跡(沒有像我們使用 rollout
呼叫要求的那樣完成 100 步的完整 rollout)。我們可以透過查看截斷條目來看到軌跡被截斷了
print(rollout["next", "truncated"])
tensor([[False],
[False],
[False],
[False],
[False],
[False],
[False],
[False],
[False],
[ True]])
這就是 TorchRL 環境 API 的簡短介紹的全部內容!
下一步¶
要進一步探索 TorchRL 環境可以做什麼,請前往查看
step_and_maybe_reset()
方法將step()
、step_mdp()
和reset()
打包在一起。有些環境,例如
GymEnv
支援透過from_pixels
參數進行渲染。 請查閱類別的文件字串以瞭解更多資訊!批量環境,特別是
ParallelEnv
,它允許您在多個處理程序上運行一個相同(或不同!)環境的多個副本。透過 Pendulum 教學 設計您自己的環境,並學習規格和無狀態環境。
請參閱關於環境的更深入教學 在專門的教學中;
如果您對 MARL 感興趣,請查看 多代理環境 API;
TorchRL 有許多工具可以與 Gym API 互動,例如透過
register_gym()
在 Gym 註冊中註冊 TorchRL 環境的方法、透過set_info_dict_reader()
讀取資訊字典的 API,或透過set_gym_backend()
控制 gym 後端的方式。
腳本的總執行時間: (0 分鐘 46.408 秒)
預估記憶體使用量: 320 MB