Le bandit manchot répartit le trafic entre plusieurs options (bannières, objets d'e-mail, offres) et apprend au fil de l'eau laquelle fonctionne le mieux. Un A/B test partage le trafic à parts égales jusqu'à la fin ; le bandit envoie de plus en plus de visiteurs vers l'option gagnante. On perd moins de ventes pendant le test.
Un joueur face à quatre machines à sous, les « bandits manchots », aux gains inconnus. Il doit jouer assez chaque machine pour repérer celle qui paie le mieux, sans gaspiller trop de pièces sur les mauvaises.
Chaque bannière est montrée au moins une fois pour obtenir un premier taux de conversion.
Avec epsilon-greedy, 90 % des visiteurs voient la bannière au meilleur taux observé et 10 % une bannière au hasard. UCB1 ajoute plutôt à chaque taux un bonus d'autant plus grand que la bannière a été peu testée.
Le taux observé de la bannière montrée est recalculé. Les parts de trafic basculent peu à peu vers la meilleure.
Quatre versions d'une bannière d'accueil dont on ignore les taux de conversion. La simulation les fixe à 3 %, 3,5 %, 4 % et 5 %, pour pouvoir juger les stratégies.
Le code compare l'A/B test à parts égales, epsilon-greedy et UCB1. Sur la simulation Python, epsilon-greedy envoie environ 90 % du trafic vers la meilleure bannière et vend nettement plus que l'A/B test. UCB1, prudent, explore encore beaucoup à cet horizon.
On compare les ventes obtenues à celles qu'aurait apportées la meilleure bannière montrée à tous. Cet écart s'appelle le regret, et c'est lui que le bandit réduit. L'A/B test poursuit un autre but : mesurer l'écart entre options avec une marge d'erreur connue.
Noms donnés pour le code de la fiche, le même en R et en Python.
Epsilon-greedy est simple et efficace, mais peut rester bloqué sur une option moyenne si l'exploration est trop faible. UCB1 n'abandonne jamais une option trop tôt, mais converge lentement quand les taux sont faibles et proches. Thompson Sampling est souvent le meilleur compromis.
Part du trafic réservée à l'exploration, ici 10 %. Trop faible, on risque de rester sur une mauvaise option ; trop forte, on gaspille du trafic sur les perdantes. On peut la faire décroître avec le temps.
Le nombre de visiteurs disponibles. Sur un horizon court, une stratégie qui exploite vite rapporte plus ; sur un horizon long, une exploration plus soutenue évite de se tromper de gagnante.
# Bannières e-commerce : bandit manchot en R
set.seed(42)
taux_reels <- c(0.030, 0.035, 0.040, 0.050) # inconnus en pratique
n_visiteurs <- 20000
k <- length(taux_reels)
jouer <- function(strategie) {
essais <- rep(0, k)
succes <- rep(0, k)
for (t in 1:n_visiteurs) {
if (strategie == "ab") bras <- (t - 1) %% k + 1 # A/B test : parts égales
else if (t <= k) bras <- t # chaque bannière une fois
else if (strategie == "epsilon" && runif(1) < 0.1) bras <- sample(k, 1) # exploration
else if (strategie == "epsilon") bras <- which.max(succes / essais) # exploitation
else bras <- which.max(succes / essais + sqrt(2 * log(t) / essais)) # UCB1
essais[bras] <- essais[bras] + 1
succes[bras] <- succes[bras] + (runif(1) < taux_reels[bras]) # le visiteur achète-t-il ?
}
cat(strategie, "| ventes :", sum(succes), "| part du trafic :", round(essais / n_visiteurs, 2), "\n")
}
for (strategie in c("ab", "epsilon", "ucb")) jouer(strategie)
cat("Référence, meilleure bannière pour tous :", n_visiteurs * max(taux_reels), "ventes attendues\n")
# Bannières e-commerce : bandit manchot en Python
import numpy as np
rng = np.random.default_rng(42)
taux_reels = np.array([0.030, 0.035, 0.040, 0.050]) # inconnus en pratique
n_visiteurs = 20000
k = len(taux_reels)
def jouer(strategie):
essais, succes = np.zeros(k), np.zeros(k)
for t in range(n_visiteurs):
if strategie == "ab":
bras = t % k # A/B test : parts égales
elif t < k:
bras = t # chaque bannière une fois
elif strategie == "epsilon": # 10 % d'exploration, 90 % d'exploitation
bras = rng.integers(k) if rng.random() < 0.1 else np.argmax(succes / essais)
else: # UCB1 : taux observé + bonus pour les bannières peu testées
bras = np.argmax(succes / essais + np.sqrt(2 * np.log(t) / essais))
essais[bras] += 1
succes[bras] += rng.random() < taux_reels[bras] # le visiteur achète-t-il ?
print(strategie, "| ventes :", int(succes.sum()), "| part du trafic :", (essais / n_visiteurs).round(2))
for strategie in ["ab", "epsilon", "ucb"]:
jouer(strategie)
print("Référence, meilleure bannière pour tous :", n_visiteurs * taux_reels.max(), "ventes attendues")
L'A/B test mesure l'écart entre options avec une marge d'erreur connue, au prix des ventes perdues pendant le test. Le bandit maximise les ventes pendant le test, mais estime mal les options qu'il a vite délaissées. L'A/B test sert à décider et documenter, le bandit à optimiser en continu.
C'est le surnom des machines à sous, qui n'ont qu'un bras et vident les poches des joueurs. Le problème consiste à choisir quel bras actionner parmi plusieurs machines aux gains inconnus. L'anglais dit multi-armed bandit.
Exploiter, c'est choisir l'option qui semble la meilleure aujourd'hui. Explorer, c'est tester une option moins connue qui pourrait se révéler meilleure. Trop exploiter fait rater la vraie gagnante, trop explorer gaspille du trafic : chaque algorithme de bandit est une façon de doser les deux.
Choisit chaque option selon la probabilité qu'elle soit la meilleure. Explore là où c'est utile, sans paramètre epsilon à régler.
Voir la fiche → quand l'effet dépend du clientCherche quelle action convient à quel client, au lieu d'une seule gagnante pour tous.
Voir la fiche → quand les décisions s'enchaînentLe bandit décide coup par coup ; le Q-learning gère des décisions dont l'effet se prolonge sur les suivantes.
Voir la fiche →Dataistudio forme les équipes au machine learning et à l'IA, sur des cas concrets.
Nous utilisons des cookies de mesure d'audience et de suivi publicitaire pour comprendre la fréquentation du site et l'efficacité de nos annonces. Rien n'est déposé sans votre accord. En savoir plus