Double Q-Learning
One table picks the winner, a second table prices it. Splitting pick and price stops noise from looking like value.
Why Does This Exist?
Q-learning backs up the best next value:
When estimates wobble, max keeps the luckiest wobble. Expected max sits above max of means:
That gap is maximization bias. With many noisy actions the agent chases phantom highs, values drift up, and learning slows. Deep Q-networks inherit the same flaw through the same max.
Double Q-learning splits the job. One estimator picks, the other prices. Noise that won the pick need not win the price. This page covers the split. Solo Q details live in Q-learning. The deep twin target version lives in double DQN.
The one idea you need first
An action value is expected discounted return after taking in . Learning rate blends old guess with new target. Discount weights near rewards over far ones. All updates below use those three symbols only.
Think of It Like This
Two separate house appraisers
You want one rental house. Ask one keen broker to both choose the best listing and name its rent, and the pick will be the listing whose rent guess happened to spike highest. Pick noise and price noise are the same number.
Hire two independent appraisers instead. Appraiser A walks all houses and names one winner. Appraiser B, who never saw the shortlist contest, prices only that winner. A lucky spike in A rarely repeats in B, so the price stays honest.
Where it stops: houses have true market rents. Q values have no fixed tag; both tables keep moving as the policy improves.
How It Actually Works
Keep and . Flip a fair coin each step. On heads update with pricing the pick:
On tails swap roles:
Act with the mean . Because each table learned from different samples, the pricing table is roughly independent of the pick, so its value is centered instead of systematically high.
Worked example
Next state has three actions with true mean each. Current noisy tables read:
Solo Q with backs up:
A phantom gain of where truth is .
Double Q picks with and prices with . Pick is because tops the row. Price is . Backup uses , not . Gap closed is . Reverse direction works the same: would pick at while prices it at . That pair is what the diagram draws.
Code
This snippet repeats the pick and price logic with plain lists. No packages needed.
def solo_target(q: list[float]) -> float: return max(q)
def double_target(q_pick: list[float], q_price: list[float]) -> tuple[int, float]: best = max(range(len(q_pick)), key=lambda i: q_pick[i]) return best, q_price[best]
q1 = [0.40, -0.20, 0.10]q2 = [-0.15, 0.30, -0.05]print(round(solo_target(q1), 2)) # -> 0.4idx, price = double_target(q1, q2)print(idx) # -> 0print(round(price, 2)) # -> -0.15idx2, price2 = double_target(q2, q1)print(idx2) # -> 1print(round(price2, 2)) # -> -0.2Solo target prints . Double pick is index priced at . The reverse pick is index priced at . Both remove the phantom high.
Watch Out For
Thinking two tables need twice the episodes
Newcomers halve updates per table and assume they must collect twice the play. Samples are shared. Each transition updates one table picked at random, so environment cost per step is unchanged. Only memory doubles.
Keep the coin fair at , update exactly one table per step, and act on the mean. If one table lags far behind the other, the coin or the learning rate drifted, not the data budget.
Sharing one target network across both deep heads
In tabular form independence comes from separate samples. In deep form teams often share the encoder or copy one target into both heads. Coupling returns and the bias creeps back.
Keep targets separate, sync them at different times or with different seeds, and log mean gap . When that gap stays positive for many batches, the heads share too much.
The Quick Version
- Max over noisy values drifts high because expected max tops max of means.
- Double Q keeps and , picks with one and prices with the other.
- Worked numbers: solo target , double target , gap closed .
- Acting uses the mean of both tables with one random table updated per step.
- Sample cost matches solo Q; only memory for the second table is extra.