[ML] 머신러닝 4강 - 값을 예측하고 싶을 땐 회귀

 

한 문장 요약!

✔️ 독립변수(X)와 종속변수(Y) 간의 관계를 활용해 연속형 결과(소수 포함 다양한 숫자)를 예측하는 것

     (연속적이지 않은 것 -> 정수만 있는 것)

 

  • 종속변수(Y) : 예측하고자 하는 변수
  • 독립변수(X) : 종속변수 외 모든 변수

회귀 분석이란?

종속변수와 하나 이상의 독립변수 간의 관계를 추정 -> 연속형 종속변수를 예측하는 통계/머신러닝 기법

예) "공부한 시간(x)에 따라 시험점수(y)가 어떻게 변하는가?"를 예측

 

- 지도학습 

  • 분류(Classification) : 결과값이 이산형(클래스 라벨)
  • 회귀(Regression) : 결과값이 연속형(숫자 값)

- 사용이유

1. 미래 값 예측 : 판매량, 주가, 온도 등 예측

2. 인과 관계 해석(통계 관점) : 특정 독립변수가 종속변수에 미치는 영향력 해석

3. 데이터 기반 의사결정 : 추세(trend)파악, 자원 분배 등

 

회귀 -> 시간에 따라 변하는 데이터들을 다루는 곳에서 굉장히 많이 사용

사례 -> 경제(주식가격, 판매량), 건강(혈압, 콜레스테롤 수치 예측), 제조(불량률, 생산량 예측) // 불량여부는 '분류'임!


선형 회귀

독립변수(X)와 종속변수(Y)가 선형적(일차 방정식 형태)으로 관계를 맺음, 직선관계, 단순한 관계

 

선형 회귀 모델 학습 과정

1. 가중치(회귀계수) 초기화

2. 손실함수(Loss Function) 설정 : 오차, 손실 -> MSE(Mean Squared Error) 사용

3. 최적화 : 수학적 방법(최소자승법), 경사하강법(Gradient Descent) 등으로 가중치 업데이트

 

-> 오차가 가장 작은 베타 값을 찾으면 방정식 결정.  X값 넣으면 Y값 예측!!

예시.

실제로는 독립변수가 1개 보다는 더 많음.

 

✍️ 장단점 : 해석이 간단하고 구현이 쉬움. but, 선형성이 아닐 경우 예측력 저하

 

선형회귀 코드

더보기
import numpy as np
import pandas as pd
from sklearn.datasets import load_diabetes
from sklearn.linear_model import LinearRegression, SGDRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

# 1. 데이터 로드
diabetes = load_diabetes()
X = diabetes.data
y = diabetes.target

print(X.shape)
print(y.shape)
# 2. 학습/테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

print(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)
# 3. 선형회귀 (LinearRegression) 모델
lin_reg = LinearRegression()
lin_reg.fit(X_train, y_train)
# 예측
y_pred_lin = lin_reg.predict(X_test)

# 성능 측정
mse_lin = mean_squared_error(y_test, y_pred_lin)
r2_lin = r2_score(y_test, y_pred_lin)

# 평균 비율 오차
def MPE(y_true, y_pred):
    return np.mean((y_true - y_pred) / y_true) * 100

print("[LinearRegression 결과]")
print("가중치(coefficient):", lin_reg.coef_)
print("절편(intercept):", lin_reg.intercept_)
print("MSE:", mse_lin)
print("R2 점수:", r2_lin)
print("평균 비율 오차 : ", MPE(y_test, y_pred_lin))

* MSE, R2, 평균비율 오차 : 평가 지표  |   * 가중치, 절편 : 머신러닝 모델을 해석할 때 사용

- 첫번째 독립변수는 크지 않은 값 -> 중요하지 않은 변수

- 931 값은 매우 중요한 값

- MSE : '0'에 가까울수록 좋은 것

- R2 : 1에 가까워지면 좋은 것

- 평균 비율 오차 : 실제 값대비 예측값이 전체적으로 몇퍼센트 정도 나타났는지. '0'에 가까울수록 좋다.

   ( * 예측값 > 실제값 : (-)값   |   예측값 < 실제값 : (+)값 )

# 4. SGDRegressor 모델 (✔️경사하강법)
sgd_reg = SGDRegressor(max_iter=6000, tol=1e-3, random_state=42)
sgd_reg.fit(X_train, y_train)
# 예측
y_pred_sgd = sgd_reg.predict(X_test)

# 성능 측정
mse_sgd = mean_squared_error(y_test, y_pred_sgd)
r2_sgd = r2_score(y_test, y_pred_sgd)

# 평균 비율 오차
def MPE(y_true, y_pred):
    return np.mean((y_true - y_pred) / y_true) * 100

print("[SGDRegressor 결과]")
print("가중치(coefficient):", sgd_reg.coef_)
print("절편(intercept):", sgd_reg.intercept_)
print("MSE:", mse_sgd)
print("R2 점수:", r2_sgd)
print("평균 비율 오차 : ", MPE(y_test, y_pred_sgd))

방정식에서 B(베타)값을 찾는 과정이었음.


다항 회귀 (Polynomial Regression)

✔️ 비선형적인 관계를 다항식 형태로 모델링 : 2차 다항식, n차 다항식

예시.

  • 제조 : 온도와 반응율 관계가 곡선 형태인 경우
  • 건강 : 나이와 특정 지표(근육량 등)가 곡선 형태인 경우

주의.

  • 고차항을 무작정 늘리면, 과적합(overfitting) 문제 : 일반화가 잘 되지 않는 상황
  • 균형을 잘 맞춰야!!

다항회귀 코드

더보기
import numpy as np
import pandas as pd
from sklearn.datasets import make_friedman1
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.pipeline import Pipeline

# 1) 비선형 데이터 생성 (make_friedman1)
# n_samples: 샘플 개수, n_features: 특성 개수, noise: 잡음 크기
X, y = make_friedman1(n_samples=1000, n_features=5, noise=1.0, random_state=42)
print(X.shape)
print(y.shape)
# 2) 학습/테스트 분리
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)

print(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)
# 3) 단순 선형회귀 모델 (비교용)
lin_reg = LinearRegression()
lin_reg.fit(X_train, y_train)
y_pred_lin = lin_reg.predict(X_test)

mse_lin = mean_squared_error(y_test, y_pred_lin)
r2_lin = r2_score(y_test, y_pred_lin)

# 평균 비율 오차
def MPE(y_true, y_pred):
    return np.mean((y_true - y_pred) / y_true) * 100

print("[단순 선형회귀 결과]")
print("MSE:", mse_lin)
print("R2:", r2_lin)
print("평균 비율 오차 : ", MPE(y_test, y_pred_lin))
print()
# 4) Polynomial Regression (2차 예시)
poly_model = Pipeline([
    ("poly", PolynomialFeatures(degree=2, include_bias=False)),
    ("lin_reg", LinearRegression())
])
poly_model.fit(X_train, y_train)
y_pred_poly = poly_model.predict(X_test)

mse_poly = mean_squared_error(y_test, y_pred_poly)
r2_poly = r2_score(y_test, y_pred_poly)

# 평균 비율 오차
def MPE(y_true, y_pred):
    return np.mean((y_true - y_pred) / y_true) * 100

print("[다항회귀(2차) 결과]")
print("MSE:", mse_poly)
print("R2:", r2_poly)
print("평균 비율 오차 : ", MPE(y_test, y_pred_poly))

* Pipeline : 여러개의 머신러닝을 차례대로 돌리거나, 전처리 후 머신러닝을 하는 등 여러가지 처리순서를 하나로 묶어서 처리하게 끔하는 sklearn의 기능


회귀모델 평가 방법

✔️ MSE (Mean Squared Error)

  • 예측값과 실제값의 차이를 제곱하여 평균
  • 오차가 클수록 제곱에 의해 더 큰 벌점 -> 큰 오차에 민감
  • "평균 제곱 오차", 회귀 모델 평가에 매우 자주 사용

✔️ MAE (Mean Absolute Error)

  • 예측값과 실제값의 차이를 절댓값으로 측정한 후 평균
  • 예측이 평균적으로 실제에서 얼마나 벗어났는지 직관적으로 표현

✔️ RMSE (Root Mean Squared Error)

  • MAE와 달리 제곱을 통해 큰 오차에 가중치를 더 주는 특징
  • 오차가 클수록 패널티가 커지므로, 큰 오차가 중요한 문제에서 자주 사용

✔️ R2 (결정 계수)

  • 0에서 1 사이의 값 (음수가 될 수도)
  • 1 에 가까울수록 학습된 모델이 데이터를 잘 설명! (0은 그 반대로 해석)
  • (-) 값이 나오면 전처리부터 잘못된 것은 없는지 전면적 검토 필요!

고급 회귀 기법

선형 회귀에 규제항을 추가하여 과적합을 방지

Lasso & Ridge Regression

 

✔️ Ridge(릿지) 회귀

  • 가중치 제곱합(L2 Norm)을 페널티로 추가
  • 가중치가 너무 커지지 않도록 방지(가중치 값을 부드럽게 줄임) -> 과적합을 낮춤 -> 일반화

 

✔️ Lasso(라쏘) 회귀

  • 가중치 절댓값합(L1 Norm)을 패널티로 추가
  • 가중치를 0으로 만들어 변수 선택(Feature Selection) 효과
💡 규제는 가중치를 제한해서 일반화시키는 데 도움을 준다. 과적합을 방지하는데 도움이 된다.

 

릿지회귀 & 라쏘회귀 코드

더보기
import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge, Lasso
from sklearn.metrics import mean_squared_error, r2_score

# 1. 데이터 로드
housing = fetch_california_housing()
X = housing.data
y = housing.target

print(X.shape)
print(y.shape)
# 2. 학습/테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

print(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)
# 3. Ridge 회귀
# alpha=1.0 (규제 세기) 는 필요에 따라 조정 가능
ridge_reg = Ridge(alpha=1.0, random_state=42)
ridge_reg.fit(X_train, y_train)

* 알파 값(위 공식에서 람다값, 0과 1사이 값)은 규제를 얼마나 강하게 해줄 것이냐를 말함

# 예측
y_pred_ridge = ridge_reg.predict(X_test)

# 성능 평가
mse_ridge = mean_squared_error(y_test, y_pred_ridge)
r2_ridge = r2_score(y_test, y_pred_ridge)

# 평균 비율 오차
def MPE(y_true, y_pred):
    return np.mean((y_true - y_pred) / y_true) * 100

print("[Ridge 회귀 결과]")
print("  가중치(coefficient):", ridge_reg.coef_)
print("  절편(intercept):", ridge_reg.intercept_)
print("  MSE:", mse_ridge)
print("  R^2 점수:", r2_ridge)
print("평균 비율 오차 : ", MPE(y_test, y_pred_ridge))
print()

 

# 4. Lasso 회귀
# alpha=0.1 정도로 조금 낮춰 볼 수도 있음 (기본값 1.0)
# alpha가 너무 크면 가중치가 0이 되어 과소적합 위험이 있습니다.
lasso_reg = Lasso(alpha=0.1, random_state=42, max_iter=10000)
lasso_reg.fit(X_train, y_train)

* max_iter : 얼마나 반복할 것인지 설정 (릿지, 라쏘 둘다 가능)

# 예측
y_pred_lasso = lasso_reg.predict(X_test)

# 성능 평가
mse_lasso = mean_squared_error(y_test, y_pred_lasso)
r2_lasso = r2_score(y_test, y_pred_lasso)

# 평균 비율 오차
def MPE(y_true, y_pred):
    return np.mean((y_true - y_pred) / y_true) * 100

print("[Lasso 회귀 결과]")
print("  가중치(coefficient):", lasso_reg.coef_)
print("  절편(intercept):", lasso_reg.intercept_)
print("  MSE:", mse_lasso)
print("  R^2 점수:", r2_lasso)
print("평균 비율 오차 : ", MPE(y_test, y_pred_lasso))

총 정리

1. 회귀 모델은 연속형 결과 변수를 예측하는 데 사용
2. 선형 회귀는 가장 기본적 형태지만, 데이터 패턴이 비선형일 경우 다항 회귀 등 고려
3. 규제 기법을 활용한 모델(라쏘, 릿지)을 가중치를 규체하여 과적합을 방지
4. 앙상블 기법을 사용하는 경우 복잡한 비선형 패턴을 더 잘 포착할 수 있음
5. 모델의 성능 평가는 MAE, RMSE, R^2 등 다양한 지표를 통해 진행