Ajustando os hiperparâmetros aleatórios da floresta com os dados das árvores #TidyTuesday

cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br


[Esteartigofoipublicadopelaprimeiravezem[Thisarticlewasfirstpublishedon Rstats em Julia Silge, e gentilmente contribuiu para os R-blogueiros]. (Você pode relatar um problema sobre o conteúdo desta página aqui)


Deseja compartilhar seu conteúdo com R-blogueiros? clique aqui se você tiver um blog ou aqui se não tiver.

Venho publicando screencasts demonstrando como usar a estrutura tidymodels, desde as primeiras etapas da modelagem até como ajustar modelos mais complexos. Hoje estou usando um #TidyTuesday conjunto de dados do início deste ano em árvores de São Francisco para mostrar como ajustar os hiperparâmetros de um modelo de floresta aleatório e, em seguida, usar o melhor modelo final.


Aqui está o código que usei no vídeo, para quem prefere ler em vez de ou além do vídeo.

Explore os dados

Nosso objetivo de modelagem aqui é prever o status legal das árvores em São Francisco no conjunto de dados #TidyTuesday. Este não é o conjunto de dados desta semana, mas é o que eu queria retornar. Como parece quase errado não usar, usaremos um modelo de floresta aleatório! 🌳

Vamos construir um modelo para prever quais árvores são mantidas pelo Departamento de Obras Públicas de São Francisco e quais não são. Podemos usar parse_number() para obter uma estimativa aproximada do tamanho do gráfico a partir do plot_size coluna. Em vez de tentar qualquer imputação, manteremos apenas as observações sem NA valores.

library(tidyverse)

sf_trees <- read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-01-28/sf_trees.csv")

trees_df %
  mutate(
    legal_status = case_when(
      legal_status == "DPW Maintained" ~ legal_status,
      TRUE ~ "Other"
    ),
    plot_size = parse_number(plot_size)
  ) %>%
  select(-address) %>%
  na.omit() %>%
  mutate_if(is.character, factor)

Vamos fazer uma pequena análise exploratória dos dados antes de ajustarmos os modelos. Como essas árvores são distribuídas por São Francisco?

trees_df %>%
  ggplot(aes(longitude, latitude, color = legal_status)) +
  geom_point(size = 0.5, alpha = 0.4) +
  labs(color = NULL)

Você pode ver ruas! E há definitivamente diferenças espaciais por categoria.

Que relações vemos com o cuidador de cada árvore?

trees_df %>%
  count(legal_status, caretaker) %>%
  add_count(caretaker, wt = n, name = "caretaker_count") %>%
  filter(caretaker_count > 50) %>%
  group_by(legal_status) %>%
  mutate(percent_legal = n / sum(n)) %>%
  ggplot(aes(percent_legal, caretaker, fill = legal_status)) +
  geom_col(position = "dodge") +
  labs(
    fill = NULL,
    x = "% of trees in each category"
  )

Modelo de construção

Podemos começar carregando o metapacote tidymodels e dividindo nossos dados em conjuntos de treinamento e teste.

library(tidymodels)

set.seed(123)
trees_split <- initial_split(trees_df, strata = legal_status)
trees_train <- training(trees_split)
trees_test <- testing(trees_split)

Em seguida, criamos uma receita para o pré-processamento de dados.

  • Primeiro, devemos dizer ao recipe() qual será nosso modelo (usando uma fórmula aqui) e quais são nossos dados de treinamento.
  • Em seguida, atualizamos a função para tree_id, como essa é uma variável, gostaríamos de manter a conveniência como identificador de linhas, mas não é um preditor ou resultado.
  • Em seguida, usamos step_other() para recolher os níveis categóricos de espécies, cuidador e as informações do site. Antes desta etapa, havia mais de 300 espécies!
  • o date A coluna com quando cada árvore foi plantada pode ser útil para ajustar esse modelo, mas provavelmente não a data exata, dada a velocidade com que as árvores crescem. Vamos criar um recurso de ano a partir da data e remover o original date variável.
  • Há muito mais árvores mantidas pelo DPW do que não, então vamos reduzir os dados para treinamento.

O objeto tree_rec é uma receita que tem não já foi treinado em dados (por exemplo, quais níveis categóricos devem ser recolhidos não foram calculados) e tree_prep é um objeto que tem foi treinado em dados.

tree_rec %
  update_role(tree_id, new_role = "ID") %>%
  step_other(species, caretaker, threshold = 0.01) %>%
  step_other(site_info, threshold = 0.005) %>%
  step_dummy(all_nominal(), -all_outcomes()) %>%
  step_date(date, features = c("year")) %>%
  step_rm(date) %>%
  step_downsample(legal_status)

tree_prep <- prep(tree_rec)
juiced <- juice(tree_prep)

Agora é hora de criar uma especificação de modelo para uma floresta aleatória na qual ajustaremos mtry (o número de preditores a serem amostrados em cada divisão) e min_n (o número de observações necessárias para manter a divisão dos nós). Esses são hiperparâmetros que não podem ser aprendidos com os dados ao treinar o modelo.

tune_spec %
  set_mode("classification") %>%
  set_engine("ranger")

Por fim, vamos reuni-los em um workflow(), que é um objeto de contêiner conveniente para transportar bits de modelos.

tune_wf %
  add_recipe(tree_rec) %>%
  add_model(tune_spec)

Este fluxo de trabalho está pronto para começar. 🚀

cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br

Hiperparâmetros de trem

Agora é hora de ajustar os hiperparâmetros para um modelo de floresta aleatório. Primeiro, vamos criar um conjunto de reamostragens de validação cruzada para usar no ajuste.

set.seed(234)
trees_folds <- vfold_cv(trees_train)

Não podemos aprender os valores certos ao treinar um único modelo, mas podemos treinar vários modelos e ver quais são os melhores. Podemos usar o processamento paralelo para acelerar isso, pois as diferentes partes da grade são independentes. Vamos usar grid = 20 para escolher 20 pontos de grade automaticamente.

doParallel::registerDoParallel()

set.seed(345)
tune_res <- tune_grid(
  tune_wf,
  resamples = trees_folds,
  grid = 20
)

tune_res
## #  10-fold cross-validation 
## # A tibble: 10 x 4
##    splits               id     .metrics          .notes          
##                                           
##  1  Fold01  
##  2  Fold02  
##  3  Fold03  
##  4  Fold04  
##  5  Fold05  
##  6  Fold06  
##  7  Fold07  
##  8  Fold08  
##  9  Fold09  
## 10  Fold10  

Como isso aconteceu? Vamos dar uma olhada na AUC.

tune_res %>%
  collect_metrics() %>%
  filter(.metric == "roc_auc") %>%
  select(mean, min_n, mtry) %>%
  pivot_longer(min_n:mtry,
    values_to = "value",
    names_to = "parameter"
  ) %>%
  ggplot(aes(value, mean, color = parameter)) +
  geom_point(show.legend = FALSE) +
  facet_wrap(~parameter, scales = "free_x") +
  labs(x = NULL, y = "AUC")

Essa grade não envolveu todas as combinações de min_n e mtry mas podemos ter uma idéia do que está acontecendo. Parece que valores mais altos de mtry são bons (acima de 10) e valores mais baixos de min_n são bons (abaixo de 10). Podemos entender melhor os hiperparâmetros ajustando mais uma vez, desta vez usando regular_grid(). Vamos definir intervalos de hiperparâmetros que queremos experimentar, com base nos resultados de nossa música inicial.

rf_grid <- grid_regular(
  mtry(range = c(10, 30)),
  min_n(range = c(2, 8)),
  levels = 5
)

rf_grid
## # A tibble: 25 x 2
##     mtry min_n
##     
##  1    10     2
##  2    15     2
##  3    20     2
##  4    25     2
##  5    30     2
##  6    10     3
##  7    15     3
##  8    20     3
##  9    25     3
## 10    30     3
## # … with 15 more rows

Podemos ajustar mais uma vez, mas desta vez de maneira mais direcionada com esse rf_grid.

set.seed(456)
regular_res <- tune_grid(
  tune_wf,
  resamples = trees_folds,
  grid = rf_grid
)

regular_res
## #  10-fold cross-validation 
## # A tibble: 10 x 4
##    splits               id     .metrics          .notes          
##                                           
##  1  Fold01  
##  2  Fold02  
##  3  Fold03  
##  4  Fold04  
##  5  Fold05  
##  6  Fold06  
##  7  Fold07  
##  8  Fold08  
##  9  Fold09  
## 10  Fold10  

Como são os resultados agora?

regular_res %>%
  collect_metrics() %>%
  filter(.metric == "roc_auc") %>%
  mutate(min_n = factor(min_n)) %>%
  ggplot(aes(mtry, mean, color = min_n)) +
  geom_line(alpha = 0.5, size = 1.5) +
  geom_point() +
  labs(y = "AUC")

Escolhendo o melhor modelo

Está muito mais claro qual é o melhor modelo agora. Podemos identificá-lo usando a função select_best()e atualize nossa especificação de modelo original tune_spec para criar nossa especificação final do modelo.

best_auc <- select_best(regular_res, "roc_auc")

final_rf <- finalize_model(
  tune_spec,
  best_auc
)

final_rf
## Random Forest Model Specification (classification)
## 
## Main Arguments:
##   mtry = 20
##   trees = 1000
##   min_n = 2
## 
## Computational engine: ranger

Vamos explorar um pouco o nosso modelo final. O que podemos aprender sobre importância variável, usando o pacote vip?

library(vip)

final_rf %>%
  set_engine("ranger", importance = "permutation") %>%
  fit(legal_status ~ .,
    data = juice(tree_prep) %>% select(-tree_id)
  ) %>%
  vip(geom = "point")

A característica de cuidador particular importante na categorização, como latitude e longitude. Interessante naquele ano (ou seja, idade da árvore) é tão importante!

Vamos fazer um fluxo de trabalho final e ajustar uma última vez, usando a função de conveniência last_fit(). Essa função se encaixa em um modelo final em todo o conjunto de treinamento e avalia no conjunto de testes. Nós apenas precisamos dar a esta função nossa divisão original de trem / teste.

final_wf %
  add_recipe(tree_rec) %>%
  add_model(final_rf)

final_res %
  last_fit(trees_split)

final_res %>%
  collect_metrics()
## # A tibble: 2 x 3
##   .metric  .estimator .estimate
##                 
## 1 accuracy binary         0.852
## 2 roc_auc  binary         0.950

As métricas para o conjunto de testes parecem boas e indicam que não houve superajuste durante o ajuste.

Vamos vincular nossos resultados de teste ao conjunto de testes original e criar mais um mapa. Onde em São Francisco há árvores previstas mais ou menos incorretamente?

final_res %>%
  collect_predictions() %>%
  mutate(correct = case_when(
    legal_status == .pred_class ~ "Correct",
    TRUE ~ "Incorrect"
  )) %>%
  bind_cols(trees_test) %>%
  ggplot(aes(longitude, latitude, color = correct)) +
  geom_point(size = 0.5, alpha = 0.5) +
  labs(color = NULL) +
  scale_color_manual(values = c("gray80", "darkred"))

var vglnk = {key: '949efb41171ac6ec1bf7f206d57e90b8'};

(função (d, t) {
var s = d.createElement
s.src = '//cdn.viglink.com/api/vglnk.js';
var r = d.getElementsByTagName
} (documento, 'script'));

Para Deixe um comentário para o autor, siga o link e comente no blog: Rstats em Julia Silge.

R-bloggers.com oferece atualizações diárias por email sobre notícias e tutoriais do R sobre o aprendizado do R e muitos outros tópicos. Clique aqui se você deseja publicar ou encontrar um emprego em ciência da dados / R.


Deseja compartilhar seu conteúdo com R-blogueiros? clique aqui se você tiver um blog ou aqui se não tiver.



cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br
Leia Também  Estimando modelos autorregressivos de vetores variáveis ​​(VAR) no tempo