Exploration vs. Exploitation
An RL agent must balance trying known good actions (exploitation) against trying new actions that might be better (exploration) — too little exploration gets stuck in local optima, too much wastes time on bad actions.
Why Does This Exist?
An agent that always takes the action it currently estimates as best will never discover that a different action might be better — especially early in training when all estimates are unreliable. An agent that always takes random actions never benefits from what it has learned.
This tension is fundamental: you must try things to know if they're good, but trying random things wastes time you could spend using what you already know. The exploration-exploitation tradeoff is the core tension every RL algorithm must navigate.
Think of It Like This
Choosing a restaurant for dinner
You have a favorite restaurant (exploit: high confidence in the quality, known value) and a hundred restaurants you've never tried (explore: unknown quality, possibly life-changing, possibly terrible). If you only ever exploit, you miss potentially better options. If you only ever explore, you never accumulate the knowledge needed to make good decisions. The right balance depends on how many dinners you have left — if you're moving cities next week, maybe explore; if you're here for years, time is on your side for exploration.
How It Actually Works
-greedy
The simplest strategy: with probability take a uniformly random action; with probability take the greedy (best-estimated) action.
if random.random() < epsilon: action = env.action_space.sample() # exploreelse: action = np.argmax(Q[state]) # exploitStrength: Simple, widely used, works well in practice.
Weakness: Wastes exploration budget on clearly suboptimal actions — treats all non-greedy actions equally, regardless of how bad they are.
Fix: Anneal over time: , converging toward pure exploitation.
Upper Confidence Bound (UCB)
UCB adds an uncertainty bonus to less-visited actions — optimism in the face of uncertainty:
Where is the visit count for action and is the total number of steps taken. As an action is visited more, its uncertainty bonus shrinks toward zero.
UCB achieves regret — provably better than -greedy's regret for the bandit problem.
Thompson Sampling (Bayesian exploration)
Maintain a posterior distribution over Q-values (or reward probabilities) for each action. At each step, sample one Q-value from each action's posterior and take the action with the highest sample:
- For each arm , sample
- Take action
- Observe reward; update posterior
As data accumulates, posteriors tighten and exploration naturally decreases. Empirically excellent across many bandit settings.
Curiosity-driven / intrinsic motivation
For sparse-reward environments where the agent rarely encounters the extrinsic reward, add an intrinsic reward based on novelty or prediction error:
Where measures how surprising the transition was — typically the prediction error of a neural network trained to predict next states. Surprising transitions (high prediction error) get a bonus reward, systematically driving exploration toward novel areas.
Code
import numpy as np
class MultiArmedBandit: """k-armed bandit with Gaussian rewards.""" def __init__(self, k=10): self.k = k self.true_means = np.random.normal(0, 1, k) def pull(self, arm): return self.true_means[arm] + np.random.normal(0, 1) @property def optimal_arm(self): return np.argmax(self.true_means)
def epsilon_greedy(bandit, n_steps=2000, epsilon=0.1): Q = np.zeros(bandit.k) N = np.zeros(bandit.k) rewards = [] for t in range(n_steps): action = (np.random.randint(bandit.k) if np.random.random() < epsilon else np.argmax(Q)) reward = bandit.pull(action) N[action] += 1 Q[action] += (reward - Q[action]) / N[action] # incremental mean rewards.append(reward) return rewards
def ucb(bandit, n_steps=2000, c=2.0): Q = np.zeros(bandit.k) N = np.zeros(bandit.k) + 1e-5 # avoid division by zero rewards = [] for t in range(1, n_steps + 1): ucb_values = Q + c * np.sqrt(np.log(t) / N) action = np.argmax(ucb_values) reward = bandit.pull(action) N[action] += 1 Q[action] += (reward - Q[action]) / N[action] rewards.append(reward) return rewards
def thompson_sampling(bandit, n_steps=2000): """Beta-Bernoulli Thompson Sampling (assumes rewards in [0,1]).""" alpha = np.ones(bandit.k) # successes + 1 beta = np.ones(bandit.k) # failures + 1 rewards = [] # Normalize true means to [0,1] for this demo means_01 = (bandit.true_means - bandit.true_means.min()) means_01 /= means_01.max() + 1e-8 for _ in range(n_steps): samples = np.random.beta(alpha, beta) action = np.argmax(samples) # Simulate binary reward reward = float(np.random.random() < means_01[action]) alpha[action] += reward beta[action] += 1 - reward rewards.append(reward) return rewards
# Compare strategiesnp.random.seed(42)bandit = MultiArmedBandit(k=10)print(f"Optimal arm: {bandit.optimal_arm}, true mean: {bandit.true_means[bandit.optimal_arm]:.2f}")
eg_r = epsilon_greedy(bandit)ucb_r = ucb(bandit)ts_r = thompson_sampling(bandit)
print(f"ε-greedy (ε=0.1) avg reward: {np.mean(eg_r):.3f}")print(f"UCB (c=2.0) avg reward: {np.mean(ucb_r):.3f}")print(f"Thompson sampling avg reward: {np.mean(ts_r):.3f}")Watch Out For
Uniform exploration in large action spaces
-greedy samples uniformly from all actions during exploration. In a space with 1000 actions where 990 are clearly suboptimal, the agent wastes 99% of its exploration budget on known-bad actions. UCB or Thompson Sampling focus exploration on actions with genuine uncertainty — a much better use of limited exploration budget.
Annealing ε too aggressively
If you reduce too quickly, the agent commits to a nearly deterministic policy before it has seen enough of the state space. Profile visit-count distributions: if many states have near-zero visit counts when is already below 0.1, the annealing schedule is too aggressive for this problem. Slow it down or switch to a count-based method.
The Quick Version
- Exploration (trying new actions) and exploitation (using known good actions) must be balanced at every step.
- -greedy: simplest — take random action with probability , anneal over time.
- UCB: optimism in uncertainty — add bonus to less-visited actions.
- Thompson Sampling: sample from posterior, act on the sample — provably excellent empirically.
- Intrinsic motivation: add novelty rewards for sparse-reward environments where extrinsic signal is rare.