Pembelajaran kebijakan secara langsung

Tanpa perantara Q-table, parameter menunjuk langsung ke aksi.

Daftar isi

Topik ini berpindah dari pendekatan berbasis nilai ke pendekatan yang mempelajari kebijakan secara langsung. Alih-alih membangun sebuah tabel nilai aksi lalu menurunkan kebijakan darinya, sebuah policy berparameter πθ(a∣s)\pi_\theta(a\mid s) dipetakan dari observasi ke distribusi probabilitas atas aksi, dan parameter θ\theta diperbarui agar probabilitas aksi yang menghasilkan return tinggi menjadi lebih besar. Pembaca akan melihat jaringan kecil di PyTorch yang menghasilkan logits, mengonversinya menjadi probabilitas lewat softmax, lalu menghitung log-probability aksi terpilih dan norma gradien yang dihasilkannya pada satu langkah pembaruan.

Kebijakan berparameter#

Pada metode berbasis nilai seperti DQN, yang dipelajari adalah action-value function Q(s,a)Q(s,a), dan kebijakan diturunkan dengan memilih aksi berdasarkan nilai tertinggi. Pendekatan ini menyembunyikan kebijakan di balik estimasi nilai, sehingga aksi hanya muncul setelah QQ konvergen. Policy gradient membalik urutan ini: yang dipelajari langsung adalah πθ(a∣s)\pi_\theta(a\mid s), yaitu probabilitas memilih aksi aa saat berada di state ss, dengan parameter θ\theta berupa bobot sebuah jaringan saraf.

Pendekatan langsung memiliki dua konsekuensi praktis. Pertama, kebijakan menjadi stochastic policy, yaitu setiap aksi memiliki probabilitas bukan nol, sehingga eksplorasi terjaga tanpa memerlukan mekanisme tambahan seperti epsilon-greedy. Kedua, gradient ascent dapat diterapkan langsung pada objective yang diharapkan, tanpa melalui persamaan Bellman. Karena kebijakan berubah bersama pembaruan, sifat on-policy menjadi alaminya, yaitu pembaruan hanya boleh memakai data yang dihasilkan oleh kebijakan saat ini.

Softmax atas logits#

Untuk ruang aksi diskret, bentuk kebijakan yang lazim adalah softmax atas logits yang dihasilkan jaringan:

πθ(a∣s)=exp⁡(zθ(s,a))∑a′exp⁡(zθ(s,a′)). \pi_\theta(a \mid s) = \frac{\exp(z_\theta(s,a))}{\sum_{a'} \exp(z_\theta(s,a'))}.

Karena penyebut adalah jumlah atas seluruh aksi, probabilitas terbentuk sebagai distribusi yang sah, yaitu bernilai non-negatif dan berjumlah satu. Logits zθ(s,a)z_\theta(s,a) adalah keluaran linear jaringan sebelum softmax, sehingga jaringan tidak perlu membatasi rentangnya. Beda besar antar logits menghasilkan distribusi yang lebih tegas pada satu aksi, sedangkan logits yang berdekatan menghasilkan distribusi yang lebih merata.

Bentuk softmax dipilih karena turunannya yang halus terhadap θ\theta. Setiap perubahan kecil pada bobot menggeser seluruh probabilitas secara kontinu, sehingga gradient ascent dapat mengoptimalkan objective dengan kenaikan bertahap. Selain itu, log-probability log⁡πθ(a∣s)\log \pi_\theta(a\mid s) dapat dihitung langsung dari logits tanpa risiko underflow numerik, yang menjadi penting ketika probabilitas sebuah aksi sangat kecil.

Forward pass dan gradien#

Blok berikut membangun jaringan kecil dengan satu lapis tersembunyi berukuran 32, melakukan forward pass pada observasi awal CartPole-v1, lalu menghitung log-probability aksi terpilih dan norma gradien dari loss yang setara dengan satu langkah gradient ascent. Bobot belum dilatih, sehingga distribusi aksi mendekati seragam.

python Kebijakan softmax dan satu langkah gradien
import warnings; warnings.filterwarnings("ignore")
import numpy as np, torch, random
np.random.seed(0); torch.manual_seed(0); random.seed(0)
import gymnasium as gym

env = gym.make("CartPole-v1")
obs, info = env.reset(seed=0)
env.action_space.seed(0)
env.observation_space.seed(0)

class Policy(torch.nn.Module):
    def __init__(self, obs_dim, n_aksi, hidden=32):
        super().__init__()
        self.net = torch.nn.Sequential(
            torch.nn.Linear(obs_dim, hidden),
            torch.nn.Tanh(),
            torch.nn.Linear(hidden, n_aksi),
        )
    def forward(self, x):
        return self.net(x)

pi = Policy(obs_dim=4, n_aksi=2, hidden=32)
x = torch.tensor(obs, dtype=torch.float32).unsqueeze(0)
logits = pi(x)
probs = torch.softmax(logits, dim=-1)
dist = torch.distributions.Categorical(probs=probs)
action = dist.sample()
log_prob = dist.log_prob(action)
reward_dummy = torch.tensor(1.0)
loss = -(log_prob * reward_dummy)
pi.zero_grad()
loss.backward()

gn = 0.0
for p in pi.parameters():
    if p.grad is not None:
        gn += float(p.grad.pow(2).sum())
gn = gn ** 0.5

print(f"logits        = [{float(logits[0,0]):.4f}  {float(logits[0,1]):.4f}]")
print(f"probs         = [{float(probs[0,0]):.4f}  {float(probs[0,1]):.4f}]")
print(f"action        = {int(action.item())}")
print(f"log_prob(a)   = {float(log_prob.item()):.4f}")
print(f"loss          = {float(loss.item()):.4f}")
print(f"norm gradien  = {gn:.4f}")
env.close()
text Keluaran
logits        = [-0.2659  -0.0233]
probs         = [0.4396  0.5604]
action        = 0
log_prob(a)   = -0.8218
loss          = 0.8218
norm gradien  = 1.5222

Karena bobot diinisialisasi dari seed tetap, keluaran dapat direproduksi persis. Logits [−0,2659,−0,0233][-0{,}2659, -0{,}0233] berdekatan, sehingga probabilitas aksi [0,4396,0,5604][0{,}4396, 0{,}5604] mendekati seragam [0,5,0,5][0{,}5, 0{,}5]. Aksi yang terambil adalah 0, dengan log-probability −0,8218-0{,}8218, yang setara dengan probabilitas e−0,8218≈0,4396e^{-0{,}8218} \approx 0{,}4396. Tanda minus pada loss 0,82180{,}8218 berasal dari konvensi gradient ascent: PyTorch melakukan penurunan terhadap loss, sehingga meminimalkan −log⁡πθ(a∣s)-\log \pi_\theta(a\mid s) setara dengan memaksimalkan log⁡πθ(a∣s)\log \pi_\theta(a\mid s).

Norma gradien 1,52221{,}5222 mengukur seberapa besar satu langkah pembaruan akan menggeser bobot. Karena reward_dummy bernilai satu, norma ini hanya merefleksikan turunan log-probability terhadap θ\theta. Bila reward_dummy diganti dengan return sebenarnya dari sebuah episode, norma akan diskalakan oleh besarnya return tersebut, yang menjadi inti policy gradient.

Kesalahan umum#

Menggunakan argmax sebagai ganti softmax. Penyebabnya, kebijakan dipikirkan sebagai pemilih deterministik sehingga logits dipotong menjadi aksi tunggal. Akibatnya, gradien menjadi nol karena argmax tidak dapat diturunkan, dan pelatihan berhenti. Perbaikannya, pertahankan softmax sehingga kebijakan tetap stokastik dan dapat diturunkan.

Lupa memanggil zero_grad() sebelum backward(). Penyebabnya, gradien dianggap otomatis bersih. Akibatnya, gradien terakumulasi lintas langkah dan norma melonjak tidak proporsional terhadap pembaruan yang dimaksudkan. Perbaikannya, panggil zero_grad() sebelum setiap backward() kecuali bila akumulasi gradien memang diinginkan.

Ringkasan#

  • Policy gradient mempelajari πθ(a∣s)\pi_\theta(a\mid s) secara langsung tanpa perantara tabel nilai aksi, sehingga kebijakan menjadi stochastic policy dan pembaruan bersifat on-policy.
  • Softmax atas logits menghasilkan distribusi probabilitas yang sah dan halus terhadap θ\theta, dengan log-probability yang stabil secara numerik.
  • Satu langkah gradien pada kebijakan tak terlatih di CartPole-v1 menghasilkan probabilitas [0,4396,0,5604][0{,}4396, 0{,}5604], log-probability −0,8218-0{,}8218, dan norma gradien 1,52221{,}5222.

Referensi#