Repository navigation
Report the Pareto k diagnostic for sis() and tis() #411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -164,7 +164,7 @@ print_dims.importance_sampling_loo <- function(x, ...) { | |
| dims <- dim(x) | ||
| cat( | ||
| "Computed from", dims[1], "posterior draws and", | ||
| dims[2], "log-likelihood terms using", class(x)[1], ".\n" | ||
| dims[2], "log-likelihood terms using", paste0(class(x)[1], ".\n") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not about this line, but related: print.importance_sampling_loo() (line 57) still doesn't print the Pareto k table. So loo(x, is_method = "sis") will now warns "Some Pareto k diagnostic values are too high. See help('pareto-k-diagnostic')", but the printed output has no diagnostic. If k means something for sis/tis now, should we print pareto_k_table() like print.psis_loo() does? I'm not sure actually, and it doesn't necessarily need to be decided in this PR |
||
| ) | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -305,6 +305,39 @@ enough_tail_samples <- function(tail_len, min_len = 5) { | |
| tail_len >= min_len | ||
| } | ||
|
|
||
| #' Pareto k diagnostic for a single vector of log ratios | ||
| #' | ||
| #' Fits the generalized Pareto distribution to the same tail as `do_psis_i()`, | ||
| #' without smoothing, so that `sis()` and `tis()` report the same diagnostic as | ||
| #' `psis()` for the same log ratios. | ||
| #' | ||
| #' @noRd | ||
| #' @param log_ratios_i A vector of log importance ratios. | ||
| #' @param tail_len_i An integer tail length. | ||
| #' @return Scalar Pareto k estimate. `Inf` if the tail is too short or constant, | ||
| #' or if the fit fails, as in `do_psis_i()`. | ||
| #' | ||
| pareto_k_i <- function(log_ratios_i, tail_len_i) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This repeats the tail selection, cutoff and constant-tail check from |
||
| if (!enough_tail_samples(tail_len_i)) { | ||
| return(Inf) | ||
| } | ||
| S <- length(log_ratios_i) | ||
| lw_sorted <- sort.int(log_ratios_i - max(log_ratios_i)) | ||
| tail_ids <- seq(S - tail_len_i + 1, S) | ||
| lw_tail <- lw_sorted[tail_ids] | ||
| if (abs(max(lw_tail) - min(lw_tail)) < .Machine$double.eps / 100) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The constant-tail case isn't covered by the tests. test_psis.R:146 tests it for do_psis_i(), so we could we add the same case for sis()/tis() |
||
| warning( | ||
| "Can't fit generalized Pareto distribution ", | ||
| "because all tail values are the same.", | ||
| call. = FALSE | ||
| ) | ||
| return(Inf) | ||
| } | ||
| cutoff <- lw_sorted[min(tail_ids) - 1] | ||
| k <- posterior::gpdfit(exp_x_minus_exp_y(lw_tail, cutoff), sort_x = FALSE)$k | ||
| if (is.na(k)) Inf else k | ||
| } | ||
|
|
||
|
|
||
| #' Throw warnings about Pareto k estimates | ||
| #' | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,7 +30,9 @@ | |
| #' } | ||
| #' \item{`diagnostics`}{ | ||
| #' A named list containing one vector: | ||
| #' * `pareto_k`: Not used in `sis`, all set to 0. | ||
| #' * `pareto_k`: Estimates of the shape parameter \eqn{k} of the | ||
| #' generalized Pareto distribution, computed from the log ratios as in | ||
| #' [psis()]. `sis()` uses them only as a diagnostic. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sis() and tis() now warn using the PSIS threshold |
||
| #' * `n_eff`: effective sample size estimates. | ||
| #' } | ||
| #' } | ||
|
|
@@ -45,7 +47,7 @@ | |
| #' If specified, the user's `r_eff` argument. | ||
| #' } | ||
| #' \item{`tail_len`}{ | ||
| #' Not used for `sis`. | ||
| #' Vector of tail lengths used for the Pareto k diagnostic. | ||
| #' } | ||
| #' \item{`dims`}{ | ||
| #' Integer vector of length 2 containing `S` (posterior sample size) | ||
|
|
@@ -135,13 +137,16 @@ is.sis <- function(x) { | |
| #' @noRd | ||
| #' @param log_ratios_i A vector of log importance ratios (for `loo()`, negative | ||
| #' log likelihoods). | ||
| #' @param tail_len_i An integer tail length for the Pareto k diagnostic. | ||
| #' @param ... Not used. Included to conform to PSIS API. | ||
| #' | ||
| #' @details Implementation standard importance sampling. | ||
| #' @return A named list containing: | ||
| #' * `lw`: vector of unnormalized log weights | ||
| #' * `pareto_k`: scalar Pareto k estimate. For IS, this defaults to 0. | ||
| do_sis_i <- function(log_ratios_i, ...) { | ||
| S <- length(log_ratios_i) | ||
| list(log_weights = log_ratios_i, pareto_k = 0) | ||
| #' * `pareto_k`: scalar Pareto k estimate, the same as for `psis()`. | ||
| do_sis_i <- function(log_ratios_i, tail_len_i, ...) { | ||
| list( | ||
| log_weights = log_ratios_i, | ||
| pareto_k = pareto_k_i(log_ratios_i, tail_len_i) | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,7 +33,9 @@ | |
| #' } | ||
| #' \item{`diagnostics`}{ | ||
| #' A named list containing one vector: | ||
| #' * `pareto_k`: Not used in `tis`, all set to 0. | ||
| #' * `pareto_k`: Estimates of the shape parameter \eqn{k} of the | ||
| #' generalized Pareto distribution, computed from the log ratios before | ||
| #' truncation as in [psis()]. `tis()` uses them only as a diagnostic. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment that I made in |
||
| #' * `n_eff`: Effective sample size estimates. | ||
| #' } | ||
| #' } | ||
|
|
@@ -48,7 +50,7 @@ | |
| #' If specified, the user's `r_eff` argument. | ||
| #' } | ||
| #' \item{`tail_len`}{ | ||
| #' Not used for `tis`. | ||
| #' Vector of tail lengths used for the Pareto k diagnostic. | ||
| #' } | ||
| #' \item{`dims`}{ | ||
| #' Integer vector of length 2 containing `S` (posterior sample size) | ||
|
|
@@ -139,6 +141,7 @@ is.tis <- function(x) { | |
| #' @noRd | ||
| #' @param log_ratios_i A vector of log importance ratios (for `loo()`, negative | ||
| #' log likelihoods). | ||
| #' @param tail_len_i An integer tail length for the Pareto k diagnostic. | ||
| #' @param ... Not used. Included to conform to PSIS API. | ||
| #' | ||
| #' @details Implementation of Truncated importance sampling (TIS), a method for | ||
|
|
@@ -148,13 +151,14 @@ is.tis <- function(x) { | |
| #' | ||
| #' @return A named list containing: | ||
| #' * `lw`: vector of unnormalized log weights | ||
| #' * `pareto_k`: scalar Pareto k estimate. For 'tis', this defaults to 0. | ||
| #' * `pareto_k`: scalar Pareto k estimate of the log ratios before truncation, | ||
| #' the same as for `psis()`. | ||
| #' | ||
| do_tis_i <- function(log_ratios_i, ...) { | ||
| do_tis_i <- function(log_ratios_i, tail_len_i, ...) { | ||
| S <- length(log_ratios_i) | ||
| log_Z <- logSumExp(log_ratios_i) - log(S) # Normalization term, c-hat in Ionides (2008) appendix | ||
| log_cutpoint <- log_Z + 0.5 * log(S) | ||
| lw_i <- pmin(log_ratios_i, log_cutpoint) | ||
| list(log_weights = lw_i, pareto_k = 0) | ||
| list(log_weights = lw_i, pareto_k = pareto_k_i(log_ratios_i, tail_len_i)) | ||
| } | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this change, a user calling sis() or tis() users the multi-column tail-length warning, which ends with "Skipping the following columns: 1, 2, ...". But nothing is skipped for these methods except the diagnostic, because the weights are computed as usual, so this could maybe be a confusing warning message? Maybe not a big deal. We could maybe reword the message in
throw_tail_length_warnings()to something like e.g. "Pareto k is set to Inf for the following columns: ..."? I'm not sure it's worth changing that message though. Just pointing this out but not a big deal.