cvTools::cvFolds()
cvTools는 교차 검증을 위한 패키지로, cvFolds( ) 함수를 사용해 데이터의 분할fold을 만드는 기능을 제공한다.
cvTools::cvFolds : n개의 관찰을 K겹 교차 검증의 R회 반복으로 분할한다. |
cvTools::cvFolds( n, # 관찰(Observation)의 수 또는 데이터의 크기 K=5, # K겹 교차 검증 R=1, # R회 반복 # 분할 방법을 지정. 기본 방식은 임의(random)다. 연속(consecutive)은 각 K마다 # 연속된 데이터를 검증 데이터로 선택한다. 상호 배치(interleaved)는 연속된 데이터를 서로 다른 # K번째 교차 검증에서 검증 데이터로 사용한다. type=c("random", "consecutive", "interleaved") ) 반환 값은 cvFolds 객체로 n, K, R, subsets(색인의 나열을 컬럼으로 한 행렬), which(각 나열이 어느 분할에 해당하는지를 뜻하는 색인)가 저장되어 있다. |
example(cvFolds)에 있는 내용으로 cvFolds의 동작 방식을 확인해보자. 다음은 10개 데이터를 K=5, R=1, 임의random 방식으로 실행한 결과다.
> install.packages("cvTools") > library(cvTools) > cvFolds(10, K=5, type="random") 5-fold CV: Fold Index 1 5 2 2 3 6 4 1 5 9 1 10 2 4 3 7 4 3 5 8
cvFolds는 각 K마다 검증 데이터로 사용할 값을 반환한다. 위 결과에서 Fold가 1일 때 Index는 5, 10이었다. 따라서 5겹 교차 검증에서 K=1일 때 5번, 10번 데이터를 검증 데이터로 사용하고 나머지를 훈련 데이터로 사용하면 된다. 마찬가지로 K=2일 경우 2번, 4번 데이터를 검증 데이터로 사용한다.
다음으로 cvFolds에서 연속consecutive과 상호 배치interleaved의 차이를 살펴보자.
> cvFolds(10, K=5, type="consecutive") 5-fold CV: Fold Index 1 1 1 2 2 3 2 4 3 5 3 6 4 7 4 8 5 9 5 10 > cvFolds(10, K=5, type="interleaved") 5-fold CV: Fold Index 1 1 2 2 3 3 4 4 5 5 1 6 2 7 3 8 4 9 5 10
위 결과를 보면 consecutive는 K=1일 때 1번, 2번 데이터를 검증 데이터로 선택하고 K=2일 때 3번, 4번을 선택했다. 이처럼 consecutive는 연속된 데이터를 차례로 검증 데이터로 사용한다. 반면 interleaved는 1번 데이터를 K=1일 때, 2번 데이터를 K=2일 때 검증 데이터로 사용하는 방식으로 연속된 데이터를 차례로 서로 다른 K의 검증 데이터로 할당한다.
다음은 아이리스 데이터에 대해 10겹 교차 검증을 3회 반복 수행하기 위해 cvFolds( )를 사용한 예다. cvFolds( ) 실행 전에 호출한 set.seed( )는 난수를 생성하는 초깃값seed을 지정하기 위해 사용했다. cvFolds( )는 난수를 사용하여 데이터를 분리하므로 매 호출 시마다 서로 다른 분할을 결과로 내놓는다. 하지만 seed를 지정해주면 매번 같은 folds를 결과로 내놓게 되어 교차 검증을 수회 반복하더라도 같은 분할을 사용해 안정적으로 모델을 개선할 수 있다.
> set.seed(719) > (cv <- cvFolds(NROW(iris), K=10, R=3)) Repeated 10-fold CV with 3 replications: Fold 1 2 3 1 92 4 86 2 52 3 144 3 17 75 5 4 13 98 61 5 61 30 16 6 9 129 148 7 8 121 49 8 31 89 37 9 136 140 82 10 37 102 141 1 90 51 78 2 119 80 132 3 116 70 97 4 11 29 93 5 39 72 45 6 94 125 114 7 68 84 25 8 75 123 69 9 131 73 87 10 100 132 63 ... 6 35 137 139 7 73 133 50 8 133 64 7 9 111 74 134 10 66 27 138
위 결과에서 Fold는 K겹 교차 검증의 K를 의미하고 컬럼 1, 2, 3은 반복 R을 의미한다. 따라서 1행 1열의 92는 K=1, R=1일 때 검증 데이터로 아이리스의 92번째 데이터를 사용하라는 의미다. 1행 2열은 4다. 이는 K=1, R=2일 때 검증 데이터로 4번째 데이터를 사용하라는 의미다.
위 표의 Fold에 해당하는 부분은 cv$which에, 실제 선택할 행을 저장한 부분은 cv$subset에 다음과 같이 저장되어 있다.
> head(cv$which, 20) [1] 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 > head(cv$subset) [,1] [,2] [,3] [1,] 92 4 86 [2,] 52 3 144 [3,] 17 75 5 [4,] 13 98 61 [5,] 61 30 16 [6,] 9 129 148
따라서 첫 번째 반복의 K=1에서 검증 데이터로 사용해야 할 행의 번호는 다음과 같이 구할 수 있다. 아래 코드에서 which( ) 함수는 주어진 조건을 만족하는 행의 번호를 얻기 위해 사용했다.
> (validation_idx <- cv$subset[which(cv$which == 1), 1])
[1] 92 90 14 105 85 95 128 56 121 112 144 5 65 146 4
첫 번째 반복 R=1의 K=1에서 훈련, 검증 데이터는 다음과 같이 구한다.
> train <- iris[-validation_idx, ] > validation <- iris[validation_idx, ]
이를 사용해 K겹 교차 검증을 반복하는 전체 코드의 모습을 그려보면 다음과 같다.
> library(foreach) > set.seed(719) > R = 3 > K = 10 > cv <- cvFolds(NROW(iris), K=K, R=R) > foreach(r=1:R) %do% { + foreach(k=1:K, .combine=c) %do% { + validation_idx <- cv$subsets[which(cv$which == k), r] + train <- iris[-validation_idx, ] + validation <- iris[validation_idx, ] + # 데이터 전처리 + + # 모델 훈련 + + # 예측 + + # 성능 평가 + return(성능 값) + } + } > # foreach의 반환 값으로부터 성능이 가장 뛰어난 모델링 방법 식별 > # 아이리스 데이터 전체에 대해 해당 방법으로 모델 생성
코드에서 주목할 만한 점은 for 대신 foreach를 사용한 점이다. foreach( )는 값을 반환할 수 있어 모델을 평가한 결과를 한 번에 모으는 데 유용하다. 또, foreach( ) 사용 시 내부의 foreach( )에서는 .combine에 c를 지정하여 결과가 리스트가 아닌 벡터로 되게 했다. 이렇게 하면 결과가 리스트의 리스트가 아니라 벡터의 리스트가 되어 조작이 용이하다.