Skip to content

Commit d09e99e

Browse files
committed
brms_rstan: general updates
1 parent b14b081 commit d09e99e

2 files changed

Lines changed: 44 additions & 38 deletions

File tree

content/tutorials/r_brms/brms_eng/workshop_1_mcmc_en_brms_eng.Rmd

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ confint(lm1, level = 0.9)
297297
We source some short functions to calculate the (log) likelihood and the prior and to execute the MCMC metropolis algorithm.
298298

299299
```{r}
300-
source(file = "./source/mcmc_functions.R")
300+
source(file = "./mcmc_functions.R")
301301
```
302302

303303
For this simple model with a small data set, we can calculate and plot the posterior for a large number of combinations of 'beta_0' and 'beta_1'.
@@ -345,7 +345,7 @@ ggplot(df, aes(x = beta_0, y = beta_1, z = post)) +
345345

346346
The starting value is quite far from the maximum likelihood.
347347
It takes a while for the MCMC to stabilize in the vicinity of the top of the mountain.
348-
Therefore, the first part of the MCMC is never used. This is called the 'burn-in' or 'warm-up'.
348+
Therefore, the first part of the MCMC is never used. This is called the 'warmup', 'burn-in' or 'tuning'.
349349

350350
We now run the same model, but with a much longer MCMC (more iterations):
351351

@@ -392,7 +392,7 @@ The MCMC for each parameter can be displayed in a so called 'trace plot' where w
392392
ann_text <- tibble(param = c("beta_0", "beta_1"),
393393
x = 210,
394394
y = c(max(mcmc_l$beta_0), max(mcmc_l$beta_1)),
395-
lab = "burn-in")
395+
lab = "warmup")
396396
mcmc_l %>%
397397
dplyr::select(iter, beta_0, beta_1) %>%
398398
pivot_longer(cols = c("beta_0", "beta_1"), names_to = "param") %>%
@@ -521,9 +521,8 @@ Some alternatives also exist:
521521

522522
![Overview of various Stan software (source: https://jtimonen.github.io/posts/post-01/)](software.png)
523523

524-
# Fitting a model with brms
525524

526-
## Loading the dataset and data exploration
525+
# Loading the dataset and data exploration
527526

528527
We load a dataset on the number of ant species in New England (USA). Type `?ants` into the console for more info.
529528

@@ -593,6 +592,9 @@ ants_df %>%
593592
As an exercise, we will create a model to compare the number of species between both habitats.
594593
From the data exploration, we already saw that the number of species seems to be higher in forests and that sites with a higher number in bogs often also have a higher number in forests.
595594

595+
596+
# Fitting a model with `brms`
597+
596598
## Specification of a linear regression
597599

598600
### Model specification
@@ -617,8 +619,8 @@ First of all we decide which MCMC parameters we will use. Type `?brm` to see wha
617619
```{r simple-model-mcmc-par}
618620
# Set MCMC parameters
619621
nchains <- 3 # number of chains
620-
niter <- 2000 # number of iterations (incl. burn-in, see next)
621-
burnin <- niter / 4 # number of initial samples to remove (= burn-in)
622+
niter <- 2000 # number of iterations (incl. warmup, see next)
623+
warmup <- niter / 4 # number of initial samples to remove (= warmup)
622624
nparallel <- nchains # number of cores for parallel computing
623625
thinning <- 1 # thinning factor (here 1 = no thinning)
624626
```
@@ -630,14 +632,14 @@ The model is fitted using the `brm()` function. The syntax is very similar to fu
630632
- `file` and `file_refit` to save the model object after it has been fitted. If you run the code again and the model has already been saved, `brm()` will simply load this model instead of refitting it.
631633

632634

633-
```{r simple-model-fit-poisson}
635+
```{r simple-model-fit-poisson, class.source = 'fold-show'}
634636
# Fit Normal model
635637
fit_normal1 <- brm(
636638
formula = sp_rich ~ habitat, # specify the model
637639
family = gaussian(), # we use the Normal distribution
638640
data = ants_df, # specify data
639641
chains = nchains, # MCMC parameters
640-
warmup = burnin,
642+
warmup = warmup,
641643
iter = niter,
642644
cores = nparallel,
643645
thin = thinning,
@@ -649,7 +651,7 @@ Before we look at the results, we first check whether the model converges well.
649651

650652
### MCMC convergence
651653

652-
There are several ways to check convergence of the MCMC algorithm for each parameter. The burn-in samples are not taken into account. First and foremost, you have *visual controls*.
654+
There are several ways to check convergence of the MCMC algorithm for each parameter. The warmup samples are not taken into account. First and foremost, you have *visual controls*.
653655

654656
We can obtain the MCMC samples with the `as_draws()` functions or visualise them at once via the [**bayesplot**](https://mc-stan.org/bayesplot/) package that is compatible with brmsfit objects.
655657

@@ -836,14 +838,14 @@ $$
836838

837839
So we need to estimate two parameters: $\beta_0$ and $\beta_1$ We use the same MCMC parameters as before. The only thing we need to adjust is the choice `family = poisson()`.
838840

839-
```{r poisson-model-fit}
841+
```{r poisson-model-fit, class.source = 'fold-show'}
840842
# Fit Poisson model
841843
fit_poisson1 <- brm(
842844
formula = sp_rich ~ habitat, # specify the model
843845
family = poisson(), # we use the Poisson distribution
844846
data = ants_df, # specify the data
845847
chains = nchains, # MCMC parameters
846-
warmup = burnin,
848+
warmup = warmup,
847849
iter = niter,
848850
cores = nparallel,
849851
thin = thinning,
@@ -898,14 +900,14 @@ $$
898900
b_0 \sim N(0, \sigma_b)
899901
$$
900902

901-
```{r rand-intercept-model-fit}
903+
```{r rand-intercept-model-fit, class.source = 'fold-show'}
902904
# Fit Poisson model with random intercept per site
903905
fit_poisson2 <- brm(
904906
formula = sp_rich ~ habitat + (1|site),
905907
family = poisson(),
906908
data = ants_df,
907909
chains = nchains,
908-
warmup = burnin,
910+
warmup = warmup,
909911
iter = niter,
910912
cores = nparallel,
911913
thin = thinning,
@@ -937,11 +939,11 @@ pp_check(fit_poisson2, type = "dens_overlay_grouped", ndraws = 100,
937939

938940
How can we objectively compare these models?
939941

940-
# Compare models
942+
## Compare models
941943

942944
Based on the PPCs we can already see which model fits the data best. Furthermore, there are some functions that **brms** provides to compare different models. With the function `add_criterion()` you can add model fit criteria to model objects. Type `?add_criterion()` to see which ones are available. See also <https://mc-stan.org/loo/articles/online-only/faq.html>
943945

944-
## Leave-one-out cross validation
946+
### Leave-one-out cross validation
945947

946948
Cross-validation (CV) is a family of techniques that attempts to estimate how well a model would predict unknown data through predictions of the model fitted to the known data. You do not necessarily have to collect new data for this. You can split your own data into a test and training dataset. You fit the model to the training dataset and then use that model to estimate how well it can predict the data in the test dataset. With leave-one-out CV (LOOCV) you leave out one observation each time as test dataset and refit the model based on all other observations (= training dataset).
947949

@@ -977,7 +979,7 @@ comp_loo %>%
977979
ul_diff = elpd_diff + qnorm(0.95) * se_diff)
978980
```
979981

980-
## K-fold cross-validation
982+
### K-fold cross-validation
981983

982984
With K-fold cross-validation, the data is split into $K$ groups. We will use $K = 10$ groups (= folds) here. So instead of leaving out a single observation each time, as with leave-one-out CV, we will leave out one $10^{th}$ of the data here. Via the arguments `folds = "stratified"` and `group = "habitat"` we ensure that the relative frequencies of habitat are preserved for each group. This technique will therefore be less precise than the previous one, but will be faster to calculate if you work with a lot of data.
983985

@@ -1023,7 +1025,7 @@ comp_kfold %>%
10231025
ul_diff = elpd_diff + qnorm(0.95) * se_diff)
10241026
```
10251027

1026-
## WAIC
1028+
### WAIC
10271029

10281030
The Widely Applicable Information Criterion (WAIC) does not use cross-validation but is a computational way to estimate the ELPD. How this happens exactly is beyond the purpose of this tutorial. It is yet another measure to apply model selection.
10291031

@@ -1055,10 +1057,11 @@ comp_waic %>%
10551057
ul_diff = elpd_diff + qnorm(0.95) * se_diff)
10561058
```
10571059

1058-
## Conclusion
1060+
### Conclusion
10591061

10601062
Both based on the PPC and the comparisons with different model selection criteria, we can conclude that the second Poisson model with random intercepts fits the data best. In principle, we could have expected this based on our own intuition and the design of the study, i.e. the use of the Poisson distribution to model numbers and the use of random intercepts to control for a hierarchical design (habitats nested within sites).
10611063

1064+
10621065
# Final model results
10631066

10641067
When we look at the model fit object, we see results that are similar to results we see when we fit a frequentist model. On the one hand we get an estimate of all parameters with their uncertainty, but on the other hand we see that this is clearly the output of a Bayesian model. We get information about the parameters we used for the MCMC algorithm, we get a 95% credible interval (CI) instead of a confidence interval and we also get the $\hat{R}$ value for each parameter as discussed earlier.

content/tutorials/r_brms/brms_nl/workshop_1_mcmc_en_brms.Rmd

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ confint(lm1, level = 0.9)
293293
We lezen enkele korte functies in om de (log) likelihood en de prior te berekenen en het MCMC metropolis algoritme uit te voeren.
294294

295295
```{r}
296-
source(file = "./source/mcmc_functions.R")
296+
source(file = "./mcmc_functions.R")
297297
```
298298

299299
Voor dit eenvoudig model met een kleine dataset kunnen we de posterior voor een groot aantal combinaties voor `beta_0` en `beta_1` uitrekenen en plotten.
@@ -340,7 +340,7 @@ ggplot(df, aes(x = beta_0, y = beta_1, z = post)) +
340340
theme(legend.position="none")
341341
```
342342

343-
De startwaarde ligt een eind van de maximale likelihood. Het duurt even voor de MCMC stabiliseert in de omgeving van de top van de berg. Daarom wordt het eerste deel van de MCMC nooit gebruikt. Dit is de 'burn-in' (of warmp-up in brms).
343+
De startwaarde ligt een eind van de maximale likelihood. Het duurt even voor de MCMC stabiliseert in de omgeving van de top van de berg. Daarom wordt het eerste deel van de MCMC nooit gebruikt. Dit is de 'warmup' (engels voor opwarmfase; ook 'burn-in' of 'tuning' in andere bibliotheken).
344344

345345
We runnen nu hetzelde model, maar met een veel langere mcmc
346346

@@ -387,7 +387,7 @@ Voor elke parameter kan de MCMC worden weergegeven in een 'trace plot'.
387387
ann_text <- tibble(param = c("beta_0", "beta_1"),
388388
x = 210,
389389
y = c(max(mcmc_l$beta_0), max(mcmc_l$beta_1)),
390-
lab = "burn-in")
390+
lab = "warmup")
391391
mcmc_l %>%
392392
dplyr::select(iter, beta_0, beta_1) %>%
393393
pivot_longer(cols = c("beta_0", "beta_1"), names_to = "param") %>%
@@ -509,9 +509,8 @@ Er bestaan ook enkele alternatieven:
509509
![Overzicht van verschillende Stan software (bron: https://jtimonen.github.io/posts/post-01/)](software.png)
510510

511511

512-
# Een model fitten met brms
513512

514-
## Dataset laden en data exploratie
513+
# Dataset laden en data exploratie
515514

516515
We laden een dataset in over het aantal mierensoorten in New England (USA). Typ `?ants` in de console voor meer info.
517516

@@ -583,6 +582,9 @@ ants_df %>%
583582

584583
Als oefening zullen we een model maken om het aantal soorten te vergelijken tussen beide habitats. Uit de data exploratie zagen we al dat het aantal hoger lijkt te liggen in bossen en dat sites met een hoger aantal in moerassen vaak ook een hoger aantal in bossen hebben.
585584

585+
586+
# Een model fitten met `brms`
587+
586588
## Specificatie van een lineaire regressie
587589

588590
### Model specificatie
@@ -607,8 +609,8 @@ Eerst en vooral besluiten we welke MCMC parameters we zullen gebruiken. Typ `?br
607609
```{r simpel-model-mcmc-par}
608610
# Instellen MCMC parameters
609611
nchains <- 3 # aantal chains
610-
niter <- 2000 # aantal iteraties (incl. burn-in, zie volgende)
611-
burnin <- niter / 4 # aantal initiële samples om te verwijderen (= burn-in)
612+
niter <- 2000 # aantal iteraties (incl. warmup, zie volgende)
613+
warmup <- niter / 4 # aantal initiële samples om te verwijderen (= warmup)
612614
nparallel <- nchains # aantal cores voor parallel computing
613615
thinning <- 1 # verdunningsfactor (hier 1 = geen verdunning)
614616
```
@@ -620,14 +622,14 @@ Het model wordt gefit a.d.h.v. de `brm()` functie. De syntax is zeer gelijkaardi
620622
- `file` en `file_refit` om het model object op te slaan nadat het gefit is. Als je de code opnieuw runt en het model is al eens opgeslaan, dan zal `brm()` dit model gewoon inladen in plaats van het opnieuw te fitten.
621623

622624

623-
```{r simpel-model-fit-poisson}
625+
```{r simpel-model-fit-poisson, class.source = 'fold-show'}
624626
# Fit Normaal model
625627
fit_normal1 <- brm(
626628
formula = sp_rich ~ habitat, # beschrijving van het model
627629
family = gaussian(), # we gebruiken de Normaal verdeling
628630
data = ants_df, # ingeven data
629631
chains = nchains, # MCMC parameters
630-
warmup = burnin,
632+
warmup = warmup,
631633
iter = niter,
632634
cores = nparallel,
633635
thin = thinning,
@@ -638,7 +640,7 @@ Voor we de resultaten bekijken, controleren we eerst of het model goed convergee
638640

639641
### MCMC convergentie
640642

641-
Er zijn verschillende manieren om convergentie van het MCMC algoritme voor elke parameter te controleren. Hierbij worden de burn-in samples niet in rekening genomen. Eerst en vooral heb je *visuele controles*.
643+
Er zijn verschillende manieren om convergentie van het MCMC algoritme voor elke parameter te controleren. Hierbij worden de warmup samples niet in rekening genomen. Eerst en vooral heb je *visuele controles*.
642644

643645
We kunnen de MCMC samples met de `as_draws()` functies verkrijgen ofwel ineens visualiseren via de [bayesplot](https://mc-stan.org/bayesplot/) package die compatibel is met brmsfit objecten.
644646

@@ -825,14 +827,14 @@ $$
825827

826828
We moeten dus twee parameters schatten: $\beta_0$ en $\beta_1$ We gebruiken dezelfde MCMC parameters als voordien. Het enige wat we moeten aanpassen is de keuze `family = poisson()`.
827829

828-
```{r poisson-model-fit}
830+
```{r poisson-model-fit, class.source = 'fold-show'}
829831
# Fit Poisson model
830832
fit_poisson1 <- brm(
831833
formula = sp_rich ~ habitat, # beschrijving van het model
832834
family = poisson(), # we gebruiken de Poisson verdeling
833835
data = ants_df, # ingeven data
834836
chains = nchains, # MCMC parameters
835-
warmup = burnin,
837+
warmup = warmup,
836838
iter = niter,
837839
cores = nparallel,
838840
thin = thinning,
@@ -888,14 +890,14 @@ $$
888890
b_0 \sim N(0, \sigma_b)
889891
$$
890892

891-
```{r rand-intercept-model-fit}
893+
```{r rand-intercept-model-fit, class.source = 'fold-show'}
892894
# Fit Poisson model met random intercepten per site
893895
fit_poisson2 <- brm(
894896
formula = sp_rich ~ habitat + (1|site),
895897
family = poisson(),
896898
data = ants_df,
897899
chains = nchains,
898-
warmup = burnin,
900+
warmup = warmup,
899901
iter = niter,
900902
cores = nparallel,
901903
thin = thinning,
@@ -929,11 +931,11 @@ Hoe kunnen we deze modellen nu objectief gaan vergelijken?
929931

930932

931933

932-
# Vergelijken van modellen
934+
## Vergelijken van modellen
933935

934936
Op basis van de PPCs kunnen we reeds zien welk model het best past bij de data. Verder zijn er nog enkele functies die **brms** voorziet om verschillende modellen te vergelijken. Met de functie `add_criterion()` kan je model fit criteria toevoegen aan model objecten. Typ `?add_criterion()` om te zien welke beschikbaar zijn. Zie ook <https://mc-stan.org/loo/articles/online-only/faq.html>
935937

936-
## Leave-one-out cross-validation
938+
### Leave-one-out cross-validation
937939

938940
Cross-validation (CV) is een familie van technieken die probeert in te schatten hoe goed een model onbekende data zou voorspellen via predicties van het model gefit op de bekende data. Hiervoor moet je niet per se nieuwe data gaan inzamelen. Je kan jouw eigen data opsplitsen in een test en training dataset. Je fit het model op de training dataset en je gebruikt dat model dan om te schatten hoe goed het de data in de test dataset kan voorspellen. Bij leave-one-out CV (LOOCV) ga je telkens één observatie weglaten en het model opnieuw fitten o.b.v. alle andere observaties.
939941

@@ -969,7 +971,7 @@ comp_loo %>%
969971
ul_diff = elpd_diff + qnorm(0.95) * se_diff)
970972
```
971973

972-
## K-fold cross-validation
974+
### K-fold cross-validation
973975

974976
Bij K-fold cross-validation worden de data in $K$ groepen opgesplitst. Wij zullen hier $K = 10$ groepen (= folds) gebruiken. In plaats van dus telkens één enkele observatie weg te laten zoals bij leave-one-out CV gaan we hier $1/10$e van de data weglaten. Via de argumenten `folds = "stratified"` en `group = "habitat"` zorgen we ervoor dat voor elke groep de relatieve frequenties van habitat bewaard blijven. Deze techniek zal dus minder precies zijn dan de vorige, maar zal sneller zijn om te berekenen indien je met heel veel data werkt.
975977

@@ -1014,7 +1016,7 @@ comp_kfold %>%
10141016
ul_diff = elpd_diff + qnorm(0.95) * se_diff)
10151017
```
10161018

1017-
## WAIC
1019+
### WAIC
10181020

10191021
Het Widely Applicable Information Criterion (WAIC) maakt geen gebruik van cross-validation maar is een computationele manier om de ELPD te schatten. Hoe dit precies gebeurt valt buiten het doel van deze workshop. Het is nog een andere maat om model selectie toe te passen.
10201022

@@ -1046,10 +1048,11 @@ comp_waic %>%
10461048
ul_diff = elpd_diff + qnorm(0.95) * se_diff)
10471049
```
10481050

1049-
## Conclusie
1051+
### Conclusie
10501052

10511053
Zowel o.b.v. de PPC als vergelijkingen met verschillende model selectie criteria, kunnen we besluiten dat het tweede Poisson model met random intercepts het best past bij de data. In principe konden we dit ook verwachten op basis van onze eigen intuïtie en het design van de studie, nl. het gebruik van de Poisson distributie om aantallen te modelleren en het gebruik van random intercepts om te controleren voor een hiërarchisch design (habitats genest in sites).
10521054

1055+
10531056
# Resultaten finale model
10541057

10551058
Als we het model fit object bekijken, zien we resultaten die vergelijkbaar zijn met resultaten zoals we die zien als we een frequentist model gefit hebben. We krijgen enerzijds een schatting van alle parameters met hun onzekerheid maar anderzijds zien we dat dit duidelijk de output is van een Bayesiaans model. Zo krijgen we info over de parameters die we gebruikt hebben voor het MCMC algoritme, we krijgen een 95 % credible interval (CI) in plaats van een confidence interval en we krijgen bij elke parameter ook de $\hat{R}$ waarde die we eerder besproken hebben.

0 commit comments

Comments
 (0)