안녕하세요. 문범우입니다.
이번 포스팅에서는 분류나 회귀에서 사용되는 KNN(K - Nearest Neighbors) 알고리즘에 대해서 알아보도록 하겠습니다.
1. KNN(K - Nearest Neighbors)
KNN, K-최근접 이웃 알고리즘은 특정공간내에서 입력과 제일 근접한 k개의 요소를 찾아, 더 많이 일치하는 것으로 분류하는 알고리즘입니다.
말로 이해하는 것보다 아래 그림을 통해 이해하는 것이 훨씬 쉬울 것 입니다.
위의 그림과 같은 좌표공간을 예시로 확인해보겠습니다.
위에서 파란색 점으로 되어 있는 그룹을 A그룹이라고 생각하고, 주황색 점으로 되어 있는 그룹을 B라고 하겠습니다.
이때 우리는 별 모양으로 표시된 입력값이 A그룹에 속하는지, B그룹에 속하는지를 알고 싶습니다.
그리고 이럴때 사용되는 KNN 알고리즘은 다음과 같이 적용됩니다.
우선 k값을 정의해줘야 합니다. 해당 k값에 대한 설명은 조금 밑에서 드리도록 하고, 우선 k를 3이라는 값으로 정했다고 생각해보겠습니다.
그럼 우리는 입력값과 가장 근접한 k개의 요소를 찾습니다. k = 3이므로 3개의 요수를 찾아보면 다음 그림과 같습니다.
별 모양의 점을 기준으로 원을 그려서 확인함으로써, 위의 빨간색 원과 같이 파란색 점 하나와 주황색 점 두개가 선택됨을 알 수 있습니다.
그리고 이를 통해 입력값은 주황색 점의 그룹, 즉 B의 그룹과 더 일치하다는 판단을 내릴 수 있습니다.
추가적으로 생각해보면, KNN 알고리즘은 정의한 k 값에 따라서 입력값의 분류 결과가 달라질 수 있습니다.
만약 우리가 k를 3으로 두지 않고 1로 두었다면, 아래 그림과 같은 원이 그려짐으로써 입력값은 A그룹으로 분류될 수 있습니다.
즉 KNN 알고리즘을 사용할 때는 적절한 k값을 찾아 설정해주는 것이 중요합니다.
또한, 위와 같이 2개의 그룹에 대해 분류를 할때 만약 k값을 짝수로 한다면 어떻게 될까요?
만약 k = 4 라고 설정했다면 다음과 같은 결과가 나옵니다.
그림에서 확인할 수 있듯이, 파란색 점 2개, 주황색 점 2개가 됨으로써 입력 값이 어떠한 그룹이라는 결론을 내릴 수 없게 됩니다.
그렇다고 무조건 k를 홀수 값으로 설정해야 하는 것은 아닙니다.
위와 같이 2개의 그룹에 대한 분류를 할때는 무조건 홀수 값을 주어야 겠지만, 만약 입력값을 3개의 그룹에 대해 분류를 할때 k = 3으로 둔다면 각각의 그룹의 요소가 하나씩 포함되어 위와 같이 어떠한 그룹이라는 결론을 내지 못하는 상황이 올 수 있습니다.
즉, KNN알고리즘을 사용할때는 분류될 그룹의 종류등을 고려하여 적절한 k값을 설정해주는 것이 중요합니다.
2. 파이썬으로 구현하기
위에서 알아본 KNN 알고리즘을 파이썬으로 구현해 보았습니다.
* 코드는 jupyter notebook에서 실행하였습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import numpy as np import pandas as pd import matplotlib.pyplot as plt %matplotlib nbagg A_x_list = [0,2,4,1,1,4] A_y_list = [4,1,5,5,2,6] A_x = np.array(A_x_list) A_y = np.array(A_y_list) B_x_list = [7,7,5,7,10,9] B_y_list = [4,0,2,2,3,3] B_x = np.array(B_x_list) B_y = np.array(B_y_list) finding_point = [5,4] plt.figure() plt.scatter(A_x,A_y) plt.scatter(B_x,B_y) plt.scatter(finding_point[0],finding_point[1], marker='*') plt.show() | cs |
먼저 입력값과 기존에 존재하는 A그룹, B그룹을 좌표상으로 나타내보았습니다.
위의 코드를 통해 나오는 그래프는 아래와 같이, 우리가 위에서 살펴봤던 예제 그림과 같습니다.
그리고 실제로 입력값을 KNN 알고리즘에 적용시켜 분류하는 코드는 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | # L리스트에서 c번째 작은 값 찾는 함수 def count_min_value(L,c): temp = L.copy() temp.sort() item = temp[c-1] return L.index(item),item # 입력값이 A그룹인지 B그룹인지 찾는 함수::KNN Algorithm 적용 def finding_AorB(k,x,y): numA = 0 numB = 0 A_xy = [] B_xy = [] # x,y 좌표가 따로 있는 것을 하나의 리스트로 통합 for i in range(len(A_x_list)): A_xy.append([A_x_list[i],A_y_list[i]]) for i in range(len(B_x_list)): B_xy.append([B_x_list[i],B_y_list[i]]) A_distance = [] B_distance = [] # x,y 좌표에 대해 입력값과의 거리 산출 for each in A_xy: dis = ((each[0] - x)**2 + (each[1] - y)**2)**(1/2) A_distance.append(dis) for each in B_xy: dis = ((each[0] - x)**2 + (each[1] - y)**2)**(1/2) B_distance.append(dis) A_result = [] B_result = [] A_min_count = 1 B_min_count = 1 while(numA + numB < k): min_A = 99999 min_B = 99999 _, min_A = count_min_value(A_distance,A_min_count) _, min_B = count_min_value(B_distance,B_min_count) if min_A < min_B: numA += 1 A_min_count += 1 A_result.append(A_xy[A_distance.index(min_A)]) A_distance[A_distance.index(min_A)] = -1 elif min_A > min_B: numB += 1 B_min_count += 1 B_result.append(B_xy[B_distance.index(min_B)]) B_distance[B_distance.index(min_B)] = -1 elif min_A == min_B: numA += 1 numB += 1 A_min_count += 1 B_min_count += 1 A_result.append(A_xy[A_distance.index(min_A)]) A_distance[A_distance.index(min_A)] = -1 B_result.append(B_xy[B_distance.index(min_B)]) B_distance[B_distance.index(min_B)] = -1 if numA > numB: print("RESULT: The point is A") elif numA < numB: print("RESULT, The point is B") elif numA == numB: print("I DON'T KNOW") print("A point is",A_result,"\nB point is",B_result,"\n") | cs |
위의 코드를 통해 k = 1, 2, 3, 4 로 실행 시켜보면 아래와 같은 결과가 나옵니다.
이렇게 하여 KNN 최근접 이웃 알고리즘에 대해서 알아보았습니다.
위에서 구현된 파이썬 코드는 아래 github주소에 있습니다 :)
https://github.com/doorBW/KNN-Algorithm-by-python
오류가 발생하거나 잘못된 점이 있다면 언제든지 피드백 해주시길 바랍니다.
'Algorithm > 알고리즘 이론' 카테고리의 다른 글
알고리즘_시간복잡도 예제 (0) | 2019.02.10 |
---|---|
알고리즘_시간복잡도, 공간복잡도, Big-O 표기법 (1) | 2019.02.09 |
알고리즘 #10_ 그리디 알고리즘(Greedy algorithm): 활동 선택 문제 (1) | 2017.12.16 |
알고리즘 #9_ 최적 이진 검색 트리(BST: Binary Search Tree) (0) | 2017.12.10 |
알고리즘 #8_ 최장 공통 부분 수열(LCS: Longest Common Subsequence) (0) | 2017.12.04 |