注意
前往結尾以下載完整的範例程式碼。
模型最佳化入門¶
作者: Vincent Moens
注意
若要在 notebook 中執行此教學課程,請在開頭新增一個包含以下內容的安裝儲存格
!pip install tensordict !pip install torchrl
在 TorchRL 中,我們嘗試像在 PyTorch 中一樣處理最佳化,使用專用的損失模組,這些模組的設計目的僅在於最佳化模型。 這種方法有效地將策略的執行與其訓練分離,並允許我們設計類似於傳統監督式學習範例中的訓練迴圈。
因此,典型的訓練迴圈看起來像這樣
>>> for i in range(n_collections):
... data = get_next_batch(env, policy)
... for j in range(n_optim):
... loss = loss_fn(data)
... loss.backward()
... optim.step()
在這個簡潔的教學課程中,您將收到損失模組的簡要概述。 由於基本使用的 API 通常很簡單,因此本教學課程將保持簡短。
RL 目標函數¶
在 RL 中,創新通常涉及探索最佳化策略的新方法(即新演算法),而不是像在其他領域中看到的那樣,專注於新架構。 在 TorchRL 中,這些演算法被封裝在損失模組中。 損失模組協調您演算法的各種元件,並產生一組可以反向傳播的損失值,以訓練相應的元件。
在本教學課程中,我們將以一個流行的離策略演算法為例,DDPG。
要建構損失模組,唯一需要的是一組定義為 :class:`~tensordict.nn.TensorDictModule`s 的網路。 在大多數情況下,其中一個模組將是策略。 也可能需要其他輔助網路,例如 Q 值網路或某種評論家。 讓我們看看這在實踐中是什麼樣子的:DDPG 需要從觀察空間到動作空間的確定性映射,以及一個預測狀態-動作對值的價值網路。 DDPG 損失將嘗試找到輸出動作的策略參數,這些動作可以最大化給定狀態的值。
為了建立損失函數,我們需要 actor 和 value 網路。如果它們是依照 DDPG 的期望所建立的,那麼我們就擁有了一個可訓練的損失模組。
from torchrl.envs import GymEnv
env = GymEnv("Pendulum-v1")
from torchrl.modules import Actor, MLP, ValueOperator
from torchrl.objectives import DDPGLoss
n_obs = env.observation_spec["observation"].shape[-1]
n_act = env.action_spec.shape[-1]
actor = Actor(MLP(in_features=n_obs, out_features=n_act, num_cells=[32, 32]))
value_net = ValueOperator(
MLP(in_features=n_obs + n_act, out_features=1, num_cells=[32, 32]),
in_keys=["observation", "action"],
)
ddpg_loss = DDPGLoss(actor_network=actor, value_network=value_net)
就是這樣!我們的損失模組現在可以使用來自環境的資料來運行(我們省略了探索、儲存和其他功能,以便專注於損失函數的功能)。
rollout = env.rollout(max_steps=100, policy=actor)
loss_vals = ddpg_loss(rollout)
print(loss_vals)
TensorDict(
fields={
loss_actor: Tensor(shape=torch.Size([]), device=cpu, dtype=torch.float32, is_shared=False),
loss_value: Tensor(shape=torch.Size([]), device=cpu, dtype=torch.float32, is_shared=False),
pred_value: Tensor(shape=torch.Size([100]), device=cpu, dtype=torch.float32, is_shared=False),
pred_value_max: Tensor(shape=torch.Size([]), device=cpu, dtype=torch.float32, is_shared=False),
target_value: Tensor(shape=torch.Size([100]), device=cpu, dtype=torch.float32, is_shared=False),
target_value_max: Tensor(shape=torch.Size([]), device=cpu, dtype=torch.float32, is_shared=False),
td_error: Tensor(shape=torch.Size([100]), device=cpu, dtype=torch.float32, is_shared=False)},
batch_size=torch.Size([]),
device=None,
is_shared=False)
LossModule 的輸出¶
正如您所看到的,從損失函數收到的 value 並非單一的純量,而是一個包含多個損失的字典。
原因很簡單:由於可能一次訓練多個網路,並且由於某些使用者可能希望在不同的步驟中分離每個模組的優化,因此 TorchRL 的目標將會返回包含各種損失成分的字典。
這種格式也允許我們傳遞與損失值相關的 metadata。一般來說,我們會確保只有損失值是可微分的,這樣您就可以簡單地對字典的值進行加總以獲得總損失。如果您想確保完全掌控發生的一切,您可以只對鍵以 "loss_"
前綴開頭的項目進行加總。
total_loss = 0
for key, val in loss_vals.items():
if key.startswith("loss_"):
total_loss += val
訓練 LossModule¶
鑑於以上所有內容,訓練模組與在任何其他訓練迴圈中所做的並沒有太大的不同。由於它封裝了模組,因此取得可訓練參數列表的最簡單方法是查詢 parameters()
方法。
我們需要一個最佳化器(或每個模組一個最佳化器,如果那是您的選擇)。
from torch.optim import Adam
optim = Adam(ddpg_loss.parameters())
total_loss.backward()
以下項目通常可以在您的訓練迴圈中找到
進一步的考量:目標參數¶
另一個需要考慮的重要方面是在 off-policy 演算法(如 DDPG)中 target 參數的存在。Target 參數通常代表參數隨時間推移的延遲或平滑版本,它們在 policy 訓練期間的 value 估計中起著至關重要的作用。與使用 value 網路參數的目前配置相比,使用 target 參數進行 policy 訓練通常被證明效率更高。通常,target 參數的管理由損失模組處理,從而免除了使用者直接的擔憂。但是,根據特定需求,使用者仍然有責任根據需要更新這些值。TorchRL 提供了幾個更新器,即 HardUpdate
和 SoftUpdate
,它們可以輕鬆地被實例化,而無需深入了解損失模組的底層機制。
from torchrl.objectives import SoftUpdate
updater = SoftUpdate(ddpg_loss, eps=0.99)
在您的訓練迴圈中,您需要在每個優化步驟或每個收集步驟更新 target 參數
updater.step()
這就是開始使用損失模組所需知道的一切!
要進一步探索該主題,請查看
腳本總執行時間: (0 分鐘 26.921 秒)
預估記憶體用量: 320 MB