A/B test bayesiano: non solo “se” B è meglio di A, ma “di quanto”

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.

È la domanda di ogni test di conversione con un risvolto SEO: due versioni della stessa landing page che si contendono lo stesso traffico organico, oppure due formulazioni di title e meta description giudicate dal loro CTR reale rispetto a quello atteso nella SERP. Ovunque ci siano successi su tentativi — clic su impression, iscrizioni su visite, conversioni su sessioni — il ragionamento che segue è lo stesso.

Di cosa parleremo:


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)) \\ \)

Detto a parole: aggiungiamo le conversioni osservate al primo parametro e le mancate conversioni al secondo, e non serve altro.

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 che riceve traffico organico, 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.

Vale la pena tenere a mente l’immagine: due rilievi di probabilità affiancati sull’asse dei tassi di conversione. Più quello di B è spostato a destra rispetto a quello di A, e meno i due si sovrappongono, più diventa probabile che B sia davvero la variante migliore.

Le due posterior Beta messe a confronto: non due numeri (9% e 12%) ma due distribuzioni, che ancora si sovrappongono un po' — ed è in quella sovrapposizione che vive il dubbio residuo.
Le due posterior Beta messe a confronto: non due numeri (9% e 12%) ma due distribuzioni, che ancora si sovrappongono un po’ — ed è in quella sovrapposizione che vive il dubbio residuo.

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?

Finora abbiamo tenuto A e B separati, un posterior ciascuno. Ma l’oggetto che ci interessa davvero non è nessuno dei due preso da solo: è la loro distanza. La risposta vive perciò 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.

La distribuzione dell'uplift B − A in punti percentuali: l'area a destra dello zero è la probabilità che B vinca (98,5%), la barra azzurra l'intervallo di credibilità al 95%, da 0,31 a 5,68 punti. Un solo grafico risponde a entrambe le domande — «se» e «di quanto».
La distribuzione dell’uplift B − A in punti percentuali: l’area a destra dello zero è la probabilità che B vinca (98,5%), la barra azzurra l’intervallo di credibilità al 95%, da 0,31 a 5,68 punti. Un solo grafico risponde a entrambe le domande — «se» e «di quanto».

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.

Ed è qui che si vede perché la perdita attesa, e non “P(B>A) è alta”, è il criterio giusto. Se rifacciamo i conti a ogni tappa del test, man mano che il traffico arriva, le due grandezze non scattano nello stesso momento: la probabilità che B vinca supera il 95% molto prima che la perdita attesa diventi davvero trascurabile.

Al crescere dei dati P(B>A) supera il 95% già intorno ai 570 visitatori per variante, ma la perdita attesa scende sotto la soglia di 0,01 punti solo verso gli 855: fermarsi appena la probabilità «sembra alta» è precisamente la trappola del peeking.
Al crescere dei dati P(B>A) supera il 95% già intorno ai 570 visitatori per variante, ma la perdita attesa scende sotto la soglia di 0,01 punti solo verso gli 855: fermarsi appena la probabilità «sembra alta» è precisamente la trappola del peeking.

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. Eppure la probabilità che B sia la migliore l’abbiamo appena vista ricalcolarsi a ogni tappa, mentre il traffico arriva — e se una variante si sta rivelando nettamente migliore, perché continuare a mandare metà dei visitatori su quella peggiore? Si può fare di meglio: usare quella probabilità in tempo reale per 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.

Questo articolo fa parte del percorso «L’approccio bayesiano», la guida ragionata agli articoli su statistica e inferenza bayesiana applicate alla SEO.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *