1 iris 데이터 작업흐름

분류문제 - 작업흐름도

2 탐색적 데이터 분석

탐색적 데이터 분석에 대해서 모형개발에 필요한 전반적인 사항은 모두 파악한다. 여기서는 skimr 팩키지로 붓꽃데이터에 대한 모든 사항을 파악한 것으로 갈음한다.

library(tidyverse)
skimr::skim(iris)
Data summary
Name iris
Number of rows 150
Number of columns 5
_______________________
Column type frequency:
factor 1
numeric 4
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
Species 0 1 FALSE 3 set: 50, ver: 50, vir: 50

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
Sepal.Length 0 1 5.84 0.83 4.3 5.1 5.80 6.4 7.9 ▆▇▇▅▂
Sepal.Width 0 1 3.06 0.44 2.0 2.8 3.00 3.3 4.4 ▁▆▇▂▁
Petal.Length 0 1 3.76 1.77 1.0 1.6 4.35 5.1 6.9 ▇▁▆▇▂
Petal.Width 0 1 1.20 0.76 0.1 0.3 1.30 1.8 2.5 ▇▁▇▅▃

3 tidymodels 도구

tidymodels 팩키지에 tidyverse 체계에서 예측모형을 구현하는 다양한 팩키지가 일괄적으로 포함되어 있다.

library(tidymodels)

4 전처리와 피쳐 공학

기계학습 예측모형을 개발할 때 훈련/시험 데이터로 쪼개고 피쳐 공학(Feature Engineering)을 통해 Basetable을 다양한 기계학습 알고리즘에 적합시키도록 사전 정지작업을 수행한다.

가장 먼저 initial_split() 함수로 훈련/시험 데이터를 7:3 비율로 쪼갠다. 특히 층화(strata)를 두어 7:3 비율이 정확히 유지되도록 지정한다. training(), testing() 함수로 basetable 모형 데이터를 훈련/시험 데이터로 분리시킨다.

iris_tbl <- iris %>% as_tibble %>% 
  janitor::clean_names()

iris_split <- initial_split(iris_tbl, prop = 0.7, strata = species)
iris_split
<Analysis/Assess/Total>
<105/45/150>
train_iris <- training(iris_split)
test_iris  <- testing(iris_split)

다음으로 recipes 팩키지를 사용해서 피쳐 공학 기법을 적용하여 다양한 기계학습 알고리즘을 적용시키도록 준비시킨다. ggplot 사용법에 익숙하신 분은 recipes 팩키지의 다양한 함수 사용법을 어려움 없이 사용할 수 있을 것이다.

recipe() 함수에 모형 공식을 작성하고, step_* 함수에 다양한 피처 공학 기법을 적용시켜 준비시킨다. 만약 피처공학된 결과를 보고자 한다면 iris_recipe %>% prep() %>% bake(new_data = train_iris) 명령어로 레서피에 정의된 결과를 파악할 수 있다.

iris_recipe <- recipe(species ~., data = train_iris) %>%
  step_center(all_predictors(), -all_outcomes()) %>%
  step_scale(all_predictors(), -all_outcomes()) %>% 
  step_zv(all_predictors()) %>% 
  prep()

summary(iris_recipe)
# A tibble: 5 x 4
  variable     type    role      source  
  <chr>        <chr>   <chr>     <chr>   
1 sepal_length numeric predictor original
2 sepal_width  numeric predictor original
3 petal_length numeric predictor original
4 petal_width  numeric predictor original
5 species      nominal outcome   original

iris_recipe 요리법을 그대로 시험 데이터에 적용시켜 나중에 예측모형 성능평가에 사용한다. 이때 사용되는 동사가 bake()로 구워둔다.

iris_testdata <- iris_recipe %>% 
  prep() %>% 
  bake(new_data = test_iris)

glimpse(iris_testdata)
Rows: 45
Columns: 5
$ sepal_length <dbl> -0.9058716, -1.1398606, -0.2039047, -0.5548881, -0.905871…
$ sepal_width  <dbl> 1.02642375, -0.13744767, 3.12139232, 0.79364947, 1.491972…
$ petal_length <dbl> -1.323235432, -1.323235432, -1.268056830, -1.157699626, -…
$ petal_width  <dbl> -1.3178584, -1.3178584, -1.0575101, -1.3178584, -1.057510…
$ species      <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa, s…

시험 데이터에 대한 전처리 및 피처 공학 적용이 완료되었다면, 다음 단계로 훈련 데이터에도 동일한 작업을 수행하는데 juice() 동사를 사용해서 해당 작업을 수행시키는데 이때 사용되는 요리법도 iris_recipe가 된다.

prep(), bake(), juice() 동사로 중간중간 결과물을 파악한다.

juice(iris_recipe) %>% 
  head()
# A tibble: 6 x 5
  sepal_length sepal_width petal_length petal_width species
         <dbl>       <dbl>        <dbl>       <dbl> <fct>  
1       -1.37       0.328         -1.38       -1.32 setosa 
2       -1.49       0.0953        -1.27       -1.32 setosa 
3       -1.02       1.26          -1.32       -1.32 setosa 
4       -0.555      1.96          -1.16       -1.06 setosa 
5       -1.49       0.794         -1.32       -1.19 setosa 
6       -1.02       0.794         -1.27       -1.32 setosa 

5 모형지정

Random Forest 모형의 경우 ranger, randomForest 팩키지가 사실의 표준으로 학계와 산업계를 대표하고 있는데 인터페이스가 서로 달라 동일한 알고리즘임에도 불구하고 구문을 별도 작성해야 하는 어려움이 있는데 parsnip이 이를 표준화했다. rand_forest()으로 모형을 정의하고 set_engine()에 팩키지명을 지정한다.

iris_ranger <- rand_forest(trees = 100) %>%
  set_mode("classification") %>% 
  set_engine("ranger") # `ranger` 팩키지
  # set_engine("randomForest") %>% # `randomForest` 팩키지

6 예측모형 작업흐름

목적은 동일하지만 데이터 형태와 예측이냐 분류냐, 적용하고자 하는 예측모형에 따라 차이가 나더라도 workflows를 사용하면 앞선 예측모형 적용과정을 매우 단순화할 수 있다.

library(workflows)

iris_workflow <- 
  workflow() %>% 
  add_model(iris_ranger) %>% 
  add_recipe(iris_recipe)

7 모형 적합

recipes 단계에서 모형공식은 지정했기 때문에 fit() 함수 내부에 이를 중복해서 지정할 필요는 없다.

iris_fit <- iris_workflow %>% 
  fit(data = train_iris)

iris_fit
══ Workflow [trained] ══════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: rand_forest()

── Preprocessor ────────────────────────────────────────────────────────────────
3 Recipe Steps

● step_center()
● step_scale()
● step_zv()

── Model ───────────────────────────────────────────────────────────────────────
Ranger result

Call:
 ranger::ranger(x = maybe_data_frame(x), y = y, num.trees = ~100,      num.threads = 1, verbose = FALSE, seed = sample.int(10^5,          1), probability = TRUE) 

Type:                             Probability estimation 
Number of trees:                  100 
Sample size:                      105 
Number of independent variables:  4 
Mtry:                             2 
Target node size:                 10 
Variable importance mode:         none 
Splitrule:                        gini 
OOB prediction error (Brier s.):  0.03441589 

8 훈련데이터 - 예측

예측모형에 대한 성능을 파악하려면 예측값을 생성되어야 한다. predict() 함수를 사용하게 되면 예측모형에 대한 예측값을 생성시킬 수 있는데 broom 팩키지와 마찬가지로 사용해서 bind_cols() 함수를 사용하게 되면 데이터프레임으로 후속 작업이 가능한 형태로 구현이 가능하다.

iris_pred <- train_iris %>% 
  bind_cols(iris_fit %>% predict(train_iris)) %>% 
  bind_cols(iris_fit %>% predict(train_iris, type = "prob"))

head(iris_pred)
# A tibble: 6 x 9
  sepal_length sepal_width petal_length petal_width species .pred_class
         <dbl>       <dbl>        <dbl>       <dbl> <fct>   <fct>      
1          4.7         3.2          1.3         0.2 setosa  setosa     
2          4.6         3.1          1.5         0.2 setosa  setosa     
3          5           3.6          1.4         0.2 setosa  setosa     
4          5.4         3.9          1.7         0.4 setosa  setosa     
5          4.6         3.4          1.4         0.3 setosa  setosa     
6          5           3.4          1.5         0.2 setosa  setosa     
# … with 3 more variables: .pred_setosa <dbl>, .pred_versicolor <dbl>,
#   .pred_virginica <dbl>

9 모형 성능

yardstic 팩키지 metrics() 함수를 사용해서 예측모형의 성능을 파악할 수 있다. 먼저, yardstick 팩키지 accuracy() 함수로 정확도를 파악한다.

iris_pred %>% 
  accuracy(truth = species, estimate = .pred_class)
# A tibble: 1 x 3
  .metric  .estimator .estimate
  <chr>    <chr>          <dbl>
1 accuracy multiclass     0.981

conf_mat() 함수를 사용해서 혼동행렬(confusion matrix)을 계산할 수 있다.

iris_pred %>% 
  conf_mat(truth = species, estimate = .pred_class)
            Truth
Prediction   setosa versicolor virginica
  setosa         35          0         0
  versicolor      0         33         0
  virginica       0          2        35

metrics() 함수를 사용하게 되면 특정 예측모형 측도를 지정하지 않고도 예측모형 성능에 대한 대략적인 정보를 획득할 수 있다.

iris_pred %>%
  metrics(truth = species, estimate = .pred_class)
# A tibble: 2 x 3
  .metric  .estimator .estimate
  <chr>    <chr>          <dbl>
1 accuracy multiclass     0.981
2 kap      multiclass     0.971

iris 데이터는 3가지 품종을 꽃에 대한 4가지 측정정보를 바탕으로 예측하는 것이라 보통 정확도만을 따지지만, tidyversedplyr, broom을 사용한 경험이 있다면 각 범주별로 ROC, AUC를 계산하는 것도 수월하게 진행할 수 있다.

gain_curve(), roc_curve() 함수를 사용해서 이득(Gain), ROC 곡선도 코드 한줄로 작성이 가능하다.

iris_pred %>%
  # gain_curve(species, .pred_setosa:.pred_virginica) %>%
  roc_curve(species, .pred_setosa:.pred_virginica) %>%
  autoplot()

마지막으로 예측모형 성능평가만을 위해서 추출한 성능평가지표(metrics)만 추출하여 데이터프레임으로 제작하여 성능지표에 대한 마무리를 한다.

iris_pred %>% 
  metrics(species, .pred_setosa:.pred_virginica, estimate = .pred_class)
# A tibble: 4 x 3
  .metric     .estimator .estimate
  <chr>       <chr>          <dbl>
1 accuracy    multiclass    0.981 
2 kap         multiclass    0.971 
3 mn_log_loss multiclass    0.0562
4 roc_auc     hand_till     0.999 

10 시험데이터 - 예측

predict() 함수를 사용하게 되면 예측모형에 대한 예측값을 생성시킬 수 있는데 broom 팩키지와 마찬가지로 사용해서 bind_cols() 함수를 사용하게 되면 데이터프레임으로 후속 작업이 가능한 형태로 구현이 가능하다.

test_pred <- test_iris %>% 
  bind_cols(iris_fit %>% predict(test_iris)) %>% 
  bind_cols(iris_fit %>% predict(test_iris, type = "prob"))

test_pred
# A tibble: 45 x 9
   sepal_length sepal_width petal_length petal_width species .pred_class
          <dbl>       <dbl>        <dbl>       <dbl> <fct>   <fct>      
 1          5.1         3.5          1.4         0.2 setosa  setosa     
 2          4.9         3            1.4         0.2 setosa  setosa     
 3          5.7         4.4          1.5         0.4 setosa  setosa     
 4          5.4         3.4          1.7         0.2 setosa  setosa     
 5          5.1         3.7          1.5         0.4 setosa  setosa     
 6          4.8         3.4          1.9         0.2 setosa  setosa     
 7          5           3            1.6         0.2 setosa  setosa     
 8          5           3.4          1.6         0.4 setosa  setosa     
 9          5.2         3.4          1.4         0.2 setosa  setosa     
10          4.8         3.1          1.6         0.2 setosa  setosa     
# … with 35 more rows, and 3 more variables: .pred_setosa <dbl>,
#   .pred_versicolor <dbl>, .pred_virginica <dbl>

yardstick 팩키지 accuracy() 함수로 시험데이터에 대한 정확도를 파악한다.

test_pred %>% 
  accuracy(truth = species, estimate = .pred_class)
# A tibble: 1 x 3
  .metric  .estimator .estimate
  <chr>    <chr>          <dbl>
1 accuracy multiclass     0.978

conf_mat() 함수를 사용해서 혼동행렬(confusion matrix)을 계산할 수 있다.

test_pred %>% 
  conf_mat(truth = species, estimate = .pred_class)
            Truth
Prediction   setosa versicolor virginica
  setosa         15          0         0
  versicolor      0         15         1
  virginica       0          0        14
 

데이터 과학자 이광춘 저작

kwangchun.lee.7@gmail.com