지도 학습 알고리즘은 분류
와 회귀
로 나뉜다.
분류 = 샘플을 몇 개의 클래스 중 하나로 분류하는 것
회귀 = 임의의 어떤 숫자를 예측하는 것
ㄴ ex) 내년도 경제 성장률 예측, 배달 도착 시간 예측
k-최근접 이웃 알고리즘
이 회귀에도 작동을 한다 !
ㄴ 예측하려는 샘플에 가장 가까운 샘플 k개 선택 -> 샘플들의 클래스를 확인하여 다수 클래스를 새로운 샘플의 클래스로 예측 ->
k-최근접 이웃회귀
= 예측하려는 샘플에 가장 가까운 샘플 k개를 선택하지만 회귀이기 때문에 이웃한 샘플의 타깃은 클래스가 아니라 수치다 !
<훈련 데이터인 농어 길이와 무게 준비>
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)
근데 훈련세트는 2차원 배열이어야 한다 !! -> perch_length
가 1차원 배열이기 때문에 이를 나눈 train_input
과 test_input
도 1차원 배열이다
=> 이 1차원 배열을 1개의 열이 있는 2차원 배열로 바꿔야 한다 !!
특성이 1개기 때문에 수동으로 2차원 배열을 만드려면 reshape()
메서드를 이용하면 된당
ex) (4,) -> (2,2)
test_array = np.array([1,2,3,4])
print(test_array.shape) # (4,)
test_array = test_array.reshape(2,2)
print(test_array.shape) # (2, 2)
이제 이 메서드를 사용해서 train_input
과 test_input
을 2차원 배열로 바꿀 건데 (42,) -> (42,1) 로 바꿔야 해서 train_input.reshape(42,1)
을 써야한다.
또한, 넘파이는 배열의 크기를 자동으로 지정하는 기능도 제공해서 크기에 -1
을 지정하면 나머지 원소 개수로 모두 채우라는 의미가 된다.
ㄴex) 첫번째 크기를 나머지 원소 개수로 채우고 두 번째 크기를 1로 하려면
train_input.reshape(-1,1)
<reshape() 메서드로 배열 크기 변경>
train_input = train_input.reshape(-1,1)
test_input = test_input.reshape(-1,1)
print(train_input.shape, test_input.shape)
output
(42, 1) (14, 1)
<객체 생성하고 fit()
메서드로 회귀 모델 훈련 후 테스트 세트 점수 확인하기>
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor()
knr.fit(train_input, train_target)
print(knr.score(test_input, test_target))
output
0.992809406101064
ㄴ 이 점수는 결정계수
이다 ! (= R^2)
R^2 = 1 - (타깃 - 예측)^2의 합 / (타깃 - 평균)^2의 합
-> 타깃의 평균 정도를 예측하는 수준이라면 결정계수는 0에 가까워지고, 예측이 타깃에 아주 가까워지면 1에 가까워진다 !!
결정계수가 얼마나 좋은지 딱 보고 아는 것은 어렵기 때문에 다른 값도 계산을 해보자 ~~ 타깃과 예측한 값 사이의 차이를 구해보면 어느 정도 예측이 벗어났는지 가늠하기 좋다 ~~
<타깃과 예측의 절댓값 오차를 평균해서 반환하기>
from sklearn.metrics import mean_absolute_error
test_prediction = knr.predict(test_input)
mae = mean_absolute_error(test_target, test_prediction)
print(mae)
output
19.157142857142862
ㄴ 예측이 평균적으로 19g 정도 타깃값과 다르다
여태까지는 훈련 세트를 사용해서 모델 훈련 후 테스트 세트로 모델을 평가했는데, 이번에는 훈련 세트를 사용해서 평가해보면 어떨까? = score()
메서드에 훈련 세트를 전달해서 점수를 출력해보는 것!!
<훈련한 모델을 사용해서 훈련 세트의 결정계수 점수 확인하기>
print(knr.score(train_input, train_target))
output
0.9698823289099254
ㄴ 앞에서 테스트 세트를 사용한 점수가 더 높다 !!
만약 훈련 세트에서는 점수가 좋았는데 테스트 세트에서 점수가 나쁘다면 ? 이를 모델이 훈련 세트에 과대적합
되었다고 한다 = 훈련 세트에만 잘 맞는 모델!
만약 훈련 세트보다 테스트 세트의 점수가 더 높거나 두 점수가 모두 낮으면 ? 이를 모델이 훈련 세트에 과소적합
되었다고 한다 = 모델이 너무 단순해서 훈련 세트에 적절히 훈련되지 않았다는 것
그럼 여태까지 했던 내 코드는 테스트세트를 사용한 점수가 더 높기 때문에 과소적합
인 것이다 ~~
해결책은??
= 모델을 조금 더 복잡하게 만들장
k-최근접 이웃 알고리즘으로는 이웃의 개수 k를 줄여서 모델을 더 복잡하게 만들 수 있다. 사이킷런의 k-최근접 이웃 알고리즘의 기본 k값은 5
여서 이를 3
으로 낮춰보장
<이웃 개수 줄이고 훈련 후 점수 출력>
knr.n_neighbors = 3
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))
ㄴ k값을 줄였더니 결정계수 점수가 높아졌다 !!
<테스트 세트의 점수 확인하기>
print(knr.score(test_input, test_target))
output
0.9746459963987609
ㄴ 테스트 세트의 점수가 훈련 세트보다 낮아져서 과소적합 문제 해결함 !!
두 점수 차가 그렇게 크지 도 않아서 모델이 과대적합 되지도 않은 것 같다 ~~
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)
from sklearn.neighbors import KNeighborsRegressor
knr = KNeighborsRegressor(n_neighbors = 3)
knr.fit(train_input, train_target)
print(knr.predict([[50]]))
output
[1033.33333333]
ㄴ 50cm 농어의 무게를 1033.3g으로 예측했는데 사실 이 농어의 무게는 훨씬 더 많이 나간다고 한다 !! 어디서 문제가 생긴걸까?
<훈련 세트와 50cm 농어, 농어의 최근접 이웃을 산점도에 표시하기>
import matplotlib.pyplot as plt
distances, indexes = knr.kneighbors([[50]]) # 50cm 농어의 이웃 구하기
plt.scatter(train_input, train_target) # 훈련 세트의 산점도 그리기
plt.scatter(train_input[indexes], train_target[indexes], marker = 'D') # 훈련 세트 중에서 이웃 샘플만 다시 그리기
# 50cm 농어 데이터
plt.scatter(50, 1033, marker = '^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
ㄴ 길이가 50cm, 무게가 1033g인 농어는 초록색 삼각형으로, 그 주변의 샘플은 주황색 마름모로 표현됨 !
이 산점도를 보면 길이가 커질수록 농어의 무게가 증가하는 경향이 있는데, 50cm 농어에서 가장 가까운 것은 45cm 근방이기 때문에 k-최근접 알고리즘은 이 샘플들의 무게 평균을 구한 것이었따 !!!!
<이웃 샘플의 타깃의 평균 구해보기>
print(np.mean(train_target[indexes]))
output
1033.3333333333333
ㄴ 일치하네... k-최근접 이웃 회귀는 가장 가까운 샘플을 찾아 타깃을 평균하기 때문에 새로운 샘플이 훈련 세트의 범위를 벗어나면 이상한 값을 예측하게 된다 ~~
: 특성이 하나인 경우 데이터의 특성을 가장 잘 나타낼 수 있는 직선을 학습하는 알고리즘 !!
<선형회귀 알고리즘 구현하고 훈련 및 예측하기>
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_input, train_target) # 선형 회귀 모델 훈련
print(lr.predict([[50]])) # 50cm 농어에 대해 예측
output
[1241.83860323]
ㄴ k-최근접 알고리즘과는 다르게 아주 높게 예측해뜸 !!
이 선형 회귀가 학습한 직선을 그려보고 이 값이 왜 나왔는지 알아보자 ~~
일단 직선을 그리기 위해 기울기와 절편을 알아야함 (y = ax + b)
x를 농어의 길이, y를 농어의 무게로 하자
기울기와 절편은 LinearRegression()에 coef와 intercept 속성에 저장되어있다
print(lr.coef_, lr.intercept_)
output
[39.01714496] -709.0186449535477
<농어 길이 15 ~ 50 까지 직선으로 그려보기>
# 훈련 세트의 산점도 그리기
plt.scatter(train_input, train_target)
# 15~50까지 1차 방정식 그래프 그리기
plt.plot([15, 50], [15*lr.coef_ + lr.intercept_ , 50*lr.coef_ + lr.intercept_])
# 50cm 농어 데이터
plt.scatter(50, 1241.8, marker = '^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
ㄴ 이 직선이 선형 회귀 알고리즘이 찾은 최적의 직선이당
-> 훈련 세트 범위를 벗어난 농어의 무게도 예측할 수 있음
<훈련 세트와 테스트 세트에 대한 결정계수 점수를 확인하기>
print(lr.score(train_input, train_target)) #0.939846333997604
print(lr.score(test_input, test_target)) #0.8247503123313558
ㄴ 훈련 세트와 테스트 세트의 점수가 조금 차이가 난다. 전체적으로 과소적합 되었음 !!
근데 과소적합이던 뭐던 지금 그래프를 보면 뭔가가 이상하다 !
weight
쪽을 잘 보면 직선이 x축에서 0과 가까워지면서 농어의 무게가 0 이하로 내려가는 것을 볼 수 있따 !!
그리고 농어의 길이와 무게에 대한 산점도를 잘 보면 직선이 아니라 왼쪽 위로 조금 구부러진 곡선에 가까움
이렇게 되면 2차 방정식의 그래프이기 때문에 길이를 제곱한 항이 훈련 세트에 추가되어야 한당
ㄴ train_input을 제곱한걸 두 배열에 나란히 붙이면 끝 ! test input도 마찬가지 !!
<농어의 길이를 제곱해서 원래 데이터 앞에 붙이기>
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) # (42, 2) (14, 2)
train_poly를 사용하여 선형 회귀 모델을 다시 훈련해야 한당. 이때 2차 방정식 그래프를 찾기 위해 훈련 세트에 제곱항을 추가했지만, 타깃값은 그대로 사용한다는 게 중요하다.
<train_poly를 사용하여 선형 회귀 모델을 다시 훈련하기>
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.predict([[50**2, 50]])) # [1573.98423528]
<모델이 훈련한 계수와 절편 출력하기>
print(lr.coef_, lr.intercept_)
output
[ 1.01433211 -21.55792498] 116.0502107827827
ㄴ> 무게 = 1.01 길이^2 - 21.6 길이 + 116.05
이렇게 다항식을 사용한 선형 회귀를 우리는 다항 회귀 라고 부른다
<훈련 세트의 산점도에 그래프로 그려보기>
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, 1574, marker = '^')
plt.xlabel('length')
plt.ylabel('weight')
plt.show()
: 여러 개의 특성을 사용한 선형 회귀
위에서 한개의 특성을 사용했을 때는 선형 회귀 모델이 학습하는 것이 직선이였는데, 2개의 특성을 사용하면 선형회귀는 평면을 학습한다 -> 선형 회귀 방정식 타깃 = a * 특성1 + b * 특성2 + 절편
이 평면이 됨
이번에는 농어의 길이 뿐만 아니라 농어의 높이와 두께도 함께 사용해보자 !!
3개의 특성을 각각 제곱해서 추가하고 여기다가 각 특성을 서로 곱해서 또 다른 특성 ( ex) 농어 길이 * 농어 높이)을 만들거임 !! = 이렇게 기존 특성을 사용해서 새 특성을 뽑아내는 작업을 특성 공학
이라고 함
import pandas as pd
df = pd.read_csv("https://bit.ly/perch_csv_data")
perch_full = df.to_numpy() #to_numpy() = 넘파이 배열로 바꿔주는 메소드
print(perch_full)
output
<타깃 데이터 준비>
import numpy as np
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])
<perch_full과 perch_weight를 훈련 세트와 테스트 세트로 나누기>
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(perch_full, perch_weight, random_state = 42)
사이킷런은 특성을 만들거나 전처리하기 위한 다양한 클래스를 제공하는데 이런 클래스를 변환기
라고 부른다!
<객체 만들고 메서드 호출>
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures()
poly.fit([[2,3]]) # 새롭게 만들 특성 조합을 찾음
print(poly.transform([[2,3]])) # 실제로 데이터를 변환
output
[[1. 2. 3. 4. 6. 9.]]
-> 변환기는 입력 데이터를 변환하는데 타깃 데이터가 필요하지 않아서 fit()
메서드에 입력 데이터만 전달 했당 => 여기에선 2개의 특성을 가진 샘플 [2,3]
이 6개의 특성을 가진 샘플로 바뀌었음
PolynomialFeatures()
는 기본적으로 각 특성을 제곱한 항을 추가하고 특성끼리 서로 곱한 항을 추가함 그래서 2와 3을 제곱한 4와 9가 추가되었고 2와 3을 곱한 6이 추가된 것!! -> 그럼 1은 왜 추가 되었냐
무게 = a * 길이 + b * 높이 + c * 두께 + d * 1
여기서 절편 = 항상 값이 1인 특성과 곱해지는 계수
특성은 (길이, 높이, 두께, 1)
이 됨. 사이킷런의 선형 모델은 자동으로 절편을 추가하므로 굳이 이렇게 특성을 만들 필요가 없어서 include_bias = False
로 지정하여 다시 특성을 변환하면
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(include_bias = False)
poly.fit([[2,3]])
print(poly.transform([[2,3]]))
output
[[2. 3. 4. 6. 9.]]
ㄴ 절편을 위한 항이 제고되고 특성의 제곱과 특성끼리 곱한 항만 추가되었음
<train_input을 변환한 데이터 train_poly에 저장하고 배열 크기 확인>
poly = PolynomialFeatures(include_bias = False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
print(train_poly.shape)
output
(42, 9)
get_feature_names_out()
메서드는 호출하면 특성이 어떻게 만들어졌는지 확인하는 아주 좋은 PolynomialFeatures 클래스의 방법이다
poly.get_feature_names_out()
output
array(['x0', 'x1', 'x2', 'x0^2', 'x0 x1', 'x0 x2', 'x1^2', 'x1 x2',
'x2^2'], dtype=object)
ㄴ x0
은 첫 번째 특성, x0^2
는 첫 번째 특성의 제곱, x0 x1
은 첫 번째 특성과 두 번째 특성의 곱
<테스트 세트 변환>
test_poly = poly.transform(test_input)
<LinearRegrssion클래스 임포트 하고 train_poly를 사용해서 모델 훈련시키기>
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))
output
0.9903183436982125
ㄴ 아주 높은 점수당 !!
<테스트 세트에 대한 점수 확인>
print(lr.score(test_poly, test_target))
output
0.9714559911594111
ㄴ 테스트 세트에 대한 점수는 높아지지 않았지만 과소적합 문제는 해결하였다
<특성 더 추가해서 만들기>
poly = PolynomialFeatures(degree = 5, include_bias = False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
test_poly = poly.transform(test_input)
print(train_poly.shape)
output
(42, 55)
ㄴ train_poly 배열의 열의 개수가 특성의 개수여서 만들어진 특성의 개수가55개나 된다 ㄷㄷ
<이 데이터로 선형 회귀 모델 다시 훈련>
lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))
output
0.9999999999996433
ㄴ 거의 완벽하다.. 점수가 ㅎㅎ
<테스트 세트에 대한 점수 출력하기>
print(lr.score(test_poly, test_target))
output
-144.40579436844948
아주 큰 음수가 나온 이유!
ㄴ 특성의 개수를 크게 늘리면 선형 모델은 매우 강력해져서 훈련 세트에 대해 거의 완벽하게 학습할 수 있지만 이런 애들은 훈련 세트에 너무 과대적합 되기 때문에 테스트 세트에서는 형편없는 점수를 나타냄
=> 특성을 줄여야 한다 ~~
: 머신러닝 모델이 훈련 세트를 넘 과도하게 학습하지 못하도록 하는 것 (= 과대적합 되지 않도록!!)
선형 회귀 모델에서는 특성에 곱해지는 계수의 크기를 작게 만드는 일
근데 특성의 스케일이 정규화 되지 않으면 계수 값도 차이가 나기 때문에 규제를 적용하기 전에 먼저 정규화를 해야한당. 그 전에는 표준점수로 바꾸었지만 이번에는 StrandardScaler를 사용해보장
<StrandardScaler 클래스를 사용하기>
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_poly)
train_scaled = ss.transform(train_poly)
test_scaled = ss.transform(test_poly)
ㄴ StrandardScaler클래스의 객체 ss를 초기화 한 후 PolynomialFeatures 클래스로 만든 train_poly를 사용해서 객체 훈련하고 훈련세트로 학습한 변환기를 사용해서 테스트 세트까지 변환해줌
선형 회귀 모델에 규제를 추가한 모델을 릿지
와 라쏘
라고 부른다.
릿지는 계수를 제곱한 값을 기준으로 규제를 적용하고
라쏘는 계수의 절댓값을 기준으로 규제를 적용함 !
<train_scaled 데이터로 릿지 모델 훈련하기>
from sklearn.linear_model import Ridge
ridge = Ridge()
ridge.fit(train_scaled, train_target)
print(ridge.score(train_scaled, train_target))
output
0.9896101671037343
<테스트 세트에 대한 점수 확인하기>
print(ridge.score(test_scaled, test_target))
output
0.9790693977615387
ㄴ 테스트 점수가 정상으로 돌아왔따!!
릿지와 라쏘 모델을 사용할 때 모델 객체를 만들 때 alpha라는 값으로 규제의 양을 임의로 조절할 수 있다. alpha값이 커지면 규제 강도가 세지므로 계수 값을 줄이고, 조금 더 과소적합 되도록 유도함. alpha값이 작으면 계수를 줄이는 역할이 줄어들고 과대적합 될 가능성이 커집니다 !
<matplotlib 임포트 하고 alpha 값 바꿀때마다 score() 메서드의 결과를 저장할 리스트 만들기>
import matplotlib.pyplot as plt
train_score=[]
test_score=[]
<alpha 값 0.001에서 100까지 10배씩 늘려서 릿지 회귀모델 훈련 후 훈련 세트와 테스트 세트 점수를 리스트에 저장하기>
alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]
for alpha in alpha_list:
ridge = Ridge(alpha=alpha)
ridge.fit(train_scaled, train_target)
train_score.append(ridge.score(train_scaled, train_target))
test_score.append(ridge.score(test_scaled, test_target))
ㄴ 0.001 부터 10배씩 늘렸기 때문에 그래프가 왼쪽이 매우 촘촘할 것이다 !! 동일한 간격으로 나타내기 위해서는 로그함수로 바꾸어서 지수로 표현해야함 (0.001 = -3, 0.01 = -2)
plt.plot(np.log10(alpha_list),train_score)
plt.plot(np.log10(alpha_list),test_score)
plt.xlabel('alpha')
plt.ylabel('R^2')
plt.show()
ㄴ 파랑색이 훈련 세트, 주황색이 테스트 세트다
그래프의 왼쪽을 보면 훈련세트랑은 아주 잘 맞는데 테스트 세트랑은 잘 안맞음 (=과대적합) 하지만 오른 쪽을 보면 훈련 세트와 테스트 세트의 점수가 모두 낮아짐 (=과소적합)
=> 적절한 알파값은 -1 (0.1)이다 ~~ 0.1로 알파값을 지정해서 최종 모델을 훈련하장
ridge = Ridge(alpha = 0.1)
ridge.fit(train_scaled, train_target)
print(ridge.score(train_scaled, train_target))
print(ridge.score(test_scaled, test_target))
output
0.9903815817570367
0.9827976465386928
ㄴ 비슷하게 높고 과대적합과 과소 적합 사이에서 균형을 맞춘다 ~~
라쏘 회귀는 릿지 회귀와 비슷함. Ridge 클래스를 Lasso 클래스로 바꾸면 끝 !!
from sklearn.linear_model import Lasso
lasso = Lasso()
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target)) #0.989789897208096
ㄴ 과대적합을 잘 억제 했음
<테스트 세트 점수 확인>
print(lasso.score(test_scaled, test_target)) #0.9800593698421883
테스트 세트 점수도 좋다 !! 라쏘 모델도 alpha로 규제 강도 조절이 가능하다
<alpha 값 바꾸어 가며 훈련 세트와 테스트 세트 간 점수 계산>
train_score=[]
test_score=[]
alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]
for alpha in alpha_list:
lasso = Lasso(alpha = alpha, max_iter = 10000) #lasso 모델 만들기
lasso.fit(train_scaled, train_target) # lasso 모델 훈련하기
train_score.append(lasso.score(train_scaled, train_target)) # 훈련 점수와 테스트 점수 저장하기
test_score.append(lasso.score(test_scaled, test_target))
<train_score와 test_score 리스트를 사용해서 그래프 그리기>
plt.plot(np.log10(alpha_list),train_score)
plt.plot(np.log10(alpha_list),test_score)
plt.xlabel('alpha')
plt.ylabel('R^2')
plt.show()
ㄴ 파랑색이 훈련 세트 그래프, 주황색이 테스트 세트 그래프이다
그래프 왼쪽은 과대적합, 오른쪽으로 갈 수록 훈련 세트와 테스트 세트의 점수가 좁혀지면서 가장 오른쪽은 아주 크게 점수가 떨어짐 (과소적합)
=> 최적의 alpha 값은 1, 10^1 = 10이다
<다시 모델 훈련하기>
lasso = Lasso(alpha=10)
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target))
print(lasso.score(test_scaled, test_target))
output
0.9888067471131867
0.9824470598706695