회귀(regression) : 클래스 중 하나로 임의의 어떤 숫자를 예측하는 문제.
예를 들면 내년도 경제 성쟝률 예측이나 배달 도착 시간 예측. 여기서는 농어의 무게 예측하는 것도 회귀에 속함!
k-최근접 이웃 분류 알고리즘
1. 예측하려는 샘플에 가장 가까운 샘플 k개를 선택.
2. 샘플들의 클래스를 확인하여 다수 클래스를 새로운 샘플의 클래스로 예측.
k-최근접 이웃 회귀
1. 예측하려는 샘플에 가장 가까운 샘플 k개 선택.
2. 여기서 회귀니까 이웃한 샘플의 타깃은 임의의 수치.
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])
훈련 데이터 준비. 어떤 형태 띠고 있는지 산점도 그려보기!
plt.scatter(perch_length, perch_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
농어 길이 커짐에 따라 무게도 늘어남!!
농어 데이터를 머신러닝 모델에 사용하기 전에 훈련 세트와 테스트 세트로 나눈다.
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_lenght가 1차원 배열이기 때문에 이를 나눈 train_input과 test_input도 1차원 배열. 이를 1개의 열이 있는 2차원 배열로 바꿔야 한다.
이를 위해 reshape() 메서드를 사용하여 2차원 배열로 바꾼다.
# 배열 확인
test_array = np.array([1,2,3,4])
print(test_array.shape)
>>> (4,)
#(2,2) 크기로 변경
test_array = test_array.reshape(2, 2)
print(test_array.shape)
>>> (2,2)
reshape() 메서드는 바꾸려는 배열의 크기 지정할 수 있음!
다만, 크기가 바뀐 새로운 배열을 반환할 때 지정한 크기가 원본 배열에 있는 원소의 개수와 다르면 에러 발생!
예를 들어, (4,) 크기의 배열을 (2,3)으로 바꾸려고 하면 에러 발생. 원본 배열의 원소는 4개인데 2 x 3 = 6개로 바꾸려고 하기 때문!
train_input의 크기는 (42,). 이를 2차원 배열인 (42,1)로 바꾸려면 train_input.reshape(42,1)과 같이 사용 가능.
하지만 넘파이에는 배열의 크기를 자동으로 지정하는 기능을 제공한다! 크기에 -1을 지정하면 나머지 원소 개수로 모두 채우라는 의미.
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)
2차원 배열로 성공적으로 변환 완료!!
사이킷런에서 k-최근접 이웃 알고리즘 구현한 클래스는 KNeighborsRegressor.
사용법은 KNeighborsClassifier와 매우 비슷!
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
# k-최근접 이웃 회귀 모델을 훈련합니다
knr.fit(train_input, train_target)
#테스트 세트 점수 확인
print(knr.score(test_input, test_target))
>>> 0.992809406101064
좋은 점수. 근데 이건 무슨 점수일까?!!
분류의 경우 테스트 세트에 있는 샘플을 정확하게 분류한 개수의 비율. (정확도라고 불렀음!) 간단하게 정답 맞힌 개수의 비율.
회귀에서는 정확한 숫자 맞힌다는 것은 거의 불가능. 예측하는 값이나 타깃이 모두 임의의 수치이기 때문!
회귀의 경에는 조금 다른 값으로 평가. 이 점수를 결정계수(coefficient of determination), R^2 라 부른다.
각 샘플의 타깃과 예측한 값의 차이를 제곱하여 더한다. 그다음 타깃과 타깃 평균의 차이를 제곱하여 더한 값으로 나눈다. 만약 타깃의 평균 정도를 예측하는 수준이라면(분자와 분모가 비슷해져) R^2는 0에 가까워지고, 예측이 타깃에 아주 가까워지면(분자가 0에 가까워지니까) 1에 가까운 값이 된다!
사이킷런의 score() 메서드가 출력하는 값은 높을수록 좋은 것. 정확도나 결정계수가 그렇다. 만약 score() 메서드가 에러율을 반환한다면 이를 음수로 만들어 실제로는 낮은 에러가 score() 메서드로 반환될 때는 높은 값이 되도록 바꾼다.
0.99면 아주 좋은 값. 하지만 정확도처럼 R^2가 직감적으로 얼마나 좋은지 이해하기 어렵다. 타깃과 예측한 값 사이의 차이를 구해보면 어느 정도 예측이 벗어났는지 가늠하기 좋음. 사이킷런은 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)
>>> 19.157142857142862
결과에서 예측이 평균적으로 19g 정도 타깃값과 다르다는 것 알 수 있다.
지금까지는 훈련 세트를 사용해 모델을 훈련하고 테스트 세트로 모델 평가했다.
but, 훈련 세트를 사용해 평가해 본다면??? 즉, score() 메서드에 훈련 세트를 전달하여 점수 출력해보는 것!! 테스트 세트의 점수와 다를 것이다!!
print(knr.score(train_input, train_target))
>>> 0.9698823289099255
모델을 훈련 세트에 훈련하면 훈련 세트에 잘 맞는 모델이 만들어진다. 훈련 세트에서 모델을 훈련했으므로 훈련 세트의 점수가 조금 더 높게 나온다.
과대적합(overfitting) : 훈련 세트에서 점수가 굉장히 좋았는데 테스트 세트에서 점수가 나쁠 때 훈련 세트에 붙이는 말.
즉, 훈련 세트에만 잘 맞는 모델이라 테스트 세트와 나중에 실전에 투입하여 새로운 샘플에 대한 예측을 만들 때 잘 동작하지 않을 것.
과소적합(underfitting) : 훈련 세트보다 테스트 세트의 점수가 높거나 두 점수가 모두 낮은 경우 훈련 세트에 붙이는 말.
즉, 모델이 너무 단순하여 훈련 세트에 적절히 훈련되지 않은 경우.
훈련 세트와 테스트 세트의 점수를 비교했을 때 훈련 세트가 너무 높으면 과대적합, 그 반대이거나 두 점수가 모두 낮으면 과소적합.
과소 적합 일어나는 이유는?? : 훈련 세트와 테스트 세트의 크기가 매우 작아서.
앞서 k-최근접 이웃 회귀로 평가한 훈련 세트와 테스트 세트의 점수는 --> 훈련 세트보다 테스트 세트의 점수가 높으니 과소적합. 이 문제를 어떻게 해결할까??
모델을 조금 더 복잡하게 만들면 된다. 즉, 훈련 세트에 더 잘 맞게 만들면 테스트 세트의 점수는 조금 낮아질 것. k-최근접 이웃 알고리즘으로 모델을 더 복잡하게 만드는 방법은 이웃의 개수 k를 줄이는 것!
이웃의 개수를 줄이면 훈련 세트에 잇는 국지적인 패턴에 민감해지고, 이웃의 개수 늘리면 데이터 전반에 있는 일반적인 패턴을 따를 것이다.
여기서 사이킷런의 k-최근접 이웃 알고리즘의 기본 k 값은 5!!
# 이웃의 개수를 3으로 설정
# n_neighbors 속성값 바꾸면 된다!
knr.n_neighbors = 3
# 모델을 다시 훈련한다.
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))
>>> 0.9804899950518966
k값 줄이니 훈련 세트의 R^2 점수가 높아짐!!
# 테스트 세트의 점수 확인
print(knr.score(test_input, test_target))
>>> 0.974645996398761
테스트 세트의 점수는 훈련 세트보다 낮아졌으므로 과소적합 문제 해결. 그리고 두 점수의 차이가 크지 않으므로 과대적합도 아님.
성공적으로 회귀 모델 훈련함!!!
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
plt.scatter(perch_length, perch_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
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)
test_array = np.array([1,2,3,4])
print(test_array.shape)
test_array = test_array.reshape(2, 2)
print(test_array.shape)
train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)
print(train_input.shape, test_input.shape)
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
# k-최근접 이웃 회귀 모델을 훈련합니다
knr.fit(train_input, train_target)
knr.score(test_input, test_target)
from sklearn.metrics import mean_absolute_error
# 테스트 세트에 대한 예측을 만듭니다
test_prediction = knr.predict(test_input)
# 테스트 세트에 대한 평균 절댓값 오차를 계산합니다
mae = mean_absolute_error(test_target, test_prediction)
print(mae)
print(knr.score(train_input, train_target))
# 이웃의 개수를 3으로 설정합니다
knr.n_neighbors = 3
# 모델을 다시 훈련합니다
knr.fit(train_input, test_target)
print(knr.score(train_input, test_target))
print(knr.score(test_input, test_target))
scikit-learn
numpy