  {"id":3888,"date":"2026-06-27T18:04:59","date_gmt":"2026-06-27T17:04:59","guid":{"rendered":"https:\/\/www.gironi.it\/blog\/?p=3888"},"modified":"2026-06-28T10:16:37","modified_gmt":"2026-06-28T09:16:37","slug":"multi-armed-bandit-thompson","status":"publish","type":"post","link":"https:\/\/www.gironi.it\/blog\/multi-armed-bandit-thompson\/","title":{"rendered":"Multi-armed bandit: ottimizzare le varianti mentre il test \u00e8 ancora in corso"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Nell&#8217;articolo sull&#8217;<a href=\"https:\/\/www.gironi.it\/blog\/ab-test-bayesiano\/\">A\/B test bayesiano<\/a> abbiamo confrontato due varianti a campione fisso: si raccolgono i dati per tutta la durata pianificata, si calcola la probabilit\u00e0 che B batta A, si decide. \u00c8 un metodo solido, ma porta con s\u00e9 un costo che di solito passa sotto silenzio.<br> Quel costo \u00e8 il traffico che, per tutta la durata del test, continuiamo a mandare sulla variante peggiore. Se a met\u00e0 esperimento B sta gi\u00e0 stravincendo, ogni visitatore assegnato ad A \u00e8 una conversione che probabilmente stiamo buttando via. <strong>Il test a campione fisso ci fa pagare l&#8217;informazione che raccogliamo: per sapere quale variante \u00e8 migliore, dobbiamo continuare a mostrare anche quella che sospettiamo essere la peggiore.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">C&#8217;\u00e8 un modo per ridurre questo conto, e si chiama <em>multi-armed bandit<\/em>. L&#8217;idea \u00e8 spostare il traffico in modo adattivo verso la variante che sta vincendo <em>mentre il test \u00e8 ancora in corso<\/em>, invece di aspettare il verdetto finale. In questo articolo lo costruiamo con uno degli algoritmi pi\u00f9 eleganti e pratici, il <em>Thompson sampling<\/em>, che \u00e8 la naturale prosecuzione del ragionamento <a href=\"https:\/\/www.gironi.it\/blog\/statistica-bayesiana\/\">bayesiano<\/a> che abbiamo seguito finora.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Di cosa parleremo<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"#esplorazione-sfruttamento\">Il dilemma fra esplorazione e sfruttamento<\/a><\/li><li><a href=\"#thompson-sampling\">Thompson sampling: lasciar decidere il posterior<\/a><\/li><li><a href=\"#regret-evitato\">Quanto si guadagna davvero: il regret evitato<\/a><\/li><li><a href=\"#quando-ha-senso\">Quando un bandit ha senso, e quando no<\/a><\/li><li><a href=\"#prova-tu\">Prova tu<\/a><\/li><li><a href=\"#per-approfondire\">Per approfondire<\/a><\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"esplorazione-sfruttamento\">Esplorazione contro sfruttamento<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Il nome viene dalle slot machine: un <em>bandito a un braccio solo<\/em> (<em>one-armed bandit<\/em>) \u00e8 la macchinetta del casin\u00f2, e immaginiamo di averne davanti diverse, ciascuna con una probabilit\u00e0 di vincita sconosciuta e diversa dalle altre. Abbiamo un numero limitato di gettoni. A ogni giocata dobbiamo scegliere quale braccio tirare. Qual \u00e8 la strategia che massimizza la vincita totale?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La traduzione per chi fa SEO o marketing \u00e8 immediata: i bracci sono le varianti (tre versioni di un <em>title tag<\/em>, di una <em>call to action<\/em>, di una landing page), i gettoni sono i visitatori, la vincita \u00e8 la conversione. Ogni visitatore va assegnato a una variante, e vogliamo massimizzare le conversioni totali lungo tutto l&#8217;esperimento.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Qui nasce la tensione di fondo, quella che rende il problema interessante. Da un lato vorremmo <strong>sfruttare<\/strong> (<em>exploit<\/em>) la variante che finora sembra la migliore, per incassare pi\u00f9 conversioni possibili. Dall&#8217;altro dobbiamo continuare a <strong>esplorare<\/strong> (<em>explore<\/em>) anche le altre, perch\u00e9 &#8220;finora sembra la migliore&#8221; si basa su pochi dati e potrebbe essere un&#8217;impressione sbagliata.<br> \u00c8 un equilibrio delicato: troppa esplorazione e sprechiamo traffico su varianti mediocri; troppo sfruttamento e rischiamo di incoronare un vincitore sbagliato sulla base di un colpo di fortuna iniziale. <strong>Il dilemma esplorazione-sfruttamento \u00e8 il cuore di ogni problema di multi-armed bandit: ogni scelta \u00e8 insieme un&#8217;occasione di guadagno e un&#8217;occasione di apprendimento, e le due cose tirano in direzioni opposte.<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"thompson-sampling\">Thompson sampling: lasciar decidere il posterior<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">L&#8217;A\/B test classico risolve il dilemma nel modo pi\u00f9 rozzo possibile: esplora e basta, in parti uguali, fino alla fine. Met\u00e0 traffico ad A, met\u00e0 a B, nessun adattamento. Thompson sampling lo risolve in modo molto pi\u00f9 astuto, e quasi sorprendente per quanto \u00e8 semplice.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Riprendiamo il filo bayesiano. Per ogni variante manteniamo un posterior sul suo tasso di conversione: come abbiamo visto stimando il conversion rate, partendo da un prior <a href=\"https:\/\/www.gironi.it\/blog\/la-distribuzione-beta-spiegata-semplice\/\">Beta<\/a> e osservando esiti binari (conversione s\u00ec\/no), il posterior di ogni braccio \u00e8 ancora una distribuzione Beta, che si aggiorna a ogni visitatore. All&#8217;inizio, quando non sappiamo nulla, ogni braccio parte da un prior non informativo Beta(1, 1).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La regola di Thompson, a parole prima che in formule, \u00e8 questa: invece di chiederci &#8220;qual \u00e8 la variante con la media pi\u00f9 alta finora?&#8221;, a ogni visitatore <strong>campioniamo un tasso a caso dal posterior di ciascun braccio, e giochiamo il braccio che ha prodotto il campione pi\u00f9 alto<\/strong>. \u00c8 un modo di scegliere &#8220;in proporzione alla probabilit\u00e0 di essere il migliore&#8221;: una variante di cui siamo molto incerti pu\u00f2 ancora vincere l&#8217;estrazione ogni tanto (ed \u00e8 cos\u00ec che continua a essere esplorata), ma man mano che i dati si accumulano i suoi campioni si concentrano e, se \u00e8 davvero peggiore, smette di essere scelta quasi da sola.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L&#8217;eleganza sta proprio qui: non c&#8217;\u00e8 un parametro di esplorazione da regolare a mano. L&#8217;incertezza del posterior <em>\u00e8<\/em> il motore dell&#8217;esplorazione. Pi\u00f9 un braccio \u00e8 incerto, pi\u00f9 i suoi campioni sono dispersi, pi\u00f9 spesso capita che vinca l&#8217;estrazione e venga provato; pi\u00f9 diventa certo, pi\u00f9 i campioni si stringono attorno al valore vero e il braccio viene giocato (o evitato) con decisione.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Simulo in R l&#8217;intero processo su tre varianti con tassi reali del 5%, 7% e 9% (che ovviamente l&#8217;algoritmo non conosce), su 5000 visitatori:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>set.seed(7)\ntrue_rates &lt;- c(0.05, 0.07, 0.09)   # 3 varianti, la terza e' la migliore\nK &lt;- length(true_rates); N &lt;- 5000\nalpha &lt;- rep(1, K); beta_ &lt;- rep(1, K)   # prior Beta(1,1) per braccio\npulls &lt;- rep(0, K); rewards &lt;- rep(0, K)\nfor (t in 1:N) {\n  theta &lt;- rbeta(K, alpha, beta_)        # campiona un tasso per braccio\n  arm &lt;- which.max(theta)                 # gioca il braccio campionato migliore\n  r &lt;- rbinom(1, 1, true_rates[arm])      # esito (conv si\/no)\n  alpha[arm] &lt;- alpha[arm] + r; beta_[arm] &lt;- beta_[arm] + (1 - r)\n  pulls[arm] &lt;- pulls[arm] + 1; rewards[arm] &lt;- rewards[arm] + r\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Il ciclo \u00e8 tutto qui. A ogni iterazione campioniamo un <code>theta<\/code> per braccio (<code>rbeta<\/code>), scegliamo il massimo (<code>which.max<\/code>), simuliamo l&#8217;esito (<code>rbinom<\/code>) e aggiorniamo i parametri del braccio giocato: una conversione aumenta di uno il suo <code>alpha<\/code>, una mancata conversione il suo <code>beta_<\/code>. Nessun&#8217;altra logica, nessuna soglia da tarare.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"regret-evitato\">Quanto si guadagna: il regret evitato<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Vediamo dove ci ha portato la simulazione. La prima cosa da guardare \u00e8 come si sono distribuiti i 5000 visitatori fra i tre bracci:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat(\"pull per braccio:\", pulls, \"\\n\")\ncat(\"conv totali bandit:\", sum(rewards), \"\\n\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output: pull per braccio = 359, 278, 4363; conv totali bandit = 424.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Dei 5000 visitatori, ben 4363 sono finiti sulla variante migliore, e solo poco pi\u00f9 di 600 in totale sulle due peggiori.<\/strong> L&#8217;algoritmo, senza che gli avessimo detto nulla sui tassi veri, ha capito da solo quale braccio premiare e ci ha dirottato la stragrande maggioranza del traffico. \u00c8 esattamente lo sfruttamento che volevamo, raggiunto attraverso un&#8217;esplorazione iniziale appena sufficiente a distinguere i bracci.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ora il confronto che d\u00e0 la misura del vantaggio. Un A\/B test classico avrebbe diviso i 5000 visitatori in parti uguali fra le tre varianti, circa 1667 ciascuna, per tutta la durata. Le conversioni attese in quello scenario sono semplicemente la media dei tre tassi moltiplicata per il numero di visitatori:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>exp_ab &lt;- sum(true_rates) \/ K * N\ncat(\"conv attese A\/B equipartito:\", round(exp_ab), \"\\n\")\ncat(\"regret evitato (circa):\", round(sum(rewards) - exp_ab), \"conv\\n\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Output: conv attese A\/B equipartito = 350; regret evitato (circa) = 74 conv.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un A\/B equipartito si sarebbe portato a casa circa 350 conversioni; il bandit ne ha raccolte 424. La differenza, circa <strong>74 conversioni in pi\u00f9 sullo stesso identico traffico<\/strong>, \u00e8 ci\u00f2 che in gergo si chiama <em>regret<\/em> evitato: il rimpianto, cio\u00e8 le conversioni perse per aver giocato i bracci sbagliati, che l&#8217;allocazione adattiva ci ha risparmiato. In termini pi\u00f9 chiari e diretti, a parit\u00e0 di visitatori il bandit ha convertito oltre il 20% in pi\u00f9, semplicemente non insistendo sulle varianti deboli.<br> Si noti che non abbiamo dovuto sacrificare nulla per ottenerlo: alla fine dell&#8217;esperimento conosciamo comunque il vincitore (anzi, lo conosciamo con grande sicurezza, visti i 4363 dati raccolti su di esso), e nel frattempo abbiamo convertito di pi\u00f9. \u00c8 il vantaggio strutturale del bandit sul test a campione fisso.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"quando-ha-senso\">Quando ha senso (e quando no)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Detto questo, un bandit non \u00e8 la risposta giusta a ogni domanda, ed \u00e8 importante capire dove brilla e dove invece un A\/B test tradizionale resta preferibile.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il bandit d\u00e0 il meglio di s\u00e9 quando il traffico \u00e8 <strong>continuo e di lungo corso<\/strong> (una pagina sempre attiva, una campagna che gira per mesi) e quando le varianti da confrontare sono <strong>molte<\/strong>: l\u00ec l&#8217;allocazione adattiva ripaga, perch\u00e9 c&#8217;\u00e8 tempo e volume per spostare il traffico e tante varianti deboli da abbandonare in fretta. \u00c8 ideale per ottimizzazioni continue in cui l&#8217;obiettivo \u00e8 massimizzare le conversioni lungo tutto il percorso, non scattare una fotografia statistica pulita in un dato momento.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Quando invece l&#8217;obiettivo \u00e8 proprio quella fotografia \u2014 una stima precisa e imparziale di <em>quanto<\/em> una variante \u00e8 migliore, magari da difendere davanti a un cliente o da usare per una decisione strategica \u2014 il test a campione fisso resta pi\u00f9 adatto: proprio perch\u00e9 esplora in parti uguali, raccoglie dati bilanciati su tutte le varianti e produce stime pi\u00f9 nette dell&#8217;effetto. Il bandit, concentrandosi presto sul vincitore, raccoglie pochi dati sui perdenti e quindi stima peggio <em>di quanto<\/em> siano peggiori.<\/p>\n\n\n\n<p class=\"has-light-gray-background-color has-background wp-block-paragraph\">Un avvertimento: il bandit d\u00e0 per scontato che i tassi di conversione restino stabili nel tempo. Ma il mondo reale \u00e8 spesso non stazionario \u2014 stagionalit\u00e0, cambi di pubblico, una promozione che parte. Se la variante migliore cambia <em>dopo<\/em> che l&#8217;algoritmo si \u00e8 gi\u00e0 concentrato su un&#8217;altra, un Thompson sampling ingenuo come quello visto qui fatica ad accorgersene, perch\u00e9 ha smesso di esplorare le alternative. In contesti che cambiano servono varianti che &#8220;dimenticano&#8221; i dati vecchi (per esempio scontando gradualmente le osservazioni passate), altrimenti si rischia di restare ancorati a un vincitore che non lo \u00e8 pi\u00f9.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prova-tu\">Prova tu<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La simulazione \u00e8 un terreno di gioco perfetto per costruire l&#8217;intuizione. Riprendendo il codice qui sopra, prova a cambiare i numeri di partenza e osserva come si ridistribuiscono i <code>pulls<\/code>:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Avvicina i tassi veri, per esempio <code>true_rates &lt;- c(0.07, 0.08, 0.09)<\/code>: con varianti pi\u00f9 simili, quanto pi\u00f9 traffico serve prima che il braccio migliore si stacchi? La concentrazione dei pull resta cos\u00ec netta?<\/li><li>Riduci drasticamente il traffico (<code>N &lt;- 500<\/code>): con pochi visitatori il bandit fa ancora in tempo a individuare il vincitore, o l&#8217;esplorazione si mangia tutto il budget?<\/li><li>Aggiungi varianti: passa a quattro o cinque bracci. Con pi\u00f9 alternative da scartare, il vantaggio sul <code>regret evitato<\/code> rispetto all&#8217;A\/B equipartito cresce o si assottiglia?<\/li><\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Suggerimento: la struttura del ciclo non cambia mai, basta modificare <code>true_rates<\/code> e <code>N<\/code>. \u00c8 proprio osservando come il vettore <code>pulls<\/code> si sbilancia (o non si sbilancia) in funzione della distanza fra i tassi che si capisce davvero cosa fa, sotto il cofano, il Thompson sampling.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Finora abbiamo usato Bayes per <em>decidere fra varianti<\/em>: stimare un tasso, confrontarne due, allocare il traffico in modo adattivo. Ma lo stesso impianto \u2014 un prior, dei dati, un posterior \u2014 serve anche per un compito diverso e molto comune: <em>classificare<\/em>, cio\u00e8 assegnare a ogni nuova osservazione l&#8217;etichetta pi\u00f9 probabile date le sue caratteristiche. \u00c8 il salto dal decidere al classificare, e l&#8217;algoritmo che lo fa con eleganza disarmante, ancora una volta partendo dal teorema di Bayes, \u00e8 il <em>Naive Bayes<\/em>: l&#8217;argomento del prossimo articolo.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"per-approfondire\">Per approfondire<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Se vuoi approfondire i bandit applicati all&#8217;ottimizzazione dei siti web, <a href=\"https:\/\/www.amazon.it\/dp\/1449341330?tag=consulenzeinf-21\" rel=\"nofollow sponsored noopener\" target=\"_blank\"><em>Bandit Algorithms for Website Optimization<\/em><\/a> di John Myles White \u00e8 il libro che consiglio. \u00c8 un volumetto agile e dichiaratamente pratico, che mette a confronto A\/B test e algoritmi bandit (epsilon-greedy, softmax, UCB) proprio nell&#8217;ottica di chi ottimizza pagine e conversioni, con il codice sotto mano. \u00c8 il punto di partenza ideale per chi vuole passare dalla simulazione che abbiamo visto a un bandit che gira davvero sul proprio sito.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Questo articolo fa parte del percorso <a href=\"https:\/\/www.gironi.it\/blog\/approccio-bayesiano\/\">\u00abL&#8217;approccio bayesiano\u00bb<\/a>, la guida ragionata agli articoli su statistica e inferenza bayesiana applicate alla SEO.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nell&#8217;articolo sull&#8217;A\/B test bayesiano abbiamo confrontato due varianti a campione fisso: si raccolgono i dati per tutta la durata pianificata, si calcola la probabilit\u00e0 che B batta A, si decide. \u00c8 un metodo solido, ma porta con s\u00e9 un costo che di solito passa sotto silenzio. Quel costo \u00e8 il traffico che, per tutta la &hellip; <a href=\"https:\/\/www.gironi.it\/blog\/multi-armed-bandit-thompson\/\" class=\"more-link\">Leggi tutto<span class=\"screen-reader-text\"> &#8220;Multi-armed bandit: ottimizzare le varianti mentre il test \u00e8 ancora in corso&#8221;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","footnotes":""},"categories":[629],"tags":[],"class_list":["post-3888","post","type-post","status-publish","format-standard","hentry","category-statistica-it"],"lang":"it","translations":{"it":3888,"en":3889},"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"post-thumbnail":false},"uagb_author_info":{"display_name":"Paolo Gironi","author_link":"https:\/\/www.gironi.it\/blog\/author\/autore-articoli\/"},"uagb_comment_info":0,"uagb_excerpt":"Nell&#8217;articolo sull&#8217;A\/B test bayesiano abbiamo confrontato due varianti a campione fisso: si raccolgono i dati per tutta la durata pianificata, si calcola la probabilit\u00e0 che B batta A, si decide. \u00c8 un metodo solido, ma porta con s\u00e9 un costo che di solito passa sotto silenzio. Quel costo \u00e8 il traffico che, per tutta la&hellip;","_links":{"self":[{"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/posts\/3888","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/comments?post=3888"}],"version-history":[{"count":4,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/posts\/3888\/revisions"}],"predecessor-version":[{"id":3935,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/posts\/3888\/revisions\/3935"}],"wp:attachment":[{"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/media?parent=3888"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/categories?post=3888"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gironi.it\/blog\/wp-json\/wp\/v2\/tags?post=3888"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}