
이전 모델의 내용을 그대로 가져와 확인합니다.
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 sklenar.model_selection import trian_test_split train_input, test_input, train_target, test_target = train_test_split( perch_length, perch_weight, random_state=42) # 2차원 배열로 변경 train_input = train_input.reshape((-1, 1)) test_input = test_input((-1 ,1)) from sklearn.neighbors import KNeighborsRegressor knr = KNeighborsRegressor(n_neighbors=3) knr.fit(train_input, train_target)
50cm, 100cm 농어를 예측해보자.
# 길이가 50cm, 100cm 샘플 예측 knr.predict([[50]]) # 1033.3333333 knr.predict([[100]]) # 1033.3333333둘 다 같은 값이 나온다.
위의 코드에서와 같이 50cm, 100cm의 농어의 무게가 동일하게 나온다 문제가 뭘까?
바로 이웃한 샘플의 데이터를 통해 예측을 한다는 것이다.
이 모델에서 학습한 샘플의 최고 길이는 44.0cm이다.
50cm의 농어도 예측값보다 훨씬 더 무겁다.
여기서 백번 양보한다 해봐도 100cm를 예측할 때 사용되는 농어의 길이는 전부 50cm 미만이다.
이렇게 k-최근접 이웃 회귀 모델은 훈련 셋의 범위를 넘어간 샘플을 예측하게 되면 엉뚱한 값이 나오게 된다.
이제 100cm가 넘어가는 길이의 농어 샘플을 예측해도 무게는 달라지지 않을 것이다.
선형 회귀(linear regression)는 feature가 하나인 경우 어떤 직선을 학습하는 알고리즘이다. 여기서 직선이란 특성을 잘 나타내는 직선을 의미한다.

- 첫 번째 그래프는 모든 농어의 무게를 하나로 예측한다.
이 직선의 위치가 훈련 셋의 평균에 가깝다면 R^2는 0에 가까운 값이 된다.- 두 번째 그래프는 완전히 반대로 예측한다. 길이가 작은 농어는 무겁고 길이가 큰 농어는 가볍게 예측한다.
그렇다면 R^2는 음수가 될 수 있다.- 특성을 가장 잘 표현하는 직선이다. 이런 직선을 머신러닝 알고리즘이 자동으로 찾을 수 있다.
이번에 사용할 scikit-learn의 class는 LinearRegression이다.
# 선형 회귀 모델 객체 생성 from sklearn.linear_model import LinearRegression lr = LinearRegression() # 훈련 lr.fit(train_input, train_target)1241.83860323 값이 나온다.
k-최근접 이웃 회귀를 사용했을 때보다 길이 50cm 농어의 무게를 훨씬 더 높게 예측했다.
선형 회귀가 학습한 직선을 확인해보자.
일단 하나의 직선을 그리려면 기울기와 절편이 있어야 한다. y = Ax + B처럼 쓸 수 있다.
여기에서 x를 농어의 길이, y를 농어의 무게라 한다.
LienarRegression의 객체가 이 데이터에 가장 잘 맞는 A와 B를 훈련 셋을 통해 찾았다.
기울기와 절편값은 coef_와 intercept_ 필드에 저장되어 있다.
이를 맷플롯립으로 산점도와 함께 확인해보자.
coef_와intercept_를 머신러닝 알고리즘이 찾은 값이라는 의미로모델 파라미터라고 부른다.
이제부터 다룰 많은 머신러닝 알고리즘의 훈련 과정은 최적의 모델 파라미터를 찾는 것과 같다.
이를모델 기반 학습이라고 부른다.
앞서 사용한 k-최근접 이웃에는 모델 파라미터가 없다. 단지 훈련 셋을 저장하기만 한다.
이를 두고사례 기반 학습이라고 부른다.
맷플롯립을 표현해주기 위해 15부터 50까지(정의역)과 이에 대한 무게(치역)을 구해야한다.
이는 [15, 50], [15기울기+절펀, 50기울기+절편]로 잡아주면 된다.
이를 plt.plot() 함수의 첫 번째, 두 번째 인자에 넣어주면 된다.
import matplotlib.pyplot as plt 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.xlabel("length") plt.ylabel("weight") plt.show()
바로 이 직선이 선형 회귀 알고리즘이 훈련 셋에서 차즌 최적의 직선이다. 길이가 50cm인 농어에 대한 예측은 이직선의 연장선에 있다.
이제 R^2점수를 확인해보자
lr.score(train_input, train_target) # 0.939846333997604 lr.score(test_input, test_target) # 0.8247503123313558
훈련 셋과 테스트 셋의 점수가 조금 차이난다. 이 모델이 훈련 셋에 과대적합되었다고 말할 수 있을까?
사실 훈련 셋의 점수도 그리 놓지 않다. 오히려 전체적으로 과소적합되었다고 말할 수 있다.
그런데 산점도 왼쪽 아래의 샘플들이 직선보다는 곡선에 가까운 형태를 따고 있다. 좀 더 좋은 모델을 사용할 수 없을까?
기존의 선형 회귀가 만든 직선은 왼쪽 아래로 쭉 뻗어 있었다. 이 직선대로 예측하면 농어의 무게가 0g 이하로 내려갈 텐데 현실에서는 있을 수 없는 일이다.
농어의 길이와 무게에 대한 산점도를 보면 일직선이라기보다 왼쪽 위로 조금 구부러진 곡선에 가깝다.
그렇다면 최적의 직선을 찾기보다 최적의 곡선을 찾는게 좋다.
2차 함수식은 y = ax^2 + bx + c의 형태이다.
그렇다면 이 모델은 무게 = a 길이^2 - b 길이 + c 에서 최적의 a,b,c 값을 찾는다.
이를 위해 농어의 길이를 제곱한 값을 훈련 셋에 추가해줘야만 한다.
(타깃은 어떤 그래프던 상관없다.)
train_poly = np.column_stack((train_input**2, train_input))
test_poly = np.column_stack((test_input**2, test_input))
이제 학습을 진행하고 50cm 농어의 예측값을 확인하자.
lr = LinearRegression() lr.fit(train_poly, train_target) lr.predict(([50**2, 50]]) # 1573.98423528
이게 어떻게 선형 결합이지? 제곱을 해주는데?
길이^2을 다른 변수로 치환한다는데, 그럼 평면아닌가?
# 구간별 직선을 그리기 위해 15에서 49까지 정수 배열을 만든다. point = np.arange(15, 50) # 훈련 세트의 산점도를 그린다. plt.scatter(train_input, train_target) # 15에서 49까지의 2차 방성식 그래프 plt.plot(point, 1.01*point**2 - 21.6*point + 116.05) plt.scatter(50, 1574, marker='^') plt.xlabel('length') plt.ylabel('weight') plt.show()
이전의 선형 회귀 모델보다 훨씬 나은 그래프가 그려졌다.
훈련 셋의 경향을 잘 따르고 있고, 무게가 음수로 나올 일도 없을 것이다.
R^2를 평가해보자
lr.score(train_poly, train_target) # 0.9706807451768623 lr.score(test_poly, test_target) # 0.9775935108325122
훈련 셋과 테스트 셋에 대한 점수가 크게 높아졌다. 하지만 여전히 테스트 셋의 점수가 조금 더 높다.
과소적합이 남아있는것 같다. 조금 더 복잡한 모델이 필요하다.(데이터 셋이 더 큰)