* 교재: 혼자 공부하는 머신러닝+딥러닝 (hanbit.co.kr)
* 문제: k-최근접 이웃 회귀를 사용하여 농어의 무게를 예측한다.
농어의 길이와 무게를 통해 농어의 무게를 예측하는 k-최근접 이웃 회귀 모델을 만든다.
* 문제 해결 과정
1. 농어 데이터 준비하기
2. 훈련 세트와 테스트 세트 만들기
3. KNeighborsRegressor 클래스를 임포트 한 후 객체 만들기
4. 모델 훈련 및 평가하기
5. 과대적합과 과소적합
1) 과소적합 경험하기
2) 과소적합 해결하기
1. 농어 데이터 준비하기
농어 56마리의 길이와 무게 리스트를 준비해 보자.
import numpy as np
perch_length = np.array([8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0, 21.0,
21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5, 22.5, 22.7,
23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5, 27.3, 27.5, 27.5,
27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0, 36.5, 36.0, 37.0, 37.0,
39.0, 39.0, 39.0, 40.0, 40.0, 40.0, 40.0, 42.0, 43.0, 43.0, 43.5,
44.0])
perch_weight = np.array([5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0, 110.0,
115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0, 130.0,
150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0, 197.0,
218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0, 514.0,
556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0, 820.0,
850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0, 1000.0,
1000.0])
파이썬 리스트에서 넘파이 배열로 변환하지 않고 바로 넘파이를 임포트 하여 넘파이 배열을 만들었다.
농어의 길이가 특성이고 무게가 타깃이다.
농어 데이터가 어떤 형태인지 산점도를 그려보자.
import matplotlib.pyplot as plt # matplotlib의 pyplot 함수를 plt로 줄여서 사용
plt.scatter(perch_length, perch_weight) # scatter()는 산점도를 그리는 함수
plt.xlable('length') # x축은 길이
plt.ylable('weight') # y축은 무게
plt.show()
농어의 길이가 커질수록 무게도 커지는 형태의 산점도가 나타났다.
2. 훈련 세트와 테스트 세트 만들기
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(perch_length, perch_weight, random_state=42)
먼저 사이킷런의 train_test_split() 함수를 임포트 한 후 perch_length와 perch_weight을 나누었다.
train_test_split() 함수는 전달되는 리스트나 배열을 비율에 맞게 훈련 세트와 테스트 세트로 나눠주며, 자체적으로 랜덤 시드를 지정할 수 있는 random_state 매개변수가 있다. 이를 사용하여 훈련 세트와 테스트 세트를 나누었고 랜덤 시드(radom_state)는 42로 지정하였다.
사이킷런에 사용할 훈련 세트는 2차원 배열이어야 한다.
perch_length는 1차원 배열이기 때문에 이를 나눈 train_input과 test_input도 1차원 배열이다. 이런 1차원 배열을 2차원 배열로 만들어보자.
train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)
print(train_input.shape, test_input.shape)
# 출력: (42, 1) (14, 1)
넘파이는 배열의 크기를 바꿀 수 있는 reshape() 메서드를 제공한다. 1차원 배열을 2차원 배열로 바꾸기 위해 reshape() 메서드를 사용하였다.
또한 넘파이는 배열의 크기를 자동으로 지정하는 기능을 제공한다. 매개변수 크기에 -1을 지정하면 나머지 원소 개수로 모두 채우라는 의미이다. 따라서 train_input.reshape(-1, 1)은 첫 번째 크기를 나머지 원소 개수로 채우고 두 번째 크기를 1로 하라는 의미이다.
3. KNeighborsRegressor 클래스를 임포트 한 후 객체 만들기
k-최근접 이웃 회귀 모델을 만드는 사이킷런 클래스인 KNeighborsRegressor를 임포트 한 KNeighborsRegressor 클래스의 객체를 만들어보자.
from sklearn.neighbors import KNeighborsRegressor # KNeighborsRegressor 임포트
knr = KNeighborsRegressor() # knr 객체 생성
4. 모델 훈련 및 평가하기
knr.fit(train_input, train_target) # 훈련
print(knr.score(test_input, test_target)) # 평가
# 출력: 0.992809406101064
0.992809406101064가 출력되었다. 0.99이면 아주 좋은 값이다.
회귀에서는 예측하는 값이나 타깃 모두 임의의 수치이기 때문에 정확한 숫자를 맞히는 것은 거의 불가능하다.
회귀는 분류와 다르게 정확도가 아닌 결정계수로 성능을 평가한다. 결정계수는 회귀 문제의 대표적인 성능 측정 도구로, 1에 가까울수록 좋고 0에 가까울수록 성능이 나쁘다는 의미이다.
예측이 어느 정도 벗어났는지 알기 위해 타깃과 예측한 값 사이의 차이를 구해보자.
from sklearn.metrics import mean_absolute_error
test_prediction = knr.predict(test_input)
mae = mean_absolute_error(test_target, test_prediction)
print(mae)
# 출력: 19.157142857142862
19.157142857142862이 출력되었다. 예측이 19g 정도 타깃값과 다르다.
사이킷런은 sklearn.metrics 패키지 아래 여러 가지 측정 도구를 제공한다. 이 중 mean_absolute_error()는 회귀 모델의 평균 절댓값 오차를 계산한다. 첫 번째 매개변수는 타깃이고 두 번째 매개변수는 예측값을 전달한다.
5. 과대적합과 과소적합
1) 과소적합 경험하기
지금까지 훈련 세트로 모델을 훈련하고 테스트 세트로 모델을 평가하였다.
이번에는 훈련 세트로 모델을 평가해 보자.
print(knr.score(train_input, train_target))
# 출력: 0.9698823289099254
score() 메서드에 훈련 세트를 전달하였더니 0.9698823289099254이 출력되었다.
테스트 세트로 평가했을 때보다 점수가 낮게 나왔다. 이를 과소적합이라고 한다.
훈련 세트보다 테스트 세트의 점수가 높거나 두 점수가 모두 너무 낮은 경우를 '모델이 훈련 세트에 과소적합되었다'라고 한다. 즉 모델이 훈련 세트에 적절히 훈련되지 않았다는 것이다.
반대로 테스트 세트보다 훈련 세트의 점수가 높을 경우 '모델이 훈련 세트에 과대적합되었다'라고 한다.
2) 과소적합 해결하기
과소적합을 해결하기 위해 모델을 더 복잡하게 만들어보자. 즉 훈련 세트에 더 잘 맞게 만들어 보자.
knr.n_neighbors = 3
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))
# 출력: 0.9804899950518966
과소적합을 해결하려면 모델을 더 복잡하게 만들어야 한다. k-최근접 이웃의 경우 k값을 줄여야 한다. 반대로 과대적합을 해결하려면 모델을 덜 복잡하게 만들어야 한다. 즉 k값을 늘려야 한다.
k-최근접 이웃 알고리즘의 기본 k값(이웃의 개수)인 5를 3으로 줄여 훈련세트에 있는 국지적인 패턴에 민감해지게 하였더니 0.9804899950518966이 출력되었다. 훈련 세트의 점수가 더 높아졌다.
이웃의 개수를 바꿀 때는 클래스 객체를 다시 만들 필요 없이 n_neighbors의 속성값을 바꾸면 된다.
테스트 세트의 점수를 출력해 보자.
print(knr.score(test_input, test_target))
#출력: 0.9746459963987609
0.9746459963987609이 출력되었다.
예상대로 테스트 세트의 점수가 훈련 세트의 점수보다 낮아졌으므로 과소적합이 해결되었다.
* 용어 정리
1. 회귀: 임의의 수치를 예측하는 문제
2. reshape(): 배열의 크기를 바꾸는 메서드, 바꾸고자 하는 배열의 크기를 매개변수로 전달하며 바꾸기 전후의 배열 원소 개수는 동일해야 함
3. k-최근접 이웃 회귀: 분류와 비슷하게 가장 가까운 k개의 이웃 샘플을 찾고 이 샘플들의 타깃값을 평균하여 예측값으로 사용
4. KNeighborsRegressor: k-최근접 이웃 회귀 모델을 만드는 사이킷런 클래스
5. 결정계수: 대표적인 회귀 문제의 성능 측정 도구
6. mean_absolute_error(): 회귀 모델의 평균 절댓값 오차를 계산하며 첫 번째 매개변수는 타깃, 두 번째 매개변수는 예측값임
7. 과대적합(overfitting): 모델의 훈련 세트 성능이 테스트 세트 성능보다 높은 것
8. 과소적합(underfitting): 훈련 세트와 테스트 세트 성능이 모두 낮거나 테스트 세트 성능이 더 높은 것
지도 학습은 크게 분류와 회귀로 나뉜다.
분류는 말 그대로 샘플을 몇 개의 클래스 중 하나로 분류하는 문제이고, 회귀는 임의의 어떤 숫자를 예측하는 문제이다. 예를 들어 내년 경제 성장률을 예측하거나 배달이 도착할 시간을 예측하는 것이 회귀 문제이다.
'Python > ML' 카테고리의 다른 글
[머신러닝] 릿지, 라쏘(농어 무게 예측3) (0) | 2023.01.31 |
---|---|
[머신러닝] 선형 회귀, 다항 회귀(농어 무게 예측2) (0) | 2023.01.30 |
[머신러닝] 데이터 전처리(생선 분류 문제3) (2) | 2023.01.20 |
[머신러닝] 샘플링 편향, 넘파이(생선 분류 문제2) (0) | 2023.01.15 |
[머신러닝] k-최근접 이웃(생선 분류 문제) (1) | 2023.01.13 |
댓글