Title: | Checkpoint MCMC Sampling with 'Stan' |
---|---|
Description: | Fit Bayesian models in Stan <doi: 10.18637/jss.v076.i01> with checkpointing, that is, the ability to stop the MCMC sampler at will, and then pick right back up where the MCMC sampler left off. Custom 'Stan' models can be fitted, or the popular package 'brms' <doi: 10.18637/jss.v080.i01> can be used to generate the 'Stan' code. This package is fully compatible with the R packages 'brms', 'posterior', 'cmdstanr', and 'bayesplot'. |
Authors: | Donald Williams [aut, cre], Tyler Matta [aut], NWEA [cph] |
Maintainer: | Donald Williams <[email protected]> |
License: | Apache License 2.0 | file LICENSE |
Version: | 0.1.1 |
Built: | 2024-11-20 06:56:03 UTC |
Source: | CRAN |
Fit Bayesian models in Stan (Carpenter et al. 2017)
with checkpointing, that is, the ability to stop the MCMC sampler at will,
and then pick right back up where the MCMC sampler left off. Custom Stan models
can be fitted, or the popular package brms (Bürkner 2017)
can be used to generate the Stan code. This package is fully compatible with the
R
packages brms,
posterior,
cmdstanr, and
bayesplot.
There are a variety of use cases for chkptstanr, including (but not limited to) the following:
The primary motivation for developing chkptstanr is to reduce the cost of fitting models with Stan when using, say, AWS, and in particular by taking advantage of so-called spot instances. These instances are "a cost-effective choice if you can be flexible about when your applications run and if your applications can be interrupted [emphasis added]" (AWS website).
chkptstanr thus allows for taking advantage of spot instances by enabling "interruptions" during model fitting. This can reduce the cost by 90 %.
Stan allows for fitting complex models. This often entails iteratively improving the model to ensure that the MCMC algorithm has converged. Typically this requires waiting until the model has finished sampling, and then assessing MCMC diagnostics (e.g., R-hat).
chkptstanr can be used to make iterative model building more efficient, e.g., by having the ability to pause sampling and examine the model (e.g., convergence diagnostics), and then deciding to stop sampling or to continue on.
Computationally intensive models can sometimes take several days to finish up. When using a personal computer, this can take up all the computing resources.
chkptstanr can be used with scheduling, such that the model is fitted during certain windows (e.g., at night, weekends, etc.)
Those familiar with Bayesian methods will know all too well that a model can take longer than expected. This can be problematic when there is another task that needs to be completed, because one is faced with waiting it out or stopping the model (and loosing all of the progress).
chkptstanr makes it so that models can be conveniently stopped if need be, while not loosing any of the progress.
Bürkner P (2017).
“brms: An R package for Bayesian multilevel models using Stan.”
Journal of statistical software, 80, 1–28.
Carpenter B, Gelman A, Hoffman MD, Lee D, Goodrich B, Betancourt M, Brubaker M, Guo J, Li P, Riddell A (2017).
“Stan: A probabilistic programming language.”
Journal of statistical software, 76(1).
Fit Bayesian generalized (non-)linear multivariate multilevel models using brms with checkpointing.
chkpt_brms( formula, data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 100, iter_typical = 150, parallel_chains = 2, threads_per = 1, chkpt_progress = TRUE, control = NULL, brmsfit = TRUE, seed = 1, path, ... )
chkpt_brms( formula, data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 100, iter_typical = 150, parallel_chains = 2, threads_per = 1, chkpt_progress = TRUE, control = NULL, brmsfit = TRUE, seed = 1, path, ... )
formula |
An object of class |
data |
An object of class |
iter_warmup |
(positive integer) The number of warmup iterations to run per chain (defaults to 1000). |
iter_sampling |
(positive integer) The number of post-warmup iterations to run per chain (defaults to 1000). |
iter_per_chkpt |
(positive integer). The number of iterations per
checkpoint. Note that |
iter_typical |
(positive integer) The number of iterations in the
initial warmup, which finds the so-called typical set.
This is an initial phase, and not included in
|
parallel_chains |
(positive integer) The maximum number of MCMC
chains to run in parallel. If parallel_chains is not
specified then the default is to look for the option
|
threads_per |
(positive integer) Number of threads to use in within-chain
parallelization (defaults to |
chkpt_progress |
logical. Should the |
control |
A named list of parameters to control the sampler's behavior.
It defaults to NULL so all the default values are used.
For a comprehensive overview see |
brmsfit |
Logical. Should a |
seed |
(positive integer). The seed for random number generation to make results reproducible. |
path |
Character string. The path to the folder, that is used for saving the checkpoints. |
... |
Additional arguments based to |
An object of class brmsfit
(with brmsfit = TRUE
)
or chkpt_brms
(with brmsfit = FALSE
)
## Not run: library(brms) library(cmdstanr) # path for storing checkpoint info path <- create_folder(folder_name = "chkpt_folder_fit1") # "random" intercept fit1 <- chkpt_brms(bf(formula = count ~ zAge + zBase * Trt + (1|patient), family = poisson()), data = epilepsy, , iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) # brmsfit output fit1 # path for storing checkpoint info path <- create_folder(folder_name = "chkpt_folder_fit2") # remove "random" intercept (for model comparison) fit2 <- chkpt_brms(bf(formula = count ~ zAge + zBase * Trt, family = poisson()), data = epilepsy, , iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) # brmsfit output fit2 # compare models loo(fit1, fit2) # using custom priors path <- create_folder(folder_name = "chkpt_folder_fit3") # priors bprior <- prior(constant(1), class = "b") + prior(constant(2), class = "b", coef = "zBase") + prior(constant(0.5), class = "sd") # fit model fit3 <- chkpt_brms( bf( formula = count ~ zAge + zBase + (1 | patient), family = poisson() ), data = epilepsy, path = path, prior = bprior, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, ) # check priors prior_summary(fit3) ## End(Not run)
## Not run: library(brms) library(cmdstanr) # path for storing checkpoint info path <- create_folder(folder_name = "chkpt_folder_fit1") # "random" intercept fit1 <- chkpt_brms(bf(formula = count ~ zAge + zBase * Trt + (1|patient), family = poisson()), data = epilepsy, , iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) # brmsfit output fit1 # path for storing checkpoint info path <- create_folder(folder_name = "chkpt_folder_fit2") # remove "random" intercept (for model comparison) fit2 <- chkpt_brms(bf(formula = count ~ zAge + zBase * Trt, family = poisson()), data = epilepsy, , iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) # brmsfit output fit2 # compare models loo(fit1, fit2) # using custom priors path <- create_folder(folder_name = "chkpt_folder_fit3") # priors bprior <- prior(constant(1), class = "b") + prior(constant(2), class = "b", coef = "zBase") + prior(constant(0.5), class = "sd") # fit model fit3 <- chkpt_brms( bf( formula = count ~ zAge + zBase + (1 | patient), family = poisson() ), data = epilepsy, path = path, prior = bprior, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, ) # check priors prior_summary(fit3) ## End(Not run)
Deterimine the number of checkpoints for the warmup and sampling, given the desired number of iterations for each and the iterations per checkpoint.
chkpt_setup(iter_sampling, iter_warmup, iter_per_chkpt)
chkpt_setup(iter_sampling, iter_warmup, iter_per_chkpt)
iter_sampling |
(positive integer) The number of post-warmup iterations to run per chain. Note: in the CmdStan User's Guide this is referred to as num_samples. |
iter_warmup |
(positive integer) The number of warmup iterations to run per chain. Note: in the CmdStan User's Guide this is referred to as num_warmup. |
iter_per_chkpt |
(positive integer) The number of iterations per check point. |
A list with the following:
warmup_chkpts
: Number of warmup checkpoints
sample_chkpts
: Number of sampling checkpoints
total_chkpts
: Total number of checkpoints
(warmup_chkpts + sample_chkpts)
iter_per_chkpt
: Iterations per checkpoint
chkpt_setup <- chkpt_setup( iter_sampling = 5000, iter_warmup = 2000, iter_per_chkpt = 10 ) chkpt_setup
chkpt_setup <- chkpt_setup( iter_sampling = 5000, iter_warmup = 2000, iter_per_chkpt = 10 ) chkpt_setup
Fit Bayesian models using Stan with checkpointing.
chkpt_stan( model_code, data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 100, iter_typical = 150, parallel_chains = 2, threads_per = 1, chkpt_progress = TRUE, control = NULL, seed = 1, path, ... )
chkpt_stan( model_code, data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 100, iter_typical = 150, parallel_chains = 2, threads_per = 1, chkpt_progress = TRUE, control = NULL, seed = 1, path, ... )
model_code |
Character string corresponding to the Stan model. |
data |
A named list of R objects (like for RStan).
Further details can be found in |
iter_warmup |
(positive integer) The number of warmup iterations to run per chain (defaults to 1000). |
iter_sampling |
(positive integer) The number of post-warmup iterations to run per chain (defaults to 1000). |
iter_per_chkpt |
(positive integer). The number of iterations per
checkpoint. Note that |
iter_typical |
(positive integer) The number of iterations in the
initial warmup, which finds the so-called typical set.
This is an initial phase, and not included in
|
parallel_chains |
(positive integer) The maximum number of MCMC
chains to run in parallel. If parallel_chains is not
specified then the default is to look for the option
|
threads_per |
(positive integer) Number of threads to use in within-chain
parallelization (defaults to |
chkpt_progress |
logical. Should the |
control |
A named list of parameters to control the sampler's behavior.
It defaults to NULL so all the default values are used.
For a comprehensive overview see |
seed |
(positive integer). The seed for random number generation to make results reproducible. |
path |
Character string. The path to the folder, that is used for saving the checkpoints. |
... |
Currently ignored. |
An objet of class chkpt_stan
## Not run: # path for storing checkpoint info path <- create_folder(folder_name = "chkpt_folder_fit1") stan_code <- make_stancode(bf(formula = count ~ zAge + zBase * Trt + (1|patient), family = poisson()), data = epilepsy) stan_data <- make_standata(bf(formula = count ~ zAge + zBase * Trt + (1|patient), family = poisson()), data = epilepsy) # "random" intercept fit1 <- chkpt_stan(model_code = stan_code, data = stan_data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) draws <- combine_chkpt_draws(object = fit1) posterior::summarise_draws(draws) # eight schools example # path for storing checkpoint info path <- create_folder(parent_folder = "chkpt_folder_fit2") stan_code <- " data { int<lower=0> n; real y[n]; real<lower=0> sigma[n]; } parameters { real mu; real<lower=0> tau; vector[n] eta; } transformed parameters { vector[n] theta; theta = mu + tau * eta; } model { target += normal_lpdf(eta | 0, 1); target += normal_lpdf(y | theta, sigma); } " stan_data <- schools.data <- list( n = 8, y = c(28, 8, -3, 7, -1, 1, 18, 12), sigma = c(15, 10, 16, 11, 9, 11, 10, 18) ) fit2 <- chkpt_stan(model_code = stan_code, data = stan_data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) draws <- combine_chkpt_draws(object = fit2) posterior::summarise_draws(draws) ## End(Not run)
## Not run: # path for storing checkpoint info path <- create_folder(folder_name = "chkpt_folder_fit1") stan_code <- make_stancode(bf(formula = count ~ zAge + zBase * Trt + (1|patient), family = poisson()), data = epilepsy) stan_data <- make_standata(bf(formula = count ~ zAge + zBase * Trt + (1|patient), family = poisson()), data = epilepsy) # "random" intercept fit1 <- chkpt_stan(model_code = stan_code, data = stan_data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) draws <- combine_chkpt_draws(object = fit1) posterior::summarise_draws(draws) # eight schools example # path for storing checkpoint info path <- create_folder(parent_folder = "chkpt_folder_fit2") stan_code <- " data { int<lower=0> n; real y[n]; real<lower=0> sigma[n]; } parameters { real mu; real<lower=0> tau; vector[n] eta; } transformed parameters { vector[n] theta; theta = mu + tau * eta; } model { target += normal_lpdf(eta | 0, 1); target += normal_lpdf(y | theta, sigma); } " stan_data <- schools.data <- list( n = 8, y = c(28, 8, -3, 7, -1, 1, 18, 12), sigma = c(15, 10, 16, 11, 9, 11, 10, 18) ) fit2 <- chkpt_stan(model_code = stan_code, data = stan_data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) draws <- combine_chkpt_draws(object = fit2) posterior::summarise_draws(draws) ## End(Not run)
Combine Checkpoint Draws
combine_chkpt_draws(object, ...)
combine_chkpt_draws(object, ...)
object |
An object of class |
... |
Currently ignored. |
An object of class draws_array
.
## Not run: path <- create_folder(folder_name = "chkpt_folder_fit1") stan_code <- " data { int<lower=0> n; real y[n]; real<lower=0> sigma[n]; } parameters { real mu; real<lower=0> tau; vector[n] eta; } transformed parameters { vector[n] theta; theta = mu + tau * eta; } model { target += normal_lpdf(eta | 0, 1); target += normal_lpdf(y | theta, sigma); } " stan_data <- schools.data <- list( n = 8, y = c(28, 8, -3, 7, -1, 1, 18, 12), sigma = c(15, 10, 16, 11, 9, 11, 10, 18) ) fit2 <- chkpt_stan(model_code = stan_code, data = stan_data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) draws <- combine_chkpt_draws(object = fit2) draws ## End(Not run)
## Not run: path <- create_folder(folder_name = "chkpt_folder_fit1") stan_code <- " data { int<lower=0> n; real y[n]; real<lower=0> sigma[n]; } parameters { real mu; real<lower=0> tau; vector[n] eta; } transformed parameters { vector[n] theta; theta = mu + tau * eta; } model { target += normal_lpdf(eta | 0, 1); target += normal_lpdf(y | theta, sigma); } " stan_data <- schools.data <- list( n = 8, y = c(28, 8, -3, 7, -1, 1, 18, 12), sigma = c(15, 10, 16, 11, 9, 11, 10, 18) ) fit2 <- chkpt_stan(model_code = stan_code, data = stan_data, iter_warmup = 1000, iter_sampling = 1000, iter_per_chkpt = 250, path = path) draws <- combine_chkpt_draws(object = fit2) draws ## End(Not run)
Create the folder for checkingpointing, which
will "house" additional folders for the .stan
model, checkpointing information, and draws from the
posterior distribution.
create_folder(folder_name = "cp_folder", path = NULL)
create_folder(folder_name = "cp_folder", path = NULL)
folder_name |
Character string. Desired name for the "parent"
folder (defaults to |
path |
Character string, when specified. Defaults to |
No return value, and instead creates a directory with folders that will contain the checkpointing samples and other information.
This creates a directory with four folders:
cmd_fit: The cmdstanr fittted models (one for each checkpoint).
cp_info: Mass matrix, step size, and initial values for next checkpoint (last iteration from previous checkpoint).
cp_samples: Samples from the posterior distribution (post warmup)
stan_model: Complied Stan model
path <- create_folder(folder_name = "cp_folder") # remove folder unlink("cp_folder", recursive = TRUE)
path <- create_folder(folder_name = "cp_folder") # remove folder unlink("cp_folder", recursive = TRUE)
CmdStanMCMC
ObjectsA convenience function for extracting the draws from a
CmdStanMCMC
object.
extract_chkpt_draws(object, phase)
extract_chkpt_draws(object, phase)
object |
An object of class |
phase |
Character string. Which phase during checkpointing?
The options included |
A 3-D draws_array
object
(iteration x chain x variable).
This can be used to extract the draws in general by setting
phase = "sample"
which then only includes the post-warmup
draws.
## Not run: library(cmdstanr) # eight schools example fit_schools_ncp_mcmc <- cmdstanr_example("schools_ncp") drws <- extract_chkpt_draws(object = fit_schools_ncp_mcmc, phase = "sample") # compare to cmdstanr all.equal(drws, fit_schools_ncp_mcmc$draws()) ## End(Not run)
## Not run: library(cmdstanr) # eight schools example fit_schools_ncp_mcmc <- cmdstanr_example("schools_ncp") drws <- extract_chkpt_draws(object = fit_schools_ncp_mcmc, phase = "sample") # compare to cmdstanr all.equal(drws, fit_schools_ncp_mcmc$draws()) ## End(Not run)
Extract the inverse metric and step size adaption from
CmdStanMCMC
objects.
extract_hmc_info(object)
extract_hmc_info(object)
object |
An object of class |
A list including
inv_metric
: Inverse metric for each chain
(with matrix = FALSE
).
step_size_adapt
: Step size adaptation for each chain.
This is primarily used internally.
## Not run: library(cmdstanr) fit_schools_ncp_mcmc <- cmdstanr_example("schools_ncp") extract_hmc_info(fit_schools_ncp_mcmc) ## End(Not run)
## Not run: library(cmdstanr) fit_schools_ncp_mcmc <- cmdstanr_example("schools_ncp") extract_hmc_info(fit_schools_ncp_mcmc) ## End(Not run)
Extract Stan State
extract_stan_state(object, phase)
extract_stan_state(object, phase)
object |
An object of class |
phase |
Character string indicating the current phase.
Options include |
A list containing the inverse metric, step size, and last MCMC draw (to be used as the initial value for the next checkpoint)
## Not run: library(cmdstanr) # eight schools example fit_schools_ncp_mcmc <- cmdstanr_example("schools_ncp") extract_stan_state(fit_schools_ncp_mcmc, "sample") ## End(Not run)
## Not run: library(cmdstanr) # eight schools example fit_schools_ncp_mcmc <- cmdstanr_example("schools_ncp") extract_stan_state(fit_schools_ncp_mcmc, "sample") ## End(Not run)
brmsfit
ObjectThis is primarily used internally, wherein the cmdstanr
object is converted into a brmsfit
object.
make_brmsfit(object, formula = NULL, data = NULL, prior = NULL, path)
make_brmsfit(object, formula = NULL, data = NULL, prior = NULL, path)
object |
An object of class |
formula |
An object of class |
data |
An object of class |
prior |
An object of class |
path |
Character string. The path to the folder, that is used for saving the checkpoints. |
An object of class brmsfit
This is primarily an internal function that constructs
a brmsfit
object.
chkpt_brms
ObjectsPrint chkpt_brms
Objects
## S3 method for class 'chkpt_brms' print(x, ...)
## S3 method for class 'chkpt_brms' print(x, ...)
x |
Object of class |
... |
Currently ignored |
No return value, and used to print the chkpt_brms
object.
This function mainly avoids printing out a list,
and it is only used when brmsfit = "FALSE"
in chkpt_brms
.
Typically, after fitting, the posterior draws should be summarized with
combine_chkpt_draws
(assuming brmsfit = "FALSE"
).
chkpt_setup
ObjectPrint chkpt_setup
Object
## S3 method for class 'chkpt_setup' print(x, ...)
## S3 method for class 'chkpt_setup' print(x, ...)
x |
An object of class |
... |
Currently ignored. |
No return value, and used to print the chkpt_setup
object.
chkpt_setup <- chkpt_setup( iter_sampling = 5000, iter_warmup = 2000, iter_per_chkpt = 10 ) chkpt_setup
chkpt_setup <- chkpt_setup( iter_sampling = 5000, iter_warmup = 2000, iter_per_chkpt = 10 ) chkpt_setup
chkpt_stan
ObjectsPrint chkpt_stan
Objects
## S3 method for class 'chkpt_stan' print(x, ...)
## S3 method for class 'chkpt_stan' print(x, ...)
x |
Object of class |
... |
Currently ignored |
No return value, and used to print the chkpt_stan
object.
This function mainly avoids printing out a list.
Typically, after fitting, the posterior draws should be summarized with
combine_chkpt_draws
.