Abbiamo avuto modo di esaminare, nell’articolo sull’A/B testing classico, come confrontare due varianti con il test per due proporzioni: calcoliamo una statistica, otteniamo un p-value, decidiamo se rifiutare l’ipotesi nulla. Funziona, ed è il pane quotidiano di chi fa esperimenti online. Ma c’è uno scarto sottile tra quello che il p-value ci dice e quello che vorremmo davvero sapere.
Il p-value risponde a una domanda contorta: “se A e B fossero identiche, quanto sarebbe improbabile osservare una differenza grande come questa?”. La domanda che ci interessa nella realtà operativa è un’altra, molto più diretta: qual è la probabilità che B sia meglio di A? E, subito dopo: di quanto è meglio, e quanto possiamo fidarci di quel “di quanto”?
L’approccio bayesiano risponde a queste due domande in modo nativo. In questo articolo lo applichiamo al confronto tra due varianti, riprendendo il filo lasciato in sospeso quando abbiamo stimato il conversion rate di una singola variante.
Di cosa parleremo:
- Due posterior invece di uno: una distribuzione per ciascuna variante
- Qual è la probabilità che B vinca davvero
- Di quanto è meglio: la distribuzione della differenza
- Quando fermarsi: expected loss e il problema del peeking
- Prova tu
- Per approfondire
Due posterior invece di uno
Quando abbiamo stimato il conversion rate di una singola variante, abbiamo visto che — partendo da un prior Beta e osservando dati binomiali — il posterior è ancora una distribuzione Beta. La regola di aggiornamento era una semplice aritmetica: se il prior è Beta(α, β) e osserviamo \( k \) conversioni su \( n \) sessioni, il posterior è:
\( Beta(\alpha + k,\ \beta + (n – k)) \\ \)In un A/B test non abbiamo una proporzione sola, ne abbiamo due: una per il controllo (A) e una per il trattamento (B). Il meccanismo, però, è identico: costruiamo un posterior per ciascuna variante, in modo indipendente, applicando la stessa regola due volte.
Facciamo un esempio al volo. Abbiamo testato due versioni di una landing page, assegnando i visitatori a caso:
- Variante A (controllo): 90 conversioni su 1000 sessioni → tasso grezzo 9,0%
- Variante B (trattamento): 120 conversioni su 1000 sessioni → tasso grezzo 12,0%
Partiamo da un prior non informativo Beta(1, 1) per entrambe — “non sappiamo nulla, prima dei dati ogni tasso è ugualmente plausibile”. Applicando la regola, il posterior di A è Beta(91, 911) e quello di B è Beta(121, 881).
Costruisco i due posterior in R, campionandoli per simulazione:
set.seed(42)
# Variante A: 90 conv / 1000 ; Variante B: 120 conv / 1000
cA <- 90; nA <- 1000; cB <- 120; nB <- 1000
# Posterior con prior uniforme Beta(1,1)
postA <- rbeta(1e5, 1 + cA, 1 + nA - cA) # Beta(91, 911)
postB <- rbeta(1e5, 1 + cB, 1 + nB - cB) # Beta(121, 881)Adesso abbiamo in mano due distribuzioni, non due numeri. Ed è proprio questo il punto: invece di confrontare 9,0% contro 12,0% come fossero due valori fissi, confrontiamo l’intera incertezza che li circonda. Le domande operative diventano operazioni su queste distribuzioni.
Qual è la probabilità che B vinca?
La prima domanda — quella che il p-value non risponde mai direttamente — è la probabilità che B sia davvero migliore di A.
Con i posterior in mano, il calcolo è disarmante: confrontiamo i campioni di B con quelli di A, coppia per coppia, e contiamo in quale frazione dei casi B supera A. Quella frazione è la probabilità che cerchiamo.
Calcolo in R la probabilità che B batta A:
cat("P(B>A) =", round(mean(postB > postA), 3), "\n")Output: P(B>A) = 0.985.
C’è il 98,5% di probabilità che la variante B converta meglio della variante A.
Si noti la differenza di registro rispetto al frequentista. Non stiamo dicendo “la differenza osservata è improbabile sotto l’ipotesi nulla”: stiamo dicendo, in modo diretto, che date le evidenze raccolte è quasi certo che B sia la variante migliore. È esattamente l’affermazione su cui vorremmo basare una decisione — e l’approccio bayesiano ce la consegna senza giri di parole.
Di quanto è meglio: la distribuzione della differenza
Sapere che B vince con probabilità del 98,5% non basta a decidere. Un miglioramento c’è quasi di sicuro, ma se fosse di due decimi di punto percentuale, forse non varrebbe la pena di mandare in produzione la nuova pagina. La domanda successiva è quindi: di quanto è meglio?
La risposta vive nella distribuzione della differenza tra i due posterior. Sottraiamo, campione per campione, il tasso di A da quello di B: otteniamo una nuova distribuzione, quella dell’uplift. Da lì leggiamo sia il valore tipico (la media) sia un intervallo di credibilità che ne quantifica l’incertezza.
Calcolo in R la differenza e il suo intervallo al 95%:
diff <- postB - postA
cat("uplift medio (punti %) =", round(mean(diff)*100, 2), "\n")
cat("CI 95% differenza =", round(quantile(diff, c(.025,.975))*100, 2), "\n")Output: uplift medio = 3.00 punti %, CI 95% = [0.31, 5.68].
Il guadagno atteso è di circa 3 punti percentuali di conversione, con un intervallo di credibilità al 95% che va da 0,31 a 5,68 punti.
Anche qui il significato è diretto, non una proprietà astratta della procedura: c’è il 95% di probabilità che il vero miglioramento di B su A stia tra 0,3 e 5,7 punti percentuali. L’intervallo non tocca lo zero, il che conferma — coerentemente con il 98,5% di prima — che B è quasi certamente superiore. Ma il dato prezioso è l’ampiezza: il miglioramento potrebbe essere modesto (mezzo punto) o robusto (oltre cinque punti), e questa forbice è un’informazione che la decisione operativa deve tenere presente.
Quando fermarsi: expected loss e il problema del peeking
Nell’articolo sull’A/B testing classico abbiamo dedicato spazio a uno degli errori più insidiosi: il peeking, cioè sbirciare i dati intermedi e fermarsi appena la differenza sembra significativa. Nell’impianto frequentista questo gonfia il tasso di falsi positivi, perché ogni occhiata è di fatto un nuovo test sulla stessa ipotesi nulla, e i test ripetuti moltiplicano le occasioni di sbagliare.
L’approccio bayesiano cambia la natura del problema. Qui non ripetiamo un test su un’ipotesi nulla: aggiorniamo una credenza. Il posterior di oggi diventa il prior di domani, e guardare i dati man mano non “consuma” un budget di errore nello stesso modo. Questo non vuol dire che possiamo fermarci a capriccio: ci serve comunque una regola di arresto dichiarata in anticipo. E la regola bayesiana naturale non è “fermati quando P(B>A) è alta”, ma si basa sull’expected loss — la perdita attesa.
L’idea è questa: se scegliamo B ma in realtà fosse A la variante migliore, sbagliamo, e l’entità dell’errore è quanto A batte B in quei casi. La perdita attesa di scegliere B è la media di questo “rimpianto” su tutta l’incertezza residua. A parole: di quanto, in media, ci pentiremmo di aver scelto B se ci sbagliassimo.
Calcolo in R la perdita attesa scegliendo B:
# Expected loss scegliendo B: perdita attesa se in realta' A fosse meglio
loss_B <- mean(pmax(postA - postB, 0))
cat("expected loss scegliendo B =", round(loss_B*100, 3), "punti %\n")Output: expected loss scegliendo B = 0.007 punti %.
La perdita attesa scegliendo B è di appena 0,007 punti percentuali: trascurabile. In termini più chiari e diretti, anche nello scenario sfortunato in cui ci sbagliassimo, il danno medio sarebbe minuscolo. Si fissa allora una soglia di tolleranza prima di iniziare — per esempio “mi fermo quando la perdita attesa scende sotto 0,01 punti” — e si lascia correre il test finché non la si raggiunge.
Un avvertimento: la libertà di guardare i dati in corsa non è un permesso di fermarsi quando il risultato ci aggrada. La regola di arresto — la soglia di expected loss, o un livello minimo di P(B>A) — va fissata prima di raccogliere i dati, esattamente come nel frequentista si fissa la dimensione campionaria. Il rigore non sta nel metodo che usiamo, ma nel decidere il criterio prima di vedere i numeri.
Prova tu
Un sito di lead generation testa due varianti del modulo di contatto. Sulla variante A si osservano 45 conversioni su 600 sessioni; sulla B, 52 conversioni su 600 sessioni.
1. Costruisci i due posterior con prior non informativo Beta(1, 1): postA <- rbeta(1e5, 1 + 45, 1 + 555) e l’analogo per B. 2. Calcola P(B>A): B è migliore con probabilità abbastanza alta da convincerti? 3. Calcola uplift medio e intervallo al 95% della differenza: l’intervallo tocca lo zero? 4. Calcola la expected loss scegliendo B. Con questi numeri (più vicini tra loro del caso visto sopra), come cambia rispetto all’esempio dell’articolo?
Suggerimento: la struttura del codice è identica a quella che abbiamo usato. Cambiano solo i conteggi di partenza — e il risultato, molto meno netto, è proprio il motivo per cui l’intervallo e la perdita attesa contano più di un semplice “ha vinto B”.
Finora abbiamo confrontato due varianti a campione fisso: raccogliamo i dati, calcoliamo, decidiamo. Ma se durante il test una variante si rivela nettamente migliore, perché continuare a mandare metà del traffico su quella peggiore? Si può fare di meglio: allocare il traffico in modo adattivo, spostandolo verso la variante che sta vincendo mentre il test è ancora in corso. È il salto dal test al bandit, l’argomento del prossimo articolo.
Per approfondire
Se vuoi approfondire l’A/B testing bayesiano con un taglio pratico e orientato al codice, Bayesian Methods for Hackers di Cameron Davidson-Pilon è il libro che consiglio. Affronta il ragionamento bayesiano partendo dalla programmazione invece che dalla matematica formale, e dedica un capitolo esplicito proprio al confronto bayesiano tra varianti — probabilità che B vinca, distribuzione della differenza, perdita attesa. È pensato per chi impara meglio leggendo codice che dimostrazioni.