Skip to contents

Returns the determine_super_learner_weights function that super_learner() and crossfit_super_learner() use when the user does not supply one.

Usage

default_determine_weights(
  outcome_type = c("continuous", "binary", "density", "multiclass")
)

Arguments

outcome_type

One of 'continuous', 'binary', 'density', or 'multiclass'.

Value

A function suitable for the determine_super_learner_weights argument.

Examples

# the weight-determination function super_learner() uses when
# determine_super_learner_weights is not supplied:
default_determine_weights("continuous")   # non-negative least squares
#> function(data, y_variable, obs_weights = NULL) {
#>   # use nonlinear least squares to produce a weighting scheme
#>   index_of_y_variable <- which(colnames(data) == y_variable)[[1]]
#>   A <- as.matrix(data[, -index_of_y_variable])
#>   b <- data[[y_variable]]
#> 
#>   # Provide better error messages when all learners fail
#>   if (ncol(A) == 0) {
#>     stop(
#>       "determine_super_learner_weights_nnls() received no columns of learner ",
#>       "predictions (after removing the y_variable column), so there is ",
#>       "nothing to determine weights over. This usually means that no ",
#>       "learners successfully produced held-out predictions."
#>     )
#>   }
#> 
#> 
#>   if (!is.null(obs_weights) && length(obs_weights) != nrow(data)) {
#>     stop("The vector of observation weights must be equal in length to ",
#>     "the data being passed to nadir::super_learner().")
#>   }
#> 
#>   # if there are weights to use, we use the weights by multiplying A and b by
#>   # the square root of the weight vector
#>   if (!missing(obs_weights) &&
#>       !is.null(obs_weights) &&
#>       is.numeric(obs_weights) && length(obs_weights) == nrow(A)) {
#> 
#>     A <- A * sqrt(obs_weights)
#>     b <- b * sqrt(obs_weights)
#>   }
#> 
#>   nnls_output <- nnls::nnls(
#>     A = A,
#>     b = b
#>   )
#> 
#>   model_weights <- nnls_output$x
#>   if (sum(model_weights) <= 0) {
#>     warning(
#>       "Non-negative least squares assigned zero weight to every learner ",
#>       "(their held-out predictions are non-positively correlated with the ",
#>       "outcome). Falling back to equal weights across learners; consider ",
#>       "including an intercept-like learner such as lnr_mean in the library."
#>     )
#>     model_weights <- rep(1, ncol(A))
#>   }
#>   model_weights <- model_weights / sum(model_weights)
#>   return(model_weights)
#> }
#> <environment: namespace:nadir>
default_determine_weights("density")      # negative-log-loss simplex
#> function(data, y_variable, obs_weights = NULL,
#>                                                  bound_eps = 1e-3) {
#>   # in density estimation, the estimates have already "looked at" the
#>   # y-variable by the time they've predicted a density estimate.
#>   if (y_variable %in% colnames(data)) {
#>     data[[y_variable]] <- NULL
#>   }
#> 
#>   # With a single learner, the ensemble weight is trivially 1
#>   # Running optim() here is (a) a constant objective, since softmax of a length-1
#>   # parameter is always 1, and (b) triggers Nelder-Mead's 1-D unreliability
#>   # warning.
#>   if (ncol(data) == 1) {
#>     return(1)
#>   }
#> 
#>   # Provide better error messages when all learners fail
#>   if (ncol(data) == 0) {
#>     stop(
#>       "determine_weights_using_neg_log_loss() received no columns of learner ",
#>       "predictions (after removing the y_variable column), so there is ",
#>       "nothing to determine weights over. This usually means that no ",
#>       "learners successfully produced held-out predictions."
#>     )
#>   }
#> 
#>   weights_after_softmax <- rep(1 / ncol(data), ncol(data))
#>   weights_before_softmax <- log(weights_after_softmax)
#> 
#>   data <- as.matrix(data)
#> 
#>   if (!is.null(obs_weights) && length(obs_weights) != nrow(data)) {
#>     stop("The vector of observation weights must be equal in length to ",
#>     "the data being passed to nadir::super_learner().")
#>   }
#> 
#>   loss_fn <- function(presoftmax_weights) {
#>     weights <- softmax(presoftmax_weights)
#> 
#>     # apply the weights to each column
#>     weights_applied <- vapply(seq_len(ncol(data)), function(j) {
#>       weights[j] * data[, j]
#>     }, numeric(nrow(data)))
#>     # sum up each row of predicted densities across learners
#>     # this is now like a weighted average, and crucially the weights sum to 1
#>     # so it's still a conditional density.
#>     predicted_densities <- rowSums(weights_applied)
#> 
#>     # bound densities away from 0 before taking logs so a single
#>     # zero-density prediction cannot make the loss Inf/NaN and abort optim()
#>     # with "function cannot be evaluated at initial parameters".
#>     #' @srrstats {G3.0} here argument \code{bound_eps} is used to establish when
#>     #' densities are essentially zero.
#>     predicted_densities <- pmax(predicted_densities, bound_eps)
#> 
#>     # now take our loss function and return it, to optimize against it
#>     negative_log_predicted_densities <- -log(predicted_densities)
#> 
#>     if (!is.null(obs_weights)) {
#>       negative_log_predicted_densities <- negative_log_predicted_densities * obs_weights
#>     }
#>     return(sum(negative_log_predicted_densities))
#>   }
#> 
#>   weights_optim <- stats::optim(
#>     par = weights_before_softmax,
#>     fn = loss_fn,
#>     method = "Nelder-Mead"
#>   )
#> 
#>   weights <- softmax(weights_optim$par)
#> 
#>   return(weights)
#> }
#> <bytecode: 0x32de30ba0>
#> <environment: namespace:nadir>