-
caret으로 하는 데이터 전처리데이터 2023. 7. 2. 22:22
“More data beats clever algorithms, but better data beats more data.” – Peter Norvig
더 많은 데이터가 더 우수한 알고리즘 보다 좋은 성과를 낸다.
하지만 많은 데이터 보다 우수한 성과를 내는 것은 품질이 더 좋은 데이터이다.데이터 전처리 개요
데이터의 전처리는 여러가지 목적으로 수행된다. 머신러닝 모델로 학습을 하기 위해서 가장 먼저 수행하는 것이 데이터 전처리 과정이다. 머신러닝의 학습 성과를 내기 위해서 데이터의 적절한 전처리는 필수적이다. 전처리를 수행해야하는 종류와 방법 등은 다양하다. 머신러닝 학습을 위한 데이터 전처리를 용이하게 하기 위한 패키지로는 오랜동안 사용해온 caret 패키지와 비교적 최근 발전하고 있는 tidymodel 기반 recipe가 있다. 이번 글에서는 익숙한 caret의 전처리 방법을 설명하고 다음 글에서 tidymodel의 recipe로 하는 전처리 절차를 다루도록 한다. 데이터 전처리는 데이터를 수집, 결합, 필터링, 정제한 후 기계학습을 효과적으로 시키기 위해 Feature Engineering을 수행하는 과정 전체를 일컫는다. 여기서는 데이터의 수집 결합 정제하는 과정보다는 Feature Engineering에 촛점을 맞추어 알아보고자 한다.
Feature Engineering : 피처 공학
기계학습 내용중 가장 번역하기 힘는 단어를 고르라면 Feature Engineering이 아닐까 싶다. 많은 사람들이 특성공학으로 번역하곤 하나, 특성공학이라는 말이 Feature Engineering의 의미를 담지 못하고 있다는 것이 내 생각이다. 차라리 조금 의역하여 “변수공학” 이라고 번역하는 것이 더 적합할 것 같기도 하다. 데이터의 변수들의 특성을 다루고 조작하고 선택하는 작업들이기 때문이다. 이글에서는 “피처 공학” 정도로 번역하기로 한다. 피처 공학에서 다루는 내용은 분산이 없거나(상수), 매우 낮은 변수를 제거하는것, 결측값 대체, 변수들의 척도조정(Scaling)과 중심화(Centering) 및 변환, PCA 등을 활용한 차원 축소 등이 이루어진다. 이 외에도 시간/날짜 데이터를 조정, 추가(예:주말여부)하고, 텍스트/이미지등 데이터에서 피처를 추출하는 작업 등 광범위한 일들이 이루어진다. 최근에는 자동화된 피처 공학의 발전이 이루어지고 있다.
결측값 대체
결측값을 처리할 때는 결측치가 발생하는 원인에 대한 고려가 필요하다. 결측값이 임의결측(Missing at Random, MAR : 결측치가 다른 변수들과 관련성이 없을 때)인 경우 통계량(중위수, 평균 등)을 이용한 결측치 대체법을 사용하고, 그렇지 않은 경우, 알고리즘을 이용한 대체법(회귀분석 등을 사용하여 결측치 값을 예측하거나 근처 값을 결측점에 채워넣는 knn 대체법 등)을 이용하여 결측치를 대체하기도 한다. iris 데이터에는 결측치가 없으므로 임으로 Sepal.Length 변수에 10개의 NA 값을 추가하였다.
set.seed (6439) iris[sample(1:nrow(iris),10),"Sepal.Length"] <-NA # Sepal.Length 변수에 임으로 결측치 10개 생성 iris %>% is.na() %>% colSums() # 컬럼별 결측치 수 확인
Sepal.Length Sepal.Width Petal.Length Petal.Width Species 10 0 0 0 0
아래는 Sepal.Length 변수의 결측치 처리는 중위수로 대체하는 방법과 knn 알고리즘을 이용하여 대체하는 방법의 예다. caret에서는 데이터 전처리를 크게 두가지 방법으로 수행할 수 있다. 단순히 데이터를 전처리만 하는 경우에는 preProcess()함수의 method 파라메터에 적절한 결측 대체의 방법을 지정한 다음 predict()함수로 데이터에 적용시키는 방법을 사용한다. 이렇게 하면 전처리된 데이터가 생성된다. 데이터를 생성해서 확인하는 작업과정을 거치지 않고 기계학습 알로리즘에 직접 전처리 결과를 적용하는 경우에는 train() 함수의 preProcess 파라메터에 적절한 결측 대체 방법을 지정한 후 학습을 진행 할 수 있다.
# preProccess 함수에 "medianImpute" 로 설정 preprocessParams <- preProcess(iris[, 1:4], method = c("medianImpute")) transformed1 <- predict(preprocessParams, iris[,1:4]) # preProcess를 "knnImpute"로 knn 모형을 이용한 결측치제거 preprocessParams <- preProcess(iris[, 1:4], method = c("knnImpute")) transformed2 <- predict(preprocessParams, iris[,1:4]) summary(transformed1) ; summary(transformed2)
Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 1st Qu.:5.200 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 Median :5.800 Median :3.000 Median :4.350 Median :1.300 Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 Max. :7.700 Max. :4.400 Max. :6.900 Max. :2.500
Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :-1.94191 Min. :-2.4258 Min. :-1.5623 Min. :-1.4422 1st Qu.:-0.93685 1st Qu.:-0.5904 1st Qu.:-1.2225 1st Qu.:-1.1799 Median :-0.05743 Median :-0.1315 Median : 0.3354 Median : 0.1321 Mean :-0.01287 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 3rd Qu.: 0.69636 3rd Qu.: 0.5567 3rd Qu.: 0.7602 3rd Qu.: 0.7880 Max. : 2.32957 Max. : 3.0805 Max. : 1.7799 Max. : 1.7064
단순히 Sepal.Length의 결측치를 중위수로 대체한 transformed1과 iris의 모든 수치형 변수를 knn으로 모델링하여 결측치를 처리한 transformed2의 데이터가 상당한 차이가 있음을 알 수 있다. 이것은 knn 알고리즘을 수행하기 위하여 변수들을 표준화(다음장 표준화 참조) 시킨 후 knn으로 결측치 값을 구했기 때문이다. 데이터를 별도로 생성하여 출력하지 않고 바로 학습모형에 입력하는 경우에는 predict() 함수대신 tranin()함수의 preProcess 파라메터를 지정하여 학습을 시키면된다.
데이터의 변환(Tranaform)
기계학습에서 데이터를 변환하는 이유는 다양하다. 숫치의 범위를 정규화(데이터를 0과 1사이로) 시키거나, 분포의 치우침(skew)을 개선시키기 위해 사용한다. 여기서는 다양한 변환에 대해 알아보자.
중심화(Centering), 척도조정(Scaling), 표준화, 정규화(normalizing)
데이터 전처리에서 센터링, 스케일링 및 정규화는 기계 학습 알고리즘을 적용하기 전에 수치형 데이터를 변환하는 데 사용되는 방법이다. 수치형 데이터를 적절하게 변환시키는 것은 모델의 성능과 정확도를 개선하는 데 도움이 된다. 특히 유사도를 평가하는데 거리 개념을 사용하는 knn 등의 알고리즘에서는 데이터의 적절한 처리가 필수적이라고 하겠다.
중심화(Centering)은 각 데이터 포인트에서 변수의 평균값을 빼서 데이터 분포를 제로 평균으로 이동하는 것이다. 이는 데이터의 편향을 제거하고 모델이 더 쉽게 학습할 수 있도록 하기 위해 수행된다. 중심화는 분포의 모양을 변경하지 않고 위치만 변경시키게된다.
data(iris) # centering preprocessParams <- preProcess(iris[, 1:4], method = c("center")) transformed_centering <- predict(preprocessParams, iris[,1:4]) summary(transformed_centering)
Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :-1.54333 Min. :-1.05733 Min. :-2.758 Min. :-1.0993 1st Qu.:-0.74333 1st Qu.:-0.25733 1st Qu.:-2.158 1st Qu.:-0.8993 Median :-0.04333 Median :-0.05733 Median : 0.592 Median : 0.1007 Mean : 0.00000 Mean : 0.00000 Mean : 0.000 Mean : 0.0000 3rd Qu.: 0.55667 3rd Qu.: 0.24267 3rd Qu.: 1.342 3rd Qu.: 0.6007 Max. : 2.05667 Max. : 1.34267 Max. : 3.142 Max. : 1.3007
척도조정(Scaling)은 각 데이터 포인트를 일반적으로 관측값을 표준 편차로 나누는 과정이다. 만일 중심화된 데이터를 표준편차로 척도조정하면 표준화된(z 값을 갖는) 데이터로 변환하게된다.
# scaling preprocessParams <- preProcess(iris[, 1:4], method = c("scale")) transformed_scaling <- predict(preprocessParams, iris[,1:4]) summary(transformed_scaling)
Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :5.193 Min. : 4.589 Min. :0.5665 Min. :0.1312 1st Qu.:6.159 1st Qu.: 6.424 1st Qu.:0.9064 1st Qu.:0.3936 Median :7.004 Median : 6.883 Median :2.4642 Median :1.7055 Mean :7.057 Mean : 7.014 Mean :2.1288 Mean :1.5734 3rd Qu.:7.729 3rd Qu.: 7.571 3rd Qu.:2.8890 3rd Qu.:2.3615 Max. :9.540 Max. :10.095 Max. :3.9087 Max. :3.2798
표준화(standardizing)
데이터를 중심화하고(평균을 빼고) 표준편차로 나누어(scaling) 표준화(z 값)시키는 과정이다.# standardize : centering과 scaling을 모두 수행하여 표준화(z값) 한 과정이다. preprocessParams <- preProcess(iris[, 1:4], method = c("center", "scale")) transformed_standardizing <- predict(preprocessParams, iris[,1:4]) summary(transformed_standardizing)
Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :-1.86378 Min. :-2.4258 Min. :-1.5623 Min. :-1.4422 1st Qu.:-0.89767 1st Qu.:-0.5904 1st Qu.:-1.2225 1st Qu.:-1.1799 Median :-0.05233 Median :-0.1315 Median : 0.3354 Median : 0.1321 Mean : 0.00000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 3rd Qu.: 0.67225 3rd Qu.: 0.5567 3rd Qu.: 0.7602 3rd Qu.: 0.7880 Max. : 2.48370 Max. : 3.0805 Max. : 1.7799 Max. : 1.7064
정규화(Normalizing) 정규화는 데이터가 특정 범위(일반적으로 0과 1 사이)에 속하도록 데이터를 변환하는 과정이다. 이는 관측치에서 최소값을 뺀 값을 변수의 범위값(최대값과 최소값의 차이)으로 나누어 조정하는 것이다. 정규화는 변수의 크기가 중요하지 않지만 변수들간의 상대적 관계가 중요하거나 변수별로 데이터의 분산이 커서 통일된 비교가 곤란할때 유용하다. 정규화 함으로써 값이 큰 변수가 값이 작은 변수를 영향력 면에서 압도하는 것을 방지할 수 있다.
# normalizing preprocessParams <- preProcess(iris[, 1:4], method = c("range")) transformed_normalizing <- predict(preprocessParams, iris[,1:4]) summary(transformed_normalizing)
Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.00000 1st Qu.:0.2222 1st Qu.:0.3333 1st Qu.:0.1017 1st Qu.:0.08333 Median :0.4167 Median :0.4167 Median :0.5678 Median :0.50000 Mean :0.4287 Mean :0.4406 Mean :0.4675 Mean :0.45806 3rd Qu.:0.5833 3rd Qu.:0.5417 3rd Qu.:0.6949 3rd Qu.:0.70833 Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.00000
Box-Cox 변환과 Yeo-Johnson 변환
Box-Cox 변환과 Yeo-Johnson 변환을 수행하는 이유는 데이터 분포의 치우침(Skew)을 개선시켜 정규분포에 더 가깝도록 만들기 위해 사용한다. Box-Cox 변환은 데이터의 값이 양수 인경우에 적용하고, Yeo-Johnson 변환은 데이터 영이나 음수에의 값에도 적용 할 수 있는 차이가 있다.
Box-Cox 변환식은 다음과 같다.
실습하기 위한 데이터는 PimaIndianDiabetes를 이용한다. PimaIndianDiabetes 데이터의 pedigree(혈통)과 age 변수를 살펴보고 Box-Cox 변환을 시켜보자.
library(mlbench) data(PimaIndiansDiabetes) summary(PimaIndiansDiabetes[,c(7,8)])
pedigree age Min. :0.0780 Min. :21.00 1st Qu.:0.2437 1st Qu.:24.00 Median :0.3725 Median :29.00 Mean :0.4719 Mean :33.24 3rd Qu.:0.6262 3rd Qu.:41.00 Max. :2.4200 Max. :81.00
Box-Cox 변환은 아래와 같이 preProcess() 함수의 method 파라메터를 “BoxCox”로 지정하고 predict() 함수수로 실행시키면 된다.
preprocessParams <- preProcess(PimaIndiansDiabetes[, c(7,8)], method = c("BoxCox")) tr_boxcox <- predict(preprocessParams, PimaIndiansDiabetes[,c(7,8)]) summary(tr_boxcox)
pedigree age Min. :-2.5510 Min. :0.8772 1st Qu.:-1.4116 1st Qu.:0.8815 Median :-0.9875 Median :0.8867 Mean :-0.9599 Mean :0.8874 3rd Qu.:-0.4680 3rd Qu.:0.8938 Max. : 0.8838 Max. :0.9019
Yeo-Johonson 변환은 변수의 값의 범위가 0이나 음수를 포함하고 있을 경우 적용하면 된다. 예를 들어 0을 포함하고 있는 임신횟수(preganant) 변수의 변환은 Yeo-Johnson 변환을 적용한다. Yeo-Johnson변환은 preProcess()함수의 method 를 “Yeojohnson”으로 지정하면 된다.
PCA 변환
PCA 변환은 주성분분석(PCA:Primary Component Analysis)을 통해 차원을 축소시켜 효과적으로 기계학습을 수행하기 위해 실시한다. 주성분 분석을 통해 변수들 중 원데이터의 분산을 95% 이상(디폴트) 설명할 수 있는 주성분들만을 뽑아서 적용한다.
아래 예는 iris 데이터를 표준화(Centering과 Scaling)시키고 PCA 분석을 통해 분산의 95% 이상을 설명할 수 있는 컴포턴트만 뽑는 예이다. PCA로 변환된 데이터를 보면, iris 데이터의 팩터형 변수인 Species외에 나머지 숫자형 변수는 제거되고 주성분 분석 수행결과인 주성분1(PC1)과 주성분2(PC2)가 생성된 것을 알 수 있다.data(iris) summary(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 Median :5.800 Median :3.000 Median :4.350 Median :1.300 Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500 Species setosa :50 versicolor:50 virginica :50
preprocessParams <- preProcess(iris, method= c("center", "scale", "pca")) # print(preprocessParams) transformed <- predict(preprocessParams, iris) summary(transformed)
Species PC1 PC2 setosa :50 Min. :-2.7651 Min. :-2.67732 versicolor:50 1st Qu.:-2.0957 1st Qu.:-0.59205 virginica :50 Median : 0.4169 Median :-0.01744 Mean : 0.0000 Mean : 0.00000 3rd Qu.: 1.3385 3rd Qu.: 0.59649 Max. : 3.2996 Max. : 2.64521
참고로 iris의 숫자형 변수를 주성분 분석을 수행하면 주성분이 4개까지 제시되고 주성분2 까지 누적 설명력이 95.8%가 됨을 알 수 있다.
tr_pca <- princomp(iris[,1:4], cor = T) summary(tr_pca)
Importance of components: Comp.1 Comp.2 Comp.3 Comp.4 Standard deviation 1.7083611 0.9560494 0.38308860 0.143926497 Proportion of Variance 0.7296245 0.2285076 0.03668922 0.005178709 Cumulative Proportion 0.7296245 0.9581321 0.99482129 1.000000000
그럼, 원래의 iris 데이터와 PCA 변환된 데이터를 가지고 랜덤포레스트로 품종을 분류하여 정확도의 차이를 비교해보자. 원래 iris 데이터는 Sepal.Length 등 4개의 변수로 Species를 예측하고, PCA로 변홚된 데이터는 PC1, PC2의 두개의 변수로 Species를 예측하게된다.
data(iris) set.seed(6439) # 데이터분할 trainIndex <- createDataPartition(iris$Species, p = 0.7, list = FALSE) trainData <- iris[trainIndex, ] testData <- iris[-trainIndex, ] # 학습을 교차검증 5-Crosss Validation ctrl <- trainControl(method = "cv", number = 5) # randaomForest로 학습 model <- train(Species ~ ., data = trainData, method = "rf", trControl = ctrl) # Evaluate the model performance # confusionMatrix(predictions, testData$Species)
set.seed(6439) # 변환된 데이터 준비 trainData_p <- transformed[trainIndex, ] testData_p <- transformed[-trainIndex, ] # 변환된 데이터로 randomForest 학습 model_pca <- train(Species ~ ., data = trainData_p, method = "rf", trControl = ctrl)
note: only 1 unique complexity parameters in default grid. Truncating the grid to 1 .
# 비교해보자 학습된 결과 비교 bikyo <- resamples(list(original_iris = model, PCA_iris = model_pca)) summary(bikyo) ; dotplot(bikyo)
Call: summary.resamples(object = bikyo) Models: original_iris, PCA_iris Number of resamples: 5 Accuracy Min. 1st Qu. Median Mean 3rd Qu. Max. NA's original_iris 0.9523810 0.9523810 0.952381 0.9714286 1.000000 1.000000 0 PCA_iris 0.9047619 0.9047619 0.952381 0.9333333 0.952381 0.952381 0 Kappa Min. 1st Qu. Median Mean 3rd Qu. Max. NA's original_iris 0.9285714 0.9285714 0.9285714 0.9571429 1.0000000 1.0000000 0 PCA_iris 0.8571429 0.8571429 0.9285714 0.9000000 0.9285714 0.9285714 0
PCA로 변환된 데이터로 학습한 모델(PCA_iris)의 정확도는 평균 93%로 원 데이터로 학습한 97%보다 4%정도 저하되었다. 이는 원데이터의 분산을 95% 설명 가능한 주성분 2개로만 학습을 수행했기 때문이다. 이러한 PCA 변환은 데이터 변수의 수가 많을 많거나 공선성 문제가 발생하는 경우 등에는 매우 유용하게 적용할 수 있는 방법이다.
NZV(Near Zero Variance) 처리방법
Near Zero Variance(역자주:거의 상수)란 변수의 값의 변화가 적어 분산이 0에 가까운 경우를 일컫는 용어이다. 예를 들어, 데이터의 관측치가 1,000개가 있는데 특정 변수의 경우 990개의 값이 값이 0이고 10개만이 1이라면, 그 변수는 서로 다른 관찰을 구분하는 데 기여하지 못하게 된다. 이런 변수를 식별하여 제거할 수 있도록 caret 패키지에서는 nearZeroVar( ) 함수가 준비되어있다.
실습 목적을 위해 iris 데이터에 임의로 NZV 변수를 하나 추가해서 실습을 해보자. iris 데이터에 꽃의 둘레를 측정한 변수가 있다하고 이를 추가하되 분산이 매우 낮은 변수로 만들어서 추가해보자.
# iris에 nzv를 갖는 임의 변수 premiter 변수 생성 perimeter <- rep(4.0, nrow(iris)) perimeter[sample(1:nrow(iris), 5)] <- 3.0 iris_nzv <- cbind(iris, perimeter) # nzv인 perimeter가 포함된 iris_nzv 생성 summary(iris_nzv)
Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 Median :5.800 Median :3.000 Median :4.350 Median :1.300 Mean :5.846 Mean :3.057 Mean :3.758 Mean :1.199 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 Max. :7.700 Max. :4.400 Max. :6.900 Max. :2.500 NA's :10 Species perimeter setosa :50 Min. :3.000 versicolor:50 1st Qu.:4.000 virginica :50 Median :4.000 Mean :3.967 3rd Qu.:4.000 Max. :4.000
# nzv 여부를 확인할 수 있는 caret의 nearZeroVar() 함수 실행하면 식별가능하다. nzv_vars <- nearZeroVar(iris_nzv, saveMetrics = TRUE) # NZV이 포함된 iris_wo_nzv 데이터에서 NZV 인 변수 제거 iris_wo_nzv <- iris_nzv[, -nearZeroVar(iris_nzv)] summary(iris_wo_nzv)
Sepal.Length Sepal.Width Petal.Length Petal.Width Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 Median :5.800 Median :3.000 Median :4.350 Median :1.300 Mean :5.846 Mean :3.057 Mean :3.758 Mean :1.199 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 Max. :7.700 Max. :4.400 Max. :6.900 Max. :2.500 NA's :10 Species setosa :50 versicolor:50 virginica :50
identical(iris, iris_wo_nzv)
[1] TRUE
고의로 NZV로 생성한 perimeter 변수를 iris에 추가한 iris_wo_nzv에서 NZV를 식별하여 제거하기 위하여 nearZeroVar()함수를 이용하였다. iris_wo_nzv에서 NZV변수를 제거하여 원래의 iris 데이터와 비교하면 동일한 데이터임을 확인 할 수 있다.
결측 처림 및 변환의 순서
데이터 전처리 과정은 한번에 모든 것을 수행하기는 쉽지 않다. 따라서 전처리 과정을 여러번에 걸쳐 수행하게 되지만 다음과 같은 사항을 고려하여 전처리 하는것이 데이터의 손실이나 중복작업을 예방 할 수 있다.
Data 전처리 순서
지금까지 caret를 이용한 다양한 전처리 개념과 방법을 알아보았다. 여기서 언급된 내용은 주로 수치형 데이터에 대한 전처리 내용이었으나, 이 외에도 숫치형 데이터를 구간화하여 팩터형으로 변환시키는 문제(예: 나이를 10대, 20대 등 과 같이 범주형으로 변환) 등은 다루지 않았다. 다음회에서는 최근 부상하고 있는 tidymodel 기반 recipe 패키지를 이용한 데이터 전처리 프로세스를 다루기로 한다.
'데이터' 카테고리의 다른 글
k-mean Clustering (2) 2024.01.21 chatGPT-4로 하는 데이터분석 (1) 2024.01.07 caret 패키지로 유방암 진단하기 (0) 2023.06.18 상관분석 (1) 2023.05.21 해모수와 비모수 (0) 2023.05.07