পাঠ ১৩ · ৩২-এর মধ্যে · মডিউল ২

Deep Q-Network — Atari থেকে general AI-এর পথে

Deep Q-Network — DeepMind's 2013/2015 breakthrough
৯ মিনিট পড়া উচ্চ · Advanced PyTorch

এই পাঠে যা শিখবেন

  • DQN architecture — CNN, Q-output
  • Loss function — Bellman target with target net
  • Training pipeline — replay, ε-decay, gradient clipping
  • PyTorch-এ scratch থেকে DQN

১ · Tabular Q-learning-এর সীমা

Atari-এ একটি screen frame — ২১০×১৬০ pixel × ৩ channel = ১০০,৮০০ মাত্রার vector। 4-frame stack — ৪০৩,২০০। possible state — astronomical।

Q-table impossible — তাই function approximation: $Q(s, a) \approx Q_\theta(s, a)$ where $\theta$ = neural network parameters।

২ · Neural network architecture

Mnih et al. (2015) — original DQN architecture:

  • Input: 84×84×4 (gray-scaled, downsampled, 4 frame stacked)।
  • Conv1: 32 filter, 8×8, stride 4, ReLU।
  • Conv2: 64 filter, 4×4, stride 2, ReLU।
  • Conv3: 64 filter, 3×3, stride 1, ReLU।
  • FC: 512 unit, ReLU।
  • Output: $|\mathcal{A}|$ unit (one Q-value per action)।
Critical design choice

Output one Q per action — single forward pass-এ সব Q। Action selection trivial — argmax। alternative (Q(s, a) input) — $|A|$ forward pass, slow।

৩ · DQN loss function

$$\mathcal{L}(\theta) = \mathbb{E}_{(s, a, r, s') \sim D} \left[ \left( r + \gamma \max_{a'} Q_{\theta^-}(s', a') - Q_\theta(s, a) \right)^2 \right]$$

$\theta^-$ — target network parameters (slow-update copy of $\theta$)। $D$ — replay buffer।

৪ · Experience replay

Each transition $(s, a, r, s', d)$ store হয় buffer-এ (বড় queue, ~1M)। Update — random batch sampling।

  • Decorrelation: consecutive frame highly correlated। random sample i.i.d. assumption-এর কাছাকাছি।
  • Sample efficiency: এক transition বহুবার ব্যবহার।
  • Stability: training distribution stable।

৫ · Target network

Bellman target $r + \gamma \max Q_\theta(s', a')$-এ $\theta$ same। প্রতি update-এ target shift — moving target chase।

Solution: separate target net $Q_{\theta^-}$ — every $C$ steps copy $\theta^- \leftarrow \theta$।

Soft update (DDPG, SAC-এ): $\theta^- \leftarrow \tau \theta + (1-\tau) \theta^-$, $\tau \approx 0.005$।

DQN Architecture & Training Loop 4 frames 84×84×4 CNN 3 conv layers FC 512 ReLU Q(s, ·) |A| outputs Q(s, a₁) = 3.2 Q(s, a₂) = 5.1 ★ Action argmax / ε-greedy Replay Buffer ~1M (s,a,r,s',d) Target Net Q_{θ⁻} copy of θ every C steps SGD Loss (target − Q)² → ∇θ Loss = E[(r + γ·max Q_{θ⁻}(s',·) − Q_θ(s,a))²] Random batch from buffer; bootstrap from target net; gradient on θ only. এই তিনটি trick (replay + target + Bellman target) — Atari breakthrough-এর core।
DQN-এর full architecture ও training loop। CNN → FC → Q-values + replay buffer + target net।

৬ · PyTorch DQN — minimal implementation

Python · PyTorch DQN
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from collections import deque
import random

class DQN(nn.Module):
    def __init__(self, state_dim, n_actions):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, 128), nn.ReLU(),
            nn.Linear(128, 128), nn.ReLU(),
            nn.Linear(128, n_actions),
        )
    def forward(self, x):
        return self.net(x)

# Hyperparameters
gamma = 0.99
lr = 1e-3
batch_size = 64
buffer = deque(maxlen=10000)
target_update_freq = 100

# Network + target
state_dim = 4   # CartPole-এর জন্য
n_actions = 2
q_net = DQN(state_dim, n_actions)
target_net = DQN(state_dim, n_actions)
target_net.load_state_dict(q_net.state_dict())
optimizer = optim.Adam(q_net.parameters(), lr=lr)

def select_action(state, eps):
    if random.random() < eps: return random.randint(0, n_actions-1)
    with torch.no_grad():
        q = q_net(torch.FloatTensor(state))
        return q.argmax().item()

def update(step):
    if len(buffer) < batch_size: return
    batch = random.sample(buffer, batch_size)
    s, a, r, s_next, done = zip(*batch)
    s = torch.FloatTensor(np.array(s))
    a = torch.LongTensor(a)
    r = torch.FloatTensor(r)
    s_next = torch.FloatTensor(np.array(s_next))
    done = torch.FloatTensor(done)

    q_pred = q_net(s).gather(1, a.unsqueeze(1)).squeeze()
    with torch.no_grad():
        q_target = r + gamma * (1 - done) * target_net(s_next).max(1)[0]
    loss = nn.MSELoss()(q_pred, q_target)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    if step % target_update_freq == 0:
        target_net.load_state_dict(q_net.state_dict())

print("DQN model + training loop ready!")

    
এটাই minimal DQN — production-এ আরও tricks (gradient clip, learning rate schedule, prioritized replay)। CartPole-এর জন্য ~১০০-৫০০ episode-এ converge।

৭ · CartPole-এ training loop

Python · DQN training
import gym
env = gym.make("CartPole-v1")
n_episodes = 300
eps_start, eps_end, eps_decay = 1.0, 0.05, 0.995
eps = eps_start
total_steps = 0
returns = []

for ep in range(n_episodes):
    state, _ = env.reset()
    G = 0
    while True:
        action = select_action(state, eps)
        next_state, reward, terminated, truncated, _ = env.step(action)
        done = terminated or truncated
        buffer.append((state, action, reward, next_state, float(done)))
        update(total_steps)
        G += reward
        state = next_state
        total_steps += 1
        if done: break
    returns.append(G)
    eps = max(eps_end, eps * eps_decay)
    if ep % 20 == 0:
        print(f"Ep {ep:3d} | return={G:6.1f} | eps={eps:.3f}")

print(f"\nFinal avg return (last 30): {np.mean(returns[-30:]):.1f}")

    
৩০০ episode-এ — return 100+ থেকে 500-এর কাছে। CartPole solved (avg return ≥ 195)।

৮ · Atari-এর success

  • ৪৯ Atari game — single architecture, hyperparameter tuning ছাড়া।
  • ২৯ গেমে human-level বা better।
  • End-to-end pixel → action — feature engineering নেই।
  • Nature 2015 cover paper — RL community-তে landmark।

৯ · DQN-এর দুর্বলতা

  • Maximization bias — Q overestimate (পাঠ ১৫: Double DQN solve)।
  • Sample inefficient — Atari-এ ~200M frame।
  • Sparse reward problem (Montezuma's Revenge fail)।
  • Discrete action — continuous control-এ adapt লাগে।

এই দুর্বলতা-গুলো address করার চেষ্টা — Double DQN, Dueling DQN, Rainbow, R2D2 — পরের পাঠে।

ভাবনার প্রশ্ন

প্র ০১DQN-এ frame stacking কেন? এবং কেন ৪ frame?

Single Atari frame — Markov নয়। একটি frame-এ ball-এর position দেখা যায়, কিন্তু velocity (direction) infer করা যায় না।

Frame stacking: ৪ consecutive frame stack করে — implicit velocity ও acceleration recover। Markov property approximate।

কেন ৪:

  • ২ frame — velocity পায়।
  • ৩ frame — acceleration।
  • ৪ frame — jerk (second-order acceleration)।
  • ৫+ — diminishing returns, computational cost বাড়ে।

Frame skip: DQN আসলে ৪ frame skip করে (action-এ stick) — gameplay-এ slow-changing temporal structure capture। তাই আসলে ১৬ raw frame interval।

Modern alternatives:

  • RNN/LSTM — temporal info implicitly।
  • Transformer — attention over history।
  • R2D2 (DeepMind) — recurrent DQN, state-of-art।
প্র ০২Replay buffer ছাড়া DQN training কেন ভেঙে পড়ে?

Replay buffer ছাড়া — agent online consecutive transitions train করে। তিনটি সমস্যা:

(১) High correlation:

  • Consecutive frame near-identical। gradient highly correlated।
  • SGD-এর i.i.d. assumption নষ্ট।
  • Estimate variance বিশাল।

(২) Catastrophic forgetting:

  • Agent এক region-এ stuck — train শুধু সেই region-এ।
  • NN অন্য region-এর Q ভুলে যায়।
  • Subsequently অন্য region-এ গেলে — performance ভাঙে।

(৩) Distribution shift:

  • Policy improve হলে — visited state distribution shift।
  • Old data drop হলে — learning unstable।

Replay-এর সমাধান:

  • Random sample — i.i.d. approximate।
  • Old data preserve — forget কম।
  • Sample reuse — efficient।

Empirical: DQN paper-এ ablation — without replay, half the games fail।

প্র ০৩Target network frequency C — সঠিক value কী? trade-off?

$C$ = target network update interval (steps)।

Original DQN: $C = 10,000$ steps। Atari-এ standard।

Trade-off:

  • $C$ small (e.g., 100):
    • Pros: target $\theta$-এর কাছাকাছি — actual Bellman target close।
    • Cons: target shift dramatically, instability।
  • $C$ large (e.g., 100,000):
    • Pros: stable target — supervised-like training।
    • Cons: stale — old data-এর উপর train।

Soft update (Polyak averaging):

$\theta^- \leftarrow \tau \theta + (1 - \tau) \theta^-$, $\tau \approx 0.005$।

DDPG, SAC এই use করে — implicit slow update। smoother।

Practical: tune দিয়ে empirical optimum। CartPole-এ $C=100$, Atari-এ $10K$।

প্র ০৪DQN কেন continuous action space-এ direct ব্যবহার করা যায় না?

DQN-এর action selection — $\arg\max_a Q(s, a)$।

Discrete action: finite set, max evaluate করা trivial — সব Q-output check।

Continuous action: infinite — argmax solve করা optimization problem।

Approaches:

  • Discretization: action space discretize ($k$ bin per dim)। curse of dimensionality — ৭-DOF arm-এ $k^7$।
  • Sample-based (CEM, Cross-Entropy): action সম্ভাবনা থেকে sample, max approximate।
  • Continuous Q (NAF): $Q(s, a)$ structurally quadratic — analytical max।
  • Deterministic policy gradient (DDPG): separate actor net, $a = \mu_\phi(s)$, jointly trained।
  • SAC: stochastic policy, max entropy framework।

Modern continuous control: DDPG, TD3, SAC, PPO — DQN-এর descendants। architecture বদলে মূল DQN ideas (replay, target net) preserve।

অনুশীলন

  1. Loss compute: batch-এ এক sample — $Q_\theta(s, a) = 5$, $r = 1$, $\max Q_{\theta^-}(s', \cdot) = 8$, $\gamma=0.99$, not done। loss $(target - Q)^2 = ?$

    Target = $1 + 0.99 \cdot 8 = 8.92$। Loss = $(8.92 - 5)^2 = 15.37$।

  2. Param count: CartPole DQN — input 4, hidden 128-128, output 2। মোট parameter?

    4×128 + 128 + 128×128 + 128 + 128×2 + 2 = 512 + 128 + 16384 + 128 + 256 + 2 = ১৭,৪১০।

  3. Modify code: উপরের DQN-এ Huber loss (smooth L1) ব্যবহার করুন। কেন স্থিতিশীলতা বাড়ে?
    loss = nn.SmoothL1Loss()(q_pred, q_target)

    Huber: small error-এ MSE-এর মত quadratic, large error-এ linear। Outlier transition (large δ) gradient explode না — DQN-এর standard।

আরও পড়ুন

পূর্ববর্তী পাঠ
পাঠ ১২ · Q-learning & SARSA