Multi-armed bandits
A bandit problem is reinforcement learning with the sequence removed — repeated choices between fixed options, and the cleanest place to learn how exploration should be done.
- 15 min read
- 3 reading levels
- Updated
Read these first
On this page 9
One lesson, three depths. Pick the one that fits you today — you can switch any time.
Beginner — No maths. Plain English.
The short answer
A bandit problem is a row of slot machines, each with a different hidden chance of paying out, and a limited number of coins.
You cannot see the odds. You can only pull and watch.
The analogy you have already lived
You are at a mela with ten rupees, standing in front of five ring-toss stalls. Each stall has different odds of you winning a prize, and nobody tells you which. Every rupee you spend finding out about a bad stall is a rupee you did not spend at the good one.
That is the whole problem. Nothing else is in it.
The strange name comes from slot machines. An old slot machine was nicknamed a "one-armed bandit" because it had one lever and it robbed you. Line several up and you have a multi-armed bandit.
What makes this different from the last lesson
The previous lesson introduced the trade-off. This one strips everything else away so we can study it properly.
In a bandit problem there is no situation to be in. There is no board, no position, no state. Every round is identical. You pick a stall, you find out, you pick again.
That removal is not a simplification for teaching. It is what makes the problem mathematically tidy enough to have provably best answers — which full reinforcement learning does not have.
full RL: where you are -> what you do -> where you end up next
bandit: what you do -> a payout, and that's allBecause "where you end up next" is gone, so is the delayed-reward problem. What is left is pure exploration versus exploitation.
How you measure how well you did
You cannot measure a bandit agent by its total winnings, because that depends on how generous the machines were.
Instead you measure regret. Imagine a genie had named the best machine on day one, and you had used only that one. Regret is how far short of the genie's total you fell.
the genie's total: the best machine's payout, every round
your total: whatever you actually got
regret: the gap between themRegret starts at zero and only ever grows. A good agent makes it grow slowly and then almost stop. A bad agent's regret grows at the same rate forever. That agent is still choosing wrongly on the last round as often as on the first.
Zero regret is impossible. You have to lose something to learn anything.
Three ways to choose
Roll a die. Most rounds, pull the machine that has paid best so far. Occasionally pull a random one. This is the epsilon-greedy method from the last lesson. It is easy and it explores blindly, wasting pulls on machines it has already ruled out.
Give unfamiliar machines the benefit of the doubt. Keep track of how sure you are about each machine, and add a bonus for uncertainty. A machine you have pulled twice gets a large bonus. A machine you have pulled four hundred times gets almost none. Then pull whichever scores highest once the bonus is added. This is called UCB, for upper confidence bound, and its slogan is "optimism in the face of uncertainty".
Guess, then act on your guess. For each machine, keep a picture of what its true payout rate might be — not one number but a spread of plausible ones. Each round, draw one number at random from each machine's spread, and pull whichever draw came out highest. A machine you know little about has a wide spread, so it sometimes draws a high number and gets tried. This is Thompson sampling, named after William Thompson, who published it in 1933 — long before anybody had a computer to run it on.
That third one wins in practice, and it usually wins by a lot.
Where you have already seen this
- Which headline a news site shows you. Several are written, and the site works out which one gets clicked while it is still showing them.
- Which advertisement fills a slot.
- Which of three checkout button designs a shop keeps. This is A/B testing done adaptively, so fewer customers see the losing version.
- Which treatment arm a trial assigns you to, in adaptive clinical trial designs.
- Which video a recommender tries on you when it has never shown you anything like it. The cold-start problem is a bandit problem wearing a different hat.
The version that shows up in real work
Real problems come with context. Which advertisement is best depends on who is looking. That is a contextual bandit: you see something about the situation before choosing, but your choice still does not change what you see next.
Contextual bandits are the workhorse of the industry. They are far more common in production than full reinforcement learning, because they are much easier to run safely. And they answer the question most businesses actually have: which of these should I show?
Remember this
- A bandit is repeated choices with no situation and no sequence — pure exploration versus exploitation.
- Score yourself by regret: the gap between you and someone who knew the answer.
- Thompson sampling — guess a value, act on the guess — is old, simple, and usually the best of the easy methods.
What to learn next
- Q-learning — what happens when your choice changes what you face next.
- Exploration vs exploitation — the intuition this lesson formalises.
- The cold-start problem — bandits doing real work inside recommender systems.
Developer — Code and libraries.
Setup
pip install numpyThree policies, measured properly
Five machines. Each pays 1 or 0 with a fixed hidden probability. The last three are close together at 0.55, 0.60 and 0.62, which is deliberately the hard case — telling near-identical options apart is where methods separate.
Each policy runs 1000 pulls, 200 times with different seeds, and we average the regret curves. One run of a bandit algorithm tells you nothing at all.
import numpy as np
TRUE = np.array([0.20, 0.50, 0.55, 0.60, 0.62]) # five machines; the last three are close
BEST = TRUE.max()
STEPS, RUNS, K = 1000, 200, 5
def choose(policy, rng, means, counts, wins, losses, t):
if policy == "epsilon":
if rng.random() < 0.1:
return int(rng.integers(K))
return int(np.argmax(means))
if policy == "ucb":
if counts.min() == 0:
return int(np.argmin(counts)) # one pull each before any maths
return int(np.argmax(means + np.sqrt(2.0 * np.log(t) / counts)))
return int(np.argmax(rng.beta(wins + 1, losses + 1))) # Thompson: sample a belief, act on it
def play(policy, seed):
rng = np.random.default_rng(seed)
means, counts = np.zeros(K), np.zeros(K)
wins, losses = np.zeros(K), np.zeros(K)
regret, running = np.zeros(STEPS), 0.0
for t in range(1, STEPS + 1):
arm = choose(policy, rng, means, counts, wins, losses, t)
payout = 1.0 if rng.random() < TRUE[arm] else 0.0
counts[arm] += 1
means[arm] += (payout - means[arm]) / counts[arm]
wins[arm] += payout
losses[arm] += 1.0 - payout
running += BEST - TRUE[arm] # regret: what this pull gave up
regret[t - 1] = running
return regret
for policy, name in [("epsilon", "epsilon-greedy 0.1"), ("ucb", "UCB1"), ("thompson", "Thompson")]:
curve = np.array([play(policy, seed) for seed in range(RUNS)]).mean(axis=0)
print(f"{name:<19} regret after 100: {curve[99]:5.1f} 500: {curve[499]:5.1f} 1000: {curve[-1]:5.1f}")
print()
print("regret = what the best machine would have paid, minus what you actually got.")
print("lower is better; zero is impossible, because you must lose something to learn anything.")epsilon-greedy 0.1 regret after 100: 12.8 500: 27.0 1000: 40.6 UCB1 regret after 100: 8.2 500: 29.7 1000: 50.6 Thompson regret after 100: 6.3 500: 18.4 1000: 27.9 regret = what the best machine would have paid, minus what you actually got. lower is better; zero is impossible, because you must lose something to learn anything.
It takes a few seconds — 600,000 pulls in a Python loop.
The result is not the one textbooks lead you to expect
Thompson sampling wins at every horizon. Lowest regret at 100 pulls, at 500, and at 1000. That is the usual finding and it is why it is the default in production systems.
UCB1 starts best and ends worst. It is ahead at 100 pulls and behind epsilon-greedy at 1000. This surprises people, so it is worth being precise about the cause. UCB1's bonus is $\sqrt{2 \ln t / N}$ and it grows with $t$. With three arms whose true rates differ by only 0.02, that bonus keeps pushing UCB1 back to arms it has already sampled hundreds of times, long past the point of usefulness.
The theory is not wrong, and this is not a contradiction. UCB1's guarantee is asymptotic. Running the same script with STEPS = 20000 and RUNS = 40 gives roughly 345 regret for UCB1 against 367 for epsilon-greedy — it does overtake, eventually and narrowly. Thompson stays around 111 the whole way.
The lesson to take from this is not "UCB is bad". It is that horizon matters as much as the algorithm. Quoting a regret bound without stating the horizon you actually run at is close to meaningless.
Line by line
np.sqrt(2.0 * np.log(t) / counts) — the confidence bonus. Numerator grows slowly with time, denominator grows with how often that arm was pulled. An arm pulled once has a bonus about 30 times larger than an arm pulled 900 times. The 2.0 is a tuning constant; halving it makes UCB less exploratory and often performs better in practice than the theoretically motivated value.
counts.min() == 0 guard — log(t)/0 is infinity, and NumPy would warn and return inf. Forcing one pull per arm first is cleaner than relying on infinity to do the right thing.
rng.beta(wins + 1, losses + 1) — this is Thompson sampling in one line. For a machine that pays 1 or 0, the belief about its hidden rate after seeing w payouts and l blanks is a Beta distribution with those counts. The + 1 on each is a uniform prior: before any data, every rate from 0 to 1 is equally plausible. Drawing one sample from each belief and taking the best is the entire algorithm.
Why the Beta draw explores by itself — an arm pulled twice has a wide, flat belief, so its draw is sometimes very high and it gets tried. An arm pulled 500 times has a narrow belief, so its draw is close to its true rate. Exploration falls out of the uncertainty automatically, with no schedule to tune. That absence of a knob is the practical reason to prefer it.
Common mistakes
Reporting one run. Bandit curves from a single seed cross each other at random. The RUNS = 200 average in this script is not decoration; drop it and the ranking of the three methods changes with the seed.
Using UCB with unbounded or unscaled rewards. The bonus assumes rewards live in a known bounded range. Feed it rupees in the thousands and the bonus is invisible, leaving pure greedy behaviour.
Assuming rewards are stationary. If the best machine changes over time, means[arm] += (payout - means[arm]) / counts[arm] averages over all of history and adapts slower and slower. Replace 1/counts[arm] with a fixed step size like 0.1, or use a sliding window.
Running a bandit where you have a full sequential problem. If your action changes what you face next, a bandit will not see it. That is the whole reason Q-learning exists.
Try it yourself
Change TRUE to [0.1, 0.1, 0.1, 0.1, 0.9] — one arm far ahead of the rest. Regret for all three methods collapses, because a single pull identifies the winner. Then try [0.5, 0.5, 0.5, 0.5, 0.51] and watch every method struggle, because distinguishing 0.50 from 0.51 needs tens of thousands of samples. The difficulty of a bandit is set by the gaps between the arms, not by the number of arms.
What to learn next
- Q-learning — what happens when your choice changes what you face next.
- Exploration vs exploitation — the intuition this lesson formalises.
- The cold-start problem — bandits doing real work inside recommender systems.
Researcher — Mathematics and papers.
The stochastic bandit
$K$ arms, each with an unknown reward distribution $\nu_i$ with mean $\mu_i$, bounded in $[0,1]$. At each round $t$ the learner picks $a_t$ and observes $X_t \sim \nu_{a_t}$. Write $\mu^* = \max_i \mu_i$ and the suboptimality gap $\Delta_i = \mu^* - \mu_i$.
Pseudo-regret after $T$ rounds: $$ \mathcal{R}(T) = T\mu^* - \mathbb{E}\left[\sum_{t=1}^{T} \mu_{a_t}\right] = \sum_{i=1}^{K} \Delta_i \, \mathbb{E}[N_i(T)] $$ where $N_i(T)$ is the number of pulls of arm $i$. The decomposition on the right is the one that matters: regret is entirely determined by how often you pull suboptimal arms, weighted by how bad they are.
The lower bound
Lai and Robbins (1985) proved that for any consistent policy, $$ \liminf_{T\to\infty} \frac{\mathcal{R}(T)}{\ln T} \ge \sum_{i : \Delta_i > 0} \frac{\Delta_i}{\mathrm{KL}(\nu_i \,|\, \nu^*)} $$ where $\mathrm{KL}$ is the Kullback-Leibler divergence between the suboptimal arm's reward distribution and the optimal arm's. Logarithmic regret is therefore not only achievable — it is the best possible. Any claim of sublogarithmic regret on a stochastic bandit is a claim of a contradiction.
The gap-dependent bound blows up as $\Delta_i \to 0$, which is why the minimax (gap-free) form is also quoted: $\Theta(\sqrt{KT})$, achieved by MOSS (Audibert and Bubeck, 2009).
UCB1
Auer, Cesa-Bianchi and Fischer (2002): pull $\arg\max_i \left[ \hat\mu_i + \sqrt{\frac{2\ln t}{N_i(t)}} \right]$.
The bonus comes from Hoeffding's inequality: with probability at least $1 - t^{-4}$, $|\hat\mu_i - \mu_i| \le \sqrt{2\ln t / N_i}$. The resulting bound is $$ \mathcal{R}(T) \le \sum_{i:\Delta_i>0} \frac{8\ln T}{\Delta_i} + \left(1 + \frac{\pi^2}{3}\right)\sum_i \Delta_i $$
The constant 8 is loose and the Hoeffding bound ignores the reward distribution's shape. KL-UCB (Garivier and Cappé, 2011) replaces it with a KL-based confidence set and attains the Lai-Robbins constant exactly. In practice KL-UCB substantially outperforms UCB1 on Bernoulli arms, and the empirical gap seen in the code above largely disappears.
Thompson sampling
Sample $\theta_i \sim p(\mu_i \mid \mathcal{D}_t)$ for each arm, pull $\arg\max_i \theta_i$, update the posterior. For Bernoulli rewards with a $\mathrm{Beta}(\alpha, \beta)$ prior the posterior is $\mathrm{Beta}(\alpha + w_i, \beta + l_i)$, conjugate and free.
Published in Thompson (1933) and ignored for seventy years. Chapelle and Li (2011) demonstrated it empirically outperforming UCB on display advertising; Agrawal and Goyal (2012) and Kaufmann, Korda and Munos (2012) then proved it achieves the Lai-Robbins bound. It is optimal, it has no tuning constant, and it is three lines of code — an unusual combination.
Its practical advantage over UCB comes from randomisation: UCB is deterministic given the history, so under delayed or batched feedback it pulls the same arm repeatedly before any update lands. Thompson sampling diversifies naturally, which matters a great deal in systems where feedback arrives minutes later.
Adversarial bandits
Drop the stochastic assumption and let an adversary choose rewards. EXP3 (Auer et al., 2002) uses exponential weights over an importance-weighted reward estimate and attains $O(\sqrt{TK\ln K})$ regret against the best fixed arm in hindsight. The $\sqrt{T}$ rate is unimprovable here — no logarithmic regret exists without stochastic structure.
Contextual bandits
At each round a context $x_t \in \mathcal{X}$ arrives before the choice, and the reward depends on $(x_t, a_t)$. This is the formulation that dominates industrial deployment.
- LinUCB (Li et al., 2010) assumes $\mathbb{E}[r \mid x, a] = x^\top \theta_a$ and builds ellipsoidal confidence sets by ridge regression; regret $\tilde{O}(d\sqrt{T})$ for $d$-dimensional contexts.
- Thompson sampling for linear bandits (Agrawal and Goyal, 2013) samples $\tilde\theta$ from a Gaussian posterior.
- Off-policy evaluation matters more than the algorithm in practice: inverse propensity scoring and doubly robust estimators (Dudík, Langford and Li, 2011) let you estimate a new policy's value from logged data, provided the logging policy was stochastic and its propensities were recorded. Systems that log only the chosen action, without the probability it was chosen with, cannot be evaluated offline at all. Record propensities from day one.
The bridge to full RL
A bandit is an MDP with $|\mathcal{S}| = 1$. Everything hard about RL that is absent here — state, transitions, delayed credit, bootstrapping — arrives at once when you add states. The optimality results in this lesson do not survive that transition, which is worth remembering whenever a deep RL paper cites a bandit bound as motivation.
References
- Lai and Robbins (1985), Asymptotically efficient adaptive allocation rules.
- Auer, Cesa-Bianchi and Fischer (2002), Finite-time Analysis of the Multiarmed Bandit Problem.
- Chapelle and Li (2011), An Empirical Evaluation of Thompson Sampling.
- Agrawal and Goyal (2012), Analysis of Thompson Sampling for the Multi-armed Bandit Problem.
- Li et al. (2010), A Contextual-Bandit Approach to Personalized News Article Recommendation — arxiv.org/abs/1003.0146.
- Lattimore and Szepesvári (2020), Bandit Algorithms — free online, and the current standard reference.
What to learn next
- Q-learning — what happens when your choice changes what you face next.
- Exploration vs exploitation — the intuition this lesson formalises.
- The cold-start problem — bandits doing real work inside recommender systems.