Skip to content
AI360Xpert

Q-Learning

Q-learning estimates the value of taking each action in each state without needing a model of the environment, converging to the optimal action-value function by bootstrapping off its own past estimates.

Q-learning estimates the value of taking each action in each state without needing a model of the environment, converging to the optimal action-value function by bootstrapping off its own past estimates.
Q-learning estimates the value of taking each action in each state without needing a model of the environment, converging to the optimal action-value function by bootstrapping off its own past estimates.

Why Does This Exist?

Most real-world problems don't give you the transition model T(ss,a)T(s' \mid s, a) or reward function R(s,a,s)R(s, a, s') in closed form — you can only observe the outcomes of actions you actually take. Q-learning is model-free: it learns optimal action values purely from experience, without ever knowing the environment dynamics.

This is the key breakthrough: you don't need to know how the world works. You just need to interact with it.

Think of It Like This

Learning which moves win at chess by playing

You learn to play chess not by memorizing a complete game tree, but by playing thousands of games and remembering which positions tended to lead to wins. After enough games, your estimate of how good each position is becomes accurate. Q-learning does exactly this: maintain a running estimate of how valuable each action is in each state, and update that estimate after every move based on what actually happened.

How It Actually Works

The Q-function

The Q-function Q(s,a)Q(s, a) estimates the expected discounted return of taking action aa in state ss, then following the optimal policy thereafter. The optimal Q-function QQ^* satisfies the Bellman optimality equation:

Q(s,a)=E[R+γmaxaQ(s,a)    s,a]Q^*(s, a) = \mathbb{E}\left[R + \gamma \max_{a'} Q^*(s', a') \;\Big|\; s, a\right]

If we know QQ^*, the optimal policy is simply: in state ss, take argmaxaQ(s,a)\arg\max_a Q^*(s, a).

The Q-update rule

After observing transition (s,a,r,s)(s, a, r, s'):

Q(s,a)Q(s,a)+α[r+γmaxaQ(s,a)Q(s,a)]TD errorQ(s, a) \leftarrow Q(s, a) + \alpha\underbrace{\left[r + \gamma \max_{a'} Q(s', a') - Q(s, a)\right]}_{\text{TD error}}

The TD error is the difference between the bootstrapped target (r+γmaxaQ(s,a)r + \gamma \max_{a'} Q(s', a')) and the current estimate. A positive TD error means the current estimate is too low — increase it. A negative error means it's too high — decrease it. α\alpha is the learning rate.

Off-policy learning

Q-learning is off-policy: the target uses maxaQ(s,a)\max_{a'} Q(s', a') (the greedy action) regardless of which action the agent actually took during exploration. This means the agent can explore with ε\varepsilon-greedy (taking random actions ε\varepsilon of the time) while still learning the optimal policy — the exploration policy and the target policy are decoupled.

Convergence guarantee

For tabular Q-learning with finite state-action spaces, QQ converges to QQ^* if:

  1. Every state-action pair is visited infinitely often.
  2. Learning rates satisfy tαt=\sum_t \alpha_t = \infty (enough total learning) and tαt2<\sum_t \alpha_t^2 < \infty (learning slows over time).

In practice: use a small constant α\alpha (e.g., 0.1) and anneal ε\varepsilon from 1.0 to 0.01.

Code

import gymnasium as gymimport numpy as np
# ── Tabular Q-learning on FrozenLake ─────────────────────────────────────────env = gym.make("FrozenLake-v1", is_slippery=True, render_mode=None)num_states  = env.observation_space.n   # 16num_actions = env.action_space.n         # 4
# Initialize Q-tableQ = np.zeros((num_states, num_actions))
# Hyperparametersalpha   = 0.1    # learning rategamma   = 0.99   # discount factorepsilon = 1.0    # initial exploration rateeps_min = 0.01   # minimum exploration rateeps_decay = 0.999
rewards_per_episode = []
for episode in range(10_000):    state, _ = env.reset()    total_reward = 0.0    done = False
    while not done:        # ε-greedy action selection        if np.random.random() < epsilon:            action = env.action_space.sample()   # explore        else:            action = np.argmax(Q[state])          # exploit
        next_state, reward, terminated, truncated, _ = env.step(action)        done = terminated or truncated
        # Q-update (Bellman target)        td_target = reward + gamma * np.max(Q[next_state]) * (1 - int(terminated))        td_error  = td_target - Q[state, action]        Q[state, action] += alpha * td_error
        state = next_state        total_reward += reward
    # Anneal exploration    epsilon = max(eps_min, epsilon * eps_decay)    rewards_per_episode.append(total_reward)
# Evaluation (greedy policy, no exploration)eval_rewards = []for _ in range(100):    state, _ = env.reset()    total = 0.0    done = False    while not done:        action = np.argmax(Q[state])        state, reward, terminated, truncated, _ = env.step(action)        done = terminated or truncated        total += reward    eval_rewards.append(total)
print(f"Greedy policy win rate: {np.mean(eval_rewards):.1%}")print("\nLearned Q-values (action with highest Q per state):")print(np.argmax(Q, axis=1).reshape(4, 4))  # 0=L, 1=D, 2=R, 3=U

Watch Out For

Overestimation bias in the Q-target

The target maxaQ(s,a)\max_{a'} Q(s', a') uses the maximum of noisy Q-estimates, which is systematically biased upward — the maximum of noisy values overestimates the true maximum. This overestimation accumulates and can cause instability or suboptimal convergence. Double Q-learning addresses this: use one network to select a=argmaxaQ1(s,a)a^* = \arg\max_{a'} Q_1(s', a') and a second network to evaluate Q2(s,a)Q_2(s', a^*), eliminating the bias.

Not visiting enough state-action pairs

Q-learning converges only when every state-action pair is visited sufficiently. With aggressive ε\varepsilon annealing, the agent commits to a nearly greedy policy before it has explored enough states. Monitor a visitation heatmap — if many states have near-zero visit counts when ε\varepsilon is already small, slow the annealing schedule.

The Quick Version

  • Q-learning is model-free and off-policy: learns the optimal action-value function directly from experience.
  • The Q-update: Q(s,a)Q(s,a)+α[r+γmaxaQ(s,a)Q(s,a)]Q(s,a) \leftarrow Q(s,a) + \alpha[r + \gamma\max_{a'} Q(s',a') - Q(s,a)].
  • Off-policy: the target uses the greedy action regardless of the exploration policy used during data collection.
  • Tabular convergence guaranteed when all state-action pairs visited sufficiently with decaying learning rates.
  • Doesn't scale to large/continuous state spaces → needs neural network approximation (DQN).