From 7e581b775b37935c17b8d54d83b40a829429c873 Mon Sep 17 00:00:00 2001 From: gufengzhou Date: Fri, 25 Aug 2023 12:46:57 +0800 Subject: [PATCH] feat: adding objective_weights This allows customisation of optimisation weights among objective functions. default is even weight c(1, 1). It also allows 0 weight, equal to removing an objective function. check function needs to be amended, because the values needs to be >=0 and the same length as obj.fun count --- R/R/model.R | 23 +++++++++++++++++++++++ R/man/robyn_mmm.Rd | 6 ++++++ R/man/robyn_run.Rd | 6 ++++++ R/man/robyn_train.Rd | 6 ++++++ 4 files changed, 41 insertions(+) diff --git a/R/R/model.R b/R/R/model.R index c2ca5c5d7..b7c291802 100644 --- a/R/R/model.R +++ b/R/R/model.R @@ -49,6 +49,10 @@ #' In other words, given the same DECOMP.RSSD score, a model with 50\% 0-coef #' variables will get penalized by DECOMP.RSSD * 1.5 (larger error), while #' another model with no 0-coef variables gets un-penalized with DECOMP.RSSD * 1. +#' @param objective_weights Numeric. Default to NULL that gives equal weights +#' to all objective functions. Set c(2, 1) to give double weight to the 1st. +#' This is an experimental feature. There's no research on optimal weight +#' setting. Subjective weights might strongly bias modelling result. #' @param seed Integer. For reproducible results when running nevergrad. #' @param outputs Boolean. Process results with \code{robyn_outputs()}? #' @param lambda_control Deprecated in v3.6.0. @@ -81,6 +85,7 @@ robyn_run <- function(InputCollect = NULL, trials = 5, iterations = 2000, rssd_zero_penalty = TRUE, + objective_weights = NULL, nevergrad_algo = "TwoPointsDE", intercept = TRUE, intercept_sign = "non_negative", @@ -160,6 +165,7 @@ robyn_run <- function(InputCollect = NULL, ts_validation = ts_validation, add_penalty_factor = add_penalty_factor, rssd_zero_penalty = rssd_zero_penalty, + objective_weights = objective_weights, refresh, seed, quiet ) @@ -290,6 +296,7 @@ robyn_train <- function(InputCollect, hyper_collect, dt_hyper_fixed = NULL, ts_validation = TRUE, add_penalty_factor = FALSE, + objective_weights = NULL, rssd_zero_penalty = TRUE, refresh = FALSE, seed = 123, quiet = FALSE) { @@ -309,6 +316,7 @@ robyn_train <- function(InputCollect, hyper_collect, ts_validation = ts_validation, add_penalty_factor = add_penalty_factor, rssd_zero_penalty = rssd_zero_penalty, + objective_weights = objective_weights, seed = seed, quiet = quiet ) @@ -345,6 +353,7 @@ robyn_train <- function(InputCollect, hyper_collect, ts_validation = ts_validation, add_penalty_factor = add_penalty_factor, rssd_zero_penalty = rssd_zero_penalty, + objective_weights = objective_weights, refresh = refresh, trial = ngt, seed = seed + ngt, @@ -401,6 +410,7 @@ robyn_mmm <- function(InputCollect, intercept_sign, ts_validation = TRUE, add_penalty_factor = FALSE, + objective_weights = NULL, dt_hyper_fixed = NULL, # lambda_fixed = NULL, rssd_zero_penalty = TRUE, @@ -529,11 +539,24 @@ robyn_mmm <- function(InputCollect, my_tuple <- tuple(hyper_count) instrumentation <- ng$p$Array(shape = my_tuple, lower = 0, upper = 1) optimizer <- ng$optimizers$registry[optimizer_name](instrumentation, budget = iterTotal, num_workers = cores) + # Set multi-objective dimensions for objective functions (errors) if (is.null(calibration_input)) { optimizer$tell(ng$p$MultiobjectiveReference(), tuple(1, 1)) + if (is.null(objective_weights)) { + objective_weights <- tuple(1, 1) + } else { + objective_weights <- tuple(objective_weights[1], objective_weights[2]) + } + optimizer$set_objective_weights(objective_weights) } else { optimizer$tell(ng$p$MultiobjectiveReference(), tuple(1, 1, 1)) + if (is.null(objective_weights)) { + objective_weights <- tuple(1, 1, 1) + } else { + objective_weights <- tuple(objective_weights[1], objective_weights[2], objective_weights[3]) + } + optimizer$set_objective_weights(objective_weights) } } diff --git a/R/man/robyn_mmm.Rd b/R/man/robyn_mmm.Rd index 7d78d9e97..c4e33e6dd 100644 --- a/R/man/robyn_mmm.Rd +++ b/R/man/robyn_mmm.Rd @@ -14,6 +14,7 @@ robyn_mmm( intercept_sign, ts_validation = TRUE, add_penalty_factor = FALSE, + objective_weights = NULL, dt_hyper_fixed = NULL, rssd_zero_penalty = TRUE, refresh = FALSE, @@ -62,6 +63,11 @@ glmnet's penalty.factor to be optimized by nevergrad. Use with caution, because this feature might add too much hyperparameter space and probably requires more iterations to converge.} +\item{objective_weights}{Numeric. Default to NULL that gives equal weights +to all objective functions. Set c(2, 1) to give double weight to the 1st. +This is an experimental feature. There's no research on optimal weight +setting. Subjective weights might strongly bias modelling result.} + \item{dt_hyper_fixed}{data.frame or named list. Only provide when loading old model results. It consumes hyperparameters from saved csv \code{pareto_hyperparameters.csv} or JSON file to replicate a model.} diff --git a/R/man/robyn_run.Rd b/R/man/robyn_run.Rd index 9946c5139..b7db5a033 100644 --- a/R/man/robyn_run.Rd +++ b/R/man/robyn_run.Rd @@ -19,6 +19,7 @@ robyn_run( trials = 5, iterations = 2000, rssd_zero_penalty = TRUE, + objective_weights = NULL, nevergrad_algo = "TwoPointsDE", intercept = TRUE, intercept_sign = "non_negative", @@ -76,6 +77,11 @@ In other words, given the same DECOMP.RSSD score, a model with 50\% 0-coef variables will get penalized by DECOMP.RSSD * 1.5 (larger error), while another model with no 0-coef variables gets un-penalized with DECOMP.RSSD * 1.} +\item{objective_weights}{Numeric. Default to NULL that gives equal weights +to all objective functions. Set c(2, 1) to give double weight to the 1st. +This is an experimental feature. There's no research on optimal weight +setting. Subjective weights might strongly bias modelling result.} + \item{nevergrad_algo}{Character. Default to "TwoPointsDE". Options are \code{c("DE","TwoPointsDE", "OnePlusOne", "DoubleFastGADiscreteOnePlusOne", "DiscreteOnePlusOne", "PortfolioDiscreteOnePlusOne", "NaiveTBPSA", diff --git a/R/man/robyn_train.Rd b/R/man/robyn_train.Rd index 5747aa4e5..5c56bedab 100644 --- a/R/man/robyn_train.Rd +++ b/R/man/robyn_train.Rd @@ -16,6 +16,7 @@ robyn_train( dt_hyper_fixed = NULL, ts_validation = TRUE, add_penalty_factor = FALSE, + objective_weights = NULL, rssd_zero_penalty = TRUE, refresh = FALSE, seed = 123, @@ -69,6 +70,11 @@ glmnet's penalty.factor to be optimized by nevergrad. Use with caution, because this feature might add too much hyperparameter space and probably requires more iterations to converge.} +\item{objective_weights}{Numeric. Default to NULL that gives equal weights +to all objective functions. Set c(2, 1) to give double weight to the 1st. +This is an experimental feature. There's no research on optimal weight +setting. Subjective weights might strongly bias modelling result.} + \item{rssd_zero_penalty}{Boolean. When TRUE, the objective function DECOMP.RSSD will penalize models with more 0 media effects additionally. In other words, given the same DECOMP.RSSD score, a model with 50\% 0-coef