data analysis

[classification] k-nn algorithm

HR대장 2020. 1. 21. 16:08
728x90

안녕하세요. 은행 IT 운영부 이지성과장입니다. ^-^;;
IT분야에서 2019년을 뜨겁게 달군 단어가 있다면 단연 "AI"일 것입니다. 
국가차원에서도 AI 전문인력을 양성하겠다는 비전을 선포한 걸 보니, 요새 AI가 대세는 대세인가봅니다.

 


저는 이 분야의 전공자도 아니며, 전문가도 아닙니다. (한마디로 허접이다...!-_-;)
다만 현재 하고 있는 업무 이외에 개인적 관심이 있는 분야이며, 제가 알고 있는 지식을 정리해서
사우님들과 공유하면 그 어떤 시너지가 나오지 않을까 하는 생각에 연재를 결심하게 되었습니다. (취지는 좋으나...얼마나 갈지..?) 

평소에 코드를 많이 보고 계시는 개발자분들이 많으시기 때문에, 이론적인 부분은 적게, (사실 이론은 나도, 몰라...skip)
실제 실습형 코드 위주로 연재를 할 계획입니다. 서론에서 말씀드렸듯, 저는 이분야의 입문자에 불과하며,

개인적으로 공부를 하면서 정리할 것이고, 오류가 포함되어 있을 수도 있습니다. (사실 습자지 지식이라...ㅋㅋ)
사내에 분명 고수가 있으실 것 같습니다. 어떠한 조언도 환영이며, 댓글로 남겨주십시오. 
저도 한 수 배울 수 있기 때문입니다. (현피 환영입니다ㅋㅋㅋ)

 


연재를 할 개발 언어는 "R"(version 3.5.3)과 "python"(version 3.7)을 사용할 계획입니다.
환경설정과 관련된 내용은 구글링을 통해 해결 또는, 메일 주시면 친절히 답변 드리도록 하겠습니다. 
관련 링크는 아래에 있습니다.

 

[R 설치링크] https://backgomc.tistory.com/34
[python 설치링크] https://wonderbout.tistory.com/22

 

 

연재의 첫번째로, "분류" (classification)이라는 주제를 잡았습니다. 
분류 알고리즘에는 여러가지 알고리즘이 있으나 그 중 가장 단순한 knn(k-nearest neighbor)  
알고리즘에 대해 알아보도록 하겠습니다. 
간단히 말해 분류는 , 컴퓨터에게 데이터를 주고 학습을 시킨 후 분류를 시키는 겁니다. 
분류 결과가 수치가 아닌 0 / 1 (남/녀 , 인지 아닌지.. yes or no) 로 도출되기 때문에, binary classification 이라고도 합니다. 
예를들면, 키와 몸무게 데이터를 주고 남자 or 여자를 분류하라. (성별분류) 
특정 문장에 사용된 단어들을 주고 스팸인지 아닌지 분류하라. (스팸분류, 스팸 인지 아닌지?) 
개인별 IT자격증 수와 근속연수를 주고 우리회사를 퇴사 할지 말지 분류...(읭?? 하하하 -_-;;) 아무튼 이렇게 쓰입니다. 

 

반대 개념으로 셋 이상을 분류하는 것을 multi - class classification 이라고 합니다. (예, 단풍나무 종류의 구분 , 단풍나무는 약 120개 이상의 종류가 있음)

 

분류 중에서 너무나도 유명한 knn 알고리즘에 대해서 설명하면, nearest neighbor에서도 유추하실 수 있듯이,
한마디로 내 주변에 있는 사람, 이웃들을 보면 나를 알수있다! 이겁니다. 
내 주변에 천재들이 많으면 난 천재일 확률이 높고, 내 주변에 양아치들이 많으면 양아치일 확률이... 
주변의 어떤 친구들이 있는지 각자 돌아보시길 바랍니다...(하하-0-;)

 

 

위 이미지는 knn 알고리즘을 설명할 때 사용되는 대표 이미지 입니다.

주황색 세모와 파란색 원으로 데이터를 공간에 표현하였을 때, 예측하려는 데이터 별은 무엇으로 분류할 수 있을까요?

 

1. knn 알고리즘에서는 주변 몇명의 친구를 볼 것인지 알려줘야 합니다.

위 이미지에서는 3명(k=3)의 친구를 보고 있네요.

주변에 가장 가까운 세명의 친구를 보고, 주황색 세모가 2개, 파란색 원이 1개라면 주황색으로 분류를 하는겁니다.

그래서 친구 몇명을 볼 지 정하는 (k값)은 항상 홀수가 되어야 합니다.

짝수 일 경우, 만약 k=4라면 2:2 로 비기는 경우가 발생되기 때문이죠...

k 값을 적절하게 3,5,7,9... 변경해 가면서 분류 정확도가 높은 k값을 찾는 것이 관건이라고 할 수 있습니다.

 

2. 그렇다면, knn  알고리즘에서 친구를 선정하는 방법 즉, 거리는 어떻게 계산하는 걸까요?

가장 심플한 방법은 "유클리드 거리 (Euclidean distance)" 입니다. 중학교 교과과정이라고 하는데요..

(전 배운기억이...쿨럭;)

 

출처: 위키백과

 

좌표 간 계산을 위한 여러가지 기법들이 아래에 있으니 관심이 있으신 분은 아래 내용을 더 구글링 하시기 바랍니다.

- 맨하탄 거리 (Manhattan distance)

- Mahalanobis distance 

- Correlation distance

- Spearman Rank correlation distance

 

이론적인 부부은 이정도로 하고, 
R을 통해 지금부터 바로 실습 들어가 보도록 하겠습니다. (첫 게시글이기도 하니...얼렁뚱땅...흡..-0-;)

 

1. 데이터확인 - R에 기본적으로 탑재되어 있는 iris(붓꽃데이터) data를 사용합니다.

[iris 데이터에 대한 설명 링크] https://ai-times.tistory.com/418

 

#iris data 확인
head(iris)

#결과리턴
#  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#1          5.1         3.5          1.4         0.2  setosa
#2          4.9         3.0          1.4         0.2  setosa
#3          4.7         3.2          1.3         0.2  setosa
#4          4.6         3.1          1.5         0.2  setosa
#5          5.0         3.6          1.4         0.2  setosa
#6          5.4         3.9          1.7         0.4  setosa

#iris data row확인
nrow(iris)

#결과리턴
#[1] 150

 

iris data 열은 sepal length / sepal width / petal length / petal width / species로 이루어져 있고,

총 150 행이 있음을 알 수 있습니다.

 

 

2. 데이터 정규화 - 왜 해야하는지는 별도로 설명하는 시간을 갖도록 하고 이번 시간에는 그냥 하도록 하겠습니다.

 

#정규화함수생성
min_max_normal <- function(x){
     num <- x  - min(x)
     m_n <- max(x) - min(x)
     print(num/m_n)
     #return(num/m_n)
 }

#정규화된 데이터 셋 구성
normal_iris <- as.data.frame(lapply(iris[1:4], min_max_normal)) 

#데이터 셋 확인
head(normal_iris)

#  Sepal.Length Sepal.Width Petal.Length Petal.Width
#1   0.22222222   0.6250000   0.06779661  0.04166667
#2   0.16666667   0.4166667   0.06779661  0.04166667
#3   0.11111111   0.5000000   0.05084746  0.04166667
#4   0.08333333   0.4583333   0.08474576  0.04166667
#5   0.19444444   0.6666667   0.06779661  0.04166667
#6   0.30555556   0.7916667   0.11864407  0.12500000

 

정규화 이후 모든 데이터의 수치가 0~1 사이로 변경된 것을 보실 수 있습니다.

 

 

3. 학습데이터와 예측데이터분리 - random sampling으로 학습 및 예측데이터 분리 (70% 학습 , 30% 예측데이터)

 

#species 열과 결합
final_normal_iris <- data.frame(normal_iris, Species= iris$Species)


#학습, 검정 데이터로 분리
#seed 생성
set.seed(123)

#샘플 키값 저장
idx <-sample(1:nrow(final_normal_iris),0.7*nrow(final_normal_iris))
idx

#  [1]  44 118  61 130 138   7  77 128  79  65 134  64  94 142  14 122  33   6 150 126 116  90  82 127  83  89  68  74  36
# [30]  18 147 108 143 146   3  55  87  25 135  26  16  46  45  40  17  15 113  48  28 114   5 132 137  12  54  20  97  71
# [59] 131  35  60   9  34  24  93  39  69 124  66 112 148  50  56   1  37 106  29 119 111   8 121  47 123  53 145  92 139
# [88]  57 115  11  86  85  95  38  70  80  43 100 104  27  30  75 101  73  23

#7:3비율로 분리
normal_iris_train <- final_normal_iris[idx,]
normal_iris_predict <- final_normal_iris[-idx,]

#분리되 데이터 row 확인
nrow(normal_iris_train)
#[1] 105

nrow(normal_iris_predict)
#[1] 45

학습데이터는 105건, 예측데이터는 45건으로 분리하였습니다.

 

 

4. 모델생성

 

#KNN 라이브러리호출
library(class)

#KNN 모델생성(k=3으로 설정)
#파라메타: train에는 학습 데이터 셋을 입력, test에는 예측 데이터 셋을 입력, cl 에는 학습데이터의 예측값 (y)값을 입력함.
model <- knn(train=normal_iris_train[,-5], 
             test=normal_iris_predict[,-5],
             cl=normal_iris_train$Species, 
             k=3)

#모델확인
summary(model)

#predict data의 30개행에 대한 예측된 y값들이 summary되어 리턴
#    setosa versicolor  virginica 
#        12         18         15

 

분류 예측을 해보니,

setosa = 12

versicolor = 18

virginica = 15

으로 분류 되었음을 알 수 있습니다.

 

 

5. 모델평가

 

#모델평가
library(gmodels)
CrossTable(x = normal_iris_predict$Species, y=model, prop.chisq = F)


#                            | model 
#normal_iris_predict$Species |     setosa | versicolor |  virginica |  Row Total | 
#----------------------------|------------|------------|------------|------------|
#                     setosa |         12 |          0 |          0 |         12 | 
#                            |      1.000 |      0.000 |      0.000 |      0.267 | 
#                            |      1.000 |      0.000 |      0.000 |            | 
#                            |      0.267 |      0.000 |      0.000 |            | 
#----------------------------|------------|------------|------------|------------|
#                 versicolor |          0 |         16 |          1 |         17 | 
#                            |      0.000 |      0.941 |      0.059 |      0.378 | 
#                            |      0.000 |      0.889 |      0.067 |            | 
#                            |      0.000 |      0.356 |      0.022 |            | 
#----------------------------|------------|------------|------------|------------|
#                  virginica |          0 |          2 |         14 |         16 | 
#                            |      0.000 |      0.125 |      0.875 |      0.356 | 
#                            |      0.000 |      0.111 |      0.933 |            | 
#                            |      0.000 |      0.044 |      0.311 |            | 
#----------------------------|------------|------------|------------|------------|
#               Column Total |         12 |         18 |         15 |         45 | 
#                            |      0.267 |      0.400 |      0.333 |            | 
#----------------------------|------------|------------|------------|------------|

 

 

 

파란색 원은 제대로 분류가 된 건입니다.

빨간색 원은 오분류 된 건입니다. (3건)

이정도면... 정확도가 괜찮다고 생각합니다.

 

이 모든 과정에 대한 코드는 제 개인 깃허브에 업로드 해 두었습니다.

[깃허브] https://github.com/jasonlee8318/R-programming/blob/master/iris_knn.R

 

 

뭐 한건 없는데, 하나 연재하기가 엄청 힘드네요...ㅠㅠ 

다잇소에 많은 글들을 올리시는 분들...대단하십니다!

 

 

올 겨울은 눈이 안와서 겨울 같지 않은 겨울 이었던거 같습니다.

즐거운 2020년 2월의 시작 되시길 바랍니다!

 

1월의 마지막 날에, to be continue...

*다음은 linear regression에 대해 연재할 계획입니다. ^0^;;

*개인적으로 공부한 내용을 정리한 것입니다. 따라서, 오류가 있을 수 있습니다.

너그러운 양해 부탁드리며 댓글 남겨 주시면 해당사항은 수정하도록 하겠습니다.

728x90