이 포스팅은 혼자 공부하는 머신러닝 + 딥러닝 책을 공부하고 정리한 것 입니다.
만약 아주 큰 농어를 골라 무게를 예측하면 어떨까? 길이가 50cm정도 된다고 생각해보자. 앞서 만든 모델을 사용해 이 농어의 무게를 예측해보자
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])
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_input=train_input.reshape(-1,1)
test_input=test_input.reshape(-1,1)
최근접 이웃 개수를 3으로 하는 모델을 훈련한다.
from sklearn.neighbors import KNeighborsRegressor
knr=KNeighborsRegressor(n_neighbors=3)
knr.fit(train_input,train_target)
print(knr.predict([[50]]))
어디서 문제가 생긴 것 일까?
import matplotlib.pyplot as plt
distances,indexes=knr.kneighbors([[50]])
plt.scatter(train_input,train_target)
plt.scatter(train_input[indexes],train_target[indexes],marker='D')
plt.scatter(50,1033,marker='^')
plt.show()
이 산점도를 보면 길이가 커질수록 농어의 무게가 증가하는 경향이 있다. 하지만 50cm 농어에서 가장 가까운 것은 45cm 근방이기 때문에 k-최근접 이웃 알고리즘은 이 샘플들의 무게를 평균한다. 이웃 샘플의 타깃의 평균을 구해 보면
print(np.mean(train_target[indexes]))
모델이 예측했던 값과 정확히 일치한다. 새로운 샘플이 훈련 세트의 범위를 벗어나면 엉뚱한 값을 예측할 수 있다.
print(knr.predict([[100]]))
distance,indexes=knr.kneighbors([[100]])
plt.scatter(train_input,train_target)
plt.scatter(train_input[indexes],train_target[indexes],marker='D')
plt.scatter(100,1033,marker='^')
plt.show()
선형 회귀
선형 회귀(linear regression)는 널리 사용되는 대표적인 회귀 알고리즘 이다. 비교적 간단하고 성능이 뛰어나기 때문에 맨 처음 배우는 머신러닝 알고리즘 중 하나이다. 특성이 하나인 경우 어떤 직선을 학습하는 알고리즘 이다.
사이킷런은 sklearn.linear_model 패키지 아래에 LinearRegression 클래스로 선형 회귀 알고리즘을 구현해 놓았다.
from sklearn.linear_model import LinearRegression
lr=LinearRegression()
lr.fit(train_input,train_target)
print(lr.predict([[50]]))
k-최근접 이웃 회귀를 사용했을 때와 달리 선형 회귀는 50cm 농어의 무게를 아주 높게 예측했다. 이 선형 회귀가 학습한 직선을 그려보고 어떻게 이런 값이 나왔는지 알아보자.
하나의 직선을 그리려면 기울기와 절편이 있어야 한다. y=ax+b 처럼 쓸 수 있다. 여기에서 x를 농어의 길이. y를 농어의 무게로 바꾸면 다음과 같다
LinearRegression 클래스가 찾은 a와 b는 lr객체의 coef_와 intercept_ 속성에 저장되어 있다.
print(lr.coef_,lr.intercept_)
농어의 길이 15에서 50까지 직선으로 그려 보자. 이 직선을 그리려면 앞에서 구한 기울기와 절편을 사용하여 (15,15x39-709)와 (50,15x39 - 709) 두 점을 이으면 된다.
plt.scatter(train_input,train_target)
plt.plot([15,50],[15*lr.coef_+lr.intercept_,50*lr.coef_+lr.intercept_])
plt.scatter(50,1241.8,marker='^')
plt.show()
훈련 세트와 테스트 세트에 대한 R^2 점수를 확인해보자.
print(lr.score(train_input,train_target))
print(lr.score(test_input,test_target))
훈련 세트와 테스트 세트의 점수가 조금 차이가 난다. 이 모델은 훈련 세트의 점수도 높지 않기 때문에 전체적으로 과소적합 되었다고 볼 수 있다. 과소적합 말고도 다른 문제가 또 있는데 그래프 왼쪽 아래부분 이다.
다항 회귀
이 직선대로 예측하면 농어의 무게가 0g 이하로 내려갈 텐데 현실에서는 있을 수 없는 일이다. 농어의 길이와 무게에 대한 산점도를 자세히 보면 일직선이라기 보다 왼쪽 위로 조금 구부러진 곡선에 가깝다. 그렇다면 최적의 직선을 찾기보다 최적의 곡선을 찾아야 겠다.
이런 2차 방정식의 그래프를 그리려면 길이를 제곱한 항이 훈련 세트에 추가되어야 한다.
train_poly=np.column_stack((train_input **2, train_input))
test_poly=np.column_stack((test_input ** 2 , test_input))
print(train_poly.shape,test_poly.shape)
lr=LinearRegression()
lr.fit(train_poly,train_target)
print(lr.predict([[50**2, 50]]))
print(lr.coef_,lr.intercept_)
이 모델은 다음과 같은 그래프를 학습했다
이러한 방정식을 다항식 이라 부르며 다항식을 사용한 선형 회귀를 다항회귀 라고 부른다.
그럼 이 2차 방정식의 계수와 절편 a,b,c를 알았으니 이전과 동일하게 훈련 세트의 산점도에 그래프로 그려 보겠다. 짧은 직선을 이어서 그리면 마치 곡선처럼 표현할 수 있다. 여기에서는 1씩 짧게 끊어서 그려 보겠다.
point=np.arange(15,50)
plt.scatter(train_input,train_target)
plt.scatter(train_input,train_target)
plt.plot(point,1.01 *point**2 -21.6*point + 116.05)
plt.scatter([50],[1574],marker='^')
plt.show()
훈련 세트와 세트와 테스트 세트의 R^2 점수를 평가해 보자.
print(lr.score(train_poly,train_target))
print(lr.score(test_poly,test_target))
훈련 세트와 테스트 세트에 대한 점수가 크게 높아졌다. 그러나 여전히 테스트 세트의 점수가 더 높다. 과소적합이 아직 남아있는 것 같다. 조금 더 복잡한 모델이 필요한 것 같다.
결론
k-최근접 이웃 회귀를 사용해서 농어의 무게를 예측했을 때 발생하는 큰 문제는 훈련 세트 범위 밖의 샘플을 예측할 수 없다는 점이였다. k-최근접 이웃 회귀는 아무리 멀리 떨어져 있더라도 무조건 가장 가까운 샘플의 타깃을 평균하여 예측한다.
이를 해결하기 위해 선형회귀를 사용했다. 사이킷런의 LinearRegression 클래스를 사용하면 k-최근접 이웃 알고리즘을 사용했을 때와 동일한 방식으로 모델을 훈련하고 예측에 사용할 수 있다. 가장 잘 맞는 직선의 방정식을 찾는다는 것은 최적의 기울기와 절편을 구한다는 의미이다. 그러나 선형회귀는 모델이 단순해서 농어의 무게가 음수일 수도 있다.
이를 해결하기 위해 다항 회귀를 사용했다. 훈련 세트와 테스트 세트의 성능이 단순한 선형 회귀보다 훨씬 높아졌다. 하지만 과소적합된 경향이 아직 남아있다. 다음 절에서는 조금 더 복잡한 모델을 만들어 이 문제를 해결하겠다. 또한 너무 복잡한 모델일 경우, 즉 과대적합된 모델을 반대로 억제하는 방법도 알아보겠다.
'Machine Learning > Basic' 카테고리의 다른 글
[4-1] 로지스틱 회귀 (0) | 2021.04.06 |
---|---|
[3-3] 특성 공학과 규제 (0) | 2021.04.06 |
[3-1] k-최근접 이웃 회귀 (0) | 2021.04.05 |
[2-2] 데이터 전처리 (0) | 2021.04.04 |
[2-1] 훈련 세트와 테스트 세트 (0) | 2021.04.03 |