본문 바로가기
Data Analytics/MachineLearning

[ML] 머신러닝 지도학습의 회귀(regression)의 종류와 실습해보기

by Istj_eff 2022. 11. 7.
💡 회귀는 여러개의 독립변수와 한 개의 종속변수 간의 상관관계를 모델링하는 것이 기본적인 개념이며, 독립변수에 영향을 미치는 회귀 계수(Regression coefficients)의 최적값을 찾아 종속변수를 예측해내는 것이다.
  • ex) 아파트의 방 개수, 방 크기, 주변 학군, 교통 등 여러개의 독립변수에 따라 아파트 가격이라는 종속변수가 어떤 관계를 나타내는지 모델링하고 예측하는 것독립변수 개수 회귀 계수의 결합

    1개 : 단일 회귀 선형 : 선형 회귀
    여러 개 : 다중(다항) 회귀 비선형 : 비선형 회귀
  • 분류는 예측값이 카테고리와 같은 이산형 클래스값이고, 회귀는 연속형 숫자값이다.
    • 회귀는 연속적인 숫자를 예측하는 것이다. 사람의 나이, 농작물의 수확량, 주식 가격 등 출력값이 연속성을 갖는것
  • 회귀에서 가장 중요한 것은 바로 회귀 계수이다
📖 아파트 예제를 가져와서 설명하자면,
Y = W1X1 + W2X2 +W3X3 +...+ WnXn 라는 선형 회귀식에서
Y = 종속변수(아파트 가격)
X = 독립변수(방 개수, 면적, 거리 등)
W = 독립변수의 값에 영향을 미치는 회귀 계수 (Regression coefficients)
  • 머신러닝 관점에서 보면 독립변수는 Feature, 종속변수는 결정 값(Target)이다.  머신러닝 회귀 예측의 핵심은 주어진 피처와 결정 값 데이터 기반에서 학습을 통해 최적의 회귀 계수를 찾아내는 것이다.
  • 이 회귀 계수가 선형이나 아니냐에 따라 선형회귀와 비선형 회귀로 나눌수 있으며, 독립변수의 개수가 한개 인지 여러개인지에 따라 단일 회귀, 다중 회귀로 나뉜다. 
  • 회귀 평가 지표(MAE, MSE, RMSE, R²)


1. 선형회귀 (Linear regression)

  • 여러가지 회귀중에서 선형 회귀(Linear regression)를 가장 많이 사용된다.
  • 선형회귀는 예측값과 실제값의 RSS(Residual Sum of Squares)를 최소화해 OLS(Ordinary Least Squares)추정 방식으로 구현한 클래스이다.
    • RSS(잔차제곱합) : 데이터와 추정 모델 사이의 불일치를 평가하는 척도
      • RSS가 작으면 모델이 데이터를 잘 설명하는 것이다.
      • 제곱합 = 설명되는 제곱합 + 잔차 제곱합
    • LinearRegression클래스는 fit()메서드로 X,y배열을 입력받으면 회귀계수인 W를 coef_속성에 저장한다.
    • LinearRegression은 계수 w = (w1,…, wp)를 사용하여 선형 모델을 피팅하여 데이터 세트에서 관찰 된 목표와 선형 근사에 의해 예측 된 목표 사이의 잔차 제곱합을 최소화한다.
    • LinearRegression parameter
더보기
  • fit_intercept: 모형에 intercept상수항(절편;그래프와 겹쳐있는 점)이 있는가 없는가를 결정하는 인수 (default : True). False로 설정하면 계산에 인터셉트가 사용되지 않고 0으로 지정된다
    • ex) 데이터가 중앙에 배치 될 것으로 예상 됨
  • normalize: 매개변수 무시 여부. fit_intercept가 False로 설정된 경우 이 매개 변수는 무시된다 . True인 경우 회귀 변수 X는 평균을 빼고 l2- 노름으로 나누어 회귀 전에 정규화된다. 표준화하려면 normalize=False를 사용하여 추정기에 fit을 호출하기 전에 StandardScaler를 사용 .
  • copy_X: X의 복사 여부. 그렇지 않으면 덮어 쓸 수 있다.
  • n_jobs: 계산에 사용할 작업 수. 이것은 n_targets> 1과 충분한 큰 문제에 대해서만 속도 향상을 제공
  • positivebool : True 로 설정 하면 계수가 양수가됩니다. 이 옵션은 고밀도 어레이에만 지원
  • coef_: fit()메서드를 수행했을때 회귀 계수가 배열 형태로 저장하는 속성. shape는 (target값 갯수, feature 갯수)
  • 선형 회귀 모델은 규제(Regularization)방법에 따라 여러 유형으로 나뉜다.
    (규제 : 선형 회귀의 과적합 문제를 해결하기 위해 회귀계수에 페널티 값을 적용하는것)
    • 일반 선형 회귀 : 예측값과 실제값의 RSS(Residual Sum of Squares)를 최소화할 수 있도록 회귀 계수를 최적화하며, 규제를 적용하지 않은 모델
    • 릿지(Ridge) : L2규제를 적용한 회귀
    • 라쏘(Lasso) : L2규제를 적용한 회귀
    • 엘라스틱넷(ElasticNet) : L1, L2 규제를 함께 결합한 모델
    • 로지스틱 회귀(Logistic Regression)

 

1-1. 단순 선형 회귀를 통해 회귀 이해하기

단순선형회귀는 독립변수도 하나, 종속변수도 하나인 선형회귀이다.

ex) 주택의 크기(독립변수)로만 주택 가격(종속변수)가 결정된다고 하면 2차원 평면에 직선형태의 관계로 표현할 수 있다. 

  • 독립 변수가 1개인 단순 선형 회귀에서는 기울기인 ω1과 절편인 ω0을 회귀 계수로 지칭한다. 위와 같은 1차 함수로 모델링했다면 실제 주택 가격은 이러한 1차 함수 값에서 실제 값만큼의 오류 값을 빼거나 더한 값이 된다.
  • 오류값은 +나 -가 될 수 있기에 전체 데이터의 오류합을 구하기 위해 단순히 더할경우 뜻하지 않게 오류합이 줄어들 수 있다.
  • 따라서 보통 오류합을 계산할 때는 절대값을 취해서 더하거나(MAE), 오류값의 제곱을 구해서 더하는 방식 RSS, (Residual Sum of Square)를 취한다. 즉 Error²=RSS이다.
    • RSS
      • ω0, ω변수(회귀계수)가 중심 변수이므로 각 편미분하여 식으로 표현
      • RSS는 비용(cost)이며 ω변수로 구성되는 RSS를 비용함수라고 한다. 비용함수를 손실함수(Loss function)라고도 한다.

 

1-2. 경사하강법 (Gradient Descent)

💡머신러닝, 딥러닝 알고리즘 학습 시 사용되는 최적화 방법(Optimizer) 중 하나이다.
알고리즘 학습의 목표는 예측값과 실제값간의 차이인 손실함수의 크기를 최소화시키는 파라미터를 찾는것이다.

2022.11.07 - [DataAnalytics/MachineLearning] - [ML] 경사하강법(Gradient Descent)

 

[ML] 경사하강법(Gradient Descent)

1. 경사하강법(Gradient Descent) ? 머신러닝, 딥러닝 알고리즘 학습 시 사용되는 최적화 방법(Optimizer) 중 하나이다. 알고리즘 학습의 목표는 예측값과 실제값간의 차이인 손실함수의 크기를 최소화시

dataanalysisdot.tistory.com

 

1-3. 선형회귀 모델을 위한 데이터 변환

  • 선형 모델은 일반적으로 feature와 target 간에 선형의 관계에 있다고 가정하고, 최적의 선형 함수를 찾아내어 결과를 예측한다.
  • 선형회귀 모델은 피처값과 타깃값의 분포가 정규분포형태를 매우 선호 한다. 특히 타깃값(y)의 경우 정규분포가 아니라 특정값의 분포가 치우친 왜곡(skew)된 형태의 분포도일 경우 예측성능에 부정적인 영향을 미칠 가능성이 높다.

  • 선형 회귀 모델을 적용하기 전에 먼저 데이터에 대한 스케일링/정규화 작업을 수행하는 것이 일반적이다.
  • 일반적으로 feature dataset과 target dataset의 스케일링/정규화 작업을 수행하는 방법은 조금 다르다.
  • feature dataset (x)
    1. StandardScaler 클래스를 이용해 평균 0, 분산 1인 표준 정규분포를 가진 데이터 세트로 변환
    2. MinMaxScaler 클래스를 이용해 최솟값이 0이고, 최댓값이 1인 값으로 정규화를 수행
    3. 스케일링/정규화를 수행한 데이터 셋에 다시 다항 특성을 적용하여 변환(1,2번으로 예측성능이 안되면 사용)
    4. 로그 변환(Log Transformation) : 원래 값에 log 함수를 적용하면 보다 정규 분포에 가까운 형태로 값이 분포하게 된다.
    5. categorical variable은 label encoding이 아닌 one-hot encoding 수행
  • target dataset (y)
    • 일반적으로 로그 변환을 사용한다. (결정 값을 정규 분포나 다른 정규값으로 변환하면 다시 원본 target 값으로 복원하기 어려울 수 있으며, 왜곡된 분포도 형태의 target 값을 로그 변환하여 예측 성능 향항이 된 경우가 많은 사례에서 검증되었기 때문이다.)
정규분포 변환, 최댓값/최솟값 정규화, 로그변환을 적용
from sklearn.preprocessing import StandardScaler, MinMaxScaler, PolynomialFeatures

# method는 표준 정규 분포 변환(Standard), 최대값/최소값 정규화(MinMax), 로그변환(Log) 결정
# p_degree는 다향식 특성을 추가할 때 적용. p_degree는 2이상 부여하지 않음

def get_scaled_data(method='None', p_degree=None, input_data=None):
    if method == 'Standard':
        scaled_data = StandardScaler().fit_transform(input_data)
    elif method == 'MinMax':
        scaled_data = MinMaxScaler().fit_transform(input_data)
    elif method == 'Log':
        scaled_data = np.log1p(input_data)
    else:
        scaled_data = input_data

    if p_degree != None:
        scaled_data = PolynomialFeatures(degree=p_degree, include_bias=False).fit_transform(scaled_data)
    return scaled_data

 

 

Ridge클래스 alpha값을 변화시키면서 feature 데이터셋을 변환하였을 경우 RMSE 값이 어떻게 변하는지 확인
# Ridge의 alpha값을 다르게 적용하고 다양한 데이터 변환방법에 따른 RMSE 추출. 
alphas = [0.1, 1, 10, 100]
#변환 방법은 모두 6개, 원본 그대로, 표준정규분포, 표준정규분포+다항식 특성
# 최대/최소 정규화, 최대/최소 정규화+다항식 특성, 로그변환 

scale_methods=[(None, None), ('Standard', None), ('Standard', 2), 
               ('MinMax', None), ('MinMax', 2), ('Log', None)]
for scale_method in scale_methods:
    X_data_scaled = get_scaled_data(method=scale_method[0], p_degree=scale_method[1], 
                                    input_data=X_data)
    print(X_data_scaled.shape, X_data.shape)
    print('\\n## 변환 유형:{0}, Polynomial Degree:{1}'.format(scale_method[0], scale_method[1]))
    get_linear_reg_eval('Ridge', params=alphas, X_data_n=X_data_scaled, 
                        y_target_n=y_target, verbose=False, return_coeff=False)
  • 결과를 보면 표준 정규 분포와 최대/최소값 정규화로 feature dataset을 변경해도 성능의 개선이 없다.
  • 2차 다항식으로 변환했을 경우 성능 개선이 있지만 다항식 변환은 feature의 개수가 많을 경우 적용하지 힘들고, 데이터가 많아지면 계산에 많은 시간이 소모된다.
  • 반면에 로그 변환을 보면 모든 alpha 값에 대해 비교적 좋은 성능 향상이 있는 것을 알 수 있다.

2. 다항회귀 (Polynomial regression)

  • 독립변수(feature)와 종속변수(target)가 하나일순 없다.
  • 회귀가 독립변수의 단항식이 아닌 2,3차 방정식과 같은 다항식으로 표현된 것을 다항(polynomial)회귀라고 한다.
  • 다항회귀는 비선형 회귀가 아니라 선형회귀임을 주의
  • 사이킷런은 다항 회귀를 위한 클래스를 제공하지 않아서 비선형 함수를 선형 모델에 적용시키는 방법으로 구현한다.
    • 사이킷런의 PolynomialFeatures클래스를 통해 feature를 Polynomial(다항식) feature로 변환한다.

2-1. 단항식 → 다항식

from sklearn.preprocessing import PolynomialFeatures
import numpy as np

# 다항식으로 변환한 단항식 생성
X = np.arange(4).reshape(2,2)
print(X)

[out]
[[0 1]
 [2 3]]

# PolynomialFeatures로 degree=2 인 2차 다항식으로 변환
# degree는 몇차로 바꿀건지 설정
poly = PolynomialFeatures(degree=2)
poly.fit(X)
poly_ftr = poly.transform(X)
print('변한된 2차 다항식 계수 피처:\\n', poly_ftr)

[out]
변한된 2차 다항식 계수 피처:
 [[1. 0. 1. 0. 0. 1.]
 [1. 2. 3. 4. 6. 9.]]

 

2-2. 3차 다항회귀 결정 함수를 임의로 설정하고 회귀 계수로 예측하기

def polynomial_func(X):
    y = 1 + 2*X[:,0] + 3*X[:,0]**2 + 4*X[:,1]**3
    return y

X = np.arange(4).reshape(2,2)
print('일차 단항식 계수 feature:\\n', X)

y = polynomial_func(X)
print('삼차 다항식 결정값:\\n', y)

[out]
일차 단항식 계수 feature:
 [[0 1]
 [2 3]]
삼차 다항식 결정값:
 [  5 125]
  • 3차 다항식 변환은 단항계수feature [x1, x2]를 3차 다항 계수 [1, x1, x2,x1², x1x2, x2², x1³, x1²x2, x1*x2², x2³]과 같이 10개의 다항 계수로 변환한다.
# 3차 다항식 변환
poly_ftr = PolynomialFeatures(degree=3).fit_transform(X)
print('3차 다항식 계수 feature:\\n', poly_ftr)

[out]
3차 다항식 계수 feature:
 [[ 1.  0.  1.  0.  0.  1.  0.  0.  0.  1.]
 [ 1.  2.  3.  4.  6.  9.  8. 12. 18. 27.]]
# Linear Regression에 3차 다항식 계수 feature와 3차 다항식 결정값으로 학습 후 회귀 계수 확인
from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(poly_ftr,y)
print('Polynomial 회귀 계수 :\\n', np.round(model.coef_,2))
print('Polynomial 회귀 shape :\\n', model.coef_.shape)

[out]
Polynomial 회귀 계수 :
 [0.   0.18 0.18 0.36 0.54 0.72 0.72 1.08 1.62 2.34]
Polynomial 회귀 shape :
 (10,)

 

2-3. 사이킷런의 Pipeline객체로 한번에 다항회귀 구현

from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
import numpy as np

def polynomial_func(X):
    y = 1 + 2*X[:,0] + 3*X[:,0]**2 + 4*X[:,1]**3
    return y

# pipeline 객체로 Streamline하게 Polinomial Feature 변환과 LinearRegression 연결
model = Pipeline([('poly', PolynomialFeatures(degree=3)),
                 ('linear', LinearRegression())])

X = np.arange(4).reshape(2,2)
y = polynomial_func(X)

model = model.fit(X,y)

print('Polynomial 회귀 계수 \\n', np.round(model.named_steps['linear'].coef_,2))

[out]
Polynomial 회귀 계수 
 [0.   0.18 0.18 0.36 0.54 0.72 0.72 1.08 1.62 2.34]

 

2-4. 다항 회귀를 이용한 과소적합과적합 이해

  • 차수(degree)가 높을수록 학습 데이터에만 너무 맞춘 학습이 이뤄져서 오히려 예측 정확도가 떨어진다. 즉, 과적합 문제 발생.
  • 원래 데이터 세트는 피처X와 target y가 잡음이 포함된 다항식의 코사인 그래프 관계를 가지도록 만들어준다. 그리고 이에 기반해 다항 회귀의 차수를 변화시키면서 그에 따른 회귀 예측 곡선과 예측정확도를 비교하는 예제이다
  • 학습데이터는 30개의 임의의 데이터인 X, X의 코사인 값에서 약간의 잡음 변동값을 더한 target인 y로 구성
import numpy as np
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score
%matplotlib inline
# 임의의 값으로 구성된 X값에 대해 코사인 변환 값을 반환
def true_func(X):
    return np.cos(1.5 * np.pi * X)

# X는 0부터 1까지 30개의 임의의 값을 순서대로 샘플링한 데이터
np.random.seed(0)
n_samples=30
X = np.sort(np.random.rand(n_samples))

y = true_func(X) + np.random.randn(n_samples)*0.1
plt.figure(figsize=(14,5))
degrees = [1,4,15]

# 다항 회귀의 차수를 1,4,15로 각각 변환시키면서 비교
for i in range(len(degrees)):
    ax = plt.subplot(1, len(degrees), i+1)
    plt.setp(ax,xticks=(), yticks=())
    
    # 개별 degree별로 polynomial변환
    polynomial_features = PolynomialFeatures(degree=degrees[i], include_bias=False)
    linear_regression = LinearRegression()
    pipeline = Pipeline([("polynomial_features", polynomial_features),
                        ("linear_regression", linear_regression)])
    pipeline.fit(X.reshape(-1,1),y)
    
    # 교차검증으로 다항회귀 평가하기
    scores = cross_val_score(pipeline, X.reshape(-1,1), y, scoring="neg_mean_squared_error", cv=10)
    
    # pipeline을 구성하는 세부 객체를 접근하는 named_steps['객체명']을 이용해 회귀계수 추출
    coefficients = pipeline.named_steps['linear_regression'].coef_
    print('\\n Degree {0} 회귀 계수는 {1}입니다' .format(degrees[i], np.round(coefficients,2)))
    print('Degree {0} MSE는 {1}입니다'.format(degress[i], -1*np.mean(scores)))
    
    # 0부터 1까지 테스트 데이터셋을 100개로 나눠 예측 수행
    X_test = np.linspace(0,1,100)
    
    # 예측값 곡선
    plt.plot(X_test, pipeline.predict(X_test[:,np.newaxis]), label="Model")
    # 실제값 곡선
    plt.plot(X_test, true_func(X_test), '--', label="True function")
    plt.scatter(X, y, edgecolor='b', s=20, label="Samples")
    
    plt.xlabel('x'); plt.ylabel('y'); plt.xlim((0,1)); plt.ylim((-2,2)); plt.legend(loc="best")
    plt.title("Degree {}\\nMSE = {:.2e}(+/- {:.2e})" .format(degrees[i], -scores.mean(), scores.std()))
    
plt.show()
[out]
Degree 1 회귀 계수는 [-1.61]입니다
Degree 1 MSE는 0.40772896250986834입니다

Degree 4 회귀 계수는 [  0.47 -17.79  23.59  -7.26]입니다
Degree 4 MSE는 0.04320874987231747입니다

Degree 15 회귀 계수는 [-2.98295000e+03  1.03899930e+05 -1.87417069e+06  2.03717225e+07
 -1.44873988e+08  7.09318780e+08 -2.47066977e+09  6.24564048e+09
 -1.15677067e+10  1.56895696e+10 -1.54006776e+10  1.06457788e+10
 -4.91379977e+09  1.35920330e+09 -1.70381654e+08]입니다
Degree 15 MSE는 182815433.47648773입니다

  • degree1 그래프
    • degree1 예측 곡선은 단순한 직선으로서 단순 선형 회귀와 똑같다. 실제 데이터 세트인 코사인 데이터 세트를 직선으로 예측하기에는 너무 단순하다.
    • 예측 곡선이 학습 데이터의 패턴을 제대로 반영하지 못하고 있는 과소적합 모델이다.
    • MSE값은 약 0.41
  • degree4 그래프
    • degree4 예측 곡선은 실제 데이터 세트와 유사한 모습이다. 변동하는 잡음까지 예측하지는 못했지만, 학습 데이터 세트를 비교적 잘 반영해 코사인 곡선 기반으로 테스트 데이터를 잘 예측한 곡선을 가진 모델이 되었다.
    • MSE 값은 약 0.04로 가장 뛰어난 예측 성능을 나타내고 있다.
  • degree 15 그래프
    • degree15 예측 곡선은 MSE 값이 182581084.83이 될 정도로 어처구니없는 오류값이 발생했다. 과(대)적합 모델이다.
    • 데이터 세트의 변동 잡음값까지 지나치게 반영한 결과, 예측 곡선이 학습 데이터 세트만 정확히 예측하고, 테스트 값의 실제 곡선과는 완전히 다른 형태의 예측 곡선이 만들어졌다.
    • 결과적으로 학습 데이터에 너무 충실하게 맞춘 과적합이 심한 모델이 되었다.

 

 

댓글