🎮 Reinforcement Learning · Lecture 10 of 21

Deep Q-Networks (DQN): Human-Level Atari from Pixels

In 2013–2015 DeepMind's DQN learned to play dozens of Atari games from raw pixels with one algorithm. We dissect the Q-network, experience replay, target networks and preprocessing, and implement DQN for CartPole.

In 2015 a paper in Nature by Mnih et al. at DeepMind reported an agent that learned to play 49 Atari 2600 games directly from screen pixels and the score, using the same architecture and hyperparameters for every game, and reached a level comparable to a professional human games tester on many of them. The Deep Q-Network (DQN) launched the field of deep reinforcement learning. Its key ideas were not a new learning rule — it is Q-learning — but the engineering that made Q-learning stable with deep neural networks.

The Q-network#

A convolutional network takes a state (a stack of the last 4 preprocessed $84 \times 84$ grayscale frames, to capture motion) and outputs a Q-value for each of the possible actions (up to 18 joystick actions). Loss for a transition $(s, a, r, s')$:

$$ \mathcal{L}(\theta) = \Big(\underbrace{r + \gamma\max_{a'}Q(s', a'; \theta^-)}_{\text{target}} - Q(s, a; \theta)\Big)^2 $$

Stabiliser 1: experience replay#

Consecutive transitions are highly correlated, and the data distribution shifts as the policy changes — both bad for stochastic gradient descent. DQN stores transitions in a large replay buffer (1 million transitions) and trains on random mini-batches from it. Benefits:

  • breaks temporal correlations (closer to i.i.d. data);
  • reuses each transition many times (data efficiency);
  • smooths the training distribution over many past policies.

This is possible because Q-learning is off-policy.

Stabiliser 2: target network#

The target depends on the same network being updated — a moving target that can chase itself into divergence. DQN computes targets with a separate target network $Q(\cdot; \theta^-)$, a copy of the online network updated only every $C$ steps (e.g. 10,000). Many implementations instead use soft updates $\theta^- \leftarrow \tau\theta + (1 - \tau)\theta^-$ with small $\tau$.

Other important details#

  • Reward clipping to $[-1, 1]$ across games (so one learning rate works everywhere).
  • Frame skipping: repeat each action for 4 frames.
  • ε-greedy exploration annealed from 1.0 to 0.1 over the first million frames.
  • Huber loss (or gradient clipping) for robustness to large TD errors.
  • RMSProp optimiser (Adam in later work).

DQN for CartPole#

python
import random, collections
import numpy as np
import torch, torch.nn as nn, torch.nn.functional as F
import gymnasium as gym

env = gym.make("CartPole-v1")
obs_dim, n_actions = env.observation_space.shape[0], env.action_space.n
q = nn.Sequential(nn.Linear(obs_dim, 128), nn.ReLU(), nn.Linear(128, 128), nn.ReLU(), nn.Linear(128, n_actions))
q_target = nn.Sequential(nn.Linear(obs_dim, 128), nn.ReLU(), nn.Linear(128, 128), nn.ReLU(), nn.Linear(128, n_actions))
q_target.load_state_dict(q.state_dict())
opt = torch.optim.Adam(q.parameters(), lr=5e-4)
buffer = collections.deque(maxlen=50_000)
gamma, batch, steps = 0.99, 64, 0

def act(s, eps):
    if random.random() < eps:
        return env.action_space.sample()
    with torch.no_grad():
        return int(q(torch.as_tensor(s, dtype=torch.float32)).argmax())

for episode in range(400):
    s, _ = env.reset(seed=episode); done, ret = False, 0.0
    while not done:
        eps = max(0.05, 1.0 - steps / 10_000)             # annealed exploration
        a = act(s, eps)
        s2, r, term, trunc, _ = env.step(a); done = term or trunc
        buffer.append((s, a, r, s2, float(term))); s = s2; ret += r; steps += 1
        if len(buffer) >= 1_000:
            S, A, R, S2, D = map(np.array, zip(*random.sample(buffer, batch)))
            S, S2 = torch.tensor(S, dtype=torch.float32), torch.tensor(S2, dtype=torch.float32)
            A, R, D = torch.tensor(A), torch.tensor(R, dtype=torch.float32), torch.tensor(D)
            with torch.no_grad():
                target = R + gamma * (1 - D) * q_target(S2).max(1).values     # bootstrap only if not terminal
            pred = q(S).gather(1, A.unsqueeze(1)).squeeze(1)
            loss = F.smooth_l1_loss(pred, target)
            opt.zero_grad(); loss.backward(); nn.utils.clip_grad_norm_(q.parameters(), 10); opt.step()
        if steps % 500 == 0:
            q_target.load_state_dict(q.state_dict())       # periodic target update
    if episode % 50 == 0:
        print(f"episode {episode:3d} return {ret:5.0f} eps {eps:.2f}")

Note that we bootstrap only when the episode did not terminate: at a true terminal state the future value is zero, while at a time-limit truncation we still bootstrap.

What DQN learned — and could not#

DQN excelled at reactive games (Breakout — where it famously learned to tunnel through the wall — Pong, Space Invaders) but failed on games requiring long-horizon exploration with sparse rewards, notably Montezuma's Revenge, where random exploration almost never finds the first reward. That failure motivated research into intrinsic motivation and curiosity-driven exploration.

JA
Written by

Janin A Apurba

B.Sc. in CSE, AUST · Advanced ICT Officer, CNRS-UNHCR. Teaching AI, ML and Deep Learning to the next generation of engineers and researchers.

Keep learning

Related lectures

🎮 Reinforcement Learning

Function Approximation in RL: From Tables to Neural Networks

Tabular methods cannot scale to large or continuous state spaces. We replace tables with parameterised functions, derive semi-gradient TD, discuss linear features and tile coding, and understand the deadly triad that makes deep RL unstable.

Advanced⏱ 4 min#229
🎮 Reinforcement Learning

Improving DQN: Double, Dueling, Prioritised Replay and Rainbow

A series of improvements fixed DQN's weaknesses: overestimation, inefficient replay, poor value decomposition and myopic returns. We study each idea and how Rainbow combined six of them.

Advanced⏱ 5 min#231
🎮 Reinforcement Learning

Multi-Armed Bandits: The Exploration–Exploitation Dilemma

Bandits are RL without states — the purest form of the exploration problem. We compare ε-greedy, optimistic initialisation, UCB and Thompson sampling, define regret, and look at contextual bandits for real decisions.

Intermediate⏱ 5 min#228