DQN ແລະ Double DQN
ເຮັດໃຫ້ RL ຄ້າຍກັບ supervised learning ຫຼາຍຂຶ້ນ.
DQN ເພີ່ມ 2 ເທັກນິກໃຫ້ NFQ ເພື່ອໃຫ້ຮຽນໄດ້ສະຖຽນ: replay buffer ແລະ target network. Double DQN ແກ້ບັນຫາການປະເມີນຄ່າສູງເກີນ ໂດຍແຍກໜ້າທີ່ເລືອກ ແລະ ປະເມີນ action.
ຄະແນນສູງສຸດຂອງ CartPole ແມ່ນ 500. ສັງເກດວ່າຄະແນນຂຶ້ນລົງບໍ່ໝັ້ນຄົງ ເຊິ່ງເປັນລັກສະນະປົກກະຕິຂອງ DRL: ລອງປ່ຽນ seed ຫຼື learning rate ແລ້ວປຽບທຽບເບິ່ງ. ໂຄດນີ້ໃຊ້ເວລາລັນປະມານ 2–5 ນາທີ.
Replay bufferເກັບປະສົບການ (s, a, r, s', done) ໄວ້ຈຳນວນຫຼາຍ ແລ້ວສຸ່ມ mini-batch ມາຮຽນ ເຮັດໃຫ້ຂໍ້ມູນໃກ້ IID ຂຶ້ນ
Target networkສຳເນົາ network ໄວ້ໃຊ້ຄິດໄລ່ target. ອັບເດດເປັນໄລຍະ ເຮັດໃຫ້ target ບໍ່ເຄື່ອນທີ່ທຸກກ້າວ
DQN : y = r + γ · max_a' Q_target(s', a')
DDQN : y = r + γ · Q_target(s', argmax_a' Q_online(s', a'))
Huber loss ເປັນ MSE ເມື່ອ error ນ້ອຍ ແລະ ເປັນ MAE ເມື່ອ error ໃຫຍ່ ຈຶ່ງບໍ່ໃຫ້ gradient ລະເບີດ.
ສິ່ງທີ່ຄວນຈື່
- Replay buffer ແກ້ບັນຫາ IID, target network ແກ້ບັນຫາ target ເຄື່ອນທີ່
- Double DQN ປ່ຽນໂຄດພຽງ 1–2 ແຖວ ແຕ່ຊ່ວຍໄດ້ຫຼາຍ
import random, collections
import numpy as np, torch, torch.nn as nn
import gymnasium as gym
env = gym.make("CartPole-v1")
n_obs, n_act = env.observation_space.shape[0], env.action_space.n
torch.manual_seed(0); random.seed(0)
def make_net():
return nn.Sequential(nn.Linear(n_obs, 128), nn.ReLU(),
nn.Linear(128, 128), nn.ReLU(),
nn.Linear(128, n_act)) # state in -> Q ທຸກ action out
online, target = make_net(), make_net()
target.load_state_dict(online.state_dict()) # target network
opt = torch.optim.Adam(online.parameters(), lr=1e-3)
buffer = collections.deque(maxlen=50_000) # replay buffer
gamma, batch_size, step = 0.99, 64, 0
for ep in range(400):
s, _ = env.reset(seed=ep)
done, ep_return = False, 0.0
while not done:
eps = max(0.05, 1.0 - step / 10_000) # epsilon ຫຼຸດແບບເສັ້ນຊື່
if random.random() < eps:
a = env.action_space.sample()
else:
with torch.no_grad():
a = int(online(torch.tensor(s)).argmax())
s2, r, term, trunc, _ = env.step(a)
buffer.append((s, a, r, s2, term))
s, done, ep_return, step = s2, term or trunc, ep_return + r, step + 1
if len(buffer) >= 1000:
batch = random.sample(buffer, batch_size) # ສຸ່ມ → ຂໍ້ມູນໃກ້ IID
S, A, R, S2, T = (torch.tensor(np.array(x)) for x in zip(*batch))
with torch.no_grad():
a2 = online(S2).argmax(1, keepdim=True) # Double DQN:
q2 = target(S2).gather(1, a2).squeeze(1) # online ເລືອກ, target ປະເມີນ
y = R.float() + gamma * q2 * (1 - T.float())
q = online(S).gather(1, A.long().unsqueeze(1)).squeeze(1)
loss = nn.functional.huber_loss(q, y) # Huber loss
opt.zero_grad(); loss.backward(); opt.step()
if step % 200 == 0:
target.load_state_dict(online.state_dict()) # ອັບເດດ target ເປັນໄລຍະ
if ep % 25 == 0:
print(f"ເກມທີ {ep:3d} ຄະແນນ {ep_return:5.0f}")
ຜົນລັບ
ເກມທີ 0 ຄະແນນ 12 ເກມທີ 25 ຄະແນນ 20 ເກມທີ 50 ຄະແນນ 15 ເກມທີ 75 ຄະແນນ 20 ເກມທີ 100 ຄະແນນ 42 ເກມທີ 125 ຄະແນນ 12 ເກມທີ 150 ຄະແນນ 128 ເກມທີ 175 ຄະແນນ 17 ເກມທີ 200 ຄະແນນ 128 ເກມທີ 225 ຄະແນນ 108 ເກມທີ 250 ຄະແນນ 136 ເກມທີ 275 ຄະແນນ 148 ເກມທີ 300 ຄະແນນ 500 ເກມທີ 325 ຄະແນນ 500 ເກມທີ 350 ຄະແນນ 89 ເກມທີ 375 ຄະແນນ 242
- #DeepRL
- #ReinforcementLearning
- #PythonສຳລັບຄົນLaos
- #AI
- #ບອກຕໍ່ຄວາມຮູ້