-
caret 패키지로 유방암 진단하기데이터 2023. 6. 18. 21:17
DALL.E가 그린 유방암 상징 유화 caret 패키지로 유방암 분류하기
머신러닝을 수행하는 단위 알고리즘을 설명하는 자료는 많이 있다. 그것이 회귀가 되었든 분류가 되었든 각 알고리즘의 개념과 특성 및 장단점을 설명하고 필요한 패키지의 사용법을 알려주는 튜토리얼 같은 자료 말이다. 그러나 머신러닝 프로젝트에서 진행되는 전체 과정을 개념과 함께 소스코드 및 수행결과의 의미까지를 설명하는 자료는 찾기 어렵다. 이번 글에서는 caret 패키지를 이용하여 머신러닝 분류 프로젝트를 수행한다. 분류를 수행하는 각 알고리즘의 원리를 설명하기 보다는 머신러닝 전체 프로세스를 살펴 볼 것이다. 데이터의 전처리부터 EDA, 여러 알고리즘을 적용해서 분류를 수행하고 그 결과를 비교 분석한 후 각 알고리즘의 하이퍼라라메터를 튜닝하여 분류의 대표적인 성과지표인 정확도와 Kappa 계수를 계산하는 과정 전체를 다루어 보고자한다. 사용하는 데이터는 Wisconsin Brest Cancer 데이터 셋을 사용하고 적용하는 알고리즘은 caret 패키지를 이용해서 수행한다. 본 내용은 필자가 대학원에서 담당하고 있는 R로 하는 머신러닝 과목의 최종 프로젝트 수행을 위해 교육하는 자료를 재구성 하였다.
library(mlbench) library(caret) library(tidyverse)
1. 데이터 설명
BreastCancer 데이터는 유방암에 관한 환자의 데이터로 총 699개의 사례와 11개의 볂수로 구성된 데이터이다. 종속변수는 Class로 양성(benign)과 악성(malignant) 두개의 레벨로 되어있다. Class 변수는 양성이 458 케이스, 악성이 241 케이스로 구성되어있다. 독립변수는 개인을 식별하기 위한 의미가 없는 Id를 제외하면, 9개의 변수로 구성되어 있고 각 독독립변수들은 순서를 갖는 범주형 변수로 10개의 레벨값을 가지고 있다. 범주형 변수를 정수형 변수로 변환하고, 결측치를 제거하는 기초적인 전처리를 수행하였다.
data(BreastCancer) data <- BreastCancer str(data)
'data.frame': 699 obs. of 11 variables: $ Id : chr "1000025" "1002945" "1015425" "1016277" ... $ Cl.thickness : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 5 5 3 6 4 8 1 2 2 4 ... $ Cell.size : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 1 1 2 ... $ Cell.shape : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 2 1 1 ... $ Marg.adhesion : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 5 1 1 3 8 1 1 1 1 ... $ Epith.c.size : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 7 2 3 2 7 2 2 2 2 ... $ Bare.nuclei : Factor w/ 10 levels "1","2","3","4",..: 1 10 2 4 1 10 10 1 1 1 ... $ Bl.cromatin : Factor w/ 10 levels "1","2","3","4",..: 3 3 3 3 3 9 3 3 1 2 ... $ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 2 1 7 1 7 1 1 1 1 ... $ Mitoses : Factor w/ 9 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 5 1 ... $ Class : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 2 1 1 1 1 ...
# 데이터 전처리 data <- data[,-1] # id 제거 for(i in 1:9) {data[,i] <- as.numeric(as.character(data[,i]))} data <- data[complete.cases(data), ] # 결측지 제거 dim(data) # 결측치 제거한 후 683 by 10
[1] 683 10
table(data$Class) # 종속변수의 불균형
benign malignant 444 239
2. 데이터 분할 및 EDA
데이터는 caret 패키지의 createDataPartition 함수를 이용하여 학습용 데이터 80% 검증용 데이터 20%로 분할하였다. 종속변수의 Class 비율을 살펴보면 양성이 65% 악성이 35%를 차지하고 있어 불균형 상태에 있음을 알수 있다.종속변수인 Class 비율의 불균형 문제를 해결하기 위해 upSample() 함수를 사용하여 학습용 데이터의 종속변수의 균형을 맞추었다. 종속변수 불균형 처리에 관한 자세한 내용은 필자의 블로그 “불균형 데이터의 분류(Imbalance Data Classification)” 편을 참조하면 되겠다.
# 1.2 test_set train_set과 트레인용 데이터셋 분리 set.seed(7) Train_Index <- createDataPartition(data$Class, p =0.80, list=FALSE) train_set <- data[Train_Index, ] test_set <- data[-Train_Index,] balanced_train_set <- upSample(train_set, train_set$Class) table(balanced_train_set$Class)
benign malignant 356 356
3. 데이터 파악을 위한 가시화
독립변수들의 분포를 알아보기 위해 변수별로 히스토그램과 확률밀도 그래프 및 박스프롯을 작성하여 독립변수들의 분포를 확인한다. 독립변수들의 분포를 확인하는 이유는 변수들의 분포를 살펴보고 skew되어 변환이(Box-Cox나 Yeo-Johnson) 필요한 변수가 있는지 살펴보는 과정이다. 여기서는 히스토그램과 확률밀도 그래프로 각 독립변수의 분포를 살펴본다.
# 2-1. histogram each attribute par(mfrow = c(3,3)) for (i in 1:9) {hist(train_set[,i], main = names(train_set)[i])}
# 2-2. Density Pot for each attributes par(mfrow=c(3,3)) for(i in 1:9) {plot(density(train_set[,i]), main=names(train_set)[i])}
일부 독립변수들이 스큐되어 있어, 박스칵스(BoxCox) 변환이 필요함을 알았다. 박스칵스 변환의 주된 용도는 데이터를 정규분포에 가깝게 만들거나 데이터의 분산을 안정화 시키기 위해 사용하는 것으로 정규성을 가정하는 분석법이나 정상성을 요구하는 분석법을 사용하기에 앞서 사용하게 된다. 박스칵스 변환은 caret 패키지의 train함수로 학습시킬때 데이터 전처리 파라메터인 preProc 파라메터를 “BoxCox”로 지정하면 변환할 수 있다.
일반적으로 EDA 과정에서 변수들간의 상관관계를 파악을 위해 상관분석을 수행한다. 상관분석은 통상 변수들간의 상관관계 매트릭스와 corplot 산점도 그래프를 통해 상관관계를 파악한다. 여기서는 상관분석을 위한 코드와 출력결과는 생략하였다.이번에는 종속변수 Class의 레벨(양성, 악성)별로 독립변수들이 어떤 분포를 갖는지 바플롯으로 확인해 보면 아래 그래프와 같다.
# bar plot은 table을 만들어서 그리니, 먼저 table을 만들어야 합니다. par(mfrow =c(3,3)) for(i in 1:9) { barplot(table(train_set$Class, train_set[,i]), main=names(train_set)[i])}
이번에는 Class의 레벨별로 박스플롯을 그려서 전체적인 윤곽을 파악해보자.
변수 종류별로 양성과 악성의 데이터 분포를 한눈에 확인해 볼 수 있다.# 2.2-4 feature density Plot par(mfrow = c(3,3)) scales <- list(x=list(relation = "free"), y=list(relation="free")) featurePlot(x = train_set[,1:9], y = train_set$Class, plot = "box", scales = scales)
4. 모델별 학습
학습을 위한 데이터 전처리, 분할, 변환 등의 과정이 끝났으니, 학습은 종속변수의 불균형을 Upsample로 처리한 balanced_train_set 데이터를 이용하여 학습을 수행한다. 다양한 알고리즘으로 분류 학습을 진행하기 앞서 seed 번호를 지정하여 학습의 재현성을 보장하도록 해야 한다. 학습을 위해 사용하는 caret의 함수는 train함수이다. train함수는 모델 학습에 필요한 다양한 조건을 파라메터로 지정함으로써 다양하게 학습 모델을 생성할 수 있다. train함수의 대표적인 파라메터는 학습시킬 데이터를 지정하는 data, 학습 방법(알고리즘)을 지정하는 method, 학습시 적용할 리샘플링 방법(예:k-cross validation)을 지정하는 trControl, 데이터 전치리 방법을 지정하는 preProc, 결과를 간명하게 지정할 것인지를 지시하는 vervose 등의 파라메터가 있다.
여기서는 KNN, Naive 배이지안, 의사결정나무, SVM(Support Vector Machine), 랜덤포레스트를 대상으로 학습을 수행한다. 또 trainControl 함수를 이용하여 반복적 교차검증 방법을 사용하여 교차검증을 수행하고 분류의 성능평가 지표는 정확도를 사용하였다.trainControl <- trainControl(method = "repeatedcv", number = 10, repeats = 3) metric <- "Accuracy" preprocess_parms <- "BoxCox"
가. KNN
KNN으로 분류를 수행한 결과를 살펴보면 총 712개 샘플에 대해 10-fold 교차 검증을 3회 실시하였으며, knn의 파라메터 k는 5,7,9 로 선정하여 knn을 수행하였음을 알 수 있다. 분류 결과 k가 5일때 정확도가 98.2%로 가장 높았음을 알 수 있다.
# KNN set.seed(7) fit.knn <- train(Class~., data = balanced_train_set, method = "knn", metric = metric, trControl = trainControl, preProc = preprocess_parms) fit.knn
k-Nearest Neighbors 712 samples 9 predictor 2 classes: 'benign', 'malignant' Pre-processing: Box-Cox transformation (9) Resampling: Cross-Validated (10 fold, repeated 3 times) Summary of sample sizes: 641, 640, 640, 640, 641, 641, ... Resampling results across tuning parameters: k Accuracy Kappa 5 0.9822302 0.9644536 7 0.9812911 0.9625753 9 0.9817738 0.9635397 Accuracy was used to select the optimal model using the largest value. The final value used for the model was k = 5.
나. 나이브 베이지안
caret 패키지에서 제공하는 나이브 베이지안 분류 알고리즘도 몇가지가 있다. 여기서는 Naive Bayes 분류알고리즘을 사용하였으며, Naive Bayes 알고리즘은 세개의 파라메터(fL (Laplace Correction), usekernel (Distribution Type), adjust (Bandwidth Adjustment))를 사용한다. 수행결과를 보면 파라메터 설정 값들에 따른 정확도와 kappa 값이 나와있다. 나이브베이지안도 98%의 정확도를 보이고 있다.
# Naive Bayse set.seed(7) fit.nb <- train(Class~., data= balanced_train_set, method ="nb", metric = metric, trControl = trainControl, preProc = preprocess_parms ) fit.nb
Naive Bayes 712 samples 9 predictor 2 classes: 'benign', 'malignant' Pre-processing: Box-Cox transformation (9) Resampling: Cross-Validated (10 fold, repeated 3 times) Summary of sample sizes: 641, 640, 640, 640, 641, 641, ... Resampling results across tuning parameters: usekernel Accuracy Kappa FALSE 0.9808281 0.9616508 TRUE 0.9780373 0.9560673 Tuning parameter 'fL' was held constant at a value of 0 Tuning parameter 'adjust' was held constant at a value of 1 Accuracy was used to select the optimal model using the largest value. The final values used for the model were fL = 0, usekernel = FALSE and adjust = 1.
다. 의사결정 나무
caret 패키지는 다양한 tree 기반 모델들을 포함하고 있으나 우리는 rpart 모델을 사용하였다. cp 파라메터가 작을때 정확도가 높아짐을 보여주고 있다. rpart 모델의 정확도는 95.8%를 보이고 있다. raprt나 randomForest 모델은 변주중요도를 제시해준다. 즉 분류 모델의 성능에 가장 영향을 미치는 변수를 가시화 시켜 보여준다. rpart에서는 Cell.Size Cell.Shape 순으로 영향이 큰것을 알 수 있다.
# rpart set.seed(7) fit.rpart <- train(Class~., data= balanced_train_set, method = "rpart", metric= metric, trControl = trainControl, preProc = preprocess_parms ) fit.rpart
CART 712 samples 9 predictor 2 classes: 'benign', 'malignant' Pre-processing: Box-Cox transformation (9) Resampling: Cross-Validated (10 fold, repeated 3 times) Summary of sample sizes: 641, 640, 640, 640, 641, 641, ... Resampling results across tuning parameters: cp Accuracy Kappa 0.01966292 0.9578283 0.9156358 0.03932584 0.9358728 0.8717539 0.85955056 0.6430257 0.2916787 Accuracy was used to select the optimal model using the largest value. The final value used for the model was cp = 0.01966292.
plot(varImp(fit.rpart))
라. 서포트벡터머신
서포트 벡터머신 중 svmRadial 모델을 사용하여 서포트벡터 모델 학습을 진행하였다.
Cost 파라메터가 0.5일때 98.6%의 정확도를 보이고 있다.# SVM RBF set.seed(7) fit.svm <- train(Class~., data = balanced_train_set, method = "svmRadial", metric = metric, trControl = trainControl, preProc = preprocess_parms ) fit.svm
Support Vector Machines with Radial Basis Function Kernel 712 samples 9 predictor 2 classes: 'benign', 'malignant' Pre-processing: Box-Cox transformation (9) Resampling: Cross-Validated (10 fold, repeated 3 times) Summary of sample sizes: 641, 640, 640, 640, 641, 641, ... Resampling results across tuning parameters: C Accuracy Kappa 0.25 0.9855235 0.9710386 0.50 0.9859865 0.9719646 1.00 0.9859865 0.9719646 Tuning parameter 'sigma' was held constant at a value of 0.1743549 Accuracy was used to select the optimal model using the largest value. The final values used for the model were sigma = 0.1743549 and C = 0.5.
마. 랜덤포레스트
앙상블 모형인 랜덤포레스는 mtry 즉 분할되는 변수의 개수가 5일때, 정확도가 98.45%로 가장 높았다. 랜덤포레스트로 확인한 변수 중요도는 Cell.Shape와 Cell.Size 순이었다. 의사결정 나무 모형인 rapart 모형의 변수 중요도와 순서에 차이가 났으나, 그 이유는 정확히 파악을 못하겠다.
# randomforest set.seed(7) fit.rf <- train(Class~., data = balanced_train_set, method = "rf", metric = metric, trControl = trainControl, preProc = preprocess_parms ) fit.rf
Random Forest 712 samples 9 predictor 2 classes: 'benign', 'malignant' Pre-processing: Box-Cox transformation (9) Resampling: Cross-Validated (10 fold, repeated 3 times) Summary of sample sizes: 641, 640, 640, 640, 641, 641, ... Resampling results across tuning parameters: mtry Accuracy Kappa 2 0.9836322 0.9672563 5 0.9845516 0.9690960 9 0.9798629 0.9597159 Accuracy was used to select the optimal model using the largest value. The final value used for the model was mtry = 5.
plot(varImp(fit.rf))
ggplot(fit.rf) # 파라메터 값별 퍼포먼스 그래프를 제시
5. 모델별 학습결과 비교
각각의 모델의 학습을 학습했으니 종합적으로 비교를 해보자. 각 모델의 학습 결과를 종합 비교해서 분석결과를 제시하는 함수는 resamples 함수 이다.
# 이제 각 모델을 비교 평가해보자. results_basic <- resamples(list(KNN=fit.knn, NB=fit.nb, DT = fit.rpart, SVM=fit.svm, RF = fit.rf)) summary(results_basic)
Call: summary.resamples(object = results_basic) Models: KNN, NB, DT, SVM, RF Number of resamples: 30 Accuracy Min. 1st Qu. Median Mean 3rd Qu. Max. NA's KNN 0.9444444 0.9719288 0.9859155 0.9822302 1.0000000 1 0 NB 0.9444444 0.9715292 0.9859155 0.9808281 0.9861111 1 0 DT 0.8873239 0.9336310 0.9577465 0.9578283 0.9860622 1 0 SVM 0.9577465 0.9755952 0.9859155 0.9859865 1.0000000 1 0 RF 0.9428571 0.9755952 0.9859155 0.9845516 1.0000000 1 0 Kappa Min. 1st Qu. Median Mean 3rd Qu. Max. NA's KNN 0.8888889 0.9438156 0.9718142 0.9644536 1.0000000 1 0 NB 0.8888889 0.9430444 0.9718142 0.9616508 0.9722222 1 0 DT 0.7742448 0.8672619 0.9154762 0.9156358 0.9721202 1 0 SVM 0.9153754 0.9511905 0.9718254 0.9719646 1.0000000 1 0 RF 0.8857143 0.9511905 0.9718254 0.9690960 1.0000000 1 0
지금까지 학습한 5개의 모델을 비교 분석해보면 정확도 평균을 기준으로 비교볼때 서포트벡터머신, 랜덤포레스트, 나이브베이지안 순으로 예측 정확도가 높음을 알 수 있다.
dotplot(results_basic)
dotplot를 통해 정확도와 카파지수를 가시화 해서 확인해 볼 수 도 있다.
6. 파라메터 튜닝
이제 베이스라인 모델을 만들어 정확도를 측정하고 모델별로 비교를 해봤으니, 정확도 지표를 개선하기 위한 파라메터 튜닝을 수행한다. caret 패키지는 디폴트로 파라메터 당 3개의 값을 적용하여 학습을 수행하도록 설계되어 있다. 학습한 모델 중 정확도가 가장 높이 나온 SVM 모델과 랜덤포레스트 모델에 대해서만 파라메터 튜닝을 실시한다. 파라메터의 수가 늘면 파라메터에 적용하는 값들의 경우의 수가 많아져 학습에 소요되는 시간이 과도하게 증가하게 되므로 여기서는 expand.grid 함수를 사용하여 튜닝할 파라메터 값의 경우를 한정하고 이를 train 함수의 tuneGrid 아큐먼트로 지정해서 튜닝을 수행하게된다.
가. SVM 모델의 튜닝
SVM의 모형에서 사용한 파라메터는 sigma와 Cost이다. sigma 파라메터는 0.025, 0.05, 0.1, 0.15 네가지로, cost인 C는 1부터 10까지를 조합하여 40개의 경우에 대해 학습을 진행하여 가장 우수한 성과를 내는 파라멘터 값을 선택하게된다.
# SVM 튜닝 grid_svm <- expand.grid(.sigma = c(0.05, 0.1, 0.15), .C = seq(1,5, by=1)) set.seed(7) fit.svm_tuned <- train(Class~., data=balanced_train_set, method="svmRadial", metric = metric, tuneGrid=grid_svm, preProc=preprocess_parms, trControl = trainControl) fit.svm_tuned
Support Vector Machines with Radial Basis Function Kernel 712 samples 9 predictor 2 classes: 'benign', 'malignant' Pre-processing: Box-Cox transformation (9) Resampling: Cross-Validated (10 fold, repeated 3 times) Summary of sample sizes: 641, 640, 640, 640, 641, 641, ... Resampling results across tuning parameters: sigma C Accuracy Kappa 0.05 1 0.9841149 0.9682215 0.05 2 0.9845844 0.9691603 0.05 3 0.9845844 0.9691603 0.05 4 0.9845844 0.9691603 0.05 5 0.9841149 0.9682193 0.10 1 0.9855103 0.9710122 0.10 2 0.9850473 0.9700863 0.10 3 0.9845778 0.9691452 0.10 4 0.9845778 0.9691452 0.10 5 0.9845711 0.9691339 0.15 1 0.9859865 0.9719646 0.15 2 0.9850473 0.9700863 0.15 3 0.9850341 0.9700598 0.15 4 0.9845711 0.9691339 0.15 5 0.9855036 0.9710001 Accuracy was used to select the optimal model using the largest value. The final values used for the model were sigma = 0.15 and C = 1.
plot(fit.svm_tuned)
SVM 모델의 튜닝결과, sigma가 0.15, cost인 C가 1 일때 정확도 평가지표가 가장 우수하게 나오는 것을 알 수 있다.
나. 랜덤포레스트 튜닝
랜덤포레스트에서는 랜덤으로 선택하게되는 변수의 수를 튜닝해서 정확도의 변화를 살펴본다. 분할에 사용하는 변수의 개수 범위를 2부터 9까지 조정하면서 모델을 학습시켰다.
# 나. randomForest(rf) 튜닝 : 이번에는 rf 튜닝을 해보자. grid_rf <- expand.grid(.mtry = seq(2, 9, by = 1)) set.seed(7) fit.rf_tuned <- train(Class~., data=balanced_train_set, method="rf", metric = metric, tuneGrid=grid_rf, preProc=preprocess_parms, trControl = trainControl) fit.rf_tuned
Random Forest 712 samples 9 predictor 2 classes: 'benign', 'malignant' Pre-processing: Box-Cox transformation (9) Resampling: Cross-Validated (10 fold, repeated 3 times) Summary of sample sizes: 641, 640, 640, 640, 641, 641, ... Resampling results across tuning parameters: mtry Accuracy Kappa 2 0.9850211 0.9700348 3 0.9845516 0.9690960 4 0.9850145 0.9700220 5 0.9845516 0.9690960 6 0.9840886 0.9681701 7 0.9826734 0.9653424 8 0.9808018 0.9615942 9 0.9803191 0.9606297 Accuracy was used to select the optimal model using the largest value. The final value used for the model was mtry = 2.
plot(fit.rf_tuned)
앞에서 학습한 기본모형과 튜닝한 모형 전체를 대상으로 resamples함수를 사용하여 결과를 종합한 결과는 아래에서 보는 바와 같다. 이 데이터로는 큰 차이는 없는 것을 알 수 있었다.
#----------------------------------------------------------------------- # 파라메터 튜닝한것 까지 결과를 한번보자 results_tuned <- resamples(list(KNN=fit.knn, NB=fit.nb, DT = fit.rpart, SVM=fit.svm, RF = fit.rf, SVM_Tuned = fit.svm_tuned, RF_Tuned = fit.rf_tuned)) summary(results_tuned)
Call: summary.resamples(object = results_tuned) Models: KNN, NB, DT, SVM, RF, SVM_Tuned, RF_Tuned Number of resamples: 30 Accuracy Min. 1st Qu. Median Mean 3rd Qu. Max. NA's KNN 0.9444444 0.9719288 0.9859155 0.9822302 1.0000000 1 0 NB 0.9444444 0.9715292 0.9859155 0.9808281 0.9861111 1 0 DT 0.8873239 0.9336310 0.9577465 0.9578283 0.9860622 1 0 SVM 0.9577465 0.9755952 0.9859155 0.9859865 1.0000000 1 0 RF 0.9428571 0.9755952 0.9859155 0.9845516 1.0000000 1 0 SVM_Tuned 0.9577465 0.9755952 0.9859155 0.9859865 1.0000000 1 0 RF_Tuned 0.9428571 0.9722222 0.9859155 0.9850211 1.0000000 1 0 Kappa Min. 1st Qu. Median Mean 3rd Qu. Max. NA's KNN 0.8888889 0.9438156 0.9718142 0.9644536 1.0000000 1 0 NB 0.8888889 0.9430444 0.9718142 0.9616508 0.9722222 1 0 DT 0.7742448 0.8672619 0.9154762 0.9156358 0.9721202 1 0 SVM 0.9153754 0.9511905 0.9718254 0.9719646 1.0000000 1 0 RF 0.8857143 0.9511905 0.9718254 0.9690960 1.0000000 1 0 SVM_Tuned 0.9153754 0.9511905 0.9718254 0.9719646 1.0000000 1 0 RF_Tuned 0.8857143 0.9444444 0.9718142 0.9700348 1.0000000 1 0
dotplot(results_tuned)
지금까지 수행한 것은 데이터로 분류 분석을 수행하는 머신러닝의 전체적인 절차이다. 이번 글에서 생략된 부분은 모형들을 앙상블로 결합하여 성능지표를 개선하는 시도는 생략하였다. 데이터의 특성에 따라 전처리하는 내용은 달라 질 수 있겠으나 전체적인 맥락은 크게 차이가 나지 않을 것으로 보인다.
'데이터' 카테고리의 다른 글
chatGPT-4로 하는 데이터분석 (1) 2024.01.07 caret으로 하는 데이터 전처리 (0) 2023.07.02 상관분석 (1) 2023.05.21 해모수와 비모수 (0) 2023.05.07 Data Stroage의 진화 (0) 2023.04.23