random forest
작업흐름도random forest
방법론은 변수 선택, 변수 내 구분점, 결측값 처리 등을 자동으로 해주는 덕분에 앞부분에 변수(Feature)에 대한 별도 작업이 필요 없을 수도 있다. 하지만, 예측력이 뛰어나며 유지보수, 모형설명 측면 등을 고려했을 때 사전에 Dry-Run
혹은 IV 방법론을 동원하여 예측모형 데이터 전처리 작업을 수행하는 것이 나름 의미가 있다.
.csv
데이터를 read_csv()
를 통해 불러와서 변수명과 변수 자료형을 향수 분석에 맞게 조정한다. 앞서 로지스틱 회귀모형을 적합시키면서 데이터에 대한 전처리 작업은 마무리 되었기 때문에 바로 다음 단계로 넘어간다.
library(tidyverse)
library(readxl)
library(janitor)
library(skimr)
churn_dat <- read_csv("data/WA_Fn-UseC_-Telco-Customer-Churn.csv")
churn_df <- churn_dat %>%
clean_names()
churn_list <- skim_to_list(churn_df)
churn_df <- churn_df %>%
mutate(churn = factor(churn, levels = c("No", "Yes"))) %>%
mutate(senior_citizen = factor(senior_citizen)) %>%
mutate(multiple_lines = ifelse(str_detect(multiple_lines, "No"), "No", "Yes"),
internet_service = ifelse(str_detect(internet_service, "No"), "No", "Yes"),
online_security = ifelse(str_detect(online_security, "No"), "No", "Yes"),
online_backup = ifelse(str_detect(online_backup, "No"), "No", "Yes"),
device_protection = ifelse(str_detect(device_protection, "No"), "No", "Yes"),
tech_support = ifelse(str_detect(tech_support, "No"), "No", "Yes"),
streaming_tv = ifelse(str_detect(streaming_tv, "No"), "No", "Yes"),
streaming_movies = ifelse(str_detect(streaming_movies, "No"), "No", "Yes")) %>%
mutate_at(vars(churn_list$character$variable), as.factor) %>%
select(-customer_id) %>%
filter(complete.cases(.))
Dry-Run
일반화 선형모형이 갖는 장점중의 하나는 상대적으로 컴퓨팅 파워를 적게 필요로 한다는 점이다. 하지만, 이런 장점이 예측모형의 성능을 상대적으로 저하되는 원인이 되기도 한다. Random Forest 및 관련된 다양한 최신 예측 알고리즘은 예측성능은 뛰어나지만 이에 못지 않게 모형개발자가 해야 될 일은 컴퓨터가 대신하기 때문에 이에 대한 설정을 다음과 같이 한다.
caret
팩키지를 활용하여 훈련데이터와 검증데이터로 분리한다.
library(caret)
library(randomForest)
## 1. 훈련/시험 데이터 생성
index_train <- createDataPartition(churn_df$churn, p = 0.1, list = FALSE)
train_df <- churn_df[index_train, ]
test_df <- churn_df[-index_train, ]
## 2. 모형검증 데이터셋 생성
cv_folds <- createMultiFolds(train_df$churn, k = 3, times = 3)
cv_ctrl <- trainControl(method = "repeatedcv", number = 3,
repeats = 3, index = cv_folds,
summaryFunction = twoClassSummary,
verbose=TRUE,
classProbs=TRUE,
allowParallel = TRUE,
search = "random")
hp_grid <- expand.grid(mtry = c(2,5,7),
ntree = c(100, 500))
## 3. 병렬 컴퓨팅 환경설정 ------
library(doSNOW)
# 실행시간
start.time <- Sys.time()
cl <- makeCluster(4, type = "SOCK")
registerDoSNOW(cl)
본격적인 학습을 시작한다.
churn_rf <- train(churn ~ ., data = train_df,
method = "rf",
trControl = cv_ctrl,
tuneLength = 15,
# tuneGrid = hp_grid,
metric = "Spec",
importance = TRUE)
Aggregating results
Selecting tuning parameters
Fitting mtry = 20 on full training set
Time difference of 26.58866 secs
tuneLength=15
를 통해 초모수(hyper parameter)에 대한 무작위 검색(random search)를 수행해서 mtry
값이 2라는 것을 찾아낸다. 그리고 중요 변수 10를 추려낸다.
library(ggRandomForests)
library(pdp)
churn_vip <- varImp(churn_rf)
# 전체 변수 중 50%만 사용하기로 함.
important_variable <- churn_vip$importance %>% rownames_to_column("variable") %>%
top_n(10, wt=Yes) %>%
pull(variable)
which_two_pcnt <- tolerance(churn_rf$results, metric = "ROC",
tol = 2, maximize = TRUE)
churn_rf$results[which_two_pcnt,]
mtry ROC Sens Spec ROCSD SensSD SpecSD
1 1 0.818462 0.9929127 0.03393639 0.02614214 0.004830467 0.02725376
xgboost
, ranger
, rpart
… 모두 변수 중요도를 나타내는 함수를 갖고 있지만, 데이터도 다르고 함수명도 다르다. vip
팩키지는 이런 면에서 변수중요도를 기반 의사결정 나무모형과 독립적으로 변수중요도만 추출해 준다는 점에서 의미가 있다.
library(gridExtra)
features <- churn_rf$finalModel$importance %>% as.data.frame() %>%
rownames_to_column(var="variable") %>% top_n(10, wt=No) %>%
arrange(desc(No)) %>%
pull(variable)
pdps <- lapply(features, FUN = function(feature) {
pd <- partial(churn_rf$finalModel, pred.var = feature, train = test_df)
autoplot(pd) +
theme_light()
})
partial(churn_rf$finalModel, pred.var = monthly_charges, train = train_df)
grid.arrange(grobs = pdps, ncol = 5)