Test AB utilizzando Pyro

Consideriamo un’azienda che ha progettato una nuova pagina di destinazione del sito Web e desidera comprendere l’impatto che ciò avrà sulla conversione, ad esempio i visitatori continuano la sessione Web sul sito Web dopo essere arrivati ​​sulla pagina? Nel gruppo di test A, ai visitatori del sito web verrà mostrata la pagina di destinazione corrente. Nel gruppo di test B ai visitatori del sito verrà mostrata la nuova landing page. Nel resto dell’articolo mi riferirò al gruppo di test A come gruppo di controllo e al gruppo B come gruppo di trattamento. L’azienda è scettica riguardo al cambiamento e ha optato per una suddivisione 80/20 del traffico delle sessioni. Il numero totale di visitatori e il numero totale di conversioni di pagina per ciascun gruppo di test sono riepilogati di seguito.

Osservazioni di prova

L’ipotesi nulla del test AB è che non ci sarà alcun cambiamento nella conversione della pagina per i due gruppi di test. Nel quadro frequentista, ciò verrebbe espresso come segue per un test bilaterale, dove r_c e r_t sono i tassi di conversione della pagina rispettivamente nei gruppi di controllo e di trattamento.

Ipotesi Nulle e Alternative

Un test di significatività cercherebbe quindi di rifiutare o di non rifiutare l’ipotesi nulla. Nel quadro bayesiano, esprimiamo l’ipotesi nulla in modo leggermente diverso affermando la stessa cosa precedente per ciascuno dei gruppi di prova.

Fermiamoci e descriviamo esattamente cosa sta succedendo durante il nostro test. La variabile che ci interessa è il tasso di conversione della pagina. Questo viene calcolato semplicemente prendendo il numero di visitatori convertiti distinti rispetto al numero totale di visitatori. L’evento che genera questo tasso è se il visitatore fa clic sulla pagina. Ci sono solo due possibili risultati qui per ogni visitatore, o il visitatore fa clic sulla pagina e converte oppure no. Alcuni di voi potrebbero riconoscere che, per ciascun visitatore distinto, questo è un esempio di processo Bernoulli; c’è una prova e due possibili risultati. Ora, quando raccogliamo una serie di queste prove di Bernoulli, abbiamo una distribuzione binomiale. Quando la variabile casuale X ha distribuzione binomiale, le diamo la seguente notazione:

Notazione della distribuzione binomiale

Dove n è il numero di visitatori (o il numero di prove di Bernoulli) e p è la probabilità dell’evento in ciascuna prova. p è ciò che ci interessa qui, vogliamo capire qual è la probabilità che un visitatore si converta sulla pagina in ciascun gruppo di test. Abbiamo osservato alcuni dati, ma come accennato nella sezione precedente, dobbiamo prima definire i nostri dati a priori. Come sempre nella statistica bayesiana, dobbiamo definire questa distribuzione a priori come una distribuzione di probabilità. Come accennato in precedenza, questa distribuzione di probabilità è una caratterizzazione della nostra incertezza. Le distribuzioni beta sono comunemente utilizzate per modellare le probabilità, poiché sono definite tra gli intervalli di (0,1). Inoltre, l’utilizzo di una distribuzione beta come nostra precedente per una funzione di verosimiglianza binomiale ci dà l’utile proprietà della coniugazione, il che significa che il nostro posteriore sarà generato dalla stessa distribuzione del nostro precedente. Diciamo che la distribuzione beta è a coniugare precedente. Una distribuzione beta è definita da due parametri, alfa e, in modo confuso, beta.

Notazione della distribuzione beta

Con l’accesso ai dati storici, possiamo affermare un preventivo informato. Non abbiamo necessariamente bisogno di dati storici, potremmo usare la nostra intuizione per informare la nostra comprensione, ma per ora supponiamo di non avere nessuno dei due (più avanti in questo tutorial utilizzeremo dati a priori informati, ma per dimostrare l’impatto, inizierò con quelli non informati) . Supponiamo di non comprendere il tasso di conversione sul sito dell’azienda e quindi di definire il nostro valore precedente come Beta(1,1). Questo si chiama flat prior. La distribuzione di probabilità di questa funzione appare come nel grafico sottostante, uguale a una distribuzione uniforme definita tra gli intervalli (0,1). Assumendo un Beta(1,1) a priori, diciamo che tutti i possibili valori del tasso di conversione della pagina sono ugualmente probabili.

Credito: autore

Ora abbiamo tutte le informazioni di cui abbiamo bisogno, i precedenti e i dati. Passiamo al codice. Il codice qui fornito fornirà un framework per iniziare con i test AB utilizzando Pyro; trascura quindi alcune funzionalità del pacchetto. Per ottimizzare ulteriormente il tuo codice e sfruttare appieno le capacità di Pyro, ti consiglio di fare riferimento alla documentazione ufficiale.

Per prima cosa dobbiamo importare i nostri pacchetti. L’ultima riga è una buona pratica, in particolare quando si lavora sui notebook, cancellando l’archivio dei parametri che abbiamo accumulato.

import pyro
import pyro.distributions as dist
from pyro.infer import NUTS, MCMC
import torch
from torch import tensor
import matplotlib.pyplot as plt
import seaborn as sns
from functools import partial
import pandas as pd

pyro.clear_param_store()

I modelli in Pyro sono definiti come normali funzioni Python. Questo è utile perché rende intuitivo seguirlo.

def model(beta_alpha, beta_beta):
def _model_(traffic: tensor, number_of_conversions: tensor):
# Define Stochastic Primatives
prior_c = pyro.sample('prior_c', dist.Beta(beta_alpha, beta_beta))
prior_t = pyro.sample('prior_t', dist.Beta(beta_alpha, beta_beta))
priors = torch.stack((prior_c, prior_t))
# Define the Observed Stochastic Primatives
with pyro.plate('data'):
observations = pyro.sample('obs', dist.Binomial(traffic, priors),\
obs = number_of_conversions)
return partial(_model_)

Alcune cose da analizzare e spiegare qui. Innanzitutto, abbiamo una funzione racchiusa all’interno di una funzione esterna, la funzione esterna restituisce la funzione parziale della funzione interna. Ciò ci consente di modificare i nostri valori a priori, senza dover modificare il codice. Ho fatto riferimento alle variabili definite nella funzione interna come primitive, penso alle primitive come variabili nel modello. Abbiamo due tipi di primitive nel modello, stocastiche e stocastiche osservate. In Pyro, non dobbiamo definire esplicitamente la differenza, aggiungiamo semplicemente l’argomento obs al metodo campione quando è una primitiva osservata e Pyro la interpreta di conseguenza. Le primitive osservate sono contenute nel gestore di contesto pyro.plate(), che è la migliore pratica e rende il nostro codice più pulito. Le nostre primitive stocastiche sono i nostri due a priori, caratterizzati da distribuzioni Beta, governate dai parametri alfa e beta che passiamo dalla funzione esterna. Come accennato in precedenza, affermiamo l’ipotesi nulla definendoli uguali. Quindi impiliamo insieme queste due primitive utilizzando tensor.stack(), che esegue un’operazione simile alla concatenazione di un array Numpy. Ciò restituirà un tensore, la struttura dati richiesta per l’inferenza in Pyro. Abbiamo definito il nostro modello, ora passiamo alla fase di inferenza.

Come accennato in precedenza, questo tutorial utilizzerà MCMC. La funzione seguente prenderà come parametro il modello che abbiamo definito sopra e il numero di campioni che desideriamo utilizzare per generare la nostra distribuzione a posteriori. Passiamo anche i nostri dati alla funzione, come abbiamo fatto per il modello.

def run_infernce(model, number_of_samples, traffic, number_of_conversions):
kernel = NUTS(model)

mcmc = MCMC(kernel, num_samples = number_of_samples, warmup_steps = 200)

mcmc.run(traffic, number_of_conversions)

return mcmc

La prima riga all’interno di questa funzione definisce il nostro kernel. Usiamo la classe NUTS per definire il nostro kernel, che sta per No-U-Turn Sampler, una versione di autotuning dell’Hamiltonian Monte Carlo. Questo dice a Pyro come campionare dallo spazio di probabilità a posteriori. Ancora una volta, approfondire questo argomento va oltre lo scopo di questo articolo, ma per ora è sufficiente sapere che NUTS ci consente di campionare dallo spazio delle probabilità in modo intelligente. Il kernel viene quindi utilizzato per inizializzare la classe MCMC sulla seconda riga, specificandola per utilizzare NUTS. Passiamo l’argomento number_of_samples nella classe MCMC che è il numero di campioni utilizzati per generare la distribuzione a posteriori. Assegniamo la classe MCMC inizializzata alla variabile mcmc e chiamiamo il metodo run(), passando i nostri dati come parametri. La funzione restituisce la variabile mcmc.

Questo è tutto ciò di cui abbiamo bisogno; il codice seguente definisce i nostri dati e chiama le funzioni che abbiamo appena realizzato utilizzando Beta(1,1) in precedenza.

traffic = torch.tensor((5523., 1379.))
conversions =torch.tensor((2926., 759.))
inference = run_infernce(model(1,1), number_of_samples = 1000, \
traffic = traffic, number_of_conversions = conversions)

Il primo elemento dei tensori del traffico e delle conversioni sono i conteggi per il gruppo di controllo, mentre il secondo elemento in ciascun tensore sono i conteggi per il gruppo di trattamento. Passiamo la funzione modello, con i parametri per governare la nostra distribuzione a priori, insieme ai tensori che abbiamo definito. L’esecuzione di questo codice genererà i nostri campioni posteriori. Eseguiamo il codice seguente per estrarre i campioni posteriori e passarli a un dataframe Pandas.

posterior_samples = inference.get_samples()
posterior_samples_df = pd.DataFrame(posterior_samples)

Nota che i nomi delle colonne di questo dataframe sono le stringhe che abbiamo passato quando abbiamo definito le nostre primitive nella funzione del modello. Ogni riga nel nostro dataframe contiene campioni estratti dalla distribuzione a posteriori e ciascuno di questi campioni rappresenta una stima del tasso di conversione della pagina, il valore di probabilità p che governa la nostra distribuzione binomiale. Ora che abbiamo restituito i campioni, possiamo tracciare le nostre distribuzioni a posteriori.

Risultati

Un modo intuitivo per visualizzare i risultati del test AB con due gruppi di test è mediante un grafico congiunto della densità dei chicchi. Ci consente di visualizzare la densità dei campioni nello spazio di probabilità su entrambe le distribuzioni. Il grafico seguente può essere prodotto dal dataframe che abbiamo appena costruito.

Credito: autore

Lo spazio di probabilità contenuto nel grafico sopra può essere diviso lungo la sua diagonale, qualsiasi cosa sopra la linea indicherebbe regioni in cui la stima del tasso di conversione è più alta nel gruppo di trattamento rispetto al gruppo di controllo e viceversa. Come illustrato nel grafico, i campioni prelevati dai posteriori sono densamente popolati nella regione, il che indicherebbe che il tasso di conversione è più elevato nel gruppo di trattamento. È importante evidenziare che la distribuzione a posteriori per il gruppo di trattamento è più ampia rispetto al gruppo di controllo, riflettendo un maggiore grado di incertezza. Questo è il risultato dell’osservazione di meno dati nel gruppo di trattamento. Tuttavia, il grafico indica fortemente che il gruppo di trattamento ha sovraperformato il gruppo di controllo. Raccogliendo una serie di campioni dal posteriore e considerando la differenza tra gli elementi, possiamo dire che la probabilità che il gruppo di trattamento abbia prestazioni migliori del gruppo di controllo è del 90,4%. Questa cifra suggerisce che il 90,4% dei campioni prelevati dalla parte posteriore verrà popolato sopra la diagonale nel grafico della densità articolare sopra.

Questi risultati sono stati ottenuti utilizzando un precedente piatto (non informato). L’uso di un preventivo informato può aiutare a migliorare il modello, in particolare quando la disponibilità dei dati osservati è limitata. Un esercizio utile consiste nell’esplorare gli effetti dell’utilizzo di diversi valori a priori. Il grafico seguente mostra la funzione di densità di probabilità Beta(2,2) e il grafico congiunto che produce quando rieseguiamo il modello. Possiamo vedere che l’utilizzo a priori di Beta(2,2) produce una distribuzione posteriore molto simile per entrambi i gruppi di test.

Credito: autore

I campioni estratti dal posteriore suggeriscono che esiste una probabilità del 91,5% che il gruppo di trattamento abbia risultati migliori rispetto al controllo. Pertanto, riteniamo con un grado di certezza più elevato che il gruppo di trattamento sia migliore del gruppo di controllo rispetto all’utilizzo di un gruppo piatto precedente. Tuttavia, in questo esempio la differenza è trascurabile.

C’è un’altra cosa che vorrei sottolineare riguardo a questi risultati. Quando abbiamo eseguito l’inferenza, abbiamo detto a Pyro di generare 1000 campioni dal posteriore. Questo è un numero arbitrario, la selezione di un numero diverso di campioni può modificare i risultati. Per evidenziare l’effetto dell’aumento del numero di campioni, ho eseguito un test AB in cui le osservazioni dei gruppi di controllo e di trattamento erano le stesse, ciascuno con un tasso di conversione complessivo del 50%. L’utilizzo di una Beta(2,2) precedente genera le seguenti distribuzioni posteriori man mano che aumentiamo in modo incrementale il numero di campioni.

Credito: autore

Quando eseguiamo la nostra inferenza con soli 10 campioni, la distribuzione posteriore per i gruppi di controllo e di trattamento è relativamente ampia e adotta forme diverse. All’aumentare del numero di campioni che estraiamo, le distribuzioni convergono, alla fine generando distribuzioni quasi identiche. Inoltre osserviamo due proprietà delle distribuzioni statistiche, il teorema del limite centrale e la legge dei grandi numeri. Il teorema del limite centrale afferma che la distribuzione delle medie campionarie converge verso una distribuzione normale all’aumentare del numero di campioni, e possiamo vederlo nel grafico sopra. Inoltre, la legge dei grandi numeri afferma che all’aumentare della dimensione del campione, la media campionaria converge verso la media della popolazione. Possiamo vedere che la media delle distribuzioni nel riquadro in basso a destra è circa 0,5, il tasso di conversione osservato in ciascuno dei campioni di prova.

Fonte: towardsdatascience.com

Lascia un commento

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