Ainsteinx Logo
Ainsteinx
👤
ยินดีต้อนรับสู่ Ainsteinx
เข้าสู่ระบบ

เปลี่ยนภาษา

สกุลเงิน

หน้าแรก
บล็อก
บริการ
บทที่ 09 · Value-based Deep RL

DQN และ Double DQN

ทำให้ RL คล้าย supervised learning มากขึ้น

DQN เพิ่ม 2 เทคนิคให้ NFQ เพื่อให้เรียนได้เสถียร: replay buffer และ target network ส่วน Double DQN แก้ปัญหาการประเมินค่าสูงเกินไป

คะแนนสูงสุดของ CartPole คือ 500 คะแนนขึ้นลงไม่คงที่เป็นเรื่องปกติของ DRL โค้ดนี้ใช้เวลารันประมาณ 2–5 นาที

Replay bufferเก็บประสบการณ์ไว้มากแล้วสุ่ม mini-batch มาเรียน ทำให้ข้อมูลใกล้ IID
Target networkสำเนา network ไว้คำนวณ 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 บรรทัดแต่ช่วยได้มาก
deep-rl-ep09-dqn.py
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
  • #ບອກຕໍ່ຄວາມຮູ້