I want to customize objective function.
For example, what I want to implement now is a loss function called Focal loss, which can be used when there is a class imbalance.
Focal Loss
Because focal loss is a generalization of cross-entropy, setting certain hyperparameters will result in exactly the same result as CE.
library(tidymodels)
library(xgboost)
#>
#> Attaching package: 'xgboost'
#> The following object is masked from 'package:dplyr':
#>
#> slice
data(agaricus.train, package = "xgboost")
data(agaricus.test, package = "xgboost")
dtrain <- with(agaricus.train, xgb.DMatrix(data, label = label, nthread = 2))
dtest <- with(agaricus.test, xgb.DMatrix(data, label = label, nthread = 2))
watchlist <- list(train = dtrain, eval = dtest)
# original cross entropy loss
logregobj <- function(preds, dtrain) {
labels <- getinfo(dtrain, "label")
preds <- 1/(1 + exp(-preds))
grad <- preds - labels
hess <- preds * (1 - preds)
return(list(grad = grad, hess = hess))
}
# focal_loss --------------------------------------------------------------
focal_loss <- function(preds, dtrain,alpha=0.5,focal_gamma=0) {
labels <- getinfo(dtrain, "label")
preds <- 1 / (1 + exp(-preds))
p<- preds
y <- labels
grad <- (y*alpha*(1-p)^focal_gamma*(focal_gamma*log(p)*p-(1-p))-
(1-y)*(1-alpha)*p^(focal_gamma)*(focal_gamma*log(1-p)*(1-p)-p))
du <- y*alpha*(1-p)^(focal_gamma-1)*(log(p)*(-focal_gamma^2 *p + (1-p)*focal_gamma)+ 2*focal_gamma*(1-p)+(1-p))
dv <- -(1-y)*(1-alpha)*p^(focal_gamma-1)*(log(1-p)*(focal_gamma^2*(1-p)-p*focal_gamma)-2*focal_gamma*p-p)
hess <- (du+dv)*p*(1-p)
return(list(grad = grad, hess = hess))
}
param <- list(max_depth = 2, eta = 1, nthread = 2,
objective = "binary:logistic", eval_metric = "auc")
bst <- xgb.train(param, dtrain, nrounds = 5, watchlist)
#> [1] train-auc:0.958228 eval-auc:0.960373
#> [2] train-auc:0.981413 eval-auc:0.979930
#> [3] train-auc:0.997070 eval-auc:0.998518
#> [4] train-auc:0.998757 eval-auc:0.998943
#> [5] train-auc:0.999298 eval-auc:0.999830
#> [1] train-auc:0.958228 eval-auc:0.960373
#> [2] train-auc:0.981413 eval-auc:0.979930
bst$params$objective
#> [1] "binary:logistic"
#> [1] "binary:logistic"
param$objective <- logregobj
bst <- xgb.train(param, dtrain, nrounds = 5, watchlist)
#> [1] train-auc:0.958228 eval-auc:0.960373
#> [2] train-auc:0.981413 eval-auc:0.979930
#> [3] train-auc:0.997070 eval-auc:0.998518
#> [4] train-auc:0.998757 eval-auc:0.998943
#> [5] train-auc:0.998120 eval-auc:0.999830
#> [1] train-auc:0.958228 eval-auc:0.960373
#> [2] train-auc:0.981413 eval-auc:0.979930
param$objective <- focal_loss# alpha = 0.5, gamma= 0 -> same result!
bst <- xgb.train(param, dtrain, nrounds = 5, watchlist)
#> [1] train-auc:0.958228 eval-auc:0.960373
#> [2] train-auc:0.981413 eval-auc:0.979930
#> [3] train-auc:0.997070 eval-auc:0.998518
#> [4] train-auc:0.998166 eval-auc:0.998943
#> [5] train-auc:0.998823 eval-auc:0.999830
I want to tuning alpha , focal_gamma
param$objective <- partial(focal_loss, alpha = 0.3, focal_gamma = 0)
bst <- xgb.train(param, dtrain, nrounds = 5, watchlist)
#> [1] train-auc:0.979337 eval-auc:0.980196
#> [2] train-auc:0.992593 eval-auc:0.993159
#> [3] train-auc:0.999934 eval-auc:0.999917
#> [4] train-auc:0.999978 eval-auc:0.999972
#> [5] train-auc:0.999978 eval-auc:0.999972
Created on 2023-02-28 with reprex v2.0.2
I checked that it works well on the existing xgb.train, but I don't know how to apply the tune function.
From now on, the code below is the way I tried.
library(tidymodels)
library(xgboost)
#>
#> Attaching package: 'xgboost'
#> The following object is masked from 'package:dplyr':
#>
#> slice
library(scales)
library(dials)
alpha <- function(range = c(0,1), trans = NULL) {
new_quant_param(
type = "double",
range = range,
inclusive = c(TRUE, TRUE),
trans = trans,
label = c(num_initial_terms = "# Initial alpha"),
finalize = NULL
)
}
focal_gamma <- function(range = c(0, 5), trans = NULL) {
new_quant_param(
type = "double",
range = range,
inclusive = c(TRUE, TRUE),
trans = trans,
label = c(num_initial_terms = "# Initial gamma"),
finalize = NULL
)
}
data<- two_class_dat |>
rename(y=Class)
set.seed(100)
splits<- initial_split(data,prop = 0.8,strata = y)
train_data <- training(splits)
test_data <- testing(splits)
resamples<- vfold_cv(data = train_data,v = 5,strata = y)
xgb_model <- boost_tree( mode = "classification",
tree_depth =tune(),
trees =tune()) |>
set_engine(engine = "xgboost" ,
objective = partial(focal_loss,
focal_gamma=tune())) #
#I wanted to tune the two hyperparameters together at first, but due to the error message, I am aiming to tune one first.
#>Error: Only one tunable value is currently allowed per argument.
#>The current argument has: `partial(focal_loss, focal_gamma = tune(), alpha = tune())`.
xgb_model |> translate()
#> Boosted Tree Model Specification (classification)
#>
#> Main Arguments:
#> trees = tune()
#> tree_depth = tune()
#>
#> Engine-Specific Arguments:
#> objective = partial(focal_loss, focal_gamma = tune())
#>
#> Computational engine: xgboost
#>
#> Model fit template:
#> parsnip::xgb_train(x = missing_arg(), y = missing_arg(), weights = missing_arg(),
#> nrounds = tune(), max_depth = tune(), objective = partial(focal_loss,
#> focal_gamma = tune()), nthread = 1, verbose = 0)
rec_base<- train_data %>%
recipe(y~.) |>
#step_mutate_at(all_numeric_predictors(), fn = list(orig = ~.)) %>%
step_normalize(all_predictors(), -all_outcomes())
xgb_workflow <-
workflow() %>%
add_recipe(rec_base) %>%
add_model(xgb_model)
xgb_workflow %>%
extract_parameter_set_dials()
#> Collection of 3 parameters for tuning
#>
#> identifier type object
#> trees trees nparam[+]
#> tree_depth tree_depth nparam[+]
#> objective objective missing
#> The parameter `objective` needs a `param` object.
#> See `vignette('dials')` to learn more.
Created on 2023-02-28 with reprex v2.0.2
The extract_parameter_set_dials() function does not recognize the focal_gamma argument.
How I can tuning objective function?
Ultimately, I want to tune not only one parameter but also several parameters together.
I want to customize objective function.
For example, what I want to implement now is a loss function called Focal loss, which can be used when there is a class imbalance.
Focal Loss
Because focal loss is a generalization of cross-entropy, setting certain hyperparameters will result in exactly the same result as CE.
I want to tuning alpha , focal_gamma
Created on 2023-02-28 with reprex v2.0.2
I checked that it works well on the existing xgb.train, but I don't know how to apply the tune function.
From now on, the code below is the way I tried.
Created on 2023-02-28 with reprex v2.0.2
The
extract_parameter_set_dials()function does not recognize thefocal_gammaargument.How I can tuning objective function?
Ultimately, I want to tune not only one parameter but also several parameters together.