-
Tidymodels를 활용한 ML Process데이터 2022. 7. 23. 16:04
Tidymodels Based Machine Learning
이 글은 R을 이용하여 머신러닝을 수행하는 새로운 추세를 소개하기위해 작성하였다. 머신러닝 알고리즘에 대한 내용이나 머신러닝 프로세스 대한 설명이 아니고 몇년전 부터 새롭게 부상하고 있는 tidymodels 라는 머신러닝을 수행하는 방법을 소개하는 글이다. 지금까지 R로 머신러닝을 하기 위한 통합적인 패키로 대표적인것은 Max Kuhn이 개발한 caret 패키지였다. (https://topepo.github.io/caret/ 참조)
caret은 Classification and Regression Training 약자로, 머신러닝 중 회귀와 분류문제에 특화된 패키지로 회귀와 분류에 필요한 다양한 머신러닝 모형들이 통합되어있다. caret 하나만으로 머신러닝의 다양한 모형과 파라메터들을 쉽게 적용하여 기계학습을 수행할 수 있다는 장점이 있다. 풍부하고 편리한 caret의 기능 때문에 현재 까지도 caret을 활용한 머신러닝을 많이 수행하고 있다.
하지만 최근 수년 전 부터 tidyverse 생태계에 기반한 머신러닝을 위해 tidymodels이 등장하여 주목을 받고 있는 상황이며 앞으로 점차 caret을 대치하여 R로 하는 머신러닝 생태계의 주역은 tidymodels가 될 전망이다. tidymodels는 머신러닝 작업을 몇가지 단계로 분할하고 각 단계별 작업 수행에 특화된 패키지들이 독립적으로 존재하며, 이런 패키지들을 이용하여 머신러닝 워크플로우를 만들어 갈 구 있다. tidymodels은 특정 목적을 수행하는 다양한 패키지들을 tidyvese 스타일로 파이프라이닝 시켜 작업의 흐름을 구성해 나간다. 또한 각각의 패키지들로 종합적인 머신러닝 프로세스를 구성하여 사용할 수도 있고 독립적으로 활용할 수도 있어 융통성있게 사용이 가능하다.
이번 글에서는 tidymodels의 다양한 패키지를 사용하여 머신러닝을 수행하는 방법을 소개한다.
이번 글에서는 다음과 같은 tidymodels의 패키지들을 활용하여 단순화된 tidymodels 머신러닝 프로세스를 구현해본다.
- rsample : 데이터 샘플링과 관련된 패키지
- recipes : 데이터 전처리 관련 패키지
- workflow : 다양한 요소들을 통합하여 머신러닝 워크플로우 생성
- parnip : 모델을 생성하고 학습 지원 패키지
- yardstick : 성능평가 관련 패키지
- broom : 각종 통계 결과에 대한 R 객체를 읽기 쉬운 형태로 제공하는 패키지
- dials : 튜닝 파라메터를 관리하고 조정하기 위한 패키지
머신러닝 문제의 정의
iris 데이터의 품종을 randomForest 알고리즘을 이용하여 수행하고 분류 결과를 평가한다. library(tidymodels)로 패키지를 로드하면 위와 같은 다양한 패키지들의 동시에 로드가 된다.
library(tidyverse) library(tidymodels) library(ranger)
데이터 준비
데이터를 준비하는 첫 단계는 데이터를 분할 하는 것이다.
set.seed(6439) iris_split <- initial_split(iris, prop = 0.7) iris_split ; class(iris_split) # rsplit이라는 객체가 생성됨
<Analysis/Assess/Total> <105/45/150>
[1] "mc_split" "rsplit"
iris_train <- iris_split %>% training() # training 데이터 생성 iris_test <- iris_split %>% testing() # test데이터 생성
데이터를 분할하기 위해 사용하는 함수는 rsample 패키지의 initial_split()로 데이터 세트를 조건 맞도록 훈련 세트 와 테스트 세트로 분리하기위한 함수이다. initial_split()함수의 인자인 prop는 훈련데이터와 테스트 데이터 비율을 지정하고 strata 인자는 나눈 비율을 적용할 종속변수를 지정한다. training()함수와 testing() 함수는 데이터를 분할하여 실제 학습 데이터와 테스트 데이터를 생성한다. 위 예제에서는 70%의 데이터를 학습용 데이터로 지정하였다. 생성된 학습용 데이터를 확인해보자.
iris_train %>% glimpse()
Rows: 105 Columns: 5 $ Sepal.Length <dbl> 4.5, 7.9, 6.7, 4.9, 4.7, 6.2, 5.5, 5.1, 4.9, 7.7, 6.… $ Sepal.Width <dbl> 2.3, 3.8, 3.3, 3.0, 3.2, 2.8, 2.3, 3.7, 2.4, 3.0, 3.… $ Petal.Length <dbl> 1.3, 6.4, 5.7, 1.4, 1.3, 4.8, 4.0, 1.5, 3.3, 6.1, 5.… $ Petal.Width <dbl> 0.3, 2.0, 2.1, 0.2, 0.2, 1.8, 1.3, 0.4, 1.0, 2.3, 2.… $ Species <fct> setosa, virginica, virginica, setosa, setosa, …
데이터 전처리
머신러닝 수행시 주의할 내용 중 하나는 학습용 데이터와 검증용 데이터를 엄격하게 구분해서 수행하는 것이다. 즉 학습용 데이터와 검증용 데이터가 혼용되지 않게하는 것이다. 통상 주어진 데이터를 전체를 먼저 전처리 한 후 전처리 된 데이터를 학습용과 검증용으로 구분하는 것은 적절치 않다고 알려져 있다. 더 좋은 방법은 학습용 데이터를 전처리 하여 학습을 시행한 후 그와 동일한 방법으로 검증용 데이터를 전처리하는 것이다. 이렇게 학습용 데이터에게 적용한 전처리 내용을 동일하게 검증용 데이터에도 적용시켜 전처리하는데 최적화 된 패키지가 recipe 패키지이다. recipe 패키는 데이터 전처리에 필요한 다양한 기능을 모두 담고 있는 강력한 패키지이다. 이름을 통해 알 수 있듯이 마치 요리하는 순서를 설명해 놓은 recipe같이 데이터를 가지고 요리하듯이 전처리 해나갈 수 있으며, 한번 작성된 recipe를 학습용 데이터에 적용시켜 전처리하고 동일한 recipe를 검증용 데이터에 적용해서 검증용 데이터에 대해서도 동일한 전처리를 수행할 수 있다.
recipe 패키지는 몇가지 단계를 거쳐 전처리를 진행하게된다. 먼저 recipe 패키지를 적용할 데이터에 대해 recipe()함수로 선언을 한다. 다음 머신러닝 수행에 필요한 전처리 작업 즉 결측치 처리, 숫자형 변수의 표준화, 범주형 변수의 더미화 등 원하는 전처리 작업 지시를 하고, prep() 함수로 recipe 객체를 완성한다. 완성된 recipe 객체는 juice() 함수에 학습용 데이터를 적용시키고, bake()함수에 검증용 데이터를 적용시켜 전처리를 완성한다. iris 데이터의 특징상 다양한 전처리가 불필요하여 상관계수가 과도하게 높은 변수를 식별하여 삭제하고, 수치형변수의 표준화 작업을 수행하도록 레시피를 구성하였다.
iris_recipe <- iris_train %>% # rsplit객체로 training 데이터 생성 recipe(Species ~.) %>% # recipe() 함수에 분류문제임을 선언 step_corr(all_predictors()) %>% # recipe는 처리는 step_* step_center(all_predictors(), -all_outcomes()) %>% step_scale(all_predictors(), -all_outcomes()) %>% prep()
recipe를 만들었으니(요리할 때와 마찬가지로 지금은 요리 절차인 레스피를 만든것이다.) recipe대로 training데이터와 testing데이터를 전처리해야 한다. 전처리는 juice()와 bake()를 함수를 이용해 완성한다. 이때 학습용 데이터는 juice() 함수를 검증용 데이터는 bake() 함수를 이용 생성한다. 이러한 흐름이 처음에는 다소 생경하게 느껴지는데, 몇번 반복해서 실습해보면 매우 타당함을 알게되도 익숙해지게 된다.
개념적으로 ’전처리를 어떻게 해야 한다’라는 레시피를 만든 다음, 동일한 레시피를 트레이닝 데이터와 테스트 데이터에 공통적으로 적용하는 개념이다.# 훈련데이터는 juice(iris_recipe) iris_train <- juice(iris_recipe) # training 데이터 전처리 수행 # 테스트 데이터는 iris_recipe 대로 테스팅 데이터를 bake한다. iris_test <- iris_recipe %>% bake(iris_test) glimpse(iris_train) # 전처리된 training 데이터
Rows: 105 Columns: 4 $ Sepal.Length <dbl> -1.6377134, 2.3395906, 0.9358362, -1.1697953, … $ Sepal.Width <dbl> -1.7601025, 1.7050993, 0.5500320, -0.1430083, … $ Petal.Width <dbl> -1.25678450, 0.98433472, 1.11616526, -1.38861505,… $ Species <fct> setosa, virginica, virginica, setosa, setosa, …
최종 생성된 학습용 데이터를 살표보면 Petal.Length 변수가 사라졌고, 수치형 변수들이 모두 표준화되어 있음을 확인할 수 있다. 데이터가 준비되었으니 랜덤포레스로 분류를 수행해보자.
모델 학습
tidymodels로 머신러닝 모델을 수행 시키기위해 사용하는 패키지가 parsnip 패키지이다. parsnip 패키지에는 다양한 모델들로 학습을 시키기 위한 다양한 종류의 함수들이 준비되어있다. 여기서는 랜텀포레스트 알고리즘(모델)을 적용하여 분류를 수행한다. 랜덤포레스트 알고리즘도 여러가지 패키지가 개발이 되어있다. 대표적으로 randomForest와 ranger가 있다. 각각의 패키지에서는 모델에 사용하는 인자를 다르게 정의해서 사용하기도 한다. 예를들어 ranger에서는 트리의 수 인자가 num.tree 인데 반해 randomForest 에서는 ntree로 지정 사용한다. 이렇게 모델별로 지정하는 인자가 달라 모델을 사용하기 위해서는 지정해야하는 인자명과 형식을 알아야 하는데, 많은 모형의 인자를 기억하기는 쉽지 않은 문제이다. tidymodels에서는 같은 모델을 사용하는 패키지는 동일한 인자로 지정하게 함으로써(예 trees = 100) 이런 문제를 단순화 시켰다. 랜덤포레스트 모델을 사용하겠다는 선언하는 함수가 rand_forest() 이다. 구체적인 수행모델들을 알아보고 싶을떄는 show_engine()라는 함수를 사용해서 어떤 모형들이 있는지 확인할 수 있고, show_model_info()함수를 통해 모델이 지정(튜닝)해주어야 하는 인자들을 확인해 볼 수 있다. 여기서는 randomForest 모형을 사용해서 분류를 수행해보자.
show_engines("rand_forest") # 랜덤포레스트 엔진 다나와 ranger,randomForest,spark
# A tibble: 6 × 2 engine mode <chr> <chr> 1 ranger classification 2 ranger regression 3 randomForest classification 4 randomForest regression 5 spark classification 6 spark regression
show_model_info("rand_forest") # 각 모델별 정보 -> 모델별 파라메터 등
Information for `rand_forest` modes: unknown, classification, regression, censored regression engines: classification: randomForest, ranger¹, spark regression: randomForest, ranger¹, spark ¹The model can use case weights. arguments: ranger: mtry --> mtry trees --> num.trees min_n --> min.node.size randomForest: mtry --> mtry trees --> ntree min_n --> nodesize spark: mtry --> feature_subset_strategy trees --> num_trees min_n --> min_instances_per_node fit modules: engine mode ranger classification ranger regression randomForest classification randomForest regression spark classification spark regression prediction modules: mode engine methods classification randomForest class, prob, raw classification ranger class, conf_int, prob, raw classification spark class, prob regression randomForest numeric, raw regression ranger conf_int, numeric, raw regression spark numeric
rand_forest() 함수에 trees = 100, mode = “classification”을 지정하여 랜텀포레스트 모델로 분류를 수행하되, 트리의 수는 100개로 한다는 선언을 한다. 다음에는 set_engine() 함수로 랜덤포레스트 모델 중 ranger를 사용하겠다고 지정한다. 이제 학습을 시킬 준비가 되었으니 fit()함수에 분류를 위한 포뮬러와 학습시킬 데이터를 지정하여 학습을 시키면 된다. 주목할 것은 이럴 절차를 타이디버스의 개념에 맞게 파이프라이닝하여 물흐르듯 코딩해 나간다는 점이다.
#------ randomForest로 학습하기 ------------- iris_rf <- rand_forest(trees = 100, mode = "classification") %>% set_engine("randomForest") %>% # 이번엔 randomForest 모형으로 fit(Species ~ ., data = iris_train) # 학습
예측 수행 : predict using test data
학습을 시켰으니 테스트 데이터로 예측(분류)를 해보자. parsnip 패키지는 예측결과를 .pred_class에 담아 티블로 반환해준다. 학습결과를 tibble 형태로 반환해 주어 머신러닝 수행 전체 절차가 tidyverse 형식을 취할 수 있도록 일관성 있게 디자인되었음을 알 수 있다. .pred_class를 test데이터의 종속변수와 쉽게 한눈에 비교하기 위해 bind_cols() 함수를 이용하여 결합시켜서 결과를 출력해보자.
#----- randomForest로 예측하기 --------- iris_rf %>% predict(iris_test) %>% bind_cols(iris_test) %>% head()
# A tibble: 6 × 5 .pred_class Sepal.Length Sepal.Width Petal.Width Species <fct> <dbl> <dbl> <dbl> <fct> 1 setosa -1.05 1.24 -1.39 setosa 2 setosa -1.52 0.781 -1.26 setosa 3 setosa -1.75 -0.374 -1.39 setosa 4 setosa -1.17 0.0880 -1.52 setosa 5 setosa -0.234 3.09 -1.12 setosa 6 setosa -0.936 1.01 -1.26 setosa
검증용 데이터인 iris_test 데이터에 .pred_class 라는 컬럼이 추가되어 있으며 그 컬럼에는 뷴류 예측 결과인 iris의 품종이 들어 있는것을 확인해 볼 수 있다. 예측한 결과를 우리에게 익숙한 분류의 평가지표인 정확도등으로 출력해 보기 위해서는 metric함수를 사용한다.
평가
모델의 성능을 측정하기위한 함수는 metrics()이다. metrics() 함수에 truth 인자에 test 데이터의 분류하고 자 했던 종속변수를 지정하고 estimate 인자에는 예측을 수행한 결과인 .pred_class를 지정하여 결과 평가를 수행한다.
iris_rf %>% predict(iris_test) %>% bind_cols(iris_test) %>% metrics(truth = Species, estimate = .pred_class)
# A tibble: 2 × 3 .metric .estimator .estimate <chr> <chr> <dbl> 1 accuracy multiclass 0.933 2 kap multiclass 0.899
예측을 species로 바로 예측을 수행할 수도 있지만, 확률로도 예측할 수 있다. 즉 species가 해당 품종일 확률을 각각 구해준다. 예측 결과를 확률로 구하기 위해서는 predict()함수의 type 인자를 prob로 지정해야한다. iris가 해당 품종일 확률값을 계산하여 각각 .pred_setosa, .pred_versicolor, .pred_virginica 변수로 반환된다.
iris_rf %>% predict(iris_test, type = "prob") %>% glimpse() # 예측결과를 각각의 확률값으로 반환
Rows: 45 Columns: 3 $ .pred_setosa <dbl> 1.00, 1.00, 0.88, 0.95, 0.94, 1.00, 1.00, 0.94, … $ .pred_versicolor <dbl> 0.00, 0.00, 0.11, 0.03, 0.06, 0.00, 0.00, 0.05, … $ .pred_virginica <dbl> 0.00, 0.00, 0.01, 0.02, 0.00, 0.00, 0.00, 0.01, …
iris_probs <- iris_rf %>% predict(iris_test, type = "prob") %>% bind_cols(iris_test) # 반환된 확률값과 테스트데이터를 비교하기 위해.. head(iris_probs)
# A tibble: 6 × 7 .pred_setosa .pred_versicolor .pred_virginica Sepal.Length Sepal.Width <dbl> <dbl> <dbl> <dbl> <dbl> 1 1 0 0 -1.05 1.24 2 1 0 0 -1.52 0.781 3 0.88 0.11 0.01 -1.75 -0.374 4 0.95 0.03 0.02 -1.17 0.0880 5 0.94 0.06 0 -0.234 3.09 6 1 0 0 -0.936 1.01 # … with 2 more variables: Petal.Width <dbl>, Species <fct>
생성된 iris_probs 티블은 각 품종임을 예측한 확률값과 원 테스트 데이터로 구성되어 있어 비교하기 수월하고 이를 이용해 gain커브와 ROC 커브를 쉽게 그릴 수 있다. gain커브는 gain_curve() 함수를, ROC 커브는 roc_curve() 함수를 사용하고, 그래프로 출력은 autoplot()함수를 사용한다.
# gain 커브 -------------- iris_probs%>% gain_curve(Species, .pred_setosa:.pred_virginica) %>% autoplot()
# ROC 커브 --------------- iris_probs%>% roc_curve(Species, .pred_setosa:.pred_virginica) %>% autoplot()
결과를 한눈에 보기 좋게 필요한 정보만을 티블로 정리해보면 다음과 같다.
# 결과를 티블로 정리 predict(iris_rf, iris_test, type = "prob") %>% bind_cols(predict(iris_rf, iris_test)) %>% bind_cols(select(iris_test, Species)) %>% head() # 결과를 티블로 정리한 형태
# A tibble: 6 × 5 .pred_setosa .pred_versicolor .pred_virginica .pred_class Species <dbl> <dbl> <dbl> <fct> <fct> 1 1 0 0 setosa setosa 2 1 0 0 setosa setosa 3 0.88 0.11 0.01 setosa setosa 4 0.95 0.03 0.02 setosa setosa 5 0.94 0.06 0 setosa setosa 6 1 0 0 setosa setosa
# 결과를 평가 메트릭으로 정리 predict(iris_rf, iris_test, type = "prob") %>% bind_cols(predict(iris_rf, iris_test)) %>% bind_cols(select(iris_test, Species)) %>% metrics(Species, .pred_setosa:.pred_virginica, estimate = .pred_class)
# A tibble: 4 × 3 .metric .estimator .estimate <chr> <chr> <dbl> 1 accuracy multiclass 0.933 2 kap multiclass 0.899 3 mn_log_loss multiclass 0.210 4 roc_auc hand_till 0.983
종합적으로 정확도 카파값, ROC커브의 AUC값들이 정리되어 출력되었다.
tidymodels을 이용한 머신러닝을 처음부터 끝까지 가장 기본적인 패키지와 함수들로 만 수행해보았다. tidymodels 패키지는 비교적 신생 패키지로 지금도 그 기능 및 성능이 꾸준히 개발되고 향상되고 있다. 이런 멋진 패키지를 만들어 R 생태계에 기여하고 있는 Max Kuhn과 Hadly Wickham 등 Grue 들에게 감사한 마음으로 글을 마무리한다.
'데이터' 카테고리의 다른 글
탐색적 데이터분석 : EDA(Explatory Data Analysis) (0) 2022.08.21 Handling Factor Variable using forcats package (0) 2022.08.07 ggplot을 이용한 데이터가시화 II (0) 2022.07.08 ggplot을 이용한 데이터 가시화 I (0) 2022.06.26 Analysis of FIFA 22 DATA (2) (0) 2022.06.10