Função para Tunar modelo de XGBoost

Motivação

Este material foi feito inspirado pelo curso de R para Machine Learning, ministrado pela Curso-R, em que mostra uma heurística sobre a tunagem do Modelo de XGBoost. O material está disponível para qualquer pessoa neste link, baixando o arquivo exemplos/12-xgboost.Rmd.

O que é XGBoost?

O modelo de XGBoost é uma extensão do Gradiente Boost, que é um modelo baseado em árvores. Se você deseja entender melhor o que é isso, sugiro este material e o repositória que está exposto nele.

Bom, o XGBoost conta com sete hiperparâmetros para encontrarmos a melhor combinação de um modelo. De que forma fazemos isso? Através da tunagem dos hiperparâmeros. Porém, quanto maior o número destes, mais demorado será a execução do modelo e maior capacidade computacional você vai precisar.

Se tivéssemos uma máquina com memória infinita, poderíamos testar infinitas possibilidades de hiperparâmetros para encontrarmos os melhores possíveis. No entando, sabemos que: uma máquina infinita custa infinitos dinheiros (R$) e ainda sim não conseguiria testar infinitas combinações e; se rodarmos em nuvem infinitamente isso, pode nos gerar também uma quantia absurda.

Com isso, foram criadas diversar maneiras de executar essa tunagem, e aqui, vou expor, em forma de função, a maneira que aprendi no curso que fiz, como falei anteriormente.

Porque uma função para tunar XGBoost?

Muitas vezes, no nosso dia a dia, estamos bem atarefados com nossos afazeres. Na minha experiência pessoal, sempre gosto de entender melhor qualquer situação problema que vem até mim, cada particularidade e cada circunstância. No entando, isso nem sempre é possível. Com isso, algo que me facilite a execução deste processo de tunagem foi bem pertinente.

Outro motivo que me fez montar essa função, foi o fato de que sempre é bom ter um “chute inicial” para os nossos trabalhos. Como já dizia o filósofo: Nada se cria, tudo se copia. Resumindo, com esta função tenho bons hiperparâmetros iniciais para que, caso eu queira, posteriormente, conseguir ajustar meu modelo.

Modelagem

Sem mais delongas, agora vamos a um teste de modelagem com a função de tunagem do XGBoost. Caso queira ter acesso a ela, só baixar o script e utilizar a função source("nome do arquivo") no seu script de modelagem.

Base trabalhada

Como sou da área de People Analytics, trabalharei com uma base que utilizo sempre de funcionários de uma empresa, onde contém informações de colaboradores desligados e ativos da mesma. Como este não é o foco deste material, não vamos nos aprofundar nas questões fora da função de tunagem.

Abaixo temos as variáveis trabalhadas e suas informações de estrutura:

base <- rio::import("../Case_Turnover_v2.csv")

base %>% 
  select_if(is.numeric) %>% 
    skimr::skim()
Data summary
Name Piped data
Number of rows 14999
Number of columns 8
_______________________
Column type frequency:
numeric 8
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
nivel_satisfacao 0 1 0.61 0.25 0.09 0.44 0.64 0.82 1 ▃▅▇▇▇
ultima_avaliacao 0 1 0.72 0.17 0.36 0.56 0.72 0.87 1 ▂▇▆▇▇
atuacao_projetos 0 1 3.80 1.23 2.00 3.00 4.00 5.00 7 ▇▆▃▂▁
horas_trabalhadas 0 1 201.05 49.94 96.00 156.00 200.00 245.00 310 ▃▇▆▇▂
tempo_empresa 0 1 3.50 1.46 2.00 3.00 3.00 4.00 10 ▇▃▁▁▁
licenca_medica 0 1 0.14 0.35 0.00 0.00 0.00 0.00 1 ▇▁▁▁▂
desligado 0 1 0.24 0.43 0.00 0.00 0.00 0.00 1 ▇▁▁▁▂
promocao_ultimos_3_anos 0 1 0.02 0.14 0.00 0.00 0.00 0.00 1 ▇▁▁▁▁
base %>% 
  select_if(is.character) %>% 
    skimr::skim()
Data summary
Name Piped data
Number of rows 14999
Number of columns 2
_______________________
Column type frequency:
character 2
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
area 0 1 2 11 0 10 0
salario 0 1 4 7 0 3 0

Em um processo normal de estudo, jamais passaria para a próxima etapa sem antes fazer uma boa análise exploratória, a fim de entender as variáveis que estão na base e trabalhar possíveis tratamentos das mesmas.

Pré processamento

Nesta etapa faremos a transformação da variável independente, dividiremos a base de treino e teste e trataremos possíveis problemas que as variáveis dependentes tenham.

base <- base %>% 
  mutate(
    desligado = as.factor(desligado)
  )

set.seed(100)
treino_teste <- base %>%  initial_split(0.7, strata = desligado)

base_treino <- training(treino_teste)
base_teste <- testing(treino_teste)

# Data prep
base_recipe <- recipe(
  desligado ~., data = base_treino
) %>% 
  step_zv(all_predictors()) %>%
  step_modeimpute(all_nominal(), -all_outcomes()) %>%
  step_medianimpute(all_numeric()) %>%
  step_novel(all_nominal(), -all_outcomes()) %>%
  step_dummy(all_nominal(), -all_outcomes())

No final do processo nossa base ficará assim:

# veirficar como ficou
juice(prep(base_recipe)) %>% head() %>% knitr::kable()
nivel_satisfacao ultima_avaliacao atuacao_projetos horas_trabalhadas tempo_empresa licenca_medica promocao_ultimos_3_anos desligado area_comercial area_executivos area_financeiro area_marketing area_medica area_pessoas area_producao_ac area_supply area_TI area_new salario_baixo salario_mediano salario_new
0.10 0.77 6 247 4 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0
0.89 1.00 5 224 5 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0
0.38 0.54 2 143 3 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0
0.41 0.46 2 128 3 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0
0.09 0.62 6 294 4 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0
0.38 0.49 2 151 3 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0

Carregamento da função

Agora vamos carregar a função que está em um script em meu diretório.

source("../Function_XGBoost_output.R")

Após isso, basta utilizarmos a função {tunagem_xgb()}, que tem os argumentos base_treino e base_recipe (esta é a base pré-processada). Ela pedirá o número de folds que você deseja repartir sua base para validação cruzada.

Obs: para este exemplo já deixei selecionada o número de folds fixo em 5, mas no script é o usuário quem define.

tunagem_xgb(base_treino, base_recipe)
[1] "Agora vamos rodar os hiperparâmetros!"
[1] "Melhores hiperparâmetros: "
[1] "Lean_rate =  0.05"
[1] "Trees =  100"
[1] "Tree_depth =  6"
[1] "Min_n =  5"
[1] "Loss_reduction =  0.1"
[1] "Sample_size =  1"
[1] "Mtry =  0.3"
[1] "Seu modelo está pronto para ser testado!"
[1] "Utilize o objeto xgb_wk para o teste do modelo"

Como podem ver, esta função vai retornar os valores dos melhores hiperparâmetros e salvar o objeto xgb_wkf, que nada mais é que o workflow (objeto que carrega todos os elementos do modelo) utilizado na modelagem. Precisaremos deste objeto para tetar nosso modelo.

Teste do modelo

Após utilização da função, vamos testar nosso modelo com o objeto xgb_wkf, como disse anteriormente:

xgb_last_fit <- xgb_wkf %>% 
  last_fit(
    split = treino_teste,
    metrics = metric_set(roc_auc, accuracy)
  )

Resultados do modelo

Agora saberemos as métricas do nosso modelo e a verificação da curva ROC. Aqui utilizei acurácia e auroc, mas claro que isso fica a critério de cada um:

collect_metrics(xgb_last_fit) %>% knitr::kable()
.metric .estimator .estimate .config
accuracy binary 0.9639248 Preprocessor1_Model1
roc_auc binary 0.9744340 Preprocessor1_Model1
# curva roc
xgb_last_fit %>% 
  collect_predictions() %>% 
  roc_curve(desligado,`.pred_1`) %>% 
  autoplot() +
  coord_flip()

Também conseguimos verificar a importância das variáveis do modelo:

xgb_last_fit %>% 
  pluck(".workflow", 1) %>%   
  pull_workflow_fit() %>% 
  vip::vip(num_features = 20)

Após este processo todo, você pode salvar este modelo e utilizar como quiser!

Considerações

Este material serve para quem deseja aplicar o processo de tunagem dos hiperparâmetros do modelo de XGBoost de maneira mais simples e quem sabe conseguir um bom chute inicial destes argumentos. Não recomendo aplicar a função diretamente a uma base, sem a análise exploratória e sem conhecimento prévio dos hiperparâmetros. No mais, aceito contribuições, sugestões e críticas! Pode acessar meu Github ou meu Linkedin.