Kampanye pelatihan

Seed, kurva pembelajaran, protokol evaluasi, dan analisis kegagalan.

Daftar isi

Kampanye pelatihan adalah cara RL dievaluasi secara nyata, yaitu menjalankan satu algoritma pada satu lingkungan dengan seed tetap, mencatat reward per episode, lalu menganalisis kurva pembelajaran yang dihasilkan. Topik ini menjalankan actor-critic pada CartPole-v1 selama 200 episode, menampilkan kurva pembelajaran bersama rata-rata bergeraknya, lalu membahas protokol evaluasi dan analisis kegagalan yang menyertainya. Pembaca akan melihat reward rata-rata 20 episode awal 11,35, puncak rata-rata bergerak 216,7, dan penurunan kembali pada episode terakhir yang menggambarkan ketidakstabilan yang lazim pada pelatihan singkat.

Komponen kampanye pelatihan#

Sebuah kampanye pelatihan terdiri atas empat komponen yang harus disusun sebelum pelatihan dimulai. Pertama, seed acak ditetapkan pada NumPy, PyTorch, Gymnasium, dan Python random agar hasil dapat direproduksi. Kedua, hyperparameter seperti learning rate, jumlah episode, dan diskon ditetapkan tetap. Ketiga, log reward per episode dicatat selama pelatihan berlangsung. Keempat, protokol evaluasi menetapkan bagaimana kebijakan akhir diuji, biasanya dengan beberapa episode tanpa eksplorasi dan seed berbeda dari pelatihan.

Tanpa seed tetap, hasil pelatihan tidak dapat direproduksi, sehingga perbandingan antar eksperimen menjadi tidak bermakna. Seed tidak hanya pada generator nomor acak utama, melainkan juga pada inisialisasi bobot jaringan dan lingkungan Gymnasium, sebab ketiganya memengaruhi jalur pelatihan. Pada kode contoh berikut, seed ditetapkan pada NumPy, PyTorch, random, serta pada env.reset, env.action_space, dan env.observation_space, sehingga keluaran identik pada setiap pengulangan.

Pelatihan actor-critic pada CartPole-v1#

Berkas kampanye_pelatihan.py berikut menjalankan actor-critic selama 200 episode pada CartPole-v1, mencatat reward per episode, lalu menggambar kurva pembelajaran bersama rata-rata bergerak 20 episode. Kurva disimpan sebagai berkas kurva_pembelajaran.svg di leaf bundle.

python kampanye_pelatihan.py
import warnings
warnings.filterwarnings("ignore")
import matplotlib
matplotlib.use("Agg")
import matplotlib.font_manager as fm
fm.fontManager.addfont('/usr/share/fonts/truetype/chinese/NotoSansSC-Regular.ttf')
fm.fontManager.addfont('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf')
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Noto Sans SC', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

import numpy as np
import torch, random
np.random.seed(0); torch.manual_seed(0); random.seed(0)
import gymnasium as gym

GAMMA = 0.99
LR = 1e-2
N_EPISODES = 200


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)


class Critic(torch.nn.Module):
    def __init__(self, obs_dim, hidden=32):
        super().__init__()
        self.net = torch.nn.Sequential(
            torch.nn.Linear(obs_dim, hidden),
            torch.nn.Tanh(),
            torch.nn.Linear(hidden, 1),
        )

    def forward(self, x):
        return self.net(x)


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

actor = Policy(4, 2)
critic = Critic(4)
opt_a = torch.optim.Adam(actor.parameters(), lr=LR)
opt_c = torch.optim.Adam(critic.parameters(), lr=LR)

episode_rewards = []
for ep in range(N_EPISODES):
    obs, _ = env.reset()
    done = False
    ep_reward = 0.0
    while not done:
        x = torch.tensor(obs, dtype=torch.float32).unsqueeze(0)
        logits = actor(x)
        dist = torch.distributions.Categorical(logits=logits)
        a = dist.sample()
        logp = dist.log_prob(a)
        v = critic(x).squeeze(-1)
        obs_next, r, term, trunc, _ = env.step(int(a.item()))
        done = term or trunc
        x_next = torch.tensor(obs_next, dtype=torch.float32).unsqueeze(0)
        with torch.no_grad():
            v_next = critic(x_next).squeeze(-1)
        v_next_val = 0.0 if term else float(v_next.item())
        delta = float(r + GAMMA * v_next_val - float(v.item()))
        target = torch.tensor(r + GAMMA * v_next_val, dtype=torch.float32)
        loss_c = (v - target).pow(2)
        opt_c.zero_grad(); loss_c.backward(); opt_c.step()
        loss_a = -(logp * delta)
        opt_a.zero_grad(); loss_a.backward(); opt_a.step()
        ep_reward += r
        obs = obs_next
    episode_rewards.append(ep_reward)
env.close()

episode_rewards = np.array(episode_rewards, dtype=float)
window = 20
moving = np.convolve(episode_rewards, np.ones(window) / window, mode="valid")

first20 = float(np.mean(episode_rewards[:20]))
last20 = float(np.mean(episode_rewards[-20:]))
peak = float(np.max(moving))
print(f"episodes               = {N_EPISODES}")
print(f"avg reward 20 awal     = {first20:.4f}")
print(f"avg reward 20 terakhir = {last20:.4f}")
print(f"puncak moving avg      = {peak:.4f}")

fig, ax = plt.subplots(figsize=(7, 3.2), constrained_layout=True)
ax.plot(np.arange(len(episode_rewards)), episode_rewards, color="#9ecae1",
        linewidth=0.9, label="reward per episode")
ax.plot(np.arange(window - 1, len(episode_rewards)), moving, color="#08519c",
        linewidth=1.8, label="rata-rata bergerak 20 episode")
ax.set_xlabel("Episode")
ax.set_ylabel("Reward")
ax.set_title("Kurva pembelajaran actor-critic pada CartPole-v1")
ax.legend(loc="upper left", bbox_to_anchor=(0.0, 1.0), framealpha=0.9)
ax.grid(True, alpha=0.3)
fig.savefig("kurva_pembelajaran.svg", format="svg")
print("kurva tersimpan: kurva_pembelajaran.svg")
text Keluaran
episodes               = 200
avg reward 20 awal     = 11.3500
avg reward 20 terakhir = 9.6000
puncak moving avg      = 216.7000
kurva tersimpan: kurva_pembelajaran.svg

Kurva pembelajaran berikut memperlihatkan reward per episode bersama rata-rata bergerak 20 episode. Garis tipis yang berosilasi adalah reward per episode, sedangkan garis tebal yang lebih halus menunjukkan tren pembelajaran.

Kurva pembelajaran actor-critic pada CartPole-v1 selama 200 episode

Membaca kurva pembelajaran#

Reward rata-rata 20 episode awal adalah 11,35, yang berarti kebijakan acak awal bertahan sekitar 11 langkah. Puncak rata-rata bergerak mencapai 216,7, yang menandakan kebijakan sempat belajar menyeimbangkan tiang dalam ratusan langkah pada sebagian episode. Akan tetapi, rata-rata 20 episode terakhir turun kembali ke 9,6, yang menggambarkan bahwa pelatihan tidak stabil pada 200 episode dengan learning rate 10−210^{-2}. Penurunan ini bukan anomali, melainkan gejala umum actor-critic murni yang sensitif terhadap learning rate dan rentan terhadap kebijakan yang terlalu percaya diri lalu runtuh.

Pola naik-turun seperti ini menunjukkan dua hal praktis. Pertama, sinyal pembelajaran memang ada, sebab puncak 216,7 jauh di atas garis dasar acak 11,35. Kedua, kebijakan terbaik tidak selalu muncul di akhir pelatihan, sehingga praktik yang sehat adalah menyimpan checkpoint kebijakan dengan performa evaluasi terbaik, bukan sekadar kebijakan terakhir. Pada produksi, pelatihan biasanya dijalankan lebih lama dengan learning rate yang lebih kecil dan beberapa seed untuk memperoleh kurva yang lebih stabil, namun 200 episode dipilih di sini agar dapat direproduksi dalam waktu singkat.

Protokol evaluasi dan analisis kegagalan#

Protokol evaluasi memisahkan kebijakan akhir dari kebijakan selama pelatihan. Setelah pelatihan, kebijakan dievaluasi pada beberapa episode tanpa eksplorasi, yaitu dengan epsilon nol pada epsilon-greedy atau pengambilan argmax langsung, dan pada seed yang berbeda dari pelatihan. Tujuannya adalah mengukur performa yang sesungguhnya, sebab kebijakan dengan eksplorasi tinggi akan tampak lebih buruk daripada kemampuan sebenarnya. Rata-rata dan deviasi standar pada episode evaluasi menjadi angka yang dilaporkan, bukan reward pelatihan.

Analisis kegagalan dimulai dengan memeriksa kurva pembelajaran secara visual. Kurva yang datar sejak awal menandakan kebijakan tidak belajar, yang dapat disebabkan oleh learning rate terlalu kecil, reward yang terlalu jarang, atau bug pada gradien. Kurva yang naik lalu turun tajam menandakan ketidakstabilan, yang lazim pada actor-critic dengan learning rate terlalu besar. Kurva yang berosilasi tanpa tren naik menandakan varians gradien yang besar, yang dapat diatasi dengan baseline atau pengurangan learning rate. Setiap pola menunjuk pada penyebab yang berbeda, sehingga diagnosis dimulai dari bentuk kurva sebelum menelusuri hyperparameter.

Kesalahan umum#

Melaporkan reward pelatihan sebagai performa akhir. Penyebabnya, reward selama pelatihan mencakup eksplorasi sehingga tidak menggambarkan kemampuan terbaik kebijakan. Akibatnya, angka yang dilaporkan terlalu rendah dan perbandingan antar eksperimen menyesatkan. Perbaikannya, evaluasi terpisah dengan eksplorasi nol dan laporkan rata-rata serta deviasi pada episode evaluasi.

Menyimpan hanya kebijakan terakhir. Penyebabnya, kebijakan terakhir dianggap pasti paling baik. Akibatnya, kebijakan yang sebelumnya lebih baik hilang, sebagaimana terlihat pada kurva di mana puncak 216,7 muncul sebelum episode terakhir. Perbaikannya, simpan checkpoint pada setiap periode evaluasi dan pilih yang berperforma terbaik pada evaluasi terpisah.

Ringkasan#

  • Kampanye pelatihan menyatukan seed tetap, hyperparameter, log reward, dan protokol evaluasi sehingga hasil dapat direproduksi dan dibandingkan.
  • Actor-critic pada CartPole-v1 selama 200 episode menghasilkan rata-rata 20 awal 11,35, puncak rata-rata bergerak 216,7, dan rata-rata 20 terakhir 9,6, yang memperlihatkan ketidakstabilan pelatihan singkat.
  • Protokol evaluasi memisahkan kebijakan akhir dari eksplorasi, dan analisis kegagalan dimulai dari bentuk kurva sebelum menelusuri hyperparameter.

Referensi#