Reinforcement LearningQ-LearningDQNRainbow DQNDeep RL

From Tabular Q-Learning to Rainbow: A Complete Tour of Value-Based Reinforcement Learning

Every major value-based RL technique explained in order of invention — tabular Q-learning, DQN, Double DQN, Dueling DQN, prioritized replay, multi-step returns, distributional RL, noisy networks — culminating in Rainbow, plus where each technique gets used in practice.

Problem Statement

Value-based reinforcement learning tries to learn a function Q(s, a) — the expected future reward of taking action a in state s and acting optimally afterward — without ever learning an explicit policy. Once you have a good Q, the policy is just “pick the action with the highest Q(s, a).” Almost every technique in this article is a different answer to the same question: how do you estimate Q accurately when the state space is huge, the targets you’re regressing against are themselves moving, and your own estimator is biased?

Tabular Q-Learning

The original algorithm (Watkins, 1989) stores Q(s, a) explicitly in a table and updates it with the Bellman equation after every step:

def q_learning_update(Q, s, a, r, s_next, alpha=0.1, gamma=0.99):
    best_next = max(Q[s_next][a2] for a2 in actions(s_next))
    td_target = r + gamma * best_next
    td_error = td_target - Q[s][a]
    Q[s][a] += alpha * td_error
    return Q

This is off-policy: it learns the value of the greedy policy (max over next actions) regardless of what action was actually taken to explore. That off-policy property is what every deep extension below inherits. The catch is obvious — a table can’t represent continuous or high-dimensional states like raw pixels.

Deep Q-Networks (DQN)

DQN (Mnih et al., 2015) replaces the table with a neural network Q(s, a; θ) and adds two stabilizers that made deep value-based RL actually work:

def dqn_loss(q_net, target_net, batch, gamma=0.99):
    s, a, r, s_next, done = batch
    q_values = q_net(s).gather(1, a)
    with torch.no_grad():
        next_q = target_net(s_next).max(dim=1).values
        td_target = r + gamma * next_q * (1 - done)
    return F.mse_loss(q_values.squeeze(), td_target)

Double DQN: Fixing Overestimation Bias

Vanilla DQN uses the same network to both pick the best next action and evaluate it (max over target_net(s_next)), which systematically overestimates Q-values — noisy estimates get picked precisely because they’re inflated. Double DQN (van Hasselt et al., 2016) decouples selection from evaluation: the online network chooses the action, the target network evaluates it.

def double_dqn_target(q_net, target_net, s_next, r, done, gamma=0.99):
    best_action = q_net(s_next).argmax(dim=1)              # selection: online network
    next_q = target_net(s_next).gather(1, best_action.unsqueeze(1)).squeeze()
    return r + gamma * next_q * (1 - done)                  # evaluation: target network

Dueling DQN: Separating Value from Advantage

Dueling networks (Wang et al., 2016) split the Q-function into two streams that share a feature backbone: a scalar state value V(s) and a per-action advantage A(s, a), recombined as:

Q(s, a) = V(s) + (A(s, a) - mean_a' A(s, a'))
graph TD
    A[Shared Conv / Feature Backbone] --> B[Value Stream V of s]
    A --> C[Advantage Stream A of s,a]
    B --> D[Combine: Q = V + A - mean of A]
    C --> D
    D --> E[Q-values per action]

    style D fill:#6366f1,color:#fff

The insight: in many states, the choice of action barely matters (e.g., no obstacle is nearby), so the network can learn V(s) from every step regardless of which action was taken, rather than needing to see every action tried in every state to learn its value.

Prioritized Experience Replay

Uniform sampling from the replay buffer wastes time on transitions the network already predicts well. Prioritized Experience Replay (PER) (Schaul et al., 2016) samples transitions with probability proportional to their TD-error magnitude — i.e., learn more from your biggest mistakes — with importance-sampling weights added to correct the bias this introduces.

Multi-Step Learning

Instead of bootstrapping off a single-step reward, n-step returns sum actual rewards over n steps before bootstrapping off the target network:

G_t^(n) = r_t + γr_{t+1} + ... + γ^(n-1)r_{t+n-1} + γ^n max_a Q(s_{t+n}, a)

This propagates reward signal through the network faster (less waiting for bootstrapping to slowly diffuse value backward one step at a time), at the cost of slightly higher variance for larger n.

Distributional RL (C51)

Instead of learning the expected return as a single number, Categorical DQN / C51 (Bellemare et al., 2017) learns the full distribution of possible returns, represented as probabilities over a fixed set of 51 discrete support atoms. Two states with the same expected value but very different risk profiles (a sure +10 vs. a 50/50 shot at +20 or 0) look identical to standard DQN — C51 tells them apart, and empirically this richer learning signal improves performance even when you only ever act greedily on the mean.

Noisy Networks

Instead of the usual epsilon-greedy exploration (act randomly with some fixed probability), NoisyNet (Fortunato et al., 2018) adds learnable parametric noise directly to the network’s weights. The amount of noise per-layer is itself a trained parameter, so the network learns to explore more in states where it’s uncertain and less where it’s confident — state-conditioned exploration for free, with no exploration schedule to tune.

Rainbow: Combining All Six

Rainbow DQN (Hessel et al., 2018) combines Double DQN, Dueling networks, Prioritized Experience Replay, Multi-step learning, Distributional RL (C51), and Noisy Networks into a single agent. Each component targets a different failure mode of vanilla DQN, and the paper’s ablations showed the combination outperforms any single component alone.

graph LR
    A[DQN] --> B[+ Double Q: fix overestimation]
    B --> C[+ Dueling: separate V and A]
    C --> D[+ PER: sample by TD-error]
    D --> E[+ Multi-step: faster credit assignment]
    E --> F[+ Distributional/C51: model return distribution]
    F --> G[+ NoisyNet: learned exploration]
    G --> H[Rainbow DQN]

    style H fill:#10b981,color:#fff

Summary Table

TechniqueProblem SolvedCore Mechanism
Tabular Q-LearningBaseline value learningExplicit table + Bellman update
DQNFunction approximation at scaleNeural net + replay buffer + target network
Double DQNOverestimation biasDecouple action selection from evaluation
Dueling DQNSample inefficiency when action rarely mattersSplit into V(s) and A(s,a) streams
Prioritized ReplayWasted gradient steps on easy transitionsSample by TD-error magnitude
Multi-Step LearningSlow reward propagationn-step bootstrapped returns
Distributional RL (C51)Loses information by only learning the meanLearn full return distribution
Noisy NetworksInefficient fixed-schedule explorationLearnable noise on network weights
Rainbow DQNAll of the above at onceCombines all six techniques

Applications

Key Learnings

  1. Every extension targets a specific, named failure mode — overestimation (Double), sample inefficiency (Dueling, PER), slow credit assignment (multi-step), and poor exploration (NoisyNet). Understanding the family means understanding what each piece is a fix for.
  2. Distributional RL is the most underrated idea here. Learning the full return distribution instead of its mean gives strictly more information to the network for free, and it stacks cleanly with every other technique.
  3. Value-based methods are naturally suited to discrete action spaces. For continuous control, the actor-critic family (a separate article on this site covers it in full) is the better fit — Q-learning still applies, but you can no longer take an explicit max over an infinite action space.