Multi-armed bandits
The simplest RL game: many choices, each with a hidden chance of reward. Learn epsilon-greedy, the classic way to balance trying and using.
In 60 seconds
Multi-armed bandits
The simplest RL game: many choices, each with a hidden chance of reward. Learn epsilon-greedy, the classic way to balance trying and using.
Keeping score: the running average
Q[arm] of its average reward, and a count N[arm] of how often it was tried. After each pull it nudges the estimate toward the new reward:Q, N = [0.0, 0.0, 0.0], [0, 0, 0] # estimates and counts for three stalls
arm = 2
for reward in [1, 0, 1, 1]: # four visits to stall 2
N[arm] += 1
Q[arm] += (reward - Q[arm]) / N[arm] # move a bit toward the new reward
print(f"after reward {reward}: Q[2] = {Q[arm]:.3f}")
Explore or exploit?
- Always picks the arm that looks best so far
- Can lock onto an arm that got lucky early
- Never discovers that another arm is better
- Picks any arm with equal chance
- Learns about every arm
- Keeps wasting pulls on bad arms forever
epsilon, such as 0.1. Each round, with chance epsilon pick a random arm (explore). Otherwise pick the arm with the highest Q (exploit). If two arms tie, pick one of them at random, so no arm is favoured by accident.import random
probs = [0.4, 0.8] # arm 1 is clearly better, but the agent does not know
def run(epsilon, seed):
rng = random.Random(seed)
Q, N = [0.0, 0.0], [0, 0]
for t in range(500):
if rng.random() < epsilon:
arm = rng.randrange(2)
else:
arm = 0 if Q[0] >= Q[1] else 1 # greedy; ties go to arm 0
r = 1 if rng.random() < probs[arm] else 0
N[arm] += 1
Q[arm] += (r - Q[arm]) / N[arm]
return N
for eps in (0.0, 0.1):
print(f"epsilon={eps}: pulls per arm = {run(eps, seed=3)}")
epsilon=0, the agent tries arm 0 first, gets some reward, and never has a reason to try arm 1. With a little exploration, it soon discovers that arm 1 is better and uses it most of the time.Where bandits show up
- Testing designs: some websites and apps use bandit methods to try several versions of a button or headline, and slowly send more people to the one that works best.
- Recommendations: choosing which item to show, while still trying new items so they get a fair chance.
- Everyday life: picking a restaurant, a route to college, or a tuition teacher is a bandit problem too.
probs to [0.75, 0.8]. The arms are now very close. Does epsilon = 0.1 still find the better one in 500 rounds? Why is it harder?Watch and read more
Lab
An epsilon-greedy chai-stall agent that finds the best stall and uses it most of the time.
The problem
Five chai stalls each serve a great cup (reward 1) with a hidden chance. The starter's choose ignores everything it learned and always picks at random.
- Press Run. Every epsilon gives the same poor result, because
choosenever usesepsilonorQ. - Write epsilon-greedy in
choose: with chanceepsilonreturn a random stall; otherwise return the stall with the highestQ. If several stalls tie for highest, pick one of them at random usingrng. - Run again and compare epsilon = 0, 0.1, 0.3 and 1.0. Which one has the best average reward? Which one finds the best stall most often?
- Press Check. It tests your agent on six new station layouts.
- Write two sentences on why epsilon = 0 sometimes does well and sometimes badly.
Practice lab
Edit the code and run it. Python runs in your browser — nothing is sent to our server.
No output. Add print(...) to see values.
You are done when
Hard questions
Try to answer before you reveal. If you can answer these, you understood the lesson.
Q1With epsilon = 0.1 and five stalls, what is the highest share of visits the best stall can get in the long run?Reveal
About 92%. The agent exploits 90% of the time and picks the best stall then. When it explores (10%), it picks at random, and one time in five that random pick is also the best stall: 0.9 + 0.1 x 0.2 = 0.92. This ceiling is why people lower epsilon over time.
Q2All estimates start at 0. What happens if you start them at 1.0 instead, with epsilon = 0?Reveal
This is called optimistic initial values. Every untried stall looks perfect, so the greedy agent tries it, gets disappointed (the average drops below 1), and moves on to the next untried stall. So it explores every stall early, even with no randomness. It is a neat trick for simple problems, but it only encourages exploring at the start.
Questions people ask
Why not explore for a while, then exploit forever?
That is a fair strategy, called "explore then commit". Its weakness is choosing how long to explore: too short and you may commit to the wrong arm, too long and you waste pulls. Epsilon-greedy keeps a small amount of exploring forever, which also helps if the arms change over time.
What if the rewards change over time?
Then a plain average learns too slowly, because old rewards count as much as new ones. A common fix is a constant step size, such as Q += 0.1 * (reward - Q), which gives recent rewards more weight.
Is 0.1 always the right epsilon?
No. It is a common starting value. The lab lets you compare values. The next unit shows how to start high and lower epsilon over time.
Why is it called 'bandit'?
Old slot machines had one lever (an "arm") and took people's money, so people called them "one-armed bandits". A game with many levers became a "multi-armed bandit". Please do not try this on real gambling machines.
Lesson test
5 questions. Get 3 right (60%) to pass and complete this lesson.