[혼자 공부하는 머신러닝+딮러닝] 회귀 알고리즘과 모델 규제 - 선형 회귀
k-최근접 이웃의 한계
농어 데이터를 다시 사용하고 50cm 농어의 무게를 예측해보자
이전까진 전 게시물과 똑같다
print(knr.predict([[50]])) # [1033.33333333]
농어의 무게를 1033g정도로 예측했다. 하지만 실제 이 농어의 무게는 훨씬 더 많이 나간다고 한다
어디서 문제가 생긴걸까?
최근접 이웃을 산점도로 표시해보자
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-최근접 이웃 알고리즘은 이 샘플들의 무게를 평균한다
따라서 이 알고리즘의 한계는 새로운 샘플이 훈련 세트의 범위를 벗어나면 엉뚱한 값을 예측할 수 있다는 것이다.
선형회귀
선형이라는 이름에서 짐작할 수 있듯이 특성이 하나인 경우 어떤 직선을 학습하는 알고리즘이다

- 농어의 무게를 하나로 예측한다. 이 직선의 위치가 만약 훈련 세트의 평균에 가깝다면 R^2는 0에 가까운 값이 된다
- 1과 완전히 반대로 예측한다. R^2의 값이 음수가 될 수 있다.
- 이 그래프가 가장 그럴싸한 직선이다.
사이킷런에서 LinearRegression클래스로 선형 회귀 알고리즘을 구현해 두었다.
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_input, train_target)
print(lr.predict([[50]])) #[1241.83860323]
LinearRegression 클래스는 y = a * x + b 이런 간단한 방정식에서 a값과 b의 값을 찾는다
a = lr.coef_ (계수 혹은 가중치)
b = lr.intercept_ (편향)
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)) # 0.9398463339976041
print(lr.score(test_input, test_target)) # 0.824750312331356
훈련 세트의 점수도 높지 않고 훈련세트와 테스트 세트의 점수가 조금 차이가 난다
전체적으로 과소적합 되었다고 볼 수 있다
뭐가 문제일까?
다항 회귀
농어의 길이와 무게에 대한 산점도를 자세히 보면 일직선이라기 보다 왼쪽 위로 조금 구부러진 곡선에 가깝다
그렇다면 최적의 직선을 찾기보단 최적의 곡선을 찾는 것이 합리적이다!

train_poly = np.column_stack((train_input ** 2, train_input))
test_poly = np.column_stack((test_input ** 2, test_input))
# column_stack = 제곱한 거랑 원래 값을 나란히 행에 붙여줌
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.predict([[50**2, 50]])) # [1573.98423528]
lr이 예측한 계수와 절편은 다음과 같다
print(lr.coef_, lr.intercept_) # [ 1.01433211 -21.55792498] 116.05021078278338
산점도를 그려보면 다음과 같다.
point = np.arange(15, 50)
plt.scatter(train_input, train_target)
plt.plot(point,( 1.01 * point )**2 - 21.6 * point + 116.05)
plt.scatter(50, pred, marker = "^")
plt.show()

단순한 선형 회귀 모델보다 훨씬 나은 그래프가 그려졌다!
print(lr.score(train_poly, train_target)) # 0.9706807451768623
print(lr.score(test_poly, test_target)) # 0.9775935108325122
여전히 테스트 점수가 조금 더 높긴하다 더 복잡한 모델이 필요해 보인다 이건 다음장에서~!