diff --git a/dashboard/R/fit_model.R b/dashboard/R/fit_model.R index 8d7487d..310892f 100644 --- a/dashboard/R/fit_model.R +++ b/dashboard/R/fit_model.R @@ -227,7 +227,7 @@ generate_model_spec <- function(method, params) { } else if (method == "SVM") { - if (params$boundary == "linear") { + if (params$boundary == "Linear") { model_spec <- svm_linear( mode = "regression", cost = !!params$cost, @@ -238,7 +238,8 @@ generate_model_spec <- function(method, params) { model_spec <- svm_rbf( mode = "regression", cost = !!params$cost, - margin = !!params$margin + margin = !!params$margin, + rbf_sigma = !!params$rbf_sigma ) |> set_engine("kernlab") } @@ -520,7 +521,7 @@ fit_model_tuning <- function( # grid_spec <- generate_grid_spec(method, model_spec, grid_size, seed) # tuning - if (n_folds > 20 | grid_size > 25) { + if (n_folds > 10 | grid_size > 25) { doFuture::registerDoFuture() future::plan(strategy = "multisession", workers = parallelly::availableCores() - 1) message("Number of parallel workers: ", future::nbrOfWorkers()) @@ -543,4 +544,3 @@ fit_model_tuning <- function( return(wkfl_fit) } - diff --git a/dashboard/R/utils.R b/dashboard/R/utils.R index 287544b..135431b 100644 --- a/dashboard/R/utils.R +++ b/dashboard/R/utils.R @@ -9,56 +9,83 @@ set_options <- function() { "dl" = c("Feed-Forward", "COMING SOON!"), "mix" = c("Feed-Forward AR", "ARIMA-Boost", "Prophet-Boost"), "ens" = c("Average", "Weighted Average", "Median"), - "stk" = c("Linear Regression", "Elastic Net") + "stk" = c("Linear Regression", "Elastic Net"), + "tune" = c( + "Elastic Net", "MARS", "KNN", "SVM", "Random Forest", "Boosted Trees", "Cubist", + "Feed-Forward", "Feed-Forward AR", "ARIMA-Boost", "Prophet-Boost" + ) ), tsf.dashboard.methods_params = list( "Naive" = NULL, "Seasonal Naive" = NULL, - "Rolling Average" = c("window_size"), + "Rolling Average" = c("window_size") |> purrr::set_names(c("Window Size")), "ETS" = c( "auto_ets", "error", "trend", "season", "damping", "smooth_level", "smooth_trend", "smooth_season" - ), + ) |> purrr::set_names(c("Auto-ETS", "Error", "Trend", "Seasonality", "Damped Trend", "Alpha", "Beta", "Gamma")), "Theta" = NULL, "SARIMA" = c( "auto_arima", "non_seasonal_ar", "non_seasonal_differences", "non_seasonal_ma", "seasonal_ar", "seasonal_differences", "seasonal_ma" - ), - "TBATS" = c("auto_tbats", "tbats_seasonal_period_1", "tbats_seasonal_period_2", "tbats_seasonal_period_3"), - "STLM" = c("auto_stlm", "trend_model", "stlm_seasonal_period_1", "stlm_seasonal_period_2", "stlm_seasonal_period_3"), + ) |> purrr::set_names(c("Auto-ARIMA", "p", "d", "q", "P", "D", "Q")), + "TBATS" = c("auto_tbats", "tbats_seasonal_period_1", "tbats_seasonal_period_2", "tbats_seasonal_period_3") |> + purrr::set_names(c("Auto-TBATS", "Seasonal Period 1", "Seasonal Period 2", "Seasonal Period 3")), + "STLM" = c("auto_stlm", "trend_model", "stlm_seasonal_period_1", "stlm_seasonal_period_2", "stlm_seasonal_period_3") |> + purrr::set_names(c("Auto-STLM", "Trend Model", "Seasonal Period 1", "Seasonal Period 2", "Seasonal Period 3")), "Prophet" = c( "auto_prophet", "growth", "logistic_cap", "logistic_floor", "changepoint_num", "changepoint_range", "prophet_season", "seasonality_yearly", "seasonality_weekly", "seasonality_daily", "prior_scale_changepoints", "prior_scale_seasonality", "prior_scale_holidays" - ), + ) |> + purrr::set_names(c( + "Auto-Prophet", "Growth", "Logistic Cap", "Logistic Floor", + "Changepoints Num", "Changepoints Range", "Seasonality", + "Yearly Seasonality", "Weekly Seasonality", "Daily Seasonality", + "Changepoint Flexibility", "Seasonality Stength", "Holidays Strength" + )), "Linear Regression" = NULL, - "Elastic Net" = c("penalty", "mixture"), - "MARS" = c("num_terms", "prod_degree", "prune_method"), - "KNN" = c("neighbors"), - "SVM" = c("boundary", "cost", "margin"), - "Random Forest" = c("rf_mtry", "rf_trees", "rf_min_n"), + "Elastic Net" = c("penalty", "mixture") |> purrr::set_names(c("Penalty", "Mixture")), + "MARS" = c("num_terms", "prod_degree", "prune_method") |> + purrr::set_names(c("Num Terms", "Interactions Degree", "Prune Method")), + "KNN" = c("neighbors") |> purrr::set_names(c("K-neighbors")), + "SVM" = c("boundary", "cost", "margin", "rbf_sigma") |> + purrr::set_names(c("Boundary Type", "Cost", "Margin", "Sigma")), + "Random Forest" = c("rf_mtry", "rf_trees", "rf_min_n") |> + purrr::set_names(c("Random Predictors", "Trees", "Min Node Size")), "Boosted Trees" = c( "boost_method", "boost_mtry", "boost_trees", "boost_min_n", "boost_tree_depth", "boost_learn_rate", "boost_loss_reduction", "boost_sample_size" - ), - "Cubist" = c("committees", "cub_neighbors", "max_rules"), - "Feed-Forward" = c("ff_hidden_units", "ff_penalty", "ff_epochs", "ff_dropout", "ff_learn_rate"), + ) |> + purrr::set_names(c( + "Boosting Method", "Random Predictors", "Trees", "Min Node Size", + "Tree Depth", "Learning Rate", "Min Loss Reduction", "Sample" + )), + "Cubist" = c("committees", "cub_neighbors", "max_rules") |> + purrr::set_names(c("Num Members", "Neighbors", "Max Rules")), + "Feed-Forward" = c("ff_hidden_units", "ff_penalty", "ff_epochs", "ff_dropout", "ff_learn_rate") |> + purrr::set_names(c("Hidden Units", "Decay", "Epochs", "Dropout", "Learning Rate")), "Feed-Forward AR" = c( "ffar_non_seasonal_ar", "ffar_seasonal_ar", "ffar_hidden_units", "ffar_penalty", "ffar_epochs", "ffar_num_networks" - ), + ) |> purrr::set_names(c("p", "P", "Hidden Units", "Decay", "Epochs", "Num Networks")), "ARIMA-Boost" = c( "arima_boost_mtry", "arima_boost_trees", "arima_boost_min_n", "arima_boost_tree_depth", "arima_boost_learn_rate", "arima_boost_loss_reduction", "arima_boost_sample_size" - ), + ) |> purrr::set_names(c( + "Random Predictors", "Trees", "Min Node Size", "Tree Depth", + "Learning Rate", "Min Loss Reduction", "Sample" + )), "Prophet-Boost" = c( "prophet_boost_mtry", "prophet_boost_trees", "prophet_boost_min_n", "prophet_boost_tree_depth", "prophet_boost_learn_rate", "prophet_boost_loss_reduction", "prophet_boost_sample_size" - ) + ) |> purrr::set_names(c( + "Random Predictors", "Trees", "Min Node Size", "Tree Depth", + "Learning Rate", "Min Loss Reduction", "Sample" + )) ), tsf.dashboard.transfs = c("log", "boxcox", "norm", "stand", "diff", "sdiff"), tsf.dashboard.test_transfs = c("test_log", "test_diff", "test_sdiff"), @@ -159,7 +186,7 @@ get_default <- function(parameter, return_value = TRUE) { "penalty" = 1, "mixture" = 0.5, # Elastic Net "num_terms" = 20, "prod_degree" = 1, "prune_method" = "backward", # MARS "neighbors" = 5, # KNN - "boundary" = "linear", "cost" = 1, "margin" = 0.1, # SVM + "boundary" = "Linear", "cost" = 1, "margin" = 0.1, "rbf_sigma" = 0.02, # SVM "rf_mtry" = 5, "rf_trees" = 500, "rf_min_n" = 5, # Random Forest "boost_method" = "XGBoost", # Boosted Trees "boost_mtry" = 5, "boost_trees" = 100, "boost_min_n" = 1, "boost_tree_depth" = 6, diff --git a/dashboard/test.R b/dashboard/test.R index 2350374..30de5f1 100644 --- a/dashboard/test.R +++ b/dashboard/test.R @@ -209,7 +209,7 @@ input <- list( n_folds = 5, metric = "RMSE", grid_size = 10, - tune_xx_rf = c("rf_mtry") + tune_xx_rf = c("mtry") ) data = data_selected diff --git a/dashboard/todo.txt b/dashboard/todo.txt index 321778d..1f8bedc 100644 --- a/dashboard/todo.txt +++ b/dashboard/todo.txt @@ -11,11 +11,10 @@ Next steps: - documentazione in alto a destra To Do: -- aggiungere rbf_sigma conditional su svm_rbf - finire update_tune_model_parameters con mapping parametri (occhio, non mtry ma rf_mtry) - testare flusso optimize fino al forecast -- change split in generate_forecast - fare la funzione parse_parameters per ottenere i nomi da mostrare in UI dal parametro +- cambiare assegnazione nomi ai parametri in UI - aggiungere metodi di automl (h2o) - pensare e aggiungere la sezione di stacking (LM + Elastic Net) diff --git a/dashboard/tsf_dashboard.Rmd b/dashboard/tsf_dashboard.Rmd index 869d178..1daaf73 100644 --- a/dashboard/tsf_dashboard.Rmd +++ b/dashboard/tsf_dashboard.Rmd @@ -51,10 +51,11 @@ dl_methods <- methods$dl mix_methods <- methods$mix ens_methods <- methods$ens stk_methods <- methods$stk +tune_methods <- methods$tune methods <- c(ts_methods, ml_methods, dl_methods, mix_methods) -methods_params <- getOption("tsf.dashboard.methods_params") -methods_params_cl <- methods_params |> map(clean_chr) +mtd_prm <- getOption("tsf.dashboard.methods_params") +mtd_prm_names <- purrr::map(mtd_prm, names) metrics <- toupper(getOption("tsf.dashboard.metrics")) ``` @@ -526,7 +527,7 @@ pickerInput( `Deep Learning` = dl_methods, `Mixed Algorithms` = mix_methods ), - selected = ts_methods[1] + selected = "Naive" ) actionButton(inputId = "apply_forecast", label = "Forecast!", icon = icon("play")) @@ -537,9 +538,8 @@ observeEvent( updateNumericInput(session = session, inputId = "n_future", value = 12) updateNumericInput(session = session, inputId = "n_assess", value = 24) updatePrettyRadioButtons(session = session, inputId = "assess_type", selected = "Rolling") - updatePickerInput(session = session, inputId = "method", selected = ts_methods[1]) - updateNumericInput(session = session, inputId = "window_size", value = 12) - shinyjs::delay(ms = 300, expr = {shinyjs::click(id = "apply_forecast")}) + updatePickerInput(session = session, inputId = "method", selected = "Naive") + # shinyjs::delay(ms = 300, expr = {shinyjs::click(id = "apply_forecast")}) } ) @@ -634,8 +634,8 @@ conditionalPanel( numericInput(inputId = "logistic_cap", label = "Logistic Cap", value = get_default("logistic_cap"), min = -Inf, max = Inf, step = 1), numericInput(inputId = "logistic_floor", label = "Logistic Floor", value = get_default("logistic_floor"), min = -Inf, max = Inf, step = 1) ), - numericInput(inputId = "changepoint_num", label = "Changepoints (num)", value = get_default("changepoint_num"), min = 0, max = Inf, step = 1), - numericInput(inputId = "changepoint_range", label = "Changepoints (range)", value = get_default("changepoint_range"), min = 0, max = 1), + numericInput(inputId = "changepoint_num", label = "Changepoints Num", value = get_default("changepoint_num"), min = 0, max = Inf, step = 1), + numericInput(inputId = "changepoint_range", label = "Changepoints Range", value = get_default("changepoint_range"), min = 0, max = 1), prettyRadioButtons(inputId = "prophet_season", label = "Seasonality", choices = c("additive", "multiplicative"), inline = TRUE, selected = get_default("prophet_season")), awesomeCheckbox(inputId = "seasonality_yearly", label = "Yearly Seasonality?", value = get_default("seasonality_yearly")), awesomeCheckbox(inputId = "seasonality_weekly", label = "Weekly Seasonality?", value = get_default("seasonality_weekly")), @@ -680,9 +680,13 @@ conditionalPanel( conditionalPanel( condition = "input.method == 'SVM'", h5("Algorithm hyperparameters: "), - prettyRadioButtons(inputId = "boundary", label = "Boundary Type", choices = c("linear", "radial"), inline = TRUE, selected = get_default("boundary")), + prettyRadioButtons(inputId = "boundary", label = "Boundary Type", choices = c("Linear", "Radial"), inline = TRUE, selected = get_default("boundary")), numericInput(inputId = "cost", label = "Cost", value = get_default("cost"), min = 0, max = Inf), - numericInput(inputId = "margin", label = "Margin", value = get_default("margin"), min = 0, max = Inf) + numericInput(inputId = "margin", label = "Margin", value = get_default("margin"), min = 0, max = Inf), + conditionalPanel( + condition = "input.boundary == 'Radial'", + numericInput(inputId = "rbf_sigma", label = "Sigma", value = get_default("rbf_sigma"), min = 0, max = Inf) + ) ) # Random Forest @@ -928,7 +932,7 @@ pickerInput( `Deep Learning` = dl_methods, `Mixed Algorithms` = mix_methods ), - selected = ts_methods[c(4, 6)], options = list("actions-box" = TRUE) + selected = c("ETS", "SARIMA"), options = list("actions-box" = TRUE) ) br() @@ -943,7 +947,7 @@ observeEvent( updateNumericInput(session = session, inputId = "comp_n_future", value = 12) updateNumericInput(session = session, inputId = "comp_n_assess", value = 24) updatePrettyRadioButtons(session = session, inputId = "comp_assess_type", selected = "Rolling") - updatePickerInput(session = session, inputId = "comp_method", selected = ts_methods[c(4, 6)]) + updatePickerInput(session = session, inputId = "comp_method", selected = c("ETS", "SARIMA")) updateMaterialSwitch(session = session, inputId = "comp_tune", value = FALSE) # shinyjs::delay(ms = 300, expr = {shinyjs::click(id = "tune_apply_forecast")}) } @@ -1073,12 +1077,11 @@ dropdownButton( pickerInput( inputId = "tune_method", label = h3("Forecast Algorithm"), multiple = FALSE, choices = list( - `Time Series` = ts_methods, `Machine Learning` = ml_methods, `Deep Learning` = dl_methods, `Mixed Algorithms` = mix_methods ), - selected = ml_methods[2] + selected = "Elastic Net" ) actionButton(inputId = "tune_apply_forecast", label = "Forecast!", icon = icon("play")) @@ -1093,8 +1096,8 @@ observeEvent( updateSliderInput(session = session, inputId = "tune_n_folds", value = 5) updateSelectInput(session = session, inputId = "tune_metric", selected = "RMSE") updateSliderInput(session = session, inputId = "tune_grid_size", value = 10) - updateSelectInput(session = session, inputId = "tune_method", selected = ml_methods[2]) - updatePickerInput(session = session, inputId = "tune_elanet", selected = methods_params_cl[["Elastic Net"]][2]) + updateSelectInput(session = session, inputId = "tune_method", selected = "Elastic Net") + updatePickerInput(session = session, inputId = "tune_elanet", selected = mtd_prm_names[["Elastic Net"]]) # shinyjs::delay(ms = 300, expr = {shinyjs::click(id = "tune_apply_forecast")}) } ) @@ -1108,8 +1111,20 @@ conditionalPanel( h5("Algorithm hyperparameters to optimize: "), pickerInput( inputId = "tune_xx_elanet", label = NULL, # _xx_ need to recognize it easily in update_tune_model_parameters() - choices = methods_params_cl[["Elastic Net"]], multiple = TRUE, - selected = methods_params_cl[["Elastic Net"]][2], + choices = mtd_prm_names[["Elastic Net"]], multiple = TRUE, + selected = mtd_prm_names[["Elastic Net"]], + options = list("actions-box" = TRUE) + ) +) + +# Random Forest +conditionalPanel( + condition = "input.tune_method == 'Random Forest'", + h5("Algorithm hyperparameters to optimize: "), + pickerInput( + inputId = "tune_xx_elanet", label = NULL, # _xx_ need to recognize it easily in update_tune_model_parameters() + choices = mtd_prm_names[["Random Forest"]], multiple = TRUE, + selected = mtd_prm_names[["Random Forest"]], options = list("actions-box" = TRUE) ) ) @@ -1159,7 +1174,7 @@ pickerInput( `Deep Learning` = dl_methods, `Mixed Algorithms` = mix_methods ), - selected = ts_methods[c(4, 6)], multiple = TRUE, + selected = c("ETS", "SARIMA"), multiple = TRUE, options = list( "actions-box" = FALSE, "max-options" = 5, @@ -1169,13 +1184,13 @@ pickerInput( pickerInput( inputId = "ens_type", label = h3("Ensemble Method"), - choices = ens_methods, selected = ens_methods[1], multiple = TRUE, + choices = ens_methods, selected = "Average", multiple = TRUE, options = list("actions-box" = TRUE) ) pickerInput( inputId = "stk_type", label = h3("Stacking Method"), - choices = stk_methods, selected = stk_methods[1], multiple = TRUE, + choices = stk_methods, selected = "Linear Regression", multiple = TRUE, options = list("actions-box" = TRUE) ) @@ -1191,8 +1206,9 @@ observeEvent( updateNumericInput(session = session, inputId = "ens_n_future", value = 12) updateNumericInput(session = session, inputId = "ens_n_assess", value = 24) updatePrettyRadioButtons(session = session, inputId = "ens_assess_type", selected = "Rolling") - updatePickerInput(session = session, inputId = "ens_method", selected = ts_methods[c(4, 6)]) - updatePickerInput(session = session, inputId = "ens_type", selected = ens_methods[1]) + updatePickerInput(session = session, inputId = "ens_method", selected = c("ETS", "SARIMA")) + updatePickerInput(session = session, inputId = "ens_type", selected = "Average") + updatePickerInput(session = session, inputId = "stk_type", selected = "Linear Regression") updateMaterialSwitch(session = session, inputId = "ens_tune", value = FALSE) # shinyjs::delay(ms = 300, expr = {shinyjs::click(id = "tune_apply_forecast")}) } @@ -1311,7 +1327,7 @@ pickerInput( `Deep Learning` = dl_methods, `Mixed Algorithms` = mix_methods ), - selected = ts_methods[4] + selected = "ETS" ) actionButton(inputId = "scn_apply_forecast", label = "Forecast!", icon = icon("play")) @@ -1322,7 +1338,7 @@ observeEvent( updateNumericInput(session = session, inputId = "scn_n_future", value = 12) updateNumericInput(session = session, inputId = "scn_n_assess", value = 24) updatePrettyRadioButtons(session = session, inputId = "scn_assess_type", selected = "Rolling") - updateSelectInput(session = session, inputId = "scn_method", selected = ts_methods[4]) + updateSelectInput(session = session, inputId = "scn_method", selected = "ETS") # shinyjs::delay(ms = 300, expr = {shinyjs::click(id = "tune_apply_forecast")}) } )