Multi-Armed Bandit: Cos’è, Come Funziona e Perché Sostituisce l’A/B Testing

Cerca:

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
Multi-Armed Bandit

Sei in una città nuova per il weekend. Esci dall’hotel per cena e ti trovi davanti tre ristoranti.

Il primo ha un profumo fantastico, ma c’è fila.

Il secondo sembra un’onesta trattoria locale.

Il terzo è quasi vuoto, ma ha un menu intrigante.

Cosa fai?

Rischi la delusione pur di scovare una perla nascosta, o vai sul sicuro?

Questa non è solo fame: è il dilemma matematico che tiene svegli i data scientist di Netflix e Spotify. Nel calcolo delle probabilità si chiama Multi-Armed Bandit (MAB), un nome che evoca le file di slot machine dei casinò (“one-armed bandits”). Ogni macchina ha una probabilità sconosciuta di pagare una vincita; il tuo obiettivo è capire quale leva tirare per massimizzare il bottino prima che finiscano i gettoni.

Pubblicità

Il cuore del problema: Esplorare o Sfruttare?

La vera sfida del Multi-Armed Bandit non è trovare l’opzione perfetta, ma gestire il bilanciamento tra due forze opposte:

  • Exploration (Esplorazione): Raccogliere informazioni provando opzioni nuove o poco note. Costa tempo e risorse, ma evita di ignorare alternative migliori.

  • Exploitation (Sfruttamento): Capitalizzare sulle informazioni già in tuo possesso, scegliendo ripetutamente l’opzione che finora ha reso di più.

Se esplori troppo a lungo, sprechi budget su alternative mediocri. Se punti tutto sullo sfruttamento immediato, rischi di accontentarti di un ottimo locale, senza accorgerti che la scelta perfetta era a un passo da te.

Questo trade-off non si limita ai ristoranti, ma governa scenari ad altissimo budget:

  • Medicina: Un medico deve decidere se somministrare un farmaco sperimentale promettente (esplorazione) o andare sul sicuro con la terapia standard (sfruttamento).

  • E-commerce: Amazon deve decidere se mostrarti un prodotto appena lanciato o il bestseller di sempre.

Algoritmi alla prova: L’approccio [math]\epsilon[/math]-Greedy

L’algoritmo più intuitivo per risolvere questo dilemma è l’Epsilon-Greedy ([math]\epsilon[/math]-greedy).

Il meccanismo definisce una costante [math]\epsilon[/math] (ad esempio [math]0.10[/math], ovvero il 10%) che determina la quota di casualità:

  • Con probabilità [math]\epsilon[/math], l’algoritmo esplora: sceglie un’opzione a caso.
  • Con probabilità [math]1-\epsilon[/math] (il 90%), l’algoritmo sfrutta: sceglie l’opzione che ha mostrato la media di successo più alta.

Ecco come si comporta questo modello simulato in Python su un ciclo di 1000 tentativi (i passaggi chiave mostrano come cambiano le stime nel tempo):

import random

# --------------------------------------------------
# PARAMETRI
# --------------------------------------------------

PROBS = [0.30, 0.50, 0.80]
NAMES = ["A", "B", "C"]
EPSILON = 0.10
T = 1000
MU_STAR = max(PROBS)

# --------------------------------------------------
# STATO DELL'AGENTE
# --------------------------------------------------

counts = [0] * len(PROBS)
rewards = [0] * len(PROBS)

# --------------------------------------------------
# FUNZIONI
# --------------------------------------------------

def estimated_mean(arm):
    """Media empirica stimata."""
    if counts[arm] == 0:
        return 0
    return rewards[arm] / counts[arm]

def choose_arm():
    """
    Epsilon-Greedy con inizializzazione corretta.
    Ogni braccio viene provato almeno una volta.
    """
    for arm in range(len(PROBS)):
        if counts[arm] == 0:
            return arm

    if random.random() < EPSILON:
        # EXPLORATION
        return random.randrange(len(PROBS))

    # EXPLOITATION
    return max(range(len(PROBS)), key=estimated_mean)
 
def pull(arm):
    """
    Simula il ristorante.
    Restituisce:
        1 = cliente soddisfatto
        0 = cliente insoddisfatto
    """
    return 1 if random.random() < PROBS[arm] else 0 
# -------------------------------------------------- 
# SIMULAZIONE 
# -------------------------------------------------- 
cum_reward = 0 
regret_theoretical = 0 
print(f"{'Step':>6} | {'Arm':^5} | {'Reward':^6} | {'CumReward':>10} | {'Regret':>8}")
print("-" * 60)

for t in range(1, T + 1):
    arm = choose_arm()
    reward = pull(arm)
    counts[arm] += 1
    rewards[arm] += reward
    cum_reward += reward
    regret_theoretical += MU_STAR - PROBS[arm]

    if t == 1 or t % 100 == 0:
        means = " | ".join(f"{NAMES[i]}={estimated_mean(i):.2f}" for i in range(len(PROBS)))
        print(f"{t:>6} | {NAMES[arm]:^5} | {reward:^6} | {cum_reward:>10} | {regret_theoretical:>8.2f}")
        print("       Stime:", means)

 

# --------------------------------------------------
# RISULTATI FINALI
# --------------------------------------------------

print("\n" + "=" * 60)
print("RISULTATI FINALI")
print("=" * 60)

print(f"\nTentativi totali : {T}")
print(f"Epsilon          : {EPSILON}")
print(f"Reward totale    : {cum_reward}")
print(f"Ottimo teorico   : {MU_STAR * T:.0f}")
print(f"Regret teorico   : {regret_theoretical:.2f}")
efficiency = (cum_reward / (MU_STAR * T)) * 100
print(f"Efficienza       : {efficiency:.1f}%")

print("\nDistribuzione scelte:\n")
for i in range(len(PROBS)):
    pct = counts[i] / T * 100
    bar = "█" * int(pct / 2)
    print(f"{NAMES[i]} ({PROBS[i]*100:.0f}%) → {counts[i]:4d} scelte ({pct:5.1f}%) {bar}")
    print(f"    Stima finale: {estimated_mean(i):.3f}")

best_arm = counts.index(max(counts))
print(f"\nBraccio preferito: {NAMES[best_arm]}")
print(f"Probabilità reale: {PROBS[best_arm]*100:.0f}%")

# --------------------------------------------------
# FORMULE
# --------------------------------------------------

print("\n" + "-" * 60)
print("FORMULE")

print("\nMedia stimata:")
print("mu_hat = successi / tentativi")

print("\nRegret teorico:")
print("R(T) = Σ (μ* - μ_a)")

print("\nReward cumulativo:")
print("Reward(T) = Σ r_t")
  Step |  Arm  | Reward |  CumReward |   Regret
------------------------------------------------------------
     1 |   A   |   0    |          0 |     0.50
       Stime: A=0.00 | B=0.00 | C=0.00
   100 |   C   |   1    |         74 |     2.90
       Stime: A=0.50 | B=0.67 | C=0.75
   200 |   C   |   1    |        150 |     5.60
       Stime: A=0.57 | B=0.71 | C=0.76
   300 |   C   |   1    |        228 |     8.90
       Stime: A=0.46 | B=0.62 | C=0.78
   400 |   C   |   1    |        309 |    11.40
       Stime: A=0.47 | B=0.54 | C=0.79
   500 |   C   |   1    |        387 |    12.50
       Stime: A=0.44 | B=0.47 | C=0.80
   600 |   C   |   0    |        459 |    16.20
       Stime: A=0.43 | B=0.47 | C=0.79
   700 |   C   |   1    |        541 |    17.30
       Stime: A=0.41 | B=0.43 | C=0.80
   800 |   C   |   1    |        619 |    19.40
       Stime: A=0.36 | B=0.48 | C=0.80
   900 |   C   |   1    |        702 |    21.00
       Stime: A=0.33 | B=0.52 | C=0.80
  1000 |   C   |   1    |        780 |    23.90
       Stime: A=0.32 | B=0.50 | C=0.80

============================================================
RISULTATI FINALI
============================================================

Tentativi totali : 1000
Epsilon          : 0.1
Reward totale    : 780
Ottimo teorico   : 800
Regret teorico   : 23.90
Efficienza       : 97.5%

Distribuzione scelte:

A (30%) →   31 scelte (  3.1%) █
    Stima finale: 0.323
B (50%) →   28 scelte (  2.8%) █
    Stima finale: 0.500
C (80%) →  941 scelte ( 94.1%) ███████████████████████████████████████████████
    Stima finale: 0.803

Braccio preferito: C
Probabilità reale: 80%

------------------------------------------------------------
FORMULE

Media stimata:
mu_hat = successi / tentativi

Regret teorico:
R(T) = Σ (μ* - μ_a)

Reward cumulativo:
Reward(T) = Σ r_t

L’evoluzione dell’apprendimento

Guardando l’avanzamento dei cicli di test, si nota chiaramente come l’algoritmo “aggiusti il tiro”:

  • Step 100: Le stime sono ancora grezze e influenzate dal rumore statistico (A=0.50, B=0.67, C=0.75). L’algoritmo sta ancora pagando il prezzo dell’incertezza.
  • Step 500: Il sistema ha isolato la tendenza reale (A=0.44, B=0.47, C=0.80). Da qui in poi scatta lo sfruttamento intensivo del ristorante C.
  • Step 1000 (Risultati Finali):
    • Ristorante A (30% reale): Scelto 31 volte → Stima finale: 0.323
    • Ristorante B (50% reale): Scelto 28 volte → Stima finale: 0.500
    • Ristorante C (80% reale): Scelto 941 volte → Stima finale: 0.803
Forse potrebbe interessarti anche:  Come Risolvere Sistemi di Equazioni Esponenziali con Python: Algoritmo, Simulatore Interattivo e Analisi Matematica

L’efficienza finale si attesta al 97.5%. Significa che abbiamo perso solo il 2.5% del rendimento massimo teorico a causa dei tentativi necessari per studiare il mercato.

Perché i Bandit stanno uccidendo l’A/B Testing tradizionale

Nelle aziende basate sui dati, il Multi-Armed Bandit sta rapidamente sostituendo i classici test A/B a causa di una metrica fondamentale: il Regret (il Rimprovero o Rimpianto).

[math]\text{Regret}(T) = T \mu^* – \sum_{t=1}^{T} r_t[/math]

In un A/B test classico, dividi il traffico al 50% tra la versione A e la versione B per un mese. Se la versione A si rivela disastrosa dopo tre giorni, sei comunque costretto a mostrarla al 50% degli utenti fino alla fine del mese per garantire la validità del test statico. Questo genera un regret lineare e una massiccia perdita di conversioni.

Il Multi-Armed Bandit (e le sue evoluzioni come il Contextual Bandit o il Thompson Sampling) adatta l’allocazione del traffico in tempo reale. Non appena l’algoritmo intuisce che un’opzione è superiore, devia automaticamente il grosso del traffico su di essa, riducendo l’esplorazione al minimo indispensabile. Il profitto sale, il regret crolla.

Altri algoritmi più sofisticati

  • Upper Confidence Bound (UCB): sceglie il braccio che massimizza una combinazione di ricompensa media e incertezza. Penalizza i bracci poco esplorati dando loro un bonus di esplorazione che diminuisce con il numero di prove.
  • Thompson Sampling: approccio bayesiano. Mantiene una distribuzione di probabilità sulla vera ricompensa di ciascun braccio e campiona da essa. È spesso lo stato dell’arte per molti problemi.
  • Gradient Bandit e algoritmi basati su policy gradient.

Analisi e peculiarità dell’esercizio

L’esercizio in Python presenta alcune finezze logiche fondamentali che meritano di essere isolate per capire perché questo codice funziona così bene:

Forse potrebbe interessarti anche:  Navigare nel Mondo delle Probabilità: Un Viaggio nella Regressione Logistica

La soluzione al problema del Cold-Start

All’interno della funzione choose_arm(), le prime righe contengono un blocco di codice cruciale:

for arm in range(len(PROBS)):
    if counts[arm] == 0:
        return arm

Senza questa inizializzazione, se il primo tentativo in assoluto sul ristorante A restituisse un successo (1) e il primo tentativo su B restituisse un fallimento (0), l’algoritmo considererebbe la media di A pari a 1.0 e quella di B pari a 0.

Selezionando l’opzione max, continuerebbe a sfruttare A senza dare a B o C nemmeno una chance iniziale. Obbligare l’algoritmo a testare ogni opzione almeno una volta azzera questo bias iniziale.

Il paradosso del 94% delle scelte

Un’analisi superficiale dei risultati finali potrebbe far sorgere un dubbio:

se l’algoritmo sfrutta il 90% delle volte ([math]1 – \epsilon = 0.90[/math]), perché il ristorante C (il migliore) è stato scelto ben 941 volte (94.1%) invece di 900?

La risposta risiede nella matematica dell’esplorazione casuale. Quando l’algoritmo decide di esplorare (il 10% delle volte), sceglie un braccio tirando a indovinare tra i 3 disponibili. Questo significa che anche durante la fase di pura esplorazione, il ristorante migliore ha comunque [math]1/3[/math] di probabilità di essere estratto.

La percentuale teorica di scelte del braccio migliore è quindi:

[math]\text{Target} = (1 – \epsilon) + \left(\frac{\epsilon}{\text{numero di bracci}}\right) = 0.90 + \left(\frac{0.10}{3}\right) = 0.90 + 0.0333 = 93.33\%[/math]

Il risultato reale della simulazione ([math]94.1\%[/math]) è perfettamente in linea con la teoria probabilistica e dimostra la correttezza del campionamento.

Il limite intrinseco di [math]\epsilon[/math]-Greedy

L’esercizio mostra anche il limite strutturale dell’algoritmo: l’esplorazione non si ferma mai. Anche al millesimo step, con stime ormai consolidate al millesimo, l’algoritmo continuerà a lanciare utenti sui ristoranti scadenti il 10% delle volte. Nella pratica aziendale, si preferiscono varianti a decadimento ([math]\epsilon[/math] decrescente nel tempo) o algoritmi superiori come il Thompson Sampling, in cui l’esplorazione si spegne dinamicamente man mano che aumenta la certezza statistica dei dati.

Metriche di performance

Per valutare un algoritmo bandit è essenziale monitorare tre grandezze:

Reward cumulativo

[math]R_t = \sum_{i=1}^{t} r_i[/math]
Misura quanto valore abbiamo accumulato fino al tempo [math]t[/math].

Frequenza di scelta dei bracci

Dopo molte iterazioni dovremmo vedere una distribuzione fortemente sbilanciata verso il braccio migliore.

Ad esempio:

Macchina Probabilità reale Scelte dopo 1000 iterazioni
A 30% 85
B 50% 120
C 80% 795

Regret (il rimpianto)

La metrica più importante nella letteratura scientifica:
[math]\text{Regret}(T) = T \mu^* – \sum_{t=1}^{T} r_t[/math]
dove [math]\mu^*[/math] è la ricompensa attesa del braccio ottimale.

Il regret misura quanto abbiamo “perso” rispetto all’oracolo che conosceva fin dall’inizio la macchina migliore.

Un buon algoritmo ha un regret che cresce logaritmicamente con [math]T[/math] (sublineare), anziché linearmente.

Simulazioni: cosa osservare nella pratica

Quando si implementa una simulazione di Multi-Armed Bandit , è istruttivo osservare:

  • Come il regret cresce nel tempo per diversi algoritmi.
  • La velocità con cui l’algoritmo identifica il braccio migliore.
  • Il comportamento con distribuzioni di reward diverse (Bernoulliane, Gaussiane, non stazionarie).
  • L’effetto del parametro [math]\epsilon[/math]: valori troppo alti portano a troppo rumore, valori troppo bassi a convergenza lenta o sub-ottima.
Forse potrebbe interessarti anche:  Multi‑Armed Bandit: Epsilon‑Greedy vs Thompson Sampling

In una tipica simulazione con i tre ristoranti, dopo poche centinaia di iterazioni l’algoritmo epsilon-greedy o Thompson Sampling sceglie il ristorante C oltre l’80-90% delle volte.

Applicazioni reali del Multi-Armed Bandit

Il problema MAB è ovunque nella vita moderna:

  • A/B Testing e ottimizzazione web: invece di test fissi, si usano algoritmi bandit per allocare più traffico alla versione migliore mentre il test è ancora in corso (Contextual Bandits).
  • Recommendation Systems: Netflix, YouTube, Spotify usano varianti di bandit per bilanciare novità e preferenze consolidate.
  • Pubblicità online (Ad Tech): scegliere quale annuncio mostrare a un utente in tempo reale.
  • Trial clinici: allocare pazienti ai trattamenti più promettenti riducendo il numero di persone che ricevono terapie inefficaci.
  • Robotica e controllo: un robot deve esplorare l’ambiente senza trascurare compiti noti.
  • Finanza: portfolio optimization sotto incertezza.
  • Personalizzazione dell’istruzione: sistemi di tutoring adattivi.

Varianti avanzate

  • Contextual Bandits: i bracci dipendono dal contesto (es. caratteristiche dell’utente). È il modello dietro molti sistemi di raccomandazione moderni.
  • Non-stationary Bandits: le probabilità cambiano nel tempo (es. preferenze dei clienti che evolvono).
  • Combinatorial Bandits: scegliere più bracci contemporaneamente (es. assortimento di prodotti).
  • Bandits con grafi o relazioni: quando esiste una struttura tra le opzioni.

Perché è fondamentale per la Scienza dei Dati

Il Multi-Armed Bandit insegna una lezione profonda: l’apprendimento ottimale non è mai solo esplorazione né solo sfruttamento. È una danza continua tra curiosità e pragmatismo.

Nell’era dei Big Data e dell’AI, dove le decisioni devono essere prese in tempo reale con informazioni parziali, padroneggiare i bandit significa passare da un approccio “testa tutto e poi scegli” a un approccio sequenziale e adattivo, molto più efficiente.

Inoltre, il concetto di regret fornisce un framework matematico rigoroso per quantificare il costo dell’incertezza, fondamentale sia nella ricerca teorica che nelle applicazioni aziendali.

Conclusione

Il problema del Multi-Armed Bandit è importante perché cattura l’essenza dell’intelligenza: prendere decisioni buone in un mondo incerto, con informazioni limitate e costi per l’esperimento. Dal semplice epsilon-greedy fino agli algoritmi bayesiani più sofisticati, la famiglia dei bandit offre strumenti potenti, eleganti e sorprendentemente pratici.

Che tu stia ottimizzando una campagna marketing, progettando un sistema di raccomandazione o semplicemente cercando di prendere decisioni migliori nella vita, capire exploration vs exploitation ti dà un vantaggio cognitivo enorme.

La prossima volta che devi scegliere un ristorante in una città nuova, ricordati: stai risolvendo un Multi-Armed Bandit. E con gli algoritmi giusti, puoi massimizzare la tua soddisfazione cumulativa nel corso della vita.

Pubblicità