본문 바로가기

AI/기계학습(Machine Learning)

[기계학습/ML]3. 머신러닝 맛보기 - K-NeighborsClassifier(K-최근접 이웃 분류)

728x90
반응형

문제 정의

  • 생선을 판매하는 앱 마켓인 "물꼬기" 에서는 생선을 판매하기로 했다.
  • 물류 센터 직원이 생선 이름을 외우지 못해 매번 물어봐서 배송 지연이 발생했다.
  • 이러한 문제를 해결하기 위해 Pupbani는 "생선 이름을 자동으로 알려주는 머신러닝"을 만들어야한다.
  • 판매하는 생선은 "도미", "빙어"이다.

어떻게 분류할까?

  • 생선의 특징(길이, 크기 , 무게 등)을 알면 구분할 수 있을 것 같다.
  • 도미를 잘 아는 박팀장이 와서 "길이가 30cm 보다 크면 다 도미야"라고 했다.
if length >= 30 :
	print("도미")
  • 하지만 30cm 보다 큰 생선을 모두 도미라고 할 수 없다.
  • 기존의 프로그램은 누군가 정해준 기준(ex. 30cm 이상은 도미야)대로 일을 한다.
  • 머신러닝은 누구도 알려주지 않는 기준을 찾아 일을 한다.
    • ex. 길이가 30~40cm인 생선은 도미야
    • 머신 러닝은 이 기준을 통해 생선이 "도미"인지 아닌지 판별도 할 수 있다.

머신러닝 만들기

데이터 준비하기1 - 도미

  • 머신러닝이 기준을 스스로 찾으려면 여러 개의 도미 생선을 보고 스스로 학습을 하여 어떤 생선이 도미인지를 구분할 기준을 찾는다.
  • 도미 데이터를 준비한다.
// 생선의 길이
bream_length = [25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7, 31.0, 31.0, 
                31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5, 34.0, 34.0, 34.5, 35.0, 
                35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5, 38.5, 39.5, 41.0, 41.0]
// 생선의 무게
bream_weight = [242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0, 475.0, 500.0, 
                500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0, 
                700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0, 925.0, 975.0, 950.0]
  • bream_length와 bream_weight 같은 특징들을 특성(Feature)라고 부른다.

도미 데이터 시각화하여 살펴 보기

  • 두 특성을 시각화하여 그래프로 표현하여 보면 데이터를 잘 이해 할 수 있고 앞으로 어떤 알고리즘을 적용해야 할지 힌트를 얻을 수 있다.
  • 길이를 X축 , 무게를 Y축으로 하여 산점도(Scatter Plot, 점으로 표시) 그래프를 그리겠다.
  • 파이썬에서 시각화를 하는 대표적인 패키지는 Matplotlib이다.
    • Matplotlib의 scatter() 메서드를 사용하면 산점도 그래프를 그릴 수 있다.
import matplotlib.pyplot as plt
// scatter(x_data,y_data)
plt.scatter(bream_length, bream_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

도미 35마리의 무게, 길이 산점도 그래프

  • 도미 35마리를 2차원 그래프(특성이 2개)에 점으로 나타냈다.
  • 생선의 길이가 길수록 무게가 많이 나간다고 생각하면 자연스럽다.
  • 그래프의 형태가 일직선에 가까운 형태 이다. - 선형(Linear)적

데이터 준비하기2 - 빙어

  • 준비한 빙어 데이터는 14마리 이다.
  • 앞에서 준비한 것 처럼 길이와 무게 데이터로 준비한다.
smelt_length = [9.8, 10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0]
smelt_weight = [6.7, 7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9]

빙어 데이터 시각화하여 살펴 보기

  • Matplotlib은 두개의 산점도를 색으로 구분하여 그려준다.
  • 한 그래프에 도미와 빙어의 데이터를 시각화한다.
plt.scatter(bream_length, bream_weight) // 도미
plt.scatter(smelt_length, smelt_weight) // 빙어
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

도미와 빙어의 길이, 무게 산점도 그래프

  • 빙어도 도미와 비슷하게 길이가 늘어나면 무게도 늘어나지만 늘어나는 정도가 다르다.
    • 무게가 길이에 영향을 덜 받는다.

머신러닝 프로그램 작성 - K-최근접이웃(K-Nearest Neighbors) 알고리즘

  • 먼저 앞에 도미와 빙어의 데이터를 길이, 무게별로 합친다.
length = bream_length+smelt_length
weight = bream_weight+smelt_weight
  • 데이터는 다음과 같은 상태가 된다.

  • 이제 머신러닝을 위한 패키지를 사용해 보자, 사용할 패키지는 사이킷런(scikit-learn)이다.
    • 사이킷런을 사용하려면 데이터를 세로 방향으로 늘어 뜨린 2차원 리스트를 만들어야 한다.

  • 이렇게 만드는 가장 쉬운 방법은 zip() 함수와 List Comprehension 구문을 사용하는 것이다.
    • 각 리스트에서 하나씩 요소를 꺼내어 [길이, 무게] 쌍의 값들을 모아놓은 2차원 리스트를 만들 수 있다.
  • 49개의 (길이,무게) 쌍을 가진 훈련 데이터를 완성 했다.
fish_data = [[l, w] for l, w in zip(length, weight)]
  • 이제 마지막으로 정답 데이터(Targer data)를 준비해야한다.
    • 도미를 1, 빙어를 0으로 생각 하고 데이터를 준비한다.
    • [1] * 35개 , [0] * 14개를 합친 총 49개의 데이터를 준비한다.
    • 정답 데이터는 훈련 데이터와 길이가 같아야 한다. 
      • ex) shape -> (49,1) , (49,2)
fish_target = [1] * 35 + [0] * 14
  • 이제 데이터가 전부 준비 되었으니 모델을 불러와 학습을 진행 한다.
  • 사이킷런 패키지의 K-최근접 이웃 알고리즘을 구현한 클래스인 "KNeighborsClassifier"를 import 한다.
    • KNeighborsClassifier는 sklearn 패키지 아래 neighbors 모듈 아래 있다.
from sklearn.neighbors import KNeighborsClassifier
  • KNeighborsClassifier 클래스의 객체를 만든다.
kn = KNeighborsClassifier()
  • 이 객체에 훈련 데이터와 정답 데이터를 전달하여 도미를 찾기 위한 기준을 학습한다.
    • 이런 과정을 훈련(Training)이라 부른다.
    • 훈련을 하는 메서드는 fit(훈련 데이터, 정답 데이터)이다.
kn.fit(fish_data, fish_target)
  • 훈련을 끝냈으니 얼마나 잘 훈련했는지 평가를 한다.
    • 모델을 평가하는 메서드는 score(훈련 데이터, 정답 데이터)이다.
    • 이 메서드는 0~1 사이의 값을 반환 한다.
print(f"훈련 결과 : {kn.score(fish_data, fish_target) * 100}%")

  • 훈련 결과가 100.0%다 모든 fish_data의 값을 다 맞췄다.
  • 이렇게 score를 통해 나온 값을 정확도(Accuracy)라고 부른다.
  • K-최근접 알고리즘은 데이터의 대한 답을 구할 때 "주위의 다른 데이터를 보고 다수를 차지하는 것을 정답"으로 사용한다.
  • 다음과 같은 경우 K-최근접 알고리즘은 모델은 생선을 무엇으로 판단할까?
    • 길이 : 30cm , 무게 : 600g인 생선
    • 시각화 하면 다음과 같다.
plt.scatter(bream_length, bream_weight)
plt.scatter(smelt_length, smelt_weight)
plt.scatter(30, 600, marker='^') // 길이: 30cm, 무게: 600g 생선
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

  • 그래프를 보면 새로운 생선은 주변에 도미가 많으므로 "도미"로 판단할 것으로 예상된다.
  • 실제 예측을 해보자
    • 예측을 하는 메서드는 predict(예측할 데이터)이다.
    • 예측할 데이터는 훈련 데이터와 같은 형태로 전달해야한다.
    • 그러므로 2차원 리스트 형태로 전달한다. 
print("예측결과 : ",end="")
print("도미") if 1 == kn.predict([[30, 600]]) else print("빙어")

  • 예상과 같이 "도미"로 예측 되었다.
  • K-최근접 이웃 알고리즘을 통해 새로운 데이터를 예측할 때는 가장 가까운 직선 거리의 어떤 데이터가 있는지 살피는 것이 중요하다.
    • 이러한 특징 때문에 데이터가 아주 많은 경우 사용하기 어렵다.
  • 훈련된 모델의 X,Y의 데이터를 확인하면 학습할 때 사용한 데이터와 일치한다.
print(kn._fit_X) // X속성
print(kn._y) // Y속성

  • K-최근접 이웃 알고리즘은 무언가 훈련되는 것이 없다.
  • 새로운 데이터가 등장하면 가지고 있는 데이터에서 가장 가까운 데이터를 참고하여 구분한다.
    • 몇개의 데이터를 참고할까?
    • KNeighborsClassifier클래스의 n_neighbors매개변수를 통해 참고 데이터 개수를 변경할 수 있다.(기본값 = 5)
  • 만약 49개의 데이터를 참고하면 어떻게 될까?
kn49 = KNeighborsClassifier(n_neighbors=49)
kn49.fit(fish_data, fish_target)
print(f"훈련 결과 : {kn49.score(fish_data, fish_target).round(2) * 100}%")

  • kn49 모델은 49개의 생선 데이터 중 도미가 35개이므로 새로운 데이터가 들어오면 항상 도미로 예측할 것 이다.
  • 정확도 = 도미의 개수 / 전체생선 개수
print(f"정확도 : {round(35/49,2)*100}%")

참고 데이터를 49개로 하는 것은 좋지 않다.

5개로 했을 때 정확도가 100%이므로 기본값 5로 하여 도미를 완벽하게 분류한 모델을 사용할 것이다.

하지만 이 방법이 과연 알맞는 방법일까? 다음 장에서 알아보자.

728x90
반응형