Skip to content
AI360Xpert
Beta

Double DQN (DDQN)

Let one network point at the best action and a second, slower network say what it is worth, so lucky noise cannot vote for itself twice.

The online net picks action a1 while the target net scores it at minus 0.20, giving 0.82 instead of the coupled 1.54
The online net picks action a1 while the target net scores it at minus 0.20, giving 0.82 instead of the coupled 1.54

Why Does This Exist?

Deep Q-networks learn from noisy estimates. Each update samples a minibatch from experience replay, backpropagates through a network, and bootstraps from its own predictions. Write the estimate as truth plus noise:

Q(s,a;θ)=q⋆(s,a)+ϵ(s,a)Q(s, a; \theta) = q_{\star}(s, a) + \epsilon(s, a)

Here q⋆q_{\star} is the true value, θ\theta holds the online weights, and ϵ\epsilon is zero mean noise. The standard target takes a max over noisy values:

yDQN=r+γmax⁡aQ(s′,a;θ−)y^{\text{DQN}} = r + \gamma \max_{a} Q(s^{\prime}, a; \theta^{-})

Here rr is the reward, γ\gamma the discount, s′s^{\prime} the next state, and θ−\theta^{-} the target network weights. The max keeps whichever noise draw is largest, so its expectation sits above the true max. That gap is maximization bias, and the twin tabular version is covered in maximization bias and double Q-learning. Over thousands of steps the inflation compounds and policies wobble.

Double DQN (van Hasselt, Guez, and Silver, 2015) splits the job across the two networks you already keep. The online net picks, the target net scores:

yDDQN=r+γQ(s′,arg⁡max⁡aQ(s′,a;θ);θ−)y^{\text{DDQN}} = r + \gamma Q(s^{\prime}, \arg\max_{a} Q(s^{\prime}, a; \theta); \theta^{-})

No third network, no extra memory. This page shows why the split removes the bias and works the arithmetic on one transition.

Think of It Like This

A scout and a valuer at an auction

You send two people to an auction. The scout walks the floor fast and points at the lot that looks most promising. The valuer arrives later, inspects only that lot, and names a price from a separate notebook.

If one person does both jobs, trouble follows. They fall in love with a lot because their first glance was generous, then they quote that same generous glance as the price. Every ledger entry drifts upward.

Split the roles and the failure breaks. The scout can still point at a dud, but the valuer has no reason to share that exact misreading, so the price stays honest. The analogy stops at the sync: in Double DQN the valuer copies the scout's notebook every few thousand steps, so the two opinions stay related but never identical at decision time.

How It Actually Works

Name the parts. Selection asks which action looks best. Evaluation asks what that action is worth. Standard DQN lets θ−\theta^{-} answer both. Double DQN lets θ\theta select and θ−\theta^{-} evaluate.

Concretely, for next state s′s^{\prime}:

  • Select with the online net: a⋆=arg⁡max⁡aQ(s′,a;θ)a^{\star} = \arg\max_{a} Q(s^{\prime}, a; \theta).
  • Evaluate with the target net: Q(s′,a⋆;θ−)Q(s^{\prime}, a^{\star}; \theta^{-}).
  • Train θ\theta toward yDDQNy^{\text{DDQN}} with the usual squared error, then copy θ\theta into θ−\theta^{-} every CC steps or blend slowly toward it.

The three designs compare like this:

  • Standard DQN selects with θ−\theta^{-} and evaluates with θ−\theta^{-}, so coupled noise inflates the target.
  • Tabular Double Q-learning keeps two tables and flips a coin each step for who selects and who evaluates, which is unbiased but needs symmetric updates.
  • Double DQN always selects with θ\theta and evaluates with θ−\theta^{-}, reusing the target net, which removes the systematic lift at zero extra cost.

Worked example

Both actions are truly worth 0.00.0 at s′s^{\prime}, so any spread is pure noise. Reward r=1.0r = 1.0 and γ=0.9\gamma = 0.9, giving a true target of 1.001.00. Suppose the nets output:

  • Online θ\theta: Q=[+0.50,−0.30]Q = [+0.50, -0.30] for [a1,a2][a_1, a_2].
  • Target θ−\theta^{-}: Q=[−0.20,+0.60]Q = [-0.20, +0.60] for [a1,a2][a_1, a_2].

Standard DQN selects and scores with θ−\theta^{-}. It picks a2a_2 at +0.60+0.60 and builds 1.0+0.9×0.60=1.541.0 + 0.9 \times 0.60 = 1.54, which overshoots truth by 0.540.54.

Double DQN selects with θ\theta. It picks a1a_1 at +0.50+0.50, then asks θ−\theta^{-} what a1a_1 is worth: −0.20-0.20. The target becomes 1.0+0.9×(−0.20)=1.0−0.18=0.821.0 + 0.9 \times (-0.20) = 1.0 - 0.18 = 0.82. One draw lands low, the next may land high, but the average across draws sits near 1.001.00 instead of floating above it. That centering is the whole trick, and it stacks well with prioritized experience replay.

Code

This snippet rebuilds both targets from the worked numbers. No training loop, just the two selection and evaluation paths.

def dqn_target(reward: float, gamma: float, q_target: list[float]) -> float:    best = max(range(len(q_target)), key=lambda i: q_target[i])    return reward + gamma * q_target[best]

def ddqn_target(reward: float, gamma: float, q_online: list[float], q_target: list[float]) -> float:    best = max(range(len(q_online)), key=lambda i: q_online[i])    return reward + gamma * q_target[best]

def main() -> None:    r, gamma = 1.0, 0.9    q_online = [0.50, -0.30]    q_target = [-0.20, 0.60]
    y_dqn = dqn_target(r, gamma, q_target)    y_ddqn = ddqn_target(r, gamma, q_online, q_target)
    print(f"DQN target: {y_dqn:.2f}")  # -> DQN target: 1.54    print(f"DDQN target: {y_ddqn:.2f}")  # -> DDQN target: 0.82    print(f"truth: 1.00, DQN error: {y_dqn - 1.0:+.2f}")  # -> truth: 1.00, DQN error: +0.54    print(f"truth: 1.00, DDQN error: {y_ddqn - 1.0:+.2f}")  # -> truth: 1.00, DDQN error: -0.18

if __name__ == "__main__":    main()

Watch Out For

Building a third network for Double DQN

Tabular Double Q-learning uses two symmetric estimators, so teams new to the deep version create two online nets plus a target and wire an alternating update. That doubles memory and compute for no gain.

You already own both roles. Freeze the target copy, let the online net pick every action, let the target score it, and sync on schedule. If your code constructs more than two Q-nets for DDQN, delete one.

The Quick Version

  • Noisy estimates plus a max give systematic overestimation, since the max keeps the luckiest noise draw.
  • Standard DQN selects and evaluates with the target net θ−\theta^{-}, which locks the bias in.
  • Double DQN selects with the online net θ\theta and evaluates with θ−\theta^{-}, at zero extra networks.
  • On the worked transition with truth 1.001.00, coupled selection gives 1.541.54 while decoupled selection gives 0.820.82.
  • Across draws the decoupled mean sits near truth instead of above it, which steadies training.