[공부] 01-3 마켓과 머신러닝

정은·2023년 4월 14일

Chapter 1. 나의 첫 머신러닝

01-3 마켓과 머신러닝


본격적으로 머신러닝을 들어가기 전 머신러닝 모델을 만들고자 한다.

생선 분류 문제

여러가지의 생선들을 프로그램으로 분류한다고 가정한다면, 어떻게 할 것인가.

  • 일반적인 프로그램은 이미 정해진 규칙을 통해 분류할 수 있음.

    A : 30cm 이상이면 도미야.

  • 머신러닝은 도미를 판별할 수 있는 새로운 규칙을 스스로 찾아서 분류할 수 있게 하는 것이다.

✅ 용어 설명

  • 클래스(class) : 머신러닝에서 여러 개의 종류를 뜻함. (도미 vs 빙어 - 2개의 클래스)
  • 분류(classification) : 머신러닝에서 여러 개의 중류 중 하나를 구별해 내는 문제를 말함.
  • 이진 분류(binary classification) : 2개의 클래스 중 하나를 고르는 문제를 말함.

1. 도미 데이터 준비하기

도미 데이터는 총 35개로 구성되어있으며, 생선의 길이무게의 정보가 있다.

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]

✅ 용어 설명

  • 특성 : 데이터를 표현하는 하나의 성질 (ex - 생선의 길이, 생선의 무게)

두 특성을 숫자로 보는 것보다 그래프로 시각화 하는 것이 데이터 분석하는데 더 용이하므로, 시각화 할 수 있는 matplotlib 라이브러리를 추가하여 산점도(scatter plot) 그래프를 그려보고자 한다.

import matplotlib.pyplot as plt

plt.scatter(bream_length, bream_weight) # x축 - 길이, y축 - 무게
plt.xlabel('length')
plt.ylabel('weight')
plt.show() # 그래프 화면에 출력

2. 빙어 데이터 준비하기

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]

빙어 데이터도 도미 데이터와 같이 산점도 그래프를 통해 시각화하고자 한다.
도미 데이터에서 사용한 코드에서 scatter 함수만 추가해주면 된다.

import matplotlib.pyplot as plt

# (x축 - 길이, y축 - 무게)
plt.scatter(bream_length, bream_weight) # 도미 데이터 
plt.scatter(smelt_length, smelt_weight) # 빙어 데이터 추가
plt.xlabel('length')
plt.ylabel('weight')
plt.show() # 그래프 화면에 출력

첫 번째 머신러닝 프로그램

우선, 머신러닝 프로그램을 개발하기 전 훈련하기 위해 데이터를 만들어야 한다.
앞서 작성한 도미 데이터와 빙어 데이터를 하나의 리스트로 합쳐야 한다.

length = bream_length + smelt_length
weight = bream_weight + smelt_weight

>>> length = [25.4, 26.3, 26.5, 29.0, ...]
>>> weight = [242.0, 290.0, 340.0, 363.0, ...]

그 다음 머신러닝 패키지인 scikit-learn은 2차원 리스트 형태의 입력 데이터만 받으므로 2차원 리스트 형태로 바꿔야한다.

2차원 리스트로 reshape 하는 방법 중 가장 간단한 것은 zip() 함수와 리스트 내포 구문을 사용하는 것이다.

zip() 함수는 나열된 리스트 각각에서 하나씩 원소를 꺼내 반환하는 함수이다.
pyhon docs에서 zip 함수의 설명은 아래와 같다.

zip(*iterables, strict=False)
Iterate over several iterables in parallel, producing tuples with an item from each one.

fish_data = [[l, w] for l, w in zip(length, weight)]

>>> fish_data = [[25.4, 242.0], [26.3, 290.0], ...]

해당 구문을 사용하여 데이터를 생성하였다.
그 다음 분류 문제는 정답이 있는 지도학습이므로, 정답 데이터 리스트를 만들어줘야한다.

fish_target = [1] * 35 + [0] * 14 # 1은 도미 (찾으려는 대상) 0은 빙어 (그 외)

>>> fish_target = [1, 1, 1, 1, ...]

이제 머신러닝을 위한 데이터가 마무리 되었으니 머신러닝 알고리즘을 구현해야한다.
우리는 skcikit-learn 의 라이브러리의 K-최근점 이웃 알고리즘(K-Nearest Neightbor; KNN) 을 사용하여 분류 문제를 해결할 것이다.

from sklearn.neighbors import KNeighborsClassifier # 라이브러리 선언

kn = KNeighborsClassifier() # KNN 모델의 객체 생성

머신러닝 모델 객체를 생성하였으니 앞서 훈련데이터인 (생선 데이터, 생선 정답 데이터)를 사용하여 도미를 찾기 위한 기준을 학습시킨다.

kn.fit(fish_data, fish_target) # 주어진 데이터로 KNN 알고리즘을 훈련한다. 

이제 객체 kn이 얼마나 잘 훈련되었는지 평가하고자 한다.
score() 함수를 사용하여 정확도를 확인해보자.

kn.score(fish_data, fish_target)

>>> 1.0 # 정확도

훈련 정확도는 100%며, 도미와 빙어를 완벽하게 분류함을 알 수 있다.

✅ 용어 설명

  • K-최근접 이웃 알고리즘(K-Nearest Neightbor; KNN) : 가장 간단한 머신러닝 알고리즘 중 하나이다. 사실 어떤 규칙을 찾기보다는 전체 데이터를 메모리에 가지고 있는 것이 전부이다.
  • 모델 : 머신러닝 프로그램에서는 알고리즘이 구현된 객체를 모델이라고 부름. 종종 알고리즘 자체를 모델이라고 부르기도 함.
  • 훈련 : 머신러닝 알고리즘이 데이터에서 규칙을 찾는 과정을 말함. 사이킷런에서는 fit()메서드가 하는 역할임.
  • 정확도 : 정확한 답을 몇 개 맞혔는지를 백분율로 나타낸 값이다. 사이킷런에서는 0 ~ 1사이의 값으로 출력한다.

    정확도 = (정확히 맞힌 개수) / (전체 데이터 개수)

1. K-최근접 이웃 알고리즘

앞서 훈련한 모델을 사용하여 새로운 데이터의 정답을 찾아보자.
predict() 함수는 새로운 데이터의 정답을 예측한다. (도미 - 1, 빙어 - 0)

kn.predict([[30, 600]])

>>> array([1]) # 즉, 도미라는 뜻

K-최근접 이웃 알고리즘은 데이터를 모두 가지고 있어야 하며, 새로운 데이터에 대해 예측할 때는 가장 가까운 직선거리에 어떤 데이터가 있는지 살피기만 하면 된다.
단점은 KNN 알고리즘의 이런 특징 때문에 데이터가 아주 많은 경우 사용하기 어렵다.
이유는 데이터가 크기 때문에 메모리가 많이 필요하고 계산하는데 시간이 많이 든다.

KNN은 주위에 몇개를 선택하는가에 따라 정확도가 달라진다.
KNeightborsClassifier클래스의 기본값은 5이다.
즉, 근처 5개를 참고하여 정답을 구한다는 뜻이다.

그럼, 만약 참고 데이터를 49개 전체로 하면 어떻게 될까?

kn49 = KNeighborsClassifier(n_neighbors=49) # 참고 데이터를 49개로 한 kn49 모델

kn49.fit(fish_data, fish_target) # 훈련
kn49.score(fish_data, fish_target) # 평가

>>> 0.7142857142857143

print(35 / 49)
>>> 0.7142857142857143

fish data 49개 중 35개의 데이터인 다수 데이터가 도미이므로 어떤 데이터를 넣어도 무조건 도미로 예측한다.
그러므로, 너무 많은 K값을 쓰는 것은 좋지 않다.

2. scikit-learn의 KNeighborsClassifier 정리

  • KNeighborsClassifier()는 k-최근접 이웃 분류 모델을 만드는 사이킷런 클래스이다.
    n_neighbors 매개변수로 이웃의 개수를 지정하고 기본값은 5이다.

  • p 매개변수로 거리를 재는 방법을 지정한다.
    p가 1일 경우 맨하튼 거리를 사용하고, 2일 경우 유클리디안 거리를 사용한다.
    기본 값은 2이다.

  • fit()은 사이킷런 모델을 훈련할 때 사용하는 메서드이다. 처음 두 매개변수로 훈련에 사용할 특성과 정답 데이터를 전달한다.

  • predict()은 사이킷런 모델을 훈련하고 예측할 때 사용하는 메서드이다. 특성 데이터만 매개변수로 받는다.

  • score()은 훈련된 사이킷런 모델의 성능을 측정한다. 처음 두 매개변수로 특성과 정답 데이터를 전달한다. 이 메서드는 먼저 predict() 메서드로 예측을 수행한 다음 분류 모델일 경우 정답과 비교하여 올바르게 예측한 개수의 비율을 반환한다.

3. 전체 소스 코드

# 마켓과 머신러닝
# 생선 분류 문제

# 도미 데이터 준비하기
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]

import matplotlib.pyplot as plt

plt.scatter(bream_length, bream_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

# 빙어 데이터 준비하기
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]

plt.scatter(bream_length, bream_weight)
plt.scatter(smelt_length, smelt_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

# 첫 번째 머신러닝 프로그램
length = bream_length+smelt_length
weight = bream_weight+smelt_weight

fish_data = [[l, w] for l, w in zip(length, weight)]
fish_target = [1]*35 + [0]*14

from sklearn.neighbors import KNeighborsClassifier

kn = KNeighborsClassifier()
kn.fit(fish_data, fish_target)
kn.score(fish_data, fish_target)

# KNN 알고리즘
# 새로운 데이터 확인 [30, 600]
plt.scatter(bream_length, bream_weight)
plt.scatter(smelt_length, smelt_weight)
plt.scatter(30, 600, marker='^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

kn.predict([[30, 600]])

# k = 49
kn49 = KNeighborsClassifier(n_neighbors=49)
kn49.fit(fish_data, fish_target)
kn49.score(fish_data, fish_target)

확인문제 🖥️

4. n_neighbors의 기본값인 5부터 49까지 바꾸어 가며 점수가 1.0 아래로 내려가기 시작하는 이웃의 개수를 찾아라.

소스코드

kn = KNeighborsClassifier()
kn.fit(fish_data, fish_target)

for n in range(5, 50):
	# k-최근접 이웃 개수 설정
    # kn.n_neighbors = __ # 이 라인을 완성해야 됌
    kn.n_neighbors = n
    
    # 점수 계산
    # score = kn.score(__, __) # 이 라인을 완성해야 됌
    score = kn.score(fish_data, fish_target)
    
    # 100% 정확도에 미치지 못하는 이웃 개수 출력
    if score < 1:
    	print(n, score)
        break
profile
정니의 이런거 저런거 기록 일지 😛

2개의 댓글

comment-user-thumbnail
2023년 4월 17일

소통해요

1개의 답글