Dua penyempurnaan DQN yang paling dikenal, yaitu Double DQN dan dueling head, menargetkan dua kelemahan berbeda yang muncul setelah DQN dasar berhasil. Topik ini memperkenalkan keduanya pada tingkat konsep, lalu memperagakan masing-masing dengan demo terpisah pada observasi tetap. Pembaca akan melihat dueling head memecah menjadi dan , serta Double DQN mengganti argmax target dari target network ke online network sehingga estimasi target berbeda dari vanilla DQN.
Bias maksimum dan overestimasi#
DQN dasar memperbarui target dengan . Operator bermasalah pada estimasi yang mengandung derau, sebab cenderung memilih aksi dengan estimasi tertinggi, yang sering kali terlalu optimis karena derau positif. Akibatnya, yang dipelajari bias ke arah overestimasi, dan bias ini menyebar ke state sebelumnya melalui bootstrap. Pada lingkungan dengan banyak aksi atau reward yang berderau, overestimasi ini dapat menyesatkan kebijakan.
Double DQN memisahkan pemilihan aksi dari evaluasinya. Alih-alih memilih dan mengevaluasi dengan target network yang sama, Double DQN memilih aksi terbaik menurut online network , lalu mengevaluasi nilai aksi tersebut dengan target network . Karena online network biasanya lebih akurat pada kebijakan saat ini sementara target network memberi evaluasi yang lebih konservatif, pemisahan ini mengurangi overestimasi tanpa mengorbankan stabilitas target network.
Dueling head dan dekomposisi nilai#
Dueling head memecah keluaran jaringan menjadi dua, yaitu nilai state dan keunggulan aksi , lalu menggabungkannya kembali menjadi . Pengurangan rata-rata menjamin identifikasi yang unik, sebab tanpa itu pasangan dan dapat digeser dengan konstanta tanpa mengubah . Dekomposisi ini membantu ketika banyak aksi memberi efek mirip, karena jaringan dapat belajar tanpa harus mengaitkannya ke setiap aksi.
Manfaat utama dueling muncul pada lingkungan dengan banyak aksi yang sebagian besar tidak relevan di state tertentu. Pada state di mana kebijakan jelas, baik dueling maupun DQN biasa memberi hasil mirip. Pada state di mana kebanyakan aksi setara, dueling mengalokasikan kapasitas ke yang umum, sehingga pembaruan pada satu aksi tidak menyesatkan estimasi state. Asumsi ini bersifat empiris dan hasilnya bergantung pada struktur lingkungan.
Demo dueling dan Double DQN#
Berkas penyempurnaan_dqn.py berikut memperagakan kedua penyempurnaan pada observasi tetap. Bagian pertama menjalankan forward pass jaringan dueling dan mencetak , , serta hasil penggabungan. Bagian kedua membandingkan target vanilla DQN dengan target Double DQN pada batch dummy, di mana online network telah digeser beberapa langkah agar berbeda dari target network.
import warnings; warnings.filterwarnings("ignore")
import numpy as np, torch
np.random.seed(0); torch.manual_seed(0)
import random; random.seed(0)
torch.set_printoptions(precision=4, sci_mode=False)
class DuelingQNet(torch.nn.Module):
def __init__(self, obs_dim, n_actions, hidden=32):
super().__init__()
self.body = torch.nn.Sequential(
torch.nn.Linear(obs_dim, hidden), torch.nn.ReLU(),
torch.nn.Linear(hidden, hidden), torch.nn.ReLU(),
)
self.value_head = torch.nn.Linear(hidden, 1)
self.adv_head = torch.nn.Linear(hidden, n_actions)
def forward(self, x):
h = self.body(x)
v = self.value_head(h)
a = self.adv_head(h)
q = v + (a - a.mean(dim=1, keepdim=True))
return q, v, a
class QNet(torch.nn.Module):
def __init__(self, obs_dim, n_actions, hidden=32):
super().__init__()
self.net = torch.nn.Sequential(
torch.nn.Linear(obs_dim, hidden), torch.nn.ReLU(),
torch.nn.Linear(hidden, hidden), torch.nn.ReLU(),
torch.nn.Linear(hidden, n_actions),
)
def forward(self, x):
return self.net(x)
# Demo dueling forward pass
duel = DuelingQNet(4, 2, 32)
obs = torch.tensor([[0.1, -0.2, 0.05, 0.3]], dtype=torch.float32)
with torch.no_grad():
q, v, a = duel(obs)
print("V(s) =", v.flatten())
print("A(s, .) =", a.flatten())
print("Q(s, .) =", q.flatten())
# Demo Double vs vanilla DQN target pada batch dummy
GAMMA = 0.99
BATCH = 4
N_ACTIONS = 2
online = QNet(4, N_ACTIONS, 32)
target = QNet(4, N_ACTIONS, 32)
target.load_state_dict(online.state_dict())
# Latih online beberapa langkah agar bobotnya bergeser dari target,
# mensimulasikan target network yang ketinggalan beberapa langkah pembaruan.
opt = torch.optim.SGD(online.parameters(), lr=0.5)
obs_fix = torch.tensor([[0.1, -0.2, 0.05, 0.3]], dtype=torch.float32)
tgt_fix = torch.tensor([[1.0, 0.0]])
for _ in range(10):
loss = torch.nn.MSELoss()(online(obs_fix), tgt_fix)
opt.zero_grad(); loss.backward(); opt.step()
np.random.seed(1)
s = torch.tensor(np.random.randn(BATCH, 4).astype(np.float32))
sn = torch.tensor(np.random.randn(BATCH, 4).astype(np.float32))
r = torch.tensor([1.0, 0.5, 0.0, -1.0])
dn = torch.tensor([0.0, 0.0, 1.0, 0.0])
with torch.no_grad():
qn_online_sn = online(sn)
qn_target_sn = target(sn)
argmax_online = qn_online_sn.argmax(dim=1)
argmax_target = qn_target_sn.argmax(dim=1)
print("argmax_online =", argmax_online.tolist())
print("argmax_target =", argmax_target.tolist())
# vanilla: target net argmax + eval
y_vanilla = r + GAMMA * (1 - dn) * qn_target_sn.max(dim=1).values
# double: argmax from online, eval with target
qn_double = qn_target_sn.gather(1, argmax_online.unsqueeze(1)).squeeze(1)
y_double = r + GAMMA * (1 - dn) * qn_double
print("y_vanilla =", y_vanilla)
print("y_double =", y_double)
print("diff =", f"{(y_vanilla - y_double).abs().mean().item():.4f}")V(s) = tensor([0.0934])
A(s, .) = tensor([-0.0543, 0.1070])
Q(s, .) = tensor([0.0127, 0.1740])
argmax_online = [0, 0, 0, 0]
argmax_target = [1, 1, 1, 1]
y_vanilla = tensor([ 1.0942, 0.6892, 0.0000, -0.8908])
y_double = tensor([ 1.0006, 0.5723, 0.0000, -1.0889])
diff = 0.1021Membaca demo dueling dan double#
Pada demo dueling, adalah nilai state, sedangkan adalah keunggulan tiap aksi relatif terhadap rata-rata. Penggabungan menghasilkan , yang memang merupakan jumlah nilai-nilai tersebut setelah pengurangan rata-rata. Pemisahan ini memungkinkan jaringan belajar nilai state tanpa mengaitkannya ke setiap aksi, yang membantu ketika banyak aksi memberi efek mirip.
Pada demo Double DQN, argmax menurut online network adalah , sedangkan argmax menurut target network adalah . Perbedaan ini langsung memengaruhi target: versus , dengan rata-rata selisih mutlak 0,1021. Vanilla DQN mengevaluasi aksi terbaik menurut target network sendiri, yang cenderung overestimasi, sedangkan Double DQN memilih aksi dengan online network lalu mengevaluasinya dengan target network, sehingga estimasi target lebih konservatif.
Kesalahan umum#
Menganggap Double DQN selalu menghasilkan target lebih rendah. Penyebabnya, hasil demo yang menunjukkan digeneralisasi. Akibatnya, bias arah dipakai sebagai diagnostik padahal arahnya bergantung pada kebetulan bobot. Perbaikannya, evaluasi Double DQN melalui rata-rata selisih pada banyak batch, bukan satu contoh, dan jangan menganggap arah tetap.
Lupa mengurangkan rata-rata keunggulan pada dueling head. Penyebabnya, tanpa pengurangan . Akibatnya, dan tidak teridentifikasi unik, sehingga pelatihan tidak stabil karena banyak pasangan memberi yang sama. Perbaikannya, kurangi rata-rata sepanjang dimensi aksi, atau gunakan bentuk identifikasi lain seperti maksimum.
Ringkasan#
- Double DQN memisahkan pemilihan aksi ( dari online network) dari evaluasinya (nilai dari target network), mengurangi overestimasi akibat operator pada estimasi berderau.
- Dueling head memecah , memungkinkan jaringan belajar nilai state tanpa mengaitkannya ke setiap aksi.
- Pada demo, argmax online dan target berbeda sepenuhnya versus , menghasilkan rata-rata selisih target 0,1021 antara vanilla dan Double DQN.
Referensi#
- Deep Reinforcement Learning with Double Q-learning — van Hasselt et al. 2016 — Makalah Double DQN yang memisahkan pemilihan dan evaluasi aksi.
- Dueling Network Architectures for Deep Reinforcement Learning — Wang et al. 2016 — Makalah dueling head yang memperkenalkan dekomposisi dan .