Post

3.회귀 알고리즘과 모델 규제

3.회귀 알고리즘과 모델 규제

3-1 K-최근접 이웃 회귀

K-최근접 이웃 회귀

분류와 회귀

  • 분류: 샘플을 몇 개의 클래스 중 하나로 분류하는 문제
  • 회귀: 클래스 중 하나로 분류하는 것이 아니라 임의의 어떤 숫자를 예측하는 문제 (두 변수 사이의 상관관계를 분헉하는 방법)

K-최근접 이웃 회귀

alt text

  • 이웃 샘플의 수치를 사용해 새로운 샘플의 타깃을 예측 -> 평균 이용

데이터 준비

  • 길이(특성) —예측—> 무게(타깃)

데이터 준비

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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])

산점도 그리기

1
2
3
4
5
import matplotlib.pyplot as plt
plt.scatter(perch_length, perch_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

alt text

훈련 세트와 테스트 세트로 나누기

1
2
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차원 배열이어야 함.

alt text

1
2
3
4
5
train_input = train_input.reshape(-1,1)
#넘파이는 배열의 크기를 자동으로 지정하는 기능도 제공하는데, 크기에 -1 지정하면 나머지 원소 개수로 모두 채우라는 의미이다. 
test_input = test_input.reshape(-1,1)
print(train_input.shape, test_input.shape)
#(42, 1) (14, 1)

결정계수(R^2)

  • 사이킷런에서 k-최근접 이웃 회귀 알고리즘을 구현한 클래스는 KNeighborsRegressor.

    학습/테스트

    1
    2
    3
    4
    5
    
    from sklearn.neighbors import KNeighborsRegressor
    knr = KNeighborsRegressor()
    knr.fit(train_input, train_target)
    print(knr.score(test_input, test_target))
    #0.9928094061010639
    

    점수의 의미?

  • 분류의 경우: 정확도(저압을 맞힌 개수의 비율)
  • 회귀의 경우: 결정계수

    alt text

    • 타깃이 예측에 가까워짐 : 1에 가까워짐
    • 타깃의 평균 정도를 예측하는 수준 : 0에 가까워짐 alt text
  • 타깃과 예측의 절댓값 오차의 평균
    1
    2
    3
    4
    5
    
    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
    

과대적합 vs 과소적합

  • 과대적합: 훈련 세트 점수가 굉장히 좋았는데 테스트 세트에서는 점수가 굉장히 나쁠때
  • 과소적합: 훈련 세트보다 테스트 세트의 점수가 높거나 두 점수가 모두 너무 낮은 경우
    • 왜 일어날까?: 훈련 세트와 테스트 세트의 크기가 매우 작기 때문.

      과소적합? 과대적합?

      1
      2
      
      print(knr.score(train_input, train_target))
      #0.9698823289099255
      
      1
      2
      
      print(knr.score(test_input, test_target))
      #0.9928094061010639
      
  • 정답: 과소적합

해결방안(과소적합)

  • 모델을 더 복잡하게 만든다.
    • 훈련 세트에 더 잘 맞게 만든다.
    • K-최근접 이웃 알고리즘: 이웃의 개수 k를 줄이면 복잡해진다(국지적인 패턴에 민감해짐)
      1
      2
      3
      4
      5
      6
      
      knr.n_neighbors = 3
      knr.fit(train_input, train_target)
      print(knr.score(train_input, train_target))
      #0.9804899950518966
      print(knr.score(test_input, test_target))
      #0.9746459963987609
      

정리

키워드

  • 회귀: 임의의 수치를 예측하는 문제. 따라서 타깃값도 임의의 수치가 됩니다.
  • K-최근접 이웃 회귀: k-최근접 이웃 알고리즘을 이용해 회귀 문제를 풉니다. 가장 가까운 이웃 샘플을 찾고 이 샘플들의 타깃값을 평균하여 예측으로 삼습니다.
  • 결정계수: 회귀 문제의 성능 측정 도구. 1에 가까울수록 좋고, 0에 가깝다면 성능이 나쁜 모델이다.
  • 과대적합
  • 과소적합

핵십 패키지와 함수

  • scikit-learn
    • KNeighborsRegressor
    • mean_absolute_error()
    • mean_squared_error()
    • reshape()

3-2 선형 회귀

K-최근접 이웃의 한계

1
2
3
print(knr.predict([[50]]))
#[1033.33333333]
# 실제 무게는 1.5kg인데 예측한 무게는 1033g이다. 차이가 많이 난다.

산점도 그려보기

1
2
3
4
5
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()

alt text

  • 새로운 샘플이 훈련 세트의 범위를 벗어나면 엉뚱한 값을 예측.

선형 회귀

1
2
3
4
5
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_input, train_target)
print(lr.predict([[50]]))
#[1241.83860323]

기울기와 절편

alt text

1
2
print(lr.coef_, lr.intercept_)
#[39.01714496] -709.0186449535477
  • coef: 기울기 / intercept: 절편
1
2
3
4
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()

alt text

점수 확인

1
2
3
4
print(lr.score(train_input, train_target))
#0.939846333997604
print(lr.score(test_input, test_target))
#0.8247503123313558
  • 문제1. 과소적합
  • 문제2. 그래프에서 왼쪽 아래로 계속 내려가면 무게가 0g 이하로 가는데 있을 수 없는 일

다항 회귀

길이 제곱한 항 추가

1
2
3
4
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)

모델 학습

1
2
3
4
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.predict([[50**2, 50]]))
#[1573.98423528]

그래프 그리기

1
2
print(lr.coef_, lr.intercept_)
#[  1.01433211 -21.55792498] 116.0502107827827
1
2
3
4
5
6
point = np.arange(15,50)
# 15 ~ 49 까지 정수 배열
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()

alt text

점수

1
2
3
4
print(lr.score(train_poly, train_target))
#0.9706807451768623
print(lr.score(test_poly, test_target))
#0.9775935108325122

정리

키워드

  • 선형 회귀: 특성과 타깃 사이의 관게를 가장 잘 나타내는 선형 방정식을 찾는다. 특성이 아나면 직선 방정식이 된다.
    • 선형 회귀가 찾은 특성과 타깃 사이의 관계는 선형 방정식의 계수 또는 가중치에 저장됩니다. 머신러닝에서 종종 가중치는 방정식의 기울기와 절편을 모두 의미하는 경우가 많습니다.
  • 모델 파라미터: 선형 회귀가 찾은 가중치처럼 머신러닝 모델이 특성에서 학습한 파라미터를 말합니다.
  • 다항 회귀: 다항식을 사용하여 특성과 타깃 사이의 관계를 나타냅니다. 이 함수는 비선형일 수 있지만 여전히 선형 회귀로 표현할 수 있습니다.

핵심 패키지와 함수

  • scikit-learn
    • LinearRegression
      • fit_intercept: True로 지정하면 절편 학습x
      • coef_: 계수를 포함한 배열
      • intercept_: 절편

3-3 특성 공학과 규제

다중 회귀

  • 다중 회귀: 여러 개의 특성을 사용한 선형 회귀

alt text

  • 특성 공학: 기존의 특성을 사용해 새로운 특성을 뽑아내는 작업

데이터 준비

판다스

  • 판다스: 유명한 데이터 분석 라이브러리
  • 데이터프레임: 판다스의 핵심 데이터 구조
  • 데이터 프레임 -> 넘파이 배열

데이터 준비

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
df = pd.read_csv('https://bit.ly/perch_csv')
perch_full = df.to_numpy()
print(perch_full)

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])

훈련 세트와 테스트 세트

1
2
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)

사이킷런의 변환기

  • 변환기: 특성을 만들거나 전처리하는 클래스
  • 추정기: 사이킷런의 모델 클래스

PolynomialReatures 클래스

  • fit(): 새롭게 만들 특성 조합을 찾는다.
  • transform(): 실제로 데이터를 변환한다.
1
2
3
4
5
6
7
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures()
poly.fit([[2, 3]])
# fit을 해야 transform이 가능합니다. 사이킷런의 일관된 api 때문에  단계로 나뉘어져 있습니다.  메서드를 하나로 붙인 fit_transform 메서드도 있습니다.

print(poly.transform([[2, 3]]))
# [[1. 2. 3. 4. 6. 9.]]
  • 추가하는 항: 각 특성을 제곱한 항 / 특성끼리 서로 곱한 항 / 1 (절편은 항상 값이 1인 특성과 곱해지는 계수이니 특성은 ‘길이, 높이, 두께, 1’이 됩니다.)
  • include_bias = False
    • 절편을 추가하지 않는다.

적용

1
2
3
4
5
6
7
8
9
10
poly = PolynomialFeatures(include_bias=False)
poly.fit(train_input)
train_poly = poly.transform(train_input)
print(train_poly.shape)
# (42, 9)

poly.get_feature_names_out()
# array(['x0', 'x1', 'x2', 'x0^2', 'x0 x1', 'x0 x2', 'x1^2', 'x1 x2','x2^2'], dtype=object)

test_poly = poly.transform(test_input)
  • PolynomialFeatures 클래스는 fit() 메서드에서 만들 특성의 조합을 준비하기만 하고 별도의 통계 값을 구하지 않음. but! 훈련 세트를 기준으로 테스트 세트를 변환하는 습관을 들이는 것이 좋습니다.

다중 회귀 모델 훈련하기

과소적합 해결

1
2
3
4
5
6
7
8
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))
# 0.9903183436982125

print(lr.score(test_poly, test_target))
# 0.9714559911594111

과대 적합

1
2
3
4
5
6
7
8
9
10
11
12
13
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)
# (42,55)

lr.fit(train_poly, train_target)
print(lr.score(train_poly, train_target))
#0.9999999999996433

print(lr.score(test_poly, test_target))
#-144.40579436844948
  • 특성 늘임 -> 과적합 (샘플 개수보다 특성이 많을 때)

규제

  • 규제: 선형 회귀 모델의 경우 특성에 곱해지는 계수의 크기를 작게 만듦(모델이 훈련 세트에 과대적합되지 않도록 만드는 것.)

정규화 하기

  • 특성의 스케일 정규화x -> 특성마다의 계수 값의 차이가 커짐
  • 훈련 세트에서 학습한 평균과 표준편차는 StandardScaler 클래스 객체의 mean_.scale_ 속성에 저장됨.
1
2
3
4
5
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_poly)
train_scaled = ss.transform(train_poly)
test_scaled = ss.transform(test_poly)

릿지 회귀

  • 릿지: 계수를 제곱한 값을 기준으로 규제 적용
1
2
3
4
5
6
7
8
from sklearn.linear_model import Ridge
ridge = Ridge()
ridge.fit(train_scaled, train_target)
print(ridge.score(train_scaled, train_target))
# 0.9896101671037343

print(ridge.score(test_scaled, test_target))
# 0.9790693977615387

alpha

  • 하이퍼파라미터: 사람이 알려줘야 하는 파라미터
  • alpha: 규제의 강도 조절(값이 크면 규제 강도가 세짐)
    • 강도 세짐 -> 계수 줄임 -> 과소적합 유도
    • 강도 약해짐 -> 과대적합 가능성 증가 최적의 alpha 값 찾기
  • R^2 그래프 그려보기
    • 훈련 세트와 테스트 세트의 점수가 가장 가까운 지점이 최적의 alpha
1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt
train_score = []
test_score = []
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))

plt.plot(np.log10(alpha_list), train_score)
plt.plot(np.log10(alpha_list), test_score)
plt.show()

alt text

  • 왼쪽: 과대적합 / 오른쪽: 과소적합
  • -1 지점이 가장 적합 -> 10^(-1) = 0.1!

최종 모델 훈련

1
2
3
4
5
6
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))
# 0.9903815817570367
# 0.9827976465386928

라쏘 회귀

1
2
3
4
5
6
7
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 값 찾기

1
2
3
4
5
6
7
8
9
10
11
12
13
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.fit(train_scaled, train_target)
    train_score.append(lasso.score(train_scaled, train_target))
    test_score.append(lasso.score(test_scaled, test_target))
# convergenceWarning 경고: 지정한 반복 횟수가 부족할 

plt.plot(np.log10(alpha_list), train_score)
plt.plot(np.log10(alpha_list), test_score)
plt.show()

alt text

  • 최적의 값은 1 -> 10^1 !

최종 모델 훈련

1
2
3
4
5
6
7
8
9
10
lasso = Lasso(alpha = 10)
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target))
# 0.9888067471131867
print(lasso.score(test_scaled, test_target))
# 0.9824470598706695

print(np.sum(lasso.coef_ == 0))
# 40
# coef_: 계수 보기 / np.sum(): 배열을 모두 더한 (true:1 , false: 0)

정리

키워드로 끝내는 핵심 포인트

  • 다중 회귀: 여러 개의 특성을 사용하는 회귀 모델. 특성이 많으면 선형 모델은 강력한 성능을 발휘합니다.
  • 특성 공학: 주어진 특성을 조합하여 새로운 특성을 만드는 일련의 작업 과정입니다.
  • 릿지: 규제가 있는 선형 회귀 모델 중 하나이며 선형 모델의 계수를 작게 만들어 과대 적합을 완화시킵니다. 릿지는 비교적 효과가 좋아 널리 사용하는 규제 방법.
  • 라쏘: 계수를 아예 0으로 만들 수 있음
  • 하이퍼파라미터: 머신러닝 알고리즘이 학습하지 않는 파라미터.

핵심 패키지와 함수

  • pandas
    • read_csv()
      • header: 데이터프레임의 열 이름으로 사용할 csv 파일의 행 번호를 지정.
      • skiprows: 파일에서 읽기 전에 건너뛸 행의 개수를 지정합니다.
      • nrows: 파일에서 읽을 행의 개수를 지정
  • scikit-learn
    • PolynomialFeatures
    • Ridge
      • alpha
      • solver: 매개변수에 최적의 모델을 찾기 위한 방법(auto, sag, saga)
    • Lasso
      • 좌표 하강법 이용
      • alpha
      • max_iter
      • random_state
This post is licensed under CC BY 4.0 by the author.

Trending Tags