분류 알고리즘 - KNN

k-Nearest Neighbors(kNN)

알고리즘 분류모형을 실습하기 위해 기본 데이테 셋을 생성한다.

 train_df = data.frame(x1 = c(8, 2, 7, 7, 3, 1), 
                              x2 = c(5, 3, 10, 3, 8, 1), 
                              y=factor(c(1, 2, 3, 1, 3, 2)))

x1은 단맛, x2는 아삭거림이라 생각하고 과일:1, 단백질:2, 채소:3 으로 분류한다.

생성된 분류 모델을 기반으로 새로입력할 데이터 셋을 만든다.
새로운 항목 - 토마토(6,4), 땅콩(3,8), 사과(10,9)
test_df = data.frame(x1=c(6,3,10), x2=c(4,8,9))
test_df
##   x1 x2
## 1  6  4
## 2  3  8
## 3 10  9

KNN분류를 하기 위해서는 class패키지를 다운받아야 한다. ##### install.packages(‘class’)

library(class)
형식) knn(훈련데이터(y제외), 검정데이터, 훈련데이터 y변수, k)

KNN 분류 알고리즘은 최적의 K값(몇개씩 군집을 만들것인가)를 찾는것이 중요한 과제이다. 일반적으로 K는 sqrt(nrow(train)). 즉 훈련데이터의 행 수의 루트 값을 사용한다.

k <- sqrt(nrow(train_df))
knn(train_df[, -3], test_df, train_df$y, k=3)
## [1] 1 3 1
## Levels: 1 2 3
k는 2.4494가 나오지만 홀수로 올림하여 사용!

분류모형 실습

실습을 하기 위해 유방암 진단 결과 데이터 셋을 활용하려 한다.

wdbc <- read.csv('C:/NCS/Rwork_II/data/wdbc_data.csv', stringsAsFactors = FALSE)
str(wdbc)
## 'data.frame':    569 obs. of  32 variables:
##  $ id               : int  87139402 8910251 905520 868871 9012568 906539 925291 87880 862989 89827 ...
##  $ diagnosis        : chr  "B" "B" "B" "B" ...
##  $ radius_mean      : num  12.3 10.6 11 11.3 15.2 ...
##  $ texture_mean     : num  12.4 18.9 16.8 13.4 13.2 ...
##  $ perimeter_mean   : num  78.8 69.3 70.9 73 97.7 ...
##  $ area_mean        : num  464 346 373 385 712 ...
##  $ smoothness_mean  : num  0.1028 0.0969 0.1077 0.1164 0.0796 ...
##  $ compactness_mean : num  0.0698 0.1147 0.078 0.1136 0.0693 ...
##  $ concavity_mean   : num  0.0399 0.0639 0.0305 0.0464 0.0339 ...
##  $ points_mean      : num  0.037 0.0264 0.0248 0.048 0.0266 ...
##  $ symmetry_mean    : num  0.196 0.192 0.171 0.177 0.172 ...
##  $ dimension_mean   : num  0.0595 0.0649 0.0634 0.0607 0.0554 ...
##  $ radius_se        : num  0.236 0.451 0.197 0.338 0.178 ...
##  $ texture_se       : num  0.666 1.197 1.387 1.343 0.412 ...
##  $ perimeter_se     : num  1.67 3.43 1.34 1.85 1.34 ...
##  $ area_se          : num  17.4 27.1 13.5 26.3 17.7 ...
##  $ smoothness_se    : num  0.00805 0.00747 0.00516 0.01127 0.00501 ...
##  $ compactness_se   : num  0.0118 0.03581 0.00936 0.03498 0.01485 ...
##  $ concavity_se     : num  0.0168 0.0335 0.0106 0.0219 0.0155 ...
##  $ points_se        : num  0.01241 0.01365 0.00748 0.01965 0.00915 ...
##  $ symmetry_se      : num  0.0192 0.035 0.0172 0.0158 0.0165 ...
##  $ dimension_se     : num  0.00225 0.00332 0.0022 0.00344 0.00177 ...
##  $ radius_worst     : num  13.5 11.9 12.4 11.9 16.2 ...
##  $ texture_worst    : num  15.6 22.9 26.4 15.8 15.7 ...
##  $ perimeter_worst  : num  87 78.3 79.9 76.5 104.5 ...
##  $ area_worst       : num  549 425 471 434 819 ...
##  $ smoothness_worst : num  0.139 0.121 0.137 0.137 0.113 ...
##  $ compactness_worst: num  0.127 0.252 0.148 0.182 0.174 ...
##  $ concavity_worst  : num  0.1242 0.1916 0.1067 0.0867 0.1362 ...
##  $ points_worst     : num  0.0939 0.0793 0.0743 0.0861 0.0818 ...
##  $ symmetry_worst   : num  0.283 0.294 0.3 0.21 0.249 ...
##  $ dimension_worst  : num  0.0677 0.0759 0.0788 0.0678 0.0677 ...

가져온 wdbc데이터 셋을 대상으로 전처리를 해주어야 한다:

  • 전처리 : 불필요한 ID 컬럼을 제거
wdbc <- wdbc[-1]
  • *목표변수 암 진단 결과(y변수)를 factor형으로 변환
wdbc$diagnosis <- factor(wdbc$diagnosis, levels = c("B", "M"))
prop.table(table(wdbc$diagnosis)) * 100
## 
##        B        M 
## 62.74165 37.25835
  • *wdbc의 데이터는 데이터의 범주가 제각각이다. 변수들의 영향도를 더 정확히 보기 위해서 변수 정규화를 한다.
normalize <- function(x){
                return ((x - min(x)) / (max(x) - min(x)))
}
wdbc_x <- as.data.frame(lapply(wdbc[2:31], normalize))
  • *모델을 만들기 전 랜덤하게 train/test 데이터를 나눠준다.
set.seed(415) # 시드값 적용 - 동일한 랜덤값 제공 
idx = sample(1:nrow(wdbc_x), 0.7*nrow(wdbc_x))
wdbc_train = wdbc_x[idx, ] # 훈련 데이터 
wdbc_test = wdbc_x[-idx, ] # 검정 데이터 
dim(wdbc_train) # [1] 398  30
## [1] 398  30
dim(wdbc_test) # [1] 171  30
## [1] 171  30
# 원본 데이터에서 y변수가 포함된 칼럼값 저장 - 분류모델 결과 확인용  
wdbc_train_y <- wdbc[idx, 1] # 훈련 데이터의 diagnosis 칼럼 
wdbc_test_y <- wdbc[-idx, 1] # 검정 데이터의 diagnosis 칼럼 

KNN분류모델 생성

# k값 구하기 - 훈련데이터의 제곱근
dim(wdbc_train) # [1] 398  30
## [1] 398  30
k = sqrt(398)
k # 19.94994 -> k = 19(홀수 지정 )
## [1] 19.94994
wdbc_pred <- knn(wdbc_train, wdbc_test, wdbc_train_y, k=19)
wdbc_pred # 분류예측모델 
##   [1] B M M B B B M B M M B B B B B B B M B M B B M B B B B B M B B B B B M
##  [36] M B B B B B B B B B B B B B B B B M B B B B B B M B M B B B B B B B B
##  [71] B B B M M B M M M M M M M M M B M B M B B B M B B B M B B B B B B B M
## [106] B B B B B B M M B M B B M B M B B M B B M B B M M B B M M B B B M M B
## [141] B M M B B M B M M M B M B B M B B B M B B B M B B M B B B B M
## Levels: B M

분류모델 성능 평가

# 검정데이터의 y변수와 분류모델 예측치와 비교 평가  
table(wdbc_pred, wdbc_test_y) # 행 : 분류예측치, 열 : 원본 data 
##          wdbc_test_y
## wdbc_pred   B   M
##         B 110   6
##         M   1  54
# 분류정확도 
(110+54)/ nrow(wdbc_test)  
## [1] 0.9590643

KNN은 분류를 가장 잘 하는 최적의 K값을 찾는것이 목표이다. 최적의 K값을 찾기 위해 for문 활용

result <- numeric()
k = 5:25
for(i in k ){
    wdbc_pred <- knn(wdbc_train, wdbc_test, wdbc_train_y, k=i)
    t <- table(wdbc_pred, wdbc_test_y)
    result[i-4] <- (t[1,1]+t[2,2])/sum(t)
}

result
##  [1] 0.9473684 0.9532164 0.9532164 0.9649123 0.9590643 0.9532164 0.9590643
##  [8] 0.9590643 0.9590643 0.9532164 0.9532164 0.9590643 0.9532164 0.9590643
## [15] 0.9590643 0.9532164 0.9590643 0.9590643 0.9590643 0.9590643 0.9590643
sort(result, decreasing = T)
##  [1] 0.9649123 0.9590643 0.9590643 0.9590643 0.9590643 0.9590643 0.9590643
##  [8] 0.9590643 0.9590643 0.9590643 0.9590643 0.9590643 0.9590643 0.9532164
## [15] 0.9532164 0.9532164 0.9532164 0.9532164 0.9532164 0.9532164 0.9473684
which(result==max(result)) # 4번째가 가장 높다. 즉 k=8일 때 가장 높은 분류율을 나타낸다.
## [1] 4