Linguaggio Stan#

Interfacce e pacchetti#

È possibile accedere al linguaggio Stan tramite diverse interfacce:

  • CmdStanPy - integrazione con il linguaggio di programmazione Python;

  • PyStan - integrazione con il linguaggio di programmazione Python;

  • CmdStan - eseguibile da riga di comando,

  • RStan - integrazione con il linguaggio \(\mathsf{R}\);

  • MatlabStan - integrazione con MATLAB;

  • Stan.jl - integrazione con il linguaggio di programmazione Julia;

  • StataStan - integrazione con Stata.

  • ScalaStan - integrazione con Scala.

Inoltre, vengono fornite interfacce di livello superiore con i pacchetti che utilizzano Stan come backend, sia in Python che in Linguaggio \(\mathsf{R}\):

  • Arviz - ArviZ è una libreria Python per l’analisi esplorativa dei modelli bayesiani. Essa funge da strumento indipendente dal backend per diagnosticare e visualizzare l’inferenza bayesiana.

  • shinystan - interfaccia grafica interattiva per l’analisi della distribuzione a posteriori e le diagnostiche MCMC in \(\mathsf{R}\);

  • bayesplot - insieme di funzioni utilizzabili per creare grafici relativi all’analisi della distribuzione a posteriori, ai test del modello e alle diagnostiche MCMC in \(\mathsf{R}\);

  • brms - fornisce un’ampia gamma di modelli lineari e non lineari specificando i modelli statistici mediante la sintassi usata in \(\mathsf{R}\);

  • rstanarm - fornisce un sostituto per i modelli frequentisti forniti da base \(\mathsf{R}\) e lme4 utilizzando la sintassi usata in \(\mathsf{R}\) per la specificazione dei modelli statistici;

  • cmdstanr - un’interfaccia \(\mathsf{R}\) per CmdStan.

Interfaccia cmdstanpy#

Negli esempi di questa dispensa verrà utilizzata l’interfaccia cmdstanpy. CmdStanPy è una interfaccia di Stan per gli utenti Python, che fornisce gli oggetti e le funzioni necessarie per condurre l’inferenza bayesiana su un modello di probabilità e dei dati. Essa racchiude l’interfaccia a riga di comando di CmdStan in un piccolo insieme di classi Python, le quali offrono metodi per analizzare e gestire l’insieme risultante di modello, dati e stime a posteriori.

Per l’installazione di CmdStanPy, si segua il link.

Codice Stan#

Stan consente agli utenti di definire un modello bayesiano attraverso il linguaggio Stan. Questo modello, di solito, viene salvato in un file di testo con estensione .stan.

Il codice Stan deve poi essere compilato. Il processo di compilazione di un modello in Stan avviene in due fasi: innanzitutto, Stan traduce il modello dal formato .stan in codice C++, il quale viene successivamente compilato in codice macchina.

Dopo la compilazione del modello (ovvero, dopo che il codice macchina è stato generato), l’utente può utilizzare l’interfaccia prescelta (per esempio, CmdStan) per campionare la distribuzione definita dal modello e per eseguire altri calcoli correlati al modello stesso.

Il codice Stan è costituito da una serie di blocchi che vengono usati per specificare un modello statistico. In ordine, questi blocchi sono: data, transformed data, parameters, transformed parameters, model, e generated quantities.

Blocco data#

Nel blocco data vengono specificate le variabili di input che saranno utilizzate nel modello Stan. Per ciascuna variabile, è necessario definire il tipo di dato e le dimensioni, oltre a eventuali vincoli sui valori che le variabili possono assumere.

Per esempio

data {
  int<lower=0> ntrials; // Numero di prove
  int<lower=0> y; // Successi osservati
  real<lower=0> alpha_prior; // Parametro alpha per il prior Beta
  real<lower=0> beta_prior; // Parametro beta per il prior Beta
}

Ecco una sintesi dei tipi di dati e delle specifiche che possono essere dichiarate:

  • int: rappresenta numeri interi.

  • real: designa numeri reali, inclusi quelli con parte decimale.

  • vector: si riferisce a un vettore unidimensionale di numeri reali.

  • matrix: indica una matrice bidimensionale di numeri reali.

  • array: descrive una sequenza ordinata di elementi che possono essere di qualsiasi tipo specificato, e può avere più di una dimensione.

È importante dichiarare le dimensioni di ciascuna variabile e, se necessario, applicare vincoli sui valori che queste possono assumere (ad esempio, specificando lower=0 e upper=1 per vincolare i valori tra 0 e 1). Questi vincoli sono utili per ottimizzare la stima dei parametri e garantire che il modello sia ben definito.

Interi#

Gli interi non vincolati vengono dichiarati utilizzando la parola chiave int. Ad esempio, la variabile N viene dichiarata come un intero nel seguente modo.

int N;

I tipi di dati interi possono essere vincolati per consentire valori solo in un intervallo specificato fornendo un limite inferiore, un limite superiore o entrambi. Ad esempio, per dichiarare N come un intero positivo, si utilizza quanto segue.

int<lower=1> N;

Reali#

Le variabili reali non vincolate vengono dichiarate utilizzando la parola chiave real. Ad esempio,

real theta;

Le variabili reali possono essere limitate utilizzando la stessa sintassi degli interi. Per esempio, la variabile sigma può essere dichiarata come non negativa come segue.

real<lower=0> sigma;

Tipi di dati vettoriali e matriciali#

Stan fornisce tre tipi di oggetti contenitore: array, vettori e matrici. I vettori e le matrici sono tipi di strutture dati più limitati rispetto agli array. I vettori sono collezioni intrinsecamente unidimensionali di valori reali o complessi, mentre le matrici sono intrinsecamente bidimensionali. I vettori, le matrici e gli array non sono assegnabili tra loro, anche se le loro dimensioni sono identiche. Una matrice 3×4 è un tipo di oggetto diverso in Stan rispetto a un array 3×4.

I vettori e le matrici non possono essere tipizzati per restituire valori interi. Sono limitati a valori reali e complessi.

Indicizzazione da 1#

Vettori e matrici, così come gli array, sono indicizzati a partire da uno (1) in Stan.

Tipi di dati array#

Stan supporta array di dimensioni arbitrarie. I valori in un array possono essere di qualsiasi tipo, in modo che gli array possano contenere valori che sono semplici reali o interi, vettori, matrici o altri array. Gli array sono l’unico modo per memorizzare sequenze di interi, e alcune funzioni in Stan, come le distribuzioni discrete, richiedono argomenti interi.

Un array bidimensionale è semplicemente un array di array. Quando viene fornito un indice a un array, restituisce il valore in quell’indice. Quando vengono forniti più di un indice, questa operazione di indicizzazione è concatenata. Ad esempio, se a è un array bidimensionale, allora a[m, n] è solo una abbreviazione per a[m][n].

Dichiarazione di variabili array#

Gli array sono dichiarati con la parola chiave array seguita dalle dimensioni racchiuse tra parentesi quadre, il tipo di elemento e il nome della variabile.

Per esempio, la variabile n viene dichiarata come un array di cinque interi come segue.

array[5] int n;

Un array bidimensionale di valori interi con tre righe e quattro colonne viene dichiarato come segue.

array[3, 4] int a;

Un array di N numeri reali vincolati tra 0 e 1 viene dichiarato come segue.

int<lower=0> N;
array[N] real<lower=0, upper=1> y;

Un array di N interi positivi viene dichiarato come segue.

int<lower=0> N;
array[N] int<lower=0> x;

Vettori#

I vettori in Stan sono vettori colonna. I vettori sono dichiarati con una dimensione (cioè, una dimensionalità). Ad esempio, un vettore reale tridimensionale di dimensione 3 viene dichiarato con la parola chiave vector, come segue.

vector[3] u;
Matrici#

Le matrici sono dichiarate con la parola chiave matrix insieme a un numero di righe e un numero di colonne. Ad esempio,

matrix[3, 3] A;
matrix[M, N] B;

dichiara A come una matrice 3×3 e B come una matrice M×N. Perché la seconda dichiarazione sia ben formata, le variabili M e N devono essere dichiarate come interi nel blocco dati o dati trasformati e prima della dichiarazione della matrice.

Miscelazione di tipi di array, vettore e matrice#

Array, vettori riga, vettori colonna e matrici non sono interscambiabili in Stan. Quindi una variabile di uno qualsiasi di questi tipi fondamentali non è assegnabile a nessuno degli altri, anche se le loro dimensioni sono identiche, né può essere utilizzata come argomento dove è richiesto l’altro.

Dizionari#

È fondamentale che i dati forniti a Stan tramite CmdStanPy siano organizzati in un oggetto di tipo dizionario. In Python, un dizionario è una collezione di coppie chiave-valore che permette di associare a ogni chiave (unica) un valore specifico. Quando si preparano i dati per un modello Stan utilizzando CmdStanPy, si crea un dizionario dove:

  • Ogni chiave corrisponde al nome di una variabile dichiarata nel blocco data del modello Stan.

  • Il valore associato a ciascuna chiave rappresenta i dati effettivi da passare al modello per quella variabile.

Questa struttura consente di mappare direttamente le variabili definite nel modello Stan ai dati che si desidera analizzare, facilitando il processo di assegnazione dei dati e assicurando che ogni variabile riceva i dati corretti.

Blocco parameters#

I parametri da stimare sono definiti all’interno del blocco parameters.

Ad esempio, consideriamo il seguente codice, dove viene dichiarata la variabile theta per rappresentare una probabilità. Si notino i vincoli che specificano che i valori possibili per theta devono essere contenuti nell’intervallo [0, 1].

parameters {
  real<lower=0, upper=1> theta; // Parametro stimato, limitato tra 0 e 1
}

Certo, ecco il testo corretto e migliorato:

Sezione model#

Nella sezione model, vengono definite le relazioni tra i dati osservati e i parametri del modello, insieme alle distribuzioni a priori di tali parametri.

A titolo di esempio, il seguente codice assegna una distribuzione a priori Beta ai parametri alpha_prior e beta_prior per il parametro theta. La verosimiglianza specifica che il meccanismo generatore dei dati osservati y è binomiale, con parametri ntrials e theta.

model {
  // Prior
  theta ~ beta(alpha_prior, beta_prior);
  
  // Likelihood
  y ~ binomial(ntrials, theta);
}

Il simbolo ~ è chiamato tilde. In generale, possiamo leggerlo come «è distribuito come», e questa notazione viene utilizzata come abbreviazione per definire distribuzioni. Pertanto, l’esempio sopra può essere scritto anche come:

\[ p(\theta \mid \alpha_p, \beta_p) = \text{Beta}(\alpha_p, \beta_p) \]

e

\[ p(y \mid \theta) = \text{Binomiale}(y \mid n, \theta) \]

Questa notazione compatta facilita la definizione delle relazioni probabilistiche nel modello.

Se non specificata, Stan utilizza una distribuzione a priori uniforme tra meno infinito e più infinito. Per ulteriori raccomandazioni sulle scelte delle distribuzioni a priori, è possibile consultare questo link.

Blocchi opzionali#

Ci sono inoltre tre blocchi opzionali:

  • Il blocco transformed data consente il pre-processing dei dati. È possibile trasformare i parametri del modello; solitamente ciò viene fatto nel caso dei modelli più avanzati per consentire un campionamento MCMC più efficiente.

  • Il blocco transformed parameters consente la manipolazione dei parametri prima del calcolo della distribuzione a posteriori.

  • Il blocco generated quantities consente il post-processing riguardante qualsiasi quantità che non fa parte del modello ma può essere calcolata a partire dai parametri del modello, per ogni iterazione dell’algoritmo. Esempi includono la generazione dei campioni a posteriori e le dimensioni degli effetti.

Sintassi#

Si noti che il codice Stan richiede i punti e virgola (;) alla fine di ogni istruzione di assegnazione. Questo accade per le dichiarazioni dei dati, per le dichiarazioni dei parametri e ovunque si acceda ad un elemento di un tipo data e lo si assegni a qualcos’altro. I punti e virgola non sono invece richiesti all’inizio di un ciclo o di un’istruzione condizionale, dove non viene assegnato nulla.

In STAN, qualsiasi stringa che segue // denota un commento e viene ignorata dal programma.

Una descrizione dettagliata della sintassi del linguaggio Stan è disponibile al seguente link.