[ML Study]ch03-1 k-최근접 이웃 회귀
혼자 공부하는 머신러닝+딥러닝 [3-1]
본 포스팅 한빛미디어의 <혼자공부하는 머신러닝+딥러닝(박해선 저)>를 요약 정리했습니다.
1. k-최근접 이웃 회귀
지도학습 알고리즘은 분류와 회귀(regression) 두 가지로 크게 나뉜다.
회귀란 클래스 중 하나로 분류하는 것이 아닌 임의의 어떤 숫자를 예측하는 것으로
정해진 클래스가 없고 임의의 수치를 출력하고, 두 변수 사이의 상관 관계를 분석하는 방법이다.
k-최근접 이웃 분류 알고리즘은 예측하려는 샘플에 가장 가까운 샘플 k개 선택하고
이 샘플들의 클래스를 확인하여 다수 클래스를 새로운 샘플의 클래스로 예측하는 것이다.
마찬가지로 k-최근접 이웃 회귀 알고리즘도 예측하려는 샘플에 가장 가까운 샘플 k개를 선택하지만
회귀이기에 이웃한 샘플의 타깃은 어떤 클래스가 아니라 임의의 수치가 된다.
이웃 샘플의 수치를 사용해 새로운 샘플 X의 타깃을 예측하는 방법으로는 수치들의 평균을 구하는 것이 있다.
ex. 이웃한 샘플의 타깃값이 각각 100, 80, 60이고 이의 평균인 80이 샘플 X의 예측 타깃값이 된다.
2. 데이터 준비
[농어 데이터를 이용해 사이킷런 회귀 모델 훈련을 해보자]
농어의 길이가 특성, 무게가 타깃으로 바로 넘파이 배열을 사용하여 시작한다.
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])**
하나의 특성을 사용하기 때문에 특성 데이터를 x축에 놓고 타깃 데이터를 y축에 놓는다.
맷플롯립을 임포트하고 scatter () 함수를 사용해 산점도를 그려보자.
import matplotlib.pyplot as plt
plt.scatter(perch_length, perch_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
농어의 길이가 커짐에 따라 무게도 늘어남을 볼 수 있다.
[농어 데이터를 훈련세트와 테스트 세트를 나눠 실행해보자]
이는 train_test_split () 함수를 사용해 나눌 것이고, random_state는 random_state = 42로 지정해준다.
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)
훈련세트는 2차원 배열이여야 한다.
perch_length가 1차원 배열이기 때문에 이를 나눈 train_input, test_input도 1차원 배열이다.
1개의 열이 있는 2차원 배열로 변경해준다.
.png)
.png)
파이썬에서 1차원 배열의 크기는 원소가 1개인 튜플로 나타낸다.
2차원 배열로 만들기 위해 억지로 하나의 열을 추가해서 배열의 크기가 (3, 1)로 된다.
넘파이 배열은 크기를 바꿀 수 있는 reshape () 메서드를 제공한다.
[(4, ) 배열을 (2, 2) 크기로 변경해보자]
test_array = np.array([1,2,3,4])
print(test_array.shape)
test array는 (4, ) 배열임을 확인한다.
test_array = test_array.reshape(2, 2)
print(test_array.shape)
reshape () 메서드는 바꾸려는 배열의 크기를 지정할 수 있다.
reshape () 메서드는 크기가 바뀐 새로운 배열을 반환할 때 지정한 크기가 원본 배열에 있는 원소의 개수와 다르면 에러가 발생한다.
[train_input과 test_input을 2차원 배열로 바꿔보자]
train_input의 크기는 (42, )이고, (42, 1)로 바꾸려면 train_input.reshape(42, 1)과 같이 사용한다.
넘파이는 배열의 크기 자동 지정하는 기능 제공하므로, 크기에 -1 지정하면 나머지 원소 개수로 모두 채우라는 의미이다.
ex. train_input.reshape(-1, 1)의 뜻은
첫 번째 크기를 나머지 원소 개수로 채우고, 두 번째 크기를 1로 하는 것이다.
trai_input = train_input.reshape(-1, 1)
test_ninput = test_input.reshape(-1, 1)
print(train_input.shape, test_input.shape)
reshape(-1, 1)과 같이 사용하면 배열의 전체 원소 개수를 보기에 편하다.
3. 결정계수(R^2)
KNeighborsRegressor는 사이킷런에서 k-최근접 이웃 회귀 알고리즘을 구현한 클래스이다.
KNeighborsClassifier과 매우 비슷하고, 객체를 생성하고 fit() 매서드로 회귀 모델을 훈련한다.
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
# k-최근접 이웃 회귀 모델을 훈련합니다.
knr.fit(train_input, train_target)
print(knr.score(test_input, test_target))
.png)
회귀에서 정확한 숫자를 맞힌다는 것은 거의 불가능하다.
그 이유는 예측하는 값이나 타깃 모두 임의의 수치이기 때문이다.
결정계수(coefficient of determination) 또는 R^2이라 한다.
결정계수의 수식은 R^2 = 1 - {(타깃 - 예측)^2의 합} / {(타깃 - 평균)^2의 합}으로 나타낼 수 있다.
타깃의 평균 정도를 예측하는 수준이라면, R^2는 0에 가까워지게 된다.
예측이 타깃에 아주 가까워지면 1에 가까운 값이 된다.
타깃과 예측한 값 사이의 차이를 구해 보면 어느정도 예측이 벗어났는지 가늠하기 좋다.
사이킷런은 sklearn.metrics 패키지 아래 여러 가지 측정 도구 제공하는데,
이 중 mean_absolute_error는 타깃과 예측의 절댓값 오차를 평균하여 반환한다.
from sklearn.metrics import mean_absolute_error
# 테스트 세트에 대한 예측을 만듭니다.
test_prediction = knr.predict(test_input)
# 테스트 세트에 대한 평균 절대값 오차를 계산합니다.
mae = mean_absolute_error(test_target, test_prediction)
print(mae)
예측이 평균적으로 19g 정도 타깃값과 다르다는 것을 알 수 있다.
score () 메서드에 훈련 세트를 전달하여 점수를 출력해 보는 것은 테스트 세트의 점수와 다를 것이다.
4. 과대적합 vs 과소적합
print(knr.score(train_input, train_target))
훈련 세트의 R^2의 점수를 확인해 본다.
보통 훈련 세트의 점수가 조금 더 높게 나온다.
훈련 세트에서 좋은 점수와 테스트 세트에서 나쁜 점수는 훈련 세트에 과대적합(overfitting)이라 한다.
즉, 훈련 세트에만 잘 맞는 모델인 것이다.
훈련 세트보다 테스트 세트의 점수가 높거나 두 점수가 모두 낮은 경우에는 훈련 세트에 과소적합(underfitting)이라 한다.
즉, 훈련 세트에 적절히 훈련되지 않은 경우인 것이다.
[ 훈련 세트와 테스트 세트의 점수를 비교 ]
과대적합 : 훈련 세트가 너무 높을 경우
과소적합 : 그 반대이거나 두 점수가 모두 낮은 경우
[ 과소적합이 일어나는 이유 ]
또 다른 원인으로는 훈련 세트와 테스트 세트의 크기가 매우 작기 때문이다.
데이터가 작으면 테스트 세트가 훈련 세트의 특징을 따르지 못할 수도 있다.
k-최근접 이웃 회귀로 평가한 훈련 세트와 테스트 세트의 점수를 비교해보자.
훈련 세트보다 테스트 세트의 점수가 높다.
이를 해결하기 위한 방법으로 모델을 조금 더 복잡하게 만들기가 있다.
즉, 훈련 세트에 더 잘맞게 만들면 테스트세트의 점수는 낮아질 것이다.
따라서, 이웃의 개수 k를 줄인다.
이웃의 개수 k를 줄이면 훈련 세트에 있는 국지적인 패턴에 민감해진다.
이웃의 개수 k를 늘리면 데이터 전반에 잇는 일반적인 패턴 따른다.
[ 기본 k값을 5에서 3으로 낮춰보자. ]
k-최근접 이웃 클래스는 이웃의 개수를 바꾸기 위해 클래스 객체를 다시 만들 필요가 없다.
n-neighbors 속성값을 3으로 한다.
# 이웃의 개수를 3으로 설정합니다.
knr.n_neighbors = 3
#모델을 다시 훈련합니다.
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))
.png)
k값 줄이면 훈련 세트의 R^2 점수가 높아진다.
print(knr.score(test_input, test_target))
.png)
테스트 세트의 점수는 훈련 세트보다 낮아진 것을 볼 수 있다. (과소적합 문제 해결)
5. 회귀 문제 다루기
회귀는 임의의 수치를 예측하는 문제이다.
k-최근접 이웃 회귀 모델은 가장 먼저 가까운 k개의 이웃을 찾고, 이웃 샘플의 타깃값을 평균하여 이 샘플의 예측값으로 사용한다.
결정계수의 값은 1에 가까울수록 좋다.
과대적합일 경우, 모델을 덜 복잡하게 만들어야 한다. (k-최근접 이웃의 경우 k값 늘림)
과소적합일 경우, 모델을 더 복잡하게 만들어야 한다. (k-최근접 이웃의 경우 k값 줄임)
.png)