Deep Q-Network — Atari থেকে general AI-এর পথে
এই পাঠে যা শিখবেন
- 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)।
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$।
৬ · PyTorch DQN — minimal implementation
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!")
৭ · CartPole-এ training loop
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}")
৮ · 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।
অনুশীলন
-
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$।
-
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 = ১৭,৪১০।
-
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।
আরও পড়ুন
- পাঠ ১৪ · Experience Replay & Target Net পরবর্তী পাঠDQN-এর দু'টি pillar deeper।
- পাঠ ১২ · Q-learning & SARSA আগের পাঠDQN-এর tabular root।
- পাঠ ১৫ · Double & Dueling DQN এই পাঠের সাথে সম্পর্কিতDQN extensions।
- সব AI Courses ABCL TECHPython, ML, DL, NLP, CV, GenAI, RL — সব একসাথে।